From a1a2f399d1c5916be1c77a13101c0d44e03800d8 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 17 Jul 2026 01:59:02 -0600 Subject: [PATCH] firmware: diagnose BNO086 I2C address --- firmware/xiao-esp32c6-sensor-node/README.md | 3 +- firmware/xiao-esp32c6-sensor-node/imu.cpp | 33 ++++++++++++++++++- .../sensor_node_config.h | 3 +- tests/test_xiao_sensor_node_contract.py | 8 +++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/firmware/xiao-esp32c6-sensor-node/README.md b/firmware/xiao-esp32c6-sensor-node/README.md index bd15350..36f5419 100644 --- a/firmware/xiao-esp32c6-sensor-node/README.md +++ b/firmware/xiao-esp32c6-sensor-node/README.md @@ -11,5 +11,6 @@ This Arduino sketch reads a BNO086 IMU and MAX-M10S GPS and publishes versioned, 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. -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. +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 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/imu.cpp b/firmware/xiao-esp32c6-sensor-node/imu.cpp index 16e0f54..f491cdf 100644 --- a/firmware/xiao-esp32c6-sensor-node/imu.cpp +++ b/firmware/xiao-esp32c6-sensor-node/imu.cpp @@ -9,6 +9,26 @@ 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); @@ -23,7 +43,18 @@ void updateEuler(ImuState& s) { bool initImu() { Wire.begin(BNO086_SDA_PIN, BNO086_SCL_PIN); - initialized = bno086.begin_I2C(BNO086_I2C_ADDRESS, &Wire); + 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); 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 716f125..b95ac61 100644 --- a/firmware/xiao-esp32c6-sensor-node/sensor_node_config.h +++ b/firmware/xiao-esp32c6-sensor-node/sensor_node_config.h @@ -12,9 +12,10 @@ #define GPS_RX_PIN D9 #define GPS_TX_PIN D10 #define BNO086_I2C_ADDRESS 0x4A +#define BNO086_I2C_ADDRESS_ALTERNATE 0x4B +#define BNO086_I2C_CLOCK_HZ 100000 #define GPS_BAUD 9600 #define RS485_BAUD 115200 #define IMU_REPORT_INTERVAL_US 20000 #define TELEMETRY_INTERVAL_MS 200 #define SENSOR_STALE_MS 2000 - diff --git a/tests/test_xiao_sensor_node_contract.py b/tests/test_xiao_sensor_node_contract.py index a0ea602..e0139d0 100644 --- a/tests/test_xiao_sensor_node_contract.py +++ b/tests/test_xiao_sensor_node_contract.py @@ -26,6 +26,14 @@ 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(): + 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 + def test_node_does_not_take_cargo_or_ble_authority(): combined = "\n".join(p.read_text() for p in NODE.glob("*.*")) assert "NimBLE" not in combined and "JBD" not in combined and "server.on(" not in combined