Skip to content

RTU Client Prematurely Aborts Request on Unexpected Slave Response #861

Description

@MrAlaskan

Summary

The current RTU confirmation handling prematurely aborts in-flight requests when a reply from an unexpected slave is received.

According to the Modbus protocol specification (Modbus Serial Line Protocol and Implementation Guide V1.02), if a master receives a frame from an unexpected slave while waiting for a response, it should discard the frame, keep the response timeout running, and remain in the "Waiting for reply" state.

Description

The current implementation handles unexpected slave replies inconsistently, violating the RTU master state machine.

When a frame with a valid CRC but a non-matching slave address is received, the pre-check layer flags the slave address mismatch as an EMBBADSLAVE error. The client confirmation path catches this error and immediately aborts the transaction by returning -1.

On a shared RS485 bus, stray packets (e.g., delayed responses from previous transactions or noise that happens to pass CRC) are common. Aborting the entire transaction due to a mismatched slave address prevents the master from receiving the correct slave's valid response that might arrive just milliseconds later.

The issue spans across the RTU pre-check function and the main confirmation handling routine:

  1. Pre-check flags EMBBADSLAVE:
    In src/modbus-rtu.c, _modbus_rtu_pre_check_confirmation compares the requested slave address (req[0]) with the responding slave address (rsp[0]). If they don't match, it explicitly sets errno = EMBBADSLAVE and returns -1:

    /* src/modbus-rtu.c:327-346 */
    static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx,
                                                  const uint8_t *req,
                                                  const uint8_t *rsp,
                                                  int rsp_length)
    {
        /* Check responding slave is the slave we requested (except for broacast
         * request) */
        if (req[0] != rsp[0] && req[0] != MODBUS_BROADCAST_ADDRESS) {
            if (ctx->debug) {
                fprintf(stderr,
                        "The responding slave %d isn't the requested slave %d\n",
                        rsp[0],
                        req[0]);
            }
            errno = EMBBADSLAVE;
            return -1;
        } else {
            return 0;
        }
    }
  2. Confirmation check aborts the transaction:
    In src/modbus.c, check_confirmation calls this pre-check. When it returns -1, the function immediately executes error recovery (sleeping and flushing the buffer) and returns -1 to the caller, terminating the request early:

    /* src/modbus.c:565-581 */
    static int check_confirmation(modbus_t *ctx, uint8_t *req, uint8_t *rsp, int rsp_length)
    {
        int rc;
        int rsp_length_computed;
        const unsigned int offset = ctx->backend->header_length;
        const int function = rsp[offset];
    
        if (ctx->backend->pre_check_confirmation) {
            rc = ctx->backend->pre_check_confirmation(ctx, req, rsp, rsp_length);
            if (rc == -1) {
                if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
                    _sleep_response_timeout(ctx);
                    modbus_flush(ctx);
                }
                return -1;
            }
        }
        // ...

Expected Behavior

Instead of instantly aborting, when a frame triggers an EMBBADSLAVE error, the master should:

  1. Discard the unexpected frame.
  2. Recalculate the remaining time against the originally configured response_timeout.
  3. Continue listening on the bus until it receives the expected reply or the original timeout expires (ETIMEDOUT).

I've submitted a PR (#860) to fix this issue. Could you take a look and see if it looks good?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions