I am using modbus-tk
to serially communicate with a device via Modbus RTU over a RS-485 network.
I am trying to figure out how to use function 23, READ_WRITE_MULTIPLE_REGISTERS
. This is my first time using function 23. Here is my current implementation:
response = modbus_master.execute(
slave=SLAVE_NUM,
function_code=cst.READ_WRITE_MULTIPLE_REGISTERS,
starting_address=2,
quantity_of_x=1,
output_value=[1],
)
While running this command, I get the following error: Modbus Error: Exception code = 1
I looked up this exception code on Wikipedia, and see:
Function code received in the query is not recognized or allowed by slave
Do you think this means my device truly does not support this function code? Or do I have a syntax problem/am mis-using this function?
I have placed my full script below.
Full Code Example
Input
#!/usr/bin/env python3
import time
from collections import namedtuple
from logging import Logger
from serial import Serial
from modbus_tk.modbus_rtu import RtuMaster
import modbus_tk.defines as cst # cst = constants
from modbus_tk.utils import create_logger
PORT = "COM3"
SLAVE_NUM = 1
MODBUS_MASTER_TIMEOUT_SEC = 5.0
ModbusHoldingReg = namedtuple(
"ModbusHoldingRegister", ["name", "address", "last_read_value", "to_write_value"]
)
shutdown_delay = ModbusHoldingReg("shutdown delay", 2, 0, None) # sec
logger = create_logger(name="console") # type: Logger
serial_ = Serial(PORT)
modbus_master = RtuMaster(serial_)
modbus_master.set_timeout(MODBUS_MASTER_TIMEOUT_SEC)
modbus_master.set_verbose(True)
# Sleep some time per [1]
# [1]: https://github.com/ljean/modbus-tk/issues/73#issuecomment-284800980
time.sleep(2.0)
# Read/write from/to multiple registers
response = modbus_master.execute(
slave=SLAVE_NUM,
function_code=cst.READ_WRITE_MULTIPLE_REGISTERS,
starting_address=shutdown_delay.address,
quantity_of_x=1,
output_value=[1],
) # type: tuple
print(response)
Output
2020-01-31 10:43:24,885 INFO modbus_rtu.__init__ MainThread RtuMaster COM3 is opened
2020-01-31 10:43:26,890 DEBUG modbus.execute MainThread -> 1-23-0-2-0-1-0-23-0-1-2-0-1-55-131
2020-01-31 10:43:31,933 DEBUG modbus.execute MainThread <- 1-151-1-143-240
---------------------------------------------------------------------------
ModbusError Traceback (most recent call last)
<ipython-input-1-f42d200d6c09> in <module>
37 starting_address=shutdown_delay.address,
38 quantity_of_x=1,
---> 39 output_value=[1],
40 ) # type: tuple
41 print(response)
c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
37 ret = fcn(*args, **kwargs)
38 except Exception as excpt:
---> 39 raise excpt
40 finally:
41 if threadsafe:
c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
35 lock.acquire()
36 try:
---> 37 ret = fcn(*args, **kwargs)
38 except Exception as excpt:
39 raise excpt
c:\path\to\venv\lib\site-packages\modbus_tk\modbus.py in execute(self, slave, function_code, starting_address, quantity_of_x, output_value, data_format, expected_length)
312 # the slave has returned an error
313 exception_code = byte_2
--> 314 raise ModbusError(exception_code)
315 else:
316 if is_read_function:
ModbusError: Modbus Error: Exception code = 1
Device Specifics
9600/8-N-1
Packages
I am using Python 3.6 on Windows 10.
pyserial==3.4
modbus-tk==1.1.0
Further to the answer from @maxy; the modbus spec states that exception code 1 (ILLEGAL FUNCTION) means:
The function code received in the query is not an allowable action for the server (or slave). This may be because the function code is only applicable to newer devices, and was not implemented in the unit selected. It could also indicate that the server (or slave) is in the wrong state to process a request of this type, for example because it is unconfigured and is being asked to return register values.
So, in this case, I'd say that the device does not support this command.
However given that another user has reported an issue with this command I thought it was worth checking the encoding:
1- Slave ID
23- Function Code
0, 2- Read Starting Address
0, 1- Quantity to Read
0, 23- Write Starting Address
0, 1 - Quantity to write
2, Write Byte Count
0,1, - Write Registers value
55,131 - CRC (have not checked)
This looks correct to me with one exception; it's not clear where the "Write Starting Address" comes from (and suspicious that it's the same as the function code). Looking at the source:
pdu = struct.pack(
">BHHHHB",
function_code, starting_address, quantity_of_x, defines.READ_WRITE_MULTIPLE_REGISTERS,
len(output_value), byte_count
)
This looks wrong to me (defines.READ_WRITE_MULTIPLE_REGISTERS
will always be 23). The code was changed to this in commit dcb0a2f115d7a9d63930c9b4466c4501039880a3; previously it was:
pdu = struct.pack(
">BHHHHB",
function_code, starting_address, quantity_of_x, starting_addressW_FC23,
len(output_value), byte_count
)
This makes more sense to me (you need a way to pass in the address to start writing and the current interface does not seem to provide this). I have added a note re this to the github issue.
So in conclusion your issue is probably due to the device but even if the device supported the command I don't think it would work due to a bug in modbus-tk.