diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 3014bc59..9391f99b 100644 --- a/variants/rak4631/platformio.ini +++ b/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} + +<../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} + + + +<../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] diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index ede755e2..45d10975 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -19,6 +19,22 @@ 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 +static Bsec2 BME680; +float rawPressure = 0; +float rawTemperature = 0; +float compTemperature = 0; +float rawHumidity = 0; +float compHumidity = 0; +float readIAQ = 0; +float readStaticIAQ = 0; +float readCO2 = 0; +#endif + #ifdef DISPLAY_CLASS DISPLAY_CLASS display; #endif @@ -43,6 +59,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 +157,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 +167,258 @@ 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", float(bsec.status)); + } + else if (bsec.status > BSEC_OK) + { + MESH_DEBUG_PRINTLN("BSEC warning code : %f", float(bsec.status)); + } + + if (bsec.sensor.status < BME68X_OK) + { + MESH_DEBUG_PRINTLN("BME68X error code : %f", bsec.sensor.status); + } + else if (bsec.sensor.status > BME68X_OK) + { + MESH_DEBUG_PRINTLN("BME68X warning code : %f", 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", (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: + readIAQ = output.signal; + MESH_DEBUG_PRINTLN("\tIAQ = %f", output.signal); + MESH_DEBUG_PRINTLN("\tIAQ accuracy = %f", output.accuracy); + break; + case BSEC_OUTPUT_RAW_TEMPERATURE: + rawTemperature = output.signal; + MESH_DEBUG_PRINTLN("\tTemperature = %f", output.signal); + break; + case BSEC_OUTPUT_RAW_PRESSURE: + rawPressure = output.signal; + MESH_DEBUG_PRINTLN("\tPressure = %f", output.signal); + break; + case BSEC_OUTPUT_RAW_HUMIDITY: + rawHumidity = output.signal; + MESH_DEBUG_PRINTLN("\tHumidity = %f", output.signal); + break; + case BSEC_OUTPUT_RAW_GAS: + MESH_DEBUG_PRINTLN("\tGas resistance = %f", output.signal); + break; + case BSEC_OUTPUT_STABILIZATION_STATUS: + MESH_DEBUG_PRINTLN("\tStabilization status = %f", output.signal); + break; + case BSEC_OUTPUT_RUN_IN_STATUS: + MESH_DEBUG_PRINTLN("\tRun in status = %f", output.signal); + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: + compTemperature = output.signal; + MESH_DEBUG_PRINTLN("\tCompensated temperature = %f", output.signal); + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: + compHumidity = output.signal; + MESH_DEBUG_PRINTLN("\tCompensated humidity = %f", output.signal); + break; + case BSEC_OUTPUT_STATIC_IAQ: + readStaticIAQ = output.signal; + MESH_DEBUG_PRINTLN("\tStatic IAQ = %f", output.signal); + break; + case BSEC_OUTPUT_CO2_EQUIVALENT: + readCO2 = output.signal; + MESH_DEBUG_PRINTLN("\tCO2 Equivalent = %f", output.signal); + break; + case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: + MESH_DEBUG_PRINTLN("\tbVOC equivalent = %f", output.signal); + break; + case BSEC_OUTPUT_GAS_PERCENTAGE: + MESH_DEBUG_PRINTLN("\tGas percentage = %f", output.signal); + break; + case BSEC_OUTPUT_COMPENSATED_GAS: + MESH_DEBUG_PRINTLN("\tCompensated gas = %f", 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[5] = { + 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.addTemperature(TELEM_CHANNEL_SELF+1, readIAQ); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF+1, readStaticIAQ); + } + #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 +426,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); diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index 3e6db0aa..0460c9e8 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -11,6 +11,9 @@ #include #include #endif +#if ENV_INCLUDE_BME680 + #include "bsec2.h" +#endif #ifdef DISPLAY_CLASS #include #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);