前回 BLE Nano で Lチカまでやったので、今回は BLE Nano を iBeacon 化して、iOS アプリから検知してみたいと思います。
また、開発環境としては前回の記事で、 Vagrant で構築した VM からだと mbed import
の途中で止まってしまったと書きましたが、 Mac 上の環境と同様で時間がかかるものの、待てば正常に進んだので、今回は Vagrant で構築した CentOS 環境から mbed cli で実行します。
サンプルコードを動かす
ひとまず動かしてみるだけであれば公開されているサンプルコードをインポートしてそのままコンパイルすれば動かせてしまいます。まずはインポート。
[vagrant@localhost vagrant]$ mbed import https://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_iBeacon/ [mbed] Importing program "BLE_iBeacon" from "https://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_iBeacon" at latest revision in the current branch [mbed] Adding library "BLE_API" from "https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API" at rev #65474dc93927 [mbed] Adding library "mbed" from "https://mbed.org/users/mbed_official/code/mbed/builds" at rev #abea610beb85 [mbed] Downloading library build "abea610beb85" (might take a minute) [mbed] Unpacking library build "abea610beb85" in "/vagrant/BLE_iBeacon/mbed" [mbed] Adding library "nRF51822" from "https://mbed.org/teams/Nordic-Semiconductor/code/nRF51822" at rev #c90ae1400bf2 [mbed] Adding library "shields/TARGET_ST_BLUENRG" from "https://developer.mbed.org/teams/ST/code/X_NUCLEO_IDB0XA1" at rev #fa98703ece8e [mbed] Couldn't find build tools in your program. Downloading the mbed 2.0 SDK tools...
続けて toolchain と target を指定してからコンパイルしてみます。
[vagrant@localhost vagrant]$ cd BLE_iBeacon/ [vagrant@localhost BLE_iBeacon]$ [vagrant@localhost BLE_iBeacon]$ mbed detect [mbed] Detected RBLAB_BLENANO, port /dev/ttyACM0, mounted /run/media/vagrant/DAPLINK [mbed] Supported toolchains for RBLAB_BLENANO +--------+-----------+-----------+-----+---------+-----+ | Target | mbed OS 2 | mbed OS 5 | ARM | GCC_ARM | IAR | +--------+-----------+-----------+-----+---------+-----+ +--------+-----------+-----------+-----+---------+-----+ Supported targets: 0 [vagrant@localhost BLE_iBeacon]$ [vagrant@localhost BLE_iBeacon]$ mbed toolchain GCC_ARM [mbed] GCC_ARM now set as default toolchain in program "BLE_iBeacon" [vagrant@localhost BLE_iBeacon]$ mbed target RBLAB_BLENANO [mbed] RBLAB_BLENANO now set as default target in program "BLE_iBeacon" [vagrant@localhost BLE_iBeacon]$ [vagrant@localhost BLE_iBeacon]$ mbed compile Building project BLE_iBeacon (RBLAB_BLENANO, GCC_ARM) Scan: . Scan: mbed Scan: env Compile [ 1.8%]: BLE.cpp 〜〜〜中略〜〜〜 Compile [100.0%]: nRF5xn.cpp Link: BLE_iBeacon /usr/local/lib/gcc-arm-none-eabi-4_9-2015q3/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m/crt0.o: In function `_start': (.text+0x52): undefined reference to `__wrap_exit' ./mbed/abea610beb85/TARGET_RBLAB_BLENANO/TOOLCHAIN_GCC_ARM/retarget.o: In function `__cxa_pure_virtual': retarget.cpp:(.text.__cxa_pure_virtual+0x4): undefined reference to `__wrap_exit' ./mbed/abea610beb85/TARGET_RBLAB_BLENANO/TOOLCHAIN_GCC_ARM/libmbed.a(mbed_error.o): In function `error': mbed_error.c:(.text.error+0x10): undefined reference to `__wrap_exit' collect2: error: ld returned 1 exit status [ERROR] /usr/local/lib/gcc-arm-none-eabi-4_9-2015q3/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m/crt0.o: In function `_start': (.text+0x52): undefined reference to `__wrap_exit' ./mbed/abea610beb85/TARGET_RBLAB_BLENANO/TOOLCHAIN_GCC_ARM/retarget.o: In function `__cxa_pure_virtual': retarget.cpp:(.text.__cxa_pure_virtual+0x4): undefined reference to `__wrap_exit' ./mbed/abea610beb85/TARGET_RBLAB_BLENANO/TOOLCHAIN_GCC_ARM/libmbed.a(mbed_error.o): In function `error': mbed_error.c:(.text.error+0x10): undefined reference to `__wrap_exit' collect2: error: ld returned 1 exit status [mbed] ERROR: "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python" returned error code 1. [mbed] ERROR: Command "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python -u /vagrant/BLE_iBeacon/.temp/tools/make.py -t GCC_ARM -m RBLAB_BLENANO --source . --build ./BUILD/RBLAB_BLENANO/GCC_ARM" in "/vagrant/BLE_iBeacon" ---
コンパイル自体は成功しましたが、ライブラリをリンクするところでエラーになってしまいました。調べたところ下記のページの情報をみつけました。
https://developer.mbed.org/forum/bugs-suggestions/topic/27426/?page=1#comment-52739
mbed.bld で指定されているビルドが古いということなので、上記ページにあるように変更してみます。
[vagrant@localhost BLE_iBeacon]$ cat mbed.bld https://mbed.org/users/mbed_official/code/mbed/builds/abea610beb85 ↓ [vagrant@localhost BLE_iBeacon]$ cat mbed.bld https://mbed.org/users/mbed_official/code/mbed/builds/4eea097334d6
そして mbed deploy で更新します。
[vagrant@localhost BLE_iBeacon]$ mbed deploy [mbed] Updating library "BLE_API" to rev #65474dc93927 [mbed] Updating library "mbed" to rev #4eea097334d6 [mbed] Downloading library build "4eea097334d6" (might take a minute) [mbed] Unpacking library build "4eea097334d6" in "/vagrant/BLE_iBeacon/mbed" [mbed] Updating library "nRF51822" to rev #c90ae1400bf2 [mbed] Updating library "shields/TARGET_ST_BLUENRG" to rev #fa98703ece8e [mbed] Updating the mbed 2.0 SDK tools...
そしてコンパイル。
[vagrant@localhost BLE_iBeacon]$ mbed compile Building project BLE_iBeacon (RBLAB_BLENANO, GCC_ARM) Scan: . Scan: mbed Scan: env Compile [ 1.8%]: BLE.cpp 〜〜〜中略〜〜〜 Compile [100.0%]: nRF5xn.cpp [Warning] nRF5xGap.h@223,93: 'void mbed::Ticker::attach_us(T*, M, timestamp_t) [with T = nRF5xGap; M = void (nRF5xGap::*)(); timestamp_t = long unsigned int]' is deprecated (declared at ./mbed/4eea097334d6/drivers/Ticker.h:121): The attach_us function does not support cv-qualifiers. Replaced by attach_us(callback(obj, method), t). [since mbed-os-5.1] [-Wdeprecated-declarations] Link: BLE_iBeacon Elf2Bin: BLE_iBeacon +-----------+-------+-------+------+ | Module | .text | .data | .bss | +-----------+-------+-------+------+ | Fill | 111 | 3 | 28 | | Misc | 32560 | 141 | 1124 | | Subtotals | 32671 | 144 | 1152 | +-----------+-------+-------+------+ Allocated Heap: 2800 bytes Allocated Stack: 2048 bytes Total Static RAM memory (data + bss): 1296 bytes Total RAM memory (data + bss + heap + stack): 6144 bytes Total Flash memory (text + data + misc): 32815 bytes Image: ./BUILD/RBLAB_BLENANO/GCC_ARM/BLE_iBeacon.hex
成功して hex ファイルが作成されました。ちなみにこの時点での最新のビルドは a330f0fddbec でした。
https://developer.mbed.org/users/mbed_official/code/mbed//builds/a330f0fddbec
ですがこのビルドを指定すると下記のようなエラーになってしまったので、 4eea097334d6
で実行しています。
[vagrant@localhost BLE_iBeacon]$ mbed compile Building project BLE_iBeacon (RBLAB_BLENANO, GCC_ARM) Scan: . Scan: mbed Scan: env Compile [ 1.8%]: BLE.cpp [Warning] toolchain.h@23,2: #warning toolchain.h has been replaced by mbed_toolchain.h, please update to mbed_toolchain.h [since mbed-os-5.3] [-Wcpp] Compile [ 3.6%]: BLEInstanceBase.cpp Compile [ 5.5%]: DiscoveredCharacteristic.cpp Compile [ 7.3%]: GapScanningParams.cpp Compile [ 9.1%]: DFUService.cpp Compile [ 10.9%]: UARTService.cpp Compile [ 12.7%]: URIBeaconConfigService.cpp Compile [ 14.5%]: main.cpp [Fatal Error] iBeacon.h@19,26: core_cmInstr.h: No such file or directory [ERROR] In file included from ./main.cpp:18:0: ./BLE_API/ble/services/iBeacon.h:19:26: fatal error: core_cmInstr.h: No such file or directory #include "core_cmInstr.h" ^ compilation terminated. [mbed] ERROR: "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python" returned error code 1. [mbed] ERROR: Command "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python -u /vagrant/BLE_iBeacon/.temp/tools/make.py -t GCC_ARM -m RBLAB_BLENANO --source . --build ./BUILD/RBLAB_BLENANO/GCC_ARM" in "/vagrant/BLE_iBeacon" ---
それではBLE Nano に hex ファイルをコピーして動作確認してみます。
[vagrant@localhost BLE_iBeacon]$ mbed detect [mbed] Detected RBLAB_BLENANO, port /dev/ttyACM0, mounted /run/media/vagrant/DAPLINK [mbed] Supported toolchains for RBLAB_BLENANO +--------+-----------+-----------+-----+---------+-----+ | Target | mbed OS 2 | mbed OS 5 | ARM | GCC_ARM | IAR | +--------+-----------+-----------+-----+---------+-----+ +--------+-----------+-----------+-----+---------+-----+ Supported targets: 0 [vagrant@localhost BLE_iBeacon]$ [vagrant@localhost BLE_iBeacon]$ cp ./BUILD/RBLAB_BLENANO/GCC_ARM/BLE_iBeacon.hex /run/media/vagrant/DAPLINK/ [vagrant@localhost BLE_iBeacon]$
BLE Nano 側での作業は一旦終了で、 iPhone 側で iBeacon を検知するために下記アプリをインストールします。
アプリを起動して下記のような内容で検知対象UUIDを登録します。
すると下記のように iBeacon が検知されます。
サンプルコードの内容
サンプルコードの内容について自分が理解した範囲で説明してみます。ちなみに mbed の BLE API のリファレンスは下記にあります。
まずは main() の中で ble.init()
で BLE Controller を初期化します。 BLE API を使う場合にはまずこれをやります。引数には初期化完了時に実行されるコールバックメソッドを指定します。
ble.init(bleInitComplete);
コールバックメソッドには BLE::InitializationCompleteCallbackContex のインスタンスが渡されます。
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
コールバックメソッドの中で iBeacon クラスを new することで BLE Advertisement を iBeacon として送るためのセットアップが行われます。uuid, majorNumber, minorNumber, txPower はとりあえずサンプルコードのデフォルト値をそのまま使います。
const uint8_t uuid[] = {0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, 0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61}; uint16_t majorNumber = 1122; uint16_t minorNumber = 3344; uint16_t txPower = 0xC8; iBeacon *ibeacon = new iBeacon(ble, uuid, majorNumber, minorNumber, txPower);
Advertisement の送信間隔を設定し、送信を開始します。
ble.gap().setAdvertisingInterval(1000); /* 1000ms. */ ble.gap().startAdvertising();
main() の中では BLE Controller の初期化が終わるまで空のループを回して待ちます。
while (!ble.hasInitialized()) { /* spin loop */ }
初期化が終わったら無限ループを回し、イベントが発生するまで待機を繰り返します。
while (true) { ble.waitForEvent(); // allows or low power operation }
まとめ
とりあえず iBeacon の Advertisement を送信するだけならサンプルコードをそのまま使ってとても簡単に実行することができてしまいました。次は色々なセンサー類と組み合わせるためにも、Peripheralとして動作できるように挑戦してみたいと思います。
また、環境面では mbed cli だと Web IDE と比べてやはりライブラリとの依存関係などで悩まされることは多くなりそうです。すぐ解決できる場合は良いのですが、困った時はあまり悩まず Web IDE に切り替えて使うのが良さそうです。