Giới thiệu series “Lập trình ESP32 38 Pin & NRF” – Phần 9
Ở các phần trước, chúng ta tập trung gửi dữ liệu cảm biến một chiều từ node → gateway:
- Phần 4–5: Gửi chuỗi, gửi struct giữa 2 ESP32 + NRF24L01.
- Phần 6–7: Mạng nhiều node cảm biến, thêm cảm biến thực tế (DHT, MQ-2).
- Phần 8: ESP32 Gateway đẩy dữ liệu lên MQTT / IoTLabs Cloud.
Trong Phần 9, chúng ta “đảo chiều”:
Dùng ESP32 + NRF24L01 để tạo bộ remote điều khiển từ xa, bật/tắt Relay/LED ở một ESP32 khác.
Ứng dụng thực tế:
- Bật/tắt đèn từ xa.
- Điều khiển ổ cắm, quạt, bơm mini… (qua relay).
- Làm remote cho xe/robot, cửa, thiết bị IoT trong nhà.
1. Mục tiêu bài học
Sau bài này, bạn sẽ:
- Hiểu mô hình TX/RX điều khiển từ xa với NRF24L01.
- Thiết kế được gói tin điều khiển dạng
struct(command, value…). - Viết code cho:
- Bộ TX (Remote): ESP32 + NRF + nút nhấn → gửi lệnh.
- Bộ RX (Thiết bị): ESP32 + NRF → nhận lệnh → điều khiển LED/Relay.
- Mở rộng được lên nhiều nút bấm, nhiều relay.
2. Kiến trúc hệ thống Remote điều khiển
Chúng ta sẽ dùng 2 bộ ESP32 + NRF24L01:
[Remote TX]
ESP32 38 Pin + NRF24L01
+ 2–4 nút nhấn (Button 1, Button 2, ...)
↓ (NRF24L01 – lệnh điều khiển)
[Thiết bị RX]
ESP32 38 Pin + NRF24L01
+ LED / Relay 1 / Relay 2 / ...
- TX (Remote): đọc trạng thái nút nhấn → gửi gói
ControlPacketqua NRF. - RX (Thiết bị): nhận gói → giải mã lệnh → bật/tắt/đảo trạng thái LED/Relay tương ứng.
Kiểu lệnh đơn giản:
command = 1: Toggle (đảo trạng thái) LED/Relay 1.command = 2: Toggle LED/Relay 2.- (hoặc bạn có thể dùng thêm
value= 0/1 để ON/OFF rõ ràng).
3. Chuẩn bị phần cứng
3.1. Phần cứng cho bộ Remote (TX)
- 1 x ESP32 DevKit 38 pin.
- 1 x NRF24L01 (8 chân).
- 2–4 x Nút nhấn (push button).
- Điện trở kéo xuống/kéo lên cho nút (hoặc dùng INPUT_PULLUP).
- Breadboard + dây Dupont.
3.2. Phần cứng cho bộ Thiết bị (RX)
- 1 x ESP32 DevKit 38 pin.
- 1 x NRF24L01 (8 chân).
- 1–2 x LED + điện trở hạn dòng (220Ω–1kΩ) hoặc module Relay 5V.
- Nguồn phù hợp cho relay nếu điều khiển tải thực (nhớ cách ly, an toàn điện).
⚠️ Lưu ý:
- Khi làm với relay + tải 220V, nhớ tuân thủ an toàn điện, cách ly tốt.
- Trong bài demo, bạn có thể chỉ dùng LED thay cho relay để test logic.
4. Nối chân NRF24L01 với ESP32 (TX & RX giống nhau)
Cả TX và RX đều dùng chung cấu hình như các bài trước:
| NRF24L01 | ESP32 DevKit 38-pin | Ghi chú |
|---|---|---|
| GND | GND | GND chung |
| VCC | 3V3 | Chỉ dùng 3.3V, không dùng 5V |
| CE | GPIO 4 | CE |
| CSN (CS) | GPIO 5 | CS |
| SCK | GPIO 18 | VSPI SCK |
| MOSI | GPIO 23 | VSPI MOSI |
| MISO | GPIO 19 | VSPI MISO |
| IRQ | (Không nối) | Bỏ trống |
Đừng quên gắn tụ 10–47µF giữa VCC–GND của NRF để giảm nhiễu nguồn.
5. Thiết kế gói tin điều khiển (ControlPacket)
Chúng ta tách gói điều khiển với gói cảm biến:
struct ControlPacket {
uint8_t nodeId; // ID của remote (neu sau nay co nhieu remote)
uint8_t command; // Ma lenh (1,2,3,...)
uint8_t target; // Muc tieu: den nao / relay nao
uint8_t value; // Gia tri: 0 = OFF, 1 = ON, 2 = TOGGLE
};
Trong bài demo, ta dùng đơn giản:
nodeId: 1 (remote chính).command: có thể bằngtarget, hoặc bạn tách rời tùy cách thiết kế.target: 1 = LED/Relay 1, 2 = LED/Relay 2.value: 2 = TOGGLE (đảo trạng thái), 0/1 nếu muốn ON/OFF cụ thể.
6. Code Remote (TX) – ESP32 + NRF + Nút nhấn
6.1. Gán chân nút nhấn & NRF
Ví dụ: 2 nút nhấn điều khiển 2 LED/Relay.
#include <Arduino.h>
#include <SPI.h>
#include <RF24.h>
// Chan NRF24L01
#define PIN_NRF_CE 4
#define PIN_NRF_CSN 5
// Chan nut nhan
#define BTN1_PIN 14 // Button 1
#define BTN2_PIN 27 // Button 2
RF24 radio(PIN_NRF_CE, PIN_NRF_CSN);
// Dia chi pipe
const byte address[6] = "00001";
struct ControlPacket {
uint8_t nodeId;
uint8_t command;
uint8_t target;
uint8_t value;
};
ControlPacket packet;
// Bien luu trang thai truoc do cua nut (de chong doi)
bool lastBtn1 = HIGH;
bool lastBtn2 = HIGH;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println(F("=== Remote ESP32 + NRF - Control Relay/LED ==="));
pinMode(BTN1_PIN, INPUT_PULLUP);
pinMode(BTN2_PIN, INPUT_PULLUP);
if (!radio.begin()) {
Serial.println(F("Loi: Khong khoi tao duoc NRF24L01 (TX)"));
while (1) { delay(1000); }
}
radio.setChannel(108);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_1MBPS);
radio.openWritingPipe(address);
radio.stopListening();
packet.nodeId = 1;
Serial.println(F("Remote san sang. Nhan nut de gui lenh..."));
}
6.2. Hàm gửi lệnh
void sendCommand(uint8_t target, uint8_t value) {
packet.command = 1; // demo: 1 = lenh dieu khien thiet bi
packet.target = target;
packet.value = value;
bool ok = radio.write(&packet, sizeof(packet));
Serial.print(F("Gui lenh: target="));
Serial.print(target);
Serial.print(F(", value="));
Serial.print(value);
Serial.print(F(" -> "));
Serial.println(ok ? F("OK") : F("FAIL"));
}
6.3. loop() – đọc nút & gửi TOGGLE
void loop() {
bool btn1 = digitalRead(BTN1_PIN);
bool btn2 = digitalRead(BTN2_PIN);
// Phat hien nhan xuong (HIGH -> LOW vi dung INPUT_PULLUP)
if (lastBtn1 == HIGH && btn1 == LOW) {
// Nut 1: dieu khien target 1, TOGGLE
sendCommand(1, 2); // 2 = TOGGLE
}
if (lastBtn2 == HIGH && btn2 == LOW) {
// Nut 2: dieu khien target 2, TOGGLE
sendCommand(2, 2); // 2 = TOGGLE
}
lastBtn1 = btn1;
lastBtn2 = btn2;
delay(50); // chong doi / debouce don gian
}
7. Code Thiết bị (RX) – ESP32 + NRF điều khiển LED/Relay
7.1. Gán chân LED/Relay & NRF
Giả sử bạn điều khiển 2 tải:
- LED/Relay 1 → GPIO 26.
- LED/Relay 2 → GPIO 25.
#include <Arduino.h>
#include <SPI.h>
#include <RF24.h>
// Chan NRF24L01
#define PIN_NRF_CE 4
#define PIN_NRF_CSN 5
// Chan LED/Relay
#define RELAY1_PIN 26
#define RELAY2_PIN 25
RF24 radio(PIN_NRF_CE, PIN_NRF_CSN);
const byte address[6] = "00001";
struct ControlPacket {
uint8_t nodeId;
uint8_t command;
uint8_t target;
uint8_t value;
};
ControlPacket packet;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println(F("=== Device ESP32 + NRF - Relay/LED Receiver ==="));
pinMode(RELAY1_PIN, OUTPUT);
pinMode(RELAY2_PIN, OUTPUT);
// Mac dinh tat
digitalWrite(RELAY1_PIN, LOW);
digitalWrite(RELAY2_PIN, LOW);
if (!radio.begin()) {
Serial.println(F("Loi: Khong khoi tao duoc NRF24L01 (RX)"));
while (1) { delay(1000); }
}
radio.setChannel(108);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_1MBPS);
radio.openReadingPipe(1, address);
radio.startListening();
Serial.println(F("Device RX san sang nhan lenh..."));
}
7.2. Hàm xử lý lệnh
void handleCommand(const ControlPacket& p) {
Serial.print(F("Nhan lenh tu node "));
Serial.print(p.nodeId);
Serial.print(F(" - target="));
Serial.print(p.target);
Serial.print(F(", value="));
Serial.println(p.value);
uint8_t pin = 0;
if (p.target == 1) {
pin = RELAY1_PIN;
} else if (p.target == 2) {
pin = RELAY2_PIN;
} else {
Serial.println(F("Target khong hop le, bo qua!"));
return;
}
// value: 0 = OFF, 1 = ON, 2 = TOGGLE
if (p.value == 0) {
digitalWrite(pin, LOW);
} else if (p.value == 1) {
digitalWrite(pin, HIGH);
} else if (p.value == 2) {
// TOGGLE
digitalWrite(pin, !digitalRead(pin));
}
}
7.3. loop() – nhận gói NRF & gọi xử lý
void loop() {
if (radio.available()) {
while (radio.available()) {
radio.read(&packet, sizeof(packet));
handleCommand(packet);
}
}
delay(10);
}
8. Mở rộng: nhiều phím, nhiều chế độ điều khiển
Một khi mô hình cơ bản đã chạy, bạn có thể mở rộng:
8.1. Thêm nhiều nút & nhiều tải
- Tăng số BTN_PIN ở remote.
- Tăng số RELAY_PIN ở thiết bị.
- Mở rộng
target= 1,2,3,4,…
8.2. Điều khiển chế độ giữ (ON/OFF rõ ràng)
Ngoài TOGGLE, bạn có thể:
- Bấm giữ → ON.
- Thả ra → OFF.
Ví dụ:
- Khi nút nhấn đang nhấn (LOW): gửi
value = 1(ON). - Khi nút nhả (HIGH): gửi
value = 0(OFF).
8.3. Kết hợp 2 chiều: vừa cảm biến, vừa điều khiển
- Node cảm biến vẫn gửi
SensorPacketvề gateway. - Gateway hoặc MQTT Client gửi ngược
ControlPacketđể điều khiển thiết bị. - Khi đó, ESP32 ở “thiết bị” có thể vừa là node, vừa là remote cho thiết bị khác.
9. Kết luận & định hướng phần tiếp theo
Trong Phần 9 của series “Lập trình ESP32 38 Pin & NRF”, bạn đã:
- Xây dựng thành công bộ remote điều khiển từ xa dùng 2 ESP32 + NRF24L01.
- Thiết kế
ControlPacketđể gửi lệnh điều khiển đơn giản (target + value). - Viết code cho TX (Remote) đọc nút nhấn và RX (Thiết bị) điều khiển LED/Relay.
Từ đây, bạn có thể:
- Nâng cấp thành remote đa kênh điều khiển nhiều thiết bị.
- Kết hợp với MQTT / IoTLabs Cloud để điều khiển từ web / mobile app → gateway ESP32 → thiết bị.


