この記事は、大阪工業大学 Advent Calendar 2019の19日目の記事です。
##:自己紹介
自分は最近やっとアウトプットをしようかなと思い始めた大学2回生です。twitterやってます。最近セキュリティ系の勉強を始めたばかりの初心者です。まぁ今回はセキュリティは全く関係ないのですが... なので、いろいろ表現に誤りがあるとは思いますがそこは何卒ご容赦を。
##:はじめに
自分は自宅でブルーベリーを栽培してるのですが、それをIoTにしようと思い始めました。まったくの初心者だったため何もかもググりまくって完成させました。その時自分が欲しい情報が少なかった(※見つけられなかった)ので少しでもこれから始める人が躓かないように記事にすることにしました。
##:材料
・ESP32-DevKitC ESP-WROOM-32開発ボード
・Arduino用 土壌湿度センサー Soil Moisture Sensor
・ブレッドボードなど小物
・arduino IDE
##:ESP32でアナログセンサーから読みよとる
最初にesp32は俗にいうarduino系のため開発もほとんどarduinoと同じようにできる。しかし一部ESP32特有のものがあるので気を付けなければならない。今回のアナログセンサーからの読み取りではarduinoと同じコードで読みとれるが、デジタルセンサーではarduinoと同じではできないので気を付けないといけない。
void setup(){
Serial.begin(115200);
while(!Serial);
}
void loop(){
Serial.println("sensor value");
int value = analogRead(35);
Serial.println(value);
delay(1000);
}
まぁ見てわかる通りloop関数内の"analogRead()"で読みとることができる。そして初心者の僕が詰まったのは**どのpinにさせばいいんだ!**コード内にさらっと出てきた"35"ってだれだよ!ググれば出てくるんだがマイコンにはもちろん各pinに役割があってその中に"ADC"というのがあって、それに対応してるpinに刺せばいいらしい。詳しくはここのサイトを見ればESP32の仕様が大体わかるはずです。
##:ESP32をWiFiにつなげる
先ほどセンサーからデータを読みとれたので次はデータを投げるためにまず、ネットにつなげる必要があります。そのために必要な”WiFi.h"をインクルードする必要があります。WiFiにつなげるだけならとても簡単な以下のコードでできます。
#include <WiFi.h>
const char* ssid = "ssid";
const char* password = "password";
void setup() {
Serial.begin(115200);
while(!Serial);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED){
delay(500); //再接続待機
Serial.print(".");
}
Serial.println("WiFi connected");
Serial.print("IP address");
Serial.println(WiFi.localIP());
}
void loop() {
}
なんとWiFiにつなげるの"WiFi.begin(ssid, password)のたった一行で済みます。"WiFi.status()で接続状態を取得することができます
##:実際にFirebase RealtimeDatabaseに投げる
Firebase RealtimeDatabaseを作成するところはほかのもっとわかりやすく書いてある記事があると思うのでそれを参照してください。
DBを作ったうえで今回必要な値は
・DBにアクセスするためのパス
・BDにアクセスするための認証キー(作った人だけ)
の二つだけです。
ここで覚えておきたいのはFirebase RealtimeDatabaseはDatabaseというよりディクショナリに近いイメージを持っておいてください
そこを踏まえたうえで、自分はブルーベリーの湿度管理なので時間を名前にしますが、そこはご自由に
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <String.h>
#define JST 3600* 9
WiFiClientSecure client;
const char* ssid ="ssid name";
const char* password = "ssid password";
const char* server = "DB host";
const char* firebase_auth = "DB secret key"; // firebase's secret
String user_path = "table directory";
uint32_t WebGet_LastTime = 0;
struct tm timeInfo;
String year;
String mon;
String day;
void setup(){
Serial.begin(115200);
while (!Serial);
WiFi_conect();
configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp"); //set NTP
WebGet_LastTime = 200000;
}
void loop(){
String now_time = create_time(); //get time
int value = analogRead(35); //get humidity
SendPatchRequest( now_time, String( value ) ); //send the data
delay(28000); //every 30 seconds
}
//WiFiconnect function
void WiFi_conect(){
WiFi.begin(ssid, password); //connect to WiFi
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(1000); // reconect 1 sencond later
}
Serial.println("WiFi connected");
Serial.println("IP address");
Serial.println(WiFi.localIP()); //esp32's localIPaddress
}
//get time
void create_time(){
String now_time;
getLocalTime(&timeInfo); //get time
year = String(timeInfo.tm_year + 1900); //convert to A.D.
mon = String(timeInfo.tm_mon + 1); //convert to real month
day = String(timeInfo.tm_mday);
if(int(timeInfo.tm_hour) / 10 == 0){ //add "0" when the hour is single digit
String now_hour = "0" + String(timeInfo.tm_hour);
now_time += now_hour;
}else{
String now_hour = String(timeInfo.tm_hour);
now_time += now_hour;
}
if(int(timeInfo.tm_min) / 10 == 0){ //add "0" when the min is single digit
String now_min = "0" + String(timeInfo.tm_min);
now_time += now_min;
}else{
String now_min = String(timeInfo.tm_min);
now_time += now_min;
}
if(int(timeInfo.tm_sec) / 10 == 0){ //add "0" when the sec is single digit
String now_sec= "0" + String(timeInfo.tm_sec);
now_time += now_sec;
}else{
String now_sec = String(timeInfo.tm_sec);
now_time += now_sec;
}
return now_time;
}
//write firebase DB
void SendPatchRequest(String str1, String str2){
if (!client.connect(server, 443)){ //connect DB
Serial.println("Connection Failed!");
}else{
Serial.println("Connected to sucsess!");
String URL;
URL = "PATCH /"; //http request method
URL += user_path + "/" + year + "/" + mon + "/" + day;
URL += ".json?auth=";
URL += String(firebase_auth);
URL += " HTTP/1.1\r\n";
String body = "{\"" + str1 + "\":\"" + str2 + "\"}";
String head;
head = "Host: ";
head += String(server) + "\r\n";
head += "Connection: keep-alive\r\n";
head += "Content-Length: ";
head += String(body.length()) + "\r\n";
head += "\r\n";
client.print(URL);
client.print(head);
client.print(body);
Serial.println("####### Send HTTP Patch Request #######");
Serial.print(URL);
Serial.print(head);
Serial.print(body);
checkServerRespons();
delay(2);
client.stop();
delay(2);
}
}
//receive response
void checkServerRespons(){
Serial.println("####### Firebase server HTTP Response #######");
while(client.connected()){
String res = client.readStringUntil('\n');
Serial.println(res);
if(res == "\r"){
Serial.println("-------------- Response Headers Recived");
break;
}
}
while(client.available()){
char c = client.read();
Serial.print(c);
}
Serial.println("\r\n------------ Body Received");
}
サーバーにつなげるのに"WiFiClientSecure.h"をインクルードする必要があります。
自分が一番躓いたのがhttp requestを作るとこでした。""で囲った部分が可変です。
・中身
"method" "URL" HTTP/1.1\r\n
HOST: "distination server" Content-Length "body.length" \r\n
"body"
データをDBに投げるだけでいいのでHTTP request のメソッドはPATCHで十分です。
・GET ほしいものを取得
・POST データの挿入
・PATCH データの更新
URLはDBまでのpathを入れれば大丈夫です。
HOSTは今回はFirebaseのserverを入れてやります。
今回はレスポンスを考慮してないので特に書いてないのですが、処理があるならcheckServerRespons()に処理を書く必要があります。
##:おわりに
継ぎ接ぎだらけのコードなのでググったらもっといいコードが出てくると思うのでこの記事ではこんな感じのコードを書けばいいんだなと思っていただけたら嬉しいです。今回はアナログセンサーを用いましたが、デジタルセンサー(bme280)でデータを読むとこまでですがコードもありますので参考までにどうぞ