RingBuf is not stored in RTC memory with RTC_DATA_ATTR
Closed this issue · 5 comments
I am making a data logger with an E-ink screen to display statistics, for that I need a running window over past readings, and ring buffer suits well for that purpose. However, I couldn't make it retain the buffered data after the deep sleep.
I wrote this minimal example to reproduce the issue:
Library versions I use: espressif32@^6.5.0
, locoduino/RingBuffer@^1.0.4
#include <Arduino.h>
#include <RingBuf.h>
RTC_DATA_ATTR uint32_t wakeupCounter = 0;
RTC_DATA_ATTR RingBuf<float, 10> testRtcBuf;
void setup() {
Serial.begin(115200);
Serial.print("Wakeup!!!!!! #");
Serial.println(++wakeupCounter);
Serial.print("testRtcBuf-before: [");
for (uint8_t i = 0; i < testRtcBuf.size(); ++i) { Serial.print(testRtcBuf[i]); Serial.print(", "); } Serial.print("] (");
Serial.print(testRtcBuf.size()); Serial.print(" / "); Serial.print(testRtcBuf.maxSize()); Serial.println(")");
testRtcBuf.pushOverwrite(1.0);
testRtcBuf.pushOverwrite(2.0);
Serial.print("testRtcBuf-after: [");
for (uint8_t i = 0; i < testRtcBuf.size(); ++i) { Serial.print(testRtcBuf[i]); Serial.print(", "); } Serial.print("] (");
Serial.print(testRtcBuf.size()); Serial.print(" / "); Serial.print(testRtcBuf.maxSize()); Serial.println(")");
Serial.println("Going to bed..");
Serial.flush();
esp_deep_sleep(5000000);
}
void loop() {
}
From the serial output, I can see that the wakeup timer gets incremented every wakeup, but the ring buffer for some reason starts over empty:
...
ets Jun 8 2016 00:22:57
rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13232
load:0x40080400,len:3028
entry 0x400805e4
Wakeup!!!!!! #3
testRtcBuf-before: [] (0 / 10)
testRtcBuf-after: [1.00, 2.00, ] (2 / 10)
Going to bed..
ets Jun 8 2016 00:22:57
rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13232
load:0x40080400,len:3028
entry 0x400805e4
Wakeup!!!!!! #4
testRtcBuf-before: [] (0 / 10)
testRtcBuf-after: [1.00, 2.00, ] (2 / 10)
Going to bed..
...
Could be I just do not understand or know something.
Hello,
I read from https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html
In Deep-sleep mode, the CPUs, most of the RAM, and all digital peripherals that are clocked from APB_CLK are powered off. The only parts of the chip that remain powered on are:
RTC controller
ULP coprocessor
RTC FAST memory
RTC SLOW memory
So since memory is powered off, all variables are lost and the CPU does a reset.
Best regards
BTW, the rst:0x5 (DEEPSLEEP_RESET)
from the console should have been your first clue 🙂
Oups, sorry. Did not see you put it in RTC memory which is not powered off. However, since the CPU does a reset constructors of objects are called and the buffer size is set to 0. To correct this, you would have to change the constructor of RingBuffer and test the reset condition.
Thank you for the quick response @Koryphon. How do people manage this in the C++ community? I mean, if I patch my local copy of the library source then other people won't benefit from it, and it would be difficult to update the library myself in the future. And creating a PR that includes some ESP32-specific code is not something desirable, I think.
Hello @vstepchik
Maybe the best way is to add a constructor with a function pointer as argument so that the read index and the size are not initialized if the fonction returns true.
/* Constructor with conditional helper. Does not init mReadIndex and mSize
* if helper returns true */
RingBuf(bool (*initHelper)(void));
and
template <typename ET, size_t S, typename IT, typename BT>
RingBuf<ET, S, IT, BT>::RingBuf(bool (*initHelper)(void)) {
if (!initHelper()) {
mReadIndex = 0;
mSize = 0;
}
}
In a sketch, the ring buffer would be instantiated with a lambda function checking the reset condition:
/* 5 is the deep sleep reset id */
RingBuf<uint8_t, 25> aBuffer( [] () { return rtc_get_reset_reason(0) == 5; } );
That way, no ESP32 within the library and it can be adapted easily to other target. Here is the modified RingBuf.h (Unfortunately I have no ESP32 to test)
RingBuf.h.zip
@Koryphon this did the trick!
I passed in a lambda []() -> bool { return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_UNDEFINED; }
and now it does not reset the internal counters. Thank you very much!
Are you planning to put it into a new release?
@Koryphon this did the trick! I passed in a lambda
[]() -> bool { return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_UNDEFINED; }
and now it does not reset the internal counters.
Fine
Thank you very much! Are you planning to put it into a new release?
Yes. Could you add a working example in a PR ?