Arrays and RTC_DATA_ATTR with DeepSleep (ESP32C6)
Posted: Wed Aug 27, 2025 3:56 pm
I'm making a low-power CO2 monitor, with an eink display, using Deep Sleep to keep the battery consumption minimal with solar charging (using a DFRobot Firebeetle 2 ESP32-C6).
It's working fine for wakeup each 60 seconds, and with retaining the value of bootCount declared with RTC_DATA_ATTR.
I want to use two arrays, also with RTC_DATA_ATTR, to populate with previous readings from which ten-minute and one-hour averages can be calculated. But these are not being retained, on wakeup they are being filled with zeroes. (Or, if I remove the value from the declaration, they contain random data, as would be expected from a newly-declared variable.)
Is the issue that arrays cannot be placed in RTC memory in this way - and if so, is there another way to retain this data through deep sleep? Or is it another problem entirely?
It's working fine for wakeup each 60 seconds, and with retaining the value of bootCount declared with RTC_DATA_ATTR.
I want to use two arrays, also with RTC_DATA_ATTR, to populate with previous readings from which ten-minute and one-hour averages can be calculated. But these are not being retained, on wakeup they are being filled with zeroes. (Or, if I remove the value from the declaration, they contain random data, as would be expected from a newly-declared variable.)
Is the issue that arrays cannot be placed in RTC memory in this way - and if so, is there another way to retain this data through deep sleep? Or is it another problem entirely?
Code: Select all
#include <GxEPD2_BW.h>
#include <DFRobot_SCD4X.h>
#include <Fonts/FreeSansBold24pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#define BATT_V_PIN 0
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 60 /* Time ESP32 will go to sleep (in seconds) */
#define TEN_MINUTE_READINGS 10 // Adjust these to match TIME_TO_SLEEP
#define ONE_HOUR_READINGS 60 // " " " "
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(14, 8, 16, 17)); // 400x300, SSD1683
DFRobot_SCD4X SCD4X(&Wire, SCD4X_I2C_ADDR);
DFRobot_SCD4X::sSensorMeasurement_t data;
char displayText[12];
struct readings {
uint16_t CO2;
float temp;
float humid;
int tempInt;
int humidInt;
float battery;
};
readings current;
RTC_DATA_ATTR int CO2_ten_minutes[TEN_MINUTE_READINGS] = {0};
RTC_DATA_ATTR int CO2_one_hour[ONE_HOUR_READINGS] = {0};
int CO2_ten_minute_average = 0;
int CO2_one_hour_average = 0;
RTC_DATA_ATTR int bootCount = 0;
int i;
void setup() {
Serial.begin(115200);
pinMode(BATT_V_PIN, INPUT);
while( !SCD4X.begin() ){
Serial.println("Communication with device failed, please check connection");
delay(3000);
}
Serial.println("SCD4X initialised OK");
bootCount++;
Serial.println("Boot count: " + String(bootCount));
SCD4X.enablePeriodMeasure(SCD4X_STOP_PERIODIC_MEASURE);
display.init(115200,true,50,false);
Serial.println("Display init OK");
display.setTextColor(GxEPD_BLACK);
}
void loop() {
display.fillScreen(GxEPD_WHITE);
SCD4X.measureSingleShot(SCD4X_MEASURE_SINGLE_SHOT);
Serial.print("Waiting for readings to be available...");
while (!SCD4X.getDataReadyStatus())
{
Serial.print(F("."));
delay(500);
}
Serial.println("taking readings now.");
// Discard the first set of data, because the chip datasheet indicates the first reading obtained after waking up is invalid
SCD4X.measureSingleShot(SCD4X_MEASURE_SINGLE_SHOT);
while(!SCD4X.getDataReadyStatus()) {
delay(100);
}
SCD4X.readMeasurement(&data);
current.CO2 = data.CO2ppm;
current.temp = data.temp;
current.humid = data.humidity;
current.tempInt = rint(data.temp);
current.humidInt = rint(data.humidity);
Serial.print("Carbon dioxide concentration : ");
Serial.print(current.CO2);
Serial.println(" ppm");
for (i = 0; i++; i < (TEN_MINUTE_READINGS - 1) ) {
CO2_ten_minutes[i] = CO2_ten_minutes[i+1];
CO2_ten_minute_average += CO2_ten_minutes[i];
}
CO2_ten_minutes[i] = current.CO2;
CO2_ten_minute_average += CO2_ten_minutes[i];
CO2_ten_minute_average /= TEN_MINUTE_READINGS;
if ( bootCount >= TEN_MINUTE_READINGS ) {
Serial.print("Ten minutes CO2 average: ");
Serial.println(CO2_ten_minute_average);
}
else {
Serial.println("Awaiting ten-minute average");
}
for (i = 0; i++; i < (ONE_HOUR_READINGS - 1) ) {
CO2_one_hour[i] = CO2_one_hour[i+1];
CO2_one_hour_average += CO2_one_hour[i];
}
CO2_one_hour[i] = current.CO2;
CO2_one_hour_average += CO2_one_hour[i];
CO2_one_hour_average /= ONE_HOUR_READINGS;
if ( bootCount >= ONE_HOUR_READINGS ) {
Serial.print("One hour CO2 average: ");
Serial.println(CO2_one_hour_average);
}
else {
Serial.println("Awaiting one-hour average");
}
Serial.print("Environment temperature : ");
Serial.print(current.temp);
Serial.println(" C");
Serial.print("Relative humidity : ");
Serial.print(current.humid);
Serial.println("%");
display.setFont(&FreeSansBold24pt7b);
display.setTextSize(2);
sprintf(displayText, "%d", current.CO2);
drawCentreString(displayText, 200, 100);
display.setTextSize(1);
sprintf(displayText, "%dC", current.tempInt);
drawCentreString(displayText, 100, 200);
sprintf(displayText, "%d%%", current.humidInt);
drawCentreString(displayText, 300, 200);
display.setFont(&FreeSansBold12pt7b);
display.setTextSize(1);
current.battery = (analogReadMilliVolts(BATT_V_PIN)*0.002);
Serial.print("Battery reading via ADC on pin 0: ");
Serial.print(current.battery, 2);
Serial.println("V");
sprintf(displayText, "Battery: %.2fV", current.battery);
drawCentreString(displayText, 200, 260);
display.nextPage();
Serial.println();
display.hibernate();
SCD4X.setSleepMode(SCD4X_POWER_DOWN);
esp_sleep_enable_timer_wakeup( ( TIME_TO_SLEEP - millis()/1000 ) * uS_TO_S_FACTOR);
Serial.println("Setup ESP32 to sleep for " + String(TIME_TO_SLEEP) +
" Seconds");
Serial.println("Going to sleep now");
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void drawCentreString(const char *buf, int x, int y)
{
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(buf, x, y, &x1, &y1, &w, &h);
display.setCursor(x - w / 2, y);
display.print(buf);
}