GoogleApps ScriptにESP8266からPOSTする

Google Apps Script (GAS)に定期的にデータを上げるための開発メモ。

#ポイント1
##定期実行はArduinoのMsTimer2の代わりにTickerライブラリを使う
 - 参考: https://qiita.com/exabugs/items/34aab51eb2d35207629b
- WiFi通信のような処理はonTickerな関数ではなくloop内に設置する。

##サンプルコード
```cpp:TickerTest.ino
#include <Ticker.h>

Ticker ticker;
bool bSend = false;

void setup() {
  ticker.attach_ms(1000, onTicker);
}

void loop() {
  if (bSend) {
    //GASへPOSTする処理

    bSend = false;
  }
}

void onTicker() {
  bSend = true;
}
```

#ポイント2
## GASへ送る方法
- HTTPSRedirectを使う
 - https://github.com/electronicsguy/ESP8266/tree/master/HTTPSRedirect
- ESP8266のボードバージョンは2.4.0を使う

##準備1:fingerprintを取得
1. opensslをインストール
2. powerdhellで`echo | .\openssl s_client -connect script.google.com:443 |& .\openssl x509 -fingerprint -noout`を実行
3. `const char* fingerprint = "XX:XX:XX:.....:XX";とする

##準備2:GASでdoPost(e)なコードを用意する
- `concole.log(e.postData.contents)`とかやればとりあえすStackdriverのログに残ると思う
- json.parseとかでjsonをパースできるね

## GASのサンプルコード

```js:コード.gs
function doPost(e) {
  console.log(e.postData.contents); //Stackdriverのログへ
}
```

##サンプルコード
https://github.com/electronicsguy/ESP8266/tree/master/HTTPSRedirect
のGooggleDocs.inoの改変です(サンプルがとても複雑なので)

###コードに残っている課題
- HTTPSRedirectのオブジェクト確保まわり
- メモリを食いつぶしてwdtで落ちる問題とか
- たまに送信できなくなる

```cpp:GAStest.ino
#include <ESP8266WiFi.h>
#include "HTTPSRedirect.h"
#include "DebugMacros.h"

// Fill ssid and password with your network credentials
const char* ssid = "*****";
const char* password = "*****";

const char* host = "script.google.com";
// Replace with your own script id to make server side changes
const char *GScriptId = "XXXXXXXXXXXX";

String url = String("/macros/s/") + GScriptId + "/exec";

const int httpsPort = 443;

// echo | openssl s_client -connect script.google.com:443 |& openssl x509 -fingerprint -noout
const char* fingerprint = "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX";

HTTPSRedirect* client = nullptr;
bool bClient = false;
const int MAX_CONNECT = 20;
int error_count = 0;
int connect_count = 0;

bool bSend;

//Ticker timer lib
Ticker ticker;
static const int TIMER_DELAY = 30 * 1000;

void setup() {
  //start serial
  Serial.begin(115200); //for debug
  Serial.println("");
  Serial.flush();

  wifiMulti.addAP("****", "*****");

  //start timer
  ticker.attach_ms(TIMER_DELAY, onTimer);
  Serial.println("setup end");
}

void loop() { //nothing again...
  static wl_status_t prevWifiStatus = WL_IDLE_STATUS;
  wl_status_t wifiStatus = wifiMulti.run();
  if ( prevWifiStatus != wifiStatus ) {
    prevWifiStatus = wifiStatus;
    if ( wifiStatus == WL_CONNECTED) {
      Serial.println("WiFi connected");
      Serial.print("SSID: ");
      Serial.println(WiFi.SSID());
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
    }
    else {
      Serial.print("WiFi error: ");
      switch(wifiStatus){
        case WL_NO_SHIELD:
          Serial.println("WL_NO_SHIELD");
          break;
        case WL_IDLE_STATUS:
          Serial.println("WL_IDLE_STATUS");
          break;
        case WL_NO_SSID_AVAIL:
          Serial.println("WL_NO_SSID_AVAIL");
          break;
        case WL_SCAN_COMPLETED:
          Serial.println("WL_SCAN_COMPLETED");
          break;
        case WL_CONNECT_FAILED:
          Serial.println("WL_CONNECT_FAILED");
          break;
        case WL_CONNECTION_LOST:
          Serial.println("WL_CONNECTION_LOST");
          break;
        case WL_DISCONNECTED:
          Serial.println("WL_DISCONNECTED");
          break;
      }

    //wifiがつながってなかったらloopを抜けて送信をしない
    return;
    }
  }
  
  if (!bSend) return;
  Serial.println("Send Start.");

  if (!bClient) {
    client = new HTTPSRedirect(httpsPort);
    bClient = true;
    client->setPrintResponseBody(true);
    client->setContentTypeHeader("application/json");
  }

  if (client != nullptr) {
    if (!client->connected()) {
      client->connect(host, httpsPort);
    }
  }
  else {
    Serial.println("Error creating client object!");
    error_count = 5;
    return;
  }

  String payload = "{ \"value1\" : \"12345\" , \"humidity\" : \"67890\"}";
  if (client->POST(url, host, payload, false)) {
    Serial.println("loop POST");
    connect_count++;
  }
  else {
    ++error_count;
    Serial.print("Error-count while connecting: ");
    Serial.println(error_count);
  }

  if (connect_count > MAX_CONNECT) {
    //error_count = 5;
    connect_count = 0;
    bClient = false;
    delete client;
    return;
  }

  if (error_count > 3) {
    Serial.println("Halting processor...");
    delete client;
    client = nullptr;
    Serial.flush();
    ESP.deepSleep(0);
  }

  bSend = false;
  Serial.println("Send Finished.");
}

void onTimer() {
  Serial.println("plz Send.");
  bSend = true;
}
```