masato-ka's diary

日々思ったこととか、やったことの備忘録。

SORACOM Airを管理できるVSCode Extentionを作ってみた。

この記事について

この記事では自作のVSCode Extentionを紹介します。今回はSORACOM AIrVSCode上から管理できるExtentionです。

作った理由

最近ではWioLTEを使い、SORACOM Beamを利用してMicrosoft Azure IoT Hubや他のクラウドサービスに接続する機会が行くかありました。WioLTEの開発では、 以前紹介したVSCode + PlatformIOを利用した環境で開発をしています。

masato-ka.hatenablog.com

また、Azure IoT HubもVSCodeのExtentionから管理することができます。そのため、Azure IoT Hubの設定とWioLTEの開発はVSCode側で完結させることができます。 WioLTEの開発を進める上で、SORACOMのサービスを利用しようとすると、どうしてもSIMへの設定が必要であったり、SORACOM Beamへの設定が必要になります。また、動作テスト時には正常にSIMが接続されているかを確認する場面が多くありました。その度に、ブラウザからSORACOMの管理画面に移動するのが煩わしく感じました。そこで、SORACOMのAPIを使い、SORACOM サービスの設定もVSCodeからできるExtentionがあると便利だなと思い開発に至りました。

開発中のプラグインについて

2018年4月1日の段階で、バージョン0.3.0として公開しています。VSCodeマーケットプレイスには公開していないため、使ってみたい方がいたら、個別にインストールしてください。いずれ、マーケットプレイスに公開したいと思っています。ライセンスはMITです。

github.com

また、SORACOM APIJavaScriptライブラリとして以下のライブラリを使わせていただきました。

github.com

主な機能

主な機能を紹介していきます。

1. SIM一覧の確認

Extentionをインストールすると、VSCodeのサイドバーに"SORACOM MANAGER"が表示されるようになります。VSCodeの設定ファイルにAPI Key IDとシークレットキーを設定することで認証を自動で行い、SIM一覧を取得できます。現状SIMの数が多くなるとすべて取得することができないため、修正する予定です。また今後はグループごとにサブツリー化して表示するなど考えています。

ちなみに、立ち上げっぱなしで、API tokenの有効期限が切れると使えなくなるので、VSCodeを再起動してください。次のバージョンでで直す予定です。ひどいバグだ。

2. SIMの操作

SIMのリストを右クリックで選択することで、各SIMに対して操作をすることができます。現在操作できるコマンドは以下の通りです。また、これらのコマンドはもちろんコマンドパレットから実行することができます。

f:id:masato-ka:20180401232340p:plain

コマンド名 意味
Get sim details SIMの詳細情報を取得する。
Update sim speed SIMの通信速度を変更する。
Update sim name SIMの名前を変更する。
Add new tag to sim SIMにタグを追加する。
Delete tag タグの削除
Show stats Air of SIM Airの通信量を取得する。
Show stats Beam of SIM Beamの通信量を取得する。

SIMのTagはメタデータサービスにより、SIM固有のデータとしてデバイス側から参照できます。WioLTEでメタデータサービスを使う場合、"Add new tag to sim"など開発時やデバック時に便利です。

Update sim speedを実行すると以下のようにSIM スピードを選択できるようになります。

f:id:masato-ka:20180401231237p:plain

3. 利用料金の確認

SIMの操作以外では利用料の確認ができます。これはそのうち常駐させたい。。。

f:id:masato-ka:20180401232404p:plain

こんな感じでダイアログが表示されます。SIM2枚持ちですが、ほとんど使ってないのがバレてしまいます。

f:id:masato-ka:20180401232014p:plain

まとめ

 VSCode Extentionの作り方についてはなんとなくわかったという程度でした。が、なれるとサクサク作れそうです。どちらかというと今回はJavaScriptの非同期処理周りではまった時間が多かった気がします。コールバック地獄にならないように、Promiseでラップしまくりましたが、なんかバットノウハウな気もしています。(理由はありませんが。)この辺りのTipsは後ほど記事にしていこうと思っています。  今後もExtentionを拡張していこうと考えています。もし使ってみたと言う方がいたら改善要望やバグをあげていただけると嬉しいです。次はグループの管理機能を追加していきたいと思います。また、依存しているライブラリの関係で、テストの実行には本番のAPIを利用しています。sandboxのAPIを活用してテストができるようにしていきたいです。JavaScriptのDIとかちょっと使ってみたいです。

Microbot Push2のAPIについて調べてみた。

この記事について

この記事ではMicrobot Push2を制御するにためにBLEのインタフェースについて調べた内容についてまとめました。Microbot Push 2とのペアリングと、モータの制御方法について記載します。

Microbot Push 2について

