BLEでM5Stackと通信

2023.9.17 2021.5.13 Coskx Lab  

1 はじめに

BLEでAndroidがM5Stackと通信
AndroidデバイスとM5StackGray間でBluetooth Low Energy(BLE)通信(Bluetooth Classicではありません)を行います。
BLEコネクション通信では,Central(or Master or Observer)とPeripheral(or Slave or Broadcaster)の間で通信が行われます。
ここでは,AndroidをCentral,M5StackをPeripheralとして使うことにします。
Central側では毎回通信相手を捜さなくてもよいように,通信前にPairing(Bonding)をしておきます。
Peripheralは常時Centralからの接続を受け付けられるように動作しています。
Centralは,通信相手を1つだけ指定して接続し,通信を始めます。

BLEコネクション通信では,双方が同じ合言葉を設けてお互いに相手を認識して通信します。
合言葉はUUIDで,SERVICEとCHARACTERISTICの2つがあります。
特定の用途では,すでに指定されたUUIDが用いられていますが,プライベートに通信ペアを作るときには, 既存のUUID以外の任意のものを使うことができます。
ここではArduinoのサンプルで使われていたものをそのまま使っていますが, 適当に変更したほうが良いでしょう。
既存のUUID以外のUUIDを生成する方法はいくつかあり,WindowsPCの場合はPowerShellで [Guid]::NewGuid() と入力すると,入力するたびに異なるUUIDを生成してくれます。
こうして得られたUUIDをCentralとPeripheralの両方に設定します。


BLE通信ひな型アプリ(Android側)
BLE通信ひな型アプリでは,EditTextで作成された文字列を送り,受信した文字列をTextViewに表示します。 このアプリでは,アプリ起動前にシステム設定で通信相手とのペアリングが行われていることが前提になります。
通信部はできるだけ裏方のクラスに隠して,文字列送信のメソッドと,文字列受信時のコールバックメソッドを使うようにします。
ただし,次の2つは表に見えるところ(MainActivity)に出します。
(1)Bluetooth通信相手(ここではM5Stack)の接続・接続解除
(2)AndroidデバイスのBluetooth機能がOFFになっていたらONにしてもらう手続き
作成に当たっては
https://www.hiramine.com/programming/blecommunicator/index.html
https://android.keicode.com/basics/bluetooth-list-paired-devices.php
を参考にし,引用させていただきました。感謝申し上げます。

更新2023.9.17
Android12以降でBLEのpermissionの扱いが変わったので更新した。
(M5Stack側のプログラムは変更なし)

2 使用環境

3 プログラムソース

sourcefiles.zipのダウンロード
作成中のpackage名が異なる場合は変更してください。

3.1 AndroidManifest

sourcefiles.zip中にAndroidManifestが含まれています。
AndroidManifast.xmlにおいて,Bluetoothを使うことを宣言しています。
<uses-permission android:name="android.permission.BLUETOOTH"/>
などがこの宣言にあたります。

3.2 Activity_main.xml

sourcefiles.zip中にactivity_main.xmlが含まれています。
次のようなスクリーンをイメージしています。
上から順に次のように配置されています。
 ・接続ボタン,接続解除ボタン
 ・通信相手のデバイス名(まだ決まっていない場合はno device)
 ・送信したい文字列作成のためのEditText(黄色の領域)
 ・送信ボタン,表示文字列クリアボタン
 ・受信文字列表示領域

3.3 MainActivity.java

sourcefiles.zip中にMainActivity.javaが含まれています。
onResume()中でAndroid端末のBluetooth機能の状態を調べ,有効化されていないときは有効化を促します。
onActivityResult()中で,有効化のチェックをしています。
onClick()中でボタンタップへの対応を行っています。
 ・ペアリング済のBLEデバイスを選択して接続
 ・BLEデバイスを切断
 ・文字列送信
 ・文字列消去
を行います。
各作業の結果はBluetoothLEWorkのOverrideされたコールバックメソッド(onBLEConnected()など)となって戻ってきます。
各コールバックメソッドでは必要に応じてボタンの無効化や有効化を行っています。
受信文字列はコールバックメソッドonMessageReceived()で受け取ります。
文字列送信はsendtext()で行っています。

3.4 BluetoothLEWork.java

sourcefiles.zip中にBluetoothLEWork.javaが含まれています。
舞台裏でBluetooth通信を行っているクラスです。スクリーンに対する直接の作業は一切行っていないので,舞台裏に隠れます。
この中に
private final UUID UUID_XXXXXX
が出てきますが,これが,Bluetoothで通信を行うことを示すプロファイル(合言葉のようなもの)になります。
SPP(シリアルポートプロファイル)と呼ばれています。

    private static final UUID UUID_SERVICE_PRIVATE         = UUID.fromString( "4fafc201-1fb5-459e-8fcc-c5c9c331914b" );
    private static final UUID UUID_CHARACTERISTIC_PRIVATE  = UUID.fromString( "beb5483e-36e1-4688-b7f5-ea07361b26a8" );
    private static final UUID UUID_NOTIFY                  = UUID.fromString( "00002902-0000-1000-8000-00805f9b34fb" );

