IoT用マイコンにおけるDeepSleep復帰時間のばらつき
1. はじめに
IoT向けのマイコンで、タイマー復帰のDeepSleepは、狙った時間に復帰しないことがあります。これは、マイコンによって異なるように感じられます。そこで、実験をしてみました。
2. 結論
ESP32シリーズは、復帰のバラツキが1秒程度あります。復帰時間の精度を求める場合は、対策が必要でしょう。
3. 用いた実験系
3.1. 全体概要
対象となるマイコンとラズパイ4を用いました。
図 観測系のイメージ
- GPIOからの500msのパルス出力と30分間のDeepSleepとを繰り返す
- ラズパイはGPIOでパルスを受け、時刻をcsvファイルに記録
- パルスの間隔が30分間隔になっているかを確認
図 マイコンとラズパイ接続した様子(写真のマイコンはラズピコW)
3.2. ESP32用 プログラム (arduino)
- GPIOから500msのパルス出力
- 30分間のDeepSleep
- 復帰後はsetup()から開始
#define PIN_TIC 26
#define SLEEP_US (30*60*1000*1000)
void setup() {
pinMode(PIN_TIC, OUTPUT);
digitalWrite(PIN_TIC, 1);
delay(500);
digitalWrite(PIN_TIC, 0);
delay(1);
esp_deep_sleep(SLEEP_US);
}
void loop() { }
3.3. ラズパイ用 観測プログラム (node-red)
図 node-redのフロー
- GPIOのデバウンス設定は25ms
- パルスの立ち上がりのみ検出
- csvファイルに Date.now()値を記録
- csvファイル名はinjectionノードを実行した時の日時
function1
の内容 (fnames
には予め、日時が入っている)
msg.payload= Date.now()
msg.fnames= global.get("fnames")
return msg;
3.4. データ
ラズパイには次のようなテキストデータが保存されます。
下から上を引いて1000で割る(ミリ秒→秒)。
その値と1800(設定値)とを比較すれば、設定した時間と実際の時間の誤差になります。
(計算自体は、ngraph for winで(dif(y)/1000-1800)と入力し、プロット時に行っています。)
1717907502028
1717909302644
1717911104008
1717912903811
1717914704018
4. 結果
4.1. 結果
ESP32 , ESP32-c3 , seeed xiao , Arduino MKR Wi-Fi 1010 (mkr と記載), Sony Spresense , Raspberry pi Pico W (rpicowと記載) に対して、それぞれ24時間以上実施した結果です。
今回は平均値よりもバラツキ(std)に着目しています。
rpicowは、microPythonでコーディングしたものを使っています。(初めに試したのがrpicowだったのですが、時間の都合上、cでは試してません)
デバイス | std[s] | mean[s] | 備考 |
---|---|---|---|
ESP32 | 0.89 | -8.16 | |
ESP32-c3 | 0.77 | 0.104 | |
xiao | 0.0009 | 0.697 | |
mkr | 0.0005 | 0.97 | |
spresense | 0.0033 | 1.92 | |
rpicow | 0.0008 | 1.75 | uPython |
図 ESP32でのSleep間隔誤差の結果
図 ESP32C3でのDeepsleepの結果
図 Seeed XiaoでのSleepの結果
図 Arduino MKR Wifi-1010でのSleepの結果
図 SpresenseでのSleepの結果
図 Raspberry PI Pico WでのSleepの結果
4.2. ESP32
- 概要
- それまでのWi-Fiマイコンの価格を100分の1以上にした価格破壊の元祖
- cpuコアはxtensa Lx6
- 結果
- DeepSleepの平均は8秒程度早い。
- バラツキが上下で1秒以上
4.3. ESP32 c3
- 概要
- CPUコアがRISC-VになったESP32
- 結果
- ESP32無印にあった大きな遅れは無い
- バラツキが上下で1秒以上
4.4. Seeed Xiao
- 概要
- SAM D21 マイコン(無線部なし)
- 超小型 Arduino互換
- 結果
- バラツキは小さい
- 平均0.7秒程度の遅れ
4.5. Arduino MKR Wifi-1010
- 概要
- Arduino正規品のWi-Fi搭載版
- SAM D21 (Cortex-M0+) 搭載
- 無線部:u-blox製NINA-W10 (中身はESP32)
- 結果
- バラツキは小さい
- 平均1秒程度の遅れ
4.6. Sony Spresense
- 概要
- Cortex-M4F (6コア)
- Arduino-IDEでも開発可能
- 無線部は無いが、有線LANボードなどオプションが結構ある
- 本体I/Oは1.8Vだが、拡張ボードI/Oはジャンパで5Vか3.3Vを選択可能
- 給電は本体USBか本体の電源供給部分にコネクタをハンダ付けして行う
- 結果
- バラツキは小さい
- 平均2秒程度の遅れ(起動自体に2秒程度かかる)
4.7. Raspberry PI Pico W
- 概要
- 無線部搭載ラズピコ
- Arm Cortex M0+
- 無線部: infineon製 CYW43439
- 結果
- バラツキは小さい
- 平均1.7秒程度の遅れ
- (コードはmicroPython)
5. まとめ
- ESP32シリーズのタイマーは30分に1秒近く乱れる
- xtensa版だけでなくRISC-V版も乱れる
- ESP32で時刻管理する場合は、NTP や BLEのCurrent Time Serviceの利用が無難
- ARM衆は優秀
- (ラズピコwは安価なのに無線部付きで優秀)
A. 付録: 利用したコード
ESP32c3 (m5stamp c3)用
#define SLEEP_US (30*60*1000*1000)
#define PIN_TIC 10
#define PIN_BOARD_BUTTON 3
void setup() {
pinMode(PIN_TIC,OUTPUT);
pinMode(PIN_BOARD_BUTTON,INPUT);
digitalWrite(PIN_TIC,1);
delay(450);
delay(50);
digitalWrite(PIN_TIC,0);
delay(1);
if ( digitalRead(PIN_BOARD_BUTTON)==0 ){
esp_deep_sleep(SLEEP_US);
}
Serial.begin(115200);
}
void loop() {
Serial.println("please reset");
delay(1000);
}
arduino MKR wi-fi 1010用
#include "ArduinoLowPower.h"
#define PIN_LED 6
#define PIN_TIC 14
#define SLEEP_MS (30*60*1000)
void setup() {
pinMode(PIN_LED,OUTPUT);
pinMode(PIN_TIC,OUTPUT);
digitalWrite(PIN_LED,LOW);
digitalWrite(PIN_TIC,HIGH);
delay(450);
digitalWrite(PIN_LED,HIGH);
delay(50);
digitalWrite(PIN_LED,LOW);
digitalWrite(PIN_TIC,LOW);
delay(1);
LowPower.deepsleep(SLEEP_MS);
}
void loop() {}
Raspberry pi pico w用(upython)
import machine
import time
SLEEP_MS = 30*60*1000
PIN_TIC = 15
bdled = machine.Pin('LED', machine.Pin.OUT)
pintic = machine.Pin(PIN_TIC, machine.Pin.OUT)
pintic.on()
bdled.on()
time.sleep(0.5)
pintic.off()
bdled.off()
time.sleep(0.5)
machine.deepsleep(SLEEP_MS)
seeed xiao用
#include "ArduinoLowPower.h"
#define PIN_LED 13
#define PIN_TIC 10
#define SLEEP_MS (30*60*1000)
void setup() {
pinMode(PIN_LED,OUTPUT);
pinMode(PIN_TIC,OUTPUT);
digitalWrite(PIN_LED,LOW);
digitalWrite(PIN_TIC,HIGH);
delay(450);
digitalWrite(PIN_LED,HIGH);
delay(50);
digitalWrite(PIN_LED,LOW);
digitalWrite(PIN_TIC,LOW);
delay(1);
LowPower.deepsleep(SLEEP_MS);
}
void loop() {}
spresense用
15番ピンをGNDに接続しておく
#include <LowPower.h>
#define PIN_TIC 13
#define PIN_SKIPDS 15
#define TIME_SLEEP_SEC (30*60)
void setup() {
LowPower.begin();
pinMode(PIN_TIC,OUTPUT);
pinMode(PIN_SKIPDS,INPUT);
pinMode(LED0, OUTPUT);
digitalWrite(PIN_TIC,HIGH);
delay(450);
digitalWrite(LED0,HIGH);
delay(50);
digitalWrite(LED0,LOW);
digitalWrite(PIN_TIC,LOW);
if (digitalRead(PIN_SKIPDS) == 0){
LowPower.deepSleep(TIME_SLEEP_SEC);
}
}
void loop() { delay(1000); }
Discussion