Phát hiện chuyển động bằng sóng Wi-Fi (CSI) trên ESP32: không cần cảm biến

Giải thích Wi-Fi CSI và hướng dẫn dùng ESP32 phát hiện người di chuyển trong phòng chỉ bằng sóng Wi-Fi từ router: lấy dữ liệu CSI, tính điểm chuyển động, đặt ngưỡng và xem trên Serial Plotter.

ESP32 có thể phát hiện người di chuyển trong phòng mà không cần gắn thêm cảm biến nào, chỉ bằng cách theo dõi tín hiệu Wi-Fi nhận từ router. Kỹ thuật này dựa trên CSI (Channel State Information, thông tin trạng thái kênh): khi có người đi lại, đường truyền của sóng Wi-Fi thay đổi và ESP32 đo được sự thay đổi đó. Bài viết giải thích nguyên lý, cách lấy dữ liệu CSI trên Arduino IDE và một thuật toán đơn giản để báo có chuyển động.

Đây là bài thử nghiệm nâng cao. Kết quả phụ thuộc nhiều vào căn phòng, router và vị trí đặt mạch; kỹ thuật này dùng để học và thử nghiệm, chưa thay được cảm biến chuyên dụng trong các ứng dụng cần độ tin cậy.

1. Sóng Wi-Fi phát hiện chuyển động thế nào?

Nguyên lý phát hiện chuyển động bằng Wi-Fi CSI: sóng từ router tới ESP32 theo đường thẳng và các đường phản xạ từ tường, trần và cơ thể người
Sóng Wi-Fi tới ESP32 theo nhiều đường; người di chuyển làm thay đổi các đường phản xạ (hình vẽ minh họa).
  • Trong nhà, sóng Wi-Fi từ router tới ESP32 theo nhiều đường cùng lúc: đường thẳng và các đường phản xạ từ tường, trần, đồ đạc, cơ thể người.
  • Wi-Fi chia kênh truyền thành nhiều sóng mang con. Với mỗi khung nhận được, ESP32 ước lượng biên độ và pha của từng sóng mang con. Tập số liệu đó là CSI.
  • Khi phòng yên tĩnh, CSI gần như không đổi từ khung này sang khung khác. Khi có người di chuyển, các đường phản xạ đổi theo và biên độ CSI dao động.
  • So với RSSI chỉ là một con số cho cả khung, CSI có hàng chục giá trị nên nhạy với thay đổi nhỏ hơn nhiều.

2. Dữ liệu CSI trên ESP32

Hình minh họa biên độ CSI của 64 sóng mang con trong một khung Wi-Fi trên ESP32: hai nhóm sóng mang có dữ liệu, nhóm sóng mang rỗng ở rìa kênh và bốn byte đầu không hợp lệ
Biên độ của 64 sóng mang con trong một khung CSI (hình minh họa, vẽ theo công thức).
  • Bài dùng phần CSI lấy từ trường LLTF của khung, có ở mọi khung Wi-Fi kiểu OFDM. Phần này gồm 64 sóng mang con, mỗi sóng mang hai byte có dấu: phần ảo rồi phần thực.
  • Thứ tự trong bộ đệm: sóng mang 0 đến 31, sau đó là -32 đến -1. Sóng mang ở giữa kênh và ở hai rìa kênh không mang dữ liệu nên có biên độ bằng 0.
  • Trên ESP32, bốn byte đầu của bộ đệm không hợp lệ, nên chương trình bỏ qua hai sóng mang đầu.
  • ESP32 chỉ có CSI khi nhận được khung. Để có dữ liệu đều đặn, chương trình ping router 20 lần mỗi giây; mỗi lần router trả lời là một khung có CSI.
  • Chương trình chỉ dùng khung do chính router đang kết nối phát ra (so địa chỉ MAC), để dữ liệu của các thiết bị khác không lẫn vào.

3. Chuẩn bị và bố trí

Bố trí: đặt ESP32 cách router khoảng 2 đến 5 m, sao cho khu vực cần theo dõi nằm giữa hoặc gần đường nối hai thiết bị. Đặt cả hai cố định, cao hơn mặt bàn, không để sát vật kim loại lớn. Mỗi lần dời mạch hoặc router cần hiệu chuẩn lại.

4. Chương trình 1: xem dữ liệu CSI

Sửa tên Wi-Fi và mật khẩu rồi nạp chương trình. Mỗi giây chương trình in số khung CSI nhận được và biên độ của sáu sóng mang con:

/*
  PYWORLD.VN - Wi-Fi CSI trên ESP32: xem dữ liệu trạng thái kênh
  ESP32 kết nối router, ping router 20 lần mỗi giây và in biên độ CSI của vài sóng mang con
  Chỉ cần mạch ESP32 và một router Wi-Fi 2,4 GHz
*/

#include <WiFi.h>
#include <esp_wifi.h>
#include <ping/ping_sock.h>
#include <lwip/ip_addr.h>

const char *WIFI_SSID = "TEN_WIFI";
const char *WIFI_PASS = "MAT_KHAU_WIFI";

#define SO_SONG_MANG 64                 // Phần LLTF của một khung có 64 sóng mang con
const uint32_t CHU_KY_PING_MS = 50;     // 20 lần mỗi giây

uint8_t macRouter[6];
float bienDo[SO_SONG_MANG];
volatile int rssiMoi = 0;
volatile uint32_t soKhung = 0;
portMUX_TYPE khoaDuLieu = portMUX_INITIALIZER_UNLOCKED;

// Được gọi mỗi khi ESP32 nhận một khung Wi-Fi có kèm CSI. Chạy trong tác vụ Wi-Fi nên phải ngắn gọn
void khiCoCsi(void *thamSo, wifi_csi_info_t *thongTin) {
  if (thongTin == NULL || thongTin->buf == NULL || thongTin->len < 2 * SO_SONG_MANG) {
    return;
  }
  if (memcmp(thongTin->mac, macRouter, 6) != 0) {
    return;   // Chỉ dùng khung do router đang kết nối phát ra
  }

  // Mỗi sóng mang con là hai byte có dấu: phần ảo rồi phần thực
  const int8_t *duLieu = thongTin->buf;
  portENTER_CRITICAL(&khoaDuLieu);
  for (int k = 0; k < SO_SONG_MANG; k++) {
    float phanAo = duLieu[2 * k];
    float phanThuc = duLieu[2 * k + 1];
    bienDo[k] = sqrtf(phanAo * phanAo + phanThuc * phanThuc);
  }
  portEXIT_CRITICAL(&khoaDuLieu);

  rssiMoi = thongTin->rx_ctrl.rssi;
  soKhung = soKhung + 1;
}

// Ping router đều đặn để router trả lời, nhờ đó có khung Wi-Fi đến với nhịp ổn định
void batDauPing() {
  IPAddress cong = WiFi.gatewayIP();
  esp_ping_config_t cauHinh = ESP_PING_DEFAULT_CONFIG();
  IP_ADDR4(&cauHinh.target_addr, cong[0], cong[1], cong[2], cong[3]);
  cauHinh.count = ESP_PING_COUNT_INFINITE;
  cauHinh.interval_ms = CHU_KY_PING_MS;

  esp_ping_callbacks_t hamGoiLai = {};
  esp_ping_handle_t phienPing = NULL;
  if (esp_ping_new_session(&cauHinh, &hamGoiLai, &phienPing) == ESP_OK) {
    esp_ping_start(phienPing);
  }
}