が出てきます。UUID_SERVICE_PRIVATEとUUID_CHARACTERISTIC_PRIVATEは任意な値で良いのですが, UUID UUID_NOTIFYは予約された値なので変更することはできません。
なお,UUID_SERVICE_PRIVATEとUUID_CHARACTERISTIC_PRIVATEは合言葉なので,同じものをM5Stack側のプログラムに記述する必要があります。

    #define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
    #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"


4 M5Stack Gray側のプログラム

先にダウンロードしたsourcefiles.zip中にM5Stackm5stack-ble.inoが含まれています。
arduinoを作業フォルダ中にフォルダm5stack-bleを作りその中m5stack-ble.inoを入れてください。
ボード設定は"M5Stack-Core-ESP32"です。
その後は最新のarduino IDEのコンパイルシステムでコンパイル&書き込みをしてください。
プログラム中の「BLEDevice::init("M5Stack");」でM5Stack Gray側のデバイス名を決定しています。
M5Stack Grayだけで起こることかもしれませんが, M5Stackのプログラムにおいて,USBを通じたSerial通信とM5ライブラリ(M5Stackの画面に表示)を同時に使用するとうまく動作できません。 そのため,Serial通信は初期化も含めて使わないようにしています。

5 接続実行の様子

接続の概要は次のようになります。
(1)M5Stackを起動,M5Stackが「BLE Start.」と表示します。
(2)Androidデバイスでペアリングして,アプリを起動します。
(3)AndroidアプリでConnectボタン押します。
(4)M5Stackがconnectを表示し,双方向の通信が可能となります。
(5)AndroidアプリでDisconnectボタンを押します。
(6)M5Stackがdisconnectを表示します。
(7)M5Stackは3秒後に再起動し,「BLE Start.」と表示します。Androidアプリは次の接続ができます。

5.1 起動時


  起動時の様子画像

M5Stackの電源を入れたところで,Androidデバイスの設定でBluetoothのペアリングをしておきます。(準備)
M5Stackは起動していますが,まだ接続されていません。

5.2 アプリのボタンConnectを押して,接続先を決めます。


  デバイス選択

ボタンConnectをタップすると,ペアリングされているデバイス名の一覧が表示されるので,M5Stackを選択します。
ここに表示されるのはAndroidデバイスとペアリングが完了しているデバイスです。

5.3 接続直後


  接続されました 接続先のデバイス名が表示されています。

BLE通信ではこの段階からM5Stackがサーバ,Androidがクライアントになります。
接続が開始した時点で,Android側はBluetoothLEWorkのBluetoothGattCallback mGattcallback中の
onServicesDiscovered()で次の2つを行います。
一つ目は
 readCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE);
です。これはクライアントAndroidがサーバM5Stackに対しread要求を発しています。
サーバM5Stackではこの要求に反応して,onRead(BLECharacteristic *pCharacteristic)(クライアントトAndroidがreadしたい要求が来たよ)が起動し,
LCDに「read」と表示し,「Hello World!」を送信しています。
Androidには「Hello World!」が受信され,表示されています。
この作業は,接続し通信できることを確認するために行っているもので,本来は不要です。
そのため,android側がreadCharacteristic()で要求しなければ,この動作はなくなり,Androidで「Hello World!」は表示されません。
二つ目の
 new Handler(Looper.getMainLooper()).postDelayed(() -> setCharacteristicNotification( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE, true ), 100); //0.1秒後
は,0.1秒後に
 setCharacteristicNotification( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE, true )
を実行するの意味で,
setCharacteristicNotification()
は"サーバM5StackがNotificationを発行してきたらこれに気づき,文字列を受け取る" という設定を有効にするという意味です。これはこの後の通信のため必要です。

5.4 Androidからの送信


  Androidからの送信です。

Androidから「Hello, M5Stack」を送信しました。
サーバM5Stackではこの受信に反応して,onWrite(BLECharacteristic *pCharacteristic)(クライアントAndroidがwriteしたよメッセージが来た)が起動し,
LCDに「Hello, M5Stack」を表示しています。

5.5 M5Stackからの送信


  M5Stackからの送信です。

M5Stack側でボタンBを押したので,文字列「Button B Pressed!」が送られました。
M5Stack側では送信と同時にnotify()でNotificationを送って, メッセージを送信したことをクライアントAndroidに知らせます。
Androidが「Button B Pressed!」を受信し,表示しています。
もし,AndroidがNotificationの受信設定をしていない, あるいはM5Stack側がNotificationを送信しなかったら, Androidはメッセージを受け取れません。

6 まとめ

Bluetooth Low Energy通信(Central)ひな型Android用アプリ(BLE Client)を作成しました。 通信部分だけを再利用可能なクラスにしました。 プログラム中で作成された文字列をPeripheralに送信するアプリ作成が楽になりました。