ESP-NOWとは
ESP-NOWは、Espressif が定義した無線通信プロトコルで、ルーターを介さずに直接、高速、低消費電力でスマートデバイスを制御できます。Wi-Fi や Bluetooth LE との共存が可能で、Espressif の ESP8266、ESP32、ESP32-S、ESP32-C など、複数シリーズの SoC をサポートしています。1
ESP-NOW はデータリンク層 ( レイヤ 2) に基づく無線通信プロトコルで、5 層の OSI 上位プロトコルを 1 層に簡素化しています。データ伝送時に、ネットワーク層、トランスポート層、セッション層、プレゼンテーション層、アプリケーション層などの複雑な階層を順次経由する必要がなく、各層でパケットヘッダを追加/解除する処理も不要です。これにより、ネットワーク混雑時のパケット損失に起因する遅延やラグを大幅に軽減し、より高い応答速度を実現しています。1
要は、ESP32ボードやM5 stackシリーズの間において、接続の柔軟性や通信の量と質を制限する代わりに、消費電力や実装の簡略化が可能なようです。
開発環境
-
Windows 11
- あらかじめUSBシリアル変換ICのドライバ(CP210x Windows Drivers)をインストールしておく
-
ESP32-DevKitC-32E
事前準備
ESP-NOWはピア・ツー・ピアの通信プロトコルであることから、接続先のアドレスをあらかじめ調査する必要があります。調査対象のESP32ボードに以下のプログラムを書き込み、シリアルでモニターします。
#include <Arduino.h>
#include <WiFi.h>
void setup() {
Serial.begin(115200);
Serial.println("MAC Address Reader\n");
}
void loop() {
Serial.print("MACWIfi: ");
Serial.println(WiFi.macAddress());
delay(1000);
}
そうすると、xx:xx:xx:xx:xx:xxという6バイトの数字(16進数)が読み出せるはずです。ボードにシールを貼り、MACアドレスをメモしておくと良いでしょう。
相互通信テスト
タイムスタンプとテキストデータをボードA・B間で送り合い相互に通信できているかテストします。
ボードA/BのMACアドレスを、マクロで切り替えて設定出来るようにしてあります。
#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
/**
* @brief 通信テスト用のデータ構造体
* @param タイムスタンプ
* @param テキストメッセージ
*/
typedef struct {
uint32_t timestamp;
char text[64];
}wifirbcom_test_t;
void onDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len);
void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status);
esp_now_peer_info_t peerInfo;
wifirbcom_test_t send_data;
wifirbcom_test_t recive_data;
/** 事前に調べた各々のMACアドレスを記載 **/
const uint8_t WIFIRBCOM_A_DIST_ADDRESS[6] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX}; // 送信先のデバイスのアドレス(6バイト)
const uint8_t WIFIRBCOM_B_DIST_ADDRESS[6] = { 0xYY, 0xYY, 0xYY, 0xYY, 0xYY, 0xYY}; // 送信先のデバイスのアドレス(6バイト)
uint8_t myaddress[6]; // 自身のMACアドレス
uint8_t distaddress[6]; // 相手のMACアドレス
char recive_text[64] = {0}; // 受信したテキストデータ
/***** ESP32のボード選択 *****/
#define ESP32BOARD_A 0
#define ESP32BOARD_B 1
#define ESP32BOARD ESP32BOARD_A // ESP32ボードの選択(ESP32BOARD_A or ESP32BOARD_B)
// #define ESP32BOARD ESP32BOARD_B // ESP32ボードの選択(ESP32BOARD_A or ESP32BOARD_B)
void setup() {
char serialString[64];
// 自他のアドレスを設定
#if (ESP32BOARD == ESP32BOARD_A)
memcpy(myaddress, WIFIRBCOM_A_DIST_ADDRESS, 6);
memcpy(distaddress, WIFIRBCOM_B_DIST_ADDRESS, 6);
#else
memcpy(myaddress, WIFIRBCOM_B_DIST_ADDRESS, 6);
memcpy(distaddress, WIFIRBCOM_A_DIST_ADDRESS, 6);
#endif
Serial.begin(115200);
// 一応自分のアドレスを表示しておく
sprintf(serialString, "ESP-NOW BOOT ! %0X:%0X:%0X:%0X:%0X:%0X",
myaddress[0], myaddress[1], myaddress[2], myaddress[3], myaddress[4], myaddress[5]);
Serial.println(serialString);
// Initialize WiFi in station mode
WiFi.mode(WIFI_STA);
// Initialize ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW initialization failed");
return; // Initialization failed
}
memcpy(peerInfo.peer_addr, distaddress, 6);
peerInfo.channel = 0; // Use current channel
peerInfo.encrypt = false; // No encryption
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return; // Adding peer failed
}
// 受信時に呼び出される関数を登録(必須)
esp_now_register_recv_cb(onDataRecv);
// 送信完了時に呼び出される関数を登録(任意)
esp_now_register_send_cb(onDataSent);
}
/**
* @brief 受信完了時に呼び出されるコールバック関数
* 受信したデータからタイムスタンプとテキスト
* を取り出し、そのまま表示する。
* @param mac_addr peer MAC address
* @param data received data
* @param data_len length of received data
*/
void onDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len)
{
memcpy(&recive_data, data, sizeof(recive_data));
sprintf(recive_text, "%ld : %s", recive_data.timestamp, recive_data.text);
Serial.println(recive_text);
}
/**
* @brief 送信完了時に呼び出されるコールバック関数。
* 送信完了時のエラー処理などは行わないので
* メッセージを表示するのみ
* @param mac_addr peer MAC address
* @param status status of sending ESPNOW data (succeed or fail)
*/
void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
Serial.println("!!SEND DATA!!");
}
/**** 周期処理 ****/
void loop() {
// 送信するタイムスタンプは起動後に経過した時間(ミリ秒)
// 送信するテキストデータは自分のMACアドレスをフォーマットに従い格納
send_data.timestamp = millis();
snprintf(send_data.text, sizeof(send_data.text), "FROM <%s> ", WiFi.macAddress().c_str());
esp_now_send(distaddress, (uint8_t*)&send_data, sizeof(send_data)); // 実際の送信処理
delay(100);
}
実験結果
左がボードB、右がボードAです。
(オレンジはボードBのMACアドレス、緑はボードAのMACアドレスです。)
ライブラリの作成
現在作成中。完成したら記事を書きます。