Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
co2ampel
ampel-firmware
Commits
4d2f0f0c
Commit
4d2f0f0c
authored
Apr 25, 2021
by
Eric Duminil
Browse files
Merge branch 'feature/sensor_states' into develop
parents
bdee5cb3
1435e91a
Changes
1
Hide whitespace changes
Inline
Side-by-side
ampel-firmware/co2_sensor.cpp
View file @
4d2f0f0c
...
...
@@ -15,6 +15,7 @@ namespace config {
const
float
temperature_offset
=
-
3.0
;
// [K] Temperature measured by sensor is usually at least 3K too high.
#endif
bool
auto_calibrate_sensor
=
AUTO_CALIBRATE_SENSOR
;
// [true / false]
const
bool
debug_sensor_states
=
false
;
// If true, log state transitions over serial console
}
namespace
sensor
{
...
...
@@ -24,12 +25,39 @@ namespace sensor {
float
humidity
=
0
;
char
timestamp
[
23
];
int16_t
stable_measurements
=
0
;
uint32_t
waiting_color
=
color
::
blue
;
bool
should_calibrate
=
false
;
/**
* Define sensor states
* INITIAL -> initial state
* 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
* NEEDS_CALIBRATION -> sensor measurements are too low (< 250 ppm)
* PREPARE_CALIBRATION -> forced calibration was initiated, waiting for stable measurements
* CALIBRATION -> the sensor does calibrate itself
*/
enum
state
{
INITIAL
,
BOOTUP
,
READY
,
NEEDS_CALIBRATION
,
PREPARE_CALIBRATION_UNSTABLE
,
PREPARE_CALIBRATION_STABLE
,
CALIBRATION
};
const
char
*
state_names
[]
=
{
"INITIAL"
,
"BOOTUP"
,
"READY"
,
"NEEDS_CALIBRATION"
,
"PREPARE_CALIBRATION_UNSTABLE"
,
"PREPARE_CALIBRATION_STABLE"
,
"CALIBRATION"
};
state
current_state
=
INITIAL
;
void
switchState
(
state
);
void
initialize
()
{
#if defined(ESP8266)
Wire
.
begin
(
12
,
14
);
// ESP8266 - D6, D5;
Wire
.
begin
(
12
,
14
);
// ESP8266 - D6, D5;
#endif
#if defined(ESP32)
Wire
.
begin
(
21
,
22
);
// ESP32
...
...
@@ -49,6 +77,8 @@ namespace sensor {
ESP
.
restart
();
}
switchState
(
BOOTUP
);
// SCD30 has its own timer.
//NOTE: The timer seems to be inaccurate, though, possibly depending on voltage. Should it be offset?
Serial
.
println
();
...
...
@@ -91,19 +121,21 @@ namespace sensor {
previous_measurement_at
=
now
;
}
void
countStableMeasurements
()
{
bool
countStableMeasurements
()
{
// Returns true, if a sufficient number of stable measurements has been observed.
static
int16_t
previous_co2
=
0
;
if
(
co2
>
(
previous_co2
-
config
::
max_deviation_during_calibration
)
&&
co2
<
(
previous_co2
+
config
::
max_deviation_during_calibration
))
{
stable_measurements
++
;
Serial
.
print
(
F
(
"Number of stable measurements : "
));
Serial
.
println
(
stable_measurements
);
waiting_color
=
color
::
green
;
switchState
(
PREPARE_CALIBRATION_STABLE
)
;
}
else
{
stable_measurements
=
0
;
waiting_color
=
color
::
red
;
switchState
(
PREPARE_CALIBRATION_UNSTABLE
)
;
}
previous_co2
=
co2
;
return
(
stable_measurements
==
config
::
enough_stable_measurements
);
}
void
startCalibrationProcess
()
{
...
...
@@ -115,10 +147,11 @@ namespace sensor {
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."
));
s
hould_calibrate
=
true
;
s
witchState
(
PREPARE_CALIBRATION_UNSTABLE
)
;
}
void
calibrateAndRestart
()
{
switchState
(
CALIBRATION
);
Serial
.
print
(
F
(
"Calibrating SCD30 now..."
));
scd30
.
setAltitudeCompensation
(
config
::
altitude_above_sea_level
);
scd30
.
setForcedRecalibrationFactor
(
config
::
co2_calibration_level
);
...
...
@@ -137,12 +170,41 @@ namespace sensor {
Serial
.
println
(
humidity
,
1
);
}
void
displayCO2OnLedRing
()
{
if
(
co2
<
250
)
{
// Sensor should be calibrated.
led_effects
::
showWaitingLED
(
color
::
magenta
);
void
switchState
(
state
new_state
)
{
if
(
new_state
==
current_state
)
{
return
;
}
if
(
config
::
debug_sensor_states
)
{
Serial
.
print
(
F
(
"Changing sensor state: "
));
Serial
.
print
(
state_names
[
current_state
]);
Serial
.
print
(
" -> "
);
Serial
.
println
(
state_names
[
new_state
]);
}
current_state
=
new_state
;
}
void
switchStateForCurrentPPM
()
{
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.
Serial
.
println
(
F
(
"Invalid sensor data - CO2 concentration supposedly 0 ppm"
));
switchState
(
BOOTUP
);
}
else
if
((
current_state
==
PREPARE_CALIBRATION_UNSTABLE
)
||
(
current_state
==
PREPARE_CALIBRATION_STABLE
))
{
// Check for pre-calibration states first, because we do not want to
// leave them before calibration is done.
bool
ready_for_calibration
=
countStableMeasurements
();
if
(
ready_for_calibration
)
{
calibrateAndRestart
();
}
}
else
if
(
co2
<
250
)
{
// Sensor should be calibrated.
switchState
(
NEEDS_CALIBRATION
);
}
else
{
switchState
(
READY
);
}
}
void
displayCO2OnLedRing
()
{
/**
* Display data, even if it's "old" (with breathing).
* A short delay is required in order to let background tasks run on the ESP8266.
...
...
@@ -157,6 +219,30 @@ namespace sensor {
}
}
void
showState
()
{
switch
(
current_state
)
{
case
BOOTUP
:
led_effects
::
showWaitingLED
(
color
::
blue
);
break
;
case
READY
:
displayCO2OnLedRing
();
break
;
case
NEEDS_CALIBRATION
:
led_effects
::
showWaitingLED
(
color
::
magenta
);
break
;
case
PREPARE_CALIBRATION_UNSTABLE
:
led_effects
::
showWaitingLED
(
color
::
red
);
break
;
case
PREPARE_CALIBRATION_STABLE
:
led_effects
::
showWaitingLED
(
color
::
green
);
break
;
case
CALIBRATION
:
// Nothing to do, will restart soon.
break
;
default:
Serial
.
println
(
F
(
"Encountered unknown sensor state"
));
// This should not happen.
}
}
/** Gets fresh data if available, checks calibration status, displays CO2 levels.
* Returns true if fresh data is available, for further processing (e.g. MQTT, CSV or LoRa)
*/
...
...
@@ -169,34 +255,15 @@ namespace sensor {
co2
=
scd30
.
getCO2
();
temperature
=
scd30
.
getTemperature
();
humidity
=
scd30
.
getHumidity
();
}
//NOTE: Data is available, but it's sometimes erroneous: the sensor outputs zero ppm but non-zero temperature and non-zero humidity.
if
(
co2
<=
0
)
{
// No measurement yet. Waiting.
led_effects
::
showWaitingLED
(
color
::
blue
);
return
false
;
}
switchStateForCurrentPPM
();
/**
* Fresh data. Log it and send it if needed.
*/
if
(
freshData
)
{
if
(
should_calibrate
)
{
countStableMeasurements
();
}
// Log every time fresh data is available.
logToSerial
();
}
if
(
should_calibrate
)
{
if
(
stable_measurements
==
config
::
enough_stable_measurements
)
{
calibrateAndRestart
();
}
led_effects
::
showWaitingLED
(
waiting_color
);
return
false
;
}
showState
();
displayCO2OnLedRing
();
return
freshData
;
}
...
...
@@ -207,6 +274,7 @@ namespace sensor {
Serial
.
print
(
F
(
"DEBUG. Setting CO2 to "
));
co2
=
fakeCo2
;
Serial
.
println
(
co2
);
switchStateForCurrentPPM
();
}
void
setAutoCalibration
(
int32_t
autoCalibration
)
{
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment