とある実験のーと

趣味ブログ

ESP32でオンラインデータロガーを作る ③IFTTTに向けてデータをPUSH

Google spreadsheetへの投稿にむけて

f:id:azospiran:20190804042057p:plain 今回はESP32からIFTTTへの投稿です。
やることは以下の通り。説明のときに 一つずつの要素コードを抜き出しますが、最後に全文を張っておきます。

  • ESP32をwifiに繋ぐ
  • Thermistorの電圧を取り温度に変換
  • 一定時間でIFTTTのイベントを呼ぶ  

また、ここで工夫している点は以下になります。

  • IFTTTのイベントを単に呼び出すだけじゃなく、データを載せて送る。
  • delay()を使わないようにして並列処理が可能になっている。
  • 平均値を出すことでノイズを抑える。

WSP32をwifiに繋ぐ

基本的に前回も紹介したここのサイトを参考にしながら進めた。また、ESP32のライブラリをインストールした後ならお試しコードが事前に用意されているのでそれでもいい。 ssidとパスワードを自分の環境に合わせて入力する。また、iftttのイベントを呼ぶためのkeyと、event名を書き換える。
void setup()でwifiへの接続を確立する。ちなみにSerial通信は115200 bpsを指定しているのでArduinoで良く使ってる9600じゃない。ここら辺のコードは先人のコピペです。

#include <WiFi.h>
const char* ssid = "************"; //wifiアクセスポイント
const char* password = "*********"; //wifiパスワード
const char* host = "maker.ifttt.com";
const int httpsPort = 443;
const char* key = "bospC***************"; //iftttのkey
const char* event="esp32_post";//iftttのイベント名

