1

I have attempted many of the fixes related to pymodpoll that exist on this platform. I am able to successfully write to the register but I am unable to read from it, I have confirmed that the register I am attempting to read from is in fact a RW register. Datasheet of the product.

The mod poll command that works properly for reading the register is this modpoll -m rtu -0 -1 -a 1 -b 9600 -p none -r 48 /dev/ttyUSB0

    from pymodbus.client import ModbusSerialClient as modbus
    #Connection to the modbus
    def connect(self) -> None:
        self.modbus = modbus(method='rtu', 
                            port="/dev/ttyUSB0", 
                            baudrate=9600,
                            stopbits=1,
                            bytesize=8,
                            timeout=1)
        self.modbus.connect()

    #function that works properly for setting the register value
    def set_voltage(self, voltage:float =0) -> None:
        if (voltage < self.min_volt) or (voltage > self.max_volt):
            raise modbusError(
                "Invalid voltage provided to the interface, voltage provided: {0}"
                .format(voltage))
        self.modbus.write_register(0x0030, int(voltage*100), unit=1)

    #Read register function
    def get_voltage_target(self) -> float:
        reg =  self.modbus.read_holding_registers(0x0030, 1, unit=1)
        print(reg.registers)

Output:

AttributeError: 'ModbusIOException' object has no attribute 'registers'

I receive this error from the reg.registers print, I am assuming this is just from the fact that I am not connecting properly to the modbus to read the proper registers.

This is the code I am currently working with, thanks for the help!


Simplified version Edit:

from pymodbus.client import ModbusSerialClient as RTUClient
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

PowerSupplyMB = RTUClient(method='rtu', port="/dev/ttyUSB0", baudrate=9600,stopbits=1,bytesize=8,timeout=1)
PowerSupplyMB.connect()
PowerSupplyMB.write_register(0x0030, int(10.2*100), unit=1)
mb_response  = PowerSupplyMB.read_holding_registers(0x0030, 1, unit=1)

if not mb_response.isError():
    '''isError() method implemented in pymodbus 1.4.0 and above'''
    print(mb_response.registers)  # Your problem is here.

else:
    # Do stuff for error handling.
    print('Error message: {}'.format(mb_response))


Output:

DEBUG:pymodbus.logging:Current transaction state - IDLE
DEBUG:pymodbus.logging:Running transaction 1
DEBUG:pymodbus.logging:SEND: 0x0 0x3 0x0 0x30 0x0 0x1 0x85 0xd4
DEBUG:pymodbus.logging:New Transaction state "SENDING"
DEBUG:pymodbus.logging:Changing transaction state from "SENDING" to "WAITING FOR REPLY"
DEBUG:pymodbus.logging:Transaction failed. (Modbus Error: [Invalid Message] No response received, expected at least 4 bytes (0 received))
DEBUG:pymodbus.logging:Frame - [b''] not ready
DEBUG:pymodbus.logging:Getting transaction 0
DEBUG:pymodbus.logging:Changing transaction state from "PROCESSING REPLY" to "TRANSACTION_COMPLETE"
Error message: Modbus Error: [Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 4 bytes (0 received)

Further investigation showed that registers contains an error - Modbus Error: [Input/Output] No Response received from the remote slave/Unable to decode response.

Hos
  • 13
  • 4
  • If you can write to the register with(in) that same script, then the issue is not connecting properly, it's probably something in the code. Is the register you are trying to write to a float? You should read 2 registers then. Which version of PyModbus are you using? Could you please add the application code itself? – Nick S. May 11 '23 at 17:40
  • The version of pymodbus I am using is 3.2.2. The register I am writing to is an int, the def I am using to write to the same register is there (set_voltage). This is all of the application code that is in use at this time, this is simply wrapped into a class, flow of the code is: class creation -> call to connect -> set_voltage(13.2) -> get_voltage_target This should return 1320 (volts are converted to millivolts, which is the int portion) – Hos May 11 '23 at 18:52
  • Okay, I haven't updated to that version yet, but why don't you try this: don't wrap your code into a class from the get-go, simply attempt to read and write in a script first, like the example on the welcome page of PyModbus docs; I'm asking about your application code to simply verify that you are indeed calling the function correctly with the client object. Thus, please post the application code as well, it'll just make it easier for us to help. My bad about the "write" register, I meant "read". Also, 13.2V -> 13,200 mV. – Nick S. May 11 '23 at 19:15
  • 1
    I updated the above with the simplified version of what I was attempting to do, sorry I was misunderstood by the voltage, the power supply is expecting the voltage in thousands as an int and moves the decimal place 2 over. So the explanation above is the correct values just misunderstood connotations on my part (can confirm that the code sets the value correctly visually on the PSU). Thank you for the help! – Hos May 11 '23 at 19:43
  • Awesome, thank you for the edit! What if you change your code to simply `print(registers.registers)`? Do you see a list? Looking at the documentation, the more appropriate thing there would be to try `print(registers.registers.getRegister(0))`. Let me know if any of these work? – Nick S. May 11 '23 at 19:45
  • Same issue for both. Traceback (most recent call last):` File "/opt/scripts/psu/test.py", line 6, in print(registers.registers) AttributeError: 'ModbusIOException' object has no attribute 'registers' – Hos May 11 '23 at 20:01
  • 1
    `read_holding_registers` will be returning an error - print that (see [this question](https://stackoverflow.com/a/52045057/11810946)). – Brits May 11 '23 at 20:04
  • Could you please change your client name to something other than 'modbus'? Maybe RTUclient? `RTUclient = ModbusSerialClient(...). RTUclient.connect(). ... mb_response = RTUclient.read_holding_registers(). print(mb_response.registers). print(mb_response.getRegister(0)),` Formatting in comments suck, but that might help. You could also import the 'ModbusSerialClient' as something else, like `ModbusClient` and leave your client name as 'modbus'. – Nick S. May 11 '23 at 20:08
  • Reply from above @Brits suggestion . Error message: Modbus Error: [Input/Output] No Response received from the remote slave/Unable to decode response – Hos May 11 '23 at 20:08
  • @Hos another thing to try is simply swapping the hardware with which you are connected to the device. Have you tried another RS232/RS485 converter with the same slave/server module? – Nick S. May 11 '23 at 20:14
  • @NickS. The power supply only has a USB connection to it, I can read the register correctly via modpoll, I am not 100% sure what you mean as a lot of this is new to me, are you suggesting changing the system that is sending the connection? OR the method of connection at the power supply end. – Hos May 11 '23 at 20:18
  • @Hos okay, if you are using the same hardware and the same connection with modpoll and you can read it, then it's still probably the software (plus, you can write to the register with PyModbus, so...). Could you make the changes I've mentioned above? Using the same name for 2 different things could be a potential issue here. – Nick S. May 11 '23 at 20:20
  • OK - can you please try performing the read without first performing a write (sometimes a delay is needed between transactions). – Brits May 11 '23 at 20:23
  • @NickS. I have updated the post above with both of your suggestions and added the more verbose outputs from logging. The issue still persists with a only the read command. – Hos May 12 '23 at 13:18
  • @Hos could you try a different name for client? PowerSupplyMB = RTUClient? Also, why did you change the parity to "E"? If the suggestion in this comment still doesn't work, then please go ahead and copy what you have in the post so far to PyModbus's Github discussions page. There is a help section. Add pictures of your hardware setup as well. It's a pain to troubleshoot this in comments and I can't add you to chat, you'll have the maintainer's attention, and it'll be easier. – Nick S. May 12 '23 at 16:08
  • 1
    @NickS. I created the discussion on the pymodpoll page [link](https://github.com/pymodbus-dev/pymodbus/discussions/1538). The parity was just an attempt at figuring out if there was issues with the connection. it attempted it in all forms with and without specifying it and all had same output. – Hos May 12 '23 at 17:48

1 Answers1

2

Your modpoll command includes -a 1 (Slave address = 1) but looking at the debugging info the request is being sent to slave 0 ("0x0 0x3 0x0 0x30 0x0 0x1 0x85 0xd4") so that would explain the lack of response.

From the docs the function definition is read_holding_registers(address: int, count: int = 1, slave: int = 0, **kwargs: Any) → ModbusResponse; in your code you say unit=1 not slave=1... (I did not pick this up because you said the write_register works; it may well do so because you are writing to the broadcast address 0 but you cannot read using that address).

Brits
  • 14,829
  • 2
  • 18
  • 31
  • Ah, snap, I had a mental lapse - I thought since 'unit' is in the same position as 'slave' typically is, it'll take it, but yes, you are correct. For reads, this would require explicitly passing in the server/slave address. That would explain why the writing is, supposedly, working - as far as I remember, you can write but not read on the broadcast address. – Nick S. May 12 '23 at 21:15
  • That makes sense - I've never tried broadcasting (will edit answer to reflect this) – Brits May 12 '23 at 21:22
  • Thank you both for the assistance, its really appreciated! – Hos May 15 '23 at 13:16
  • @Hos you are very welcome! I feel this speaks to Python's best practices - keeping module import names separate from variable names and using correct argument names as well! :) – Nick S. May 16 '23 at 16:45