IoTLabs

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

Lập trình ESP32 & NRF24L01 – Phần 8: ESP32 Gateway đẩy dữ liệu lên MQTT / IoTLabs Cloud

Giới thiệu series “Lập trình ESP32 38 Pin & NRF” – Phần 8

Ở các phần trước, bạn đã:

  • Dựng được mạng nhiều node cảm biến → 1 gateway ESP32 dùng NRF24L01.
  • Kết hợp cảm biến thực tế như DHT11/DHT22 và MQ-2 (khí gas), gửi dữ liệu bằng struct SensorPacket.

Trong Phần 8, chúng ta sẽ đưa hệ thống ra khỏi phạm vi bàn test bằng cách:

  • Cho ESP32 Gateway kết nối WiFi.
  • Đóng gói dữ liệu thành chuỗi JSON.
  • Publish lên MQTT broker / IoTLabs Cloud để các ứng dụng khác (Dashboard web, mobile app, Node-RED, Home Assistant,…) có thể subscribe và hiển thị.

1. Mục tiêu bài học

Sau bài này, bạn sẽ:

  • Biết cách cấu hình ESP32 kết nối WiFi (SSID, password).
  • Hiểu khái niệm MQTT broker, topic, payload trong bối cảnh IoTLabs Cloud.
  • Publish dữ liệu cảm biến dạng JSON từ gateway ESP32 lên MQTT.
  • Kiểm tra dữ liệu bằng MQTT client (MQTTX, MQTT Explorer, Node-RED, v.v.).

2. Kiến trúc hệ thống: NRF24L01 → ESP32 Gateway → MQTT / IoTLabs Cloud

Kiến trúc tổng quát sau khi thêm MQTT:

[Node 1] ESP32 + NRF + DHT + MQ-2
[Node 2] ESP32 + NRF + ...
[Node 3] ESP32 + NRF + ...
                ↓ (NRF24L01)
        ESP32 Gateway + NRF
                ↓ (WiFi)
          MQTT Broker / IoTLabs Cloud
                ↓
      Dashboard / Mobile App / Node-RED
  • Lớp thiết bị (Device layer): các node cảm biến gửi SensorPacket qua NRF24L01.
  • Lớp gateway: ESP32 nhận struct, chuyển thành JSON.
  • Lớp cloud: MQTT broker (có thể là Mosquitto, HiveMQ, IoTLabs Cloud MQTT,…).

3. Chuẩn bị trước khi code

3.1. Thông tin WiFi & MQTT

Bạn cần chuẩn bị:

  • WiFi:
    • WIFI_SSID: tên mạng WiFi.
    • WIFI_PASSWORD: mật khẩu.
  • MQTT broker:
    • MQTT_SERVER: địa chỉ IP hoặc domain (ví dụ: broker.hivemq.com, mqtt.iotlabs.vn, 192.168.1.100).
    • MQTT_PORT: thường là 1883 (MQTT không SSL).
    • MQTT_USER, MQTT_PASSWORD: nếu broker yêu cầu đăng nhập (nếu không, có thể để trống).
  • Topic MQTT (gợi ý chuẩn IoTLabs):
    • Topic chung cho mạng cảm biến: iotlabs/sensors/gateway1
    • Hoặc mỗi node 1 topic, ví dụ: iotlabs/sensors/gateway1/node/1 iotlabs/sensors/gateway1/node/2

Trong bài này, để đơn giản, ta sử dụng 1 topic chung và embed nodeId trong JSON.

3.2. Thư viện cần cài

Trong Arduino IDE:

  1. Cài PubSubClient (by Nick O’Leary) cho MQTT.
  2. Đã cài RF24 ở các phần trước.

4. Thiết kế JSON cho dữ liệu cảm biến

Từ các phần trước, struct dữ liệu (ví dụ Phần 7):

struct SensorPacket {
  uint8_t  nodeId;
  float    temperature;
  float    humidity;
  float    gas;
  uint32_t uptime;
};

Ta sẽ chuyển sang JSON dạng:

{
  "nodeId": 1,
  "temperature": 27.5,
  "humidity": 60.3,
  "gas": 15.2,
  "uptime": 123,
  "timestamp": 1710000000
}
  • timestamp có thể là millis() hoặc thời gian thực nếu bạn có NTP/RTC (trong bài demo dùng tạm millis()/1000).

5. Code ESP32 Gateway: nhận NRF24L01 và publish MQTT

5.1. Khai báo thông tin WiFi & MQTT

⚠️ Bạn nên để các thông tin này ở file cấu hình riêng hoặc secrets.h khi deploy thực tế.

#include <Arduino.h>
#include <WiFi.h>
#include <SPI.h>
#include <RF24.h>
#include <PubSubClient.h>

// ===== Thong tin WiFi =====
const char* WIFI_SSID     = "YourWiFiSSID";
const char* WIFI_PASSWORD = "YourWiFiPassword";

// ===== Thong tin MQTT =====
const char* MQTT_SERVER   = "192.168.1.100";   // IP broker hoac domain
const uint16_t MQTT_PORT  = 1883;
const char* MQTT_USER     = "";                // de trong neu broker khong yeu cau
const char* MQTT_PASSWORD = "";

// Topic chung
const char* MQTT_TOPIC    = "iotlabs/sensors/gateway1";

// ===== NRF24L01 pins =====
#define PIN_NRF_CE   4
#define PIN_NRF_CSN  5

RF24 radio(PIN_NRF_CE, PIN_NRF_CSN);
WiFiClient espClient;
PubSubClient mqttClient(espClient);

const byte address[6] = "00001";

struct SensorPacket {
  uint8_t  nodeId;
  float    temperature;
  float    humidity;
  float    gas;
  uint32_t uptime;
};

SensorPacket packet;

5.2. Hàm kết nối WiFi

void connectWiFi() {
  Serial.print(F("Dang ket noi WiFi: "));
  Serial.println(WIFI_SSID);

  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  uint8_t retries = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (++retries > 40) { // ~20 giay
      Serial.println();
      Serial.println(F("Khong ket noi duoc WiFi, reset lai ESP32"));
      ESP.restart();
    }
  }

  Serial.println();
  Serial.print(F("Da ket noi WiFi, IP: "));
  Serial.println(WiFi.localIP());
}

5.3. Hàm kết nối MQTT broker

void connectMQTT() {
  mqttClient.setServer(MQTT_SERVER, MQTT_PORT);

  while (!mqttClient.connected()) {
    Serial.print(F("Dang ket noi MQTT..."));

    // Client ID nen unique, co the them chip ID
    String clientId = "ESP32Gateway-" + String((uint32_t)ESP.getEfuseMac(), HEX);

    bool ok;
    if (strlen(MQTT_USER) > 0) {
      ok = mqttClient.connect(clientId.c_str(), MQTT_USER, MQTT_PASSWORD);
    } else {
      ok = mqttClient.connect(clientId.c_str());
    }

    if (ok) {
      Serial.println(F("OK"));
    } else {
      Serial.print(F("Fail, rc="));
      Serial.print(mqttClient.state());
      Serial.println(F(" -> thu lai sau 3s"));
      delay(3000);
    }
  }
}

5.4. Hàm publish JSON lên MQTT

void publishSensorPacket(const SensorPacket& p) {
  // Tao JSON don gian bang snprintf
  char payload[256];
  unsigned long ts = millis() / 1000; // timestamp tam thoi

  snprintf(payload, sizeof(payload),
           "{\"nodeId\":%u,\"temperature\":%.2f,\"humidity\":%.2f,\"gas\":%.2f,\"uptime\":%lu,\"timestamp\":%lu}",
           p.nodeId,
           p.temperature,
           p.humidity,
           p.gas,
           (unsigned long)p.uptime,
           ts);

  Serial.print(F("MQTT publish -> "));
  Serial.print(MQTT_TOPIC);
  Serial.print(F(" : "));
  Serial.println(payload);

  if (!mqttClient.publish(MQTT_TOPIC, payload)) {
    Serial.println(F("Canh bao: publish that bai!"));
  }
}

5.5. setup() – khởi tạo tất cả

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println(F("=== ESP32 NRF Gateway -> MQTT / IoTLabs Cloud ==="));

  // Ket noi WiFi
  connectWiFi();

  // Khoi tao NRF24L01
  if (!radio.begin()) {
    Serial.println(F("Loi: Khong khoi tao duoc NRF24L01"));
    while (1) { delay(1000); }
  }

  radio.setChannel(108);
  radio.setPALevel(RF24_PA_LOW);
  radio.setDataRate(RF24_1MBPS);

  radio.openReadingPipe(1, address);
  radio.startListening();

  // Ket noi MQTT
  connectMQTT();
}

5.6. loop() – đọc NRF và đẩy MQTT

void loop() {
  // Duy tri ket noi MQTT
  if (!mqttClient.connected()) {
    connectMQTT();
  }
  mqttClient.loop();

  // Neu co du lieu tu NRF
  if (radio.available()) {
    while (radio.available()) {
      radio.read(&packet, sizeof(packet));

      Serial.print(F("NRF RX - node="));
      Serial.print(packet.nodeId);
      Serial.print(F(", T="));
      Serial.print(packet.temperature, 1);
      Serial.print(F(", H="));
      Serial.print(packet.humidity, 1);
      Serial.print(F(", Gas="));
      Serial.print(packet.gas, 1);
      Serial.print(F(", Uptime="));
      Serial.println(packet.uptime);

      // Publish len MQTT
      publishSensorPacket(packet);
    }
  }

  delay(10);
}

Đến đây, mỗi lần gateway nhận được một gói SensorPacket từ bất kỳ node nào, nó sẽ:

  1. In log ra Serial.
  2. Đóng gói thành JSON.
  3. Publish lên topic iotlabs/sensors/gateway1 trên MQTT broker / IoTLabs Cloud.

6. Kiểm tra dữ liệu bằng MQTT Client

Sau khi nạp firmware cho gateway và những node cảm biến trước đó:

  1. Đảm bảo gateway đã kết nối WiFi và MQTT thành công (xem log Serial).
  2. Mở một MQTT client trên máy tính hoặc điện thoại, ví dụ:
    • MQTTX
    • MQTT Explorer
    • Node-RED (node mqtt in)
  3. Cấu hình:
    • Host: MQTT_SERVER
    • Port: MQTT_PORT
    • User/Password nếu có
  4. Subscribe đến topic: iotlabs/sensors/gateway1
  5. Bạn sẽ thấy các payload JSON xuất hiện liên tục, ví dụ: {"nodeId":1,"temperature":27.50,"humidity":61.20,"gas":12.30,"uptime":345,"timestamp":1710000123} {"nodeId":2,"temperature":29.10,"humidity":55.80,"gas":25.60,"uptime":210,"timestamp":1710000126}

Từ đây, bạn có thể:

  • Lưu vào database (InfluxDB, MySQL,…).
  • Vẽ biểu đồ trên Grafana.
  • Tạo rule cảnh báo (notify Telegram, email,…).

7. Một số lưu ý & best practices

7.1. Bảo mật MQTT

  • Demo này dùng MQTT không mã hóa (port 1883), phù hợp cho mạng LAN / lab.
  • Khi triển khai thật, nên dùng MQTT over TLS (port 8883) + user/password hoặc token.
  • IoTLabs Cloud có thể cung cấp MQTT TLS + ACL cho từng thiết bị / project.

7.2. Độ tin cậy khi publish

  • PubSubClient sử dụng QoS 0 (mặc định). Với dữ liệu realtime liên tục (sensor), QoS 0 thường đủ.
  • Nếu cần đảm bảo hơn, bạn có thể triển khai thêm cơ chế queue lại khi mất MQTT (lưu tạm trong RAM/Flash rồi gửi bù).

7.3. Tối ưu payload JSON

  • Nếu băng thông hạn chế, có thể:
    • Rút gọn key: n, t, h, g, u, ts.
    • Sử dụng định dạng nhị phân riêng (nhưng JSON dễ debug hơn).

8. Bài tập mở rộng

8.1. Topic riêng cho từng node

  • Thay vì 1 topic chung, bạn có thể publish theo: char topic[64]; snprintf(topic, sizeof(topic), "iotlabs/sensors/gateway1/node/%u", packet.nodeId); mqttClient.publish(topic, payload);
  • Sau đó trên dashboard, bạn tách theo từng node dễ dàng hơn.

8.2. Thêm trạng thái gateway

  • Định kỳ (ví dụ mỗi 30 giây), gateway publish thêm một topic health: iotlabs/gateway1/status Payload chứa: {"ip":"192.168.1.50","rssi":-62,"uptime":600}

8.3. Tích hợp Node-RED / Dashboard web

  • Dùng Node-RED:
    • mqtt injsonui_gauge / ui_chart để hiển thị nhiệt độ, độ ẩm, gas.
  • Dùng web app riêng của IoTLabs:
    • Backend subscribe MQTT.
    • Lưu dữ liệu vào DB.
    • Frontend (React/Vue) gọi API để hiển thị.

9. Kết luận & bước tiếp theo

Trong Phần 8 của series “Lập trình ESP32 38 Pin & NRF”, bạn đã:

  • Biến ESP32 Gateway thành cầu nối giữa mạng NRF24L01MQTT / IoTLabs Cloud.
  • Kết nối ESP32 với WiFi, MQTT broker.
  • Đóng gói dữ liệu cảm biến từ nhiều node thành JSON và publish lên topic.
  • Kiểm tra dữ liệu bằng các MQTT client và hình dung cách xây dựng dashboard.

Từ đây, bạn có thể dễ dàng:

  • Tạo hệ thống giám sát môi trường online.
  • Gửi cảnh báo qua Telegram / Zalo / email.
  • Tích hợp với các dự án lớn hơn trong hệ sinh thái IoTLabs.

Ở các phần tiếp theo (gợi ý):

  • Phần 9: Thiết kế dashboard web đơn giản hiển thị dữ liệu từ IoTLabs MQTT.
  • Phần 10: Tối ưu năng lượng cho node cảm biến (Deep Sleep, pin, năng lượng mặt trời,…).

Hẹn gặp bạn trong những phần tiếp theo trên iotlabs.vn!