@ -6,6 +6,10 @@
# define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors
# define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors
# endif
# endif
// ============================================================
// Sensor library includes and static driver instances
// ============================================================
# ifdef ENV_INCLUDE_BME680
# ifdef ENV_INCLUDE_BME680
# ifndef TELEM_BME680_ADDRESS
# ifndef TELEM_BME680_ADDRESS
# define TELEM_BME680_ADDRESS 0x76
# define TELEM_BME680_ADDRESS 0x76
@ -31,7 +35,7 @@ static Adafruit_AHTX0 AHTX0;
# ifndef TELEM_BME280_ADDRESS
# ifndef TELEM_BME280_ADDRESS
# define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address
# define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address
# endif
# endif
# define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Ath mospheric pressure at sea level
# define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level
# include <Adafruit_BME280.h>
# include <Adafruit_BME280.h>
static Adafruit_BME280 BME280 ;
static Adafruit_BME280 BME280 ;
# endif
# endif
@ -40,7 +44,7 @@ static Adafruit_BME280 BME280;
# ifndef TELEM_BMP280_ADDRESS
# ifndef TELEM_BMP280_ADDRESS
# define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address
# define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address
# endif
# endif
# define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Ath mospheric pressure at sea level
# define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level
# include <Adafruit_BMP280.h>
# include <Adafruit_BMP280.h>
static Adafruit_BMP280 BMP280 ( TELEM_WIRE ) ;
static Adafruit_BMP280 BMP280 ( TELEM_WIRE ) ;
# endif
# endif
@ -51,7 +55,7 @@ static Adafruit_SHTC3 SHTC3;
# endif
# endif
# if ENV_INCLUDE_SHT4X
# if ENV_INCLUDE_SHT4X
# define TELEM_SHT4X_ADDRESS 0x44 //0x44 - 0x46
# define TELEM_SHT4X_ADDRESS 0x44
# include <SensirionI2cSht4x.h>
# include <SensirionI2cSht4x.h>
static SensirionI2cSht4x SHT4X ;
static SensirionI2cSht4x SHT4X ;
# endif
# endif
@ -161,381 +165,414 @@ public:
static RAK12500LocationProvider RAK12500_provider ;
static RAK12500LocationProvider RAK12500_provider ;
# endif
# endif
bool EnvironmentSensorManager : : begin ( ) {
// ============================================================
# if ENV_INCLUDE_GPS
// I2C bus scanner
# ifdef RAK_WISBLOCK_GPS
// Probes every valid address and records which ones ACK.
rakGPSInit ( ) ; //probe base board/sockets for GPS
// This runs before any sensor library is touched, so a missing
# else
// or misbehaving device cannot stall or crash the boot sequence.
initBasicGPS ( ) ;
// ============================================================
# endif
# endif
# if ENV_PIN_SDA && ENV_PIN_SCL
static void scanI2CBus ( TwoWire * wire , bool found [ 128 ] ) {
# ifdef NRF52_PLATFORM
for ( uint8_t addr = 0x08 ; addr < 0x78 ; addr + + ) {
Wire1 . setPins ( ENV_PIN_SDA , ENV_PIN_SCL ) ;
wire - > beginTransmission ( addr ) ;
Wire1 . setClock ( 100000 ) ;
found [ addr ] = ( wire - > endTransmission ( ) = = 0 ) ;
Wire1 . begin ( ) ;
}
# else
}
Wire1 . begin ( ENV_PIN_SDA , ENV_PIN_SCL , 100000 ) ;
# endif
// ============================================================
MESH_DEBUG_PRINTLN ( " Second I2C initialized on pins SDA: %d SCL: %d " , ENV_PIN_SDA , ENV_PIN_SCL ) ;
// Per-sensor init and query functions
# endif
//
// init(wire, address) — called only when the address was seen
// on the bus. Returns 0 on failure, or the number of
// telemetry channels the sensor will consume (1 for all
// single-output sensors; INA3221 returns one per enabled
// hardware channel; MLX90614 and RAK12035+calibration
// return 2).
//
// query(channel, sub_channel, lpp) — called once per active
// sensor entry during querySensors(). sub_channel is always
// 0 for single-output sensors.
// ============================================================
# if ENV_INCLUDE_AHTX0
# if ENV_INCLUDE_AHTX0
if ( AHTX0 . begin ( TELEM_WIRE , 0 , TELEM_AHTX_ADDRESS ) ) {
static uint8_t init_ahtx0 ( TwoWire * wire , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found AHT10/AHT20 at address: %02X " , TELEM_AHTX_ADDRESS ) ;
return AHTX0 . begin ( wire , 0 , addr ) ? 1 : 0 ;
AHTX0_initialized = true ;
}
} else {
static void query_ahtx0 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
AHTX0_initialized = false ;
sensors_event_t humidity , temp ;
MESH_DEBUG_PRINTLN ( " AHT10/AHT20 was not found at I2C address %02X " , TELEM_AHTX_ADDRESS ) ;
AHTX0 . getEvent ( & humidity , & temp ) ;
lpp . addTemperature ( ch , temp . temperature ) ;
lpp . addRelativeHumidity ( ch , humidity . relative_humidity ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_BME680
# ifdef ENV_INCLUDE_BME680
if ( BME680 . begin ( TELEM_BME680_ADDRESS ) ) {
static uint8_t init_bme680 ( TwoWire * , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found BME680 at address: %02X " , TELEM_BME680_ADDRESS ) ;
// Wire was set in the static constructor; begin() takes address only.
BME680_initialized = true ;
return BME680 . begin ( addr ) ? 1 : 0 ;
} else {
}
BME680_initialized = false ;
static void query_bme680 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
MESH_DEBUG_PRINTLN ( " BME680 was not found at I2C address %02X " , TELEM_BME680_ADDRESS ) ;
if ( BME680 . performReading ( ) ) {
lpp . addTemperature ( ch , BME680 . temperature ) ;
lpp . addRelativeHumidity ( ch , BME680 . humidity ) ;
lpp . addBarometricPressure ( ch , BME680 . pressure / 100 ) ;
lpp . addAltitude ( ch , 44330.0 * ( 1.0 - pow ( ( BME680 . pressure / 100 ) / TELEM_BME680_SEALEVELPRESSURE_HPA , 0.1903 ) ) ) ;
lpp . addAnalogInput ( ch , BME680 . gas_resistance ) ;
}
}
}
# endif
# endif
# if ENV_INCLUDE_BME280
# if ENV_INCLUDE_BME280
if ( BME280 . begin ( TELEM_BME280_ADDRESS , TELEM_WIRE ) ) {
static uint8_t init_bme280 ( TwoWire * wire , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found BME280 at address: %02X " , TELEM_BME280_ADDRESS ) ;
if ( ! BME280 . begin ( addr , wire ) ) return 0 ;
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 ,
BME280 . setSampling ( Adafruit_BME280 : : MODE_FORCED ,
Adafruit_BME280 : : SAMPLING_X1 , // temperature
Adafruit_BME280 : : SAMPLING_X1 ,
Adafruit_BME280 : : SAMPLING_X1 , // pressure
Adafruit_BME280 : : SAMPLING_X1 ,
Adafruit_BME280 : : SAMPLING_X1 , // humidity
Adafruit_BME280 : : SAMPLING_X1 ,
Adafruit_BME280 : : FILTER_OFF ,
Adafruit_BME280 : : FILTER_OFF ,
Adafruit_BME280 : : STANDBY_MS_1000 ) ;
Adafruit_BME280 : : STANDBY_MS_1000 ) ;
BME280_initialized = true ;
return 1 ;
} else {
}
BME280_initialized = false ;
static void query_bme280 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
MESH_DEBUG_PRINTLN ( " BME280 was not found at I2C address %02X " , TELEM_BME280_ADDRESS ) ;
if ( BME280 . takeForcedMeasurement ( ) ) {
lpp . addTemperature ( ch , BME280 . readTemperature ( ) ) ;
lpp . addRelativeHumidity ( ch , BME280 . readHumidity ( ) ) ;
lpp . addBarometricPressure ( ch , BME280 . readPressure ( ) / 100 ) ;
lpp . addAltitude ( ch , BME280 . readAltitude ( TELEM_BME280_SEALEVELPRESSURE_HPA ) ) ;
}
}
}
# endif
# endif
# if ENV_INCLUDE_BMP280
# if ENV_INCLUDE_BMP280
if ( BMP280 . begin ( TELEM_BMP280_ADDRESS ) ) {
static uint8_t init_bmp280 ( TwoWire * , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found BMP280 at address: %02X " , TELEM_BMP280_ADDRESS ) ;
// BMP280 static instance was constructed with TELEM_WIRE; begin() uses it.
MESH_DEBUG_PRINTLN ( " BMP sensor ID: %02X " , BMP280 . sensorID ( ) ) ;
return BMP280 . begin ( addr ) ? 1 : 0 ;
BMP280_initialized = true ;
}
} else {
static void query_bmp280 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
BMP280_initialized = false ;
lpp . addTemperature ( ch , BMP280 . readTemperature ( ) ) ;
MESH_DEBUG_PRINTLN ( " BMP280 was not found at I2C address %02X " , TELEM_BMP280_ADDRESS ) ;
lpp . addBarometricPressure ( ch , BMP280 . readPressure ( ) / 100 ) ;
lpp . addAltitude ( ch , BMP280 . readAltitude ( TELEM_BMP280_SEALEVELPRESSURE_HPA ) ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_SHTC3
# if ENV_INCLUDE_SHTC3
if ( SHTC3 . begin ( TELEM_WIRE ) ) {
static uint8_t init_shtc3 ( TwoWire * wire , uint8_t ) {
MESH_DEBUG_PRINTLN ( " Found sensor: SHTC3 " ) ;
// Adafruit_SHTC3::begin() does not accept an address (fixed at 0x70).
SHTC3_initialized = true ;
return SHTC3 . begin ( wire ) ? 1 : 0 ;
} else {
}
SHTC3_initialized = false ;
static void query_shtc3 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
MESH_DEBUG_PRINTLN ( " SHTC3 was not found at I2C address %02X " , 0x70 ) ;
sensors_event_t humidity , temp ;
SHTC3 . getEvent ( & humidity , & temp ) ;
lpp . addTemperature ( ch , temp . temperature ) ;
lpp . addRelativeHumidity ( ch , humidity . relative_humidity ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_SHT4X
# if ENV_INCLUDE_SHT4X
SHT4X . begin ( * TELEM_WIRE , TELEM_SHT4X_ADDRESS ) ;
static uint8_t init_sht4x ( TwoWire * wire , uint8_t addr ) {
uint32_t serialNumber = 0 ;
// SensirionI2cSht4x::begin() does not probe the hardware; use serialNumber()
int16_t sht4x_error ;
// as the actual presence check since it performs a real I2C transaction.
sht4x_error = SHT4X . serialNumber ( serialNumber ) ;
SHT4X . begin ( * wire , addr ) ;
if ( sht4x_error = = 0 ) {
uint32_t serial = 0 ;
MESH_DEBUG_PRINTLN ( " Found SHT4X at address: %02X " , TELEM_SHT4X_ADDRESS ) ;
return ( SHT4X . serialNumber ( serial ) = = 0 ) ? 1 : 0 ;
SHT4X_initialized = true ;
}
} else {
static void query_sht4x ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
SHT4X_initialized = false ;
float temperature , humidity ;
MESH_DEBUG_PRINTLN ( " SHT4X was not found at I2C address %02X " , TELEM_SHT4X_ADDRESS ) ;
if ( SHT4X . measureLowestPrecision ( temperature , humidity ) = = 0 ) {
lpp . addTemperature ( ch , temperature ) ;
lpp . addRelativeHumidity ( ch , humidity ) ;
}
}
}
# endif
# endif
# if ENV_INCLUDE_LPS22HB
# if ENV_INCLUDE_LPS22HB
if ( LPS22HB . begin ( ) ) {
static uint8_t init_lps22hb ( TwoWire * , uint8_t ) {
MESH_DEBUG_PRINTLN ( " Found sensor: LPS22HB " ) ;
// LPS22HBClass is constructed with the wire reference; begin() uses it.
LPS22HB_initialized = true ;
return LPS22HB . begin ( ) ? 1 : 0 ;
} else {
}
LPS22HB_initialized = false ;
static void query_lps22hb ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
MESH_DEBUG_PRINTLN ( " LPS22HB was not found at I2C address %02X " , 0x5C ) ;
lpp . addTemperature ( ch , LPS22HB . readTemperature ( ) ) ;
lpp . addBarometricPressure ( ch , LPS22HB . readPressure ( ) * 10 ) ; // convert kPa to hPa
}
}
# endif
# endif
# if ENV_INCLUDE_INA3221
# if ENV_INCLUDE_INA3221
if ( INA3221 . begin ( TELEM_INA3221_ADDRESS , TELEM_WIRE ) ) {
static uint8_t init_ina3221 ( TwoWire * wire , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found INA3221 at address: %02X " , TELEM_INA3221_ADDRESS ) ;
if ( ! INA3221 . begin ( addr , wire ) ) return 0 ;
MESH_DEBUG_PRINTLN ( " %04X %04X " , INA3221 . getDieID ( ) , INA3221 . getManufacturerID ( ) ) ;
for ( int i = 0 ; i < TELEM_INA3221_NUM_CHANNELS ; i + + ) {
for ( int i = 0 ; i < 3 ; i + + ) {
INA3221 . setShuntResistance ( i , TELEM_INA3221_SHUNT_VALUE ) ;
INA3221 . setShuntResistance ( i , TELEM_INA3221_SHUNT_VALUE ) ;
}
}
INA3221_initialized = true ;
// Each enabled hardware channel becomes its own telemetry channel.
} else {
uint8_t enabled = 0 ;
INA3221_initialized = false ;
for ( int i = 0 ; i < TELEM_INA3221_NUM_CHANNELS ; i + + ) {
MESH_DEBUG_PRINTLN ( " INA3221 was not found at I2C address %02X " , TELEM_INA3221_ADDRESS ) ;
if ( INA3221 . isChannelEnabled ( i ) ) enabled + + ;
}
return enabled > 0 ? enabled : 1 ;
}
static void query_ina3221 ( uint8_t ch , uint8_t sub_ch , CayenneLPP & lpp ) {
// sub_ch is the index of the nth enabled hardware channel.
uint8_t seen = 0 ;
for ( int i = 0 ; i < TELEM_INA3221_NUM_CHANNELS ; i + + ) {
if ( INA3221 . isChannelEnabled ( i ) ) {
if ( seen = = sub_ch ) {
float v = INA3221 . getBusVoltage ( i ) ;
float c = INA3221 . getCurrentAmps ( i ) ;
lpp . addVoltage ( ch , v ) ;
lpp . addCurrent ( ch , c ) ;
lpp . addPower ( ch , v * c ) ;
return ;
}
seen + + ;
}
}
}
}
# endif
# endif
# if ENV_INCLUDE_INA219
# if ENV_INCLUDE_INA219
if ( INA219 . begin ( TELEM_WIRE ) ) {
static uint8_t init_ina219 ( TwoWire * wire , uint8_t ) {
MESH_DEBUG_PRINTLN ( " Found INA219 at address: %02X " , TELEM_INA219_ADDRESS ) ;
// INA219 static instance was constructed with the address; begin() uses it.
INA219_initialized = true ;
return INA219 . begin ( wire ) ? 1 : 0 ;
} else {
}
INA219_initialized = false ;
static void query_ina219 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
MESH_DEBUG_PRINTLN ( " INA219 was not found at I2C address %02X " , TELEM_INA219_ADDRESS ) ;
lpp . addVoltage ( ch , INA219 . getBusVoltage_V ( ) ) ;
lpp . addCurrent ( ch , INA219 . getCurrent_mA ( ) / 1000.0f ) ;
lpp . addPower ( ch , INA219 . getPower_mW ( ) / 1000.0f ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_INA260
# if ENV_INCLUDE_INA260
if ( INA260 . begin ( TELEM_INA260_ADDRESS , TELEM_WIRE ) ) {
static uint8_t init_ina260 ( TwoWire * wire , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found INA260 at address: %02X " , TELEM_INA260_ADDRESS ) ;
return INA260 . begin ( addr , wire ) ? 1 : 0 ;
INA260_initialized = true ;
}
} else {
static void query_ina260 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
INA260_initialized = false ;
lpp . addVoltage ( ch , INA260 . readBusVoltage ( ) / 1000.0f ) ;
MESH_DEBUG_PRINTLN ( " INA260 was not found at I2C address %02X " , TELEM_INA260_ADDRESS ) ;
lpp . addCurrent ( ch , INA260 . readCurrent ( ) / 1000.0f ) ;
lpp . addPower ( ch , INA260 . readPower ( ) / 1000.0f ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_INA226
# if ENV_INCLUDE_INA226
if ( INA226 . begin ( ) ) {
static uint8_t init_ina226 ( TwoWire * , uint8_t ) {
MESH_DEBUG_PRINTLN ( " Found INA226 at address: %02X " , TELEM_INA226_ADDRESS ) ;
// INA226 static instance was constructed with address and wire.
if ( ! INA226 . begin ( ) ) return 0 ;
INA226 . setMaxCurrentShunt ( TELEM_INA226_MAX_AMP , TELEM_INA226_SHUNT_VALUE ) ;
INA226 . setMaxCurrentShunt ( TELEM_INA226_MAX_AMP , TELEM_INA226_SHUNT_VALUE ) ;
INA226_initialized = true ;
return 1 ;
} else {
}
INA226_initialized = false ;
static void query_ina226 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
MESH_DEBUG_PRINTLN ( " INA226 was not found at I2C address %02X " , TELEM_INA226_ADDRESS ) ;
lpp . addVoltage ( ch , INA226 . getBusVoltage ( ) ) ;
lpp . addCurrent ( ch , INA226 . getCurrent_mA ( ) / 1000.0f ) ;
lpp . addPower ( ch , INA226 . getPower_mW ( ) / 1000.0f ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_MLX90614
# if ENV_INCLUDE_MLX90614
if ( MLX90614 . begin ( TELEM_MLX90614_ADDRESS , TELEM_WIRE ) ) {
static uint8_t init_mlx90614 ( TwoWire * wire , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found MLX90614 at address: %02X " , TELEM_MLX90614_ADDRESS ) ;
return MLX90614 . begin ( addr , wire ) ? 2 : 0 ; // 2 channels: object temp, ambient temp
MLX90614_initialized = true ;
}
} else {
static void query_mlx90614 ( uint8_t ch , uint8_t sub_ch , CayenneLPP & lpp ) {
MLX90614_initialized = false ;
if ( sub_ch = = 0 )
MESH_DEBUG_PRINTLN ( " MLX90614 was not found at I2C address %02X " , TELEM_MLX90614_ADDRESS ) ;
lpp . addTemperature ( ch , MLX90614 . readObjectTempC ( ) ) ;
else
lpp . addTemperature ( ch , MLX90614 . readAmbientTempC ( ) ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_VL53L0X
# if ENV_INCLUDE_VL53L0X
if ( VL53L0X . begin ( TELEM_VL53L0X_ADDRESS , false , TELEM_WIRE ) ) {
static uint8_t init_vl53l0x ( TwoWire * wire , uint8_t addr ) {
MESH_DEBUG_PRINTLN ( " Found VL53L0X at address: %02X " , TELEM_VL53L0X_ADDRESS ) ;
return VL53L0X . begin ( addr , false , wire ) ? 1 : 0 ;
VL53L0X_initialized = true ;
}
} else {
static void query_vl53l0x ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
VL53L0X_initialized = false ;
VL53L0X_RangingMeasurementData_t measure ;
MESH_DEBUG_PRINTLN ( " VL53L0X was not found at I2C address %02X " , TELEM_VL53L0X_ADDRESS ) ;
VL53L0X . rangingTest ( & measure , false ) ;
lpp . addDistance ( ch , measure . RangeStatus ! = 4 ? measure . RangeMilliMeter / 1000.0f : 0.0f ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_BMP085
# ifdef ENV_INCLUDE_BMP085
// First argument is MODE (aka oversampling)
static uint8_t init_bmp085 ( TwoWire * wire , uint8_t ) {
// choose ULTRALOWPOWER
return BMP085 . begin ( 0 , wire ) ? 1 : 0 ; // mode 0 = ULTRALOWPOWER
if ( BMP085 . begin ( 0 , TELEM_WIRE ) ) {
}
MESH_DEBUG_PRINTLN ( " Found sensor BMP085 " ) ;
static void query_bmp085 ( uint8_t ch , uint8_t , CayenneLPP & lpp ) {
BMP085_initialized = true ;
lpp . addTemperature ( ch , BMP085 . readTemperature ( ) ) ;
} else {
lpp . addBarometricPressure ( ch , BMP085 . readPressure ( ) / 100 ) ;
BMP085_initialized = false ;
lpp . addAltitude ( ch , BMP085 . readAltitude ( TELEM_BMP085_SEALEVELPRESSURE_HPA * 100 ) ) ;
MESH_DEBUG_PRINTLN ( " BMP085 was not found at I2C address %02X " , 0x77 ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_RAK12035
# if ENV_INCLUDE_RAK12035
RAK12035 . setup ( * TELEM_WIRE ) ;
static uint8_t init_rak12035 ( TwoWire * wire , uint8_t addr ) {
if ( RAK12035 . begin ( TELEM_RAK12035_ADDRESS ) ) {
// RAK12035 requires setup() before begin().
MESH_DEBUG_PRINTLN ( " Found sensor RAK12035 at address: %02X " , TELEM_RAK12035_ADDRESS ) ;
RAK12035 . setup ( * wire ) ;
RAK12035_initialized = true ;
if ( ! RAK12035 . begin ( addr ) ) return 0 ;
} else {
# ifdef ENABLE_RAK12035_CALIBRATION
RAK12035_initialized = false ;
return 2 ; // moisture channel + calibration channel
MESH_DEBUG_PRINTLN ( " RAK12035 was not found at I2C address %02X " , TELEM_RAK12035_ADDRESS ) ;
# else
}
return 1 ;
# endif
# endif
return true ;
}
bool EnvironmentSensorManager : : querySensors ( uint8_t requester_permissions , CayenneLPP & telemetry ) {
next_available_channel = TELEM_CHANNEL_SELF + 1 ;
if ( requester_permissions & TELEM_PERM_LOCATION & & gps_active ) {
telemetry . addGPS ( TELEM_CHANNEL_SELF , node_lat , node_lon , node_altitude ) ; // allow lat/lon via telemetry even if no GPS is detected
}
}
static void query_rak12035 ( uint8_t ch , uint8_t sub_ch , CayenneLPP & lpp ) {
if ( sub_ch = = 0 ) {
lpp . addTemperature ( ch , RAK12035 . get_sensor_temperature ( ) ) ;
lpp . addPercentage ( ch , RAK12035 . get_sensor_moisture ( ) ) ;
} else {
# ifdef ENABLE_RAK12035_CALIBRATION
float cap = RAK12035 . get_sensor_capacitance ( ) ;
float wet = RAK12035 . get_humidity_full ( ) ;
float dry = RAK12035 . get_humidity_zero ( ) ;
lpp . addFrequency ( ch , cap ) ;
lpp . addTemperature ( ch , wet ) ;
lpp . addPower ( ch , dry ) ;
if ( cap > dry ) RAK12035 . set_humidity_zero ( cap ) ;
if ( cap < wet ) RAK12035 . set_humidity_full ( cap ) ;
# endif
}
}
# endif
// ============================================================
// Sensor descriptor table
//
// Each entry maps an I2C address to a sensor's init and query
// functions. Only entries whose ENV_INCLUDE_* guard is defined
// are compiled in. The sentinel at the end keeps the array
// non-empty regardless of which sensors are enabled.
//
// Ordering here determines channel assignment at runtime:
// the first detected+initialized sensor gets channel 2, the
// next gets channel 3, and so on.
// ============================================================
struct SensorDef {
uint8_t address ;
const char * name ;
uint8_t ( * init ) ( TwoWire * wire , uint8_t address ) ;
void ( * query ) ( uint8_t channel , uint8_t sub_channel , CayenneLPP & telemetry ) ;
} ;
if ( requester_permissions & TELEM_PERM_ENVIRONMENT ) {
static const SensorDef SENSOR_TABLE [ ] = {
# if ENV_INCLUDE_AHTX0
# if ENV_INCLUDE_AHTX0
if ( AHTX0_initialized ) {
{ TELEM_AHTX_ADDRESS , " AHT10/AHT20 " , init_ahtx0 , query_ahtx0 } ,
sensors_event_t humidity , temp ;
AHTX0 . getEvent ( & humidity , & temp ) ;
telemetry . addTemperature ( TELEM_CHANNEL_SELF , temp . temperature ) ;
telemetry . addRelativeHumidity ( TELEM_CHANNEL_SELF , humidity . relative_humidity ) ;
}
# endif
# endif
# ifdef ENV_INCLUDE_BME680
# if ENV_INCLUDE_BME680
{ TELEM_BME680_ADDRESS , " BME680 " , init_bme680 , query_bme680 } ,
if ( BME680_initialized ) {
if ( BME680 . performReading ( ) ) {
telemetry . addTemperature ( TELEM_CHANNEL_SELF , BME680 . temperature ) ;
telemetry . addRelativeHumidity ( TELEM_CHANNEL_SELF , BME680 . humidity ) ;
telemetry . addBarometricPressure ( TELEM_CHANNEL_SELF , BME680 . pressure / 100 ) ;
telemetry . addAltitude ( TELEM_CHANNEL_SELF , 44330.0 * ( 1.0 - pow ( ( BME680 . pressure / 100 ) / TELEM_BME680_SEALEVELPRESSURE_HPA , 0.1903 ) ) ) ;
telemetry . addAnalogInput ( next_available_channel , BME680 . gas_resistance ) ;
next_available_channel + + ;
}
}
# endif
# endif
# if ENV_INCLUDE_BME280
# if ENV_INCLUDE_BME280
if ( BME280_initialized ) {
{ TELEM_BME280_ADDRESS , " BME280 " , init_bme280 , query_bme280 } ,
if ( BME280 . takeForcedMeasurement ( ) ) { // trigger a fresh reading in forced mode
telemetry . addTemperature ( TELEM_CHANNEL_SELF , BME280 . readTemperature ( ) ) ;
telemetry . addRelativeHumidity ( TELEM_CHANNEL_SELF , BME280 . readHumidity ( ) ) ;
telemetry . addBarometricPressure ( TELEM_CHANNEL_SELF , BME280 . readPressure ( ) / 100 ) ;
telemetry . addAltitude ( TELEM_CHANNEL_SELF , BME280 . readAltitude ( TELEM_BME280_SEALEVELPRESSURE_HPA ) ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_BMP280
# if ENV_INCLUDE_BMP280
if ( BMP280_initialized ) {
{ TELEM_BMP280_ADDRESS , " BMP280 " , init_bmp280 , query_bmp280 } ,
telemetry . addTemperature ( TELEM_CHANNEL_SELF , BMP280 . readTemperature ( ) ) ;
telemetry . addBarometricPressure ( TELEM_CHANNEL_SELF , BMP280 . readPressure ( ) / 100 ) ;
telemetry . addAltitude ( TELEM_CHANNEL_SELF , BMP280 . readAltitude ( TELEM_BMP280_SEALEVELPRESSURE_HPA ) ) ;
}
# endif
# endif
# if ENV_INCLUDE_SHTC3
# if ENV_INCLUDE_SHTC3
if ( SHTC3_initialized ) {
{ 0x70 , " SHTC3 " , init_shtc3 , query_shtc3 } ,
sensors_event_t humidity , temp ;
SHTC3 . getEvent ( & humidity , & temp ) ;
telemetry . addTemperature ( TELEM_CHANNEL_SELF , temp . temperature ) ;
telemetry . addRelativeHumidity ( TELEM_CHANNEL_SELF , humidity . relative_humidity ) ;
}
# endif
# endif
# if ENV_INCLUDE_SHT4X
# if ENV_INCLUDE_SHT4X
if ( SHT4X_initialized ) {
{ TELEM_SHT4X_ADDRESS , " SHT4X " , init_sht4x , query_sht4x } ,
float sht4x_humidity , sht4x_temperature ;
int16_t sht4x_error ;
sht4x_error = SHT4X . measureLowestPrecision ( sht4x_temperature , sht4x_humidity ) ;
if ( sht4x_error = = 0 ) {
telemetry . addTemperature ( TELEM_CHANNEL_SELF , sht4x_temperature ) ;
telemetry . addRelativeHumidity ( TELEM_CHANNEL_SELF , sht4x_humidity ) ;
}
}
# endif
# endif
# if ENV_INCLUDE_LPS22HB
# if ENV_INCLUDE_LPS22HB
if ( LPS22HB_initialized ) {
{ 0x5C , " LPS22HB " , init_lps22hb , query_lps22hb } ,
telemetry . addTemperature ( TELEM_CHANNEL_SELF , LPS22HB . readTemperature ( ) ) ;
telemetry . addBarometricPressure ( TELEM_CHANNEL_SELF , LPS22HB . readPressure ( ) * 10 ) ; // convert kPa to hPa
}
# endif
# endif
# if ENV_INCLUDE_INA3221
# if ENV_INCLUDE_INA3221
if ( INA3221_initialized ) {
{ TELEM_INA3221_ADDRESS , " INA3221 " , init_ina3221 , query_ina3221 } ,
for ( int i = 0 ; i < TELEM_INA3221_NUM_CHANNELS ; i + + ) {
// add only enabled INA3221 channels to telemetry
if ( INA3221 . isChannelEnabled ( i ) ) {
float voltage = INA3221 . getBusVoltage ( i ) ;
float current = INA3221 . getCurrentAmps ( i ) ;
telemetry . addVoltage ( next_available_channel , voltage ) ;
telemetry . addCurrent ( next_available_channel , current ) ;
telemetry . addPower ( next_available_channel , voltage * current ) ;
next_available_channel + + ;
}
}
}
# endif
# endif
# if ENV_INCLUDE_INA219
# if ENV_INCLUDE_INA219
if ( INA219_initialized ) {
{ TELEM_INA219_ADDRESS , " INA219 " , init_ina219 , query_ina219 } ,
telemetry . addVoltage ( next_available_channel , INA219 . getBusVoltage_V ( ) ) ;
telemetry . addCurrent ( next_available_channel , INA219 . getCurrent_mA ( ) / 1000 ) ;
telemetry . addPower ( next_available_channel , INA219 . getPower_mW ( ) / 1000 ) ;
next_available_channel + + ;
}
# endif
# endif
# if ENV_INCLUDE_INA260
# if ENV_INCLUDE_INA260
if ( INA260_initialized ) {
{ TELEM_INA260_ADDRESS , " INA260 " , init_ina260 , query_ina260 } ,
telemetry . addVoltage ( next_available_channel , INA260 . readBusVoltage ( ) / 1000 ) ;
telemetry . addCurrent ( next_available_channel , INA260 . readCurrent ( ) / 1000 ) ;
telemetry . addPower ( next_available_channel , INA260 . readPower ( ) / 1000 ) ;
next_available_channel + + ;
}
# endif
# endif
# if ENV_INCLUDE_INA226
# if ENV_INCLUDE_INA226
if ( INA226_initialized ) {
{ TELEM_INA226_ADDRESS , " INA226 " , init_ina226 , query_ina226 } ,
telemetry . addVoltage ( next_available_channel , INA226 . getBusVoltage ( ) ) ;
telemetry . addCurrent ( next_available_channel , INA226 . getCurrent_mA ( ) / 1000.0 ) ;
telemetry . addPower ( next_available_channel , INA226 . getPower_mW ( ) / 1000.0 ) ;
next_available_channel + + ;
}
# endif
# endif
# if ENV_INCLUDE_MLX90614
# if ENV_INCLUDE_MLX90614
if ( MLX90614_initialized ) {
{ TELEM_MLX90614_ADDRESS , " MLX90614 " , init_mlx90614 , query_mlx90614 } ,
telemetry . addTemperature ( TELEM_CHANNEL_SELF , MLX90614 . readObjectTempC ( ) ) ;
telemetry . addTemperature ( TELEM_CHANNEL_SELF + 1 , MLX90614 . readAmbientTempC ( ) ) ;
}
# endif
# endif
# if ENV_INCLUDE_VL53L0X
# if ENV_INCLUDE_VL53L0X
if ( VL53L0X_initialized ) {
{ TELEM_VL53L0X_ADDRESS , " VL53L0X " , init_vl53l0x , query_vl53l0x } ,
VL53L0X_RangingMeasurementData_t measure ;
VL53L0X . rangingTest ( & measure , false ) ; // pass in 'true' to get debug data
if ( measure . RangeStatus ! = 4 ) { // phase failures
telemetry . addDistance ( TELEM_CHANNEL_SELF , measure . RangeMilliMeter / 1000.0f ) ; // convert mm to m
} else {
telemetry . addDistance ( TELEM_CHANNEL_SELF , 0.0f ) ; // no valid measurement
}
}
# endif
# endif
# ifdef ENV_INCLUDE_BMP085
# if ENV_INCLUDE_BMP085
{ 0x77 , " BMP085 " , init_bmp085 , query_bmp085 } ,
if ( BMP085_initialized ) {
telemetry . addTemperature ( TELEM_CHANNEL_SELF , BMP085 . readTemperature ( ) ) ;
telemetry . addBarometricPressure ( TELEM_CHANNEL_SELF , BMP085 . readPressure ( ) / 100 ) ;
telemetry . addAltitude ( TELEM_CHANNEL_SELF , BMP085 . readAltitude ( TELEM_BMP085_SEALEVELPRESSURE_HPA * 100 ) ) ;
}
# endif
# endif
# if ENV_INCLUDE_RAK12035
# if ENV_INCLUDE_RAK12035
if ( RAK12035_initialized ) {
{ TELEM_RAK12035_ADDRESS , " RAK12035 " , init_rak12035 , query_rak12035 } ,
# endif
{ 0 , nullptr , nullptr , nullptr } // sentinel — keeps the array non-empty
} ;
// RAK12035 Telemetry is Channel 2
static const size_t SENSOR_TABLE_SIZE = ( sizeof ( SENSOR_TABLE ) / sizeof ( SENSOR_TABLE [ 0 ] ) ) - 1 ;
telemetry . addTemperature ( 2 , RAK12035 . get_sensor_temperature ( ) ) ;
telemetry . addPercentage ( 2 , RAK12035 . get_sensor_moisture ( ) ) ;
// RAK12035 CALIBRATION Telemetry is Channel 3, if enabled
// ============================================================
// begin() — scan the I2C bus, then initialize only what was
// found. A sensor whose address does not ACK during the scan
// is never touched by a library call, preventing hangs or
// crashes caused by absent or misbehaving hardware.
// ============================================================
# ifdef ENABLE_RAK12035_CALIBRATION
bool EnvironmentSensorManager : : begin ( ) {
// Calibration Data Screen is Channel 3
# if ENV_INCLUDE_GPS
float cap = RAK12035 . get_sensor_capacitance ( ) ;
# ifdef RAK_WISBLOCK_GPS
float _wet = RAK12035 . get_humidity_full ( ) ;
rakGPSInit ( ) ;
float _dry = RAK12035 . get_humidity_zero ( ) ;
# else
initBasicGPS ( ) ;
# endif
# endif
telemetry . addFrequency ( 3 , cap ) ;
# if ENV_PIN_SDA && ENV_PIN_SCL
telemetry . addTemperature ( 3 , _wet ) ;
# ifdef NRF52_PLATFORM
telemetry . addPower ( 3 , _dry ) ;
Wire1 . setPins ( ENV_PIN_SDA , ENV_PIN_SCL ) ;
Wire1 . setClock ( 100000 ) ;
Wire1 . begin ( ) ;
# else
Wire1 . begin ( ENV_PIN_SDA , ENV_PIN_SCL , 100000 ) ;
# endif
MESH_DEBUG_PRINTLN ( " Second I2C initialized on pins SDA: %d SCL: %d " , ENV_PIN_SDA , ENV_PIN_SCL ) ;
# endif
// Scan the I2C bus before touching any sensor library.
bool detected [ 128 ] = { } ;
scanI2CBus ( TELEM_WIRE , detected ) ;
if ( cap > _dry ) {
// Walk the sensor table and initialize only detected devices.
RAK12035 . set_humidity_zero ( cap ) ;
_active_sensor_count = 0 ;
for ( size_t i = 0 ; i < SENSOR_TABLE_SIZE & & _active_sensor_count < MAX_ACTIVE_SENSORS ; i + + ) {
const SensorDef & def = SENSOR_TABLE [ i ] ;
if ( ! detected [ def . address ] ) {
MESH_DEBUG_PRINTLN ( " %s not detected at I2C address %02X " , def . name , def . address ) ;
continue ;
}
uint8_t n = def . init ( TELEM_WIRE , def . address ) ;
if ( n = = 0 ) {
MESH_DEBUG_PRINTLN ( " %s found at %02X but failed to initialize " , def . name , def . address ) ;
continue ;
}
MESH_DEBUG_PRINTLN ( " Found %s at address: %02X " , def . name , def . address ) ;
for ( uint8_t sub = 0 ; sub < n & & _active_sensor_count < MAX_ACTIVE_SENSORS ; sub + + ) {
_active_sensors [ _active_sensor_count + + ] = { def . query , sub } ;
}
}
}
if ( cap < _wet ) {
return true ;
RAK12035 . set_humidity_full ( cap ) ;
}
}
# endif
// ============================================================
// querySensors() — GPS stays on channel 1; each active sensor
// gets the next available channel in the order it was
// initialized.
// ============================================================
bool EnvironmentSensorManager : : querySensors ( uint8_t requester_permissions , CayenneLPP & telemetry ) {
next_available_channel = TELEM_CHANNEL_SELF + 1 ;
if ( requester_permissions & TELEM_PERM_LOCATION & & gps_active ) {
telemetry . addGPS ( TELEM_CHANNEL_SELF , node_lat , node_lon , node_altitude ) ;
}
if ( requester_permissions & TELEM_PERM_ENVIRONMENT ) {
for ( int i = 0 ; i < _active_sensor_count ; i + + ) {
_active_sensors [ i ] . query ( next_available_channel , _active_sensors [ i ] . sub_channel , telemetry ) ;
next_available_channel + + ;
}
}
# endif
}
}
return true ;
return true ;
}
}
@ -555,8 +592,6 @@ const char* EnvironmentSensorManager::getSettingName(int i) const {
return " gps " ;
return " gps " ;
}
}
# endif
# endif
// convenient way to add params (needed for some tests)
// if (i == settings++) return "param.2";
return NULL ;
return NULL ;
}
}
@ -567,8 +602,6 @@ const char* EnvironmentSensorManager::getSettingValue(int i) const {
return gps_active ? " 1 " : " 0 " ;
return gps_active ? " 1 " : " 0 " ;
}
}
# endif
# endif
// convenient way to add params ...
// if (i == settings++) return "2";
return NULL ;
return NULL ;
}
}
@ -584,11 +617,7 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val
}
}
if ( strcmp ( name , " gps_interval " ) = = 0 ) {
if ( strcmp ( name , " gps_interval " ) = = 0 ) {
uint32_t interval_seconds = atoi ( value ) ;
uint32_t interval_seconds = atoi ( value ) ;
if ( interval_seconds > 0 ) {
gps_update_interval_sec = interval_seconds > 0 ? interval_seconds : 1 ;
gps_update_interval_sec = interval_seconds ;
} else {
gps_update_interval_sec = 1 ; // Default to 1 second if 0
}
return true ;
return true ;
}
}
# endif
# endif
@ -652,16 +681,10 @@ void EnvironmentSensorManager::rakGPSInit(){
//search for the correct IO standby pin depending on socket used
//search for the correct IO standby pin depending on socket used
if ( gpsIsAwake ( WB_IO2 ) ) {
if ( gpsIsAwake ( WB_IO2 ) ) {
// MESH_DEBUG_PRINTLN("RAK base board is RAK19007/10");
// MESH_DEBUG_PRINTLN("GPS is installed on Socket A");
}
}
else if ( gpsIsAwake ( WB_IO4 ) ) {
else if ( gpsIsAwake ( WB_IO4 ) ) {
// MESH_DEBUG_PRINTLN("RAK base board is RAK19003/9");
// MESH_DEBUG_PRINTLN("GPS is installed on Socket C");
}
}
else if ( gpsIsAwake ( WB_IO5 ) ) {
else if ( gpsIsAwake ( WB_IO5 ) ) {
// MESH_DEBUG_PRINTLN("RAK base board is RAK19001/11");
// MESH_DEBUG_PRINTLN("GPS is installed on Socket F");
}
}
else {
else {
MESH_DEBUG_PRINTLN ( " No GPS found " ) ;
MESH_DEBUG_PRINTLN ( " No GPS found " ) ;