firmware: verify against actual target hardware, fix real conflicts
Researched the exact modules the user is building with and fixed three
concrete issues the earlier speculative firmware got wrong:
1. WiFi: confirmed the target board (Waveshare ESP32-P4-Module-DEV-KIT,
chip ESP32-P4NRW32) bridges WiFi through an onboard ESP32-C6
co-processor over a fixed 7-pin SDIO link (CLK18/CMD19/D0-14/D1-15/
D2-16/D3-17/RESET54, cross-confirmed against Espressif's own
esp-hosted-mcu docs). wifi_manager.c's esp_wifi_init()/esp_wifi_start()
calls don't need to change — esp_wifi_remote/esp_hosted provide a
drop-in-compatible API — but the component manifest (new
main/idf_component.yml) and sdkconfig.defaults were missing entirely.
2. Real pin conflict: the presence sensor's original UART pins (17/18)
directly collided with the SDIO CLK/D3 pins above — wiring it there
would have broken WiFi, the sensor, or both. Moved to GPIO4/5.
3. Swapped placeholder parts for the user's actual hardware:
- BME280 -> BMP280 (GY-BMP280 module): temp+pressure only, no humidity.
Rewrote the driver rather than just renaming it — the old code would
have read nonexistent humidity registers and reported garbage
forever. 3.3V-only wiring note added (the BME280 assumption of
5V-tolerant logic doesn't hold for this specific breakout).
- LD2410 -> RD-03E (Ai-Thinker, not Hi-Link — a different manufacturer
with a different, incompatible UART protocol). Rewrote the frame
parser against the RD-03E's actual (if less-documented) 5-byte
simple-report format. Reports numeric distance instead of a boolean,
which better fits both the hardware's actual output and the backend's
statistical anomaly detector.
README, CMakeLists.txt, and all cross-references updated to match.
This commit is contained in:
244
firmware/esp32p4-sensor-node/main/bmp280.c
Normal file
244
firmware/esp32p4-sensor-node/main/bmp280.c
Normal file
@@ -0,0 +1,244 @@
|
||||
// Bosch BMP280 driver — see bmp280.h for wiring and honesty notes.
|
||||
//
|
||||
// UNVERIFIED AGAINST REAL HARDWARE: this has been written against the public
|
||||
// BMP280 datasheet (Bosch Sensortec, document rev 1.23) and ESP-IDF's
|
||||
// documented `driver/i2c_master.h` API surface, and reasoned about carefully,
|
||||
// but never compiled with a real ESP-IDF toolchain nor run against a real
|
||||
// sensor. Register addresses and the compensation formula below are
|
||||
// transcribed as directly as possible from the datasheet's section 3.11.1
|
||||
// (register map) and 3.11.3 (double-precision compensation formula
|
||||
// reference implementation) to minimize transcription risk, but a real
|
||||
// bring-up should sanity-check first readings against a known-good
|
||||
// reference (e.g. compare to a household thermometer/barometer).
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include "bmp280.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "bmp280";
|
||||
|
||||
// --- Register map (BMP280 datasheet section 3.11.1) ------------------------
|
||||
#define REG_CHIP_ID 0xD0
|
||||
#define REG_RESET 0xE0
|
||||
#define REG_STATUS 0xF3
|
||||
#define REG_CTRL_MEAS 0xF4
|
||||
#define REG_CONFIG 0xF5
|
||||
#define REG_PRESS_MSB 0xF7 // press(3) + temp(3) = 6 bytes, burst-read from here
|
||||
#define REG_CALIB00 0x88 // dig_T1..dig_P9, 24 bytes: 0x88-0x9F
|
||||
|
||||
#define CHIP_ID_EXPECTED 0x58 // BMP280 (vs. BME280's 0x60 — a chip-ID
|
||||
// mismatch here likely means a BME280 is
|
||||
// wired instead; we proceed anyway since the
|
||||
// temp/pressure register map and compensation
|
||||
// math is identical between the two parts)
|
||||
|
||||
#define RESET_MAGIC 0xB6
|
||||
|
||||
#define STATUS_MEASURING_BIT 0x08
|
||||
|
||||
typedef struct {
|
||||
uint16_t dig_T1;
|
||||
int16_t dig_T2;
|
||||
int16_t dig_T3;
|
||||
uint16_t dig_P1;
|
||||
int16_t dig_P2;
|
||||
int16_t dig_P3;
|
||||
int16_t dig_P4;
|
||||
int16_t dig_P5;
|
||||
int16_t dig_P6;
|
||||
int16_t dig_P7;
|
||||
int16_t dig_P8;
|
||||
int16_t dig_P9;
|
||||
} bmp280_calib_t;
|
||||
|
||||
static i2c_master_bus_handle_t s_bus = NULL;
|
||||
static i2c_master_dev_handle_t s_dev = NULL;
|
||||
static bmp280_calib_t s_calib;
|
||||
static bool s_ready = false;
|
||||
|
||||
static esp_err_t write_reg(uint8_t reg, uint8_t val) {
|
||||
uint8_t buf[2] = { reg, val };
|
||||
return i2c_master_transmit(s_dev, buf, sizeof(buf), 1000 /* ms */);
|
||||
}
|
||||
|
||||
static esp_err_t read_regs(uint8_t reg, uint8_t *out, size_t len) {
|
||||
return i2c_master_transmit_receive(s_dev, ®, 1, out, len, 1000 /* ms */);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static esp_err_t read_calibration(void) {
|
||||
uint8_t buf[24]; // 0x88..0x9F
|
||||
|
||||
esp_err_t err = read_regs(REG_CALIB00, buf, sizeof(buf));
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
s_calib.dig_T1 = u16(buf[0], buf[1]);
|
||||
s_calib.dig_T2 = s16(buf[2], buf[3]);
|
||||
s_calib.dig_T3 = s16(buf[4], buf[5]);
|
||||
s_calib.dig_P1 = u16(buf[6], buf[7]);
|
||||
s_calib.dig_P2 = s16(buf[8], buf[9]);
|
||||
s_calib.dig_P3 = s16(buf[10], buf[11]);
|
||||
s_calib.dig_P4 = s16(buf[12], buf[13]);
|
||||
s_calib.dig_P5 = s16(buf[14], buf[15]);
|
||||
s_calib.dig_P6 = s16(buf[16], buf[17]);
|
||||
s_calib.dig_P7 = s16(buf[18], buf[19]);
|
||||
s_calib.dig_P8 = s16(buf[20], buf[21]);
|
||||
s_calib.dig_P9 = s16(buf[22], buf[23]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bmp280_init(void) {
|
||||
i2c_master_bus_config_t bus_cfg = {
|
||||
.i2c_port = BMP280_I2C_PORT,
|
||||
.sda_io_num = BMP280_I2C_SDA_GPIO,
|
||||
.scl_io_num = BMP280_I2C_SCL_GPIO,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.flags.enable_internal_pullup = true,
|
||||
};
|
||||
esp_err_t err = i2c_new_master_bus(&bus_cfg, &s_bus);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c_new_master_bus failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = BMP280_I2C_ADDR,
|
||||
.scl_speed_hz = BMP280_I2C_CLK_HZ,
|
||||
};
|
||||
err = i2c_master_bus_add_device(s_bus, &dev_cfg, &s_dev);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c_master_bus_add_device failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t chip_id = 0;
|
||||
err = read_regs(REG_CHIP_ID, &chip_id, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "chip id read failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
if (chip_id != CHIP_ID_EXPECTED) {
|
||||
ESP_LOGW(TAG, "unexpected chip id 0x%02x (want 0x%02x) -- check wiring/address", chip_id, CHIP_ID_EXPECTED);
|
||||
// Don't hard-fail: temp+pressure register map/compensation is
|
||||
// identical on a BME280 too, so a mis-wired-but-present sensor of
|
||||
// either part can still usefully report both readings.
|
||||
}
|
||||
|
||||
err = write_reg(REG_RESET, RESET_MAGIC);
|
||||
if (err != ESP_OK) return err;
|
||||
vTaskDelay(pdMS_TO_TICKS(10)); // datasheet: allow >= 2ms after reset
|
||||
|
||||
err = read_calibration();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "calibration read failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
s_ready = true;
|
||||
ESP_LOGI(TAG, "BMP280 init ok (chip id 0x%02x)", chip_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// 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).
|
||||
|
||||
static double compensate_temperature(int32_t adc_T, double *out_t_fine) {
|
||||
double var1 = (((double)adc_T) / 16384.0 - ((double)s_calib.dig_T1) / 1024.0) * ((double)s_calib.dig_T2);
|
||||
double var2 = ((((double)adc_T) / 131072.0 - ((double)s_calib.dig_T1) / 8192.0) *
|
||||
(((double)adc_T) / 131072.0 - ((double)s_calib.dig_T1) / 8192.0)) * ((double)s_calib.dig_T3);
|
||||
*out_t_fine = var1 + var2;
|
||||
return (var1 + var2) / 5120.0; // degrees C
|
||||
}
|
||||
|
||||
static double compensate_pressure(int32_t adc_P, double t_fine) {
|
||||
double var1 = (t_fine / 2.0) - 64000.0;
|
||||
double var2 = var1 * var1 * ((double)s_calib.dig_P6) / 32768.0;
|
||||
var2 = var2 + var1 * ((double)s_calib.dig_P5) * 2.0;
|
||||
var2 = (var2 / 4.0) + (((double)s_calib.dig_P4) * 65536.0);
|
||||
var1 = (((double)s_calib.dig_P3) * var1 * var1 / 524288.0 + ((double)s_calib.dig_P2) * var1) / 524288.0;
|
||||
var1 = (1.0 + var1 / 32768.0) * ((double)s_calib.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)s_calib.dig_P9) * p * p / 2147483648.0;
|
||||
var2 = p * ((double)s_calib.dig_P8) / 32768.0;
|
||||
p = p + (var1 + var2 + ((double)s_calib.dig_P7)) / 16.0;
|
||||
return p; // Pa
|
||||
}
|
||||
|
||||
esp_err_t bmp280_read(sensor_reading_t *out, size_t max_out, size_t *out_count) {
|
||||
*out_count = 0;
|
||||
if (!s_ready) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (max_out < 2) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Forced mode: osrs_t=1 (001), osrs_p=1 (001), mode=forced (01).
|
||||
// ctrl_meas = 0b001_001_01 = 0x25
|
||||
esp_err_t err = write_reg(REG_CTRL_MEAS, 0x25);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// Poll status until the "measuring" bit clears, with a hard cap so a
|
||||
// wedged bus/sensor can't hang the telemetry task forever.
|
||||
for (int attempt = 0; attempt < 20; attempt++) {
|
||||
uint8_t status = 0;
|
||||
err = read_regs(REG_STATUS, &status, 1);
|
||||
if (err != ESP_OK) return err;
|
||||
if ((status & STATUS_MEASURING_BIT) == 0) {
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
if (attempt == 19) {
|
||||
ESP_LOGW(TAG, "measurement did not complete in time");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t raw[6];
|
||||
err = read_regs(REG_PRESS_MSB, raw, sizeof(raw));
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
int32_t adc_P = ((int32_t)raw[0] << 12) | ((int32_t)raw[1] << 4) | (raw[2] >> 4);
|
||||
int32_t adc_T = ((int32_t)raw[3] << 12) | ((int32_t)raw[4] << 4) | (raw[5] >> 4);
|
||||
|
||||
double t_fine = 0.0;
|
||||
double temp_c = compensate_temperature(adc_T, &t_fine);
|
||||
double press_pa = compensate_pressure(adc_P, t_fine);
|
||||
|
||||
size_t n = 0;
|
||||
memset(&out[n], 0, sizeof(out[n]));
|
||||
strncpy(out[n].sensor_type, "temperature", SENSOR_READING_TYPE_MAXLEN - 1);
|
||||
out[n].value = temp_c;
|
||||
strncpy(out[n].unit, "c", SENSOR_READING_UNIT_MAXLEN - 1);
|
||||
out[n].metadata = NULL;
|
||||
n++;
|
||||
|
||||
memset(&out[n], 0, sizeof(out[n]));
|
||||
strncpy(out[n].sensor_type, "pressure", SENSOR_READING_TYPE_MAXLEN - 1);
|
||||
out[n].value = press_pa / 100.0; // Pa -> hPa
|
||||
strncpy(out[n].unit, "hpa", SENSOR_READING_UNIT_MAXLEN - 1);
|
||||
out[n].metadata = NULL;
|
||||
n++;
|
||||
|
||||
*out_count = n;
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user