A production-ready openPOWERLINK Managing-Node (MN) daemon in C plus a
Python package + PySide6 GUI to read and write the I/O of a B&R X20 BC8083
POWERLINK bus coupler. Built for a Debian/antiX (PREEMPT_RT) target running the
MN on interface eth0, talking to the BC8083 as controlled node node-ID 1.
Intended as the I/O layer for a larger application (e.g. an oven controller):
the C daemon owns the real-time POWERLINK cycle; your Python app imports
powerlink_io.PowerlinkIO and just reads/writes.
| Slot | Module | Type | Channels | CiA401 index |
|---|---|---|---|---|
| 1 | X20 DI 9371 | Digital Input | 12 | 0x6000 |
| 2 | X20 DO 9322 | Digital Output | 12 | 0x6200 |
| 3 | X20 AI 4622 | Analog Input | 4 (INT16) | 0x6401 |
| 4 | X20 AO 4622 | Analog Output | 4 (INT16) | 0x6411 |
+------------------------------- Debian target (MN) --------------------------------+
| openPOWERLINK stack liboplkmn.a (userspace, direct-link, raw-socket Edrv) |
| | |
| powerlinkd (C daemon) -- loads config/mnobd.cdc, mirrors the process image |
| | processSync(): PI_OUT -> SHM (DI/AI) ; SHM -> PI_IN (DO/AO) |
| v |
| POSIX shared memory /dev/shm/powerlink_io (stable, versioned struct) |
| ^ |
| Python powerlink_io -- ctypes mirror -- pl-io CLI + PySide6 GUI + your app|
+----------------- eth0 (USB RTL8152) -- POWERLINK -- X20 BC8083 (node 1) ------------+
The daemon translates the openCONFIGURATOR process-image fields into a stable
shared-memory layout (daemon/src/powerlink_shm.h), so Python code never depends
on generated naming.
daemon/ C MN daemon (powerlinkd) + shared-memory contract + CMake
config-tools/ Bootstrap CDC generator (gen_config.py) + openCONFIGURATOR guide
python/ uv project: powerlink_io package, pl-io CLI, PySide6 GUI example
systemd/ Autostart unit + interface notes
scripts/ Build / deploy / install helpers
sudo apt update
sudo apt install build-essential cmake git python3
# Python tooling:
curl -LsSf https://astral.sh/uv/install.sh | sh # installs uvYou also need the openPOWERLINK V2 sources on the target (the daemon links
against them). Clone them where convenient, e.g. ~/openPOWERLINK_V2:
git clone https://github.com/OpenAutomationTechnologies/openPOWERLINK_V2.gitThe default Linux Edrv uses raw sockets (
PF_PACKET) and works with the USB Realtek NIC — no libpcap needed.
Let OPLK=~/openPOWERLINK_V2 (adjust to your path).
# 1) Build the openPOWERLINK MN library (liboplkmn)
scripts/build_stack.sh "$OPLK"
# 2) Build the daemon (generates a bootstrap CDC if none exists)
scripts/build_daemon.sh "$OPLK"
# 3) Python environment
cd python && uv sync # add --extra gui for the PySide6 exampleTwo supported paths (see config-tools/):
-
Bootstrap (fast start):
gen_config.pybuildsmnobd.cdc+xap.hfromconfig-tools/modules.yamlusing the standard CiA401 mapping. Already run for you bybuild_daemon.sh. Regenerate after editing the YAML:python3 config-tools/gen_config.py config-tools/modules.yaml -o daemon/config
⚠️ Best-effort — verify against the real BC8083 (see Troubleshooting). -
openCONFIGURATOR (authoritative): import the B&R XDD and generate the files. Full guide:
config-tools/openconfigurator/README.md. Copy the resultingmnobd.cdc+xap.hintodaemon/config/.
sudo daemon/build/powerlinkd -d eth0 -c daemon/config/mnobd.cdc -y 5000Watch for the CN reaching OPERATIONAL. In another terminal:
cd python
uv run pl-io watch # live I/O view
uv run pl-io do 0 1 # switch DO 0 on
uv run pl-io ao 1 --volts 4.5sudo OPLK_BASE_DIR="$OPLK" scripts/install_service.sh
sudo systemctl start powerlinkd
journalctl -u powerlinkd -fThe service runs on eth0 after network-online.target, with mlockall and
real-time limits. Override the interface/cycle in /etc/default/powerlinkd.
Interface details: systemd/eth0-powerlink.md.
from powerlink_io import PowerlinkIO
with PowerlinkIO() as io:
io.write_do(0, True) # digital output 0 on
io.write_ao_volts(1, 4.5) # analog output 1 -> 4.5 V
di = io.read_di() # list[bool] (12)
ai = io.read_ai_volts() # list[float] (4)
if not io.status().cn_operational:
print("BC8083 not operational!")GUI example:
cd python
uv run --extra gui python examples/gui_pyside6.pyDevelop/commit on Windows; build on the Debian target. To sync + build in one go (from Git Bash):
scripts/deploy.sh user@<target-ip> /home/user/openPOWERLINK_V2| Symptom | Likely cause / fix |
|---|---|
shm ... not found in Python |
Daemon not running, or ran as a different user. Start powerlinkd first. |
CN never reaches OPERATIONAL |
Wrong node-ID (check BC8083 rotary switch = 1), wiring, or CDC mapping. Watch daemon log + Wireshark on eth0. |
| Digital inputs stay 0 (LEDs on) | TPDO mapping / payload limit wrong. Verify with openCONFIGURATOR-generated CDC. |
| Analog values implausible | Check scaling (PowerlinkIO(ai_full_scale_v=..)) and the AI/AO subindex mapping. |
| Frequent cycle/PRes errors | USB NIC jitter — raise the cycle time; consider an Intel PCIe NIC. |
library not found at build |
Build the stack first (build_stack.sh); ensure OPLK_BASE_DIR is correct. |
- USB NIC:
eth0(r8152) is not ideal for hard real-time POWERLINK. Start with a 5–10 ms cycle. A PCIe Intel NIC + kernel Edrv is the upgrade path. - Privileges: the daemon needs raw sockets + interface config + RT
scheduling, so it runs as root (or grant
CAP_NET_RAW,CAP_NET_ADMIN). - Consistency: inputs/status use a seqlock; outputs are written by the client
and read by the daemon each cycle. Layout is versioned (
PL_SHM_VERSION).
MIT (this project). openPOWERLINK is BSD-licensed by B&R Industrial Automation.