2016年6月3日金曜日

Googleに統合されたFirebaseを使ってIoTとスマホの連携を実践する

はじめに

Googleに買収された Firebase が、Googleサービスに統合されより使いやすく強力になりました。またFirebaseの持つリアルタイム性を生かした IoTプロジェクトも現れてきています。今回はI/Oに発表されずにいた小粒ななIoTプロジェクトに焦点を当て、Googleに統合されたFirebaseの設定やIoTとしての活用方法について紹介いたします。




Google Samplesを覗こう

毎年 I/O の時期になると新しい機能のサンプルコートがGithubのGoogleSamplesに掲載されています。今回もI/Oの基調講演が終わったタイミングで大量に追加が行われていました。
そんな中、地道に更新を続けていたプロジェクトがFirebase Arduinoです。
https://github.com/googlesamples/firebase-arduino

古いけど新しいFirebase Arduino

Firebase Arduino自体は FirebaseがGoogleに買収される前に「Realtime IoT」のサンプルとして公開されいていたものでしたが、 当時はRaspberryPiなどのLinuxベースのデバイス+Adruinoという構成で作られており、 高価すぎてとてもIoTのThing側として使えるものではありませんでした。
今回公開されたものは安上がりにIoTデバイスを構築できるESP8266ベースのArduinoを使うようになっています。 ブレッドボード工作で1ノードあたり1000円ぐらいで作成できましたのでIoTとして使うのにかなり期待できるものになっていると思います。

(注意)正式版になるまで、もう少しお待ち下さい

Firebase Arduinoのプラットフォームとして利用するESP8266パッケージは、本ブログ掲載時点においてサーバーとの通信を繰り返し行うとウォッチドッグリセットに至るという致命的な問題を抱えています。

https://github.com/googlesamples/firebase-arduino/issues/175
https://github.com/googlesamples/firebase-arduino/issues/164
どうやらヒープ領域を使い果たしているようです。
作者自身も "The Arduino library is under heavy development, experimental, unversioned and its API is not stable."と書いているぐらいなので安定するまでは多少かかりそうですね。

この事象は ESP8266 core 2.3.0-rc1版で改善されていることから本ブログではRC版による動作確認を行っています。
でもRC版だと不安という方はESP8266 core の 2.3版がリリースされるまでお待ち下さい

今回作成するもの

テーマとして非常停止ボタンの状態をIoTで拡散するシステムを作成します。
IoT装置についている非常停止ボタンが押されたら警告表示灯を赤点灯させるとともに関連する装置に通知しそれらの装置側では黄点灯を行えるようにします。またスマホアプリ側にも警告が通知されるようにして、その後スマホから警告表示灯を復旧できるようにします。



作成してみましょう

Firebaseプロジェクト登録

まずはfirebaseののコンソールにて新規のプロジェクト登録を行います。

https://console.firebase.google.com/

アカウントはgoogleアカウントに統合されていますので、お手持ちのgmailアカウントを使うとよいでしょう。プロジェクト登録が終わったら、Arduinoスケッチで使う認証キーを取得するようにします。

プロジェクトの新規作成

初回ログインのときは次のような画面が出ているはずです。
ここで「新規プロジェクトを作成」ボタンを押します。



プロジェクト名とリージョン選択のダイアログが出てきますので、プロジェクト名に任意の名前と、国/地域に"日本"を設定し「プロジェクト作成」ボタンを押します。



このような画面が表示されれぱプロジェクト作成完了です。



FirebaseのプロジェクトURLと認証シークレットIDを取得

Arduinoで接続するのに必要となる設定値を取得します。
画面左上あたりにある歯車アイコンを押し、プロジェクトの設定を開きます。



設定のデータベースを選択すると、データベース接続のシークレットが表示されます。
なお表示画面が出た時点では認証シークレットIDは●文字でマスクされていますので「表示する」ボタンを押して認証シークレットIDを表示できるようにします。表示された認証シークレットIDはArduinoスケッチで使いますのでメモしておきましょう。



次に左側にあるDatabaseを選択しデータ項目を表示させます。
表示された内容のうち矢印で示されたURLもArduinoスケッチで使いますので、こちらもメモしておきましょう。



IoT装置

今回作成したIoT装置は写真のようにブレッドボードで作成しています



・秋月電子のESP-WROOM-02 DIP化キット K09758
・LED(4色)
・非常停止ボタン
  押しやすいスイッチを選びましょう
・普通のブレッドボード
・USB micoroBコネクタ変換基板 (サンハヤト CK-37)
・3.3V 3端子レギュレータ (秋月電子 I-00538)
  付属のコンデンサーも利用します
・トランジスタ 2SC1855
 100mA程度スイッチングできれば他のトランジスタでも構いません
・抵抗 10K x 10 ,100Ω,150Ω,220Ω,330Ω

・セラミックコンデンサー 0.1uF


写真では細かいところが見づらいと思いますので各ポートの接続についても記載します
 GPIO_0: 警報スイッチ(Adrudino書き込み) 10KΩプルアップ
 GPIO_2: 10KΩプルアップ
 GPIO_4: LED赤色
 GPIO_12: LED緑色 10KΩプルダウン
 GPIO_13: LED黄色 10KΩプルダウン
 GPIO_14: LED青色
 GPIO_15: 10KΩプルダウン
 EN: 10KΩプルアップ
 RST: 10KΩプルダウン
 TO(AN1): 10KΩプルダウン


Arduinoスケッチ

1.スケッチを書く前に開発環境をセットアップします

ArduinoIDEは1.6.8以降が必須です、本ブログでは1.6.9を使用しました。

1-1) Arduino-IDEにESP8266を追加します

ファイル → 環境設定にて「追加のボードマネージャのURL」に次のURLを追加します
http://arduino.esp8266.com/staging/package_esp8266com_index.json

もし既にStable版が追加済みの場合はそのまま追加してしまうとスケッチのコンパイルに失敗します。まずボードマネージャからStable版のesp8266をアンインストールしてから stagingに置き換えるようにしてください。元に戻す時も同様にstaging版を消してから入れ替えるようにしてください。

ツール → ボードマネージャを起動し esp8266を追加します。

1-2) firebase-masterをダウンロードしライブラリとして登録
公式サイトからmasterブランチのアーカイブを取得します。

https://github.com/googlesamples/firebase-arduino/archive/master.zip

zip形式でダウンロードされますので、そのまま次のメニューを用いてライブラリ登録します
Sketch > Include Library > Add .ZIP Library

2.サンプルスケッチ

本ブログに掲載したスケッチをこちらでで公開しています。
なお接続先の部分が未設定になっていますので使用される環境に合わせて変更してください。

#define FIREBASE_HOST "FirebaseホストURL(XXXXXXXXX.firebaseio.com)"
#define FIREBASE_AUTH "Firebaseのシークレット"
#define WIFI_SSID "アクセスポイントSSID"
#define WIFI_PASSWORD "アクセスポイントパスワード"


スケッチの詳細は次の通りとなっています。
setup()
スケッチの初期化です、GPIOポートの初期化とWiFiアクセスポイント、Firebaseへの接続を行っています
void setup() {
  Serial.begin(9600);

  // LED pins
  pinMode(RledPin, OUTPUT);
  digitalWrite(RledPin, LOW);
  pinMode(YledPin, OUTPUT);
  digitalWrite(YledPin, LOW);
  pinMode(GledPin, OUTPUT);
  digitalWrite(GledPin, LOW);
  pinMode(BledPin, OUTPUT);
  digitalWrite(BledPin, HIGH);

  // Alert switch
  pinMode(buttonPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(buttonPin), gpio_isr, FALLING);

  // connect to wifi.
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  WiFi.mode(WIFI_STA); // omajinai
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("connected: ");
  Serial.println(WiFi.localIP());

  // setup firebase.
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  Firebase.set("state", 1);

  // clear LED 
  digitalWrite(RledPin, LOW);
  digitalWrite(YledPin, LOW);
  digitalWrite(GledPin, LOW);
  digitalWrite(BledPin, LOW);

  // WiFi lowpower setting
  wifi_set_sleep_type(LIGHT_SLEEP_T);
}

gpio_isr()
非常停止ボタンの割り込み処理です
ただし時間の掛かる警告発報などはISR内部ではできないことから、処理をloop()側で行えるようフラグ設定を行っています。
void gpio_isr() {
  ist = 1;
}

loop()
100mS周期で動作し、Firebaseに対して現在の状態取得を5回(500ms)ごとに行うようになっていて、Firebase側に記録された状態フラグに合わせてLEDの点灯を変化させるようになっています。
また非常停止ボタンが押された場合は赤色LEDのみを点灯させるとともに Firebaseに対して異常状態を通知します。
void loop() {
  if (ist==1) {
    Serial.print("+");
    int newButton = digitalRead(buttonPin);
    if ( newButton == 0) {
      digitalWrite(RledPin, HIGH);
      digitalWrite(YledPin, LOW);
      digitalWrite(GledPin, LOW);
      alarm = 1;
      Firebase.set("state", 2);
      count = 0;
    }
    ist = 0;
  }

  if (count++ >= 5) {
    int fire_state = Firebase.getInt("state");
    Serial.print(fire_state);

    if (alarm == 1) {
      if (fire_state == 1) {
        Serial.print("-");
        alarm = 0;
      }
    }
    
    if (alarm != 1) {
      digitalWrite(RledPin, LOW);
      switch (fire_state) {
        case 1:
          digitalWrite(GledPin, HIGH);
          digitalWrite(YledPin, LOW);
          break;
        case 2:
          digitalWrite(GledPin, LOW);
          digitalWrite(YledPin, HIGH);
          break;
      }
    }
    count = 0;
  }
  delay(100);

}


(tips)Firebase Arduino ライブラリAPIの注意点

Firebase Arduino ライブラリAPIを使ってFirebaseから数値を取得する際に注意しなけらばならないこととして、通信異常時に取得される値が"0"となる動作仕様があります。
(Android ライブラリの場合はnullポインタとなるものが数値ゼロとなる)
つまり0は不定値であることも含まれることから、数値として扱う場合はエラーチェック等の留意が必要となります。


(tips)Firebaseデーターベースを肥大化させない

Firebaseデータベースはプロジェクトごとに1つのJSONデータという形式となっており、Firebase Arduino ライブラリでも読み出し時には全JSONデータを取得してから必要な要素を抽出するという処理を行っています。そのためデータベースに多くの要素を含ませてしまうと通信データ量が増え速度低下を引き起こすことになります。
メッセージ交換に使うデーターベースは必要最小限の要素にまとめるようにしましょう。

(tips)Firebase Arduino ライブラリAPIについて

Firebase Arduino ライブラリで使用可能なライブラリAPIをまとめました。

初期化

Firebase Arduinoライブラリを初期化します、基本的にsetup()内で使用するようにしてください。
void FirebaseArduino::begin(const String& host, const String& auth)

データ追加

Firebaseデータベースにデータを追加します。
String FirebaseArduino::pushInt(const String& path, int value)
String FirebaseArduino::pushFloat(const String& path, float value)
String FirebaseArduino::pushBool(const String& path, bool value) 
String FirebaseArduino::pushString(const String& path, const String& value)
String FirebaseArduino::push(const String& path, const JsonVariant& value)

データ更新

Firebaseデータベースのデータを更新します。データベースにデータがない場合は追加となります
void FirebaseArduino::setInt(const String& path, int value)
void FirebaseArduino::setFloat(const String& path, float value) 
void FirebaseArduino::setBool(const String& path, bool value)
void FirebaseArduino::setString(const String& path, const String& value)
void FirebaseArduino::set(const String&; path, const JsonVariant& value)

データ取得

Firebaseデータベースから指定されたデータ要素を取得します。
なお通信エラーが起きたときの不正値がゼロとなるので注意が必要です。
FirebaseObject FirebaseArduino::get(const String& path) 
int FirebaseArduino::getInt(const String& path)
float FirebaseArduino::getFloat(const String& path)
String FirebaseArduino::getString(const String& path)
bool FirebaseArduino::getBool(const String& path)

データ削除

指定されたデータ要素を削除します
void FirebaseArduino::remove(const String& path)

ストリームデータ

ストリームデータを扱います
void FirebaseArduino::stream(const String& path)

接続チェック

Firebaseデータベースと接続されているか確認します
bool FirebaseArduino::available()

エラー確認

最後に実行したAPIのエラー状態を返します
bool success();
bool failed();
bool FirebaseArduino::failed()
const String& FirebaseArduino::error()

