ModbusRTUSlave

ModbusRTUSlave implements the Modbus RTU slave. ModbusRTUSlave support function codes 1 (Read Coils), 2 (Read Discrete Inputs), 3 (Read Holding Registers), 4 (Read Input Registers), 5 (Write Single Coil), 6 (Write Single Holding Register), 15 (Write Multiple Coils), and 16 (Write Multiple Holding Registers).

UiFlow2 Example

CoreS3 RTU Slave

Open the cores3_rtu_slave_example.m5f2 project in UiFlow2.

This example demonstrates how to use the ModbusRTUSlave class.

UiFlow2 Code Block:

example.png

Example output:

None

MicroPython Example

CoreS3 RTU Slave

This example demonstrates how to use the ModbusRTUSlave class.

MicroPython Code Block:

  1# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
  2#
  3# SPDX-License-Identifier: MIT
  4
  5import os, sys, io
  6import M5
  7from M5 import *
  8import modbus
  9from unit import ISO485Unit
 10
 11
 12label0 = None
 13label1 = None
 14label2 = None
 15modbusrtuslave_0 = None
 16iso485_0 = None
 17
 18
 19starting_register1 = None
 20slave_list1 = None
 21register1 = None
 22slave_value1 = None
 23starting_register4 = None
 24slave_list4 = None
 25register2 = None
 26slave_value2 = None
 27starting_register2 = None
 28slave_list2 = None
 29starting_register6 = None
 30slave_list6 = None
 31starting_register5 = None
 32slave_list5 = None
 33starting_register3 = None
 34slave_list3 = None
 35
 36
 37def modbus_read_coils_cb(args):
 38    global \
 39        label0, \
 40        label1, \
 41        label2, \
 42        modbusrtuslave_0, \
 43        iso485_0, \
 44        starting_register1, \
 45        slave_list1, \
 46        register1, \
 47        slave_value1, \
 48        starting_register4, \
 49        slave_list4, \
 50        register2, \
 51        slave_value2, \
 52        starting_register2, \
 53        slave_list2, \
 54        starting_register6, \
 55        slave_list6, \
 56        starting_register5, \
 57        slave_list5, \
 58        starting_register3, \
 59        slave_list3
 60    _, starting_register1, slave_list1 = args
 61    label0.setText(str("read coils"))
 62    label1.setText(str(starting_register1))
 63    label2.setText(str(slave_list1))
 64
 65
 66def modbus_write_single_coil_cb(args):
 67    global \
 68        label0, \
 69        label1, \
 70        label2, \
 71        modbusrtuslave_0, \
 72        iso485_0, \
 73        starting_register1, \
 74        slave_list1, \
 75        register1, \
 76        slave_value1, \
 77        starting_register4, \
 78        slave_list4, \
 79        register2, \
 80        slave_value2, \
 81        starting_register2, \
 82        slave_list2, \
 83        starting_register6, \
 84        slave_list6, \
 85        starting_register5, \
 86        slave_list5, \
 87        starting_register3, \
 88        slave_list3
 89    _, register1, slave_value1 = args
 90    label0.setText(str("write single coil"))
 91    label1.setText(str(register1))
 92    label2.setText(str(slave_value1))
 93
 94
 95def modbus_read_input_registers_cb(args):
 96    global \
 97        label0, \
 98        label1, \
 99        label2, \
