Firebase Realtime Database のデータ保存、取得、ストリーミング受信実験( ESP32 , M5Stack )

記事公開日:2018年9月1日


スポンサーリンク

10.Firebase Realtime database への書き込みリクエスト

では、今度は Firebase Realtime database へ書き込む場合の HTTP リクエストを考えます。

これも Firebase Realtime database ドキュメントの REST によるデータ保存項目に書かれています。

https://firebase.google.com/docs/database/rest/save-data?hl=ja

これによると、サーバーのデータ書き換えコマンドのうち、以下が使用できるとのこと。

● PUT
● PATCH
● POST
● DELETE

PUT を使ってしまうと、データベース内の子ノードまで全て書き換えてしまう可能性があるので、ここでは PATCH を使います。

これらは、先ほど実験したようなブラウザの URL 入力欄では使えないコマンドですが、HTMLファイルや ESP32 等の Wi-Fiマイコンならばリクエストできます。

例として、PATCH の最低限の HTTP リクエストは以下のようになります。

PATCH /test_user1.json?auth=xxxxxxxxxxxxxxxx HTTP/1.1
Host: testproject01-439eb.firebaseio.com
Connection: keep-alive
Content-Length: 12
(空行)
{"text":"0"}

空行の後の {“text”:”0″} が body 部分で、これによって、text 内のデータが “0” に書き替えられます。
これは JSON形式というもので送ります。

これを PUTコマンドにしてしまうと、他のノードも消されて、text だけになってしまうので、やはりここでは PATCH が良いということになります。

重要なのは、Content-Length で、body部分の文字数を Firebase サーバーへしっかり伝えなければならないことです。

このリクエストを送って、Firebase の Autentication ( auth 認証 )が通れば、HTTP レスポンスが返ってきます。

HTTP/1.1 200 OK
Server: nginx
Date: Sat, 01 Sep 2018 04:50:11 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 12
Connection: keep-alive
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Strict-Transport-Security: max-age=31556926; includeSubDomains; preload

{"text":"0"} 

Firebaseサーバーから返ってきたレスポンス文字列を全て一字も逃さず受信し切ることが重要です。
もし、取りこぼしてしまったら、それが溜まりに溜まって、連続書き込みが不安定になると思われます。

これさえ分かれば、ESP32 で Firebase へデータを書き込むことが可能です。

11.ESP32 および M5Stack で Firebase Realtime database に書き込む

では、前項を踏まえて、ESP32 および M5Stack を使って、Firebase の Realtime database へ書き込んでみたいと思います。

スケッチ(ソースコード)入力

Arduino IDE に以下のスケッチを入力してみてください。
Firebase Realtime database の test_user1 内の text フィールドデータ書き込みです。

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

const char* ssid = "xxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください
const char* password = "xxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください

const char* host = "xxxxxx-xxxx.firebaseio.com";
const char* firebase_auth = "xxxxxxxxxxxxxxxxxxxxxxxxx"; //database secrets

String user_path = "test_user1";
int count = 0;

WiFiClientSecure client;

//*****セットアップ******************
void setup() {
  Serial.begin(115200);

  WiFi.begin( ssid, password );

  Serial.print( "connecting" );
  while ( WiFi.status() != WL_CONNECTED ) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print( "connected: " );
  Serial.println( WiFi.localIP() );

  delay(2000);
}
//****メインループ********************
void loop() {
  sendPatchRequest( "text", String( count ) );
  count++;
  if( count > 10 ) count = 0;
}
//******Firebase Realtime Database 書き替え**************
void sendPatchRequest( String str1, String str2 ){
  if( !client.connect( host, 443 ) ){
    Serial.println( "Connection failed!" );
  }else{
    Serial.println( "Connected to server!" );
    String req_url_str;
    req_url_str = "PATCH /";
    req_url_str += user_path;
    req_url_str += ".json?auth=";
    req_url_str += String( firebase_auth );
    req_url_str += " HTTP/1.1\r\n";

    String body = "{\"" + str1 + "\":\"" + str2 + "\"}";

    String head;
    head = "Host: ";
    head += String( host ) + "\r\n";
    head += "Connection: keep-alive\r\n";
    head += "Content-Length: ";
    head += String( body.length() ) + "\r\n";
    head += "\r\n"; //空行

    client.print( req_url_str );
    client.print( head );
    client.print( body );

    Serial.println( "####### Send HTTP Patch Request #######" );
    Serial.print( req_url_str );
    Serial.print( head );
    Serial.println( body );

    checkServerRespons();

    delay(2);
    client.stop(); //これ絶対必要
    delay(2);
  }
}
//***********************************
void checkServerRespons(){
  //Firebaseサーバーからのレスポンスを全て受信し切ることが重要
  Serial.println("####### Firebase server HTTP Response #######");
  while( client.connected() ){
    String resp_str = client.readStringUntil('\n'); //ラインフィードまで文字列を読み込む
    Serial.println( resp_str );
    if( resp_str == "\r" ) { //空行検知
      Serial.println("------------- Response Headers Received");
      break;
    }
  }
  //レスポンスヘッダが返ってきた後、body部分が返って来る
  while( client.available() ){
    char c = client.read();
    Serial.print( c );
  }
  Serial.println("\r\n----------- Body Received");
}

