question

monokristallin avatar image
monokristallin asked

Need help with python modbus queery

Hello.

I have updated to pymodbus 3.1.3 and have now dificulties to read VenusOS data, other than from the com.victronenergy.system (Id=100)

My Solar Cgargers have the IDs 238 and 239 and show up like this in the Modbus Page.

With the code below I demand Voltage and SOC from ID 100, but when refering to ID 238, an error message appears.

This is the output of the code below:

Battery Voltage from System 100:
263
SOC:
72
Error: Exception Response(132, 4, GatewayPathUnavailable).

In the modbus section of Venus OS I read:

"Error processing function code 4, unit id 0, start address 789, quantity 1, src 192.168.178.41: error finding service with device type solar charger at device instance 0"

So, somehow I am doing something wrong, adressing the correct ID, I guess.

Does anybody know, how to change the code to make it work?

Thanks already in advance.


Here is the Python3 code:


from pymodbus.constants import Endian
from pymodbus.client import ModbusTcpClient
from pymodbus.payload import BinaryPayloadDecoder

client = ModbusTcpClient('192.168.178.50', port=502, timeout=3, unit_id=100)
result = client.read_input_registers(840, 1)

if not result.isError():
    decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big)
    voltage = decoder.decode_16bit_uint()
    print("Battery Voltage from System 100:")
    print(voltage)
else:
    print("Error:", result)


result = client.read_input_registers(843, 1)    
if not result.isError():
    decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big)
    SOC = decoder.decode_16bit_uint()
    print("SOC:")
    print(SOC)
else:
    print("Error:", result)

   
   
client = ModbusTcpClient('192.168.178.50', port=502, timeout=3, unit_id=238)
result = client.read_input_registers(789, 1)


if not result.isError():
    decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big)
    voltage = decoder.decode_16bit_uint()
    print("PV Power from Solar Charger:")
    print(voltage)
else:
    print("Error:", result)


Modbus TCP
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

8 Answers
matt1309 avatar image
matt1309 answered ·

Hi @Monokristallin

You've probably already seen but just in case https://www.victronenergy.com/live/ccgx:modbustcp_faq

That error GatewayPathUnavailable is described in the link as due to incorrect Device ID or unit ID. I can see you've got the unit ID from the excel and guessing you've already double checked the device ID is correct in Modbus TCP menu on venus os.


My only other guess is maybe see what happens if you use read_holding_registers instead of input. Victron page says there's no difference between these functions. I only say that because when setting up openhab for modbus TCP i set mine to holding and had no issues. Bit of a long shot tbh but if you've tried everything else...

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

monokristallin avatar image
monokristallin answered ·

Hi Matt.

Thanks for the faq. The funny thing is, that I got all of this working on an older raspberry system. Now, setting it up on a new Raspi-OS, it doesn't work anymore. I can only access ID0. The code I used before did not work at all, anymore. This is due to the upgrade to pymodbus version 3.1.3.

Somehow this version does not seem to get the ID number through to VenusOS or I am doing something wrong, addressing it. Me and my new buddy ChatGPT are at the end of our knowledge. :-/

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image
matt1309 answered ·

Hi @Monokristallin

Really odd. I'm not very familiar with python or this library (are they libraries in python or packages?)

The modbus error reads like the unit ID is not being sent correctly to victron system.

"Error processing function code 4, unit id 0, start address 789, quantity 1, src 192.168.178.41: error finding service with device type solar charger at device instance 0"

However you've already confirmed you're sending the correct ID.


This is unlikely but my last guess. Just to make sure nothing funky is happening in memory could you try renaming the second client and results to maybe client2 and results2.

Maybe client that is open for Unit ID 100 is still open or at least not fully cleared out and therefore not being fully overridden when you override it for Solar charge controller data. Making a new client aka client2 and results2 might fix the issues of Unit ID not being sent correctly.


After a quick google I think client.close() might do the same thing as the above however to play it completely safe i'd go with client2 and results2.

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

monokristallin avatar image
monokristallin answered ·

Hi Matt.

Thanks again. Your idea was good. However, it did not impress the system. ID0 and ID100 are getting interpretated the same way by VenusOS, by the way. This is why I still can access the system values, using ID100. It gets turned to 0 as any other ID. But everything other than that is not recocnized.

