ESP32の使い方 〜ディープスリープ時間の上限について

先日、Timer Cameraを使って、3時間に1度の間隔で写真を撮影する機会がありました。
単純にESP32のディープスリープ機能を使って実現しようと思ったのですが、その際に、設定したスリープ時間よりも短い時間で撮影されてしまうという現象に遭遇しました。

以下のように、スリープ時間を設定してディープスリープに移行するだけなのですが、設定した時間(10800秒=3時間)よりもかなり早く起動してしまいます。

unsigned long interval = 10800; // unit:sec
esp_sleep_enable_timer_wakeup(interval*1000000);
esp_deep_sleep_start();

考えてみると、これまでディープスリープ時間を1時間より大きく設定したことはなかったようにも思います。
もしかしたらディープスリープ時間に上限があるのでは?と思い、少し調べてみました。

ESP32ではありませんが、ESP8266のディープスリープ時間について、スイッチサイエンスの こちら の記事に書かれていました。
スリープ時間は「マイクロ秒単位」で「32ビット符号なし整数」で指定するため、スリープ時間の最大値は約71.5分になるというものです。

私が実施していた上記の設定でも、「interval」を「unsigned long(32ビット符号なし整数)」で設定しているため、「interval*1000000」が「32ビット符号なし整数」の上限を超えてしまい、おかしな値になってしまっているようです。

ただ、ESP32のドキュメント(こちら)を見ると、「esp_sleep_enable_timer_wakeup」の引数は「64ビット符号なし整数」で設定できるようです。
また、ESP32ディープスリープについてのArduino IDEスケッチ例(「ファイル」>「スケッチ例」>「ESP32」>「DeepSleep」>「TimerWakeUp」)を見ると、「#define uS_TO_S_FACTOR 1000000ULL」と、単位換算のための係数に「ULL(unsigned long long:64ビット符号なし整数」という接尾辞が付いていました。
つまり、スリープ時間をきちんと「64ビット符号なし整数」で設定すれば、より長い時間も設定できるようです。

実際に試してみました。



ディープスリープ時間を10800秒(3時間)に設定、起動するたびにデータをWebサーバに送信し、速やかにディープスリープに移行するというものです。

従来の設定方法でのスケッチはこちらです。

#include <WiFi.h>

unsigned long interval = 10800; // unit:sec

const char*   ssid     = "XXXXXXXX";
const char*   password = "XXXXXXXX";

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

  // ディープスリープ時間の設定
  esp_sleep_enable_timer_wakeup(interval*1000000);

  // Wi-Fi接続
  Serial.printf("Connecting to %s\n", ssid);
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf("\nWiFi connected\n");
  // データ送信
  sendRequest();
  // Wi-Fi切断
  WiFi.disconnect(true);
  Serial.printf("WiFi disconnected\n");

  // スリープに移行
  Serial.println("### DEEP SLEEP START");
  esp_deep_sleep_start();
  delay(1000);
}

void loop() {}

設定方法を変更したスケッチはこちらです。

#include <WiFi.h>

unsigned long interval = 10800; // unit:sec

const char*   ssid     = "XXXXXXXX";
const char*   password = "XXXXXXXX";

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

  // ディープスリープ時間の設定
  esp_sleep_enable_timer_wakeup(interval*1000000ULL);

  // Wi-Fi接続
  Serial.printf("Connecting to %s\n", ssid);
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf("\nWiFi connected\n");
  // データ送信
  sendRequest();
  // Wi-Fi切断
  WiFi.disconnect(true);
  Serial.printf("WiFi disconnected\n");

  // スリープに移行
  Serial.println("### DEEP SLEEP START");
  esp_deep_sleep_start();
  delay(1000);
}

void loop() {}

送信されたデータを確認したところ、従来の設定方法では、およそ2200秒毎にデータが送られていました。
対して、設定方法を変更した方では、およそ10740秒毎にデータが送られていました。所望のとおりの結果です。

「10800*1000000」が「32ビット符号なし整数」ではいくらになるのか、こちらのスケッチで確認してみました。

unsigned long interval = 10800; // unit:sec

void setup() {
  Serial.begin(115200);
  uint32_t val0 = interval*1000000;
  uint64_t val1 = interval*1000000ULL;
  Serial.print("uint32_t : ");
  Serial.println(val0);
  Serial.print("uint64_t : ");
  Serial.println(val1);
}

void loop() {}

結果は以下のとおりで、上記の結果と一致します。設定時間が2210秒と誤って認識されているのが上記の挙動の原因であることがわかりました。

uint32_t : 2210065408
uint64_t : 10800000000

ちなみに、先ほどのArduino IDEスケッチ例の、かなり古いバージョンのものを確認したところ、「#define uS_TO_S_FACTOR 1000000」と、単位換算の係数に接尾辞がついていませんでした。
この当時のスケッチ例を参考にしてスケッチをつくり、現在までそれを使い回してきたため、今回の問題に遭遇してしまったようです。