Skip to content

Commit 5a64b48

Browse files
Merge fad2461 into 00f5916
2 parents 00f5916 + fad2461 commit 5a64b48

File tree

9 files changed

+163
-165
lines changed

9 files changed

+163
-165
lines changed

Dockerfile.client

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,5 @@ FROM micropython/unix:v1.18
1111
# COPY umodbus /root/.micropython/lib/umodbus
1212

1313
RUN micropython-dev -m upip install micropython-ulogging
14-
RUN micropython-dev -m upip install micropython-urequests
1514

1615
CMD [ "micropython-dev", "-m", "examples/tcp_client_example.py" ]

Dockerfile.host

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,5 @@ FROM micropython/unix:v1.18
1111
# COPY umodbus /root/.micropython/lib/umodbus
1212

1313
RUN micropython-dev -m upip install micropython-ulogging
14-
RUN micropython-dev -m upip install micropython-urequests
1514

1615
CMD [ "micropython-dev", "-m", "examples/tcp_host_example.py" ]

Dockerfile.test_tcp_example

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,3 @@ FROM micropython/unix:v1.18
1313
# COPY mpy_unittest.py /root/.micropython/lib/mpy_unittest.py
1414

1515
RUN micropython-dev -m upip install micropython-ulogging
16-
RUN micropython-dev -m upip install micropython-urequests

Dockerfile.tests

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ COPY umodbus /root/.micropython/lib/umodbus
1414
COPY mpy_unittest.py /root/.micropython/lib/mpy_unittest.py
1515

1616
RUN micropython-dev -m upip install micropython-ulogging
17-
RUN micropython-dev -m upip install micropython-urequests
1817
RUN micropython-dev -c "import mpy_unittest as unittest; unittest.main('tests')"
1918

2019
ENTRYPOINT ["/bin/bash"]

changelog.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
<!-- ## [Unreleased] -->
1616

1717
## Released
18+
## [2.1.1] - 2022-12-27
19+
### Fixed
20+
- Removed unnecessary dependency to `micropython-urequests` from Docker files, setup guide and package setup file
21+
- Enable Modbus Client mode for RTU implementation, see #40, removed during #33
22+
1823
## [2.1.0] - 2022-12-27
1924
### Added
2025
- Typing hints available for all functions of [umodbus](umodbus), see #27
@@ -177,8 +182,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
177182
- PEP8 style issues on all files of [`lib/uModbus`](lib/uModbus)
178183

179184
<!-- Links -->
180-
[Unreleased]: https://github.com/brainelectronics/micropython-modbus/compare/2.1.0...develop
185+
[Unreleased]: https://github.com/brainelectronics/micropython-modbus/compare/2.1.1...develop
181186

187+
[2.1.1]: https://github.com/brainelectronics/micropython-modbus/tree/2.1.1
182188
[2.1.0]: https://github.com/brainelectronics/micropython-modbus/tree/2.1.0
183189
[2.0.0]: https://github.com/brainelectronics/micropython-modbus/tree/2.0.0
184190
[1.2.0]: https://github.com/brainelectronics/micropython-modbus/tree/1.2.0

docs/SETUP.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,7 @@ mkdir /pyboard/lib/umodbus
103103
cp umodbus/* /pyboard/lib/umodbus
104104
```
105105

106-
As this package depends on [`micropython-urequests`][ref-urequests] to perform
107-
TCP requests those files have to be copied as well to the MicroPython board.
108-
This is of course only necessary if TCP connection are used, in case only
109-
serial (RTU )Modbus communication is used this step can be skipped.
110-
111-
### Additional MicroPython packages
106+
### Additional MicroPython packages for examples
112107

113108
To use this package with the provided [`boot.py`][ref-package-boot-file] and
114109
[`main.py`][ref-package-boot-file] file, additional modules are required,
@@ -151,7 +146,6 @@ README for further instructions.
151146
[ref-upy-firmware-download]: https://micropython.org/download/
152147
[ref-remote-upy-shell]: https://github.com/dhylands/rshell
153148
[ref-umodbus-module]: https://github.com/brainelectronics/micropython-modbus/tree/develop/umodbus
154-
[ref-urequests]: https://micropython.org/pi/urequests/urequests-0.6.tar.gz
155149
[ref-package-boot-file]: https://github.com/brainelectronics/micropython-modbus/blob/c45d6cc334b4adf0e0ffd9152c8f08724e1902d9/boot.py
156150
[ref-package-main-file]: https://github.com/brainelectronics/micropython-modbus/blob/c45d6cc334b4adf0e0ffd9152c8f08724e1902d9/main.py
157151
[ref-github-be-mircopython-modules]: https://github.com/brainelectronics/micropython-modules

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@
3535
license='MIT',
3636
cmdclass={'sdist': sdist_upip.sdist},
3737
packages=['umodbus'],
38-
install_requires=['micropython-urequests']
38+
install_requires=[]
3939
)

umodbus/modbus.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
import time
1717

1818
# custom packages
19+
from . import functions
20+
from . import const as Const
21+
from .common import Request
1922

2023
# typing not natively supported on MicroPython
2124
from .typing import dict_keys, List, Optional, Union
@@ -41,6 +44,157 @@ def __init__(self, itf, addr_list: List[int]) -> None:
4144
for reg_type in self._changeable_register_types:
4245
self._changed_registers[reg_type] = dict()
4346

47+
def process(self) -> bool:
48+
"""
49+
Process the Modbus requests.
50+
51+
:returns: Result of processing, True on success, False otherwise
52+
:rtype: bool
53+
"""
54+
reg_type = None
55+
req_type = None
56+
57+
request = self._itf.get_request(unit_addr_list=self._addr_list,
58+
timeout=0)
59+
if request is None:
60+
return False
61+
62+
if request.function == Const.READ_COILS:
63+
# Coils (setter+getter) [0, 1]
64+
# function 01 - read single register
65+
reg_type = 'COILS'
66+
req_type = 'READ'
67+
elif request.function == Const.READ_DISCRETE_INPUTS:
68+
# Ists (only getter) [0, 1]
69+
# function 02 - read input status (discrete inputs/digital input)
70+
reg_type = 'ISTS'
71+
req_type = 'READ'
72+
elif request.function == Const.READ_HOLDING_REGISTERS:
73+
# Hregs (setter+getter) [0, 65535]
74+
# function 03 - read holding register
75+
reg_type = 'HREGS'
76+
req_type = 'READ'
77+
elif request.function == Const.READ_INPUT_REGISTER:
78+
# Iregs (only getter) [0, 65535]
79+
# function 04 - read input registers
80+
reg_type = 'IREGS'
81+
req_type = 'READ'
82+
elif (request.function == Const.WRITE_SINGLE_COIL or
83+
request.function == Const.WRITE_MULTIPLE_COILS):
84+
# Coils (setter+getter) [0, 1]
85+
# function 05 - write single coil
86+
# function 15 - write multiple coil
87+
reg_type = 'COILS'
88+
req_type = 'WRITE'
89+
elif (request.function == Const.WRITE_SINGLE_REGISTER or
90+
request.function == Const.WRITE_MULTIPLE_REGISTERS):
91+
# Hregs (setter+getter) [0, 65535]
92+
# function 06 - write holding register
93+
# function 16 - write multiple holding register
94+
reg_type = 'HREGS'
95+
req_type = 'WRITE'
96+
else:
97+
request.send_exception(Const.ILLEGAL_FUNCTION)
98+
99+
if reg_type:
100+
if req_type == 'READ':
101+
self._process_read_access(request=request, reg_type=reg_type)
102+
elif req_type == 'WRITE':
103+
self._process_write_access(request=request, reg_type=reg_type)
104+
105+
return True
106+
107+
def _create_response(self,
108+
request: Request,
109+
reg_type: str) -> Union[bool, int,
110+
List[bool], List[int]]:
111+
"""
112+
Create a response.
113+
114+
:param request: The request
115+
:type request: Request
116+
:param reg_type: The register type
117+
:type reg_type: str
118+
119+
:returns: Values of this register
120+
:rtype: Union[bool, int, List[int], List[bool]]
121+
"""
122+
data = []
123+
if type(self._register_dict[reg_type][request.register_addr]) is list:
124+
data = self._register_dict[reg_type][request.register_addr]
125+
else:
126+
data = [self._register_dict[reg_type][request.register_addr]]
127+
128+
return data[:request.quantity]
129+
130+
def _process_read_access(self, request: Request, reg_type: str) -> None:
131+
"""
132+
Process read access to register
133+
134+
:param request: The request
135+
:type request: Request
136+
:param reg_type: The register type
137+
:type reg_type: str
138+
"""
139+
if request.register_addr in self._register_dict[reg_type]:
140+
vals = self._create_response(request=request, reg_type=reg_type)
141+
request.send_response(vals)
142+
else:
143+
request.send_exception(Const.ILLEGAL_DATA_ADDRESS)
144+
145+
def _process_write_access(self, request: Request, reg_type: str) -> None:
146+
"""
147+
Process write access to register
148+
149+
:param request: The request
150+
:type request: Request
151+
:param reg_type: The register type
152+
:type reg_type: str
153+
"""
154+
address = request.register_addr
155+
val = 0
156+
valid_register = False
157+
158+
if address in self._register_dict[reg_type]:
159+
if reg_type == 'COILS':
160+
valid_register = True
161+
162+
if request.function == Const.WRITE_SINGLE_COIL:
163+
val = request.data[0]
164+
if 0x00 < val < 0xFF:
165+
valid_register = False
166+
request.send_exception(Const.ILLEGAL_DATA_VALUE)
167+
else:
168+
val = (val == 0xFF)
169+
elif request.function == Const.WRITE_MULTIPLE_COILS:
170+
tmp = int.from_bytes(request.data, "big")
171+
val = [
172+
bool(tmp & (1 << n)) for n in range(request.quantity)
173+
]
174+
175+
if valid_register:
176+
self.set_coil(address=address, value=val)
177+
elif reg_type == 'HREGS':
178+
valid_register = True
179+
val = list(functions.to_short(byte_array=request.data,
180+
signed=False))
181+
182+
if request.function == Const.WRITE_SINGLE_REGISTER:
183+
self.set_hreg(address=address, value=val[0])
184+
elif request.function == Const.WRITE_MULTIPLE_REGISTERS:
185+
self.set_hreg(address=address, value=val)
186+
else:
187+
# nothing except holding registers or coils can be set
188+
request.send_exception(Const.ILLEGAL_FUNCTION)
189+
190+
if valid_register:
191+
request.send_response()
192+
self._set_changed_register(reg_type=reg_type,
193+
address=address,
194+
value=val)
195+
else:
196+
request.send_exception(Const.ILLEGAL_DATA_ADDRESS)
197+
44198
def add_coil(self,
45199
address: int,
46200
value: Union[bool, List[bool]] = False) -> None:

0 commit comments

Comments
 (0)