iBeacon & Raspberry Pi & iPhone & Azure で天気予報

某OSS関連団体の技術発表会セミナーのため、タイトルの通りの複雑な構成で「近寄ると天気予報をしゃべってくれる装置」というどこにでもあるような工作をした・・・その備忘録です。

はじめに

iBeacon のさわりの説明は Wikipedia あたりをご覧いただくとして、とりあえずエンジニア的には「iBeacon だけで双方向通信ができるん?」という期待というか疑問というかがはじめに浮かぶのではないでしょうか。残念ながらこの答えは「できません」です。

iBeacon のプロトコルは、Beacon 側が特定の情報をブロードキャストして受信側はそれを受け取るだけなので、もし双方向通信させたいなら BLE (Bluetooth Low Energy)をそのまま使ったり、TCP/IP を使ったりする必要があります。なので、iBeacon で何か面白いことをやってみよう…と思ってもある程度の作り込み(iPhone アプリ)が発生することから、(うちの部署のような)既存システムを組み合わせることだけをやっているエンジニアにはちょと敷居が高いかもしれません。

というわけで、この文書は、あくまでも「やってみた」的に iBeacon を実験した記録です。以降、間違い等ありましたらご指摘をいただけると助かります。

iBeacon でなにをしよう

では、iBeacon を使って何の実験をしましょうか。大げさなものだと、例えば展示会の各ブースに Beacon 端末を置いて、お客さんが近づいたブースに特化した広告をその人の iPhone 端末にバーンと表示したり、ノベルティの引換券を配布したり。しかし、これらを行うには iPhone アプリに精通している必要があり、iPhone アプリなんて作ったことのない私にはちょっと、いやかなり敷居が高いです。

今回は Raspberry Pi で Beacon 端末(ブロードキャスト発信側)を作ります。なのでその中は Linux です。これが複雑になる分には(私としては)問題ないので、じゃあ Beacon に iPhone を近づけたら Raspberry Pi に何か喋らせてみよう、と。しかし単に「いらっしゃいませ」的な固定された言葉を喋らせるのもアレなので、今日の天気とか、現在のドル円の価格とか(FX 大好き)喋らせてみようということになり(TKIさん、案ありがとうです)、そんなくだらないものを作ります。

下記はイメージ図です。
20160330202445

一言で動きを説明すると、iPhoneを持った人が Raspberry Pi に近づくと Raspberry Pi が今日の天気を教えてくれるという代物です。

もう少しだけ詳しく説明します。Raspberry Pi の Bluetooth が iBeaconブロードキャストしています。それを自作の iPhone アプリが検知し、Windows Azure 上に構築した Web サーバにある CGI をたたきます。CGI は livedoor のお天気 API を叩いて今日の天気の文字列を取得します。それを Open JTalk で音声ファイル化して Raspberry Pi に ssh で渡してしゃべらせます。

技術的なキーワードをまとめます。

  • Raspberry Pi
  • iBeacon ペリフェラル
  • Microsoft Azure
  • Open JTalk
  • iPhone アプリ
  • iBeacon セントラル

環境構成

実現の環境を考えます。以下のものを使うことにしました。

  • Raspberry Pi 1 Model B+
    OS: Raspbian 3.18.11+ #781
    用途: iBeacon ビーコン発信側 = ペリフェラル
  • iPhone 4S (私物)
    用途: iBeacon 受信側 = セントラル
  • Linuxサーバ
    プラットフォーム: Microsoft Azure
    OS: CentOS 7
    なんと MS で CentOS が使えます。インスタンスの作成も早い!
    用途: iPhoneアプリと Raspberry pi の仲介 Web サーバ

各々の仕組み

Raspberry Pi

Rasberry Pi の役割は、iBeacon のブロードキャストをすることと音声を喋らせることです。

iBeacon ペリフェラル

iBeacon のためにまずは標準 Bluetooth スタックの Bluez パッケージを apt-get します(上記したように Rasberry Pi の OS は Raspbian ですが、このインストール等については割愛します)。また、この後でコンパイルするプログラムのために libbluetooth-dev もインストールしておきます。以下は Raspberry Pi の Raspbian に root でログインして実行します。

# apt-get install bluez libbluetooth-dev

Bluez を使った iBeacon の実例プログラム(bluez-ibeacon)を公開している方がいらっしゃるので、iBeacon ペリフェラルを実現するにはそれを使うのが一番簡単です。下記のようにそれを取得してコンパイルします。

# git clone https://github.com/carsonmcdonald/bluez-ibeacon.git
# cd bluez-ibeacon/bluez-beacon
# make

bluez-ibeacon/bluez-beacon/ ディレクトリ下に ibeacon という実行可能なファイルが出来ます。これはそのまま実行できます。とりあえず以下のように実行してみます。