void batCsi() {
  wifi_csi_config_t cauHinh = {};
  cauHinh.lltf_en = true;              // Lấy CSI từ phần LLTF, có ở mọi khung OFDM
  cauHinh.htltf_en = true;
  cauHinh.stbc_htltf2_en = true;
  cauHinh.ltf_merge_en = true;
  cauHinh.channel_filter_en = true;    // Làm mượt giữa các sóng mang con kề nhau
  cauHinh.manu_scale = false;

  esp_wifi_set_csi_config(&cauHinh);
  esp_wifi_set_csi_rx_cb(khiCoCsi, NULL);
  esp_wifi_set_csi(true);
}

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.printf("Dang ket noi WiFi %s", WIFI_SSID);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf("\nDa ket noi, kenh %d, IP: %s\n", WiFi.channel(), WiFi.localIP().toString().c_str());

  memcpy(macRouter, WiFi.BSSID(), 6);
  batCsi();
  batDauPing();
}

void loop() {
  static uint32_t soKhungTruoc = 0;
  delay(1000);

  float banSao[SO_SONG_MANG];
  portENTER_CRITICAL(&khoaDuLieu);
  memcpy(banSao, bienDo, sizeof(banSao));
  portEXIT_CRITICAL(&khoaDuLieu);

  uint32_t soKhungHienTai = soKhung;
  Serial.printf("Khung/giay: %lu | RSSI %d dBm | bien do song mang 6, 12, 20, 44, 52, 58: %.1f %.1f %.1f %.1f %.1f %.1f\n",
                (unsigned long)(soKhungHienTai - soKhungTruoc), rssiMoi,
                banSao[6], banSao[12], banSao[20], banSao[44], banSao[52], banSao[58]);
  soKhungTruoc = soKhungHienTai;
}

Khi phòng yên tĩnh, sáu con số biên độ thay đổi rất ít giữa các dòng. Thử đi lại giữa router và mạch: các con số nhảy rõ rệt. Dòng Khung/giay nên ở quanh 20; nếu bằng 0, xem mục xử lý lỗi.

5. Cách tính điểm chuyển động

Sáu bước xử lý dữ liệu Wi-Fi CSI trên ESP32: khung CSI, biên độ, chuẩn hóa, phương sai trượt, điểm chuyển động và so với ngưỡng
Các bước xử lý từ khung CSI đến quyết định có chuyển động (hình vẽ minh họa).
  1. Biên độ: với mỗi sóng mang con, biên độ bằng căn bậc hai của tổng bình phương phần thực và phần ảo.
  2. Chuẩn hóa: ESP32 tự chỉnh độ lợi máy thu theo từng khung nên biên độ tuyệt đối lúc lớn lúc nhỏ. Chia mỗi biên độ cho trung bình của cả khung để chỉ còn lại hình dạng của đường bao.
  3. Phương sai trượt: với từng sóng mang con, chương trình giữ một trung bình trượt và một phương sai trượt. Phòng yên tĩnh thì phương sai nhỏ; có chuyển động thì phương sai tăng.
  4. Điểm chuyển động: lấy trung bình độ lệch chuẩn của mọi sóng mang con, nhân 100 để ra đơn vị phần trăm.
  5. Ngưỡng: trong 10 giây đầu, chương trình đo mức nền lúc phòng yên tĩnh rồi đặt ngưỡng bằng hai lần mức nền.

6. Chương trình 2: phát hiện chuyển động

/*
  PYWORLD.VN - Phát hiện chuyển động bằng Wi-Fi CSI trên ESP32, không cần cảm biến
  Tính "điểm chuyển động" từ độ dao động biên độ CSI; LED GPIO2 sáng khi điểm vượt ngưỡng
  In dạng "diem:..,nguong:..,chuyen_dong:.." để xem bằng Serial Monitor hoặc Serial Plotter
*/

#include <WiFi.h>
#include <esp_wifi.h>
#include <ping/ping_sock.h>
#include <lwip/ip_addr.h>

const char *WIFI_SSID = "TEN_WIFI";
const char *WIFI_PASS = "MAT_KHAU_WIFI";

#define LED_PIN 2
#define SO_SONG_MANG 64
#define SONG_MANG_DAU 2                 // Bỏ hai sóng mang đầu: bốn byte đầu của khung CSI trên ESP32 không hợp lệ

const uint32_t CHU_KY_PING_MS = 50;         // 20 khung mỗi giây
const float    HE_SO_LAM_MUOT = 0.05;       // Càng nhỏ càng mượt nhưng phản ứng càng chậm
const float    HE_SO_NGUONG   = 2.0;        // Ngưỡng = mức nền lúc phòng yên tĩnh x hệ số này
const float    NGUONG_TOI_THIEU = 0.5;
const unsigned long THOI_GIAN_HIEU_CHUAN_MS = 10000;
const unsigned long THOI_GIAN_GIU_MS = 2000;   // Giữ trạng thái "có chuyển động" thêm 2 giây

uint8_t macRouter[6];
float giaTriTb[SO_SONG_MANG];           // Trung bình trượt của biên độ đã chuẩn hóa
float phuongSai[SO_SONG_MANG];          // Phương sai trượt
bool daKhoiTao = false;
volatile float diemChuyenDong = 0;
volatile uint32_t soKhung = 0;
unsigned long lucBatDauHieuChuan = 0;

void khiCoCsi(void *thamSo, wifi_csi_info_t *thongTin) {
  if (thongTin == NULL || thongTin->buf == NULL || thongTin->len < 2 * SO_SONG_MANG) {
    return;
  }
  if (memcmp(thongTin->mac, macRouter, 6) != 0) {
    return;
  }

  // Bước 1: biên độ từng sóng mang con và trung bình của cả khung
  const int8_t *duLieu = thongTin->buf;
  float bienDo[SO_SONG_MANG];
  float tong = 0;
  int soHopLe = 0;
  for (int k = SONG_MANG_DAU; k < SO_SONG_MANG; k++) {
    float phanAo = duLieu[2 * k];
    float phanThuc = duLieu[2 * k + 1];
    bienDo[k] = sqrtf(phanAo * phanAo + phanThuc * phanThuc);
    if (bienDo[k] > 0) {
      tong += bienDo[k];
      soHopLe++;
    }
  }
  if (soHopLe < 20) {
    return;
  }
  float trungBinhKhung = tong / soHopLe;

  // Bước 2: chuẩn hóa theo trung bình khung để loại ảnh hưởng của bộ tự chỉnh độ lợi,
  //         rồi cập nhật trung bình trượt và phương sai trượt của từng sóng mang con
  float tongDoLech = 0;
  for (int k = SONG_MANG_DAU; k < SO_SONG_MANG; k++) {
    if (bienDo[k] <= 0) {
      continue;   // Sóng mang rỗng ở giữa và hai rìa kênh
    }
    float x = bienDo[k] / trungBinhKhung;
    if (!daKhoiTao) {
      giaTriTb[k] = x;
      phuongSai[k] = 0;
    }
    float lech = x - giaTriTb[k];
    giaTriTb[k] += HE_SO_LAM_MUOT * lech;
    phuongSai[k] = (1.0f - HE_SO_LAM_MUOT) * (phuongSai[k] + HE_SO_LAM_MUOT * lech * lech);
    tongDoLech += sqrtf(phuongSai[k]);
  }
  daKhoiTao = true;

  // Bước 3: điểm chuyển động = độ lệch chuẩn trung bình của các sóng mang con, tính theo phần trăm
  diemChuyenDong = 100.0f * tongDoLech / soHopLe;
  soKhung = soKhung + 1;
}

void batDauPing() {
  IPAddress cong = WiFi.gatewayIP();
  esp_ping_config_t cauHinh = ESP_PING_DEFAULT_CONFIG();
  IP_ADDR4(&cauHinh.target_addr, cong[0], cong[1], cong[2], cong[3]);
  cauHinh.count = ESP_PING_COUNT_INFINITE;
  cauHinh.interval_ms = CHU_KY_PING_MS;

  esp_ping_callbacks_t hamGoiLai = {};
  esp_ping_handle_t phienPing = NULL;
  if (esp_ping_new_session(&cauHinh, &hamGoiLai, &phienPing) == ESP_OK) {
    esp_ping_start(phienPing);
  }
}

void batCsi() {
  wifi_csi_config_t cauHinh = {};
  cauHinh.lltf_en = true;
  cauHinh.htltf_en = true;
  cauHinh.stbc_htltf2_en = true;
  cauHinh.ltf_merge_en = true;
  cauHinh.channel_filter_en = true;
  cauHinh.manu_scale = false;

  esp_wifi_set_csi_config(&cauHinh);
  esp_wifi_set_csi_rx_cb(khiCoCsi, NULL);
  esp_wifi_set_csi(true);
}

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

  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);   // Tắt chế độ tiết kiệm điện của Wi-Fi để khung đến đều
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.printf("Dang ket noi WiFi %s", WIFI_SSID);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf("\nDa ket noi, kenh %d\n", WiFi.channel());

  memcpy(macRouter, WiFi.BSSID(), 6);
  batCsi();
  batDauPing();
  lucBatDauHieuChuan = millis();
  Serial.println("Hieu chuan 10 giay: giu phong yen tinh, khong di lai");
}

void loop() {
  static float tongNen = 0;
  static int soMauNen = 0;
  static float nguong = 0;
  static bool dangChuyenDong = false;
  static unsigned long lanVuotNguong = 0;

  delay(200);
  float diem = diemChuyenDong;

  // Giai đoạn hiệu chuẩn: đo mức nền khi phòng yên tĩnh (bỏ 2 giây đầu để bộ lọc ổn định)
  if (nguong == 0) {
    unsigned long daQua = millis() - lucBatDauHieuChuan;
    if (daQua > 2000 && soKhung > 40) {
      tongNen += diem;
      soMauNen++;
    }
    if (daQua > THOI_GIAN_HIEU_CHUAN_MS + 2000 && soMauNen > 0) {
      float mucNen = tongNen / soMauNen;
      nguong = max(mucNen * HE_SO_NGUONG, NGUONG_TOI_THIEU);
      Serial.printf("Muc nen %.2f, nguong %.2f\n", mucNen, nguong);
    }
    return;
  }

  if (diem > nguong) {
    lanVuotNguong = millis();
    dangChuyenDong = true;
  } else if (dangChuyenDong && millis() - lanVuotNguong > THOI_GIAN_GIU_MS) {
    dangChuyenDong = false;
  }
  digitalWrite(LED_PIN, dangChuyenDong ? HIGH : LOW);

  Serial.printf("diem:%.2f,nguong:%.2f,chuyen_dong:%.2f\n", diem, nguong, dangChuyenDong ? nguong : 0.0f);
}

7. Kết quả mong đợi

Hình minh họa đồ thị điểm chuyển động tính từ Wi-Fi CSI theo thời gian: mức nền thấp khi phòng yên tĩnh, vượt ngưỡng khi có người đi lại và khi có cử động nhỏ
Cách đọc đồ thị điểm chuyển động trên Serial Plotter (hình minh họa, vẽ theo công thức).
Hình minh họa Serial Monitor của hai chương trình Wi-Fi CSI trên ESP32: số khung mỗi giây và biên độ sóng mang con, mức nền, ngưỡng và điểm chuyển động
Kết quả mong đợi trên Serial Monitor của hai chương trình (hình minh họa).
  1. Nạp chương trình, mở Tools > Serial Monitor ở 115200 baud và rời khỏi vùng giữa router với mạch trong 12 giây đầu.
  2. Khi thấy dòng Muc nen ..., nguong ..., đóng Serial Monitor và mở Tools > Serial Plotter để xem ba đường: diem, nguong và chuyen_dong.
  3. Đi lại trong phòng: đường diem vọt lên trên nguong, LED trên mạch (GPIO2) sáng. Đứng yên khoảng 2 giây: LED tắt.

8. Tinh chỉnh

Hằng sốMặc địnhTác dụng khi thay đổi
HE_SO_NGUONG2.0Tăng lên 3 hoặc 4 nếu hay báo giả; giảm xuống 1.5 nếu bỏ sót chuyển động nhỏ
HE_SO_LAM_MUOT0.05Nhỏ hơn thì đường điểm mượt hơn nhưng phản ứng chậm hơn
THOI_GIAN_GIU_MS2000Thời gian giữ trạng thái có chuyển động sau lần vượt ngưỡng cuối
CHU_KY_PING_MS50Giảm xuống 20 để có 50 khung mỗi giây, phản ứng nhanh hơn nhưng chiếm mạng hơn
THOI_GIAN_HIEU_CHUAN_MS10000Hiệu chuẩn lâu hơn cho mức nền ổn định hơn

9. Giới hạn cần biết

  • Phụ thuộc môi trường: quạt đang quay, rèm bay, thú nuôi, cửa mở ra đóng vào đều làm CSI thay đổi và có thể bị báo là chuyển động.
  • Xuyên tường: sóng Wi-Fi đi qua tường nên người đi lại ở phòng bên hoặc hành lang cũng có thể làm điểm tăng.
  • Người đứng yên: giống PIR, cách tính trong bài chỉ nhận ra chuyển động, không nhận ra người ngồi yên.
  • Phụ thuộc router: router đổi kênh, đổi công suất hoặc có thiết bị khác truyền dữ liệu nhiều làm mức nền thay đổi. Khi đó cần khởi động lại mạch để hiệu chuẩn lại.
  • Không dùng cho an ninh: kỹ thuật này có cả báo giả lẫn bỏ sót. Với đèn tự bật hoặc báo động, relay nên được điều khiển bằng cảm biến chuyên dụng, hoặc kết hợp cả hai nguồn tín hiệu.

10. Xử lý lỗi thường gặp

Hiện tượngNguyên nhân thường gặpCách xử lý
Dừng ở dòng Dang ket noi WiFiSai tên, mật khẩu hoặc router chỉ phát 5 GHzKiểm tra lại; ESP32 chỉ dùng Wi-Fi 2,4 GHz. Xem bài kết nối Wi-Fi trên ESP32
Khung/giay bằng 0 hoặc rất thấpRouter không trả lời ping, hoặc đang trả lời bằng kiểu khung không có CSISửa hàm batDauPing() để ping một máy khác đang bật trong cùng mạng; thử router khác
Không bao giờ in dòng Muc nenKhông nhận được khung CSI nào nên chưa hiệu chuẩn đượcChạy chương trình 1 trước để chắc có khung CSI
LED sáng liên tục dù phòng yên tĩnhCó người đi lại lúc hiệu chuẩn nên mức nền sai, hoặc trong phòng có quạt đang quayNhấn nút EN để hiệu chuẩn lại khi phòng yên; tăng HE_SO_NGUONG
Đi lại mà điểm tăng rất ítNgười di chuyển ở xa đường truyền giữa router và mạchDời mạch để khu vực cần theo dõi nằm giữa router và mạch; giảm HE_SO_NGUONG
Lỗi biên dịch ở các hàm esp_wifi_set_csiGói esp32 quá cũ hoặc chọn nhầm loại mạchCập nhật gói esp32; chọn đúng DOIT ESP32 DEVKIT V1 trong Tools > Board

11. Xem thêm


Tin tức liên quan

Phased array siêu âm 40 kHz với ESP32 (phần 2): mạch đệm 12 V, đầu thu và quét búp sóng
Phased array siêu âm 40 kHz với ESP32 (phần 2): mạch đệm 12 V, đầu thu và quét búp sóng

Phần 2 của loạt phased array siêu âm: thêm mạch đệm 12 V bằng hai module L298N, lắp mạch thu dùng LM358, đo biên độ 40 kHz bằng ADC của ESP32 và chạy chương trình tự quét góc lái để xem búp sóng.

Phased array siêu âm 40 kHz với ESP32 (phần 1): nguyên lý lái búp sóng và tạo 8 kênh lệch pha
Phased array siêu âm 40 kHz với ESP32 (phần 1): nguyên lý lái búp sóng và tạo 8 kênh lệch pha

Nguyên lý phased array và cách dùng ESP32 tạo 8 kênh xung 40 kHz lệch pha để lái búp sóng siêu âm: công thức độ trễ, giới hạn do khoảng cách đầu phát, cấu hình LEDC với hpoint, sơ đồ nối và code.

Phát hiện chuyển động bằng cảm biến PIR HC-SR501 với ESP32: tự bật đèn qua relay
Phát hiện chuyển động bằng cảm biến PIR HC-SR501 với ESP32: tự bật đèn qua relay

Hướng dẫn dùng cảm biến chuyển động PIR HC-SR501 với ESP32: nguyên lý, cách chỉnh module, đọc tín hiệu bằng ngắt và ghép relay làm đèn tự bật khi có người, tự tắt sau 60 giây.

Làm gateway LoRa sang MQTT bằng ESP32: node cảm biến chạy pin gửi dữ liệu về broker
Làm gateway LoRa sang MQTT bằng ESP32: node cảm biến chạy pin gửi dữ liệu về broker

Hướng dẫn làm hệ thống LoRa hoàn chỉnh với ESP32 và SX1278: node chạy pin đo điện áp, gửi gói rồi ngủ sâu; gateway nhận gói LoRa và publish dữ liệu JSON lên broker MQTT qua Wi-Fi.

Cấu hình LoRa trên ESP32: SF, băng thông, tỉ lệ mã, công suất và cách thử tầm xa
Cấu hình LoRa trên ESP32: SF, băng thông, tỉ lệ mã, công suất và cách thử tầm xa

Giải thích bốn tham số cấu hình LoRa (SF, băng thông, tỉ lệ mã, công suất), cách tính thời gian phát một gói và hướng dẫn làm bộ thử tầm xa bằng ESP32, SX1278 và màn hình OLED.

Hướng dẫn dùng LoRa SX1278 (Ra-02) với ESP32: nối dây SPI, gửi và nhận gói tin đầu tiên
Hướng dẫn dùng LoRa SX1278 (Ra-02) với ESP32: nối dây SPI, gửi và nhận gói tin đầu tiên

Hướng dẫn nối module LoRa SX1278 Ra-02 433 MHz với ESP32 qua SPI, cài thư viện LoRa trên Arduino IDE, viết chương trình gửi và nhận gói tin, đọc RSSI, SNR và xử lý lỗi thường gặp.

ESP-NOW nhiều node với ESP32: nhiều mạch gửi về một trạm thu, hiển thị trên OLED
ESP-NOW nhiều node với ESP32: nhiều mạch gửi về một trạm thu, hiển thị trên OLED

Hướng dẫn dựng mô hình ESP-NOW nhiều node: tối đa bốn ESP32 gửi số đo về một trạm thu, trạm thu hiện điện áp, RSSI và tình trạng từng node trên màn hình OLED 0.96 inch.

Điều khiển relay từ xa bằng ESP-NOW: remote ESP32 một nút nhấn có phản hồi trạng thái
Điều khiển relay từ xa bằng ESP-NOW: remote ESP32 một nút nhấn có phản hồi trạng thái

Hướng dẫn làm bộ điều khiển relay từ xa bằng hai mạch ESP32 và ESP-NOW hai chiều: remote gửi lệnh, mạch relay báo lại trạng thái, lọc theo địa chỉ MAC và bật mã hóa.


Bình luận
Đã thêm vào giỏ hàng