pymodbus-dev / pymodbus

A full modbus protocol written in python
Other
2.22k stars 907 forks source link

modbus request starting address off by one #1909

Closed ickedude closed 8 months ago

ickedude commented 8 months ago

Versions

Pymodbus Specific

Description

It seems that modbus stores data starting at the wrong address: off-by-error!

Code and Logs

#!/usr/bin/env python3

import asyncio

from pymodbus import __version__ as pymodbus_version
from pymodbus import pymodbus_apply_logging_config
from pymodbus.datastore.store import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext
from pymodbus.datastore import ModbusServerContext
from pymodbus.server import StartAsyncTcpServer
from pymodbus.framer.socket_framer import ModbusSocketFramer

async def run_modbus_server():
    ds = ModbusSequentialDataBlock(0, [0]*100)
    slave_context = ModbusSlaveContext(di=ds, co=ds, hr=ds, ir=ds)
    server_context = ModbusServerContext(slaves=slave_context, single=True)
    server = await StartAsyncTcpServer(
        context=server_context,
        address=('192.168.10.10', 5020),
        framer=ModbusSocketFramer,
    )
    return server

async def main() -> None:
    pymodbus_apply_logging_config('DEBUG')
    server = asyncio.create_task(run_modbus_server())
    await server

if __name__ == "__main__":
    asyncio.run(main(), debug=True)

First example: Inspecting the below logged package starting address bytes (0) and compare to the logged setValue address (1).

0xa5 0x36 0x0 0x0 0x0 0x3f 0xff 0x10 0x0 0x0 0x0 0x1c
 ^    ^    ^   ^   ^   ^    ^    ^    ^   ^   ^___^___ Quantity of Registers = 28
 |    |    |   |   |   |    |    |    |___|___ Starting Address = 0
 |    |    |   |   |   |    |    |___ Function Code
 |    |    |   |   |   |    |___ Unit Identifier
 |    |    |   |   |___|___Length
 |    |    |___|___ Protocol Identifier
 |____|___ Transaction Identifier
2023-12-08 08:13:52,798 DEBUG logging:102 Awaiting connections server_listener
2023-12-08 08:13:52,799 INFO  logging:96 Server listening.
2023-12-08 08:13:52,925 DEBUG logging:102 Connected to server
2023-12-08 08:13:52,926 DEBUG logging:102 recv: 0xa5 0x36 0x0 0x0 0x0 0x3f 0xff 0x10 0x0 0x0 0x0 0x1c 0x38 0x0 0x0 0x15 0xc2 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0xd 0xbc 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x19 0xbb 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x3 0x4e 0x0 0x0 0xc3 0x34 old_data:  addr=None
2023-12-08 08:13:52,927 DEBUG logging:102 Handling data: 0xa5 0x36 0x0 0x0 0x0 0x3f 0xff 0x10 0x0 0x0 0x0 0x1c 0x38 0x0 0x0 0x15 0xc2 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0xd 0xbc 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x19 0xbb 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x3 0x4e 0x0 0x0 0xc3 0x34
2023-12-08 08:13:52,927 DEBUG logging:102 Processing: 0xa5 0x36 0x0 0x0 0x0 0x3f 0xff 0x10 0x0 0x0 0x0 0x1c 0x38 0x0 0x0 0x15 0xc2 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0xd 0xbc 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x19 0xbb 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x3 0x4e 0x0 0x0 0xc3 0x34
2023-12-08 08:13:52,927 DEBUG logging:102 Factory Request[WriteMultipleRegistersRequest': 16]
2023-12-08 08:13:52,927 DEBUG logging:102 validate: fc-[16] address-1: count-28
2023-12-08 08:13:52,927 DEBUG logging:102 setValues[16] address-1: count-28
2023-12-08 08:13:52,928 DEBUG logging:102 send: 0xa5 0x36 0x0 0x0 0x0 0x6 0xff 0x10 0x0 0x0 0x0 0x1c

Second example: starting address should be 2, but logged setValue address is 3 The strange thing here is that the client reports a register count of zero, wh

0x0 0x0 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x2 0x0 0x0
 ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^___^___ Quantity of Registers = 0
 |   |   |   |   |   |   |   |   |___|___ Starting Address = 2
 |   |   |   |   |   |   |   |___ Function Code
 |   |   |   |   |   |   |___ Unit Identifier
 |   |   |   |   |___|___Length
 |   |   |___|___ Protocol Identifier
 |___|___ Transaction Identifier
2023-12-08 08:30:32,322 DEBUG logging:102 Connected to server
2023-12-08 08:30:32,323 DEBUG logging:102 recv: 0x0 0x0 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x2 0x0 0x0 old_data:  addr=None
2023-12-08 08:30:32,323 DEBUG logging:102 Handling data: 0x0 0x0 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x2 0x0 0x0
2023-12-08 08:30:32,323 DEBUG logging:102 Processing: 0x0 0x0 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x2 0x0 0x0
2023-12-08 08:30:32,323 DEBUG logging:102 Factory Request[WriteSingleRegisterRequest': 6]
2023-12-08 08:30:32,324 DEBUG logging:102 validate: fc-[6] address-3: count-1
2023-12-08 08:30:32,324 DEBUG logging:102 setValues[6] address-3: count-1
2023-12-08 08:30:32,324 DEBUG logging:102 getValues: fc-[6] address-3: count-1
2023-12-08 08:30:32,324 DEBUG logging:102 send: 0x0 0x0 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x2 0x0 0x0
janiversen commented 8 months ago

In the first example, there are several client errors:

0xff 0x10 0x0 0x0 0x0 0x1c 0x38

the slave is ff, but you have not defined that slave in your datastore. the function is 0x10, which do not exist. the starting address is 0x0, 0x0 which is normally not allowed the number of reqisters requested is HUGE 0x1c, 0x38

So any response at all is a surprise.

janiversen commented 8 months ago

In the second example you use function code 0x06 which is "write single register" so there are no count....you set the value to 0x00, 0x00

I think you need to read up on the modbus protocol.

Regarding the offset of 1, that is very common on modbus devices, the modbus protocol specification do NOT make any assumption on how data is stored, so an offset of e.g. 17 would be just as legal. For a larger explanation please see the datastore simulator documentation, that contains a couple of drawing explaining the difference between the address in the request and in memory.

janiversen commented 8 months ago

Closing this, as it seems more to be misunderstanding than a bug.

janiversen commented 8 months ago

Sorry function 0x10 exist it is "write multiple registers" but you missed the "byte count" byte, which is just after the quantity.

The comments about addressing is still valid, address= 0x0, 0x0 on the network is not normal.

ickedude commented 8 months ago

First of all thank you for the quick responses. You are totally right, it is not an issue and more information can be found in commit 741d6f0 as well as the mentioned specifaction section 4.4. MODBUS Addressing model.

The error was just to follow the documentation of the device vendor. So if the vendor is talking about the device is writing to register zero, than my assumption was to that I have to read from register zero and not from one. For easy of mapping in your mind you can use zero_mode = True for the slave context and follow the vendor documentation without off-by-one errors in your head.

janiversen commented 8 months ago

Actually that parameter is on its way to the graveyard...I am not sure it still works.

ickedude commented 8 months ago

From what I have tested yet it works perfectly fine, as it just does the +1 in getValues, setValues and validate of the slave context

janiversen commented 8 months ago

Justas it is expected to work!