test: make firmware logic bugs catchable without hardware (Workstream F)
The firmware has never been flashed, and a real bug already reached the repo because of it: RD03E_FRAME_LEN was 5 for a 6-byte frame, so the footer check collided with the distance high byte and EVERY distance reading was garbage — always `lo | 0x5500`, about 218 metres, regardless of what the sensor saw. That was pure logic with no hardware dependency. It should have been catchable on a laptop, and there was simply no way to run the code. Extracted the hardware-free logic out of the three drivers — rd03e_parse, bmp280_compensate, mems_level — as moves rather than rewrites, carrying the explanatory comments along with the code they explain. The drivers now own only their bus I/O and call into the pure units, so nothing changes for the real device. `./run_tests.sh` builds them with gcc -Wall -Wextra -Werror plus a dependency-free assert harness: 175 checks, 0 failed, from a clean tree. Proven to catch the actual bug rather than assumed to: reintroducing FRAME_LEN 5 fails four checks, including one that reads "a simple-report frame is 6 bytes, not 5", plus the truncated-frame and 5-byte-window cases. Restored, green again. This does NOT make the firmware verified, and the README says so plainly — it is called a narrow exception and scoped to pure logic. Wiring, timing, real register behaviour and the reconstructed RD-03E frame format all still need the physical board. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,32 +1,15 @@
|
||||
// Ai-Thinker RD-03E driver — see rd03e.h for wiring and honesty notes.
|
||||
//
|
||||
// UNVERIFIED AGAINST REAL HARDWARE, and the frame format below is
|
||||
// reconstructed from a third-party bring-up write-up (electroniclinic.com's
|
||||
// RD-03E/ESP32 tutorial), not Ai-Thinker's own datasheet PDF (not available
|
||||
// while writing this) — treat this as the least-certain protocol detail in
|
||||
// this driver. What's cross-confirmed from multiple independent sources:
|
||||
// UART is 256000 baud / 8N1, and the module also has a separate, more
|
||||
// complex configuration-frame protocol (0xFD 0xFC 0xFB 0xFA header /
|
||||
// 0x04 0x03 0x02 0x01 footer) for calibration and firmware queries — this
|
||||
// driver does NOT implement that; it only reads the module's free-running
|
||||
// "simple report" output frames, which need no configuration to start
|
||||
// streaming after power-up.
|
||||
//
|
||||
// Simple report frame, as reconstructed (6 bytes total):
|
||||
// [0] 0xAA frame header
|
||||
// [1] gesture code raw value, meaning not confirmed against an
|
||||
// official datasheet — reported as-is in
|
||||
// metadata rather than translated to a label
|
||||
// that might be wrong
|
||||
// [2] distance lo byte distance_cm = lo | (hi << 8), little-endian
|
||||
// [3] distance hi byte
|
||||
// [4..5] 0x55 0x55 frame footer
|
||||
// Real bring-up should verify this against a logic analyzer capture before
|
||||
// trusting field values, same as the LD2410 driver this replaced.
|
||||
// UNVERIFIED AGAINST REAL HARDWARE. This file owns only the UART I/O; the
|
||||
// frame format, the frame scanner, and the honesty notes about how that
|
||||
// format was reconstructed all live in rd03e_parse.h/.c, which is
|
||||
// ESP-IDF-free so it can be unit-tested on a host with plain gcc (see
|
||||
// ../test/). Read rd03e_parse.h before trusting any field value from here.
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "rd03e.h"
|
||||
#include "rd03e_parse.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -36,18 +19,12 @@ static const char *TAG = "rd03e";
|
||||
|
||||
#define RD03E_RX_BUF_SIZE 512
|
||||
#define RD03E_SCRATCH_SIZE 256
|
||||
#define RD03E_FRAME_LEN 6
|
||||
|
||||
static const uint8_t FRAME_HEADER = 0xAA;
|
||||
static const uint8_t FRAME_FOOTER[2] = { 0x55, 0x55 };
|
||||
// Frame layout, frame length and the header/footer constants live in
|
||||
// rd03e_parse.h — one definition, host-tested.
|
||||
|
||||
static bool s_ready = false;
|
||||
|
||||
typedef struct {
|
||||
uint8_t gesture;
|
||||
uint16_t distance_cm;
|
||||
} rd03e_frame_t;
|
||||
|
||||
esp_err_t rd03e_init(void) {
|
||||
uart_config_t cfg = {
|
||||
.baud_rate = RD03E_UART_BAUD,
|
||||
@@ -107,23 +84,9 @@ esp_err_t rd03e_read(sensor_reading_t *out, size_t max_out, size_t *out_count) {
|
||||
return ESP_OK; // nothing new isn't a driver failure
|
||||
}
|
||||
|
||||
bool parsed_any = false;
|
||||
rd03e_frame_t latest = {0};
|
||||
|
||||
// Scan for the newest complete, validated frame in whatever arrived
|
||||
// this cycle; keep overwriting `latest` so we report the freshest one.
|
||||
for (int i = 0; i + RD03E_FRAME_LEN <= len; i++) {
|
||||
if (buf[i] != FRAME_HEADER) {
|
||||
continue;
|
||||
}
|
||||
if (memcmp(&buf[i + 4], FRAME_FOOTER, 2) != 0) {
|
||||
continue; // not a real header byte, or a corrupted frame
|
||||
}
|
||||
latest.gesture = buf[i + 1];
|
||||
latest.distance_cm = (uint16_t)buf[i + 2] | ((uint16_t)buf[i + 3] << 8);
|
||||
parsed_any = true;
|
||||
i += RD03E_FRAME_LEN - 1; // loop's i++ moves past this frame
|
||||
}
|
||||
// All frame-finding/validation is in the pure, host-tested unit.
|
||||
bool parsed_any = rd03e_parse_latest(buf, (size_t)len, &latest);
|
||||
|
||||
if (!parsed_any) {
|
||||
ESP_LOGD(TAG, "no complete/valid RD-03E frame in this read window");
|
||||
|
||||
Reference in New Issue
Block a user