8.Firebase Realtime Database から EventSource / Server-Sent Eventsによるストリーミングデータ取得
さて、Firebase Realtime Database の最もスゴイところは、データをストリーミング配信してくれることです。
そして、データベース内のデータが更新されたデータだけを抽出して送信してくれるところです。
これは、メチャメチャ便利な機能です。
実は、Firebase Realtime Database には、ストリーミング受信の EventSource / Server-Sent Events がサポートされています。
そのドキュメントはRealtime Database の REST 使用の以下のリンクに書いてあります。
https://firebase.google.com/docs/database/rest/retrieve-data?hl=ja
この、EventSource / Server-Sent Events というのは、私も以前、Arduino や ESP-WROOM-02 ( ESP8266 ) を使って以下の複数の記事で試したことがあります。
https://www.mgo-tec.com/tag/server-sent-events
この当時はホントに感動しました。
マイコンからのデータがストリーミングでスマホへ配信してくれるんです。
ただし、EventSource / Server-Sent Events は、一方向のみです。
今回は、Firebase サーバーからの受信方向のみとなります。
そして、EventSource / Server-Sent Events の素晴らしい機能がもう一つあります。
ブラウザを閉じても、再度起動すると自動的に再接続してストリーミング受信をしてくれるというところです。
これは便利ですね。
この EventSource / Server-Sent Events はとっても簡単な命令で実現可能なのです。
HTTP GET リクエストを送る時、HTTP ヘッダに以下を加えるだけです。
Accept: text/event-stream
それを加えた、Firebase Realtime database への最低限の HTTP GET リクエストは以下のようになります。
auth や Host 、そしてtest_user1 は各個人のプロジェクトによって異なります。
GET /test_user1.json?auth=xxxxxxxxxxxxxxxxx HTTP/1.1 Host: testproject01-439eb.firebaseio.com Accept: text/event-stream Connection: close (空行)
これだけで、auth認証が通ってしまうのは、Twitter API より遙かに簡単ですね。
最後の空行は必ず必要です。
空行と言っても、”¥r¥n”というコードを送ります。
“¥r” は ASCII コードが 0xAh のラインフィードです。
ラインフィードは次の行へ移動という制御です。
“¥n”は ASCII コードで 0xDh でキャリッジリターンというもので、行の先頭へ移動という制御です。
要するに、”¥r¥n” はEnterキーを押したようなものです。
この GET リクエストを M5Stack や ESP32 などのマイコンで送り、Firebase サーバーからのデータを常時待ち受けていれば、マイコンでストリーミング受信可能です。
では、次は実際に M5Stack や ESP32 で Firebase からのストリーミングデータを受信してみます。
9.ESP32 および M5Stack による、Firebase Realtime database からのストリーミングデータ取得
では、Wi-Fi & Bluetooth マイコン ESP32 を搭載したモジュールを使って、Firebase Realtime database からのデータをストリーミング受信してみます。
ESP32 モジュールの準備
まず、手元にWi-Fi マイコン、ESP-WROOM-32 ( ESP32 )開発ボードまたは M5Stack を用意します。
以下のようにいろいろな種類の ESP32 マイコンボードがありますが、どれでも良いです。
● M5Stack
(追記)
M5Stack Basicは、この記事を書いた当時より格段にバージョンアップしております。
以下のスイッチサイエンスさんの公式サイトをご参照ください。
https://www.switch-science.com/collections/%E5%85%A8%E5%95%86%E5%93%81/products/9010
これについては、以下の記事も参照してください。
M5Stack ( ESP32 搭載 ) を分解したり電源を入れてみて、いろいろ思ったこと
● ESP-WROOM-32 ( ESP32 )開発ボード
ESPr Developer 32 については、以下の記事を参照してください。
ESPr Developer 32 ( スイッチサイエンス製 ) を使ってみました
Arduino – ESP32 のインストール
Arduino IDE で開発します。
Arduino IDE は ver 1.8.6 で動作確認しています。
Arduino core for the ESP32 ( 以下 Arduino – ESP32 )は Stable 版のver 1.0.0 で動作確認しています。
Arduino – ESP32 のインストール方法は以下の記事を参照してください。
Arduino core for the ESP32 のインストール方法
Wi-Fi環境の確認
Wi-Fiルーターを起動しておき、事前に ESP32 ( ESP-WROOM-32 )がインターネットに接続できるように設定しておいてください。
ファイアウォールや MACアドレスフィルタリングの設定を確認しておいてください。
ESP32 の MACアドレスを調べる方法は以下の記事を参照してください。
ESP-WROOM-32 ( ESP32 ) チップ・メモリ・MACアドレス情報取得方法
スケッチ(ソースコード)入力
では、先で述べた HTTP GET リクエストを踏まえて、以下のスケッチを入力していきます。
Arduino – ESP32 には、HTTPClient という便利なライブラリがありますが、アマチュアの私としては、逆にしくみが解りにくくなってしまうので、最低限のWiFi および WiFiClientSecure ライブラリだけ使います。
因みに、Firebase はhttps の SSL 通信です。
client.setInsecure();
を追加しました。
ESP32さん、ありがとうございました。
(2022/03/17)
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
//Arduino core for the ESP32 ver 2.0.2 #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"; WiFiClientSecure client; //*****セットアップ****************** void setup() { Serial.begin(115200); Serial.println(); delay(1000); 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); client.setInsecure(); getServerSentEvents(); } //****メインループ******************** void loop() { checkServerRespons(); } //*********************************** void getServerSentEvents(){ Serial.println( "\nStarting connection to server..." ); if( !client.connect( host, 443 ) ){ Serial.println( "Connection failed!" ); }else{ Serial.println( "Connected to server!" ); String req_url_str; req_url_str = "GET /"; req_url_str += user_path + ".json?auth="; req_url_str += String( firebase_auth ) + " HTTP/1.1\r\n"; String req_header_str; req_header_str = "Host: "; req_header_str += String( host ) + "\r\n"; req_header_str += "Accept: text/event-stream\r\n"; req_header_str += "Connection: close\r\n"; req_header_str += "\r\n"; //空行 Serial.println( "Send Server-Sent Events GET request." ); //FirebaseサーバーへGETリクエスト送信 client.print( req_url_str ); client.print( req_header_str ); Serial.print( req_url_str ); Serial.print( req_header_str ); } } //*********************************** void checkServerRespons(){ while( client.available() ){ char c = client.read(); if( c == '\r' ) Serial.print( "\\r"); //キャリッジリターン if( c == '\n' ) Serial.print( "\\n"); //ラインフィード Serial.print( c ); } }
かなり簡単なスケッチだと思います。
5-6行目で、各自の Wi-Fiルーターの SSID とパスワードに書き換えてください。
8-11行目ではご自分の Firebase プロジェクトに合わせて書き換えてください。
8行目の host は下図の様に Firebase console 画面を開き、アドレスをコピペしてください。
auth は先ほど述べた Database secrets ( データベースシークレット )です。
GET リクエストヘッダも最低限です。
コンパイル書き込み実行、およびシリアルモニタで確認
では、上記のスケッチを Arduino IDE でコンパイル書き込み実行してみてください。
そして、シリアルモニタを 115200bps で起動してください。
すると、下図の様に表示されると思います。
このように、GET リクエストデータを送ってから、Firebaseサーバーから HTTP レスポンスヘッダが返ってきます。
レスポンスヘッダを良く見ると、”¥r¥n” というキャリッジリターン(CR)とラインフィード(LF)というコードが各行にあり、レスポンスヘッダが終了する時には”¥r¥n”のみの行、つまり空行が入ります。
その後、Server-Sent Events のストリーミングデータが送られてきます。
event は、初回時は必ず PUT となっています。
初回の data は
{"path":"/","data":{"color":"red","text":"hello world"}}
という形で送られてきます。
そして、Firebase Realtime database を何も操作していない場合は、30秒毎に Firebase サーバーから
event: keep-alive\n data: null\n
というデータが送られてきます。
そして、ブラウザなどで、Firebase Realtime Database の color のところを書き換えると、
event: put\n data: {"path":"/color","data":"blue"}\n
という感じで、変更されたデータのみが、即、送られてきます。
これは便利ですね。
パソコン画面の動画ではこんな感じです。
「動画」
なかなかイイ感じですよね。
Firebase console 画面の Realtime database の値が変わっていることが分かると思います。
そして、Server-Sent Events のストリーミングデータを受信している時に”¥n”のラインフィード(LF)しか送られていないことに注目してください。
ヘッダではキャリッジリターンも送られてきましたが、ここではラインフィードのみです。
このことを利用して、ヘッダ情報とストリーミングデータ情報を振り分けるプログラミングすると良いと思います。
以上をうまく利用すると、M5Stack のディスプレイには素早いデータ表示ができると思います。
この EventSource / Server-Sent Events は、HTML上で JavaScript プログラムを書いて、ブラウザで表示させると、一度ブラウザを閉じても、次に起動したときに自動でコネクション接続して自動でストリーミング受信してくれます。
とっても便利です。
JavaScript の書き方は次回の記事で紹介したいと思います。
コメント
突然のコメントにて失礼します。
Firebase Realtime Databaseの内容でもないので申し訳ないのですが、
ESP32でキーマトリクス回路を作りたいと思い、
ESP32DEVkitCに74HC138のデマルチプレクサをつないで
作ったのですが、ちとうまくいかないところがありまして、
もしESP32でデマルチプレクサかマルチプレクサを使われた経験が
ございましたら、伺いたいことがございます。
いかがでしょうか。
※Arduinoではうまくいったキーマトリクス回路を
そのままESP32に流用しようとしています。
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
mgo-tecさん
ご返信いただきまして誠ありがとうございます。
リンクして頂いたページを中心に、
SPI通信を勉強してみようと思います。
すみませんが、もう1点伺いたいことがございます。
Arduino(ATmega328p)に74HC138をつなげば、
キーマトリクス回路を正常に組めています。
そこで、ESP32と74HC138の間にATmega328pを噛ませて、
ESP32 – ATmega328p – 74HC138 – ボタン達のようにつなぎ、
ESP32とATmega328pで何らかの通信をする方法を
素人考えで考えてみたのですが、
SPI通信などを利用して、ATmega328pからESP32に値を送る
ようなことは可能でしょうか?
お忙しいところ恐れ入りますが、何かご存知でしたら、
ご返信いただければ助かります。
Yosukeさん
多分、ESP32 と Arduino で SPI データ通信は可能だと思いますが、そのデバイス間ではシリアル通信しかやったことがありませんので、アドバイスできません。
私が個人的に思うには、Arduino を噛ませないでも、ESP32 だけで十分可能ではないかと思います。
ESP32 のクロックの方が高速ですし・・・。
ということで、あまりお役に立てず、申し訳ございません。
mgo-tecさん
ご返信頂きましてありがとうございます。
EPS32にマルチプレクサやデマルチプレクサをつなぎますと、
押していないボタンが押されたような反応になってしまう等、
笑ってしまうほど意図しない挙動になってしまい、
Arduinoで出来ていたことがこんなにできないのかと困っておりました。
ただ、やはり安価にBluetooth通信が出来るの機能は捨てがたいので、
アドバイス頂いた通り、EPS32のみでもう少し試行錯誤を
続けてみようと思います。
EPS32ユーザーの方とお話できただけでも嬉しかったです。
本当にありがとうございました。
いえいえ。
大したアドバイスしておりませんが、うまくできるといいですね。
(^^)
質問です!!
M5Stackを使っています。
Wi-Fi、サーバーとの接続共に良好ですが、「400 Bad request」が返ってきてしまいます。
これってfirebaseのデータベースのシークレットが既に廃止されてるからなんでしょうか?
Firebase Admin SDKを使用しないといけませんか?
さっさ さん
記事をご覧いただき、ありがとうございます。
今、直ぐに検証できない状態ですので、もう少々お待ちくださいませ・・・。
返信ありがとうございます。
いつも参考にしています。
めちゃめちゃ助かってます!
さっさ さん
お待たせしました。
当方のM5Stackで確認したところ、特に問題有りませんでした。
久々にFirebaseコンソールを開きましたが、従来のデータベースシークレットはまだ存在しています。
赤文字で「データベースのシークレットは廃止された」と表示されておりますが、「レガシーの Firebase トークン生成ツールを使用する」と表示されているので、問題ありません。
因みに、このページの7項目
7.ブラウザのURL 入力欄にURL を入力して、Realtime Database の値を取得してみる
を参照して、ブラウザのURL欄にデータベースシークレット入りのURLを入力してみてください。
エラーが出ていなければ、問題無く使用できるはずです。
あと、気になったのは、Arduinoスケッチのhostのところのアドレスには「https://」は不要ですのでご注意ください。
以上、再度確認してみてくださいませ。
突然のコメントにて失礼します。
4ページ目のソースコード39行目の前に
client.setInsecure();
が必要だと思われます。これを付け足すことで、DBに接続することができました。
ESP32さん
ご指摘、ありがとうございます。
この記事は3年以上前に書いたもので、いろいろと変わってしまったようですね。
確かに当方で試した結果、ルート証明書が無いためにアクセスしてくれませんでした。
Arduino core ESP32 のバージョンは2.0.2 です。
おっしゃる通り、
client.setInsecure();
を足せばアクセスできました。いつの間にかこんな関数ができていたんですね。
教えて頂き、ありがとうございます。
早速、ソースコードも修正しました。
ただ、5ページ目のPATCHを使うコードはまだ動かないので、検証中です。
できたらまたこのコメント欄でご報告します。
以前、指摘をさせていたいただいた者です。修正ありがとうございます。
質問です。Firebase Realtime Databaseで読み込みしながら、書き込みをしたいです。ソースコードを教えていただけないでしょうか。
「client.stop(); //これ絶対必要」をデータベースに書き込み終わったときに実行してしまうと、読み込みが途切れてしまい、データベースのデータが変わっても、変わったのがわからなくなってしまいます。(わかりにくい文章ですみません。)
お忙しいところ恐れ入りますが、ご返信いただければ助かります。
ESP32さん
PATCHリクエストの連続書き込みがようやくできました。5ページ目のコードを修正しました。
また、そのついでにご要望にお応えして、データの送信とEventストリーム受信の両方を行うスケッチを書いてみました。
Arduino core ESP32 のバージョンが2.0.2になると、だいぶ仕様が変わっていましたね。
PATChリクエストはclient.stopしてしまうと、clientインスタンスを開放しないと再接続してくれなかったので、関数内でインスタンス生成する方式に変え、Eventストリーム受信のclientインスタンスは別で生成しました。
以下のリンクにコードを載せておきます。
https://www.mgo-tec.com/blog-entry-firebase-realtime-database-sever-sent-events-esp32-m5stack.html/5#title12
教えていただきありがとうございます。
読み込みと書き込み同時にできました!
来年の夏休みの工作に活用していきたいです。
それはよかったです。
こちらこそ、情報いただきありがとうございました。
m(_ _)m