Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions umodbus/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,13 @@ def __init__(self, function_code: int, exception_code: int) -> None:

class CommonModbusFunctions(object):
"""Common Modbus functions"""
def __init__(self):
pass
def __init__(self, byte_order: str = 'big'):
if byte_order == 'big':
self._byte_order = '>'
elif byte_order == 'little':
self._byte_order = '<'
else:
raise ValueError("byte_order must be 'big' or 'little'")

def read_coils(self,
slave_addr: int,
Expand Down Expand Up @@ -200,7 +205,8 @@ def read_holding_registers(self,
modbus_pdu=modbus_pdu,
count=True)

register_value = functions.to_short(byte_array=response, signed=signed)
register_value = functions.to_short(byte_array=response, signed=signed,
byte_order=self._byte_order)

return register_value

Expand Down Expand Up @@ -232,7 +238,8 @@ def read_input_registers(self,
modbus_pdu=modbus_pdu,
count=True)

register_value = functions.to_short(byte_array=response, signed=signed)
register_value = functions.to_short(byte_array=response, signed=signed,
byte_order=self._byte_order)

return register_value

Expand Down Expand Up @@ -295,7 +302,8 @@ def write_single_register(self,
modbus_pdu = functions.write_single_register(
register_address=register_address,
register_value=register_value,
signed=signed)
signed=signed,
byte_order=self._byte_order)

response = self._send_receive(slave_addr=slave_addr,
modbus_pdu=modbus_pdu,
Expand All @@ -309,7 +317,8 @@ def write_single_register(self,
function_code=Const.WRITE_SINGLE_REGISTER,
address=register_address,
value=register_value,
signed=signed)
signed=signed,
byte_order=self._byte_order)

return operation_status

Expand Down Expand Up @@ -372,7 +381,8 @@ def write_multiple_registers(self,
modbus_pdu = functions.write_multiple_registers(
starting_address=starting_address,
register_values=register_values,
signed=signed)
signed=signed,
byte_order=self._byte_order)

response = self._send_receive(slave_addr=slave_addr,
modbus_pdu=modbus_pdu,
Expand All @@ -386,7 +396,8 @@ def write_multiple_registers(self,
function_code=Const.WRITE_MULTIPLE_REGISTERS,
address=starting_address,
quantity=len(register_values),
signed=signed
signed=signed,
byte_order=self._byte_order
)

return operation_status
59 changes: 37 additions & 22 deletions umodbus/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ def write_single_coil(output_address: int,

def write_single_register(register_address: int,
register_value: int,
signed: bool = True) -> bytes:
signed: bool = True,
byte_order: str = '>') -> bytes:
"""
Create Modbus message to writes a single register

Expand All @@ -139,16 +140,18 @@ def write_single_register(register_address: int,
:type register_value: int
:param signed: Flag whether data is signed or not
:type signed: bool
:param byte_order: The byte order
:type byte_order: str

:returns: Packed Modbus message
:rtype: bytes
"""
fmt = 'h' if signed else 'H'

return struct.pack('>BH' + fmt,
pdu = struct.pack('>BH',
Const.WRITE_SINGLE_REGISTER,
register_address,
register_value)
register_address)
pdu += struct.pack(byte_order + fmt, register_value)
return pdu


def write_multiple_coils(starting_address: int,
Expand Down Expand Up @@ -194,7 +197,8 @@ def write_multiple_coils(starting_address: int,

def write_multiple_registers(starting_address: int,
register_values: List[int],
signed: bool = True) -> bytes:
signed: bool = True,
byte_order: str = '>') -> bytes:
"""
Create Modbus message to update multiple coils

Expand All @@ -204,6 +208,8 @@ def write_multiple_registers(starting_address: int,
:type register_values: List[int, bool]
:param signed: Flag whether data is signed or not
:type signed: bool
:param byte_order: The byte order
:type byte_order: str

:returns: Packed Modbus message
:rtype: bytes
Expand All @@ -215,20 +221,23 @@ def write_multiple_registers(starting_address: int,
byte_count = quantity * 2
fmt = ('h' if signed else 'H') * quantity

return struct.pack('>BHHB' + fmt,
pdu = struct.pack('>BHHB',
Const.WRITE_MULTIPLE_REGISTERS,
starting_address,
quantity,
byte_count,
*register_values)
byte_count)
pdu += struct.pack(byte_order + fmt, *register_values)

return pdu


def validate_resp_data(data: bytes,
function_code: int,
address: int,
value: int = None,
quantity: int = None,
signed: bool = True) -> bool:
signed: bool = True,
byte_order: str = '>') -> bool:
"""
Validate the response data.

Expand All @@ -244,14 +253,15 @@ def validate_resp_data(data: bytes,
:type quantity: int
:param signed: Indicates if signed
:type signed: bool
:param byte_order: The byte order
:type byte_order: str

:returns: True if valid, False otherwise
:rtype: bool
"""
fmt = '>H' + ('h' if signed else 'H')

if function_code in [Const.WRITE_SINGLE_COIL, Const.WRITE_SINGLE_REGISTER]:
resp_addr, resp_value = struct.unpack(fmt, data)
fmt = 'h' if signed else 'H'
resp_addr, resp_value = struct.unpack(byte_order + 'H' + fmt, data)

# if bool(True) or int(1) is used as "output_value" of
# "write_single_coil" it will be internally converted to int(0xFF00),
Expand All @@ -267,7 +277,7 @@ def validate_resp_data(data: bytes,
return True
elif function_code in [Const.WRITE_MULTIPLE_COILS,
Const.WRITE_MULTIPLE_REGISTERS]:
resp_addr, resp_qty = struct.unpack(fmt, data)
resp_addr, resp_qty = struct.unpack(byte_order + 'HH', data)

if (address == resp_addr) and (quantity == resp_qty):
return True
Expand All @@ -280,7 +290,8 @@ def response(function_code: int,
request_register_qty: int,
request_data: list,
value_list: Optional[list] = None,
signed: bool = True) -> bytes:
signed: bool = True,
byte_order: str = '>') -> bytes:
"""
Construct a Modbus response Protocol Data Unit

Expand All @@ -296,6 +307,8 @@ def response(function_code: int,
:type value_list: Optional[list]
:param signed: Indicates if signed
:type signed: bool
:param byte_order: The byte order
:type byte_order: str

:returns: Protocol data unit
:rtype: bytes
Expand Down Expand Up @@ -333,10 +346,10 @@ def response(function_code: int,
for s in signed:
fmt += 'h' if s else 'H'

return struct.pack('>BB' + fmt,
function_code,
quantity * 2,
*value_list)
pdu = struct.pack('>BB', function_code, quantity * 2)
pdu += struct.pack(byte_order + fmt, *value_list)

return pdu

elif function_code in [Const.WRITE_SINGLE_COIL,
Const.WRITE_SINGLE_REGISTER]:
Expand Down Expand Up @@ -398,22 +411,24 @@ def bytes_to_bool(byte_list: bytes, bit_qty: Optional[int] = 1) -> List[bool]:
return bool_list


def to_short(byte_array: bytes, signed: bool = True) -> bytes:
def to_short(byte_array: bytes, signed: bool = True, byte_order: str = '>') -> bytes:
"""
Convert bytes to tuple of integer values

:param byte_array: The byte array
:type byte_array: bytes
:param signed: Indicates if signed
:type signed: bool
:param byte_order: The byte order
:type byte_order: str

:returns: Integer representation
:rtype: bytes
"""
response_quantity = int(len(byte_array) / 2)
fmt = '>' + (('h' if signed else 'H') * response_quantity)
fmt = (('h' if signed else 'H') * response_quantity)

return struct.unpack(fmt, byte_array)
return struct.unpack(byte_order + fmt, byte_array)


def float_to_bin(num: float) -> bin:
Expand Down
11 changes: 9 additions & 2 deletions umodbus/modbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ class Modbus(object):
:param addr_list: List of addresses
:type addr_list: List[int]
"""
def __init__(self, itf, addr_list: List[int]) -> None:
def __init__(self, itf, addr_list: List[int], byte_order: str = 'big') -> None:
self._itf = itf
self._addr_list = addr_list
if byte_order == 'big':
self._byte_order = '>'
elif byte_order == 'little':
self._byte_order = '<'
else:
raise ValueError("byte_order must be 'big' or 'little'")

# modbus register types with their default value
self._available_register_types = ['COILS', 'HREGS', 'IREGS', 'ISTS']
Expand Down Expand Up @@ -233,7 +239,8 @@ def _process_write_access(self, request: Request, reg_type: str) -> None:
elif reg_type == 'HREGS':
valid_register = True
val = list(functions.to_short(byte_array=request.data,
signed=False))
signed=False,
byte_order=self._byte_order))

if request.function in [Const.WRITE_SINGLE_REGISTER,
Const.WRITE_MULTIPLE_REGISTERS]:
Expand Down
26 changes: 16 additions & 10 deletions umodbus/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,20 @@ def __init__(self,
parity: Optional[int] = None,
pins: List[Union[int, Pin], Union[int, Pin]] = None,
ctrl_pin: int = None,
uart_id: int = 1):
uart_id: int = 1,
byte_order: str = 'big'):
super().__init__(
# set itf to Serial object, addr_list to [addr]
Serial(uart_id=uart_id,
baudrate=baudrate,
data_bits=data_bits,
stop_bits=stop_bits,
parity=parity,
pins=pins,
ctrl_pin=ctrl_pin),
[addr]
itf=Serial(uart_id=uart_id,
baudrate=baudrate,
byte_order=byte_order,
data_bits=data_bits,
stop_bits=stop_bits,
parity=parity,
pins=pins,
ctrl_pin=ctrl_pin),
addr_list=[addr],
byte_order=byte_order
)


Expand All @@ -76,7 +79,9 @@ def __init__(self,
stop_bits: int = 1,
parity=None,
pins: List[Union[int, Pin], Union[int, Pin]] = None,
ctrl_pin: int = None):
ctrl_pin: int = None,
byte_order: str = 'big'):
super().__init__(byte_order=byte_order)
"""
Setup Serial/RTU Modbus

Expand All @@ -95,6 +100,7 @@ def __init__(self,
:param ctrl_pin: The control pin
:type ctrl_pin: int
"""
super().__init__(byte_order=byte_order)
# UART flush function is introduced in Micropython v1.20.0
self._has_uart_flush = callable(getattr(UART, "flush", None))
self._uart = UART(uart_id,
Expand Down
9 changes: 6 additions & 3 deletions umodbus/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@

class ModbusTCP(Modbus):
"""Modbus TCP client class"""
def __init__(self):
def __init__(self, byte_order: str = 'big'):
super().__init__(
# set itf to TCPServer object, addr_list to None
TCPServer(),
None
None,
byte_order=byte_order
)

def bind(self,
Expand Down Expand Up @@ -77,7 +78,9 @@ class TCP(CommonModbusFunctions):
def __init__(self,
slave_ip: str,
slave_port: int = 502,
timeout: float = 5.0):
timeout: float = 5.0,
byte_order: str = 'big'):
super().__init__(byte_order=byte_order)
self._sock = socket.socket()
self.trans_id_ctr = 0

Expand Down