stephane / libmodbus

A Modbus library for Linux, Mac OS, FreeBSD and Windows
http://libmodbus.org
GNU Lesser General Public License v2.1
3.3k stars 1.71k forks source link

RTS propagation delay #606

Closed jfsimon1981 closed 1 year ago

jfsimon1981 commented 2 years ago

Hello, I'm implementing a modbus RTU application on beagleboard using libmodbus. It works except the RTS signal get just too late to the RS485 converter configured half duplex, so that the responding device has first bits mixed with the TX signal being not yet shut down on the emitter (beagleboard).

I have tuned RTS delay to 0 us and yet measured between 75 us and 115 us overlap. I measures 5 us period when toggling 1/0 using GPIO through file device, hence few questions:

I am able to make a custom library to solve this issue, if needed, but I'd need help and pointer to where should this modification go.

Thanks,

Regards

Jean-François

elockman commented 2 years ago

@jfsimon1981 I believe I am seeing a similar issue. I can communicate with a couple devices at 115200, just fine, but I have a device that uses 9600 baud that drops leading response bytes due to the RTS switching time.

Opening /dev/ttyS2 at 9600 bauds (N, 8, 1)
Connected
PM-MODEL
DELAY: 0
[01][03][00][00][00][01][84][0A]
Waiting for a confirmation...
<02><23><6E><20><98>
Request for slave 2 ignored (not 1)
The responding slave 2 isn't the requested slave 1
<23><6E> is the expected response, but the leading bytes are missing. Setting Delay to 0us does not help. Is there a standard way to resolve this issue within the libmodbus package?
jfsimon1981 commented 2 years ago

Hi,

I have modified the library to get 100 us less, but the issue isn't really solved, apparently, the delay function is not perfectly stable. I also added a for loop with multiple usleep() just before the send+rts which seems to stabilize the usleep delay function before real need.

It's possible the issue lies in the OS, I was wondering if libmodbus requires real time kernel in linux.

The way send+rts works is the lib sends UART request to send the data buffer, afterwards, it does not have a return from rts, therefore just waits for a calculated time depending upon baud rate and number of bytes sent.

This is the quack, at this point, the delay is not perfectly sync with the ending of uart, and in half duplex, the responding client may over a not yet released line.

It's in file "modbus-rtu.c" function _modbus_rtu_ioctl_rts() when it calls usleep(). I set delay to 0, reduced the usleep by 100us since the delay had about 100us extra all the time.

Help on the topic still appreciated by the way,

Hardware is the beagle board black (ARM) running debian.

Regards

Jean-François

elockman commented 2 years ago

Thanks for the quick response. I am using a custom SAMA5D2 board running a custom yocto linux build, so I had the same concern with requiring a real time kernel.

jfsimon1981 commented 2 years ago

Hi, If you have a solution to this issue please leave infos, this would be useful. Likewise if I can properly sort this out I'll post.

Does this reliably work with RT kernel ?

Regards,

Jean-François

elockman commented 2 years ago

I do not have a RT kernel running. I'll post the solution, if I find it.

mhei commented 2 years ago

I've strong doubts, that you can fix such hardware issues with non-bare-metal-OS. Software workarounds like here in libmodbus or even in kernel space, are always operating on a best-effort base. In my eyes, the only valid, real solution is using hardware that is capable to release the transmitter as soon as last byte left TX register. Some embedded MCUs supports this, e.g. Microchip (formerly Atmel), natively - not sure whether SAMA5D2 does. If not and you can build your own RS-485 port, then use RS-485 line drivers with auto-direction control - a little bit more expensive than normal ones, yes. USB to RS-485 devices usually also work.

elockman commented 2 years ago

@mhei I am using a USART port with a TI serial to RS485 chip. I have code working well on some devices with 115200 baud. Looking at the code, libmodbus is attempting to time the RTS release based on the message size, plus a dwell. Previously, I had an issue with the response having a leading NULL <0> character on the devices with 115200 baud. Biasing the bus didn't resolve the leading NULL, but adding a pull-up on the USART RX line did. I may have to increase the pull-up value. It doesn't seem that Linux is having an issue with being fast enough, but there may be an issue with calculating timing based on baud rate, since modbus-RTU is time based. I've had other pressing issues, but I'll post my findings once I get back to it.

elockman commented 2 years ago

From what I saw, results were wrong, but consistent. I suspect, making some changes to the calcs would prove a software work-around is possible.

Delay is calculated here: https://github.com/stephane/libmodbus/blob/d9054d40ccfbf40210c3680d4ff027edc8e79484/src/modbus-rtu.c#L1290

Delay is implemented here: https://github.com/stephane/libmodbus/blob/d9054d40ccfbf40210c3680d4ff027edc8e79484/src/modbus-rtu.c#L291

jfsimon1981 commented 2 years ago

Hi, From backup pictures of the scope in lab, I got a response in 3.68 ~ 3.74 ms for 7 bytes, with the formula we are 520 us / byte (3640 us). I'll conduct further tests to track the issue. In theory there's no issue.

From the outlook, inducing the delay with usleep() is not perfectly accurate, perhaps time to start/return the function, from unix tests just now, usleep(4160) which what's needed for 8 bytes at (1, N, 1, 19200 bauds) consistently gives 76~104 us extra.

At this point, I think the theory how the lib works is fine, only the call to usleep() really takes a small and slightly changing extra time.

The tested extra time - just now on Linux Mint - is also what I found in the lab on the beagle board (debian), about 100 us extra.

I used to correct this extra time by adding -100 in the formula you quoted from mosbus-rtu.c in order to compensate, but that work around is not perfect, because usleep() does not always add the same extra delay, thus occasionally the modbus line goes wrong.

That's my job for early next year, this is a client project, I must find how to stabilize this so it works almost perfectly most of the time.

Thanks for taking time to check those 2 files, I got to line 291, but I missed line 1290, now I understand the inside better. There's no fault in the timing. As Micheal said, this is not easy to solve in SW.

My intent is to check how in Linux we can ask a small function to not be interruptible by OS in order to provide a perfectly accurate stable timer for delay purpose. Not sure that can be done without real time kernel, that was the question. I'll get back on the list if I get a solution.

Regards,

Jean-François

jfsimon1981 commented 2 years ago

Hello, Mat on beagle board irc highlighted that there is per modbus spec be 3.5 char stop which is at 19.2 kbaud ~ 2 ms. Above 19.2k Baud, the spec calls for constant value 1.75 ms.

This stop time allows for a lot of margin for RTS to get back, hence we are probably away from the main point. There should be no huryr and plenty of time for RTS to be cleared and the library should not need tweaks of that order.

At the moment, I'm investigating why the slave in question seems to respond immediately after a master request, that should not happen, something else in the timing has to be the cause.

Hope this helps.

Jean-François

mhei commented 2 years ago

@jfsimon1981: I think you got confused by the Modbus timeouts... The RTS time handling calculations above tries to determine how long libmodbus must ensure that RTS is asserted to turn the hardware transceiver on. This is done on a rough calculation how long it takes with a known baudrate. (BTW: at the moment, I'm wondering whether an approach using tcdrain et al. would be better... ) The Modbus timeout is another topic: in my understanding it does not prevent a Modbus slave to respond earlier to a request in case the slave could decode it properly. This "decoding" can be done with analyzing the Modbus packet and the length fields etc. and checking the CRC. The timeout is only used to "sync" Modbus master and slave in case of errors, e.g. when the Modbus packet transmission is interrupted or similar things. But I will have a look at the spec again, it's already sometime ago I've read it :smile:

jfsimon1981 commented 1 year ago

Good day, I switched to hardware layer for the RTS timing, although the library delay worked after reducing below the minimum available delay (by tweaking the library), it got never very stable to to os latency when interrupts occured during frames.

Therefore i believe the RS485 RTS timing should be handled on HW/kernel level which gave about 2% error rate on the line.

I still have some tests and various devices to test with but that seems all right that way.

I'll improve if I have time some of the library aspect and will post on it if i can get reliable improvements on that. It's possible to confuse the server when the line with client drops some bits, so that it does not recover since the buffer is partially filled in.

I haven't studied this in detail, at the moment, i initialize fully the library for each frame since this happened randomly and seems to get stuck.

Hope i can investigate this and provide tweaks on that, if i get a properly implemented solution i'll post on the issue poll separately.

Regards,

Jean-François