RasPi4のBLEで周囲のデバイス検知

Posted by: otsuka

RaspberryPi4 搭載の BLE(Bluetooth Low Energy) で周囲にある BLE デバイスを検知してみます。

BLE ではペリフェラル(周辺機器になる側)が自分の情報を知らせるため、自分の機器情報が入ったアドバタイズフレームを送信しています。このアドバタイズの情報を拾うため、セントラル(周辺機器と接続して要求を出す側)はスキャンを実行します。

そこで、RaspberryPi4 を BLE のセントラルにしてスキャンをしてみます。

linux の Bluetooth プロトコルスタック Bluez の CUI 設定・操作用ユーティリティーコマンドで

$ hcitool lescan

したり、 

$ bluetoothctl scan on

したりすることで周囲の BLE デバイスを見ることができますが、これらのコマンドの出力はログに保存したり加工したりするのには向いていないので、Linux BLE の Python interface である
bluepy を利用します。

https://github.com/IanHarvey/bluepy


RaspberryPi4 の動作環境は 2021年9月8日時点で最新の RaspberryPi OS lite May 7th 2021 版です。

pi@raspberrypi:~ $ cat /etc/rpi-issue
Raspberry Pi reference 2021-05-07
Generated using pi-gen, https://github.com/RPi-Distro/pi-gen, dcfd74d7d1fa293065ac6d565711e9ff891fe2b8, stage2

イメージを microSD に書き込んで起動後、以下の設定だけ行っています。

  • ssh と Serial Port を有効に
  • locale を en_US.UTF8 に
  • localtime を Tokyo に
  • WLAN Country を JP に

最新の状態に更新

$ sudo apt update && apt upgrade

完了後の環境は以下のようになります。

pi@raspberrypi:~ $ uname -a
Linux raspberrypi 5.10.60-v7l+ #1449 SMP Wed Aug 25 15:00:44 BST 2021 armv7l GNU/Linux
pi@raspberrypi:~ $ hciconfig -a
hci0: Type: Primary Bus: UART
BD Address: DC:A6:32:17:69:B5 ACL MTU: 1021:8 SCO MTU: 64:1
UP RUNNING
RX bytes:2402 acl:0 sco:0 events:105 errors:0
TX bytes:2610 acl:0 sco:0 commands:105 errors:0
Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH SNIFF
Link mode: SLAVE ACCEPT
Name: 'raspberrypi'
Class: 0x000000
Service Classes: Unspecified
Device Class: Miscellaneous,
HCI Version: 5.0 (0x9) Revision: 0x156
LMP Version: 5.0 (0x9) Subversion: 0x6119
Manufacturer: Cypress Semiconductor Corporation (305)

bluepy のインストール手順に従ってまず必要な deb パッケージをインストールします。
(依存関係を満たすため python3-dev をはじめとした関連のパッケージが40個ほど入ります。)

$ sudo apt install python3-pip libglib2.0-dev

pip で bluepy をインストールします。

$ sudo pip3 install bluepy

bluepy には詳細なドキュメントがありますので、このドキュメントの scanner クラスの sample コード
The Scanner class
https://github.com/IanHarvey/bluepy/blob/master/docs/scanner.rst
を Python3 向けに修正して実行してみます。

scan.py

from bluepy.btle import Scanner, DefaultDelegate

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleDiscovery(self, dev, isNewDev, isNewData):
        if isNewDev:
            print("Discovered device", dev.addr)
        elif isNewData:
            print("Received new data from", dev.addr)

scanner = Scanner().withDelegate(ScanDelegate())
devices = scanner.scan(10.0)

for dev in devices:
    print("Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi))
    for (adtype, desc, value) in dev.getScanData():
        print("  %s = %s" % (desc, value))

BLE インタフェースは実行に root 権が必要なので以下のように sudo 付きで scanpy を実行すると、
・10秒スキャンを行い、
・スキャンによって新しいデバイスが見つかるか発見済みのデバイスから新たなアドバタイズを受け取るごとに表示し、
・最後に10秒間のスキャンで得られた周囲のデバイスのアドバタイズフレームの中の情報をデバイスごとに表示します。

pi@raspberrypi:~ $ sudo python3 scan.py
Discovered device 70:97:5f:37:7f:c9
Discovered device 74:b3:4f:8b:3b:6c
Discovered device 74:cb:e0:50:8e:74
Discovered device 5e:39:49:2c:88:d8
Discovered device 68:52:ec:2b:2d:ba
Discovered device 78:7a:91:b8:3a:21
Discovered device 7b:80:2c:40:57:b6
Discovered device 51:a0:dd:5f:b0:2e
Discovered device 98:7b:f3:60:62:9c
Discovered device 64:54:fd:f2:69:0e
Discovered device 09:5d:29:55:07:c8
Discovered device 29:cf:17:78:d4:f4
Discovered device 70:56:81:c2:5e:f6
Discovered device 67:e1:2c:eb:b2:44
Discovered device 6a:99:d3:df:06:0d
Discovered device 21:9b:4b:dc:5e:61
Discovered device 3e:ea:33:2c:ec:43
Discovered device 42:76:22:d6:b6:d5
Discovered device 56:ac:a2:45:52:93
Discovered device 72:6d:97:5e:af:45
Discovered device 63:59:a2:09:ff:7d
Discovered device 0c:96:e6:0f:08:d8
Discovered device 45:e7:eb:10:95:4e
Discovered device 6e:fe:ff:f4:e2:31
Device 70:97:5f:37:7f:c9 (random), RSSI=-62 dB
  Flags = 1a
  Tx Power = 07
  Manufacturer = 4c0010064b1ed7e54b94
Device 74:b3:4f:8b:3b:6c (random), RSSI=-83 dB
  Flags = 1a
  Tx Power = 18
  Manufacturer = 4c0010052f98eb26e7
Device 74:cb:e0:50:8e:74 (random), RSSI=-69 dB
  Flags = 06
  Manufacturer = 4c000c0e0084cd7bf6e59963e66ad97a119810054a1c1b1993
Device 5e:39:49:2c:88:d8 (random), RSSI=-83 dB
  Flags = 1a
  Tx Power = 0c
  Manufacturer = 4c0010054a1ca25ef4
Device 68:52:ec:2b:2d:ba (random), RSSI=-69 dB
  Flags = 06
  Manufacturer = 4c000c0e004f75485c720987cb205cb9dd221005001c0a5ff1
Device 78:7a:91:b8:3a:21 (random), RSSI=-73 dB
  Complete 16b Services = 0000fd6f-0000-1000-8000-00805f9b34fb
  16b Service Data = 6ffd218baea0921f3bc34ffa997944882786c7bdbf0d
Device 7b:80:2c:40:57:b6 (random), RSSI=-74 dB
  Flags = 06
  Manufacturer = 4c000c0e00ff080537d6734c5233bb079b13
Device 51:a0:dd:5f:b0:2e (random), RSSI=-75 dB
  Flags = 1a
  Tx Power = 08
  Manufacturer = 4c001005611c87a844
Device 98:7b:f3:60:62:9c (public), RSSI=-53 dB
  Flags = 06
  Incomplete 128b Services = 1bc5ffa0-0200-62ab-e411-f254e005dbd4
  Manufacturer = 900c1858
  Complete Local Name = Mooshimeter V.1
  0x12 = 0600a000
  Tx Power = 04
Device 64:54:fd:f2:69:0e (random), RSSI=-75 dB
  Flags = 1a
  Tx Power = 0c
  Manufacturer = 4c001006061e6c05061f
Device 09:5d:29:55:07:c8 (random), RSSI=-71 dB
  Flags = 1a
  Complete 16b Services = 0000fd6f-0000-1000-8000-00805f9b34fb
  16b Service Data = 6ffd27b6f680653f6008c2424034996032c34eea7e30
Device 29:cf:17:78:d4:f4 (random), RSSI=-71 dB
  Flags = 1a
  Complete 16b Services = 0000fd6f-0000-1000-8000-00805f9b34fb
  16b Service Data = 6ffdc0e6ad2a55bf44ab7a1f84563e91817a66b437d8
Device 70:56:81:c2:5e:f6 (public), RSSI=-74 dB
  Flags = 06
  Manufacturer = 4c001005441c8e9ac7
Device 67:e1:2c:eb:b2:44 (random), RSSI=-90 dB
  Flags = 1a
  Tx Power = 05
  Manufacturer = 4c0010060c1e680e9252
Device 6a:99:d3:df:06:0d (random), RSSI=-87 dB
  Manufacturer = 06000109200289dbc5c6eeb4e2af65a7b7e4e32b733b47ceaad63cf66c
Device 21:9b:4b:dc:5e:61 (random), RSSI=-94 dB
  Flags = 1a
  Complete 16b Services = 0000fd6f-0000-1000-8000-00805f9b34fb
  16b Service Data = 6ffd782da8cba14adfba59bba02f608f6742d7fa8231
Device 3e:ea:33:2c:ec:43 (random), RSSI=-94 dB
  Flags = 1a
  Manufacturer = 4c00130702f10a309907b5
Device 42:76:22:d6:b6:d5 (random), RSSI=-67 dB
  Flags = 1a
  Tx Power = 18
  Manufacturer = 4c001005229838e6a4
Device 56:ac:a2:45:52:93 (random), RSSI=-92 dB
  Flags = 1a
  Tx Power = 07
  Manufacturer = 4c001006201e5af246cc
Device 72:6d:97:5e:af:45 (random), RSSI=-95 dB
  Flags = 1a
  Tx Power = 0c
  Manufacturer = 4c001007071f57f8c8e618
Device 63:59:a2:09:ff:7d (random), RSSI=-94 dB
  Flags = 1a
  Tx Power = 18
  Manufacturer = 4c0010050a181fa6cb
Device 0c:96:e6:0f:08:d8 (public), RSSI=-94 dB
  Manufacturer = 2d0102000110847b307f4b1d4055a9dc3dab2b838b7c979507aa4742
Device 45:e7:eb:10:95:4e (random), RSSI=-67 dB
  Flags = 1a
  Tx Power = 18
  Manufacturer = 4c0010052498e97a24
Device 6e:fe:ff:f4:e2:31 (random), RSSI=-97 dB
  Flags = 1a
  Tx Power = 07
  Manufacturer = 4c001006701e7a493331