./ibeacon 200 ba6c600463454c8bb242ad651dc95821 0 0 -53
  • 第一引数 “advertisement time in ms”
    Advertising フレームをブロードキャストする間隔 ms。200 を指定しているので 0.2 秒毎にブロードキャストすることになります。
  • 第二引数 “UUID”
    iBeacon の proximity UUID を指定します。これは、リージョンの固有識別番号です。通常、サービス単位や企業単位で一つの UUID を利用します。上記実行例の値は uuidgen コマンドで取得した適当な値です。
  • 第三引数 “major number”
    上記のリージョン内を分割した領域を表す「メジャー番号」を指定します。例えば、ある企業のビル内のフロアをメジャー番号で識別するような使い方をします。今回の場合はそのような使い方はしないのでこれを固定値を(0)にします。
  • 第四引数 “minor number”
    リージョンとメジャー番号で分割した領域をさらに分割した領域を表す「マイナー番号」を指定します。
  • 第五引数 “RSSI calibration amount”
    おそらく Measured Power と言われる送信機と受信機の間が 1m の場合の RSSI 値(受信信号強度: Received Signal Strength Indication, 単位 dB)です。これは、事前に別の iBeacon セントラルの iPhone アプリで計測(実際に 1m の距離で RSSI を計測)しておき、それを値として与えることにしました(Bluetooth ドングルの機種により値は違うと思われます)。今回は -53dB としました。この値をブロードキャストする情報の中に含めて「俺は 1m のところでは -53dB の強度を出すビーコンだよ」と受信端末に教えてあげているみたいです。これで受信端末は送信機からどれくらい離れているか計算できるってことらしいです。

上記で Raspberry Pi を iBeacon 発信機(ペリフェラル)にできました。

Raspberry Pi から音声

Raspberry Pi から音声を出すのは簡単です。Raspbian をインストールすると自動で ALSA が入ります。aplay コマンドも入るのでそれを使って wav ファイル等を再生することにします。aplay は後述の Microsoft Azure にある CentOS サーバ(以降サーバと記します)上の CGI から叩かなければなりません。しかし、今回の構成では Raspberry Pi とサーバは同じネットワークになく、Raspberry Pi も社内のプライベートアドレスしか持っていません。「やってみた」的な内容で VPN を張るのも大げさなので、ssh のリモートポートフォワードで解決してしまいましょう。(以下に記すコマンド実行は後述の Microsoft Azure 上に CentOS のインスタンスを作ってからやってください。)
Raspberry Pi 上の 22 番ポートをサーバの 10022 番ポートにフォワードします。これは Raspberry Pi(Raspbian)上の ssh コマンドで実行します。以下にコマンド例を記します。

# ssh -f -N -oServerAliveInterval=20 -R10022:localhost:22 user@<サーバのIPアドレス>

これでサーバの 10022 ポートにアクセスするとRaspberry Pi(Raspbian)の 22 ポートにフォワードされるようになりました。これでサーバ上の CGI から localhost の 10022 ポートに対して ssh を叩けば、Raspberry Pi (Paspbian)上コマンドを実行できます。しかし接続の際にパスワードを求められてしまうとそこで CGI が止まってしまいますので、公開鍵認証でノーパスワードで接続できるようにします。root ユーザで接続したいので、Raspberry Pi (Raspbian)上の ~root/.ssh/authorized_keys にサーバの apache ユーザの ssh 公開鍵を登録して…ってわかりますよね。このあたりの説明は割愛させてもらいます。とにもかくにもこれでサーバから ssh ノーパスワードで Raspberry Pi にアクセスできるようになりました。

Microsoft Azure 上の Linux サーバ

今回のアプリは iBeacon を iPhone アプリが検知し、そのタイミングで HTTP で Web サーバ上の CGI を叩くような仕組みにします。ですので、インターネットに接続された Web サーバが必要です。これをクラウド上の Microsoft Azure を使ってインスタンスで作成することとします。OS は CentOS 7 です。※別途 Microsoft Azure の契約が必要。

なんと Microsoft のプラットフォーム上に Linux が構築できちゃうんです。ということで、Microsoft Azure でのインスタンス作成は、簡単なので割愛します。

このサーバ上では、CGI でリクエストを受け付けて中で以下の処理を行います。

  1. 今日の天気を以下 URL から取得 (city=130010 で東京の天気が取得できます)
    http://weather.livedoor.com/forecast/webservice/json/v1?city=130010
  2. 取得できる天気情報は json なので、これを解析して本日の天気を取り出す。
  3. OpenJTalk を使って「本日の天気は××です」ということを喋らせた wav ファイルを作成する。
  4. この wav ファイルを標準出力に流しつつ ssh コマンドで Raspberry Pi に接続して aplay を実行。→ Raspberry Pi が喋る。

