2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

電子工作初心者がESP32からFirebase RealtimeDatabaseにデータを投げるまで

この記事は、大阪工業大学 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と同じではできないので気を付けないといけない。

analogsensor.ino
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につなげるだけならとても簡単な以下のコードでできます。

WiFiconnect.ino
#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というよりディクショナリに近いイメージを持っておいてください
そこを踏まえたうえで、自分はブルーベリーの湿度管理なので時間を名前にしますが、そこはご自由に

esp32_FBDB.ino
#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)でデータを読むとこまでですがコードもありますので参考までにどうぞ

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
2
Help us understand the problem. What are the problem?