IoT

2017年01月20日

さよなら Parse

はてなブックマークに登録

はじめに

世界中の利用者の指先と視線を凝固させた突然の発表からまもなく 1年、Parse.com の全サービスが いよいよ 2017年1月28日(土)に終了します。拡張性が高く高機能でありながら使い勝手の良い優れたサービスだったので終息が惜しまれます。

      https://parse.com/

手元では 2年ほど前に IoT の実験として試作した以下のしくみ「Anpi」で Parse.com を採り入れました。

  • mbed と Parse で作る高齢者世帯安否確認システム - 当ブログ
    一般に BaaS の主な目的はアプリケーションの対向サーバ側機能を代替・補完することにあり、サーバの管理運用やサーバ側コード開発に踏み込むコストを抑制しアプリ本体の開発に注力可能となることが利用者にとってのメリットですが、Parse にはサーバ上でユーザコードを実行することのできる「Cloud Code」というしくみが用意されています。(中略)今回作った装置は Parse サーバ上に設置した自作コードをそのまま呼び出して必要な処理を行っています。このように目的に応じて柔軟に利用できることが Parse の魅力のひとつと言ってよいでしょう。
この「Anpi」はシンプルながら実用性が高く現在もプライベートで大いに役立っています。そのためすでに Parse.com からの乗り換えを完了していますが、移行の過程で複数のプラットフォームを対象として再実装を横断的に試してみました。今後必要となった際にスムースに想起できる道具立ての選択肢は多いほうが好ましく、今回のように具体的な要件があればその実装を通じて未体験のサービスの特徴や個性を見定めやすいと考えたためです。この記事はそういった試みの記録で、日本国内ではまだ知名度の低いものにも触れています。興味のある方はご覧下さい。

※文中の記述はいずれも 2016年11月から12月の時点の状況に基づくものであり現在の事情とは異なる可能性があります。あらかじめご了承下さい。

手元の要件と移行先選定基準

「Anpi」において Parse.com サイドで行っていた処理は以下の内容です。

  • 屋内に設置ずみの装置が人感センサ反応時に最短 30 分間隔で送信してくる情報をデータストアへ保存
  • 定刻に直近 60 レコード分をレポートとして所定のアドレスへメール送信
  • 装置につないだボタンが長押しされたら所定のアドレスへメールで通知(緊急メール)
  • メール配信には Parse.com が 公式 API で連携している Mailgun サービスを利用

要件に特に煩雑なものはなくこれらを吸収可能なサービスはいくつも存在するでしょう。その上で、今回は次の三点を移行先選定の基準とすることにしました。

  • 運用に手がかからないこと
  • 柔軟性があること
  • 費用がかからないこと
絵に描いたようなユーザエゴではありますが、当時 Parse.com を選んだ理由はこれらのすべてを満たしていたためでもあります。予備調査を経て次のサービス・プラットフォームを検討の対象としました。

  1. IFTTT + Google Drive + Google Apps Script
  2. Kii Cloud
  3. back4app(+ AWS Lambda)
  4. Backand

なお、要件のうち「緊急メール」の発信については以前「今、ワンボタンの IoT デバイスが面白い」でも利用した SendGrid サービスの API を装置側のコードから直接叩くことにしました。メールの内容は決め打ちなので各サービスに仲介させるよりもそのほうが合理的と考えたためです。

以下、上のよっつを順番にピックアップしてみます。

移行先候補 1: IFTTT + Google Drive + Google Apps Script

もっともシンプルに要件を満たす道具立てとしてまず IFTTT と Google サービスの組み合わせを考えました。IFTTT 経由で Google Drive 上の Spreadsheet をデータストアとして利用し Google Apps Script でサーバサイドの処理を実行する内容です。

仮移行を通じての * 個人的な * 印象

GOOD !

  • シンプルかつ柔軟
  • Google, IFTTT ともに今後課金の発生する可能性がきわめて低い
  • Google, IFTTT ともに今後サービスが終息する可能性がきわめて低い
  • コードを含めすべてをブラウザ上で操作できるため運用上の自由度が高い
! GOOD ?

  • 今回の要件には十分だが規模の大きいデータを扱うには不向き
  • あくまでも SaaS とハブサービスの組み合わせであるため当然ながらプッシュ通知など一般的な IoT プラットフォームの提供する機能は代替できない

移行先候補 2: Kii Cloud

Kii CloudKii 株式会社様の提供する日本発の BaaS です。

仮移行を通じての * 個人的な * 印象

GOOD !

  • 多機能かつ無料枠が広い
  • Parse.com と同様に BaaS でありながらサーバ機能拡張が可能
  • サーバのリージョンを自由に選択可能であり特に中国リージョンの存在は大きい
  • キーバリュー形式のデータは使用容量にカウントされないため他のサービスとの併用にも好適か
  • アクセス制御機能が充実
  • 日本語のリソースが充実している

! GOOD ?

  • ドキュメントの情報量は豊かだが通読性がもうひとつ?リンクの張り方にも改善の余地がありそうな印象(このあたりが改善されればより利用しやすくなるかも)
  • 開発者ポータルは UI・機能ともに今後の進化が期待される
  • 開発用のコマンドラインツールが Node.js ベースなのは利点と欠点が半々くらい? バッチジョブのスケジュール変更といった操作はブラウザ上でできると嬉しい・・

移行先候補 3: back4app(+ AWS Lambda)

back4appBACK4APP SERVICOS DIGITAIS LTDA(本社 米カリフォルニア)の提供する BaaS です。

仮移行を通じての * 個人的な * 印象

GOOD !

  • 無料枠が広い
  • 最初から Parse.com の代替用として起ち上げられた純度の高い Parse Alternative であるため Parse.com との親和性が高く移行が相対的に容易
  • UI, ドキュメントがわかりやすい

! GOOD ?

  • 記事中のジョブ設定の問題など現時点では荒削りな側面も見られる
  • Parse.com を継承するサービスとしては良好だが、逆にそのことが独立した BaaS としての新鮮味や個性の発露を削いでいる印象も?

移行先候補 4: Backand

BackandModuBiz Ltd(本社イスラエル Tel Aviv)の提供する BaaS です。

仮移行を通じての * 個人的な * 印象

GOOD !

  • 多機能かつ拡張性に優れている上に非常に使いやすい
  • テスト・デバッグ機能も充実しており UI もわかり易い
  • ファイルホスティングまわりの操作以外はすべてブラウザ上で完結できるため機動性が高い
  • ユーザビリティは今回の中で一番。あとは可用性とスケーラビリティ次第か

! GOOD ?

  • 無料枠の狭さが残念。また、Prototype Plan $0 -> Hobby Plan $19/月 -> Work Plan $49/月 ... といった大きめの料金差に比して待遇差が小さめ
  • 運営側の内部的な指標である「Cache Memory」「Compute Units」のように利用者が主体的にコントロールすることの難しい指標がプランの基準に含まれているため利用目処を立てにくい(むしろ、利用するならはじめから UNLIMITED な料金プランを選ぶべき)
  • 日本のみならず国外でもまだあまりメジャーではなく情報がとても少ない。優れたサービスであるにもかかわらず世間への浸透圧が低い一因は上の料金体系にもあるのではないか?

おわりに

手元要件の Parse.com からの移行にあたり以上よっつの環境を試してみました。最終的にこの中のひとつを「Anpi」の乗り換え先に決定して現在に至ります。当初はどれを選んだのかを書くつもりでしたが無粋にも思いやめておくことにしました。いずれも質の高いサービスでそれぞれに十分なメリットがあります。

ほんの10年ほど前には影もなかったものが日進月歩で進化していく状況にリアルタイムで向き合っていると10年後の世界への想像が膨らみます。この時代の傍らを去っていく Parse.com をユーザのひとりとして敬意と感謝の念をもって見送りたいと思います。


(tanabe)

klab_gijutsu2 at 14:26|この記事のURLComments(0)TrackBack(0)
2016年12月22日

新しい Amazon Dash Button に「マイク」が残されている理由

はてなブックマークに登録

昨年以下の記事でピックアップした「Amazon Dash Button」が 2016年12月5日に日本国内でもリリースされ話題を集めています。

Amazon.co.jp での販売開始からほどなく Button の多くが品切れとなり 12月22日現在も入荷待ちの状態が続いています。 このデバイスを実質無料で配布する巨大多国籍企業 Amazon のパワーは凄まじいですね。

新旧 Dash Button の違い

現在提供されている Dash Button は 2015年に米国でデビューしたオリジナルとは異なるものです。上のブログ記事でも触れた Matthew Petroff 氏のサイトには新旧 Button それぞれについて詳細かつ網羅的な興味ぶかい記事が掲載されています。

The Amazon Dash Button it an Internet connected button that allows ordering a single product from Amazon.
           :
Others have already posted about disassembling it, so I’ll focus mostly on the electronics, since the aforementioned blog posts are missing high-resolution images of thecircuit board and don’t quite get some details correct.
Amazon updated the Dash Button’s hardware to revision two earlier this year, so I decided it was time for a new teardown (here’s last year’s teardown). The new product number is JK29LP; the old product number is JK76PL. While the form factor and case remained much the same, the internals changed substantially. The major highlights are a switch from Broadcom to Atmel chips, a switch from an Energizer lithium battery to a Duracell alkaline battery, and the addition of Bluetooth Low Energy.
           :

Overall, the new Dash Button appears to be a revision designed to reduce production cost, centered around a reduction in energy usage, which allows for use of a considerably cheaper, alkaline battery.
同氏の記事を参考にしながらまず新旧 Button の主な違いを整理してみます。
(※余談ながら、同じ記事に注目した数日前の GIGAZINE さんの記事に先ほど気がつきましたが、特にぶつかるものでもないため草稿の内容のまま書くことにします)

電源がリチウム一次乾電池からアルカリ乾電池に

旧 Dash Button には単四型リチウム一次乾電池が内蔵されていました。

http://dsas.blog.klab.org/archives/52233150.html

ちなみに Amazon Dash Button は 米エナジャイザー社Ultimate Lithium 乾電池 単4形 (二次電池ではない)1本を内蔵している。アルカリ電池に比べ「最大 9倍長持ち」を惹句とする 現時点でおそらく最強の乾電池。

データシートによると Ultimate Lithium AAA は「Max Discharge: 1.5 Amps Continuous, 2.0Amps Pulse (2 sec on / 8 sec off)」と高容量

現在の Dash Button ではこの高価な電池に代えて米デュラセル社の単四型アルカリ乾電池が使用されています。
旧:Energizer Ultimate Lithium AAA Battery
新:Duracell Ultra AAA Alkaline Battery
https://mpetroff.net/
(2016-12-14 時点での両者の小売価格の例) 電池交換は新 Button においても不可であるため上記の変更が使用寿命に及ぼす影響が気になりますが、Petroff 氏による新旧両 Button の消費電流実測結果(下グラフ:目盛幅の違いに注意)によればスリープ状態では新 Button が 2.0μA以下、旧 Button が 2.3μA以下と前者のほうが良好であり、また、ボタン押下後のアクティブ状態においては新 Button のほうがおおむね 10%前後レベルが高いものの処理を完了し再度スリープするまでの所要時間は旧 Button の半分以下と、電源仕様の変更に伴い電力消費を抑制する作りに変更されている様子が窺えます。
https://mpetroff.net/

マイクロコントローラ / Wi-Fi チップの変更, BLE チップ追加, Flash メモリ容量増加

新 Button では、以前の ST マイクロエレクトロニクス社製マイクロコントローラ STM32F205 が Atmel 社(2016年4月より Microchip Technology 傘下)製 ATSAMG55J19A-MU に、Broadcom 社製の Wi-Fi モジュール BCM943362WCD4 WICED が Atmel 社製 ATWINC1500B に変更されています。一方で 新 Button の Flash メモリは倍容量の 32Mビットに増強されており、また、旧 Button には存在しなかった BLE チップがセットアップ用に追加されています。これら一連の構成要素の変更は性能強化とコストダウンの両立を目的とした判断の結果と考えられます。


U5) マイクロコントローラ:ST STM32F205
U9) Wi-Fi モジュール:
Broadcom BCM943362WCD4 WICED
U6) フラッシュメモリ: Micron M25P16 (16Mbit)
マイクあり


U1) マイクロコントローラ:Atmel ATSAMG55J19A-MU
U19) Wi-Fi チップ:Atmel ATWINC1500B
U22) BLE チップ:Cypress CYBL10563-68FNXI
U15) フラッシュメモリ: Micron N25Q032 (32Mbit)
マイクあり
https://mpetroff.net/

セットアップ方法の変更

新旧 Button はセットアップの方法が異なります。Android または iOS 端末と公式ショッピングアプリを利用する点は共通ですが、新版が同アプリと Button との応酬に BLE 通信を使う内容であるのに対し、旧版では Android 環境では Wi-Fi 通信、iOS 環境では超音波通信を利用する仕様でした。ちなみに、旧版では Fire Phone もセットアップに利用することが可能でした。

旧 Dash Button のセットアップ

web.archive.org に旧 Button のセットアップ手順説明ページのキャッシュが残っています。 この内容から、ボタン長押しにより移行するセットアップモードにおいて旧 Button がダミー Wi-Fi アクセスポイント 兼 音声情報のリスナーとして振る舞っていたことがわかります。

※下の図は上記キャッシュの iOS 端末向けの説明箇所からの抜粋
※動画は旧版のセットアップの様子(Youtube 2015ー08-06 投稿, iPhone)3分20秒あたりから


http://www.amazon.com/
https://youtu.be/NSrdo5oNzsI?t=203
Android 端末を使ったセットアップに Dash Button のダミー Wi-Fi アクセスポイントへの一時的な切り替えが利用されている一方で iOS 端末でのセットアップに音声信号が採用されたのは JailBreak しない限り接続先の Wi-Fi アクセスポイントをプログラムから変更不可であることに起因するもののようです。

新 Dash Button のセットアップ

BLE チップが搭載された新 Button ではセットアップ時の公式アプリとの応酬に BLE 通信が利用されています。

この変更によって Android / iOS プラットフォームでの手順が統一されシンプルでスマートになった反面、旧版では特に言及されていなかった対象 OS バージョンが「iOS 8.3 or higher, Android 4.1 or higher」に限定される形となりました。また、BLE 非対応の Fire Phone(2015年下期販売終了)も手順説明から消えています。

現在の Dash Button セットアップ手順説明ページ

画面遷移

1
2
3
4
5
6
7
8
9

新 Dash Button にマイクが残されている理由

旧 Button が iOS 端末でのセットアップ時に音声信号を利用するためにマイクロフォンを内蔵していたことは理解できます。では、なぜ BLE 通信を利用する新 Button にもマイクが残されているのでしょう?使わない部品であれば製造コスト削減のためにも撤去するほうが合理的なはずです。まさか Amazon が何か良からぬことを企んでいるのでしょうか?

前掲の手順説明ページを読んでいるうちにふと以下の記述が気になりました。

Note: Some Dash Buttons and phones do not support Bluetooth connections. If your phone does not connect to your button, select Skip Bluetooth Setup. Then, follow the instructions in the app. Your phone then uses other connection options.
注: スマートフォンとDash Buttonが接続されない場合は、 Bluetoothをスキップを選択します。次に、アプリの画面に 表示される手順に従います。

公式アプリが Button と BLE 接続を確立できない場合はどのような所作となるのでしょう?興味を感じ試してみることにしました。

BLE 接続不可の状況を再現するもっとも簡単な方法は「Button からの BLE アドバタイジングを発生させない」ことです。つまり、アプリに求められたタイミングでわざと Button のセットアップモードを起動せずそのまま放置しておけばよいでしょう。

結論として、アプリは BLE 接続をしばらく試行した後に自動的に旧 Button でのセットアップシーケンスへ移行することがわかりました。 BLE 接続を諦めると、Android 版アプリは Button のダミー AP へ Wi-Fi 接続の切り替えを試み、iOS 版アプリは端末のスピーカーへ Button を接近させることを利用者へ促します。そのタイミングで新 Button をセットアップモードで起動すると旧スタイルでのセットアップ処理が滞りなく行われます。

つまり、新 Button のセットアップモードは旧 Button でのそれと同一の I/F を備えています。この実装は、BLE の利便性を取り入れつつも間口の広い旧版での機構を温存することによりセットアップ段階でのトラブルを可能な限り吸収することに加え、旧 Button - 新アプリ間の互換性をシンプルに保つことを目的とするものと考えられます。

Android, iOS 端末それぞれでこの操作を行った様子の動画を以下に示します。

  • 新 Button を旧スタイルでセットアップ - Android 版(2分9秒 環境音あり)
  • 新 Button を旧スタイルでセットアップ - iOS 版(1分33秒 環境音あり)
なお、アプリは「端末の Bluetooth 機能が OFF の場合」に以下のメッセージを表示します。ここで Android 版では「拒否」、iOS 版では「Bluetooth をスキップ」を選択することで同様に旧スタイルのセットアップシーケンスへ移行することを確認しました。
Andorid 版
iOS 版

図のように iOS 版に関しては本項の冒頭に引用した英語版の説明記事とほぼ整合しますが、日本語版記事は翻訳が十分ではなく(意図的なもの?)、また、Android ユーザが記事中のメッセージを目にする機会はありません。

米国内にのみ存在する旧 Button は電池寿命で徐々に消えていく過程にあります。また、BLE 非対応の端末も次第に世間から姿を消していくことでしょう。上に掲げた現 Dash Button の二段構えのセットアップ I/F はあるいは過渡的なものかもしれません。


(tanabe)
klab_gijutsu2 at 14:41|この記事のURLComments(0)TrackBack(0)
2016年08月30日

既成の BLE デバイスを自作プログラムから利用する試み

はてなブックマークに登録

前回の記事:
  • BLE デバイス間の通信内容をパケットレベルで読み解いてみる
    題材には低価格で出回っているありふれた Anti-Lost 系 BLE デバイス A を選びました。この小さなデバイスには LED・ブザーと押しボタンスイッチが 実装されており、対向スマホアプリとの間で双方向のやりとりが可能なつくりになっています。


    デバイス A の UI
前回は BLE パケットスニファを使って既成の BLE デバイス「A」と対向アプリ間の通信内容を記録しその精査を試みました。そこで得られた情報をもとに、今回は実験の一環としてデバイス A と連携する Android アプリの試作を行います。パケットログから窺える BLE セントラル側の所作を自作プログラムで再現することは論理的に可能でしょう。

デバイス A とアプリの連携に必要な処理

前回の記事から、デバイス A とアプリの連携に関する要素をピックアップしてみます。

  • アプリからデバイス A の LED・ブザーをコントロール

    • 「Scene 6: アプリからデバイス A の LED・ブザーを操作」
      Frame 616

      対向アプリ上の所定のボタンを押下すると、Frame 345, Frame 238 の示す、 "「Immediate Alert」サービス配下の「Alert Level」キャラクタリスティック" の Value Handle 0x0025 に値「2(High Alert)」が書き込まれる

      デバイス A の LED・ブザーはファームウェアレベルでこの Alert Level キャラクタリスティックに紐づけられている模様。High Alert 値が書き込まれるとそれに反応して短時間 LED とブザーが ON になる

    ー> デバイス A の提供する Immediate Alert サービス配下の Alert Level キャラクタリスティックにアプリから値 2 を書き込めばデバイス A の LED・ブザーが ON になり、値 0 を書き込めば OFF になる

  • デバイス A のボタン押下をアプリへ通知

    • 「Scene 5: デバイス A ボタン押下時のアプリへの通知を設定」
      Frame 491

      Frame 453、Frame 439、Frame 453 の示す通り、ハンドル 0x0036 は、 "ユーザ定義サービス 1(UUID = 0xFFE0)配下のユーザ定義キャラクタリスティック(UUID = 0xFFE1)配下の Client Characteristic Configuration Descriptor (CCCD)" である

      当該ユーザ定義キャラクタリスティック(UUID = 0xFFE1)のプロパティには Notify が設定されており、クライアントである対向アプリから CCCD 0x0036 に Notification bit (0x0001) を書き込んでおくことで、このキャラクタリスティックの値が更新された時に Value Handle である 0x0035 経由でアプリ側へ通知(Notification)が行われるようになる

    • 「Scene 7: デバイス A のボタンを押すとアプリへ通知」
      Frame 711

      デバイス A の物理ボタンはファームウェアレベルで Frame 439 の示す "ユーザ定義サービス 1(UUID = 0xFFE0)配下のユーザ定義キャラクタリスティック(UUID = 0xFFE1)" に紐づけられている模様。このボタンを押すと当該キャラクタリスティックの値がデバイス内部で更新され Frame 491 での仕込みに基づきアプリ側へ通知が行われる

    ー> 初期処理として、デバイス A の提供するユーザ定義サービス 1(UUID = 0xFFE0)配下のユーザ定義キャラクタリスティック(UUID = 0xFFE1)の持つ Client Characteristic Configuration Descriptor (詳細:1, 2) にアプリから値 0x0001を書き込んでおく。それ以降にデバイス A のボタンが押下されると当該キャラクタリスティック経由でデバイス A からアプリへ通知が行われる
処理そのものには特に難しそうな要素もなく、アプリからデバイス A への接続後に所定のキャラクタリスティック・デスクリプタの操作を適切に行うことがポイントとなりそうです。

サービス・キャラクタリスティック・デスクリプタの UUID について

BLE ネイティブの世界では所定のキャラクタリスティックやデスクリプタの I/O には各エントリに紐づけられたハンドルが使用されますが、抽象化された Android API での処理対象はオブジェクトです。所定のキャラクタリスティックやデスクリプタのオブジェクトの取得にはそれぞれの UUID が必要であるため、プログラムの記述に際しては、パケットログ上に記録された所定のハンドルがどの UUID のエントリのものであるかを正確に把握する必要があります。
また、サービス - キャラクタリスティック - デスクリプタは階層関係にあるため、所定のエントリのオブジェクトを取得する手続きは最上位にあるサービスのオブジェクトが常に起点となります。

