ESP32-WROOM-32EのBlutoothSerialでモジュール間通信

組み込みエンジニア

こんにちは、ENGかぴです。

ESP32-WROOM-32EはWiFi通信とBluetooth通信ができるのが特徴です。親機と子機に分けたESP-WROOM-32EにBluetoothSerialライブラリを実装し子機のセンサー情報を親機に通信して動作確認を行いました。

BluetoothSerialライブラリを使ってBluetooth通信を行う方法は下記記事にまとめています。スマホをESP-WROOM-32Eの親機に置き換えて動作確認を行います。

ESP32-WROOM-32EでBluetoothSerialを実装する

ESP32-WROOM-32Eの開発環境はArduino IDEを使用しています。親機と子機はESP32-WROOM-32E開発ボード(秋月電子)を使用しています。センサーにはAE-BME280(秋月電子)を使用しています。

ESP32-WROOM-32Eを使って動作確認したことをまとめています。

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

BluetoothSerialライブラリを実装する

動作確認の全体構成
動作確認の全体構成

親機はマスターとして動作し子機に対して1秒毎に接続要求を行います。子機はスレーブとして動作し5秒毎にセンサーの情報を取得して親機にBluetooth通信を行います。接続が確立していると親機は子機から受信したデータをシリアルモニターに表示します。

ライブラリの初期化

#include "BluetoothSerial.h"

String BT_PARENT = "ESP32-BME280-PARENT";
String BT_CHILD = "ESP32-BME280-CHILD";

BluetoothSerial SerialBT; //インスタンス化

void setup() {
  SerialBT.enableSSP(); //ペアリングの際のセキュリティ
  if( digitalRead(PIN_DI_1) == 0){ //子機
    SerialBT.begin(BT_CHILD);
    bme280.begin();
  }
  else{ //親機
    SerialBT.begin(BT_PARENT,true);
    ParentFlg = true;
    connectchk = SerialBT.connect(BT_CHILD);
  }
}

BluetoothSerialライブラリを使用するためにBluetoothSerial.hをインクルードします。BluetoothSerialクラスの型の変数を宣言します。例ではSerialBTという変数名でインスタンス化しています。

enableSSP()関数はペアリング時のセキュリティで接続時に認証・暗号化を行う設定です。

begin()関数を使ってBlutoothSerialの初期化を行います。第1引数にBluetoothデバイスの名称を指定します。第2引数にマスターとして使用する場合はtrueを指定します。スレーブの場合は特に指定する必要はありません。

親機は子機に接続要求(ペアリング)するマスターとして動作させるためtrueを指定しています。子機のデバイス名に「ESP32-BME280-CHILD」、親機のデバイス名に「ESP32-BME280-PARENT」を指定しています。

親機はconnect()関数で接続要求(ペアリング)を行います。引数に接続するデバイス名を指定します。子機と接続するため子機のデバイス名を指定しています。

テックジム|ゼロからはじめるPython入門講座の申込

接続の確認

if( timBLEConchk == TIME_UP ){
  timBLEConchk = CON_CHK_MAX;
  connectchk = SerialBT.connect(); //接続:true 切断:false
}

親機と子機の接続が切断した場合は再接続しない限り通信ができません。connect()関数を使用すると最初にconnect()関数で指定した引数の条件で再接続することができます。connect()関数は接続が確立しているとtrueをセットし、切断しているとfalseをセットします。

定期的にconnect()関数をコールすることで接続状態を管理し再接続させることができます。例ではタイマーを使って1秒毎に接続状態を確認しています。

データの読み込み(受信)

if( SerialBT.available()){
  Serial.write(SerialBT.read());
}

BluetoothSerialクラスの型の変数においてクラスのメンバー関数を使ってデータを受信します。available()関数でBluetooth通信でデータが受信していることを確認します。データがあればread()関数で受信したデータを取得します。例ではBluetooth通信で取得したデータをシリアルモニターに表示しています。

データの書き込み(送信)

SerialBT.print("Temperature = ");
SerialBT.print(bme280data[0]);
SerialBT.println(" °C");

Bluetooth通信でデータを送信する場合はprint()関数write()関数を使用します。引数に文字列を指定しますが、print()関数を使用するとテキストデータを送信しwrite()関数はバイナリデータを送信する使い分けがあります。println()を使用すると文字列を送信した後に改行コードを付加します。

スポンサーリンク

動作確認

親機と子機の接続を確立(ペアリング)し、子機が送信したBME280モジュールのデータを親機のシリアルモニターに表示します。

動作確認用の回路図
動作確認用の回路図

