diff --git a/examples/Sensors/Analog/Analog.ino b/examples/Sensors/Analog/Analog/Analog.ino similarity index 100% rename from examples/Sensors/Analog/Analog.ino rename to examples/Sensors/Analog/Analog/Analog.ino diff --git a/examples/Sensors/Teros10/Teros10.ino b/examples/Sensors/Analog/Teros10/Teros10.ino similarity index 100% rename from examples/Sensors/Teros10/Teros10.ino rename to examples/Sensors/Analog/Teros10/Teros10.ino diff --git a/examples/Sensors/ADS1115/ADS1115.ino b/examples/Sensors/I2C/ADS1115/ADS1115.ino similarity index 100% rename from examples/Sensors/ADS1115/ADS1115.ino rename to examples/Sensors/I2C/ADS1115/ADS1115.ino diff --git a/examples/Sensors/AS7262/AS7262.ino b/examples/Sensors/I2C/AS7262/AS7262.ino similarity index 100% rename from examples/Sensors/AS7262/AS7262.ino rename to examples/Sensors/I2C/AS7262/AS7262.ino diff --git a/examples/Sensors/AS7263/AS7263.ino b/examples/Sensors/I2C/AS7263/AS7263.ino similarity index 100% rename from examples/Sensors/AS7263/AS7263.ino rename to examples/Sensors/I2C/AS7263/AS7263.ino diff --git a/examples/Sensors/AS7265X/AS7265X.ino b/examples/Sensors/I2C/AS7265X/AS7265X.ino similarity index 100% rename from examples/Sensors/AS7265X/AS7265X.ino rename to examples/Sensors/I2C/AS7265X/AS7265X.ino diff --git a/examples/Sensors/EZO/EZO-CO2/EZO-CO2.ino b/examples/Sensors/I2C/EZO/EZO-CO2/EZO-CO2.ino similarity index 100% rename from examples/Sensors/EZO/EZO-CO2/EZO-CO2.ino rename to examples/Sensors/I2C/EZO/EZO-CO2/EZO-CO2.ino diff --git a/examples/Sensors/EZO/EZO-DisOxygen/EZO-DisOxygen.ino b/examples/Sensors/I2C/EZO/EZO-DisOxygen/EZO-DisOxygen.ino similarity index 100% rename from examples/Sensors/EZO/EZO-DisOxygen/EZO-DisOxygen.ino rename to examples/Sensors/I2C/EZO/EZO-DisOxygen/EZO-DisOxygen.ino diff --git a/examples/Sensors/EZO/EZO-ORP/EZO-ORP.ino b/examples/Sensors/I2C/EZO/EZO-ORP/EZO-ORP.ino similarity index 100% rename from examples/Sensors/EZO/EZO-ORP/EZO-ORP.ino rename to examples/Sensors/I2C/EZO/EZO-ORP/EZO-ORP.ino diff --git a/examples/Sensors/EZO/EZO-PH/EZO-PH.ino b/examples/Sensors/I2C/EZO/EZO-PH/EZO-PH.ino similarity index 100% rename from examples/Sensors/EZO/EZO-PH/EZO-PH.ino rename to examples/Sensors/I2C/EZO/EZO-PH/EZO-PH.ino diff --git a/examples/Sensors/EZO/EZO-RGB/EZO-RGB.ino b/examples/Sensors/I2C/EZO/EZO-RGB/EZO-RGB.ino similarity index 100% rename from examples/Sensors/EZO/EZO-RGB/EZO-RGB.ino rename to examples/Sensors/I2C/EZO/EZO-RGB/EZO-RGB.ino diff --git a/examples/Sensors/K30/K30.ino b/examples/Sensors/I2C/K30/K30.ino similarity index 100% rename from examples/Sensors/K30/K30.ino rename to examples/Sensors/I2C/K30/K30.ino diff --git a/examples/Sensors/MB1232/MB1232.ino b/examples/Sensors/I2C/MB1232/MB1232.ino similarity index 100% rename from examples/Sensors/MB1232/MB1232.ino rename to examples/Sensors/I2C/MB1232/MB1232.ino diff --git a/examples/Sensors/MMA8451/MMA8451.ino b/examples/Sensors/I2C/MMA8451/MMA8451.ino similarity index 100% rename from examples/Sensors/MMA8451/MMA8451.ino rename to examples/Sensors/I2C/MMA8451/MMA8451.ino diff --git a/examples/Sensors/MS5803/MS5803.ino b/examples/Sensors/I2C/MS5803/MS5803.ino similarity index 100% rename from examples/Sensors/MS5803/MS5803.ino rename to examples/Sensors/I2C/MS5803/MS5803.ino diff --git a/examples/Sensors/MS5803_2_Sensors/MS5803_2_Sensors.ino b/examples/Sensors/I2C/MS5803_2_Sensors/MS5803_2_Sensors.ino similarity index 100% rename from examples/Sensors/MS5803_2_Sensors/MS5803_2_Sensors.ino rename to examples/Sensors/I2C/MS5803_2_Sensors/MS5803_2_Sensors.ino diff --git a/examples/Sensors/Multiplexer/Multiplexer.ino b/examples/Sensors/I2C/Multiplexer/Multiplexer.ino similarity index 100% rename from examples/Sensors/Multiplexer/Multiplexer.ino rename to examples/Sensors/I2C/Multiplexer/Multiplexer.ino diff --git a/examples/Sensors/I2C/SEN55/SEN55.ino b/examples/Sensors/I2C/SEN55/SEN55.ino new file mode 100644 index 00000000..c7b2c902 --- /dev/null +++ b/examples/Sensors/I2C/SEN55/SEN55.ino @@ -0,0 +1,35 @@ +/** + * This is an example use case for using the SEN55 Sensor + * + * MANAGER MUST BE INCLUDED FIRST IN ALL CODE + */ +#include + +#include + +Manager manager("Device", 1); +// Manager Reference, Whether or not we should measure particulate matter or nor +Loom_SEN55 sen55(manager, true); + +void setup() { + + // Start the serial interface + manager.beginSerial(); + + // Initialize the manager + manager.initialize(); +} + +void loop() { + // Measure the data from the sensors + manager.measure(); + + // Package the data into JSON + manager.package(); + + // Print the JSON document to the Serial monitor + manager.display_data(); + + // Wait for 5 seconds + manager.pause(5000); +} diff --git a/examples/Sensors/SHT31/SHT31.ino b/examples/Sensors/I2C/SHT31/SHT31.ino similarity index 100% rename from examples/Sensors/SHT31/SHT31.ino rename to examples/Sensors/I2C/SHT31/SHT31.ino diff --git a/examples/Sensors/STEMMA/STEMMA.ino b/examples/Sensors/I2C/STEMMA/STEMMA.ino similarity index 100% rename from examples/Sensors/STEMMA/STEMMA.ino rename to examples/Sensors/I2C/STEMMA/STEMMA.ino diff --git a/examples/Sensors/TSL2591/TSL2591.ino b/examples/Sensors/I2C/TSL2591/TSL2591.ino similarity index 100% rename from examples/Sensors/TSL2591/TSL2591.ino rename to examples/Sensors/I2C/TSL2591/TSL2591.ino diff --git a/examples/Sensors/ZXGesture/ZXGesture.ino b/examples/Sensors/I2C/ZXGesture/ZXGesture.ino similarity index 100% rename from examples/Sensors/ZXGesture/ZXGesture.ino rename to examples/Sensors/I2C/ZXGesture/ZXGesture.ino diff --git a/examples/Sensors/MAX/MAX31856/MAX31856.ino b/examples/Sensors/SPI/MAX/MAX31856/MAX31856.ino similarity index 100% rename from examples/Sensors/MAX/MAX31856/MAX31856.ino rename to examples/Sensors/SPI/MAX/MAX31856/MAX31856.ino diff --git a/examples/Sensors/MAX/MAX31865/MAX31865.ino b/examples/Sensors/SPI/MAX/MAX31865/MAX31865.ino similarity index 100% rename from examples/Sensors/MAX/MAX31865/MAX31865.ino rename to examples/Sensors/SPI/MAX/MAX31865/MAX31865.ino diff --git a/examples/Sensors/NOVASDS/NOVASDS.ino b/examples/Sensors/Serial/NOVASDS/NOVASDS.ino similarity index 100% rename from examples/Sensors/NOVASDS/NOVASDS.ino rename to examples/Sensors/Serial/NOVASDS/NOVASDS.ino diff --git a/src/Hardware/Loom_Hypnos/Loom_Hypnos.cpp b/src/Hardware/Loom_Hypnos/Loom_Hypnos.cpp index 0fa6c705..54b3021a 100644 --- a/src/Hardware/Loom_Hypnos/Loom_Hypnos.cpp +++ b/src/Hardware/Loom_Hypnos/Loom_Hypnos.cpp @@ -9,7 +9,7 @@ Loom_Hypnos::Loom_Hypnos(Manager& man, HYPNOS_VERSION version, TIME_ZONE zone, b pinMode(5, OUTPUT); // 3.3v power rail pinMode(6, OUTPUT); // 5v power rail pinMode(LED_BUILTIN, OUTPUT); // Status LED - + // Create the SD Manager if we want to use SD if(useSD){ sdMan = new SDManager(manInst, sd_chip_select); @@ -26,7 +26,7 @@ Loom_Hypnos::Loom_Hypnos(Manager& man, HYPNOS_VERSION version, TIME_ZONE zone, b ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// -Loom_Hypnos::~Loom_Hypnos(){ +Loom_Hypnos::~Loom_Hypnos(){ if(sdMan != nullptr) delete sdMan; } @@ -77,11 +77,11 @@ void Loom_Hypnos::enable(bool enable33, bool enable5){ ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// -void Loom_Hypnos::disable(){ +void Loom_Hypnos::disable(bool disable33, bool disable5){ // Disable the 3.3v and 5v rails on the Hypnos - digitalWrite(5, HIGH); - digitalWrite(6, LOW); - digitalWrite(LED_BUILTIN, LOW); + digitalWrite(5, (disable33) ? HIGH : LOW); + digitalWrite(6, (disable5) ? LOW : HIGH); + digitalWrite(LED_BUILTIN, LOW); if(enableSD){ // Disable SPI pins/SD chip select to save power @@ -115,7 +115,7 @@ bool Loom_Hypnos::registerInterrupt(InterruptCallbackFunction isrFunc, int inter LOG(F("Interrupt successfully attached!")); } else{ - + attachInterrupt(digitalPinToInterrupt(interruptPin), isrFunc, triggerState); attachInterrupt(digitalPinToInterrupt(interruptPin), isrFunc, triggerState); LOG(F("Interrupt successfully attached!")); @@ -124,7 +124,7 @@ bool Loom_Hypnos::registerInterrupt(InterruptCallbackFunction isrFunc, int inter pinToInterrupt.insert(std::make_pair(interruptPin, std::make_tuple(isrFunc, triggerState, interruptType))); FUNCTION_END; return true; - } + } else{ detachInterrupt(digitalPinToInterrupt(interruptPin)); ERROR(F("Failed to attach interrupt! Interrupt callback evaluated to a null pointer, it is possible you forgot to supply a callback function")); @@ -134,8 +134,8 @@ bool Loom_Hypnos::registerInterrupt(InterruptCallbackFunction isrFunc, int inter FUNCTION_END; return false; - - + + } ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -153,7 +153,7 @@ bool Loom_Hypnos::reattachRTCInterrupt(int interruptPin){ attachInterrupt(digitalPinToInterrupt(interruptPin), std::get<0>(pinToInterrupt[interruptPin]), std::get<1>(pinToInterrupt[interruptPin])); attachInterrupt(digitalPinToInterrupt(interruptPin), std::get<0>(pinToInterrupt[interruptPin]), std::get<1>(pinToInterrupt[interruptPin])); - + } else{ LowPower.attachInterruptWakeup(interruptPin, std::get<0>(pinToInterrupt[interruptPin]), std::get<1>(pinToInterrupt[interruptPin])); @@ -167,7 +167,7 @@ bool Loom_Hypnos::reattachRTCInterrupt(int interruptPin){ ////////////////////////////////////////////////////////////////////////////////////////////////////// void Loom_Hypnos::wakeup(){ - detachInterrupt(pinToInterrupt.begin()->first); // Detach the interrupt so it doesn't trigger again + detachInterrupt(pinToInterrupt.begin()->first); // Detach the interrupt so it doesn't trigger again } ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -182,7 +182,7 @@ void Loom_Hypnos::initializeRTC(){ ERROR(F("Couldn't start RTC! Check your connections... Execution will now hang as this is likely a fatal error")); return; } - + // This may end up causing a problem in practice - what if RTC loses power in field? Shouldn't happen with coin cell batt backup if (RTC_DS.lostPower()) { WARNING(F("RTC lost power, let's set the time!")); @@ -202,14 +202,14 @@ void Loom_Hypnos::initializeRTC(){ RTC_DS.writeSqwPinMode(DS3231_OFF); - // We successfully started the RTC + // We successfully started the RTC LOG(F("DS3231 Real-Time Clock Initialized Successfully!")); RTC_initialized = true; snprintf(output, OUTPUT_SIZE, "Custom time successfully set to: %s", getCurrentTime().text()); LOG(output); FUNCTION_END; - - + + } ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -220,7 +220,6 @@ DateTime Loom_Hypnos::get_utc_time(){ // Subtract 30 minutes from this zone if(timezone == TIME_ZONE::ACST) return now + TimeSpan(0, timezone, -30, 0); - if(isDaylightSavings()){ return now + TimeSpan(0, (timezone)-1, 0, 0); }else{ @@ -245,7 +244,7 @@ bool Loom_Hypnos::isDaylightSavings(){ ////////////////////////////////////////////////////////////////////////////////////////////////////// DateTime Loom_Hypnos::getCurrentTime(){ if(RTC_initialized) - return RTC_DS.now(); + return RTC_DS.now(); else{ LOG(F("Attempted to pull time when RTC was not previously initialized! Returned default datetime")); return DateTime(); @@ -288,7 +287,7 @@ bool Loom_Hypnos::networkTimeUpdate(){ ////////////////////////////////////////////////////////////////////////////////////////////////////// void Loom_Hypnos::dateTime_toString(DateTime time, char array[21]){ - + // Formatted as: YYYY-MM-DDTHH:MM:SSZ snprintf_P(array, 21, PSTR("%u-%02u-%02uT%u:%u:%uZ"), time.year(), time.month(), time.day(), time.hour(), time.minute(), time.second()); } @@ -312,7 +311,7 @@ void Loom_Hypnos::set_custom_time(){ // Entering the year LOG(F("Enter the Year (Four digits, e.g. 2020)")); - + while(computer_year == ""){ computer_year = Serial.readStringUntil('\n'); } @@ -337,7 +336,7 @@ void Loom_Hypnos::set_custom_time(){ } snprintf(output, OUTPUT_SIZE, "Day Entered: %s", computer_day.c_str()); LOG(output); - + // Entering the hour LOG(F("Enter the Hour (0 ~ 23)")); @@ -376,7 +375,7 @@ void Loom_Hypnos::set_custom_time(){ ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// -void Loom_Hypnos::setInterruptDuration(const TimeSpan duration){ +void Loom_Hypnos::setInterruptDuration(const TimeSpan duration){ FUNCTION_START; char output[OUTPUT_SIZE]; @@ -397,13 +396,23 @@ void Loom_Hypnos::setInterruptDuration(const TimeSpan duration){ /* Sleep Functionality */ ////////////////////////////////////////////////////////////////////////////////////////////////////// -void Loom_Hypnos::sleep(bool waitForSerial){ +void Loom_Hypnos::sleep(bool waitForSerial, bool disable33, bool disable5){ + // Try to power down the active modules // If the alarm set time is less than the current time we missed our next alarm so we need to set a new one, we need to check if we have powered on already so we dont use the RTC that isn't enabled bool hasAlarmTriggered = false; + if(shouldPowerUp){ hasAlarmTriggered = RTC_DS.getAlarm(1).unixtime() <= RTC_DS.now().unixtime(); } + + + //disable(disable33, disable5); + pre_sleep(); // Pre-sleep cleanup + disable(disable33, disable5); // Disable the power rails + shouldPowerUp = true; + LowPower.sleep(); // Go to sleep and hang + post_sleep(waitForSerial); // Wake up if(!hasAlarmTriggered){ @@ -437,8 +446,7 @@ void Loom_Hypnos::pre_sleep(){ attachInterrupt(digitalPinToInterrupt(pinToInterrupt.begin()->first), std::get<0>(pinToInterrupt.begin()->second), std::get<1>(pinToInterrupt.begin()->second)); // Disable the power rails - disable(); - + // disable(); <------------ TODO: test with this commented out } ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -449,12 +457,12 @@ void Loom_Hypnos::post_sleep(bool waitForSerial){ if(shouldPowerUp){ USBDevice.attach(); Serial.begin(115200); - + enable(); // Re-init the modules that need it - manInst->power_up(); - + manInst->power_up(); + // Clear any pending RTC alarms RTC_DS.clearAlarm(); @@ -516,7 +524,7 @@ void Loom_Hypnos::getTimeZoneFromSD(const char* fileName){ if(!json["timezone"].isNull()) timezone = timezoneMap[json["timezone"].as()]; LOG(F("Timezone successfully loaded!")); - + } FUNCTION_END; } @@ -554,8 +562,8 @@ void Loom_Hypnos::createTimezoneMap(){ ////////////////////////////////////////////////////////////////////////////////////////////////////// bool Loom_Hypnos::logToSD() { - FUNCTION_START; - sdMan->log(getCurrentTime()); + FUNCTION_START; + sdMan->log(getCurrentTime()); FUNCTION_END; } -////////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/Hardware/Loom_Hypnos/Loom_Hypnos.h b/src/Hardware/Loom_Hypnos/Loom_Hypnos.h index b4ce0162..fddbdc05 100644 --- a/src/Hardware/Loom_Hypnos/Loom_Hypnos.h +++ b/src/Hardware/Loom_Hypnos/Loom_Hypnos.h @@ -93,7 +93,7 @@ class Loom_Hypnos : public Module{ * @param use_custom_time Use a specific time set by the user that is different than the compile time * @param useSD Whether or not SD card functionality should be enabled */ - Loom_Hypnos(Manager& man, HYPNOS_VERSION version, TIME_ZONE zone, bool use_custom_time = false, bool useSD = true); + Loom_Hypnos(Manager& man, HYPNOS_VERSION version, TIME_ZONE zone, bool use_custom_time = true, bool useSD = true); /** * Cleanup any dynamically allocated pointers @@ -109,13 +109,13 @@ class Loom_Hypnos : public Module{ * @param enable33 whether or not to enable the 3.3v rails * @param enable5 whether or not to enable the 5v and 12v rails */ - void enable(bool enable33 = true, bool emable5 = true); + void enable(bool enable33 = true, bool enable5 = true); /** * Disables the Hypnos Board * Disables the Power Rails and sets the SPI pins to INPUT which effectively disables them */ - void disable(); + void disable(bool disable33 = true, bool disable5 = true); /* SD Functionality */ @@ -156,8 +156,10 @@ class Loom_Hypnos : public Module{ /** * Drops the Feather M0 and Hypnos board into a low power sleep waiting for an interrupt to wake it up and pull it out of sleep * @param waitForSerial Whether or not we should wait for the user to open the serial monitor before continuing execution + * @param disable33 Whether or not to disable 3.3V rails + * @param disable5 Whether or not to disable 5V and 12V rails */ - void sleep(bool waitForSerial = false); + void sleep(bool waitForSerial = false, bool disable33 = true, bool disable5 = true); /** * Get the current time from the RTC @@ -268,4 +270,4 @@ class Loom_Hypnos : public Module{ -}; \ No newline at end of file +}; diff --git a/src/Module.h b/src/Module.h index 20d6af09..12165b9a 100644 --- a/src/Module.h +++ b/src/Module.h @@ -20,9 +20,7 @@ #define TIMER_RESET #endif -#define OUTPUT_SIZE 200 -#define MAX_JSON_SIZE 2000 - +#define OUTPUT_SIZE 256 /** * General overarching interface to provide basic unified functionality diff --git a/src/Sensors/I2C/Loom_SEN55/Loom_SEN55.cpp b/src/Sensors/I2C/Loom_SEN55/Loom_SEN55.cpp new file mode 100644 index 00000000..5b2c6644 --- /dev/null +++ b/src/Sensors/I2C/Loom_SEN55/Loom_SEN55.cpp @@ -0,0 +1,166 @@ +#include "Loom_SEN55.h" +#include "Logger.h" + +////////////////////////////////////////////////////////////////////////////////////////////////////// +Loom_SEN55::Loom_SEN55( + Manager& man, + bool measurePM, + bool useMux + ) : I2CDevice("SEN55"), manInst(&man), measurePM(measurePM){ + + // Register the module with the manager + if(!useMux) + manInst->registerModule(this); + } +////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////// +void Loom_SEN55::initialize() { + FUNCTION_START; + char output[OUTPUT_SIZE]; + char errorMessage[OUTPUT_SIZE]; + + /* Initialize wire and start the sensor using the standard I2C interface */ + Wire.begin(); + sen5x.begin(Wire); + + // Attempt to reset the device + uint16_t error = sen5x.deviceReset(); + if(error){ + /* Stringify the errro and log the error */ + errorToString(error, errorMessage, OUTPUT_SIZE); + snprintf(output, OUTPUT_SIZE, "Error occurred while attempting to reset device: %s, module will not be initialized!", errorMessage); + ERROR(output); + moduleInitialized = false; + return; + } + else{ + LOG("Sensor successfully initialized!"); + } + + /* Determine if we want to measure with the particulate matter readings or not */ + if(measurePM){ + error = sen5x.startMeasurement(); + } + else{ + error = sen5x.startMeasurementWithoutPm(); + } + + if(error){ + errorToString(error, errorMessage, OUTPUT_SIZE); + snprintf(output, OUTPUT_SIZE, "Error occurred when attempting to collect measurement: %s", errorMessage); + ERROR(output); + FUNCTION_END; + return; + } + + // Wait for required one second as described in the readMeasuredValues() documentation + LOG("Waiting 10 seconds seconds so that we can warm the sensor up"); + delay(10000); + FUNCTION_END; +} +////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////// +void Loom_SEN55::measure() { + FUNCTION_START; + char output[OUTPUT_SIZE]; + char sensorError[OUTPUT_SIZE]; + + /* TODO: Implement this once we know the raw integration works. + // Get the current connection status + bool connectionStatus = checkDeviceConnection(); + + // If we are connected and we need to reinit + if(connectionStatus && needsReinit){ + initialize(); + needsReinit = false; + } + + // If we are not connected + else if(!connectionStatus){ + ERROR(F("No acknowledge received from the device")); + FUNCTION_END; + return; + }*/ + + /* Attempt to initiate a measurement with the sensor */ + + uint16_t error = 0; + + // Give the sensor time to prepare for measuring + bool dataReady = false; + uint16_t startTime = millis(); + LOG("Waiting for data to be ready... If not ready in 10 seconds we will stop trying"); + while(!dataReady && millis() < startTime + 10000){ + error = sen5x.readDataReady(dataReady); + if(error){ + errorToString(error, sensorError, OUTPUT_SIZE); + snprintf(output, OUTPUT_SIZE, "Failed to check if data was ready to be read: %s", sensorError); + ERROR(output); + } + } + + // If the data was not ready we don't want to update the sensor values + if(dataReady){ + LOG("Device was ready to read a new sample!"); + // Request the measured values form the sensor + error = sen5x.readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex + ); + + // Check if we had an error reading the sensor values + if(error){ + errorToString(error, sensorError, OUTPUT_SIZE); + snprintf(output, OUTPUT_SIZE, "Error occurred when reading measurement: %s", sensorError); + ERROR(output); + FUNCTION_END; + return; + } + }else{ + ERROR("No new data was ready within the given time period."); + } + + FUNCTION_END; +} +////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////// +void Loom_SEN55::package() { + FUNCTION_START; + JsonObject json = manInst->get_data_object(getModuleName()); + + // Only include the PM measurements if we are actually measuring PM + if(measurePM){ + json["PM1_0"] = massConcentrationPm10p0; + json["PM2_5"] = massConcentrationPm2p5; + json["PM4_0"] = massConcentrationPm4p0; + json["PM10_0"] = massConcentrationPm10p0; + } + json["AmbientHumidity"] = (isnan(ambientHumidity) ? -1 : ambientHumidity); + json["AmbientTemperature"] = (isnan(ambientTemperature) ? -1 : ambientTemperature); + json["VocIndex"] = (isnan(vocIndex) ? -1 : vocIndex); + json["NoxIndex"] = (isnan(noxIndex) ? -1 : noxIndex); + FUNCTION_END; +} +////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////// +void Loom_SEN55::adjustTempOffset(float offset) { + FUNCTION_START; + char output[OUTPUT_SIZE]; + char sensorError[OUTPUT_SIZE]; + + if(moduleInitialized){ + uint16_t error = sen5x.setTemperatureOffsetSimple(offset); + if(error){ + errorToString(error, sensorError, OUTPUT_SIZE); + snprintf(output, OUTPUT_SIZE, "Failed to adjust sensor offset: %s", sensorError); + ERROR(output); + } + } + FUNCTION_END; +} +////////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/Sensors/I2C/Loom_SEN55/Loom_SEN55.h b/src/Sensors/I2C/Loom_SEN55/Loom_SEN55.h new file mode 100644 index 00000000..7294d467 --- /dev/null +++ b/src/Sensors/I2C/Loom_SEN55/Loom_SEN55.h @@ -0,0 +1,198 @@ +#pragma once + +#include +#include + +#include "../I2CDevice.h" +#include "Loom_Manager.h" + +/** + * SEN55 Air Quality sensors, supports pm 1.0, 2.5, 4.0, 10 as well as Temp/Humidity and Nox and Voc index + * + * NOTE: To get accurate results using the SE555 it should be powered on and remain on as according to the data sheet, + * the switch-on behavior for the VOC Index is ~ 1 hr and the NOx is ~ 6 hours. + * Data sheet: https://cdn.sparkfun.com/assets/5/b/f/2/8/Sensirion_Datasheet_SEN5x.pdf (Page 8) + * + * @author Will Richards + */ +class Loom_SEN55 : public I2CDevice{ + protected: + + // Manager controlled functions + void measure() override; + void initialize() override; + void power_up() override {}; + void power_down() override {}; + void package() override; + + public: + /** + * Constructs a new SEN55 sensor + * + * @param man Reference to the manager that is used to universally package all data + * @param measurePM This sets whether or not we should conduct measurements without PM readings (uses less power) + * @param useMux Whether or not we are using the multiplexer class + */ + Loom_SEN55( + Manager& man, + bool measurePM = true, + bool useMux = false + ); + + /** + * Adjust the temperature reading offset in celsius. By default it accounts for the internal heating of the sensor. + * Adjustments should be made based off this datasheet: https://cdn.sos.sk/productdata/a8/47/608a6927/sen54-sdn-t.pdf + * + * @param offset The additional temperature offset in degrees celsius + */ + void adjustTempOffset(float offset); + + /** + * Copied directly from the SEN55 library + * setVocAlgorithmTuningParameters() - Sets the tuning parameters of the VOC + * algorithm. + * + * Supported sensors: SEN54, SEN55 + * + * @note This command is available only in idle mode. In measure mode, this + * command has no effect. In addition, it has no effect if at least one + * parameter is outside the specified range. + * + * @param indexOffset VOC index representing typical (average) conditions. + * Allowed values are in range 1..250. The default value is 100. + * + * @param learningTimeOffsetHours Time constant to estimate the VOC + * algorithm offset from the history in hours. Past events will be forgotten + * after about twice the learning time. Allowed values are in range 1..1000. + * The default value is 12 hours. + * + * @param learningTimeGainHours Time constant to estimate the VOC algorithm + * gain from the history in hours. Past events will be forgotten after about + * twice the learning time. Allowed values are in range 1..1000. The default + * value is 12 hours. + * + * @param gatingMaxDurationMinutes Maximum duration of gating in minutes + * (freeze of estimator during high VOC index signal). Set to zero to + * disable the gating. Allowed values are in range 0..3000. The default + * value is 180 minutes. + * + * @param stdInitial Initial estimate for standard deviation. Lower value + * boosts events during initial learning period, but may result in larger + * device-to-device variations. Allowed values are in range 10..5000. The + * default value is 50. + * + * @param gainFactor Gain factor to amplify or to attenuate the VOC index + * output. Allowed values are in range 1..1000. The default value is 230. + * + * @return 0 on success, an error code otherwise + */ + uint16_t setVocAlgorithmTuningParameters(int16_t indexOffset, + int16_t learningTimeOffsetHours, + int16_t learningTimeGainHours, + int16_t gatingMaxDurationMinutes, + int16_t stdInitial, + int16_t gainFactor) { return sen5x.setVocAlgorithmTuningParameters(indexOffset, learningTimeOffsetHours, learningTimeGainHours, gatingMaxDurationMinutes, stdInitial, gainFactor);}; + + + /** + * setNoxAlgorithmTuningParameters() - Sets the tuning parameters of the NOx + * algorithm. + * + * Supported sensors: SEN55 + * + * @note This command is available only in idle mode. In measure mode, this + * command has no effect. In addition, it has no effect if at least one + * parameter is outside the specified range. + * + * @param indexOffset NOx index representing typical (average) conditions. + * Allowed values are in range 1..250. The default value is 1. + * + * @param learningTimeOffsetHours Time constant to estimate the NOx + * algorithm offset from the history in hours. Past events will be forgotten + * after about twice the learning time. Allowed values are in range 1..1000. + * The default value is 12 hours. + * + * @param learningTimeGainHours The time constant to estimate the NOx + * algorithm gain from the history has no impact for NOx. This parameter is + * still in place for consistency reasons with the VOC tuning parameters + * command. This parameter must always be set to 12 hours. + * + * @param gatingMaxDurationMinutes Maximum duration of gating in minutes + * (freeze of estimator during high NOx index signal). Set to zero to + * disable the gating. Allowed values are in range 0..3000. The default + * value is 720 minutes. + * + * @param stdInitial The initial estimate for standard deviation parameter + * has no impact for NOx. This parameter is still in place for consistency + * reasons with the VOC tuning parameters command. This parameter must + * always be set to 50. + * + * @param gainFactor Gain factor to amplify or to attenuate the NOx index + * output. Allowed values are in range 1..1000. The default value is 230. + * + * @return 0 on success, an error code otherwise + */ + uint16_t setNoxAlgorithmTuningParameters(int16_t indexOffset, + int16_t learningTimeOffsetHours, + int16_t learningTimeGainHours, + int16_t gatingMaxDurationMinutes, + int16_t stdInitial, + int16_t gainFactor) { return sen5x.setNoxAlgorithmTuningParameters(indexOffset, learningTimeOffsetHours, learningTimeGainHours, gatingMaxDurationMinutes, stdInitial, gainFactor); }; + /** + * Get the PM 1.0 reading + */ + float getPM1() { return massConcentrationPm1p0; }; + + /** + * Get the PM2.5 reading + */ + float getPM25() { return massConcentrationPm2p5; }; + + /** + * Get the PM4.0 reading + */ + float getPM4() { return massConcentrationPm4p0; }; + + /** + * Get the PM 10 reading + */ + float getPM10() { return massConcentrationPm10p0; }; + + /** + * Get the PM2.5 reading + */ + float getHumidity() { return ambientHumidity; }; + + /** + * Get the temperature reading + */ + float getTemperature() { return ambientTemperature; }; + + /** + * Get the VOX Index reading + */ + float getVOCIndex() { return vocIndex; }; + + /** + * Get NOX Index + */ + float getNOXIndex() { return noxIndex; }; + + private: + Manager* manInst; // Instance of the manager + SensirionI2CSen5x sen5x; // Instance of the SEN55 object + + bool measurePM; // Are we measuring particulate matter or not + + /* Sensor readings */ + float massConcentrationPm1p0; + float massConcentrationPm2p5; + float massConcentrationPm4p0; + float massConcentrationPm10p0; + float ambientHumidity; + float ambientTemperature; + float vocIndex; + float noxIndex; + + +}; \ No newline at end of file diff --git a/src/Sensors/I2C/Loom_SHT31/Loom_SHT31.h b/src/Sensors/I2C/Loom_SHT31/Loom_SHT31.h index 22320357..6b031c4b 100644 --- a/src/Sensors/I2C/Loom_SHT31/Loom_SHT31.h +++ b/src/Sensors/I2C/Loom_SHT31/Loom_SHT31.h @@ -22,7 +22,7 @@ class Loom_SHT31 : public I2CDevice{ public: /** - * Constructs a new TSL2591 sensor + * Constructs a new SHT31 sensor * @param man Reference to the manager that is used to universally package all data * @param address I2C address that is assigned to the sensor */