COCOA 発信機(EN API仕様)を M5Stick Plus で作る

前回、COCOA の EN API 仕様のアドバタイズを Windows + C# で受信する方法を書きました。今回は M5Stick Plus で COCOA 発信機を作る方法です。

EN API のデータフォーマット

  • Flags: 1A
  • Complete 16-bit Service UUIDs: FD6F
  • Service Data:
    – 16-bit Service UUID: FD6F
    – RPI (Rolling Proximity Identifier): 16 bytes
    – AEM (Associated Encrypted Metadata): 4 bytes

の形でデータをセットしていきます。

M5Stick CPlus での実装例

基本的に set_advertising_data 関数でアドバタイズデータをセットして、advertising->start() で発信し続けます。loop 関数は特に必要ないのですが、後でログなどを入れる予定です。

RPI と AEM はダミーデータです。本格的に EN API 仕様に合わせるならば暗号化されたデータを入れることになりますが、今回は送受信実験のためなので確認しやすい値をいれておきます。

#include <M5StickCPlus.h>   // m5stack/M5StickCPlus
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLEAdvertising.h>

// BLE関連
BLEAdvertising *advertising;
// アドバタイズデータ作成
void set_advertising_data();

/**
 * @brief BLE初期化
 */
void ble_init() {
  // BLEの初期化
  BLEDevice::init("cocoa");
  set_advertising_data();
}

/**
 * @brief アドバタイズ送信設定
 * 
 */

// Rolling Proximity Identifier
char rpi[16] { 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 
               0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04 };  
// Asscociated Encrypted Metadata
char aem[4]  { 0x05, 0x05, 0x05, 0x05, };  

void set_advertising_data()
{
    // アドバタイズ送信パワー変更(-3dBm ~ 10dBm:デフォルト0dBm)
    esp_power_level_t dbm = ESP_PWR_LVL_N0;
    
    (ESP_BLE_PWR_TYPE_ADV, dbm);
      // アドバタイズデータ作成
    BLEAdvertisementData advertisementData;
    // Flags
    advertisementData.setFlags(0x1A);
    // Complete 16-bit Service UUID
    advertisementData.setCompleteServices(BLEUUID("FD6F"));
    // Service Data(Service Data 16-bit Service UUID)
    std::string strServiceData = "";
    // Append RPI and AEM
    for (int i = 0; i < sizeof(rpi); i++) {
        strServiceData += rpi[i];
    }
    for (int i = 0; i < sizeof(aem); i++) {
        strServiceData += aem[i];
    } 
    // Service Data 16-bit Service UUID
    advertisementData.setServiceData(BLEUUID("FD6F"), strServiceData);

    // アドバタイズの設定
    advertising = BLEDevice::getAdvertising();
    // advertising->setAdvertisementType(ADV_TYPE_NONCONN_IND); // コネクション不要
    // advertising->setScanResponse(false);
    advertising->setAdvertisementData(advertisementData);
    // アドバタイズ間隔を 100 ms に設定
    advertising->setMinInterval(0x00A0); // 約100 ms
    advertising->setMaxInterval(0x00A0); // 約100 ms
}

/**
 * @brief 初期化
 *
 */
void setup() {
  M5.begin();

  // 動作周波数を80MHzにする(BLEが使用できる最低の周波数)
  // setCpuFrequencyMhz(80);
  // デバッグ用
  Serial.begin(115200);
  Serial.println("M5StickC Plus BLE Transmitter Start");
  // BLE初期化
  ble_init();
  // アドバタイズ開始
  advertising->start();
}

/**
 * @brief メインループ
 *
 */
int count = 0;
void loop() {
  M5.update();
  // 画面表示
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.printf("COCOA Transmitter\n");
  M5.Lcd.printf("Count: %d\n", count++);
  sleep(1);
}

1. esp_ble_tx_power_set 関数でアドバタイズの送信パワーを設定
2. BLEAdvertisementData クラスでアドバタイズデータを作成
3. setFlags 関数で Flags を 0x1A に設定
4. setCompleteServices 関数で Complete 16-bit Service UUIDs を FD6F に設定
5. setServiceData 関数で Service Data を設定(16-bit Service UUID + RPI + AEM)
6. BLEAdvertising クラスでアドバタイズの設定を行い、start 関数でアドバタイズを開始

  • advertising->setAdvertisementType で ADV_TYPE_NONCONN_IND を指定して、コネクション不要に設定
  • advertising->setScanResponse でスキャンレスポンスを無効に設定
  • setMinInterval と setMaxInterval でアドバタイズ間隔を設定(100ms)

setAdvertisementType と setScanResponse はデフォルトのままで大丈夫なはずです。この値は、Flags に設定されます。advertisementData.setFlags(0x1A) で設定済みです。

Core Specification Supplement, PartA, Section 1.3

  • 0x01: LE Limited Discoverable Mode
  • 0x02: LE General Discoverable Mode
  • 0x04: BR/EDR Not Supported
  • 0x08: Simultaneous LE and BR/EDR to Same Device Capable (Controller)
  • 0x10: Previously Used

0x1A は 00011010 なので、LE General Discoverable Mode と BR/EDR Not Supported が立っています。

M5Stick Plus で動作確認

ハングアップしてないことを確認するために 1 秒ごとに画面が変わります。

受信確認

前回作成した Windows + C# 版の受信プログラムで、M5StickC Plus から発信された COCOA フォーマットのアドバタイズを受信できます。

もうひとつ、発信間隔を 1 秒程度に替えたものを実験してみます。

    // アドバタイズ間隔を 1000 ms に設定
    advertising->setMinInterval(0x00A0*10); // 約1000 ms
    advertising->setMaxInterval(0x00A0*10); // 約1000 ms

windows のツールですが、受信間隔が

  • 100 msec 発信のときは、約 1000 msec ごとに受信
  • 1000 msec 発信のときは、約 2000 msec ごとに受信

しています。先の duty cycle の関係があるので、発信間隔と受信間隔は比例しないわけです。なので、発信間隔を間延びさせて受信側の Scan Window を広げるとか、逆に Scan Window を狭めて発信間隔を短くするとか、そういう調節ができます。

逆に言えば、ここの事実を考慮しないと EN API 仕様の受信はうまくいない可能性が高いということですね。windows の場合は duty cylce を調節できないので、どれだけの Scan Window になっているかは不明ですが、Android の場合はある程度設定ができます。iOS の場合は、duty cycle が自動調節になっているので、これも不明なところが多いです。

以前、Android で実験をしたときは、Android の場合は受信頻度が機種によって非常に悪いときがあります。となりに置いてある Windows マシンの受信間隔よりも Android のほうが間延びして受信しています。最悪の場合は 30 秒程度おくれることもあります。
このあたりは、再び Android での受信機と、m5stack/ESP32 での受信機を作って実験してみる必要がありそうです。

カテゴリー: 開発, FolkBears パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

*