ESP-NOW — cum vorbesc 2 plăci ESP32 direct prin aer, fără router
Fără WiFi, fără server, fără cloud. ESP-NOW e ca walkie-talkies între ESP32-uri: peer la peer, 5 linii de cod, channel 1. Aici înveți de ce mesajele se pot pierde și cum să nu-ți pese.
Ce înveți
- Diferența între WiFi cu router și ESP-NOW peer-to-peer
- Ce-i un MAC address broadcast (FF:FF:FF:FF:FF:FF)
- De ce trebuie să forțezi același channel pe toate plăcile
- Cum 'descoperi' vecini cu mesaje ALIVE la fiecare 2 secunde
- De ce mesajele se pot pierde și ce faci (trimite de 3 ori)
Ce ai nevoie
Înainte de această lecție: ai trecut Test 1 (placa N1 singură) și Test 2 (microfon I2S adăugat). Acum N1 vorbește cu N2 — fără router, fără internet. Magia se cheamă ESP-NOW.
De ce ESP-NOW și nu WiFi normal?
Imaginează-ți că vrei să vorbești cu prietenul tău în parc. Două variante:
Varianta WiFi normal — luați amândoi telefoanele, vă conectați la router-ul de acasă, deschideți Whatsapp, scrieți mesaj. Mesajul tău călătorește: telefon tău → router → server Whatsapp → router → telefon prieten. 3 hop-uri, are nevoie de internet, ia 200ms+.
Varianta walkie-talkie — luați amândoi walkie-talkies, alegeți același canal, apăsați și vorbiți. 0 hop-uri, nu există router, nu există internet, 5ms.
ESP-NOW = walkie-talkie pentru ESP32. Plăcile vorbesc direct între ele prin radio, fără router, fără internet, fără configurare.
Acoperire reală: ~200m line-of-sight (vezi de la o casă la cealaltă), ~30-50m printr-un perete, ~10m prin 2 pereți. Fără antene externe. Suficient pentru macheta orașului tău și mult peste asta.
Ca să vorbească 2 ESP32 prin ESP-NOW
Ai nevoie de:
- WiFi-ul pornit pe mod STA (Station, dar fără să te conectezi la router):
WiFi.mode(WIFI_STA); - Initialize ESP-NOW:
esp_now_init(); - Adaugi un „peer” — adică zici „vreau să trimit mesaje către cineva”:
esp_now_add_peer(...). Cel mai simplu: peer broadcast. - Trimiți mesaje:
esp_now_send(...) - Înregistrezi un callback — codul care rulează când primești un mesaj:
esp_now_register_recv_cb(...)
Cinci linii de cod și ai radio direct între plăci. Restul e doar conținutul mesajelor.
Broadcast MAC: cum trimiți „la toți”
Fiecare ESP32 are un MAC unic (cum AB:CD:EF:01:02:03). Ca să trimiți la cineva anume, ai nevoie de MAC-ul lui. Dar tu nu știi MAC-urile vecinilor înainte să-i întâlnești.
Trucul: există un MAC special — FF:FF:FF:FF:FF:FF — care înseamnă „TOATE
plăcile care ascultă pe canal”. Trimiți o dată acolo, primesc toate vecinii.
Asta-i exact ce face VIA: fiecare placă trimite mesaje ALIVE la broadcast la fiecare 2 secunde. Celelalte plăci primesc, notează MAC-ul expeditorului în lista de „vecini cunoscuți” și pot răspunde direct.
N1 broadcast: "ALIVE N1" → recep. de N2 și N3
N2 broadcast: "ALIVE N2" → recep. de N1 și N3
N3 broadcast: "ALIVE N3" → recep. de N1 și N2
După 3 secunde, toate trei plăcile se cunosc. Fără configurare manuală.
Channel — frecvența radio
Walkie-talkies au „channel” (1, 2, 3…) — frecvența la care merge. Doi walkie-talkies pe channel diferite nu se aud între ele.
ESP-NOW la fel. ESP32 are 13 canale WiFi (1-13). Plăcile trebuie să fie pe ACELAȘI canal ca să se audă.
Problemă comună: când pornești WiFi STA, ESP32 alege canalul „cel mai liniștit” — care poate fi diferit pe fiecare placă. Sau, dacă ai router în apropiere care emite pe channel 6, plăcile se mută acolo.
Soluția: forțezi explicit canalul 1 pe toate plăcile:
#include <esp_wifi.h>
// ...
WiFi.mode(WIFI_STA);
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
Channel 1 e safe — nu se ciocnește cu router-ele clasice (de obicei 6, 11).
Codul minim — N1 vorbește cu N2
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
uint8_t BROADCAST_MAC[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
typedef struct {
char node_id[4]; // "N1\0" sau "N2\0"
uint32_t scor; // contor pieton sau alt număr
} mesaj_t;
mesaj_t outgoing = { "N1", 0 };
unsigned long ultimul_alive = 0;
const unsigned long INTERVAL_ALIVE = 2000; // 2 secunde
// ESP32 Arduino core v3.x — primul parametru e esp_now_recv_info*
void on_recv(const esp_now_recv_info* info, const uint8_t* data, int len) {
mesaj_t* m = (mesaj_t*)data;
Serial.print("RECEPȚIE de la ");
Serial.print(m->node_id);
Serial.print(" — scor: ");
Serial.println(m->scor);
// info->src_addr conține MAC-ul expeditorului dacă-l vrei
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
if (esp_now_init() != ESP_OK) {
Serial.println("EROARE: ESP-NOW init");
while (true) delay(1000);
}
Serial.println("OK: ESP-NOW pornit");
// Adaug broadcast ca peer
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, BROADCAST_MAC, 6);
peer.channel = 1;
peer.encrypt = false;
esp_now_add_peer(&peer);
esp_now_register_recv_cb(on_recv);
}
void loop() {
if (millis() - ultimul_alive >= INTERVAL_ALIVE) {
outgoing.scor++; // sau citește o valoare reală
esp_now_send(BROADCAST_MAC, (uint8_t*)&outgoing, sizeof(outgoing));
Serial.print("Trimis: ");
Serial.print(outgoing.node_id);
Serial.print(" scor ");
Serial.println(outgoing.scor);
ultimul_alive = millis();
}
}
Pe N2: schimbi "N1" cu "N2" în outgoing.node_id. Rest identic.
Flashezi ambele, deschizi 2 Serial Monitor-uri în paralel (sau treci între ele). În 2 secunde ar trebui să vezi pe N1 mesaje primite de la N2 și viceversa.
De ce mesajele se pot pierde (și ce faci)
ESP-NOW e fără confirmare (unacknowledged). Trimiti un mesaj — poate ajunge, poate nu. Cauze:
- Coliziune radio — 2 plăci au transmis exact în același timp
- Interferență — router în apropiere, microunde aprinse
- Distanță prea mare — la limita razei
Soluție simplă: trimite mesajele critice de 3 ori la 50ms diferență. Probabilitate de pierdere a TUTUROR celor 3 = foarte mică.
// Mesaj critic (sirenă detectată)
mesaj_sirena.sirena = true;
for (int i = 0; i < 3; i++) {
esp_now_send(BROADCAST_MAC, &mesaj_sirena, sizeof(mesaj_sirena));
delay(50);
}
Pentru mesajele ALIVE (de descoperire vecini), n-are importanță dacă se pierde unul — vine altul în 2 secunde.
Troubleshoot rapid
| Simptom | Verifici |
|---|---|
| Ambele OLED-uri arată „Vecini: 0” după 30s | esp_now_init() returnează eroare? Verifică cu Serial |
| Plăcile se văd intermitent | Channel diferit → esp_wifi_set_channel(1, ...) pe AMBELE |
| N2 vede pe N1 dar invers nu | Setup asimetric — copy-paste identic, doar NODE_ID diferă |
| Mesaje par a se trimite dar callback nu prinde | esp_now_register_recv_cb() lipsește, sau peer broadcast nu-i adăugat |
Tabel complet: planul VIA — Test 3 troubleshoot.
Recap
5 idei mari:
- ESP-NOW = walkie-talkie pentru ESP32. Vorbesc direct, fără router.
- Broadcast
FF:FF:FF:FF:FF:FFtrimite la toți vecinii care ascultă pe canal. - Channel-ul trebuie forțat pe toate plăcile, altfel se pierd intermitent.
- Mesajele se pot pierde — pentru cele critice, trimiti de 3 ori.
- Descoperire automată: mesaje ALIVE la fiecare 2s. Toate plăcile se găsesc.
Pas următor — Test 4 (3 plăci integrate)
Acum 2 plăci se înțeleg. Adaugi a treia (N3) și testezi scenariile de la concurs (ambulanță, plug-and-play, pe macheta orașului) — vezi planul VIA — Test 4.
După lecția asta, poți
- Faci 2 sau 20 ESP32-uri să vorbească fără router, oriunde în casă (până la 200m line-of-sight)
- Recunoști când e channel mismatch (vecinii apar și dispar fantomatic)
- Folosești callback recepție fără să te pierzi în pointeri C++
- Înțelegi de ce ESP-NOW e baza sistemelor mesh fără cloud (sisteme IoT serioase)