Arduino

ESP32を電池で長く動かすDeep Sleep設定

更新: 中村 拓也
Arduino

ESP32を電池で長く動かすDeep Sleep設定

ESP32は、Wi-Fi送信で80〜250mAを消費する一方、Deep Sleepでは10μA前後まで落とせる、電池駆動の伸びしろが極端に大きいマイコンです。2016年に登場して以降、屋外センサーや小型IoTでよく使われてきましたが、寿命を決めるのは瞬間の電流ではなく平均電流であり、

ESP32は、Wi-Fi送信で80〜250mAを消費する一方、Deep Sleepでは10μA前後まで落とせる、電池駆動の伸びしろが極端に大きいマイコンです。
2016年に登場して以降、屋外センサーや小型IoTでよく使われてきましたが、寿命を決めるのは瞬間の電流ではなく平均電流であり、「起きている時間をいかに短くするか」が設計の本質になります。
筆者はワークショップでESP32の作例を扱うたび、サンプル通りに書いた受講者が「電池がすぐ切れる」とつまずく場面に何度も立ち会ってきましたが、原因の大半はUSB接続のまま測っていたことや、開発ボードのLED・変換ICが数mA食っていたことでした。
この記事では、タイマーで起きて処理して眠る基本コード、平均電流から駆動日数を逆算する考え方、そして「計算より持たない」実機の落とし穴を、コード・測定・設計をつないで整理します。

ゴール:電池1本で数か月動かす全体像

ESP32を電池1本で数か月動かすには、まず「起きている時間をどれだけ短くするか」を設計の中心に置く必要があります。
常時起動の数十mAと、Deep Sleepの10μA前後では桁が違いすぎて、同じ電池でも寿命は別物になるからです。
狙うべきゴールは、瞬間の最小電流を下げることではなく、平均電流を徹底して下げることにあります。

なぜ常時起動だと電池がすぐ切れるのか

ESP32は、Wi-Fiを使わない待機でも数十mAを食い続けます。
Wi-Fi送信中なら80〜250mAに達し、一般的な乾電池では数日どころか十数時間で尽きることもあります。
電池で動かすとすぐ切れる、という印象の正体はここにあります。
開発ボードで最初に試したとき、Deep Sleepを入れたのに数日で止まり、後からUSB-シリアル変換ICや電源LEDが常時消費していたと分かった失敗がありました。
チップ本体を眠らせても、周辺回路が起きたままでは意味が薄いのです。

Deep Sleepで到達できる消費電流の目安

Deep Sleepでは、チップはほぼ停止し、消費電流を10μA前後まで落とせます。
構成によっては10〜150μAのレンジに入りますが、アクティブ時のmA級と比べれば約1万倍の差です。
ワークショップで屋外温湿度ロガーを作る回では、まず電流計でこの差を見せると、受講者は一気に納得します。
数値だけでは伝わりにくい「眠らせる意味」が、針の振れ方で体感になるからです。
ほとんどの時間を眠らせられれば、月単位から年単位の駆動が現実になります。

寿命を決めるのは『瞬間の最小電流』ではなく『平均電流』

寿命を左右するのは、最小電流ではなく平均電流です。
短く起きて長く眠るほど平均は下がり、同じ電池容量でも持続時間は伸びます。
実務では、平均電流を「アクティブ電流×起床時間+スリープ電流×睡眠時間」で見積もり、そこから駆動時間を逆算します。
対象になるのは、屋外の温湿度ロガー、ポスト投函通知、ドア開閉センサー、リモコンのように、たまに動いてほとんど眠っていてよい機器です。
逆に、常時通信や常時計測が必要な用途では、この考え方だけでは足りません。
Deep Sleepは万能ではないが、長時間駆動の核心はここにある、という線引きを先に持っておくと設計がぶれません。

ESP32の4つのスリープモードと使い分け

ESP32には Modem Sleep、Light Sleep、Deep Sleep、Hibernation という段階があり、眠りが深くなるほど消費電流は下がる代わりに、復帰の速さや扱える周辺回路に制約が増えます。
バッテリー寿命を最優先にするなら、主役は Deep Sleep です。
実際、受講者の作品でも Light Sleep と Deep Sleep を取り違えて「眠らせたのに思ったほど電池が持たない」となる例がよくあり、桁の違いを押さえるだけで設計の考え方が変わります。

Light Sleep:速く復帰したいとき

Light Sleep は概ね 0.8mA 前後まで下がり、CPU の状態やメモリを保ったまま素早く復帰できます。
ボタンを押した瞬間の反応を残したいリモコン作例では、この速さが効きます。
筆者もボタン応答が要る構成では Light Sleep を選び、押してから動き出す待ち時間の短さに助けられました。
ただし、Deep Sleep の μA 級と比べると消費電流は 2 桁ほど大きく、短い休止を何度も挟む用途では電池持ちの差としてはっきり現れます。

Deep Sleep:間欠動作で寿命を最大化したいとき

Deep Sleep は CPU とほとんどの周辺回路を落とすため、ESP32 で最も省電力です。
1 分おきに計測するロガーでは、復帰の速さよりも「どれだけ長く眠らせるか」が効くので、こちらが向いています。
復帰時はリセット相当で動き、プログラムは setup() から再実行されます。
loop() の途中から続くわけではないので、起床のたびに初期化し直す前提で組む必要があります。
筆者の経験でも、この違いを腹落ちさせるとコード設計の迷いが一気に減りました。

復帰後にsetup()から走る前提でコードを組む

Deep Sleep をまたいで値を残したいなら、RTC メモリを使います。
RTC_DATA_ATTR を付けた変数だけが RTC メモリ(約8KB)に置かれ、眠っている間も保持されます。
起動回数カウンタ、前回の測定値、状態フラグの保存に向いており、毎回ゼロから作り直すより実装が素直になります。
復帰要因も esp_sleep_get_wakeup_cause() で分岐できるので、タイマーで起きたのか、別の条件で起きたのかを見分けながら処理を分けていきましょう。

判断軸は、復帰の速さが要るか、どれだけ眠っていられるか、電池容量はどれだけか、の3点です。
応答性重視なら Light Sleep、間欠動作で電池寿命を伸ばしたいなら Deep Sleep が基本になります。
迷ったら、この2つの差から考えるのがおすすめです。

Deep Sleepの基本コード:タイマーで定期復帰

Deep Sleepの基本は、眠る時間をタイマーで指定し、処理を終えたら esp_deep_sleep_start() で休ませる流れです。
Arduino IDEでもこの3手順を押さえれば、ESP32を一定間隔で起こして計測や送信を回す間欠動作が組めます。
実装の芯はシンプルですが、単位の取り違えと起動時の状態保持を外すと、動いているのに挙動が読めないスケッチになります。

スリープ時間の指定とμs単位の落とし穴

esp_sleep_enable_timer_wakeup() の引数はマイクロ秒単位です。
60秒眠らせたいなら 60 * 1000000 = 60000000 を渡します。
ここを秒の感覚で 60 と書くと、ほとんど待たずに起きてしまうので、ワークショップでも毎回数人が踏みます。
筆者は板書で最初に ×1000000 を強調して、計算式ごと先回りで潰すようにしています。

流れは、起床後に処理を済ませ、次の起床時刻を設定してから esp_deep_sleep_start() を呼ぶだけです。
Arduino IDEでもこの順番を守れば、タイマー復帰の骨格がそのまま成立します。
処理の前に眠ってしまうと本末転倒なので、起きるたびに必要な仕事を終えてから眠る構成にしましょう。

RTCメモリで起動回数や状態を引き継ぐ

