Skip to main content

Basic EtherNet/IP scanner

Project description

ethernetip2

Python Version License

A Python implementation of the EtherNet/IP protocol for industrial automation and control systems.

🎯 Overview

This library provides a clean, Pythonic interface to communicate with industrial devices using the EtherNet/IP (Ethernet Industrial Protocol) standard. It supports both explicit messaging (request/response) and implicit messaging (I/O connections) commonly used in industrial automation.

What is EtherNet/IP?

EtherNet/IP is an industrial network protocol that adapts the Common Industrial Protocol (CIP) to standard Ethernet. It's widely used in factory automation, process control, and building automation systems.

✨ Features

  • 🔌 Explicit Messaging - Request/response communication for configuration and data access
  • Implicit I/O - High-speed cyclic data exchange for real-time control
  • 🎛️ Adapter / Target Mode - Act as an EtherNet/IP device: serve Identity, respond to Forward Open, and run Class 1 cyclic I/O
  • 🔍 Device Discovery - Network scanning and device identification
  • 🧵 Thread-Safe - Built-in locking mechanisms for concurrent operations
  • ⚙️ Configurable - Centralized configuration for timeouts, buffer sizes, and I/O parameters
  • 📝 Well-Documented - Clear API with comprehensive docstrings
  • 🐍 Python 2/3 Compatible - Works with Python 2.7 and Python 3.7+

📦 Installation

Release Installation

pip install ethernetip2

From Source

git clone https://github.com/krisl/python-ethernetip.git
cd python-ethernetip
uv sync

Dependencies

  • dpkt - Packet creation and parsing
pip install dpkt

🚀 Quick Start

Device Discovery

Scan your network for EtherNet/IP devices:

import ethernetip

# Create EtherNet/IP instance
enip = ethernetip.EtherNetIP()

# Create explicit connection
conn = enip.explicit_conn("192.168.1.100")

# Scan network
devices = conn.scanNetwork("192.168.1.255", timeout=5)

for device in devices:
    print(f"Found: {device.product_name.decode()}")

Read Device Identity

import ethernetip

# Connect to device
enip = ethernetip.EtherNetIP("192.168.1.100")
conn = enip.explicit_conn()

# Register session
conn.registerSession()

# Read device attributes (Identity Object, Instance 1)
for attr in range(1, 8):
    status, data = conn.getAttrSingle(
        ethernetip.CIP_OBJ_IDENTITY,
        instance=1,
        attribute=attr
    )
    if status == 0:
        print(f"Attribute {attr}: {data}")

I/O Connection (Real-time Data Exchange)

import ethernetip
import time

# Setup connection
enip = ethernetip.EtherNetIP("192.168.1.100")
conn = enip.explicit_conn()
conn.registerSession()

# Register I/O assemblies (1 byte input, 1 byte output)
input_data = enip.registerAssembly(
    ethernetip.EtherNetIP.ENIP_IO_TYPE_INPUT,
    size=1,
    instance=101,
    conn=conn
)
output_data = enip.registerAssembly(
    ethernetip.EtherNetIP.ENIP_IO_TYPE_OUTPUT,
    size=1,
    instance=100,
    conn=conn
)

# Start I/O communication
enip.startIO()
conn.sendFwdOpenReq(
    inputinst=101,
    outputinst=100,
    configinst=1
)
conn.produce()

# Control outputs
try:
    while True:
        # Set output bit 0
        output_data[0] = True
        time.sleep(0.5)

        # Read input bit 0
        print(f"Input bit 0: {input_data[0]}")

        output_data[0] = False
        time.sleep(0.5)
except KeyboardInterrupt:
    pass

# Cleanup
conn.stopProduce()
conn.sendFwdCloseReq(101, 100, 1)
enip.stopIO()

Adapter / Target Mode

Besides acting as a scanner (originator), the library can act as an EtherNet/IP adapter (target/device): it listens for scanner connections, serves the Identity object, responds to Forward Open / Forward Close, and runs Class 1 cyclic I/O.

import time
import ethernetip
from ethernetip.adapter import EtherNetIPAdapter, IO_TYPE_CONSUME, IO_TYPE_PRODUCE
from ethernetip.cip_objects import IdentityObject

identity = IdentityObject(vendor_id=0x1234, device_type=0x000C,
                          product_code=0x0001, product_name="my-device")
adapter = EtherNetIPAdapter(ip="0.0.0.0", identity=identity)

# O->T (consumed): bytes the scanner writes to us
adapter.registerAssembly(150, 4, IO_TYPE_CONSUME,
                         on_update=lambda data: print("got output:", data.hex()))
# T->O (produced): bytes we serve to the scanner
inp = adapter.registerAssembly(100, 4, IO_TYPE_PRODUCE)

adapter.start()
try:
    while True:
        time.sleep(1.0)
        inp.data[0] ^= 0x01      # toggle a bit the scanner will see
finally:
    adapter.stop()

Assembly direction follows the device-vendor convention:

  • IO_TYPE_PRODUCE — produced by the adapter (Target → Originator "input").
  • IO_TYPE_CONSUME — consumed by the adapter (Originator → Target "output").

A runnable version is in examples/adapter_example.py.

📚 API Overview

Main Classes

EtherNetIP

Main class for managing EtherNet/IP communications.

enip = ethernetip.EtherNetIP(ip="192.168.1.100")