以下に、パケットログから所定のサービス以下の各エントリの UUID を見つける方法と、各 UUID からそれぞれのエントリのオブジェクトを取得する Android コードの例を示します。

パケットログから所定のサービス以下の各エントリの UUID を拾う

  • 前回記事中のパケットログ Frame 229 では 0x0001 - 0xffff のハンドル空間を対象に Read By Group Type Request で GATT Primary Service Declaration を照会、そのレスポンスが Frame 231 です

           (クリックで大きく表示)

    ここではレスポンスに含まれる 3件のレコードのうち 2件めに注目してみます

    Opcode: Read By Group Type Response (0x11)
             :
    
    Attribute Data, Handle: 0x000c, Group End Handle: 0x000f
      Handle: 0x000c
      Group End Handle: 0x000f
      Value: 0118
             :
    
    • Primary Service Declaration の照会に対する Read By Group Type Response の「Value」には当該サービスの UUID が格納される
    • この例では 16ビット UUID = 0x1801 であり、これは、BLE 既定の Generic Attribute サービスを示す
    • 当該サービスはハンドルグループ 0x000c - 0x000f を占有する
  • Frame 287 では上記のハンドルグループ 0x000c - 0x000f を対象に Read By Type Request で GATT Characteristic Declaration を照会、そのレスポンスが Frame 289 です

           (クリックで大きく表示)

    レスポンスに注目します

    Opcode: Read By Type Response (0x09)
             :
    
    Attribute Data, Handle: 0x000d
      Handle: 0x000d
      Value: 200e00052a
    
    • Characteristic Declaration の照会に対する Read By Type Response の「Value」には当該キャラクタリスティックのプロパティ・Value Handle・UUID が格納される(詳細:1a, 2a, 1b

      (※表は BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 532 より)
    • ここでは「value: 200e00052a」につき、プロパティ = 0x20 (Indicate), Characteristic Value Handle = 0x000e, 16ビット UUID = 0x2A52
    • 16ビット UUID = 0x2A52 は、BLE 既定の Service Changed キャラクタリスティックを示す
  • Frame 302 では残りのハンドル 0x000f についての情報を GATT へ Find Information Request で照会、そのレスポンスが Frame 305 です

           (クリックで大きく表示)

    レスポンスに注目します

    Opcode: Find Information Response (0x05)
    UUID Format: 16-bit UUIDs (0x01)
    Handle: 0x000f
    UUID: Client Characteristic Configuration (0x2902)
    
    • ハンドル 0x000f は Client Characteristic Configuration Descriptor (CCCD) であり、この CCCD は直前の Indicate プロパティを持つ Service Changed キャラクタリスティックに属する
      (※CCCD の 16ビット UUID は 0x2902 固定

以上のことから、デバイス A 上の Generic Attribute サービスの構成は以下の内容であることがわかります。

Generic Attribute サービス(UUID = 0x1801)
  |  Handle Group = 0x000c - 0x000f
 |
  +-- Service Changed キャラクタリスティック(UUID = 0x2A52)
        |   Handle = 0x000d, Value Handle = 0x000e
        |
        +-- Client Characteristic Configuration Descriptor (UUID = 0x2902)
              Handle = 0x000f

所定の UUID のエントリのオブジェクトを取得する

ここまでに登場した UUID はすべて 16ビット値でしたが、16 ビット UUID は Bluetooth 用にアサインされている本来の 128ビット UUID の固定部分(BASE_UUID)を省略した表現です。

  • BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part B] page 227
    2.5 SEARCHING FOR SERVICES
           :
    2.5.1 UUID
           :
    To reduce the burden of storing and transferring 128-bit UUID values, 
    a range of UUID values has been pre-allocated for assignment to 
    often-used, registered purposes. The first UUID in this pre-allocated
    range is known as the Bluetooth Base UUID and has the value 
    00000000-0000-1000-8000-00805F9B34FB,
           :
    
つまり、Generic Attribute サービスの 16ビット UUID「0x1801」は、128ビット UUID「00001801-0000-1000-8000-00805F9B34FB」です。

Android API を用いて前出の Generic Attribute サービスと Service Changed キャラクタリスティック、およびその配下の Client Characteristic Configuration Descriptor のオブジェクトを取得するコードのイメージを示します。

private BluetoothGatt mBtGatt;

private BluetoothGattCharacteristic mChServiceChanged;
private BluetoothGattDescriptor mCCCD;

// Generic Attribute サービス の UUID
private UUID mUuidSvcGenericAttribute = UUID.fromString("00001801-0000-1000-8000-00805f9b34fb");
// Service Changed キャラクタリスティックの UUID 
private UUID mUuidChServiceChanged    = UUID.fromString("00002a52-0000-1000-8000-00805f9b34fb");
// Client Characteristic Configuration Descriptor の UUID (固定値)
private UUID mUuidCCCD                = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
                 :

private BluetoothGattCallback mGattCallback = new bleGattCallback();
                 :

// デバイス A へ接続後、GATT の提供する各サービス以下の一覧を取得
mBtGatt.discoverServices();(mCtx, false, mGattCallback);
                 :

// GATT イベントハンドラ
private class bleGattCallback extends BluetoothGattCallback {
  @Override
  // GATT サービス一覧取得完了
  public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    super.onServicesDiscovered(gatt, status);

    // デバイス A の Generic Attribute サービスの
    // Service Changed キャラクタリスティックオブジェクトを取得
    BluetoothGattService svc = gatt.getService(mUuidSvcGenericAttribute);
    mChServiceChanged = svc.getCharacteristic(mUuidChServiceChanged);

    // Service Changed キャラクタリスティックの
    // Client Characteristic Configulation Descriptor を取得
    mCCCD = mChServiceChanged.getDescriptor(mUuidCCCD);
                 :
  }

ちなみに、アプリ開発の初期に BluetoothGattCallback の onServicesDiscovered() に次の要領のコードを挿入して 各 GATT サービス配下の全エントリの UUID を階層的に出力し保存しておくと何かと便利です。

public void onServicesDiscovered(BluetoothGatt gatt, int status) {
  super.onServicesDiscovered(gatt, status);

  List<BluetoothGattService> serviceList = gatt.getServices();

  Log.d(TAG, "onServicesDiscovered: serviceList.size=" + serviceList.size());

  for (BluetoothGattService s : serviceList) {
    Log.d(TAG, "onServicesDiscovered: svc uuid=" + s.getUuid().toString());
    List<BluetoothGattCharacteristic> chlist = s.getCharacteristics();
    Log.d(TAG, "onServicesDiscovered: chrlist.size=" + chlist.size());

    for (BluetoothGattCharacteristic c : chlist) {
      UUID uuid = c.getUuid();
      Log.d(TAG, "onServicesDiscovered:  chr uuid=" + uuid.toString());
      List<BluetoothGattDescriptor> dlist = c.getDescriptors();

      Log.d(TAG, "onServicesDiscovered:  desclist.size=" + dlist.size());
      for (BluetoothGattDescriptor d : dlist) {
        Log.d(TAG, "onServicesDiscovered:   desc uuid=" + d.getUuid());
      }
    }
  }

デバイス A への接続後に上のコードを実行した際のログです。(※見やすさのためにサービスごとに改行を挿入)

当然ながら、この内容は前回採取したパケットログ内の各エントリの情報と符合しています。

作成したアプリ

以上の内容にもとづいてアプリを作成しました。ごくシンプルなものですが期待通りに動いています。

ソースコード一式

動画:動作の様子

作成した Android アプリとデバイス A の連携の様子を収めた動画です。デバイス A は UI 部分のみを露出しています。
(34秒 アラーム音あり 音量注意)
    

メモ:実装手順など

private BluetoothAdapter mBtAdapter;
private BluetoothLeScanner mBtScanner;
private BluetoothDevice mBtDevice;
private BluetoothGatt mBtGatt;

1. BluetoothAdapter 〜 BluetoothLeScanner を取得

  • BluetoothAdapter - developer.android.com
    Represents the local device Bluetooth adapter. The BluetoothAdapter lets you
    perform fundamental Bluetooth tasks, such as initiate device discovery, query
    a list of bonded (paired) devices, instantiate a BluetoothDevice using a known
    MAC address, and create a BluetoothServerSocket to listen for connection
    requests from other devices, and start a scan for Bluetooth LE devices.
                      :
    
    static BluetoothAdapter	getDefaultAdapter()
    
    Get a handle to the default local Bluetooth adapter. 
    
  • BluetoothAdapter - getBluetoothLeScanner - developer.android.com
    BluetoothLeScanner getBluetoothLeScanner ()
    
    Returns a BluetoothLeScanner object for Bluetooth LE scan operations. 
    
    • BluetoothLeScanner - developer.android.com
      BluetoothLeScanner
      
      This class provides methods to perform scan related operations for Bluetooth
      LE devices. An application can scan for a particular type of Bluetooth LE
      devices using ScanFilter. It can also request different types of callbacks
      for delivering the result. 
      
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
mBtScanner = mBtAdapter.getBluetoothLeScanner();

2. アドバタイジングパケットのスキャン 〜 対象とする BluetoothDevice を取得

private ScanCallback mScanCallback = new bleScanCallback();
                 :

mBtScanner.startScan(mScanCallback);
                 :

private class bleScanCallback extends ScanCallback {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        if (.......) {
          mBtDevice = result.getDevice();
        }
    }
    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Log.e(TAG, "onScanFailed: err=" + errorCode);
    }
}

3. デバイスへの接続

  • BluetoothDevice - connectGatt - developer.android.com
    BluetoothGatt connectGatt (Context context, 
                    boolean autoConnect, 
                    BluetoothGattCallback callback)
    
    Connect to GATT Server hosted by this device. Caller acts as GATT client. 
    The callback is used to deliver results to Caller, such as connection status
    as well as any further GATT client operations. The method returns a
    BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client
    operations.
                      :
    callback 	BluetoothGattCallback: GATT callback handler that will
    receive asynchronous callbacks.
                      :
    
  • BluetoothGattCallback - developer.android.com
    •  onConnectionStateChange - developer.android.com
      void onConnectionStateChange (BluetoothGatt gatt, 
                      int status, 
                      int newState)
      
      Callback indicating when GATT client has connected/disconnected to/from
      a remote GATT server.
                        :
      newState 	int: Returns the new connection state. Can be one of
      STATE_DISCONNECTED or STATE_CONNECTED
                        :
      
private BluetoothGattCallback mGattCallback = new bleGattCallback();
                 :

mBtGatt = mBtDevice.connectGatt(mCtx, false, mGattCallback);
                 :

private class bleGattCallback extends BluetoothGattCallback {
  @Override
  public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                      int newState) {
    super.onConnectionStateChange(gatt, status, newState);
    if (newState == BluetoothProfile.STATE_CONNECTED) {
      // 接続確立 - デバイスの GATT サービス一覧の取得へ
    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
      // 切断完了の処理
    }
  }
                 :

4. GATT サービス - キャラクタリスティック - デスクリプタを探索 〜 必要なオブジェクトを取得

  • BluetoothGatt - discoverServices - developer.android.com
    boolean discoverServices ()
    
    Discovers services offered by a remote device as well as their
    characteristics and descriptors.
    
    This is an asynchronous operation. Once service discovery is completed, 
    the onServicesDiscovered(BluetoothGatt, int) callback is triggered. 
    If the discovery was successful, the remote services can be retrieved
    using the getServices() function. 
                      :
    
  • BluetoothGattCallback - onServicesDiscovered - developer.android.com
    void onServicesDiscovered (BluetoothGatt gatt, 
                    int status)
    
    Callback invoked when the list of remote services, characteristics
    and descriptors for the remote device have been updated, ie new services
    have been discovered.
                      :
    
  • BluetoothGatt - getService - developer.android.com
    getService
    
    BluetoothGattService getService (UUID uuid)
    
    Returns a BluetoothGattService, if the requested UUID is supported by
    the remote device.
    
    This function requires that service discovery has been completed for
    the given device.
    
    If multiple instances of the same service (as identified by UUID) exist,
    the first instance of the service is returned.
    
    Requires BLUETOOTH permission.
    
    Parameters
    uuid 	UUID: UUID of the requested service
    Returns
    BluetoothGattService 	BluetoothGattService if supported, or null if the
                            requested service is not offered by the remote device. 
    
    • BluetoothGattService - getCharacteristic - developer.android.com
      getCharacteristic
      
      BluetoothGattCharacteristic getCharacteristic (UUID uuid)
      
      Returns a characteristic with a given UUID out of the list of
      characteristics offered by this service.
      
      This is a convenience function to allow access to a given characteristic
      without enumerating over the list returned by getCharacteristics()
      manually.
      
      If a remote service offers multiple characteristics with the same UUID,
      the first instance of a characteristic with the given UUID is returned.
      
      Parameters
      uuid 	UUID
      Returns
      BluetoothGattCharacteristic  GATT characteristic object or null if no
                                   characteristic with the given UUID was found. 
      
      
      • BluetoothGattCharacteristic - getDescriptor - developer.android.com
        getDescriptor
        
        BluetoothGattDescriptor getDescriptor (UUID uuid)
        
        Returns a descriptor with a given UUID out of the list of descriptors
        for this characteristic.
        
        Parameters
        uuid  UUID
        Returns
        BluetoothGattDescriptor  GATT descriptor object or null if no
                                 descriptor with the given UUID was found. 
        
private BluetoothGattCharacteristic mChAlertLevel = null;
private BluetoothGattCharacteristic mChUser1 = null;
private BluetoothGattDescriptor mDescUser1 = null;

// デバイス A の提供するサービス・キャラクタリスティック群の UUID より
private UUID mUuidSvcImAlert   = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
private UUID mUuidChAlertLevel = UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
private UUID mUuidSvcUser1     = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb");
private UUID mUuidChUser1      = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb");
// UUID for Client Characteristic Configuration Descriptor
// - BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 537
private UUID mUuidCCCD         = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
                 :

private BluetoothGattCallback mGattCallback = new bleGattCallback();
                 :

mBtGatt.discoverServices();(mCtx, false, mGattCallback);
                 :

private class bleGattCallback extends BluetoothGattCallback {
  @Override
  // GATT サービス一覧取得完了
  public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    super.onServicesDiscovered(gatt, status);

    // デバイス A の Immediate Alert サービスの
    // Alert Level キャラクタリスティックオブジェクトを取得
    BluetoothGattService svc = gatt.getService(mUuidSvcImAlert);
    mChAlertLevel = svc.getCharacteristic(mUuidChAlertLevel);

    // デバイス A のユーザ定義サービス 1 の ユーザ定義キャラクタリスティックの
    // Client ​Characteristic Configulation Descriptor を取得
    svc = gatt.getService(mUuidSvcUser1);
    mChUser1 = svc.getCharacteristic(mUuidChUser1);
    mDescUser1 = mChUser1.getDescriptor(mUuidCCCD);
                 :
  }

5. 所定のキャラクタリスティックからの通知を有効に

// デバイス A への Alert 指示用
private byte[] mCmdAlertOff = new byte[] {(byte)0x00}; // OFF (No Alert)
private byte[] mCmdAlertOn  = new byte[] {(byte)0x02}; // ON (High Alert)
                 :

  // デバイス A のユーザ定義サービス 1 の ユーザ定義キャラクタリスティックの
  // Client ​Characteristic Configulation Descriptor を取得
  svc = gatt.getService(mUuidSvcUser1);
  mChUser1 = svc.getCharacteristic(mUuidChUser1);
  mDescUser1 = mChUser1.getDescriptor(mUuidCCCD);

  // 同キャラクタリスティックの値変更時の通知を有功にして
  // 同 CCCD へ ENABLE_NOTIFICATION_VALUE を書き込んで通知へ待機
  mBtGatt.setCharacteristicNotification(mChUser1, true);
  byte[] val = new byte[] {(byte)0x01, (byte)0x00};
  mDescUser1.setValue(val);
  mBtGatt.writeDescriptor(mDescUser1);
                 :

private class bleGattCallback extends BluetoothGattCallback {
  @Override
  public void onCharacteristicChanged (BluetoothGatt gatt,
                                       BluetoothGattCharacteristic ch) {
    Log.d(TAG, "onCharacteristicChanged");
    // デバイス A のユーザ定義キャラクタリスティック 1 からの通知を受信
    if (ch == mChUser1) {
        Toast.makeText(mCtx, "* P U S H E D *", Toast.LENGTH_SHORT).show();
    }
  }
  @Override
  public void onDescriptorWrite (BluetoothGatt gatt,
                                BluetoothGattDescriptor desc,
                                int status) { // writeDescriptor() 結果
    super.onDescriptorWrite(gatt, desc, status);
    Log.d(TAG, "onDescriptorWrite: sts=" + status);
    if (desc == mDescUser1) {
      // デバイス A のユーザ定義サービス 1 の ユーザ定義キャラクタリスティックの
      // Client ​Characteristic Configulation Descriptor への書き込みが完了
    }
  }
                 :

6. 所定のキャラクタリスティックへの書き込み

// デバイス A への Alert 指示用
private byte[] mCmdAlertOff = new byte[] {(byte)0x00}; // OFF (No Alert)
private byte[] mCmdAlertOn  = new byte[] {(byte)0x02}; // ON (High Alert)
                 :

  mChAlertLevel.setValue(mCmdAlertOn);
  mBtGatt.writeCharacteristic(mChAlertLevel);
                 :

private class bleGattCallback extends BluetoothGattCallback {
  @Override
  public void onCharacteristicWrite(BluetoothGatt gatt,
                                    BluetoothGattCharacteristic ch,
                                    int status) { // writeCharacteristic 結果
    super.onCharacteristicWrite(gatt, ch, status);
    if (ch == mChAlertLevel) { 
      Log.d(TAG, "mChAlertLevel: onCharacteristicWrite: sts=" + status);
    }
  }
                 :

(tanabe)
klab_gijutsu2 at 14:28|この記事のURLComments(0)TrackBack(0)
2016年08月24日

BLE デバイス間の通信内容をパケットレベルで読み解いてみる

はてなブックマークに登録

前回の記事:
パケットスニファを使って実際に BLE 機器間の応酬を追ってみました。備忘をかねて以下に情報を控えます。題材には低価格で出回っているありふれた Anti-Lost 系 BLE デバイス A を選びました。この小さなデバイスには LED・ブザーと押しボタンスイッチが実装されており、対向スマホアプリとの間で双方向のやりとりが可能なつくりになっています。


デバイス A の UI

操作内容

デバイス A と Android 端末を使って以下の操作を行いスニファで BLE パケットのログを採取しました。

  1. デバイス A の電源を入れる
  2. Android 端末上の対向アプリからデバイスを探索しデバイス A との接続を確立
  3. アプリからデバイス A の LED+ブザーを ON にする
  4. デバイス A のボタンを押しアプリへ通知を送る
  5. アプリ上でデバイス A との接続を切断

応酬の全体像

スニファの Wireshark ログより

上の操作時に収集したパケットログの全体図です。   (クリックで大きく表示)

    

要約

パケットログの内容の要約を以下に示します。   (クリックで大きく表示)

  1. デバイス A の電源を入れる
    • デバイス A がアドバタイジングを開始 (ADV_IND)
  2. スマホ上の対向アプリからデバイスを探索しデバイス A との接続を確立
    • デバイスのスキャン (SCAN_REQ / SCAN_RSP) を経て接続要求 (CONNECT_REQ) 〜接続が確立するとデバイス A はアドバタイジングを停止
    • 機能情報を交換 (LL_FEATURE_REQ / LL_FEATURE_RSP)
    • デバイス A の提供する GATT プライマリサービス一覧を取得
    • 各サービス配下のエントリ(Included Service, キャラクタリスティック)一覧を取得
    • 各キャラクタリスティック配下のデスクリプタを取得
    • デバイス A のボタンが押された時に通知されるよう所定の Client Characteristic Configuration descriptor へアプリから Notification Bit 0x0001 を書き込む
  3. アプリからデバイス A の LED+ブザーを ON にする
    • アプリ上の所定のボタンが押されたタイミングでアプリはデバイス A の Immediate Alert サービス配下の Alert Level キャラクタリスティックへ値 2 (High Alert)を書き込む。これにより一定時間 A の LED とブザーが ON になる
  4. デバイス A のボタンを押しアプリへ通知を送る
    • デバイス A のボタンが押されたタイミングでデバイス A は自機のユーザ定義サービス 1 (UUID=0xFFE0) 配下のユーザ定義キャラクタリスティック (UUID=0xFFE1) へ値 1 を書き込む。当該キャラクタリスティック配下の Client Characteristic Configuration descriptor にはアプリ側から予め Notification Bit がセットされているためアプリへ通知される
  5. アプリ上でデバイス A との接続を切断
    • アプリからデバイス A へ LL_TERMINATE_IND を送る

応酬の詳細

全体の流れを見渡したところで次に個々のパケットデータを読み進めていきます。

  • 以下の記事には Wireshark のスクリーンショットをログとして添えています  (それぞれクリックで大きく表示)
  • ログには BLE パケットデータ本体に加え nRF Sniffer が解析の便宜のためにログへ出力する「Nordic BLE sniffer meta」ヘッダが含まれています
    • nRF Sniffer User Guide v1.1 (PDF) - www.nordicsemi.com Page 8 より
      4   Using Wireshark
      
      All BLE packets detected by the Sniffer are passed to Wireshark and are
      wrapped in a header which contains useful meta-information not present
      in the BLE packet itself. Wireshark dissects the packets and separates
      the actual packet from the meta-information.
                   :
      
  • 記事には下記資料からの抜粋へのリンクを参照用に適宜挿入しています
  • 随所に「https://developer.bluetooth.org/gatt/」下のサービス・キャラクタリスティックの記事へのリンクを張っていますが、2016年7月頃までは参照可能だった各記事が現在はリンク切れになっており(再構成中?)、さらに現時点では代替ページが見当たらないためリンク先はやむなく http://web.archive.org/ 上のキャッシュとしています。ただし web.archive.org 上にも保存履歴のない記事については一階層上のページへのリンクを張っています
    例:「Link Loss」サービスの単独ページのキャッシュは web.archive.org にも見当たらないため、上位のサービス一覧ページのキャッシュをターゲットに

局面

Scene 1: デバイス A のアドバタイジングと SCAN_REQ / SCAN_RSP

Frame 124 - 126 はデバイス A 発のアドバタイジングパケット

