概要
ESP-WROOM-02を使った温度測定の仕組みを作ったり直したりしているが、ESP8266 Core for Arduino (2.4.1版)を使っているときに遭遇した小ネタを少しばかり。
ネタとしては、
- ESPClass::restart() でリスタートしない問題
- ディープスリープ中のRSTピン操作での復帰検出
- ENピンをリセットに使う話
- WROOM-02Dをはんだ付けした話
などです。
今回は動作確認のために以下のような回路を使った。
上の回路図のように、ESP-WROOM-02を実装済の秋月電子のピッチ変換基板(AE-ESP-WROOM-02)を中心に、必要な抵抗、コンデンサ、LEDおよびタクトスイッチをブレッドボードに載せ、電源は外部から+3.3Vを与えた。
今まで載せた回路と比べると、ENピンにスイッチ(EN-SW)を追加しているのが変更点で、このスイッチについては後述する。
ESP-WROOM-02 Datasheet (Version2.6) のFigure 5-2. ESP-WROOM-02 Peripheral Schematics、 および、ESP8266 System Description (Version 2.2)の1.4.2. Power-on Sequence and Power Reset に従って、ENと+3.3V間には0.1μFのセラミックコンデンサをはさんでRC遅延回路を構成し、電源オンからEN = Hとなるまでに若干の時間を稼いでいる。
また、GPIO0とGND間にSW1、RSTとGND間にリセット用のRST-SWを配置しており、SW1を押したままRST-SWを押し先にRST-SWを離してからSW1を離すことでUARTダウンロードモードとなる。ブートしてしまえばSW1は好きなように使えるので、今回は押下検出用のスイッチとして使う。
ESP.restart() 問題
フラッシュメモリにスケッチをダウンロードした直後、ESP.restart() を実行しても正しく動作しなかったので試してみた。検索してみると、比較的有名な問題点のようだった。
テスト用に以下のスケッチを使った。setup() でLED1を点灯し、loop() では200msecごとにSW1の検出を行う。digitalRead(SW1); は押下されているときに0を返すので、そのとき ESP.restart(); を実施する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <ESP.h> extern "C" { #include "user_interface.h" } #define LED 15 #define SW1 0 void setup() { pinMode(LED, OUTPUT); digitalWrite(LED, 1); pinMode(SW1, INPUT); Serial.begin(74880); rst_info *prst = ESP.getResetInfoPtr(); Serial.printf("reset reason = %d, %s\n", prst->reason, ESP.getResetReason().c_str()); } void loop() { delay(200); if (digitalRead(SW1) == 0) { ESP.restart(); // ESP.deepSleep(2 * 1000 * 1000); } Serial.print("."); } |
(最初に掲載したスケッチでは、ESP.deepSleep(); の引数が2000秒間になっていたので修正した)。
なお、ブート時のメッセージが残るようシリアルスピードは74880bpsとしている。
上記のスケッチをビルドしてWROOM-02にダウンロードすると、シリアルモニタには以下のように表示された。
1 2 |
(文字化け)reset reason = 6, External System(文字化け) ................. |
ピリオドが連続して表示されるから、loop() は回っているだろう。ここに表示されている reset reason = 6 はスケッチによって出力している。
ここで、SW1を押下して ESP.restart(); させてみると一連の文字化けが表示されしばらくするとLEDが消灯した。シリアルモニタにはリスタート時のメッセージも表示されずピリオドも表示されない。何か異常な状態に陥ったままフリーズしたように見える。
だが、一度RST-SWを押してやり、その後SW1を押すことを繰り返すと以下のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
ets Jan 8 2013,rst cause:2, boot mode:(3,6) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 6, External System ...... (文字化け)reset reason = 4, Software/System restart ................. (文字化け)reset reason = 4, Software/System restart ...... (文字化け)reset reason = 4, Software/System restart .... |
SW1を押下するたびごとに ESP.restart() による再起動を繰り返すし、リスタート要因も 4 を返してくれる。これは意図通り。
ESP.restart() を使う場合は、ビルドしたスケッチのダウンロード後にリセットするなり電源を入れ直す必要があるようだ。ふつうに使っているならばスケッチの投入後にリセットか電源オンを行うはずなので問題なさそうではあるが、スケッチの修正とダウンロードを繰り返し、なおかつ ESP.restart(); を含んだコードを走らせているとこういうのに遭遇したりする。フラッシュダウンロード直後にこのような挙動をする理由は分からないのだが、リセット必須と覚えておくことにする。
なお、ESP.getResetInfoPtr(); で得られるリスタート要因コードについては、ドキュメント ( ESP8266 Non-OS SDK API Reference Version 2.2 ) に書かれており、rst_info構造体の reason にはリスタート要因として以下の値が格納されるとのこと。
1 2 3 4 5 6 7 8 9 |
enum rst_reason { REANSON_DEFAULT_RST = 0, // ノーマルスタート。電源オンなど。 REANSON_WDT_RST = 1, // ハードウェアウォッチドッグによるリセット REANSON_EXCEPTION_RST = 2, // 例外によるリセット。GPIO状態は変化しない REANSON_SOFT_WDT_RST = 3, // ソフトウェアウォッチドッグによるリセット。GPIO状態は変化しない REANSON_SOFT_RESTART = 4, // ソフトウェアによるリセット。GPIO状態は変化しない REANSON_DEEP_SLEEP_AWAKE= 5, // ディープスリープ復帰 REANSON_EXT_SYS_RST = 6, // 外部要因(RSTピン)によるリセット。 }; |
ディープスリープ中のリスタート理由の検出
上記のスケッチの中の、18行目( ESP.restart() )をコメントとし、次の行の ESP.deepSleep(…); のコメントを外す。これにより、SW1を押下するたびごとに2秒間のディープスリープに入るようになる。ディープスリープ中はLEDが消灯するのでそれと分かる。
スケッチのダウンロード後、ボードの電源をオフにしてからオンにしてやり、ディープスリープ開始~終了の様子を見ると以下のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
ets Jan 8 2013,rst cause:1, boot mode:(3,0) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 0, Power on ............ ets Jan 8 2013,rst cause:2, boot mode:(3,6) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 5, Deep-Sleep Wake ......................... |
まず、電源投入によってスタートする( reset reason == 0 , REANSON_DEFAULT_RST )。そして、LED点灯中( loop() が回っている状態)にSW1を押下することでディープスリープに入りLEDが消灯する。その2秒後にはディープスリープから復帰してリスタートするのでLEDが点灯する。シリアルモニタには、 reset reason = 5 (REANSON_DEEP_SLEEP_AWAKE) が表示される。これは意図どおり。
だが、SW1を押してディープスリープに入れてやりLEDが消灯しているあいだにRST-SWボタンを押してみると以下のようになる。
1 2 3 4 5 6 7 8 9 10 |
ets Jan 8 2013,rst cause:2, boot mode:(3,6) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 5, Deep-Sleep Wake ............ |
RST-SWを押したのに、リスタート要因はディープスリープからの復帰となってしまう。
ディープスリープしていないLED点灯中(loop() が回っている状態)でRST-SWボタンを押してやると、以下のように外部要因によるリセットと表示される。
1 2 3 4 5 6 7 8 9 10 |
ets Jan 8 2013,rst cause:2, boot mode:(3,6) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 6, External System ........................... |
つまり、ディープスリープ中にRSTピンをLにしても、スケッチからは正しいリスタート要因を得ることができない。ディープスリープからの自動復帰時にはGPIO16にLパルスがでる。それをきっかけにしてリスタートできるようRSTとGPIO16を直結しているのだから、当然といえば当然なのだが。
ENピンでリセットする
今回の回路でENピンとGND間にEN-SWを入れたのは、ディープスリープ中であってもEN = Lとしてリスタートすれば、ディープスリープ復帰以外の要因を得ることができるためである。
まず、電源を投入ししばらくしてEN-SWを押すとシリアルモニタには以下のように表示される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
ets Jan 8 2013,rst cause:1, boot mode:(3,0) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 0, Power on ............. ets Jan 8 2013,rst cause:1, boot mode:(3,7) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 0, Power on .............................. |
この中の最初のブートはパワーオンによるもので、二度目はEN-SWを押下したことによるものになる。いずれも reset reason == 0 (REANSON_DEFAULT_RST) となっているし、ブートメッセージ先頭の “rst cause: “も1である(1はパワーオン、rst cause: 2 はRSTピンによるリスタート)。
次にディープスリープ時にEN-SWを押下すると以下のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
ets Jan 8 2013,rst cause:1, boot mode:(3,0) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 0, Power on ... ets Jan 8 2013,rst cause:1, boot mode:(3,6) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld reset reason = 0, Power on .............................. |
通常動作時と同じように reset reason == 0 が得られる。
ディープスリープ中にENピンをLに落とすと、電源オン時との区別は付かないものの、少なくともディープスリープからの復帰とは区別できることが分かった。
余談だが、boot modeの第二パラメータで切り分けできそうなので、ESP.getBootMode(); というメソッドについて調べてみたが、拡張ブートモードに関するものでリスタート要因とは無関係だった。
ESP8266 System Description version 2.2の、1.4.2. Power-on Sequence and Power Reset によると、ENピンを200μsec期間Lレベル(0.6V以下)に維持すると、ESP8266EXはパワーオフすると書いてあるから、電源オン時と区別がつかなくて当然かもしれない。
リセットとRTCメモリとまとめ
今回リスタートやディープスリープについて調べたのは、ディープスリープ中に512バイトの情報を維持できるrtcメモリを使い、ディープスリープ復帰時の挙動を決めるようなスケッチを書いていて、手動でリセットしたのかスリープから復帰したのかを切り分ける要因を明確にしたかったからだった。
先に紹介した ESP8266 Non-OS SDK API Reference Version 2.2 によれば、rtcメモリもリスタート方法によって影響を受けるようだ。概略としては以下のようなことが書いてある。
- 電源投入時: RTCメモリはランダムな内容をもつ。
- ENピンによるリセット: RTCメモリはランダムな内容をもつ。
- RSTピンによるリセット: RTCメモリ内容は変更されない。
- ソフトウェアによるリスタート: RTCメモリ内容は変更されない。
- ウォッチドッグによるリセット: RTCメモリ内容は変更されない。
ディープスリープからの復帰はRSTピンによるリスタートと同じことなるので、RTCメモリの内容は変更されない。
ボード上に用意したリセットスイッチを押したときに電源オン時と同様に動いて欲しいとき(つまり、何もかもリセットしたいとき)、ENピンによるリスタートの方が適しているだろう。また、スケッチのダウンロードもEN-SWで代用できた。SW1とEN-SWをほぼ同時に押下し、EN-SWを離した後でSW1を離すことで、Arduino-IDEからのスケッチのダウンロードもうまくいった。
これらのことから、これからは基板上のリセットスイッチはENピンに接続しようと思っている。先に掲げたESP8266 System Description version 2.2には、RSTピンを使わない場合にはプルアップ抵抗も省略してよいと書いてあるから、ほんのわずか部品も減って楽もできる。
ついでにESP-WROOM-02Dでも確認
Espressif 社は、2017年の後半にESP-WROOM-02Dという製品をリリースしており、スイッチサイエンスで買い求めることができる。データシートの概要によればRF性能が最適化されているとのことだが、WROOM-02と同じコア(ESP8266EX)なのでその他の部分に大きな違いはないのだろう。見た感じ、アンテナ部分のパターンがわずかに太くなっているような気もするが、精密に測ったわけでもないのでなんとも言えない。
ちょっと前から、ブレッドボードではなく2.54mmピッチのユニバーサル基板にはんだ付けして使うとき、秋月のピッチ変換基板はちょっと大きいなと思っていたので、よりコンパクトなスイッチサイエンスのフル版ピッチ変換基板と共に買い求めて載せてみた。WROOM-02を自分ではんだ付けしたことがなかったので、変換基板に実装済のWROOM-02Dがあれば当然そちらにしたのだけど購入した時点では売ってなかった。
WROOM-02Dと変換基板のはんだ付けは、ふだん使っている30Wのはんだごてと0.6mm径のヤニ入りはんだを使って行った。ESP-WROOM-02(D)の端子のピッチは1.5mmと広めなので思っていたほど苦労はなかったものの、余計なはんだを吸い取ってやり直したり、よく見えないのでマクロレンズを近接させて拡大表示しながら確認したりもした。
フル版のピッチ変換基板をふつうのブレッドボードに載せると幅がギリギリになり電源レールが使いにくいので、幅が広いブレットボードというのを使うことにした。これはESP32開発ボードを載せるのに使ったサンハヤト社のニューブレッドボード(SAD-101)によく似ている、というより同じに見える。販売しているページを読むとポイント数が400と408で違うはずなのだけど、実物を手にとってみても違いが分からなかった。実は簡易包装版のニューブレッドボードだったりしないかな。
今回の回路を幅が広いブレッドボードに載せるとこんな具合になる。掲載した回路図はピッチ変換基板として秋月のAE-ESP-WROOM-02を使ったときのピン番号を振っている。この変換基板とはピン配置が異なっていることに注意。
この変換基板を使うときは、ワイヤや抵抗を変換基板の下に配置するのが流儀だろう。もっと詰めて載せれば写真左側半分くらいは空けられそうだ。そうした場合、電源レールが片側にしかないのでワイヤがちょっとうるさくなるかもしれないが。
ボードには3つのスイッチが載っているが、左からRST-SW、EN-SW、SW1となっている。上から見ると変換基板のシルク上にずいぶんヤニが流れているのが気になるところ。
実験用のスケッチをダウンロードして同じように試してみたが、結果はまったく同じだった。そりゃそうでしょうという感じか。ここのところブレッドボードの接触不良に悩まされているので、この変換基板は温度センサーと共にユニバーサル基板にはんだ付けしてしまう予定です。