RasPi4のBLEで検知した周囲のデバイスをDB保存

Posted by: otsuka

RasPi4のBLEで周囲のデバイス検知 の続きです。

bluepy は
Peripheral class
https://ianharvey.github.io/bluepy-doc/peripheral.html
を利用してペリフェラルを操作することができます。
すなわち、BLE センサータグに接続してデータを定期的に取り出したり、BLE ビーコンの uuid などで存在・不在の確認をしたり、といったことができます。
ですが、そのためには予め各デバイスの情報を知っていなければなりません。

そこで今回はペリフェラルの操作は行わず、アドバタイズのデータだけを長時間取得して保存し、グラフで見られるようにすることでデバイスの情報から周囲の情報を得られないか試してみます。

前回の scan.py を修正し、10分間隔で10秒スキャンを継続して行い、スキャンで取得したデバイスの MACアドレスと RSSI の値を時系列データベース influxDB に保存していくよう変更します。

influxDB
https://www.influxdata.com/
は時系列データの扱いに特化したデータベースです。

2021年9月9日時点での最新 version は 2.0.8 ですが、2系の version は 64bit 専用になりましたので 32bit の RaspberryPi OS で使用する場合には1系の最新 1.8.9 をインストールします。

公式サイトのドキュメント
https://docs.influxdata.com/influxdb/v1.8/introduction/install/
を参照してインストールしますが、こちらの debian での例の通りに実行してしまうと
DISTRIB_CODENAME が raspbian になってしまいインストールに失敗しますので、以下のような手順で実行します。

まず influxDB リポジトリの公開鍵を追加してリポジトリを登録し、 

$ curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
OK
$ echo "deb https://repos.influxdata.com/debian buster stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

登録できたら apt コマンドでインストールします。

$ sudo apt update
$ sudo apt install lnfluxdb

systemd service での自動起動の設定は apt でのインストール時に行われています。

install 直後はもう起動できていますが、default の設定では systemd service での起動時間が RasPi 向けとしては少し短く設定されていて再起動後自動起動に失敗しますので、
/usr/lib/influxdb/scripts/influxd-systemd-start.sh
の中の設定値
max_attempts=10

max_attempts=60
に調整しておきます。

インストールできたら正常にデータが保存できるか確認します。

influxDB の CLI/shell である influx を立ち上げ、BLEスキャン結果のデータを入れる database を scandata という名前で作成します。

$ influx
Connected to http://localhost:8086 version 1.8.9
InfluxDB shell version: 1.8.9
> CREATE DATABASE scandata
> SHOW DATABASES
name: databases
name
----
_internal
scandata
> 

データを insert してみます。
influx からは SQL のような InfluxQL で以下のようにデータを登録できます。

> INSERT sensor,macaddr=00:30:b7:8b:da:8f rssi=-94

influxDB は REST API を持っていますので http リクエストでも insert できます。

$ curl -i -XPOST "http://localhost:8086/write?db=testdata" --data-binary "sensor,macaddr=00:AA:bb:00:aa:BB rssi=-90"
HTTP/1.1 204 No Content
Content-Type: application/json
Request-Id: 97eb12f4-122d-11ec-8048-dca6321769b1
X-Influxdb-Build: OSS
X-Influxdb-Version: 1.8.9
X-Request-Id: 97eb12f4-122d-11ec-8048-dca6321769b1
Date: Fri, 10 Sep 2021 11:52:37 GMT

insert したデータを確認してみます。
influx では以下のようになります。

> select * from sensor
name: sensor
time                macaddr           rssi
----                -------           ----
1631274080581163589 00:30:b7:8b:da:8f -94
1631274757323715237 00:AA:bb:00:aa:BB -90

http リクエストでは JSON で結果が返ってきます。

$ curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=testdata" --data-urlencode "q=SELECT * FROM sensor"
{
    "results": [
        {
            "statement_id": 0,
            "series": [
                {
                    "name": "sensor",
                    "columns": [
                        "time",
                        "macaddr",
                        "rssi"
                    ],
                    "values": [
                        [
                            "2021-09-10T11:41:20.581163589Z",
                            "00:30:b7:8b:da:8f",
                            -94
                        ],
                        [
                            "2021-09-10T11:52:37.323715237Z",
                            "00:AA:bb:00:aa:BB",
                            -90
                        ]
                    ]
                }
            ]
        }
    ]
}

bluepy と連携させて取得したデータを influxDB に保存するため、Python の influxDB クライアント
influxdb-python を pip でインストールします。

$ sudo pip install influxdb

以下のような Python コードで json 形式のデータを用意して influxDB クライアントに渡し書き込むことで influxDB にデータを登録することができます。

import datetime
import influxdb

dbclient = influxdb.InfluxDBClient(
    host='localhost',
    port=8086,
    database='scandata'
)

def write_to_influxdb(macaddr, rssi):
    json_data = [
        {
            'measurement': 'sensor',
            'tags': {
                'macaddr': macaddr
            },
            'time': datetime.datetime.utcnow(),
            'fields': {
                'rssi': rssi
            }
        }
    ]
    dbclient.write_points(json_data)

write_to_influxdb("DC:A6:32:17:69:B5", -90)

このような influxDB への書き込みコードと bluepy による BLE スキャンコードを組み合わせ、以下のようなコードを作成して実行します。

10分に1回、10秒間 BLE スキャンを行い、そのスキャンで得られた 周囲の BLE デバイスの MAC アドレスと RSSI (受信信号強度) を influxDB に保存する、を24時間実行します。 

## ble_scan.py

from bluepy import btle
#import pprint
import time
import datetime

import influxdb

dbclient = influxdb.InfluxDBClient(
    host='localhost',
    port=8086,
    database='scandata'
)

def write_to_influxdb(macaddr, rssi):
    json_data = [
        {
            'measurement': 'sensor',
            'tags': {
                'macaddr': macaddr
            },
            'time': datetime.datetime.utcnow(),
            'fields': {
                'rssi': rssi
            }
        }
    ]
    #pprint.pprint(json_data)
    dbclient.write_points(json_data)


scanner = btle.Scanner()

# 24 * (60/10) = 144
for i in range(144):
    devices = scanner.scan(10.0)

    # loop counter
    print(i)

    # scan time
    dt_now = datetime.datetime.now()
    print(dt_now)

    # number of scanned devices
    n = len(devices)
    print(n)

    # devices data lists list
    blist = []


    for dev in devices:
        #print("\nDevice %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi))
        # each device data list
        alist = []
        alist.append(dev.addr)
        alist.append(dev.rssi)
        blist.append(alist)

    # sort list by Device addr
     blist = sorted(blist, reverse=False, key=lambda x: x[0])
     #pprint.pprint(blist)

    # write scan result to influx
    for bl in blist:
        print("Device %s, RSSI=%d dB" % (bl[0], bl[1]))
        write_to_influxdb(bl[0], bl[1])

    print("Sleeping...")
    time.sleep(600)

一スキャンで見つかったデバイスのリストを MAC アドレスでソートしてから influxDB に入れていますが、スキャンで見つかった順に influxDB に保存しても構いません。

実行すると10分ごとに以下のような出力をしながら 24時間実行されます。

0
2021-09-10 19:55:14.981760
23
Device 01:bb:43:e7:87:8d, RSSI=-68 dB
Device 0a:90:34:bb:17:e1, RSSI=-67 dB
Device 35:51:76:a5:4c:f4, RSSI=-66 dB
Device 41:63:0b:03:e5:17, RSSI=-66 dB
Device 41:bf:f9:4c:e7:97, RSSI=-66 dB
Device 44:88:30:77:ad:68, RSSI=-84 dB
Device 49:59:e8:00:2a:88, RSSI=-85 dB
Device 49:7c:f7:1f:92:8e, RSSI=-78 dB
Device 4c:7d:c3:ef:c7:c3, RSSI=-86 dB
Device 5d:6b:5f:5c:43:44, RSSI=-99 dB
Device 5f:ab:8c:68:22:12, RSSI=-77 dB
Device 60:c4:7e:5b:e1:b8, RSSI=-70 dB
Device 6b:d5:68:3d:5d:42, RSSI=-60 dB
Device 6f:80:a2:38:5b:55, RSSI=-77 dB
Device 6f:bc:f9:d9:b5:ed, RSSI=-87 dB
Device 70:56:81:c2:5e:f6, RSSI=-72 dB
Device 76:8e:cc:e4:6c:4f, RSSI=-93 dB
Device 79:b5:6f:2c:21:a9, RSSI=-69 dB
Device 98:7b:f3:60:62:9c, RSSI=-64 dB
Device e0:68:7c:11:44:e1, RSSI=-85 dB
Device e1:71:45:c1:aa:ee, RSSI=-93 dB
Device f9:cc:20:ef:34:ee, RSSI=-67 dB
Device ff:46:23:b9:09:ab, RSSI=-62 dB
Sleeping...
1
2021-09-10 20:05:26.510816
23
Device 03:9b:ad:4d:97:56, RSSI=-73 dB
Device 0a:90:34:bb:17:e1, RSSI=-57 dB
Device 0c:96:e6:0f:08:d8, RSSI=-97 dB
Device 35:51:76:a5:4c:f4, RSSI=-71 dB
Device 39:61:0c:7b:39:3a, RSSI=-67 dB
Device 41:bf:f9:4c:e7:97, RSSI=-74 dB
Device 43:1c:1c:0d:a2:c8, RSSI=-67 dB
Device 4f:61:9f:f7:f5:95, RSSI=-94 dB
Device 57:d6:ac:f4:13:d0, RSSI=-74 dB
Device 64:3c:d9:95:5a:2c, RSSI=-83 dB
Device 6b:d5:68:3d:5d:42, RSSI=-54 dB
Device 6e:0f:e5:48:b1:06, RSSI=-80 dB
Device 6e:ae:b1:3f:6b:57, RSSI=-85 dB
Device 6f:80:a2:38:5b:55, RSSI=-75 dB
Device 6f:bc:f9:d9:b5:ed, RSSI=-65 dB
Device 70:56:81:c2:5e:f6, RSSI=-74 dB
Device 73:4e:ce:09:81:18, RSSI=-71 dB
Device 76:8e:cc:e4:6c:4f, RSSI=-94 dB
Device 98:7b:f3:60:62:9c, RSSI=-62 dB
Device c4:07:4e:1c:7d:08, RSSI=-66 dB
Device e1:71:45:c1:aa:ee, RSSI=-94 dB
Device ea:76:5b:9b:dc:73, RSSI=-76 dB
Device ff:46:23:b9:09:ab, RSSI=-56 dB
Sleeping...

実行終了後 influx で確認すると、以下のようにスキャン結果が保存されています。

$ influx
Connected to http://localhost:8086 version 1.8.9
InfluxDB shell version: 1.8.9
> use scandata
Using database scandata
> select * from sensor;
name: sensor
time                macaddr           rssi
----                -------           ----
1630313799503545000 08:12:00:79:41:6b -74
1630313799690273000 11:c7:a7:8c:7e:0f -71
1630313799710583000 26:f6:0a:8b:17:46 -74
1630313799731214000 30:a2:c2:12:68:8c -81
1630313799750611000 36:ea:b8:d6:0e:74 -59
1630313799774557000 3f:7c:1b:85:b8:4c -83
1630313799790678000 43:13:8d:c7:0e:90 -99
1630313799808237000 44:da:8e:07:c3:eb -79
1630313799827009000 4b:1a:c2:7b:15:67 -79
1630313799845829000 4b:ad:ca:10:57:f3 -81
1630313799874741000 4c:d2:2a:8a:b3:ad -71
1630313799892616000 57:d9:f0:9b:c2:6d -86
1630313799909000000 58:73:c1:09:dc:41 -77
1630313799925467000 60:12:f3:fb:c3:5f -87
1630313799944276000 65:67:6f:58:a1:21 -73
1630313799962325000 6a:75:87:c0:58:e4 -92
1630313799980420000 6a:ea:23:f0:4b:29 -75
1630313799999337000 6c:7c:cb:43:c6:89 -60
1630313800018086000 6e:63:6e:9e:46:5e -95
1630313800036258000 70:36:40:c1:d1:56 -98
1630313800050865000 70:56:81:c2:5e:f6 -84
1630313800067715000 71:6f:e9:fc:3c:78 -79
1630313800086769000 73:5e:95:6a:ee:8c -78
1630313800105073000 74:b7:8f:4a:cf:32 -89
1630313800123674000 75:ab:71:40:b0:13 -71
1630313800142423000 7b:bf:f7:ea:1a:f9 -81
1630313800161935000 7b:ce:91:bc:a8:97 -86
1630313800178694000 7e:8a:88:75:53:08 -68
1630313800195473000 7e:ab:e7:8a:47:3b -101
1630313800211289000 98:7b:f3:60:62:9c -59
1630313830277412000 08:12:00:79:41:6b -74
1630313830300662000 11:c7:a7:8c:7e:0f -71
1630313830320193000 26:f6:0a:8b:17:46 -67
1630313830341978000 30:85:93:2a:f9:10 -97
1630313830364674000 30:a2:c2:12:68:8c -85
1630313830383749000 36:ea:b8:d6:0e:74 -59
1630313830396886000 3f:7c:1b:85:b8:4c -83
1630313830410930000 43:13:8d:c7:0e:90 -100
1630313830427487000 44:5e:fe:20:76:9f -102
1630313830444765000 44:da:8e:07:c3:eb -77
1630313830457828000 45:6b:9e:37:a5:ff -82
1630313830471855000 4b:ad:ca:10:57:f3 -76
1630313830486406000 4c:d2:2a:8a:b3:ad -76
1630313830503484000 57:d9:f0:9b:c2:6d -86
1630313830520226000 58:73:c1:09:dc:41 -78
1630313830534132000 60:12:f3:fb:c3:5f -76
1630313830548664000 65:67:6f:58:a1:21 -73
1630313830564628000 6a:ea:23:f0:4b:29 -85
1630313830581803000 6c:7c:cb:43:c6:89 -60
1630313830595658000 6e:63:6e:9e:46:5e -91
1630313830610706000 70:36:40:c1:d1:56 -96
1630313830626431000 70:56:81:c2:5e:f6 -91
1630313830642914000 71:6f:e9:fc:3c:78 -76
1630313830657184000 73:5e:95:6a:ee:8c -76
1630313830672244000 75:ab:71:40:b0:13 -74
1630313830689080000 79:9f:05:42:ac:c7 -99
1630313830705335000 7b:bf:f7:ea:1a:f9 -87
1630313830721956000 7b:ce:91:bc:a8:97 -89
1630313830742660000 7e:8a:88:75:53:08 -84
1630313830757370000 7e:ab:e7:8a:47:3b -96
1630313830771797000 7f:66:61:23:c1:0b -92
1630313830787536000 98:7b:f3:60:62:9c -65
........................................
以下、大量に続きます。