CGI のソースはここに置いておきます(この CGI は、以下の設定と前述の ssh のポートフォワードの設定を行うと動作が可能になります)。
なお、上記の処理中にある OpenJTalk は RPM がどこにもないので野良ビルドします(そのうち自前の RPM を作成します)。以下野良ビルドメモです(全部 /usr/local/ 下に入れます)。

OpenJTalk は HTS という音声認識エンジンを使います。これを下記からダウンロードします。執筆時点の最新は ver.1.09 でした。
http://osdn.jp/projects/sfnet_hts-engine/downloads/hts_engine%20API/hts_engine_API-1.09/hts_engine_API-1.09.tar.gz/

ダウンロード後、以下のようにコンパイルしてインストールします(メンドイので全部 root でやってます)。

# tar xvzf hts_engine_API-1.09.tar.gz
# cd hts_engine_API-1.09/
# ./configure --prefix=/usr/local/hts_engine_API
# make
# make install

OpenJTalk 本体を以下からダウンロードします。
http://osdn.jp/projects/sfnet_open-jtalk/downloads/Dictionary/open_jtalk_dic-1.08/open_jtalk_dic_utf_8-1.08.tar.gz/

ダウンロード後、下記のようにコンパイルしてインストールします。執筆時点の最新は ver.1.08 です。

# tar xvzf open_jtalk-1.08.tar.gz
# cd open_jtalk-1.08
# ./configure --prefix=/usr/local/open_jtalk --with-hts-engine-header-path=/usr/local/hts_engine_API/include --with-hts-engine-library-path=/usr/local/hts_engine_API/lib --with-charset=UTF-8
# make
# make install

さらに、付属の音声ファイルはあまりいい声でないので MMDAgent の Example に入っている htsvoice ファイルを使うことにします。下記からダウンロードします。
http://osdn.jp/projects/sfnet_mmdagent/downloads/MMDAgent_Example/MMDAgent_Example-1.4/MMDAgent_Example-1.4.zip/

ダウンロード後、Voice/mei/ ディレクトリ下の .htsvoice という拡張子のファイルを適当な場所にコピーしておきます。下記例は HTS をインストールしたディレクトリに配置しています。

# unzip MMDAgent_Example-1.4.zip
# mkdir /usr/local/hts_engine_API/hts_voice
# cp -r MMDAgent_Example-1.4/Voice/mei/*htsvoice /usr/local/hts_engine_API/hts_voice/

これで、上記した CGI が動くようになります。この CGI を /var/www/cgi-bin/ 下に配置して、http 経由でアクセスすると Raspberry Pi から声がします。つまり、iPhone アプリで iBeacon を検知した際に http でこの CGI にアクセスするようにすると、目的の動作が完成です。ということで後は iPhone アプリを作ります。

Microsoft Azure 上の Linux サーバ
iPhone アプリの経験が全く無いので、以下に公開されているソースを参考に作りました。
https://github.com/oggata/iBeaconDemo/blob/master/iBeaconDemo/ViewController.swift

開発環境は以下です。

  • Xcode 7.0 (beta)
  • Swift 2.0

iPhone アプリは iBeacon 端末からの距離を proximity という変数により取得します。これは何メートルとかの具体値ではなく、以下の 4 つの状態で表されます。

  • Immediate (至近距離: およそ 10cm以内)
  • Near (近距離: およそ 1m 以内)
  • Far (遠距離: およそ 1m 以上)
  • Unknown (不明)

今回のアプリは、iPhone 側で iBeacon を検知し、その距離が proximity が「Immediate(至近距離)」になったら CGI を叩く仕様にします。なお、一回 CGI を叩いたらフラグを立てて、そのフラグが立っている間は proximity が「Immediate(至近距離)」だったとしても CGI を叩かないようにします。 そうしないと、検知のためのループが回る毎に CGI を叩いてしまうからです。そして、そのフラグは proximity が「Immediate(至近距離) またはNear(近距離)」以外になった場合に解除するようにします。

ソースコードの公開は都合により割愛しますm(__)m

作成したソースコードを Xcode でビルドし、iPhone に仕込みます。Xcode7 以降、iPhone への自作アプリのインストールは、iOS Developer Program への登録なしで行えるようになったので、こういったテスト利用の場合は非常に楽ちんです。ということで、 iPhone 側の準備もできました。

全体的な動作確認

iPhone を iBeacon に近づけ、初めてその距離が約 10cm 以内程度になったときに RaspberryPi から「キョウノテンキハ ハレ デス」という声が聞こえるはずです。
それほどの感動も無いですね  ; -(

Be the first to comment

コメント投稿

Your email address will not be published.


*