mirror of
https://github.com/ruvnet/RuView
synced 2026-08-04 19:31:42 +00:00
Bug #2 (root cause): LD2410 probe-detection matched only the 4-byte head 0xF4F3F2F1, so a floating UART at 256000 baud could phantom-detect a sensor and spawn a UART task. Now requires a full validated report frame (head + sane length + tail 0xF8F7F6F5), extracted to mmwave_detect.h and shared with a host unit test (test_mmwave_detect.c, 8 vectors) so firmware and test can't diverge. Matches the validate-before-trust approach used for MR60 in #1107. Bug #1: sendto ENOMEM used a fixed 100 ms backoff too short to drain sustained lwIP/WiFi buffer pressure, so a node could stay stuck. Now exponential (100->200->...->2000 ms per consecutive ENOMEM, reset on first successful send). Removing the phantom LD2410 task (bug #2) also removes the extra load that tipped the reporter's tier-2 node into the stuck state. Validated on ESP32-S3 QFN56 rev v0.2 (the reporter's silicon): tier-2 streams ~100 frames/s with no stuck ENOMEM and correctly reports no mmWave (no phantom). LD2410 predicate truth table proven (head-without-tail REJECTED). Could not reproduce the reporter's environment-specific floating-pin noise, so the deterministic proof is the host unit test.
This commit is contained in:
@@ -44,9 +44,9 @@ FUZZ_DURATION ?= 30
|
||||
FUZZ_JOBS ?= 1
|
||||
|
||||
.PHONY: all clean run_serialize run_edge run_nvs run_all test_adr110 run_adr110 \
|
||||
test_vitals run_vitals host_tests
|
||||
test_vitals run_vitals test_mmwave_detect run_mmwave_detect host_tests
|
||||
|
||||
all: fuzz_serialize fuzz_edge fuzz_nvs test_adr110 test_vitals
|
||||
all: fuzz_serialize fuzz_edge fuzz_nvs test_adr110 test_vitals test_mmwave_detect
|
||||
|
||||
# --- ADR-110 encoding unit tests ---
|
||||
# Host-side, no libFuzzer needed — plain C99 deterministic table tests
|
||||
@@ -69,8 +69,19 @@ test_vitals: test_vitals_count_presence.c $(MAIN_DIR)/edge_processing.h
|
||||
run_vitals: test_vitals
|
||||
./test_vitals
|
||||
|
||||
host_tests: run_adr110 run_vitals
|
||||
@echo "Host tests passed (ADR-110 + vitals #998/#996)"
|
||||
# --- mmWave LD2410 detection predicate (#1135 bug #2) ---
|
||||
# Host-side, no libFuzzer. Proves a floating-UART head pattern (0xF4F3F2F1)
|
||||
# without a valid frame length+tail is REJECTED, so a phantom LD2410 is never
|
||||
# detected on a node with no sensor wired. Tests the real predicate the
|
||||
# firmware uses (../main/mmwave_detect.h) — test and firmware can't disagree.
|
||||
test_mmwave_detect: test_mmwave_detect.c $(MAIN_DIR)/mmwave_detect.h
|
||||
cc -std=c99 -Wall -Wextra -I$(MAIN_DIR) -o $@ $<
|
||||
|
||||
run_mmwave_detect: test_mmwave_detect
|
||||
./test_mmwave_detect
|
||||
|
||||
host_tests: run_adr110 run_vitals run_mmwave_detect
|
||||
@echo "Host tests passed (ADR-110 + vitals #998/#996 + mmwave detect #1135)"
|
||||
|
||||
# --- Serialize fuzzer ---
|
||||
# Tests csi_serialize_frame() with random wifi_csi_info_t inputs.
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @file test_mmwave_detect.c
|
||||
* @brief Host-side unit tests for the LD2410 frame-validation predicate (#1135).
|
||||
*
|
||||
* Proves the phantom-detection fix: a floating UART can emit the 4-byte head
|
||||
* 0xF4F3F2F1, but the predicate rejects it unless a sane length + matching tail
|
||||
* 0xF8F7F6F5 are also present. Tests the REAL predicate from mmwave_detect.h
|
||||
* (the same code the firmware's probe_at_baud calls).
|
||||
*
|
||||
* cc -std=c99 -Wall -I../main -o test_mmwave_detect test_mmwave_detect.c && ./test_mmwave_detect
|
||||
*
|
||||
* Exits 0 on all-pass; prints the failing case otherwise.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mmwave_detect.h"
|
||||
|
||||
static int failures = 0;
|
||||
#define CHECK(cond, msg) do { \
|
||||
if (!(cond)) { printf("FAIL: %s\n", msg); failures++; } \
|
||||
else { printf("ok: %s\n", msg); } \
|
||||
} while (0)
|
||||
|
||||
/* Build a valid LD2410 report frame: F4F3F2F1 | len(LE) | data[len] | F8F7F6F5 */
|
||||
static int make_frame(uint8_t *out, uint16_t dlen)
|
||||
{
|
||||
int n = 0;
|
||||
out[n++] = 0xF4; out[n++] = 0xF3; out[n++] = 0xF2; out[n++] = 0xF1;
|
||||
out[n++] = (uint8_t)(dlen & 0xFF); out[n++] = (uint8_t)(dlen >> 8);
|
||||
for (uint16_t k = 0; k < dlen; k++) out[n++] = (uint8_t)(0xAA ^ k);
|
||||
out[n++] = 0xF8; out[n++] = 0xF7; out[n++] = 0xF6; out[n++] = 0xF5;
|
||||
return n;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t buf[256];
|
||||
|
||||
/* 1. A real basic-report frame (data len 13) validates. */
|
||||
int n = make_frame(buf, 13);
|
||||
CHECK(mmwave_ld2410_valid_at(buf, 0, n), "valid basic frame (len=13) accepted");
|
||||
|
||||
/* 2. A real engineering-report frame (data len 35) validates. */
|
||||
n = make_frame(buf, 35);
|
||||
CHECK(mmwave_ld2410_valid_at(buf, 0, n), "valid engineering frame (len=35) accepted");
|
||||
|
||||
/* 3. Head magic present but NO valid tail — the #1135 phantom case. */
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
buf[0]=0xF4; buf[1]=0xF3; buf[2]=0xF2; buf[3]=0xF1; buf[4]=13; buf[5]=0;
|
||||
/* data present but tail is zeros, not F8F7F6F5 */
|
||||
CHECK(!mmwave_ld2410_valid_at(buf, 0, 64), "head magic without valid tail REJECTED (#1135)");
|
||||
|
||||
/* 4. Head magic with insane length is rejected. */
|
||||
memset(buf, 0xFF, sizeof(buf));
|
||||
buf[0]=0xF4; buf[1]=0xF3; buf[2]=0xF2; buf[3]=0xF1; buf[4]=0xFF; buf[5]=0xFF; /* len=65535 */
|
||||
CHECK(!mmwave_ld2410_valid_at(buf, 0, 200), "head magic with oversized length REJECTED");
|
||||
|
||||
/* 5. Pure noise (no head) is rejected. */
|
||||
for (int k = 0; k < 64; k++) buf[k] = (uint8_t)(0x5A + k);
|
||||
CHECK(!mmwave_ld2410_valid_at(buf, 0, 64), "non-header noise REJECTED");
|
||||
|
||||
/* 6. Truncated frame (tail would run past the buffer) is rejected. */
|
||||
n = make_frame(buf, 13);
|
||||
CHECK(!mmwave_ld2410_valid_at(buf, 0, n - 2), "truncated frame (tail past buffer) REJECTED");
|
||||
|
||||
/* 7. Valid frame at a non-zero offset still validates. */
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
n = make_frame(buf + 7, 13);
|
||||
CHECK(mmwave_ld2410_valid_at(buf, 7, 7 + n), "valid frame at offset 7 accepted");
|
||||
|
||||
/* 8. Repeated head bytes without a frame (worst-case noise) rejected. */
|
||||
for (int k = 0; k + 3 < 64; k += 4) {
|
||||
buf[k]=0xF4; buf[k+1]=0xF3; buf[k+2]=0xF2; buf[k+3]=0xF1;
|
||||
}
|
||||
CHECK(!mmwave_ld2410_valid_at(buf, 0, 64), "repeated bare head bytes REJECTED");
|
||||
|
||||
printf("\n%s (%d failures)\n", failures ? "FAILED" : "ALL PASS", failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user