Microbot Push 2はNARAN社から発売されている物理的なスイッチを押すためのデバイスです。このデバイスを使うとスマホのアプリから物理的なスイッチを押すことができます。照明のスイッチや家電製品などを手軽に遠隔操作することができます。

我が家の寝室の照明スイッチはなぜか部屋の一番奥にあります。そのため、夜部屋に入る時に電気をつけるのが大変めんどくさいです。そこで今回実現したかったことはGoogle Homeにお願いするとMicrobot Push 2が部屋の照明を押してくれる仕組みです。 Microbot Push 2単体ではBLEのインタフェースしか存在しませんが、IFTTTやスマートスピーカーと連携させる仕組みがいくつかあります。

1. Prota S

Prota SはNaran社から発売されているMicrobot Push 2と接続できるハブです。こちらを購入することでMicrobot Push 2をIFTTTから制御することができます。 以下商品ぺーじは並行輸入品のページです。ご注意ください。

Prota S [並行輸入品]

Prota S [並行輸入品]

2. Raspberry Pi + Prota Pi

Prota OSな高額なので買うのはちょっとという場合は、ご家庭にあるRaspberry Piが利用できます。公式から配布されているRaspberry Pi用のOS、Prota Piを利用するとProta S相当のものが作れます。

prota.info

BLEのインタフェースの調査

上記2の方法でおおよそのユースケースはカバーできそうですがもう少し柔軟な構成でMicrobot Push2を使いたい場合はBLEのインタフェースを直接叩く必要があります。そこで、BLEのインタフェースがどのようになっているか調査しました。

参考にしたプロジェクト

Microbot Push2用のサーバをOSSで開発していらっしゃる方がいます。

github.com

このリポジトリの以下のディレクトリでMicrobot Push 2のBLEインタフェースについて記載があります。

https://github.com/VRGhost/PyPush/blob/master/docs/microbot_ble_api.md

このドキュメンではMicrobot Push 2のBLEサービスとキャラクタリスティックについて記載があります。しかし、ペアリングをどのように実施するか、実際に使うための記載になっていません。(不明瞭の部分も多いです)そこで、上記のドキュメントとこのかたのソースコードを参考にしながらMicrobot Push 2をペアリングしてスイッチの制御を行うまでを、Bluezのgatttoolで実行するコマンドを組み立ててみました。

$ sudo gatttool -b [Microbot pushのMAC] -t random -I 
[xx:xx:xx:xx:xx:xx][LE]> connect
Attempting to connect to xx:xx:xx:xx:xx:xx
Connection successful
[XX:XX:XX:XX:XX:XX][LE]> char-write-req 0x2A90 0000
Characteristic value was written successfully
[XX:XX:XX:XX:XX:XX][LE]> char-write-req 0x2A90 000000
Characteristic value was written successfully
#このタイミングでMicrobot Push2がペアリングモードになるので本体のボタンを押す。
[XX:XX:XX:XX:XX:XX][LE]> char-write-req 0x2A11 01 # スイッチ動作
Characteristic value was written successfully

connect から最初の書き込みまでに30秒以内に実行しないとMicrobot Push 2側から切断されます。また char-write-req 0x2A90 0000char-write-req 0x2A90 000000のコマンドのペイロードは本来接続元のMACアドレスをベースに組み立てるようですが、これでもgatttool上から接続できました。ただ、Python ラッパのgattoollibから実行すると''' char-write-req 0x2A90 000000 '''の部分で動作が止まってしまいます。LightBlueなどのツールではうまく動作します。何が原因かわかりませんが、この部分時間があるときにまた調べてみたいとお思います。

Azure、SORACOM、Futabaのサーボで金賞をもらった話。

この記事について

この記事ではIoT ALGYAN(あるじゃん)さん主催のFutaba製コマンド方式サーボIoTアプリコンテストに参加 して、考えたことや審査会当日では語りきれなかったことなどを記録として残しています。記録しないと忘れちゃう。

イベントの概要

イベントの募集ページは以下です。

algyan.connpass.com

双葉電子工業様から参加者にコマンド方式サーボモータRS304MDが配られ、2ヶ月でサーボとMicrosoft Azureを使ったIoTアプリケーションを組み上げる というのがお題でした。それ以外のテーマは自由です。

コマンド方式サーボRS304MDについて

ホビー用2足歩行ロボットにも使われているコマンド方式サーボモータです。サーボモータは通常PWMのパルス幅で角度を指定しますが、このサーボはシリアル通信で指示角度を与えて制御します。また、モータを動かすだけでなく、現在角度の取得、トルクのON/OFFやコンプライアンス制御(バネのように動かす)、モータ負荷(電流値)や電圧、温度を取得することができます。  アクチュエーションだけでなく、センシングに活用できそうというのがアイディアを考える点でもキーポイントとなりました。

www.futaba.co.jp

応募したもの

今回のコンテストで作成したアプリの詳細は以下の当日の資料をご覧ください。

サマリー

  • 小物を管理するアプリケーション
  • モータでリフター機構を作成
  • 上に乗せたものの重さをセンサーレスで計測する。
  • Microsoft Azure、SORACOMを利用する。

結果について

今回のイベントは順位がつきます。今回私は金賞をいただくことができました。 双葉電子工業様から賞状をいただきました。金賞も嬉しい話ですが、他の方の発表の中で公開したサーボモータのライブラリが思ったよりも利用いただいていたことが印象的でした。嬉しかったです。(中にはこれで間に合ったと言っていただける方もありがたや!)

f:id:masato-ka:20180320114945j:plain

コンテスト応募の動機とお題に対して考えたこと

コンテスト応募の動機について

コンテストに応募する上で自分の動機はIoTっぽいものを作ってみたいという純粋な動機とともにまだ触ったことのない新しい技術やサービスを習得したいというモチベーションがあったためです。また、自分自身同じ趣向の人と仲良くなりたいというモチベーションがあったため、交流をキーワードに参加しました。

新しい技術という点ではAzureとSORACOMを利用したアーキテクチャを自分なりに作れていけたのでよかったかなと思います。また、サーボモータのライブラリを開発し公開を行いました。このライブラリは他の参加者からのフィードバックもあり、今後も育てていきたいなと思っています。

github.com

モータのライブラリ詳細については以下の記事です。

masato-ka.hatenablog.com

交流をキーワードに積極的にコミュニティに関わっていこうと考えて行動しました。上記のように協力をいただいたり、アドバイスや次に繋がりそうなお話もいただいたりとよかったと思います。ただ、より色んな人と話したり、Azureやクラウド周りの部分も意見が欲しかったなと若干不完全燃焼感が残っています。この部分はまた別の機会でチャレンジしていきたいです。貪欲に生きたい。

お題に対して考えたこと

応募作品を考える上で以下の3つをテーマにしました。

  1. モータからの取得情報を活用したIoTサービスを考える。
  2. Microsoft Azureを触ってIoT系のサービスやServerlessといった技術の勘所を抑える。
  3. 利用方法が明確になってなかったWioLTEとSORACOMを利用する。

これに加えて、生活の課題を解決しようというテーマで一人ブレストを行いテーマを検討しました。その他のネタとしては腕のリハビリテーションマシンなどがありましたが、機構の面白さなどから今回のネタを採用しました。

自分の作品の売りポイント

1. リフトアップ機構

ものを持ち上げる。持ち上げたものの重さを測ることができる。をキーワードにいくつかの機構を考えましたが、最終的に今回のリフト機構を採用しました。機械設計は素人なのですが、10年以上前の大学の製図の時間を思い出し、見よう見まねでOSXKeynoteで図面を起こしました。加工は東急ハンズの工房で行いました。

  • プロトタイプ2の組み立て

f:id:masato-ka:20180307114957j:plain

以下は機構の動作動画です。動画の後半は副賞としてもらった高トルク版のRM303に差し替えています。 今まで厳しかった500mlのペットボトルも余裕。ええもんもろた。

www.youtube.com

2. Azure IoT Hub とSORACOMの連携

ここはいずれ別の記事に起こそうと思っています。タイミングよくSORACOM BeamとAzure IoT Hubの下り制御ができるようになったため、活用しました。また、SORACOM SIMのIMSIをAzure IoT HubのIDとして利用しています。そのため、認証情報はSORACOM Beamにあり、Azure IoT Hubに接続するために必要なデバイスIDもSORACOMのメタデータサービスから取得することができます。したがって次のような利点があります。

  1. バイスに固有の情報を書き込む必要がない(書き込みと設定が楽)
  2. 管理アプリケーション側からのデバイス追加処理を簡略化できる。

f:id:masato-ka:20180320110719p:plain

他の参加者さんの作品で特に面白いと思ったアイディア

並び順に意味はありませんが当日見てなるほどなと思ったもの

1. ボトルマネージャー(すながわさん)

 サーボモータでボトルを振って、その時のサーボの負荷から水量を推定するというもの。個人的にIoT居酒屋を将来やってみたいと思っているのでこの装置は使えるなと思いました。是非資料公開いただきたい。

2. IoTアロマディフューザー bluewell(ブルーウェルさん)

 アロマオイルの瓶を複数装置の中に設置しておく。サーボモータで空気の流路を変え、遠隔から部屋の香りを制御できるというアイディア。これも何かに使えそうと思ったアイディアでした。

3. 金庫番サーボ 情報科学専門学校 IoTゼミさん

