Node.js
iBeacon
BLE

MacやLinuxでnodeを使ってiBeaconをつくれるとはどういうことなのか?

More than 3 years have passed since last update.

この投稿はBluetooth Low Energy Advent Calendar 2014 の7日目の記事です!

今回も前日に引き続き @kazuph 担当させていただきます!

前置き

MacとiPhoneしか持ってない人がiBeaconを試したい!って思った時には、アプリなどを使ってどちらかをiBeaconの受信側・発信側にすることによって実現できると思います。

実際にそういう記事は多く書かれていて、例えばQiitaだと以下の記事にまず行き着くのではないでしょうか?

Node.js - たった6行!最も簡単にiBeaconの電波を「発信」する方法 - Qiita

beacon.js
Bleacon = require('bleacon');

var uuid = 'E86367B8D8A84BE082DE4653A7333113';
var major = 0;
var minor = 0;
var measuredPower = -59;

Bleacon.startAdvertising(uuid, major, minor, measuredPower);

こんな感じのコードを以下のように実行するだけです。

$ node ./beacon.js

はい、これでMacがiBeaconになります。とても簡単です(ただしYosemiteだと動かない)。

ここまではいいのですが、ではなんでこれだけのコードでMacがiBeaconになるのか?

またこのライブラリをRaspberry Piでも使ってみたのですが、同じように一瞬でRaspberry PiがiBeaconになりました(ただしBluetooth 4.0対応のドングルが必要)。ソースを共有しているの?それとも同じコードでも動くようにかけるの?

ということで今回はそれが気になったので、ちょっとライブラリbleaconを深堀りしたいと思います。

nodeモジュール「bleacon」

ソースの置い方は簡単です。まずはgithubでこのライブラリを探しましょう。

https://github.com/sandeepmistry/node-bleacon

どうやらこれっぽいです。

言語割合を見てみましょう。

javascript juce

JSだけで書いてあることがわかります。ここで思うのはBluetoothというデバイスレベルの操作を行っているのに、ローレベルな言語であるObj-CやC/C++が登場しないということがあるのか?AppleがわざわざJS直でBLEをいじれるAPIを提供しているか?それは確率が低いのではないか?ということです。

ということは、このbleaconは他のCで書かれた別のライブラリに依存してるのではないかということです。

では次に何を見るか?package.jsonを見ると依存しているライブラリ一覧を見ることができます。

bleaconの依存してるライブラリ

package.json

 "dependencies": {
"noble": "~0.3.0",
"debug": "~0.7.2",
"bleno": "~0.1.0",
"bignum": "~0.6.2"
},

ありました!この中でbleに関連してそうなのはnobleblenoです。いかにもBLEを取り扱えそうなライブラリです。

次はこの2つを見てみます。

noble, bleno

noble
bleno

nobleがCentralとなるためのライブラリ、blenoがPeripherals となるためのライブラリのようです。

また推理通りC言語に依存しているライブラリのようです。そしてObjective-Cに依存していないとうことは、MacでBluetoothを使うためのフレームワークであるIOBluetoothにも依存していないということです。つまり素のCでBluetoothをいじっていうるということになります。

では次にCのソースを炙り出して見ます。

~/sandeepmistry $ find . -type f -name "*.c" -exec wc -l "{}" ";"
     296 ./bleno/src/hci-ble.c
     235 ./bleno/src/l2cap-ble.c
     188 ./noble/src/hci-ble.c
     203 ./noble/src/l2cap-ble.c

左の数字は行数を表しています。blenoの方がCのパーセンタイルが多かったですが、実際に行数も多いですね。

Cの中身を見てみる。

今回はiBeaconが気になっての記事なのでPeripherals化できるライブラリであるblenoを見てみます。

ファイル名にあるhciと、l2cap、、、あれ、これどこかで見たような気が、、、

あ、そうだBLEの仕様とかわふうさんのブログで見たやつや

BLEの通信仕様 - Reinforce-Lab.'s Blog

ちょっと中身を覗いてみましょう。

hci-ble.c, l2cap-ble.c

hci-ble.cから見てみます。

hci-ble.c
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

いきなりありました。bluetoothというライブラリがあるみたいです。

▼ functions
    hci_le_set_advertising_data(int d
    hci_le_set_advertising_parameters
    hci_le_set_scan_response_data(int
    main(int argc, const char* argv[]
    signalHandler(int signal)

関数リストを見ると以外にシンプルですが、名前からアドバタイジング関係のことをしているのがわかります。

nodeからはこれを単に叩いているだけなので、最低このcのコードだけでもアドバタイジングできそうです。

l2cap-ble.cはどうでしょう?

l2cap-ble.c
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>

追加でl2cap.hをincludeしていることがわかります。中身見たいw

Macにはbluetooth.hなんてない

中身みたいなぁって思ったんですが、どうやらMacにはbluetooth.hなんてないみたいです。

ここでちょっと悩んだんですが、lib/bleno.jsを見たらこう書いてありました。

bleno.js
var platform = os.platform();

if (platform === 'darwin') {
  bindings = require('./mac/bindings');
} else if (platform === 'linux') {
  bindings = require('./linux/bindings');
} else {
  throw new Error('Unsupported platform');
}

あ、bindingするやつを変えてました。一旦linux/bindingsの中身を見てましょう。

lib/linux/bindings.js
var HciBle = require('./hci-ble');
var L2capBle = require('./l2cap-ble');

更にhci-ble.jsを見ると、

lib/linux/hci-ble.js
var HciBle = function() {
  var hciBle = __dirname + '/../../build/Release/hci-ble';

  debug('hciBle = ' + hciBle);

  this._hciBle = spawn(hciBle);
  this._hciBle.on('close', this.onClose.bind(this));

  this._hciBle.stdout.on('data', this.onStdoutData.bind(this));
  this._hciBle.stderr.on('data', this.onStderrData.bind(this));

  this._hciBle.on('error', function() { });

  this._buffer = "";
};

これでわかりましたが、Linux版の時はhci-ble.cをビルドしてできたバイナリを読み込んで出力をnodeから読み込んでいるみたいです。

じゃあMacはどうやってるのか?

lib/mac/binding.jsを見てみます。

lib/mac/bindings.js
var XpcConnection = require('xpc-connection');

var BlenoBindings = function() {
  this._xpcConnection = new XpcConnection('com.apple.blued');

  this._xpcConnection.on('error', function(message) {
    this.emit('xpcError', message);
  }.bind(this));

  this._xpcConnection.on('event', function(event) {
    this.emit('xpcEvent', event);
  }.bind(this));
};



blenoBindings.startAdvertisingIBeacon = function(data) {
  var osRelease = parseFloat(os.release());

  if (osRelease >= 14) {
    throw new Error('OS X 10.10 does not support advertising iBeacon');
  }

  this.sendCBMsg(8, {
    kCBAdvDataAppleBeaconKey: data
  });
};


XPC…だと…?

Cocoaの日々: [Mac] Lion から導入された XPC Services

Mac OS X 10.7 から XPC Services という仕組みが導入された。

リファレンスによれば XPC Services は単一のアプリケーション専用に利用できる軽量なヘルパーツール(lightweight helper tools)と定義されている。

XPC Services はOS上のプロセスに近い(プロセスなのかまでは調べきれなかったがプロセス間通信と説明されているのでそうなんだと思う。Mac OS X で何か軽量なプロセスが用意されているのだろうか。)。

OSXの機能にメッセージベースでやりとりできるAPI(プロトコル?)らしい。たとえばkCBMsgId6という文字列を受け取っている部分があるのですが、このID番号によって機能が違うらしいです。

もうなんの記事だかわからなくなってきました。

しかもnodeのモジュールがある。作者もbleaconと同じ人。
※nobleもblenoも作者が一緒!

sandeepmistry/node-xpc-connection

ijouna-doryoku

はい、Macでxcp経由でBLEをいじるためにほぼC++で書かれたこんなライブラリを開発していました。

この人、まさかこれのためにこんなに努力して…(´;ω;`)ブワッ

結論

もともとiBeaconのための探求でしたが、結果的にnodeとC/C++での異常な努力を垣間見ることになりました。

またnobleとblenoを使えばnodeから自由にBLEアプリケーションを書くことができそうですが、linuxとmac両対応できているのは、それぞれのプラットフォーム毎にBluetoothの実装をゴリゴリプログラムを書いた先人(@sandeepmistry)がいるからです。現在Yosemiteではblenoは対応しきれてないみたいですが、またいつか異常な努力によって解決してくれるでしょう。

idai-na-hito

sandeepmistry (Sandeep Mistry)

ありがとう@sandeepmistry、本当にありがとう、、、

 
 
 
 
 
 

明日は @shu223 さんの担当です!

お楽しみに!!