ESP32でBLEを受信してwi-fiでデータを送信

nRF52840を使ったLowPowerデバイスの開発 その1」の続きです。nRF52840でアドバタイズしたデータを受信してwi-fiでサーバーに送信します。「ESP8266で体温計(ウェアラブルIoT体温計)」でやっていたと同じ事ですが身体に装着する装置を小型にし、電流が必要なwi-fiへの接続を外付けの装置にしています。

ESP32はBLEとwi-fiを両方使えるので今回の用途にはぴったりです。デベロッパーキットをブレッドボードに挿してLEDを付けた簡単電子工作です。LEDは送信確認で光らせているので無しでも出来ます。

nRF52840の体温計を身体に装着し、家のwi-fiが繋がる場所にこのESP32を置きます。BLEの届く範囲で連続して体温を記録することが出来ます。数メートルの範囲内で届いているようです。装置を2Fに置いて1Fでも受信できていました。BLEのスペックとしてはもっと距離が出ても良さそうですが、身体にアンテナが近いのでちょっと難しいようです。

開発にはArduino IDEを使います。

Arduinoスケッチ

/*
  2021.05.31 BLE+wifiデータ送信テスト
  ESP32 devkit
  BLEを受信してデータを受け取った場合にwi-fiに接続してデータをサーバに送信する。
  測定間隔はBroadcaster側で決まる
  broadCaster はnRF52
*/

#include <Arduino.h>
#include <BLEDevice.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>

WiFiMulti wifiMulti;

/* 基本属性定義  */
#define SPI_SPEED   115200          // SPI通信速度

/* スキャン制御用 */
#define DEVICE_NAME "nRF52"  // 対象デバイス名
const int scanning_time = 3;        // スキャン時間(秒)
BLEScan* pBLEScan;                  // Scanオブジェクトへのポインター

/* 受信データ構造体 */
struct tmpData {
  float   temperature;            // 温度
};

/* LEDピン */
const int ledPin = 12;              // LEDの接続ピン

void setup() {
  Serial.begin(SPI_SPEED);
  pinMode(ledPin, OUTPUT);          // GPIO設定:LED
  digitalWrite(ledPin, LOW);

  BLEDevice::init("");                        // BLEデバイスを作成する
  Serial.println("Client application start...");
  pBLEScan = BLEDevice::getScan();            // Scanオブジェクトを取得して、
  pBLEScan->setActiveScan(false);             // パッシブスキャンに設定する

  //wifi setup
  wifiMulti.addAP("wi-fiのSSID-1", "wi-fiのパスワード-1");
  wifiMulti.addAP("wi-fiのSSID-2", "wi-fiのパスワード-2");
  wifiMulti.addAP("wi-fiのSSID-3", "wi-fiのパスワード-3");

  WiFi.disconnect(true);
  WiFi.mode(WIFI_STA);
}

void loop() {
  struct tmpData td;

  // 所定時間だけスキャンして、見つかったデバイス数を取得する
  BLEScanResults foundDevices = pBLEScan->start(scanning_time);
  int count = foundDevices.getCount();

  for (int i = 0; i < count; i++) {     // 受信したアドバタイズデータを調べて
    BLEAdvertisedDevice dev = foundDevices.getDevice(i);

    std::string mfData = dev.getManufacturerData();

    //受信したデータがnRF52からなのかチェックする。その1。データ長をチェック
    if (mfData.length() == 5) {
      std::string data = dev.getManufacturerData();

      // 受信データを調べるnRF52から送信されたデータなのか確認する
      if (data[0] == 0xf3 && data[1] == 0 && data[2] == 1) {
        //温度を求めて送信へ
        td.temperature = (float)(data[4] << 8 | data[3]) / 100.00;
        displayData(&td);

        //受け取った値が体温らしい温度かどうかチェックする。範囲内なら送信
        if (td.temperature > 10.00 && td.temperature < 50.00) {
          digitalWrite(ledPin, HIGH);
          setndDataToServer(&td);//データを送信する
          digitalWrite(ledPin, LOW);
        }
      }
    }
  }
  delay(1000);
}



/* 受信データを表示する */
void displayData(struct tmpData* td) {
  Serial.print("  Temperature: ");        Serial.println(td->temperature);
}

/* wi-fiに接続してデータをサーバに送信 */
void setndDataToServer(struct tmpData* td) {
  Serial.println("setndDataToServer");
  WiFi.disconnect();

  if ((wifiMulti.run() == WL_CONNECTED)) {

    HTTPClient http;

    //体温をStringとして変数に入れる
    String tempString = String(td->temperature);

    Serial.print("tempString: ");   Serial.println(tempString);

    //送信するテキスト
    String urlString = "http://データ受信用のPHPのURL/tempData.php?temp=" + tempString;
    http.begin(urlString); //HTTP

    //Serial.print("[HTTP] GET...\n");
    // start connection and send HTTP header
    int httpCode = http.GET();

    // httpCode will be negative on error
    if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      //Serial.printf("[HTTP] GET... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        //Serial.println(payload);
      }
    } else {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end();
  } else {
    Serial.println("wi-fi connection fail");
  }
}

wifiMultiを使って複数のwi-fiを登録できます。強度の強いwi-fiに自動的に接続してくれます。自宅と職場とか複数のSSIDを登録して持ち運んで使うとかの用途が考えられます。

BLEでアドバタイズされたデータを受信してデータ長が合致するもので、中身のデータを見てnRF52から送信されたものならサーバーにデータをwi-fiで送信します。本当はUUIDやデバイスネームで識別したいのですが、デバイスネームを取り出せなかったのでデータ長とデータの中身で識別する方法にしてます。

送信されたデータを受信するPHPは以下の通りです。適当なサーバに置いてください。上記のサンプルではhttps:のサーバには繋がらないのでhttp:のサーバーの方が簡単です。

<?php

date_default_timezone_set('Asia/Tokyo');//タイムゾーンの設定

//文字コードの設定
mb_internal_encoding("UTF-8");

$mv = $_GET["temp"];


$fhandle = fopen("./log.csv", "a+");//読み込み/書き出し用でオープンします。 ファイルポインタをファイルの終端

//現在時刻の取得
fwrite($fhandle,date("Y/m/d G:i"));
fwrite($fhandle,",");
fwrite($fhandle,$mv);
fwrite($fhandle, "\n");


//レスポンス
header("Content-type: text/html");
echo "data=";
echo $mv;
echo "C\n";

受信したデータをlog.csvに日付と時刻を追加して追記していくだけです。

データをwebブラウザでグラフ表示するには「ESP8266+CO2モニタ」に書いてあるhtmlとphpが使えますので、そちらを参照してください。