ESP8266からGoogleスプレッドシートに書き込む

種々のIoT用データ投稿サービスはありますが、Googleスプレッドシートに書き込んでしまえばグラフ化・共有等々、何かと便利です。 ESP8266から直接スプレッドシートにデータを蓄積してしまいましょう。

Googleスプレッドシートスクリプト

今回は温湿度・気圧センサであるBME280を例として用いることとします。BME280から得た温度、湿度、気圧の値をESP8266よりPOSTで送ります。送られてくるクエリストリングの例として

temperature=26.75&pressure=1015.84&humidity=36.42

のような形であると想定します。

データの受信にはGoogle Apps Script(GAS)を利用します。下記の説明はかなり天下り式なので気になった部分についてはGoogle Apps Scriptで検索するとよいかと思います。

まず、Googleスプレッドシートを開き、新規ドキュメントを作成します。その後「ツール」->「スクリプトエディタ」を選択しスクリプトエディタを開きます。

f:id:soiyadakara:20181031211517p:plain:w400

最初はテンプレートが入っていますが、それを消してdoPost関数を書いてやります。

f:id:soiyadakara:20181031223444p:plain:w400

function doPost(e) {
  // パラメータ取り出し
  var temperature = e.parameter.temperature,
      pressure = e.parameter.pressure,
      humidity = e.parameter.humidity;
  
  // パラメータがそろっていなければ終了
  if(temperature && pressure && humidity){
    try{
      // スプレッドシート情報収集
      var spreadsheet = SpreadsheetApp.openById("<スプレッドシートID>"),
          sheet = spreadsheet.getSheets()[0],
          date = new Date();
      // スプレッドシートへ書き込み
      sheet.appendRow([date, temperature, pressure, humidity, e.postData.getDataAsString()]); 
      
    }catch(e){
      console.log(e);
    }
  }
}

こんな感じ。なおスプレッドシートのIDはスプレッドシートのURLに情報があります。

https://docs.google.com/spreadsheets/d/<スプレッドシートID>/edit#gid=0

doPost関数が準備できれば、それを公開する作業に移ります。適当なプロジェクト名で保存し、「公開」->「ウェブアプリケーションとして導入」を選択。

f:id:soiyadakara:20181031213410p:plain:w400

「アプリケーションにアクセスできるユーザー」を「全員(匿名ユーザーを含む)」に変更、「導入」を選択。

f:id:soiyadakara:20181031215455p:plain:w400

認証が必要と言われるので、「許可を確認」し、アカウントでログイン。さらに「このアプリは確認されていません」と出るので、左下「詳細を表示」を選択、下に出てくる「無題のプロジェクト(安全ではないページ)に移動」を選択。

f:id:soiyadakara:20181031214517p:plain:w400

すると、ようやくURLが与えられます。URLをメモしておきます。

f:id:soiyadakara:20181031214550p:plain:w400

このURLをテストしてみます。curlコマンドでPOSTメッセージを送ってみます。

$ curl --include --data 'temperature=26.75&pressure=1015.84&humidity=36.42' -X POST '<コピーしたURL>'

f:id:soiyadakara:20181031223133p:plain:w400

ちゃんとパラメータの対応等がとれていれば、問題なくスプレッドシートに追加されているはずです。うまく追加されない場合はデバッグをする必要がありますが、、、デバッグは少し大変です。

f:id:soiyadakara:20181031223335p:plain:w400

後はこのPOSTメッセージをESP8266で送るだけです。

ESP8266プログラム

ESP8266はArduinoで開発します。BME280とはI2Cで接続し、データ取得はAdafruitのライブラリを利用します。取得したデータのPOSTはWiFiClientSecureを用いて実装しました。

下記プログラムで、約1分毎に温湿度、気圧を測定しPOSTしてくれます。 実際にWiFi機能を利用しているのはpostValues関数内で、毎回接続するようになっています。

少しスプレッドシートを開いて眺めているとおもしろいかもしれません…?

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char* ssid     = "<WiFi SSID>";
const char* password = "<WiFi Password>";

const String host = "script.google.com";   // コピーしたURLのホスト部
const String url = "/macros/s/<文字列>/exec";  // URL後半部分

Adafruit_BME280 bme; // I2C

void setup() {
  Serial.begin(9600);
  Serial.println(F("BME280 test"));

  bool status;

  // BME280への接続
  Wire.begin(4, 5); // SDA=IO4, SCL=IO5
  status = bme.begin(0x76, &Wire);  // BME280アドレス指定
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  Serial.println();

  // おまじないに切断
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
}


void loop() {
  postValues();
  delay(10*1000);
}


void postValues() {
  // WiFi接続開始
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  
  WiFiClientSecure sslclient;
  String params;

  // センサデータ取得、パラメータ準備
  params = "temperature=" + String(bme.readTemperature(), 2);
  params += "&pressure=" + String(bme.readPressure()/100.0F, 2);
  params += "&humidity=" + String(bme.readHumidity(), 2);
  Serial.println(params);

  // SSL接続開始、POSTメッセージ送信
  if (sslclient.connect(host, 443) > 0) {
    sslclient.println("POST " + url + " HTTP/1.1");
    sslclient.println("Host: " + host);
    sslclient.println("User-Agent: ESP8266/1.0");
    sslclient.println("Connection: close");
    sslclient.println("Content-Type: application/x-www-form-urlencoded;");
    sslclient.print("Content-Length: ");
    sslclient.println(params.length());
    sslclient.println();
    sslclient.println(params);
    delay(10);
    String response = sslclient.readString();
    int bodypos =  response.indexOf("\r\n");
  } else {
    // HTTP client errors
    Serial.println("[HTTPS] no connection or no HTTP server.");
  }

  // 送信完了、接続終了
  WiFi.disconnect();
}

こんな感じ。

f:id:soiyadakara:20181031231901p:plain:w400

所感

GAS、なかなか便利です。

BME280の温度が体感よりも高めな気がします。 使ったBME280モジュールには3.3Vのレギュレータ等が乗っているため、そこからの熱を拾っているのでしょうか。 精度を追求するなら電源を切るといった事が必要かもしれません。

また、今回はESP8266のスリープ機能を利用していませんが、ロガーとしての用途ならばスリープ機能を利用し電池等で動かしたいですね。