サーボモータダイアル式金庫のダイアルに使えるというものでした。遠隔からのロックや、スマホからの暗証番号設定を行えるとのこと。個人的にはワンタイムパスワードのように毎回、開錠番号が変わったりするともっと面白いのかなと思いました。

4. フライ バイ ワイキー 高瀬さん

飛行機のフライバイワイヤ方式に習って、シリンダー錠をフライバイワイヤにしたもの。フライバイワイヤってロマンがありますよね。

上記に挙げた方以外の作品もどれも力作ぞろいでしたので、コンテストの時しか見れないのは惜しいものばかりでした。どこかで一挙展示とかすると面白そうです。

Arduino ファンもくもく会について

実は準備期間中Arduinoファンもくもく会というものにも参加していました。参加の動機は前述の交流がメインでしたが、実は他の参加者の方もきており、ここでの情報交換やいただいたアドバイスがかなり参考になりました。今後も参加していきたい活動のひとつです。

arduinofun.connpass.com

感想

全体を通して大変おもしろかたです。でも準備に時間(あとお金も)をかなり使ったなと思いまいました。ここを無駄にしないためにも今後につなげていきたいです。やってみてまだ自分もこんなもの作る根気があるんだなと思ったので、これからもいろいろ挑戦していければと思います。あとチーム組んで参加できたら面白いだろうなと思いました。また今回スポンサーだった双葉電子工業さんと主催のIoT ALGYAN(あるじゃん)さんにはチャレンジの場を提供いただきかつ素晴らしい結果までいただいて感謝しかありません。これからも是非面白い企画を作っていただきたいです。参加するだけでなく企画や運営、ネタの提供などお手伝いする機会があればより嬉しいと思っています。(貧欲に生きたい。)

RS304をWio LTEで動かすライブラリを書いてみた。

この記事について

この記事ではFUTABA社製のホビー用サーボモータRS304をWio LTE(Arduino)で動かすためのライブラリを紹介します。 現在のところWio LTE, Arduino Leonardoでの動作を確認しています。まだまだ作成中ですのですが、最低限の機能が実装できたので公開します。

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

   ライブラリは以下のGitHubリポジトリで開発しています。ダウンロードしたzipをArduino IDEに読み込ませるか、ライブラリフォルダにおくことで使えます。 利用方法はreadmeをサンプルを確認してください。

github.com

現在対応しているサーボの動作は以下の通りです。

  1. サーボの角度指定
  2. トルクのON, OFF, BREAK
  3. 角度の取得

サーボとArduinoの接続回路

以下、先人の回路を参考にして下さい。74HC125で半二重全二重の変換回路を組みます。

制限事項

 現在のところサーボのコンプライアンス制御や細かい設定は実装していません。徐々に実装していく予定です。また、最低限の動作しか確認していませんので、動作は保証できません。さらにハードウェアシリアルの利用が必要です。ソフトウェアシリアルは現在は対応していませんが、今後対応してく予定です。

まとめ

 使ってみて不具合や対応してほしい、提案などあればコメント欄やGitHubのIssueにあげていただけるとありがたいです。未実装機能の中で希望があれば、優先して実装していきたいと思います。また、プルリクエストや提案があれば対応いたします。現在のところ@lutecla16vさんがLeonardoでの動作報告とESP32へのインポートを検討されています。

WioLTEをVSCode(PlatformIO)で開発する

この記事について

  • 追記 20180317 Wio LTEArduino coreライブラリを現時点最新の1.1.3に変更しました。

この記事ではSeeed社から発売されているWio LTEをVisualStudio CodeにインストールしたPlatformIO環境で開発する方法を紹介します。PlatformIOの対応プラットフォームにWioLTEがなかったためビルドスクリプトと設定情報を用意しました。

Wio LTEについて

Wio LTEはSeeed社から発売されているLTE SIMが刺さるCortex-M4(STM32F407)の開発ボードです。I2CやUART, デジタル、アナログI/OをGrove端子で提供しています。また、Arduino互換ボードを謳い開発にはArduino IDEを使います。他のArduinoとほぼ違いなく開発ができます。その他にはEspruinoというJavaScriptで開発を行うフレームワークを使う方法もあります。GitHubリポジトリにはmbed対応のリポジトリもあるようです。   Arduinoのライブラリなどをそのまま流用したいという理由から、Arduino IDEでの開発を選択するとArduino IDEを使うことになります。しかし、Arduino IDEはビルドとバイナリの転送ができるメモ帳と言っても過言ではない使い勝手の悪さです。「()」やダブルコーテーションの入力補助やコードのサジェッションを受けることができません。また、Gitなどの構成管理ツールと言った他の開発ツールとのインテグレーションが気軽にできません。高機能なエディタやターミナルのツールを駆使することになります。  そこで今回はPlatformIOを使いVSCodeでWIo LTEの開発を行っていきます。

