← До всіх проєктів
Бездротовий зв’язок Середній

ESP-NOW: бездротовий пульт між двома ESP32 без роутера

Протокол ESP-NOW дозволяє платам обмінюватися короткими повідомленнями напряму з затримкою в кілька мілісекунд. Робимо кнопку-пульт і приймач зі світлодіодом.

Складність
Середній
Час
1–2 h
Категорія
Бездротовий зв’язок

Що таке ESP-NOW

ESP-NOW — власний протокол Espressif поверх 2.4 ГГц радіо. Плати обмінюються пакетами до 250 байт напряму за MAC-адресою, без точки доступу й без з'єднання:

  • затримка — одиниці мілісекунд;
  • дальність — десятки метрів у приміщенні, 100+ м на відкритому просторі;
  • ідеально для пультів, датчиків на батарейках і зв'язку «багато датчиків → один шлюз».

Компоненти

  • 2 × ESP32 DevKit
  • Світлодіод + резистор 330 Ом (або вбудований LED на GPIO 2)
  • Кнопка — підійде вбудована кнопка BOOT (GPIO 0)

Крок 1. Дізнаємося MAC приймача

Прошийте в плату-приймач код нижче — він виведе свою MAC-адресу в Serial Monitor.

Приймач (receiver)

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

typedef struct {
  uint8_t pressed;
  float chipTemp;
} Message;

const int LED_PIN = 2;

// Сигнатура для ядра arduino-esp32 3.x
void onReceive(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
  if (len != sizeof(Message)) return;
  Message msg;
  memcpy(&msg, data, sizeof(msg));
  digitalWrite(LED_PIN, msg.pressed ? HIGH : LOW);

  const uint8_t *m = info->src_addr;
  Serial.printf("From %02X:%02X:%02X:%02X:%02X:%02X  pressed=%d  chip=%.1f°C\n",
                m[0], m[1], m[2], m[3], m[4], m[5], msg.pressed, msg.chipTemp);
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  WiFi.mode(WIFI_STA);
  Serial.print("Receiver MAC: ");
  Serial.println(WiFi.macAddress());

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW init failed");
    return;
  }
  esp_now_register_recv_cb(onReceive);
}

void loop() {}

Запишіть адресу на кшталт 24:6F:28:AA:BB:CC.

Крок 2. Передавач (sender)

Підставте MAC приймача в receiverMac:

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

uint8_t receiverMac[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};  // ← ваш MAC

typedef struct {
  uint8_t pressed;
  float chipTemp;
} Message;

const int BUTTON_PIN = 0;  // кнопка BOOT
int lastState = HIGH;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW init failed");
    return;
  }

  esp_now_peer_info_t peer = {};
  memcpy(peer.peer_addr, receiverMac, 6);
  peer.channel = 0;      // поточний канал
  peer.encrypt = false;
  if (esp_now_add_peer(&peer) != ESP_OK) {
    Serial.println("Failed to add peer");
  }
}

void loop() {
  int state = digitalRead(BUTTON_PIN);
  if (state != lastState) {
    lastState = state;
    Message msg = { (uint8_t)(state == LOW), temperatureRead() };
    esp_err_t res = esp_now_send(receiverMac, (uint8_t *)&msg, sizeof(msg));
    Serial.println(res == ESP_OK ? "sent" : "send error");
    delay(30);  // простий антидребезг
  }
}

Перевірка

Натисніть BOOT на передавачі — світлодіод на приймачі засвітиться, а в його Serial Monitor з'являться рядки з MAC відправника і температурою чипа.

Важливі нюанси

  • Структура повідомлення має бути однаковою на обох платах — інакше len не збігатиметься.
  • Канал Wi-Fi. Якщо приймач одночасно підключений до роутера, ESP-NOW працює на каналі роутера. Передавач теж має бути на ньому (виставте peer.channel або esp_wifi_set_channel()).
  • Ядро 2.x використовує іншу сигнатуру колбеку: void onReceive(const uint8_t *mac, const uint8_t *data, int len).
  • Для шифрування задайте peer.encrypt = true та ключі esp_now_set_pmk() / peer.lmk.

Куди розвивати

  • Датчик на батарейці: прокинувся → надіслав по ESP-NOW (≈50 мс) → заснув. Це в рази економніше за Wi-Fi.
  • Шлюз «ESP-NOW → MQTT»: одна плата приймає дані з десятків датчиків і пересилає їх у Home Assistant.