void setup() {
//netwerk settings, wifiへの接続
  Serial.begin(115200);
  Serial.println();
  Serial.print("connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(5000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  WiFiClientSecure client;
  Serial.print("connecting to ");
  Serial.println(host);
  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }

Thermistorの電圧を取り温度に変換、millis()で投稿時間を制御

wifiに接続した後にTHermistorの電圧値をとって温度に変換する。 この処理はvoid roop()の中行う。関連するコードは以下の通り、GPIO34にThermistorを繋ぎ、電圧値を取得した。以前書いたのと同じ。 azospiran.hatenablog.jp

ここで、投稿するまでの待ち時間をdelay()で指定する例が多かったのだが、delayは処理全て止めてしまうので、その待ち時間の間に他のコードを走らせたいなど汎用性が下がってしまうことが分かった。なので、プログラムが開始時間を0としてカウントしているmillis()という関数を採用して、一定時間毎に色んな処理を並列して進められるようにした。並列処理したいコードの数だけtimerを準備すれば良いと思う。参考にしたサイト。 Arduino - delayを無くしたい|teratail

今回はSerial通信にリアルタイムの値を5秒毎に表示し(timer1)、IFTTTに5分ごとに投げるため(timer)に2つのタイマーを用意した。そしてif文でmillisを監視し、前回の投稿時間から指定秒だけ経つのを待つ。

試行錯誤の結果、IFTTTの時間あたりのイベント呼び出し回数に制限があることが分かった。1分毎だとダメで5分ごとだと引っかからないみたい。

delay()を使ってないので、投稿までのThermistorの値を積算してcounterの値で割ることで平均値を出すことにした。5分間で何千回とloopが回ってるはずなので、これでノイズの影響を大分軽減できるはず。

関連するコードを 一応抜き出した。

#include <math.h> //数値計算する際に必要らしい

//msでプログラムが始まったところからカウント, delayを使わない場合
long dt=300000;//何秒間隔でiftttに投稿するか[ms]
unsigned long timer = millis();
//serialに投稿する間隔の時につかうtimer1 [ms]
unsigned long timer1=millis();


//void setup()内
//input thermistor thermistorの入力
  pinMode(34, INPUT);  

//void roop()内
//iftttに投稿するまでシグナル値平均値を計算させる
thermVal+=analogRead(34);
counter ++;

//シリアルモニタへの現在地の出力 (5秒毎)
if (millis()>timer1+5000){
timer1=millis();
Vout=analogRead(34)*3.3/4095;
R1=(3.3/Vout-1)*R0;
  double R=R1/R25;
  T=1/(1/B*log(R)+1/298.15);
  T=T-273.15;
  Serial.print("Current T: ");
  Serial.println(T);
}

//iftttに投稿, dt時間毎
if (millis()>timer+dt){
  timer=millis();

//read thermistor value
thermValav=thermVal/counter; //平均値を計算
Vout=thermValav*3.3/4095; //Thermistorの電圧値を計算
R1=(3.3/Vout-1)*R0;
  double R=R1/R25;
  T=1/(1/B*log(R)+1/298.15);
  T=T-273.15;

一定時間でIFTTTのイベントを呼ぶ 

先ほどの2つめのif文の中の続きで、計算した値 (今回は電圧値、抵抗値、温度)をIFTTTに送る為に整えていく。 IFTTTはkeyとイベント名が載ったurlを実行することでイベントを発生させられるが、そのままではデータを載せられず、トリガーが実行されるだけである。値も一緒にポストするにはJSON方式で記述しないといけないらしいが、標準ESP32の標準のhttpライブラリはjsonに対応していない。
以下のサイトが大変参考になりました。ありがとうございます。

https://shiiiiiiiii1.tumblr.com/post/178921930788/esp32%E3%81%8B%E3%82%89ifttt%E3%81%AEwebhooks%E3%81%AEapi%E3%82%92%E5%8F%A9%E3%81%84%E3%81%A6%E3%81%BF%E3%82%8B
shiiiiiiiii1.tumblr.com JSONはHTTPCliant.hライブラリの機能を使わないといけないらしいので、他の事例と少し書式が違っている。
このif文の最後でシグナル値の積算とcounterを初期化して次の測定に備えてる。

//loop()内
//ifttへのイベントに一緒に送る値
String val1=String(Vout,5);
String val2=String(R1,5);
String val3=String(T,5);

// iftttのURLを作成 (HTTPClient.hの機能を使う)
// maker.ifttt.com/trigger/{event}/with/key/{key}
String url = "http://maker.ifttt.com/trigger/";
url += event;
url += "/with/key/";
url += key;
String json;
  
// JSON データの作成
json = "{ \"value1\" : " + val1 + ", \"value2\" : " + val2 + ", \"value3\" : " + val3 + " }";  
Serial.print(json);

 HTTPClient http;   // インスタンスの生成
 http.begin(url);   // HTTP リクエストの設定
 http.addHeader("Content-Type", "application/json");   // 送信形式を JSON データに設定
 http.POST(json);   // POST リクエストで送信
 http.end();
Serial.println("_post");

counter=0; //counterとシグナル値の合計値を初期化
thermVal=0;
}

一応、これでコードを構成している内容は網羅しているとおもう。あとLEDをGPIO4に付けてるので、IFTTTに投稿するときだけ光るようにした。

最後に

IFTTT側の制限で一見val1-3の3種類しかデータを載せられないように見えるが、データをカンマ区切りの文字列などにして乗せれば無限にデータを送れると思う。その後の処理はGoogle spreadsheet側の仕事だと思う。 次回はSpreadsheet側での処理が少し必要だったのでそれについて書きます。

以下、コード全文。

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

const char* ssid = "************"; //wifiアクセスポイント
const char* password = "*********"; //wifiパスワード
const char* host = "maker.ifttt.com";
const int httpsPort = 443;
const char* key = "bospC***************"; //iftttのkey
const char* event="esp32_post";//iftttのイベント名

//Thermistor
double Vout;//出力電圧
double R1;//サーミスタ抵抗値
double B=4000.00; //補正係数
double T; //サーミスタ温度
double R0=10.000; //参照抵抗kΩ
double R25=10.000; //25°Cの時の抵抗
double thermVal=0;
double thermValav;
int counter=0;

//msでプログラムが始まったところからカウント, delayを使わない場合
long dt=300000;//何秒間隔で投稿するか[ms]
unsigned long timer = millis();
//serialに投稿する間隔 [ms]
unsigned long timer1=millis();

void setup() {
//netwerk settings, wifiへの接続
  Serial.begin(115200);
  Serial.println();
  Serial.print("connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(5000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  WiFiClientSecure client;
  Serial.print("connecting to ");
  Serial.println(host);
  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }
//output settings LEDへの出力
  pinMode(4, OUTPUT);

//input thermistor thermistorの入力
  pinMode(34, INPUT);  
}

void loop(){
//iftttに投稿するまでシグナル値平均値を計算させる
thermVal+=analogRead(34);
counter ++;

//シリアルモニタへの現在地の出力 (5秒毎)
if (millis()>timer1+5000){
timer1=millis();
Vout=analogRead(34)*3.3/4095;
R1=(3.3/Vout-1)*R0;
  double R=R1/R25;
  T=1/(1/B*log(R)+1/298.15);
  T=T-273.15;
  Serial.print("Current T: ");
  Serial.println(T);
}

//iftttに投稿, dt時間毎
if (millis()>timer+dt){
  timer=millis();
//LEDの点灯
digitalWrite(4, HIGH);
//read thermistor value
thermValav=thermVal/counter; //平均値を計算
Vout=thermValav*3.3/4095;
R1=(3.3/Vout-1)*R0;
  double R=R1/R25;
  T=1/(1/B*log(R)+1/298.15);
  T=T-273.15;

//ifttへのイベントに一緒に送る値
String val1=String(Vout,5);
String val2=String(R1,5);
String val3=String(T,5);

// iftttのURLを作成 (HTTPClient.hの機能を使う)
// maker.ifttt.com/trigger/{event}/with/key/{key}
String url = "http://maker.ifttt.com/trigger/";
url += event;
url += "/with/key/";
url += key;
String json;
  
// JSON データの作成
json = "{ \"value1\" : " + val1 + ", \"value2\" : " + val2 + ", \"value3\" : " + val3 + " }";  
Serial.print(json);

 HTTPClient http;   // インスタンスの生成
 http.begin(url);   // HTTP リクエストの設定
 http.addHeader("Content-Type", "application/json");   // 送信形式を JSON データに設定
 http.POST(json);   // POST リクエストで送信
 http.end();
Serial.println("_post");

//LED消す
digitalWrite(4, LOW);
counter=0; //counterとシグナル値の合計値を初期化
thermVal=0;
}
}

これまでの内容はこちら

azospiran.hatenablog.jp azospiran.hatenablog.jp azospiran.hatenablog.jp