co2_sensor.cpp 4.91 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#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.
49
50
51
52
    Serial.println();
    Serial.print(F("Setting SCD30 timestep to "));
    Serial.print(config::measurement_timestep);
    Serial.println(" s.");
53
54
    scd30.setMeasurementInterval(config::measurement_timestep);  // [s]

55
    Serial.print(F("Setting temperature offset to -"));
56
57
58
    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.
59
    delay(100);
60

61
    Serial.print(F("Temperature offset is : -"));
62
63
64
    Serial.print(scd30.getTemperatureOffset());
    Serial.println(" K");

65
    Serial.print(F("Auto-calibration is "));
66
67
68
    Serial.println(config::auto_calibrate_sensor ? "ON." : "OFF.");
  }

Eric Duminil's avatar
Eric Duminil committed
69
70
71
72
73
74
75
76
77
78
79
80
  bool updateDataIfAvailable() {
    //TODO: Save count of stable measurements
    //TODO: Compare time to previous measurements, check that it's not too far away from config::measurement_interval
    if (scd30.dataAvailable()) {
      co2 = scd30.getCO2();
      temperature = scd30.getTemperature();
      humidity = scd30.getHumidity();
      return true;
    }
    return false;
  }

81
82
  void waitUntilMeasurementsAreStable() {
    //TODO: Refactor completely, in order to avoid very long loop?
83
84
85
86
    /** 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.
     */
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    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();
117
118
119
120
121
122
123
    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);
Eric Duminil's avatar
TODOs    
Eric Duminil committed
124
    //TODO: Add LEDs off and move to util::reset()
125
126
127
128
    FS_LIB.end();
    ESP.restart();
  }
}