Adruinoスケッチの書き込み

ブレッドボードにはArduinoスケッチを書き込むための回路が付いていませんので写真のような回路を追加します。
追加部品: FT232RL USBシリアル変換モジュール K-01977
(昔のモデルではありますがUSBコネクタからの電源供給に電流制限が入っていないため使いやすいです)
書き込みのときは赤いスイッチを押しながらUSBシリアル変換モジュール側のコネクタとPCを接続してください。


書き込みの際にはAuduino-IDEに対して次のようなセッティングを行います。
ボードはESP8266 Generic ESP8266 Module もしくは ESPineo を選択します。シリアルポートは使用しているFTDIモジュールのCOM番号を指定してください。あとはデフォルトで動作するはずです。


なおESP8266版のArduinoはスケッチ書き込み時にファームウェア全領域を書き換えていることから、書き込みに多少時間が掛かります。IDEのステータス表示に進行状態が表示されますので気長に待つようにして下さい。


動作させてみる

ブレッドボードにあるUSBコネクタにスマホ充電器を接続すると青色LEDが点灯し、WiFiアクセスポイントへの接続が開始されます。アクセスポイントへの接続が成功しFirebaseへのアクセスが成功すると緑色LEDが点灯します。
電源ON
正常状態


ボード上の赤色スイッチを押すと押したボードだけ赤色LEDが点灯し、他のボードは黄色LEDが点灯となります。
警報ボタンを押した後
他のボードから警報が発せられた場合

今回のサンプルコードではブレッドボード単体で試せるようFirebaseに初期値を書き込むようになっており、どれか一枚のボードの電源を入れなおすと他のボードも緑色LEDに戻ります。

また動作状態はFirebaseのコンソールからも確認することができます。

<通常状態>


<警報状態>


Androidアプリ

今回作成したAndroidアプリは状態表示と警報復帰ボタン1個のシンプルな構成になっています。 ソースコードはこちらで配布しています。

サンプルコードの利用方法

なおgoogle service経由でFirebaseを使う場合はSDKを適時更新する必要がありますので予め更新しておいてください
Android Support Repository > Revison32
GooglePlay Service > Revison30
Google Repository > Revison 26




パッケージ名の変更

配布ソースコードを用いる場合、パッケージ名が重複したままだとFirebaseプロジェクト登録でエラーとなりますので、リファクタリング機能を使って任意の名前に変更します。

Android Serviceファイルの取得

次にfirebaseののコンソールにてプロジェクト更新を行います。
既にプロジェクト自体は登録済みだと思いますので、そのプロジェクトを選択します。
https://console.firebase.google.com/

Googleに統合されたFirebaseでは他のgoolgeサービスと同様にアプリ認証情報をjsonファイルで登録するようになっています。あらかじめ次の方法でjsonファイルを取得しておいて下さい。
ホーム画面でAndroidのアイコンを押します



アプリ登録ダイアログが表示されますのでアプリ情報を入力します。
アプリ名は先ほど書き換えたパッケージ名を指定します。
デバッグ用の署名証明書はオプション扱いですが念のためデバッグ署名証明書も入力します。
(ユーザ認証でOAUTHを使う場合に必要となります)
入力が終わったら「アプリを追加」ボタンを押しましょう。




Androidアプリに必要なJSONファイル(google-service.json)のダウンロードが始まるのでローカルドライブに保存します。
またダイアログでアプリへの導入方法ガイドが表示されますので参考にしてください。




セキュリティの無効化

今回の記事ではFirebaseの基本的な動作を理解してもらうために、あえて接続セキュリティを無効化した形で説明を行います <非常に危険です!>
実際にお使いになる際は、アプリの目的にあわせたセキュリティを設定するようにしてください。

次に左側にあるDatabaseを選択しルール項目を表示させます



表示されているルールを次のように書き換えます

{
  "rules": {
    ".read": true,
    ".write": true
  }
}


アプリの概略

Androidアプリとしての実装部分は省略して Firebaseと接続する部分のみ解説します。

onCreate()

アプリ初期化にてFirebaseAPIの初期化と接続するオブジェクトを指定します。
Firebase接続先などの設定項目はAPPフォルダにセットしたJSONファイル(google-service.json)により自動設定されますのでソースコードから記述する必要はありません。
FirebaseDatabase database = FirebaseDatabase.getInstance();
mDbRef = database.getReference("state"); // リファレンスオブジェクトに利用対象のキーを指定
mDbRef.addValueEventListener(postListener); // データーベース更新リスナーの登録


onDataChange(DataSnapshot dataSnapshot)

指定されたデータ要素が更新されたときに呼び出されるリスナーです。 Firebaseデータベースのデータ構造に合わせて、親要素もしくは子要素を取得するようにしてください 今回のアプリではgetReference()で指定した親要素のみを扱うことから、そのまま文字列としてデータを取得しています
String value = (String) dataSnapshot.getValue().toString();


onCancelled(DatabaseError databaseError)

アプリからデータ更新に失敗したときのリスナーです、エラー処理が必要な場合はここに実装します。


Firebaseデータ書き換え

setValue()を呼び出すことによりFirebaseデータベースのデータ更新を行うことができます。
なおFirebaseのデータベースはJSON構造であるため、このメソッドの引数はJSONで保存したいデータ型に合わせる必要があります。型が間違っていてもエラーにはならずそのままのデータ型で上書きされてしまうため、読み出し時にゼロもしくはnullポインタとなります。
mDbRef.setValue(1); // データ書き込み


(tips)アプリ待機時の電力削減について

FirebaseDatabaseの更新を受信できるようにしたままにしておくと、アプリがバックグラウンド状態でもリアルタイムにデータ更新しようとするためアプリの電力消費が激増します。アプリがフォアグランドにいない間はgoOffline()を用いて一時的にオフライン状態にしておきましょう。
またフォアグランドに戻ったときにgoOnline()を呼ぶことにより接続状態に戻すことができます。もしオフライン状態のときにデータ更新があった場合は、goOnline()呼出し後に onDataChange()が呼び出され更新を受信することができます。

今回のアプリではonPause()でオフライン、onResume()で再接続を行うようにしています。
@Override
protected void onPause() {
    mFBdb.goOffline();
    super.onPause();
}
@Override
protected void onResume() {
    super.onResume();
    mFBdb.goOnline();
}

アプリ動作

アプリケーションを起動するとFirebaseデータベースにアクセスし現在の状態が表示されます。もし警戒状態になったときは"異常を検出しました"というメッセージ表示に変わるとともに「警戒解除」ボタンが有効になります。「警戒解除」ボタンを押すとFirebaseデータベースを更新し"異常なし"表示に戻ります。


まとめ

今回はかなり足早ではありましたがFirebaseとArudionoの連携について紹介いたしました。デバイスとデータベース、Androidアプリの連携が簡単になるといろいろなアイデアが浮かんでくるかと思います。
そんな皆さまのお役に立てば幸いです。

140 180 Android , Arduino , ESP-WROOM-02 , Firebase , IoT , 電子工作

記載されている会社名、および商品名等は、各社の商標または登録商標です。

5 コメント:

  1. このコメントは投稿者によって削除されました。

    返信削除
  2. このコメントは投稿者によって削除されました。

    返信削除
  3. このコメントは投稿者によって削除されました。

    返信削除
  4. お世話になります。
    Firebase.set("state", 1); を設定するタイミングで
    please start sntp first というERRORがコンソールに出て書き込みできません。sntp関連は導入しましたか?

    返信削除
  5. 弊社からの返答が遅れまして大変申し訳ございません。
    また記事に対するコメントありがとうございます。

    こちらで確認したところ、ご指摘頂きました現象は再現しませんでした。
    ArduinoIDE環境にsntp関連のモジュールをインストールせずに動作しております。
    念のため弊社で確認した際の環境を記載いたしますので、参考となれば幸いです。
    ArduinoIDE 1.6.9 (windows)
    + ボードマネージャでの追加:
    esp8266 by ESP8266 Community バージョン2.3.0
    ※記事ではRC版を使用して記述となっておりましたが、正式版がリリースされましたので現在はこちらを推奨いたします。
    + ライブラリ追加:
    Firebase Arduino (master) 2016/09/15にダウンロードしたものを使用

    返信削除

Related Posts Plugin for WordPress, Blogger...