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>
78 lines
3.3 KiB
C
78 lines
3.3 KiB
C
// Ai-Thinker RD-03E "simple report" frame scanner — PURE LOGIC.
|
|
//
|
|
// This unit is deliberately free of ESP-IDF: no UART, no esp_err_t, no
|
|
// FreeRTOS, no logging. It includes only <stdint.h>/<stddef.h>/<stdbool.h>
|
|
// so it can be compiled and tested on a host with plain gcc (see
|
|
// ../test/). `rd03e.c` does the UART I/O and calls in here to do the
|
|
// actual parsing.
|
|
//
|
|
// The reason this split exists: the first version of this firmware had
|
|
// RD03E_FRAME_LEN set to 5 for a 6-byte frame, so the footer check read
|
|
// the distance high byte instead of the second footer byte and every
|
|
// distance reading came back as `lo | 0x5500` (~218 m). That was pure
|
|
// logic with zero hardware dependency and should have been catchable on a
|
|
// laptop. Now it is.
|
|
//
|
|
// 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.
|
|
//
|
|
// Host tests prove the SHAPE of the parse (byte order, frame length,
|
|
// footer validation, resynchronisation) against this reconstructed spec.
|
|
// They cannot prove the reconstructed spec is what the silicon emits.
|
|
|
|
#pragma once
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
// Total bytes in one simple-report frame: header + gesture + 2 distance
|
|
// bytes + 2 footer bytes. Six, not five — see the note above.
|
|
#define RD03E_FRAME_LEN 6
|
|
|
|
#define RD03E_FRAME_HEADER 0xAAu
|
|
#define RD03E_FRAME_FOOTER0 0x55u
|
|
#define RD03E_FRAME_FOOTER1 0x55u
|
|
|
|
typedef struct {
|
|
uint8_t gesture;
|
|
uint16_t distance_cm;
|
|
} rd03e_frame_t;
|
|
|
|
// Scan `buf` (`len` bytes) for complete, footer-validated simple-report
|
|
// frames and write the NEWEST one (highest offset) to *out.
|
|
//
|
|
// Returns true if at least one valid frame was found, false otherwise
|
|
// (in which case *out is untouched). A NULL buf, a NULL out, or a buffer
|
|
// shorter than one frame are all "no frame", not a crash.
|
|
bool rd03e_parse_latest(const uint8_t *buf, size_t len, rd03e_frame_t *out);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|