Arduino ESP32からBluetoothシリアル(SPP)デバイスに接続する

Arduino ESP32は、Bluetoothシリアル(SPP)のスレーブデバイスになるサンプルは付属しており、PCからはBluetoothシリアルで容易に接続することができました。
(例えば arduino-esp32/SerialToSerialBT.ino at master · espressif/arduino-esp32 · GitHub など。)

少し前に、Bluetoothシリアルのマスター側になり、他のデバイスに接続するためのライブラリやサンプルも用意されたようです。
arduino-esp32/SerialToSerialBTM.ino at master · espressif/arduino-esp32 · GitHub

これを使って、ESP32からBluetooth対応のUSB電圧/電流センサーに接続して情報を取得してみました。使ったUSBセンサーはこれ。

UM25C

arduino-esp32 は、Arduino IDEのボードマネージャから1.0.4にアップデートしたものを使っています。

ESP32からデバイスBluetooth SPPで接続する

まずは、setupでデバイスに接続を行ってみます。
接続は、Bluetoothバイスの名前、もしくはアドレスを直接指定して行います。
アドレスの方が高速に接続できるようです。

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

String name = "UM25C";
uint8_t address[6]  = {0x00, 0x11, 0x55, 0x33, 0x22, 0x77};  // アドレス直接指定も可能
char *pin = "1234"; //<- 接続用PIN。標準以外の場合は指定が必要

void setup() {
  Serial.begin(115200);

  // 初期化。名前とtrueを引数として渡すとマスターになる
  SerialBT.begin("ESP32test", true); 

  Serial.println(String("Connecting to ") + name + " ...");
  // 標準以外のPINが必要な場合はここで指定する
  // SerialBT.setPin(pin);

  // 名前を指定して接続する場合。スキャンのためか、10〜15秒程度かかる
  bool connected = SerialBT.connect(name);
  // アドレスを指定して接続する場合。こちらは2〜3秒で高速に接続できる
  //bool connected = SerialBT.connect(address);
  
  if (connected) {
    Serial.println("Connected Succesfully!");
  } else {
    while (!SerialBT.connected(10000)) {
      Serial.println("Failed to connect. retry ...");
    }
  }
}

Bluetooth SPPデバイスと通信する

このUSBセンサーは、0xf0 という 1 byte のコマンドを送ると、130バイトほどのバイナリデータを返してくるようになっています。
1秒に1回 ESP32 からコマンドを送り、受け取ったデータをシリアルでPCに返してみます。

byte buffer[130];
int pos = 0;

void loop() {
  static unsigned long last_wr = 0;
  if (millis() - last_wr > 1000) {
    SerialBT.write(0xf0);
    last_wr = millis();
  }
  while (SerialBT.available()) {
    buffer[pos++] = SerialBT.read();
    if (pos >= sizeof(buffer)) {
      handle_data();
      pos = 0;
    }
  }
  delay(20);
}

void handle_data() {
  String data = 
    String("{\"V\":") + String((buffer[2] * 256 + buffer[3]) / 1000.0, 3) +
    ",\"A\":" + String((buffer[4] * 256 + buffer[5]) / 10000.0, 4) +
    ",\"W\":" + String((buffer[8] * 256 + buffer[9]) / 1000.0, 3) +
    ",\"C\":" + String(buffer[10] * 256 + buffer[11]) +
    ",\"mAh\":" + String(buffer[16] * 65536L + buffer[17] * 4096L + buffer[18] * 256 + buffer[19]) +
    ",\"mWh\":" + String(buffer[20] * 65536L + buffer[21] * 4096L + buffer[22] * 256 + buffer[23]) +
    ",\"ohm\":" + String((buffer[124] * 256 + buffer[125]) / 10.0, 1) +
    "}\n";
  Serial.print(data);
}

これで、シリアルモニターにUSBセンサーの値がJSONっぽい形式で返ってきました。

{"V":4.697,"A":1.2048,"W":5.658,"C":19,"mAh":646,"mWh":3049,"ohm":3.8}
{"V":4.700,"A":1.3035,"W":6.126,"C":19,"mAh":647,"mWh":3051,"ohm":3.6}

現状の問題点

どういうわけか、デバイスと接続できなくても20〜30秒程度経過すると connected = true が返ってきてしまいました。
また、途中でデバイス側のリセットなどで切断された場合、仮に SerialBT.disconnect(); SerialBT.connect(); を実行し直しても再接続がうまくいきません。
仕方なく、一定時間応答がない場合、 ESP.restart() でESP32全体を再起動するという処理を入れると、うまく再接続されています。