Kết nối không dây

ESP-NOW - giao tiếp ESP32 với ESP32, làm tay cầm và robot bầy đàn

7 phút đọc05/07/2026HocLapTrinhRobot
ESP-NOW - giao tiếp ESP32 với ESP32, làm tay cầm và robot bầy đàn

ESP-NOW gửi gói tin trực tiếp giữa các ESP32 không cần router, trễ vài ms, xa 100m+. Code cặp tay cầm joystick - robot hoàn chỉnh và mẫu robot bầy đàn. Trong bài viết này, chúng ta sẽ cùng tìm hiểu chi tiết và ứng dụng thực tế trong robot.

ESP-NOW là gì và vì sao dân robot mê nó?

ESP-NOW là giao thức không dây riêng của Espressif cho phép các ESP32 (và ESP8266) bắn gói tin trực tiếp cho nhau trên sóng 2.4 GHz: không cần router, không quá trình kết nối WiFi, không TCP/IP. Gói tin đi thẳng từ chip đến chip theo địa chỉ MAC — như tin nhắn bắn giữa hai bộ đàm đã lưu số của nhau.

Ba con số làm nên danh tiếng của nó với robot:

  • Độ trễ ~2-10 ms — nhanh hơn HTTP hàng chục lần, ngang tay cầm RC thương mại
  • Tầm 50-200 m ngoài trời thoáng (xa hơn hẳn kết nối WiFi thông thường vì không cần duy trì phiên)
  • 0 đồng — mọi ESP32 đều có sẵn; cặp tay cầm + robot tự chế trọn bộ ~300.000 đ

Đánh đổi: chỉ nói chuyện được giữa các chip Espressif (điện thoại không tham gia trực tiếp), gói tin tối đa 250 byte — quá đủ cho lệnh điều khiển.

Khái niệm tối thiểu cần nắm

  • Địa chỉ MAC: số danh định 6 byte của mỗi ESP32 (dạng 24:6F:28:xx:xx:xx). Lấy bằng WiFi.macAddress() — bước đầu tiên của mọi dự án ESP-NOW là nạp code in MAC lên hai bo và ghi lại
  • Peer: thiết bị đối tác đã đăng ký — muốn gửi cho ai phải add MAC người đó làm peer trước
  • Callback: hàm được gọi tự động khi nhận gói (onReceive) hoặc khi gửi xong (onSent) — mô hình sự kiện thay vì hỏi vòng
  • Struct chung: hai bên thống nhất một cấu trúc dữ liệu (kiểu và thứ tự field giống hệt nhau) — gói tin chính là bản chụp struct đó

Dự án chuẩn: tay cầm joystick điều khiển robot

Phần cứng tay cầm

ESP32 + module joystick 2 trục (10.000-15.000 đ) + hộp pin — joystick trả 2 giá trị analog (X, Y) vào GPIO 34, 35.

Code bên TAY CẦM (gửi)

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

// MAC của ESP32 TRÊN ROBOT - thay bằng số của bạn
uint8_t robotMac[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};

typedef struct {
  int16_t x;      // -255..255
  int16_t y;
  uint8_t buttons;
} ControlPacket;

ControlPacket packet;

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);            // ESP-NOW cần mode STA
  esp_now_init();

  esp_now_peer_info_t peer = {};
  memcpy(peer.peer_addr, robotMac, 6);
  peer.channel = 0;
  esp_now_add_peer(&peer);
}

void loop() {
  // đọc joystick, quy về -255..255 với vùng chết ở giữa
  int rawX = analogRead(34), rawY = analogRead(35);
  packet.x = map(rawX, 0, 4095, -255, 255);
  packet.y = map(rawY, 0, 4095, -255, 255);
  if (abs(packet.x) < 20) packet.x = 0;   // deadzone chống trôi
  if (abs(packet.y) < 20) packet.y = 0;

  esp_now_send(robotMac, (uint8_t *)&packet, sizeof(packet));
  delay(20);                       // 50 gói/giây - quá đủ mượt
}

Code bên ROBOT (nhận)

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

typedef struct {
  int16_t x;
  int16_t y;
  uint8_t buttons;
} ControlPacket;

volatile int16_t joyX = 0, joyY = 0;
volatile unsigned long lastPacket = 0;

void onReceive(const esp_now_recv_info_t *info,
               const uint8_t *data, int len) {
  if (len == sizeof(ControlPacket)) {
    ControlPacket p;
    memcpy(&p, data, len);
    joyX = p.x;
    joyY = p.y;
    lastPacket = millis();
  }
}

void setup() {
  Serial.begin(115200);
  setupMotors();
  WiFi.mode(WIFI_STA);
  Serial.println(WiFi.macAddress());   // MAC để điền vào code tay cầm
  esp_now_init();
  esp_now_register_recv_cb(onReceive);
}

void loop() {
  if (millis() - lastPacket > 300) {   // FAILSAFE mất sóng
    drive(0, 0);
    return;
  }
  // trộn lái kiểu arcade: Y = ga, X = lái
  int left  = constrain(joyY + joyX, -255, 255);
  int right = constrain(joyY - joyX, -255, 255);
  drive(left, right);
}

Hai khối đáng học nhất: arcade mixing (một joystick lái cả hai bánh — công thức cộng trừ ngắn mà thanh lịch) và failsafe 300 ms (mất sóng là robot dừng, không phóng tiếp vô định — quy tắc an toàn bắt buộc của mọi thiết bị điều khiển từ xa).

Từ một-một đến bầy đàn

ESP-NOW không giới hạn ở cặp đôi:

  • Broadcast: gửi tới MAC FF:FF:FF:FF:FF:FF — mọi ESP32 trong tầm cùng nhận. Một lệnh "xuất phát!" cho cả chục robot đua — nền tảng của màn đồng diễn robot
  • Một tay cầm nhiều robot: thêm trường robotId vào struct, robot chỉ phản ứng gói đúng số mình — chuyển kênh điều khiển bằng nút bấm trên tay cầm
  • Chuỗi tiếp sức (mesh thô): robot A ngoài tầm tay cầm nhưng trong tầm robot B — B nhận và phát tiếp. ESP-NOW không có mesh chính thức nhưng tự dựng tiếp sức 2-3 chặng khá dễ, thành bài học định tuyến trực quan

Robot bầy đàn (swarm) cấp độ học đường — vài robot chia sẻ vị trí, giữ đội hình, tránh nhau — hoàn toàn khả thi trên nền broadcast + struct chung, và là đề tài nghiên cứu KHKT ăn điểm sáng tạo.

Bẫy kỹ thuật cần biết trước

  • API callback đổi theo phiên bản core: core 3.x dùng esp_now_recv_info_t như code trên; core 2.x cũ là (const uint8_t *mac, ...) — code trên mạng lẫn lộn hai đời, lỗi biên dịch callback gần như chắc chắn gặp; sửa theo đúng core đang cài
  • Hai bên phải cùng kênh WiFi: mặc định cùng channel 0 là ổn; nhưng nếu robot đồng thời kết nối WiFi router (dual mode), kênh bị router quyết định — tay cầm phải đặt theo đúng kênh đó
  • Struct hai bên phải khớp từng byte: thêm field một bên mà quên bên kia = dữ liệu lệch pha; giữ file định nghĩa struct chung copy sang cả hai sketch
  • Không mã hóa mặc định: ai biết MAC đều gửi lệnh được; ESP-NOW hỗ trợ mã hóa peer (LMK/PMK) — bật khi dự án ra khỏi phòng lab
  • ADC2 vẫn bị cấm khi radio bật: joystick ở tay cầm cắm vào GPIO 32-39 (ADC1) như code trên

Câu hỏi thường gặp

ESP-NOW và Bluetooth, cái nào điều khiển robot tốt hơn?

Nếu cả hai đầu đều là ESP32: ESP-NOW thắng về trễ, tầm và độ đơn giản giao thức. Bluetooth thắng khi đầu điều khiển là điện thoại — điện thoại không nói được ESP-NOW. Tay cầm tự chế → ESP-NOW; app điện thoại → Bluetooth/BLE (xem bài Điều khiển robot từ xa ở mục Kiến thức).

Gửi được dữ liệu cảm biến ngược từ robot về tay cầm không?

Được — ESP-NOW hai chiều tự nhiên: robot cũng add tay cầm làm peer và send struct telemetry (pin, khoảng cách). Gắn màn OLED lên tay cầm hiện số liệu là có "tay cầm FPV mini" — nâng cấp được yêu thích của học sinh.

250 byte mỗi gói có chật không?

Lệnh điều khiển dùng ~5-10 byte — dư 25 lần. 250 byte chỉ chật khi truyền ảnh/âm thanh, việc vốn không phải đất của ESP-NOW; luồng dữ liệu lớn hãy dùng WiFi thường.

Pin tay cầm dùng được bao lâu?

ESP32 phát ESP-NOW mỗi 20 ms ăn trung bình ~100-150 mA — pin 18650 2500 mAh chạy ~15-20 giờ. Muốn hơn: tăng chu kỳ gửi lên 50 ms khi joystick đứng yên, hoặc light sleep giữa các lần gửi — tay cầm chạy cả tháng cho người chịu tối ưu.

Tài nguyên tải về

Tải bản PDF của bài viết để in giáo án hoặc gửi trước cho học viên.

Tải PDF
Chia sẻ bài viết