Deep Sleepから戻ると setup() から走り直すため、普通の変数は初期化され直します。
そこで使うのが RTC_DATA_ATTR です。
RTC_DATA_ATTR int bootCount = 0; のように宣言しておけば、スリープをまたいで値を保持でき、起動回数を数えたり前回の状態を残したりできます。
起きるたびに bootCount++ して表示すれば、何回目の復帰かがすぐ分かります。

筆者も昔、起動回数カウンタをRTCメモリに置いたつもりで RTC_DATA_ATTR を付け忘れ、毎回0に戻る原因探しに時間を使いました。
動いてはいるのに履歴だけ消えるので、かなり見落としやすいのです。
こういう失敗は、スリープ制御の学習でいちばん気持ちを削る部分でしょう。
だからこそ、状態を残す変数は先に RTC_DATA_ATTR を付けておくのがおすすめです。

復帰要因を判定して処理を分岐する

esp_sleep_get_wakeup_cause() を使うと、なぜ起きたかを判定できます。
タイマーで起きたのか、外部割り込みで起きたのかを分けられるので、定期計測と異常時通知を1本のスケッチにまとめやすくなります。
たとえば普段は温湿度を送信し、外部イベントで起きたときだけ追加処理を走らせる、という作り方ができます。

この分岐が入ると、Deep Sleepは単なる省電力モードではなく、状態機械として扱えるようになります。
setup() の中で復帰要因を見て処理を変え、最後に眠るところまで閉じておけば、loop() はほとんど出番がありません。
loop() に処理を書くと眠るタイミングがずれやすいので、setup() 内で「起きる・処理する・眠る」を完結させる構成を基本にしましょう。

外部割り込みやセンサーで起こす

ボタンや人感センサー、リードスイッチで「イベントが起きたときだけ」ESP32を起こしたいなら、Deep Sleep の外部割り込み復帰を使います。
タイマー復帰と組み合わせれば、一定間隔の定期計測と異常時の即通知を両立できるので、待機中の消費電力を抑えながら動きだけは取りこぼしません。
ここでの選び分けは ext0、ext1、タッチウェイクアップ、ULP の4つをどう組み合わせるかに尽きます。
配線と復帰条件を先に決めておくと、あとから「起きない」を減らせます。

ext0:1ピンで起こす

ext0 は単一の RTC GPIO を条件にして起こす方式で、設定の考え方が素直です。
PIR センサーやリードスイッチのように、1本の入力が変化すれば十分な用途なら扱いやすく、回路もシンプルにまとまります。
筆者がポスト投函通知を作ったときも、最初はリードスイッチで ext0 復帰にしておくと組みやすく、配線ミスの切り分けもしやすかったです。

ただし ext0 は手軽なぶん、消費電流は概ね 50〜100μA とやや大きめです。
寝かせる時間が長い装置では、この差が積み上がって電池寿命に効いてきます。
Deep Sleep 復帰に使える GPIO は RTC 対応ピンに限られ、無印 ESP32 では 0,2,4,12〜15,25〜27,32〜39 などに絞られます。
任意のピンでは起こせないので、ボードに部品を載せる前にピン表を見て、センサーの足をどこへ逃がすかを決めておきましょう。

ext1:複数ピンで起こす・省電力

ext1 は複数ピンの条件で復帰できる方式で、ext0 より省電力です。
消費電流は概ね 10μA 前後に収まり、複数のボタンやセンサーをまとめて監視したいときに向いています。
筆者はポスト投函通知を作ったあと、もう1つボタンも足したくなって ext1 に組み替えましたが、そのときに RTC 対応ピンの制約へきれいに当たりました。
便利そうな空きピンが RTC 非対応だったので、配線を引き直して対応ピンへ寄せたのです。

