Commit 14767d76 authored by Käppler's avatar Käppler
Browse files

co2_sensor: wait for acclimatization after startup

The SCD30 sensor has a response time of 20 s to reach
67 % of its final reading. The first measurement
can thus be unreliable, if the environment changed
shortly prior to startup. Set measurement interval to the
shortest possible value (2 s) and wait then, until the
measurements have stabilized. Show a blue led effect in
the meanwhile.
parent 6ad8554c
......@@ -3,10 +3,12 @@
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 measurement_timestep_accl = 2; // [s] Measurement timestep during acclimatization
const uint16_t altitude_above_sea_level = ALTITUDE_ABOVE_SEA_LEVEL; // [m]
uint16_t co2_calibration_level = ATMOSPHERIC_CO2_CONCENTRATION; // [ppm]
int8_t max_deviation_during_calibration = 30; // [ppm]
int8_t enough_stable_measurements = 60;
const uint8_t max_deviation_during_accl = 20; // [%]
#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.
......@@ -97,9 +99,9 @@ namespace sensor {
//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(config::measurement_timestep_accl);
Serial.println(" s during acclimatization.");
scd30.setMeasurementInterval(config::measurement_timestep_accl); // [s]
sensor_console::defineIntCommand("co2", setCO2forDebugging, F(" 1500 (Sets co2 level, for debugging purposes)"));
sensor_console::defineIntCommand("timer", setTimer, F(" 30 (Sets measurement interval, in s)"));
......@@ -122,6 +124,18 @@ namespace sensor {
previous_measurement_at = now;
}
bool hasSensorSettled() {
static uint16_t last_co2 = 0;
uint16_t delta;
delta = abs(co2 - last_co2);
last_co2 = co2;
// We assume the sensor has acclimated to the environment if measurements
// change less than a specified percentage of the current value.
if (co2 > 0 && (100 * delta / config::max_deviation_during_accl) < co2) {
return true;
} else return false;
}
bool countStableMeasurements() {
// Returns true, if a sufficient number of stable measurements has been observed.
static int16_t previous_co2 = 0;
......@@ -185,6 +199,16 @@ namespace sensor {
}
void switchStateForCurrentPPM() {
if (current_state == BOOTUP) {
if (!hasSensorSettled()) return;
switchState(READY);
Serial.println(F("Sensor acclimatization finished."));
Serial.print(F("Setting SCD30 timestep to "));
Serial.print(config::measurement_timestep);
Serial.println(" s.");
scd30.setMeasurementInterval(config::measurement_timestep); // [s]
switchStateForCurrentPPM(); // Check all other conditions again
}
if (co2 == 0) {
// NOTE: Data is available, but it's sometimes erroneous: the sensor outputs
// zero ppm but non-zero temperature and non-zero humidity.
......
    • Feel free to write complete names in variables, even if they become long. _boot or _startup could possibly be used instead of _accl.

    • +-20% seems like a lot. Before calibration, +-30ppm is required, which is close to 5%.

    • if (boolean) return true else false; can simply be replaced by return boolean;

    • (100 * delta / config::max_deviation_during_accl) < co2 seems correct, but I'd expect delta < co2 * max_deviation / 100 to be more readable. Is it to avoid overflow?

    • The recursive call switchStateForCurrentPPM(); could be removed, couldn't it? current_state cannot be BOOTUP anymore, so letting the code run after the first if would be like calling switchStateForCurrentPPM(); again.

  • Feel free to write complete names in variables, even if they become long. _boot or _startup could possibly be used instead of _accl.

    You're right. I will fix this.

    +-20% seems like a lot. Before calibration, +-30ppm is required, which is close to 5%.

    While I understand the necessity for calibration, having perfectly stable measurements was not the goal here. Otherwise it may be that in a constantly changing environment the sensor will never start working. But feel free to propose another value. 5%, however, is too low IMHO.

    if (boolean) return true else false; can simply be replaced by return boolean;

    Sure, thanks.

    (100 * delta / config::max_deviation_during_accl) < co2 seems correct, but I'd expect delta < co2 * max_deviation / 100 to be more readable. Is it to avoid overflow?

    I don't really know why I wrote it that complicated. Your suggestion is clearly superior, but overflow could occur in both variants, uh oh... So we have to cast co2 to uint32_t, right?

    The recursive call switchStateForCurrentPPM(); could be removed, couldn't it? current_state cannot be BOOTUP anymore, so letting the code run after the

    Yes, that was a non-intentional leftover... Will remove it.

  • max_deviation could be defined as uint32_t, and delta could be compared to max_deviation * co2 / 100. That should do, right?

    20% is probably fine for startup, okay. I'll check if it's possible to avoid duplicate code between countStableMeasurements hasSensorSettled. Possibly by passing the delta as argument.

Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment