二酸化炭素センサの値をGoogleスプレッドシートに保存する

 頒布している、二酸化炭素センサキットは、いまのところローカルで測定結果を4桁7セグのLEDに表示するだけのものです。
 しばらく二酸化炭素の数値を見ていると、測定結果を時系列で保存したり、グラフ化してみたりしたくなると思います。ローカルでデータを保存するのは、SDカードを接続可能にしたり、そのSDを回収したりがめんどくさいので、クラウドサービスに送信するのが簡単です。
 クラウドサービスにはいろいろあるのですが、料金がかかったり、新しくユーザー登録やクレジットカードの登録を要求されたり、めんどうなところがあります。さらに蓄積したデータをBIツールなどで可視化するのも手間です。
 Googleスプレッドシートを使えば、外部からのデータの登録やグラフ化、マクロを使った処理などが簡単にできて便利です。
 前回、GASを使ってスプレッドシートを更新する仕組みを作りましたので、ESP32からArduinoスケッチにデータの送信を組み込んでみます。

 GASとの通信には、HTTPSが必須になります。HTTPでの通信と比べてちょっとめんどくさいですが、経路を暗号化することはデータ秘匿のために重要です。CO2センサの値が外部に漏れると、在宅状況などが第三者にわかってしまいます。

 現在の、二酸化炭素センサキットは、二酸化炭素センサとI2Cで通信して、その結果を4桁7セグのLEDに表示しています。値の更新は毎秒行っていますが、さすがにこのデータを全て送るのはデータの量が多すぎるでしょう。適当に時間間隔をとってやる必要があるでしょう。

 GASの利用にも制限があります。二酸化炭素センサの値を投げるくらいでは、無料アカウントでも特に問題は出ないと思われます。プロパティのリード・ライトが一日5万回、全実行時間が一日90分があるくらいだと思います。
 この制限はアカウントごとに適用されるので、他でも使用している場合は合算されるので注意が必要になります。詳細は、下記のURLを御覧ください。

  Quotas for Google Services | Apps Script | Google Developers
  https://developers.google.com/apps-script/guides/services/quotas

 ESP2866からGASと通信するライブラリとして、HTTPSRedirectがよく知られています。このライブラリがESP32にも対応しています。

  ESP8266/HTTPSRedirect at master · electronicsguy/ESP8266
  https://github.com/electronicsguy/ESP8266/tree/master/HTTPSRedirect

 HTTPSRedirectでは、TLSで通信する場合の接続先の正当性の確認をfinger printで行っています。マイコンなど、リソースが限られた環境で、高度な計算を必要とするTLSの通信のうち、証明書の正当性を確認することを簡単に行う手段で、組み込み環境では使用されることがあります。
 この方法は、CA署名で接続先の証明書を確認しないので、証明書が更新されたら通信できなくなるのですが、一般的なWebサービスの証明書の期限は長く設定されているので問題にはならないでしょう。
 証明書のfpを取得する方法はWebサービスで提供しているものもありますが、正しい情報が取得できているという保証がないので、コマンドで自分で取得するのがいいでしょう。
 接続するサーバーの証明書のfinger printを取得するには、opensslコマンドを使用します。

$ openssl s_client -connect script.google.com:443 < /dev/null 2>/dev/null | openssl x509 -fingerprint -noout -in /dev/stdin
SHA1 Fingerprint=3D:27:04:E9:96:64:53:9E:E1:F5:80:A2:AF:83:83:16:9A:CA:CC:72

 証明書の期限を確認しておきましょう。

$ openssl s_client -connect script.google.com:443 < /dev/null 2> /dev/null | openssl x509 -text | grep "Not After"
            Not After : Jan  2 20:54:46 2020 GMT

 うぉ!超短い。スタートはいつだろう?

$ openssl s_client -connect script.google.com:443 < /dev/null 2> /dev/null | openssl x509 -text | grep "Not "
            Not Before: Oct 10 20:54:46 2019 GMT
            Not After : Jan  2 20:54:46 2020 GMT

 調べてみると、有効期限は案外短く3ヶ月未満でした。セキュリテイ的にはそういうトレンドになっているのでしょう。組み込み環境にはつらい世の中です。どこからかfpの値を取得するような仕組みにしないと、ハードコードすると突然通信できなくなって詰むことになります。

 Webからfpを取る方法を考える必要がありますが、あまり良いサービスはなさそうです。
 Webブラウザでfpを取得するサービスをgrc.comで運用しています。

  GRC | SSL TLS HTTPS Web Server Certificate Fingerprints
  https://www.grc.com/fingerprints.htm

 fpの値を簡単なAPIでは取ることはできませんでした。残念。
 自分でサービスを運用するしかないようです。
 HTTPSRedirectの利用はやめたほうが要さそうです。

 二酸化炭素センサの値を送る前に、NodeMCU-32Sを使って簡単にデータが送れるかどうか確認してみます。

#include <WiFi.h>
#include <WiFiClientSecure.h>

const char *ssid = "<My SSID>"; 
const char *password = "<My PASS>";  
const char *host = "script.google.com"; 
const String gasid = "AKfycbz5da5_XD6xDULiNdF5771LaWQz1q2A2Njmbb11RcT53XsDVqY";

void setup() {
  Serial.begin(115200);
  delay(100);
  
  WiFi.begin(ssid, password); 
  Serial.println("Connecting... ");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("connected");
  Serial.print("address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  WiFiClientSecure client;
  int co2ppm = 1000;
  String params;
  const String url = "/macros/s/" + gasid + "/exec?";
  params = "co2ppm=" + String(co2ppm);
  Serial.println(params);

  if (client.connect(host, 443) > 0) {
    client.println("POST " + url + " HTTP/1.1");
    client.println("Host: " + String(host));
    client.println("User-Agent: CO2 Sensor/1.0");
    client.println("Connection: close");
    client.println("Content-Type: application/x-www-form-urlencoded;");
    client.print("Content-Length: ");
    client.println(params.length());
    client.println();
    client.println(params);
    delay(10);
    String response = client.readString();
    Serial.println(response);
  } else {
    Serial.println("connection nothing.");
  }
  delay(5000);
}

 シリアルポートには以下のように表示されます。

Connecting... 
....connected
address: 192.168.0.20
co2ppm=1000
HTTP/1.1 302 Moved Temporarily
Content-Type: text/html; charset=UTF-8
Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: Mon, 01 Jan 1990 00:00:00 GMT
Date: Sun, 03 Nov 2019 08:01:36 GMT
Location: https://script.googleusercontent.com/macros/echo?user_content_key=dcaayVKcNzRiUhZI4H32eH3769eDSSNLJAfxXgETjMbDiHSnOyy87daFmbmaHIQkSMAzBpPCTe6T9bFQ_OYSF0JqpRvt4n2Dm5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnEtxa5lmY_Cq0vcw6x1jU0n8G55N0may1HDGvea1TGZUmAmD-jkGEWLVqVciHb3g6OIqWsDnSrEd&lib=MLCdUekLmiulGCk4TKtYk0cd3KZMsK3dn
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
Alt-Svc: quic=":443"; ma=2592000; v="46,43",h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
Accept-Ranges: none
Vary: Accept-Encoding
Connection: close

<HTML>
<HEAD>
<TITLE>Moved Temporarily</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Moved Temporarily</H1>
The document has moved <A HREF="https://script.googleusercontent.com/macros/echo?user_content_key=dcaayVKcNzRiUhZI4H32eH3769eDSSNLJAfxXgETjMbDiHSnOyy87daFmbmaHIQkSMAzBpPCTe6T9bFQ_OYSF0JqpRvt4n2Dm5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnEtxa5lmY_Cq0vcw6x1jU0n8G55N0may1HDGvea1TGZUmAmD-jkGEWLVqVciHb3g6OIqWsDnSrEd&amp;lib=MLCdUekLmiulGCk4TKtYk0cd3KZMsK3dn">here</A>.
</BODY>
</HTML>

 スプレッドシートが更新されているのも確認できます。1ループに8秒程度かかっているようです。プログラムには、ディレイは5秒と少ししか入っていないので、GASとの通信処理には3秒弱かかっているようです。

 これを従来のCO2センサのスケッチに追加して、値を反映させます。
 表示の割り込みと、クラウドへの送信を両立させるのに注意が必要です。
 クラウド側への送信は、あまり高頻度でもデータがたまってたいへんですので、10分に一回に設定します。一回のループで2秒程度は遅延しているので、これも全体で調整するといいでしょう。
 センサー側からは、送信時刻は送っていないので、スケッチにNTPクライアントを導入する必要はありません。

 現在は、WiFi設定がスケッチ内部にハードコード(直接書かれている)された状態ですので、これをアプリケーション経由で設定できるようにするといいでしょう。

 GASやスプレッドシートのマクロを使うと、値を監視してメールを送信したり、数値をグラフ化してPWAを使ってスマホで見られるようにすることもできます。

 LDKに設置して結果を少しグラフ化してみた。在宅がバレバレだ。


オリジナル投稿:
二酸化炭素センサの値をGoogleスプレッドシートに保存する|kinneko|pixivFANBOX
https://kinneko.fanbox.cc/posts/632438