From d5578ce796261b54d154e39b233af4b8fdff634b Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 17 Jul 2026 02:47:13 -0600 Subject: [PATCH] firmware: discard partial RS485 echoes --- docs/CHANGELOG.md | 1 + firmware/xiao-esp32c6-sensor-node/rs485_transport.cpp | 4 +++- tests/test_xiao_sensor_node_contract.py | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 19c2ca3..5119bf2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,6 +5,7 @@ - Added standalone XIAO ESP32-C6 BNO086/MAX-M10S sensor-node firmware and a versioned RS485 telemetry contract without changing Cargo `/api/v1` or JBD/NimBLE behavior. - Added XIAO-side persistent level calibration with stability checking and RS485 start/clear commands; no dashboard behavior changed. - Prevented echoed or oversized RS485 frames from starving the XIAO sensor loop. +- Silently discard malformed partial RS485 echoes instead of emitting recursive `invalid_json` responses. ## Unreleased diff --git a/firmware/xiao-esp32c6-sensor-node/rs485_transport.cpp b/firmware/xiao-esp32c6-sensor-node/rs485_transport.cpp index c0b458a..11df96f 100644 --- a/firmware/xiao-esp32c6-sensor-node/rs485_transport.cpp +++ b/firmware/xiao-esp32c6-sensor-node/rs485_transport.cpp @@ -89,7 +89,9 @@ void pollRs485Commands(HardwareSerial& serial, const ImuState& imu) { StaticJsonDocument<192> request; DeserializationError error = deserializeJson(request, commandBuffer); commandBuffer = ""; - if (error) { publishCalibrationResult(serial, "invalid_json"); continue; } + // Partial self-echoes and line noise are not commands. Dropping them + // silently avoids turning one bad fragment into a response/echo loop. + if (error) continue; String type = request["type"] | ""; // Some RS485 boards echo transmitted frames into RX. Ignore our own output. if (type == "sensor_status" || type == "calibration_response") continue; diff --git a/tests/test_xiao_sensor_node_contract.py b/tests/test_xiao_sensor_node_contract.py index 503b5f1..66a08a5 100644 --- a/tests/test_xiao_sensor_node_contract.py +++ b/tests/test_xiao_sensor_node_contract.py @@ -61,6 +61,7 @@ def test_rs485_echo_cannot_starve_sensor_loop(): assert "bytesRemaining" in transport assert "discardCommandUntilNewline" in transport assert 'type == "sensor_status"' in transport + assert 'publishCalibrationResult(serial, "invalid_json")' not in transport def test_node_does_not_take_cargo_or_ble_authority(): combined = "\n".join(p.read_text() for p in NODE.glob("*.*"))