100        modbusrtuslave_0, \
101        iso485_0, \
102        starting_register1, \
103        slave_list1, \
104        register1, \
105        slave_value1, \
106        starting_register4, \
107        slave_list4, \
108        register2, \
109        slave_value2, \
110        starting_register2, \
111        slave_list2, \
112        starting_register6, \
113        slave_list6, \
114        starting_register5, \
115        slave_list5, \
116        starting_register3, \
117        slave_list3
118    _, starting_register4, slave_list4 = args
119    label0.setText(str("read input register"))
120    label1.setText(str(starting_register4))
121    label2.setText(str(slave_list4))
122
123
124def modbus_write_single_registers_cb(args):
125    global \
126        label0, \
127        label1, \
128        label2, \
129        modbusrtuslave_0, \
130        iso485_0, \
131        starting_register1, \
132        slave_list1, \
133        register1, \
134        slave_value1, \
135        starting_register4, \
136        slave_list4, \
137        register2, \
138        slave_value2, \
139        starting_register2, \
140        slave_list2, \
141        starting_register6, \
142        slave_list6, \
143        starting_register5, \
144        slave_list5, \
145        starting_register3, \
146        slave_list3
147    _, register2, slave_value2 = args
148    label0.setText(str("write single registers"))
149    label1.setText(str(register2))
150    label2.setText(str(slave_value2))
151
152
153def modbus_read_discrete_inputs_cb(args):
154    global \
155        label0, \
156        label1, \
157        label2, \
158        modbusrtuslave_0, \
159        iso485_0, \
160        starting_register1, \
161        slave_list1, \
162        register1, \
163        slave_value1, \
164        starting_register4, \
165        slave_list4, \
166        register2, \
167        slave_value2, \
168        starting_register2, \
169        slave_list2, \
170        starting_register6, \
171        slave_list6, \
172        starting_register5, \
173        slave_list5, \
174        starting_register3, \
175        slave_list3
176    _, starting_register2, slave_list2 = args
177    label0.setText(str("read  discrete input"))
178    label1.setText(str(starting_register2))
179    label2.setText(str(slave_list2))
180
181
182def modbus_write_multiple_registers_cb(args):
183    global \
184        label0, \
185        label1, \
186        label2, \
187        modbusrtuslave_0, \
188        iso485_0, \
189        starting_register1, \
190        slave_list1, \
191        register1, \
192        slave_value1, \
193        starting_register4, \
194        slave_list4, \
195        register2, \
196        slave_value2, \
197        starting_register2, \
198        slave_list2, \
199        starting_register6, \
200        slave_list6, \
201        starting_register5, \
202        slave_list5, \
203        starting_register3, \
204        slave_list3
205    _, starting_register6, slave_list6 = args
206    label0.setText(str("write multiple registers"))
207    label1.setText(str(slave_list6))
208    label2.setText(str(slave_list6))
209
210
211def modbus_write_multiple_coils_cb(args):
212    global \
213        label0, \
214        label1, \
215        label2, \
216        modbusrtuslave_0, \
217        iso485_0, \
218        starting_register1, \
219        slave_list1, \
220        register1, \
221        slave_value1, \
222        starting_register4, \
223        slave_list4, \
224        register2, \
225        slave_value2, \
226        starting_register2, \
227        slave_list2, \
228        starting_register6, \
229        slave_list6, \
230        starting_register5, \
231        slave_list5, \
232        starting_register3, \
233        slave_list3
234    _, starting_register5, slave_list5 = args
235    label0.setText(str("write multiple coils"))
236    label1.setText(str(slave_list5))
237    label2.setText(str(slave_list5))
238
239
240def modbus_read_holding_registers_cb(args):
241    global \
242        label0, \
243        label1, \
244        label2, \
245        modbusrtuslave_0, \
246        iso485_0, \
247        starting_register1, \
248        slave_list1, \
249        register1, \
250        slave_value1, \
251        starting_register4, \
252        slave_list4, \
253        register2, \
254        slave_value2, \
255        starting_register2, \
256        slave_list2, \
257        starting_register6, \
258        slave_list6, \
259        starting_register5, \
260        slave_list5, \
261        starting_register3, \
262        slave_list3
263    _, starting_register3, slave_list3 = args
264    label0.setText(str("read holding register"))
265    label1.setText(str(starting_register3))
266    label2.setText(str(slave_list3))
267
268
269def setup():
270    global \
271        label0, \
272        label1, \
273        label2, \
274        modbusrtuslave_0, \
275        iso485_0, \
276        starting_register1, \
277        slave_list1, \
278        register1, \
279        slave_value1, \
280        starting_register4, \
281        slave_list4, \
282        register2, \
283        slave_value2, \
284        starting_register2, \
285        slave_list2, \
286        starting_register6, \
287        slave_list6, \
288        starting_register5, \
289        slave_list5, \
290        starting_register3, \
291        slave_list3
292
293    M5.begin()
294    Widgets.setRotation(1)
295    Widgets.fillScreen(0x222222)
296    label0 = Widgets.Label("label0", 79, 60, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18)
297    label1 = Widgets.Label("label1", 73, 100, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18)
298    label2 = Widgets.Label("label2", 67, 135, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18)
299
300    iso485_0 = ISO485Unit(2, port=(1, 2))
301    iso485_0.init(
302        tx_pin=None,
303        rx_pin=None,
304        baudrate=115200,
305        data_bits=None,
306        stop_bits=None,
307        parity=None,
308        ctrl_pin=None,
309    )
310    modbusrtuslave_0 = modbus.ModbusRTUSlave(iso485_0, device_address=1, verbose=True)
311    modbusrtuslave_0.set_callback(modbusrtuslave_0.READ_COILS_EVENT, modbus_read_coils_cb)
312    modbusrtuslave_0.set_callback(
313        modbusrtuslave_0.WRITE_SINGLE_COIL_EVENT, modbus_write_single_coil_cb
314    )
315    modbusrtuslave_0.set_callback(
316        modbusrtuslave_0.READ_INPUT_REGISTERS_EVENT, modbus_read_input_registers_cb
317    )
318    modbusrtuslave_0.set_callback(
319        modbusrtuslave_0.WRITE_SINGLE_REGISTER_EVENT, modbus_write_single_registers_cb
320    )
321    modbusrtuslave_0.set_callback(
322        modbusrtuslave_0.READ_DISCRETE_INPUTS_EVENT, modbus_read_discrete_inputs_cb
323    )
324    modbusrtuslave_0.set_callback(
325        modbusrtuslave_0.WRITE_MULTIPLE_REGISTERS_EVENT, modbus_write_multiple_registers_cb
326    )
327    modbusrtuslave_0.set_callback(
328        modbusrtuslave_0.WRITE_MULTIPLE_COILS_EVENT, modbus_write_multiple_coils_cb
329    )
330    modbusrtuslave_0.set_callback(
331        modbusrtuslave_0.READ_HOLDING_REGISTERS_EVENT, modbus_read_holding_registers_cb
332    )
333    modbusrtuslave_0.add_coil(1000, True)
334    modbusrtuslave_0.add_coil(1001, False)
335    modbusrtuslave_0.add_coil(1002, True)
336    modbusrtuslave_0.add_coil(1003, False)
337    modbusrtuslave_0.add_coil(1004, True)
338    modbusrtuslave_0.add_discrete_input(1000, True)
339    modbusrtuslave_0.add_discrete_input(1001, False)
340    modbusrtuslave_0.add_discrete_input(1002, True)
341    modbusrtuslave_0.add_discrete_input(1003, False)
342    modbusrtuslave_0.add_discrete_input(1004, True)
343    modbusrtuslave_0.add_holding_register(1000, 0x0102)
344    modbusrtuslave_0.add_holding_register(1001, 0x0304)
345    modbusrtuslave_0.add_holding_register(1002, 0x0304)
346    modbusrtuslave_0.add_holding_register(1003, 0x0304)
347    modbusrtuslave_0.add_holding_register(1004, 0x0304)
348    modbusrtuslave_0.add_input_register(1000, 0x0102)
349    modbusrtuslave_0.add_input_register(1001, 0x0304)
350    modbusrtuslave_0.add_input_register(1002, 0x0304)
351    modbusrtuslave_0.add_input_register(1003, 0x0304)
352    modbusrtuslave_0.add_input_register(1004, 0x0304)
353
354
355def loop():
356    global \
357        label0, \
358        label1, \
359        label2, \
360        modbusrtuslave_0, \
361        iso485_0, \
362        starting_register1, \
363        slave_list1, \
364        register1, \
365        slave_value1, \
366        starting_register4, \
367        slave_list4, \
368        register2, \
369        slave_value2, \
370        starting_register2, \
371        slave_list2, \
372        starting_register6, \
373        slave_list6, \
374        starting_register5, \
375        slave_list5, \
376        starting_register3, \
377        slave_list3
378    M5.update()
379    modbusrtuslave_0.tick()
380
381
382if __name__ == "__main__":
383    try:
384        setup()
385        while True:
386            loop()
387    except (Exception, KeyboardInterrupt) as e:
388        try:
389            from utility import print_error_msg
390
391            print_error_msg(e)
392        except ImportError:
393            print("please update to latest firmware")

Example output:

None

API

ModbusRTUSlave

class modbus.ModbusRTUSlave(uart, device_address: int = 1, context=None, ignore_unit_id: bool = False, verbose: bool = False)

Create a ModbusRTUSlave object.

Parameters:
  • uart (UART) – UART object or RS485 object.

  • device_address (int) – Device address. The address is 0 to 247.

  • context (dict) – Modbus server context.

  • ignore_unit_id (bool) – Ignore unit ID(or Device address).

  • verbose (bool) – Verbose mode.

context is a dictionary that contains the Modbus server context. the format is as follows:

{
    "discrete_inputs": [
        {
            "register": 0,  # Start address of the discrete inputs.
            "value": [False, True, False, True]  # Values of the discrete inputs. quantity is the length of the list.
        }
    ],
    "coils": [
        {
            "register": 0,  # Start address of the coils.
            "value": [False, True, False, True]  # Values of the coils. quantity is the length of the list. quantity is the length of the list.
        }
    ],
    "input_registers": [
        {
            "register": 0,  # Start address of the input registers.
            "value": [0x0000, 0x0001, 0x0002, 0x0003]  # Values of the input registers. quantity is the length of the list. quantity is the length of the list.
        }
    ],
    "holding_registers": [
        {
            "register": 0,  # Start address of the holding registers.
            "value": [0x0000, 0x0001, 0x0002, 0x0003]  # Values of the holding registers. quantity is the length of the list. quantity is the length of the list.
        }
    ],
}

UiFlow2 Code Block:

init.png

MicroPython Code Block:

from modbus import ModbusRTUSlave
# slave = ModbusRTUSlave(uart, device_address=1, context=context)
start()

Start the Modbus RTU slave.

Returns:

None

UiFlow2 Code Block:

start.png

MicroPython Code Block:

slave.start()
stop()

Stop the Modbus RTU slave.

Returns:

None

UiFlow2 Code Block:

stop.png

MicroPython Code Block:

slave.stop()
add_coil(register: int, value: bool)

Add a coil to the modbus register dictionary.

Parameters:
  • register (int) – address of the coils. The address is 0x0000 to 0xFFFF.

  • value (bool) – Value to add. The value is True or False.

Returns:

None

UiFlow2 Code Block:

add_coil.png

MicroPython Code Block:

slave.add_coil(0, True)
add_discrete_input(register: int, value: bool)

Add a discrete input to the modbus register dictionary.

Parameters:
  • register (int) – address of the discrete inputs. The address is 0x0000 to 0xFFFF.

  • value (bool) – Value to add. The value is True or False.

Returns:

None

UiFlow2 Code Block:

add_discrete_input.png

MicroPython Code Block:

slave.add_discrete_input(0, True)
add_holding_register(register: int, value: int)

Add a holding register to the modbus register dictionary.

Parameters:
  • register (int) – address of the holding registers. The address is 0x0000 to 0xFFFF.

  • value (int) – Value to add. The value is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

add_holding_register.png

MicroPython Code Block:

slave.add_holding_register(0, 100)
add_input_register(register: int, value: int)

Add an input register to the modbus register dictionary.

Parameters:
  • register (int) – address of the input registers. The address is 0x0000 to 0xFFFF.

  • value (int) – Value to add. The value is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

add_input_register.png

MicroPython Code Block:

slave.add_input_register(0, 100)
remove_coil(register: int)

Remove a coil from the modbus register dictionary.

Parameters:

register (int) – address of the coils. The address is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

remove_coil.png

MicroPython Code Block:

slave.remove_coil(0)
remove_discrete_input(register: int)

Remove a discrete input from the modbus register dictionary.

Parameters:

register (int) – address of the discrete inputs. The address is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

remove_discrete_input.png

MicroPython Code Block:

slave.remove_discrete_input(0)
remove_holding_register(register: int)

Remove a holding register from the modbus register dictionary.

Parameters:

register (int) – address of the holding registers. The address is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

remove_holding_register.png

MicroPython Code Block:

slave.remove_holding_register(0)
remove_input_register(register: int)

Remove an input register from the modbus register dictionary.

Parameters:

register (int) – address of the input registers. The address is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

remove_input_register.png

MicroPython Code Block:

slave.remove_input_register(0)
get_coil(register: int)

Get the coil value.

Parameters:

register (int) – address of the coils. The address is 0x0000 to 0xFFFF.

Returns:

bool - The value of the coil.

UiFlow2 Code Block:

get_coil.png

MicroPython Code Block:

value = slave.get_coil(0)
get_discrete_input(register: int)

Get the discrete input value.

Parameters:

register (int) – address of the discrete inputs. The address is 0x0000 to 0xFFFF.

Returns:

bool - The value of the discrete input.

UiFlow2 Code Block:

get_discrete_input.png

MicroPython Code Block:

value = slave.get_discrete_input(0)
get_holding_register(register: int)

Get the holding register value.

Parameters:

register (int) – address of the holding registers. The address is 0x0000 to 0xFFFF.

Returns:

int - The value of the holding register.

UiFlow2 Code Block:

get_holding_register.png

MicroPython Code Block:

value = slave.get_holding_register(0)
get_input_register(register: int)

Get the input register value.

Parameters:

register (int) – address of the input registers. The address is 0x0000 to 0xFFFF.

Returns:

int - The value of the input register.

UiFlow2 Code Block:

get_input_register.png

MicroPython Code Block:

