D-BusからBLE Advertisementを送信する

 今まではRaspberry PiをBLEのCentralとして他のデバイスへの接続などを試していましたが、今回はPeripheralとしてAdvertisementを送信してみました。

 PythonでAdvertisementを送信している例があったので下記サイトを参考にさせてもらいました。

qiita.com

 また、BLEについての下記サイトも参考にさせてもらいました。

http://events.linuxfoundation.org/sites/events/files/slides/Doing%20Bluetooth%20Low%20Energy%20on%20Linux.pdf

インタフェースの確認と設定

 Advertisementを送信するには、 org.bluez.LEAdvertisingManager1RegisterAdvertisement() メソッドを使うようなので、まずはインタフェースを確認してみます。

irb(main):001:0> require 'dbus'
irb(main):002:0> bus = DBus::SystemBus.instance
irb(main):003:0> bluez = bus.service('org.bluez')
irb(main):004:0> adapter = bluez.object('/org/bluez/hci0')
irb(main):005:0> adapter.introspect
〜〜〜ここまでのアウトプットは省略〜〜〜
irb(main):006:0> adapter.interfaces
=> ["org.freedesktop.DBus.Introspectable", "org.bluez.Adapter1", "org.freedesktop.DBus.Properties", "org.bluez.GattManager1", "org.bluez.Media1", "org.bluez.NetworkServer1"]

 該当のインタフェースが存在していない様子。試しに bluetoothctl でも見てみます。

[bluetooth]# advertise on
LEAdvertisingManager not found

 やはりインタフェースがみつからないようです。

 BlueZのドキュメントを確認してみます。

advertising-api.txt\doc - bluez.git - Bluetooth protocol stack for Linux

Service org.bluez
Interface org.bluez.LEAdvertisingManager1 [Experimental]
Object path /org/bluez/{hci0,hci1,…}

 org.bluez.LEAdvertisingManager1 は Experimental となっているようです。

 BlueZのインストール時の内容を確認してみましたが、 --enable-experimental オプションは付けてビルドしてました。

blog.akanumahiroaki.com

 bluetoothd のプロセスを確認してみます。

pi@raspberrypi:~ $ ps aux | grep bluetoothd
root       658  0.0  0.3   4780  3280 ?        Ss   22:23   0:00 /usr/local/libexec/bluetooth/bluetoothd
pi        1122  0.0  0.2   4276  2008 pts/3    S+   22:54   0:00 grep --color=auto bluetoothd

 下記記事を参照したところ、 --experimental オプションを付けて bluetoothd を起動する必要があるようです。

blog.mrgibbs.io

 /etc/systemd/system/bluetooth.target.wants/bluetooth.service を下記のように編集します。

ExecStart=/usr/local/libexec/bluetooth/bluetoothd  
 ↓  
ExecStart=/usr/local/libexec/bluetooth/bluetoothd --experimental

 編集後にRaspberry Piを再起動して、 bluetoothd を再確認します。

pi@raspberrypi:~ $ ps aux | grep bluetoothd
root       717  0.0  0.3   4780  3248 ?        Ss   23:05   0:00 /usr/local/libexec/bluetooth/bluetoothd --experimental
pi         910  0.0  0.1   4276  1812 pts/1    S+   23:09   0:00 grep --color=auto bluetoothd

 --experimental オプション付きで bluetoothd が起動しました。 bluetoothctl で確認してみます。

[bluetooth]# advertise on
Advertising object registered
[bluetooth]# advertise off
Advertising object unregistered
Agent unregistered

 bluetoothctl でも advertise コマンドが使えるようになりました。Rubyからも確認してみます

irb(main):006:0> adapter.interfaces
=> ["org.freedesktop.DBus.Introspectable", "org.bluez.Adapter1", "org.freedesktop.DBus.Properties", "org.bluez.GattManager1", "org.bluez.Media1", "org.bluez.NetworkServer1", "org.bluez.LEAdvertisingManager1"]

 org.bluez.LEAdvertisingManager1 インタフェースが追加されました。

Advertisementの送信(bluetoothctl)

 bluetoothctl でPeripheralとしてAdvertisementを送信してみます。

pi@raspberrypi:~ $ sudo bluetoothctl
[bluetooth]# advertise peripheral
Advertising object registered

 上記実行時のHCIの動作を hcidump で確認すると下記のようになりました。

pi@raspberrypi:~ $ sudo hcidump -i hci0
HCI sniffer - Bluetooth packet analyzer ver 5.23
device: hci0 snap_len: 1500 filter: 0xffffffff
< HCI Command: LE Set Advertising Parameters (0x08|0x0006) plen 15
    min 1280.000ms, max 1280.000ms
    type 0x00 (ADV_IND - Connectable undirected advertising) ownbdaddr 0x00 (Public)
    directbdaddr 0x00 (Public) 00:00:00:00:00:00
    channelmap 0x07 filterpolicy 0x00 (Allow scan from any, connection from any)
> HCI Event: Command Complete (0x0e) plen 4
    LE Set Advertising Parameters (0x08|0x0006) ncmd 1
    status 0x00
< HCI Command: LE Set Advertise Enable (0x08|0x000a) plen 1
> HCI Event: Command Complete (0x0e) plen 4
    LE Set Advertise Enable (0x08|0x000a) ncmd 1
    status 0x00

 PeripheralとしてAdvertisementを送信しているので、 ADV_IND - Connectable undirected advertising となっており、他のデバイスからの接続が可能なタイプのAdvertisementになっていることがわかります。

 ではLightBlueから接続してみます。

f:id:akanuma-hiroaki:20170621074917p:plain:w300 f:id:akanuma-hiroaki:20170621074923p:plain:w300

 デバイスの一覧に raspberrypi が表示され、タップして接続することができました。

 次にBroadcastで送信してみます。

[bluetooth]# advertise broadcast
Advertising object registered

 hcidump での出力は下記の通り。

< HCI Command: LE Set Advertising Data (0x08|0x0008) plen 32
> HCI Event: Command Complete (0x0e) plen 4
    LE Set Advertising Data (0x08|0x0008) ncmd 1
    status 0x00
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6
    bdaddr 32:DC:0C:A9:69:2B
> HCI Event: Command Complete (0x0e) plen 4
    LE Set Random Address (0x08|0x0005) ncmd 1
    status 0x00
< HCI Command: LE Set Advertising Parameters (0x08|0x0006) plen 15
    min 1280.000ms, max 1280.000ms
    type 0x03 (ADV_NONCONN_IND - Non connectable undirected advertising) ownbdaddr 0x01 (Random)
    directbdaddr 0x00 (Public) 00:00:00:00:00:00
    channelmap 0x07 filterpolicy 0x00 (Allow scan from any, connection from any)
> HCI Event: Command Complete (0x0e) plen 4
    LE Set Advertising Parameters (0x08|0x0006) ncmd 1
    status 0x00
< HCI Command: LE Set Advertise Enable (0x08|0x000a) plen 1
> HCI Event: Command Complete (0x0e) plen 4
    LE Set Advertise Enable (0x08|0x000a) ncmd 1
    status 0x00

 今度は ADV_NONCONN_IND - Non connectable undirected advertising となっており、他のデバイスからの接続はできない(Advertisementパケットの参照だけできる)タイプのAdvertisementになっていることがわかります。

 とりあえず bluetoothctl からAdvertisementを送信して他のデバイスから検知することができましたが、Rubyから送信することにはかなり調べたもののまだ成功していないので、今後実現していきたいと思います。