Arduino Uno の後継とも言われる、Intel Curie 搭載の Genuino (アメリカではArduino、商標の問題でアメリカ国外では Genuino) 101 が技術適合基準を通った物が販売され、日本でも問題なく使える様になったので購入して使ってみた。
Uno にはないアーキテクチャ以外の大きな特徴としては、IMU(xyz等の値を取れる、慣性計測装置)と、Bluetooth LE(BLE) での無線通信がオンボードでサポートされたことであろう。
本エントリーでは、BLE を使った L チカについて解説する。
Arduino IDE セットアップ
arduino.cc から最新の Arduino IDE 1.6.8 をダウンロード後、ボードマネージャで Genuino (Arduino) 101 を追加。Curie 向けのツールチェイン群が落とされる。なお、2016年3月現在、まだ Curie 周りのツールチェインはオープンソースになってない模様。
追記・https://github.com/01org/corelibs-arduino101 にてOSSになってました。コアライブラリはGPLv2なんだ。
いつも通りスケッチの例、01.Basics -> Blink で普通のLチカができることを確認する。ただ Mac OSX 10.11 の環境だからなのか、コンパイル後のスケッチ書き込み時に "Flashing is taking longer than expected, Try pressing MASTER_RESET button" と言われ、物理的な MASTER RESET ボタンを押さないと書き込みが起きないことが多数…。ちょっとつらい。
BLE で Lチカ
ボードマネージャで 101 を入れると、スケッチのサンプルに各種 Curie が表示される様になる。
これの CurieBLE を選ぶと BLE のサンプルスケッチを実行できる。まずは LED を選ぶ。BLE のペリファラルとなる101と、セントラルとなるPCやスマフォとの接続はこんな感じ。
- ペリファラルで各種設定。localNameや、サービスとキャラクタリティクスの登録をする。
- ペリファラルはアドバタイジングを開始し、それを発見するセントラルからの接続を待ち受ける。
- セントラルは目的のペリファラルをアドバタイジングから見つけ接続し、処理を行う。
- ペリファラルもセントラルから接続されたら処理を行う
なお、ペリファラルとセントラルはどっちがクライアントでどっちがサーバという概念ではなく、どちらもサーバにもクライアントにもなり得る。
この辺の話は 書籍、iOS×BLE Core Bluetoothプログラミング が Web エンジニアには大変解りやすいと思う。
101 での BLE 処理
さて、101 のペリファラルの処理をどう書くのか LED のサンプルスケッチを元に追っていく。
BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming)
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service
ledService という LED 向けの独自 GATT サービスのインスタンスを作ってる。BLE には 標準的なGATTサービスは仕様として定義されているが、それ以外の物はオレオレサービスを作ることで対応する。LED 向けサービスは仕様にないので、自分で 19B10000-E8F2-537E-4F6C-D104768A1214
という UUID でサービスを定義している。
なおオフィシャルサイトのチュートリアルであるArduino/Genuino 101 CurieBLE Heart Rate Monitor では、HeartRate サービス を題材に扱っていて、HeartRate は仕様定義されているので 180D
という UUID でサービスを定義している。
サービスに、続いてキャラクタリティクスの定義。
// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEUnsignedCharCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
読み書きできるキャラクタリティクスを定義している。UUIDはサービスの19B10000
とは1違いの19B10001
としている箇所がある。なお第二引数でのビットフラグの他の指定としては、変更時にイベントを発生させる BLENotify 等もあるが、今回は読み書きするだけなのでこれで ok。
setup() では初期化処理。この辺はまぁみたままなので。サービスもキャラクタリティクスも同じ関数の addAttribute で登録させるインターフェイスは気持ち悪いけど…。
// set advertised local name and service UUID:
blePeripheral.setLocalName("LED");
blePeripheral.setAdvertisedServiceUuid(ledService.uuid());
// add service and characteristic:
blePeripheral.addAttribute(ledService);
blePeripheral.addAttribute(switchCharacteristic);
// set the initial value for the characeristic:
switchCharacteristic.setValue(0);
// begin advertising BLE service:
blePeripheral.begin();
これでペリファラルの待ち受け(advertising送出)が完了。あとはメインループで処理している。
BLECentral central = blePeripheral.central();
if (central) {
while (central.connected()) {
if (switchCharacteristic.written()) {
if (switchCharacteristic.value()) { // any value other than 0
Serial.println("LED on");
digitalWrite(ledPin, HIGH); // will turn the LED on
} else { // a 0 value
Serial.println(F("LED off"));
digitalWrite(ledPin, LOW); // will turn the LED off
}
}
}
}
なお CurieBLE の他のサンプル、CallbackLED ではメインループでは blePeripheral.poll()
するだけにとどめ、setup で登録した各種 BLE のイベントハンドラで Callback 処理を行う様にしているので、きちんと実装するときはそちらを参考にした方が良い。
説明はこれぐらいにして、LED のサンプルスケッチを101を書き込めばペリファラルの準備は完了。
iPhone をセントラルとして接続してLチカする
では続いてセントラルとなるデバイス、一般的にはPCやスマートフォンが多い、から接続する。まずは iPhone。簡単に BLE のペリファラルに接続していろいろ出来るアプリ、LightBlue Explorerを入れる。
起動すると GENUINO 101-xxxx (xxxxは101自体のUUIDの後ろ4文字)というペリファラルが見つかるので接続する。なおこのペリファラル(101)自体のUUIDは、基板の裏のQRコードにも書いてあり、QRコードを読み取ることでUUIDが解るようになってる。
今回はledServiceしか提供してないので、ledServiceのUUIDが表示されてるので、タップし、その中のキャラクタリティクスの Write new value をタップし、HEX(16進数)の入力 UI が出てくるので 1 を入れ DONE を押すと 101 の LED が光る!再び 0を入力すると LEDが消える!というわけでLチカを iPhone 経由で行うことが出来た。文章と静止画だけだと大変地味だ…。
PC をセントラルとして接続する
PC からはプログラミングしてPCがセントラルとして接続してみる。PCのBLEプログラミングには noble を使うのがかんたん。
1秒ごとにLチカさせるコードはこんな感じ。なお iPhone から 101 に接続したままだと101がアドバタイジングしてないので見つけられず、接続することも出来ないので注意。
var noble = require('noble');
var LED_SERVICE_UUID = '19B10000-E8F2-537E-4F6C-D104768A1214'.replace(/\-/g, '');
var LED_STATE_CHARACTERISTIC_UUID = '19B10001-E8F2-537E-4F6C-D104768A1214'.replace(/\-/g, '');
noble.on('stateChange', function(state) {
if (state === 'poweredOn') {
noble.startScanning();
} else {
noble.stopScanning();
}
});
noble.on('discover', peripheral => {
var advertisement = peripheral.advertisement;
console.log(advertisement.localName);
if (advertisement.serviceUuids.find(uuid => uuid.toUpperCase() === LED_SERVICE_UUID)) {
console.log('found LED service');
noble.stopScanning();
peripheral.connect(function(error) {
peripheral.discoverSomeServicesAndCharacteristics([LED_SERVICE_UUID], [LED_STATE_CHARACTERISTIC_UUID],
function(error, services, characteristics) {
var characteristic = characteristics[0];
var value = 0;
var toggleLED = function() {
var buffer = new Buffer(1);
value = (value == 0 ? 1 : 0);
buffer.writeUInt8(value, 0);
console.log('toggle', value);
characteristic.write(buffer, true, () => setTimeout(toggleLED, 1000));
};
toggleLED();
});
});
}
});
抽象化できる noble-device を使うとこんな感じ。
var NobleDevice = require('noble-device');
var LED_SERVICE_UUID = '19B10000-E8F2-537E-4F6C-D104768A1214'.replace(/\-/g, '');
var LED_STATE_CHARACTERISTIC_UUID = '19B10001-E8F2-537E-4F6C-D104768A1214'.replace(/\-/g, '');
function LedService() {}
LedService.prototype.writeLedState = function(data, callback) {
this.writeUInt8Characteristic(LED_SERVICE_UUID, LED_STATE_CHARACTERISTIC_UUID, data, callback);
};
var LedDevice = function(device) { NobleDevice.call(this, device) };
NobleDevice.Util.inherits(LedDevice, NobleDevice);
NobleDevice.Util.mixin(LedDevice, LedService);
LedDevice.is = function(device) {
return device.advertisement.serviceUuids.find(uuid => uuid.toUpperCase() === LED_SERVICE_UUID);
};
LedDevice.discover(function(device) {
device.connectAndSetup(function(callback) {
var value = 0;
var toggleLED = function() {
value = (value == 0 ? 1 : 0);
console.log('toggle', value);
device.writeLedState(value, () => setTimeout(toggleLED, 1000));
};
toggleLED();
});
});
101 で Lチカしてみた感想
使ってみた感じ、BLE で出てくる専門概念、GATT等々を全く理解せずとも実装できるのが、Arduino ならではと感じる。とりあえず手軽に BLE で何かしたいときに、複雑なことは覚えずにとも使え、さらに BLE をきちんと学びたい人は、ちゃんと理解して進めば良いと思う。
しかしながら現在 Curie 関連のコードが OSS で無いため、どんな挙動をするのかがトライアンドエラーでしか解らなく、ちゃんと理解することが現時点では難しいのも確か。Curie.h のドキュメントがどこにあるのかも未だに解らないし、101(Arudino)のBLE関係のきちんとしたドキュメントも無いしソースも読めないのはハマったときに辛くなりそうではある…。
追記: Arduino IDE の設定でコンパイル時の詳細表示で、Intel Curie 関係のツールチェインのパスが解り、その中にソースコードがあることを教えて頂いた。
$ cd ~/Library/Arduino15/packages/Intel
$ ag BLENotify
...
hardware/arc32/1.0.5/libraries/CurieBLE/src/BLECharacteristic.cpp
170: if (_properties & (BLERead | BLENotify | BLEIndicate)) {
hardware/arc32/1.0.5/libraries/CurieBLE/src/BLECharacteristic.h
53: BLENotify = 0x10,
ヘッダのみならず、実装のc/cppのソース読める!
さらに追記・レポジトリはこちら