From 050a7e2d0a8a94f2fe4bb70418df79f38387c908 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 30 Apr 2026 23:00:19 +0700 Subject: [PATCH] Fixed sensor initialization and automatic selection of I2C addresses for BME/BMP sensors - Added i2c_probe to probe sensors before initializing to avoid setup unnecessary sensors and to avoid bugs from unused sensors to impact the main code - Added automatically selection of I2C addresses 0x76 and 0x77 for BME280, BMP280 and BME680. --- src/helpers/SensorManager.h | 2 + .../sensors/EnvironmentSensorManager.cpp | 133 ++++++++++-------- .../sensors/EnvironmentSensorManager.h | 1 + 3 files changed, 81 insertions(+), 55 deletions(-) diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 89a174c228..d4aa63b70f 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -2,6 +2,7 @@ #include #include "sensors/LocationProvider.h" +#include #define TELEM_PERM_BASE 0x01 // 'base' permission includes battery #define TELEM_PERM_LOCATION 0x02 @@ -15,6 +16,7 @@ class SensorManager { double node_altitude; // altitude in meters SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } + virtual bool i2c_probe(TwoWire& wire, uint8_t addr) { return false; } virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 19472406d8..749ea68971 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -7,8 +7,10 @@ #endif #ifdef ENV_INCLUDE_BME680 -#ifndef TELEM_BME680_ADDRESS -#define TELEM_BME680_ADDRESS 0x76 +#if defined(TELEM_BME680_ADDRESS) +uint8_t TELEM_BME680_ADDRESSES[] = { TELEM_BME680_ADDRESS }; +#else +uint8_t TELEM_BME680_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BME680 #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include @@ -28,8 +30,10 @@ static Adafruit_AHTX0 AHTX0; #endif #if ENV_INCLUDE_BME280 -#ifndef TELEM_BME280_ADDRESS -#define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address +#if defined(TELEM_BME280_ADDRESS) +uint8_t TELEM_BME280_ADDRESSES[] = { TELEM_BME280_ADDRESS }; +#else +uint8_t TELEM_BME280_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BME280 #endif #define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level #include @@ -37,8 +41,10 @@ static Adafruit_BME280 BME280; #endif #if ENV_INCLUDE_BMP280 -#ifndef TELEM_BMP280_ADDRESS -#define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address +#if defined(TELEM_BMP280_ADDRESS) +uint8_t TELEM_BMP280_ADDRESSES[] = { TELEM_BMP280_ADDRESS }; +#else +uint8_t TELEM_BMP280_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BMP280 #endif #define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level #include @@ -161,6 +167,12 @@ class RAK12500LocationProvider : public LocationProvider { static RAK12500LocationProvider RAK12500_provider; #endif +bool EnvironmentSensorManager::i2c_probe(TwoWire &wire, uint8_t addr) { + wire.beginTransmission(addr); + uint8_t error = wire.endTransmission(); + return (error == 0); // If found i2c device, the error is 0 +} + bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS #ifdef RAK_WISBLOCK_GPS @@ -182,7 +194,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_AHTX0 - if (AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { + if (i2c_probe(*TELEM_WIRE, TELEM_AHTX_ADDRESS) && AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); AHTX0_initialized = true; } else { @@ -192,46 +204,55 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_BME680 - if (BME680.begin(TELEM_BME680_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); - BME680_initialized = true; - } else { - BME680_initialized = false; - MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESS); + for (size_t i = 0; i < sizeof(TELEM_BME680_ADDRESSES) / sizeof(TELEM_BME680_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BME680_ADDRESSES[i]) && BME680.begin(TELEM_BME680_ADDRESSES[i], TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESSES[i]); + BME680_initialized = true; + break; + } else { + BME680_initialized = false; + MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESSES[i]); + } } #endif #if ENV_INCLUDE_BME280 - if (BME280.begin(TELEM_BME280_ADDRESS, TELEM_WIRE)) { - MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); - MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); - // Reduce self-heating: single-shot conversions, light oversampling, long standby. - BME280.setSampling(Adafruit_BME280::MODE_FORCED, + for (size_t i = 0; i < sizeof(TELEM_BME280_ADDRESSES) / sizeof(TELEM_BME280_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BME280_ADDRESSES[i]) && BME280.begin(TELEM_BME280_ADDRESSES[i], TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESSES[i]); + MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); + // Reduce self-heating: single-shot conversions, light oversampling, long standby. + BME280.setSampling(Adafruit_BME280::MODE_FORCED, Adafruit_BME280::SAMPLING_X1, // temperature Adafruit_BME280::SAMPLING_X1, // pressure Adafruit_BME280::SAMPLING_X1, // humidity Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000); - BME280_initialized = true; - } else { - BME280_initialized = false; - MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS); + BME280_initialized = true; + break; + } else { + BME280_initialized = false; + MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESSES[i]); + } } - #endif +#endif #if ENV_INCLUDE_BMP280 - if (BMP280.begin(TELEM_BMP280_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESS); - MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID()); - BMP280_initialized = true; - } else { - BMP280_initialized = false; - MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESS); - } - #endif + for (size_t i = 0; i < sizeof(TELEM_BMP280_ADDRESSES) / sizeof(TELEM_BMP280_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BMP280_ADDRESSES[i]) && BMP280.begin(TELEM_BMP280_ADDRESSES[i])) { + MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESSES[i]); + MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID()); + BMP280_initialized = true; + break; + } else { + BMP280_initialized = false; + MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESSES[i]); + } + } +#endif #if ENV_INCLUDE_SHTC3 - if (SHTC3.begin(TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, 0x70) && SHTC3.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found sensor: SHTC3"); SHTC3_initialized = true; } else { @@ -242,21 +263,23 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_SHT4X - SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS); - uint32_t serialNumber = 0; - int16_t sht4x_error; - sht4x_error = SHT4X.serialNumber(serialNumber); - if (sht4x_error == 0) { - MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS); - SHT4X_initialized = true; - } else { - SHT4X_initialized = false; - MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS); + if (i2c_probe(*TELEM_WIRE, TELEM_SHT4X_ADDRESS)) { + SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS); + uint32_t serialNumber = 0; + int16_t sht4x_error; + sht4x_error = SHT4X.serialNumber(serialNumber); + if (sht4x_error == 0) { + MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS); + SHT4X_initialized = true; + } else { + SHT4X_initialized = false; + MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS); + } } #endif #if ENV_INCLUDE_LPS22HB - if (LPS22HB.begin()) { + if (i2c_probe(*TELEM_WIRE, 0x5C) && LPS22HB.begin()) { MESH_DEBUG_PRINTLN("Found sensor: LPS22HB"); LPS22HB_initialized = true; } else { @@ -266,7 +289,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA3221 - if (INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA3221_ADDRESS) && INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); @@ -281,7 +304,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA219 - if (INA219.begin(TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA219_ADDRESS) && INA219.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); INA219_initialized = true; } else { @@ -291,17 +314,17 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA260 - if (INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA260_ADDRESS) && INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA260 at address: %02X", TELEM_INA260_ADDRESS); INA260_initialized = true; } else { INA260_initialized = false; - MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA260_ADDRESS); + MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA219_ADDRESS); } #endif #if ENV_INCLUDE_INA226 - if (INA226.begin()) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA226_ADDRESS) && INA226.begin()) { MESH_DEBUG_PRINTLN("Found INA226 at address: %02X", TELEM_INA226_ADDRESS); INA226.setMaxCurrentShunt(TELEM_INA226_MAX_AMP, TELEM_INA226_SHUNT_VALUE); INA226_initialized = true; @@ -312,7 +335,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_MLX90614 - if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_MLX90614_ADDRESS) && MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS); MLX90614_initialized = true; } else { @@ -322,7 +345,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_VL53L0X - if (VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_VL53L0X_ADDRESS) && VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found VL53L0X at address: %02X", TELEM_VL53L0X_ADDRESS); VL53L0X_initialized = true; } else { @@ -334,7 +357,7 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_BMP085 // First argument is MODE (aka oversampling) // choose ULTRALOWPOWER - if (BMP085.begin(0, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, 0x77) && BMP085.begin(0, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found sensor BMP085"); BMP085_initialized = true; } else { @@ -345,7 +368,7 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_RAK12035 RAK12035.setup(*TELEM_WIRE); - if (RAK12035.begin(TELEM_RAK12035_ADDRESS)) { + if (i2c_probe(*TELEM_WIRE, TELEM_RAK12035_ADDRESS) && RAK12035.begin(TELEM_RAK12035_ADDRESS)) { MESH_DEBUG_PRINTLN("Found sensor RAK12035 at address: %02X", TELEM_RAK12035_ADDRESS); RAK12035_initialized = true; } else { @@ -716,7 +739,7 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ gps_detected = true; return true; } - + pinMode(ioPin, INPUT); MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next"); return false; @@ -759,7 +782,7 @@ void EnvironmentSensorManager::loop() { #if ENV_INCLUDE_GPS if (gps_active) { - _location->loop(); + _location->loop(); } if (millis() > next_gps_update) { diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 32413ebc03..38de556068 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -47,6 +47,7 @@ class EnvironmentSensorManager : public SensorManager { #else EnvironmentSensorManager(){}; #endif + bool i2c_probe(TwoWire& wire, uint8_t addr) override; bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; #if ENV_INCLUDE_GPS