#include "co2_sensor.h" namespace config { // Values should be defined in config.h uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor) const uint16_t altitude_above_sea_level = ALTITUDE_ABOVE_SEA_LEVEL; // [m] uint16_t co2_calibration_level = ATMOSPHERIC_CO2_CONCENTRATION; // [ppm] #ifdef TEMPERATURE_OFFSET // Residual heat from CO2 sensor seems to be high enough to change the temperature reading. How much should it be offset? // NOTE: Sign isn't relevant. The returned temperature will always be shifted down. const float temperature_offset = TEMPERATURE_OFFSET; // [K] #else const float temperature_offset = -3.0; // [K] Temperature measured by sensor is usually at least 3K too high. #endif const bool auto_calibrate_sensor = AUTO_CALIBRATE_SENSOR; // [true / false] } namespace sensor { SCD30 scd30; int16_t co2 = 0; float temperature = 0; float humidity = 0; String timestamp = ""; void initialize() { #if defined(ESP8266) Wire.begin(12, 14); // ESP8266 - D6, D5; #endif #if defined(ESP32) Wire.begin(21, 22); // ESP32 /** * SCD30 ESP32 * VCC --- 3V3 * GND --- GND * SCL --- SCL (GPIO22) //NOTE: GPIO3 Would be more convenient (right next to GND) * SDA --- SDA (GPIO21) //NOTE: GPIO1 would be more convenient (right next to GPO3) */ #endif // CO2 if (scd30.begin(config::auto_calibrate_sensor) == false) { Serial.println("Air sensor not detected. Please check wiring. Freezing..."); while (1) { LedEffects::showWaitingLED(color::red); } } // SCD30 has its own timer. //NOTE: The timer seems to be inaccurate, though, possibly depending on voltage. Should it be offset? Serial.println(); Serial.print(F("Setting SCD30 timestep to ")); Serial.print(config::measurement_timestep); Serial.println(" s."); scd30.setMeasurementInterval(config::measurement_timestep); // [s] Serial.print(F("Setting temperature offset to -")); Serial.print(abs(config::temperature_offset)); Serial.println(" K."); scd30.setTemperatureOffset(abs(config::temperature_offset)); // setTemperatureOffset only accepts positive numbers, but shifts the temperature down. delay(100); Serial.print(F("Temperature offset is : -")); Serial.print(scd30.getTemperatureOffset()); Serial.println(" K"); Serial.print(F("Auto-calibration is ")); Serial.println(config::auto_calibrate_sensor ? "ON." : "OFF."); } //NOTE: should time offset be used to reset measurement_timestep? void checkTimerDeviation() { static int32_t previous_measurement_at = 0; // Used to monitor sensor timer offset. int32_t now = millis(); Serial.print("Measurement time offset : "); Serial.print(now - previous_measurement_at - config::measurement_timestep * 1000); Serial.println(" ms."); previous_measurement_at = now; } bool updateDataIfAvailable() { //TODO: Save count of stable measurements static int16_t previous_co2 = 0; if (scd30.dataAvailable()) { // checkTimerDeviation(); co2 = scd30.getCO2(); temperature = scd30.getTemperature(); humidity = scd30.getHumidity(); return true; } return false; } void waitUntilMeasurementsAreStable() { //TODO: Refactor completely, in order to avoid very long loop? /** From the sensor documentation: * For best results, the sensor has to be run in a stable environment in continuous mode at * a measurement rate of 2s for at least two minutes before applying the FRC command and sending the reference value. */ Serial.println(F("Setting SCD30 timestep to 2s, prior to calibration.")); scd30.setMeasurementInterval(2); // [s] The change will only take effect after next measurement. Serial.println(F("Waiting until the measurements are stable for at least 2 minutes.")); Serial.println(F("It could take a very long time.")); //######################################################################################################## // (c) Mario Lukas // https://github.com/mariolukas/Watterott-CO2-Ampel-Plus-Firmware/blob/main/CO2-Ampel_Plus/Sensor.cpp#L57 uint32_t last_color = color::blue; int stable_measurements = 0, last_co2 = 0; for (stable_measurements = 0; stable_measurements < 60;) { if (scd30.dataAvailable()) { co2 = scd30.getCO2(); //No more than +/-30ppm variation compared to previous measurement. if ((co2 > (last_co2 - 30)) && (co2 < (last_co2 + 30))) { last_color = color::green; stable_measurements++; } else { last_color = color::red; stable_measurements = 0; } last_co2 = co2; } LedEffects::showKITTWheel(last_color, 1); } //######################################################################################################## } void startCalibrationProcess() { waitUntilMeasurementsAreStable(); Serial.print("Starting SCD30 calibration..."); scd30.setAltitudeCompensation(config::altitude_above_sea_level); scd30.setForcedRecalibrationFactor(config::co2_calibration_level); Serial.println(" Done!"); Serial.println("Sensor calibrated."); Serial.println("Sensor will now restart."); LedEffects::showKITTWheel(color::green, 5); //TODO: Add LEDs off and move to util::reset() FS_LIB.end(); ESP.restart(); } }