Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Single Coil Write and Holding Register Write Initial Response Error #50

Open
j-broome opened this issue Jan 3, 2023 · 11 comments
Open
Labels
bug Something isn't working client Client implementation specific issue question Further information is requested

Comments

@j-broome
Copy link

j-broome commented Jan 3, 2023

Using an RS-485 Transceiver with automatic flow control (MAX22028) and also tried with THVD-2410 (flow control pin).
THVD-2410 works with the flow control code, but when I try to use a transceiver without the flow control pin, the single coil write and single register write responses are duplicated 3 times. Timeout shows to be 8020 microseconds. The issue is masked with the THVD-2410 and likely 95% of people attempting this because the transceiver keeps the responses from reaching the 485 bus. Below is my test code to recreate the issue.

from umodbus.serial import ModbusRTU
from machine import Pin
tx = machine.Pin(0)
rx = machine.Pin(1)

rtu_pins = (tx, rx) # (TX, RX)
slave_addr = 3 # address on bus as client
baudrate = 9600
client = ModbusRTU(
addr=slave_addr, # address on bus
baudrate=baudrate, # optional, default 9600
pins=rtu_pins, # given as tuple (TX, RX)
# data_bits=8, # optional, default 8
# stop_bits=1, # optional, default 1
# parity=None, # optional, default None
ctrl_pin=None, # optional, control DE/RE
uart_id=0 # optional, see port specific documentation
)

When I do not use flow control I get 3 consecutive response messages on the Rx line with function code 6:
image

Using function code 16 works though:
image

THVD-2410 (w/flow control) setup - this works flawlessly - absolutely no errors
image

MAX22028 (automatic flow control) setup - only fails on the initial response
image
After settings a holding register to say 0, the initial response coming out of the device is wrong, but it does somehow take that value, so when the register is read with FC3, it indeed has the correct value in it.

Wondering if this is happening because the RX buffer is not actually getting cleared somehow, but I can't find any discrepancies in the umodbus code.

Originally posted by @j-broome in #7 (comment)

@j-broome j-broome changed the title Using an RS-485 Transceiver with automatic flow control (MAX22028) and also tried with THVD-2410 (flow control pin). Single Coil Write and Holding Register Write Initial Response Error Jan 3, 2023
@brainelectronics brainelectronics added client Client implementation specific issue question Further information is requested labels Jan 3, 2023
@beyonlo
Copy link

beyonlo commented Jan 3, 2023

Hello @j-broome

I'm working with RS-485 using the the MAX485CSA chip that use of course the flow control (the ctrl_pin) and works fine. Unfortunately I don't have a RS-485 chip with automatic flow control to do some tests. But just for a test, could you please to try to change the speed from 9600 to 115200? Need to configure that new speed in both sides, Slave and Master.

I see that in the https://github.com/brainelectronics/micropython-modbus/blob/develop/umodbus/serial.py#L116 there is a different timeout calculation for the baudrate speed. So, maybe can be a good test to check.

@j-broome
Copy link
Author

j-broome commented Jan 3, 2023

Wow! Using 115,200 baud works. Do you have an idea on how to fix this for 9600? The company I am at uses only 9600 for their field communication.
image

@beyonlo
Copy link

beyonlo commented Jan 3, 2023

Wow! Using 115,200 baud works.

@j-broome I'm glad that that works for you!

Do you have an idea on how to fix this for 9600? The company I am at uses only 9600 for their field communication.

Well, now we can to confirm that problem is probably a relation between baudrate and that https://github.com/brainelectronics/micropython-modbus/blob/develop/umodbus/serial.py#L116 timeout calculation. I'm sorry, I don't know how to fix that, but the @brainelectronics is the expert and maybe he can help you with that fix.

Anyway, if you want to try another test, change that code to:

        self._t35chars = 1750   # 1750us (approx. 1.75ms)
        #if baudrate <= 19200:
            # 4010us (approx. 4ms) @ 9600 baud
            #self._t35chars = (3500000 * (data_bits + stop_bits + 2)) // baudrate
        #else:
            #self._t35chars = 1750   # 1750us (approx. 1.75ms)

So, this test code, no matter what the baudrate you choose, the timeout will be always self._t35chars = 1750. Probably what I'm suggesting you is a bad idea, because if there is a different calculation for different baudrate is because is necessary, but you can just to try! :)

@brainelectronics brainelectronics added the bug Something isn't working label Jan 3, 2023
@brainelectronics
Copy link
Owner

Will check by tomorrow after fixing #35 for you @beyonlo.

@j-broome I also assume the change of the wait time here

total_frame_time_us = self._t1char * len(serial_pdu)
could solve your issue. It might ran into a timeout as it is waiting to long for a response and thereby requesting the data again, that's potentially why you see the request several times

@j-broome
Copy link
Author

j-broome commented Jan 4, 2023

@brainelectronics I am actually seeing the response 3 times. The request only happens once.

@j-broome
Copy link
Author

j-broome commented Jan 9, 2023

Any update on this? I am still seeing some errors.

@brainelectronics
Copy link
Owner

@j-broome not yet. I was busy with #55 this evening. I'll check & resolve it until the end of this week.

Did you play around with the mentioned sleep time in my previous comment?

@j-broome
Copy link
Author

Yes, I attempted several things, but still seeing the issue on the higher baud rates as well.

@brainelectronics
Copy link
Owner

Hi again @j-broome
I've now tested the setup on real hardware with two RP2. Once with 9600 baud, and then with 115200 baud using the rtu_host_example.py and rtu_client_example.py. Only modification was the usage of ctrl_pin, I've used Pin 2 on both devices.

I've monitored the ctrl_pin and the UART data pins on both devices, see the attached Saleae logs.
RP2-RP2-log.zip

At 9600 baud the control pin is activated round about 1.15ms before the first byte is sent from host to client and deactivated round about 1.10ms after the last byte. Similar timings on client side. The client sends its answer after round about 9ms.
RP2-RP2-9600

At 115200 baud the control pin is also activated round about 1.15ms before the first byte is sent from host to client and deactivated round about 0.65ms after the last byte. Similar timings on client side. The client sends its answer after round about 7ms.
RP2-RP2-115200

The pre-send timing is the same for 115200 and 9600 baud, as it is always, independent of the used baudrate, waiting for 1000us + the MicroPython processing time to get to the UART write state (which is around 0.15ms), see

if self._ctrlPin:
self._ctrlPin(1)
time.sleep_us(1000) # wait until the control pin really changed
send_start_time = time.ticks_us()
self._uart.write(serial_pdu)

The post-send timing depends on the calculated frame time. This depends on the length of the message and the calculated time for a single byte based on the baudrate, see
self._t1char = (1000000 * (data_bits + stop_bits + 2)) // baudrate

self._uart.write(serial_pdu)
if self._ctrlPin:
total_frame_time_us = self._t1char * len(serial_pdu)
while time.ticks_us() <= send_start_time + total_frame_time_us:
machine.idle()
self._ctrlPin(0)

My assumption is that your device is still in transmission mode (control pin active), while the other one is already sending its response. After the first successful exchange of data, it will notice this state and send the response a bit later. Nevertheless does this assumption not explain why it only happens on function code 6, but not on function code 16 ... It could explain why 115200 baud are working, but 9600 baud not. Like I said, at 115200 baud the control pin is only 0.65ms active after the transmission finished, whereas at 9600 baud, it is active for 1.15ms. Those 0.5ms could make the difference.

Could you maybe capture the control pin states and UART data with a logic analyser on your side as I did?

@j-broome
Copy link
Author

j-broome commented Feb 3, 2023

I have no control pin as I am using the MAX22028 uart to 485 chip with auto flow control. I switched to FreeRTOS with libmodbus and now have no issues, so I do not believe it is the hardware.

@brainelectronics
Copy link
Owner

Hey @j-broome may you can give release 2.3.5 a try again? MicroPython v1.20.0 is recommended as it introduces the flush function for UART, implemented in #75, for older MicroPython firmware versions the timing has been improved as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working client Client implementation specific issue question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants