Publishing to an HMI over Modbus

Publishing to an HMI over Modbus#

The program acts as a Modbus server and the HMI reads from it. It listens on port 1502. To use the standard Modbus port 502, run the program as root; any port above 1024 works without root.

#!/usr/bin/env python3
"""Expose backplane readings over Modbus TCP."""

import signal
import time

import synergy_logic
from pyModbusTCP.server import DataBank, ModbusServer

running = True

def shutdown(signum, frame):
    global running
    running = False

signal.signal(signal.SIGTERM, shutdown)
signal.signal(signal.SIGINT, shutdown)

base = synergy_logic.Base()
level = base[1][1]
valve = base[2][1]

bank = DataBank()
server = ModbusServer(host="0.0.0.0", port=1502, no_block=True, data_bank=bank)
server.start()

try:
    while running:
        # Publish the reading into input register 1
        bank.set_input_registers(1, [level.value])

        # Take the setpoint from holding register 1
        setpoint = bank.get_holding_registers(1, 1)[0]
        valve.value = min(setpoint, 4095)

        time.sleep(0.1)
finally:
    valve.value = 0
    server.stop()

Install the dependency with:

pip3 install --user --break-system-packages pyModbusTCP

This installs the package for the slm-rp4 user only. A program run as root needs it installed with sudo.

Register numbering

pyModbusTCP addresses registers by offset within their block, without the leading digit of the traditional 4xxxx / 3xxxx notation. Holding register 400001 is offset 0, 400002 is offset 1, 423000 is offset 22999.

Most HMI packages use the long form. Getting this wrong is the usual cause of “the tag exists but never updates”.

To build a dashboard on the device that connects to this server, see FUXA: A Local HMI.