  • 全 40 チャネルのうちアドバタイジングパケット用の 37, 38, 39 の 3 チャネルが順次使用されていることが見てとれる
  • 「0x8e89bed6」の Access Address はアドバタイジングチャネルパケットで使用される固定値
  • デバイス A のアドバタイジングのタイプは Connectable Undirected(詳細: 1, 2, 3, 4) であることを示す一般的な「ADV_IND」である
  • 「Advertising Data: 020106020a000702031802180418」に含まれる不完全 16ビットサービスクラス UUID は次の内容
    • 16 bit uuid: 0x1803 --> 既定の「Link Loss」サービス
    • 16 bit uuid: 0x1802 --> 既定の「Immediate Alert」サービス
    • 16 bit uuid: 0x1804 --> 既定の「Tx Power」サービス
Frame 124

    

Frame 125

    

Frame 126

    

Frame 127

接続に際し対向アプリがデバイス A に対して SCAN_REQ を発行

    

Frame 128

デバイス A が対向アプリからの SCAN_REQ に対し SCAN_RSP を返信。ここでは ScanRspData としてデバイスのローカルネームが渡されている

    

Scene 2: 接続の確立と情報交換

Frame 217

対向アプリがデバイス A に対して CONNECT_REQ(詳細:1a, 2a, 1b, 2b)を発行して接続を試みる

  • 「Connection Request」中の「Connection Access Address」には、接続確立後のデータ通信において Access Address として双方が一意に使用する任意のアドレスが含まれる。ここでは「0xa6a188c8」
  • 「Connection Request」中の「Channel map」には接続確立後のデータ通信で使用するチャネル番号のリストが提示される。ここでは 0 - 36 の全データチャネルが指定されている
  • その他のパラメータについては上のリンクの資料を参照のこと
  • CONNECT_REQ に対する直接のレスポンスは発生しない。接続が確立するとデータ通信アイドル時にスレーブ - マスタ間で空パケット(Empty PDU)の応酬が始まるためこれが接続成否の判定に用いられる

    

Frame 218, 224 は接続確立後の最初の応酬。接続が確立すると対向アプリはマスタ、デバイス A はスレーブの位置づけとなる。ここでは リンク層制御 PDU の LL_FEATURE_REQ および LL_FEATURE_RSP によりマスタ - スレーブ間で Feature Exchange を行っている。ここでは双方とも「Supported feature: LE Encryption (0)」のみを提示(ただしここでは以降の通信において暗号化は行われていない)
Frame 218

    

Frame 224

    

Frame 222 ではアプリ側が「Device Name」を要求し Frame 226 でデバイス A 側がそれに応えている。
「Device Name」は、既定の Generic Access(GAP)サービス配下の既定のキャラクタリスティック(UUID = 0x2A00)であり、GATT サーバは必ず GAP サービスを含んでいる(詳細:1, 2
Frame 222

  Opcode: Read By Type Request (0x08)
  Starting Handle: 0x0001
  Ending Handle: 0xffff
  UUID: Device Name (0x2a00)

    

Frame 226

  Opcode: Read By Type Response (0x09)
  Length: 6
  Attribute Data, Handle: 0x0003
      Handle: 0x0003
      Value: ********

    

(GAP サービスおよび Device Name キャラクタリスティックは後続の Frame 231, Frame 266 であらためて表に現れる)

Scene 3: デバイス A の GATT サービス群の取得

(Scene 3, Scene 4 共通の基礎知識)
BLUETOOTH SPECIFICATION Version 4.2(PDF) [Vol 3, Part G] より
    2.6 GATT PROFILE HIERARCHY
       2.6.1 Overview, 2.6.2 Service, 2.6.3 Included Services, 2.6.4 Characteristic
  3 SERVICE INTEROPERABILITY REQUIREMENTS
     3.1 SERVICE DEFINITION, 3.2 INCLUDE DEFINITION, 3.3 CHARACTERISTIC DEFINITION
       3.3.1 Characteristic Declaration
         3.3.1.1 Characteristic Properties, 3.3.1.2 Characteristic Value Attribute Handle, 3.3.1.3 Characteristic UUID
       3.3.2 Characteristic Value Declaration

初期処理として、マスタはスレーブの提供するサービス一覧を取得する (詳細:1, 2, 3

  • アプリ側が、デバイス A の GATT サーバの提供する公開サービス(プライマリサービス)の問合せを開始。 以降、GATT サーバ上の所定のサービスへアクセスするためのハンドルのアドレスと当該サービスの種類を識別する UUID を順次取得する
  • ハンドルのアドレス空間は 0x0000 - 0xFFFF であり GATT サーバ側の応答に応じて照会範囲を絞っていく


      (図は 「BLUETOOTH SPECIFICATION Version 4.2」 より引用)

Frame 229

まずハンドル 0x0001 - 0xffff 全範囲についてプライマリサービスを照会

    Opcode: Read By Group Type Request (0x10)
    Starting Handle: 0x0001
    Ending Handle: 0xffff
    UUID: GATT Primary Service Declaration (0x2800)

    

Frame 231

以下のみっつのサービスの情報が得られた

  • ハンドルグループ 0x0001 - 0x000b:既定の「Generic Access」サービス(UUID = 0x1800)が使用
  • ハンドルグループ 0x000c - 0x000f:既定の「Generic Attribute」サービス(UUID = 0x1801)が使用
  • ハンドルグループ 0x0010 - 0x0022:既定の「Device Information」サービス(UUID = 0x180A)が使用

    Opcode: Read By Group Type Response (0x11)
    Length: 6
    Attribute Data, Handle: 0x0001, Group End Handle: 0x000b
        Handle: 0x0001
        Group End Handle: 0x000b
        Value: 0018
    Attribute Data, Handle: 0x000c, Group End Handle: 0x000f
        Handle: 0x000c
        Group End Handle: 0x000f
        Value: 0118
    Attribute Data, Handle: 0x0010, Group End Handle: 0x0022
        Handle: 0x0010
        Group End Handle: 0x0022
        Value: 0a18

    

Frame 235

続けてハンドル 0x0023 - 0xffff 範囲のプライマリサービスを照会

    

Frame 238

以下のみっつのサービスの情報が得られた

  • ハンドルグループ 0x0023 - 0x0025:既定の「Immediate Alert」サービス(UUID = 0x1802)が使用
  • ハンドルグループ 0x0026 - 0x002a:既定の「Tx Power」サービス(UUID = 0x1804)が使用
  • ハンドルグループ 0x002b - 0x002d:既定の「Link Loss」サービス(UUID = 0x1803)が使用

    

Frame 242

続けてハンドル 0x002e - 0xffff 範囲のプライマリサービスを照会

    

Frame 247

以下のみっつのサービスの情報が得られた

  • ハンドルグループ 0x002e - 0x0032:既定の「Battery Service」サービス(UUID = 0x180F)が使用
  • ハンドルグループ 0x0033 - 0x0037:ユーザ定義のサービス 1(UUID = 0xFFE0)が使用
  • ハンドルグループ 0x0038 - 0x003a:ユーザ定義のサービス 2(UUID = 0xFFF0)が使用

    

Frame 250

続けてハンドル 0x003b - 0xffff 範囲のプライマリサービスを照会

    

Frame 253

「Attribute Not Found (0x0a)」のエラーが返る。これ以上プライマリサービスが存在しないことを意味する。サービスの照会はここまで

    Opcode: Error Response (0x01)
    Request Opcode in Error: Read By Group Type Request (0x10)
    Handle in Error: 0x003b
    Error Code: Attribute Not Found (0x0a)

    

Scene 4: デバイス A 各サービス配下のキャラクタリスティック - デスクリプタの取得

(Scene 3, Scene 4 共通の基礎知識)
BLUETOOTH SPECIFICATION Version 4.2(PDF) [Vol 3, Part G] より
    2.6 GATT PROFILE HIERARCHY
       2.6.1 Overview, 2.6.2 Service, 2.6.3 Included Services, 2.6.4 Characteristic
  3 SERVICE INTEROPERABILITY REQUIREMENTS
     3.1 SERVICE DEFINITION, 3.2 INCLUDE DEFINITION, 3.3 CHARACTERISTIC DEFINITION
       3.3.1 Characteristic Declaration
         3.3.1.1 Characteristic Properties, 3.3.1.2 Characteristic Value Attribute Handle, 3.3.1.3 Characteristic UUID
       3.3.2 Characteristic Value Declaration

次に、収集ずみの各プライマリサービスのハンドルグループごとに以下を行う (詳細:1, 2, 3, 4, 5, 6

  • 所定のサービスに含まれる Included Service の照会


      (図は 「BLUETOOTH SPECIFICATION Version 4.2」 より引用)

  • 所定のサービスに含まれるキャラクタリスティックの照会


      (図は 「BLUETOOTH SPECIFICATION Version 4.2」 より引用)

    • 所定のキャラクタリスティックに含まれるデスクリプタの照会


        (図は 「BLUETOOTH SPECIFICATION Version 4.2」 より引用)

Frame 255

前掲の Frame 231 の示すハンドルグループ 0x0001 - 0x000b の「Generic Access」サービス内の Included Service を照会

    Opcode: Read By Type Request (0x08)
    Starting Handle: 0x0001
    Ending Handle: 0x000b
    UUID: GATT Include Declaration (0x2802)

    

Frame 259

当該サービス内に Included Service は存在しない(注:図のログではスニファがパケットデータを取りこぼしている)

    

Frame 262

ハンドルグループ 0x0001 - 0x000b の「Generic Access」サービス内のキャラクタリスティックを照会

    Opcode: Read By Type Request (0x08)
    Starting Handle: 0x0001
    Ending Handle: 0x000b
    UUID: GATT Characteristic Declaration (0x2803)

    

Frame 266

以下のみっつのキャラクタリスティックの情報が得られた

(※キャラクタリスティック の Value フィールドの構成は前掲の「BLUETOOTH SPECIFICATION Version 4.2] - [Vol 3, Part G] p.532 「3.3.1 Characteristic Declaration」 に、プロパティ値の意味は同じく p.533 「3.3.1.1 Characteristic Properties」 に説明あり)

  • ハンドル:0x0002
    「value: 080300002a」より、プロパティ = 0x08 (Write), Characteristic Value Handle = 0x0003
    UUID = 0x2A00 = 既定の「Device Name
  • ハンドル:0x0004
    「value: 020500012a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0005
    UUID = 0x2A01 = 既定の「Appearance
  • ハンドル:0x0006
    「value: 020700042a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0007
    UUID = 0x2A04 = 既定の「Peripheral Preferred Connection Parameters

    Opcode: Read By Type Response (0x09)
    Length: 7
    Attribute Data, Handle: 0x0002
      Handle: 0x0002
      Value: 080300002a
    Attribute Data, Handle: 0x0004
      Handle: 0x0004
      Value: 020500012a
    Attribute Data, Handle: 0x0006
      Handle: 0x0006
      Value: 020700042a

    

Frame 270

続けてハンドル 0x0007 - 0x000b 範囲のキャラクタリスティックを照会

    

Frame 275

以下のふたつのキャラクタリスティックの情報が得られた

  • ハンドル:0x0008
    「value: 020900022a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0009
    UUID = 0x2A02 = 既定の「Peripheral Privacy Flag
  • ハンドル:0x000a
    「value: 020b00032a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x000b
    UUID = 0x2A03 = 既定の「Reconnection Address

    

Frame 279

前掲の Frame 231 の示すハンドルグループ 0x000c - 0x000f の「Generic Attribute」サービス内の Included Service を照会

    

Frame 284

当該サービス内に Included Service は存在しない

    

Frame 287

ハンドルグループ 0x000c - 0x000f の「Generic Attribute」サービス内のキャラクタリスティックを照会

    

Frame 289

以下のキャラクタリスティックの情報が得られた

  • ハンドル:0x000d
    「value: 200e00052a」より、プロパティ = 0x20 (Indicate), Characteristic Value Handle = 0x000e
    UUID = 0x2A52 = 既定の「Service Changed

    

Frame 293

続けてハンドル 0x000e - 0x000f 範囲のキャラクタリスティックを照会

    

Frame 298

当該サービス内にはこれ以上キャラクタリスティックは存在しない

    

Frame 302

Frame 289 の示す通り 0x0000e は Service Changed キャラクタリスティックの Value Handle であり、残る0x000f に関する情報を GATT に問い合わせてみる (詳細:1, 2

    Opcode: Find Information Request (0x04)
    Starting Handle: 0x000f
    Ending Handle: 0x000f

    

Frame 305

0x000f は Service Changed キャラクタリスティックの Client Characteristic Configuration Descriptor (UUID = 0x2902) (詳細:1, 2) である旨の情報が得られた

    Opcode: Find Information Response (0x05)
    UUID Format: 16-bit UUIDs (0x01)
    Handle: 0x000f
    UUID: Client Characteristic Configuration (0x2902)

    

Frame 308

前掲の Frame 231 の示すハンドルグループ 0x0010 - 0x0022 の「Device Information」サービス内の Included Service を照会

    

Frame 312

当該サービス内に Included Service は存在しない

    

Frame 315

ハンドルグループ 0x0010 - 0x0022 の「Device Information」サービス内のキャラクタリスティックを照会

    

Frame 319

以下のみっつのキャラクタリスティックの情報が得られた

  • ハンドル:0x00011
    「value: 021200292a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0012
    UUID = 0x2A29 = 既定の「Manufacturer Name String
  • ハンドル:0x00013
    「value: 021400242a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0014
    UUID = 0x2A24 = 既定の「Model Number String
  • ハンドル:0x00015
    「value: 021600252a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0014
    UUID = 0x2A25 = 既定の「Serial Number String

    

Frame 323

続けてハンドル 0x0016 - 0x0022 範囲のキャラクタリスティックを照会

    

Frame 326

以下のみっつのキャラクタリスティックの情報が得られた

  • ハンドル:0x00017
    「value: 021800262a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0018
    UUID = 0x2A26 = 既定の「Firmware Revision String
  • ハンドル:0x00019
    「value: 021a00272a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x001a
    UUID = 0x2A27 = 既定の「Hardware Revision String
  • ハンドル:0x0001b
    「value: 021c00282a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x001c
    UUID = 0x2A28 = 既定の「Software Revision String

    

Frame 330

続けてハンドル 0x001c - 0x0022 範囲のキャラクタリスティックを照会

    

Frame 333

以下のみっつのキャラクタリスティックの情報が得られた

  • ハンドル:0x0001d
    「value: 021e00232a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x001e
    UUID = 0x2A23 = 既定の「System ID
  • ハンドル:0x0001f
    「value: 0220002a2a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0020
    UUID = 0x2A2A = 既定の「IEEE 11073-20601 Regulatory Certification Data List
  • ハンドル:0x00021
    「value: 022200502a」より、プロパティ = 0x02 (Read), Characteristic Value Handle = 0x0022
    UUID = 0x2A50 = 既定の「PnP ID

    

Frame 337

前掲の Frame 238 の示すハンドルグループ 0x0023 - 0x0025 の「Immediate Alert」サービス内の Included Service を照会
(注:このリクエストに対する正しいレスポンスは「Attribute Not Found (0x0a)」だが、スニファ取りこぼしのためログが欠落している)

    

Frame 341

ハンドルグループ 0x0023 - 0x0025 の「Immediate Alert」サービス内のキャラクタリスティックを照会

    

Frame 345

以下のキャラクタリスティックの情報が得られた

  • ハンドル:0x0024
    「value: 042500062a」より、
    プロパティ = 0x04 (Write without Response),
    Characteristic Value Handle = 0x0025
    UUID = 0x2A06 = 既定の「Alert Level

    

Frame 349

前掲の Frame 238 の示すハンドルグループ 0x0026 - 0x002a の「Tx Power」サービス内の Included Service を照会
(注:このリクエストに対する正しいレスポンスは「Attribute Not Found (0x0a)」だが、スニファ取りこぼしのためログが欠落している)

    

Frame 355

ハンドルグループ 0x0026 - 0x002a の「Tx Power」サービス内のキャラクタリスティックを照会

    

Frame 359

以下のキャラクタリスティックの情報が得られた

  • ハンドル:0x0027
    「value: 122800072a」より、プロパティ = 0x12 (Read | Notify), Characteristic Value Handle = 0x0028
    UUID = 0x2A07 = 既定の「Tx Power Level

    

Frame 362

続けてハンドル 0x0028 - 0x002a 範囲のキャラクタリスティックを照会

    

Frame 366

当該サービス内にはこれ以上キャラクタリスティックは存在しない

    

Frame 369

Frame 359 の示す通り 0x00028 は Tx Power Level キャラクタリスティックの Value Handle であり後続の 0x0029 - 0x002a に関する情報を GATT に問い合わせてみる (詳細:1, 2

    

Frame 373

0x0029 は Tx Power Level キャラクタリスティックの Client Characteristic Configuration Descriptor (UUID = 0x2902) (詳細:1, 2) である旨の情報が得られた

    

Frame 376

残る0x002a に関する情報を GATT に問い合わせてみる (詳細:1, 2

    

Frame 380

0x002a は Tx Power Level キャラクタリスティックの Characteristic Presentation Format Descriptor (UUID = 0x2904) (詳細:1, 2, 3, 4) である旨の情報が得られた

    Opcode: Find Information Response (0x05)
    UUID Format: 16-bit UUIDs (0x01)
    Handle: 0x002a
    UUID: Characteristic Presentation Format (0x2904)

    

Frame 384

前掲の Frame 238 の示すハンドルグループ 0x002b - 0x002d の「Link Loss」サービス内の Included Service を照会
(注:このリクエストに対する正しいレスポンスは「Attribute Not Found (0x0a)」だが、スニファ取りこぼしのためログが欠落している)

    

Frame 390

ハンドルグループ 0x002b - 0x002d の「Link Loss」サービス内のキャラクタリスティックを照会

    

Frame 392

以下のキャラクタリスティックの情報が得られた

  • ハンドル:0x002c
    「value: 0a2d00062a」より、プロパティ = 0x0a (Read | Write), Characteristic Value Handle = 0x002d
    UUID = 0x2A06 = 既定の「Alert Level

    

Frame 395

前掲の Frame 247 の示すハンドルグループ 0x002e - 0x0032 の「Battery Service」サービス内の Included Service を照会

    

Frame 397

当該サービス内に Included Service は存在しない

    

Frame 401

ハンドルグループ 0x002e - 0x0032 の「Battery Service」サービス内のキャラクタリスティックを照会

    

Frame 404

以下のキャラクタリスティックの情報が得られた

  • ハンドル:0x002f
    「value: 123000192a」より、プロパティ = 0x12 (Read | Notify), Characteristic Value Handle = 0x0030
    UUID = 0x2A19 = 既定の「Battery Level

    

Frame 407

続けてハンドル 0x0030 - 0x0032 範囲のキャラクタリスティックを照会

    

Frame 411

当該サービス内にこれ以上キャラクタリスティックは存在しない

    

Frame 415

Frame 404 の示す通り 0x00030 は Battery Level キャラクタリスティックの Value Handle であり後続の 0x0031 - 0x0032 に関する情報を GATT に問い合わせてみる (詳細:1, 2

    

Frame 420

0x0031 は Battery Level キャラクタリスティックの Client Characteristic Configuration Descriptor (UUID = 0x2902) (詳細:1, 2) である旨の情報が得られた

    

Frame 423

残る0x0032 に関する情報を GATT に問い合わせてみる (詳細:1, 2

    

Frame 427

0x0032 は Battery Level キャラクタリスティックの Characteristic Presentation Format Descriptor (UUID = 0x2904) (詳細:1, 2, 3, 4) である旨の情報が得られた

    

Frame 431

前掲の Frame 247 の示すハンドルグループ 0x0033 - 0x0037 のユーザ定義サービス 1(UUID = 0xFFE0)内の Included Service を照会

    

Frame 434

当該サービス内に Included Service は存在しない

    

Frame 437

ハンドルグループ 0x0033 - 0x0037 のユーザ定義サービス 1 内のキャラクタリスティックを照会

    

Frame 439

以下のキャラクタリスティックの情報が得られた

  • ハンドル:0x0034
    「value: 103500e1ff」より、
    プロパティ = 0x10 (Notify),
    Characteristic Value Handle = 0x0035
    UUID = 0xFFE1 : ユーザ定義のキャラクタリスティック

    

Frame 442

続けてハンドル 0x0035 - 0x0037 範囲のキャラクタリスティックを照会

    

Frame 446

当該サービス内にこれ以上キャラクタリスティックは存在しない

    

Frame 450

Frame 439 の示す通り 0x00035 はユーザ定義キャラクタリスティック(UUID = 0xFFE1)の Value Handle であり後続の 0x0036 - 0x0037 に関する情報を GATT に問い合わせてみる (詳細:1, 2

    

Frame 453

0x0036 はユーザ定義キャラクタリスティック(UUID = 0xFFE1)の Client Characteristic Configuration Descriptor (UUID = 0x2902) (詳細:1, 2) である旨の情報が得られた

    

Frame 456

残る0x0037 に関する情報を GATT に問い合わせてみる (詳細:1, 2

    

Frame 458

0x0037 はユーザ定義キャラクタリスティック(UUID = 0xFFE1)の Characteristic User Description Descriptor (UUID = 0x2901)である旨の情報が得られた

    

Frame 462

前掲の Frame 247 の示すハンドルグループ 0x0038- 0x003a のユーザ定義サービス 2(UUID = 0xFFF0)内の Included Service を照会

    

Frame 465

当該サービス内に Included Service は存在しない

    

Frame 468

ハンドルグループ 0x0038 - 0x003a のユーザ定義サービス 2 内のキャラクタリスティックを照会

    

Frame 470

以下のキャラクタリスティックの情報が得られた

  • ハンドル:0x0039
    「value: 043a00f1ff」より、プロパティ = 0x04 (Write without Response), Characteristic Value Handle = 0x003a
    UUID = 0xFFF1 : ユーザ定義のキャラクタリスティック

    

Scene 5: デバイス A ボタン押下時のアプリへの通知を設定

Frame 491

Frame 453Frame 439Frame 453 の示す通り、ハンドル 0x0036 は、 "ユーザ定義サービス 1(UUID = 0xFFE0)配下のユーザ定義キャラクタリスティック(UUID = 0xFFE1)配下の Client Characteristic Configuration Descriptor (CCCD)" である

当該ユーザ定義キャラクタリスティック(UUID = 0xFFE1)のプロパティには Notify が設定されており、クライアントである対向アプリから CCCD 0x0036 に Notification bit (0x0001) を書き込んでおくことで、このキャラクタリスティックの値が更新された時に Value Handle である 0x0035 経由でアプリ側へ通知(Notification)が行われるようになる
(詳細: 1a, 2a, 1b, 2b

    Opcode: Write Request (0x12)
    Handle: 0x0026
    Value: 0100

    

Frame 495

    Opcode: Write Response (0x13)

    

Scene 6: アプリからデバイス A の LED・ブザーを操作

Frame 616

対向アプリ上の所定のボタンを押下すると、Frame 345, Frame 238 の示す、 "「Immediate Alert」サービス配下の「Alert Level」キャラクタリスティック" の Value Handle 0x0025 に値「2(High Alert)」が書き込まれる

デバイス A の LED・ブザーはファームウェアレベルでこの Alert Level キャラクタリスティックに紐づけられている模様。High Alert 値が書き込まれるとそれに反応して短時間 LED とブザーが ON になる

なお、Frame 345 の示すように Alert Level キャラクタリスティックのプロパティには「Write without Response」(0x04) が設定されているため、アプリから値を書き込んでもデバイス A からのレスポンスは発生しない

    Opcode: Write Command (0x52)
    Handle: 0x0025
    Value: 02

    

Scene 7: デバイス A のボタンを押すとアプリへ通知

Frame 711

デバイス A の物理ボタンはファームウェアレベルで Frame 439 の示す "ユーザ定義サービス 1(UUID = 0xFFE0)配下のユーザ定義キャラクタリスティック(UUID = 0xFFE1)" に紐づけられている模様。このボタンを押すと当該キャラクタリスティックの値がデバイス内部で更新され Frame 491 での仕込みに基づきアプリ側へ通知が行われる

    Opcode: Handle Value Notification (0x1b)
    Handle: 0x0035
    Value: 01

    

Scene 8: アプリ側からデバイス A との接続を切断

対向アプリ上の所定のボタンを押下するとリンク層制御 PDU の LL_TERMINATE_IND (0x02) がデバイス A 側に送出され両者間の接続が終了する(詳細:1, 2

Frame 779

    


(tanabe)
klab_gijutsu2 at 19:02|この記事のURLComments(0)TrackBack(0)
2016年08月18日

技適マークつき BLE パケットスニファを入手する

はてなブックマークに登録

Bluetooth Low Energy (BLE) の勉強のために BLE パケットを覗いてみたいと思いました。BLE の通信プロトコルは複雑ですが、パケットの内容を適宜精査すれば座学的な情報の向こう側にある実像を捉えることが可能となるでしょう。

国内では次のような BLE プロトコルアナライザが販売されています。もっともこういった数百万円オーダーの専用機にはなかなか手を出せません。

もっと手軽な方法として、BLE チップ・モジュールベンダの提供するパケットスニファを利用する選択があります。代表的な製品をピックアップしてみます。 2016年8月時点では日本国内で正規に流通している BLE パケットスニファ製品は見当たりません。もちろん国外から調達することは可能ですが、電波法に基づく技術基準適合(技適)証明とのかねあいが気になるところです。

パケットスニファと技適

たとえば、前掲の Nordic Semiconductor 社製「nRF51 Dongle」は技適証明を受けていないためディストリビュータが次のように注意を促しています。

  • nRF51 USB dongle for emulator,firmware - jp.rs-online.com
    警告
    
    本開発キットは技術基準適合証明を受けておりません。本製品のご使用に際しては、
    電波法遵守のため、以下のいずれかの措置を取っていただく必要がありますので
    ご注意ください。
    
     - 電波法施行規則第 6 条第 1 項第 1 号に基づく平成 18 年 3 月 28 日総務省告示
       第 173 号で定められた電波暗室等の試験設備内で使用する。
     - 実験局の免許を取得したのち使用する。
     - 技術基準適合証明を取得したのち使用する。
    
ただ、電波法は受信のみを目的とするものを規制対象外としています。
  • 電波法 (最終改正:平成二七年五月二二日法律第二六号) - law.e-gov.go.jp
       第一章 総則
              :
    第二条 この法律及びこの法律に基づく命令の規定の解釈に関しては、次の定義に
           従うものとする。 
              :
      五  「無線局」とは、無線設備及び無線設備の操作を行う者の総体をいう。
           但し、受信のみを目的とするものを含まない。 
              :
    
機能の性質上、パケットスニファの通信上の役割は受信に特化しています。ではこのデバイスを規制対象外と判断し安心して国内で使うことは適切でしょうか? 実はさらに考慮すべき話題があります。

上の記事のように、nRF51 Dongle は mbed 対応のプログラマブルな無線通信デバイスです。つまり、この「無線設備」は元来「受信のみを目的とするもの」ではなくむしろ「操作を行う者」によってプログラムを書き換え可能であることを特長のひとつに掲げている製品です。上に挙げたスニファデバイスはいずれも同様の側面を持っています。

以下の例のように、技適マークつきの製品においてさえ「ファームウェアの書き換え」との整合性を一意に判断できない事情を考え合わせると、技適マークなしのこれらのスニファ製品の日本国内での使用の是非はやはり微妙かもしれません。

  • ESP-WROOM-02のファームウェアを書き換えた場合、技適はどうなるのか - スイッチサイエンス
    ユーザーによるファームウェアの書き換えが、ESP-WROOM-02の工事設計認証を
    無効にする可能性について、メーカーのEspressif Systemsに確認をしました。
    同社は登録認証機関に確認した上で、Arduino core for ESP8266 WiFi chip
    または同社製SDKを使っている限りにおいては、認証には影響を与えないという
    回答を下さいました。他の開発環境など、ファームウェアを書き換える部分に
    よっては、認証に影響を及ぼし得るとのことですので、ご注意ください。
    
  • モノワイヤレス製品情報 - MONO-WIRELESS.COM - mono-wireless.com
    電波法規(技適)について
             :
    更にファームウエアを書き換えると認証の範囲を外れてしまう無線モジュール
    も存在しています。弊社製品は全て技適認証に適合した無線モジュールですので
    コンプライアンスに背くことなく安心してご使用していただけます。併せて
    「電波法についての考慮事項」を参照ください。
    
  • DD-WRT - wikipedia
    電波法による規制
    
    日本においては無線機器に対してメーカー側が想定していない非公式ファーム
    ウェアへの書き換えを行った時点で技適マークが無効となり、無線LANを利用する
    場合は電波法に違反する[要出典]。
             :
    

Adafruit 社製「Bluefruit LE Sniffer」

そんなわけで前掲のスニファの導入は一旦保留していましたが、情報を探しているうちにふとある製品のスペックに目がとまりました。

MDBT40 は、Nordic Semiconductor 社製のメジャーな nRF51822 チップを搭載した 中国 Raytac 社の BLE モジュールです。前に BLE まわりの製品調査を行っていた折にこの MDBT40 が日本の技適証明を取得ずみであることを知り名前が印象に残っていました。

MDBT40 を載せた製品は下記の例のように国内で流通しています。

  • Adafruit Feather 32u4 Bluefruit LE - スイッチサイエンス
    BLE(Bluetooth Low Energy)機能付きの小型Arduino互換ボードです。
    Adafruit Bluefruit LE Microの後継品です。
                    :
    Arduino Leonardoなどに搭載されているATmega 32u4を搭載。
    BLEモジュールであるMDBT40は、総務省の工事設計認証(いわゆる技適)を
    取得済みなので、日本国内で使用することができます。
                    :
    
Adafruit 社製のこの「Bluefruit LE Sniffer」の製品写真をよく見ると、技適マークつきの MDBT40 が載っていることを確認できます。 (クリックで原寸画像を表示)

cdn-shop.adafruit.com

このスニファであれば技適関連のジレンマなしに利用できそうです。本体価格は直販で $29.95。以下、Adafruit 社公式サイトより。

Bluefruit LE Sniffer のポイントをざっくりまとめてみます。

さっそく Adafruit 社サイトでオーダーしました。 配送方法には最も安価な United States Postal Service の「First-Class Package International Service™ incl. $1.60 insurance : $16.40」を指定、計 $46.35 を PayPal で決済し一週間ほどで到着しました。

発送連絡後に USPS サイトで配送状況を確認すると経由地に「JAMAICA」の表記あり。さては Japan -> Jamaica のミスか? と疑いましたがこれはカリブ海域にある国の名前ではなく JFK 空港そばの「JAMAICA, NY 11430」でした。物流の要衝のようですが恥ずかしながらニューヨークにジャマイカという地名があることを初めて知りました。Adafruit さん疑ってごめんなさい(^^;

使ってみる

Windows PC で Bluefruit LE Sniffer を使用する最短の手順を示します。詳細は nRF Sniffer アーカイブ内の「nRF Sniffer User Guide」に記載されています。

  1. FTDI 社の仮想 COM ポートドライバを未導入ならインストール
  2. 最新の nRF Sniffer の zip アーカイブをダウンロードし適当なフォルダ A へ展開
  3. Wireshark(v1.10.1 以降)を未導入ならインストール
  4. Bluefruit LE Sniffer を USB ポートへ接続し Sniffing の対象とするデバイスを近接させておく
    図は「nRF Sniffer User Guide v1.2」より
  5. フォルダ A 直下の「ble-sniffer_win_<version>_Sniffer.exe」を実行
  6. コンソールが開きアドバタイジング中の BLE デバイスが一覧表示される
  7. カーソルキー+ENTER または「#」番号で対象とするデバイスを選択
  8. デバイスを選択した状態で「w」キーを押下すると Wireshark が起動。あとはデバイス側で必要な操作を行えばよい

付記:デュアルユース品と輸出規制

ご存知の方も多いと思いますが、MouserRSDigi-key といった一般のディストリビュータから所定の電子部品を購入しようとすると、その製品の内容と在庫元・出荷元の国や地域によっては軍事転用の可能な「デュアルユース品目」として輸出規制に引っかかり、所定の書類一式の提出を求められ審査のために数週間程度待たされる場合があります。

今回、某ディストリビュータへ所定の商品が輸出規制の対象が否かを事前に判別する方法の有無を尋ねたところ「注文を受け実際に輸出手続きを開始しなければわからない」との回答でした。 輸出規制に関する注意書きの有無はまちまちですが、以下に一例を引用します。

  • nRF51-Dongle Nordic Semiconductor | Mouser - www.mouser.jp
    This product may require additional documentation to export
    from the United States.
    この製品をアメリカから配送するには、追加の書類が必要になる
    ことがあります。
    
一方、直販も行っている Adafruit は自社製品の輸出管理を主体的に実施している旨を公式サイト上で宣言しています。こういう場合には直販を利用するほうが面倒が少ないようです。


(tanabe)
klab_gijutsu2 at 11:35|この記事のURLComments(4)TrackBack(0)
2016年08月03日

Tomoru と Pochiru と Linking

はてなブックマークに登録

Tomoru

Tomoru は、昨年(2015年)11月より国内のクラウドファンディングサービス Makuake で支援募集の行われた Bluetooth Low Energy(BLE)デバイスです。「Project Linking」の立ち上げとともに当時話題になったためご存知の方も多いことでしょう。2016年8月現在は製造元である株式会社 Braveridge公式ストアAmazon から入手することができます。以前このブログで「blink(1)」という製品をピックアップしたことがありますが、個人的に「光りもの」は好物で、この Tomoru も支援募集への応募によって届いた個体を持っています。

Tomoru

www.products.braveridge.com

現時点では対応アプリが全体的にやや大人しめで Linking 自体も本格的な普及はこれからのようですが、手元では BLE の勉強がてらにこの可愛いデバイスを楽しんでいます。

Pochiru

Braveridge 社はその後も活発に Linking 対応製品をリリースしています。最近目にとまった「Pochiru」というデバイスを買ってみました。Pochiru には LED に加えネーミングどおり押しボタンが実装されています。そのため通信先の機器との双方向のやりとりが可能です。

Pochiru

linkingiot.com
ボタンものの BLE デバイスはいろいろ販売されていますが、リモートシャッターやメディアプレイヤーの操作といった所定の目的に閉じたものが多く、廉価でありながら自作のアプリケーションと汎用的に連携可能であることが Pochiru の魅力です。この価格でこのサイズの BLE ボタンデバイスを自作することの難度を考えるとコストパフォーマンスの高い製品と言えるでしょう。

プログラムを書く

Tomoru や Pochiru のような Linking 対応デバイス用のアプリは Project Linking が無償で公開している SDK を利用して作成します。BLE まわりのハンドリングはすべて Linking 本体が仲介するため、抽象化された Linking API を呼び出すだけでデバイスとの連携が可能です。

Project Linking の開発者向けページの中ほどの「API guide」項の利用規約を確認の上 SDK をダウンロードし、「API 仕様書」の「ポーティング」ページの説明に添って開発環境をセットアップします。後は「API 仕様書」と SDK アーカイブに含まれる「LinkingIFDemo」のコードを読み合わせれば要領を理解しやすいでしょう。

手元では Android 版 SDK を利用しています。上記のサンプルを元に作成した簡単なアプリのコードを以下に示します。アプリの「LED」ボタンを押すと連携ずみデバイスの LED が点灯し、連携ずみデバイス側のボタン押下等のイベントによってアプリが通知を受けるとその旨を画面に表示する内容です。

試作:Linking01

MainActivity.java
/**
 *
 * Linking01
 *
 * - 連携ずみ Linking デバイスの LED を点灯させる
 * - Linking デバイスからの通知を受け取る
 *
 */

package jp.klab.Linking01;

import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.nttdocomo.android.sdaiflib.Define;
import com.nttdocomo.android.sdaiflib.NotifyNotification;
import com.nttdocomo.android.sdaiflib.SendOther;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {

    private Context mCtx;
    private Button mButtonLED;

    private static final byte LINKING_IF_PATTERN_ID = 0x20; //LEDパターンの設定項目ID(固定値)
    private static final byte LINKING_IF_COLOR_ID = 0x30;   //LED色の設定項目ID(固定値)
    private static final byte COLOR_ID_RED = 0x01;  // 点灯色
    private static final byte BLINK_PATTERN = 0x22; // 点灯パターン

    private NotifyNotification mNotifyNotification;
    private MyNotificationInterface mMyNotificationInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCtx = this;
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mButtonLED = (Button)findViewById(R.id.buttonLED);
        mButtonLED.setOnClickListener(this);
        // Linking デバイスからの通知受信用
        mMyNotificationInterface = new MyNotificationInterface();
        mNotifyNotification = new NotifyNotification(this, mMyNotificationInterface);
    }

    @Override
    public void onClick(View v) {
        // LED ボタン押下で連携ずみデバイスあてに LED 点灯指示を送る
        if (v == (View)mButtonLED) {
            SendOther sendOther = new SendOther(this);
            sendOther.setIllumination(
                    new byte[] {
                            LINKING_IF_PATTERN_ID,
                            BLINK_PATTERN,
                            LINKING_IF_COLOR_ID,
                            COLOR_ID_RED
                    });
            sendOther.send();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mNotifyNotification.release();
    }

    private class MyNotificationInterface implements NotifyNotification.NotificationInterface {
        @Override
        public void onNotify() { // 通知を受信した
            // Linking デバイスからの通知内容は SharedPreferences に記録される
            SharedPreferences preference =
                    getSharedPreferences(Define.NotificationInfo, Context.MODE_PRIVATE);
            int DEVICE_ID = preference.getInt("DEVICE_ID", -1);
            int DEVICE_BUTTON_ID = preference.getInt("DEVICE_BUTTON_ID", -1);
            // Toast 表示
            Toast.makeText(mCtx, "onNotify: DEVICE_ID=" + DEVICE_ID +
                    " DEVICE_BUTTON_ID=" + DEVICE_BUTTON_ID, Toast.LENGTH_SHORT).show();
            // 音も鳴らす
            ToneGenerator toneGenerator
                           = new ToneGenerator(AudioManager.STREAM_SYSTEM, ToneGenerator.MAX_VOLUME);
            toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING);
        }
    }
}

試作の動作の様子

(動画:36秒 音なし)

Linking でのデバイス・アプリの設定手順

Linking 対応デバイスと Linking アプリを連携させるためには事前に設定が必要です。手順は以下の要領です。

  1. 端末に「Linking」をインストールする(未インストールの場合のみ)

             Linking(Android 版)- play.google.com

  2. 連携させるアプリ(ここでは自作の「Linking01」)のインストールを行い Linking を実行する
      
  3. 「デバイスの検索」を行い連携対象のデバイスを Linking に接続する(当該デバイスを未登録の場合のみ)
            
         
  4. デバイスの登録・接続が完了したら「デバイス詳細画面」へ移動し当該デバイスと連携させるアプリケーション(複数可。ここでは「Linking01」)を設定する
            
  5. Linking を抜け上の手順で設定したアプリを起動してデバイスとの連携を確認する
         

備考: デバイスの Linking への再接続について

手元の Android 5.1 端末の環境では以下の現象が見られます。Linking のバージョンは 2016年8月現在最新の「03.10.00000」です。

  • Linking に接続ずみのデバイスを Linking 上で「切断」すると再接続できなくなる
               
手元ではこの状況に以下の手順で対処しています。
  • Android の Bluetooth 設定において当該デバイスとのペアを解除してから Linking 上で再接続を行う
               
         
関連記事
(tanabe)
klab_gijutsu2 at 19:54|この記事のURLComments(2)TrackBack(0)
2016年03月11日

「入院者と外部とのコミュニケーション」への IoT の応用

はてなブックマークに登録

フェイススケールのこと

先日身内が入院した折にこういう顔の絵の入ったクリアファイルを病棟で見かけました。

フェイススケール(英: Face scale)とは、現在」の痛みを「にっこり笑った顔」から普通の顔、
そして「しかめっ面」そして「泣き顔」までの様々な段階の顔を用意して、神経痛などの痛みを
訴えている患者にどのぐらい痛むのかを示してもらうことで、その痛み度を客観的に知るために、
ペインクリニック・麻酔科などで用いられる用具・用紙のことである。
看護師さんがベッドに横になったままのご高齢者にこれを見せながら大きな明るい声で尋ねている様子が印象に残りました。たった一枚の印刷物ですが、自由に話したり体を動かしたりすることのむずかしい状態の入院者とのやりとりには特に有用なツールであることが想像されます。

入院者と外部とのコミュニケーションへの IoT の応用

携帯端末での電子メール利用の普及により入院者と外部との連絡はメール以前の時代に比べ格段に便利になりました。病室内での通話は無理でもメールの送受信は多くの場合認められており、おそらくは施設側が伝言を取り次ぐ頻度も昔よりもずっと少なくなったことでしょう。

その一方で、症状や状況によっては小さなキーを操作することが困難であったり、あるいは携帯端末やメールとの接点を想像しにくい雰囲気の入院者も少なくないことを連日の病棟内の光景であらためて実感しました。残念なことに、現実にはそういう人たちにこそ関係者との日常的なつながりがより必要なケースが多いようにも感じられます。たとえ最小限の内容であっても当事者間で手軽にコミュニケーションをとることができれば双方の不安が確実に軽くなることを考えあわせると、社会の高齢化が進行する状況にあってこの話題は潜在的な必要性の高いテーマのひとつと言えるのではないでしょうか。

試みに、フェイススケールをヒントに病室内での使用を想定してごく簡単な操作で離れた相手とメッセージを交わすしくみを形にしてみました。身近な場面での実用性のある IoT のアイディアとして紹介します。

試作装置の外観と概要

下の写真の装置のいずれかのボタンを押すと相手側の装置の同じ絵の下の緑の LED が点滅し、併せてその絵柄を添えたメールが所定のアドレスへ届きます。
親しい間柄の人や自分自身が病室のベッドで苦しんだ経験のある方にはわかるのではないかと思いますが、いま入院者に「ボタンを押すだけの余力」と「絵柄を選択する意識」があるということを情報として伝えられることには、離れて案ずる側にとっても入院者自身にとってもそれ自体に大きな意味があります。また、一方通行ではなく相手からの反応が手元に届くことは双方の精神的な支えとなるでしょう。写真のとおり、互いに言葉や意味ではなく気持ちや意識を伝えることに主眼を置いてそれぞれのボタンの絵柄はあえて解釈の余地の広いものを選んでいます。

※クリックで大きく表示
 

装置を病室に設置したイメージ

メールには押したボタンの画像がインラインで表示されリンクのクリックで返信用のページが開く

  • インターネット接続環境を備えた入院施設が少ないことを考慮し装置は現在もっとも低コストでモバイル通信のハンドリングが可能な Raspberry Pi + 3G モデムの構成で MVNO SIM を使用しておりコンセントひとつで稼働可能
  • 自分が押したボタンは赤 LED の点灯、相手の押したボタンは緑の LED の緩やかな点滅で示される
  • 装置間のメッセージングには Milkcocoa 、メール送信には SendGrid を利用
  • 赤ボタンの押下で各 LED を消灯、5秒以上の長押しで装置をシャットダウン
  • 右端の LED は装置の稼働を示すパイロットランプ

動作の様子(動画:2分20秒 音声なし)

このデモ動画は入院者側が実装置を使用し相手側がインターネット接続環境のもとで Web 版の仮想装置を使用することを想定した内容です。もちろん二台の実装置間での応酬も可能です。

画像について

今回の試作では画像がコミュニケーションの仲介者として大きな役割を担っており、これらは病室という気持ちの沈みがちな空間に小さな癒しを灯すシンボルでもあります。一連の画像は Beeboo.SLI(ビブースライ)様が運用される画像素材サイト 「イラスト無料素材 イラストボックス」I<アイ>さん が掲載された作品を加工して使用させて頂いています。I<アイ>さんの素敵なイラストと素晴らしいサイトに謹んで御礼申し上げます。

装置づくり

メインの要素

これらに説明は要らないでしょう。いずれも「持っていると便利」な道具です。
Raspberry Pi 2 Model B

アマゾンで 5,000円前後
3G USB モデム: L-05A

2015年10月に「イオシス」で 1,180円で購入(中古)
MVNO SIM:0 SIM
デジモノステーション 2016年02月号付属版)

手元の SORACOM Air とどちらを使うか迷ったが今回は 0 SIM を使用

筐体の材料と加工

今回の装置では操作用のボタンの選定が大きなポイントでした。小さすぎず大きすぎないものを探し以下の製品を選びました。

パネル取付用押しボタンスイッチ(緑色)です。過酷な使用に耐えられるヘビーデューティー
仕様です。プッシュ機構部(樹脂製)とマイクロスイッチで構成されています。

このボタンは取りつけ時の占有直径がおよそ 34mm で、これを 5mm 程度の間隔で 7つ設置するには最低 28cm ほどの長さが必要です。100円ショップをいくつか見てまわり筐体として写真のパスタケースを選びました(長さ:30.6cm 幅:8.5cm 高さ:7.6cm)。 蓋の方が少し広めの台形で、固い底のほうを上にすれば安定感があります。

このケースの底にボタンを取りつけるには厚さ 3mm のポリプロピレン板に 30mm 程度のまるい穴が 7つ必要です。今の自分の工作力ではこのように硬く厚みのある素材に綺麗なまるい穴をあけることが難しく、練習用の予備であれこれやってみましたがなかなかうまくいきません。店先でたまたま野菜調理用の金属製の抜き型が目にとまりました。下の写真の大2 + 小2 セットの「小」の持ち手側の外径が 3cm に近く、試しにこれを熱してケースに押し当てるとちょうどよいサイズの綺麗な穴が得られました。ケースの両端には 1cm のマージンを設け、シャットダウンボタン用の一番右の穴は隣りと 1cm 離してあとは 5mm 間隔で穴をあけました。

野菜の抜き型 (金属製)

穴をあけ終えたケース

パーツの取りつけ

実際にボタンを取りつけてみると全体の印象は当初のイメージどおりでした。ただ、ボタン 7個、LED 13個分の計 40本の配線を内部の残りのスペースの四方へめぐらせるには電子工作用の一般的なワイヤでは固すぎて取り回しに融通が利かず、柔らかい導線を加工して筐体内の隙間へ這わせることにしました。スペース節約のため GND への繋ぎ込みにはメスのピンソケットのオス側をスズメッキ線で連結したものを使いました。

ざっくり結線。ワイヤが固い

導線にLEDと抵抗とハンダづけして末端をオス・メス化

裁断した色画用紙を加工

ケースへ一式を収納して動作確認

ソフトウェア要素

作成したプログラム類一式を以下の場所で公開しています。

https://github.com/mkttanabe/iot_hospital

使用しているソフトウェア資産とサービス

土台の Raspberry Pi 2 Model B には標準ディストリビューションの Raspbian をインストールずみで今回は以下のソフトウェア資産を使用しています。

また、ふたつの優れたサービスを利用しています。
  • Milkcocoa 装置間のメッセージング用
  • SendGrid 実装置からのメール送信用

メインプログラム

実装置用のプログラム本体は Node.js スクリプトとして記述しています。

/home/pi/node/iot_hospital.js

Web 版仮想装置のページは以下の URL から参照できます。

http://dsas.blog.klab.org/data/iot_hospital/index.html?ID02&ID01

注:Web 版の動作には Milkcocoa アカウントを取得し 22行めに正しい APP ID を指定する必要があります

モデムまわり

L-05A + 0 SIM の組み合わせでのインターネットへの接続には wvdial ユーティリティを使用しています。/etc/wvdial.conf は以下の内容。"sudo wvdial" の実行で接続を開始します。

/etc/wvdial.conf

[Dialer Defaults]
init1 = ATZ
init2 = AT+CGDCONT=1,"IP","so-net.jp"
Password = nuro
Username = nuro
Dial Attempts = 3
Stupid Mode = 1
Modem Type = Analog Modem
Dial Command = ATD
Stupid Mode = yes
Baud = 115200
New PPPD = yes
Modem = /dev/ttyACM0
ISDN = 0
Phone = *99***1#
Carrier Check = no

※L-05A はゼロインストール機能を内蔵しており出荷時の設定により USB 接続時にはデフォルトで CD-ROM デバイスとして認識されます。eject コマンドで毎回これを解除するのが煩わしいため手元ではデフォルトで USB モデムとして認識されるように設定を変更しています。

モデムモードに設定した L-05A は lsusb コマンドで以下の要領で表示されます。

Bus 001 Device 004: ID 1004:6124 LG Electronics, Inc. 

スタートアップまわり

システム起動時に今回の一式を自動実行するために /etc/rc.local に iot_hospital.sh の呼び出しを記述しています。 iot_hospital.sh は L-05A が装着されていれば wvdial でインターネット接続を試行し、接続に成功すれば前掲の iot_hospital.js を実行します。

/etc/rc.local

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

# run app
sudo /bin/bash /home/pi/node/iot_hospital.sh &

exit 0

/home/pi/node/iot_hospital.sh


(tanabe)
klab_gijutsu2 at 07:01|この記事のURLComments(2)TrackBack(0)
2016年01月01日

ネットワーク対応の光通知デバイスを自作してみる

はてなブックマークに登録

「blink(1)」のこと

コードネームのような製品名ですが、「blink(1)」は KickStarter 発のカスタマイズ可能な USB デバイスです。接続先の機器やネットワークから所定の情報を受けとると LED 光の色や明滅により人間への通知を行うもので、2012年に製品化され 2013年には IFTTT との連携機能を強化した blink(1) mk2 がリリースされています。

文字の情報は人間が意識的にそこに向き合わなければ伝わらず、音の情報は状況により場の環境を乱す可能性があります。一方、「光」による情報はそれが適度な明るさであれば他の何かを妨げることなく人間の視界へ届き自然に認識され得ます。そのことを応用した例は他にも見られますが、blink(1) もまた光の特性を活かした機器のひとつです。

www.kickstarter.com gigazine.net gigazine.net
※詳細なレポートが GIGAZINE 様の以下の一連の記事に掲載されています。 この blink(1)、なかなか楽しそうなので使ってみたいと思いました。ただ、日本国内には今のところ代理店がなく国外のマーケットから購入することになりますが、これが * 微妙に高価 * なのですね。 そこで、一連の機能の中でもっとも興味のあるネットワーク連携部分を自作してみることにしました。どこかで何かがあったら知らせるというのはまさに IoT 向きのテーマであり、また、普段は主にバイプレイヤーである LED が一躍主役となるちょっと面白いケースでもあります。装置は「Wi-Fi 機能つきの小さなマイコン」としてこのところ何かと重宝している ESP8266 モジュールをメインに構成すれば手近な素材のみでも USB ポートへ直挿し可能なサイズに収めることが可能でしょう。装置へのメッセージング用のソリューションには今回 PubNub を選びました。

PubNub のこと / ESP8266 との関係

PubNub は Pub/Sub モデルのリアルタイムメッセージング機能を主軸とする BaaS です。 2010年のローンチ以来スマホや Web アプリ向けのプッシュ通知サービス等を展開、近年は IoT 方面にも注力しており対応言語・環境の広い有用なプラットフォームのひとつです。サポート対象は現在も拡大中、ちなみに無料枠もなかなかの太っ腹です。

また、GET リクエストベースの REST API セットが用意されており(HTTPS, HTTP)、これを利用すれば他のシステムやデバイスからも PubNub の所定の Channel へ柔軟にメッセージを送ることが可能です。 このように便宜の大きい PubNub ですが、この記事を書いている 2015年12月時点では実は肝心の ESP8266 がまだサポートされていません。でも幸い非公式のクライアントライブラリが公開されていることを知りました。本家の GitHub リポジトリにも fork されていることから非公式ながらも公認の位置づけのようです。 上の記事のとおりこの非公式ライブラリは任意のタイミングでの双方向通信には対応していない(subscribe() 呼び出し後の受信完了前には publish() 不可)ものの、今回想定している装置はもっぱら受信側であるため特に支障はなさそうです。作者の Kurt Clothier さん自身が「can be adjusted for a particular need」と書いているようにライブラリコードは用途に応じ適宜手を加えて利用すれば良いでしょう。
なお、このライブラリは Espressif 社による純正の ESP8266 SDK 向けに記述されています。より扱いやすい Arduino IDE for ESP8266 環境でも利用できるように併せて手を加えることにしました。

試作の動作の様子 (動画:1分11秒 環境音・操作音あり)

下の動画は、作成した装置へ PubNub の Web コンソールからメッセージを送り動作を確認している様子です。各メッセージはそれぞれ一行の REST API 記述に置き換えることができます。     

手元での現在の使途

手元ではこの試作を下記の要領で使用中。「光による通知」が想像以上に便利で実用的であることを実感しています。スマートさや表現性は blink(1) に及ばないものの必要になれば好きなようにいじれることが手作りの利点ですね。

  • 黄点灯:明日の予報が雨の場合
    (IFTTT レシピを利用)

         クリックで可読大表示
    ありがちなテーマだが実際に使ってみると手放せなくなる。好天続きで何となく油断している折に不意に点灯を目にしたら直ちに予報をチェック。

  • 青点滅:高齢者世帯の安否確認装置に反応があった場合
    Parse.com サーバサイドの Cloud Code を利用)

         クリックで可読大表示
    人を検知すると最短 30分間隔で Parse.com へレポートを送出する自作装置を実家で稼働中。対向するサーバ側コードに試作への通知処理を追加。光れば安心、メールによる通知よりも即時性が高く煩雑感が小さい。

  • 赤点滅:特定のユーザからのツイートがあった場合
    (IFTTT レシピを利用)

          クリックで可読大表示

    件数は少ないがいつも非常に有用な情報を提供してくれる某氏のツイートを見落とさないために。

  • 緑点滅:所定のブログが更新された場合
    (IFTTT レシピを利用)

          クリックで可読大表示

    とりあえず昨年末 「KLab Advent Calendar 2015」のチェック用に使ってみたらとても快適。

  • 管理用 HTML フォーム

          クリックで可読大表示

    PC・スマホの Web ブラウザから装置へメッセージを手早く簡単に送るために用意。 [ソース]

装置について

外観と構成

    

       クリックで可読大表示

材料

メモ

  • blink(1) は PC など対応機器の機能を利用するが今回の装置はネットワーク専用であり通信機能を内蔵しているため USB AC アダプタでも使える
  • USB 端子は規格上両端の2本が 5V (500mA) OUT と GND
        
  • フルカラー LED ではなく赤・緑・青・黄の 4つの単色 LED を使用。LED 用の抵抗は現物の発色をみながら「赤 75Ω 緑 75Ω 青 470Ω 黄 390Ω」に
  • LED の表示は各色「点灯・点滅」の二種類で合計 8パターン。これは手元での実利用を想定し妥当と判断した数
    (複数 LED の点滅時は明滅のタイミングが重ならないようプログラム側で管理)
  • 輪ゴムは USB コネクタ外抜け防止用
  • エアキャップはタッパ蓋の下に中敷き。LED 光拡散、外観柔和化、USB コネクタとタクトスイッチの圧迫用
  • タッパ蓋の上からタクトスイッチを押すと装置がリセットされる。電源接続時と LED 消灯用に使う

プログラムについて

ESP8266 用 PubNub ライブラリ

前述のように現時点では PubNub は ESP8266 を正式にサポートしておらず Espressif 社純正 SDK 向けの非公式ライブラリが公開されているのみです。これに以下の要領で手を加えて使用しています。

  • Arduino IDE で利用できるように
  • publish() の完了通知用にコールバックを登録可能に
  • HTTPS 対応

以下にソースコードを掲載します。ライブラリ「pubnub-esp8266」はファイル一式をローカルの Arduino ライブラリフォルダ下の pubnub-esp8266/ 下へコピーして使います。

(キーワード "DSAS" 部分がオリジナルからの変更箇所)

pubnub.h - GitHub

pubnub.cpp - GitHub

Arduino スケッチ

アプリ側のコードです。Arduino の SimpleTimer ライブラリを併用しています。

LEDMessage.ino - GitHub

作るのは簡単、アイディア次第でとても便利に使えます。


(tanabe)
klab_gijutsu2 at 00:41|この記事のURLComments(0)TrackBack(0)
2015年11月06日

今、ワンボタンの IoT デバイスが面白い

はてなブックマークに登録

米アマゾンが 2015年3月31日に発表した「Amazon Dash Button」はボタンを押すだけで所定の日用品のオーダーを可能とする小さなデバイスです。電池交換は不可。アイテムごとに専用のデバイスが用意されています。

Dash Buttonはただの通過点、アマゾンが真に狙うは家電すべてとの連携ショッピング:ギズモード・ジャパン
当初 Dash Button の利用は招待制でデバイスは無料で配布されていました。その後$4.99 での販売に変更され、2015年9月に米アマゾンは Dash Button ビジネスの拡大方針を決定しています。好調のようです。

なお、今のところこのサービスの日本での開始はスケジュールされておらず、米アマゾンから Dash Button 単体を購入することもできません。

2016-12-22 追記:
Amazon.co.jp は 2016年12月5日に日本国内で Dash Button サービスを開始しました。これに伴い関連する以下の記事を当ブログに追加しています。

ワンボタンデバイスのメリットと応用例

Amazon Dash Button は事業形態としても面白そうですがこの記事ではデバイス本体に注目します。Dash Button の実体はマイコンと無線 LAN モジュールを主要素とする乾電池駆動の IoT デバイスです。

       
http://mpetroff.net/

Dash Button は独立した装置なので必要な折にスマートフォンアプリ等よりも素早く使用することが可能。ボタンひとつの単機能であることが手軽さと即応性をさらに高めています。 この迷いようのないユーザインターフェイスは、その素朴さとは裏腹に、関係者が消費者への効果的なアプローチを模索する過程において綿密に戦略を練った結果と想像されます。一見「誰でも思いつきそうなこと」はそれが本当に実現されればしばしば斬新です。手軽さといえばスマホばかりに目が向けられる現状においてあえて専用ハードの形態を選んだ発想には学ぶべき要素があるように思います。

このデバイスのアイディアはビジネス用途以外の IoT においても広く活用することが可能でしょう。「物理ボタンの押下」という手間がかからず誰にでもできる簡単な操作をトリガーにどのような処理を行うかはアイディア次第であり、その便宜と魅力に気づいた向きがこの安価な Dash Button を本来の目的以外のことに利用し始めています。以下にいくつか例を挙げます。

  • How I Hacked Amazon’s $5 WiFi Button to track Baby Data (2015ー08ー10) - medium.com
    おそらくこれが最初の Dash Button 転用例。Edward Benson 氏は自宅で乳児の排泄日時を手早く記録・管理する手段を探す中で、夜中にスマホを操作する煩わしさもなくボタン押下のみで完結する Dash Button の長所に着目した。
    "using your smart phone at night disrupts sleep. I want a simple button I can stick to the wall and push to record"

    Amazon へのオーダーを行う上で本来必要な初期設定を意図的に保留。デバイス本体をハックするのではなく、ボタン押下〜デバイス起床時に LAN 上に流れる ARP パケットに注目し、当該デバイスの MAC アドレスを検出すればそれをトリガーに PC で処理を実行する手法によって要件を実現した。


    https://medium.com/
    「The Internet of Things is already here.」という結語が印象に残る。デバイスのコストを事実上度外視して Button を供給しているアマゾンにとってはあまり歓迎できない試みと思われるが、本体を改変しているわけではなく意味的には単に「未登録」の状態に過ぎない。この手の転用が拡がれば何らかの対抗措置を敷かれる可能性が皆無ではないものの、現時点ではこうした行為に対し直接クレームをつけることは難しいだろう。

  • Amazon Dash — It’s Dinner Time! (2015ー08ー27) - theappslab.com
    Raymond Xie 氏は Dash Button のコストパフォーマンスの高さに大喜び。稼働中に熱を持つ PiTFT をコードを抜かず簡単に ON/OFF する目的にさっそく利用していたところ奥方からもっと実用的なことはできないの?と聞かれ、ヘッドホン装着率の高い二階の子供たちを夕食時にいちいち呼びに行く代わりに Dash Button で子供部屋の フィリップス ヒュー を点滅させることを思いつく。Button は台所の奥方の手に。

  • PizzaDash (2015ー09ー12) - github.com/bhberson
    前出の Benson 氏の記事に触発された、と Brody Berson 氏は Dash Button をドミノピザの宅配オーダーに利用(無論アマゾンとは無関係)。ボタン押下をトリガーに Raspberry Pi が ドミノピザ公式 API を叩いて注文成立。
    2015-11-25 追記:
    英国ドミノピザが公式にワンボタンのオーダーサービスを開始する旨の情報あり (2015-11-23) "PizzaDash" からの影響の有無は不明だが、dominos 社はこれまでに独自の様々なオンラインオーダーシステムを確立しており IT 活用にきわめて積極的

  • Amazon dash button automation silliness. (2015ー09ー26) - www.youtube.com
    Michael Donnelly 氏による Youtube への投稿動画。玄関の照明を点灯/消灯したり、ガレージの車の内部が一定の温度になったらクラクションを鳴らしたり。

  • Dash Hacking: Bare-Metal STM32 Programming - learn.adafruit.com
    Adafruit 社TONY DICOLA 氏による正面からの Dash Button ハックの試み。ファームウェアを書き換え本体の LED を自作のコードで制御。"this guide didn't touch on the Dash's WiFi module yet" とのこと。

また、米アマゾンウェブサービス(AWS)は 2015年10月の AWS IoT ベータ公開にあわせ、Amazon Dash Button と同一のハードを使った AWS IoT Button (beta)を発表しました。10月6〜9日にラスベガスで開催された「AWS re:Invent 2015」の会場では参加者へ AWS IoT 体験用に配布されています。ボタンを押すと AWS IoT サービスへ通知され利用者の記述した処理を実行できるしくみです。
        

さらに、クラウドファンディング発の次のようなオリジナルのボタン製品も登場しています。またこれからも新しいものが出てくることでしょう。

このように「ボタンひとつ」の IoT デバイスの有用性がいま注目されています。

工作と実験

こういった事情を背景にワンボタンデバイスを自作する向きもあり例 1例 2、試みに手元でも手近な材料を使って作ってみることにしました。装置が形になればそこにどのような処理を絡めるかは自由ですが、まずもっとも身近な題材のひとつとして自分あてのメール送信機能を組み込みました。

  • 今回の「ワンボタンメーラ」は試作上の題材ではありますが、実用を想定すると以前手がけた高齢者世帯安否確認のしくみと併せ、遠隔世帯からの手間いらずの ping 的定刻連絡やナースコール的な使い方ができそうです

試作の動作の様子 (動画:30秒)

    

装置について

試作した装置の材料と構成は以下の通りです。Arduino IDE for ESP8266 プロジェクトのリソースを使用し ESP8266 Wi-Fi モジュールをスタンドアロンのマイコンとして制御しています。
(価格は 2015年10月時点)

  • ESP8266 モジュール CDP-ESP8266 - Cerevo ¥842(税別)
  • バッテリー RW-111(電話機用:3.6V 800mAh) - ロワジャパン ¥780(税込) amazon
    ※説明欄の「単三電池三本の形状」の記述は「単四電池三本」が正しい
  • ブレッドボード EIC-15010 - スイッチサイエンス ¥216(税込)
  • ジャンプワイヤ・抵抗・タクトスイッチ・赤 LED
  • タッパ・ペーパータオル

     

     

メモ

  • タクトスイッチはペーパータオルをクッションにタッパ蓋のたわみを利用して操作。チープながらこの中敷きは装置を保護し外観を和らげ LED のシェードの役割も
  • ロワのバッテリーは手元の固定電話機 (ユニデン社製 UCT-002 用にストックしていたもの。定格もサイズもぴったりだが充電には電話機への装着が必要。充電器の有無を同社へ問い合わせるとやはり「本体充電のみ」とのこと
  • バッテリー消費抑制のため ESP8266 モジュールは平時はディープスリープ状態で待機させる。ボタン押下でリセットをかけると起床して必要な処理を行いふたたびスリープへ
    • ディープスリープ中の ESP8266 の使用電流は公称 10μA、手元の実測で 11μA
    • ESP8266EX の Operating Voltage は 3.0〜3.6V、Operating Current(Average value) は公称 80mA(下図)、今回の実験でボタン押下〜再スリープまでの実測では LED 点灯分を含めおよそ 60mA。処理完了までの所要時間は 10秒前後

                  ESP8266EX Datasheet Version 4.4 (2015-08-01) Page 8
                      (クリックで可読大表示)

    • バッテリー駆動可能期間を大まかに試算してみる。
      条件として、使用者が 1日に 2回、起床時と就寝時にボタンを押すものとする。この場合、前掲の値を当てはめると 1日のうち 2 × 10秒間が 80mA、残りの 86380秒間が 11μA(0.011mA)であり、平均をとるとおよそ 0.03mA。 自然放電特性を加味せず www.digikey.jp の 電池寿命カリキュレータで単純計算すると今回のロワの 800mAh のバッテリーの場合は次の結果となる
      800mAh / 0.03mA * 0.7 ≒ 18666.67 時間 ≒ 777.78 日 ≒ 25.1 月 ≒ 2.1 年

    • ちなみに Amazon Dash Button は 米エナジャイザー社Ultimate Lithium 乾電池 単4形(二次電池ではない)1本を内蔵している。アルカリ電池に比べ「最大 9倍長持ち」を惹句とする現時点でおそらく最強の乾電池。下記記事の Button 分解写真にこの電池の様子も写っている。電極がタブ付けされており電池交換は想定されていない模様
      Amazon Dash Button Teardown - mpetroff.net
      AWS IoT Buttonを分解してみた - Developers.IO
      ※プラス極側の黒地に白文字の「12-2034」「12-2035」等の表記は未使用時の品質保持期限を示す。20年!

      http://mpetroff.net/
    • データシートによると エナジャイザー Ultimate Lithium AAA は "Max Discharge: 1.5 Amps Continuous, 2.0Amps Pulse (2 sec on / 8 sec off)" と高容量であり、Dash Button のように間欠的に電力を使用する機器においては後者のメリットを最大限に享受できるものと想像される
    • 上の mpetroff.net の記事に次の記述がある。内部で 3.3V に昇圧しているとのこと
      "The Dash Button operates at 3.3 V boosted from the battery’s nominal 1.5 V, drawing 200–300 mA from the battery when on and 2.3 μA when in sleep."
      現時点では Dash Button の battery life に関する公式の記述は見当たらず
    • エナジャイザーの Ultimate Lithium 乾電池は性能相応に高価い。バッテリーだけをみても Dash Button の$4.99という価格設定への興味を誘われる
      Energizer L92BP-Energizer Ultimate Lithium AAA Battery (4-Pack) - www.amazon.com $7.99
      エナジャイザー リチウム乾電池単4形 4本パック - www.amazon.co.jp ¥1250
  • ブレッドボードを使わなければ配線部分をもっと小さくできるが全体のサイズは実験用としては手頃か

プログラムについて

メールの送信には「SendGrid」を利用しています。その方面ではメジャーなサービスであり、国内に正規代理店株式会社 構造計画研究所があり日本版の公式サイトも運営されています。濫用防止のためサインアップの際には使用目的等の詳細説明が求められアカウント発行可否は人間によって審査されます。

フリープランで 400通/日の送信が可能であり手元での利用にはまず十分そうです。ドキュメント・API も充実しています。 今回は単純明快でくみしやすい初版のほうの Web API を利用することにしました。 ところで、多くのクラウドサービスがそうであるように SendGrid Web API の利用にも HTTPS が必須です。 Arduino IDE for ESP8266 プロジェクトは 2015-08-31 より TLS 1.0/1.1 への対応を始めており利用者の一人として大いに期待しているのですが、この記事を書いている 2015年11月初旬の時点ではなお活発に開発が進行中で、stable になるまでにはまだ多少時間がかかりそうです。 そうした事情もあり、先だって「ESP8266 モジュールの AT コマンドに SSL クライアント機能を追加する」の試みで Espressif 社によるサンプルプログラムをもとに構成した HTTPS クライアントコードに手を加え、当面 Arduino IDE でライブラリとして使うことにしました。もちろんメールサービス以外の用途にも利用できるでしょう。
※手元の Arduino IDE for ESP8266 のバージョンは現時点で stable な
 2015-07-23 リリースの「1.6.5-947-g39819f0」なのですが、この版に
 同梱されている Espressif ライブラリを使ってスケッチをビルドすると
 SendGrid の API サーバ である https://api.sendgrid.com/ への接続
 要求時に異常終了の発生を確認しました。同ライブラリ群の配置された
 packages/esp8266/hardware/esp8266/1.6.5-947-g39819f0/tools/sdk/lib/
 配下の *.a を現時点で最新の esp_iot_sdk_v1.4.0_15_09_18 に
 含まれる一式に差し替えたところ問題の解消が見られました。

以下にソースコードを掲載します。ライブラリ「EspHttpsClient」はファイル一式をローカルのライブラリフォルダ下の EspHttpsClient/ 下へコピーして使います。

ライブラリ:EspHttpClient

EspHttpClient.h - GitHub

EspHttpClient.cpp - GitHub

スケッチ:OneButtonMailer

OneButtonMailer.h - GitHub

OneButtonMailer.ino

また、多少遅延が発生する可能性はあるものの、専用のサービスの代わりに手近に IFTTTMaker Channel を利用する選択肢もあるでしょう。目的がメール送信であれば下図の例の要領でレシピを作成しておきます。 (クリックで可読大表示)

     

このレシピを呼び出すスケッチです。

OneButtonMailer_IFTTT.h - GitHub

OneButtonMailer_IFTTT.ino

付録

Espressif 社のドキュメントより ESP8266 のスリープモードに関する記述箇所を以下に抜粋します。
(クリックで可読大表示)

ESP8266EX Datasheet Version 4.4 (2015-08-01) 」より

   

ESP8266 SDK API Guide Version 1.4 (2015-09-18)」中の system_deep_sleep() および system_deep_sleep_set_option() のリファレンス

   


(tanabe)
klab_gijutsu2 at 14:46|この記事のURLComments(0)TrackBack(0)
2015年10月08日

ESP8266 モジュール + Blynk でさらりと Wake On Wan

はてなブックマークに登録

Android / iOS 端末から所定の IoT デバイスの遠隔制御や監視を可能とする Kickstarter 発の Blynk が人気を集めています。2015年10月現在 Blynk はまだ開発の途上にあり、今の時点では Backer(出資者)でなくとも実装ずみのすべての機能を無償で利用することができます(※)。シンプルで取りまわしのしやすいこのサービスを使い、ちょっとした実用性のある道具として遠隔の PC をスマホで Wake On Wan するものを作ってみました。なかなか便利なので一式を公開します。

  • (※)公式サイトの「FAQ」より
    + I backed Blynk on Kickstarter. Where are my widgets and
      why the app is free?
    
      - App is free becasuse otherwise you would have to pay to
        download it. This is how AppStore and Google Play works.
      - Current Blynk release has a limited amount of widgets.
        We decided to make them free for everyone until we
        implement store. After that,every widget will be paid.
        However every backer will get them for free (according
        to their pledge).
    
Blynk は ESP8266 Wi-Fi モジュールに対応しており、以下の記事では技適マークつきの「ESP-WROOM-02」を使用しています。このシリーズのモジュールは自立したマイコンとして動作可能であるため IoT デバイスの主要素としてもっとも手軽な選択肢のひとつと言えるでしょう。

準備

まず Blynk の利用を構成するみっつの要素を整理しておきます。

  • Blynk アプリ
    Google Play / AppStore で公開中の専用アプリを使ってユーザが構成した制御用のアプリ
  • IoT デバイス
    専用の Blynk ライブラリを使ってユーザが作成したプログラムを導入ずみの制御対象の装置
  • Blynk サーバ
    Blynk アプリと IoT デバイスの連係を仲介するクラウドサービス(※オープンソースであり自前での構築も可能)

Android / iOS 端末にはそれぞれの公式ストア上で配布されている専用のアプリ「Blynk」をインストールします。利用者はこのアプリを使って独自の Blynk アプリを構成することができます。
Blynk アプリに対向する IoT デバイスのためのプログラム開発には Arduino IDE を使用します。あらかじめ IDE に ESP8266 用ライブラリモジュールを導入しておき、そこへ Blynk の提供するライブラリ群をインストールします。
Blynk の公式ドキュメントには Blynk を利用する上で必要となるこういった情報・手順がわかりやすく記述されています。また、ネットを検索すれば多くの有用な記事を参照することができます。

試作の題材について

手元のニーズに基き、Blynk を使った遠隔制御の身近な応用例として次の内容を想定しました。IoT デバイスは対象の PC と同じ LAN 環境へ設置します。

  1. 所定の PC を Wake On Wan する
  2. 所定の PC へ ping を打つ
  3. 所定の AC 機器への給電を ON / OFF する
  4. 制御対象の IoT デバイスをリセットする

外出中に自宅の PC を利用したくなる場合があります。PC をずっと起ち上げたままにしておくのは好ましくないので、これまでは遊んでいる Android 端末を自宅で待機させておき GCM 経由で所定の PC を Wake On Lan するという方法をとっていました。それはそれで問題なく機能しているのですが、必要な手順を踏むのがだんだん面倒になってきたことに加え、その場で手早く PC の起床状況を確認できないことに不便を感じていました。確認方法は所定の IP アドレスに対する ping テストで十分でしょう。

上記 3.は拡張用です。手元では留守の夜の防犯用に自宅の部屋の電灯を任意のタイミングで点灯/消灯するために使っています。これまでは上の PC に接続した USB 赤外線リモコンで操作を行っていましたが、それも面倒なので別の手段がほしいと思っていました。

実装方法について

Wake On Lan マジックパケットの送出と所定の PC への ping 打ちは Blynk の「Virtual Pin」機能を使って IoT デバイス側と連携すれば簡単に実現できると考えました。Blynk アプリ上に配置したボタンの設定において「OUTPUT」に物理ピンではなく適当な仮想ピンの番号を割り当てて、受け側の IoT デバイス側のコードに "BLYNK_WRITE(仮想ピン番号)" ハンドラを定義してそこに必要な処理を記述すればよいはずです。

  • Send data from app to hardware - Blynk 公式ドキュメント - blynkkk.github.io
    Send data from app to hardware
    
    You can send any data from Widgets in the app to your
    hardware.
    
    All Controller Widgets can send data to Virtual Pins on
    your hardware. 
    For instance, code below shows how to get values from
    the Button Widget in the App
    
    BLYNK_WRITE(V1) //Button Widget is writing to pin V1
    {
      int pinData = param.asInt(); 
    }
    
    When you press Button, Blynk App sends 1 On second
    click - it sends 0
    
    This is how Button Widget is set up:
  • Wake On Lan マジックパケットの仕様に関する記事
  • Espressif 社公式フォーラムの以下の記事の末尾に ESP8266 で ping を実行するサンプルコードあり
ping 結果をアプリへ通知するには、アプリ側に設置したデータ INPUT 用のウィジェットに所定の仮想ピン番号を設定しておき、その番号に対し IoT デバイス側のコードで "Blynk.virtualWrite(仮想ピン番号, データ)" API を使ってデータを送ってやればよさそうです。

  • Virtual Pins - Blynk 公式ドキュメント - blynkkk.github.io
    Virtual Pins
    
    Virtual Pins are designed to send any data from your
    microcontroller to the Blynk App and back. Think about
    Virtual Pins as channels for sending any data. Make sure
    you differentiate Virtual Pins from physical pins on your
    hardware. Virtual Pins have no physical representation.
    
    Virtual Pins can be used to interface with libraries
    (Servo, LCD and others) and implement custom functionality. 
    The device may send data to the Widget to the Virtual Pin
    like this:
    
    Blynk.virtualWrite(pin, "abc");
    Blynk.virtualWrite(pin, 123);
    Blynk.virtualWrite(pin, 12.34);
    
AC 機器への給電の ON / OFF の制御には ESP8266 モジュールからの 3.3V 出力信号でリレースイッチを操作すればよいでしょう。入力側と出力側の絶縁性の高いソリッドステートリレー(SSR) は動作時にカチリと音のする有接点リレーに比べ値段が高めですが秋月電子さんが電子工作向きの手頃なキットを販売しています。

※余談ながら、個人的にはこのように高電圧を扱うものは本当はあまり自作したくないです。その方面に素養がなくても部品を揃えれば作るのは簡単ですが、「手作りの楽しさ」などよりも安全性がもっとも重要ですから、手頃な価格で堅牢な完成品を入手できるならそれを使いたいというのが正直なところです。残念ながら今のところそういう商品は見当たらないようですが(需要はあると思うのですが・・)、上のキットを使ってリレーつき電源ケーブルを作る方法が下記の書籍にわかりやすく記載されています。

動作の様子

ひと通り形になったものが動作している様子を以下の動画に収めています。(1分56秒 環境音あり)

内容:

  • IoT デバイス稼働中は死活確認用の LED が 2秒間隔で点滅、そのタイミングでアプリの TICK フィールド(デバイス稼働秒数)が更新される
  • アプリの BOOT PC ボタン押下で IoT デバイスが所定の PC を Wake On Lan する
  • アプリの PING ボタン押下で IoT デバイスが所定の PC へ ping を投げアプリの OK / NG フィールドへ結果をカウント
  • アプリの AC DEVICE ボタン押下で IoT デバイスへ接続した AC 機器への給電を ON / OFF
  • アプリの RESTART ボタンを長押し(3秒以上)してリリースすると IoT デバイスは再始動する

リソース一式

今回の Blynk アプリおよび IoT デバイスを構成する要素は以下のとおりです。

※各画像はクリックで実寸表示

Blynk アプリ側

Android / iPhone での当該 Blynk アプリの画面表示

プロジェクトと各ウィジェットの設定

IoT デバイス側

装置全体の様子
※ESP-WROOM-02 モジュールは Cerevo 社製のブレイクアウト基板「CDP-ESP8266」とともに使用
※稼働状況確認用に IO4 に赤色 LED を、AC 機器制御用に IO12 にリレーつきケーブルを装着しているが不要ならどちらも省略可

リレーつき電源ケーブル

ソースコード
SimpleTimer ライブラリのインストールが必要です)

ESP8266_WakeOnWan.ino - github.com/mkttanabe

留意すべき事項など

Blynk は便利で有用ですが現在進行形で開発中であるため利用時に若干注意しなければならない点もあります。今回手元で気づいた話題をメモしておきます。

Blynk ライブラリ旧バージョンでの不具合について(重要)

2015-08-06 にリリースされた v0.3.0 までのBlynk ライブラリには ESP8266 モジュールのハンドリングに大きな不具合があります。同モジュール − Blynk サーバ間の持続接続に不意に切断が発生した場合に無限ループに陥りデバイスをリセットしない限り再接続不能となる問題です。この不具合は 2015-09-24 にリリースされた v0.3.1 では修正されており、切断が発生した場合に接続を自動復旧する機構が正常に機能するようになっています。この修正により連続稼働が安定するようになりました。Blynk で ESP8266 を使う場合は必ずこの v0.3.1 以降のライブラリを使うべきです。公式フォーラム上の以下の記事に関連する応酬があります。

Android - iOS 端末間の Blynk アプリの互換性について

Blynk は Android と iOS に対応していますが、両方のプラットフォームで等しく動作する Blynk アプリを構成するためには以下の点への注意が必要です。

  • 2015年10月8日時点の Android 版 Blynk の最新バージョンは "1.0RC7 fix"、iOS 版 Blynk の最新バージョンは "1.3" です。バージョン番号は iOS 版のほうが上ですが、Android 版で利用可能なウィジェットうち iOS 版ではまだ実装されていないものがいくつかあります。下図左は Android 版 "1.0RC7 fix、右は iOS 版 "1.3" でのウィジェット一覧です。

    iOS 版未実装のウィジェットを Android 版で配置して保存したアプリを iOS 版で開くとその部分が空白となります。それだけではなく、そのアプリを iOS 版で保存すると当該ウィジェットは失われます。要注意です。


  • Android 版では LED ウィジェットが正常に機能しません
以上の事情により、現時点では、両プラットフォーム間での互換性の確保が必要なアプリにおいては iOS 版で利用できるウィジェットのみを使用し、また、LED ウィジェットは使わないようにする必要があります。今後の改善が期待されます。

最後に

Prepare to Blynk! - www.kickstarter.com に掲載されているイラスト

がんばれ Blynk!


(tanabe)
klab_gijutsu2 at 05:49|この記事のURLComments(0)TrackBack(0)
2015年08月27日

ESP8266 モジュールの AT コマンドに SSL クライアント機能を追加する

はてなブックマークに登録

中国 Espressif Systems 社製 ESP8266EX 搭載の「ESP-WROOM-02」は興味ぶかいモジュールです。 今年(2015年)上半期に技適を取得し人気を集めているこの製品は安価であるにも関わらず自立したマイコンとして扱える点が素晴らしく、また、出荷時のファームウェアに組み込まれている AT コマンドセットを利用すればホスト側に負荷をかけず簡単に丸ごと通信処理を代行してくれる手軽さにも好感が持てます。

ただ、この AT コマンドセットで HTTPS リクエストを処理する方法が見当たらないことを不思議に思っていました。あらためてドキュメントを確認するとどうやら AT コマンドで SSL 通信を扱うことはできないようです。プログラマブルであることがこの製品の魅力のひとつであるとは言え 出荷時の状態で SSL を利用できないのはそのこと自体が残念に思われます。AT コマンドのインターフェイスそのものは何かと便利なのでこれを拡張する形で SSL への対応を試みました。

最終的にはサーバ証明書の検証も含め期待通りに動作しましたが、その過程で意外な曲折もあり、手元で経験したことを備忘をかねてほぼ順番どおりに記録しておくことにします。興味のある方は追ってみて下さい。

AT コマンド拡張について

メーカーは次のページで ESP8266 用の SDK を公開しています。

2015-08-21 時点の最新版は以下の通りです。

SDK アーカイブを覗いてみると examples/at/ ディレクトリ下に AT コマンドセットを拡張するためのサンプルコードが収録されており「AT+CIUPDATE」コマンドは他のコマンド群とは別にここで定義されていることが見てとれます。

examples/at/user/user_main.c

コードの末尾付近の at_custom_cmd[] テーブルには「AT+CIUPDATE」に加え「AT+TEST」という拡張コマンドが記述されています。拡張コマンドの定義の方法は次の記事に説明があります。

In the example of \esp_iot_sdk\examples\at\user\user_main.c, 
ways are delivered on how to implement a self-defined AT Command,
 AT+TEST”.
The structure, at_funcationType, is used to define four types of
a command, e.g., “AT+TEST”. 

"at_testCmd" is a testing command and it’s formatted as AT+TEST=?.
In the example of AT, the registered callback is “at_testCmdTest”; 
the testing demand could be designed as the value range of the
return parameter. If registered as NULL, there will be no testing
command.

"at_queryCmd" is a query command and it’s formatted as AT+TEST?.
In the example of AT, the registered callback is “at_queryCmdTest” ; 
the query command could be designed as returning the current value. 
If registered as NULL, there will be no query command.

"at_setupCmd" is a setup command and it’s formatted as
AT+TEST=parameter1,parameter2,........ In the example of AT, 
the registered callback is "at_setupCmdTest"; 
the setup command could be designed as the value of the parameter; 
if registered as NULL, there will be no setup command.

"at_exeCmd" is an execution command and it’s formatted as AT+TEST. 
In the example of AT, the registered callback is “at_exeCmdTest”; 
if registered as NULL, there will be no execution command.
一連の書式は他のコマンドでも見かけるものです。一読したところでは既存のコマンドセットにオリジナルの AT コマンドを追加することはあまり難しくないようです。

SDK に同梱のビルドずみファームを書き込んでみる

SDK の bin/ ディレクトリにはメーカー側がビルドしたファームウェアバイナリ群が格納されています。
ESP-WROOM-02 出荷時の標準ファーム(AT コマンドセット)は以下のバイナリから構成されます。

  • bin/boot_v1.2.bin ---- ブートローダ
  • bin/at/user1.1024.new.2.bin ---- AT コマンドプログラム本体
  • bin/blank.bin ---- 領域初期化用 中身は 0xFF * 4096バイト
  • bin/esp_init_data_default.bin --- "Stores default RF parameter values"

自作のファームをビルドして書き込む前に、まずこれらを練習台として内蔵フラッシュメモリに書き込んでみることにしました。 正しく書き込めるようになっておけばいざと言うときの切り戻しも楽でしょう。

「bin/at/readme.txt」冒頭に次の記述があります。

download:
boot_v1.2+.bin      	0x00000
user1.1024.new.2.bin  	0x01000
blank.bin           	0x7e000 & 0xfe000

※「user1.1024.new.2.bin」の「1024」は 1024KB(8Mbit)容量のフラッシュメモリ使用を想定してビルドされたバイナリであることを意味する。ESP-WROOM-02 は 32Mbit のフラッシュメモリを内蔵しているが、以下の記事においてはこのバイナリの仕様に準じ 8Mbit 空間範囲の使用を前提とする。

※「esp_init_data_default.bin」には RF(Radio Frequency)パラメータのデフォルト値が保持されている旨の説明が SDK の「ESP8266 IOT SDK User Manual」に記載されている。次の記事にも説明あり。

ファームウェアの書き込みは以下の「Flash Download Tool」を使い USB - シリアル変換器経由で行います。ツールは Windows 版と Python 版が用意されています。 書き込みはモジュールの IO0 ピンを Lo にセットして「UART Download Mode」へ移行し次の要領で。
(図は Windows 版)
    ※1024KB フラッシュ使用時のメモリマップ
    
         - SDK の「Espressif IOT SDK User Manual」より
    
    Note 
    
    • System param (system parameter area) is the last 16KB offlash. 
    
    • User param is the user parameter area used by Espressif demo code
      ( IOT_Demo or AT ). If users develop their own application, 
      user data can be saved in any available flash area. 
    
    • User Data area ( green area in pictures below ) means the flash
      area that may be available, if program area doesn’t reach the
      maximum size, remaining area can be used to save user data. 
    
動作確認
AATTEE00


OK
AT+GMR
AT version:0.40.0.0(Aug  8 2015 14:45:58)
SDK version:1.3.0
compile time:Aug  8 2015 17:19:38
OK
AT+CWMODE=1

OK
AT+CWLAP
+CWLAP:(1,"GPMAP",-93,"10:6f:3f:XX:XX:XX",1,-47)
+CWLAP:(0,"000D0BF6C164_G",-91,"00:16:01:XX:XX:XX",5,-32)
+CWLAP:(3,"hogehogeSSID",-72,"00:22:cf:XX:XX:XX",10,-91)
+CWLAP:(2,"STWEBKAIGI",-88,"4c:e6:76:XX:XX:XX",10,-27)
+CWLAP:(4,"elecom2g-DE8564",-90,"00:90:fe:XX:XX:XX",10,-26)
+CWLAP:(3,"e-timer-DE8564",-90,"02:90:fe:XX:XX:XX",10,-26)
+CWLAP:(3,"auhome_acyCYh",-92,"8c:4c:dc:XX:XX:XX",1,-31)

OK

開発環境の導入

ESP8266 モジュールのファームウェア開発には現在 Arduino 向けの IDE・言語を用いることも可能ですが SDK に含まれるコードはいずれもネイティブの開発環境を前提としているため今回はそれを使います。

メーカーは必要な一式をインストールずみの開発環境(Linux)の VirtualBox 仮想マシンイメージを配布しており、導入手順・方法は以下の記事で説明されています。

ポイントなど
  • SDK の一式を配置した PC 上のディレクトリを "share" の名前で VirtualBox 仮想マシンの共有フォルダとして設定しておく
  • 仮想マシンのデスクトップ上の "LXTerminal" を起動するとアカウント esp8266 でターミナルが開く
  • アカウント esp8266 ホームディレクトリの ./mount.sh を実行すると PC 上の共有フォルダが ~/Share にマウントされる
  • アカウント esp8266 のパスワードは "espressif"

※仮想マシンのタイムゾーンは CST 北京時間に設定されているため変更が必要です

ファームウェアをビルドしてみる

SDK の examples/ 下には複数のサンプルプロジェクトが配置されており、利用するプロジェクトのファイル群をそこから app/ 下へ丸ごとコピーして使います。今回は前述のとおり examples/at/* を使用します。

仮想マシン側で app/ へ移動し gen_misc.sh スクリプトを実行するとバイナリがビルドされます。 以下のビルド開始時のオプション指定はメーカー側ビルドでの内容に準じています。

$ pwd
/home/esp8266/Share/esp_iot_sdk_v1.3.0_15_08_08_build/esp_iot_sdk_v1.3.0/app

$ make clean
make -C user clean;
make[1]: Entering directory `/mnt/Share/esp_iot_sdk_v1.3.0_15_08_08_build/esp_iot_sdk_v1.3.0/app/user'
rm -f -r .output/eagle/debug
make[1]: Leaving directory `/mnt/Share/esp_iot_sdk_v1.3.0_15_08_08_build/esp_iot_sdk_v1.3.0/app/user'
rm -f -r .output/eagle/debug

$./gen_misc.sh
gen_misc.sh version 20150511

Please follow below steps(1-5) to generate specific bin(s):
STEP 1: choose boot version(0=boot_v1.1, 1=boot_v1.2+, 2=none)
enter(0/1/2, default 2):
1
boot mode: new

STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)
enter (0/1/2, default 0):
1
generate bin: user1.bin

STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)
enter (0/1/2/3, default 2):

spi speed: 40 MHz

STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)
enter (0/1/2/3, default 0):

spi mode: QIO

STEP 5: choose spi size and map
    0= 512KB( 256KB+ 256KB)
    2=1024KB( 512KB+ 512KB)
    3=2048KB( 512KB+ 512KB)
    4=4096KB( 512KB+ 512KB)
    5=2048KB(1024KB+1024KB)
    6=4096KB(1024KB+1024KB)
enter (0/2/3/4/5/6, default 0):
2
spi size: 1024KB
spi ota map:  512KB + 512KB


start...

make[1]: Entering directory `/mnt/Share/esp_iot_sdk_v1.3.0_15_08_08_build/esp_iot_sdk_v1.3.0/app/user'
DEPEND: xtensa-lx106-elf-gcc -M -Os -g -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions
-nostdlib -mlongcalls -mtext-section-literals -ffunction-sections -fdata-

(中略)

!!!
Support boot_v1.2 and +
Generate user1.1024.new.2.bin successully in folder bin/upgrade.
boot.bin------------>0x00000
user1.1024.new.2.bin--->0x01000
!!!

以上の手順で bin/upgrade/ 下に生成される user1.1024.new.2.bin を前出の bin/at/user1.1024.new.2.bin の代わりにフラッシュメモリの 0x01000 番地を起点に書き込めば完了です。

なお、boot*.bin, blank.bin はユーザによるビルドの対象ではありません。これらは SDK に含まれているものをそのまま使います。

HTTPS クライアント処理はどのように書けばいいの?

SDK にはドキュメントとして「ESP8266 SSL User Manual」が同梱されています。しかし、現時点では ESP8266 モジュールでの SSL 利用に関する情報は少ないのが実状です。

ちなみに、IBM 所属の Neil Kolban 氏が 2015年8月1日に公開した「Kolban's book on the ESP8266」は技術情報が詳細に記述された貴重な資料ですが、同著においても SSL 処理については API の紹介のみに留まっています。

各国の利用者も手探りの途中といった印象で次のような応酬が随所で見受けられます。


By helpme -  Tue Jun 30, 2015 5:14 am

Can the ESP8266 support HTTPS websites? Does the Espressif SDK
support SSL function?
By Angelo Santagata -  Tue Jun 30, 2015 4:51 pm

Alas I no, but I happened to see this ESP8266 SSL Example on their website!
http://bbs.espressif.com/viewtopic.php?f=21&t=389

hopefully someone can port it to the SDK
というわけで、メーカー側は下記ページに HTTPS クライアント処理のサンプルコードを掲載しています。

Here is a demo code of ESP8266 as TCP SSL client. 
It is based on ESP8266 SDK without OS.

It just like ESP8266 to be normal TCP client except
change espconn_xxx to be espconn_secure_xxx

If your SSL packet is larger than 2048 bytes, please
try to call espconn_secure_set_size
to enlarge SSL buffer size (max:8192 )

このコードを下敷きに AT コマンドを拡張すればよいと考えました。

AT コマンド拡張では SSL 処理が正常動作しない?? (SDK v1.2.0)

さっそく自作の +GETTEST コマンドを書いてみました。
ちなみにこの実験の時点で使用していた SDK は 2015年8月初旬当時の最新版だった v1.2.0 です。

試作した +GETTEST コマンドは次の内容です。

AT+GETTEST="hostname" で当該ホストへ "GET /" リクエストを送る

AT+GETTEST  --- 引数がなければ www.example.com:80 へ HTTP リクエスト

AT+GETTEST="www.example.com",0 --- 2nd arg が 0 なら
                                   80番ポートへ HTTP リクエスト
AT+GETTEST="www.example.com",1 --- 2nd arg が 1 なら
                                   443番ポートへ HTTPS リクエスト

AT+GETTEST="www.example.com",1,0 --- 3rd arg が 0 ならサーバ証明書検証なしで
                                     443番ポートへ HTTPS リクエスト
AT+GETTEST="www.example.com",1,1 --- 3rd arg が 1 ならサーバ証明書検証ありで
                                     443番ポートへ HTTPS リクエスト
ソースコードを以下に示します。
(注:これらはその後の調査結果も反映したものです。最初の実験の段階ではずっとラフな内容でした) ところがこの AT+GETTEST コマンドは期待通りには動作しませんでした。HTTP リクエストは正しく処理されるものの HTTPS リクエストではエラーが発生するのです。
AATTEE00


OK
AT+GMR
AT version:0.30.0.0(Jul  3 2015 19:35:49)
SDK version:1.2.0
compile time:Aug 11 2015 14:09:10
OK


AT+GETTEST="www.example.com",1
mode : sta(18:fe:34:a4:0f:a0) + softAP(1a:fe:34:a4:0f:a0)
add if0
11
f 0, 221
scandone
add 0
aid 4

connected with hogehogeSSID, channel 10
WIFI CONNECTED
dhcp client start...
WIFI GOT IP
ip:10.10.0.19,mask:255.255.255.0,gw:10.10.0.1
got ip !!!
start connect to server
client handshake start. 
client handshake failed
reconnect callback, error code -28 !!! 
エラーコード -28 は SDK の include/espconn.h に定義されています
/* Definitions for error constants. */

#define ESPCONN_OK          0    /* No error, everything OK. */
#define ESPCONN_MEM        -1    /* Out of memory error.     */
#define ESPCONN_TIMEOUT    -3    /* Timeout.                 */
#define ESPCONN_RTE        -4    /* Routing problem.         */
#define ESPCONN_INPROGRESS  -5    /* Operation in progress    */

#define ESPCONN_ABRT       -8    /* Connection aborted.      */
#define ESPCONN_RST        -9    /* Connection reset.        */
#define ESPCONN_CLSD       -10   /* Connection closed.       */
#define ESPCONN_CONN       -11   /* Not connected.           */

#define ESPCONN_ARG        -12   /* Illegal argument.        */
#define ESPCONN_ISCONN     -15   /* Already connected.       */

#define ESPCONN_HANDSHAKE  -28   /* ssl handshake failed	 */
#define ESPCONN_SSL_INVALID_DATA  -61   /* ssl application invalid	 */
ハンドシェイクに失敗しているようですがこれでは何もわかりません。さて?

SDK v1.2.0 での AT コマンド拡張内で SSL が機能しない理由

不審に思い情報を探したところ同じ問題に遭遇したユーザとスタッフの応酬に気づきました。

Re: SSL connection via AT commands

Postby doughboy ≫ Tue Apr 21, 2015 11:59 pm

I am writing AT command to support SSL, but it seems none of
the espconn_secure_* api works. 
At least when I connect to real ssl servers using real
cerfificates. (and yes, I have set buffer size to 8196 and
use the patched ssl library) esp module will
reset (I think due to wdt timeout). I already reported this
via email.
Re: SSL connection via AT commands

Postby Espressif_Faye ≫ Mon Apr 27, 2015 10:22 am

Hi, doughboy,

We debugged on your problem, it's the RAM limitation cause that.

AT commands has only 17KBytes available heap now,
SSL need 12KBytes free heap size, 
if you espconn_secure_set_size to set 5KBytes,it is not enough..

So SSL can not be used in AT commands because RAM is limited. 

Sorry for the inconvenience.

AT コマンドプログラム実行時には残ヒープが 17KB 程度だが、SSL 処理には 12KB のヒープが必要で、そこに espconn_secure_set_size() で数 KB の SSL バッファ確保を指定すると完全に枯渇してしまう。そのため、AT コマンドで SSL を扱うことはできない、という話です。

ホストへの接続要求の前に system_print_meminfo() および system_get_free_heap_size() を呼び出してみると次の結果でした。


data  : 0x3ffe8000 ~ 0x3ffe89d8, len: 2520
rodata: 0x3ffe89e0 ~ 0x3ffeabd4, len: 8692
bss   : 0x3ffeabd8 ~ 0x3fff5010, len: 42040
heap  : 0x3fff5010 ~ 0x3fffc000, len: 28656

system_get_free_heap_size=16592 

なるほど。なお、AT コマンドセットは SDK においてはライブラリ形式で供給されており、自作コマンドを追加することはできても既存のものを変更することはできません。

さて?

冒頭に書いたように、ESP-WROOM-02 を通信モジュールとして扱う状況においてはシリアル経由でのコマンド渡しで簡単に通信処理を利用できることには便宜があります。

しかし、そこで SSL を利用できないことは大きなマイナスであり実用を想定すると使途が限定されることになるでしょう。そのため既存の AT コマンドセットに代わるインターフェイス込みでの SSL 処理の実装が必要であり、その方法はいろいろ考えられるもののどうも遠まわりな印象があります。

それはそれとして、一方では ESP-WROOM-02 は自立したマイコンとしての側面も持ち合わせているため、要件によっては単体で処理をまかなうことが可能であるわけで、とどのつまり製品出荷時の AT コマンドセットは「おまけ」程度のものと割り切るべきなのかも?

そんなことを考えながら、SSL 処理そのものが正常に動作することを確認するために前掲のプログラムを AT コマンドセットと切り離し単体で起動する形に変更して動作を試してみました。
ホストへの接続要求前のヒープ状況は以下の通りで、HTTPS リクエストは問題なく処理されました。やはりメモリ不足の問題のようです。


data  : 0x3ffe8000 ~ 0x3ffe8560, len: 1376
rodata: 0x3ffe8560 ~ 0x3ffe9764, len: 4612
bss   : 0x3ffe9768 ~ 0x3fff2b30, len: 37832
heap  : 0x3fff2b30 ~ 0x3fffc000, len: 38096

system_get_free_heap_size=34856

サーバ証明書の検証について

立ち止まっていても仕方がないので、いずれにしても必要となる SSL サーバ証明書の検証に手をつけてみることにしました。

SDK に同梱の「ESP8266 SSL User Manual」には HTTPS クライアント処理においてサーバ証明書を検証する方法が次のように記述されています。

(クリックで実寸表示)

上記ドキュメントからの抜粋

CA verify function default to be disabled, user can enable it
by espconn_secure_ca_enable.

3.1. Generate CA Certificate

(1) Put script “make_cert.py”and CA certificate into
    the same folder. 
(2) Run script “make_cert.py” to generate esp_ca_cert.bin
    which contains all CA certificates
    (2 CA certificates at most) in the same folder. 
    Download address of esp_ca_cert.bin depends on
    espconn_secure_ca_enable.

3.2. CA Verify

STEP 1: ESP8266 connects to server, read esp_ca_cert.bin
        from flash, get the corresponding SSL 
        ctx. Only 2 CA certificates is allowed at most.
STEP 2: ESP8266 starts TLS handshake, get certificate from
        SSL server, check with the CA in step 1:
        • if CA check fail, connection break;
        • if succeed, CA verify pass. 
4.2. espconn_secure_ca_enable

Function: 
  Enable SSL CA (certificate authenticate) function 

Note:
  • CA function is disabled by default 
  • If user want to call this API, please call it before 
                 espconn_secure_accept
                 (ESP8266 as TCP SSL server) or 
                 espconn_secure_connect
                 (ESP8266 as TCP SSL client)
Prototype: 
  bool espconn_secure_ca_enable (uint8 level, uint16 flash_sector) 

Parameter: 
  uint8 level : set configuration forESP8266
                SSL server/client:
                 0x01 SSL client;
                 0x02 SSL server;
                 0x03 both SSL client and SSL server 

  uint16 flash_sector : flash sector in which CA
  (esp_ca_cert.bin) is downloaded. For example, 
  flash_sector is 0x3B, then esp_ca_cert.bin need to
  download into flash 0x3B000

Return:
  true : succeed
  false  : fail 

「make_cert.py」は SDK には含まれません。下記ページにダウンロードリンクが掲載されています。

以下の手順で「https://www.example.com/」のサーバ証明書の検証を試みました。

  • DigiCert のルート証明書をブラウザから X509 形式(DER エンコード)でエクスポートし拡張子を .cer とする
  • *.cer を置いたディレクトリで上記の make_cert.py を実行し「esp_ca_cert.bin」を生成する
  • espconn_secure_connect() の前に下記行を記述しuser1.bin をフラッシュメモリサイズ 1024KB の指定でビルド
    espconn_secure_ca_enable(0x01, 0x65)); // 0x01 = client
  • 他の *.bin ファイルと一緒に、esp_ca_cert.bin をフラッシュメモリの 0x65000 番地(User Data area)を起点に書き込む
    ※ビルドした user1.bin のサイズは 300KB ほどであり余裕をみて 400KB として
    ブート領域の 4KB と合算すると 404KB につき 404 * 1024 = 413696 = 0x65000
    - 下のメモリマップ(再掲)を参照のこと
    ※1024KB フラッシュ使用時のメモリマップ
    
         - SDK の「Espressif IOT SDK User Manual」より
    
    Note 
    
    • System param (system parameter area) is the last 16KB offlash. 
    
    • User param is the user parameter area used by Espressif demo code
      ( IOT_Demo or AT ). If users develop their own application, 
      user data can be saved in any available flash area. 
    
    • User Data area ( green area in pictures below ) means the flash
      area that may be available, if program area doesn’t reach the
      maximum size, remaining area can be used to save user data. 
    
  • 期待通りにサーバ証明書の検証が行われ www.example.com への HTTPS リクエストは正常に処理され、ルート CA が DigiCert ではないサイトへの HTTPS リクエストにおいてはハンドシェイクの時点でエラーとなることを確認

解決:SDK v1.3.0 なら AT コマンド拡張内で SSL 処理が動く!

そうこうしている内に、2015-08-08 に公開された esp_iot_sdk_v1.3.0_15_08_08 のリリースノートの次の記述が目に飛び込んできました。

Optimization:

1.Memory optimization to save 12KBytes.
            :

前述のとおり、SDK v1.2.0 を使ってビルドした AT コマンド拡張版で SSL が機能しなかったのはメモリ不足が原因だったため、v1.3.0 で 12KB 余裕が出来たのであればあるいは動作可能ではないかと考えました。 実際に v1.3.0 でビルドした user1.bin で AT+GETTEST コマンドを試したところ HTTPS リクエストが正常に処理されることを確認、ホストへの接続要求前のヒープ状況は以下の通りでした。まずはめでたし、です。


data  : 0x3ffe8000 ~ 0x3ffe83ac, len: 940
rodata: 0x3ffe83b0 ~ 0x3ffe9d40, len: 6544
bss   : 0x3ffe9d40 ~ 0x3fff18b8, len: 31608
heap  : 0x3fff18b8 ~ 0x3fffc000, len: 42824

system_get_free_heap_size=31376

動作の様子 (動画:2分19秒)

今回試作した GETTEST コマンドは所定のホストに対し "GET /" を行うのみの内容ですが、以上のように大枠での動作を確認できたため今後は任意の URI の指定や POST メソッドへの対応など実用上必要な肉付けを手元で行うことになるでしょう。楽しみです。

上の動画で使用している現時点のファーム一式のうち user1.bin 以外のバイナリを以下のアーカイブに収めています。 自分の環境の AP の SSID とパスワードをソースに記述して user1.bin をビルドし、図の要領でこれらのバイナリ一式を ESP-WROOM-02 のフラッシュメモリに書き込めば AT+GETTEST コマンドが動作するはずです。興味のある方は自己責任でお試し下さい。


(tanabe)


klab_gijutsu2 at 18:00|この記事のURLComments(0)TrackBack(0)
2015年04月21日

「TWE-LITE」ファームウェアプログラミングの試み

はてなブックマークに登録

2017-01-13 追記:
TWE-LITE(旧表記「TWE-Lite」)のメーカーが変わり同社からのリクエストに基づいて関連箇所の改訂を行いました。結果として、2015年4月に公開した元の記事に2017年1月現在の新しい情報やキーワードが部分的に反映された内容となっています。念のため要所に注釈を加えており混乱につながる要素は特に含まれていないと判断していますが、記事参照の際にはご注意下さい。

モノワイヤレス株式会社様の ZigBee 無線マイコンモジュール「TWE-LITE DIP」はコストパフォーマンスが高く人気のある製品です。手軽にノード間の無線通信を実現できるため IoT を構成する要素としても所定の圏内に閉じた機器間ネットワークのための道具立てとしても有用でしょう。
標準のものに加え複数の典型的な用途に即したファームウェア群とそのソースコードが開発環境とともに無償で公開されていることも興味深く、製品はホスト PC から専用の USB アダプタ (TWE-LITE R) または市販の USB シリアル変換モジュール/ケーブル経由で自由にファームを書き換えられるようにデザインされています。

このように単なる通信モジュールではなくプログラマブルなマイコンとしての側面を持ち合わせながら価格が低廉であることが TWE-LITE DIP の大きな魅力です。また、「超簡単!」の惹句の通り出荷時の標準ファームでは扱いやすさが特に重視されており、そういった間口の広さと奥の深さがこの製品の特長と言ってよいでしょう。

試みのきっかけ

TWE-LITE ユーザはメーカーの提示するソフトウェア使用許諾契約書に基づいて TWE-NET SDK を利用することができます。また、規約の認める範囲で公式ファームのソースコードを改変したりその内容を公開することも可能です。

ネットを参照すると現時点では実際に公式ファームに手を加えて TWE-LITE DIP を使っている利用者は製品の人気に比べまだあまり多くないように見受けられます(注:2015年04月時点の記述)。その背景には、まず公式のファームウェア群が充実しているため改変の不要なケースの多いことが想像されますが、それに加えて、精密に記述されている各ファームのボリュームのあるソースコードはその内容を適切に理解しなければ手を触れにくいという事情もあるように思われます。メーカーが公開しているこれらのファームは基本的に実用を目的とするものであって教育用ではありませんから、利用する側はコードの世界をじっくり楽しみながらノウハウを覚えるスタンスに立つことが好ましいでしょう。

ただ、ここしばらく TWE-LITE に触れた印象では、そこに「ちょっとした足がかり」があれば公式ファームのコードをより見渡しやすくなり、そのことがこの優れた製品を手元で活用する機会を拡げることにもつながるように感じました。思い浮かべたのは "Hello, world!" のようにもっとも単純で簡潔な内容からはじまり徐々に処理が肉付けされていくイメージの小さなサンプルファームコード群です。でも残念ながら今のところそういうものは見当たりません(注:2015年04月時点の記述)。そこで、実験と勉強をかねて現時点での自分の到達点なりにそういうコードを書いてみることにしました。一連の過程での疑問点・不明点の解決には言うまでもなく公式ファームのソースと上記の SDK マニュアルが非常に参考になりました。この試みは今後も継続するかもしれませんが、まずはここまでのソースコードと動作の様子を公開します。興味のある方は覗いてみて下さい。

※本記事に掲載のソースコードには公式ファームウェアのソースからの
 引用が含まれます。取り扱いに際しては著作権表記を確認の上、前掲の
 「モノワイヤレスソフトウェア使用許諾契約書」の内容を遵守して下さい。
  なお、本記事での各ソースコードの掲載ならびにメーカー公式サイト上の
  個々のページへの直接のリンク・公式文書からの情報の抜粋については
  いずれも事前にモノワイヤレス様より承諾を得ています。

※本記事の本文および本記事に掲載のソースコードには誤りが含まれて
 いる可能性があります。そのことがいかなる損害に繋がったとしても
 筆者および KLab は一切の責任を負いません。あらかじめご了承下さい。

技術情報について

TWE-LITE 用プログラミングに必要な情報は多岐に渡り、メーカー公式サイトの情報前掲の TWE-NET SDK マニュアル、また、NXP 社製 JN5164 用のペリフェラル (TWE-LITE の I/O ポートまわり) API のマニュアル等が主な情報源となります。後続のソースコードを参照する上で最小限必要なもっとも基本的な資料三点を以下に抜粋します。
※図はクリックで大きく表示されます

TWE-LITE DIP のピン配置   (メーカー公式サイトの「超簡単!TWE標準アプリ」ページより)

TWE-LITE ファームウェアコードの動作フロー   (前掲の SDK マニュアルより)

Test01: LED を点滅させる

まずはマイコン・電子工作の世界での「Hello, world!」にあたる LED の点滅、いわゆる「L チカ」を行います。ネットワーク通信は行いません。

動作の様子

装置の写真と構成図(クリックで大きく表示)

ソースコード  GitHub

ユーザ定義のイベントハンドラ内で 1秒周期の E_EVENT_TICK_SECOND 通知に呼応し出力ポートの Lo, Hi をトグルする

Test02: スイッチ操作に LED の状態を連動させる

上の Test01 は全自動かつエンドレスですが、今度は人間の操作に反応させてみます。タクトスイッチの ON/OFF に LED の点灯/消灯 が連動します。ネットワーク通信は行いません。

動作の様子

装置の写真と構成図(クリックで大きく表示)

ソースコード  GitHub

既定のイベントハンドラ cbToCoNet_vMain() 内でタクトスイッチの状態に応じて LED の状態を変化させる

Test03: シリアル経由でデバッグメッセージを出力

プログラミングにはデバッグのための手段が不可欠です。Test02 のコードに、シリアル接続経由で PC 上のターミナルアプリへトレース文を出力する処理を加えてみます。PC との接続には専用の USB アダプタ 「TWE-LITE R」を使用しています。 (市販の USB シリアル変換モジュール/ケーブルも利用できます ネットワーク通信は行いません。

動作の様子

装置の写真と構成図(クリックで大きく表示)

ソースコード  GitHub

起動時に UART とデバッグ出力用の初期化を行い vfPrintf() 関数を使ってシリアルへトレース文を出力

Test04: スイッチ押下でメッセージを送信 〜 受信側は LED で反応

無線送受信を行います。Test02 の装置と同じものをもうひとつ用意します。タクトスイッチが押下されるとメッセージをブロードキャストし、受信した側は LED を一定時間点灯させます。

動作の様子

装置の写真と構成図(クリックで大きく表示)

ソースコード  GitHub

スイッチ押下時に ToCoNet_bMacTxReq() によりブロードキャストを実行。既定のイベントハンドラ cbToCoNet_vTxEvent() および cbToCoNet_vRxEvent() 内で送受信通知への対応を行う

Test05: 電力消費を抑制した送信専用コードと装置

上記 Test04 での送信処理を独立させ、消費電力を抑えることを目的にメーカー公式の「無線タグアプリ(App_Tag)」(注:2015年04月時点での名称は「Samp_Monitor」)押しボタン・磁気スイッチ対応機能における子機処理(EndDevice_Input)での以下の要所を取り入れた内容です。

  • TWE-LITE のモード設定ビット 1 (M1) が GND に接続されていれば(すなわち M1 が Lo であれば)、デジタル入力 1 (DI1) の立ち上がり (Lo -> Hi) を送信のトリガーとする
    (典型的には平時が導通状態の磁気リードスイッチが磁界から離れ切断された状況)
    • 立ち上がりトリガーの場合 節電のため DI1 の内部プルアップを無効化する。そのためこの場合は外部プルアップ抵抗を設置する。(公式サイト上の記事を参考に 1MΩ 抵抗を使用)
  • M1 が GND に接続されていなければ DI1 の立ち下がり (Hi -> Lo) をトリガーとする
    (典型的には平時が非導通状態のタクトスイッチが押下された状況)

  • 送信を終えたらすみやかに Sleep 状態へ移行し DI1 の状態が変化すると Sleep から復帰する

受信側は Test04 のものをそのまま使います。以下の動画・写真では送信側の装置は磁気リードスイッチを使っており立ち上がり検出を行っています。 ちなみにこの装置の待機(Sleep)状態の消費電流を測ったところ 3μA(0.003mA)でした。一般に CR2032 の放電容量はおよそ 225mAh であることから、下記のサイトを利用して単純計算すると待機継続可能期間は 225mAh / 0.003mA * 0.7 = 52500時間 = 2187.5日 ≒ 5.99年となります。実際の電池寿命は送信頻度によって大きく変わるでしょう。

電池寿命カリキュレータ - www.digikey.jp

動作の様子

装置の写真と構成図(クリックで大きく表示)

ソースコード  GitHub

電源投入直後やリセット後はまずそのまま Sleep 状態へ移行。DI1 の状態が変化すると起床して送信を行いふたたび Sleep へ。DI1 の立ち上がり/立ち下がりのどちらで送信を行うかは M1 の Lo / Hi 状態で決定する

付録:「無線タグアプリ」カスタマイズの記録

2015年4月、手元の実験的要件への対応のために前掲の公式アプリ「無線タグアプリ(App_Tag)」(当時の名称は「Samp_Monitor」)の親機(Parent/)および子機(EndDevice_Input/)コードのカスタマイズを試みました。以下はその記録です。

変更を加えたソース・ヘッダ

※ベースのバージョンは 2015年4月20日当時の最新版「Samp_Monitor v1.4.1 β」です(2017年1月現在は既に公開終了)
※変更箇所は識別子「MODIFIED_BY_KLAB」で区別しています

変更内容

EndDevice_Input:

  • センサモードが PKT_ID_BUTTON(押しボタン・磁気スイッチ) の場合、一度の送信完了でただちにスリープ状態へ移行せず所定の回数繰り返し送信を行ってから移行する (diff)

Parent:

  • 電子ブザーの接続を想定し DO4 の使用を追加。センサモードが PKT_ID_BUTTON の子機からパケットを受信した場合、DO1 の LED トグルに合わせ DO4 の状態もトグルする
  • PKT_ID_BUTTON の子機からの受信発生後は親機がリセットされるまで DO1 の LED を点滅させる(受信有無を事後に目で確認するための便宜) (diff)

以下の動画・写真では子機側の装置は磁気リードスイッチを使用し立ち上がり検出を行っています。したがってこの子機のパラメータ設定において「m:センサ種別の設定」には 0xFE、「p:センサ固有パラメータの設定」には 1 を指定しています。

動作の様子

装置の写真と構成図 上:親機 下:子機 (クリックで大きく表示)

(備考)

2017年1月13日時点での最新版 SDK 「2014/8月号」に収録されている旧 Samp_Monitor v1.3.2 の子機側ソースコードには本来の意図とは異なるものと考えられるロジックが含まれています。

  1. 子機のパラメータ設定内容をセーブ領域から読み出すタイミング
    (/TWESDK/Wks_ToCoNet/Samp_Monitor/EndDevice_Input/Source/EndDevice_Input.c)
    ※ 旧 Samp_Monitor v1.4.1β で改修されました
  2. 子機のセンサモードが PKT_ID_BUTTON (0xFE) の場合の固有パラメータ 立ち上がり (1) / 立ち下がり (0) 指定に対する処理
    (/TWESDK/Wks_ToCoNet/Samp_Monitor/EndDevice_Input/Source/ProcessEv_Button.c)
    ※ 旧 Samp_Monitor v1.3.3 で改修されました

そのため、今の時点で「無線タグアプリ」を使う場合は単体で配布されている版を利用するほうが良いでしょう。


(tanabe)
klab_gijutsu2 at 16:37|この記事のURLComments(12)TrackBack(1)
2015年02月17日

mbed と Parse で作る高齢者世帯安否確認システム

はてなブックマークに登録

2017-01-30 追記:
2017年1月末の Parse.com サービス終了に伴い以下の記事を追加しました。他サービスへの移行に関する情報等を記述しています。

さよなら Parse - 当ブログ

個人的に、高齢者世帯の安否を日常的に静かに確認できる仕組みがほしいと思っていました。要件として想定していたのは次の二点。現地にはインターネットへの接続環境が整っています。

  • 現地での被監視感や拘束感が希薄であること
  • 情報を自分でハンドリングできること
実現方法を考えると案外悩ましく、よくあるリモートカメラの類は手軽な反面 日常を生々しく監視するようでNG、自作の選択肢はあれど PC をフロントエンドとすることには何かと牛刀感があり気が進まず、また、各社様による優れたサービスの数々にも関心はあるものの恒常的に費用がかかることは当然としても自分が個人的に望む柔軟性・拡張性に適合する仕組みをなかなか見出すことができず導入には至らずにいました。
以前から頭の中でときどきそういうループを巡らせていたのですが、先日ふとマイコンとクラウドサービスを組み合わせて利用することを思いつきました。折からの「モノのインターネット(Internet of Things : IoT)」の潮流にも合致するためその視点からも面白そうです。

マイコン方面には今までほとんど馴染みがなかったのですが、情報収集を経て選択した mbed マイコンボードを利用することでフロントエンドの装置本体もそれを動かすためのプログラムも手早く作ることができました。バックエンドにはメジャーな BaaS のひとつである Parse を利用しサーバ側処理もシンプルに仕上げています。必要な費用は装置材料分のおよそ 1万円程度(2015年2月現在)のみでクラウドサービスは手元では無料枠内でゆったりと使えており、今後もいろいろ手を加えていくつもりです。なかなか便利なものになったので現時点での内容と構成を紹介します。離れて暮らす家族を気に懸けておられる方のご参考となれば幸いです。

全体像を図に示します。

動作の様子

装置の動きとクラウドサービスとの連携の様子を以下の動画に収めています。

動画1:装置が人間を検知すると Parse サーバへデータを送出

動画の内容
(無音 1分15秒)

  1. 装置へ給電 〜 初期化が完了するまで全 LED が点滅
  2. 赤外線センサが人を検知し赤 LED が点灯 〜 Parse サーバへ検知情報を送出
  3. PC 上のブラウザでは Parse アプリ「Anpi」の「Detected」 クラス(テーブル)のデータを表示中
  4. 表示を更新して新しいデータが追加されていることを確認
  5. Gmail で前日受信したレポートメール(人を検知した日付時刻と気温情報の直近の一覧)を表示してみる
  6. 最後にふたたび装置の赤外線センサに手をかざして反応を試してみる

動画2:装置の「緊急ボタン」が長押しされるとその場でメール通知

動画の内容
環境音+ブザー音あり 30秒)

  1. 緊急ボタンを押すと装置の LED が反応 〜 短時間の押下はそのまま受け流される
  2. 2秒程度長押しすると操作者にブザーで長押しの受け入れを伝え Parse サーバへ緊急ボタン押下を通知
  3. ボタン押下がメールで通知される

mbed について

ARM 社 によって開発された mbed は ARM マイコン搭載のコンピュータボードと専用の開発環境によって構成され「高速プロトタイピングツール」のキャッチフレーズの通りシンプルな手順で手早く扱うことができるように設計されています。現在さまざまな mbed ボードが販売されていますが、今回選んだのはもっとも代表的な「mbed NXP LPC1768」です。Cortex-M3 プロセッサを載せたこの単三電池二本分ほどの大きさのボードには多様な入出力ポートに加えイーサネット用コントローラまでもが実装されています。

初めて mbed に触れる場合は mbed.org へのユーザ登録が必要です。製品に同梱の USB ケーブルで mbed LPC1768 を PC へ接続するとボード上のストレージがマウントされます。ストレージ上の「MBED.HTM」をブラウザへロードするとユーザ登録画面へ遷移するのでそこへ必要な情報を投入します。登録が完了すると mbed のオンラインコンパイラをブラウザから利用できるようになります。C, C++ でコードを記述し「Compile」ボタンを押すとサーバ側でビルドが行われ、そこで生成された実行形式をダウンロード 〜 mbed の USB ストレージへコピーしてボードのリセットボタンを押下するとプログラムが実行されます。そこから先は、コードを編集したり既存のライブラリをインポートしたり mbed ボードを装着した回路に手を加えたり、、といった手順の繰り返しです。作法どおり最初はまずあれこれサンプルコードをビルドして動かしたりそれに手を加えて試したりということから始めました。なお、mbed ボードのコントローラは USB 仮想シリアルポート経由でホスト PC とのシリアル通信をサポートしており PC 側でターミナルアプリを使えばデバッグにも便利です。

mbed の世界ではコミュニティが発展しており様々な資産が共有されています。わかり易く有用な記事・文書も数多く存在するのでとても参考になります。

装置について

製作した装置の材料と組み立て方、制御用プログラムについて説明します。

材料

(※価格はいずれも2015年1月時点の税込額)

  • マイコンボード mbed NXP LPC1768 ¥5,800 秋月電子通商
  • 焦電型赤外線センサーモジュール (SB612A) ¥600 秋月電子通商
  • LAN コネクタ (mbed用イーサネット接続キット) ¥514 スイッチサイエンス
  • IC 温度センサ (LM61BIZ) ¥200 (4個入) 秋月電子通商
  • 電子ブザー (PKB24SPCH3601) ¥150 秋月電子通商  
    ※不要なら省略可
  • ブレッドボード (EIC-801) ¥257 スイッチサイエンス
  • ジャンプワイヤ (固いオス〜オス)  ¥257 (70本入) スイッチサイエンス
  • ジャンプワイヤ (柔らかいオス〜メス) ¥691 (50本入) スイッチサイエンス
  • 赤色 LED ¥350 (100個入) 秋月電子通商  
    ※不要ならカーボン抵抗とともに省略可
  • 75 Ω カーボン抵抗 ¥100 (100本入) 秋月電子通商  
    ※不要なら LED とともに省略可
  • 押しボタン・ピンコネクタ・導線 近所のホームセンターで 5〜600 円くらいで購入 
    ※不要なら省略可
  • 適当なケース 廃物利用
※上記参考価格計 ¥9,519
※LAN ケーブルと DC 5V USB-Aタイプ の AC アダプタは手持ちのものを利用

組み立て

LAN コネクタである「mbed用イーサネット接続キット」の組み立てのみハンダづけが必要ですが、その他の配線・パーツの取り回しはすべてブレッドボード上で行うため材料が揃っていれば装置の組み立ては簡単です。写真のようにブレッドボード上の配線には固いジャンプワイヤ、センサやブザーの接続にはオス−メスの柔らかいジャンプワイヤを使いました。実体配線図をあわせて掲載します。(クリックで拡大)

むき出しのままでは扱いにくいので前に買った PC 用パーツの外箱を再利用して簡単なケースを用意しました。素材が薄いプラスチックと紙なので加工しやすく耐久性もそれなりにありそうです。

焦電型赤外線センサモジュールは白いレンズの部分を露出しないと機能しないためサイズに合わせてケースに切り込みを入れました。測定誤差軽減のために IC 温度センサも外に引き出しています。外観を不透明にしているのは設置先での存在感を抑えるためで、LED は動作確認用と割り切りトラブルが起こった場合のみ点滅状態を見ることにしています。

プログラム

上記の装置構成にあわせて作成した mbed LPC1768 用プログラムを以下の場所で公開しています。

Parse サーバ上の対向処理との連携のために main.cpp の冒頭に当該 Parse アプリケーションの ID と REST API キーを記述する形にしています。公開したソースでは伏字にしていますが、後述の手順で当該 Parse アプリのコピーを作成しその ID とキーをここに記述してリビルドすれば実際に連携を試すことができます。Parse 上の所定の ID とキーは Parse へのログイン後、[Dashboard] - [(アプリ名)] - [Settings] - [Keys] から確認できます。

装置には識別用に任意の名前をつけることができます。名前は mbed ボードの USB ストレージ上の "id.txt" ファイルに記述します。この名前は装置が人を検知した際に Parse サーバへ送出する情報のひとつとして扱われます。

装置が人を検知するたびに Parse サーバへデータを送るのは不経済なので、データ送出の最短間隔を以下の定義で指定しています。
#define HTTPS_REQUEST_INTERVAL 1800 // 30分

クラウドサービスについて

装置の対向処理用に用意した Parse 用のサーバコードと、そのコードからメール送信のために呼び出している Mailgun サービスについて説明します。

Mailgun の準備

Mailgun は多くのプログラミング言語に対応したクラウドメールサービスです。REST API セットに加え Python, Ruby, Java, C#, PHP 用のライブラリが公開されており、自作のコードで簡単にメール送信をハンドリングできる便利なサービスなので今回の話題とは無関係にアカウントを作って試してみるのも良いでしょう。サインアップ時にクレジットカード情報の入力は不要です。

http://www.mailgun.com/

Mailgun にアカウントを作成すると一件の「Sandbox Domain」が割り当てられます。この Mailgun Subdomain はテスト用とされておりメール送信は 300件/日までという制約がありますが、自前のドメインがあればそれを登録することでこの制約はなくなります。なお、無料枠はこの記事の時点で 10,000件/月までとなっています。[詳細]
ちなみに手元では今のところ一日 300件もメールを送信できればまず十分なのでひとまず Sandbox Domain をそのまま使っています。

Parse からの Mailgun API 呼び出しにおいては Mailgun アカウントに紐付けられた「API Key」を使用します。Parse は Mailgun と公式に連携しているためごくシンプルなコード記述で Mailgun API を呼び出すことができます。

Parse の準備

Parse は Facebook 傘下の Parse.com が開発・運営する現在もっともメジャーな BaaS(Backend as a Service) のひとつです。ユーザ管理・データストア・スマホへのプッシュ通知・アクセス解析・ホスティング・SNS 連携・位置情報連携など多くの機能から構成されており対象プラットフォーム・言語も広範です。無料枠も広く 30リクエスト/秒まで、プッシュ通知 100万件/月までは無料、また、アクセス解析機能も自由に利用できます。[詳細]

https://parse.com/

一般に BaaS の主な目的はアプリケーションの対向サーバ側機能を代替・補完することにあり、サーバの管理運用やサーバ側コード開発に踏み込むコストを抑制しアプリ本体の開発に注力可能となることが利用者にとってのメリットですが、Parse にはサーバ上でユーザコードを実行することのできる「Cloud Code」というしくみが用意されています。これを利用することで標準の機能のみではカバーできない固有の要件に対応できる余地が大きくなり、また、フロントエンドのアプリケーションを一切伴わず所定のサーバコードのみを Parse サーバ上で実行するといった使い方も可能となります。今回作った装置は Parse サーバ上に設置した自作コードをそのまま呼び出して必要な処理を行っています。このように目的に応じて柔軟に利用できることが Parse の魅力のひとつと言ってよいでしょう。

Parse サーバ用に作成したコードを https://github.com/mkttanabe/Anpi で公開しています。このコードを自分の Parse アカウント環境で使用する手順を以下に記述します。

1. 「Anpi」アプリと「Detected」クラスの作成

Parse へログイン後、[Dashboard] - [Create a new App] をクリックして「Anpi」という名前のアプリを作成し同アプリの [Core] へ移動

[Dashboard] - [Anpi] - [Core] - [Data] の [+ Add Class] をクリックして Custom タイプの「Detected」クラスを作成
※ここでいう「クラス」とは Parse 用語であり、「テーブル」の概念とほぼ同じものです

[+Col] ボタンをクリックして Detected クラスに次の各カラムを追加。カラム並びは任意に調整可

  • 「posted」Date 型
  • 「deviceId」String 型
  • 「deviceAddress」String 型
  • 「temperature」Number 型

装置から受信した人検知情報はこの Detected クラスのデータとして保存されます。

2. Parse コマンドラインツールの導入と Cloud Code のセットアップ

Parse 用の開発を行う PC には専用のコマンドラインツールの設置が必要です。公式ドキュメント「Cloud Codeとは」の冒頭の説明にそって開発環境へツールをインストールします。インストールが終わったら任意の開発用フォルダへ移動し「parse new」コマンドで「Anpi」アプリ用 Cloud Code の雛形を生成します。

$ parse new Anpi
Email: your@mail.com
Password:
1:Anpi
Select an App: 1

github.com/mkttanabe/Anpi 下の Parse/Anpi/cloud/main.js の内容を開発用フォルダ下の Anpi/cloud/main.js へ上書きし、冒頭の下記の箇所を自分の Mailgun ドメインと API Key、送信先とするメールアドレスで書き換えます。

var MAILGUN_DOMAIN = 'sandboxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.mailgun.org';
var MAILGUN_KEY    = 'key-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
var MAILGUN_TO     = 'Makoto Tanabe <XXXXXXXXXXXXX@XXXXX.XXXX>';
var MAILGUN_FROM   = 'Safety Alert <postmaster@' + MAILGUN_DOMAIN + '>';

開発用フォルダ下の Anpi/ へ移動しそこから「parse deploy」コマンドを実行すると配下の各ファイルが Parse サーバへ転送されます。

$ cd Anpi
$ parse deploy
Uploading source files
Finished uploading files
New release is named v1

Parse サーバへ転送したコードの動作は 公式ドキュメント「Cloud Codeとは] - [はじめに] - [シンプルな関数] に記述された方法で確認できます。

3. ジョブの登録

上の手順で deploy した main.js には Detected クラス上の直近 30件のデータをメールでレポートするためのジョブ用関数「report」が含まれています。 ジョブの登録・編集・削除は [Dashboard] - [Anpi] - [Core] - [Jobs] から行います。図の UI から任意の内容で設定可能です。

※無料枠の Parse アカウントでは複数のジョブを同時に実行することはできません

以上の手順・設定で、今回作成した mbed ベースの装置本体と各クラウドサービスが連携して稼動します。アイディア次第で応用も可能でしょう。こういう仕組みを低コストで自作できる時代になりました。


(tanabe)
klab_gijutsu2 at 07:54|この記事のURLComments(0)TrackBack(0)
Blog内検索
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント
最新トラックバック
Archives