본문 바로가기
  • 재미있는 펌웨어와 코딩
ESP32/펌웨어

ESP32 공유기 없이 통신 (1:1, 1:N) ESP-NOW

by 윤재윤호 2023. 10. 8.

IP 공유기 없이 ESP32 모듈간 peer to peer 통신을 합니다.

1:1 또는 1:N 통신이 가능합니다.

ESP-NOW 통신을 기본으로 제공하므로 별도의 라이브러리는 필요치 않습니다.

통신 하려는 모듈의 MAC 어드레스만 알면 됩니다.

 

샘플소스

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

#define LED 2

// 통신 장치 정보 (반드시 전역 변수로 선언)
esp_now_peer_info_t peerInfo;

// 통신할 ESP32 MAC 주소
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// 데이터 전송 시 콜백
void OnDataSent( const uint8_t *mac_addr, esp_now_send_status_t status ) {
//  Serial.println( status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail" );
}

// 데이터 수신 시 콜백
void OnDataRecv( const uint8_t * mac, const uint8_t *incomingData, int len ) {
  String str = (char*)incomingData;
  Serial.println( str );
  if( str.equalsIgnoreCase( "ON" ) ) digitalWrite( LED, HIGH );
  else if( str.equalsIgnoreCase( "OFF" ) ) digitalWrite( LED, LOW );
}

// peer 추가
bool AddPeer( const uint8_t* mac ) {
  memcpy( peerInfo.peer_addr, mac, 6 ); // MAC 주소
  peerInfo.channel = 0; // 통신 채널 0
  peerInfo.encrypt = false; // 암호화 안함
  
  // peer 추가
  if( esp_now_add_peer(&peerInfo) != ESP_OK ) return false;
  return true;
}

// 메시지 전송
bool SendDataNow( const uint8_t* mac, String msg ) {
  char buf[250];
  msg.toCharArray( buf, 250 );
  
  // 메시지 전송
  esp_err_t result = esp_now_send( mac, (uint8_t *)&buf, msg.length()+1 );
   
  if( result == ESP_OK ) return true;
  return false;
}
 
void setup() {
  Serial.begin( 115200 );
  pinMode( LED, OUTPUT );

  WiFi.mode( WIFI_STA ); // 와이파이는 스테이션 모드로.
  Serial.println( WiFi.macAddress() ); // 나의 MAC 어드레스.

  // ESP-NOW 초기화
  if( esp_now_init() != ESP_OK ) {
    Serial.println( "Error initializing ESP-NOW" );
    return;
  }

  // 데이터 전송 시 콜백함수 등록
  esp_now_register_send_cb( OnDataSent );
  
  // peer 등록
  AddPeer( broadcastAddress );

  // 데이터 수신 시 콜백함수 등록
  esp_now_register_recv_cb( OnDataRecv );
}
 
void loop() {
  SendDataNow( broadcastAddress, "ON" );
  delay( 500 );
  SendDataNow( broadcastAddress, "OFF" );
  delay( 500 );
}

 

먼저, 2개의 ESP32 모듈을 준비합니다.

GPIO2번에 LED를 연결합니다. 모듈 내부에 사용하는 BUILTIN LED를 사용하면 편리합니다.

Master 와 Slave를 구분하지 않아도 되므로, 위의 소스코드에서 상대방의 MAC 주소만 바꿔서 펌웨어를 넣습니다.

2개의 모듈을 동작 시키면  LED가 점멸하면 성공 입니다.

 

코드 설명

라이브러리 추가없이 헤더 파일 선언

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

 

통신할 모듈의 정보를 담는 변수 선언. 주의할 점은 반드시 전역 변수로 선언 해야 합니다.

통신할 모듈의 MAC 어드레스를 입력합니다. (자신의 어드레스 아님)

// 통신 장치 정보 (반드시 전역 변수로 선언)
esp_now_peer_info_t peerInfo;

// 통신할 ESP32 MAC 주소
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

 

데이터 전송시 성공 여부를 받는 콜백함수.

// 데이터 전송 시 콜백
void OnDataSent( const uint8_t *mac_addr, esp_now_send_status_t status ) {
//  Serial.println( status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail" );
}

 

데이터 수신 콜백함수 입니다.

수신된 데이터를 분석하여 LED를 동작 시킵니다.

문자열로 수신를 받게 만들었습니다. 나중에 JSON 형식으로 데이터를 주고 받을때 스트링 방식이 좋을것 같습니다.

지금은 단순히 ON, OFF 문자열로 받습니다.

송신한 장치의 MAC 어드레스를 받을 수 있기 때문에 여러개의 장치와 통신중에 누가 보냈는지 알 수 있습니다.

// 데이터 수신 시 콜백
void OnDataRecv( const uint8_t * mac, const uint8_t *incomingData, int len ) {
  String str = (char*)incomingData;
  Serial.println( str );
  if( str.equalsIgnoreCase( "ON" ) ) digitalWrite( LED, HIGH );
  else if( str.equalsIgnoreCase( "OFF" ) ) digitalWrite( LED, LOW );
}

 

통신할 모듈을 MAC 어드레스로 추가 합니다.

// peer 추가
bool AddPeer( const uint8_t* mac ) {
  memcpy( peerInfo.peer_addr, mac, 6 ); // MAC 주소
  peerInfo.channel = 0; // 통신 채널 0
  peerInfo.encrypt = false; // 암호화 안함
  
  // peer 추가
  if( esp_now_add_peer(&peerInfo) != ESP_OK ) return false;
  return true;
}

 

원하는 장치에 문자열의 데이터를 보냅니다. 문자열이 아닌 uint8_t 데이터로 보내야 되는데 저는 JSON을 염두에 두고

문자열로 보내도록 만들었습니다.

데이터의 길이는 최대 250 바이트 입니다. (250바이트를 넘지 않게 합니다.)

만약, 여러개의 장치에 모두 보내려면 MAC 어드레스 대신에 esp_now_send( 0, ...) 을 넣어서 보냅니다.

// 메시지 전송
bool SendDataNow( const uint8_t* mac, String msg ) {
  char buf[250];
  msg.toCharArray( buf, 250 );
  
  // 메시지 전송
  esp_err_t result = esp_now_send( mac, (uint8_t *)&buf, msg.length()+1 );
   
  if( result == ESP_OK ) return true;
  return false;
}

 

와이파이는 스테이션 모드로 설정 합니다.

나의 MAC 어드레스를 보여 줍니다.

  WiFi.mode( WIFI_STA ); // 와이파이는 스테이션 모드로.
  Serial.println( WiFi.macAddress() ); // 나의 MAC 어드레스.

 

ESP-NOW 통신을 초기 설정 해줍니다.

  // ESP-NOW 초기화
  if( esp_now_init() != ESP_OK ) {
    Serial.println( "Error initializing ESP-NOW" );
    return;
  }

  // 데이터 전송 시 콜백함수 등록
  esp_now_register_send_cb( OnDataSent );
  
  // peer 등록
  AddPeer( broadcastAddress );

  // 데이터 수신 시 콜백함수 등록
  esp_now_register_recv_cb( OnDataRecv );

 

여기서는 반복적으로 ON, OFF 문자열을 보내도록 했습니다.

상황에 맞도록 데이터를 보내면 됩니다.

void loop() {
  SendDataNow( broadcastAddress, "ON" );
  delay( 500 );
  SendDataNow( broadcastAddress, "OFF" );
  delay( 500 );
}

 

통신거리는 주변환경과 안테나 종류에 따라서 다르기 때문에 직접 테스트 해보시는게 확실합니다.

저도 개활지에서 200m 정도 나온다고 해서 테스트 해봤지만 20~30m 정도 넘으니까 통신이 잘 안됩니다.

(물론, 어디까지나 제 주변환경 때문 입니다.)

 

공유기를 통하여 Webserver로 동작 시키면서 다른 모듈과 통신이 가능 합니다.

여러 분야에서 응용하면 좋을것 같습니다.