【ザッと解説】

4-5行目で、ご自分の Wi-FiルーターのSSID とパスワードに書き換えてください。

7-10行目はご自分の Firebaseプロジェクトに合わせて書き換えてください。
7行目の host は下図の様に Firebase console 画面を開き、Realtime database 画面のアドレスをコピペしてください。

firebase_realtime_database40.jpg


auth は先ほど述べた Database secrets ( データベースシークレット )です。

重要なのは、61-63行目で Firebase サーバーへ PATCH リクエストを送ったら、70行目の関数で、Firebaseサーバーからのレスポンスを待ちます。
これは、78-95行の関数です。
一文字も漏らさずに文字列を受け取らねばなりません。

82行目の readStringUntil( ‘\n’ ) では、’\n’ というラインフィード制御文字まで検索して、resp_strに格納されます。
ですが、’\n’ は捨てられます。
ですから、空行の場合は必然と ‘\r’ だけ格納されますので、84行のように空行を検出できます。
空行を検出したら、後は body 部分が返って来るので、それを漏らさず受信します。

このように、サーバーからのレスポンス文字列を一文字も漏らさず受信することが、安定してサーバーと通信するためには重要だと個人的に思っています。

そして、Firebase からのレスポンス文字列を全て受信し切ったら、73行目にあるように
client.stop();
を入れなければなりません。
前後にdelay(2) がありますが、おまじないと思って下さい。

client.stop();しないと、なぜかデータの連続書き込みできませんでした。

以上までが終われば、新たに PATCH リクエストを送信できます。
これが終わる前に、次の PATCH リクエストを送ってしまうと、サーバーとの通信が不安定になると思われます。

コンパイル書き込み実行

では、Arduino IDE でコンパイル書き込み実行してみてください。

シリアルモニタを 115200bps で起動してみると、下図の様に表示されると思います。

firebase_realtime_database38.jpg


Firebaseサーバーに連続書き込みしているので、Firebase console の Realtime database 画面を見てみると、下図の様に常に黄色の枠で囲われていると思います。
これは、データ書き込みが正常完了しているという証拠です。

firebase_realtime_database39.jpg


このプログラムでは見てお分かりの通り、Firebaseサーバーからのレスポンス受信が終わってから次の PATCH 書き込みリクエストを送っています。
私の環境の場合、1.5秒ほどのサイクルで次のデータ書き込みしていました。
SSL通信で暗号化しているので、HTTP 通信ではこれが限界だと思います。

動画ではこんな感じになります。

「動画」

でも、高速連続通信を望まないのであれば、これで必要充分だと思います。
特に、センサデータを書き込む場合は、最速で3秒もあれば十分ではないかと思います。

ラジコンのように高速リアルタイム性を求めるのであれば、Blynk とか WebSocket を使えば良いと思います。

えらい記事が長くなったので、スマホ側のプログラミングは次回の記事にしたいと思います。

編集後記

いかがでしょうか。

今回は、Firebase Realtime database との HTTP 通信のみによるデータの読み込み、および書き込みの方法を紹介してみました。

これさえできれば、アマチュアプログラマや電子工作家には十分すぎるくらい色々な IoT 機器開発ができそうですね。
しかもインターネットさえつながる環境があれば、地球の裏側でも通信可能です。

Firebase はあまりにも素晴らしいクラウドサービスです。
これからは Firebase の時代だと個人的に思っています。
ヤバイ電子工作や IoT がどんどんできそうですね。

