ESP-NOW는 ESP32에서 지원하는 ESP32간의 통신 방식이다. WiFi 방식을 기반하고, 실제로 소스에도 WiFi Station 모드를 명시하는데 정확한 내부 로직은 더 알아봐야 할 것 같다. ESP32가 직접 AP(공유기 역할)을 할 수도 있고 공유기에 연결되어 인터넷과 ESP-NOW 통신을 병행할 수 있어서, 아마 WiFi 방식을 활용한 ESP32만의 기술이 있는 것 같다.
 
공유기에 연결하지 않고 ESP-NOW를 사용하는 방식과 공유기 연결 + ESP-NOW 방식을 둘 다 사용해봤을 땐, ESP-NOW만 사용하는 방식이 패킷 손실이 적고, 통신 거리가 비교적 멀리까지 되어서 이 부분은 조금 더 공부해야할 듯하다..
 
공유기를 함께 사용할 경우 특별히 통신할 모든 ESP32 소스에 채널을 고정해야 통신이 가능하다.

#define CHANNEL 6

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println(WiFi.localIP());

  esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);

  initESPNow();

  server.on("/", handleRoot);
  server.on("/send", HTTP_POST, handleSend);
  server.begin();
}

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

 

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.


WEB에서 특정 ID를 입력받으면 해당 정보를 노드에 브로드캐스팅하는 방식으로 만들었는데, WEB도 몇번 입력을 넣다보면 로딩이 걸려서 해결이 필요하다.. 우선 브로드캐스팅을 할 ESP32와 점등할 ESP32를 1m정도 거리를 두고 점등 테스트를 해보았다.
 
구조

서버가 되는 ESP32(공유기에 연결, WEB 호스팅)

클라이언트(몇번 노드가 점멸할지 요청)

노드 ESP32



 
브로드캐스팅용 소스 전문(Master)

#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include <WebServer.h>


const char* ssid = "와이파이명";
const char* password = "비밀번호";

#define CHANNEL 6  // 모두 동일하게 설정해야 함

WebServer server(80);

#pragma pack(push, 1)

typedef struct struct_message {
  int ids[30];          
  int count;            
  char label[20];        
  bool ledState;        
} struct_message;
#pragma pack(pop)

struct_message outgoingData;

// 브로드캐스팅용 MAC 주소 셋팅
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};


String inputIDs = "";

void sendESPNowMessage() {

  inputIDs.trim();
  inputIDs.replace(" ", "");
  int idIndex = 0;

  int lastIndex = 0;
  while (true) {
    int commaIndex = inputIDs.indexOf(',', lastIndex);
    String token = (commaIndex == -1) ? inputIDs.substring(lastIndex) : inputIDs.substring(lastIndex, commaIndex);
    outgoingData.ids[idIndex++] = token.toInt();
    if (commaIndex == -1 || idIndex >= 30) break;
    lastIndex = commaIndex + 1;
  }
  outgoingData.count = idIndex;

 
  strcpy(outgoingData.label, "HELLO");
  outgoingData.ledState = true;

// 패킷손실을 고려해서 3번 쏘게 만듬
for (int i = 0; i < 3; i++) {
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t*)&outgoingData, sizeof(outgoingData));
  if (result == ESP_OK) {
    Serial.printf("전송 성공\n", i + 1);
  } else {
    Serial.printf("전송 실패\n", i + 1);
  }
  delay(50);
}

  inputIDs = "";
}

void handleRoot() {
  String html = "<!DOCTYPE html><html><head><meta charset='utf-8'><title>ESP-NOW Sender</title></head><body>";
  html += "<form action='/send' method='POST'>";
  html += "<input name='ids' placeholder='예: 1,2,3,4' style='width: 300px; height: 40px; font-size: 18px;'><br><br>";
  html += "<input type='submit' value='전송' style='width: 150px; height: 40px; font-size: 18px;'>";
  html += "</form></body></html>";

  server.send(200, "text/html", html);
}

void handleSend() {
  if (server.hasArg("ids")) {
    inputIDs = server.arg("ids");
    Serial.print("ID List : ");
    Serial.println(inputIDs);
    sendESPNowMessage();
  }
  server.sendHeader("Location", "/");
  server.send(303);
}

void initESPNow() {
  if (esp_now_init() != ESP_OK) {
    Serial.println("초기화 실패");
    ESP.restart();
  }

  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = CHANNEL;
  peerInfo.encrypt = false;

  if (!esp_now_is_peer_exist(broadcastAddress)) {
    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
      Serial.println("브로드캐스트 피어 등록 fail");
      return;
    }
  }
}

#define CHANNEL 6

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println(WiFi.localIP());

  esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);

  initESPNow();

  server.on("/", handleRoot);
  server.on("/send", HTTP_POST, handleSend);
  server.begin();
}

void loop() {
  server.handleClient();
}

 
 

+ Recent posts