Problem with RS485 direction control
Description
I am using a ST3485 RS485 transceiver with the DE/RE pins shortened and connected to a GPIO pin.
Running as Modbus master, the request is received correctly by the slave but the response is not received and umodbus throws an error (see below).
I suspect some timing issue with the transceiver switching to read mode but can't quite find the root cause of this.
Any ideas?
Reproduction steps
Initialization:
mbm0 = ModbusRTUMaster(pins=(Pin(GP_UART0_TX), Pin(GP_UART0_RX)),ctrl_pin=GP_UART0_DC, uart_id=0)
Send request to slave
mbm0.read_holding_registers(42,1,1)
MicroPython version
MicroPython v1.19.1-994-ga4672149b
MicroPython board
Raspberry Pico
MicroPython Modbus version
v2.3.4
Relevant log output
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/lib/umodbus/common.py", line 199, in read_holding_registers
File "/lib/umodbus/serial.py", line 287, in _send_receive
File "/lib/umodbus/serial.py", line 177, in _uart_read
File "/lib/umodbus/serial.py", line 149, in _exit_read
IndexError: bytearray index out of range
User code
No response
Additional informations
No response
I did some more testing and found that when reading the response I always get first a single byte 0x00 and after that the actual response frame. The single byte causes the reported IndexError in _exit_read
So I did a simple modification throwing away always the first single byte of the response. This basically works. However quite often the response frame is not received completely, leading to CRC error.
So I was wondering if software direction control via IO pin has actually been tested and if it is reliable?
Have you fitted a 120 ohm resistor on the line connection on the slave. I run RS485 over wifi and all my slaves need a 120 ohm resistor as they all think they are the last one on the cable.
I have a regular RS485 bus with 120 Ohm termination. The bus itself works fine. When using HW direction control I don't get any errors.
@ondrej1024 Hello!
So I was wondering if software direction control via IO pin has actually been tested and if it is reliable?
I'm using this lib for RS-485 using the MAX485CSA chip, that use a control pin, like do you are using, and are working without errors mostly of time - here you can see one of my tests. I wrote mostly of time because sometimes happen Invalid CRC reading COILS WHEN first I write multiple COILS and after that I try to read the COILS again - here you can see this problem happening. Bellow a example of error:
>>> host.read_coils(slave_addr=10, starting_addr=125, coil_qty=16)
[True, True, True, True, True, False, False, False, False, True, True, True, False, False, False, True]
>>> host.write_multiple_coils(slave_addr=10, starting_address=125, output_values=[1,1,0])
True
>>> host.read_coils(slave_addr=10, starting_addr=125, coil_qty=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/lib/umodbus/common.py", line 136, in read_coils
File "/lib/umodbus/serial.py", line 289, in _send_receive
File "/lib/umodbus/serial.py", line 322, in _validate_resp_hdr
OSError: invalid response CRC
Look that first read_coils() works, but after write_multiple_coils() the read_coils() stop to works, or sometimes works and sometimes not works
I don't know if your problem is the same of that, or if your problem has something to do with this problem, but the @brainelectronics opened an issue for that #52
I use only reading/writing holding registers, no coils.
Anyway I found that I have some issue with the ground reference of my circuitry. With a proper reference I always get a valid response from the slave, so SW direction control is working fine.
However I still see an initial 0x00 byte in the RX buffer immediately after sending the request. So I have to take care of this, otherwise I get a CRC error. I don't have an explanation for this.
Hey @ondrej1024 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. In case the reported issue was based on a HW problem you might close this issue
Yes, I will test this the next days and report back.
I have tested release 2.3.5 now with MP 1.20.0 but I still see the same issue. I get CRC error.
This is the code I use:
from machine import Pin
from umodbus.serial import Serial as ModbusRTUMaster
GP_UART0_TX = 0 # RS485 DI
GP_UART0_RX = 1 # RS485 RO
GP_UART0_DC = 2 # RS485 DC
mbm0 = ModbusRTUMaster(pins=(Pin(GP_UART0_TX), Pin(GP_UART0_RX)), ctrl_pin=GP_UART0_DC, uart_id=0)
mbm0.read_holding_registers(1,1,1)
And this is the error message:
Traceback (most recent call last):
File "<stdin>", line 10, in <module>
File "/lib/umodbus/common.py", line 199, in read_holding_registers
File "/lib/umodbus/serial.py", line 314, in _send_receive
File "/lib/umodbus/serial.py", line 347, in _validate_resp_hdr
OSError: invalid response CRC
The above code works fine with release 2.3.4 and this patched version of serial.py: serial.txt
Hi again @ondrej1024 may you can give release 2.3.7 another try again? I've added you proposed wait time after the flush. In case the reported issue was based on a HW problem you might close this issue
Just wanted to say this is likely a hardware issue and add a potential fix for anyone else that ends up here from searching.
I use the same SP3485. The datasheet says that if ~RE and DE are both pulled high (as they are when using a common GPIO to enable transmission) then the RO output should be High-Z. On my shoddy breadboard setup, RO was actually getting pulled low. This looks like a start bit on the UART RX, resulting in the reception of a 0x00 character.
Adding a pullup on RO is a hardware fix. If you need a software bodge fix, clear any bytes received before the peripheral should have had a chance to respond.
while self._uart.any() > 0:
# print('ditching bad rx')
self._uart.read()
For example, somewhere in the _send(...) method around here (or just right before/after if you don't care too much about affecting the timings):
https://github.com/brainelectronics/micropython-modbus/blob/e9ab05a2f1b37796446208a024368725953fc299/umodbus/serial.py#L278-L287
Comparison of without pullup resistor on RO vs with pullup, showing what looks like a failed RX to the UART as it is missing the stop bit. Top orange trace is RO (UART RX on the Pico I'm using).
Without:
With:
With the pullup in place, the expected packets are received and CRCs pass etc.
I agree, also my problem seems to be a hardware issue. There is no pullup on the RO line here and, although I haven't tried to solder one, I am pretty sure it would fix it. So I guess we can close this issue now.