この経験で痛感したのは、外部復帰は「回路を組めば何とかなる」種類の機能ではないことです。
ワークショップでも、受講者が非 RTC ピンで設定して「起きない」と詰まる場面が定番なので、配線前にピン表を確認させる手順を徹底しています。
複数のボタンを同時に扱う、あるいはできるだけ電流を削りたいなら ext1 を選び、どのピンが監視対象になるかを先に固定してしまうと設計が安定します。

ℹ️ Note

タッチウェイクアップも、指で触れるだけで起こせる便利な手段です。物理ボタンを増やしたくないときや、軽い接触を入力にしたいときは候補になりますが、結局のところ RTC 系の入力設計と並べて考えるのが近道です。

ULPコプロセッサで主CPUを眠らせたまま監視する

ULP コプロセッサは、主CPUを眠らせたまま GPIO や ADC を概ね 150μA 程度で監視し、閾値を超えたときだけ主CPUを起こせます。
常時見張りたいが平均電流は抑えたい、という上級用途で強い選択肢です。
たとえば電圧の低下やセンサー値の変化を小刻みに見て、異常時だけ起床させる構成なら、タイマー復帰よりも細かな条件分岐を持たせやすくなります。

複数要因の組み合わせも考えやすいところです。
ext1 で外部入力を待ちつつ、ULP でアナログ量を監視し、さらにタイマーで周期確認を入れると、入力の取りこぼしと長時間停滞の両方に備えられます。
イベント駆動を主軸にしながら、必要な場面だけ主CPUを起こす。
この考え方にすると、ESP32 の Deep Sleep はずっと実用的になります。

開発ボードで消費電流が下がらない原因と対策

開発ボードで「計算より全然電池が持たない」とき、原因はスケッチではなく基板側にあることが多いです。
Deep Sleepに入っても、USB-シリアル変換IC、レギュレータ、電源LEDが数mAを流し続ければ、チップ単体の10μA前後という理屈は簡単に崩れます。
まずUSBを外し、電池給電で実測するところから切り分けると、見えていなかった消費源が一気に浮かび上がります。

USB・変換IC・LEDという『隠れた数mA』

ワークショップで「USBを挿したまま測って10mAだ、おかしい」と相談される場面は定番です。
ここでまず確認するのは、USBをつないだ状態では真のDeep Sleep電流を見ていない、という事実です。
USBからは電源だけでなくデータ線経由でも電流が回り込み、ボード上のUSB-シリアル変換ICやレギュレータ、保護回路が生きたままになるため、マイコン本体の消費が埋もれてしまいます。
筆者も最初にDeep Sleepを入れたのに数日で電池が切れ、電流計を直列に入れて調べ直した結果、犯人が開発ボードの変換ICとLEDの常時数mAだと突き止めました。
bareモジュール構成に替えた瞬間、寿命が目に見えて伸びたのを覚えています。

GPIOの浮き・内部抵抗無効によるリークを止める

次に疑うべきはGPIOの浮きです。
RTC周辺を落とすモードでは内部プルアップ/プルダウンが無効になり、入力ピンがふわっと浮いたままになります。
するとピンの電位が定まらず、わずかなリーク電流が積み重なって、Deep Sleepのはずなのに消費が下がりません。
対処ははっきりしていて、外部抵抗で電位を固定するか、esp_sleep_pd_configでRTC周辺を保持して内部抵抗を生かすことです。
ここを潰さずにソフトだけ直しても、電池は思ったほど持たないままです。

低消費電流を狙うなら低リークボードやbareモジュールへ

本気で低消費電流を狙うなら、ボード選びで勝負が半分決まります。
電源LEDが常時点灯するだけで数mA消えることがあり、LEDを外して初めて数字が落ちるケースも珍しくありません。
さらにUSB-シリアル変換ICや高静止電流のレギュレータが載った開発ボードは、便利な反面、Deep Sleepの評価には不利です。
最終的には、LEDや変換ICを積まない低リーク設計のボードか、最初からbareモジュールを使う構成が現実的です。
切り分けの順序は、USBを外して電池給電で実測し、次にLEDと変換ICのアイドル消費を疑い、最後にGPIOの浮きを潰す流れで進めましょう。

