ESP32でESP-NOWを使って通信してみよう
今回は、ESP32とM5StickC PLUSでESP-NOWを試してみました。
ESP-NOWとは、ESP32やESP8266間で無線でデータを送受信できる通信方法
です。無線LANのIEEE
802.11を使用して実現しているようですが、WiFiネットワークではないためアクセスポイントの必要がありません。
また、Bluetoothのようなセントラル・ペリフェラルといった複雑なペアリングの必要もないので簡単に開発できます。
はじめに
ESP32とESP32 PICOが内蔵されているM5StickC PLUSを使って、ESP-NOWを試してみました。ESP32を受信機として、M5StickC PLUSを送信機にしました。ESP-NOWを使って送信するには、受信機のMACアドレスが分かれば送信可能です。 正確には複数台に送るブロードキャスト方式もありますが、ここでは扱いません。 M5StickC PLUSのボタンを押すたびにESP32のLEDが点滅するプログラムを作ります。
ESP-NOWとは
ESP-NOWをもう少し詳しくみてみます。こちらにESP-NOWのドキュメントがありますので、目を通しておくと良いでしょう。 https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html
ドキュメントを翻訳すると、ESP-NOWは次のように説明されてます。
ESP-NOWは、Espressifによって定義された一種のコネクションレス型Wi-Fi通信プロトコルです。 ESP-NOWでは、アプリケーションデータはベンダー固有のアクションフレームにカプセル化され、接続なしで1つのWi-Fiデバイスから別のWi-Fiデバイスに送信されます。 CBC-MACプロトコル(CCMP)を使用したCTRは、セキュリティのためにアクションフレームを保護するために使用されます。 ESP-NOWは、スマートライト、リモートコントロール、センサなどで広く使用されてます。
TCPなどのハンドシェイクがないためデータが到達したかどうかの確証は得られませんが、高速で省電力な通信を実現できるようです。
ドキュメントには関数がズラリと書かれてますが、単純な送受信だけならばほんのわずかな関数だけでESP-NOWを実現できます。
プログラム

それではESP-NOWを使ったメインプログラムの紹介です。 次のプログラムでは、送信機のM5StickC PLUSのAボタンが押されるとESP-Now通信を使ってESP32のMACアドレス宛へメッセージが投げられます。受信機のESP32でメッセージが受信されると、GPIO13につながったLEDが交互に点滅され動画のような動作を振る舞います。
受信側のプログラム(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) {
= false;
ledState (LED_PIN, LOW);
digitalWrite}
else {
= true;
ledState (LED_PIN, HIGH);
digitalWrite}
}
void onReceive(const uint8_t* mac_addr, const uint8_t* data, int data_len) {
char macStr[18];
(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
snprintf[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
mac_addr.println();
Serial.printf("Last Packet Recv from: %s\n", macStr);
Serial.printf("Last Packet Recv Data(%d): ", data_len);
Serialfor (int i = 0; i < data_len; i++) {
.print(data[i]);
Serial.print(" ");
Serialif (data[i] == 222) {
();
toggleLed}
}
}
void setup() {
.begin(115200);
Serial(LED_PIN, OUTPUT);
pinMode.println(WiFi.macAddress()); // このアドレスを送信側へ登録します
Serial
.mode(WIFI_STA);
WiFi.disconnect();
WiFiif (esp_now_init() == ESP_OK) {
.println("ESP-Now Init Success");
Serial}
(onReceive);
esp_now_register_recv_cb}
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];
(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
snprintf[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
mac_addr
.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");
M5}
void setup() {
.begin();
M5.Lcd.fillScreen(BLACK);
M5
.mode(WIFI_STA);
WiFi.disconnect();
WiFiif (esp_now_init() == ESP_OK) {
.println("ESPNow Init Success");
Serial}
esp_now_peer_info_t peerInfo;
(peerInfo.peer_addr, slaveAddress, 6);
memcpy.channel = 0;
peerInfo.encrypt = false;
peerInfo
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
.println("Failed to add peer");
Serialreturn;
}
(onSend);
esp_now_register_send_cb}
void loop() {
.update();
M5
if (M5.BtnA.wasPressed()) {
uint8_t data[2] = { 111, 222 }; // 送信データ
esp_err_t result = esp_now_send(slaveAddress, data, sizeof(data));
.print("Send Status: ");
Serialswitch (result)
{
case ESP_OK:
.println("Success");
Serialbreak;
case ESP_ERR_ESPNOW_NOT_INIT:
.println("ESPNOW not Init.");
Serialbreak;
case ESP_ERR_ESPNOW_ARG:
.println("Invalid Argument");
Serialbreak;
case ESP_ERR_ESPNOW_INTERNAL:
.println("Internal Error");
Serialbreak;
case ESP_ERR_ESPNOW_NO_MEM:
.println("ESP_ERR_ESPNOW_NO_MEM");
Serialbreak;
case ESP_ERR_ESPNOW_NOT_FOUND:
.println("Peer not found.");
Serialbreak;
default:
.println("Not sure what happened");
Serialbreak;
}
}
(1);
delay}
まとめ
いかがだったでしょうか。うまくESP-NOWの通信ができましたでしょうか。 私は、ボタンを押したときのLEDの反応速度が有線と変わらないことに驚きました。ESP-NOW、これから大活躍しそうな予感です。 最初でもお伝えしましたが、ESP-NOWではルーターの必要がないこと、BLEのGATTのような複雑な記述がないことがとても良いですね。いままで敷居が高く感じてたアイデアも、ESP-NOWを使えばサクッと実現できそうです。
関連記事
- 【Raspberry Pi】Ambientへセンサデータを送信してグラフ化
- はじめてのBLE通信、iOSからESP32のLチカ
- ダイソーのBluetoothリモコンシャッターをESP32でハックする
- ESP32でWiFi接続を簡単にする「WiFiHelper」ライブラリ開発のリリースノート
- ダイソーのリモコンシャッターをESP32で通信するためのヘルパー関数