概要

arduino-esp32を使って、ESP-WROOM-32でiBeaconをスキャンすることができた。


背景と目的

以前、esp-idfを使ってiBeaconをスキャンできたが、Arduino用SDKのarduino-esp32では難しかった。しかし、最新版では、BLE_scanというサンプルコードが追加されており、これを使えばiBeaconのスキャンができそうだ。なので、やってみる。


詳細

1.方針

2017/10/14時点でarduino-esp32には、どうやらNeil Kolbanさんという人が親切にも作ってくれたESP32_BLE_Arduinoというサンプルが含まれているようなので、ありがたく使用させていただく。BLE_scanというサンプルコードは、BLEデバイスをスキャンしてアドバタイジングデータを取得できるようなので、iBeaconだけを検出するようにちょっと変更してみる。


2.コーディング

2.1 iBeaconを扱うクラス

まず、iBeaconを扱うための処理をまとめておいたほうがいいと思ったので、BLEAdvertisedDeviceという型の変数を受け取って、iBeaconか判断し、iBeaconであれば、uuid、major、minor、rssiを読み取って保持するクラスを作成。細かい処理はlibraries\BLE\src\BLEAdvertisedDevice.cppあたりを参考にした。

// iBeaconを扱うクラス
class iBeacon
{
private:
  String uuid;
  uint16_t major;
  uint16_t minor;
  int rssi;
public:
  bool createByAdvertisedDevice(BLEAdvertisedDevice advertisedDevice); // BLEAdvertisedDeviceからデータを組み立てる
  String getUUID() { return uuid; }
  uint16_t getMajor() { return major; }
  uint16_t getMinor() { return minor; }
  int getRSSI() { return rssi; }
};

// BLEAdvertisedDeviceからデータを組み立てる
bool iBeacon::createByAdvertisedDevice(BLEAdvertisedDevice advertisedDevice) {

  char work[7];
  
  // 16進データをもらう
  String hexString = (String) BLEUtils::buildHexData(nullptr, (uint8_t*)advertisedDevice.getManufacturerData().data(), advertisedDevice.getManufacturerData().length());
  // iBeaconかチェック
  if (hexString.substring(0, 8).equals("4c000215")) {
    // iBeaconのとき
    uuid =  hexString.substring(8, 16) + "-" + 
            hexString.substring(16, 20) + "-" + 
            hexString.substring(20, 24) + "-" + 
            hexString.substring(24, 28) + "-" + 
            hexString.substring(28, 40);  
    ("0x" + hexString.substring(40, 44)).toCharArray(work, 7);
    major = (uint16_t) atof(work);
    ("0x" + hexString.substring(44, 48)).toCharArray(work, 7);
    minor = (uint16_t) atof(work);
    rssi = advertisedDevice.getRSSI();
    return true;
  } else {
    // iBeaconじゃないとき
    return false;
  }
}
2.2 Arduinoプログラム

次に、サンプルを上記のiBeaconクラスを使って修正。uuid、major、minor、rssiをシリアルに出力する。

#include "BLEDevice.h"
#include "BLEUtils.h"
#include "BLEScan.h"
#include "BLEAdvertisedDevice.h"

int scanTime = 2; //In seconds

/*
ここに、上記のiBeaconクラスを記述
*/

BLEScan* pBLEScan;

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      // アドバタイジングデータを受け取ったとき
      iBeacon ibcn;      
      if (ibcn.createByAdvertisedDevice(advertisedDevice)) {
        char uuid[37];
        ibcn.getUUID().toCharArray(uuid, 37);
        Serial.printf("UUID: %s, Major: %d, Minor: %d, RSSI: %d \n", uuid, ibcn.getMajor(), ibcn.getMinor(), ibcn.getRSSI());
      }
    }
};

void setup() {
  Serial.begin(115200);
  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan(); //create new scan
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
}

void loop() {
  BLEScanResults foundDevices = pBLEScan->start(scanTime);
}

3.動作確認

上記のプログラムを動かしてみたところ、以下のようになった。ちゃんとiBeaconを検出できている。が、フラッシュメモリをかなり消費する。これはもともとのサンプル自体が大きいためだ。なので、メモリ不足に注意が必要。

UUID: 00000000-1ae9-1001-b000-001c4d6465e, Minor: 1, Minor: 4, RSSI: -66

まとめ

arduino-esp32を使って、ESP-WROOM-32でiBeaconをスキャンすることができた。esp-idfを使わないで済む場面がまた1つ増えてよかった。