No description
  • Python 99.1%
  • Makefile 0.9%
Find a file
2026-02-07 18:03:49 +01:00
docs prepare for documentation with sphinx 2022-12-11 16:12:02 +01:00
examples flake8 fixes 2021-03-14 17:27:06 +01:00
src/ethernetip add verbose packets on failed check 2026-02-07 18:00:01 +01:00
tests test: extend unittests 2026-02-07 18:00:21 +01:00
.gitignore Initial commit 2021-03-14 12:54:41 +01:00
CHANGELOG.md doc: add readme and changelog 2026-02-07 14:30:58 +01:00
LICENSE initial 2021-03-14 13:01:43 +01:00
Makefile Makefile: add pypi upload target 2023-07-21 11:51:23 +02:00
noxfile.py release 1.1.2 2025-05-04 15:17:31 +02:00
pyproject.toml fix build package 2021-03-14 16:55:32 +01:00
README.md doc: add readme and changelog 2026-02-07 14:30:58 +01:00
setup.cfg fix build package 2021-03-14 16:55:32 +01:00
setup.py release 1.1.2 2025-05-04 15:17:31 +02:00

python-ethernetip

Python Version License Code Style

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
  • 🔍 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 ethernetip

From Source

git clone https://codeberg.org/paperwork/python-ethernetip.git
cd python-ethernetip
pip install -e .

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()

📚 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 library uses nox to trigger tests, doc generation and code style check.

make nox

Code Style

This project follows PEP 8 with some modifications (see setup.cfg):

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!