BLEでXIAO-ESP32S3と通信

2023.9.19 Coskx Lab  

1 はじめに

BLEでAndroidがXIAO-ESP32S3と通信
AndroidデバイスとXIAO-ESP32S3間でBluetooth Low Energy(BLE)通信(Bluetooth Classicではありません)を行います。
BLEコネクション通信では,Central(or Master or Observer)とPeripheral(or Slave or Broadcaster)の間で通信が行われます。
ここでは,AndroidをCentral,XIAO-ESP32S3をPeripheralとして使うことにします。

ESP32 Bluetooth4.2でのBLE通信のペアリングでは,セキュリティについては設定をしなくてもよかったのですが,ESP32S3 Bluetooth5.0ではペアリングにおいてセキュリティの設定が必要になったようです。
PINをESP32S3のプログラムに設定しておき,Androidとペアリングする際におなじPINを入力することでPairingが完了します。
(ここで使用するAndroidスマホのアプリ"Serial Bluetooth Terminal"は起動の度に,通信相手を探して接続するようになっているため,ペアリングを必要としていません。

eripheralは常時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通信相手(ここではXIAO-ESP32S3)のスキャン&接続・接続解除
(2)AndroidデバイスのBluetooth機能がOFFになっていたらONにしてもらう手続き
作成に当たっては
https://www.hiramine.com/programming/blecommunicator/index.html
https://android.keicode.com/basics/bluetooth-list-paired-devices.php
を参考にし,引用させていただきました。感謝申し上げます。

2 使用環境

3 プログラムソース

sourcefiles2.zipのダウンロード
フォルダ"ESP32-S3"以外はすべてAndroidアプリの構成ファイルです。
作成中の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が含まれています。
次のようなスクリーンをイメージしています。
上から順に次のように配置されています。
 ・接続ボタン(ペアリングされたデバイスから選ぶ)
 ・接続ボタン(BLEデバイスをスキャンして,対象のデバイスを選ぶ)
 ・接続解除ボタン
 ・通信相手のデバイス名(まだ決まっていない場合は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は合言葉なので,同じものをXIAO-ESP32S3側のプログラムに記述する必要があります。

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

3.5 activity_device_list.xml

BLEデバイスをスキャンして,見つかったデバイスを選択してもらうActivityのスクリーンを定義しています。
 ・スキャン中であることを示すグルグル
 ・スキャン開始
 ・スキャン停止
 ・スキャンをやめて戻る
のボタン
見つかったデバイスのリスト
を表示します。

3.6 listitem_device.xml

見つかったデバイスのリストを構成する部品

3.7 DeviceListActivity.java

BLEデバイスをスキャンして,見つかったデバイスを選択してもらうActivity
スキャンは10秒間行うと止まるようになっているので,再びスキャン開始ボタンで再開できるようにしています。
見つかったデバイスをタップすると,選択したことになって,このActivityは終了します。


4 XIAO-ESP32S3側のプログラム

先にダウンロードしたsourcefiles2.zip中にESP32-BLE-prtyp.inoが含まれています。
arduinoを作業フォルダ中にフォルダESP32-BLE-prtypを作りその中ESP32-BLE-prtyp.inoを入れてください。
ボード設定は"XIAO_ESP32S3"です。
その後は最新のarduino IDEのコンパイルシステムでコンパイル&書き込みをしてください。
プログラム中の「BLEDevice::init("ESP32-BLE-prtyp");」でXIAO-ESP32S3側のデバイス名を決定しています。

5 接続実行の様子

接続の概要は次のようになります。
(1)XIAO_ESP32S3を起動,XIAO_ESP32S3が「BLE Start.」とシリアルモニタに表示します。
(2)AndroidアプリでScan and Connectボタン押します。
(3)XIAO_ESP32S3が見つかったらタップして選択します。
(4)XIAO_ESP32S3がconnectを表示し,双方向の通信が可能となります。
(5)AndroidアプリでDisconnectボタンを押します。
(6)XIAO_ESP32S3がdisconnectを表示します。
(7)XIAO_ESP32S3は3秒後に再起動し,「BLE Start.」と表示します。Androidアプリは次の接続ができます。

5.1 起動時

XIAO-ESP32S3の電源を入れると,"BLE started." とシリアルモニタに表示します。
XIAO-ESP32S3は起動していますが,まだ接続されていません。

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


  デバイス選択

スキャン開始後,ペアリングされているデバイス名の一覧が表示されるので,ESP32-BLE-prtypを選択します。

5.3 接続直後

BLE通信ではこの段階からXIAO-ESP32S3がサーバ,Androidがクライアントになります。
接続が開始した時点で,Android側はBluetoothLEWorkのBluetoothGattCallback mGattcallback中の
onServicesDiscovered()で次の2つを行います。
一つ目は
 readCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE);
です。これはクライアントAndroidがサーバXIAO-ESP32S3に対しread要求を発しています。
サーバXIAO-ESP32S3ではこの要求に反応して,onRead(BLECharacteristic *pCharacteristic)(クライアントトAndroidがreadしたい要求が来たよ)が起動し,
シリアルモニタに「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()
は"サーバXIAO-ESP32S3がNotificationを発行してきたらこれに気づき,文字列を受け取る" という設定を有効にするという意味です。これはこの後の通信のため必要です。


  通信の様子

5.4 Androidからの送信

Androidから「This is Android.」を送信しました。
サーバXIAO-ESP32S3ではこの受信に反応して,onWrite(BLECharacteristic *pCharacteristic)(クライアントAndroidがwriteしたよメッセージが来た)が起動し,
シリアルモニタに「This is Android.」を表示しています。

5.5 XIAO-ESP32S3からの送信

XIAO-ESP32S3側でシリアルモニタのテキストボックスから「This is ESP32S3」を入力したので, それがAndroidに送られました。
XIAO-ESP32S3側では送信と同時にnotify()でNotificationを送って, メッセージを送信したことをクライアントAndroidに知らせます。
Androidが「This is ESP32S3」を受信し,表示しています。
もし,AndroidがNotificationの受信設定をしていない, あるいはXIAO-ESP32S3側がNotificationを送信しなかったら, Androidはメッセージを受け取れません。

5.6 Androidでdisconnect

Andoroid側でDisconnectをタップしたので,切断され,XIAO-ESP32S3側も"Disconnected"を表示しています。

6 XIAO-ESP32S3の発熱

本論とは無関係ですが,XIAO ESP32S3がBLE通信するときは,発熱が大きく問題が生ずる可能性があるため,放熱対策をしたほうが安全と思われます。
Bluetooth4.0(LE)は通信範囲100mと言われています。(手元で試したら直性の見通せる場所で40m) Bluetooth5.0(LE)は通信範囲400mと言われています。(手元で試したら直性の見通せる場所で130mまでしか試せなかったのですが,問題なく通信できました) 通信距離が2倍になると4倍の電力を消費するため,この発熱はBluetooth5.0(LE)になったことに起因していると考えられます。

7 まとめ

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