esp32のBLE scanでRSSIが取得できるか?
現時点で "esp32 ble scan RSSI" でweb検索すると、上手くいかないとか、変な値が取得できるといった情報ばかりが見つかる。そこで、本当にできないのか試してみたら上手くいったのでメモ。
開発環境の準備
まずesp-idfの開発環境を揃える。esp-idfはWindowsよりLinuxで開発したほうが色々と捗るので、WindowsのVirtualboxでdebian linuxを動作させ、その中でesp-idfの開発環境をセットアップした。特にハマる点もないので詳細な手順は省略。検索すればすぐ見つかる。
次に、ソースを見る際に grep -r しまくっても良いが、IDEの方がなにかと便利なので、eclipseからesp-idfを扱えるようにセットアップした。ついでにeclipseからbuild→flashまでできるようにした。これも簡単なので詳細な手順は省略。
一点だけハマる可能性があるのは、Virtualboxにesp32開発ボードのUSB-Serialのインターフェイスをアタッチする所で、何かの拍子に乱暴に切断してしまった後だと再アタッチが失敗することがある。そんな場合は、windowsのデバイスマネージャの「ポート(COMとLPT)」にあるCOMポートを削除してから開発ボードを接続して再アタッチするとうまくいく。これに気づくまではいちいちwindows自体を再起動させていた。
BLE scanできるサンプルソース
esp-idf/examples/bluetooth/gatt_client/
がそれ。昔はexample 15_gatt_clientと呼ばれていたもののようだ。
このプロジェクトをeclispeにインポートして色々と探ってみると、rssiはesp_gap_ble_api.hで定義されているesp_ble_gap_cb_param_tという共用体にintとして入っており、gattc_demo.cのesp_gap_cbというコールバック関数の中でアクセスできることがわかる。よって、この関数の中で
case ESP_GAP_BLE_SCAN_RESULT_EVT: { esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param; switch (scan_result->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: for (int i = 0; i < 6; i++) { ESP_LOGI(GATTC_TAG, "%x:", scan_result->scan_rst.bda[i]); } ESP_LOGI(GATTC_TAG, "\n"); adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); ESP_LOGI(GATTC_TAG, "Searched Device Name Len %d\n", adv_name_len); ESP_LOGI(GATTC_TAG, "RSSI: %d\n", scan_result->scan_rst.rssi); //これを追加。以下省略。
とRSSIを出力するようにしてみた。
簡単なテスト
使ったesp32デバイスはdoitの開発ボードと、秋月でも買えるespressifの開発ボードで、両方とも同じ動作をした。
変更したプログラムをビルドしてesp32にflashして、適当なシリアルモニタ(LinuxのCuteComが使いやすかった)で出力を見てみると、BLEタグ(スマートフォンで代用。BLE Peripheral Simulatorを使用。)がesp32の数十cm程度の近くにあるときは
\0x1b[0;32mI (9350) GATTC_DEMO: RSSI: -64
程度で、数メートル離すと
\0x1b[0;32mI (7260) GATTC_DEMO: RSSI: -85
程度となったので、正常な値を取得できているようだ。ただ、当然だがesp32の向きが変わると値が大きく変動するので、色々と工夫してタグの接近・離脱を判断する必要はある。
まとめ
安価な据え置き型ble scanner(wifiへのゲートウェイ)としての利用可能性が薄ぼんやりと見えた。その用途では、これまではRaspberry PiやEdisonなどを使うしかなかったが、esp32で代用できれば色々と可能性が広がる。