子機のESP32-WROOM-32EとBME280モジュールの配線例を示しています。回路図の番号はESP32-WROOM-32Eの左上を1ピンとした時反時計回りにピンを数えた場合の番号としています。ピン番号横の()内の番号はシルク印刷されているピンの名称です。

AE-BME280モジュールをI2C(Wire)で使用します。AE-BME280モジュールにはプルアップ抵抗が付属されているのでWire用のプルアップ抵抗は不要です。

マイコン内蔵のプルアップ抵抗を使用しても問題ありませんが抵抗分が大きいほど通信波形が鈍り通信ができなくなることがあるためAE-BME280モジュールのプルアップ抵抗を使用することをお勧めします。

25ピンをGNDにすると子機として動作します。オープンにすると親機として動作します。親機はESP32-WROOM-32EモジュールをUSBに接続してシリアルモニターに表示するのみなので配線例はありません。

データを比較のために親機をシリアルモニターで表示し、子機をTera Termでモニターします。

Tera Termによる子機の表示
Tera Termによる子機の表示
シリアルモニターによる親機の表示
シリアルモニターによる親機の表示

電源をONすると子機は5秒毎に温湿度と気圧のデータを取得して表示していることが確認できました。親機は子機と接続が確立すると無線受信したデータをシリアルモニターに表示していることが確認できました。

子機のデータが親機のシリアルモニターに同一のデータが表示されているためBlutoothSerialによる通信がうまくいっていることが分かります。

子機がTera Term上で表示しているデータがシリアルモニターに表示されないことがあり数回に一度はデータの取りこぼしが発生していることも分かりました。

頻度がそれほど多くなかったため通信環境によってデータを取りこぼす可能性があると割り切ったシステム構成が必要だと言えます。

スポンサーリンク

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include "BluetoothSerial.h"
#include <Adafruit_BME280.h>

#define TIM_BME280_WAIT 500
#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10
#define PIN_DI_1 25
#define CON_CHK_MAX 100 

String BT_PARENT = "ESP32-BME280-PARENT";
String BT_CHILD = "ESP32-BME280-CHILD";
BluetoothSerial SerialBT;
Adafruit_BME280 bme280;
float bme280data[3];
int16_t timBme280Set;
int16_t timBLEConchk;
uint32_t timer= millis();
bool  ParentFlg;
bool  connectchk;

/* プロトタイプ宣言 */
void mainTimer(void);
void Bme280Get(void);

// the setup routine runs once when you press reset:
void setup() {

  Serial.begin(115200);
  pinMode(PIN_DI_1, INPUT_PULLUP);
  delay(10);

  SerialBT.enableSSP();
  if( digitalRead(PIN_DI_1) == 0){
    SerialBT.begin(BT_CHILD);
    bme280.begin();
  }
  else{
    SerialBT.begin(BT_PARENT,true);
    ParentFlg = true;
    connectchk = SerialBT.connect(BT_CHILD);
  }
}

// the loop routine runs over and over again forever:
void loop() {

  mainTimer();

  if(ParentFlg){
    if( timBLEConchk == TIME_UP ){
      timBLEConchk = CON_CHK_MAX;
      connectchk = SerialBT.connect(); //setup()で接続した条件で再接続
    }

    if(connectchk){
      if( SerialBT.available()){
        Serial.write(SerialBT.read());
      }
    }
  }
  else{
    if( timBme280Set == TIME_UP ){
      timBme280Set = TIM_BME280_WAIT;
      Bme280Get();
    }
  }
}

/* タイマ管理 */
void mainTimer(void){

  if( millis() - timer >= BASE_CNT ){
    timer = millis();

    if( timBme280Set > TIME_UP ){
      --timBme280Set;
    }

    if( timBLEConchk > TIME_UP ){
      --timBLEConchk;
    }
  }
}

/* BME280のデータ取得 */
void Bme280Get(void){

  Serial.print("Temperature = ");
  bme280data[0] = bme280.readTemperature();
  Serial.print(bme280data[0]);
  Serial.println(" °C");

  SerialBT.print("Temperature = ");
  SerialBT.print(bme280data[0]);
  SerialBT.println(" °C");

  Serial.print("Pressure = ");
  bme280data[1] = bme280.readPressure() / 100.0F;
  Serial.print(bme280data[1]);
  Serial.println(" hPa");

  SerialBT.print("Pressure = ");
  SerialBT.print(bme280data[1]);
  SerialBT.println(" hPa");

  Serial.print("Humidity = ");
  bme280data[2] = bme280.readHumidity();
  Serial.print(bme280data[2]);
  Serial.println(" %");
  Serial.println();

  SerialBT.print("Humidity = ");
  SerialBT.print(bme280data[2]);
  SerialBT.println(" %");
  SerialBT.println();
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました