4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ESP32の周りのBLEデバイスを監視

Last updated at Posted at 2022-09-11

バッファローの「探せるキーホルダー」や、Logitecの「ぶるタグ」、懐かしい「Stick-N-Find」やNTTドコモのProject Linkingの「Pochiru」や「Tomoru」など、あるいは、LINE Beaconなど、常に電源On状態でアドバタイズし続けるBLEデバイスの、稼働状態や出入りを監視します。

BLEアドバタイズやレスポンスのスキャンデータには、デバイスがサポートするサービスUUIDが含まれています。
そこに、以下のUUIDが含まれていたら、生存していることとしてサーバに通知します。

 Stick-N-Find:bec26202-a8d8-4a94-80fc-9ac1de37daa6
 Project Linking:b3b36901-50d3-4044-808d-50835b13a6cd
 Immediate Alert Service:0x1802
 LINE Things:0xfe6f

スキャンするBLE CentralデバイスとしてESP32を使います。
M5StampC3が小さくてちょうど良い感じです。とはいっても、どのESP32でも動作します。

ソースコードもろもろは以下にあります。

poruruba/NearbyBleDeviceScan

BLEデバイスの検出(@ESP32)

BLEデバイスを検出したら、HTTP Postで通知するか、MQTTで通知するか、2種類を用意しておきました。
スキャンと通知の流れは以下のようにしています。

① 30秒BLEアドバタイズをスキャンします。スキャンしたデータにさきほどのUUIDが含まれていた場合にはそのデバイスの情報を記憶しておきます。
② スキャン完了後、検出したBLEデバイスの情報一覧をサーバにプッシュします。
③ 60分経過したら、①に戻ってスキャンを再実施します。

BLEスキャン

arduino/src/main.cpp
  BLEDevice::init("NearbyBleDevice");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
//  pBLEScan->setInterval(1349);
//  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);

  pBLEScan->start(SCAN_DURATION_SEC, myScanCompleteCallback);

  Serial.println("setup finished\n");

MyAdvertisedDeviceCallbacksのコールバック関数に、検出したすべてのBLEデバイスが引数に渡されて呼び出されます。
myScanCompleteCallbackのコールバック関数は、スキャン終了時に呼び出され、サーバにBLEデバイス情報一覧をプッシュするようにします。

検出したデバイスは以下の部分で、サービスUUIDによりフィルタリングしています。

arduino/src/main.cpp
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.println(advertisedDevice.toString().c_str());
    if (advertisedDevice.haveServiceUUID() ){
      uint8_t device_type = DEVICE_TYPE_UNKNOWN;
      if(advertisedDevice.isAdvertisingService(sticknfind_discoverServiceUUID))
        device_type = DEVICE_TYPE_STICKNFIND;
      else if(advertisedDevice.isAdvertisingService(immediate_alert_discoverServiceUUID))
        device_type = DEVICE_TYPE_IMMEDIATE_ALERT;
      else if(advertisedDevice.isAdvertisingService(linking_discoverServiceUUID))
        device_type = DEVICE_TYPE_PROJECT_LINKING;
      else if(advertisedDevice.isAdvertisingService(line_things_discoverServiceUUID))
        device_type = DEVICE_TYPE_LINE_THINGS;
      else
        return;

      BLE_DEVICE_ENTRY entry = { getCurrentTime(), true, device_type,advertisedDevice.getAddressType(),
        advertisedDevice.haveRSSI() ? advertisedDevice.getRSSI(): 0, advertisedDevice.haveName() ? advertisedDevice.getName() : "" };
      device_list[advertisedDevice.getAddress().toString()] = entry;

      Serial.print("*** BLE Advertised Device found: ");
      Serial.println(advertisedDevice.toString().c_str());
    }
  }
};

サーバへプッシュ

サーバにプッシュする際に、BLEデバイス情報一覧はJSON形式にします。

arduino/src/main.cpp
void myScanCompleteCallback(BLEScanResults results)
{
  Serial.println("counting start");

  long ret;

  time_t current_time = getCurrentTime();
  char address[18];
  json_request.clear();
  sprintf(address, "%02x:%02x:%02x:%02x:%02x:%02x", ble_mac_address[0], ble_mac_address[1], ble_mac_address[2], ble_mac_address[3], ble_mac_address[4], ble_mac_address[5]);
  json_request["ble_mac_address"] = address;
  sprintf(address, "%02x:%02x:%02x:%02x:%02x:%02x", wifi_mac_address[0], wifi_mac_address[1], wifi_mac_address[2], wifi_mac_address[3], wifi_mac_address[4], wifi_mac_address[5]);
  json_request["wifi_mac_address"] = address;
  sprintf(address, "%d.%d.%d.%d", wifi_ip_address[0], wifi_ip_address[1], wifi_ip_address[2], wifi_ip_address[3]);
  json_request["wifi_ip_address"] = address;

  int index = 0;
  for (std::unordered_map<std::string, BLE_DEVICE_ENTRY>::iterator iterator = device_list.begin(); iterator != device_list.end(); iterator++){
    std::pair<std::string, BLE_DEVICE_ENTRY> element = *iterator;
    std::string mac_address = element.first;

    if( device_list[mac_address].connected && device_list[mac_address].updated_at < (current_time - EXPIRE_DURATION_SEC) ){
      device_list[mac_address].updated_at = getCurrentTime();
      device_list[mac_address].connected = false;
    }

    BLE_DEVICE_ENTRY entry = element.second;
    json_request["device_list"][index]["mac_address"] = (char*)mac_address.c_str(),
    json_request["device_list"][index]["name"] = (char*)entry.name.c_str(),
    json_request["device_list"][index]["connected"] = entry.connected;
    json_request["device_list"][index]["device_type"] = entry.device_type;
    json_request["device_list"][index]["address_type"] = entry.address_type;
    json_request["device_list"][index]["rssi"] = entry.rssi;
    json_request["device_list"][index]["updated_at"] = entry.updated_at;
    index++;
  }
  if( serializeJson(json_request, json_buffer, sizeof(json_buffer)) <= 0 ){
    Serial.println("serializeJson error");
    return;
  }

#ifdef _MQTT_ENABLE_
  ret = mqttPublish(json_buffer);
  if( ret != 0 )
    Serial.println("mqtt publish failed");
#endif

#ifdef _HTTP_ENABLE_
  ret = httpPost(HTTP_PUT_URL, json_buffer);
  if( ret != 0 )
    Serial.println("httpPost error");
#endif
}

サーバ側の実装(@Node.js)

Node.jsサーバにて、HTTP Postで送られてきたBLEデバイス情報の一覧を管理します
MQTTで送られてきた場合の処理と、HTTP Post呼び出しで送られてきた場合の両方で動くようにしておきました。

nodejs/api/controllers/bledevice-api/index.js
'use strict';

const HELPER_BASE = process.env.HELPER_BASE || "/opt/";
const Response = require(HELPER_BASE + 'response');

let device_list = new Map();

exports.mqtt_handler = async (event, context) => {
  console.log(event);

  for( let device of event.payload.device_list ){
    device.host_address = event.payload.ble_mac_address;
    device_list.set(device.mac_address, device);
  }
};

exports.handler = async (event, context, callback) => {
	var body = JSON.parse(event.body);
	console.log(body);
  if( event.path == "/bledevice-list" ){
  	return new Response({ list: [...device_list.values()] });
  }else if( event.path == "/bledevice-put" ){
    for( let device of body.device_list ){
      device.host_address = body.ble_mac_address;
      device_list.set(device.mac_address, device);
    }
    return new Response({});
  }
};

BLEデバイスの状態表示ページ(@ブラウザ)

Node.jsで管理されているBLEデバイス一覧は、WebAPI呼び出しで取得できるようにし、Webページから可視化するようにしました。
こんな感じです。大した処理ではないので、ソースコードをご参照ください。

image.png

参考

 obniz-nobleでBLEスキャンをグラフ表示してみた
 ESP32のArduinoでBLEデバイスの出入りをモニタリングする

以上

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?