Files
qtalker---/firmware/esp32p4-sensor-node/main/bmp280_compensate.c
Indiana 0966fa8cfc 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>
2026-07-31 13:17:35 +00:00

67 lines
2.7 KiB
C

// BMP280 compensation maths — pure logic. See bmp280_compensate.h.
#include "bmp280_compensate.h"
static int16_t s16(uint8_t lsb, uint8_t msb) {
return (int16_t)((uint16_t)msb << 8 | lsb);
}
static uint16_t u16(uint8_t lsb, uint8_t msb) {
return (uint16_t)((uint16_t)msb << 8 | lsb);
}
void bmp280_calib_from_regs(const uint8_t buf[BMP280_CALIB_LEN], bmp280_calib_t *out) {
if (buf == NULL || out == NULL) {
return;
}
out->dig_T1 = u16(buf[0], buf[1]);
out->dig_T2 = s16(buf[2], buf[3]);
out->dig_T3 = s16(buf[4], buf[5]);
out->dig_P1 = u16(buf[6], buf[7]);
out->dig_P2 = s16(buf[8], buf[9]);
out->dig_P3 = s16(buf[10], buf[11]);
out->dig_P4 = s16(buf[12], buf[13]);
out->dig_P5 = s16(buf[14], buf[15]);
out->dig_P6 = s16(buf[16], buf[17]);
out->dig_P7 = s16(buf[18], buf[19]);
out->dig_P8 = s16(buf[20], buf[21]);
out->dig_P9 = s16(buf[22], buf[23]);
}
void bmp280_adc_from_regs(const uint8_t raw[BMP280_RAW_LEN], int32_t *out_adc_P, int32_t *out_adc_T) {
if (raw == NULL || out_adc_P == NULL || out_adc_T == NULL) {
return;
}
*out_adc_P = ((int32_t)raw[0] << 12) | ((int32_t)raw[1] << 4) | (raw[2] >> 4);
*out_adc_T = ((int32_t)raw[3] << 12) | ((int32_t)raw[4] << 4) | (raw[5] >> 4);
}
// Bosch datasheet 3.11.3 double-precision reference compensation formulas,
// transcribed near-verbatim (variable names kept close to the original so
// it's checkable against the datasheet PDF side-by-side).
double bmp280_compensate_temperature(const bmp280_calib_t *c, int32_t adc_T, double *out_t_fine) {
double var1 = (((double)adc_T) / 16384.0 - ((double)c->dig_T1) / 1024.0) * ((double)c->dig_T2);
double var2 = ((((double)adc_T) / 131072.0 - ((double)c->dig_T1) / 8192.0) *
(((double)adc_T) / 131072.0 - ((double)c->dig_T1) / 8192.0)) * ((double)c->dig_T3);
*out_t_fine = var1 + var2;
return (var1 + var2) / 5120.0; // degrees C
}
double bmp280_compensate_pressure(const bmp280_calib_t *c, int32_t adc_P, double t_fine) {
double var1 = (t_fine / 2.0) - 64000.0;
double var2 = var1 * var1 * ((double)c->dig_P6) / 32768.0;
var2 = var2 + var1 * ((double)c->dig_P5) * 2.0;
var2 = (var2 / 4.0) + (((double)c->dig_P4) * 65536.0);
var1 = (((double)c->dig_P3) * var1 * var1 / 524288.0 + ((double)c->dig_P2) * var1) / 524288.0;
var1 = (1.0 + var1 / 32768.0) * ((double)c->dig_P1);
if (var1 == 0.0) {
return 0.0; // avoid divide-by-zero per datasheet's own guard
}
double p = 1048576.0 - (double)adc_P;
p = (p - (var2 / 4096.0)) * 6250.0 / var1;
var1 = ((double)c->dig_P9) * p * p / 2147483648.0;
var2 = p * ((double)c->dig_P8) / 32768.0;
p = p + (var1 + var2 + ((double)c->dig_P7)) / 16.0;
return p; // Pa
}