[modbus] Heap-free response path (#17377)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
Bonne Eggleston
2026-07-24 11:45:59 -10:00
committed by GitHub
co-authored by Claude Fable 5 J. Nick Koston J. Nick Koston
parent d12300679e
commit 5e2d428e69
22 changed files with 267 additions and 63 deletions
@@ -57,7 +57,7 @@ bool ModbusController::send_next_command_() {
}
// Queue incoming response
void ModbusController::on_modbus_data(const std::vector<uint8_t> &data) {
void ModbusController::on_response(std::span<const uint8_t> request_pdu, std::span<const uint8_t> response_pdu) {
if (this->command_queue_.empty()) {
ESP_LOGW(TAG, "Received modbus data but command queue is empty");
return;
@@ -78,8 +78,10 @@ void ModbusController::on_modbus_data(const std::vector<uint8_t> &data) {
this->online_callback_.call((int) current_command->function_code, current_command->register_address);
}
// Move the commandItem to the response queue
current_command->payload = data;
// Move the commandItem to the response queue. The span points into the hub's receive buffer, so
// copy the payload into the command for deferred processing in loop().
auto data = modbus::helpers::server_pdu_payload(response_pdu);
current_command->payload.assign(data.begin(), data.end());
this->incoming_queue_.push(std::move(current_command));
ESP_LOGV(TAG, "Modbus response queued");
this->command_queue_.pop_front();
@@ -93,8 +95,11 @@ void ModbusController::process_modbus_data_(const ModbusCommandItem *response) {
response->on_data_func(response->register_type, response->register_address, response->payload);
}
void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) {
ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code);
void ModbusController::on_error(std::span<const uint8_t> request_pdu, modbus::ModbusExceptionCode exception_code) {
// The request function code (request_pdu[0]) already carries what the log needs; the exception bit only
// ever appears on the response, so no masking is needed here.
const uint8_t function_code = request_pdu.empty() ? 0 : request_pdu[0];
ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, static_cast<uint8_t>(exception_code));
if (this->command_queue_.empty()) {
return;
}