はじめに
モノづくり界隈の作品でもよく使われている通信方法の 1つで、手軽な通信を行える「ESP-NOW」に関する記事です。
自分が持っているデバイス関連だと、直近で入手した「StackChan」のコントローラーを使った制御に ESP-NOW が使われています。
今回の記事は、この「ESP-NOW」の公式ドキュメントを少し見てみた、という話です。下調べなので、内容はかなりざっくりな感じです。
公式情報で仕様の情報などを見てみる
公式情報の 1つを見ていく
公式情報を見ていきます。以下の Espressif のページに書かれているように、ESP-NOW は Espressif が定義した無線通信のプロトコルです。
●ESP-NOW ワイヤレス通信プロトコル | Espressif Systems
https://www.espressif.com/ja-jp/solutions/low-power-solutions/esp-now
端末間の通信を行うことができるものですが、その際、ルーターを介さずに通信ができます。そして、Wi-Fi や Bluetooth LE との共存が可能で、「Espressif の ESP8266、ESP32、ESP32-S、ESP32-C」などの複数シリーズをサポートしているようです。
さらに、上記のページの下の方を見ていきます。
上記の図と説明を見てみます。これによると、「ESP-NOW は、OSI参照モデルのデータリンク層(レイヤ2)に基づく無線通信プロトコルであり、その上の 5層の OSI 上位プロトコルを 1層に簡素化している」とのことです。
それについて、先ほどのページ内の図の部分を抜き出して見てみると、以下のように表現されていました。
内容的には、『データ伝送時に「ネットワーク層、トランスポート層、セッション層、プレゼンテーション層、アプリケーション層」などの複雑な階層を順次経由する必要がなく、各層でパケットヘッダを追加/解除する処理も不要となる』というもののようです。
他の公式情報
上記のページをさらに下に進んでいくと、以下の記載部分がありました。
上記に掲載された 3つのリンクは、以下になるようです。
●espressif/esp-now: A connectionless Wi-Fi communication protocol
https://github.com/espressif/esp-now
●ESP-NOW - ESP32-C3 - — ESP-IDF Programming Guide latest documentation
https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/network/esp_now.html
●ESP-NOW - - — ESP-FAQ latest documentation
https://docs.espressif.com/projects/esp-faq/en/latest/application-solution/esp-now.html
上記 GitHub上のサンプル
上記 GitHub上のサンプルを見てみます。
●esp-now/examples at master · espressif/esp-now
https://github.com/espressif/esp-now/tree/master/examples
以下は、サンプルのフォルダ名をリストで書いたものです。
- coin_cell_demo
- control
- get-started
- ota
- provisioning
- security
- solution
- wireless_debug
とりあえず最初に見てみるのが良さそうなのは、「get-started」あたりでしょうか。
●esp-now/examples/get-started at master · espressif/esp-now
https://github.com/espressif/esp-now/tree/master/examples/get-started
ちなみに、「get-started」の「main/app_main.c」を見てみると、以下のようなコードでした。
ざっくり処理を見てみると、このコードは送信側・受信側の両方の役割を兼ねた実装がされているようでした。この内容を例えば 2台のデバイスに書き込んで使うと、片方の UART に入ってきたデータがもう片方に送られて、送られた先でログに出力される、というやりとりができるもののようでした。
/* Get Start Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp_mac.h"
#endif
#include "espnow.h"
#include "espnow_storage.h"
#include "espnow_utils.h"
#include "driver/uart.h"
// You can modify these according to your boards.
#define UART_BAUD_RATE 115200
#define UART_PORT_NUM 0
#define UART_TX_IO UART_PIN_NO_CHANGE
#define UART_RX_IO UART_PIN_NO_CHANGE
static const char *TAG = "app_main";
static void app_uart_read_task(void *arg)
{
esp_err_t ret = ESP_OK;
uint32_t count = 0;
size_t size = 0;
uint8_t *data = ESP_CALLOC(1, ESPNOW_DATA_LEN);
ESP_LOGI(TAG, "Uart read handle task is running");
espnow_frame_head_t frame_head = {
.retransmit_count = CONFIG_RETRY_NUM,
.broadcast = true,
};
for (;;) {
size = uart_read_bytes(UART_PORT_NUM, data, ESPNOW_DATA_LEN, pdMS_TO_TICKS(10));
ESP_ERROR_CONTINUE(size <= 0, "");
ret = espnow_send(ESPNOW_DATA_TYPE_DATA, ESPNOW_ADDR_BROADCAST, data, size, &frame_head, portMAX_DELAY);
ESP_ERROR_CONTINUE(ret != ESP_OK, "<%s> espnow_send", esp_err_to_name(ret));
ESP_LOGI(TAG, "espnow_send, count: %" PRIu32 ", size: %u, data: %s", count++, size, data);
memset(data, 0, ESPNOW_DATA_LEN);
}
ESP_LOGI(TAG, "Uart handle task is exit");
ESP_FREE(data);
vTaskDelete(NULL);
}
static void app_uart_initialize()
{
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
#if SOC_UART_SUPPORT_REF_TICK
.source_clk = UART_SCLK_REF_TICK,
#elif SOC_UART_SUPPORT_XTAL_CLK
.source_clk = UART_SCLK_XTAL,
#endif
};
ESP_ERROR_CHECK(uart_param_config(UART_PORT_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_PORT_NUM, UART_TX_IO, UART_RX_IO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_driver_install(UART_PORT_NUM, 8 * ESPNOW_DATA_LEN, 8 * ESPNOW_DATA_LEN, 0, NULL, 0));
xTaskCreate(app_uart_read_task, "app_uart_read_task", 4 * 1024, NULL, tskIDLE_PRIORITY + 1, NULL);
}
static void app_wifi_init()
{
esp_event_loop_create_default();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
ESP_ERROR_CHECK(esp_wifi_start());
}
static esp_err_t app_uart_write_handle(uint8_t *src_addr, void *data,
size_t size, wifi_pkt_rx_ctrl_t *rx_ctrl)
{
ESP_PARAM_CHECK(src_addr);
ESP_PARAM_CHECK(data);
ESP_PARAM_CHECK(size);
ESP_PARAM_CHECK(rx_ctrl);
static uint32_t count = 0;
ESP_LOGI(TAG, "espnow_recv, <%" PRIu32 "> [" MACSTR "][%d][%d][%u]: %.*s",
count++, MAC2STR(src_addr), rx_ctrl->channel, rx_ctrl->rssi, size, size, (char *)data);
return ESP_OK;
}
void app_main()
{
espnow_storage_init();
app_uart_initialize();
app_wifi_init();
espnow_config_t espnow_config = ESPNOW_INIT_CONFIG_DEFAULT();
espnow_init(&espnow_config);
espnow_set_config_for_data_type(ESPNOW_DATA_TYPE_DATA, true, app_uart_write_handle);
}
いったん、公式情報を見るのはここまでにして、実装に関する情報を検索などもしつつ見てみます。
実装に関する情報を見てみる
他に、実装に関する情報をいくつか軽く下調べなどをしたので、メモとして残します。
M5Stack関連
自分は M5Stack関連で使いそうなので、とりあえず M5Stack関連の実装に関する情報を見てみます。
UIFlow
UIFlow 2.0/UIFlow で確かブロックがあったはず、と思って見てみました。
UIFlow 2.0 だと、ESP-NOW のブロックはここにありました。
●M5-ESPNOW — UIFlow2 Programming Guide master documentation
https://uiflow-micropython.readthedocs.io/en/2.2.7/system/m5espnow.html
関連する公式ドキュメントは以下になるようです。
●M5-ESPNOW — UIFlow2 Programming Guide master documentation
https://uiflow-micropython.readthedocs.io/en/latest/system/m5espnow.html
また UIFlow だと、以下のように「高度なブロック」の下に「EspNow」というメニュー名のものがありました。
こちらのほうの関連する公式ドキュメントは以下になるようです。
●ESP-NOW
https://docs.m5stack.com/en/uiflow/blockly/advanced/espnow
テキストコーディング関連
以下は、テキストコーディング関連での実装の情報です。
まずは、M5Stack公式の GitHubリポジトリ上で「ESP-NOW」で検索してみたものです(※ さらに、対象をコードにした時の URL です)。
●Code search results
https://github.com/search?q=org%3Am5stack+ESP-NOW&type=code
ざっと見ていったのですが、ここで出てきたものは今段階では使いづらい感じがするので、Web検索でシンプルそうな事例の情報を探してみます。
以下は、ざっくり検索してシンプルなコード例が掲載されていたと思ったものの抜粋です。
●ESP-NOWの一番シンプルで簡単なサンプル - ESP32など
https://tak054.hateblo.jp/entry/2024/10/25/225819
●ESP32でESP-NOWを使って通信してみよう | araisun.com
https://araisun.com/esp32-m5stickc-plus-esp-now/
●3110/m5stack-esp-now-serial: M5Stack のシリアル出力を別の M5Stack に ESP-NOW でブロードキャストする
https://github.com/3110/m5stack-esp-now-serial
あと、M5Stack関連の情報が色々と掲載されている Lang-ship さんのサイトに「ESP-NOW」のタグがあったので、その URL と関連するページの一部を掲載してみます。
●arduino-esp32 v3でのESP-NOW研究 その1 基本動作 | Lang-ship
https://lang-ship.com/blog/work/arduino-esp32-v3-esp-now-1/
●arduino-esp32 v3でのESP-NOW研究 その2 ピア追加と暗号化通信 | Lang-ship
https://lang-ship.com/blog/work/arduino-esp32-v3-esp-now-2/
●arduino-esp32 v3でのESP-NOW研究 その3 速度別送信成功率 | Lang-ship
https://lang-ship.com/blog/work/arduino-esp32-v3-esp-now-3/
おわりに
とりあえず、「ESP-NOW」に関する公式の説明を少し見てみたり、実装関連の情報を検索などしつつメモしてみたりしました。
この後は、実際に実機での実装のテストができればと思います。
余談
以下は余談です。
ESP-NOW を知ったきっかけ
自分が ESP-NOW を知ったのは、コロナ禍より前の 2018年ごろだったと記憶しています。
そのころに参加した、LT が行われる技術コミュニティイベントで、LT をしていた方のお一人がデバイス間通信で使った話をされていて、それが自分が知ったきっかけでした。
そのわりに、ESP-NOW をきちんと使うことがないままに、時間が過ぎていました(自分が作る作品だと、ブラウザを絡めるものなどが多くあったりして、リアルタイム通信では WebSocket や MQTT などを多様していました)。とても便利なプロトコルだと思うので、直近で、何か作品で活用してみようと思っています。








