GASで作る貧乏IoT(1) ESP32からPOSTして、レスポンスを取得する。
2021.05.30追記
どうもGCP(Google Cloud Platform)のアカウントを持っていると、GCP側で別の設定をしないと動かないようです。
`GCPの画面からGASのプロジェクト作る -> OAuth設定 -> GASファイル作成` という手順が必要っぽい。
Google Apps ScriptのログをGoogle Cloud Platformで確認する方法 – hidetoshl.com
-----
例によって久々の更新です。
近況
- AWS色々覚えたものの、諸事情により、課金ダメージを受けた。(※1)
- なので個人でIoT周りやるなら、請求が発生し得るものはしばらく触りたくないというお気持ちに。
で、あんまり好きじゃないけど、一時期仕事で触っていたGoogle Apps Script(以下GAS)やるか、、となったものの、相変わらずトラップが多いので、備忘録的にまとめようと思いました。
というわけで「GASで作る貧乏IoT」です
内容は主に電子工作趣味のハードウェア、組み込みの人向けに必要そうなことだけ。
気が向いたら何回か書くと思いますが、GASの文法とかWebのお作法などは特に触れません。
GAS(Google Apps Script)について雑に書く
- Googleが提供するJavaScript互換のスクリプト環境。
- 2020年2月からはEcmaScript対応になり、モダンな書き方ができるようになったらしい。(※2)
- Googleフォーム、スプレッド、ドキュメント、メール、カレンダー連携には強い
- 正確な時間ではないが、一応、定期実行もできる。
- 6分を超える処理はできない。(タイムアウト扱いになって色々トラブルが起きる)
- IDE上でデバッグやステップ実行できる
- 無料で使える。(色々不満はあるけど、これ超重要)
今回のお題 「ESP32からデータをPOSTして、レスポンスを取得する。」
注意!!!) doPost関数は業務利用は、ほぼ無理と思います。
趣味の範疇でのみ利用可能と捉えてください。(※3)
●概要
- ESP32(Arduino)からGASにデータをPOSTする
- POSTした結果をスプレッドに記入。
- 処理終了後にレスポンスを返す。
重要)スプレッドに書くだけならさして苦労はないものの、レスポンスを返す場合はライブラリ「HttpRedirect」を使う必要があります。(※4)
動作確認はM5Stackを利用。たぶんESP8266でも動くはず。
●ざっくりした流れ
1)GASのプロジェクトを用意し、Webアプリケーションとして公開する
2)Arduinoに書き込みを行う。
GAS側やること
●スクリプトエディタを立ち上げる
方法1) スプレッドシートも使いたい場合
・スプレッドを起動。メニューの<ツール> - <スクリプトエディタ>を開く
方法2) 独立したプログラムファイルを作りたい場合
・Googleドライブの新規作成で、"Google Apps Script"を選択
●大まかな書き方
- "function doPost(e) {}"の中に処理を書く
- "e.JSON.parse(e.postData.contents);"で、ESP32から送ったJSON風のデータを受ける。
- "return ContentService.createTextOutput( 文字列 )"; でESP32にレスポンスを返す。
●コード例
- 文法はJavaScriptを検索すればだいたい困らないはず。
- GoogleサービスのAPI処理については、↓のように個別のサービスを探す必要がある。
https://developers.google.com/sheets/api/guides/concepts?hl=ja
- 今は日本語のブログも増えているので、検索「GAS スプレッド 追記」の要領でだいたい出てくるはず。(※5)
ファイル"GAS_sample.gs"
var sheet_id="*****";//スプレッドのID。https://docs.google.com/spreadsheets/d/{この部分}/edit#gid=2037815417 //デバイスからのPOSTで実行される関数 function doPost(e) { //POSTのpayloadを格納 var parsed_data; try { parsed_data = JSON.parse(e.postData.contents); } catch(_exception){ // JSON以外がPOSTされたら例外を返して終了 return ContentService.createTextOutput("JSONデータの解釈が出来ません。: " + _exception.message); } //GASデバッグ用なら↑をコメントアウトしてこういう書き方でOK //var parsed_data=JSON.parse('{"values_01":"10.0", "values_02":"20.5"}'); var spread_file_object = SpreadsheetApp.openById(sheet_id); var sheet_name = "log"; var sheet_object = spread_file_object.getSheetByName(sheet_name); // スプレッドに行を追記 //sheet_object.appendRow([new Date(), parsed_data.values_01, parsed_data.values_02]); //sheet_object.getRange("A1").setValue(parsed_data.values_01); SpreadsheetApp.flush(); //描かなくても動く。一応、遅延対策。 //ESP32にレスポンスを返す return ContentService.createTextOutput( "success"); }
特定のセル1つに値を入れる場合はこう書く。
sheet_object.getRange("A1").setValue(parsed_data.values_01);
複数セルの場合はsetValues()になるので若干処理が変わる。
●アクセスできるようにする設定
1)スクリプトエディタのメニュー"公開" -> "webアプリケーションして導入" を開く
Current web app URL:
-> 変更する部分はなし。URLはArduinoのコードに記載するので、コードの上の方にでも書いておくのがベター。
/macros/s/●●/exec というルール。Arduinoのファイルに書くScriptIDはこれを使う。
Project version
-> Newを選択
※コードの変更を反映する度に都度更新が必要。GASを書き換えてctrl+Sだけだと、最新に反映されない。
Execute the app as:
-> 自分のGoogleアカウントを選択
WHo has access to the app
-> Anyone,even anymous を選択
色々許可を求められます。
2)アクセスの許可
IDE上の虫マーク横の関数「doPost」を選択して、虫マークをクリックして実行。
途中、実行の許可を求められるのでOKする。
注意!!!)一度実行の許可をして動かさないと利用できない。
ESP32(Arduino側)
●やること
1)下記のGitHubからHTTPSRedirect.cppとHTTPSRedirect.hとDebugMacros.hをコピーする
タブ追加でファイルを追加してコピペで良い。
2)HTTPSRedirect.cppの89行目。 //stop();の//を消して実行できるようにする。
case 301:
case 302:
{
Serial.print(""piyo"");
// Get re-direction URL from the 'Location' field in the header
if (getLocationURL()){
stop(); // may not be required
※コメントアウトしたままだと、スプレッドは更新されるがESP32側が停止する。
※ESP32での動作確認時に変更。もしかしたら8266は変更不要かもしれない。
参考)https://github.com/electronicsguy/ESP8266/issues/91
コード例
ファイル名「ESP32_TO_GAS.ino」
//#include <ESP8266WiFi.h> //ESP8266なら<WiFi.hの代わりにこちら> #include <WiFi.h> // ESP32用 #include "HTTPSRedirect.h" // Fill ssid and password with your network credentials const char* ssid = "****"; // TODO)入力 const char* password = "****"; // TODO)入力 const char* host = "script.google.com"; const int httpsPort = 443; // Replace with your own script id to make server side changes //TODO) google scriptのscriptIDを入力 const char *GScriptId = "*****"; const String url = String("/macros/s/") + GScriptId+ "/exec"; // https://script.google.com/macros/s/{この部分}/exec HTTPSRedirect* client = nullptr; void setup() { Serial.begin(115200); Serial.print("Connecting to wifi: "); Serial.println(ssid); Serial.flush(); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } int count; void loop() { do_post(); delay(60000); } void do_post(){ bool flag; char stringCount[10]; sprintf( stringCount, "%d", count); count++; // Use HTTPSRedirect class to create a new TLS connection client = new HTTPSRedirect(httpsPort); client->setPrintResponseBody(true); client->setContentTypeHeader("application/json"); Serial.print("Connecting to "); Serial.println(host); // Try to connect for a maximum of 5 times flag = false; 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){ Serial.print("Could not connect to server: "); Serial.println(host); Serial.println("Exiting..."); return; } float sensor_value_01=10.0; //TODO)任意のセンサ値を設定 float sensor_value_02=20.0; //TODO)任意のセンサ値を設定 String payload = "{"; payload += "\"values_01\": \"" + String(sensor_value_001) +"\""; payload += "\"values_02\": \"" + String(sensor_value_002) +"\","; payload += "}"; Serial.println("==POSTCOMMAND=============================="); Serial.println( payload ); client->POST(url, host, payload, false); Serial.println("==POSTCOMMAND=============================="); Serial.print( "Status :"); Serial.println( client->getStatusCode() ); Serial.print( "reasonPhrase:"); Serial.println( client->getReasonPhrase() ); Serial.print( "body :"); Serial.println( client->getResponseBody() ); //GASでreturnした値 // delete HTTPSRedirect object delete client; client = nullptr; }
以上。
GAS側のレスポンスは適当な文字列にしていますが、レスポンスによってESP32で分岐を加えると色々遊べると思います。
ぶっちゃけAWS S3にデータ置く方がはるかに楽だし、マイコンよりもラズパイ使った方が情報も多くて簡単。
しかし、ハードを小さく安価に。ソフトもお金をかけたくない。Ambientのようなサービスを使わず自前で用意したいという場合は、もう一周回ってGASでいいやと思いました。
次回は時間があれば、M5CameraかMaixduinoの画像をGoogleドライブにアップロードなど。
ちなみにGET/POSTがどうとか、Webのお作法的なものを基礎から押さえたいという方は「Webを支える技術」あたり読むのが良いかもしれません。
※1) 仕事都合により個人で使ってたAWS RDSが、ほんのちょっとしか使ってないのに請求$400overした。
カードの不正利用疑いで一瞬止められてたのもあり、深夜に4-50万と見間違えて死ぬかと思った。
※2) JavaScriptに対する愛が薄いので、突っ込んだ話はしません。詳しく知りたい場合は検索「EcmaScript GAS」を推奨。
※3) 多くの企業は情シスが社内ドメイン以外からのGsuiteアカウントへのアクセスに制限をしているはず。
※4) GASの仕様でdoPost関数使うとリダイレクトが発生するが、ライブラリ「WiFiClientSecure」はリダイレクトに追従しない。
そのため、レスポンスを取ろうとすると、レスポンスコード302で詰まる。
※4) 書籍もいくつか出ているけれど、特に買う必要はないと思う。仕事のために数日で覚えるために買ったけれど、あまり必要なかった。