NodeMCU (ESP8266) 2台をESP-NOWで連携させることで、無線環境の無い屋外でもワイヤレス通信で制御が可能となります。
今回は、片方のボタンで片方のLEDを制御するプログラムを作成を通し、ESP-NOWの「データ受信時に呼ばれるコールバック関数」の仕組みについて学びます。
コールバック関数とは
LEDが付いている受信機側ではプログラム本体のVoidloop内しか実行できないため、外部の送信機側からスイッチが押されたかどうかの判断できません。
そこでコールバック関数(OnDataRecv)を使い、後からVoidLoopの外から指示が来るかも知れないと事前に登録(予約)することで、受信機側でも外部からの信号を受け入れること(イベント駆動)が可能となります。
今回は行いませんが、送信できたか確認するための送信完了確認関数(OnDataSend)も利用できるので、目の届かない範囲で動いたかどうかの確認をしながら遠隔操作できます。
ESP-NOWを使う上で最も重要なのは、送信機が受信機のMACアドレスを知る必要があることです。そのため、まずは受信機側のMACアドレスを調べて、それを送信機のコードに書き込む、という手順が必要になります。
以下に、受信機と送信機のコード、および設定手順を示します。
1. 準備:ハードウェアの接続
✅ 送信機側
- D5 (GPIO14) ← プッシュボタン
- GND ← プッシュボタンのもう一方の脚(D5ピンをINPUT_PULLUP(内部プルアップ抵抗有効)で使うため、外付け抵抗は不要です)
✅ 受信機側
- D5 (GPIO14) ← LEDのアノード(+、長い方の脚)
- LEDのカソード(-、短い方の脚) → 220Ω 程度の抵抗 → GND
2. 受信機(LED側)のプログラム

まず、LEDを接続したNodeMCUにこのコードを書き込みます。
このコードは、自身のMACアドレスをシリアルモニタに出力する機能も兼ねています。
#include <ESP8266WiFi.h>
#include <espnow.h>
// D5ピン (GPIO14) にLEDを接続
#define LED_PIN D5
// データ受信時に呼ばれるコールバック関数
void OnDataRecv(uint8_t *mac_addr, uint8_t *incomingData, uint8_t len) {
// 1バイトのデータ(ボタンの状態)を受信
if (len > 0) {
uint8_t buttonState = incomingData[0]; // 0: 押された, 1: 離された
// ボタンの状態に応じてLEDを制御
// buttonStateが0 (押された) なら HIGH (LED点灯)
// buttonStateが1 (離された) なら LOW (LED消灯)
if (buttonState == 0) {
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
}
}
void setup() {
Serial.begin(115200);
// LEDピンを出力に設定
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // 初期状態はLEDオフ
// WiFiをステーションモードに設定
WiFi.mode(WIFI_STA);
// ★重要★ 自分のMACアドレスをシリアルモニタに表示
Serial.println("ESP-NOW Receiver");
Serial.print("MAC Address: ");
Serial.println(WiFi.macAddress());
// ESP-NOWの初期化
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// 自身の役割をSLAVE (受信) に設定
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
// データ受信コールバック関数を登録
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
// ESP-NOWはコールバックで動作するため、loop内は空でOK
}3. 送信機(ボタン側)のプログラム
受信機のMACアドレスをシリアルモニターで調べてから、このコードをボタンを接続したNodeMCUに書き込みます。

#include <ESP8266WiFi.h>
#include <espnow.h>
// D5ピン (GPIO14) にボタンを接続
#define BUTTON_PIN D5
// ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
// ★重要★
// 上記「2. 受信機のプログラム」を実行して
// シリアルモニタに表示されたMACアドレスをここに書き換える
uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
// ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
// ボタンの直前の状態を記憶する変数
uint8_t lastButtonState = HIGH; // PULLUPなので初期値はHIGH (離されている)
void setup() {
Serial.begin(115200);
// ボタンピンを内部プルアップ有効の入力に設定
pinMode(BUTTON_PIN, INPUT_PULLUP);
// WiFiをステーションモードに設定
WiFi.mode(WIFI_STA);
// ESP-NOWの初期化
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// 自身の役割をCONTROLLER (送信) に設定
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
// ペア(宛先)を登録
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
void loop() {
// 現在のボタンの状態を読む (押されているとLOW, 離されているとHIGH)
uint8_t buttonState = digitalRead(BUTTON_PIN);
// ボタンの状態が変化した時だけ送信
if (buttonState != lastButtonState) {
// 状態(0 または 1)を送信
esp_now_send(broadcastAddress, &buttonState, sizeof(buttonState));
// 現在の状態を記憶
lastButtonState = buttonState;
// 簡単なチャタリング防止
delay(50);
}
}4. 実行手順
- 受信機側の設定
- LEDを接続したNodeMCUに「2. 受信機(LED側)のプログラム」を書き込みます。
- Arduino IDEのシリアルモニタを開きます(ボーレート: 115200)。
- NodeMCUのリセットボタンを押すと、シリアルモニタに
MAC Address: XX:XX:XX:XX:XX:XXのように表示されます。 - このMACアドレス(例:
AA:BB:CC:DD:EE:FF)をメモします。
- 送信機側の設定
- 「3. 送信機(ボタン側)のプログラム」のコードを開きます。
uint8_t broadcastAddress[] = ...の部分を、先ほどメモしたMACアドレスに書き換えます。AA:BB:CC:DD:EE:FFだった場合、{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}のように書き換えます。
- MACアドレスを書き換えたコードを、ボタンを接続したNodeMCUに書き込みます。
- 動作確認
- 両方のNodeMCUの電源を入れます(PCのUSB給電やモバイルバッテリーなどでOKです)。
- 送信機側のプッシュボタンを押すと、受信機側のLEDが点灯します。
- ボタンを離すと、LEDが消灯します。


コメント