ModbusRTUClient.begin() does not currently expose any way to set the underlying RS485.setDelays(preDelay, postDelay) values, even though on mbed-core boards (Arduino Opta especially) the default postDelay of 0 causes the last byte of every Modbus query to be truncated on the wire and the slave silently rejects the frame.
The result is a confusing failure mode: ModbusRTUClient.requestFrom() returns false, ModbusRTUClient.lastError() returns 4 (timeout), and there is nothing in the API or README that points users at the fix.
This issue is upstream of the underlying RS485 bug filed at arduino-libraries/ArduinoRS485#75. Even after that bug is fixed in the "ArduinoRS485" library, the ArduinoModbus library is worth improving:
Symptoms (current behavior, Opta + SunSaver MPPT, default settings)
ModbusRTUClient.begin(9600, SERIAL_8N2); // returns true
ModbusRTUClient.requestFrom(1, HOLDING_REGISTERS, 0x0008, 1); // returns false
ModbusRTUClient.lastError(); // returns 4 (timeout)
Every call fails. The slave never sees a valid CRC because the last byte of the request is corrupted.
Required workaround (today)
ModbusRTUClient.begin(9600, SERIAL_8N2);
ModbusRTUClient.setTimeout(500);
RS485.setDelays(0, 1200); // <-- MUST be after begin(), not before
Calling RS485.setDelays() before ModbusRTUClient.begin() does nothing, because begin() re-applies its own RS485 init and overwrites the delays. This ordering requirement is also undocumented.
Suggested fixes (any of these would help)
-
Add a passthrough method. Something like:
ModbusRTUClient.setRS485Delays(0, 1200);
that internally calls RS485.setDelays(...) after begin() has finished.
-
Apply a sensible default postDelay on mbed/Opta boards inside
ModbusRTUClient.begin(). Computed from baud rate (~11 bit-times):
#if defined(ARDUINO_OPTA) || defined(ARDUINO_PORTENTA_H7_M7)
RS485.setDelays(0, (11UL * 1100000UL) / baudrate);
#endif
-
Document the requirement in the README under "Using on Opta / mbed boards" with a code snippet, and call out the begin()-then-setDelays() ordering explicitly.
Why this matters
ArduinoModbus is the recommended Modbus library for the Opta, which Arduino markets specifically for industrial RS-485 / Modbus applications. Having the out-of-the-box configuration silently fail with a generic "timeout" error is a significant onboarding obstacle — we lost ~2 days of bench time to this before a forum thread pointed at setDelays().
References
Environment
- arduino-cli 1.x
- Core:
arduino:mbed_opta (latest)
- Library: ArduinoModbus (latest from master)
- Library: ArduinoRS485 (latest from master)
- Board: Arduino Opta WiFi (AFX00002), FQBN
arduino:mbed_opta:opta
- Slave: Morningstar SunSaver MPPT via MRC-1 MeterBus->RS-485 adapter,
9600 8N2, slave ID 1
ModbusRTUClient.begin()does not currently expose any way to set the underlyingRS485.setDelays(preDelay, postDelay)values, even though on mbed-core boards (Arduino Opta especially) the defaultpostDelayof0causes the last byte of every Modbus query to be truncated on the wire and the slave silently rejects the frame.The result is a confusing failure mode:
ModbusRTUClient.requestFrom()returns false,ModbusRTUClient.lastError()returns4(timeout), and there is nothing in the API or README that points users at the fix.This issue is upstream of the underlying RS485 bug filed at arduino-libraries/ArduinoRS485#75. Even after that bug is fixed in the "ArduinoRS485" library, the ArduinoModbus library is worth improving:
Symptoms (current behavior, Opta + SunSaver MPPT, default settings)
Every call fails. The slave never sees a valid CRC because the last byte of the request is corrupted.
Required workaround (today)
Calling
RS485.setDelays()beforeModbusRTUClient.begin()does nothing, becausebegin()re-applies its own RS485 init and overwrites the delays. This ordering requirement is also undocumented.Suggested fixes (any of these would help)
Add a passthrough method. Something like:
that internally calls
RS485.setDelays(...)after begin() has finished.Apply a sensible default postDelay on mbed/Opta boards inside
ModbusRTUClient.begin(). Computed from baud rate (~11 bit-times):Document the requirement in the README under "Using on Opta / mbed boards" with a code snippet, and call out the
begin()-then-setDelays()ordering explicitly.Why this matters
ArduinoModbus is the recommended Modbus library for the Opta, which Arduino markets specifically for industrial RS-485 / Modbus applications. Having the out-of-the-box configuration silently fail with a generic "timeout" error is a significant onboarding obstacle — we lost ~2 days of bench time to this before a forum thread pointed at
setDelays().References
ModbusServer::writeInputRegistersnot implemented #18https://github.com/SenaxInc/SenaxTankAlarm/blob/master/TankAlarm-112025-Common/src/TankAlarm_Solar.cpp
https://github.com/SenaxInc/SenaxTankAlarm/blob/master/firmware/sunsaver-modbus-test/sunsaver-modbus-test.ino
Environment
arduino:mbed_opta(latest)arduino:mbed_opta:opta9600 8N2, slave ID 1