Raspberry Pi2からSensorTagのデータをBluebirdのCoroutineとPromiseで取得する

BeagleBone BlackでSensorTagを使ったときのように、Raspberry Pi 2でもNode.jsのコードからデータを取得してみます。node-sensortagライブラリに付属しているtest.jsのサンプルコードはAsync.jsasync.seriesを使っています。最近はES6のPromiseとGenerator、Coroutineで書くようにしているのでBluebirdを使うコードに書き直してみます。

必要なもの

SensorTag

新しい製品のSensorTag 2015発売されていますが、以前購入した古いSensorTagを利用します。

Bluetooth USBアダプタ

BLE対応のアダプタはプラネックスのBT-Micro4を購入しました。Raspberry Piに挿してlsusbコマンドを実行するとデバイスが認識されています。

$ lsusb
...
Bus 001 Device 005: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)

BlueZ

BlueZはLinux用のBluetooth プロトコルスタックです。RPi Bluetooth LEによると、Raspberry Piのapt-getからインストールできるパッケージは少し古いです。

バージョンの確認

最初にRaspberry Pi 2のカーネルとRaspbianのバージョンを確認します。

$ uname -a
Linux raspberrypi 3.18.13-v7+ #785 SMP PREEMPT Mon May 18 17:53:02 BST 2015 armv7l GNU/Linux
$ cat /etc/debian_version
7.8

apt-getからインストールするbluezパッケージは4.99-2でした。

$ sudo apt-get update
$ apt-cache show bluez
...
Version: 4.99-2

ソースからインストール

RPi Bluetooth LEに掲載されている手順でインストールしていきます。

古いパッケージがインストールされている場合は削除します。

$ sudo apt-get remove bluez

ビルドに必要なパッケージをインストールします。

$ sudo apt-get update
$ sudo apt-get install libdbus-1-dev libdbus-glib-1-dev libglib2.0-dev libical-dev libreadline-dev libudev-dev libusb-dev make

最新の5.33をビルドしてインストールします。

$ mkdir -p ~/work/bluepy
$ cd ~/work/bluepy
$ wget https://www.kernel.org/pub/linux/bluetooth/bluez-5.33.tar.gz
$ tar xzvf bluez-5.33.tar.gz
$ cd bluez-5.33
$ ./configure --disable-systemd
$ make
$ sudo make install

hciconfigコマンドでBLEアダプタの状態をみるとDOWNしています。

$ hciconfig
hci0: Type: BR/EDR Bus: USB
BD Address: 00:1B:DC:06:1C:CF ACL MTU: 310:10 SCO MTU: 64:8
DOWN
RX bytes:55911 acl:1143 sco:0 events:2170 errors:0
TX bytes:20152 acl:1144 sco:0 commands:370 errors:0

sudoでupしてUP RUNNINGの状態にします。

$ sudo hciconfig hci0 up
$ hciconfig
hci0: Type: BR/EDR Bus: USB
BD Address: 00:1B:DC:06:1C:CF ACL MTU: 310:10 SCO MTU: 64:8
UP RUNNING
RX bytes:56475 acl:1143 sco:0 events:2199 errors:0
TX bytes:20270 acl:1144 sco:0 commands:399 errors:0

Node.jsのインストール

Node.jsでBluebirdを使う場合、--harmonyオプションと0.11+が必要になります。node_armから最新の0.12.6をRaspberry Piにインストールします。

$ wget http://node-arm.herokuapp.com/node_latest_armhf.deb
$ sudo dpkg -i node_latest_armhf.deb
$ npm -v
2.11.2
$ node -v
v0.12.6

プロジェクト

nose-sensortagを使ったNode.jsのサンプルコードを書いていきます。リポジトリはこちらです。プロジェクトのディレクトリを作成して移動します。

$ mkdir -p ~/node_apps/bluebird-sensortag
$ cd !$

gitignore.ioから.gitignoreをダウンロードします。

$ curl https://www.gitignore.io/api/linux,node > .gitignore

package.json

package.jsonに必要なパッケージをdependenciesフィールドに定義します。node-sensortagに付属しているサンプル用にAsync.jsも使います。scriptsフィールドには--harmonyフラグを追加します。

~/node_apps/bluebird-sensortag/package.json
{
"name": "node-bluebird-sensortag",
"description": "node-bluebird-sensortag",
"version": "0.0.1",
"private": true,
"dependencies": {
"sensortag": "~1.1.1",
"async": "~1.4.2",
"bluebird": "~2.9.34"
},
"scripts": {
"start": "node --harmony app.js"
}
}

パッケージをインストールします。

$ npm install

app.js

Promise.coroutineで使うGenerator内部では、yieldする関数はPromiseを返す必要があります。node-sensortagのライブラリはPromiseを返さないためPromisificationが必要になります。

今回はPromise.promisifyAllを使い、SensorTagオブジェクトの関数をすべてPromisificationします。Promisificationされた関数にはAsyncがサフィックスされます。

test.jsにあるように本来はsensorTag.enableIrTemperature(callback);として、callbackに取得したデータを渡します。Promiseにするとthenで取得することができます。非同期のコードがとても見やすくなりました。

~/node_apps/bluebird-sensortag/app.js
'use strict';
var Promise = require('bluebird'),
SensorTag = require('sensortag');
Promise.promisifyAll(SensorTag);
SensorTag.discover(function(sensorTag) {
console.log('discovered: ' + sensorTag);
sensorTag.on('disconnect', function() {
console.log('Tag Disconnected');
process.exit(0);
});
Promise.coroutine(function* () {
console.log('connectAndSetUp');
yield sensorTag.connectAndSetUpAsync();
console.log('enableIrTemperature');
yield sensorTag.enableIrTemperatureAsync();
yield Promise.delay(2000);
console.log('readIrTemperature');
yield sensorTag.readIrTemperatureAsync()
.then(function(data) {
console.log('\tobject temperature = %d °C',
data[0].toFixed(1));
console.log('\tambient temperature = %d °C',
data[1].toFixed(1));
});
console.log('disableIrTemperature');
yield sensorTag.disableIrTemperatureAsync();
console.log('enableHumidity');
yield sensorTag.enableHumidityAsync();
yield Promise.delay(2000);
console.log('readHumidity');
yield sensorTag.readHumidityAsync()
.then(function(data) {
console.log('\ttemperature = %d °C', data[0].toFixed(1));
console.log('\thumidity = %d %', data[1].toFixed(1));
});
console.log('enableMagnetometer');
yield sensorTag.enableMagnetometerAsync();
console.log('enableBarometricPressure');
yield sensorTag.enableBarometricPressureAsync();
yield Promise.delay(2000);
console.log('readBarometricPressure');
yield sensorTag.readBarometricPressureAsync()
.then(function(data) {
console.log('\tpressure = %d hPa', data.toFixed(1));
});
console.log('disableBarometricPressure');
sensorTag.disableBarometricPressureAsync();
console.log('disconnect');
yield sensorTag.disconnectAsync();
})();
});

プログラムの実行

付属のtest.js

SensorTagの電源を入れてから、node-sensortag付属のtest.jsをsudoで実行してみます。

$ cd ~/node_apps/node-bluebird-sensortag/node_modules/sensortag
$ sudo node test.js
discovered: {"id":"b4994c34d5c0","type":"cc2540"}
connectAndSetUp
readDeviceName
device name = TI BLE Sensor Tag
readSystemId
system id = b4:99:4c:00:00:34:d5:c0
readSerialNumber
serial number = N.A.
readFirmwareRevision
firmware revision = 1.4 (Jul 12 2013)
readHardwareRevision
hardware revision = N.A.
readSoftwareRevision
software revision = N.A.
readManufacturerName
manufacturer name = Texas Instruments
enableIrTemperature
readIrTemperature
object temperature = 24.6 °C
ambient temperature = 27.4 °C
disableIrTemperature
enableAccelerometer
readAccelerometer
x = 0 G
y = 0.2 G
z = 3.9 G
disableAccelerometer
enableHumidity
readHumidity
temperature = 27.7 °C
humidity = 66.4 %
disableHumidity
enableMagnetometer
readMagnetometer
x = -58.5 μT
y = 89.3 μT
z = 7.5 μT
disableMagnetometer
enableBarometricPressure
readBarometricPressure
pressure = 1002.9 mBar
disableBarometricPressure
enableGyroscope
readGyroscope
x = 2.3 °/s
y = 2 °/s
z = 0.3 °/s
disableGyroscope
readTestData
data = 63
readTestConfiguration
configuration = 0
readSimpleRead - waiting for button press ...
left: false
right: true
disconnect
disconnected!

SensorTagからBLEでRaspberry Piにデータを送信して、Node.jsのプログラムから取得することができました。

app.jsの実行

取得している環境データは減らしていますが、BluebirdのCoroutineで書いたコードも正常に動作しました。

$ sudo npm start
> node-bluebird-sensortag@0.0.1 start /home/pi/node_apps/node-bluebird-sensortag
> node --harmony app.js
discovered: {"id":"b4994c34d5c0","type":"cc2540"}
connectAndSetUp
enableIrTemperature
readIrTemperature
object temperature = 23.6 °C
ambient temperature = 27.2 °C
disableIrTemperature
enableHumidity
readHumidity
temperature = 27.6 °C
humidity = 61.4 %
enableMagnetometer
enableBarometricPressure
readBarometricPressure
pressure = 1005.8 hPa
disableBarometricPressure
disconnect
Tag Disconnected