So, my workaround is now ti get somehow back to pymodbus 2.5.3, which is, according to githib, made for Python <= 3.7.

I'll comment on it when (or if) I have succedded...

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

monokristallin avatar image
monokristallin answered ·

Okay,

if anyone is interested how I solved the problem, here is the (weird) solution.

Actually it is a workaround, not really a solution.

I went back to an older pi image:

https://downloads.raspberrypi.org/raspios_full_armhf/images/raspios_full_armhf-2020-05-28/

I have set up my software and installed

pip3 install pymodbus.

This did not work, I always got th error message:

ModuleNotFoundError: No module named 'pymodbus'

So, I deinstalled it again and downloaded

https://github.com/pymodbus-dev/pymodbus/archive/refs/tags/v2.5.3.zip

from

https://pypi.org/project/pymodbus/

and unzipped it next to my python code.

Here is my Python 3.7.3 code:


from pymodbus.constants import Defaults
from pymodbus.constants import Endian
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.payload import BinaryPayloadDecoder
 
Defaults.Timeout = 25
Defaults.Retries = 20
client = ModbusClient('192.168.178.50', port='502')
result= client.read_input_registers(850,1,unit=100)
decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big)
voltage=decoder.decode_16bit_uint()

print(voltage)
print("--")

Defaults.Timeout = 25
Defaults.Retries = 20
client = ModbusClient('192.168.178.50', port='502')
result= client.read_input_registers(855,1,unit=100)
decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big)
voltage=decoder.decode_16bit_uint()


print(voltage)
print("--")

print(voltage)
print("--")
result= client.read_input_registers(789,1,unit=238)
decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big)
voltage=int(decoder.decode_16bit_uint()/10)
print(voltage)
print("--")


result= client.read_input_registers(771,1,unit=238)
decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big)
battvoltage=(decoder.decode_16bit_uint()/100)
print(battvoltage)
print("--")


That what it gives me back:

515
--
0
--
0
--
410
--
26.67
--


So, I am back alive.

However, if anyone konws what the error about later modbus versions is: any answer is appreciated as I am now stuck to an old raspi OS.



2 comments
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

I had my hopes up that the reusing of client was the issue. Can't help but notice that In this python 2 version you haven't included unit in the client unlike python 3.


Could that not be the issue in python 3 version? As the python 2 code above you're not redefining client because you don't need to as it isn't looking at a specific unit id.

As though client has access to all unit Ids and then reads specific one when you specify it when you call read_input_registers?

0 Likes 0 ·
Show more comments
tenerion avatar image
tenerion answered ·

same problem today for me with update from pymodbus 2.5.3 to 3.3.1

result = client.read_input_registers(register, 2, unit = _unit) # GatewayPathUnavailable

no error with this code:

result = client.read_input_registers(register, 2, _unit)


additional info:

write_register(33,3,int(238)) # Same behavior - third parameter must not contain "unit".


ps. sorry for my english ;)

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

monokristallin avatar image
monokristallin answered ·

I recently came up with another solution on how to read out the SOC in a very simple way. It's in this post, which is actually a question on how to set the SOC:

https://community.victronenergy.com/questions/257704/how-to-set-soc-preferable-in-node-red-to-a-daly-bm.html

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

elvis avatar image
elvis answered ·

Here is a working modbus script for the newer versions of pymodbus (tested on pymodbus 3.6.3) and venus 3.20~40. pip install pymodbus

subtle changes are.

the import is "from pymodbus.client" and not "from pymodbus.client.sync"

unit is no longer a valid parameter, change to slave.

use .BIG instead of .Big

#!/usr/bin/env python3

from pymodbus.constants import Endian
from pymodbus.client import ModbusTcpClient as ModbusClient
from pymodbus.payload import BinaryPayloadDecoder

BmvID = 223

ip = "192.168.20.156"

client = ModbusClient(ip, port='502')

def modbus_register(address, slave):
    msg     = client.read_holding_registers(address, slave=slave)
    decoder = BinaryPayloadDecoder.fromRegisters(msg.registers, byteorder=Endian.BIG)
    msg     = decoder.decode_16bit_int()
    return msg

BatterySOC  = modbus_register(266, BmvID) / 10

print(f"BatterySOC {BatterySOC}")
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

Related Resources