電池容量から駆動日数を見積もる

電池容量から駆動日数を見積もるときは、まず平均電流を式で出し、そこから必要な電池容量と周期を逆算します。
ここで基準になるのは、電池の「大きさ」よりも、1周期あたりにどれだけ電流を食うかです。
設計の起点をここに置くと、作例の寿命が感覚ではなく数式で読めるようになります。

平均電流を計算する

平均電流=(アクティブ電流×アクティブ時間+スリープ電流×スリープ時間)÷周期、という式で考えると、寿命設計の骨格が見えてきます。
屋外ロガーを設計するときも、先に計測頻度を決めてこの式に当てはめ、数か月駆動に届くかを確認してから配線に入る流れが実用的でした。
ワークショップでも「何日持つの?」と聞かれたら、まずこの式を一緒に書き、計算の主役を平均電流に置きます。

アクティブ時間(特にWi-Fi接続)を削るのが最効率

平均電流を下げるうえで、最も効くのはアクティブ時間を短くすることです。
特にWi-Fi接続は数秒かかることがあり、その間は80〜250mAを消費しますから、接続を速くする、送信回数を減らす、起きている時間を1秒でも削るだけで結果が変わります。
たとえば30秒周期で1秒だけ起きてWi-Fi送信する作例なら、平均電流はおおよそ数mA規模に収まり、駆動は十数日〜のオーダーになります。
ここで効くのは待機の工夫ではなく、起きている時間そのものを減らす設計です。

乾電池・18650・コイン電池での寿命の目安

駆動時間は、電池容量(mAh)÷平均電流(mA)=駆動時間(h)で概算できます。
アルカリ単3は概ね2000mAh前後、18650リチウムは2000〜3500mAh、CR2032のコイン電池は200mAh前後なので、必要駆動日数から電池を選び、逆に電池からスリープ周期を決める考え方が取りやすくなります。
もっとも、実運用では自己放電やレギュレータの変換損失、低温での容量低下が入るため、理論値の7〜8割程度に割り引いて見積もるのが安全です。
余裕を見た設計にしておくと、現場で本当に持つ寿命につながります。

この記事をシェア

中村 拓也

大手メーカーで組込みシステムの開発に15年従事。Arduino・Raspberry Piを活用した自作IoTデバイスの制作実績多数。電子工作の基礎から応用まで、実務経験に基づいた解説を得意とする。

関連記事

電子工作入門

M5Stackは、約5cm四方のケースにESP32マイコン、カラー液晶、3ボタン、スピーカー、Wi-Fi/Bluetoothまで詰め込んだ小型開発デバイスです。電子工作で最初に立ちはだかるはんだ付けとブレッドボード配線を飛ばせるので、ArduinoやRaspberry Piのように部品を自分でつなぐ前提から、

Arduino

『Arduino』のスターターキット選びで最初に迷うのは、純正か互換か、そしてUNO R3かUNO R4かという2点です。この記事では、そこを先に整理したうえで、価格、搭載ボード、学べる量、日本語対応を横並びで比べ、5製品の現行性と純正・互換の違い、最初に作れる作例まで一気に見渡せる形でまとめます。

Arduino

Arduinoで無線LANにつながる工作を始めたいなら、最短で結果が出る入口はESP32です。この記事では、Arduino IDEにESP32 by Espressif Systemsを追加し、自宅Wi‑Fiへ接続してIPアドレスを確認するところまでを、

Arduino

『Arduino』でモーターを動かすときは、まず「どのモーターを選ぶか」より先に、本体から直接つながない理由を押さえるのが近道です。サーボ、DC、ステッピングは制御したいものがそれぞれ違い、必要になる配線も信号も、サーボ信号、PWM+方向、STEP/DIRときれいに分かれます。