Skip to content

Conversation

@wooni005
Copy link
Contributor

Hello,
First I would like to thank you for this great modbus library, which works like a charm. But maybe you can consider this change and make the Modbus RTU functionality even faster.

We are currently using pymodbus on our project reading data from an inverter with 9600 baud. We read 7 register blocks every second. We noticed that reading a 1 register is taking around 103ms, but it should be a lot faster at 9600 baud.
After some searching I found a time.sleep(self._recv_interval) at row 257 of the serial.py which causes the delay. It is mostly executed twice, and is filled with 0.05 (50ms) which explains the 100ms delay.

In the past the time.sleep was 10ms and we found that the _recv_interval was introduced in commit:
8af46d8
We've tested the changes with reading different sizes of register blocks and didn't noticed any issue.

There are 2 small changes in this pull request:

1 - Calculating the duration of 1 byte
The calculation of duration of reading 1 byte in serial.py row 144. Originally there was 1 + 8 + 2, which I presume is a start bit, 8 databits and 2 stopbits. For the new calculation I took the bytesize and number of stopbits from the arguments of the class init.

2 - Calculating the receive interval
I don't know the background of the formula for calculating the _recv_interval value, but it is returning these values for the various baud rates:
230400 -> 10ms
115200 -> 20ms
57600 -> 30ms
38400 -> 40ms
19200 -> 50ms
9600 -> 50ms
4800 -> 50ms

The _recv_interval is only used for sleeping while waiting for the reading ready signal and the while loop is always looping 2 times (didn't investigate why), but that gives our delay of 100ms. If you compare the 100ms to the time of reading a 16 bits register at 19200 baud, which is 0.104ms. In the 100ms time, it's possible to read 961 registers.
In my adapted formula the waiting times will be like this:
230400 -> 1ms
115200 -> 1ms
57600 -> 1ms
38400 -> 2ms
19200 -> 3ms
9600 -> 6ms
4800 -> 13ms
Basically there will be sampled every 4 bytes / 2 registers if the reading is ready or not.
I think that the waiting time does not have to be limited to 50ms, but I had a minimum of 1ms.

Testing

Used test code:

from pymodbus.client import ModbusSerialClient
import datetime

# import logging
# logging.basicConfig()
# log = logging.getLogger()
# log.setLevel(logging.DEBUG)

# prepare connection
tty_dev = "/dev/ttyUSB0"
print("tty_dev:", tty_dev, "\n")
client = ModbusSerialClient(port = tty_dev, stopbits = 1, bytesize = 8, parity = 'N', baudrate = 9600, timeout = 2.0)
unit_id = 247

connection = client.connect()

readStart = datetime.datetime.now()
retValue = client.read_holding_registers(11000, 2, slave = unit_id)
readStop = datetime.datetime.now()

print(f"Read: {retValue}")

readDelta = (readStop.microsecond - readStart.microsecond) / 1000
print("Reading registers time: %f ms" % readDelta)

Before the changes, reading 2 registers:

tty_dev: /dev/ttyUSB0

Read: ReadHoldingRegistersResponse (2)
Reading registers time: 102.040000 ms

After the changes, reading 2 registers:

tty_dev: /dev/ttyUSB0

Read: ReadHoldingRegistersResponse (2)
Reading registers time: 35.949000 ms

@janiversen
Copy link
Collaborator

I will review your changes later, however you should consider using async, it is a lot faster please see our performance example.

Copy link
Collaborator

@janiversen janiversen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

@wooni005
Copy link
Contributor Author

Thank you too!

@janiversen janiversen merged commit a61ab08 into pymodbus-dev:dev Oct 12, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants