はじめに
この記事では、M5StickC PLUS のボタンを押すと ESP32 のLEDが点滅するデモを通じて、ESP-NOWによるデバイス間通信の実装方法を解説します。ESP-NOWはWiFiアクセスポイント不要・相手のMACアドレスだけで通信できる手軽な無線プロトコルで、BLE(Bluetooth Low Energy) と比べてもコードがシンプルです。M5StickC PLUSとESP32開発ボードがあれば、この記事のコードをそのまま動かせます。
ESP-NOWとは
ESP-NOWをもう少し詳しくみてみます。こちらにESP-NOWのドキュメントがありますので、目を通しておくと良いでしょう。
ドキュメントを翻訳すると、ESP-NOWは次のように説明されています。
ESP-NOWは、Espressifによって定義されたコネクションレス型Wi-Fi通信プロトコルです。アプリケーションデータはベンダー固有のアクションフレームにカプセル化され、接続なしで1つのWi-Fiデバイスから別のWi-Fiデバイスへ送信されます。CBC-MACプロトコル(CCMP)を使用したCTRでアクションフレームを暗号化してセキュリティを確保します。スマートライト・リモコン・センサなどで広く使われています。
TCPのようなハンドシェイクがないためデータ到達の保証はありませんが、高速かつ省電力な通信を実現できます。単純な送受信であれば使う関数はわずかで、すぐに動かせます。
プログラミング

M5StickC PLUSを送信機、ESP32を受信機として実装します。送信するには受信機のMACアドレスが必要なので、まず受信側のプログラムを書き込んでMACアドレスを確認してください。
受信側のプログラム(ESP32)
Serial.println(WiFi.macAddress()); を実行することで、ESP32のMACアドレスをシリアルモニタへ表示します。表示されたMACアドレスをメモして下さい。
ESP-NOWを使ってデータを受信するには、esp_now_init() で初期化し、esp_now_register_recv_cb(onReceive) でデータ受信時のコールバックを登録します。WiFiモードは WIFI_STA(ステーションモード)を指定します。
#include <esp_now.h>
#include <WiFi.h>
const int LED_PIN = 13;
bool ledState = false;
void toggleLed() {
if (ledState) {
ledState = false;
digitalWrite(LED_PIN, LOW);
}
else {
ledState = true;
digitalWrite(LED_PIN, HIGH);
}
}
void onReceive(const uint8_t* mac_addr, const uint8_t* data, int data_len) {
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.println();
Serial.printf("Last Packet Recv from: %s\n", macStr);
Serial.printf("Last Packet Recv Data(%d): ", data_len);
for (int i = 0; i < data_len; i++) {
Serial.print(data[i]);
Serial.print(" ");
if (data[i] == 222) {
toggleLed();
}
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
Serial.println(WiFi.macAddress()); // このアドレスを送信側へ登録します
WiFi.mode(WIFI_STA);
WiFi.disconnect();
if (esp_now_init() == ESP_OK) {
Serial.println("ESP-Now Init Success");
}
esp_now_register_recv_cb(onReceive);
}
void loop() {
}
送信側のプログラム(M5StickC PLUS)
slaveAddress を、受信機のMACアドレスに書き換えてください。esp_now_peer_info_t で送信先の情報を登録し、esp_now_register_send_cb(onSend) で送信完了イベントを登録します(不要なら省略可)。データの送信は esp_now_send(slaveAddress, data, sizeof(data)) だけです。
#include <M5StickCPlus.h>
#include <esp_now.h>
#include <WiFi.h>
uint8_t slaveAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // 受信機のMACアドレスに書き換えます
void onSend(const uint8_t* mac_addr, esp_now_send_status_t status) {
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(5, 10);
M5.Lcd.println(macStr);
M5.Lcd.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Failed");
}
void setup() {
M5.begin();
M5.Lcd.fillScreen(BLACK);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
if (esp_now_init() == ESP_OK) {
Serial.println("ESPNow Init Success");
}
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, slaveAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
esp_now_register_send_cb(onSend);
}
void loop() {
M5.update();
if (M5.BtnA.wasPressed()) {
uint8_t data[2] = { 111, 222 }; // 送信データ
esp_err_t result = esp_now_send(slaveAddress, data, sizeof(data));
Serial.print("Send Status: ");
switch (result)
{
case ESP_OK:
Serial.println("Success");
break;
case ESP_ERR_ESPNOW_NOT_INIT:
Serial.println("ESPNOW not Init.");
break;
case ESP_ERR_ESPNOW_ARG:
Serial.println("Invalid Argument");
break;
case ESP_ERR_ESPNOW_INTERNAL:
Serial.println("Internal Error");
break;
case ESP_ERR_ESPNOW_NO_MEM:
Serial.println("ESP_ERR_ESPNOW_NO_MEM");
break;
case ESP_ERR_ESPNOW_NOT_FOUND:
Serial.println("Peer not found.");
break;
default:
Serial.println("Not sure what happened");
break;
}
}
delay(1);
}
まとめ
ESP-NOWを使うと、ルーター不要・数行のコードだけでESP32デバイス間の無線通信が実現できます。ボタンを押したときのLED反応速度が有線と変わらないほど速く、IoTプロジェクトへの応用範囲は広いです。ESP32の無線通信をもっと活用したい方は、以下の記事も参考にしてみてください。
