AESP-IDFを使ってみる

ESPNOW


ESPNOWはEspressIFが提供するPeerToPeerの通信規格です。
標準的なWi-Fiと同様に2.4GHzの周波数帯域で動作し、データパケットに標準的なWi-Fiチャネル(約20〜22MHzの帯域幅)を使 用して、
ルーターなしでESP32/ESP8266デバイス間の直接的な低消費電力通信を可能にします。
WiFiチャネルの選択やユーザ名、パスワードの認証などを行わないので、WiFiに比べ短時間、省電力で動くのが特徴です。
ESPNOWはESP8266でも利用可能で、ESP8266とESP32の間でもESPNOWを使った通信が可能です。
PeerToPeerの通信規格としてはZigBeeなどが有りますが、使用する無線の中心周波数と周波数帯域がWiFiとは異なります。

WiFi channel1
中心周波数: 2412 MHz (2.412 GHz)
周波数帯域: 2401MHz-2423MHz (使用帯域: 20-22 MHz)

ZigBee channel11
中心周波数: 2405 MHz
周波数帯域: 2402.5Mhz-2407.5MHz (使用帯域: 2.5 MHz)

ESPNOWは同じチャネルを使用している相手にだけメッセージが届きます。
ESPNOWの受信側がチャネル#11を使用する場合、送信側もチャネル番号#11を使用する必要が有ります。
APに接続した場合、使用するWiFiのチャネルはAPが決めますが、APに接続していない状態では、WiFiのデフォルトのチャネルは#1で す。
ESPNOWでチャネル#11を使いたい場合、以下のコードでWiFiのチャネルを#11に変更する必要が有ります。
    /* Change WiFi primary channel */
    uint8_t primary;
    wifi_second_chan_t second;
    ESP_ERROR_CHECK(esp_wifi_get_channel(&primary, &second));
    ESP_LOGI(TAG, "current primary channel=%d", primary);
    primary = 11;
    ESP_ERROR_CHECK(esp_wifi_set_channel(primary, WIFI_SECOND_CHAN_NONE));
    ESP_ERROR_CHECK(esp_wifi_get_channel(&primary, &second));
    ESP_LOGI(TAG, "new primary channel=%d", primary);

あまり知られていない更新ですが、ESP-IDF V5.4からESPNOWのバージョンが2.0に更新されました。
ESPNOW Ver1では最大ペイロードサイズは250バイトでしたが、Ver2では1470バイトに拡張されています。
ESP-IDFのESPNOWはVer1とVer2で相互に通信することができます。
ESP8266のESPNOWはVer1なので、ESP8266と通信するときは250バイトまでの制限が有ります。




こ ちらにESPNOW通信のサンプルが公開されています。
このサンプルを実行するためには2台のESP32が必要です。
menuconfigを起動するとWiFi modeの選択が有ります。


ESP32はどのモデルもStation InterfaceとSoftAP Interfaceの2つのWiFi Interfaceを持っています。
それぞれのInterfaceには、異なるMACアドレスがアサインされています。
ESPNOWはStation Interface/SoftAP InterfaceのどちらのInterfaceを使っても通信することができます。
WiFi modeはどちらのInterfaceを使うかの選択で、WiFi modeと言うメニュー名が分かりにくいです。
WiFi modeの選択ではなく、WiFi Interfaceの選択です。

Station Interfaceを使うとESPNOWの省電力機能を使うことができます。
esp_now_set_wake_window() と esp_wifi_connectionless_module_set_wake_interval() を使ってSleepの設定を行います。


こちらがStation Interfaceを使った時のロギングです。


こちらがSoftAP Interfaceを使った時のロギングです。
送信側のMACアドレスが24:0a:c4:c5:46:fcから24:0a:c4:c5:46:fdに変わります。




なかなか難解なコードですが、このサンプルではBroadcast通信とUnicast通信の使い方が紹介されています。
このサンプルは最初はBroadcastで通信相手を探しますが、相手が見つかるとUnicast送信に切り替えます。
送信先のMACアドレスが「FF:FF:FF:FF:FF:FF」の場合はBroadcast通信、それ以外の場合はUnicast通信となりま す。
ESPNOWで通信を行う場合、相手側のデバイスにデータを送信する前に、esp_now_add_peer()を使って、
相手側のMACアドレスをデバイスリストに登録しておく必要が有ります。
Broadcast通信を行う場合も、「FF:FF:FF:FF:FF:FF」のアドレスを、デバイスリストに登録しておく必要が有ります。
デバイスリストに登録する際に、通信を暗号化するかどうかを指定することができます。
このサンプルではBroadcast通信は非暗号化、Unicast通信は暗号化で通信を行っています。
暗号化通信を行うときは送受信側で同じ暗号キーを使う必要が有ります。


Unicast通信に切り替えて100回送信すると、送信を終了します。
こちらが送信側のロギングです。


こちらが受信側のロギングです。


コードを少し変更して、通信速度を測定してみました。
パケット長1470バイトを100回送信した場合、70.6KBytes/Sec(552KBits/Sec)程度の通信速度です。
そこで、使用するWiFiのチャネルを変えて、同じ条件で試してみました。
チャネルの使用状況により通信速度は大きく変わります。
WiFi Channel KBytes/Sec KBits/Sec
1 70.0 547
3 78.1 610
5 94.8 741
7 96.7 755
9 98.6 770
11 78.1 610
13 90.7 709
14 102.7 802



ESP8266の場合、ESPNOWとWiFiを同時に使うことができませんでしたが、
ESP32ではこの制限がなくなり、ESPNOWとWiFiを同時に使うことができます。
WiFiをStationモードで使う場合、電波の混雑状況などを考慮して、使用するチャネルはAPが決めます。
ESPNOWとWiFiを同時に使う場合、ESPNOWもWiFiと同じチャネルを使って通信を行います。
ESPNOWが使用するチャネルを変更することはできません。



ESPNOWでは送信完了時と受信完了時にCallBack関数が呼ばれます。
ESP-IDF V5.5から送信完了時のCallBack関数のIFが(いきなり)変わりました。
こ ちらに短い説明が有ります。
以下のコードで、V5.4とV5.5のどちらでもビルドできるようになります。
esp_now_send_info_tは、ドキュメントによると typedef wifi_tx_info_t esp_now_send_info_t です。
V5.4までは送信先MACアドレスしか渡りませんでしたが、V5.5ではそれ以外に802.11のパケットなども渡るみたいです。
ESPNOWも802.11を使うので、この変数型に統一したようです。
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
static void example_espnow_send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status)
#else
static void example_espnow_send_cb(const uint8_t *mac_addr, esp_now_send_status_t status)
#endif
{
    example_espnow_event_t evt;
    example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
    const uint8_t *mac_addr = tx_info->des_addr;
#endif

    if (mac_addr == NULL) {
        ESP_LOGE(__FUNCTION__, "Send cb arg error");
        return;
    }

    evt.id = EXAMPLE_ESPNOW_SEND_CB;
    memcpy(send_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
    send_cb->status = status;
    if (xQueueSend(xQueueESPNOWSend, &evt, ESPNOW_MAXDELAY) != pdTRUE) {
        ESP_LOGW(__FUNCTION__, "xQueueSend fail");
    }
}



こちらにESPNOWのサ ンプルコードが公開されています。
ESP8266もESP32もArduino環境のコードですが、Linux用のESPNOWのコードが公開されています。
LinuxでESPNOWを使う場合、WiFiインターフェースをモニター モードに変更する必要が有ります。
WiFiのモニター モードは、WiFi アダプターの特別なモードで、送信先に関係なく、特定のチャネルのすべてのワイヤレス トラフィックをキャプチャできます。
こちらが、wlx1cbfceaae44dのInterfaceをmonitorモードに変更する手順です。
$ iwconfig
lo        no wireless extensions.

enp2s0    no wireless extensions.

wlx1cbfceaae44d  IEEE 802.11  ESSID:off/any
          Mode:Managed  Access Point: Not-Associated   Tx-Power=20 dBm
          Retry short limit:7   RTS thr:off   Fragment thr:off
          Power Management:off


$ sudo ifconfig wlx1cbfceaae44d
wlx1cbfceaae44d: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 1c:bf:ce:aa:e4:4d  txqueuelen 1000  (イーサネット)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

$ sudo ifconfig wlx1cbfceaae44d down

$ sudo iwconfig wlx1cbfceaae44d mode monitor

$ sudo ifconfig wlx1cbfceaae44d up

$ sudo ifconfig wlx1cbfceaae44d
wlx1cbfceaae44d: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        unspec 1C-BF-CE-AA-E4-4D-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (不明なネット)
        RX packets 2412317  bytes 380570756 (380.5 MB)
        RX errors 0  dropped 109773  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

続く...