From e7141400d64554fce60f2e2baa7ef805fda86b7c Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 21 Oct 2026 14:26:07 -0700 Subject: [PATCH] Fix SolarXiao INA260 discovery and carrier I2C pins --- .../sensors/EnvironmentSensorManager.cpp | 10 ++++++---- test/test_xiao_battery_safety.py | 18 ++++++++++++++++-- variants/xiao_nrf52/XiaoNrf52Board.cpp | 4 +++- variants/xiao_nrf52/platformio.ini | 8 ++++++-- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 920d3100a..dd93d9254 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -919,14 +919,16 @@ static const SensorDef SENSOR_TABLE[] = { { TELEM_INA3221_ADDRESS, "INA3221", init_ina3221, query_ina3221, query_ina3221_voltage }, #endif +#if ENV_INCLUDE_INA260 + // INA260 verifies its device ID before configuring the chip. Give it first + // claim when it shares 0x40 with INA219, whose driver has no identity check. + { TELEM_INA260_ADDRESS, "INA260", init_ina260, query_ina260, + query_ina260_voltage }, +#endif #if ENV_INCLUDE_INA219 { TELEM_INA219_ADDRESS, "INA219", init_ina219, query_ina219, query_ina219_voltage }, #endif -#if ENV_INCLUDE_INA260 - { TELEM_INA260_ADDRESS, "INA260", init_ina260, query_ina260, - query_ina260_voltage }, -#endif #if ENV_INCLUDE_INA226 { TELEM_INA226_ADDRESS, "INA226", init_ina226, query_ina226, query_ina226_voltage }, diff --git a/test/test_xiao_battery_safety.py b/test/test_xiao_battery_safety.py index 91aee038c..7f14984ee 100644 --- a/test/test_xiao_battery_safety.py +++ b/test/test_xiao_battery_safety.py @@ -88,7 +88,11 @@ void pinMode(int pin,int value) { void delay(unsigned) {} void analogReadResolution(int) {} void analogReference(int) {} -struct FakeWire { void setPins(int,int) {} void begin() {} } Wire; +struct FakeWire { + int sda=-1, scl=-1; + void setPins(int data,int clock) { sda=data; scl=clock; } + void begin() {} +} Wire; struct NRF52Board { static void begin() {} }; struct NRF52BoardDCDC { static void begin() {} }; struct Config { int lpcomp_ain_channel; int lpcomp_refsel; }; @@ -108,6 +112,10 @@ int main() { // Repeated startup, optional peripherals and USB/charging status must not // create a HIGH pulse; the production code deliberately does not infer USB. for(int repeat=0;repeat<3;++repeat) { @CALLS@ } +#ifdef CHECK_XIAO_BOARD_I2C + reset(); XiaoNrf52Board().begin(); check(); + assert(Wire.sda==30 && Wire.scl==31); +#endif for(uint8_t reason : {SHUTDOWN_REASON_LOW_VOLTAGE, SHUTDOWN_REASON_BOOT_PROTECT, SHUTDOWN_REASON_USER, 0}) { reset(); shutdown(reason); check(); @@ -122,7 +130,7 @@ int main() { class XiaoBatterySafetyTests(unittest.TestCase): - def run_case(self, optional=False, mutation=None): + def run_case(self, optional=False, mutation=None, board_i2c=False): compiler = os.environ.get("CXX") or shutil.which("g++") or shutil.which("clang++") self.assertIsNotNone(compiler, "a host C++11 compiler is required") with tempfile.TemporaryDirectory(prefix="xiao-battery-safety-") as directory: @@ -134,6 +142,9 @@ class XiaoBatterySafetyTests(unittest.TestCase): flags = ["-DNRF52_POWER_MANAGEMENT"] if optional: flags.append("-DOPTIONAL_PINS") + if board_i2c: + flags += ["-DPIN_BOARD_SDA=30", "-DPIN_BOARD_SCL=31", + "-DCHECK_XIAO_BOARD_I2C"] build = subprocess.run([compiler, "-std=c++11", "-Wall", "-Wextra", "-Werror", *sanitizer, *flags, str(cpp), "-o", str(binary)], text=True, capture_output=True, timeout=30) @@ -150,6 +161,9 @@ class XiaoBatterySafetyTests(unittest.TestCase): def test_optional_button_i2c_display_led_startup(self): self.run_case(optional=True) + def test_carrier_i2c_pins_override_generic_wire_pins(self): + self.run_case(optional=True, board_i2c=True) + def test_old_ikoka_high_startup_is_rejected(self): self.run_case(mutation="old-begin-high") diff --git a/variants/xiao_nrf52/XiaoNrf52Board.cpp b/variants/xiao_nrf52/XiaoNrf52Board.cpp index 5812d6fa2..3ad000955 100644 --- a/variants/xiao_nrf52/XiaoNrf52Board.cpp +++ b/variants/xiao_nrf52/XiaoNrf52Board.cpp @@ -46,7 +46,9 @@ void XiaoNrf52Board::begin() { pinMode(PIN_USER_BTN, INPUT_PULLUP); #endif -#if defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); +#elif defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL); #endif diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 553326708..b219ceb98 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -167,8 +167,9 @@ lib_deps = ; ============================================================================= ; SolarXiao (wehooper4) boards -; Same Xiao nRF52840 module. I2C is already on the NFC pads in the base target; -; these solar builds additionally skip the RTC probe. +; Same Xiao nRF52840 module. The carrier uses SDA=P0.09, SCL=P0.10 +; on the NFC pads, and its INA260 is at 0x40. Skip the RTC probe because +; the LTC4162 charger shares the usual RTC address. ; 30S = standard SX1262 (22 dBm), 33S = 2W PA variant (9 dBm limit) ; ============================================================================= @@ -176,6 +177,9 @@ lib_deps = extends = Xiao_nrf52 build_flags = ${Xiao_nrf52.build_flags} -D DISABLE_I2C_RTC_SCAN + -D PIN_BOARD_SDA=PIN_NFC1 + -D PIN_BOARD_SCL=PIN_NFC2 + -D TELEM_INA260_ADDRESS=0x40 -D MANUFACTURER_STRING='"SolarXiao 30S"' [env:solarxiao_30S_companion_radio_ble]