1

I am trying to get data from a modbus device through pymodbus in raspberrypi 3

from pymodbus.client.sync import ModbusSerialClient as ModbusClient

client = ModbusClient(method = 'rtu', port = '/dev/ttyUSB0', baudrate = 115200)
client.connect()

result = client.read_input_registers(0x3100,6,unit=1)
solarVoltage = float(result.registers[0] / 100.0)
solarCurrent = float(result.registers[1] / 100.0)
batteryVoltage = float(result.registers[4] / 100.0)
chargeCurrent = float(result.registers[5] / 100.0)

# Do something with the data

client.close()

The code above is working fine. But i want to get the bits from the information given below

enter image description here

e.g i am trying something like this

result = client.read_input_registers(0x3200,unit=1)

but when i call result.registers the output it shows is 0 but i want to get the Values of D0 to D15. How can i do that? thanks

Hsn
  • 1,168
  • 2
  • 16
  • 39

2 Answers2

2

The description in the documentation tells you how to interpret each bit of your values.
pymodbus's read_input_registers() returns a units16 (unsigned int 2 bytes) for each register (see official documentation), this means that it can be a value between 0 and 65535.

result = client.read_input_registers(0x3200, 2, unit=1)
value1 = result.registers[0] # 33059
value2 = result.registers[1] # 9359

This values can be transformed in binary values:

print format(value1, '016b') # 1000000100100011
print format(value2, '016b') # 0010010010001111

each one of these bit can be indexed from 0 to 15 (from right to left), and then we can split them as described in the documentation:

value1 D3-D0: 0011
Value1 D7-D4: 0010
Value1 D8: 1
Value1 D15: 1

For each subset of bit, the documentation provides us a number in hex value, and each number in hex value can be converted in binary:

D3-D0:
00H (bin: 0000) Normal
01H (bin: 0001) Overvolt
02H (bin: 0010) Undervolt
03H (bin: 0011) Low Volt Disconnect
04H (bin: 0100) OverTemp
and same for other sets...

If the set contains only one bit, we consider True(1)/False(0) behaviour.

Comparing this values with our sets, we understand that 33059 means: Low Volt Disconnect, Low Temp, Battery internal resistance abnormal, Wrong identification for rated voltage (a disaster!) or in your case, 0 means Normal, Normal, Normal, Correct identification for rated voltage.

If we apply the same to value2(9359), we will understand that:

1 D0----|---Standby
1 D1----|---Fault
1 D2----|---|---Equalization
1 D3----|---|
0 D4----|---ok
0 D5----|---//
0 D6----|---//
1 D7----|---The load is short
0 D8----|---ok
0 D9----|---ok
1 D10---|---Input is over current
0 D11---|---ok
0 D12---|---ok
1 D13---|---Charging MOSFET is short
0 D14---|---|---Normal
0 D15---|---|

Obviously you don't want to do all this by hand: even if there are many ways to program this work, i suggest you to use a bitmask:

# Define each mask as a tuple with all the bit at 1 and distance from the right:
D3_D0_mask = (0b1111, 0)
D7_D4_mask = (0b1111, 4)
D8_mask = (0b1, 8)
D15_mask = (0b1, 15)
# compare each mask to the value, after shifting to the right position:
print D3_D0_mask[0]&(value1>>D3_D0_mask[1]) == 4 # False, Fault
print D3_D0_mask[0]&(value1>>D3_D0_mask[1]) == 3 # True, Low Volt Disconnect
print D3_D0_mask[0]&(value1>>D3_D0_mask[1]) == 2 # False, Under Volt
print D3_D0_mask[0]&(value1>>D3_D0_mask[1]) == 1 # False, Overvolt
print D3_D0_mask[0]&(value1>>D3_D0_mask[1]) == 0 # False, Normal

print D7_D4_mask[0]&(value1>>D7_D4_mask[1]) == 2 # True, Low Temp
print D7_D4_mask[0]&(value1>>D7_D4_mask[1]) == 1 # False, Over Temp
print D7_D4_mask[0]&(value1>>D7_D4_mask[1]) == 0 # False, Normal

print D8_mask[0]&(value1>>D8_mask[1]) == 1 # True, Battery internal resistance abnormal

print D15_mask[0]&(value1>>D15_mask[1]) == 1 # True, Wrong identification for rated voltage

The optimisation on this code should be evident.
As you see, anyway, we are getting the output that we expected.

Gsk
  • 2,929
  • 5
  • 22
  • 29
1

I think you are missing the value for number of registers to read. I guess you want to read two registers. So the command would be

result = client.read_input_registers(0x3200, 2, unit=1)
Pal
  • 920
  • 6
  • 6
  • Hi, Pal thanks for the reply. the command you have mentioned is only giving me back [0],[1] which is the same output i get by calling the 3200 and 3201 separately. My question is what does that D0,D1..... means in that case and how to access them? Thanks – Hsn Oct 28 '17 at 19:31
  • 1
    I now understand what you are asking. Yes the values you are getting back are integer values. Split them to bits to get the bit values. In your case if 3200 reads 0, it means D0-D15 are zeroes and if 3201 reads 1, then D0=1, and rest of them D1 - D15 are zeros. – Pal Oct 29 '17 at 18:32
  • 1
    You can convert your integer to binary and then access individual bit range as per your spec.
    `result = "{0:<016b}".format(result)`
    `voltage = int(result[0:3])`
    `batt_temp = int(result[4:7]) `
    `batt_resistance = int(result[8])`
    `error = int(result[15]) `
    – Sanju Oct 30 '17 at 07:27