🚀 超進化版!ESP32で実現するESP-NOWワイヤレス通知システム(MACアドレス自動登録編)

【DIY】ホームセーフティ実践

元のESP32とESP-NOWを使ったワイヤレス通知システムは非常に高速で便利ですが、親機(通知機)のMACアドレスを手動で書き込むのは面倒ですよね。

この改良版では、子機(センサー)が自動で親機を探し出し、MACアドレスを登録して通信を開始する 「自動ペアリング機能」 を実装します。これにより、親機を交換したり、複数のセンサーを追加したりする際のセットアップが格段に簡単になります!

1. 自動ペアリングの仕組み(ディスカバリ・プロトコル)

ESP-NOWは通常、ピア(通信相手)のMACアドレスを知っている必要がありますが、ブロードキャスト機能を使うことで、自動で相手を探せます。

  1. 子機: 起動時、宛先をFF:FF:FF:FF:FF:FF(ブロードキャスト)として「親機を探しています(TYPE 0: REQUEST)」というメッセージを送信。
  2. 親機: ブロードキャストメッセージを受信したら、その送信元(子機)のMACアドレスを一時的に登録。
  3. 親機: 子機に対して「私のMACアドレスはこれです(TYPE 1: RESPONSE)」というメッセージをユニキャストで返信。
  4. 子機: 親機からの返信を受信し、その送信元(親機)のMACアドレスを正式なピアとして登録。
  5. 子機: ペアリング完了!以降、登録した親機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アドレスを認識して再接続します。

ぜひ、この超進化版のプログラムを試して、よりスマートなワイヤレス通知システムを構築してください!

タイトルとURLをコピーしました