Firebase Realtime database を使った ESP32, M5Stack, スマートフォン相互通信してみる

M5Stack

Firebase Realtime database のデータベースシークレットを発行しておく

以下の記事を参照して、Firebase Realtime database のデータベースシークレットを発行しておいてください。

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

Firebase Realtime database の作成およびユーザーの作成

以下の記事を参照して、Firebase Realtime database を「test_user1」という階層で、それぞれ「text」や「color」という階層データを作成しておきます。
そして、E-mail ログインのユーザーを作成しておいてください。
スマホのブラウザで通信できるようにしておいてください。

Firebase Realtime Database をスマホで操作およびストリーミング受信する実験

また、それに加えて、
{ speed: “010ms” }
というデータも追加しておいてください。
数値だと、読み取り抽出の桁が合わなくなるので、文字列にして、前にゼロを置いて、3桁の数値にします。

Arduino core for the ESP32 のインストール

M5Stack や ESP32-WROOM-32 開発ボードは、Arduino IDE で開発します。
Arduino IDE は ver 1.8.6 で動作確認しています。

Arduino core for the ESP32 は Stable版 1.0.0 で動作確認済みです。
インストール方法は以下の記事を参照してください。

Arduino core for the ESP32 のインストール方法

M5Stack に日本語漢字東雲フォントが表示できるように、micro SDHC カードにフォントデータをコピーしておく

以下の記事を参照して、micro SDHC カードに /font/フォルダを作成して、東雲フォントを保存しておいてください。

Arduino – ESP32 ( SPIFFS 又は micro SD ) 自作Fontライブラリインストール方法

SDカード用の項目を参照してください。
3つのファイルが必要です。

Utf8Sjis.tbl
shnmk16.bdf
shnm8x16.bdf  ( shnm8x16r.bdf のファイル名中の ’r’ 文字を削除しておく)

自作ライブラリのインストール

私の自作した Arduino – ESP32 用ライブラリをインストールします。
GitHub の以下のリンクにあります。

https://github.com/mgo-tec/ESP32_mgo_tec

現在、beta ver 1.0.40 です。
素人アマチュア自作ライブラリですので、動作保証はしません。
でも、不具合や誤りがありましたら、コメント投稿等でご連絡いただけると助かります。
古いバージョンは、必ずフォルダごと削除してから再インストールしてください。

また、この自作ライブラリには、Arduino IDE 標準の Timeライブラリが必要です。
これは外部ライブラリになっているので、GitHub の以下のページから ZIPファイルをダウンロードしてインストールしてください。

https://github.com/PaulStoffregen/Time

ZIPファイルからの Arduino IDE へのインストール方法は以下の記事を参照してください。

GitHubにある ZIP形式ライブラリ のインストール方法 ( Arduino IDE )

今回のバージョンは、Firebase関連を新規追加しました。
そして、LCD ILI9341 の文字列スクロールを簡単にする関数を追加しました。
その他、多々修正しています。

M5Stack にスケッチを書き込む

では、以下のスケッチを Arduino IDE に書き込んでみてください。

コンパイルで、以下のエラーが出る場合、
display_bme280_i2c.h:44:64:fatal error
display_bme680_i2c.h:44:64:fatal error
そのファイルのある、Sensor フォルダごと削除して、再コンパイルしてください。
只今、ライブラリ修正中です。
(2018/09/24)

 

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <mgo_tec_esp32_m5stack_firebase.h>

const int8_t sck = 18; // SPI clock pin
const int8_t miso = -1; // MISO(master input slave output) don't using
const int8_t mosi = 23; // MOSI(master output slave input) pin
const int8_t cs = 14; // Chip Select pin
const int8_t dc = 27; // Data/Command pin
const int8_t rst = 33; // Reset pin
const int8_t lcd_led_pin = 32;

const uint8_t cs_sd = 4; //SD card CS ( Chip Select )

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

const char* utf8sjis_file = "/font/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
const char* shino_full_font_file = "/font/shnmk16.bdf"; //オリジナル東雲全角フォントファイル
const char* shino_half_font_file = "/font/shnm8x16.bdf"; //半角フォントファイル名を定義

const char* host = "xxxxxxxx.firebaseio.com"; //自分のRealtime database URL
const char* firebase_auth = "xxxxxxxxxxxxxxxxxxxx"; //従来のデータベースシークレット
String user_path = F("test_user1");

