元のESP32とESP-NOWを使ったワイヤレス通知システムは非常に高速で便利ですが、親機(通知機)のMACアドレスを手動で書き込むのは面倒ですよね。
この改良版では、子機(センサー)が自動で親機を探し出し、MACアドレスを登録して通信を開始する 「自動ペアリング機能」 を実装します。これにより、親機を交換したり、複数のセンサーを追加したりする際のセットアップが格段に簡単になります!
1. 自動ペアリングの仕組み(ディスカバリ・プロトコル)
ESP-NOWは通常、ピア(通信相手)のMACアドレスを知っている必要がありますが、ブロードキャスト機能を使うことで、自動で相手を探せます。
- 子機: 起動時、宛先を
FF:FF:FF:FF:FF:FF
(ブロードキャスト)として「親機を探しています(TYPE 0: REQUEST)」というメッセージを送信。 - 親機: ブロードキャストメッセージを受信したら、その送信元(子機)のMACアドレスを一時的に登録。
- 親機: 子機に対して「私のMACアドレスはこれです(TYPE 1: RESPONSE)」というメッセージをユニキャストで返信。
- 子機: 親機からの返信を受信し、その送信元(親機)のMACアドレスを正式なピアとして登録。
- 子機: ペアリング完了!以降、登録した親機MACアドレス宛にセンサーデータを送信。
2. 共通のデータ構造の定義
親機と子機が、通信が探索フェーズか通常データかを識別できるように、メッセージタイプを定義します。これは親機と子機、両方のスケッチの先頭に追加します。
C++
#include <esp_now.h>
#include <WiFi.h>
// MACアドレスのブロードキャストアドレス(全デバイス宛)
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// メッセージタイプ定数
#define TYPE_DISCOVERY_REQUEST 0 // 探索要求(子機 → 親機)
#define TYPE_DISCOVERY_RESPONSE 1 // 探索応答(親機 → 子機)
#define TYPE_SENSOR_DATA 2 // 通常データ(元の記事のセンサーデータ)
// 送受信するデータ構造
typedef struct struct_message {
uint8_t messageType;
// センサーデータやブザーON/OFFなどの具体的なデータはここに入れます
// 例: int sensorValue;
uint8_t data[250];
} struct_message;
3. センサー(子機)側のプログラム変更
子機は、ペアリングが完了するまで親機探索を繰り返し、MACアドレスを取得したら通信を開始します。
A. 変数とOnDataRecvの変更
C++
// 子機側 (Client/Sensor)
uint8_t gatewayMacAddress[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
bool isPaired = false; // ペアリング状態を管理
unsigned long lastDiscoveryAttempt = 0;
const long discoveryInterval = 5000; // 5秒ごとに探索
// データ受信コールバック
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
if (!isPaired) {
// ペアリングモード中の受信処理
struct_message incomingMsg;
memcpy(&incomingMsg, incomingData, sizeof(incomingMsg));
if (incomingMsg.messageType == TYPE_DISCOVERY_RESPONSE) {
Serial.println("✅ 親機から応答を受信!MACアドレスを登録します。");
// 応答の送信元MACアドレス(mac)が親機のアドレス
memcpy(gatewayMacAddress, mac, 6);
// 親機MACアドレスをピアとして登録
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, gatewayMacAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) == ESP_OK) {
isPaired = true;
Serial.println(">>> ペアリング完了!親機との通信を開始します。");
// ブロードキャストピアはもう不要なので削除
esp_now_del_peer(broadcastAddress);
} else {
Serial.println("❌ ピア登録に失敗。再探索します。");
}
}
} else {
// 通常のデータ受信処理(必要に応じて)
// ...
}
}
B. loop()での探索処理
C++
// 親機探索関数
void discoverGateway() {
if (millis() - lastDiscoveryAttempt >= discoveryInterval) {
lastDiscoveryAttempt = millis();
// ブロードキャストアドレスをピアとして登録(送信できるようにする)
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
// 探索要求メッセージを作成し、ブロードキャストで送信
struct_message discoveryMsg;
discoveryMsg.messageType = TYPE_DISCOVERY_REQUEST;
esp_now_send(broadcastAddress, (uint8_t *) &discoveryMsg, sizeof(discoveryMsg));
Serial.println("親機探索要求をブロードキャスト送信中...");
}
}
void loop() {
if (!isPaired) {
discoverGateway(); // ペアリングが完了するまで探索を繰り返す
} else {
// ペアリング完了後の通常の処理(元の記事のセンサーデータ送信ロジック)
// esp_now_send(gatewayMacAddress, ...); のように、取得したMACアドレス宛に送信
// ...
// delay(100);
}
}
4. 通知機(親機)側のプログラム変更
親機は、子機からの探索要求を受信したら、自身のMACアドレスが送信元となる応答メッセージを返信する処理を追加します。
A. setup()の変更
ブロードキャストされた探索要求を受信するために、ブロードキャストアドレスをピアとして登録します。
C++
// 親機側 (Gateway/Notifier)
void setup() {
// ... Wi-Fi初期化、ESP-NOW初期化、ブザーピン設定など ...
// ✅ ブロードキャストを受信するためにピアとして登録
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
}
B. OnDataRecvでの応答処理
C++
// データ受信コールバック
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
struct_message incomingMsg;
memcpy(&incomingMsg, incomingData, sizeof(incomingMsg));
if (incomingMsg.messageType == TYPE_DISCOVERY_REQUEST) {
// ✅ 子機からの探索要求を受信!
Serial.println("子機からの探索要求を受信。応答を返信します。");
// 1. 子機(要求元)をピアとして一時的に登録(ユニキャスト返信のために必須)
// 応答の送信元MACアドレス(mac)が子機のアドレス
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, mac, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
// 2. 応答メッセージを作成し、要求元のMACアドレス(子機)へユニキャストで返信
struct_message responseMsg;
responseMsg.messageType = TYPE_DISCOVERY_RESPONSE;
esp_err_t result = esp_now_send(mac, (uint8_t *) &responseMsg, sizeof(responseMsg));
if (result == ESP_OK) {
Serial.println("応答メッセージを送信しました。");
} else {
Serial.println("❌ 応答メッセージの送信に失敗。");
}
// 3. 一時的に登録した子機ピアを削除
// ※親機から子機へもブザーOFF信号などを送る場合は、この削除は行いません。
esp_now_del_peer(mac);
} else if (incomingMsg.messageType == TYPE_SENSOR_DATA) {
// 通常のセンサーデータ受信処理(元の記事のブザー鳴動処理など)
// ...
}
}
5. まとめと応用
この自動ペアリング機能により、親機側のMACアドレスを気にする必要がなくなり、ESP-NOWを使ったワイヤレスシステムの構築が非常にスムーズになります。
応用:
- マルチセンサー化: 複数のセンサー子機に同じコードを書き込むだけで、すべての親機が自動でペアリングを試行し、通知システムに参加できます。
- 設置場所の変更: 親機を別の部屋に移動しても、子機は自動的に新しいMACアドレスを認識して再接続します。
ぜひ、この超進化版のプログラムを試して、よりスマートなワイヤレス通知システムを構築してください!