brainelectronics / micropython-modbus

MicroPython Modbus RTU Slave/Master and TCP Server/Slave library
GNU General Public License v3.0
104 stars 45 forks source link

Fix timing issues with UART communication #75

Closed wpyoga closed 1 year ago

wpyoga commented 1 year ago

This pull request fixes timing issues when sending a command and receiving a response:

The missing initial bytes issue is apparent when we use a TTL-to-RS485 converter board that does not have automatic flow control. This is usually in the form of an EN pin, or DE & ~RE pin jumpered together. They are equivalent. This can be seen in the extremely popular MAX485 chip. This is caused by disabling the EN pin too late -- thus messing up the initial slave response.

The fix for the issue with receiving long slave responses is not perfect, but should be good enough for now. I was able to consistently trigger this issue by reading many (64, 96, and even 125) registers at once. An ideal solution would involve letting the user/caller specify the timeout value for reading a response.

P.S. Apologies to @brainelectronics , I did not see your PR #73 before going with this approach. However, I believe this solution will fix both your problem (ticks rollover) and the initial bytes being truncated.

P.P.S. This pull request should (may) also fix these issues:

beyonlo commented 1 year ago

@wpyoga Thank you very much for this PR :partying_face:

I will test it as soon is possible with the MAX485 using control pin, and I will report the result tests here!

beyonlo commented 1 year ago

@wpyoga Works like a charm with the MAX485 using control pin :partying_face:

I'm using the semi official examples to run Slave RTU (rtu_client_example.py) and Master RTU (rtu_host_example.py) and I have no CRC errors anymore. Ps: I call semi official examples because we (@brainelectronics) is waiting the PR #56 be merged to merge that examples (sync and async) too. Follow the tests:

Slave RTU (sync):

$ mpremote run rtu_client_example.py 
MicroPython infos: (sysname='esp32', nodename='esp32', release='1.20.0', version='v1.20.0 on 2023-04-26', machine='ESP32S3 module (spiram) with ESP32S3')
Used micropthon-modbus version: 0.0.0
Using pins (17, 18) with UART ID 1
Setting up registers ...
Register setup done
Serving as RTU client on address 10 at 115200 baud
my_coil_get_cb, called on getting COILS at 123, currently: [1, False, False, False, False, False, False, False, False, False, False, False]
my_coil_set_cb, called on setting COILS at 123 to: [False]
my_coil_get_cb, called on getting COILS at 123, currently: [False, False, False, False, False, False, False, False, False, False, False, False]
my_hr_get_cb, called on getting HREGS at 93, currently: [19]
my_hr_set_sb, called on setting HREGS at 93 to: [44]
my_hr_get_cb, called on getting HREGS at 93, currently: [44]
my_di_get_cb, called on getting ISTS at 67, currently: [0]
my_ir_get_cb, called on getting IREGS at 10, currently: [60001]
Incremented current value by +1 before sending response
Resetting register data to default values ...
Default values restored

Master RTU (sync):

$ mpremote run rtu_host_example.py 
MicroPython infos: (sysname='esp32', nodename='esp32', release='1.20.0', version='v1.20.0 on 2023-04-26', machine='ESP32S3 module (spiram) with ESP32S3')
Used micropthon-modbus version: 0.0.0
Using pins (17, 18) with UART ID 1
Requesting and updating data on RTU client at address 10 with 115200 baud

Status of COIL 123: [True, False, False, False, False, False, False, False, False, False, False, False]
Result of setting COIL 123 to True
Status of COIL 123: [False, False, False, False, False, False, False, False, False, False, False, False]

Status of HREG 93: (19,)
Result of setting HREG 93 to True
Status of HREG 93: (44,)

Status of IST 67: [False]

Status of IREG 10: (60002,)

Resetting register data to default values...
Result of setting COIL 42: True

Finished requesting/setting data on client
brainelectronics commented 1 year ago

So nice! Great work @wpyoga. Big thank you again to @beyonlo for testing. Will merge and release this tomorrow.

wpyoga commented 1 year ago

Thanks guys! I'm glad I could help, I was intially only trying to fix issues I saw on my end :)

brainelectronics commented 1 year ago

@beyonlo could you re-do your tests? I had to add support for MicroPython v1.19.1 and earlier as UART.flush() got introduced in 1.20.0, but some of my products are running on MicroPython v1.18. I did some quick tests locally with my setups at 9600, 57600 and 115200 baud which look good, see screenshots. The changes of @wpyoga are not affected by my additions

9600 57600 115200
beyonlo commented 1 year ago

@brainelectronics As requested, I re-do the tests using the release https://github.com/brainelectronics/micropython-modbus/archive/refs/tags/2.3.5.tar.gz but the version do not show 2.3.5, it show 0.0.0 - why? I tested in 9600, 57600 and 115200 and all works, but here I will paste just using the 115200:

Slave RTU (sync) - (rtu_client_example.py):

$ mpremote run rtu_client_example.py 
MicroPython infos: (sysname='esp32', nodename='esp32', release='1.20.0', version='v1.20.0 on 2023-04-26', machine='ESP32S3 module (spiram) with ESP32S3')
Used micropthon-modbus version: 0.0.0
Using pins (37, 38) with UART ID 1
Setting up registers ...
Register setup done
Serving as RTU client on address 10 at 115200 baud
my_coil_get_cb, called on getting COILS at 123, currently: [1, False, False, False, False, False, False, False, False, False, False, False]
my_coil_set_cb, called on setting COILS at 123 to: [False]
my_coil_get_cb, called on getting COILS at 123, currently: [False, False, False, False, False, False, False, False, False, False, False, False]
my_hr_get_cb, called on getting HREGS at 93, currently: [19]
my_hr_set_sb, called on setting HREGS at 93 to: [44]
my_hr_get_cb, called on getting HREGS at 93, currently: [44]
my_di_get_cb, called on getting ISTS at 67, currently: [0]
my_ir_get_cb, called on getting IREGS at 10, currently: [60001]
Incremented current value by +1 before sending response
Resetting register data to default values ...
Default values restored

Master RTU (sync) - (rtu_host_example.py):

$ mpremote run rtu_host_example.py 
MicroPython infos: (sysname='esp32', nodename='esp32', release='1.20.0', version='v1.20.0 on 2023-04-26', machine='ESP32S3 module (spiram) with ESP32S3')
Used micropthon-modbus version: 0.0.0
Using pins (17, 18) with UART ID 1
Requesting and updating data on RTU client at address 10 with 115200 baud

Status of COIL 123: [True, False, False, False, False, False, False, False, False, False, False, False]
Result of setting COIL 123 to True
Status of COIL 123: [False, False, False, False, False, False, False, False, False, False, False, False]

Status of HREG 93: (19,)
Result of setting HREG 93 to True
Status of HREG 93: (44,)

Status of IST 67: [False]

Status of IREG 10: (60002,)

Resetting register data to default values...
Result of setting COIL 42: True

Finished requesting/setting data on client