Repository navigation
Home
Stevie Chryn edited this page Jul 31, 2026
·
4 revisions
Welcome to the ethernetip-emulator wiki!
Ethernetip-emulator is a Python-based EtherNet/IP device emulator built on top of cpppo. It lets you define PLC tags in plain Python, react to tag writes with event-driven callbacks, and run a fully functional EtherNet/IP server — all without physical hardware.
pip install ethernetip-emulatorA 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},
)| 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 |
| 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 |
┌─────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────┘
-
Tags are declared once at import time via
@tag_registry.register. -
Custom Datatypes can be declared once at import time via
@actions.datatype. Although this is completely optional if you like the built-in datatypes -
device_controllerbuilds the tag list and starts the EtherNet/IP server. -
AttributeDeviceintercepts every PLC write and fires the matchingon_changelisteners. -
Listeners use
actions.<type>helpers to read state, write back to tags, or trigger application logic.
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.
Copyright 2026 Merck KGaA, Darmstadt, Germany and/or its affiliates. All rights reserved.