7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

noble-device を使って node から扱える BLE のライブラリを実装する

Last updated at Posted at 2016-02-27

node.js から BLE のデバイスと通信したい。noble というライブラリで BLE のデバイスに node 側がセントラルとして通信できる。

noble は BTスタックとしては、Mac なら XPC で com.apple.blued と通信し(XPC 通信の node のライブラリも同じ作者作、すごい)、Linux なら HCI で通信し、それらを抽象化してあつかってるので、Mac でも Linux でも、同一コードで取り扱える。

セントラルとして通信

BLE で node 側(PC)がセントラルとして通信するには、ざっくりこんな手段になる。

  1. アドバタイズメントパケットをスキャンする
  2. アドバタイズメントパケットの中から、目的のペリファラルとなるデバイスを発見する
  3. ペリファラルと接続する
  4. 提供しているサービスやキャラクタリティクスを調べる
  5. それらに対して書き込みや読み込み、通知(notify)を受け取る

これらを noble で一つ一つ行う(ある程度は抽象化されてる)必要があるが、noble-device を使うと良い感じに書くことが出来る。

Heart Rate Measurement サービスのライブラリを実装する

試しに HRM のライブラリを実装してみる。

別に GATT のベールプロファイルであろうが、自前の独自プロファイルであろうが実装できるが、今回は HRM で。なおなぜ HRM かというと、mbed のペリファラル BLE 実装のサンプルコードが HRM だったから…。

定義は以下の通りで、シンプル。

0x180D のサービスと、キャラクタリティクス各種、measurement の 0x2A37 (notify) と体の位置情報の Body Sensor Location 0x2A38 (read)、Heart Rate Control Point (write) に対応すれば良い。

var HEART_RATE_MEASUREMENT_SERVICE_UUID = '180d';
var MEASUREMENT_UUID                    = '2a37';
var BODY_SENSOR_LOCATION_UUID           = '2a38';
var CONTROL_POINT_UUID                  = '2a39';

function HeartRateMeasumentService() {
}

HeartRateMeasumentService.prototype.readBodySensorLocation = function(callback) {
  this.readUInt8Characteristic(HEART_RATE_MEASUREMENT_SERVICE_UUID, BODY_SENSOR_LOCATION_UUID, callback);
};

HeartRateMeasumentService.prototype.writeControllPoint = function(data, callback) {
  this.writeUInt8Characteristic(HEART_RATE_MEASUREMENT_SERVICE_UUID, CONTROL_POINT_UUID, data, callback);
};


HeartRateMeasumentService.prototype.notifyMeasument = function(callback) {
  this.onMeasumentChangeBinded = this.onMeasumentChange.bind(this);
  this.notifyCharacteristic(HEART_RATE_MEASUREMENT_SERVICE_UUID, MEASUREMENT_UUID, true, this.onMeasumentChangeBinded, callback);
};

HeartRateMeasumentService.prototype.unnotifyMeasument = function(callback) {
  this.notifyCharacteristic(HEART_RATE_MEASUREMENT_SERVICE_UUID, MEASUREMENT_UUID, false, this.onMeasumentChangeBinded, callback);
};

HeartRateMeasumentService.prototype.onMeasumentChange = function(data) {
  this.convertMeasument(data, function(counter) {
    this.emit('measumentChange', counter);
  }.bind(this));
};

HeartRateMeasumentService.prototype.readMeasument = function(callback) {
  this.readDataCharacteristic(HEART_RATE_MEASUREMENT_SERVICE_UUID, MEASUREMENT_UUID, function(error, data) {
    if (error) {
      return callback(error);
    }

    this.convertMeasument(data, function(counter) {
      callback(null, counter);
    });
  }.bind(this));
};

HeartRateMeasumentService.prototype.convertMeasument = function(data, callback) {
  var flags = data.readUInt8(0);
  if (flags & 0b00000001) {
    // uint16
    callback(data.readUInt16LE(1));
  } else {
    // uint8
    callback(data.readUInt8(1));
  }
};

notify の対応がちょっと面倒だけど、他はさくっと。ライブラリとしての実装はここまでで、続いてこのライブラリを inherits と mixin でつなぎこんで、noble-device のデバイスに機能追加を行う。

var async = require('async');
var NobleDevice = require('noble-device');

var HRM = function(device) {
  NobleDevice.call(this, device);
};

NobleDevice.Util.inherits(HRM, NobleDevice);
// 標準的な DeviceInformationService も mixin する
NobleDevice.Util.mixin(HRM, NobleDevice.DeviceInformationService);
NobleDevice.Util.mixin(HRM, HeartRateMeasumentService);

また、アドバタイズからどのペリファラルと接続するかを is 関数で実装する。今回は localName が HRM-DEVICE なら接続される。

HRM.is = function(device) {
  var localName = device.advertisement.localName;
  return localName === 'HRM-DEVICE';
};

あとは適当に処理を。

HRM.discover(function(device) {
  console.log('discovered: ' + device);

  device.on('disconnect', function() {
    console.log('disconnected!');
    process.exit(0);
  });

  device.on('measumentChange', function(data) {
    console.log("update measument: " + data);
  });

  device.connectAndSetUp(function(callback) {
    console.log('connectAndSetUp');
    device.notifyMeasument(function(counter) {
      console.log('notifyMeasument');
    });
  });
});

これを実行し、HRM のサービスを提供してる BLE のペリファラルデバイスと接続できたら以下のような表示になる。

$ node hrm.js
discovered: {"uuid":...}
connectAndSetUp
notifyMeasument
update measument: 103
update measument: 104
update measument: 105
update measument: 106
update measument: 107
update measument: 108
update measument: 109
// BLE 機器の電源を落とした
disconnected!

(追記:HRM Service自体本家にマージされました)

なお、noble-device の参考コードとしては、様々なセンサーやボタンが搭載されている TI の CC2650 などの BLE のセンサータグを noble-device を用いて実装してるコードが大変参考になった。

7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?