BLE Uart通信 クラスで便利に (XIAO ESP32S3)

2025.7.12 Coskx Lab  

1 はじめに

ESP32でBLE Uart通信を行いますが,BLE Uart通信に関する記述をクラス化し,初期化,送信,受信の3つだけ自分で記述するようにします。 ここではAndroidとの通信を例として載せましたが,iOS機器やWindowsPC相手の通信も可能です。 BLE UART Profileを使って通信します。(Bluetooth Classicではありません。)


BLEコネクション通信では,Central(or Master or Observer)とPeripheral(or Slave or Broadcaster)の間で通信が行われます。
ここでは,AndroidをCentral,XIAO-ESP32S3をPeripheralとして使うことにします。
(PeripheralはServer,CentralはClientと説明されることもあります。)

なお,ここで使用するAndroidの2つのアプリSerial Bluetooth Terminal","Bluefruit Connect"はアプリ起動の度に,通信相手を探して接続するようになっているため,ペアリングを必要としていません。

2 使用環境

3 準備

3.1 ArduinoIDE

(1)Arduino IDEのメニューバーから[ファイルFile] -> [環境設定(Preferences)]
Additional board manager URLsに
https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json
および
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
を設定してOK
(2)ツール[Tools] > ボード[Board] > Boards Manager
検索boxに「ESP」を入力すると複数のボード候補が出てくるので
esp32 by Espressif Systems
をinstall
(3)ツール[Tools] > ボード[Board]でESP32を探すとXIAO ESP32S3が見つかるので,これを選ぶ。

3.2 XIAO ESP32S3

XIAO ESP32S3の説明書を読んでプログラムの転送方法を確認します。
(USBケーブルでつないで書き込むだけです。)

4 スケッチコード(サーバプログラム)

プログラムでは,初期化して,通信先と接続が完了します。
通信相手の機器から文字列を受け取り,文字列に応じて,現在時刻(起動してからmilli秒)を1秒ごとに送信します。
送信するときは,関数transferString()を使います。
受信するときは,こちらから関数を呼び出すのではなく,受信時にやるべきことを記述した関数を作り,その関数名をクラスオブジェクトに事前に教えておきます。
受信時にやるべきことを記述した関数は,void callbackRXstring(String str)としました。(別の名前でもOKです)
DEVICENAME "UART ESP32S3"
は自分の名前です。通信相手のAndroidスマホなどから見つけてもらう名前です。初期化の時点で決定します。

//BLE_uart_DEMO.ino//
//XIAO-ESP32S3

#include <Handy_BLE_Uart.h>

#define DEVICENAME "Xiao-eps32S3"

Handy_BLE_Uart bleuart;

void setup() {
  Serial.begin(115200);
  // Create the BLE Device
  bleuart.setCallbackFunc(callbackRXstring);
    //受信時に呼び出される関数の登録
  bleuart.begin(DEVICENAME, 123456, true);
  //bleuart.begin(DEVICENAME, -1, true);
    //初期化
    //自分のデバイス名登録,PINの登録,受信文字列のエコーバックの有無
    //(PINに-1を与えるとPIN不使用になる)
  Serial.println("** start advertising");
  while (!bleuart.checkDeviceStatus()) {
    delay(100);
  }
  Serial.println("connected");
  delay(1000);
  bleuart.transferString("send me 'go' or 'stop'");
}

bool requested = false;

void loop() {
  static int count = 0;
  if (requested) {
    long currenttime = millis();
    bleuart.transferString(String(currenttime));
  }
  delay(1000);
}

void callbackRXstring(String str) {
  Serial.println("RX_Value = " + str);
  if (str.indexOf("go") == 0) requested = true;
  else if (str.indexOf("stop") == 0) requested = false;
}

クラスの定義は次の2つのファイル「Handy_BLE_Uart.h」と「Handy_BLE_Uart.cpp」です。上記プログラムと同じフォルダに入れてください。

//Handy_BLE_Uart.h//

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <Wire.h>

#ifndef Handy_BLE_Uart_h
#define Handy_BLE_Uart_h

class Handy_BLE_Uart : public BLECharacteristicCallbacks, public BLEServerCallbacks {
  public:
    void setCallbackFunc(void (*func)(String));
          //Registers the function name to be called when a string is received.
          //Incoming strings can be received via this registered function.
    void begin(String devicename, int pin, bool enableechoing);
          //devicename:     Set a device name for this device.
          //pin:            Set a PIN(Personal Identification Number)
          //                of about 6 digits for Gluetooth pairing.
          //                If negative values, the PIN is not used. 
          //enableechoing:  Send an echo back the incoming string 
          //                to the central.
    bool checkDeviceStatus();
          //Checks whether BLE communication is possible.
          //Returns true when communication is possible.
    void transferString(String str);
          //Sends a string.
  private:
    void onWrite(BLECharacteristic *pCharacteristic) override;
    void onConnect(BLEServer* pServer) override;
    void onDisconnect(BLEServer* pServer) override;
    BLEServer *pServer = NULL;
    bool echoing = false;
    bool Rxgot = false;
    String Strgot;
    bool deviceConnected = false;
    BLECharacteristic *pTxCharacteristic;
    void (*callbackfunc)(String);
};

#endif

//Handy_BLE_Uart.cpp//

#include <Handy_BLE_Uart.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

void Handy_BLE_Uart::onConnect(BLEServer* pServer) {
  deviceConnected = true;
  Serial.println("** device connected");
};

void Handy_BLE_Uart::onDisconnect(BLEServer* pServer) {
  deviceConnected = false;
  Serial.println("** device disconnected");
  delay(500); // give the bluetooth stack the chance to get things ready
  pServer->startAdvertising(); // restart advertising
};

void Handy_BLE_Uart::onWrite(BLECharacteristic *pCharacteristic) {
  String rxValue = pCharacteristic->getValue();
  if (rxValue.length() > 0) {
    rxValue.trim();
    if (Serial) {
      //Display on serial monitor for debug
      Serial.print("rxVal: ");
      Serial.println(rxValue);
    }
    if (echoing) {
      //Reply as is
      transferString(rxValue);
    }
    callbackfunc(rxValue);
  }
}

void Handy_BLE_Uart::setCallbackFunc(void (*func)(String)) {
  callbackfunc = func;
}

void Handy_BLE_Uart::begin(String devicename, int pin, bool enableechoing) {

  // Create the BLE Device
  echoing = enableechoing;
  BLEDevice::init(devicename); //BLE Device Name scaned and found by clients

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  //pServer->setCallbacks(new MyServerCallbacks());
  pServer->setCallbacks(this);

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
                    CHARACTERISTIC_UUID_TX,
                    BLECharacteristic::PROPERTY_NOTIFY
                  );
                      
  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
                    CHARACTERISTIC_UUID_RX,
                    BLECharacteristic::PROPERTY_WRITE
                  );

  //pRxCharacteristic->setCallbacks(new MyCallbacks());
  pRxCharacteristic->setCallbacks(this);

  // Start the service
  pService->start();

  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("start advertising");
  Serial.println("Waiting a client connection to notify...");

  //The ESP32S3 bluetooth 5.0 requires security settings.
  //Without it, an error will occur when trying to pair with other devices.
  //Using a 6-digit PIN as the authentication method seems to work.
  //This PIN allows the device to be paired with an Client device.
  //Client device users will be prompted to key in a 6-digit PIN, '123456'.
  BLESecurity *pSecurity = new BLESecurity();
  if (0<=pin) {
    pSecurity->setStaticPIN(pin);
  } else {
    //Setting ESP_LE_AUTH_REQ_SC_ONLY instead of the PIN setting eliminates the need for PIN input during pairing.
    pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY);
  }
}

bool Handy_BLE_Uart::checkDeviceStatus() {
  return deviceConnected;
}

void Handy_BLE_Uart::transferString(String str) {
      pTxCharacteristic->setValue(str + "\n");
      pTxCharacteristic->notify();
      delay(10);
}

5 AndroidスマホアプリとXIAO ESP32S3のペアリング

◎このペアリング作業は不要です。
XIAO ESP32S3を起動後,Androidスマホの設定からBluetoothの設定に移ります。
新しいデバイスとペア設定に進むと,XIAO ESP32S3のデバイス名として設定した"UART ESP32S3"が見つかると思います。
そこでPINを要求されるので,XIAO ESP32S3のプログラム中で設定したPIN "123456"を入力するとpairingが完了します。

6 Androidスマホアプリ"Serial Bluetooth Terminal"の準備

Android playから,"Serial Bluetooth Terminal"をダウンロードしてインストールします。

  "Serial Bluetooth Terminal"のアイコン

最初の起動で,メニュー等の探検をします。
〇左上三本線メニューでは
settings→terminal→Show timestampをONにしておくとよいでしょう。
settings→terminal→Buffer sizeは表示内容を保存する作業メモリのサイズです。できるだけ大きくとるのが良いと思います。(200kBでは100文字のデータ約2000行分です。場合によっては無制限もあり)
settings→Receiveは,NLを設定してください。
settings→Send→Local echoはONが標準です。自分が送信した文字列も表示されます。
settings→Misc.→Save....は表示内容(LOG)保存先フォルダの設定です。
〇右上3点メニューには
Data→Saveがあって,表示内容を保存の時使います。

7 XiaoESP32S3と"Serial Bluetooth Terminal"の通信

先にXiaoESP32S3に電源を与え,立ち上げておきます。

その後,"Serial Bluetooth Terminal"で 左上三本線メニューでDevices→BluetoothLE→SCAN で付近のBLEデバイスを見つけて表示してくれます。
ここでは,XiaoESP32S3のプログラムの
   #define DEVICENAME "UART ESP32S3"
