pyhys / minimalmodbus

Easy-to-use Modbus RTU and Modbus ASCII implementation for Python.
Apache License 2.0
306 stars 146 forks source link

Silent period not working for multiple devices #100

Open thalesmaoa opened 1 year ago

thalesmaoa commented 1 year ago

I'm using minimalmodbus to read multiple drivers in a roll. When there is only one device, I have no problem. When I use two units, I start to get unresponsiveness. After debugging, I noticed that this arise due to silent period not being respected. Same problem as stated here.

To fix it, I just added a time.sleep(.0031) before each minimalmodbus call.

Now comes the issue. I'm using async to improve the code performance and avoid blocking it. During those 3,1ms that I'm blocking my code, I could be doing something else. Is there a way to put this silent period inside the minimalmodbus function?

When I call self.instrument.read_registers(49, 1), it should block it self for another writing. I'm kind lost how to fix it.

j123b567 commented 1 year ago

It would be really interesting to see the real waveform on the bus e.g. from the logic analyzer.

From my experience, this happens to me when the device does not respect the spec. In the past, our developers have created devices that responded immediately upon request, ignoring the silence between request and response. Here's what happens when master M tries to communicate with two such devices (A and B).

From the perspective of M From the perspective of B
M -> send request to A M sends request to A
A -> send response to M Continuation of the previous request
M -> send request to B Probably response of the A. The second message is always the response, right? ignoring
Response timeout It wasn't for me, was it?

If you can't fix the device, you will probably end up with the sleep approach. You can derive a serial port class and override read and/or write functions to add some delay there so you don't need to add them everywhere in the code (see example implementation of manual handling of DE in RS485 https://github.com/pyserial/pyserial/blob/master/serial/rs485.py)

thalesmaoa commented 1 year ago

What a great reply, thanks @j123b567 . The device is 250 miles away. Next time I go there, I will capture the signals. I understood the problem and somehow the idea of fixing it.

Device B is not respecting silent period, thus I need to wait more time so it can recognize that I'm talking to him. After looking for your suggestion, I start wondering: why minimalmodbus just make a simple sleep https://github.com/pyhys/minimalmodbus/blob/185afffd2d65b585f69bc5e3f640270d2bd27444/minimalmodbus.py#L1421 instead of configure it in the source of pyserial https://github.com/pyserial/pyserial/blob/31fa4807d73ed4eb9891a88a15817b439c4eea2d/serial/rs485.py#L69 .

Something like

instrument.serial._alternate_rs485_settings.delay_before_tx = 2.005

Also, this approach does have a problem. It will add another delay when M make a second request to A in sequence.

M -> A (ok, wait 2.005ms) A -> M (ok, wait 2.005ms) M -> A (ok, wait 2.005ms) A -> M (ok, wait 2.005ms) M -> B (bad, wait 2.005, but it must be 5.805ms)

The great challenge is how to identify that I've changed between devices, since it it handled by python async itself? I don't see how it is possible since the user sends random requests for writing. Maybe, an Ethernet <-> RS485 will give me better sleeping nights :laughing:.