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:
-
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;
}
}
-
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:
- Discard the unexpected frame.
- Recalculate the remaining time against the originally configured
response_timeout.
- 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?
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
EMBBADSLAVEerror. 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:
Pre-check flags
EMBBADSLAVE:In
src/modbus-rtu.c,_modbus_rtu_pre_check_confirmationcompares the requested slave address (req[0]) with the responding slave address (rsp[0]). If they don't match, it explicitly setserrno = EMBBADSLAVEand returns-1:Confirmation check aborts the transaction:
In
src/modbus.c,check_confirmationcalls this pre-check. When it returns-1, the function immediately executes error recovery (sleeping and flushing the buffer) and returns-1to the caller, terminating the request early:Expected Behavior
Instead of instantly aborting, when a frame triggers an
EMBBADSLAVEerror, the master should:response_timeout.ETIMEDOUT).I've submitted a PR (#860) to fix this issue. Could you take a look and see if it looks good?