[modbus] Properly support client-mode broadcast sends (#17467)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Bonne Eggleston
2026-08-11 08:31:15 -05:00
committed by GitHub
co-authored by J. Nick Koston
parent eefd2a00c7
commit 8ee3c8d41d
7 changed files with 405 additions and 15 deletions
@@ -109,8 +109,22 @@ void ModbusCommandItem::on_not_sent(std::span<const uint8_t> request_pdu) {
// Fired once per wire transmission (including hub re-queues from a retry), so the on_command_sent
// trigger reflects when the frame actually went out, not when it was queued.
void ModbusCommandItem::on_sent(std::span<const uint8_t> request_pdu) {
if (this->controller_ != nullptr)
this->controller_->command_sent(static_cast<int>(this->function_code_), this->start_address_);
if (this->controller_ == nullptr)
return;
this->controller_->command_sent(static_cast<int>(this->function_code_), this->start_address_);
// A broadcast (address 0) is never answered (Modbus 4.1), so the hub delivers no terminal callback.
// on_sent is this command's only callback, so drop the one-shot from the queue here, or it would leak.
// Test the address the frame went to, not address_: a custom command's frame carries its own address
// (frame[0]), which may differ from this controller's. (unqueue_command() is a no-op for a poll.)
uint8_t wire_address = this->address_;
if (this->function_code_ == FunctionCode::CUSTOM) {
std::span<const uint8_t> frame =
this->custom_data_ != nullptr ? std::span<const uint8_t>(*this->custom_data_) : this->payload;
if (!frame.empty())
wire_address = frame[0];
}
if (wire_address == modbus::BROADCAST_ADDRESS)
this->controller_->unqueue_command(this);
}
bool ModbusCommandItem::on_no_response(std::span<const uint8_t> request_pdu) {