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.

Cảm biến PIR HC-SR501 phát hiện người hoặc vật ấm di chuyển trong tầm vài mét, giá rẻ và tiêu thụ rất ít điện, nên được dùng nhiều trong đèn cầu thang, đèn hành lang và báo động. Bài viết hướng dẫn dùng HC-SR501 với ESP32 trên Arduino IDE: đọc tín hiệu bằng ngắt, rồi ghép với module relay để làm đèn tự bật khi có người và tự tắt sau một khoảng thời gian.

1. Cảm biến PIR hoạt động thế nào?

Nguyên lý cảm biến PIR: thấu kính Fresnel chia không gian thành các vùng nhìn xen kẽ, người đi ngang qua làm tín hiệu thay đổi và chân OUT lên mức cao
Cảm biến PIR phát hiện nguồn nhiệt di chuyển qua các vùng nhìn (hình vẽ minh họa).
  • PIR là viết tắt của Passive Infrared, cảm biến hồng ngoại thụ động. Cảm biến không phát ra tia nào, chỉ thu bức xạ hồng ngoại từ các vật ấm.
  • Bên trong có hai phần tử nhạy nhiệt mắc ngược nhau. Khi cả hai nhận cùng một lượng nhiệt (phòng yên tĩnh), tín hiệu triệt tiêu. Khi một nguồn nhiệt di chuyển, hai phần tử nhận lệch nhau và tạo ra xung.
  • Vòm nhựa trắng là thấu kính Fresnel, chia vùng quan sát thành nhiều dải. Vì thế PIR nhạy nhất với chuyển động cắt ngang trước cảm biến và kém nhạy hơn khi người đi thẳng về phía cảm biến.
  • PIR chỉ phát hiện chuyển động. Người ngồi yên lâu sẽ không còn được nhận ra.

2. Tìm hiểu module HC-SR501

Các phần chỉnh trên module PIR HC-SR501: biến trở Sx chỉnh độ nhạy, biến trở Tx chỉnh thời gian giữ, jumper H L chọn chế độ kích và ba chân VCC, OUT, GND
Hai biến trở, jumper và ba chân của HC-SR501 (hình vẽ dạng sơ đồ, vị trí trên module thật có thể khác).
Thông sốGiá trị thường gặp
Điện áp nguồn4,5 đến 20 V DC, thường dùng 5 V
Mức tín hiệu chân OUTMức cao khoảng 3,3 V, mức thấp 0 V
Tầm phát hiệnKhoảng 3 đến 7 m, chỉnh bằng biến trở độ nhạy
Góc phát hiệnKhoảng 110 độ
Thời gian giữ mức caoKhoảng 3 giây đến 5 phút, chỉnh bằng biến trở thời gian
Thời gian khóaKhoảng 2,5 giây sau khi OUT xuống thấp, trong lúc này cảm biến không nhận chuyển động mới
Thời gian ổn địnhKhoảng 30 đến 60 giây sau khi cấp nguồn
Dòng tiêu thụ lúc chờRất nhỏ, dưới 0,1 mA

Các số trên là thông số thường đi kèm module HC-SR501; module của từng lô hàng có thể khác đôi chút. Vì chân OUT ra mức 3,3 V nên nối thẳng vào chân GPIO của ESP32 được, không cần mạch chuyển mức.

3. Chuẩn bị

4. Sơ đồ nối dây

Sơ đồ nối ESP32 DevKit V1 với cảm biến PIR HC-SR501 và module relay: OUT của PIR nối D27, IN1 của relay nối D18, hai module dùng nguồn 5 V từ VIN
Sơ đồ nối ESP32 với PIR HC-SR501 và module relay (hình vẽ minh họa).
Linh kiệnChânESP32 DevKit V1
Cảm biến PIRVCCVIN (5 V từ cổng USB)
OUTD27 (GPIO27)
GNDGND
Module relayVCCVIN (5 V từ cổng USB)
IN1D18 (GPIO18)
GNDGND

PIR và relay cùng lấy 5 V từ chân VIN, nên chia nguồn qua hàng nguồn của breadboard. Chương trình 1 chỉ cần cảm biến PIR; có thể nối relay sau.

5. Chương trình 1: đọc PIR bằng ngắt

Chương trình in ra lúc bắt đầu và lúc hết chuyển động, đồng thời bật LED trên mạch (GPIO2) khi chân OUT ở mức cao:

/*
  PYWORLD.VN - Cảm biến chuyển động PIR HC-SR501 với ESP32: đọc tín hiệu bằng ngắt
  In ra lúc bắt đầu và lúc hết chuyển động, kèm thời gian chân OUT ở mức cao
  Nối dây: VCC = VIN (5 V), GND = GND, OUT = D27
*/

#define PIR_PIN 27
#define LED_PIN 2

const unsigned long THOI_GIAN_ON_DINH_MS = 30000;   // Cảm biến cần khoảng 30 đến 60 giây để ổn định sau khi cấp nguồn

volatile bool coThayDoi = false;
bool dangCoNguoi = false;
unsigned long lucBatDau = 0;

// Hàm ngắt: chỉ đặt cờ, việc in và xử lý làm trong loop()
void IRAM_ATTR khiPirDoiMuc() {
  coThayDoi = true;
}

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

  Serial.println("Dang cho cam bien on dinh (30 giay), tranh di chuyen truoc cam bien...");
  delay(THOI_GIAN_ON_DINH_MS);

  attachInterrupt(digitalPinToInterrupt(PIR_PIN), khiPirDoiMuc, CHANGE);
  Serial.println("San sang");
}

void loop() {
  if (!coThayDoi) {
    return;
  }
  coThayDoi = false;

  bool mucCao = (digitalRead(PIR_PIN) == HIGH);
  if (mucCao && !dangCoNguoi) {
    dangCoNguoi = true;
    lucBatDau = millis();
    digitalWrite(LED_PIN, HIGH);
    Serial.println("Phat hien chuyen dong");
  } else if (!mucCao && dangCoNguoi) {
    dangCoNguoi = false;
    digitalWrite(LED_PIN, LOW);
    Serial.printf("Het chuyen dong, OUT o muc cao %.1f giay\n", (millis() - lucBatDau) / 1000.0);
  }
}

Trong 30 giây đầu, đứng yên hoặc rời khỏi vùng quan sát để cảm biến ổn định. Sau đó đi ngang trước cảm biến và quan sát Serial Monitor. Thời gian chân OUT ở mức cao cho biết biến trở thời gian đang đặt ở đâu.

6. Chương trình 2: đèn tự bật, tự tắt

Giản đồ thời gian của đèn tự bật bằng PIR và relay: chuyển động, chân OUT của PIR giữ mức cao theo Tx, relay giữ thêm 60 giây sau lần chuyển động cuối
Relay đóng ngay khi có chuyển động và nhả sau 60 giây không còn chuyển động (hình minh họa, không theo tỉ lệ).

Thay vì dùng thời gian giữ của module, chương trình tự đếm thời gian. Cách này cho phép đổi thời gian giữ ngay trong code và dễ thêm điều kiện khác về sau.

/*
  PYWORLD.VN - Đèn tự bật khi có người: PIR HC-SR501 + relay với ESP32
  Có chuyển động thì đóng relay; relay tự nhả sau một khoảng thời gian không còn chuyển động
  PIR: VCC = VIN (5 V), GND = GND, OUT = D27
  Module relay bản 5 V: VCC = VIN, GND = GND, IN1 = D18
*/

#define PIR_PIN   27
#define RELAY_PIN 18

// true:  module kích mức thấp (IN = LOW thì relay đóng)
// false: module kích mức cao (IN = HIGH thì relay đóng)
const bool RELAY_ACTIVE_LOW = true;

const unsigned long THOI_GIAN_ON_DINH_MS = 30000;   // Bỏ qua tín hiệu trong lúc cảm biến ổn định
const unsigned long THOI_GIAN_GIU_MS     = 60000;   // Giữ đèn thêm 60 giây sau lần chuyển động cuối

volatile bool coChuyenDong = false;
bool denDangBat = false;
bool daSanSang = false;
unsigned long lanChuyenDongCuoi = 0;

void IRAM_ATTR khiPirLenCao() {
  coChuyenDong = true;
}

void datRelay(bool bat) {
  bool muc = RELAY_ACTIVE_LOW ? !bat : bat;
  digitalWrite(RELAY_PIN, muc ? HIGH : LOW);
  denDangBat = bat;
}

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);

  // Đặt mức "tắt" trước khi chuyển chân thành ngõ ra để relay không đóng lúc khởi động
  digitalWrite(RELAY_PIN, RELAY_ACTIVE_LOW ? HIGH : LOW);
  pinMode(RELAY_PIN, OUTPUT);
  datRelay(false);

  attachInterrupt(digitalPinToInterrupt(PIR_PIN), khiPirLenCao, RISING);
  Serial.println("Dang cho cam bien on dinh (30 giay)...");
}

void loop() {
  // Trong 30 giây đầu cảm biến có thể báo sai nên không điều khiển đèn
  if (!daSanSang) {
    if (millis() < THOI_GIAN_ON_DINH_MS) {
      coChuyenDong = false;
      return;
    }
    daSanSang = true;
    coChuyenDong = false;
    Serial.println("San sang");
  }

  // Chân OUT còn ở mức cao nghĩa là vẫn đang có chuyển động
  if (coChuyenDong || digitalRead(PIR_PIN) == HIGH) {
    coChuyenDong = false;
    lanChuyenDongCuoi = millis();
    if (!denDangBat) {
      datRelay(true);
      Serial.println("Co chuyen dong -> BAT den");
    }
  }

  if (denDangBat && millis() - lanChuyenDongCuoi >= THOI_GIAN_GIU_MS) {
    datRelay(false);
    Serial.printf("Khong con chuyen dong %lu giay -> TAT den\n", THOI_GIAN_GIU_MS / 1000);
  }
}

7. Kết quả mong đợi

Hình minh họa Serial Monitor của hai chương trình PIR HC-SR501 với ESP32: phát hiện và hết chuyển động, bật đèn và tắt đèn sau 60 giây
Kết quả mong đợi trên Serial Monitor của hai chương trình (hình minh họa).
  • Chương trình 2: đi ngang trước cảm biến, relay đóng (nghe tiếng "tách", LED kênh 1 trên module relay sáng).
  • Tiếp tục di chuyển trong phòng: relay giữ nguyên, bộ đếm được đặt lại sau mỗi lần phát hiện.
  • Rời khỏi vùng quan sát: khoảng 60 giây sau lần phát hiện cuối, relay nhả.

8. Giải thích các điểm chính

  • attachInterrupt(digitalPinToInterrupt(PIR_PIN), ham, CHANGE): gọi hàm ngắt mỗi khi chân OUT đổi mức. Chương trình 2 dùng RISING vì chỉ cần biết lúc bắt đầu có chuyển động.
  • IRAM_ATTR và biến volatile: hàm ngắt đặt trong RAM và chỉ đặt một cờ; mọi việc in, đóng relay làm trong loop(). Cách viết này giống bài ngắt GPIO trên ESP32.
  • Kết hợp ngắt và đọc mức: ngoài cờ ngắt, chương trình 2 còn đọc digitalRead(PIR_PIN). Chừng nào chân OUT còn ở mức cao, thời điểm chuyển động cuối vẫn được cập nhật.
  • Đếm thời gian bằng millis(): không dùng delay() dài, nên chương trình vẫn phản ứng ngay với chuyển động mới trong lúc đèn đang bật.
  • Bỏ qua 30 giây đầu: lúc mới cấp nguồn, HC-SR501 thường kéo chân OUT lên cao một hai lần dù không có ai.

9. Chỉnh module và chọn vị trí lắp

  • Biến trở thời gian: khi đã để chương trình đếm thời gian, vặn biến trở thời gian về mức nhỏ nhất (khoảng 3 giây) để cảm biến sớm sẵn sàng cho lần phát hiện sau.
  • Jumper: đặt ở H (kích lại) để chân OUT giữ mức cao suốt lúc còn chuyển động.
  • Biến trở độ nhạy: bắt đầu ở giữa, tăng dần cho đến khi phủ đủ khu vực cần quan sát. Độ nhạy quá cao dễ báo giả.
  • Vị trí lắp: đặt cao khoảng 2 đến 2,5 m, hướng sao cho người đi ngang qua vùng nhìn. Tránh hướng thẳng vào cửa sổ có nắng, máy lạnh, quạt sưởi, bếp.
  • Tránh nhiễu: đặt cảm biến cách mạch ESP32 và anten ít nhất một gang tay. Sóng Wi-Fi phát gần có thể làm HC-SR501 báo giả.

An toàn điện: khi dùng relay đóng cắt đèn 220 V, phải ngắt điện trước khi đấu dây, đặt mạch trong hộp cách điện và không chạm vào mạch lúc có điện. Xem cách đấu tải và các lưu ý trong bài ESP32 điều khiển module relay.

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

Hiện tượngNguyên nhân thường gặpCách xử lý
Báo có chuyển động liên tục dù không có aiCảm biến chưa ổn định, độ nhạy quá cao, đặt sát mạch ESP32 hoặc hướng vào nguồn nhiệtChờ đủ một phút sau khi cấp nguồn; giảm độ nhạy; dời cảm biến ra xa mạch và nguồn nhiệt
Không bao giờ báo chuyển độngNối nhầm thứ tự ba chân hoặc cấp nguồn 3,3 V thay vì 5 VTháo vòm trắng để đọc tên chân; nối VCC vào VIN
Đèn tắt khi người vẫn ngồi trong phòngPIR chỉ phát hiện chuyển động, người ngồi yên không tạo tín hiệuTăng THOI_GIAN_GIU_MS; đặt cảm biến gần chỗ ngồi hơn
Sau khi đèn tắt, đi vào ngay nhưng đèn không bật lại liềnCảm biến đang trong thời gian khóa khoảng 2,5 giâyHiện tượng bình thường của HC-SR501
Relay đóng ngược: có người thì nhả, không có người thì đóngModule relay kích mức caoĐổi RELAY_ACTIVE_LOW thành false
Mạch khởi động lại khi relay đóngNguồn 5 V từ cổng USB yếuDùng nguồn 5 V riêng cho relay và nối chung GND; xem bài mạch hạ áp LM2596 và MP1584

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 sóng Wi-Fi (CSI) trên ESP32: không cần cảm biến
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.

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