//--------------------------------------
mgo_tec_esp32_bv1::ILI9341Spi LCD;
mgo_tec_esp32_bv1::SdShinonomeFont SFR(cs_sd, 40000000);
mgo_tec_esp32_bv1::FontParameter font;
mgo_tec_esp32_bv1::ScrolleParameter scl_set;
mgo_tec_esp32_bv1::DispShinonomeFnt disp_fnt;
mgo_tec_esp32_bv1::FirebaseRD firebase;
mgo_tec_esp32_bv1::MessageWindow sse_msg, patch_msg;

boolean isSSE = false; //Server-Sent Events 通信中かどうかの判定
boolean isRequestSSE = false; //最初にServer-Sent Eventsリクエストする
boolean isRequestPatch = false;
boolean isNew_firebase_data = false;
boolean isNew_firebase_color = false;
boolean isNew_firebase_speed = false;
boolean isScroll_stop = false;

String event_text_str = "?";
String event_color_str = "#000000";
String event_speed_str = "015";
uint8_t interval = 15; //スクロール速度調整は、正確にはintervalである
uint8_t red = 0, green = 0, blue = 0;
//-----ボタンスイッチ 引数初期化--------
mgo_tec_esp32_bv1::ButtonSwitch btnA, btnB, btnC;
const uint8_t buttonA_GPIO = 39;
const uint8_t buttonB_GPIO = 38;
const uint8_t buttonC_GPIO = 37;

//*****セットアップ******************
void setup() {
  Serial.begin(115200);
  pinMode( buttonA_GPIO, INPUT ); //GPIO #39 は内部プルアップ無し
  pinMode( buttonB_GPIO, INPUT ); //GPIO #38 は内部プルアップ無し
  pinMode( buttonC_GPIO, INPUT ); //GPIO #37 は内部プルアップ無し

  SFR.init3File(utf8sjis_file, shino_half_font_file, shino_full_font_file);

  LCD.ILI9341init(sck, miso, mosi, cs, dc, rst, lcd_led_pin, 40000000, false); //microSDを使う場合、必ず false にする
  LCD.displayClear();
  LCD.brightness(255); //LCD LED max brightness 255

  firebase.init( host, firebase_auth );
  initScrolleFont();
  initMessage();

  //オープニング画面のみのメッセージウィンドウ設定
  mgo_tec_esp32_bv1::MessageWindow msg;
  msg.m_y0 = 140;
  msg.m_size = 2;
  msg.dispMsgWindow( 0, "WiFi connecting..." );

  //WiFiアクセスポイント接続
  WiFi.begin(ssid, password);

  Serial.print( F("connecting") );
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print( F("connected: ") );
  Serial.println( WiFi.localIP() );
  delay(2000);
  //Firebaseへ Server-Sent Events(SSE) GETリクエスト
  firebase.sendGetRequestSSE( user_path );

  TaskHandle_t th; //マルチタスクハンドル定義
  xTaskCreatePinnedToCore(Task1, "Task1", 8192, NULL, 5, &th, 0); //マルチタスク起動
}
//****メインループ********************
void loop() {
  //文字列を東雲フォントに変換
  if( isNew_firebase_data == true ){
    disp_fnt.newSetText( scl_set, event_text_str );
    isNew_firebase_data = false;
  }
  //文字列スクロール
  if( isScroll_stop == false ){
    disp_fnt.scrolleText( font, scl_set );
  }

  //sse connectOK でメッセージウィンドウを消すためには、この位置にすること。
  patch_msg.dispWebGetStatusMsgLong( firebase.patch_status, "Patch" );
  sse_msg.dispWebGetStatusMsgLong( firebase.sse_status, "SSE" );

  if( isNew_firebase_color == true ){
    pickUpColorData( event_color_str, red, green, blue );
    //ディスプレイの下半分を色で塗りつぶす
    LCD.drawRectangleFill( 0, 120, 319, 239, red, green, blue );
    //スクロール文字色セット
    font.red = red, font.green = green, font.blue = blue;
    LCD.scrolleFontColorSet( font );
    isNew_firebase_color = false;
  }
}
//************ マルチタスクループ ******************
void Task1(void *pvParameters) {
  while(1){
    String sse_str;
    if( firebase.pickUpStrSSEdataAll( sse_str ) == true ){
      isNew_firebase_data = firebase.pickUpStrToTargetStr( sse_str, "text", event_text_str );
      event_text_str = event_text_str + " "; //文字スクロールを見やすくするため、全角スペースを追加。
      isNew_firebase_color = firebase.pickUpStrToTargetStr( sse_str, "color", event_color_str );
      isNew_firebase_speed = firebase.pickUpStrToTargetStr( sse_str, "speed", event_speed_str );
      if( isNew_firebase_speed == true ){
        pickUpSpeedData( event_speed_str, interval );
        scl_set.interval = interval;
        isNew_firebase_speed = false;
      }
    }
    button_action(); //ボタン操作
    delay(1); //マルチタスクの場合、これ絶対必要!
  }
}
//****** メッセージウィンドウ表示初期化 **************
void initMessage(){
  patch_msg.m_x0 = 8;
  patch_msg.m_y0 = 140;
  patch_msg.m_size = 2;
  patch_msg.m_txt_length = 18;
  patch_msg.m_padding = 4;
  sse_msg.m_x0 = 8;
  sse_msg.m_y0 = 180;
  sse_msg.m_size = 2;
  sse_msg.m_txt_length = 18;
  sse_msg.m_padding = 4;
}
//***************************************************
void pickUpColorData( String color_str, uint8_t &Red, uint8_t &Green, uint8_t &Blue ){
  Red = 0, Green = 0, Blue = 0;
  String red_str = color_str.substring( 1, 3 );
  String green_str = color_str.substring( 3, 5 );
  String blue_str = color_str.substring( 5, 7 );
  uint8_t red_value = strtol( red_str.c_str(), NULL, 16 );
  uint8_t green_value = strtol( green_str.c_str(), NULL, 16 );
  uint8_t blue_value = strtol( blue_str.c_str(), NULL, 16 );
  Red = (uint8_t)floor( (double)red_value / 8.0 ); //0~31範囲に変換
  Green = (uint8_t)floor( (double)green_value / 4.0 ); //0~63範囲に変換
  Blue = (uint8_t)floor( (double)blue_value / 8.0 ); //0~31範囲に変換
  Serial.printf( "red,green,blue = %02X, %02X, %02X\r\n", red_value, green_value, blue_value );
  Serial.printf( "red,green,blue = %2d, %2d, %2d\r\n", Red, Green, Blue );
}
//***************************************************
void pickUpSpeedData( String speed_str, uint8_t &speed_value ){
  String str = speed_str.substring( 0, 3 );
  speed_value = strtol( str.c_str(), NULL, 10);
  if( speed_value == 100 ){
    isScroll_stop = true;
  }else{
    isScroll_stop = false;
  }
  Serial.printf( "speed = %d\r\n", speed_value );
}
//****** スクロール表示初期化*************
void initScrolleFont(){
  uint8_t txt_max = 13;
  scl_set.interval = 15;
  font.Xsize = 3;
  font.Ysize = 6;
  LCD.XscrolleFontArrayInit( font, scl_set, txt_max, font.Xsize, font.Ysize);
  LCD.scrolleFontSetUp( font, scl_set );
}
//****************************************
void button_action(){
  btnA.buttonAction( buttonA_GPIO, true, 30, 500 ); //チャタリング対策 30ms, 長押し500ms
  switch( btnA.ButtonStatus ){
    case btnA.MomentPress: //Aボタン瞬時押しの場合
      Serial.println( F("Button A Moment Press") );
      firebase.patchHTTPrequest( user_path, "text", "M5Stack Gray Pushed Button A " );
      break;
    case btnA.ContPress: //Aボタン長押しの場合
      Serial.println( F("-------------Button A Cont Press") );
      firebase.patchHTTPrequest( user_path, "color", "#FF0000" );
      break;
    default:
      break;
  }

  btnB.buttonAction( buttonB_GPIO, true, 30, 500 ); //チャタリング対策 30ms, 長押し500ms
  switch( btnB.ButtonStatus ){
    case btnB.MomentPress: //Bボタン瞬時押しの場合
      Serial.println( F("Button B Moment Press") );
      firebase.patchHTTPrequest( user_path, "text", "M5Stack Gray ボタンBが押されたよ " );
      break;
    case btnB.ContPress: //Bボタン長押しの場合
      Serial.println( F("-------------Button B Cont Press") );
      firebase.patchHTTPrequest( user_path, "color", "#00FF00" );
      break;
    default:
      break;
  }

  btnC.buttonAction(buttonC_GPIO, true, 30, 500); //チャタリング対策 30ms, 長押し500ms
  switch( btnC.ButtonStatus ){
    case btnC.MomentPress: //Cボタン瞬時押しの場合
      Serial.println( F("Button C Moment Press") );
      firebase.patchHTTPrequest( user_path, "text", "M5Stack Gray ぼたんCだよ~ん " );
      break;
    case btnC.ContPress: //Cボタン長押しの場合
      Serial.println( F("-------------Button C Cont Press") );
      firebase.patchHTTPrequest( user_path, "color", "#0000FF" );
      break;
    default:
      break;
  }
}

【解説】

