ESP32 Deep Sleep 関係のメモ

#ESP32 Deep Sleep系のメモ

##Deep Sleep中の値の保持
検索で出てくる日本のページとかで紹介されている system_rtc_mem_read / system_rtc_mem_write を使う方法は、 #include user_interface.h がエラーになる。

いずれにしろその方法だと、メモリが有効かどうか（電源ON時かDeepSleep後か）をCRCなどで検出して自分で初期化する必要がある。

それよりは、RTC_DATA_ATTRを使う方が簡単。

```save_data_deepsleep.cc
RTC_DATA_ATTR int bootCounter = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);
  bootCounter ++;
  Serial.printf("Boot counter(%08x) = %d\n", &bootCounter,bootCounter);
}

void loop() {
    esp_sleep_enable_timer_wakeup(5 * 1000 * 1000);  // wakeup every 5secs
    esp_deep_sleep_start();  
}
```
この方法なら、ほとんど普通の変数と同様に扱える。

https://github.com/espressif/esp-idf/blob/master/docs/en/api-reference/system/sleep_modes.rst
に拠れば、RTC_DATA_ATTRはSlow Memoryに配置され、RTC_DATA_ATTRが使わる場合は自動的にSLOWメモリは保持される設定になるよう。以下のコードを試してみる。

```rtc_memory_test.cc
RTC_DATA_ATTR int bootCounter    = 111;
RTC_FAST_ATTR int fastMemoryData = 222;
RTC_SLOW_ATTR int slowMemoryData = 333;

void setup() {
  Serial.begin(115200);
  delay(1000);

  bootCounter ++;
  slowMemoryData ++;
  fastMemoryData ++;

  Serial.printf("Boot counter(%08x) = %d\n", &bootCounter,bootCounter);
  Serial.printf("slowMemoryData(%08x) = %d\n", &slowMemoryData,slowMemoryData);
  Serial.printf("fastMemoryData(%08x) = %d\n", &fastMemoryData,fastMemoryData);
}

void loop() {
    esp_sleep_enable_timer_wakeup(5 * 1000 * 1000);  // wakeup every 5secs
    esp_deep_sleep_start();  
}
```

```rtc_memory_test.cc
Boot counter(50000210) = 112
slowMemoryData(50000214) = 334
fastMemoryData(3ff8006c) = 0
```

アドレスを見ると、確かに、Slowメモリに保持されている。

ところで、Slowメモリに保持されているデータであるが、その初期化はFastメモリ上で管理されているらしく、FastメモリをDeep Sleep中に非保持にすると、正常に動作しなくなる。

```rtc_memory_test.cc
RTC_SLOW_ATTR int bootCounter    = 111;
RTC_FAST_ATTR int fastMemoryData = 222;
RTC_SLOW_ATTR int slowMemoryData = 333;

void setup() {
  Serial.begin(115200);
  delay(1000);

  bootCounter ++;
  slowMemoryData ++;
  fastMemoryData ++;

  Serial.printf("Boot counter(%08x) = %d\n", &bootCounter,bootCounter);
  Serial.printf("slowMemoryData(%08x) = %d\n", &slowMemoryData,slowMemoryData);
  Serial.printf("fastMemoryData(%08x) = %d\n", &fastMemoryData,fastMemoryData);

  // Fastメモリを保持しないようにしてみる
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
}

void loop() {
    esp_sleep_enable_timer_wakeup(5 * 1000 * 1000);  // wakeup every 5secs
    esp_deep_sleep_start();  
}
```

```
Boot counter(50000204) = 112
slowMemoryData(50000200) = 334
fastMemoryData(3ff8006c) = 0

Boot counter(50000204) = 112
slowMemoryData(50000200) = 334
fastMemoryData(3ff8006c) = 0

(毎回同じデータが出力される)
```

##Deep Sleepからの外部入力トリガ復帰

例えば、スイッチなどを監視する場合の、外部のスイッチの変化での復帰処理。
以下のコードは、GPIO2が変化するか、60秒ごとにブートカウンターが+1される。

```rtc_memory_test.cc
RTC_SLOW_ATTR int bootCounter    = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);

  bootCounter ++;
  Serial.printf("Boot counter(%08x) = %d\n", &bootCounter,bootCounter);

  // 以下3つのconfigはデフォルト値なので省略しても良い
  // Deep Sleep中にPull Up を保持するために
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
  // Deep Sleep中にメモリを保持するために
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON);

  pinMode(GPIO_NUM_2, INPUT_PULLUP);
}

void loop() {
  int nowData = digitalRead(GPIO_NUM_2);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_2, (nowData==0) ? 1 : 0 );
  esp_sleep_enable_timer_wakeup(60 * 1000 * 1000);  // wakeup every 1min
  esp_deep_sleep_start();  
}
```

ESP32にはエッジ割り込み機能がないので、現在の値の逆を復帰条件として毎回設定する必要がある。
スイッチはON時にGNDに直結させるタイプを使用。なので、PULLUPしている。
内部のPULLUPは、esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF)してしまうと、Deep Sleep中にPull Upもなくなってしまうので、動作しなくなるので注意。
外部でPullupする方法もあるが、その場合外付けの抵抗が電力を消費するので、他に盛大にLEDをつけていてその制御を保持したくないなどの条件でなければ、素直に内部のPullupに電力を供給したほうが良いと判断。(検証していない）

シビアなタイミングで復帰条件設定が逆になる可能性も否定できないことと、WatchDog、あるいは生存確認のためにも、時々タイマーで復帰させるのが現実的と思われる。あまり作例では見なかったが、タイマーでの復帰と、GPIO監視の復帰は両立できる。
どちらの条件（電源ONをいれると３つの条件）で復帰したかは、esp_sleep_get_wakeup_cause()の戻り値で判定可能。