0
votes

I am trying to use modbus-tk to serially communicate with a device via Modbus RTU over a RS-485 network.

To understand how to use modbus-tk, I am trying to query:

  • Input register: Year of Manufacture
  • Register address: 0x7543 = 30019
  • Device: 1
  • Data type: 16-bit integer
  • Value = 2019 (verified using a free master modbus simulator).

Year of Manufacture Register

Here is the code I use, based primarily on rtumaster_example.py. Input:

#!/usr/bin/env python3


import time
from collections import namedtuple
from logging import Logger

from serial import Serial
from modbus_tk import modbus_rtu
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

ModbusReg = namedtuple("ModbusInputRegister", ["name", "block_type", "address"])

year_of_manuf = ModbusReg(
    "year of manufacture", cst.HOLDING_REGISTERS, 18
)  # 0x7543 = 30019


logger = create_logger(name="console")  # type: Logger
serial_ = Serial(PORT)
modbus_master = modbus_rtu.RtuMaster(serial_)
modbus_master.set_timeout(MODBUS_MASTER_TIMEOUT_SEC)
modbus_master.set_verbose(True)
time.sleep(2)  # Per https://github.com/ljean/modbus-tk/issues/73#issuecomment-284800980

logger.info(
    modbus_master.execute(
        slave=SLAVE_NUM,
        function_code=cst.READ_INPUT_REGISTERS,
        starting_address=year_of_manuf.address,
    )
)

Output:

2020-01-21 10:38:09,031 INFO    modbus_rtu.__init__     MainThread      RtuMaster COM3 is opened
2020-01-21 10:38:11,048 DEBUG   modbus.execute  MainThread      -> 1-4-0-18-0-0-80-15
2020-01-21 10:38:11,077 DEBUG   modbus.execute  MainThread      <- 1-132-3-3-1
---------------------------------------------------------------------------
ModbusError                               Traceback (most recent call last)
<ipython-input-2-9afaebcf3a35> in <module>
      7     slave=SLAVE_NUM,
      8     function_code=cst.READ_INPUT_REGISTERS,
----> 9     starting_address=year_of_manuf.address,
     10 )

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 = 3

It looks like this exception is for if the slave has returned an error

What do you think I am doing wrong? I am new to this library.


What I Have Read

Device Specifics

  • Device: SST Sensing's OXY-LC-485
  • Modbus RTU, 9600/8-N-1
  • User Guide (section 7.1.2.1 contains set of input registers)
  • Device is plugged into Windows machine that I run this Python script

Packages

I am using Python 3.6 on Windows 10.

pyserial==3.4
modbus-tk==1.1.0

**EDIT 1**

Per @Brits comment, I updated my registry addresses to be correct function codes and data frame addresses.

**EDIT 2**

Updated question since I am getting a different error after more correct library usage.

1
Modbus addressing can be a bit confusing. 0x7543 (30019) translates to function code 3 (READ_HOLDING_REGISTERS) register 18 (decimal - data frame address is 0 based) when used with most libraries (have not used modbus-tk but as you pass the function seperatly I strongly suspect this is the issue) More info on wikipedia and this questionBrits
Okay thank you for pointing that out @Brits! I was indeed a bit confused. I have corrected the problem and edited the question. I am still getting the same ModbusInvalidResponseError, any other ideas?Intrastellar Explorer
I was thinking that I am not making a Modbus slave, so I removed that portion from my question. Now I'm getting a ModbusError. See the traceback in question for more detailsIntrastellar Explorer
Exception code 3 is "Illegal Data Value" (meaning something with the request is invalid). Since I have not used modbus-tk its difficult to comment too much but would suggest trying logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 18, 1)) (modified from the example code). I suspect an issue might be that the last param, quantity_of_x, defaults to 0. This param becomes the 'Quantity of Registers' in the modbus request packet; 0 is invalid for that (a request to get no registers).Brits
Hello @Brits! Thank you again for circling back, you were indeed correct!! I read the source close more closely, and I realized it the quantity_of_x parameter should not be left as the default value of 0. I wonder why a default was specified in the library, if it won't ever work.Intrastellar Explorer

1 Answers

0
votes

My problem was a lack of understanding of the Master.execute method. I did not realize that the quantity_of_x parameter needed to be made non-zero. A special thanks to @Brits for helping point out several key problems I had with my original code.

When the arg function_code == defines.READ_INPUT_REGISTERS, quantity_of_x means the amount of H (C type, unsigned short, length = 2-byte = 16-bit, source) to read (source code modbus.py, line 138).

Since the device's data type for the year_of_manuf register is 16-bit unsigned integer, the correct change was to add quantity_of_x=1 as an argument.

Functional Input:

logger.info(
    modbus_master.execute(
        slave=SLAVE_NUM,
        function_code=cst.READ_INPUT_REGISTERS,
        starting_address=year_of_manuf.address,
        quantity_of_x=1,
    )
)

Output:

2020-01-21 18:42:05,520 DEBUG   modbus.execute  MainThread      -> 1-4-0-18-0-1-145-207
2020-01-21 18:42:05,560 DEBUG   modbus.execute  MainThread      <- 1-4-2-7-227-250-137
2020-01-21 18:42:05,562 INFO    <ipython-input-1-2d4d0280e33d>.<module> MainThread      (2019,)