pymodbus-dev / pymodbus

A full modbus protocol written in python
Other
2.27k stars 929 forks source link

Support for single 32bits register in RTU #669

Closed nikito7 closed 2 years ago

nikito7 commented 3 years ago

Versions

Pymodbus Specific

Description

With Modbus TCP registers above 2 bytes work correctly.

My device have single registers with N bytes. Like clock, serial number (count:1; variable size)

But in RTU or rtuovertcp still fails.

Maybe a CRC issue?

Code and Logs

 # code and logs here.
nikito7 commented 3 years ago

Related? https://github.com/riptideio/pymodbus/pull/608 https://github.com/riptideio/pymodbus/pull/511

nikito7 commented 3 years ago

How I can use recvPacket?

#!/usr/bin/env python
"""
Pymodbus Synchronous Client Examples
--------------------------------------------------------------------------

The following is an example of how to use the synchronous modbus client
implementation from pymodbus.

It should be noted that the client can also be used with
the guard construct that is available in python 2.5 and up::

    with ModbusClient('127.0.0.1') as client:
        result = client.read_coils(1,10)
        print result
"""
# --------------------------------------------------------------------------- #
# import the various client implementations
# --------------------------------------------------------------------------- #
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
# from pymodbus.client.sync import ModbusUdpClient as ModbusClient
# from pymodbus.client.sync import ModbusSerialClient as ModbusClient

# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
          '%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

UNIT = 0x1

def run_sync_client():
    # ------------------------------------------------------------------------#
    # choose the client you want
    # ------------------------------------------------------------------------#
    # make sure to start an implementation to hit against. For this
    # you can use an existing device, the reference implementation in the tools
    # directory, or start a pymodbus server.
    #
    # If you use the UDP or TCP clients, you can override the framer being used
    # to use a custom implementation (say RTU over TCP). By default they use
    # the socket framer::
    #
    #    client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer)
    #
    # It should be noted that you can supply an ipv4 or an ipv6 host address
    # for both the UDP and TCP clients.
    #
    # There are also other options that can be set on the client that controls
    # how transactions are performed. The current ones are:
    #
    # * retries - Specify how many retries to allow per transaction (default=3)
    # * retry_on_empty - Is an empty response a retry (default = False)
    # * source_address - Specifies the TCP source address to bind to
    # * strict - Applicable only for Modbus RTU clients.
    #            Adheres to modbus protocol for timing restrictions
    #            (default = True).
    #            Setting this to False would disable the inter char timeout
    #            restriction (t1.5) for Modbus RTU
    #
    #
    # Here is an example of using these options::
    #
    #    client = ModbusClient('localhost', retries=3, retry_on_empty=True)
    # ------------------------------------------------------------------------#

    from pymodbus.transaction import ModbusRtuFramer
    client = ModbusClient('10.1.0.40', port=8888, framer=ModbusRtuFramer, strict=True)
    client.connect()

    log.debug("Read input registers")
    rr = client.read_input_registers(1, 1, unit=UNIT)
    assert(not rr.isError())     # test that we are not an error

    client.close()

if __name__ == "__main__":
    run_sync_client()

Rtuovertcp bridge:

14:40:57.174 TCP: to MCU/1: 010400010001600A
14:40:57.267 TCP: from MCU: 01040C07E50A02060E270E00003C800F6F

Script above:

(venv) user@vbox:~$ ./rtuovertcp.py
2021-10-02 14:40:57,852 MainThread      DEBUG    sync           :215      Connection to Modbus server established. Socket ('10.1.0.51', 36175)
2021-10-02 14:40:57,854 MainThread      DEBUG    rtuovertcp     :75       Read input registers
2021-10-02 14:40:57,855 MainThread      DEBUG    transaction    :139      Current transaction state - IDLE
2021-10-02 14:40:57,855 MainThread      DEBUG    transaction    :144      Running transaction 1
2021-10-02 14:40:57,856 MainThread      DEBUG    transaction    :273      SEND: 0x1 0x4 0x0 0x1 0x0 0x1 0x60 0xa
2021-10-02 14:40:57,857 MainThread      DEBUG    sync           :76       New Transaction state 'SENDING'
2021-10-02 14:40:57,858 MainThread      DEBUG    transaction    :287      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2021-10-02 14:40:57,979 MainThread      DEBUG    transaction    :375      Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2021-10-02 14:40:57,980 MainThread      DEBUG    transaction    :297      RECV: 0x1 0x4 0xc 0x7 0xe5 0xa 0x2
2021-10-02 14:40:57,980 MainThread      DEBUG    rtu_framer     :240      Frame - [b'\x01\x04\x0c\x07\xe5\n\x02'] not ready
2021-10-02 14:40:57,981 MainThread      DEBUG    transaction    :465      Getting transaction 1
2021-10-02 14:40:57,981 MainThread      DEBUG    transaction    :224      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Traceback (most recent call last):
  File "/home/user/./rtuovertcp.py", line 82, in <module>
    run_sync_client()
  File "/home/user/./rtuovertcp.py", line 77, in run_sync_client
    assert(not rr.isError())     # test that we are not an error
