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:
154
firmware/esp32p4-sensor-node/main/rd03e.c
Normal file
154
firmware/esp32p4-sensor-node/main/rd03e.c
Normal file
@@ -0,0 +1,154 @@
|
||||
// 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 (5 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.
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "rd03e.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "rd03e";
|
||||
|
||||
#define RD03E_RX_BUF_SIZE 512
|
||||
#define RD03E_SCRATCH_SIZE 256
|
||||
#define RD03E_FRAME_LEN 5
|
||||
|
||||
static const uint8_t FRAME_HEADER = 0xAA;
|
||||
static const uint8_t FRAME_FOOTER[2] = { 0x55, 0x55 };
|
||||
|
||||
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,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
|
||||
esp_err_t err = uart_param_config(RD03E_UART_PORT, &cfg);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "uart_param_config failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
// uart_set_pin(port, tx_pin, rx_pin, rts_pin, cts_pin) -- our TX GPIO
|
||||
// wires to the module's RX (labeled "RX" on the module), and our RX
|
||||
// GPIO wires to the module's TX output (labeled "OT1" on the module).
|
||||
err = uart_set_pin(RD03E_UART_PORT, RD03E_UART_TX_GPIO, RD03E_UART_RX_GPIO,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "uart_set_pin failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = uart_driver_install(RD03E_UART_PORT, RD03E_RX_BUF_SIZE, 0, 0, NULL, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "uart_driver_install failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
s_ready = true;
|
||||
ESP_LOGI(TAG, "RD-03E UART init ok on port %d (%d baud)", RD03E_UART_PORT, RD03E_UART_BAUD);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t rd03e_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 < 1) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
uint8_t buf[RD03E_SCRATCH_SIZE];
|
||||
// The module free-runs its simple report frames, so a short read should
|
||||
// find at least one complete frame already queued in the RX ring buffer.
|
||||
int len = uart_read_bytes(RD03E_UART_PORT, buf, sizeof(buf), pdMS_TO_TICKS(200));
|
||||
if (len < 0) {
|
||||
ESP_LOGW(TAG, "uart_read_bytes error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (len == 0) {
|
||||
ESP_LOGD(TAG, "no UART data from RD-03E this cycle");
|
||||
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 + 3], 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
|
||||
}
|
||||
|
||||
if (!parsed_any) {
|
||||
ESP_LOGD(TAG, "no complete/valid RD-03E frame in this read window");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Reported as a numeric distance (not a boolean "presence" flag, unlike
|
||||
// the LD2410 this replaced) — a handheld "sense a presence at a
|
||||
// distance" device wants magnitude, and it lets the backend's
|
||||
// statistical anomaly detector treat "something got suddenly close" as
|
||||
// the anomaly signal, the same way it already treats a temperature
|
||||
// spike, rather than only firing on a coarse absent/present transition.
|
||||
memset(&out[0], 0, sizeof(out[0]));
|
||||
strncpy(out[0].sensor_type, "presence", SENSOR_READING_TYPE_MAXLEN - 1);
|
||||
out[0].value = (double)latest.distance_cm;
|
||||
strncpy(out[0].unit, "cm", SENSOR_READING_UNIT_MAXLEN - 1);
|
||||
|
||||
cJSON *meta = cJSON_CreateObject();
|
||||
if (meta != NULL) {
|
||||
// Raw code, not translated to a label -- see the honesty note above
|
||||
// about the gesture byte's exact meaning being unconfirmed.
|
||||
cJSON_AddNumberToObject(meta, "gesture_code", latest.gesture);
|
||||
}
|
||||
out[0].metadata = meta;
|
||||
|
||||
*out_count = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user