今回は、Wi-Fi マイコンモジュールの ESP32 および M5Stack 側のプログラミングでしたが、次回はスマホ側の HTML および JavaScript プログラミングも紹介したいと思います。
それで、スマホと双方向通信が可能になります。
アプリを開発しなくても、ブラウザがあれば通信可能です。
アマチュアにとっては画期的なことだと思います。
なんかワクワクしますね。

ということで、今回はここまでです。

ところで、このブログの維持運営が厳しい状態は変わりありませんので、引き続きご支援いただけると助かります。
宜しくお願いいたします。

支援方法はこちらの記事をご覧ください。
m(_ _)m


スポンサーリンク


 

mgo-tec電子工作ブログ管理人おすすめ
Amazon.co.jp
M5Stack Basic
スイッチサイエンス
Amazon.co.jp
ESPr Developer 32
スイッチサイエンス(Switch Science)
Amazon.co.jp
Amazon.co.jp

「Firebase Realtime Database のデータ保存、取得、ストリーミング受信実験( ESP32 , M5Stack )」への6件のフィードバック

  1. 突然のコメントにて失礼します。
    Firebase Realtime Databaseの内容でもないので申し訳ないのですが、
    ESP32でキーマトリクス回路を作りたいと思い、
    ESP32DEVkitCに74HC138のデマルチプレクサをつないで
    作ったのですが、ちとうまくいかないところがありまして、
    もしESP32でデマルチプレクサかマルチプレクサを使われた経験が
    ございましたら、伺いたいことがございます。
    いかがでしょうか。
    ※Arduinoではうまくいったキーマトリクス回路を
    そのままESP32に流用しようとしています。

    1. Yosukeさん

      コメント投稿ありがとうございます。
      ただ、残念ながら、デマルチプレクサとかマルチプレクサは使ったことがありません。
      それとはかなり異なるかも知れませんが、ESP8266でSPI通信で、I/Oエキスパンダを使って、GPIOを増設したことがあります。
      以下の記事です。
      ESP-WROOM-02 ( ESP8266 )の GPIO を増設して キャラクタディスプレイ 8bitモード制御してみた
      GPIO 増設した ESP-WROOM-02 ( ESP8266 )で、8bitモードのグラフィックディスプレイ を制御してみた
      視認性クッキリ! 大き目ドットの OLED WS0010 で日本語漢字フォント電光掲示板風スクロール&スマホ操作
      大き目ドットの有機EL, WS0010 で Yahoo ニュース リアルタイム 電光掲示板を作ってみた

      とりあえず、この程度です。
      お役に立てず、すみません。
      m(_ _)m

  2. mgo-tecさん

    ご返信いただきまして誠ありがとうございます。
    リンクして頂いたページを中心に、
    SPI通信を勉強してみようと思います。

    すみませんが、もう1点伺いたいことがございます。
    Arduino(ATmega328p)に74HC138をつなげば、
    キーマトリクス回路を正常に組めています。
    そこで、ESP32と74HC138の間にATmega328pを噛ませて、
    ESP32 – ATmega328p – 74HC138 – ボタン達のようにつなぎ、
    ESP32とATmega328pで何らかの通信をする方法を
    素人考えで考えてみたのですが、
    SPI通信などを利用して、ATmega328pからESP32に値を送る
    ようなことは可能でしょうか?

    お忙しいところ恐れ入りますが、何かご存知でしたら、
    ご返信いただければ助かります。

    1. Yosukeさん

      多分、ESP32 と Arduino で SPI データ通信は可能だと思いますが、そのデバイス間ではシリアル通信しかやったことがありませんので、アドバイスできません。
      私が個人的に思うには、Arduino を噛ませないでも、ESP32 だけで十分可能ではないかと思います。
      ESP32 のクロックの方が高速ですし・・・。
      ということで、あまりお役に立てず、申し訳ございません。

  3. mgo-tecさん

    ご返信頂きましてありがとうございます。
    EPS32にマルチプレクサやデマルチプレクサをつなぎますと、
    押していないボタンが押されたような反応になってしまう等、
    笑ってしまうほど意図しない挙動になってしまい、
    Arduinoで出来ていたことがこんなにできないのかと困っておりました。
    ただ、やはり安価にBluetooth通信が出来るの機能は捨てがたいので、
    アドバイス頂いた通り、EPS32のみでもう少し試行錯誤を
    続けてみようと思います。

    EPS32ユーザーの方とお話できただけでも嬉しかったです。
    本当にありがとうございました。

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください