AMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(後編)
この記事について
この記事はAMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(前編)の続きです。AMG8833のデータを使い、WEBブラウザ上でヒートマップ表示にチャレンジしています。
前回まではESP8266を使い、AMG8833からデータを取得するところまで紹介しました。今回はESP8266から受け取ったセンサデータをサーバ側の可視化について説明します。
サーバ側の構成について
サーバ側の基本的な動作はESP8266からセンサデータをHTTPで受け取ります。受け取ったデータはそのままキューに送りつけます。また可視化用のブラウザからのリクエスト受け付けるとデータの可視化の非同期処理を起動します。この非同期処理は先ほどのセンサデータを送っているキューからデータを取り出し、ブラウザにSSE(Server sent event)で非同期にデータを送り続けます。前編で説明した通り、センサデータを逐次送りつけ、ブラウザの表示をリアルタイム表示させたいと思い、バックエンド(センサ受信処理)とフロントエンド(ブラウザの表示)を非同期通信で実装しました。サーバ側の受信スレッドを枯渇させないように、別スレッドとして、SSEの送信処理を実装しています。また、センサデータの受信スレッドとフロントエンドの送信処理をメッセージキューで同期させています。メッセージキューは別にサービスを建てたくなかったので、組みこみActiveMQを利用しています。 ヒートマップなどの表示処理はブラウザ側で実行します。サーバ側のバックエンドの処理はSpringBootで実装します。ブラウザ表示のフロントエンドはHTMLとAngularJSで実装しています。ブラウザは初回表示時にフロントエンドを取得し、その後、サーバからのSSEを受信する都度画面の描画を変更します。
今回実装したサーバ側コードは以下のリポジトリにアップしています。
バックエンド処理の実装
バックエンドは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の設定です。
- 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
- pom.xml
<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)]; }
動作
実際に動作させた動画は以下の通りです。わかりやすく氷をセンサの前にくぐらせています。また、チョキやパーなどもわかる程度にデータが取れています。
まとめ
今回はAMG8833のデータをESP8266で取得し、WEBブラウザで可視化するところまでやりました.AMG8833自体は指定温度での割り込み機能などがあり、センサとしては高度な処理ができそうです。また、補間処理をすることで8x8から画素を広げて表示することにもチャレンジしたいです。 サーバ側はフルスクラッチで実装していますが、クラウドサービスなどを使ってデータの取得部分を置き換えるようにしたいです。
AMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(前編)
この記事について
この記事ではサーモパイルセンサ(温度センサアレイ)を利用し、サーモグラフィを作成した内容を記載しています。今回は赤外線温度センサアレイとしてAMG8833を利用しています。スイッチサイエンスさんから購入することができます。なお、この記事はデバイス側の実装までを紹介した前編です。サーバ側の実装は後編で紹介しています。(2017/01/06追記)
AMG8833について
AMG8833は赤外線温度センサが8x8の2次元アレイになっているセンサです。Panasonicから発売されています。エアコンなどに搭載し、室内の人の位置などを検知することを目的に作られているようです。計測温度は0度〜80度です。電源とI2Cの端子のみでデータを取得することができます。ブレークアウト基板に実装されたものは1万円程度するものがほとんどです。そんな中スイッチサイエンスさんから4000円代でContaと呼ばれるスイッチサイエンス社規格の拡張ボード向け部品として発売されています。
もちろん、端子を使えばブレットボードに直差しで動かすことが可能です。今回はこのモジュールから取得したデータをサーモグラフィ画像ぽくヒートマップを表示します。
ヒートマップ表示の検討
初期検討
GUIのプログラミングをするのが面倒だったので、WEBブラウザで簡易的なヒートマップを作成することにしました。そのため、AMG8833からI2Cで取得したデータを何らかの方法でWEBブラウザに送る必要があります。基本方針としてはマイコンで取得したデータをWEBサービスに送り、そこからWEBブラウザに表示するようにしました。デバイスからサーバ間はHTTPでデータを送信します。デバイスからのセンサ値をサーバ側で受診後、すぐにWEBブラウザを更新したいため、サーバとWEBブラウザの通信はリアルタイムな通信を利用したいと考え、今回はSSE(Server sent event)を採用しました。まとめると今回の構成は以下の図のようになっています。
デバイスの実装
AMG8833からのデータ取得
データ取得にはESP8266(WROOM-02)を利用しています。I2Cで取得したデータを外部のサーバに送りたかったのとある程度のCPUパワーが欲し句、手持ちで使えそうなものを選択しました。今回はこれ以外にもデバック用にOLEDディスプレイをI2C経由で接続しています。上記で紹介したAMG8833はI2C用のプルアップ抵抗が付属していないのでI2CのSDAとSLCラインそれぞれにプルアップ抵抗(10kΩ)を入れています。OLEDディスプレイ側についてるのでなくても問題ないかもしれませんが、念のため入れておきます。
ライブラリの利用
AMG8833はAdafruitからライブラリが公開されています。今回はこちらのライブラリを利用しました。Arduino IDEを利用している場合はIDEのライブラリの管理からインストールすることができます。
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のデータが表示されます。温度に応じて表示される図形が変わります。
まとめ
最終的に実装したデバイス側のソースコードをGitHubに公開しています。
64要素のfloat型配列をそのままJSONとしてサーバ側に送信し続けるコードになっています。次回はこのデータを受けて、ブラウザにヒートマップを表示する実装を紹介します。
Wio LTEとRN4020を使ってBLEゲートウェイ化からのSORACOM Harvestで可視化する。
この記事について
先日SORACOMさんとSeeedさん主催の「Wio LTE ユーザイベント」と言うイベントに参加しました。その際に、会場で販売されていたWio LTEを購入したので、利用のレポートとしてこの記事を紹介します。今回はWio LTEとRN4020を接続してBLEセンサビーコンの温度情報をSORACOMのデータ可視化サービスであるSORACOM Harvestで可視化してみました。てんこ盛りの内容のようですがとても手軽に実現できます。
Wio LTEについて
Wio LTEはArduino互換(Arduino IDEでソフトウェアが書ける)でSORACOMのSIM が刺さり単体でLTE通信ができます。また、Groveコネクタで外部のセンサや機器と接続できます。主な端子として Digital Analogの入出力端子、I2C UARTを搭載しています。Seeedさんが製造しており、日本ではSORACOMさんから購入することができます。単体の価格は9800円(税別)です。もちろん技適も通っており、LTE通信を使いセンサデータを送りたい場合には最も簡単なボードになると思います。
Wio LTEの一般的な使い方はGroveコネクタにセンサを接続して、LTE経由でデータを送受信する使い方になります。今回はWio LTEに直接センサをつながずにBLEのゲートウェイとして利用します。
全体の構成
全体の構成は以下の写真のようになっています。Wio LTEのUART端子に無理やりRN4020を接続しています。また、白いものが[BLEのセンサビーコン、[Wx2Beacon]e(http://masato-ka.hatenablog.com/entry/2017/09/12/223129)です。RN4020とWx2Beaconの詳細についてはそれぞれ過去記事参照ください。 Wio LTEはシリアル経由でRN4020を操作します。RN4020はWio LTEからの指示を受けて、一定時間ごとにセントラルとしてWx2Beaconに接続し、最新のセンサ値を取得します。その後、Wio LTE側でセンサ値をデコードし、SORACOM Harvestに送っています。
- SORACOM Harvestで可視化した温度データ。
ソースコードは以下のリポジトリに上がっています。 github.com
以下、ソースコードをかいつまんで紹介します。
RN4020との接続
過去記事で紹介したシリアルからRN4020をセントラルで使う方法をWio LTEのGrove側のシリアルから実行しただけです。Wio LTEとRN4020の結線ではRXとTXはクロスに接続してください。
GroveのシリアルはSerial
となります。SerialUSB
はPCと接続しているUSBケーブルに割り当てられており、コンソール出力として使っています。
double get_temprature(){ SerialUSB.println("### Connect to periferal"); Serial.println("E,1,E6E4B1DA83BC"); delay(2000); trashSerialBuffer(); SerialUSB.println("#### Read data"); Serial.println("CHR,0019"); delay(500); copySerialBuffer(buffer); String raw_data = String(buffer); #ifdef PRINT_DEBUG SerialUSB.print("Dump raw_data: "); SerialUSB.println(raw_data.substring(2,40)); #endif String data = raw_data.substring(2,40); double temp = templature(data.substring(2,6)); #ifdef PRINT_DEBUG SerialUSB.println(temp); #endif SerialUSB.println("#### Disconnect"); Serial.println("K"); delay(500); trashSerialBuffer(); return temp; }
取得したセンサデータの変換
RN4020から取得されたデータは以下のような形式になっています。先頭のR,
と終わりの.
はバリです。それ以外は16進数表記の文字列です。つまり2文字で1byteの扱いとなります。またリトルエンディアン表記となります。
R,038C081D14070002007127420D7D1A5907650B.
Wx2Beaconの温度データは2バイト目と3バイト目に格納されています。つまりこの場合"8C08"が温度データに該当します。さらにリトルエンディアンですので"0x088C"となります。これを10進数に変換して0.01をかけると温度の値(セルシウス度)となります。以下が、変換の一連のコードです。上記のコードのようにtemplatureに文字列を渡すと変換できます。
int hex_format_char_to_decimal(const char* hex_char){ #ifdef PRINT_DEBUG SerialUSB.println(hex_char); SerialUSB.println(sizeof(hex_char)); #endif if(sizeof(hex_char) != sizeof(char)*4){return -1;} int result = 0; int base = 0; for(int i=0; i < 2; ++i){ if(65 <= hex_char[i] && hex_char[i] <=70){ result += hex_char[i]-55; } if(48 <= hex_char[i] && hex_char[i] <= 57){ result += hex_char[i]-48; } base = (16*(1-i)==0)? 1 : 16*(1-i); result *= base; } #ifdef PRINT_DEBUG SerialUSB.println(result); #endif return result; } int hex_format_str_to_decimal(String str){ #ifdef PRINT_DEBUG SerialUSB.println("raw:" + str); SerialUSB.println(str.length()); #endif if(str.length() != 4){ SerialUSB.println("break"); return -1; } int lsb = hex_format_char_to_decimal(str.substring(0,2).c_str()); int msb = hex_format_char_to_decimal(str.substring(2,4).c_str()); if(msb == -1 || lsb == -1){return -1;} return (msb<<8) + lsb; } double templature(String raw){ return hex_format_str_to_decimal(raw.c_str()) * 0.01; }
SORACOM Harvestへの接続
SORACOM Harvestへの接続はWioLTEのサンプルコード通りです。そちらを参考にしていただいた方が良いと思います。 UDPのソケットを開き8541に送りつけます。ユーザやSIMの設定はSORACOMの管理画面から設定しているため不要のようです。これだけで マネージドな可視化サービスに遅れるのは非常にすぐれものです。
void send_to_harvest(char *data){ int connectId; SerialUSB.println("### Open."); connectId = Wio.SocketOpen("harvest.soracom.io", 8514, WIOLTE_UDP); if (connectId < 0) { SerialUSB.println("### ERROR! ###"); return; } SerialUSB.println("### Send."); SerialUSB.print("Send:"); SerialUSB.print(data); SerialUSB.println(""); if (!Wio.SocketSend(connectId, data)) { SerialUSB.println("### ERROR! ###"); return; } SerialUSB.println("### Receive."); int length; do { length = Wio.SocketReceive(connectId, data, sizeof (data)); if (length < 0) { SerialUSB.println("### ERROR! ###"); return; } } while (length == 0); SerialUSB.print("Receive:"); SerialUSB.print(data); SerialUSB.println(""); SerialUSB.println("### Close."); if (!Wio.SocketClose(connectId)) { SerialUSB.println("### ERROR! ###"); return; } }
動作の全体像。
これまで紹介したメソッドはloopメソッドで以下のように使われています。
void loop() { char data[100]; double temp = get_temprature(); sprintf(data,"{\"temp\":%.1f}", temp); SerialUSB.println(data); send_to_harvest(data); delay(600000L); }
まとめ
Wio LTEは簡単にネットワークにつなげ、SORACOMのサービスを使うライブラリも充実しているので、LTE環境で何かしようとする際にはとても強力なツールになりそうです。また、RN4020とつなぐことでIoTゲートウェイっぽい使い方ができるので、利用シーンも広がりそうです。今年の冬休みの工作にいかがでしょうか。またWx2Beaconの温度データだけ使いましたが他にもセンサがついてるので多くの情報の可視化にチャレンジしたいです。
RN4020をBLEセントラルとして利用する。
この記事について
この記事ではRN4020をBLEセントラルデバイスとして利用し、BLEのペリフェラルデバイスとやりとりするための方法を紹介します。これまではRN4020をペリフェラルデバイスとして利用してきましたが、RN4020自信はセントラルとしても動作することが可能です。
RN4020のセントラルとしての利用方法
セントラルとして活用するには以下の方法で行います。
1. セントラル動作モードの設定
RN4020をセントラルとして活用するためにはSR
コマンドで動作モードを変更するだけで利用できます。
SR,80000000 R,1
以上でRN4020がセントラルデバイスとして動作始めます。
2. デバイスの検索
続いて、ペリフェラルデバイスの検索を行い、接続したいデバイスを発見します。今回は接続対象のデバイスとしてWx2Beaconを利用します。Wx2Beaconの詳細については以下の記事を確認してください。
ペリフェラルデバイスの検索を行うためにはF
コマンドを実行します。以下のように結果が表示されます。
F 62CEEEEE4D99,1,,,-4E 579E9EEEE4DB,0,,11111101C75C5666666A678911111111,-52 E6E4B1DA83BC,1,,,-14 FEA458B80011,1,,,-43
データは「,」区切りのデータとして出力れます。先頭がデバイスのUUIDとなります。また、2つ目はアドレスの種類を表します。接続のために必要になるためこの2つを控えておきます。一番最後がRSSI(電波強度)となります。目的のデバイスをRN4020に最も近づけておき、一番値が大きい(ここでは-14)デバイスを探すとわかりやすいです。今回は「E6E4B1DA83BC,1」を値として記録しておきます。
目的のデバイスが見つかったらX
コマンドを使いスキャンを停止します。
3. デバイスへの接続
目的のデバイスが見つかったらE
コマンドを使い接続します。デバイスの検索で記録した「E6E4B1DA83BC,1」を使い接続します。
E,1,E6E4B1DA83BC AOK Connected ConnParam:0028,0004,0190
デバイス検索時とは逆に入力します。接続を切断する場合はK
を実行します。
K Connection End
4. キャラクタリスティックの操作
サービス又はキャラクタリスティックの検索はペリフェラルとして利用するのと同様LS
又はLC
を利用します。ペリフェラルとして利用するときは自身に設定されたサービスとキャラクタリスティックを表示していましたが、セントラルとして動作する場合は接続先デバイスのサービスとキャラクタリスティックを表示します。
今回の場合は以下のように表示されます。
LC 180A 2A29,000E,02 2A24,0010,02 2A25,0012,02 2A27,0014,02 2A26,0016,02 0C4C3000770046F4AA96D5E974E32A54 0C4C3001770046F4AA96D5E974E32A54,0019,02 0C4C3001770046F4AA96D5E974E32A54,001A,10 0C4C3002770046F4AA96D5E974E32A54,001C,02 0C4C3003770046F4AA96D5E974E32A54,001E,0A 0C4C3004770046F4AA96D5E974E32A54,0020,02 0C4C3005770046F4AA96D5E974E32A54,0022,02 0C4C3006770046F4AA96D5E974E32A54,0024,02 0C4C3006770046F4AA96D5E974E32A54,0025,10 END
WxBeacon2の場合は 0C4C3001770046F4AA96D5E974E32A54,0019,02
センサデータを読み出すことができます。先頭からUUID,HandleID,パーミッション(0x02はRead)となっています。このキャラクタリスティックに対してCHR
コマンドでデータを読み出します。UUIDではなく、HandleIDを使うことに注意してください。
CHR, 0019 R,038C081D14070002007127420D7D1A5907650B.
対象のデータが取れました。これをデコードすることでWxBeacon2のセンサ情報を取得できます。Arduinoなどのマイコンを使うことで簡単にBLEセントラルデバイスを構築できそうです。
Web Bluetooth API とRN4020のワークショップを開催しました。
この記事について
この記事では2017/10/20に開催したWeb Bluetooth APIとRN4020のワークショップの模様を記録しています。このワークショップでは以下過去記事の内容をベースに実際に皆さんにWeb Bluetooth APIを使ったアプリケーションとRN4020を使ったBLEデバイスの開発を体験していただきました。
募集時のconpassページは以下です。
開催の経緯と目的
このハンズオンはBLE周りのライブラリ情報を集める中で、Web Bluetooth APIと言うウェブブラウザからBLEを制御できるAPIを知ったことがきっかけです。Web Bluetooth APIで簡単にアプリが作れるなら、それに対向してデバイス側も 簡単に作れるものがないかなと探し当てたのがRN4020でした。 この2つで何かやってみたいなと思ったのと、前々からイベントを企画してみたいと考えていたので今回のワークショップを企画しました。 このワークショップを通して、BLE開発の敷居がぐっと下がってきたことを参加者の方に体験いただければよかったと思います。また、自分自身の知識の整理もやっていく予定でした。
開催模様
ワークショップ会場について
今回は友人の@atottoさんに場所を手配いただき、GROOVE X様のセミナールームをお借りすることができました。GROOVE X様はあのPepperの生みの親である林要さんが創業された会社でロボット界隈注目のスタートアップ企業です。社屋や会議室もとてもお洒落な場所でした。写真は入り口でこの裏手にセミナー会場が隠れています。お洒落。
ワークショップ内容につてい
今回のワークショップではBLEの基本やWeb Bluetooth API, RN4020の基礎知識を知ってもらう知識説明を30分実施した後、RN4020を利用したデバイス開発とWeb Bluetooth APIを利用したサンプルアプリケーションの開発を行いました。資料はGitHubのWikiページで作成し、以下の通りとなっています。
ハンズオン資料へのリンク
Home · masato-ka/bel-key-security-app Wiki · GitHub
全体でハンズオン実施者は6名でしたがそのうち最後まで完了した方が3名、2章のデバイス開発まで完了した方が3名でした。完了しなかった方の大半はWindows PCを利用されていたため、Web Bluetooth APIの機能を確認する手間がかかっていたのが原因のようです。演習時間はおおよそ90分程度でした。
実際に回路を組み立てている様子です。資料の実体配線図に沿ってブレットボード上で組み立てを行います。
みなさん、デバイスが動くところまで到達したため、BLE開発の敷居が下がったことを理解してもらう点では概ね目的を達成できたかと思います。
反省点
実際に開催してみてよかった点や悪かった面です。
良かった点
主だってよかった点は以下のところです。
今回は資料をGitHubのWiki ページで作成しました。間違いが発覚してもその場でコミットして共有するということができました。演習中誰かが見つけた間違いを共有できますし、ハンズオン後も共有できるので非常に便利です。またハンズオンの部品は自腹購入していたのですが、みなさん気に入っていただけた様子で引き取っていただくことができました。この辺は参加者の方に興味を持ってもらうことに成功した証ではないかなと思っています。 今回演習時間は90分でしたが達成率は50%でした。また未達成の方の離脱理由を考えると演習時間としてはちょうど良いか少し短めだと思います。平日夜開催という制約を考えるとちょうどいい時間ではないかと思いました。
悪かった点
逆に悪かった点
演習中に資料の記載漏れに気づく点が幾つかありました。良い点であげた通りその場で修正できたのでしが、そこで少し時間を取られていたのでその点はチェックの甘を反省しています。また、回路の組み立てなど部品の詳細な説明がなかった点が気になります。今回の参加者はたまたま回路について知識のある方が多かったため問題ありませんでしたが、そうでない方には少し難しい内容だったかと思います。Web Bluetooth APIの方ではそもそもコピペで動くようにしていたので問題ないと思いますが、Angular.jsやJavaScriptの説明は必要だったかもしれません。また手持ちの環境でWeb Bluetooth APIの環境をお持ちでない方が数名いらっしゃいました。まだまだ対応環境が限られるためこの辺りも課題があると思います。
今後に向けて
今回は参加者のバックグラウンドに助けられた面もありますが、もう少し内容を整理しないと継続は難しいかなという印象です。Web Bluetooth APIの対応環境を持ってな方でも試せるような事前の環境作りをしたり、参加者のスキル要件をもっと明確化するなど対応が必要かもしれません。 次回開催は未定ですが、別の内容をするにせよまた何かしら実施したいと思っています。その際は今回の反省を踏まえて開催したいです。
RN4020でI2Cの値を読み取る
この記事について
この記事ではRN4020のI2Cを利用する方法について紹介します。RN4020にはI2Cポートが付いています。EEPROMなどを接続しデータを一時的にためておくために利用できます。もちろん他のマイコンと同じようにI2Cポートをもったセンサを接続することができます。そこで、RN4020のI2Cポートに温湿度センサを取り付けて動作させてみました。
RN4020のI2C仕様について
RN4020は21Pinと22PinがI2Cのポートとなっています。データシート上では21PinがSDA, 22PinがSCAになっていますが、実際には22PinがSDA, 21PinがSCAと逆になっています。(2017/10/16現在)秋月電子のボードも他のブレークアウトボードもデータシートにならって軒並み逆に記載されています。そのため基板の印字と回路図を比較する必要があります。おそらく逆につければ大丈夫です。I2Cバスのプルアップ抵抗はRN4020についていないません。そのため、センサモジュールについていなければ別途つける必要があります。ロジックレベルは3.3Vです。
また、RN4020のI2Cはファームウェアのバージョンが1.20以降で利用できます。現在販売されているものは1.23が多いようです。(最新はバージョン1.3)念のため、シリアルからV
コマンドを使ってバージョンを確認しておきましょう。
RN4020はI2Cのマスタとして動作します。スレーブ動作は設定できません。また、EEPROM の読み書きを実行するコマンドがありますが、それ以外は指定データをシリアルバスにそのまま流す低レベルなAPIしかありません。
I2Cのコマンド
RN4020でI2Cを利用するためには以下の5つのコマンドを覚えれば大丈夫です。以下の5つのコマンドの組み合わせでI2Cのスレーブにアクセスします。
コマンド | 用途 |
---|---|
]A | I2Cポートの初期化 |
]C | I2Cのイベント制御 |
]Z | I2Cポートの開放 |
]W | データの書き込み |
]R | データの取得 |
I2Cコマンド利用方法
今回はHDC1000と呼ばれるI2Cインタフェースの温湿度センサから温度の値を読み取る方法を例にとり、コマンドの使い方を説明します。 このセンサは前まで秋月電子通商で手に入りましたが最近は廃盤になったようです。センサのアドレスは7bitアドレスで1000000を持っています。上記のコマンドを使ってHDC1000から温度データを取得するには以下のように実行します。
]A,1,2 //(1) ]C,0 //(2) ]W,80020000 // (3) ]W,8000 //(4) ]C,1 //(5) ]W,81//(6) ]R,02 //(7) 6541 //16進数表記
- (1) I2Cの初期化
最初の引数1はI2Cのクロックスピードです。100kHzを指定します。4を指定すると400kHzになります。また2番目の引数はGPIOを1,2,3,4,7で指定します。指定したGPIOは出力モードとなり、I2Cのスタート時にHighになります。スレーブの電源として利用することで、普段は電源を切っておくことができます。今回はGPIOを使わずに実験のため3.3Vの電源に直接接続しています。
- (2) I2Cのスタート
]C
の引数に0コマンドをつけ、I2Cのスタートビットをバスに流します。この時、I2Cの初期化で指定したGPIOがHighになります。
- (3) HDC1000の初期化
HDC1000はデータの取得方法など個別に細かく設定することができます。起動時に0x02のレジスタに値を書き込みます。今回は0x0000を書き込みます。詳細は以下のデータシートを参考にしてください。
]Wコマンドは指定されたバイト列をMSB側からバスに出力するコマンドです。データにバスを流すためのコマンドですので必ずしもスレーブへのWrite要求を出すコマンドではありません。そのため、I2Cの先頭バイトには7bitのスレーズ指定のアドレスと1byteのRead(1)/Wrie(0)を指定する必要があります。今回、HDC1000は2進数7bit表記で1000000
がアドレスになります。最後にWriteを表す0をつけることで書き込みを表します。その後、書き込み先レジスタアドレス0x02と書き込みデータ0x0000を付与します。
実際にこの時の出力をロジックアナライザで確認すると以下の図のようになっています。
もしデバイスアドレスが8bit表記であれば左へ1bitシフトしたものにRead/Writeのbitを付け足します。(MSB側は必ず0になっているはず。。。)
- (4) 温度レジスタ0x00へアクセス
引き続き]W
コマンドでレジスタ0x00へアクセスします。HDC1000ではこのジレスタにアクセスすることで、センサないのA/Dが温度の値を変換することになっています。Write要求ですが特にデータは書き込まず問題ありません。同じようにいかに信号の状態を出力しておきます。
- (5) I2Cリセット
温度を読み取るまでのRN4020側の書き込みは完了したため、一度I2Cのバスを解放します。
(4)から(5)までのロジックアナライザ上での動作になります。
- (6) リード要求
RN4020からリード要求を出します。この場合もバスに信号を流すため]W
コマンドを利用します。0x81はアドレス1000000
のLSB側にRead要求の1
を足したものになります。
I2CバスにREAD要求のコマンドが出力されます。
- (7) データの読み取り
I2Cのデータの読み取りはリードコマンド]R
を使います。引数は受信するバイト数でここでは2バイトを表します。HDC1000の場合結果は4桁の16進数で帰ってきます。これを指定された式で計算して温度値を読み取ります。
(5)から(7)までのロジックアナライザ上での動きは以下の通りです。前述の説明と照らし合わせてみてください。
以上でRN4020のI2Cの値の読み取りになります。]Wと]Rの使い方に気をつければ他のデバイスやアクセス方法でも問題なく利用できると思います。ロジックアナライザの出力とコマンドを照らし合わせると出力がどうなるか理解できるかと思います。
I2Cスクリプトサンプル
I2Cはもちろんスクリプトモードからも利用できます。上記の例を参考に温度データを一定間隔でNotifyするスクリプトです。キャラクタリスティックには2byteでRead Notifyが指定されています。スクリプトとプライベートサービスの詳細は過去記事を御覧ください。
- 温度データを一定間隔で送るスクリプト
@PW_ON A @CONN ]A,1,2 ]C,0 ]W,80020000 ]W,8000 ]C,1 SM,1,00002710 @TMR1 ]W,81 $VAR1=]R,02 SHW,000B,$VAR1 SM,2,00002710 @TMR2 ]W,8000 ]C,1 SM,1,00002710 @DISCON ]Z A
上記例では温度の変換命令''']W,8000```と読み取り命令を別のタイマーイベントとし、相互に繰り返すようになっています。(お互いのタイマーイベントを指定している。)これはHDC1000の温度変換の結果がくるのを少し待たないといけないためです。(ハードウェア割り込みもありますが今回は不使用です。)
まとめ
今回はRN4020でI2Cを利用してみました。I2Cもスタンドアローンで利用できるので、I2Cのセンサをつないでお手軽に利用することができます。簡単なセンサーデバイスはすぐに作れそうです。注意点としてはデータシート状のI2CのピンSDAとSCLが逆になっている点、]W,]Rのそれぞれの使い方です。ここさえ押さえればすぐに利用できます。データシートは何年もほったらかしのようですが、いつか修正されるのでしょうか。。。
Web Bluetooth APIと自作BLEデバイスで戸締り確認をしてみる。
この記事について
この記事ではWeb Bluetooth API戸締り確認アプリを紹介します。デバイス側はRN4020を使って実装しました。Web Bluetooth APIとRN4020を使ってBLEのアプリを作ってみたかったので、製作しました。実用性はありませんが、それぞれの使い方を覚えるのには大変良さそうな題材です。
戸締り確認アプリ
戸締り確認アプリは大まかに以下の仕様になっています。
- BLE経由で鍵の開閉を確認することができる。
- 鍵の状態が変わったらBLE経由で通知が来る。
- 鍵が開場した場合にブザーが鳴る。
デバイスの紹介
今回RN4020で作るBLEデバイスにはホール素子とブザーが付いています。ホール素子は磁石が近づくと信号を出力するセンサです。自転車の速度計などに利用されています。また、ブザーについてはRN4020のデジタル出力で直接駆動できないため、トランジスタで増幅しています。抵抗値などはかなり適当です。
実体配線図っぽいのは以下の通りです。
RN4020のサービスとキャラクタリスティックは以下のように設定しました。一つのサービスに開閉のデータを送るキャラクタリスティックとブザーを制御するためのキャラクタリスティックの2つを設定しています。また開閉のキャラクタリスティックについてはReadとNotifyの2つを設定しています。後ほどスクリプトでRN4020のIOポートとつなげるため、データ長は1バイトにしています。(単純な動作なので)
- サービス
- UUID 123456789012345678901234567890FF
- 鍵開閉のキャラクタリスティック
- UUID 12345678901234567890123456789011
- Mode Read,Notify
- Length 1byte(0x02の場合ロック,0x00の場合アンロック)
- ブザーのキャラクタリスティック
- UUID 12345678901234567890123456789022
- Mode Write(応答なし)
- Length 1byte(0x04の場合に発音,0x00の場合に消音)
実際のコマンドは以下のように設定します。
PS,123456789012345678901234567890FF PC12345678901234567890123456789011,12,01 PC12345678901234567890123456789022,04,01
@PW_ON |O,04,00 |O,04,%000E A @CONN SM,1,000F4240 @TMR1 $VAR1 = |I,02 SHW,000B,$VAR1 SM,1,000F4240 @DISCON A
スクリプトやその他設定の方法については以下を参照してください。
Webブライザ側のアプリケーションについて
Webブラウザ側のアプリケーションについては以下のUIを作りました。AugularJSとを利用して作成しています。
connectを押すとBLEの接続要求が実行されます。またreadやSubscribe/Unsubscribeでデバイス側のホール素子の値を取得します。
ソースコード全体については以下のリポジトリに置いてあります。
アプリの実行
アプリの実行時の動作を動画にしてみました。ブザーの音がするので音量は控えめにしてください。
デバイスへの接続ご、サブスクライブ(デバイスからのNotifyをスタート)を行い、画面上のLock Unlock表示が変わります。また、Alertをセットすることで、Unlock時にブザーが鳴るようになります。
まとめ
Web Bluetooth API とRN4020を使うことで簡単にBLEを使ったアプリケーションを作ることができました。今回は半日程度で両方実装できたので非常にお手軽だったと思います。BLEで何かしようと思った場合、スマフォなどのアプリは作れるけど、そもそも取り扱えるBLEデバイスの種類がないので面白いことができないといった場合にはRN4020でサクッとデバイスを作ってしまうのもいいかもしれません。また、自作のBLEデバイスを作ってみたけれど、コントロールするスマフォアプリを作れない、市販デバイスを気軽にハックしたいという場合はWeb Bluetooth APIを利用するとサクッと見栄えのいいアプリが作れると思います。コンセプトを試したいときなどアイディアの試作に利用できそうです。
今回の作例を使ったワークショップを2017年10月20日に開催予定です。どなたでもご参加いただけます。