AssertionError
nikito7 commented 3 years ago

Work in TCP

(venv) user@vbox:~$ ./tcp.py
2021-10-02 14:48:16,870 MainThread      DEBUG    sync           :215      Connection to Modbus server established. Socket ('10.1.0.51', 53761)
2021-10-02 14:48:16,871 MainThread      DEBUG    tcp            :80       Read input registers
2021-10-02 14:48:16,872 MainThread      DEBUG    transaction    :139      Current transaction state - IDLE
2021-10-02 14:48:16,873 MainThread      DEBUG    transaction    :144      Running transaction 1
2021-10-02 14:48:16,874 MainThread      DEBUG    transaction    :273      SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x4 0x0 0x1 0x0 0x1
2021-10-02 14:48:16,875 MainThread      DEBUG    sync           :76       New Transaction state 'SENDING'
2021-10-02 14:48:16,876 MainThread      DEBUG    transaction    :287      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2021-10-02 14:48:17,086 MainThread      DEBUG    transaction    :375      Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2021-10-02 14:48:17,087 MainThread      DEBUG    transaction    :297      RECV: 0x0 0x1 0x0 0x0 0x0 0xf 0x1 0x4 0xc 0x7 0xe5 0xa 0x2 0x6 0xe 0x30 0xf 0xff 0x80 0x0 0x80
2021-10-02 14:48:17,087 MainThread      DEBUG    socket_framer  :147      Processing: 0x0 0x1 0x0 0x0 0x0 0xf 0x1 0x4 0xc 0x7 0xe5 0xa 0x2 0x6 0xe 0x30 0xf 0xff 0x80 0x0 0x80
2021-10-02 14:48:17,088 MainThread      DEBUG    factory        :266      Factory Response[ReadInputRegistersResponse: 4]
2021-10-02 14:48:17,088 MainThread      DEBUG    transaction    :454      Adding transaction 1
2021-10-02 14:48:17,089 MainThread      DEBUG    transaction    :465      Getting transaction 1
2021-10-02 14:48:17,089 MainThread      DEBUG    transaction    :224      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
janiversen commented 2 years ago

Just to be sure, Home assistant modbus integration do not have this problem! @nikito7 have this problem with the modbus configuration because he wants to do something that do not adhere to the modbus standard.

nikito7 commented 2 years ago

2006 standard!!!

Most implementations had fixes or tweaks for single register 4bytes or more.

My device have uint32 in a single register !!

janiversen commented 2 years ago

β€œmost” is really grasping for air, lets see some numbers to support your claim ! please do not forget that the modbus organization consist of the biggest modbus companies so if they want it, it would at least be in a draft spec.

Feel free to point at a newer standard where register_size is variable and I will be happy to support your claim.

nikito7 commented 2 years ago

Some users still use usb rtu

The problem is in rtuframer

4 bytes is non standard, but works with Modbus TCP

 2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] Current transaction state - IDLE
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] Running transaction 1
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] SEND: 0x1 0x4 0x0 0x28 0x0 0x1 0xb1 0xc2
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.client.sync] New Transaction state 'SENDING'
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] RECV: 0x1 0x4 0x4 0x0 0x4b 0xa0 0xd7
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.framer.rtu_framer] Frame - [b'\x01\x04\x04\x00K\xa0\xd7'] not ready
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] Getting transaction 1
2022-03-26 10:36:25 DEBUG (SyncWorker_5) [pymodbus.transaction] Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
nikito7 commented 2 years ago

RECV: 0x1 0x4 0x4 0x0 0x4b 0xa0 0xd7

pymodbus.framer.rtu_framer] Frame - [b'\x01\x04\x04\x00K\xa0\xd7'] not ready

This is strange

x00K?

janiversen commented 2 years ago

your system delivers an incomplete frame.

nikito7 commented 2 years ago

Only misses CRC but it's normal in pymodbus I think

RECV is correct without crc.

Framer processing not

I suppose that is hardcoded 2 bytes

But it works in Modbus TCP....

Should be easy to fix, but I don't understand the code πŸ˜†

dhoomakethu commented 2 years ago

@nikito7 I do not understand this

Only misses CRC but it's normal in pymodbus I think

RECV is correct without crc. and I suppose that is hardcoded 2 bytes

