Skip to main content

EtherNet/IP Emulator (Python)

ethernetip-emulator is a high level binary communications framework built around cpppo (pronounced ‘c’3*’p’‘o’ in Python) that exposes a set of tags and allows for the programatic execution .

Architecture

.
├───example
│   ├───basic_datatypes
│   │   └─── *.py
│   ├───mfg_line
│   │   └─── *.py
│   ├───orange_pi
│   │   └─── *.py
│   └───raspberry_pi
│       └─── *.py
├───src
│   └───ethernetip_emulator
│       ├───client
│       ├───server
│       │   ├───datatypes
│       │   │   ├───templates
│       │   │   │   └─── *.py
│       │   │   └─── *.py
│       │   └─── *.py
│       └─── *.py
└───tests
    └───server
        ├───datatypes
        │   ├───mock
        │   │   └─── test_*.py
        │   ├───templates
        │   │   └─── test_*.py
        │   └─── test_*.py
        └─── test_*.py
        

Requirements

  • Python 3.10+
  • cpppo (for Ethernet/IP attribute/device behavior)
  • black | Development Dependency (for code formatting)
  • coverage | Developer Dependency (for coverage reporting)
  • RPi.GPIO or OPi.GPIO | Optional Dependency (for gpio i/o)

Getting Started

pip install ethernetip-emulator

A minimal project needs three files:

File Purpose
tags.py Declare tags with @tag_registry.register
actions.py React to tag changes with @actions.<type>.on_change
__main__.py Start the server with device_controller
# __main__.py
from ethernetip_emulator.server.device import AttributeDevice, apidict, device_controller
from ethernetip_emulator.server.tag_specs import tag_registry
import tags    # registers all tags (can also just be imported in __init__.py)
import actions # registers all listeners (can also just be imported in __init__.py)

if __name__ == "__main__":
    server_control = apidict(timeout=1.0)
    AttributeDevice.set_server_control(server_control)

    with AttributeDevice._actions.bind(AttributeDevice):
        device_controller(
            argv=tag_registry.build_argv(base_args=["--print"]),
            attribute_class=AttributeDevice,
            server={"control": server_control},
        )

Guides

Core

Guide Description
Tag Registry Define tags with @tag_registry.register, use namespaced prefixes, and organise tags across files
Actions Read and write tags at runtime, register on_change listeners, and understand key and defer
Device Start and stop the server, understand how tag writes are intercepted, and apply startup defaults

Datatypes

Guide Description
Datatypes Overview All built-in types at a glance, and a step-by-step guide to creating custom datatypes
Basic Datatypes Scalar types: BOOL, integers, floats, and strings — shared get_val / set_val / on_change API
Bool Array BOOLARRAY — bit-level access, list operations, bulk mutations
Numeric Arrays SINTARRAY · USINTARRAY · INTARRAY · UINTARRAY · DINTARRAY · UDINTARRAY · LINTARRAY · ULINTARRAY
Real Arrays REALARRAY · LREALARRAY — same API as numeric arrays with floating-point zero tolerance (1e-9)
String Array SSTRINGARRAY — list operations using "" as the empty slot sentinel
GPIO GPIO — maps EtherNet/IP tags to physical hardware pins on a Raspberry Pi or Orange Pi
Counter COUNTER — composite datatype that models a up/down counter with control tags
Timer TIMER — composite datatype that models a timer with control tags

Typical Workflow

┌─────────────────────────────────────────────────────┐
│                    Your Project                     │
│                                                     │
│  tags.py ──────► tag_registry                       │
│                      │                              │
│                       ▼                             │
│  datatypes.py ─► actions.datatype                   │
│                      │                              │
│                       ▼                             │
│  __main__.py ──► device_controller                  │
│                      │                              │
│                       ▼                             │
│              AttributeDevice                        │
│           (intercepts PLC writes)                   │
│                      │                              │
│                       ▼                             │
│  actions.py ──► on_change listeners                 │
│                      │                              │
│                       ▼                             │
│              actions.<type>.set_val / get_val       │
└─────────────────────────────────────────────────────┘
  1. Tags are declared once at import time via @tag_registry.register.
  2. Custom Datatypes can be declared once at import time via @actions.datatype. Although this is completely optional if you like the built-in datatypes
  3. device_controller builds the tag list and starts the EtherNet/IP server.
  4. AttributeDevice intercepts every PLC write and fires the matching on_change listeners.
  5. Listeners use actions.<type> helpers to read state, write back to tags, or trigger application logic.

Project Layout Convention

your_project/
├── __main__.py          # server entrypoint
├── tags.py              # tag definitions
├── actions.py           # on_change handlers
└── datatypes/           # optional: custom datatype classes
    ├── __init__.py
    └── mytype.py

For larger projects, split tags and actions into sub-modules and import them from a top-level __init__.py so all registrations are triggered before the server starts.

Supporting the Development of EtherNet/IP Emulator

EtherNet/IP Emulator's development depends on your contributions. Right now it is just me working on this, so any contributions are welcome!

License

Copyright 2026 Merck KGaA, Darmstadt, Germany and/or its affiliates. All rights reserved.

Metadata

Release files for ethernetip-emulator 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ethernetip-emulator 0.1.1
File Size Uploaded
ethernetip_emulator-0.1.1.tar.gz 22.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ethernetip-emulator 0.1.1
File Interpreter ABI Platform
ethernetip_emulator-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 68.6 kB

Release files / ethernetip_emulator-0.1.1.tar.gz

Download URL ethernetip_emulator-0.1.1.tar.gz
Size 22.8 kB
Tags Source
SHA-256 checksum
How to use checksums
666d93bfd590bc277029a3677c5940ed7dd534656f105e896ac3d52172a0ace6
BLAKE2b-256 checksum
How to use checksums
1a50cbc2e72f4b10c176acdf617dc149fffeb8e7ff82eff6d79fe40df8e9e121
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / ethernetip_emulator-0.1.1-py3-none-any.whl

Download URL ethernetip_emulator-0.1.1-py3-none-any.whl
Size 45.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a42e05ed83e5680d23e1db2910e345249c1eba5379ff01160fd4cfa6184e0a3c
BLAKE2b-256 checksum
How to use checksums
27aaf556883451420d7d83e121469670b5f2b641f9497cd8750d7e9c4efc4f07
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

0.2

2 release files

This release

0.1.1 This release

2 release files

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page