Skip to content

Commit cd8576b

Browse files
Merge pull request #32 from davidkreidler/lowEnergyMode
v5.7 Low Energy Mode (50 days runtime): - Change the refresh rate in Battery mode to every 5 min via the Menu -> Display -> Update (5min). This will exend Battery life to about 50 days. Plotly colors and font size: - Use dark green for the CO2 title and Graph. Also fill the area below. - Use red for Temperature and blue for Humidity. - The GitHub version link now opens a new tab.
2 parents 0bd8940 + d3f885a commit cd8576b

File tree

3 files changed

+97
-36
lines changed

3 files changed

+97
-36
lines changed

OpenCO2_Sensor.ino

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
- WiFiManager: https://github.com/tzapu/WiFiManager
1111
- ArduinoMqttClient (if MQTT is defined)
1212
*/
13-
#define VERSION "v5.6"
13+
#define VERSION "v5.7"
1414

1515
#define HEIGHT_ABOVE_SEA_LEVEL 50 // Berlin
1616
#define TZ_DATA "CET-1CEST,M3.5.0,M10.5.0/3" // Europe/Berlin time zone from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
1717
#define LIGHT_SLEEP_TIME 500
18-
#define DEEP_SLEEP_TIME 29124
18+
#define DEEP_SLEEP_TIME 29124 // 30 sec
19+
#define LOW_ENERGY_DEEP_SLEEP_TIME 287924 // 5 min
1920
#define DEEP_SLEEP_TIME_NO_DISPLAY_UPDATE DEEP_SLEEP_TIME + 965 // offset for no display update
2021
static unsigned long lastMeasurementTimeMs = 0;
2122

@@ -86,7 +87,7 @@ SensirionI2cScd4x scd4x;
8687
USBMSC usbmsc;
8788

8889
RTC_DATA_ATTR bool USB_ACTIVE = false, initDone = false, BatteryMode = false, comingFromDeepSleep = false;
89-
RTC_DATA_ATTR bool LEDonBattery, LEDonUSB, useSmoothLEDcolor, invertDisplay, useFahrenheit, useWiFi, english;
90+
RTC_DATA_ATTR bool LEDonBattery, LEDonUSB, useSmoothLEDcolor, invertDisplay, useFahrenheit, useWiFi, lowEnergyMode, english;
9091
RTC_DATA_ATTR uint8_t ledbrightness, HWSubRev, font;
9192
RTC_DATA_ATTR float maxBatteryVoltage;
9293

