Examples

Usage examples of this micropython-modbus library


RTU

Note

Check the port specific MicroPython UART documentation for further details.

A Raspberry Pi Pico e.g. requires the UART pins as a tuple of Pin, like rtu_pins = (Pin(4), Pin(5)) and the corresponding uart_id for those pins, whereas ESP32 boards can use almost any pin for UART communication as shown in the following examples and shall be given as rtu_pins = (25, 26). If necessary, the uart_id parameter may has to be adapted to the pins used.

Client/Slave

With this example the device is acting as client (slave) and providing data via RTU (serial/UART) to a requesting host device.

from umodbus.serial import ModbusRTU

# RTU Client/Slave setup

# the following definition is for an ESP32
rtu_pins = (25, 26)         # (TX, RX)
uart_id = 1

# the following definition is for a RP2
# rtu_pins = (Pin(0), Pin(1))     # (TX, RX)
# uart_id = 0
#
# rtu_pins = (Pin(4), Pin(5))     # (TX, RX)
# uart_id = 1

# the following definition is for a pyboard
# rtu_pins = (Pin(PB6), Pin(PB7))   # (TX, RX)
# uart_id = 1

slave_addr = 10             # address on bus as client

client = ModbusRTU(
    addr=slave_addr,        # address on bus
    pins=rtu_pins,          # given as tuple (TX, RX)
    # baudrate=9600,        # optional, default 9600
    # data_bits=8,          # optional, default 8
    # stop_bits=1,          # optional, default 1
    # parity=None,          # optional, default None
    # ctrl_pin=12,          # optional, control DE/RE
    uart_id=uart_id         # optional, default 1, see port specific documentation
)

register_definitions = {
    "COILS": {
        "EXAMPLE_COIL": {
            "register": 123,
            "len": 1,
            "val": 1
        }
    },
    "HREGS": {
        "EXAMPLE_HREG": {
            "register": 93,
            "len": 1,
            "val": 19
        }
    },
    "ISTS": {
        "EXAMPLE_ISTS": {
            "register": 67,
            "len": 1,
            "val": 0
        }
    },
    "IREGS": {
        "EXAMPLE_IREG": {
            "register": 10,
            "len": 1,
            "val": 60001
        }
    }
}

# use the defined values of each register type provided by register_definitions
client.setup_registers(registers=register_definitions)

while True:
    try:
        result = client.process()
    except KeyboardInterrupt:
        print('KeyboardInterrupt, stopping RTU client...')
        break
    except Exception as e:
        print('Exception during execution: {}'.format(e))

Host/Master

With this example the device is acting as host (master) and requesting on or setting data at a RTU (serial/UART) client/slave.

from umodbus.serial import Serial as ModbusRTUMaster

# RTU Host/Master setup

# the following definition is for an ESP32
rtu_pins = (25, 26)         # (TX, RX)
uart_id = 1

# the following definition is for a RP2
# rtu_pins = (Pin(0), Pin(1))     # (TX, RX)
# uart_id = 0
#
# rtu_pins = (Pin(4), Pin(5))     # (TX, RX)
# uart_id = 1

# the following definition is for a pyboard
# rtu_pins = (Pin(PB6), Pin(PB7))   # (TX, RX)
# uart_id = 1

host = ModbusRTUMaster(
    pins=rtu_pins,          # given as tuple (TX, RX)
    # baudrate=9600,        # optional, default 9600
    # data_bits=8,          # optional, default 8
    # stop_bits=1,          # optional, default 1
    # parity=None,          # optional, default None
    # ctrl_pin=12,          # optional, control DE/RE
    uart_id=uart_id         # optional, default 1, see port specific documentation
)

coil_status = host.read_coils(slave_addr=10, starting_addr=123, coil_qty=1)
print('Status of coil 123: {}'.format(coil_status))

TCP

Client/Slave

With this example the device is acting as client (slave) and providing data via TCP (socket) to a requesting host device.

import network
from umodbus.tcp import ModbusTCP

# network connections shall be made here, check the MicroPython port specific
# documentation for connecting to or creating a network

# TCP Client/Slave setup
# set IP address of this MicroPython device explicitly
# local_ip = '192.168.4.1'  # IP address
# or get it from the system after a connection to the network has been made
# it is not the task of this lib to provide a detailed explanation for this
station = network.WLAN(network.STA_IF)
local_ip = station.ifconfig()[0]
tcp_port = 502      # port to listen for requests/providing data

client = ModbusTCP()

# check whether client has been bound to an IP and a port
if not client.get_bound_status():
    client.bind(local_ip=local_ip, local_port=tcp_port)

register_definitions = {
    "COILS": {
        "EXAMPLE_COIL": {
            "register": 123,
            "len": 1,
            "val": 1
        }
    },
    "HREGS": {
        "EXAMPLE_HREG": {
            "register": 93,
            "len": 1,
            "val": 19
        }
    },
    "ISTS": {
        "EXAMPLE_ISTS": {
            "register": 67,
            "len": 1,
            "val": 0
        }
    },
    "IREGS": {
        "EXAMPLE_IREG": {
            "register": 10,
            "len": 1,
            "val": 60001
        }
    }
}

# use the defined values of each register type provided by register_definitions
client.setup_registers(registers=register_definitions)

while True:
    try:
        result = client.process()
    except KeyboardInterrupt:
        print('KeyboardInterrupt, stopping TCP client...')
        break
    except Exception as e:
        print('Exception during execution: {}'.format(e))

Host/Master

With this example the device is acting as host (master) and requesting on or setting data at a TCP (socket) client/slave.

from umodbus.tcp import TCP as ModbusTCPMaster

# valid network connections shall be made here

# RTU Host/Master setup
slave_tcp_port = 502            # port to send request on
slave_ip = '192.168.178.69'     # IP address of client, to be adjusted

host = ModbusTCPMaster(
    slave_ip=slave_ip,
    slave_port=slave_tcp_port,
    # timeout=5.0               # optional, timeout in seconds, default 5.0
)

coil_status = host.read_coils(slave_addr=10, starting_addr=123, coil_qty=1)
print('Status of coil 123: {}'.format(coil_status))

Callbacks

Callbacks can be registered to be executed after setting a register with on_set_cb or to be executed before getting a register with on_get_cb.

Note

Getter callbacks can be registered for all registers with the on_get_cb parameter whereas the on_set_cb parameter is only available for coils and holding registers as only those can be set by a external host.

Warning

Keep the get callback actions as short as possible to avoid potential request timeouts due to a to long processing time.

def my_coil_set_cb(reg_type, address, val):
    print('Custom callback, called on setting {} at {} to: {}'.
          format(reg_type, address, val))


def my_coil_get_cb(reg_type, address, val):
    print('Custom callback, called on getting {} at {}, currently: {}'.
          format(reg_type, address, val))


# define some registers, for simplicity only a single coil is used
register_definitions = {
    "COILS": {
        "EXAMPLE_COIL": {
            "register": 123,
            "len": 1,
            "val": 1,
            "on_get_cb": my_coil_get_cb,
            "on_set_cb": my_coil_set_cb
        }
    }
}

# use the defined values of each register type provided by register_definitions
client.setup_registers(registers=register_definitions)

# callbacks can also be defined after a register setup has been performed
client.add_coil(
    address=123,
    value=bool(1),
    on_set_cb=my_coil_set_cb,
    on_get_cb=my_coil_get_cb
)