Skip to content

Commit 1891c7c

Browse files
committed
libs/modbus: Add RTU Slave and TCP Server support.
Signed-off-by: lbuque <[email protected]>
1 parent 5f00c0f commit 1891c7c

14 files changed

+1371
-262
lines changed

m5stack/libs/modbus/examples/ssr.py renamed to m5stack/libs/modbus/examples/m5stack_acssr_controller.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 lbuque, written for M5Stack
2+
#
3+
# SPDX-License-Identifier: MIT
4+
15
import sys
26
import os
37
import time

m5stack/libs/modbus/examples/test_rtu_master.py renamed to m5stack/libs/modbus/examples/rtu_master.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 lbuque, written for M5Stack
2+
#
3+
# SPDX-License-Identifier: MIT
4+
15
import sys
26
import time
37

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 lbuque, written for M5Stack
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import sys
6+
import os
7+
8+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9+
10+
import modbus
11+
import serial
12+
import time
13+
import asyncio
14+
15+
16+
ser = serial.Serial("COM22", 115200)
17+
slave = modbus.ModbusRTUSlave(
18+
uart=ser,
19+
verbose=True,
20+
context={
21+
"discrete_inputs": [
22+
{
23+
"register": 67, # register address of the input status register
24+
"value": [
25+
0,
26+
], # used to set a register, not possible for ISTS
27+
"description": "Optional description of the input status register",
28+
"range": [0, 1],
29+
"unit": "activated",
30+
}
31+
],
32+
"coils": [
33+
{
34+
"register": 1000, # register address of the coil
35+
"value": [
36+
True,
37+
False,
38+
True,
39+
False,
40+
True,
41+
False,
42+
True,
43+
False,
44+
True,
45+
False,
46+
], # used to set a register
47+
"description": "Optional description of the coil", # the onwards mentioned keys are optional
48+
"range": [
49+
0,
50+
1,
51+
], # may provide a range of the value, only for documentation purpose
52+
"unit": "BOOL", # may provide a unit of the value, only for documentation purpose
53+
}
54+
],
55+
"input_registers": [
56+
{
57+
"register": 10, # register address of the input register
58+
"value": [
59+
60001,
60+
], # used to set a register, not possible for IREGS
61+
"description": "Optional description of the static input register",
62+
"range": [0, 65535],
63+
"unit": "millivolt",
64+
}
65+
],
66+
"holding_registers": [
67+
{
68+
"register": 93, # register address of the holding register
69+
"value": [
70+
19,
71+
], # used to set a register
72+
"description": "Optional description of the holding register",
73+
"range": [0, 65535],
74+
"unit": "Hz",
75+
}
76+
],
77+
},
78+
)
79+
80+
81+
def modbus_read_coils_cb(modbus_obj, starting_register: int, value: list):
82+
print("modbus_read_coils_cb", starting_register, value)
83+
84+
85+
def modbus_read_discrete_inputs_cb(modbus_obj, starting_register: int, value: list):
86+
print("modbus_read_discrete_inputs_cb", starting_register, value)
87+
88+
89+
def modbus_read_holding_registers_cb(modbus_obj, starting_register: int, value: list):
90+
print("modbus_read_holding_registers_cb", starting_register, value)
91+
92+
93+
def modbus_read_input_registers_cb(modbus_obj, starting_register: int, value: list):
94+
print("modbus_read_input_registers_cb", starting_register, value)
95+
96+
97+
def modbus_write_single_coil_cb(modbus_obj, register: int, value: bool):
98+
print("modbus_write_single_coil_cb", register, value)
99+
100+
101+
def modbus_write_single_registers_cb(modbus_obj, register: int, value: int):
102+
print("modbus_write_single_registers_cb", register, value)
103+
104+
105+
def modbus_write_multiple_coils_cb(modbus_obj, starting_register: int, value: list):
106+
print("modbus_write_multiple_coils_cb", starting_register, value)
107+
108+
109+
def modbus_write_multiple_registers_cb(modbus_obj, starting_register: int, value: list):
110+
print("modbus_write_multiple_registers_cb", starting_register, value)
111+
112+
113+
# READ_COILS_EVENT = 0x01
114+
# READ_DISCRETE_INPUTS_EVENT = 0x02
115+
# READ_HOLDING_REGISTERS_EVENT = 0x03
116+
# READ_INPUT_REGISTERS_EVENT = 0x04
117+
# WRITE_SINGLE_COIL_EVENT = 0x05
118+
# WRITE_SINGLE_REGISTER_EVENT = 0x06
119+
# WRITE_MULTIPLE_COILS_EVENT = 0x0F
120+
# WRITE_MULTIPLE_REGISTERS_EVENT = 0x10
121+
122+
slave.set_callback(slave.READ_COILS_EVENT, modbus_read_coils_cb)
123+
slave.set_callback(slave.READ_DISCRETE_INPUTS_EVENT, modbus_read_discrete_inputs_cb)
124+
slave.set_callback(slave.READ_HOLDING_REGISTERS_EVENT, modbus_read_holding_registers_cb)
125+
slave.set_callback(slave.READ_INPUT_REGISTERS_EVENT, modbus_read_input_registers_cb)
126+
slave.set_callback(slave.WRITE_SINGLE_COIL_EVENT, modbus_write_single_coil_cb)
127+
slave.set_callback(slave.WRITE_SINGLE_REGISTER_EVENT, modbus_write_single_registers_cb)
128+
slave.set_callback(slave.WRITE_MULTIPLE_COILS_EVENT, modbus_write_multiple_coils_cb)
129+
slave.set_callback(slave.WRITE_MULTIPLE_REGISTERS_EVENT, modbus_write_multiple_registers_cb)
130+
131+
asyncio.run(slave.run_async())

m5stack/libs/modbus/examples/test_tcp_client.py renamed to m5stack/libs/modbus/examples/tcp_client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 lbuque, written for M5Stack
2+
#
3+
# SPDX-License-Identifier: MIT
4+
15
import os
26
import sys
37

Lines changed: 15 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 lbuque, written for M5Stack
2+
#
3+
# SPDX-License-Identifier: MIT
14
import sys
25
import os
36

47
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
58

6-
import modbus
7-
import serial
8-
import time
99
import asyncio
10+
import modbus
1011

11-
12-
ser = serial.Serial("COM22", 115200)
13-
slave = modbus.ModbusRTUSlave(
14-
uart=ser,
15-
verbose=True,
12+
srv = modbus.ModbusTCPServer(
13+
"0.0.0.0",
14+
5000,
1615
context={
1716
"discrete_inputs": [
1817
{
1918
"register": 67, # register address of the input status register
20-
"val": [
19+
"value": [
2120
0,
2221
], # used to set a register, not possible for ISTS
2322
"description": "Optional description of the input status register",
@@ -28,7 +27,7 @@
2827
"coils": [
2928
{
3029
"register": 1000, # register address of the coil
31-
"val": [
30+
"value": [
3231
True,
3332
False,
3433
True,
@@ -51,7 +50,7 @@
5150
"input_registers": [
5251
{
5352
"register": 10, # register address of the input register
54-
"val": [
53+
"value": [
5554
60001,
5655
], # used to set a register, not possible for IREGS
5756
"description": "Optional description of the static input register",
@@ -62,7 +61,7 @@
6261
"holding_registers": [
6362
{
6463
"register": 93, # register address of the holding register
65-
"val": [
64+
"value": [
6665
19,
6766
], # used to set a register
6867
"description": "Optional description of the holding register",
@@ -73,57 +72,8 @@
7372
},
7473
)
7574

75+
# asyncio.run(srv.run_async())
7676

77-
def cb_01(start_reg, reg_num, context):
78-
print("cb_01:")
79-
print("\tstart register:", start_reg)
80-
print("\tregister number:", reg_num)
81-
print("\tcontext:", context)
82-
83-
84-
def cb_02(start_reg, reg_num, context):
85-
print("cb_02:")
86-
print("\tstart register:", start_reg)
87-
print("\tregister number:", reg_num)
88-
print("\tcontext:", context)
89-
90-
91-
def cb_03(start_reg, reg_num, context):
92-
print("cb_03:")
93-
print("\tstart register:", start_reg)
94-
print("\tregister number:", reg_num)
95-
print("\tcontext:", context)
96-
97-
98-
def cb_04(start_reg, reg_num, context):
99-
print("cb_04:")
100-
print("\tstart register:", start_reg)
101-
print("\tregister number:", reg_num)
102-
print("\tcontext:", context)
103-
104-
105-
def cb_05(reg_addr, data, context):
106-
print("cb_05:")
107-
print("\tregister address:", reg_addr)
108-
print("\tdata:", data)
109-
110-
111-
def cb_06(reg_addr, data, context):
112-
print("cb_06:")
113-
print("\tregister address:", reg_addr)
114-
print("\tdata:", data)
115-
116-
117-
# https://blog.csdn.net/xukai871105/article/details/16368567
118-
def cb_15(start_reg, reg_num, data, context):
119-
print("cb_15:")
120-
121-
122-
# https://blog.csdn.net/xukai871105/article/details/16368567
123-
def cb_16(start_reg, reg_num, data, context):
124-
print("cb_16:")
125-
126-
127-
slave.cb[0x01] = cb_01
128-
129-
asyncio.run(slave.run_async())
77+
srv.start()
78+
while True:
79+
srv.tick()

m5stack/libs/modbus/examples/test_tcp_server.py

Lines changed: 0 additions & 70 deletions
This file was deleted.

m5stack/libs/modbus/modbus/frame.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ def parse_frame(cls, frame, fr_type=None):
212212
if debug:
213213
print("Parsing RTU frame: " + " ".join(["{:02x}".format(x) for x in frame]))
214214

215+
if len(frame) < 2:
216+
return
217+
215218
device_addr = frame[0]
216219
func_code = frame[1]
217220

@@ -420,6 +423,9 @@ def parse_frame(cls, frame):
420423
if debug:
421424
print("Parsing TCP frame: " + " ".join(["{:02x}".format(x) for x in frame]))
422425

426+
if len(frame) < 8:
427+
return
428+
423429
transaction_id = (frame[0] << 8) + frame[1]
424430
length = (frame[4] << 8) + frame[5]
425431
unit_id = frame[6]
@@ -503,7 +509,7 @@ def parse_frame(cls, frame):
503509
print(f)
504510
return f
505511

506-
raise ValueError("Could not parse Frame " + " ".join(["{:02x}".format(x) for x in frame]))
512+
# raise ValueError("Could not parse Frame " + " ".join(["{:02x}".format(x) for x in frame]))
507513

508514
@classmethod
509515
def _check_both(cls, frame, length):

0 commit comments

Comments
 (0)