Methods:

  • explicit_conn(ipaddr) - Create an explicit messaging connection
  • registerAssembly(iotype, size, inst, conn) - Register I/O assembly
  • startIO() / stopIO() - Start/stop I/O communication
  • listIDUDP(ipaddr, timeout) - Send ListIdentity request via UDP

EtherNetIPExpConnection

Explicit connection for request/response messaging and I/O.

Session Management:

  • registerSession() - Establish session
  • unregisterSession() - Close session

Device Information:

  • listID() - Get device identity
  • listServices() - Get available services
  • scanNetwork(broadcast, timeout) - Scan for devices

Attribute Services:

  • getAttrSingle(class, instance, attribute) - Read single attribute
  • setAttrSingle(class, instance, attribute, data) - Write single attribute
  • getAttrAll(class, instance) - Read all attributes

I/O Connection:

  • sendFwdOpenReq(...) - Open I/O connection
  • sendFwdCloseReq(...) - Close I/O connection
  • produce() - Start producing output data
  • stopProduce() - Stop producing output data

CIP Object IDs

Common CIP object class IDs are provided as constants:

ethernetip.CIP_OBJ_IDENTITY        # 0x01 - Device identity
ethernetip.CIP_OBJ_MESSAGE_ROUTER  # 0x02 - Message router
ethernetip.CIP_OBJ_ASSEMBLY        # 0x04 - Assembly object
ethernetip.CIP_OBJ_CONNECTION      # 0x05 - Connection object
ethernetip.CIP_OBJ_TCPIP           # 0xF5 - TCP/IP interface
ethernetip.CIP_OBJ_ETHERNET_LINK   # 0xF6 - Ethernet link
# ... and more

⚙️ Configuration

Global Configuration

Customize default behavior using the global config object:

from ethernetip import config

# Adjust timeouts
config.DEFAULT_TIMEOUT = 15  # seconds
config.IO_SOCKET_SELECT_TIMEOUT = 3  # seconds

# Adjust buffer sizes
config.RECV_BUFFER_SIZE = 2048  # bytes
config.RECV_BUFFER_SIZE_LARGE = 8192  # bytes

# Adjust I/O timing
config.UDP_IO_RPI_DEFAULT = 50  # ms (faster I/O updates)
config.UDP_IO_MIN_RPI = 5  # ms

Custom Configuration Class

For application-specific settings:

from ethernetip import EtherNetIPConfig

class ProductionConfig(EtherNetIPConfig):
    """Optimized for production environment"""
    DEFAULT_TIMEOUT = 30
    UDP_IO_RPI_DEFAULT = 100
    RECV_BUFFER_SIZE = 4096

# Apply configuration
import ethernetip
ethernetip.config = ProductionConfig()

Available Configuration Options

Parameter Default Description
DEFAULT_TIMEOUT 10 Default socket timeout (seconds)
IO_SOCKET_SELECT_TIMEOUT 2 I/O socket select timeout (seconds)
RECV_BUFFER_SIZE 1024 Standard receive buffer size (bytes)
RECV_BUFFER_SIZE_LARGE 4096 Large receive buffer size (bytes)
UDP_IO_RPI_DEFAULT 100 Default I/O update rate (milliseconds)
UDP_IO_MIN_RPI 8 Minimum I/O update rate (milliseconds)
FWD_OPEN_RPI_MULTIPLIER 1000 RPI conversion multiplier (ms to µs)
TICK_TIME 250 Timeout ticks for unconnected send

📖 Examples

Check out the examples/ directory for more detailed examples:

🛠️ Development

This project uses uv for environment management and nox for test/lint/docs.

uv sync        # install the project into the venv
uv run pytest  # run tests
make nox       # run full nox suite

Code Style

This project follows PEP 8 with some modifications (see pyproject.toml):

Building Documentation

make docs

🤝 Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Guidelines

  • Follow the existing code style
  • Add tests for new features
  • Update documentation as needed
  • Ensure all tests pass before submitting

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔗 Resources

Version History

See CHANGELOG.md for complete version history.


Made with ❤️ for Industrial Automation

If you find this library useful, please consider giving it a ⭐ on Codeberg!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ethernetip2-1.1.2.tar.gz (48.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ethernetip2-1.1.2-py3-none-any.whl (32.4 kB view details)

Uploaded Python 3

File details

Details for the file ethernetip2-1.1.2.tar.gz.

File metadata

  • Download URL: ethernetip2-1.1.2.tar.gz
  • Upload date:
  • Size: 48.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for ethernetip2-1.1.2.tar.gz
Algorithm Hash digest
SHA256 2e166045ebc77787275cac57ccb4a279f64ec952f99b39aea3f16e638dc80bd4
MD5 56ee61391b9e026b162286f9ca62a9ca
BLAKE2b-256 28610ff73fb4674672cb19bc0fe8cfe410a1026e148f87f600ef2b26a8e7ad57

See more details on using hashes here.

File details

Details for the file ethernetip2-1.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for ethernetip2-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 23965f688c98951c779f6c888ade37c38695752f3b6374b00c214771092abf12
MD5 2fc4713d3a3309b519f376f22752aaad
BLAKE2b-256 a92a2f99e0fc3188a1cf3f08484babf8e92bd880c3c62b791cdbb25c2db707b8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page