value = slave.get_input_register(0)
set_coil(register: int, value: bool)

Set the coil value.

Parameters:
  • register (int) – address of the coils. The address is 0x0000 to 0xFFFF.

  • value (bool) – Value to set. The value is True or False.

Returns:

None

UiFlow2 Code Block:

set_coil.png

MicroPython Code Block:

slave.set_coil(0, True)
set_multi_coils(register: int, value: list)

Set the multi coils value.

Parameters:
  • register (int) – address of the coils. The address is 0x0000 to 0xFFFF.

  • value (list) – Values to set. The value is a list of True or False.

Returns:

None

UiFlow2 Code Block:

set_multi_coils.png

MicroPython Code Block:

slave.set_multi_coils(0, [True, False])
set_discrete_input(register: int, value: bool)

Set the discrete input value.

Parameters:
  • register (int) – address of the discrete inputs. The address is 0x0000 to 0xFFFF.

  • value (bool) – Value to set. The value is True or False.

Returns:

None

UiFlow2 Code Block:

set_discrete_input.png

MicroPython Code Block:

slave.set_discrete_input(0, True)
set_multi_discrete_input(register: int, value: list)

Set the multi discrete inputs value.

Parameters:
  • register (int) – address of the discrete inputs. The address is 0x0000 to 0xFFFF.

  • value (list) – Values to set. The value is a list of True or False.

Returns:

None

UiFlow2 Code Block:

set_multi_discrete_input.png

MicroPython Code Block:

slave.set_multi_discrete_input(0, [True, False])
set_holding_register(register: int, value: int)

Set the holding register value.

Parameters:
  • register (int) – address of the holding registers. The address is 0x0000 to 0xFFFF.

  • value (int) – Value to set. The value is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

set_holding_register.png

MicroPython Code Block:

slave.set_holding_register(0, 100)
set_multi_holding_register(register: int, value: list)

Set the multi holding registers value.

Parameters:
  • register (int) – address of the holding registers. The address is 0x0000 to 0xFFFF.

  • value (list) – Values to set. The value is a list of 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

set_multi_holding_register.png

MicroPython Code Block:

slave.set_multi_holding_register(0, [100, 200])
set_input_register(register: int, value: int)

Set the input register value.

Parameters:
  • register (int) – address of the input registers. The address is 0x0000 to 0xFFFF.

  • value (int) – Value to set. The value is 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

set_input_register.png

MicroPython Code Block:

slave.set_input_register(0, 100)
set_multi_input_register(register: int, value: list)

Set the multi input registers value.

Parameters:
  • register (int) – address of the input registers. The address is 0x0000 to 0xFFFF.

  • value (list) – Values to set. The value is a list of 0x0000 to 0xFFFF.

Returns:

None

UiFlow2 Code Block:

set_multi_input_register.png

MicroPython Code Block:

slave.set_multi_input_register(0, [100, 200])
tick()

Modbus RTU slave tick function. This function should be called in the main loop.

Returns:

None

UiFlow2 Code Block:

tick.png

MicroPython Code Block:

slave.tick()
set_callback(func_code: int, handler)

Set the callback function for the function code.

Parameters:
  • func_code (int) – Function code. The function code is 1 to 6, 15, 16. the symbol is defined in the modbus.ModbusRTUSlave (*_EVENT etc.).

  • handler – Callback function.

Returns:

None

UiFlow2 Code Block:

read_coils_callback.png

read_discrete_inputs_callback.png

read_holding_registers_callback.png

read_input_registers_callback.png

write_multiple_coils_callback.png

write_multiple_registers_callback.png

write_single_coil_callback.png

write_single_register_callback.png

MicroPython Code Block:

def callback(arg):
    pass
slave.set_callback(1, callback)
READ_COILS_EVENT

Function code 1 (Read Coils).

READ_DISCRETE_INPUTS_EVENT

Function code 2 (Read Discrete Inputs).

READ_HOLDING_REGISTERS_EVENT

Function code 3 (Read Holding Registers).

READ_INPUT_REGISTERS_EVENT

Function code 4 (Read Input Registers).

WRITE_SINGLE_COIL_EVENT

Function code 5 (Write Single Coil).

WRITE_SINGLE_HOLDING_REGISTER_EVENT

Function code 6 (Write Single Holding Register).

WRITE_MULTIPLE_COILS_EVENT

Function code 15 (Write Multiple Coils).

WRITE_MULTIPLE_HOLDING_REGISTERS_EVENT

Function code 16 (Write Multiple Holding Registers).