PlatformIO

 PlatformIOはPythonで書かれた組み込み向けの開発ツールです。ArduinoだけでなくMbedやネイティブな環境での開発をサポートしています。開発ボード(CPU)-フレームワークの関係を管理し、そのためのツールとインストールとビルド転送をラップしているイメージです。

platformio.org

 PlatformIOは前述のようにPythonで書かれており、コマンドラインで実行します。しかし、VSCodeAtomエディタにPlatformIO IDEというプラグンが用意されており各エディタにインテグレーションすることができます。  PlatformIOは前述のように開発ボードの種類(CPU)-フレームワークの関係を管理しています。逆に言うと開発ボードに対応した定義がなければPlatformIO上で開発することができません。現在対応しているプラットフォームはPlatformIOのコマンドから調べることができます。現在のところWio LTEはPlatformIOに登録されていません。またSTM32F4とArduinoフレームワークで開発を行うプラットフォームを試してみましたが、ビルドがうまくいきませんでした。そこで、Wio LTEのプラットフォーム定義を作成する必要があります。

プラットフォームを定義する

PlatformIOで独自にプラットフォームの定義を行う場合、大きく、以下の3つが必要になります。

  1. platoform.json
  2. board.json
  3. ビルドスクリプト

詳細は以下のドキュメントを参考にしていください。  Custom Development Platform — PlatformIO 3.5.3a3 documentation

platform.jsonはプラットフォームの基本的な定義を記載します。一番重要なのはどの開発ツールを利用するのか、どこからダウンロードするかです。ここでいう開発ツールは開発のためのフレームワークコンパイルや実行ファイルを作るためのビルドツール、開発ボードへ実行ファイルを転送するアップロードです。

それぞれ以下のように設定しています。

 名前 説明
oolchain-gccarmnoneeabi ARMのコンパイラリンカなどのビルドツール
framework-Seeed_STM32F4 Wio LTE用のArduinoフレームワーク Seeed社のリポジトリに公開されているものにPlatformIO用のJSONファイルを追加
tool-stm32duino DFUのアップロードツール

board.jsonは対象のボードのコンパイルオプションやCPUの設定を記載します。ビルドスクリプトから参照してい利用します。ビルドスクリプトはSConsというPython製のビルドツールを使って記載します。Makefileの置き換えとして開発されたようです。ビルドスクリプトにはコンパイル、リンクのためパスのしていや各種フラグの設定を行います。また、アップロード方法についても定義しておきます。

今回作成したプラットフォームの定義は以下のリポジトリに置いています。

github.com

利用方法

  1. PlatformIOのインストール

以下のページを参考にVSCodeへPlatformIO IDE Pluginをインストールしてください。

PlatformIO IDE for VSCode — PlatformIO 3.5.3a3 documentation

  1. Wio LTEプラットフォームの設定

以下のようにプラットホーム定義をcloneします。clone先のリポジトリはPlatformIOのホームディレクトリ配下にあるplatformsディレクトリです。macOSの場合はホームディレクトリ配下の.platformsの中になります。Windowsの場合はおそらくユーザディレクトリ配下の.platforms以下となります。

> cd $USER_HOME/.platformio/platforms
> git clone https://github.com/masato-ka/platform-wiolte.git

また、WioLTEforArduinoライブラリをlibフォルダにクローンします。

>cd $USER_HOME/.platformio/lib
>git clone https://github.com/SeeedJP/WioLTEforArduino

上記実行後VSCodeを再起動します。

  1. プロジェクトの作成

VSCodeを立ち上げるとPlatformIOのホーム画面が立ち上がるようになります。ホーム画面にある 「New Project」を選択後、Wio LTEArduinoを選択しFinishを押下します。

f:id:masato-ka:20180122221455p:plain

その後、プロジェクトが作成され、後はArduino IDEと同様にコードを書いてビルド後アップロードします。VSCodeの下側にビルドボタンとアップロードボタン、シリアルモニタのボタンが表示されるはずです。またアップロード時にはWio LTEをDFUモードにしてください。コードを書くとこのようにサジェッションが出てきます。

f:id:masato-ka:20180122221901p:plain

まとめ

簡単に書いてますが、ビルドスクリプトを作成するのに手間取りました。もしよければ使っていただけると嬉しいです。OSX環境しか試してませんが、他の環境でももれなく動くはずです。不具合があれば報告していただければ可能な範囲で対応します。公式で対応されるのが待たれます。PlatformIO本体への登録ってどうやるんだろうな。。。 また今回の内容をLTにまとめて2/6のSORACOM UG LT大会で話せればと思っています。(抽選に当たってと仕事が片付けば。。。)

AMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(後編)

この記事について

この記事はAMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(前編)の続きです。AMG8833のデータを使い、WEBブラウザ上でヒートマップ表示にチャレンジしています。

masato-ka.hatenablog.com

前回まではESP8266を使い、AMG8833からデータを取得するところまで紹介しました。今回はESP8266から受け取ったセンサデータをサーバ側の可視化について説明します。

サーバ側の構成について

 サーバ側の基本的な動作はESP8266からセンサデータをHTTPで受け取ります。受け取ったデータはそのままキューに送りつけます。また可視化用のブラウザからのリクエスト受け付けるとデータの可視化の非同期処理を起動します。この非同期処理は先ほどのセンサデータを送っているキューからデータを取り出し、ブラウザにSSE(Server sent event)で非同期にデータを送り続けます。前編で説明した通り、センサデータを逐次送りつけ、ブラウザの表示をリアルタイム表示させたいと思い、バックエンド(センサ受信処理)とフロントエンド(ブラウザの表示)を非同期通信で実装しました。サーバ側の受信スレッドを枯渇させないように、別スレッドとして、SSEの送信処理を実装しています。また、センサデータの受信スレッドとフロントエンドの送信処理をメッセージキューで同期させています。メッセージキューは別にサービスを建てたくなかったので、組みこみActiveMQを利用しています。  ヒートマップなどの表示処理はブラウザ側で実行します。サーバ側のバックエンドの処理はSpringBootで実装します。ブラウザ表示のフロントエンドはHTMLとAngularJSで実装しています。ブラウザは初回表示時にフロントエンドを取得し、その後、サーバからのSSEを受信する都度画面の描画を変更します。

f:id:masato-ka:20180106112641p:plain

今回実装したサーバ側コードは以下のリポジトリにアップしています。

github.com

バックエンド処理の実装

バックエンドはSpringBootで実装しています。

センサデータの受信

センサデータは[30.0,30.0,30.0,・・・・・・]JSON形式でPOSTリクエストで送信されます。これを以下のRestControllerで取得します。

@RestController
@RequestMapping("/api/v1/thermography")
public class Controller {

    private final JmsTemplate jmsTemplate;

    private final Queue queue;

    public Controller(JmsTemplate jmsTemplate, Queue queue) {
        this.jmsTemplate = jmsTemplate;
        this.queue = queue;
    }

    @PostMapping
    public void postThermoGraphyTelemetory(@RequestBody List<Double> payload) {
        jmsTemplate.convertAndSend(queue, payload); //(1)
    }

}
  • (1) 受け取ったデータはinmemory.queueと言う名前のキューに入れます。バックエンド側ではDoubleオブジェクトのリストとして表現されています。

ServerSentEvent

ServerSentEventの受信処理はRestContorollerに以下のエンドポイントを追加します。非同期処理のタイムアウト値は1分に設定します。(SSEが1分送信されなければタイムアウトします。)

    private final AsyncEmitData asyncEmitData;
    private final JmsTemplate jmsTemplate;

    private final Queue queue;

    public Controller(AsyncEmitData asyncEmitData, JmsTemplate jmsTemplate, Queue queue) {
        this.asyncEmitData = asyncEmitData;
        this.jmsTemplate = jmsTemplate;
        this.queue = queue;
    }

   @GetMapping("/sse")
    public SseEmitter getThermoGraphySSE() throws IOException, InterruptedException {
        SseEmitter emitter = new SseEmitter(60000L);
        log.info("do Async");
        asyncEmitData.streming(emitter);
        log.info("close Async");
        return emitter;
    }

実際に非同期処理を実施しているのはAsyncEmitData classで実装しています。

@Component
public class AsyncEmitData {

    private boolean isLock = true;

    private final JmsTemplate jmsTemplate;

    public AsyncEmitData(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }


    @Async
    public void streming(SseEmitter emitter) throws IOException, InterruptedException {

        jmsTemplate.setReceiveTimeout(0);// (1)
        while (true) {
            List<Double> result = (List<Double>) jmsTemplate.receiveAndConvert("inmemory.queue"); //(2)
            emitter.send(result); //(3)
            log.info("send event1");
            if (result == null) {
                break;
            }
        }
        log.info("complete sse session.");
        emitter.complete();
    }

}

別途@EnableAsyncアノテーションを指定します。

  • (1) JmsTemplateの受信処理はデフォルトで非同期処理のため、タイムアウトを0に指定し同期的に受信待ちするようにします。
  • (2) JmsTemplate.receiveAndConvertでinmemoru.queueのデータを待ち受けします。
  • (3) キューから取得したデータをそのままSSEとして送信します。またデータがnullであればサーバから切断します。

Embedded ActiveMQの利用

組みこみActiveMQの設定です。

@Configuration
@EnableJms
public class ActiveMqConfig {

    @Bean
    public Queue queue() {
        return new ActiveMQQueue("inmemory.queue");
    }

}
  • application.yml
spring:
  activemq:
    in-memory: true
    pool:
      enabled: false
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

ブラウザ側処理

ヒートマップの表示処理

ヒートマップの表示はTableを温度の値ごとにAngularJSで表示させます。

  • index.html
<!DOCTYPE html>
<html lang="en" ng-app="App">
<head>
    <meta charset="UTF-8">
    <title>Viaualization AMG88xx</title>
    <style>
        table {
            border: none;
        }
        td {
            width: 50px;
            height: 50px;
            border-style: none;
            border: none;
        }

        tr {
            border: none;
        }
    </style>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
    <script type=text/javascript src="./js/controller.js"></script>
</head>
<body ng-controller="ApplicationController">
<center>
<h1>Visualization.</h1>
<table bgcolor="white" >
    <tr ng-repeat="line in image">
        <td ng-repeat="pixel in line" style="background-color: rgb({{pixel[0]}},{{pixel[1]}},{{pixel[2]}})">1</td>
    </tr>
</table>
</center>
</body>
</html>
  • controller.js
ar app = angular.module('App',[]);
var es = new EventSource('/api/v1/thermography/sse');

app.controller('ApplicationController', ['$scope',function($scope){

    es.addEventListener('message', function (event) {

        var acceptData = JSON.parse(event.data);

        var temp = []

        for (var i = 0; i <= 56; i += 8) {
            temp.push(acceptData.slice(i, i + 8));
        }
        $scope.image = [];
        $scope.image = temp.map(function (line) {
            return line.map(function (temp) {
                return translateTempToRGB(temp);
            })
        });
        $scope.$apply();
    });

    data = function () {
        $scope.image = [];
        var temp = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]];

        $scope.image = temp.map(function (line) {
            return line.map(function (temp) {
                return translateTempToRGB(temp);
            })
        });
    }
    data();
}]);

SSEでデータを取得するたびにAngularJSで表示を切り替えています。温度の値をRGBに変換する処理translateTempTpRGBファンクションは以下のような実装です。

var translateTempToRGB = function(temp){
    var scale = 1 / 50.0;
    temp * scale;
    return colorRGBBar(temp * scale);
}

var sigmoid = function(x, gain, offset){
    var value = (x+offset)*gain;
    return ((Math.tanh(value*0.5)+1)*0.5);
}

var colorRGBBar = function(x){
    var gain = 10
    var offset_x = 0.2
    x = (x * 2) - 1
    red = sigmoid(x, gain, -1*offset_x)
    blue = 1-sigmoid(x, gain, offset_x)
    green = sigmoid(x, gain, 0.6) + (1-sigmoid(x,gain,-1*0.6))
    green = green - 1.0
    return [Math.floor(red*255),Math.floor(green*255),Math.floor(blue*255)];
}

動作

実際に動作させた動画は以下の通りです。わかりやすく氷をセンサの前にくぐらせています。また、チョキやパーなどもわかる程度にデータが取れています。


blog20180106

まとめ

今回はAMG8833のデータをESP8266で取得し、WEBブラウザで可視化するところまでやりました.AMG8833自体は指定温度での割り込み機能などがあり、センサとしては高度な処理ができそうです。また、補間処理をすることで8x8から画素を広げて表示することにもチャレンジしたいです。 サーバ側はフルスクラッチで実装していますが、クラウドサービスなどを使ってデータの取得部分を置き換えるようにしたいです。

AMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(前編)

この記事について

この記事ではサーモパイルセンサ(温度センサアレイ)を利用し、サーモグラフィを作成した内容を記載しています。今回は赤外線温度センサアレイとしてAMG8833を利用しています。スイッチサイエンスさんから購入することができます。なお、この記事はデバイス側の実装までを紹介した前編です。サーバ側の実装は後編で紹介しています。(2017/01/06追記)

masato-ka.hatenablog.com

AMG8833について

AMG8833は赤外線温度センサが8x8の2次元アレイになっているセンサです。Panasonicから発売されています。エアコンなどに搭載し、室内の人の位置などを検知することを目的に作られているようです。計測温度は0度〜80度です。電源とI2Cの端子のみでデータを取得することができます。ブレークアウト基板に実装されたものは1万円程度するものがほとんどです。そんな中スイッチサイエンスさんから4000円代でContaと呼ばれるスイッチサイエンス社規格の拡張ボード向け部品として発売されています。

www.switch-science.com

もちろん、端子を使えばブレットボードに直差しで動かすことが可能です。今回はこのモジュールから取得したデータをサーモグラフィ画像ぽくヒートマップを表示します。

ヒートマップ表示の検討

初期検討

GUIのプログラミングをするのが面倒だったので、WEBブラウザで簡易的なヒートマップを作成することにしました。そのため、AMG8833からI2Cで取得したデータを何らかの方法でWEBブラウザに送る必要があります。基本方針としてはマイコンで取得したデータをWEBサービスに送り、そこからWEBブラウザに表示するようにしました。デバイスからサーバ間はHTTPでデータを送信します。デバイスからのセンサ値をサーバ側で受診後、すぐにWEBブラウザを更新したいため、サーバとWEBブラウザの通信はリアルタイムな通信を利用したいと考え、今回はSSE(Server sent event)を採用しました。まとめると今回の構成は以下の図のようになっています。

f:id:masato-ka:20180105164439p:plain

バイスの実装

AMG8833からのデータ取得

データ取得にはESP8266(WROOM-02)を利用しています。I2Cで取得したデータを外部のサーバに送りたかったのとある程度のCPUパワーが欲し句、手持ちで使えそうなものを選択しました。今回はこれ以外にもデバック用にOLEDディスプレイをI2C経由で接続しています。上記で紹介したAMG8833はI2C用のプルアップ抵抗が付属していないのでI2CのSDAとSLCラインそれぞれにプルアップ抵抗(10kΩ)を入れています。OLEDディスプレイ側についてるのでなくても問題ないかもしれませんが、念のため入れておきます。

ライブラリの利用

AMG8833はAdafruitからライブラリが公開されています。今回はこちらのライブラリを利用しました。Arduino IDEを利用している場合はIDEのライブラリの管理からインストールすることができます。

github.com

AMG8833はI2Cのアドレスを入力ピンへの入力で0x68と0x69のどちらかに指定することができます。Adafruitのライブラリはデフォルトで0x69が指定されていますが、スイッチサイエンスのボードは0x68が設定されています。(基板上に実装されている端子をショートすることで切り替えできます。)そのため、ライブラリを利用する場合はアドレスを明示的に指定する必要があります。データを読み出し、OLEDにヒートマップを読み出すコードは以下のようになります。

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_AMG88xx.h>
#include "SSD1306.h"

const int pixel_array_size = 8*8;
const float thd = 29.0;
float pixels[pixel_array_size];

Adafruit_AMG88xx amg;
SSD1306  display(0x3c, 4, 5);

void setup() {
    Serial.begin(115200);
    bool status = amg.begin(0x68);//(1)
    Serial.println("-- Thermal Camera Test --");
    if(!status){
        Serial.println("AMG88xx is not available. Please connect the sensor.");
        while(1);
    }
    //OLED Initialize.
    display.init();
    display.flipScreenVertically();

}

//Draw rectangle to OLED.
void black_rect(int x, int y, int size){
    display.drawRect(x,y,size,size);
}

//Draw rectangle to OLED.
void circle(int x, int y, int radius){
    int r = radius/2;
    display.drawCircle(x+r, y+r, r);
}

//Draw X to OLED.
void cross(int x, int y, int size){
    
    for(int i=0; i < size; ++i){
        display.setPixel(x+i, y+i);
        display.setPixel(x+size-i, y+i);
    }
}

//Draw fill rectangle to OLED.
void white_rect(int x,int y, int size){
    display.setColor(WHITE);
    display.fillRect(x,y,size,size);
}

//Draw heatmap image.
void draw_display(float pixels[], float thd){
    int x=0;
    int y=0;
    int size = 7;
    for(int i =0; i < pixel_array_size; ++i){
        if(i%8==0){y+=size;x=0;}
        x = size * (i%8);
        if(pixels[i] > 24 && pixels[i] <= 29){
            cross(x,y,size);
        }else if(pixels[i] > 29 && pixels[i] <= 34){
            circle(x,y,size);
        }else if(pixels[i] > 34){
            white_rect(x,y,size);
        }else{
            black_rect(x,y,size);  
        }
    }
    Serial.println("");
}

void loop() {
    amg.readPixels(pixels);//(2)
    Serial.println(" ");
    delay(1000);
    display.clear();
    draw_display(pixels, thd);
    display.display();
}
  • (1) AMG8833のイニシャライズ時にI2Cのアドレスを指定します。
  • (2) readPixelsメソッドにサイズが64のfloat型配列を渡し、計測値を取得します。

実行すると以下のようにOLEDに8x8のデータが表示されます。温度に応じて表示される図形が変わります。

f:id:masato-ka:20180104162232j:plain

まとめ

最終的に実装したデバイス側のソースコードGitHubに公開しています。

github.com

64要素のfloat型配列をそのままJSONとしてサーバ側に送信し続けるコードになっています。次回はこのデータを受けて、ブラウザにヒートマップを表示する実装を紹介します。