ESP32でESP-NOWを使って通信してみよう
今回は、ESP32とM5StickC PLUSでESP-NOWを試してみました。 ESP-NOWとは、__ESP32やESP8266間で無線でデータを送受信できる通信方法__ です。無線LANのIEEE 802.11を使用して実現しているようですが、WiFiネットワークではないためアクセスポイントの必要がありません。 また、Bluetoothのようなセントラル・ペリフェラルといった複雑なペアリングの必要もないので簡単に開発できます。
▼ Bluetoothを使ったBLE通信はこちら。
はじめに
ESP32とESP32 PICOが内蔵されているM5StickC PLUSを使って、ESP-NOWを試してみました。ESP32を受信機として、M5StickC PLUSを送信機にしました。ESP-NOWを使って送信するには、受信機のMACアドレスが分かれば送信可能です。 正確には複数台に送るブロードキャスト方式もありますが、ここでは扱いません。 M5StickC PLUSのボタンを押すたびにESP32のLEDが点滅するプログラムを作ります。
つかうもの
ESP-NOWを使うには、ESP32やESP8266のチップが搭載されているマイコンボードを使用します。本記事では、こちらのESP32とM5StickC PLUSを使用しました。
ESP-NOWとは
ESP-NOWをもう少し詳しくみてみます。こちらにESP-NOWのドキュメントがありますので、目を通しておくと良いでしょう。 ESP-NOW-ESP32-—ESP-IDF Programming Guide latest documentation
ドキュメントを翻訳すると、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) {
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の通信ができましたでしょうか。 私は、ボタンを押したときのLEDの反応速度が有線と変わらないことに驚きました。ESP-NOW、これから大活躍しそうな予感です。 最初でもお伝えしましたが、ESP-NOWではルーターの必要がないこと、BLEのGATTのような複雑な記述がないことがとても良いですね。いままで敷居が高く感じてたアイデアも、ESP-NOWを使えばサクッと実現できそうです。