diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5119bf2..846fb57 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,7 @@ - Added XIAO-side persistent level calibration with stability checking and RS485 start/clear commands; no dashboard behavior changed. - Prevented echoed or oversized RS485 frames from starving the XIAO sensor loop. - Silently discard malformed partial RS485 echoes instead of emitting recursive `invalid_json` responses. +- Replaced sensor auto-detection with the verified installation settings: BNO086 address `0x4B` and MAX-M10S UART at 38400 baud. ## Unreleased diff --git a/firmware/xiao-esp32c6-sensor-node/README.md b/firmware/xiao-esp32c6-sensor-node/README.md index c9f2bea..3c62a0a 100644 --- a/firmware/xiao-esp32c6-sensor-node/README.md +++ b/firmware/xiao-esp32c6-sensor-node/README.md @@ -9,9 +9,9 @@ This Arduino sketch reads a BNO086 IMU and MAX-M10S GPS and publishes versioned, | GPS TX to XIAO RX | D9 | | GPS RX from XIAO TX | D10 | -Power both sensors from 3.3V with shared ground. Required Arduino libraries are Adafruit BNO08x, TinyGPSPlus, and ArduinoJson 6.x. Enable **USB CDC On Boot** for diagnostics. GPS uses hardware UART0 and RS485 uses hardware UART1. Startup probes 9600, 38400, 115200, and 4800 baud and reports the first rate producing checksum-valid NMEA. +Power both sensors from 3.3V with shared ground. Required Arduino libraries are Adafruit BNO08x, TinyGPSPlus, and ArduinoJson 6.x. Enable **USB CDC On Boot** for diagnostics. GPS uses hardware UART0 at the verified 38400 baud; RS485 uses hardware UART1. -At startup the firmware scans D7/D8 and accepts either valid BNO08x address, `0x4A` or `0x4B`. If neither appears in the scan, verify 3.3V/GND, swap-check SDA and SCL, and confirm the breakout's PS0/PS1 jumpers are configured for I2C. +The BNO086 uses the verified `0x4B` I2C address on D7/D8. If initialization fails, verify 3.3V/GND, SDA/SCL wiring, and the breakout's PS0/PS1 configuration. The node sends `sensor_status` at 115200 baud every 200 ms. See `docs/SENSOR_NODE_PROTOCOL.md`. The initial firmware is transmit-only; add a dashboard receiver after bench-testing the node, RS485 polarity, and sensor axes. diff --git a/firmware/xiao-esp32c6-sensor-node/gps.cpp b/firmware/xiao-esp32c6-sensor-node/gps.cpp index e7e921d..9ef974c 100644 --- a/firmware/xiao-esp32c6-sensor-node/gps.cpp +++ b/firmware/xiao-esp32c6-sensor-node/gps.cpp @@ -4,39 +4,13 @@ namespace { TinyGPSPlus parser; } namespace { -uint32_t activeBaud = GPS_FALLBACK_BAUD; - -bool baudHasValidNmea(HardwareSerial& serial, uint32_t baud) { - TinyGPSPlus probe; - serial.end(); - serial.begin(baud, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); - unsigned long started = millis(); - while (millis() - started < GPS_BAUD_DETECT_MS) { - while (serial.available()) probe.encode(serial.read()); - if (probe.passedChecksum() > 0) return true; - delay(1); - } - return false; -} +uint32_t activeBaud = GPS_BAUD; } void initGps(HardwareSerial& serial) { - const uint32_t candidates[] = {9600, 38400, 115200, 4800}; - Serial.println("Detecting GPS UART baud..."); - for (uint32_t baud : candidates) { - Serial.print("Trying GPS baud "); - Serial.println(baud); - if (!baudHasValidNmea(serial, baud)) continue; - activeBaud = baud; - Serial.print("Valid GPS NMEA detected at "); - Serial.println(activeBaud); - return; - } - - activeBaud = GPS_FALLBACK_BAUD; - serial.end(); serial.begin(activeBaud, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); - Serial.println("No valid GPS NMEA detected; falling back to 9600 baud"); + Serial.print("Starting GPS UART at "); + Serial.println(activeBaud); } void updateGps(HardwareSerial& serial, GpsState& s) { diff --git a/firmware/xiao-esp32c6-sensor-node/imu.cpp b/firmware/xiao-esp32c6-sensor-node/imu.cpp index f491cdf..5ef6197 100644 --- a/firmware/xiao-esp32c6-sensor-node/imu.cpp +++ b/firmware/xiao-esp32c6-sensor-node/imu.cpp @@ -9,26 +9,6 @@ Adafruit_BNO08x bno086(-1); sh2_SensorValue_t value; bool initialized = false; float radiansToDegrees(float r) { return r * 180.0f / PI; } -bool addressResponds(uint8_t address) { - Wire.beginTransmission(address); - return Wire.endTransmission() == 0; -} - -uint8_t findBnoAddress() { - Serial.println("Scanning I2C bus on D7 SDA / D8 SCL..."); - uint8_t bnoAddress = 0; - for (uint8_t address = 1; address < 127; address++) { - if (!addressResponds(address)) continue; - Serial.print("I2C device found at 0x"); - if (address < 0x10) Serial.print('0'); - Serial.println(address, HEX); - if (address == BNO086_I2C_ADDRESS || address == BNO086_I2C_ADDRESS_ALTERNATE) { - bnoAddress = address; - } - } - return bnoAddress; -} - void updateEuler(ImuState& s) { float sinr = 2 * (s.real * s.i + s.j * s.k); float cosr = 1 - 2 * (s.i * s.i + s.j * s.j); @@ -45,16 +25,9 @@ bool initImu() { Wire.begin(BNO086_SDA_PIN, BNO086_SCL_PIN); Wire.setClock(BNO086_I2C_CLOCK_HZ); delay(100); - - uint8_t address = findBnoAddress(); - if (address == 0) { - Serial.println("No BNO086 found at 0x4A or 0x4B"); - return false; - } - Serial.print("Starting BNO086 at 0x"); - Serial.println(address, HEX); - initialized = bno086.begin_I2C(address, &Wire); + Serial.println(BNO086_I2C_ADDRESS, HEX); + initialized = bno086.begin_I2C(BNO086_I2C_ADDRESS, &Wire); if (initialized) initialized = bno086.enableReport(SH2_ROTATION_VECTOR, IMU_REPORT_INTERVAL_US); return initialized; } diff --git a/firmware/xiao-esp32c6-sensor-node/sensor_node_config.h b/firmware/xiao-esp32c6-sensor-node/sensor_node_config.h index cf4cc56..91a568b 100644 --- a/firmware/xiao-esp32c6-sensor-node/sensor_node_config.h +++ b/firmware/xiao-esp32c6-sensor-node/sensor_node_config.h @@ -11,11 +11,9 @@ #define BNO086_SCL_PIN D8 #define GPS_RX_PIN D9 #define GPS_TX_PIN D10 -#define BNO086_I2C_ADDRESS 0x4A -#define BNO086_I2C_ADDRESS_ALTERNATE 0x4B +#define BNO086_I2C_ADDRESS 0x4B #define BNO086_I2C_CLOCK_HZ 100000 -#define GPS_FALLBACK_BAUD 9600 -#define GPS_BAUD_DETECT_MS 2500 +#define GPS_BAUD 38400 #define RS485_BAUD 115200 #define IMU_REPORT_INTERVAL_US 20000 #define TELEMETRY_INTERVAL_MS 200 diff --git a/tests/test_xiao_sensor_node_contract.py b/tests/test_xiao_sensor_node_contract.py index 66a08a5..76556ca 100644 --- a/tests/test_xiao_sensor_node_contract.py +++ b/tests/test_xiao_sensor_node_contract.py @@ -30,19 +30,17 @@ def test_imu_does_not_redeclare_arduino_degrees_macro(): assert "float degrees(" not in imu assert "radiansToDegrees" in imu -def test_imu_scans_both_bno08x_i2c_addresses(): +def test_imu_uses_verified_i2c_address(): config = source("sensor_node_config.h") imu = source("imu.cpp") - assert "BNO086_I2C_ADDRESS 0x4A" in config - assert "BNO086_I2C_ADDRESS_ALTERNATE 0x4B" in config - assert "findBnoAddress()" in imu - assert "Wire.endTransmission()" in imu + assert "BNO086_I2C_ADDRESS 0x4B" in config + assert "findBnoAddress" not in imu -def test_gps_auto_detects_common_nmea_baud_rates(): +def test_gps_uses_verified_uart_baud(): + config = source("sensor_node_config.h") gps = source("gps.cpp") - assert "baudHasValidNmea" in gps - assert "{9600, 38400, 115200, 4800}" in gps - assert "passedChecksum() > 0" in gps + assert "GPS_BAUD 38400" in config + assert "baudHasValidNmea" not in gps def test_level_calibration_is_persistent_and_non_blocking(): calibration = source("calibration.cpp")