ESP8266の電池交換タイミングを知りたい
ESP8266(ESPr Developpter,以下ESPr)は、標準でWifiに繋げられて、GPIOがあって、電池で動いて、小さくて、まぁまぁ安くて、しかも超低消費電力で寝てくれるディープスリープ機能まであるというイケてるボードです。
これを使って電池駆動でサーバーに情報を送る機器を作ろうと考えているのですが、知らないうちに電池が切れてたりすると困ります。
開発ボードであるESPrには3.3V端子があり、ここにレギュレータからの出力が繋がっています。そしてプログラム中でこの電圧を測る機能が付いています。
動作中にここの電圧を監視して、一定以下になったら電池交換を促せば電池切れを防止できそうです。
そこでまず、3.3V端子が何ボルトまで下がったら止まるのか、それはだいたい何時間後なのかを知るために、ESPrを電池で動かし3.3V端子の電圧をGoogleスプレッドシートに記録してみることにしました。
ESP8266でGoogle Spread Sheetに書き込むのはちょっと面倒らしい
Google Spread Sheetは、Google Apps Script(GAS)で作ったWebアプリケーションと連動できます。WebアプリケーションにGETやPOSTメソッドでデータを渡してスプレッドシートに記録できます。
これを使えばわざわざサーバーを立てたり長時間ローカルPCを点けっぱなしにせずにすみます。
GASはちょっともっさりしたサービスで、高速に大量のデータを受け取ることは難しそうですが、数秒以上の間隔が開いてれば大丈夫でしょう。
しかし調べてみると、ESPrでGASで作ったWebアプリケーションにアクセスするのは結構難しいことが分かりました。
GASのWebアプリにはHTTPSアクセスが必要なこと、さらに最初にアクセスしたURLからリダイレクトされるので、それに追随できる必要があるそうです。(このへんは後述するHTTPSRedirectのREADMEに詳しい説明があります)
で、よく使われるESPr用のHTTPライブラリだとこのあたりが上手くいかないらしい。っていうか、うまくいきませんでしたとか、たいへんでした系の記事が結構見つかったので尻込みしました。
うーん、困った。
困った時のIFTTTでうまくいったかに見えたが…
そこで思い出したのがIFTTT(イフト:IF This Then Thatの略らしい)です。
IFTTTは複数のWebサービスを連結させられるサービスです。
ひとつのサービスで設定した条件を満たしたら = IF This
別のサービスに何かをやらせる = Then That
ということができます。普通ならそれぞれのAPIを勉強しないといけないわけですが、IFTTTだと簡単にできます。
GoogleやLINE、TwitterとかのIDを持っていれば、アカウント連携で使えるので、APIのようにデベロッパー登録してアクセスキーを発行してもらうなんていう手間もありません。
IF ThisとThen Thatには、色んなモジュール的なものが用意されているので、ほとんどコードを書かなくても使えます。
ちなみに一連の処理を記述したものをIFTTTではアプレットと呼ぶみたいです。
で、IF Thenの中にはWebhook(特定のアドレスへのhttpアクセス)が用意されているので、ここにESPrからアクセスすればそれがトリガーとなって、他のWebサービスで何かをさせられます。このためIoTでもよく使われてるらしいです。
Then Thatには、LINEでメッセージを送るとか、Google Spread Sheetにデータを書くモジュールもあって、まさに私のやりたいことが揃っていました。
しかも検索したらESPrからIFTTTのWebhookを利用する例がいくつも見つかりました。
そんなわけでいそいそとIFTTTの無料プランに加入しました。
最初はIFTTT経由でLINE Notifyを動かしてメッセージをPUSHしてみたところ、これがあっさりとうまくいき、「やっぱクラウドってすげーなー」とか思ってたわけです。
ところが数日後に今度はGoogle Spread Sheetにデータを書くアプレットを作ったら動かない。あれれ、前は動いたLINE Notifyも動かなくなってる。
IFTTTで特にエラーがでてる感じでもなくてWebhookは受け付けてるようなのにLINE NotifyもGoogle Spread Sheetも動かない。
ノンプログラミング型のサービスは肝心なところがブラックボックスですから、こうなると原因の究明が難しいです。
そんなわけで今回はIFTTTは諦めて、ESPrから直接Google Spread SheetのAPIを叩いてみることにしました。
HTTPSRedirectライブラリが素晴らしかった
いろいろ検索したり試したりして、最終的にうまくいったのが「HTTPSRedirect」というライブラリを使った方法でした。
HTTPSでアクセスでき、しかもGAS Webアプリのリダイレクトにも対応しています。つか、早い話がESP8266でGoogle DocumentやSpread Sheetを使うためのライブラリみたいです。
GETメソッドとPUTメソッドを使ってパラメーターやJSONをGASに渡せるので、あとはGAS上でいろんな処理が書けます。
また、GASからのデータも受け取れるので、いろんなことに使えそうです。
しかも、ESPrに書き込んですぐに試せるサンプルコード(GoogleDocs.ino)が付いています。このサンプルが素晴らしいのは、作者が用意してくれた Google Spread Sheet を使って試せることです。
普通なら自分でスプレッドシートを作って、そこにサーバーサイドスクリプトを書いて試す必要があります。
この場合、動かなかったときにESPr側の問題なのかサーバーサイドの問題なのかを特定するのが大変です。
作者提供のスプレッドシートへのアクセスができればとりあえずESPr側はオッケーとなるので、その後の開発がぐっと楽になります。本当にありがたい。
サンプルを使うときは,まずHTTPSRedirectライブラリをダウンロードしてArduino IDEにインストールします。
後はライブラリと一緒に付いてくる "GoogleDocs.ino"をそのままESPrに書き込めば、上記のスプレッドシートへの書き込みと読み出しが始まります。arduino IDEのシリアルモニタを開いておきましょう。
詳細は、Githubで読めるREADMEの"Sec. IV Working Example (Using Google Docs)"を参照してください。詳しいやり方が書いてあります。
サンプルコードGoogleDocs.inoをざっくり読み解く
この、GoogleDocs.ino、サンプルコードにしては意外と長くて色んな処理をしています。参考のためにどんな動作をしているのかざっくり書いておきます。
setup( ) 内では以下の処理があります。
-
Wifiに接続
-
Google Apps ScriptのWebアプリにHTTPSで接続
-
POSTメソッドでESPrのヒープメモリとスタックメモリの残り量を送信
-
GETメソッドに?value="Hello"パラメーターを付けて送信
-
POSTメソッドでESPrのヒープメモリとスタックメモリの残り量を送信
-
GETメソッドに?calパラメーターを付けてグーグルカレンダーの1週間分の予定をリクエスト
-
POSTメソッドでESPrのヒープメモリとスタックメモリの残り量を送信
-
Webアプリから切断
loop()内では以下の処理があります。
-
Google Apps ScriptのWebアプリにHTTPSで接続
-
POSTメソッドでESPrのヒープメモリとスタックメモリの残り量を送信
-
GETメソッドに?readパラメーターを付けてスプレッドシートのA1の値をリクエスト
-
POSTメソッドでESPrのヒープメモリとスタックメモリの残り量を送信
-
4秒待って繰り返す
Setup()でもloop()でも、繰り返しESPrのメモリ残量をPOSTするのが特徴です。
最初ソースを見たときにいきなり、ESP.getFreeHeap() とか ESP.getFreeContStack() とかが出てきて、うわーそんなメモリとか弄らないといけないの?と絶望しかけましたが、よく見ると単に送信するデータとしてメモリ残量を使っていただけでした。たぶんライブラリ開発してるときにメモリ残量とか知りたかったんじゃないかとおもいます。
POSTメソッドでは、送るJSON内にcommand:という項目があり、これを使ってサーバーサイドの処理を変えられます。
パラメーターに応じた処理やPOSTデータの処理はもちろん、サーバーサイドのGASに依存します。
サーバーサイドGoogleScript.gsは割とシンプル
サーバーサイドスクリプトのGoogleScript.gsは割とシンプルです。
GASで作るWebアプリケーションの特徴である doPost()と doGet() 関数にPOSTメソッド、GETメソッドの処理を書いてあります。
doPostでは
parsedData = JSON.parse(e.postData.contents);
でPOSTされたJSONを取り出し、
switch (parsedData.command)
でコマンドに応じて分岐します。
doGet()は、
var val = e.parameter.value; var cal = e.parameter.cal; var read = e.parameter.read;
でパラメーターを取り出して、if文で分岐しています。
後半にはカレンダーから一週間分の予定を取り出す関数が定義されています。
ESPr Developperの3.3V端子の電圧をGoogle Spread Sheetに記録するプログラム(マイコン側)
いよいよ本題です。まずはESPr側に書き込むプログラム。
ESPrの3.3V pinの電圧は、setup()より前に
ADC_MODE(ADC_VCC);
と書いておけば、あとから好きなところで
int v3 = ESP.getVcc();
と書いて取得できます。
数値は"3421"のように4桁の整数で得られるので、送信前に1000分の1に整形しています。
実際に使うときは冒頭にある ssid , password , GScriptId 3つのパラメーターを自分の環境に合わせて設定します。
const char* ssid = ""; //Wifi SSID
const char* password = ""; //Wifiパスワード
const char *GScriptId = ""; // "Google Apps ScriptでWebアプリケーションとして公開"した時に得られるURLの一部
ソースコード
#include <ESP8266WiFi.h> #include "HTTPSRedirect.h" ADC_MODE(ADC_VCC); //to watch 3.3V pin voltage const char* ssid = ""; const char* password = ""; const char *GScriptId = ""; const char* host = "script.google.com"; const int httpsPort = 443; String url = String("/macros/s/") + GScriptId + "/exec"; //payload_baseはJSONデータとしてコマンドとシート名、値を送るためのURLの前半部分 //ここではappentRowだけだが、サーバースクリプトでコマンドに応じた処理を作っておくと色んなコマンドが使える String payload_base = "{\"command\": \"appendRow\", \ \"sheet_name\": \"Sheet1\", \ \"values\": "; //paload_baseと送る値を合体するための変数 String payload = ""; HTTPSRedirect* client = nullptr; void setup() { Serial.begin(115200); Serial.flush(); Serial.println("************** ESP8266 to Google SS Starting up! ***************"); Serial.println(); Serial.print("Connecting to wifi: "); Serial.println(ssid); // flush() is needed to print the above (connecting...) message reliably, // in case the wireless connection doesn't go through Serial.flush(); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void loop() { static int error_count = 0; bool flag = false;// connection success or fail client = new HTTPSRedirect(httpsPort); client->setInsecure(); client->setPrintResponseBody(true); client->setContentTypeHeader("application/json"); Serial.print("Connecting to "); Serial.println(host); // Try to connect for a maximum of 5 times for (int i = 0; i < 5; i++) { int retval = client->connect(host, httpsPort); if (retval == 1) { flag = true; break; } else Serial.println("Connection failed. Retrying..."); } if (!flag) { delete client; Serial.print("Could not connect to server: "); Serial.println(host); Serial.println("GO TO SLEEP....."); ESP.deepSleep( 0 ); delay( 1000 ); } Serial.print("Successfully Connected to "); Serial.println(host); //**** read 3.3v pin and send data to Google SpreadSheet **** Serial.println("POST append memory data to spreadsheet:"); int v3 = ESP.getVcc();//voltage of 3.3v pin String vcc_3v3 = String(float(ESP.getVcc()) / 1000); Serial.println( "3.3V:>>" + String(v3)); payload = payload_base + "\"" + vcc_3v3 + "\"}"; // to send multiple values, separate with "," like this. Server side can handle array, so no neet to change GAS script. //複数の値を渡すときはこんな感じでカンマで区切る。サーバーサイドスクリプトは配列対応なのでデータ数が増えても変更不要 //payload = payload_base + "\"" + vcc_3v3 + "," + data1 + "," + data2 + "\"}"; if (client->POST(url, host, payload)) { Serial.println("Success! send data"); } else { error_count++; Serial.print("Error!"); Serial.println(error_count); } if (error_count > 3) { Serial.println("Halting processor..."); delete client; client = nullptr; Serial.flush(); ESP.deepSleep(0); } delete client; delay(60000);//interval of posting }
EPSrからのデータを受け取ってGoogle Spread Sheetに追記するプログラム (Google Apps Script)
サーバーサイド側です。
doPost()でESPrからのPOSTを受け取ります。
渡されたデータに日付を加えてスプレッドシートに追記します。
このスクリプトは必ずスプレッドシートに付属した形で書き込みます。
まず、記録用のスプレッドシートを作ります。
ツールメニューから「スクリプトエディタ」を起動して以下のソースを入力します。
完成したら、公開メニューから「Webアプリケーションとして導入」を選択。
必ずバージョン「New」、アクセス権は「Anyone even anonymous」(誰でも可、匿名を含む)でデプロイします。
最初はアクセス権限を要求する画面が出ますので許可します。
作成されたWebアプリケーションのURLからID部分を抜き出して、EPSr用プログラムのGScriptIdに設定します。
なお、HTTPSRedirectのサンプルソース内のコメントでは、SpreadsheetApp.getActiveSpreadsheet() では上手く動かないので、スプレッドシートIDが必要とありました。しかし、とりあえず動いているので、SpreadsheetApp.getActiveSpreadsheet() を使って汎用性を高めてあります。
ソースコード
//自分のスプレッドシート情報を取得 var SS = SpreadsheetApp.getActiveSpreadsheet(); var SHEET = SS.getSheets()[0]; var str = ""; function doPost(e) { var parsedData; var result = {}; try { parsedData = JSON.parse(e.postData.contents); } catch(f){ return ContentService.createTextOutput("Error in parsing request body: " + f.message); } if (parsedData !== undefined){ switch (parsedData.command) { case "appendRow": var dataArr = parsedData.values.split(","); var now = Utilities.formatDate(new Date(), "JST", "yyyy/MM/dd HH:mm:ss"); dataArr.unshift(now); SHEET.appendRow(dataArr); str = "Success(appendRow)"; SpreadsheetApp.flush(); break; } return ContentService.createTextOutput(str); } else{ return ContentService.createTextOutput("Error! Request body empty or in incorrect format."); } }
測定結果:約41時間後、2.72Vで動作停止
ESPrに単四電池3本用(4.5V)の電池ボックスに百均の新品アルカリ電池を入れ、VIN端子に接続して動かしてみました。
1分おきに3.3V端子の電圧が記録されていますから、最後に電圧が記録された直後に動作が停止したと判断できます。
結果は、初期電圧が3.46V。最後の記録が、経過時間41:23:58、電圧は2.72Vでした。
ずっと3.46Vを維持してきた電圧が下がり始めてから約12時間で動作停止となります。警告してすぐに交換できるとは限りませんから、特にスリープ無しで連続動作させる場合には、電圧が3.46Vを切ったらすぐに警告を出した方が良いかもしれません。
他のセンサーなどの負荷が加わるとまた結果が違ってくると思います。そうしたらまたこのプログラムを利用して計測すればいいでしょう。
とりあえず動いたのでめでたしめでたしでした。