What exactly are we talking about here ? The CRC is calculated and a missing CRC is considered as an invalid frame. You can try add some more delay to see if all the bytes are received.

nikito7 commented 2 years ago

Makes sense. It's this?

How dynamically configure?

# * strict - Applicable only for Modbus RTU clients.
    #            Adheres to modbus protocol for timing restrictions
    #            (default = True).
    #            Setting this to False would disable the inter char timeout
    #            restriction (t1.5) for Modbus RTU

Maybe need change code?

Because pymodbus will expect only a two bytes register data...


Anyway

Modbus TCP don't have CRC. How pymodbus known the number of bytes to read?

Because all work. Even a special 12 bytes register...


So RTU will need some dynamically expected size...

Forgetting big anormal registers. I only want try to read a single 32bits register (4 bytes)

nikito7 commented 2 years ago

Modpoll for example have a option for it:

Use Daniel/Enron single register 32-bit mode

nikito7 commented 2 years ago

@janiversen

Just to be sure, Home assistant modbus integration do not have this problem! @nikito7 have this problem with the modbus configuration because he wants to do something that do not adhere to the modbus standard.

Tell it to all EDP clients in Portugal. πŸ˜†

Tasmota work. ESPHome work. Modpoll work. Home Assistant (TCP) work.

Why shouldn't pymodbus RTU support 32bits too...

janiversen commented 2 years ago

because it is non-standard and if tcp works then you have a solution. If you want a non-standard solution the best way is to fork this repo and make your changes locally.

btw this is not a question of crc, it is simply your device sends an unexpected structure (1 register is 4 bytes not 2).

dhoomakethu commented 2 years ago

@nikito7 it is good to have and I am willing to accept any PR's if you are willing to work on it. This is something not really required by the majority of the users and being the only developer who is working on this project at my free time, it is quite difficult to accomodate this feature request with so many items pending in the backlog.

nikito7 commented 2 years ago
(pymodbus) D:\[ssn]\[dev]\pymodbus>py rtu.py
2022-03-29 05:39:12,667 MainThread      DEBUG    sync           :215      Connection to Modbus server established. Socket ('10.1.0.71', 54760)
2022-03-29 05:39:12,667 MainThread      DEBUG    rtu            :75       Read input registers
2022-03-29 05:39:12,669 MainThread      DEBUG    transaction    :139      Current transaction state - IDLE
2022-03-29 05:39:12,670 MainThread      DEBUG    transaction    :144      Running transaction 1
2022-03-29 05:39:12,671 MainThread      DEBUG    transaction    :273      SEND: 0x1 0x4 0x0 0x1 0x0 0x1 0x60 0xa
2022-03-29 05:39:12,672 MainThread      DEBUG    sync           :76       New Transaction state 'SENDING'
2022-03-29 05:39:12,673 MainThread      DEBUG    transaction    :287      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2022-03-29 05:39:12,788 MainThread      DEBUG    transaction    :375      Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2022-03-29 05:39:12,789 MainThread      DEBUG    transaction    :297      RECV: 0x1 0x4 0xc 0x7 0xe6 0x3 0x1d 0x2 0x5 0x27 0x9 0x0 0x0 0x3c 0x80 0x52 0xc7
2022-03-29 05:39:12,791 MainThread      DEBUG    rtu_framer     :185      Getting Frame - 0x4 0xc 0x7 0xe6 0x3 0x1d 0x2 0x5 0x27 0x9 0x0 0x0 0x3c 0x80
2022-03-29 05:39:12,792 MainThread      DEBUG    factory        :266      Factory Response[ReadInputRegistersResponse: 4]
2022-03-29 05:39:12,793 MainThread      DEBUG    rtu_framer     :107      Frame advanced, resetting header!!
2022-03-29 05:39:12,793 MainThread      DEBUG    transaction    :454      Adding transaction 1
2022-03-29 05:39:12,794 MainThread      DEBUG    transaction    :465      Getting transaction 1
2022-03-29 05:39:12,795 MainThread      DEBUG    transaction    :224      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'

Not really a solution but worked. Make that dynamic will be hard. And maybe not the correct code to change πŸ˜†

Just a test anyway Single 12 bytes register: clock/date

20220329_054739

ruimarinho commented 2 years ago

I've just hit exactly this problem as I was using pymodbus via https://github.com/gavinying/modpoll. I've been reading about this issue and it looks like Enron or Enron/Daniels Modbus is standard modbus with a few vendor extensions.

I see that @rahulraghu94 published his Daniel/Enron Modbus work under https://github.com/u9n/enron-modbus if anyone's interested, although it would be great to see this incorporated under pymodbus especially because of the ripple effect through Home Assistant and so other many open source projects that rely on this library.