@@ -196,7 +197,7 @@ void HandleRootClient() {
196197
message += "<div id='CO2Plot' style='width:100%;max-width:1400px'></div>\n";
197198
message += "<div id='TempHumPlot' style='width:100%;max-width:1400px'></div>\n";
198199

199-
message += "<a href='https://github.com/davidkreidler/OpenCO2_Sensor/releases'>Installed Version: ";
200+
message += "<a target='_blank' rel='noopener noreferrer' href='https://github.com/davidkreidler/OpenCO2_Sensor/releases'>Installed Version: ";
200201
message += VERSION;
201202
message += "</a>\n";
202203
message += "<script>\n";
@@ -236,8 +237,8 @@ void HandleRootClient() {
236237
server.sendContent(Buffer);
237238

238239
message = "];\n";
239-
message += "const data = [{x:times, y:yValues, mode:'lines'}];\n";
240-
message += "const layout = {yaxis: { title: 'CO2 (ppm)'}, title: 'History', plot_bgcolor:'black', paper_bgcolor:'black'};\n";
240+
message += "const data = [{x:times, y:yValues, mode:'lines', fill: 'tonexty', fillcolor: 'rgba(0, 100, 00, 0.3)', line: {color: '#006400'}}];\n";
241+
message += "const layout = {yaxis: {range: [Math.min(...data[0].y), Math.max(...data[0].y) * 1.05], title: {text: 'CO2 (ppm)', font: { color: '#006400',size: 20}}}, title: {text: 'History', font: { color: '#808080', size: 40}}, plot_bgcolor:'black', paper_bgcolor:'black'};\n";
241242
message += "Plotly.newPlot('CO2Plot', data, layout);\n";
242243
server.sendContent(message);
243244

@@ -268,10 +269,9 @@ void HandleRootClient() {
268269

269270
message = "];\nconst numPoints2 = " + String(index) + ";\n";
270271
message += "let times2 = generateValues(startTime, endTime, numPoints2).map(time => new Date(time));\n";
271-
message += "const data2 = [{x: times2, y: y1Values, name: 'Temperature', mode:'lines'}, ";
272-
message += "{x: times2, y: y2Values, name: 'Humidity', yaxis: 'y2', mode:'lines'}];\n";
273-
message += "const layout2 = { showlegend: false, yaxis: {title: 'Temperature (" + String(useFahrenheit? "*F" : "*C") ;
274-
message += ")'}, yaxis2: { title: 'Humidity (%)', overlaying: 'y', side: 'right'}, plot_bgcolor:'black', paper_bgcolor:'black'};\n";
272+
message += "const data2 = [{x: times2, y: y1Values, name: 'Temperature', line: { color: '#FF4500' }, mode:'lines'}, {x: times2, y: y2Values, name: 'Humidity', line: { color: '#1E90FF' }, yaxis: 'y2', mode:'lines'}];\n";
273+
message += "const layout2 = { showlegend: false, yaxis: {title: {text: 'Temperature (*" + String(useFahrenheit? "F" : "C");
274+
message += ")', font: { color: '#FF4500',size: 20}}}, yaxis2: {title: {text: 'Humidity (%)', font: { color: '#1E90FF',size: 20}}, overlaying: 'y', side: 'right'}, plot_bgcolor:'black', paper_bgcolor:'black'};\n";
275275
message += "Plotly.newPlot('TempHumPlot', data2, layout2);\n";
276276

277277
message += "</script>\n</body>\n</html>\n";
@@ -322,8 +322,13 @@ void loadCredentials() {
322322
#endif /* MQTT */
323323

324324
float getTempOffset() {
325-
if (useWiFi) return 12.2;
326-
else return 4.4;
325+
if (!BatteryMode) {
326+
if (useWiFi) return 12.2;
327+
return 4.4;
328+
} else {
329+
if (lowEnergyMode) return 0.0;
330+
return 0.8;
331+
}
327332
}
328333

329334
void initOnce() {
@@ -358,6 +363,7 @@ void initOnce() {
358363
preferences.begin("co2-sensor", true);
359364
maxBatteryVoltage = preferences.getFloat("MBV", 3.95);
360365
useWiFi = preferences.getBool("WiFi", false);
366+
lowEnergyMode = preferences.getBool("lowEnergy", false);
361367
LEDonBattery = preferences.getBool("LEDonBattery", false);
362368
LEDonUSB = preferences.getBool("LEDonUSB", true);
363369
ledbrightness = preferences.getInt("ledbrightness", 5);
@@ -375,7 +381,7 @@ void initOnce() {
375381
scd4x.setSensorAltitude(HEIGHT_ABOVE_SEA_LEVEL);
376382
scd4x.setAutomaticSelfCalibrationEnabled(1); // Or use setAutomaticSelfCalibrationTarget if needed
377383
scd4x.setTemperatureOffset(getTempOffset());
378-
scd4x.startPeriodicMeasurement();
384+
if (!(BatteryMode && lowEnergyMode)) scd4x.startPeriodicMeasurement();
379385

380386
displayInit();
381387
delay(3000); // Wait for co2 measurement
@@ -820,29 +826,56 @@ void loop() {
820826
goto_light_sleep(5000 - (millis() - lastMeasurementTimeMs));
821827
}
822828

823-
bool isDataReady = false;
824-
uint16_t ready_error = scd4x.getDataReadyStatus(isDataReady);
825-
if (ready_error || !isDataReady) {
826-
if (BatteryMode && comingFromDeepSleep) goto_deep_sleep(DEEP_SLEEP_TIME/2);
827-
else goto_light_sleep(LIGHT_SLEEP_TIME/2);
828-
return; // otherwise continues running!
829+
if (!(BatteryMode && lowEnergyMode)) {
830+
bool isDataReady = false;
831+
uint16_t ready_error = scd4x.getDataReadyStatus(isDataReady);
832+
if (ready_error || !isDataReady) {
833+
if (BatteryMode && comingFromDeepSleep) goto_deep_sleep(DEEP_SLEEP_TIME/2);
834+
else goto_light_sleep(LIGHT_SLEEP_TIME/2);
835+
return; // otherwise continues running!
836+
}
829837
}
830838

831839
// Read co2 measurement
832-
uint16_t new_co2 = 400;
840+
uint16_t new_co2 = 420;
833841
float new_temperature = 0.0f;
834-
uint16_t error = scd4x.readMeasurement(new_co2, new_temperature, humidity);
842+
uint16_t error;
843+
if (BatteryMode && lowEnergyMode) {
844+
scd4x.stopPeriodicMeasurement();
845+
scd4x.wakeUp();
846+
scd4x.setTemperatureOffset(getTempOffset());
847+
//delay(10);
848+
scd4x.measureSingleShot(); // Ignore first measurement after wake up.
849+
error = scd4x.measureAndReadSingleShot(new_co2, new_temperature, humidity);
850+
scd4x.powerDown();
851+
} else {
852+
error = scd4x.readMeasurement(new_co2, new_temperature, humidity);
853+
}
835854
lastMeasurementTimeMs = millis();
836855
if (error) {
837-
char errorMessage[256];
838-
errorToString(error, errorMessage, 256);
839-
displayWriteError(errorMessage);
856+
if(TEST_MODE) {
857+
char errorMessage[256];
858+
errorToString(error, errorMessage, 256);
859+
displayWriteError(errorMessage);
860+
} else { // retry
861+
goto_light_sleep(LIGHT_SLEEP_TIME/2);
862+
return;
863+
}
840864
} else {
841865
extern uint16_t refreshes;
842-
if (BatteryMode || (refreshes % 6 == 1)) saveMeasurement(new_co2, new_temperature, humidity);
843-
/* don't update in Battery mode, unless CO2 has changed by 3% or temperature by 0.5°C */
866+
if (BatteryMode || (refreshes % 6 == 1)) {
867+
saveMeasurement(new_co2, new_temperature, humidity);
868+
869+
if (BatteryMode && lowEnergyMode) { // fill measurements of past 5 min
870+
for (int i=0; i<9; i++) {
871+
saveMeasurement(new_co2, new_temperature, humidity);
872+
}
873+
}
874+
}
875+
876+
/* don't update in Battery mode, unless CO2 has changed by 4% or temperature by 0.5°C */
844877
if (!TEST_MODE && BatteryMode && comingFromDeepSleep) {
845-
if ((abs(new_co2 - co2) < (0.03 * co2)) && (fabs(new_temperature - temperature) < 0.5)) {
878+
if ((abs(new_co2 - co2) < (0.04 * co2)) && (fabs(new_temperature - temperature) < 0.5)) {
846879
goto_deep_sleep(DEEP_SLEEP_TIME_NO_DISPLAY_UPDATE);
847880
}
848881
}
@@ -893,10 +926,11 @@ void loop() {
893926
if (BatteryMode) {
894927
if (!comingFromDeepSleep) {
895928
scd4x.stopPeriodicMeasurement();
896-
scd4x.setTemperatureOffset(0.8);
897-
scd4x.startLowPowerPeriodicMeasurement();
929+
scd4x.setTemperatureOffset(getTempOffset());
930+
if (!lowEnergyMode) scd4x.startLowPowerPeriodicMeasurement();
898931
}
899-
goto_deep_sleep(DEEP_SLEEP_TIME);
932+
if (lowEnergyMode) goto_deep_sleep(LOW_ENERGY_DEEP_SLEEP_TIME);
933+
else goto_deep_sleep(DEEP_SLEEP_TIME);
900934
}
901935

902936
goto_light_sleep(LIGHT_SLEEP_TIME);

epd_abstraction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum LEDMenuOptions {
2222
NUM_LED_OPTIONS
2323
};
2424
enum DisplayMenuOptions {
25+
UPDATE,
2526
INVERT,
2627
TEMP_UNIT,
2728
LANGUAGE,
@@ -55,6 +56,7 @@ const char* EnglishLEDmenuItems[NUM_LED_OPTIONS] = {
5556
"Exit"
5657
};
5758
const char* EnglishOptionsMenuItems[NUM_DISPLAY_OPTIONS] = {
59+
"Update",
5860
"Invert",
5961
"Unit",
6062
"English",
@@ -86,6 +88,7 @@ const char* GermanLEDmenuItems[NUM_LED_OPTIONS] = {
8688
"Beenden"
8789
};
8890
const char* GermanOptionsMenuItems[NUM_DISPLAY_OPTIONS] = {
91+
"Update",
8992
"Invert",
9093
"Einheit",
9194
"German",

epd_abstraction.ino

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,18 @@ void OptionsMenu() {
210210
}
211211
if (mspressed > 1000) { // long press
212212
switch (selectedOption) {
213+
case UPDATE:
214+
lowEnergyMode = !lowEnergyMode;
215+
preferences.begin("co2-sensor", false);
216+
preferences.putBool("lowEnergy", lowEnergyMode);
217+
preferences.end();
218+
219+
if (BatteryMode) {
220+
scd4x.stopPeriodicMeasurement();
221+
scd4x.setTemperatureOffset(getTempOffset());
222+
if (!lowEnergyMode) scd4x.startPeriodicMeasurement();
223+
}
224+
break;
213225
case INVERT:
214226
invertDisplay = !invertDisplay;
215227
preferences.begin("co2-sensor", false);
@@ -790,9 +802,11 @@ void displayOptionsMenu(uint8_t selectedOption) {
790802
else OptionsMenuItem = GermanOptionsMenuItems[i];
791803
Paint_DrawString_EN(5, 25*(i+1), OptionsMenuItem, &Font24, WHITE, BLACK);
792804
}
793-
Paint_DrawString_EN(166, 50, (useFahrenheit? "*F":"*C"), &Font24, WHITE, BLACK);
794-
Paint_DrawNum(149, 100, (int32_t)(font+1), &Font24, BLACK, WHITE);
795-
Paint_DrawString_EN(166, 100, "/2", &Font24, WHITE, BLACK);
805+
if (lowEnergyMode) Paint_DrawString_EN(200-17*4, 25, "5min", &Font24, WHITE, BLACK);
806+
else Paint_DrawString_EN(200-17*5, 25, "30sec", &Font24, WHITE, BLACK);
807+
Paint_DrawString_EN(166, 25*3, (useFahrenheit? "*F":"*C"), &Font24, WHITE, BLACK);
808+
Paint_DrawNum(149, 25*5, (int32_t)(font+1), &Font24, BLACK, WHITE);
809+
Paint_DrawString_EN(166, 25*5, "/2", &Font24, WHITE, BLACK);
796810

797811
invertSelected(selectedOption);
798812
updateDisplay();
@@ -1225,12 +1239,13 @@ void displayinfo() {
12251239
// only needed for debugging. This interrupts the SCD40
12261240
/*float tOffset;
12271241
scd4x.stopPeriodicMeasurement();
1242+
delay(10);
12281243
scd4x.getTemperatureOffset(tOffset);
1229-
if (BatteryMode) scd4x.startLowPowerPeriodicMeasurement();
1230-
else scd4x.startPeriodicMeasurement();
1244+
if (!BatteryMode) scd4x.startPeriodicMeasurement();
1245+
else if (!lowEnergyMode) scd4x.startLowPowerPeriodicMeasurement();
12311246
Paint_DrawString_EN(1, 145, "T_offset:", &Font16, WHITE, BLACK);
12321247
char offset[20];
1233-
sprintf(offset, "%.1f", tOffset);
1248+
snprintf(offset, sizeof(offset), "%.2f", tOffset);
12341249
Paint_DrawString_EN(122, 145, offset, &Font16, WHITE, BLACK);*/
12351250

12361251
updateDisplay();
@@ -1311,6 +1326,15 @@ void displayBattery(uint8_t percentage) {
13111326
BlackImage[y+x*25] = ~BlackImage[y+x*25];
13121327
}
13131328
}
1329+
1330+
/* low Energy Mode */
1331+
if (lowEnergyMode) {
1332+
Paint_DrawRectangle(97, 13, 115, 47, BLACK, DOT_PIXEL_2X2, DRAW_FILL_EMPTY); //case
1333+
Paint_DrawLine(103, 10, 109, 10, BLACK, DOT_PIXEL_3X3, LINE_STYLE_SOLID);//nippel
1334+
Paint_DrawLine(106, 25, 106, 35, BLACK, DOT_PIXEL_2X2, LINE_STYLE_SOLID);//+
1335+
Paint_DrawLine(102, 30, 110, 30, BLACK, DOT_PIXEL_2X2, LINE_STYLE_SOLID);//+
1336+
//Xstart, Ystart, Xend, Yend
1337+
}
13141338
#endif /* EINK_1IN54V2 */
13151339

13161340
#ifdef EINK_4IN2

0 commit comments

Comments
 (0)