0
votes

I am trying to use Python (PyCharm) to read a register on a modbus device. I have confirmed the COM port, Baud rate and other communication settings and I can use the devices application to read the value (it is a water level logger). I am getting no response from the instrument.

Register is readable in mbpoll using -

mbpoll -B -m RTU  -t 4:float -a 1 -b 19200 -r 46 -c 2 /dev/ttyUSB0

enter image description here (Address different as running on Pi not PC)

And MBPOLL - enter image description here enter image description here enter image description here

My code is as follows -

import minimalmodbus
import serial

instrument = minimalmodbus.Instrument('COM5', 1)  # port name, slave address (in decimal)
instrument.serial.port = 'COM5'                     # this is the serial port name
instrument.serial.baudrate = 19200         # Baud
instrument.serial.bytesize = 8
instrument.serial.parity   = serial.PARITY_EVEN
instrument.serial.stopbits = 1
instrument.serial.timeout  = 3          # seconds
instrument.address = 1                         # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU   # rtu or ascii mode
instrument.clear_buffers_before_each_transaction = True

temperature = instrument.read_float(registeraddress=40046, functioncode=3, number_of_registers=2, byteorder=0)  # Registernumber, number of decimals
print(temperature)
 

Error Received - enter image description here

import minimalmodbus
import serial

instrument = minimalmodbus.Instrument('COM5', 1)  # port name, slave address (in decimal)
instrument.serial.port = 'COM5'                     # this is the serial port name
instrument.serial.baudrate = 19200         # Baud
instrument.serial.bytesize = 8
instrument.serial.parity   = serial.PARITY_EVEN
instrument.serial.stopbits = 1
instrument.serial.timeout  = 0.1        # seconds
instrument.address = 1                         # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU   # rtu or ascii mode
#nstrument.clear_buffers_before_each_transaction = True

temperature = instrument.read_float(registeraddress=45, functioncode=4, number_of_registers=2, byteorder=0)  # Registernumber, number of decimals

try:
    print(temperature)
except:
    print(temperature)

Edit to include a try - except

Any help appreciated!

EDIT: Link to device manual - https://in-situ.com/en/pub/media/support/documents/Modbus_Manual.pdf Device is a Level Troll 400 Connected to PC via manufactures cable

EDIT 2: I have tried to incorporate minimal modbus structure but to no avail.

EDIT 3: I am able to read a register using Modbus Poll. Register is 40046, so I understand this to be register 45 of the holding registers? How do I translate this to minimalmodbus?

EDIT 4: I am not married to minimal modbus - I am happy to use any tool to get this done

EDIT 5: I have also tried depth = instrument.read_long(x, x) with different values

2
Please provide a link to the device manual.Tagli
19600 is an unusual baud rate; did you mean 9600 or 19200? (looks like 19200 is the default).Brits
What is the slave address? 2 or 1? minimalmodbus.Instrument('COM5', 2) # portname, slaveaddr vs. instrument.address = 1VPfB
Hopefully all requested edits made!Joshua Patterson

2 Answers

2
votes

Just updating with the solution if anyone happens to stumble upon this and has the same problem. As semi-alluded to in other suggestions the device I am connecting to has a 'sleep' period or the like and needs to be polled once, unsuccessfully, before successfully returning values for any subsequent polls. I apologies for my armature code but the solution that works for me is the below -

import minimalmodbus
import serial

instrument = minimalmodbus.Instrument('COM5', 1)  # port name, slave address (in decimal)
instrument.serial.port = 'COM5'                     # this is the serial port name
instrument.serial.baudrate = 19200         # Baud
instrument.serial.bytesize = 8
instrument.serial.parity   = serial.PARITY_EVEN
instrument.serial.stopbits = 1
instrument.serial.timeout  = 1        # seconds
instrument.address = 1                         # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU   # rtu or ascii mode
#nstrument.clear_buffers_before_each_transaction = True
try:
    temperature = instrument.read_long(registeraddress=9001, functioncode=3,
                                        byteorder=0)  # Registernumber, number of decimals

    print(temperature)


except:
    pass

try:
    temperature = instrument.read_long(registeraddress=9001, functioncode=3,
                                        byteorder=0)  # Registernumber, number of decimals

    print(temperature)


except:
    pass

1
votes

The device manual isn't clear about the register start address, but the first register it mentions has the address of 1.

Similarly, the mbpoll command-line utility (not the one with GUI) isn't very clear about the start address. But its documentation mentions that the default value for -r parameter is 1.

I think it's safe to assume that both use the same addressing which starts from 1, as the command-line tool has no problems accessing the value.

But MinimalModbus API clearly mentions that its register start address is 0. So when using this library, you need to use registeraddress = 45 for accessing the temperature, not 46 or 40046.

But why won't 46 work? Normally, one would expect it to grab data starting from the next register and print some garbage, but not timeout. But we can't know how the device works internally. Maybe a request to access the temperature register actually triggers some measurement function and then returns a value. A request to access an unaligned data (with a wrong register value) can be simply rejected by the firmware.

If you still get timeouts with registeraddress = 45, your Python runtime may have some problems accessing the serial port. As I stated in my comment, I recommend using a logic analyzer to see what's going on on the wire. Without such a tool, you're doing blind-debugging.