でつけた名前"UART ESP32S3"が見つかるのでこれをタップします。

  Serial Bluetooth Terminalデバイス選択画面

"Serial Bluetooth Terminal"から"start"を送信すると,1秒ごとに時刻データが送信されてきます。
"Serial Bluetooth Terminal"から"stop"を送信すると,データ送信が止まります。
画面上の受信データは,右上3点メニューからData→Saveでファイル保存することができます。

"Serial Bluetooth Terminal"から"start"や"stop"を送信するときは,画面下部のテキストボックスに入力して送信ボタンをタップするか,予めマクロボタンに"start"や"stop"の文字列を登録(マクロボタンのタップ&ホールドで登録出来る)しておいて,それらのボタンをタップます。
次の通信画面では,予めマクロボタンに"start"や"stop"の文字列が登録されています。


    Serial Bluetooth Terminal通信画面

8 通信距離

AndroidスマホをCentralとして,XIAO ESP32S3に付属の紙のようなアンテナをつけた状態で通信距離を測定しました。
見通しの良い直線で130mまでの通信が確認できました。
測定場所の都合でそれ以上距離を伸ばせなかったですが,通信限界はもう少し先になると思います。
Bluetooth4.0(LE)は通信範囲100mと言われています。(手元で試したら直線の見通せる場所で50mでした。)
Bluetooth5.0(LE)は通信範囲400mと言われています。

10 発熱に関して

通信時には指先で触っていられないほどESP32S3が高温になっていたので気になったので長時間通信を行いました。
AndroidスマホをCentralとして,XIAO ESP32S3に付属の紙のようなアンテナをつけた状態で通信を行いました。
通信は1秒ごとに行われ,XIAO ESP32S3からAndroidスマホに温度[℃],気圧[hPa],標高[m]を送信していました。

30℃を超える気温の中で,連続送信していたところ,1時間程度で通信が途絶えてしまいました。
再度送信を開始したところ,今度は3時間ほどで通信が途絶えてしまいました。
気温が30℃を下回ったところで,再再度送信を開始したところ,8時間の送信で異常は起きませんでした。
周囲温度が高いときは,異常終了する可能性があることがわかりました。

一般に通信距離が2倍になると4倍の電力を消費するため,この発熱はBluetooth5.0(LE)になったことに起因していると考えられます。

11 まとめ

ESP32S3でBLEuart通信する舞台裏作業をクラス化し,メイン側プログラムの負担を減らしました。
Androidスマホからの指令によって文字列をXIAO ESP32S3が送るプログラム例を示しました。



この記事ではAndroidスマホのアプリ"Serial Bluetooth Terminal"を使いました。
他にも"Bluefruit Connect"というがあり,同じような使い方が出来ます。 → 付録
iPhoneにもアプリ"Bluefruit Connect"がありAndroid版"Bluefruit Connect"と同じ使い方ができます。
WindowsPCにも同様なアプリ"BLE_serial_terminal"があり,同様の使い方ができます。
接続作業などは以下を参考にしてください。   iOSデバイス版の解説
  WindowsPC版の解説


付録 "Bluefruit Connect"

アプリ"Bluefruit Connect"にはAndroid版とiOS版があり,同じ操作で使うことができて便利です。

A1 Androidデバイスアプリ"Bluefruit Connect"の準備

Android playから,"Bluefruit Connect"をダウンロードしてインストールします。

  "Bluefruit Connect"のアイコン

A2 XiaoESP32S3と"Bluefruit Connect"の通信

先にXiaoESP32S3に電源を与え,立ち上げておきます。

その後,"Bluefruit Connect"を立ち上げると最初にSelect Deviceの画面になります。
ここでは,XiaoESP32S3のプログラムの
   #define DEVICENAME "UART ESP32S3"
でつけた名前"UART ESP32S3"が見つかるのでconnectボタンをタップします。

  Bluefruit Connectデバイス選択画面

次にModulesと書かれたモジュール選択画面になりますので,UARTを選択します。

  Bluefruit Connectモジュール選択画面

UARTの白い画面になったら,"Bluefruit Connect"から"start"を送信すると,1秒ごとにデータが送信されてきます。
受信した3つの数値は温度[℃],気圧[hPa],標高[m]を表しています。
"Bluefruit Connect"から"stop"を送信すると,データ送信が止まります。
"Bluefruit Connect"から"start"や"stop"を送信するときは,画面下部のテキストボックスに入力してSENDボタンをタップします。


    Bluefruit Connect通信画面

画面上の送受信データの表示形式は,右上メニューからDisplay Modeで切り替えられます。
(time stamp付きの表示もできます。)

画面上の送受信データのファイルへの保存は,右上メニューからExportで出来ます。
保存形式をcsvにするとtime stamp付きでの保存になります。