●13-14行:
ご自分の WiFi ルーター(アクセスポイント)のSSID とパスワードを書き換えてください 。

●20-21行:
Firebase 1回目の以下の記事を参照して、ご自分のデータベースの URL とデータベースシークレットキーに書き換えてください。

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

●29-30行:
私の自作ライブラリで、beta ver 1.0.40 から新たに追加したクラスです。

●94-137行:
マルチタスクで動かす場合、SPI通信するものは core 番号を同じにした方が良いです。
setup関数は core 1 で、メインloop関数も core 1 です。
よって、メインloop内には LCD ILI9341 の制御をまとめて、また、SDカードアクセスもメイン loop 内にまとめます。

そして、Firebase Realtime database にアクセスしてテキスト文字列を取得する関数は、全て core 0 のタスク 121-138行にまとめています。
ボタンスイッチ関数も core 0 です。
このマルチタスクの振り分けついてはこちらの記事を参照してください。

●96-103行:
ここで、Firebase Realtime database で変更されたUTF-8文字列を東雲フォントに変換しています。
ヒープメモリを使っていて、新たな文字列に変換する場合、毎回 Delete してヒープ領域を解放して、新たにヒープメモリを確保しています。
そして、102行目で文字列をスクロールさせています。
自作ライブラリで、beta ver 1.0.40 から、かなり簡単にできるようになったと思います。

●123-133行:
今回新たに作成したライブラリ関数です。
「test_user1」階層のデータで変更されたものだけが Server-Sent Events で自動的に送られてくるので、124行の pickUpStrSSEdataAll 関数で文字列を抽出します。
そして、その文字列から更に pickUpStrToTargetStr 関数で、ターゲットのテキストデータを抽出します。

●152-165行:
Firebase Realtime database から送信されてきた色文字列データを数値に変換します。
ブラウザの Color Picker から Firebase へは、HTMLカラーコード文字列で送られてきます。
赤色ならば #FF0000 という文字列です。
FFは 16進数です。
red = FF = 255
green = 00 = 0
blue = 00 = 0

よって、この文字列を10進数値に変換しなければいけません。
これには、C言語に標準的に備わっている strtol 関数を使うと大変便利です。
そして、0~255までの10進数に変換したら、160-162行にあるように、赤色と青色は 0~31の数値範囲に変換し、緑色は 0~63の範囲に変換します。
これは ILI9341 の仕様です。

●167-176行:
同じように、strtol 関数で数値に変換して、速度数値(interval値)を算出しています。
数値が 100 の場合、スクロールを停止します。

では、次の項ではスマホ側のプログラミングを紹介します。

コメント

  1. juchang より:

    mgo-tec 様

    待望の新作発表、早速試させていただきました。
    コンパイル書き込みをすると、
    未使用 : C:\Users*********\ESP32_mgo_tec-master\src/ESP32_mgo_tec_bV1/Sensor/display_bme680_i2c.h:44:64:fatal error: bme280.h: No such file or directry
    となります。
    このプログラムでは ESP32_mgo_tec_bV1/Sensor は不要と思い削除し、再コンパイル書き込みをすると、書き込みは可能となりますが、「 WiFi conenecting…」「 SSE Connecting 」の表示のままです。
    A ボタンを押すと、「 Patch Connecting 」「 SSE Connecting 」という表示に変わり、その後「 Patch Failed 」「 SSE Connectinng 」となります。
    他の方のコメントを参考にと出るのを待っていたのですが、待ちきれず投稿した次第です。
    アドバイスの程よろしくお願い致します。

    • mgo-tec mgo-tec より:

      juchangさん

      いつも検証いただき、ありがとうございます。

      私は、全く気付きませんでした。
      BOSCH bme680.h, bme280.h をインストールしていたので、そういうエラーが出なかったのです。
      display_bme280_i2c.h ファイル等で、extern していたことが原因です。
      応急処置として、juchangさんのやったように、Sensorフォルダごと削除してください。
      コンパイルする時に、不要なヘッダファイルまでインクルードされてしまうとは、全然知らずにいました。
      とっても勉強になりました。
      只今修正中ですので、しばらくそのままご利用ください。

      また、「 WiFi conenecting…」「 SSE Connecting 」の表示のままというのは、Firebase Console で事前にデータを入力していないとそういう状態になるかも知れません。
      今調査中ですので、分かりましたらお知らせしたいと思います。

      度々の不手際、申し訳ございません。
      やっぱり、まだまだ素人だなぁと実感しています。
      m(_ _)m

      • mgo-tec mgo-tec より:

        juchangさん

        お待たせしました。
        こちらでいろいろ検証したところ、コンパイルエラーが出た対象ファイルを削除していただくことが最善と思います。
        display_bme280_i2c.h エラーが出た場合、
        display_bme280_i2c.h
        display_bme280_i2c.cpp
        の2つのファイルを削除する。
        display_bme680_i2c.h エラーが出た場合、
        display_bme680_i2c.h
        display_bme680_i2c.cpp
        を削除していただく。
        あるいは、Sensorフォルダごと削除していただくことです。
        記事も修正しました。
        これは、ホントに気付かなかったです。
        当方ではすべてのファイルがインストールされているので、ファイルが無い場合にエラーが出るとは考えも及びませんでした。
        コンパイラについて、もっと勉強しなきゃと思いました。

        ところで、WiFi conenecting…」「 SSE Connecting 」の表示のままという現象は当方では確認できませんでした。
        原因は分からず、申し訳ございません。
        m(_ _)m

  2. juchang より:

    mgo-tec 様

    M5stack で動作確認致しました。
    プログラム21行目のデータベースシークレットキーの入力ミスが原因でした。
    まだ細かい動作確認はできていませんが取り急ぎご報告まで。
    ありがとうございました。

    • mgo-tec mgo-tec より:

      juchangさん

      動いて良かったですね。
      (^^)
      それにしても今回は私自身もとっても勉強になりました。
      これからのライブラリ作成に良い教材となりました。
      こちらこそ、いつもありがとうございました。
      m(_ _)m

  3. juchang より:

    mgo-tec 様

    LCD ILI9341 モジュールを試してみました。
    手持ちの HiLetgo 2.8″TFT LCD では、SD カードスロットが使えず、micro SD カードスロットを別置きとし、A、B、C ボタンも設置しました。
    プログラムを実行すると、M5stack と同じ動作をすることを確認致しました。
    今回、M5stack と同じPin ナンバーが使えるのが大変ありがたいです。
    この、「M5stack もどき」でこれまでの M5stack のプログラムを動かすのが楽しみです。
    これからの新作を期待しております。

    • mgo-tec mgo-tec より:

      juchangさん

      いつもコメントありがとうございます。
      無事動いて良かったです。
      2.8″ TFT は画面大きそうで、良さそうですね。
      私も、他にESP32開発ボードが数枚あり、わりと重宝しております。

      ところで、Yahoo記事を長時間取得し続けていると、取得失敗する新たな原因を本日追記しました。
      ESP32 および M5Stack で数時間後に Web 記事取得失敗する問題について

      client.connect 関数の使い方を変えれば、殆ど失敗は無くなりました。
      自分のライブラリも修正して次回の記事ではバージョンアップします。

      ということで、しばらくお待ちくださいませ。

  4. マッキー より:

    お世話になります。

    超初心者的質問なのですが、mgo_tec_esp32_m5stack_firebase.hの
    リンク先を教えてください。
    隅から隅まで見たつもりでありますが見当たりません。

  5. マッキー より:

    おはようございます。

    githubのリンク先には、ESP32_mgo_tec-masterはあるのですが、mgo_tec_esp32_m5stack_firebase.hがどうしても見つかりません。
    ためしに、ESP32_mgo_tec-masterをインクルードしてコンパイルしても
    エラーがでます。途方に暮れています。
    Firebaseの実験がしたくてうずうずしています。
    私も69歳になってぼけてきたのかと自虐しています。

    • mgo-tec mgo-tec より:

      あれ?
      おかしいですね。
      私の環境では問題無く動いています。
      GitHub から ZIPファイルをダウンロードして、ZIP形式のまま Arduino IDE にインストールされていますか?
      GitHubにある ZIP形式ライブラリ のインストール方法 ( Arduino IDE )

      私の環境は、Windows 10
      Arduino IDE 1.8.6
      Arduino core for the ESP32 stable 1.0.0

      つい先ほど試しても問題ありませんでした。

      これでもダメなら、GitHubのページから辿っていくと、srcの中に mgo_tec_esp32_m5stack_firebase.h があります。
      そのテキストをコピペして、テキストエディタで保存してみてください。
      また、ESP32_mgo_tec beta ver 1.0.40 では、他にもいくつかアップデートしたファイルがありますので、GitHubのページにあるのと同じ構成でテキストエディタでコピペしてファイルを作ってください。
      ZIP形式インストールが上手くいっていないのでしょうか?
      それとも、ダウンロードした際にウィルスソフトで弾かれている可能性もありますね。
      謎ですね。
      他のライブラリは問題無くインストールできますか?

  6. マッキー より:

    お騒がせしました。
    見つかりました。w
    歳のせいですね。

タイトルとURLをコピーしました