IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Lập trình ESP32 & NRF24L01 – Phần 9: Remote điều khiển Relay/LED từ xa

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 ControlPacket qua 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:

NRF24L01ESP32 DevKit 38-pinGhi chú
GNDGNDGND chung
VCC3V3Chỉ dùng 3.3V, không dùng 5V
CEGPIO 4CE
CSN (CS)GPIO 5CS
SCKGPIO 18VSPI SCK
MOSIGPIO 23VSPI MOSI
MISOGPIO 19VSPI 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ằng target, 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 SensorPacket về 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 appgateway ESP32thiết bị.