Bluetooth Low Energy (BLE) は消費電力の低い通信方式ですが、直接インターネットにはデーターを送れません。BLEで送信したセンサーデーターをクラウドに送るためには、BLEで送信されたデーターを受信してTCP/IPでクラウドに送るBLEゲートウェイが必要です。

M5StackでBLE環境センサー端末を作る」ではBluetooth Low Energy (BLE) でデーターを発信するセンサー端末を作りました。今回はこの端末からデーターを受けて、クラウドにデーターを送るBLEゲートウェイを作ります。

ESP32を使ったBLEゲートウェイは「BLE環境センサー・ゲートウェイ(ESP32編)」をご覧ください。

全体の構成

センサー端末はM5StackにBME280を接続し、温度、湿度、気圧を測定し、BLEで発信するものです。M5Stackに搭載されているものと同じマイコンESP32を搭載するESPr Developer 32でも動作します。センサー端末の開発は「M5StackでBLE環境センサー端末を作る」をご覧ください。

BLEゲートウェイはBLE通信ができて、インターネットにも接続できるものが必要です。今回はRaspberry Piを使います。

BLEデバイスの動作

BLE通信にはコネクトモードとブロードキャストモードがあります。

コネクトモードでは、センサー端末などのペリフェラルが自らの存在を発信(アドバタイズ)します。セントラルと呼ばれるBLEゲートウェイはアドバタイジングパケットをスキャンし、自分が必要とするペリフェラルを見つけ、ペリフェラルに接続(コネクト)してデーターの送受信をおこないます。

ブロードキャストモードでは、ペリフェラルがセンサーデーターなどをアドバタイジングパケットに載せて送り、セントラルがそれを受信します。

コネクトモードは双方向通信ですので、ペリフェラルからデーターを取得するだけでなく、セントラル側からパラメーター設定をおこなうといった場合はコネクトモードが必要です。ただし、ペリフェラルはセントラル側からのコネクトを待つ必要があるため、一般にはDeep Sleepできず、電力消費を抑えにくくなります。

ブロードキャストモードは単方向です。ペリフェラルからデーターを取得するだけなら、ブロードキャストモードが適しています。しかもブロードキャストモードは、ペリフェラルがデーターを送りたいときだけアドバタイズし、あとはDeep Sleepすることができるので、ペリフェラル側の電力消費を低く抑えられます。

コネクトモード、ブロードキャストモード、それぞれで動作するセンサー端末を開発したので、BLEゲートウェイもそれぞれのモードの物を作ります。

ハードウェア、OS、Python

BLEゲートウェイのハードウェアは「Raspberry Pi Zero WH」を使いました。65mm x 30mm と非常に小さいサイズですが、Wi-FiとBLE通信機能があり、RAMも512MBあります。BLEゲートウェイを動かすには十分な性能です。

OSは「Raspbian GNU/Linux 9.4 (stretch)」を使いました。OSのセットアップは「Raspberry Pi Zero(W)のセットアップ」などをご覧ください。

Pythonはpyenvを導入し、Pythonは3.6.5をインストールしました。pyenvとPythonのインストールは「RaspberryPiにpyenvを導入」などをご覧ください。

Raspberry Pi Zero WHへのPythonのインストールは非常に時間がかかります。ネットワーク環境にもよりますが、私の環境では1時間6分かかりました。途中、不安になりますが、気長にお待ちください。

BLEライブラリー

PythonのBLEライブラリーにはいくつかのものがあります。「PythonからBLEを制御するライブラリの調査」に比較レポートがあります。今回はこの中からPython3系で使えてドキュメントがしっかりしている「bluepy」を使いました。ライブラリーを選択する上で、ドキュメントの充実は重要です。APIや関数インタフェースの仕様、使い方、事例などが豊富にあるとプログラミングもとても楽になります。

bluepyとAmbientライブラリーのインストール

Raspberry Piへのbluepyのインストールは次のようにおこないます。「pi$」と書いてある部分はRaspberry Piでのコマンド実行です。

pi$ sudo apt-get install libglib2.0-dev
pi$ pip install bluepy

これはpyenvを使っている場合ですが、使っていない場合は次のようにします。

pi$ sudo pip3 install bluepy

PythonでAmbientにデーターを送信するライブラリーがあるので、それも次のようにインストールします。

pi$ pip install git+https://github.com/AmbientDataInc/ambient-python-lib.git

ブロードキャストモードのBLEゲートウェイ

ブロードキャストモードでは、センサー端末がセンサーデーターをアドバタイジングパケットの中のアドバタイジングデーターという領域に載せて発信します。アドバタイジングデーターは次のような構造の最大31バイトのデーターです。

BLE環境センサーでは下図のようにアドバタイジングデーターをセットしています。

ブロードキャストモードのBLEゲートウェイプログラムは比較的単純です。センサー端末が送るアドバタイジングデーターをスキャンして見つけ、アドバタイジングデーターからセンサー値を取り出し、Ambientに送ります。

bluepyでは、Scannerクラスのインスタンスを作り、scan()メソッドを呼ぶとスキャンが始まります。withDelegate()メソッドでdelegate関数を指定すると、スキャンでBLEデバイスが見つかった時にcallback関数として呼ばれます。delegate関数にはDefaultDelegateクラスが用意してあり、このクラスのサブクラスを作り、handleDiscovery()メソッドをオーバーライドすることで、独自のcallback関数を定義します。

handleDiscovery()の引数のscanEntryはscanEntryクラスです。getScanData()メソッドで(Ad Type、その説明、Ad Typeに対する値)のタプルが得られます。

スキャンすると、BLE電波が届く範囲にあって、その時アドバタイズしている複数のBLE端末からのアドバタイジングデーターが得られます。その中で説明(desc)が’Manufacturer’で値の先頭(value[0:4])のカンパニーIDが0xffffのデーターがBLE環境センサーのアドバタイジングデーターです。そのデーターを見つけて、データーから温度、湿度、気圧を取り出し、Ambientに送ります。

プログラム全体はGithubに置きました。

EnvSensorBleGw/src/gw_RPi/env2ambientBS.py

BLEデバイスにアクセスするのにroot権限が必要なので、プログラムは次のように起動します。

pi$ sudo python env2ambientBS.py

このままだとRaspberry Piからログアウトすると、プログラムが終了してしまいます。Raspberry Piからログアウトしても、BLEゲートウェイプログラムが終了しないようにするには、次のように起動します。

pi$ sudo nohup python env2ambientBS.py < /dev/null &

センサー端末とBLEゲートウェイを動かし、データーを送信したAmbientのチャネルページを見ると、送信した温度、湿度、気圧データーが確認できます。

コネクトモードのBLEゲートウェイ

コネクトモードでは、センサー端末はサービスとキャラクタリスティクスを生成して、アドバタイズを始め、コネクトを待ちます。

BLEゲートウェイ側では、センサー端末が送るアドバタイジングデーターをスキャンして、対象の端末を見つけたら、端末と通信するスレッドを起動します。

スキャンデーターを見つけた時のcallback関数は次のようになります。

端末と通信する部分のプログラムは次のようになります。EnvSensorクラスはペリフェラルと通信するPeripheralクラスとThreadクラスを継承します。スレッドが起動されると、ペリフェラルに接続(connect)し、UUIDを指定してキャラクタリスティクスを取得し、キャラクタリスティクスのデーターを読むことでセンサー値を取得し、Ambientに送ります。

今回のプログラムは1台のセンサー端末と通信しますが、複数台の端末と通信するような拡張を考え、端末と通信する部分はスレッドにしました。

getCharacteristics()とread()はセンサー端末と通信をおこなうので、通信に失敗する場合があります。実際のプログラムでは例外処理をしています。

センサー値を取得したら次の取得まで接続を切って(disconnect)待つ方法と、接続したまま待つ方法があります。接続を切る場合、センサー端末は通常アドバタイズを再開します。

通信パラメーターの設定によりますが、接続したまま待つほうが、接続を切ってアドバタイズするよりもセンサー端末の消費電力を低く抑えられます。そこで、接続したままにして、何らかの理由で接続が切れたときだけ再接続するようにします。

プログラム全体はGithubに置きました。

EnvSensorBleGw/src/gw_RPi/env2ambientCS.py