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:
@@ -2,13 +2,12 @@
|
||||
//
|
||||
// UNVERIFIED AGAINST REAL HARDWARE. Written against ESP-IDF's documented
|
||||
// `driver/i2s_std.h` API (the current idiomatic I2S driver, superseding the
|
||||
// older monolithic `driver/i2s.h`) and the INMP441 family's well-documented
|
||||
// output format: 24-bit signed PCM, MSB-first, left-justified in a 32-bit
|
||||
// I2S slot (Philips/standard I2S timing). The right-shift-by-8 used below
|
||||
// to recover the 24-bit sample from the 32-bit slot, and the dBFS
|
||||
// reference level (2^23, a 24-bit signed sample's full-scale magnitude),
|
||||
// are the commonly-documented values for this exact mic family — but
|
||||
// "commonly documented" is not "verified against this specific board," so
|
||||
// older monolithic `driver/i2s.h`). This file owns only the I2S traffic;
|
||||
// the RMS -> dBFS maths, the 24-bit-in-32-bit-slot shift and the honesty
|
||||
// notes about both live in mems_level.h/.c, which is ESP-IDF-free and
|
||||
// unit-tested on a host with plain gcc (see ../test/).
|
||||
//
|
||||
// "Commonly documented" is not "verified against this specific board," so
|
||||
// treat the very first real readings as a sanity check, not a given: talk
|
||||
// near the mic and confirm the reported level actually rises before
|
||||
// trusting it unattended.
|
||||
@@ -18,6 +17,7 @@
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "mems_mic.h"
|
||||
#include "mems_level.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -25,8 +25,7 @@
|
||||
|
||||
static const char *TAG = "mems_mic";
|
||||
|
||||
// dBFS reference: full-scale magnitude of a 24-bit signed sample.
|
||||
#define FULL_SCALE_24BIT (8388608.0) // 2^23
|
||||
// dBFS reference level and noise floor live in mems_level.h.
|
||||
|
||||
static i2s_chan_handle_t s_rx_chan = NULL;
|
||||
static bool s_ready = false;
|
||||
@@ -119,26 +118,9 @@ esp_err_t mems_mic_read(sensor_reading_t *out, size_t max_out, size_t *out_count
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// RMS over the block. The mic's 24-bit sample is left-justified in the
|
||||
// 32-bit I2S slot -- shift right 8 to recover it before squaring, so
|
||||
// the magnitude lines up with FULL_SCALE_24BIT below.
|
||||
double sum_sq = 0.0;
|
||||
for (size_t i = 0; i < n_samples; i++) {
|
||||
double sample = (double)(s_sample_buf[i] >> 8);
|
||||
sum_sq += sample * sample;
|
||||
}
|
||||
double rms = sqrt(sum_sq / (double)n_samples);
|
||||
|
||||
// dBFS: 20*log10(rms / full_scale). A true-silent input gives rms=0,
|
||||
// which is -inf in dB -- clamp to a floor rather than emit a value the
|
||||
// JSON encoder/backend can't handle.
|
||||
double dbfs;
|
||||
if (rms < 1.0) {
|
||||
dbfs = -120.0; // effective noise floor
|
||||
} else {
|
||||
dbfs = 20.0 * log10(rms / FULL_SCALE_24BIT);
|
||||
if (dbfs < -120.0) dbfs = -120.0;
|
||||
}
|
||||
// All level arithmetic is in the pure, host-tested unit.
|
||||
double rms = mems_level_rms(s_sample_buf, n_samples);
|
||||
double dbfs = mems_level_dbfs(rms);
|
||||
|
||||
memset(&out[0], 0, sizeof(out[0]));
|
||||
strncpy(out[0].sensor_type, "evp", SENSOR_READING_TYPE_MAXLEN - 1);
|
||||
|
||||
Reference in New Issue
Block a user