diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 6d39e905..c300cbe4 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -1,12 +1,45 @@ #include "EnvironmentSensorManager.h" +#if ENV_INCLUDE_AHTX0 static Adafruit_AHTX0 AHTX0; +#endif +#if ENV_INCLUDE_BME280 static Adafruit_BME280 BME280; +#endif +#if ENV_INCLUDE_INA3221 static Adafruit_INA3221 INA3221; +#endif +#if ENV_INCLUDE_INA219 static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); +#endif bool EnvironmentSensorManager::begin() { + #if ENV_INCLUDE_GPS + initBasicGPS(); + #endif + #if ENV_INCLUDE_AHTX0 + if (AHTX0.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) { + MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); + AHTX0_initialized = true; + } else { + AHTX0_initialized = false; + MESH_DEBUG_PRINTLN("AHT10/AHT20 was not found at I2C address %02X", TELEM_AHTX_ADDRESS); + } + #endif + + #if ENV_INCLUDE_BME280 + if (BME280.begin(TELEM_BME280_ADDRESS, &Wire)) { + MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); + MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); + BME280_initialized = true; + } else { + BME280_initialized = false; + MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS); + } + #endif + + #if ENV_INCLUDE_INA3221 if (INA3221.begin(TELEM_INA3221_ADDRESS, &Wire)) { MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); @@ -19,7 +52,9 @@ bool EnvironmentSensorManager::begin() { INA3221_initialized = false; MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS); } + #endif + #if ENV_INCLUDE_INA219 if (INA219.begin(&Wire)) { MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); INA219_initialized = true; @@ -27,30 +62,39 @@ bool EnvironmentSensorManager::begin() { INA219_initialized = false; MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS); } - - if (AHTX0.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); - AHTX0_initialized = true; - } else { - AHTX0_initialized = false; - MESH_DEBUG_PRINTLN("AHT10/AHT20 was not found at I2C address %02X", TELEM_AHTX_ADDRESS); - } - - if (BME280.begin(TELEM_BME280_ADDRESS, &Wire)) { - MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); - MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID); - BME280_initialized = true; - } else { - BME280_initialized = false; - MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS); - } + #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) { + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, 0.0f); // allow lat/lon via telemetry even if no GPS is detected + } + if (requester_permissions & TELEM_PERM_ENVIRONMENT) { + + #if ENV_INCLUDE_AHTX0 + if (AHTX0_initialized) { + 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 + + #if ENV_INCLUDE_BME280 + if (BME280_initialized) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, BME280.readTemperature()); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME280.readHumidity()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME280.readPressure()); + telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA)); + } + #endif + + #if ENV_INCLUDE_INA3221 if (INA3221_initialized) { for(int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) { // add only enabled INA3221 channels to telemetry @@ -64,27 +108,110 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } } } + #endif + + #if ENV_INCLUDE_INA219 if (INA219_initialized) { 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++; } - if (AHTX0_initialized) { - sensors_event_t humidity, temp; - AHTX0.getEvent(&humidity, &temp); + #endif - telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity); - } - - if (BME280_initialized) { - telemetry.addTemperature(TELEM_CHANNEL_SELF, BME280.readTemperature()); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME280.readHumidity()); - telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME280.readPressure()); - telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA)); - } } return true; -} \ No newline at end of file +} + + +int EnvironmentSensorManager::getNumSettings() const { + #if ENV_INCLUDE_GPS + return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected + #endif + return NULL; +} + +const char* EnvironmentSensorManager::getSettingName(int i) const { + #if ENV_INCLUDE_GPS + return (gps_detected && i == 0) ? "gps" : NULL; + #endif + return NULL; +} + +const char* EnvironmentSensorManager::getSettingValue(int i) const { + #if ENV_INCLUDE_GPS + if (gps_detected && i == 0) { + return gps_active ? "1" : "0"; + } + #endif + return NULL; +} + +bool EnvironmentSensorManager::setSettingValue(const char* name, const char* value) { + #if ENV_INCLUDE_GPS + if (gps_detected && strcmp(name, "gps") == 0) { + if (strcmp(value, "0") == 0) { + stop_gps(); + } else { + start_gps(); + } + return true; + } + #endif + return false; // not supported +} + +#if ENV_INCLUDE_GPS +void EnvironmentSensorManager::initBasicGPS() { + Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); + Serial1.begin(9600); + + // Try to detect if GPS is physically connected to determine if we should expose the setting + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, HIGH); // Power on GPS + + // Give GPS a moment to power up and send data + delay(1000); + + // We'll consider GPS detected if we see any data on Serial1 + gps_detected = (Serial1.available() > 0); + + if (gps_detected) { + MESH_DEBUG_PRINTLN("GPS detected"); + digitalWrite(PIN_GPS_EN, LOW); // Power off GPS until the setting is changed + } else { + MESH_DEBUG_PRINTLN("No GPS detected"); + digitalWrite(PIN_GPS_EN, LOW); + } + +} + + +void EnvironmentSensorManager::start_gps() { + gps_active = true; + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, HIGH); +} + +void EnvironmentSensorManager::stop_gps() { + gps_active = false; + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, LOW); +} + +void EnvironmentSensorManager::loop() { + static long next_gps_update = 0; + + _location->loop(); + + if (millis() > next_gps_update) { + if (_location->isValid()) { + node_lat = ((double)_location->getLatitude())/1000000.; + node_lon = ((double)_location->getLongitude())/1000000.; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + next_gps_update = millis() + 1000; + } +} +#endif \ No newline at end of file diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index fa3a1a1e..2a72f2d0 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -1,32 +1,65 @@ #pragma once -#include #include -#include -#include -#include -#include +#include +#include + +#if ENV_INCLUDE_AHTX0 #define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address +#include +#endif +#if ENV_INCLUDE_BME280 #define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address +#define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level +#include +#endif +#if ENV_INCLUDE_INA3221 #define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address -#define TELEM_INA219_ADDRESS 0x40 // INA219 single channel current sensor I2C address - #define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts #define TELEM_INA3221_NUM_CHANNELS 3 +#include +#endif +#if ENV_INCLUDE_INA219 +#define TELEM_INA219_ADDRESS 0x40 // INA219 single channel current sensor I2C address +#include +#endif + -#define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level class EnvironmentSensorManager : public SensorManager { protected: int next_available_channel = TELEM_CHANNEL_SELF + 1; + bool AHTX0_initialized = false; + bool BME280_initialized = false; bool INA3221_initialized = false; bool INA219_initialized = false; - bool BME280_initialized = false; - bool AHTX0_initialized = false; + + bool gps_detected = false; + bool gps_active = false; + + #if ENV_INCLUDE_GPS + LocationProvider* _location; + void start_gps(); + void stop_gps(); + void initBasicGPS(); + #endif + + public: + #if ENV_INCLUDE_GPS + EnvironmentSensorManager(LocationProvider &location): _location(&location){}; + #else EnvironmentSensorManager(){}; + #endif bool begin() override; - bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + #if ENV_INCLUDE_GPS + void loop() override; + #endif + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; }; diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 568e20d7..11f73d81 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -13,6 +13,14 @@ build_flags = ${nrf52840_base.build_flags} -D PIN_BOARD_SDA=8 -D PIN_OLED_RESET=-1 -D PIN_USER_BTN=6 + -D PIN_GPS_RX=3 + -D PIN_GPS_TX=4 + -D PIN_GPS_EN=5 + -D ENV_INCLUDE_GPS=1 + -D ENV_INCLUDE_AHTX0=1 + -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_INA3221=1 + -D ENV_INCLUDE_INA219=1 build_src_filter = ${nrf52840_base.build_src_filter} + + @@ -23,7 +31,8 @@ lib_deps= ${nrf52840_base.lib_deps} adafruit/Adafruit INA219 @ ^1.2.3 adafruit/Adafruit AHTX0 @ ^2.0.5 adafruit/Adafruit BME280 Library @ ^2.3.0 - + stevemarple/MicroNMEA @ ^2.0.6 + [env:Faketec_Repeater] extends = Faketec build_src_filter = ${Faketec.build_src_filter} @@ -99,7 +108,7 @@ build_flags = ${Faketec.build_flags} -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SSD1306Display ; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 + -D MESH_DEBUG=1 build_src_filter = ${Faketec.build_src_filter} + +<../examples/companion_radio> diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index 841bd1dc..2369bd7a 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -2,6 +2,9 @@ #include "target.h" #include +#if ENV_INCLUDE_GPS +#endif + PromicroBoard board; RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); @@ -10,7 +13,13 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -EnvironmentSensorManager sensors; +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif #ifdef DISPLAY_CLASS DISPLAY_CLASS display;