Skip to content

Modbus RTU on Opta needs RS485.setDelays() — please document and/or expose a passthrough #179

@dorkmo

Description

@dorkmo

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)

  1. Add a passthrough method. Something like:

    ModbusRTUClient.setRS485Delays(0, 1200);

    that internally calls RS485.setDelays(...) after begin() has finished.

  2. 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
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: codeRelated to content of the project itselftopic: documentationRelated to documentation for the projecttype: enhancementProposed improvement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions