ArduinoModbus gives CRC16 errors with Uno Wifi R4

The ArduinoModbus library isn't supported with the Uno Wifi R4, but I've been trying to get it working.

First step was just to get it to compile, there were a bunch of lines that said:
`#if defined(ARDUINO) && defined(AVR)

I changed them to:
`#if defined(ARDUINO) && (defined(AVR) || (defined(ARDUINO_ARCH_RENESAS_UNO)))

to include the R4.

I have a very simple test program that calls ModbusRTUClient.begin() in setup and ModbusRTUClient.requestFrom() in loop. It works with an Uno R2, but not with the R4. I've hooked up a Modbus analyzer (WinModbus), and with the R4 version it says that the packet has failed checksum.

Under R2, the packet that is produced is: 0F 03 8D 00 15 09 E8
Under R4, the packet that is produced is: 0F 03 8D 00 15 09 FF

Same packet, except the last byte is FF instead of E8.

The function in the Modbus library that produces the crc checksum is called crc16. So in my test program I added this definition:
`extern uint16_t crc16(uint8_t *buffer, uint16_t buffer_length);

then in setup I put in the following code:

uint8_t buf[8] = {'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F'};

  
    uint16_t thecrc=crc16(buf, 8);
    Serial.println("CRC Called");
    Serial.println(thecrc, HEX);
  while(1); // returns 18DD for R2, R4

This returns the same value for both boards, 18DD.

So I'm really at a loss. I also tried using the ModbusMaster library, (which has other issues). It was able to produce identical packets for both boards.

Any thoughts as to where I should be looking?

OK, I did some further testing, I had the board output to the serial monitor the packet before transmitting. The crc is being calculated fine, somehow in transmission the last byte is getting changed from E8 to FF.

Curiouser and curiouser.

Are you using half-duplex, RS485? A typical problem is setting the TX disable at the right time. If disabled too soon, the data line goes high and what you see on the line is all 1's.

I think that's it. I've noticed with the Modbus library that randomly inserting delay() statements fixes a lot of issues.

I had jumped to the conclusion that it was something with the CRC routines, there's a human tendency to suspect that which you fear the most. The crc16() function in the ArduinoModbus library has a "#if defined(ARDUINO)" statement, which is the kind of thing that makes me leery, an algorithm like that shouldn't be platform-dependent. And the adjacent code even has "#if defined(_WIN32)" which we all know is a highway to perdition.

If your transmission is being cut short as I suspect it is, and as @bobcousins also suggested, then the flush() function on the serial port should wait until the final byte has been transmitted before returning. You should then be safe to flip back to RX without chopping off the end of the message.

I've found that flush() does not in fact work that way, I have to insert a delay() after flush() to keep the bytes from being truncated.

The ArduinoModbus library uses the RS485 library as the interface to the serial port. RS485 has a member setDelays() which allows you to insert a delay at the start and end of transmission. By default both delays are 50ms, I'm going to try bumping them up and see if that helps.

Be careful when increasing the delay - especially the end of transmission delay - as you can end up in a situation where your RS485 transceiver is still in TX mode when the remote device is ready to respond.

I've not got an R4 but I wonder if there is an issue with the way the serial port is implemented such that the flush() command isn't working as it should.

I've just found this discussion:

which may be the reason that flush() isn't working correctly.

In that thread is the comment:

Flush does not work with any of the Serial ports, with the currently released code.

From October '23.

Doing some googling, it seems established that flush() doesn't work on the R4, it just returns immediately. This means that any code that depends on flush() isn't going to work.

I'm going to try estimating the transmit time using the Baud rate and the byte count, and inserting a delay of slightly longer. That might get my code working but I don't see it as a general purpose reliable solution.

Just a quick follow-up: I changed the RS485 code to use SoftwareSerial throughout instead of HardwareSerial. With this I was able to get the R4 to communicate with a Modbus device.

Except that you have to fix the bug with SoftwareSerial described in this thread:

It's demoralizing how buggy the R4 code is.

To be fair, most software is quite buggy.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.