ESP32 두 대 사이에 데이터를 주고받으려면 보통 두 보드를 같은 공유기에 연결하고 HTTP나 MQTT 같은 방식으로 통신한다. 하지만 이 방법은 공유기가 반드시 있어야 하고, 전원을 켤 때마다 공유기에 접속하는 데 수 초가 걸리며, 공유기가 없는 야외나 공유기와 멀리 떨어진 곳에서는 사용할 수 없는 한계가 있다. 블루투스를 사용하면 공유기는 필요 없지만 페어링과 연결 과정이 번거롭고 통신 거리도 짧다.
ESP-NOW는 Espressif에서 만든 무선 통신 방식으로, 공유기 없이 ESP32끼리 MAC 주소만으로 바로 데이터를 주고받을 수 있다. 연결 과정이 없어 전원을 켜자마자 통신할 수 있고 반응 속도도 매우 빠르다. 이 글에서는 ESP-NOW의 특징을 Wi-Fi, BLE와 비교하고, MAC 주소 확인 방법과 버튼으로 다른 ESP32의 LED를 켜는 송신, 수신 예제 코드를 소개한다.


출처: Espressif ESP-NOW 공식 문서, Apache License 2.0
1. ESP-NOW 소개
ESP-NOW는 ESP32의 2.4GHz Wi-Fi 하드웨어를 그대로 사용하지만, 공유기 접속이나 IP 주소 같은 TCP/IP 과정을 모두 생략하고 Wi-Fi 데이터 프레임에 짧은 데이터를 직접 담아 보내는 방식이다. 데이터를 받을 기기의 MAC 주소를 상대(Peer)로 등록하기만 하면 바로 데이터를 보낼 수 있다.
특징:
- 공유기가 필요 없으며, 전원을 켠 후 바로 통신할 수 있다.
- 한 번에 최대 250바이트의 데이터를 보낼 수 있다. 센서 값, 버튼 상태, 명령어 등을 보내기에 충분한 크기이다.
- 1:1 통신뿐 아니라 1:N(여러 대에 보내기), N:1(여러 대에서 받기), 양방향 통신이 모두 가능하다.
- 데이터를 보낸 후 바로 딥슬립에 들어갈 수 있어 배터리로 동작하는 센서에 적합하다.
- ESP32, ESP32-S3, ESP32-C3, ESP32-C6, ESP8266 등 Espressif의 Wi-Fi 칩끼리 서로 통신할 수 있다.
ESP-NOW는 아두이노 ESP32 보드 패키지에 기본으로 포함되어 있어 별도의 라이브러리를 설치할 필요가 없다. 최신 ESP-IDF 5.4 이상에서는 한 번에 보낼 수 있는 데이터가 최대 1470바이트로 늘어난 ESP-NOW v2도 지원하지만, 이 글에서는 모든 보드에서 사용할 수 있는 250바이트 기준으로 설명한다.
2. 주요 사양 및 Wi-Fi, BLE 통신과의 비교
ESP32에서 사용할 수 있는 무선 통신 방식의 차이는 다음과 같다.
| 항목 | Wi-Fi (공유기 경유) | BLE | ESP-NOW |
| 공유기 | 필요 | 불필요 | 불필요 |
| 연결 과정 | 공유기 접속, IP 할당 (수 초) | 검색, 연결 (수 초) | 없음 (MAC 주소 등록) |
| 1회 전송 크기 | 제한 거의 없음 | 기본 20바이트 (확장 시 수백 바이트) | 250바이트 (v2: 1470바이트) |
| 반응 속도 | 수십~수백 ms | 수십 ms | 수 ms |
| 통신 거리 | 공유기 범위 | 약 10~30m | 실내 수십 m, 야외 약 100~200m |
| 연결 기기 수 | 공유기 성능에 따라 다름 | 수 대 | 최대 20대 (암호화 시 최대 17대) |
| 인터넷 연결 | 가능 | 불가 | 불가 |
| 스마트폰 연결 | 가능 | 가능 | 불가 |
ESP-NOW는 스마트폰이나 인터넷과는 직접 통신할 수 없지만, ESP32끼리 짧은 데이터를 빠르게 주고받는 데는 가장 간단하고 빠른 방법이다. 인터넷 연결이 필요하면 여러 ESP32의 데이터를 ESP-NOW로 한 대에 모은 후, 그 한 대만 Wi-Fi로 서버에 보내는 방식으로 함께 사용할 수 있다.
3. 준비물 및 연결
이 글의 예제는 ESP32 개발보드 2대를 사용하며, 한 대는 버튼 상태를 보내는 송신기, 다른 한 대는 데이터를 받아 LED를 켜는 수신기로 사용한다.
- 송신기: 버튼의 한쪽을 GPIO4, 다른 쪽을 GND에 연결한다. 내부 풀업 저항을 사용하므로 저항은 필요 없다.
- 수신기: 대부분의 ESP32 개발보드는 GPIO2에 파란색 LED가 내장되어 있으므로 그대로 사용한다. 내장 LED가 없는 보드는 GPIO2에 220Ω 저항과 LED를 연결한다.
4. 개발환경 및 MAC 주소 확인
아두이노 IDE에 ESP32 보드 패키지(esp32 by Espressif Systems)가 설치되어 있으면 ESP-NOW를 바로 사용할 수 있다. 이 글의 코드는 보드 패키지 3.x 버전을 기준으로 작성되었다.
ESP-NOW로 데이터를 보내려면 먼저 수신기 ESP32의 MAC 주소를 알아야 한다. 다음 코드를 수신기로 사용할 보드에 업로드하고 시리얼 모니터(115200bps)를 열면 MAC 주소가 출력된다.
#include <WiFi.h>
#include <esp_wifi.h>
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.STA.begin();
uint8_t mac[6];
esp_wifi_get_mac(WIFI_IF_STA, mac);
Serial.printf("MAC 주소: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void loop() {
}
- 6~7 Line: ESP32를 Wi-Fi 스테이션 모드로 설정하고 Wi-Fi 기능을 시작한다. ESP-NOW는 스테이션 모드의 MAC 주소를 사용한다.
- 9~10 Line: 스테이션 모드의 MAC 주소 6바이트를 읽는다.
- 11~12 Line: MAC 주소를 "24:6F:28:AA:BB:CC" 같은 16진수 형식으로 출력한다.
출력된 MAC 주소는 메모해 두었다가 송신기 코드에 입력한다. 시리얼 모니터에 아무것도 나오지 않으면 보드의 RESET(EN) 버튼을 한 번 누르면 된다.
[이미지: 시리얼 모니터에 출력된 MAC 주소]
5. 예제 코드
1) 송신기 코드
버튼 상태와 전송 횟수를 구조체에 담아 0.2초마다 수신기로 보내는 코드이다.
#include <WiFi.h>
#include <esp_now.h>
uint8_t receiverMac[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};
typedef struct {
int count;
bool button;
} Message;
Message msg;
const int BTN_PIN = 4;
esp_now_peer_info_t peerInfo;
void setup() {
Serial.begin(115200);
pinMode(BTN_PIN, INPUT_PULLUP);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW 초기화 실패");
return;
}
memcpy(peerInfo.peer_addr, receiverMac, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
}
void loop() {
msg.count++;
msg.button = (digitalRead(BTN_PIN) == LOW);
esp_err_t result = esp_now_send(receiverMac, (uint8_t *)&msg, sizeof(msg));
if (result == ESP_OK) {
Serial.printf("전송: count=%d, button=%d\n", msg.count, msg.button);
} else {
Serial.println("전송 실패");
}
delay(200);
}
- 1~2 Line: Wi-Fi 라이브러리와 ESP-NOW 라이브러리를 포함한다.
- 4 Line: 앞에서 확인한 수신기의 MAC 주소를 입력한다. "24:6F:28:AA:BB:CC"라면 각 자리 앞에 0x를 붙여 입력하면 된다.
- 6~9 Line: 보낼 데이터의 형식을 구조체로 정의한다. 수신기 코드에도 똑같은 구조체를 정의해야 한다.
- 11~13 Line: 보낼 데이터 변수, 버튼 핀 번호, 상대 기기 정보를 담을 변수를 선언한다.
- 18 Line: ESP-NOW를 사용하기 위해 Wi-Fi를 스테이션 모드로 설정한다. 공유기에는 연결하지 않는다.
- 20~23 Line: ESP-NOW를 초기화하고, 실패하면 메시지를 출력한다.
- 25~28 Line: 수신기의 MAC 주소를 상대 기기(Peer)로 등록한다. channel을 0으로 하면 현재 Wi-Fi 채널을 사용하고, encrypt를 false로 하면 암호화 없이 보낸다.
- 32~33 Line: 전송 횟수를 1 증가시키고, 버튼이 눌렸으면 true, 아니면 false를 저장한다.
- 35 Line: 구조체 데이터를 바이트 배열로 바꿔 수신기로 보낸다.
- 36~40 Line: 전송 요청 결과를 시리얼 모니터에 출력한다.
- 41 Line: 0.2초마다 데이터를 보낸다.
esp_now_send()의 결과가 ESP_OK인 것은 전송 요청이 정상적으로 처리되었다는 의미이며, 수신기가 실제로 받았는지까지 확인하려면 esp_now_register_send_cb() 함수로 전송 완료 콜백 함수를 등록하면 된다. 단, 전송 완료 콜백 함수의 형식은 보드 패키지 버전에 따라 다르므로 사용하는 버전의 예제(파일 -> 예제 -> ESP32 -> ESPNow)를 참고한다.
2) 수신기 코드
송신기에서 받은 데이터를 시리얼 모니터에 출력하고, 버튼이 눌린 상태이면 LED를 켜는 코드이다.
#include <WiFi.h>
#include <esp_now.h>
typedef struct {
int count;
bool button;
} Message;
Message msg;
const int LED_PIN = 2;
void onReceive(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
memcpy(&msg, data, sizeof(msg));
digitalWrite(LED_PIN, msg.button ? HIGH : LOW);
Serial.printf("수신: count=%d, button=%d\n", msg.count, msg.button);
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW 초기화 실패");
return;
}
esp_now_register_recv_cb(onReceive);
}
void loop() {
}
- 4~7 Line: 송신기와 똑같은 형식의 구조체를 정의한다. 구조체의 형식이 다르면 데이터가 잘못 읽힌다.
- 12 Line: 데이터를 받았을 때 자동으로 실행되는 콜백 함수이다. info에는 보낸 기기의 MAC 주소 등이 들어 있고, data와 len은 받은 데이터와 길이이다.
- 13 Line: 받은 데이터를 구조체 변수에 복사한다.
- 14 Line: 버튼이 눌린 상태이면 LED를 켜고, 아니면 끈다.
- 15 Line: 받은 데이터를 시리얼 모니터에 출력한다.
- 21 Line: Wi-Fi를 스테이션 모드로 설정한다.
- 23~26 Line: ESP-NOW를 초기화한다.
- 27 Line: 데이터를 받았을 때 실행할 콜백 함수를 등록한다.
- 30~31 Line: 데이터 수신은 콜백 함수에서 처리하므로 loop()에는 코드가 없다.
보드 패키지 2.x 버전을 사용한다면 12 Line의 콜백 함수 첫 번째 인자를 const uint8_t *mac으로 바꿔야 컴파일된다. 두 보드에 각각 코드를 업로드하고 송신기의 버튼을 누르면, 공유기 없이도 수신기의 LED가 거의 바로 켜지는 것을 확인할 수 있다.
[이미지: 송신기 버튼을 누르자 수신기 LED가 켜진 모습과 시리얼 모니터 출력]
3) 여러 대와 통신하기
- 1:N 전송: 송신기에서 여러 수신기의 MAC 주소를 각각 esp_now_add_peer()로 등록한 후, esp_now_send()의 첫 번째 인자에 원하는 수신기의 주소를 넣거나 NULL을 넣으면 등록된 모든 기기로 보낸다.
- N:1 수신: 여러 송신기가 같은 수신기 주소로 데이터를 보내면 되고, 수신기에서는 콜백 함수의 info->src_addr로 어느 송신기가 보냈는지 구분할 수 있다.
- 양방향 통신: 두 보드가 서로의 MAC 주소를 상대 기기로 등록하고, 송신 코드와 수신 콜백 함수를 모두 넣으면 된다.
6. 주요 용도 및 주의사항
추천 주요 용도:
- 무선 리모컨: RC카, 로봇, 조명, 블라인드 등을 버튼이나 조이스틱으로 제어하는 무선 리모컨을 만들 수 있다.
- 무선 센서 네트워크: 여러 곳에 배치한 온습도 센서, 문열림 센서의 값을 한 대의 ESP32로 모을 수 있다.
- 배터리 센서: 깨어나서 측정하고 데이터를 보낸 후 바로 딥슬립에 들어가는 방식으로 배터리 수명을 크게 늘릴 수 있다.
- 공유기가 없는 장소: 야외, 창고, 작업장처럼 공유기가 없는 곳에서 기기끼리 통신하는 장치를 만들 수 있다.
사용 시 주의사항:
- Wi-Fi 채널 일치: ESP-NOW와 공유기 연결을 함께 사용하는 보드는 공유기의 채널로 동작하므로, 다른 보드들도 같은 채널을 사용하도록 설정해야 통신이 된다.
- 데이터 크기 제한: 한 번에 250바이트까지만 보낼 수 있으므로 큰 데이터는 나누어 보내야 한다.
- 전송 보장 없음: 데이터를 받지 못해도 자동으로 다시 보내지 않으므로, 중요한 데이터는 전송 완료 콜백으로 결과를 확인하고 실패하면 다시 보내도록 만들어야 한다.
- 콜백 함수 처리: 수신 콜백 함수는 Wi-Fi 작업 중에 실행되므로 delay()나 오래 걸리는 작업을 넣지 말고, 데이터만 저장한 후 loop()에서 처리하는 것이 좋다.
- 보안: 암호화하지 않은 데이터는 다른 기기에서도 받을 수 있으므로 도어락처럼 보안이 필요한 장치에는 암호화(encrypt) 기능을 사용해야 한다.
구매좌표:
'각종 부품 사용방법 > 프로세서' 카테고리의 다른 글
| 아두이노 우노 R4 WiFi 사양과 사용방법(UNO R3 비교) (0) | 2026.10.21 |
|---|---|
| 아두이노 나노 R4 사양과 주요 용도(Nano R3 비교) (0) | 2026.10.20 |
| 아두이노 UNO Q 소개와 사용방법 (0) | 2026.10.08 |
| ESP32 타입 종류와 용도 가이드: 내 프로젝트에 맞는 보드 선택법 (0) | 2026.09.19 |
| 아두이노 나노 33 시리즈 비교 (0) | 2021.08.23 |









