micro:bitのBLEのA・BボタンイベントをNode.js nobleで取得してみるメモです。
いきさつ
まず、BLEは特にデリケートなので、参考までに使用Raspberry Piのインストール情報を記載しておきます。
[Kernel]
No LSB modules are available.
Distributor ID: Raspbian
Description: Raspbian GNU/Linux 9.3 (stretch)
Release: 9.3
Codename: stretch
Linux version 4.9.59-v7+ (dc4@dc4-XPS13-9333) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611) ) #1047 SMP Sun Oct 29 12:19:23 GMT 2017
[Node.js]
v6.13.0
2017-11-29-raspbian-stretch.img をベースに設定しています。nobleを使うにあたってのBluetooth apt-get も済ませている状況です。→参考:noble/noble: A Node.js BLE (Bluetooth Low Energy) central module
私の環境ですと、BLE自体はいくらか動作するものの、2018/02/18時点で node-bbc-microbit がうまく動かせないので、ひとまず、micro:bitのBLEのA・BボタンイベントをNode.js nobleで取得してみることにしました。
node-bbc-microbit サンプルのbutton-listener.jsを参考にやってみたものの、connectingにはなるものの、connectedにはならない。このあたりは私の理解不足がありそうなので、いずれ再挑戦したいです。
micro:bit
micro:bit側ソースコードはこちらです。
micro:bitとHoloLensをBluetoothで接続できたメモをベースに、接続時・切断時・起動時にLEDサービスとボタンサービスが立ち上がる仕組み。
JavaScriptとしては以下のように書かれています。
bluetooth.onBluetoothConnected(() => { basic.showIcon(IconNames.Happy) }) bluetooth.onBluetoothDisconnected(() => { basic.showIcon(IconNames.Asleep) }) bluetooth.startLEDService() bluetooth.startButtonService() basic.showIcon(IconNames.Heart)
Raspberry Pi側 Node.js
nobleインストール
以前の記事、
NodeJSでBLE通信ができるnobleライブラリでkonashiとつなげたメモ – 1ft-seabass.jp.MEMO
こちらを参考に、 noble@1.9.0 が入りました。
micro:bit ボタンサービスのUUIDを把握する
このあたりは node-bbc-microbit/button-service.js の情報がわかりやすいので参考にします。
var BUTTON_SERVICE_UUID = 'e95d9882251d470aa062fa1922dfa9a8'; var BUTTON_A_CHARACTERISTIC_UUID = 'e95dda90251d470aa062fa1922dfa9a8'; var BUTTON_B_CHARACTERISTIC_UUID = 'e95dda91251d470aa062fa1922dfa9a8';
noble側ソースコード
以前のnoble記事 を元に書いていきます。UUIDも把握できてますし、一度、実装の経験があるとわかりやすいですね。
var noble = require('noble'); var BUTTON_SERVICE_UUID = 'e95d9882251d470aa062fa1922dfa9a8'; var BUTTON_A_CHARACTERISTIC_UUID = 'e95dda90251d470aa062fa1922dfa9a8'; var BUTTON_B_CHARACTERISTIC_UUID = 'e95dda91251d470aa062fa1922dfa9a8'; var BUTTON_A_CHARACTERISTIC; var BUTTON_B_CHARACTERISTIC; // 状態がパワーONだったらスキャンに移行 noble.on('stateChange', function(state) { console.log('on -> stateChange: ' + state); if (state === 'poweredOn') { noble.startScanning(); } else { noble.stopScanning(); } }); noble.on('scanStart', function() { console.log('on -> scanStart'); }); noble.on('scanStop', function() { console.log('on -> scanStop'); }); // discover 機器が発見されたら noble.on('discover', function(peripheral) { console.log('on -> discover: ' + peripheral); // まずスキャンをとめる noble.stopScanning(); // 接続時のイベント peripheral.on('connect', function() { console.log('on -> connect'); this.discoverServices(); }); // 切断時のイベント peripheral.on('disconnect', function() { console.log('on -> disconnect'); }); // 見つけたサービス(機器)へのアクセス peripheral.on('servicesDiscover', function(services) { for(i = 0; i < services.length; i++) { // サービスがBUTTON_SERVICE_UUIDと一致した時だけ処理 if(services[i]['uuid'] == BUTTON_SERVICE_UUID){ // サービスのcharacteristic捜索 services[i].on('includedServicesDiscover', function(includedServiceUuids) { console.log('on -> service included services discovered [' + includedServiceUuids + ']'); this.discoverCharacteristics(); }); // characteristic取得イベント services[i].on('characteristicsDiscover', function(characteristics) { // characteristics配列から必要なCHARACTERISTICSをUUIDから判断してcharacteristic格納 for(j = 0; j < characteristics.length; j++) { if( BUTTON_A_CHARACTERISTIC_UUID == characteristics[j].uuid ){ console.log("BUTTON_A_CHARACTERISTIC_UUID exist!!"); BUTTON_A_CHARACTERISTIC = characteristics[j]; BUTTON_A_CHARACTERISTIC.on('read',function(data,isNotice){ console.log("BUTTON_A_CHARACTERISTIC"); console.log(data); }); BUTTON_A_CHARACTERISTIC.subscribe(function(error){ console.log("BUTTON_A_CHARACTERISTIC subscribe start"); console.log(error); }); } if( BUTTON_B_CHARACTERISTIC_UUID == characteristics[j].uuid ){ console.log("BUTTON_B_CHARACTERISTIC_UUID exist!!"); BUTTON_B_CHARACTERISTIC = characteristics[j]; BUTTON_B_CHARACTERISTIC.on('read',function(data,isNotice){ console.log("BUTTON_B_CHARACTERISTIC"); console.log(data); }); BUTTON_B_CHARACTERISTIC.subscribe(function(error){ console.log("BUTTON_B_CHARACTERISTIC subscribe start"); console.log(error); }); } } }); services[i].discoverIncludedServices(); } } }); // 機器との接続開始 peripheral.connect(); });
こちらを button_listener_noble.js で保存します。
実行してみる
実行してみると、無事ログが出てきて接続に成功します。
BUTTON_A_CHARACTERISTIC subscribe start
null
BUTTON_B_CHARACTERISTIC subscribe start
null
となっているのは、nullはエラーがないことを示しているので成功です。
こちらで接続完了です。
早速、A・B・A・Bと交互に押してみましょう。
今回はreadで受信したデータ内容も表示しています。Bufferとして、押された時は Buffer01、話した時はBuffer00で受け取れていますね!
余談:うまく接続できない一部のケースはBluetooth再起動で回復できる
もしdiscoverで止まったりnoble warning: unknown peripheralで止まったりうまく接続できないケースがあります。
以下のようなケース。
pi@raspi-main:~/Desktop/noble_test $ node noble_sample.js on -> stateChange: poweredOn on -> scanStart on -> discover: {"id":" ","address":" ","addressType":"public","connectable":true,"advertisement":{"manufacturerData":{"type":"Buffer","data":[ ]},"serviceData":[{"uuid":" ","data":{"type":"Buffer","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}}],"serviceUuids":[" "],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]},"rssi":-66,"state":"disconnected"} on -> scanStop
idやaddressは伏せていますが、Bufferデータはあるものの、つながらない状態。
pi@raspi-main:~/Desktop/noble_test $ node noble_sample.js on -> stateChange: poweredOn on -> scanStart on -> discover: {"id":" ","address":" ","addressType":"random","connectable":true,"advertisement":{"localName":"BBC micro:bit [ ]","serviceData":[],"serviceUuids":[],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]},"rssi":-40,"state":"disconnected"} on -> scanStop on -> connect noble warning: unknown peripheral < id >
もうひとつ。connect できるけど noble warning: unknown peripheral になるケース。
これらはBluetoothを手動で down → up 再起動で復帰できることがあります。
sudo hciconfig hci0 down sudo hciconfig hci0 up
要するに、一回停止して起動してやるのですが、調子良く動きだすことが多いです。ちなみに、 sudo hciconfig hci0 reset だと何かが残っているらしく回復しませんでした。
もし、なにか接続できないトラブルが起きたら、Raspberry Pi全体の再起動をかける前に試してみて下さい。
まとめ
ということで、micro:bitのBLEのA・BボタンイベントをNode.js nobleで取得してみました。
これができると、HoloLensでのC#で頑張って接続プログラムを検証する前に、一旦Node.jsで実装やデータの送受信の仕方を試してから確信を持って対応できますし、もしつまづいたときには、イチからやり直すわけでなく、中間のポイントを持てるので、少し安心できますね。
それでは、よき micro:bit & Node.js Life を!