Browse Source

RAK4631: BME680 add and GPS cleanup

- Added the BME680 environment sensor functionality
- Added the GPS Repeater env for those wanting it
- Cleaned up the GPS and other RAK4631SensorManager code

Verified build and functionality on normal and GPS repeater and companion envs.

IAQ readout is still a work in progress, but a placeholder can be seen on Channel 2 of the app telemetry.
cod3doomy 1 year ago
parent
commit
001b996a24
3 changed files with 285 additions and 42 deletions
  1. 28 1
      variants/rak4631/platformio.ini
  2. 234 32
      variants/rak4631/target.cpp
  3. 23 9
      variants/rak4631/target.h

+ 28 - 1
variants/rak4631/platformio.ini

@@ -23,7 +23,6 @@ lib_deps =
   ${nrf52840_base.lib_deps}
   adafruit/Adafruit SSD1306 @ ^2.5.13
   stevemarple/MicroNMEA @ ^2.0.6
-  sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27
 
 [env:RAK_4631_Repeater]
 extends = rak4631
@@ -41,6 +40,30 @@ build_src_filter = ${rak4631.build_src_filter}
   +<helpers/ui/SSD1306Display.cpp>
   +<../examples/simple_repeater>
 
+[env:RAK_4631_GPS_Repeater]
+extends = rak4631
+build_flags =
+  ${rak4631.build_flags}
+  -D DISPLAY_CLASS=SSD1306Display
+  -D ADVERT_NAME='"RAK4631 GPS Repeater"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D MAX_NEIGHBOURS=8
+  -D FORCE_GPS_ALIVE=1
+  -D ENV_INCLUDE_GPS=1
+  -D ENV_INCLUDE_BME680=1
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${rak4631.build_src_filter}
+  +<helpers/ui/SSD1306Display.cpp>
+  +<../examples/simple_repeater>
+lib_deps = 
+  ${rak4631.lib_deps}
+  sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27
+  https://github.com/boschsensortec/Bosch-BSEC2-Library
+  https://github.com/boschsensortec/Bosch-BME68x-Library
+
 [env:RAK_4631_room_server]
 extends = rak4631
 build_flags =
@@ -104,6 +127,7 @@ build_flags =
   -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256
   -D ENV_INCLUDE_GPS=1
+  -D ENV_INCLUDE_BME680=1
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${rak4631.build_src_filter}
@@ -112,6 +136,9 @@ build_src_filter = ${rak4631.build_src_filter}
   +<../examples/companion_radio>
 lib_deps =
   ${rak4631.lib_deps}
+  sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27
+  https://github.com/boschsensortec/Bosch-BSEC2-Library
+  https://github.com/boschsensortec/Bosch-BME68x-Library
   densaugeo/base64 @ ~1.4.0
 
 [env:RAK_4631_terminal_chat]

+ 234 - 32
variants/rak4631/target.cpp

@@ -19,6 +19,21 @@ RAK4631SensorManager sensors = RAK4631SensorManager(nmea);
 RAK4631SensorManager sensors;
 #endif
 
+#if ENV_INCLUDE_BME680
+#ifndef TELEM_BME680_ADDRESS
+#define TELEM_BME680_ADDRESS    0x76      // BME680 environmental sensor I2C address
+#endif
+#include <bsec2.h>
+static Bsec2 BME680;
+float rawPressure = 0;
+float rawTemperature = 0;
+float compTemperature = 0;
+float rawHumidity = 0;
+float compHumidity = 0;
+float readIAQ = 0;
+float readCO2 = 0;
+#endif
+
 #ifdef DISPLAY_CLASS
   DISPLAY_CLASS display;
 #endif
@@ -43,6 +58,10 @@ void scanDevices(TwoWire *w)
                 Serial.println("\tFound RAK12500 GPS Sensor");
                 deviceOnline |= RAK12500_ONLINE;
                 break;
+            case 0x76:
+                Serial.println("\tFound RAK1906 Environment Sensor");
+                deviceOnline |= BME680_ONLINE;
+                break;
             default:
                 Serial.print("\tI2C device found at address 0x");
                 if (addr < 16) {
@@ -137,7 +156,7 @@ bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){
     ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT);
     disStandbyPin = ioPin;
     gps_active = true;
-    gps_present = true;
+    gps_detected = true;
     return true;
   }
   else
@@ -147,73 +166,256 @@ bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){
 }
 #endif
 
+#if ENV_INCLUDE_BME680
+void checkBMEStatus(Bsec2 bsec)
+{
+  if (bsec.status < BSEC_OK)
+  {
+    MESH_DEBUG_PRINTLN("BSEC error code : %f", String(bsec.status));
+  }
+  else if (bsec.status > BSEC_OK)
+  {
+    MESH_DEBUG_PRINTLN("BSEC warning code : %f", String(bsec.status));
+  }
+
+  if (bsec.sensor.status < BME68X_OK)
+  {
+    MESH_DEBUG_PRINTLN("BME68X error code : %f", String(bsec.sensor.status));
+  }
+  else if (bsec.sensor.status > BME68X_OK)
+  {
+    MESH_DEBUG_PRINTLN("BME68X warning code : %f", String(bsec.sensor.status));
+  }
+}
+void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
+{
+    if (!outputs.nOutputs)
+    {
+      MESH_DEBUG_PRINTLN("No new data to report out");
+      return;
+    }
+
+    MESH_DEBUG_PRINTLN("BSEC outputs:\n\tTime stamp = %f", String((int) (outputs.output[0].time_stamp / INT64_C(1000000))));
+    for (uint8_t i = 0; i < outputs.nOutputs; i++)
+    {
+        const bsecData output  = outputs.output[i];
+        switch (output.sensor_id)
+        {
+          case BSEC_OUTPUT_IAQ:
+              MESH_DEBUG_PRINTLN("\tIAQ = %f", String(output.signal));
+              MESH_DEBUG_PRINTLN("\tIAQ accuracy = %f", String((int) output.accuracy));
+              break;
+          case BSEC_OUTPUT_RAW_TEMPERATURE:
+              rawTemperature = output.signal;
+              MESH_DEBUG_PRINTLN("\tTemperature = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_RAW_PRESSURE:
+              rawPressure = output.signal;
+              MESH_DEBUG_PRINTLN("\tPressure = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_RAW_HUMIDITY:
+              rawHumidity = output.signal;
+              MESH_DEBUG_PRINTLN("\tHumidity = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_RAW_GAS:
+              MESH_DEBUG_PRINTLN("\tGas resistance = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_STABILIZATION_STATUS:
+              MESH_DEBUG_PRINTLN("\tStabilization status = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_RUN_IN_STATUS:
+              MESH_DEBUG_PRINTLN("\tRun in status = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
+              compTemperature = output.signal;
+              MESH_DEBUG_PRINTLN("\tCompensated temperature = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
+              compHumidity = output.signal;
+              MESH_DEBUG_PRINTLN("\tCompensated humidity = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_STATIC_IAQ:
+              readIAQ = output.signal;
+              MESH_DEBUG_PRINTLN("\tStatic IAQ = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_CO2_EQUIVALENT:
+              readCO2 = output.signal;
+              MESH_DEBUG_PRINTLN("\tCO2 Equivalent = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
+              MESH_DEBUG_PRINTLN("\tbVOC equivalent = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_GAS_PERCENTAGE:
+              MESH_DEBUG_PRINTLN("\tGas percentage = %f", String(output.signal));
+              break;
+          case BSEC_OUTPUT_COMPENSATED_GAS:
+              MESH_DEBUG_PRINTLN("\tCompensated gas = %f", String(output.signal));
+              break;
+          default:
+              break;
+        }
+    }
+}
+#endif
+
 bool RAK4631SensorManager::begin() {
   
   #ifdef MESH_DEBUG
   scanDevices(&Wire);
   #endif
 
-#if ENV_INCLUDE_GPS
-  //search for the correct IO standby pin depending on socket used
-  if(gpsIsAwake(P_GPS_STANDBY_A)){
-    MESH_DEBUG_PRINTLN("GPS is on socket A");
-  }
-  else if(gpsIsAwake(P_GPS_STANDBY_C)){
-    MESH_DEBUG_PRINTLN("GPS is on socket C"); 
-  }
-  else if(gpsIsAwake(P_GPS_STANDBY_F)){
-    MESH_DEBUG_PRINTLN("GPS is on socket F"); 
-  }
-  else{
-    MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); 
-    gps_active = false;
-    gps_present = false;
-    return false;
-  }
+  #if ENV_INCLUDE_GPS
+    //search for the correct IO standby pin depending on socket used
+    if(gpsIsAwake(P_GPS_STANDBY_A)){
+      MESH_DEBUG_PRINTLN("GPS is on socket A");
+    }
+    else if(gpsIsAwake(P_GPS_STANDBY_C)){
+      MESH_DEBUG_PRINTLN("GPS is on socket C"); 
+    }
+    else if(gpsIsAwake(P_GPS_STANDBY_F)){
+      MESH_DEBUG_PRINTLN("GPS is on socket F"); 
+    }
+    else{
+      MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); 
+      gps_active = false;
+      gps_detected = false;
+      return false;
+    }
 
-  //Now that GPS is found and set up, set to sleep for initial state
-  stop_gps();
-#endif
+    #ifndef FORCE_GPS_ALIVE
+    //Now that GPS is found and set up, set to sleep for initial state
+    stop_gps();
+    #endif
+  #endif
+
+  #if ENV_INCLUDE_BME680
+
+    bsecSensor sensorList[4] = {
+      BSEC_OUTPUT_IAQ,
+    //  BSEC_OUTPUT_RAW_TEMPERATURE,
+      BSEC_OUTPUT_RAW_PRESSURE,
+    //  BSEC_OUTPUT_RAW_HUMIDITY,
+    //  BSEC_OUTPUT_RAW_GAS,
+    //  BSEC_OUTPUT_STABILIZATION_STATUS,
+    //  BSEC_OUTPUT_RUN_IN_STATUS,
+      BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
+      BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
+    //  BSEC_OUTPUT_STATIC_IAQ,
+    //  BSEC_OUTPUT_CO2_EQUIVALENT,
+    //  BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
+    //  BSEC_OUTPUT_GAS_PERCENTAGE,
+    //  BSEC_OUTPUT_COMPENSATED_GAS
+    };
+
+    if(!BME680.begin(TELEM_BME680_ADDRESS, Wire)){
+      checkBMEStatus(BME680);
+      bme680_present = false;
+      bme680_active = false;
+      return false;
+    }
+    
+    MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS);
+    bme680_present = true;
+    bme680_active = true;
+
+    if (SAMPLING_RATE == BSEC_SAMPLE_RATE_ULP)
+	  {
+		  BME680.setTemperatureOffset(BSEC_SAMPLE_RATE_ULP);
+	  }
+	  else if (SAMPLING_RATE == BSEC_SAMPLE_RATE_LP)
+	  {
+		  BME680.setTemperatureOffset(TEMP_OFFSET_LP);
+	  }
+
+    if (!BME680.updateSubscription(sensorList, ARRAY_LEN(sensorList), SAMPLING_RATE))
+    {
+      checkBMEStatus(BME680);
+    }
+
+    BME680.attachCallback(newDataCallback);
+
+  #endif
 }
 
-#if ENV_INCLUDE_GPS
 bool RAK4631SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
+  #ifdef ENV_INCLUDE_GPS
   if (requester_permissions & TELEM_PERM_LOCATION && gps_active) {   // does requester have permission?
     telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude);
   }
+  #endif
+
+  if (requester_permissions & TELEM_PERM_ENVIRONMENT) {
+
+    #if ENV_INCLUDE_BME680
+    if (bme680_active) {
+      telemetry.addTemperature(TELEM_CHANNEL_SELF, compTemperature);
+      telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, compHumidity);
+      telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, rawPressure);
+      telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF+1, readIAQ);
+    }
+    #endif
+  }
   return true;
 }
 
 void RAK4631SensorManager::loop() {
   static long next_update = 0;
   
+  #ifdef ENV_INCLUDE_GPS
   _nmea->loop();
+  #endif
 
-  if (millis() > next_update && gps_active) {
-    node_lat = (double)ublox_GNSS.getLatitude()/10000000.;
-    node_lon = (double)ublox_GNSS.getLongitude()/10000000.;
-    node_altitude = (double)ublox_GNSS.getAltitude()/1000.;
-    MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude);
+  if (millis() > next_update) {
     
+    #ifdef ENV_INCLUDE_GPS
+    if(gps_active){
+      node_lat = (double)ublox_GNSS.getLatitude()/10000000.;
+      node_lon = (double)ublox_GNSS.getLongitude()/10000000.;
+      node_altitude = (double)ublox_GNSS.getAltitude()/1000.;
+      MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude);
+    }
+    #endif
+
+    #ifdef ENV_INCLUDE_BME680
+    if(bme680_active){
+      if (!BME680.run()){
+        checkBMEStatus(BME680);
+      }
+    }
+    #endif
     next_update = millis() + 1000;
   }
+
 }
 
-int RAK4631SensorManager::getNumSettings() const { return 1; }  // just one supported: "gps" (power switch)
+int RAK4631SensorManager::getNumSettings() const { 
+  #if ENV_INCLUDE_GPS
+    return gps_detected ? 1 : 0;  // only show GPS setting if GPS is detected
+  #else
+    return 0;
+  #endif
+}
 
 const char* RAK4631SensorManager::getSettingName(int i) const {
-  return i == 0 ? "gps" : NULL;
+  #if ENV_INCLUDE_GPS
+    return (gps_detected && i == 0) ? "gps" : NULL;
+  #else  
+    return NULL;
+  #endif
 }
 
 const char* RAK4631SensorManager::getSettingValue(int i) const {
-  if (i == 0) {
+  #if ENV_INCLUDE_GPS
+  if (gps_detected && i == 0) {
     return gps_active ? "1" : "0";
   }
+  #endif
   return NULL;
 }
 
 bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) {
-  if (strcmp(name, "gps") == 0) {
+  #if ENV_INCLUDE_GPS
+  if (gps_detected && strcmp(name, "gps") == 0) {
     if (strcmp(value, "0") == 0) {
       stop_gps();
     } else {
@@ -221,9 +423,9 @@ bool RAK4631SensorManager::setSettingValue(const char* name, const char* value)
     }
     return true;
   }
+  #endif
   return false;  // not supported
 }
-#endif
 
 mesh::LocalIdentity radio_new_identity() {
   RadioNoiseListener rng(radio);

+ 23 - 9
variants/rak4631/target.h

@@ -11,6 +11,9 @@
   #include <helpers/sensors/LocationProvider.h>
   #include <SparkFun_u-blox_GNSS_Arduino_Library.h>
 #endif
+#if ENV_INCLUDE_BME680
+  #include "bsec2.h"
+#endif
 #ifdef DISPLAY_CLASS
   #include <helpers/ui/SSD1306Display.h>
 #endif
@@ -20,7 +23,7 @@
 class RAK4631SensorManager: public SensorManager {
   #if ENV_INCLUDE_GPS
     bool gps_active = false;
-    bool gps_present = false;
+    bool gps_detected = false;
     LocationProvider * _nmea;
     SFE_UBLOX_GNSS ublox_GNSS;
     uint32_t disStandbyPin = 0;
@@ -32,20 +35,26 @@ class RAK4631SensorManager: public SensorManager {
     bool gpsIsAwake(uint32_t ioPin);
   #endif
 
+  #if ENV_INCLUDE_BME680
+    bool bme680_active = false;
+    bool bme680_present = false;
+    #define SAMPLING_RATE		BSEC_SAMPLE_RATE_ULP
+  #endif
+
   public:
   #if ENV_INCLUDE_GPS
     RAK4631SensorManager(LocationProvider &nmea): _nmea(&nmea) { }
-
-    bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
-    void loop() override;
-    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;
   #else
     RAK4631SensorManager() { }
   #endif
-    bool begin() override;
+
+  void loop() override;
+  bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
+  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;
+  bool begin() override;
 };
 
 extern RAK4631Board board;
@@ -75,6 +84,11 @@ enum {
   RAK12500_ONLINE     = _BV(14),
 };
 
+#if ENV_INCLUDE_BME680
+  void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec);
+  void checkBMEStatus(Bsec2 bsec);
+#endif
+
 bool radio_init();
 uint32_t radio_get_rng_seed();
 void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);