Commit ef911533 authored by Eric Duminil's avatar Eric Duminil
Browse files

Merge branch 'refactor/fixstartup' into develop

parents 701ac714 79f137e8
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
namespace config { namespace config {
// Values should be defined in config.h // Values should be defined in config.h
uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor) uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor)
const uint16_t measurement_timestep_bootup = 2; // [s] Measurement timestep during acclimatization
const uint16_t altitude_above_sea_level = ALTITUDE_ABOVE_SEA_LEVEL; // [m] const uint16_t altitude_above_sea_level = ALTITUDE_ABOVE_SEA_LEVEL; // [m]
uint16_t co2_calibration_level = ATMOSPHERIC_CO2_CONCENTRATION; // [ppm] uint16_t co2_calibration_level = ATMOSPHERIC_CO2_CONCENTRATION; // [ppm]
int8_t max_deviation_during_calibration = 30; // [ppm] int8_t max_deviation_during_calibration = 30; // [ppm]
int8_t enough_stable_measurements = 60; int8_t enough_stable_measurements = 60;
const uint8_t max_deviation_during_bootup = 20; // [%]
#ifdef TEMPERATURE_OFFSET #ifdef TEMPERATURE_OFFSET
// Residual heat from CO2 sensor seems to be high enough to change the temperature reading. How much should it be 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. // NOTE: Sign isn't relevant. The returned temperature will always be shifted down.
...@@ -28,31 +30,27 @@ namespace sensor { ...@@ -28,31 +30,27 @@ namespace sensor {
/** /**
* Define sensor states * Define sensor states
* INITIAL -> initial state * BOOTUP -> initial state, until first >0 ppm values are returned
* BOOTUP -> state after initializing the sensor, i.e. after scd.begin()
* READY -> sensor does output valid information (> 0 ppm) and no other condition takes place * READY -> sensor does output valid information (> 0 ppm) and no other condition takes place
* NEEDS_CALIBRATION -> sensor measurements are too low (< 250 ppm) * NEEDS_CALIBRATION -> sensor measurements are too low (< 250 ppm)
* PREPARE_CALIBRATION -> forced calibration was initiated, waiting for stable measurements * PREPARE_CALIBRATION_UNSTABLE -> forced calibration was initiated, last measurements were too far apart
* CALIBRATION -> the sensor does calibrate itself * PREPARE_CALIBRATION_STABLE -> forced calibration was initiated, last measurements were close to each others
*/ */
enum state { enum state {
INITIAL,
BOOTUP, BOOTUP,
READY, READY,
NEEDS_CALIBRATION, NEEDS_CALIBRATION,
PREPARE_CALIBRATION_UNSTABLE, PREPARE_CALIBRATION_UNSTABLE,
PREPARE_CALIBRATION_STABLE, PREPARE_CALIBRATION_STABLE
CALIBRATION
}; };
const char *state_names[] = { const char *state_names[] = {
"INITIAL",
"BOOTUP", "BOOTUP",
"READY", "READY",
"NEEDS_CALIBRATION", "NEEDS_CALIBRATION",
"PREPARE_CALIBRATION_UNSTABLE", "PREPARE_CALIBRATION_UNSTABLE",
"PREPARE_CALIBRATION_STABLE", "PREPARE_CALIBRATION_STABLE" };
"CALIBRATION" };
state current_state = INITIAL; state current_state = BOOTUP;
void switchState(state); void switchState(state);
void initialize() { void initialize() {
...@@ -78,28 +76,33 @@ namespace sensor { ...@@ -78,28 +76,33 @@ namespace sensor {
ESP.restart(); ESP.restart();
} }
switchState(BOOTUP); // Changes of the SCD30's measurement timestep do not come into effect
// before the next measurement takes place. That means that after a hard reset
// SCD30 has its own timer. // of the ESP the SCD30 sometimes needs a long time until switching back to 2 s
//NOTE: The timer seems to be inaccurate, though, possibly depending on voltage. Should it be offset? // for acclimatization. Resetting it after startup seems to fix this behaviour.
Serial.print(F("Setting SCD30 timestep to ")); scd30.reset();
Serial.print(config::measurement_timestep);
Serial.println(" s.");
scd30.setMeasurementInterval(config::measurement_timestep); // [s]
Serial.print(F("Setting temperature offset to -")); Serial.print(F("Setting temperature offset to -"));
Serial.print(abs(config::temperature_offset)); Serial.print(abs(config::temperature_offset));
Serial.println(" K."); Serial.println(F(" K."));
scd30.setTemperatureOffset(abs(config::temperature_offset)); // setTemperatureOffset only accepts positive numbers, but shifts the temperature down. scd30.setTemperatureOffset(abs(config::temperature_offset)); // setTemperatureOffset only accepts positive numbers, but shifts the temperature down.
delay(100); delay(100);
Serial.print(F("Temperature offset is : -")); Serial.print(F("Temperature offset is : -"));
Serial.print(scd30.getTemperatureOffset()); Serial.print(scd30.getTemperatureOffset());
Serial.println(" K"); Serial.println(F(" K"));
Serial.print(F("Auto-calibration is ")); Serial.print(F("Auto-calibration is "));
Serial.println(config::auto_calibrate_sensor ? "ON." : "OFF."); Serial.println(config::auto_calibrate_sensor ? "ON." : "OFF.");
// 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_bootup);
Serial.println(F(" s during acclimatization."));
scd30.setMeasurementInterval(config::measurement_timestep_bootup); // [s]
sensor_console::defineIntCommand("co2", setCO2forDebugging, F(" 1500 (Sets co2 level, for debugging purposes)")); 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)")); sensor_console::defineIntCommand("timer", setTimer, F(" 30 (Sets measurement interval, in s)"));
sensor_console::defineCommand("calibrate", startCalibrationProcess, F(" (Starts calibration process)")); sensor_console::defineCommand("calibrate", startCalibrationProcess, F(" (Starts calibration process)"));
...@@ -117,10 +120,20 @@ namespace sensor { ...@@ -117,10 +120,20 @@ namespace sensor {
int32_t now = millis(); int32_t now = millis();
Serial.print(F("Measurement time offset : ")); Serial.print(F("Measurement time offset : "));
Serial.print(now - previous_measurement_at - config::measurement_timestep * 1000); Serial.print(now - previous_measurement_at - config::measurement_timestep * 1000);
Serial.println(" ms."); Serial.println(F(" ms."));
previous_measurement_at = now; 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.
return (co2 > 0 && delta < ((uint32_t) co2 * config::max_deviation_during_bootup / 100));
}
bool countStableMeasurements() { bool countStableMeasurements() {
// Returns true, if a sufficient number of stable measurements has been observed. // Returns true, if a sufficient number of stable measurements has been observed.
static int16_t previous_co2 = 0; static int16_t previous_co2 = 0;
...@@ -151,7 +164,6 @@ namespace sensor { ...@@ -151,7 +164,6 @@ namespace sensor {
} }
void calibrateAndRestart() { void calibrateAndRestart() {
switchState(CALIBRATION);
Serial.print(F("Calibrating SCD30 now...")); Serial.print(F("Calibrating SCD30 now..."));
scd30.setAltitudeCompensation(config::altitude_above_sea_level); scd30.setAltitudeCompensation(config::altitude_above_sea_level);
scd30.setForcedRecalibrationFactor(config::co2_calibration_level); scd30.setForcedRecalibrationFactor(config::co2_calibration_level);
...@@ -177,19 +189,25 @@ namespace sensor { ...@@ -177,19 +189,25 @@ namespace sensor {
if (config::debug_sensor_states) { if (config::debug_sensor_states) {
Serial.print(F("Changing sensor state: ")); Serial.print(F("Changing sensor state: "));
Serial.print(state_names[current_state]); Serial.print(state_names[current_state]);
Serial.print(" -> "); Serial.print(F(" -> "));
Serial.println(state_names[new_state]); Serial.println(state_names[new_state]);
} }
current_state = new_state; current_state = new_state;
} }
void switchStateForCurrentPPM() { void switchStateForCurrentPPM() {
if (co2 == 0) { if (current_state == BOOTUP) {
// NOTE: Data is available, but it's sometimes erroneous: the sensor outputs if (!hasSensorSettled()) {
// zero ppm but non-zero temperature and non-zero humidity. return;
Serial.println(F("Invalid sensor data - CO2 concentration supposedly 0 ppm")); }
switchState(BOOTUP); switchState(READY);
} else if ((current_state == PREPARE_CALIBRATION_UNSTABLE) || (current_state == PREPARE_CALIBRATION_STABLE)) { Serial.println(F("Sensor acclimatization finished."));
Serial.print(F("Setting SCD30 timestep to "));
Serial.print(config::measurement_timestep);
Serial.println(F(" s."));
scd30.setMeasurementInterval(config::measurement_timestep); // [s]
}
if ((current_state == PREPARE_CALIBRATION_UNSTABLE) || (current_state == PREPARE_CALIBRATION_STABLE)) {
// Check for pre-calibration states first, because we do not want to // Check for pre-calibration states first, because we do not want to
// leave them before calibration is done. // leave them before calibration is done.
bool ready_for_calibration = countStableMeasurements(); bool ready_for_calibration = countStableMeasurements();
...@@ -236,8 +254,6 @@ namespace sensor { ...@@ -236,8 +254,6 @@ namespace sensor {
case PREPARE_CALIBRATION_STABLE: case PREPARE_CALIBRATION_STABLE:
led_effects::showWaitingLED(color::green); led_effects::showWaitingLED(color::green);
break; break;
case CALIBRATION: // Nothing to do, will restart soon.
break;
default: default:
Serial.println(F("Encountered unknown sensor state")); // This should not happen. Serial.println(F("Encountered unknown sensor state")); // This should not happen.
} }
...@@ -264,7 +280,9 @@ namespace sensor { ...@@ -264,7 +280,9 @@ namespace sensor {
showState(); showState();
return freshData; // Report data for further processing only if the data is reliable
// (state 'READY') or manual calibration is necessary (state 'NEEDS_CALIBRATION').
return freshData && (current_state == READY || current_state == NEEDS_CALIBRATION);
} }
/***************************************************************** /*****************************************************************
...@@ -288,7 +306,7 @@ namespace sensor { ...@@ -288,7 +306,7 @@ namespace sensor {
if (timestep >= 2 && timestep <= 1800) { if (timestep >= 2 && timestep <= 1800) {
Serial.print(F("Setting Measurement Interval to : ")); Serial.print(F("Setting Measurement Interval to : "));
Serial.print(timestep); Serial.print(timestep);
Serial.println("s."); Serial.println(F("s."));
sensor::scd30.setMeasurementInterval(timestep); sensor::scd30.setMeasurementInterval(timestep);
config::measurement_timestep = timestep; config::measurement_timestep = timestep;
led_effects::showKITTWheel(color::green, 1); led_effects::showKITTWheel(color::green, 1);
...@@ -300,7 +318,7 @@ namespace sensor { ...@@ -300,7 +318,7 @@ namespace sensor {
Serial.print(F("Force calibration, at ")); Serial.print(F("Force calibration, at "));
config::co2_calibration_level = calibrationLevel; config::co2_calibration_level = calibrationLevel;
Serial.print(config::co2_calibration_level); Serial.print(config::co2_calibration_level);
Serial.println(" ppm."); Serial.println(F(" ppm."));
sensor::startCalibrationProcess(); sensor::startCalibrationProcess();
} }
} }
......
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