読者です 読者をやめる 読者になる 読者になる

mbedのBLE_APIライブラリで128bitUUIDを利用する

表題の件をもう少し詳しく説明するとBLEにおいてGATTサービスのUUIDは128bitなのだが、mbedのBLEスタックであるBLE_APIライブラリのサンプルコードでは16bitのUUIDが使われたものばかりで、128bitで利用しようとすると少々ややこしかったのでここに書いておく。
なぜサンプルコードが16bitUUIDばかり使うかというと、Bluetooth規格で定められたサービスであれば16bitで定義されているからで、大体の利用がそれで足りるからだろう。しかし、独自のサービスをいれたい場合、16bitのUUIDは利用してはいけない*1ので、128bitのUUIDを利用しないといけないのだ。

前提

BLE_APIは2015年10月7日時点での最新版を利用した。また、mbedはBLE Nanoを利用している。
https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/

BLE Nanoキット

BLE Nanoキット

基本

UUIDの型は以下のようにすればいい。

uint8_t yourUUID[16] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

今回は利用しなかったが、BLE_APIライブラリにはUUIDクラスも提供されていおり、そこのコンストラクタに渡す引数がこの型になっている。

UUIDの作り方

UUIDはuuidgenというコマンドですぐ作れる。かんたん!

$ uuidgen
8DFC2C6E-FC34-4AE9-B1C3-55B3C925D9A9

ところで、今回はBLEで接続する先はiOSだったのだが、iOSはこの文字列をそのままCBUUIDのイニシャライザに投げ込めばOK。

注意点

現状、BLE_APIにおいてはUUIDは2箇所で設定する必要がある。GATTサーバーとGAPでのアドバタイズである。GATTサーバーに登録したら自動的にアドバタイズされるほど優しく作られてはいない。またGAPでアドバタイズするとき、UUIDのバイト列は反転させる必要があってマジか!って感じがあった。

実装

GATTサーバー

GattSerivceクラスのコンストラクタに上記の型のUUIDを渡して生成し、GattServerクラスでaddServiceの引数に渡す

ReadOnlyGattCharacteristic<bool>  someState;
GattCharacteristic* charTable[] = {&someState};
GattService hogeService(yourUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
ble.gattServer().addService(hogeService);

GAPでのアドバタイズ

GAPのアドバタイズパケットの設定に128bitUUIDをバイト列の順序を逆にして渡す。

/* setup advertising */
const static char     DEVICE_NAME[] = "DEVICE_NAME";

BLE ble;
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
//advertiseする時、IDを反転する必要がある
uint8_t advertiseServiceID[16];
for(int i=0; i<16; i++){
    advertiseServiceID[i] = yourUUID[16 - 1 - i];
}

ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t *) advertiseServiceID, sizeof(advertiseServiceID));
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble.gap().setAdvertisingInterval(1000); /* 1000ms. */
ble.gap().startAdvertising();

あとがき

GATTサーバー側を先に書いて動かしてみたら、アドバタイズしているサービスはそのままだったので、アドバタイズのところのコードを変えてみたら、アドバタイズされたサービスのUUIDがひっくり返って表示されてびっくりした。また、GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDSとIDに複数形がついてるが、実際はパケットの長さの制約上1個のIDしか入らないという面白いお話もある。
実際に動くコードについては整理したら公開するつもりです。

*1:開発ならよい