|
| 1 | +# Examples |
| 2 | + |
| 3 | +Usage examples of this `micropython-modbus` library |
| 4 | + |
| 5 | +--------------- |
| 6 | + |
| 7 | +## RTU |
| 8 | + |
| 9 | +```{note} |
| 10 | +Check the port specific |
| 11 | +[MicroPython UART documentation](https://docs.micropython.org/en/latest/library/machine.UART.html) |
| 12 | +for further details. |
| 13 | +
|
| 14 | +A Raspberry Pi Pico e.g. requires the UART pins as a tuple of `Pin`, like |
| 15 | +`rtu_pins = (Pin(4), Pin(5))` and the corresponding `uart_id` for those pins, |
| 16 | +whereas ESP32 boards can use almost any pin for UART communication as shown in |
| 17 | +the following examples and shall be given as `rtu_pins = (25, 26)`. If |
| 18 | +necessary, the `uart_id` parameter may has to be adapted to the pins used. |
| 19 | +``` |
| 20 | + |
| 21 | +### Client/Slave |
| 22 | + |
| 23 | +With this example the device is acting as client (slave) and providing data via |
| 24 | +RTU (serial/UART) to a requesting host device. |
| 25 | + |
| 26 | +```python |
| 27 | +from umodbus.serial import ModbusRTU |
| 28 | + |
| 29 | +# RTU Client/Slave setup |
| 30 | +# the following example is for an ESP32 |
| 31 | +rtu_pins = (25, 26) # (TX, RX) |
| 32 | +slave_addr = 10 # address on bus as client |
| 33 | + |
| 34 | +client = ModbusRTU( |
| 35 | + addr=slave_addr, # address on bus |
| 36 | + pins=rtu_pins, # given as tuple (TX, RX) |
| 37 | + # baudrate=9600, # optional, default 9600 |
| 38 | + # data_bits=8, # optional, default 8 |
| 39 | + # stop_bits=1, # optional, default 1 |
| 40 | + # parity=None, # optional, default None |
| 41 | + # ctrl_pin=12, # optional, control DE/RE |
| 42 | + # uart_id=1 # optional, see port specific documentation |
| 43 | +) |
| 44 | + |
| 45 | +register_definitions = { |
| 46 | + "COILS": { |
| 47 | + "EXAMPLE_COIL": { |
| 48 | + "register": 123, |
| 49 | + "len": 1, |
| 50 | + "val": 1 |
| 51 | + } |
| 52 | + }, |
| 53 | + "HREGS": { |
| 54 | + "EXAMPLE_HREG": { |
| 55 | + "register": 93, |
| 56 | + "len": 1, |
| 57 | + "val": 19 |
| 58 | + } |
| 59 | + }, |
| 60 | + "ISTS": { |
| 61 | + "EXAMPLE_ISTS": { |
| 62 | + "register": 67, |
| 63 | + "len": 1, |
| 64 | + "val": 0 |
| 65 | + } |
| 66 | + }, |
| 67 | + "IREGS": { |
| 68 | + "EXAMPLE_IREG": { |
| 69 | + "register": 10, |
| 70 | + "len": 1, |
| 71 | + "val": 60001 |
| 72 | + } |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +# use the defined values of each register type provided by register_definitions |
| 77 | +client.setup_registers(registers=register_definitions) |
| 78 | + |
| 79 | +while True: |
| 80 | + try: |
| 81 | + result = client.process() |
| 82 | + except KeyboardInterrupt: |
| 83 | + print('KeyboardInterrupt, stopping RTU client...') |
| 84 | + break |
| 85 | + except Exception as e: |
| 86 | + print('Exception during execution: {}'.format(e)) |
| 87 | +``` |
| 88 | + |
| 89 | +### Host/Master |
| 90 | + |
| 91 | +With this example the device is acting as host (master) and requesting on or |
| 92 | +setting data at a RTU (serial/UART) client/slave. |
| 93 | + |
| 94 | +```python |
| 95 | +from umodbus.serial import Serial as ModbusRTUMaster |
| 96 | + |
| 97 | +# RTU Host/Master setup |
| 98 | +# the following example is for an ESP32 |
| 99 | +rtu_pins = (25, 26) # (TX, RX) |
| 100 | + |
| 101 | +host = ModbusRTUMaster( |
| 102 | + pins=rtu_pins, # given as tuple (TX, RX) |
| 103 | + # baudrate=9600, # optional, default 9600 |
| 104 | + # data_bits=8, # optional, default 8 |
| 105 | + # stop_bits=1, # optional, default 1 |
| 106 | + # parity=None, # optional, default None |
| 107 | + # ctrl_pin=12, # optional, control DE/RE |
| 108 | + # uart_id=1 # optional, see port specific documentation |
| 109 | +) |
| 110 | + |
| 111 | +coil_status = host.read_coils(slave_addr=10, starting_addr=123, coil_qty=1) |
| 112 | +print('Status of coil 123: {}'.format(coil_status)) |
| 113 | +``` |
| 114 | + |
| 115 | +## TCP |
| 116 | + |
| 117 | +### Client/Slave |
| 118 | + |
| 119 | +With this example the device is acting as client (slave) and providing data via |
| 120 | +TCP (socket) to a requesting host device. |
| 121 | + |
| 122 | +```python |
| 123 | +import network |
| 124 | +from umodbus.tcp import ModbusTCP |
| 125 | + |
| 126 | +# network connections shall be made here, check the MicroPython port specific |
| 127 | +# documentation for connecting to or creating a network |
| 128 | + |
| 129 | +# TCP Client/Slave setup |
| 130 | +# set IP address of this MicroPython device explicitly |
| 131 | +# local_ip = '192.168.4.1' # IP address |
| 132 | +# or get it from the system after a connection to the network has been made |
| 133 | +# it is not the task of this lib to provide a detailed explanation for this |
| 134 | +station = network.WLAN(network.STA_IF) |
| 135 | +local_ip = station.ifconfig()[0] |
| 136 | +tcp_port = 502 # port to listen for requests/providing data |
| 137 | + |
| 138 | +client = ModbusTCP() |
| 139 | + |
| 140 | +# check whether client has been bound to an IP and a port |
| 141 | +if not client.get_bound_status(): |
| 142 | + client.bind(local_ip=local_ip, local_port=tcp_port) |
| 143 | + |
| 144 | +register_definitions = { |
| 145 | + "COILS": { |
| 146 | + "EXAMPLE_COIL": { |
| 147 | + "register": 123, |
| 148 | + "len": 1, |
| 149 | + "val": 1 |
| 150 | + } |
| 151 | + }, |
| 152 | + "HREGS": { |
| 153 | + "EXAMPLE_HREG": { |
| 154 | + "register": 93, |
| 155 | + "len": 1, |
| 156 | + "val": 19 |
| 157 | + } |
| 158 | + }, |
| 159 | + "ISTS": { |
| 160 | + "EXAMPLE_ISTS": { |
| 161 | + "register": 67, |
| 162 | + "len": 1, |
| 163 | + "val": 0 |
| 164 | + } |
| 165 | + }, |
| 166 | + "IREGS": { |
| 167 | + "EXAMPLE_IREG": { |
| 168 | + "register": 10, |
| 169 | + "len": 1, |
| 170 | + "val": 60001 |
| 171 | + } |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +# use the defined values of each register type provided by register_definitions |
| 176 | +client.setup_registers(registers=register_definitions) |
| 177 | + |
| 178 | +while True: |
| 179 | + try: |
| 180 | + result = client.process() |
| 181 | + except KeyboardInterrupt: |
| 182 | + print('KeyboardInterrupt, stopping TCP client...') |
| 183 | + break |
| 184 | + except Exception as e: |
| 185 | + print('Exception during execution: {}'.format(e)) |
| 186 | +``` |
| 187 | + |
| 188 | +### Host/Master |
| 189 | + |
| 190 | +With this example the device is acting as host (master) and requesting on or |
| 191 | +setting data at a TCP (socket) client/slave. |
| 192 | + |
| 193 | +```python |
| 194 | +from umodbus.tcp import TCP as ModbusTCPMaster |
| 195 | + |
| 196 | +# valid network connections shall be made here |
| 197 | + |
| 198 | +# RTU Host/Master setup |
| 199 | +slave_tcp_port = 502 # port to send request on |
| 200 | +slave_ip = '192.168.178.69' # IP address of client, to be adjusted |
| 201 | + |
| 202 | +host = ModbusTCPMaster( |
| 203 | + slave_ip=slave_ip, |
| 204 | + slave_port=slave_tcp_port, |
| 205 | + # timeout=5.0 # optional, timeout in seconds, default 5.0 |
| 206 | +) |
| 207 | + |
| 208 | +coil_status = host.read_coils(slave_addr=10, starting_addr=123, coil_qty=1) |
| 209 | +print('Status of coil 123: {}'.format(coil_status)) |
| 210 | +``` |
| 211 | + |
| 212 | +## Callbacks |
| 213 | + |
| 214 | +Callbacks can be registered to be executed *after* setting a register with |
| 215 | +`on_set_cb` or to be executed *before* getting a register with `on_get_cb`. |
| 216 | + |
| 217 | +```{note} |
| 218 | +Getter callbacks can be registered for all registers with the `on_get_cb` |
| 219 | +parameter whereas the `on_set_cb` parameter is only available for coils and |
| 220 | +holding registers as only those can be set by a external host. |
| 221 | +``` |
| 222 | + |
| 223 | +```{eval-rst} |
| 224 | +.. warning:: |
| 225 | + Keep the get callback actions as short as possible to avoid potential |
| 226 | + request timeouts due to a to long processing time. |
| 227 | +``` |
| 228 | + |
| 229 | +```python |
| 230 | +def my_coil_set_cb(reg_type, address, val): |
| 231 | + print('Custom callback, called on setting {} at {} to: {}'. |
| 232 | + format(reg_type, address, val)) |
| 233 | + |
| 234 | + |
| 235 | +def my_coil_get_cb(reg_type, address, val): |
| 236 | + print('Custom callback, called on getting {} at {}, currently: {}'. |
| 237 | + format(reg_type, address, val)) |
| 238 | + |
| 239 | + |
| 240 | +# define some registers, for simplicity only a single coil is used |
| 241 | +register_definitions = { |
| 242 | + "COILS": { |
| 243 | + "EXAMPLE_COIL": { |
| 244 | + "register": 123, |
| 245 | + "len": 1, |
| 246 | + "val": 1, |
| 247 | + "on_get_cb": my_coil_get_cb, |
| 248 | + "on_set_cb": my_coil_set_cb |
| 249 | + } |
| 250 | + } |
| 251 | +} |
| 252 | + |
| 253 | +# use the defined values of each register type provided by register_definitions |
| 254 | +client.setup_registers(registers=register_definitions) |
| 255 | + |
| 256 | +# callbacks can also be defined after a register setup has been performed |
| 257 | +client.add_coil( |
| 258 | + address=123, |
| 259 | + value=bool(1), |
| 260 | + on_set_cb=my_coil_set_cb, |
| 261 | + on_get_cb=my_coil_get_cb |
| 262 | +) |
| 263 | +``` |
0 commit comments