Raspberry Pi + PIR: Báo động chuyển động cơ bản
Cơ bản1/8/2026- Tác giả: IoTlabs Maker

Raspberry Pi + PIR: Báo động chuyển động cơ bản

Xây dựng báo động chuyển động cơ bản trên Raspberry Pi bằng cảm biến PIR HC-SR501, kích hoạt LED và buzzer khi phát hiện người/vật di chuyển trong vùng quét.

Raspberry PiGPIOPIRHC-SR501PythonRPi.GPIOBao dong chuyen dong

Dự án nhập môn GPIO thứ hai: dùng cảm biến hồng ngoại thụ động PIR (HC-SR501) để phát hiện chuyển động và kích hoạt báo động bằng LED + buzzer trên Raspberry Pi.

Bạn sẽ học cách đọc tín hiệu digital từ PIR, xử lý bằng cả polling lẫn GPIO interrupt (event detection), và xây dựng một hệ thống báo động có lịch arm/disarm giống thiết bị an ninh gia đình thực tế.

Hướng dẫn chi tiết

Hướng dẫn dùng cảm biến PIR HC-SR501 để phát hiện chuyển động và kích hoạt báo động LED + buzzer trên Raspberry Pi, từ polling cơ bản đến GPIO interrupt và hệ thống có lịch arm/disarm.

1. Giới thiệu

PIR (Passive Infrared) là loại cảm biến chuyển động rẻ và phổ biến nhất trong các dự án báo động DIY: nó phát hiện sự thay đổi bức xạ hồng ngoại khi có người/vật ấm đi qua vùng quét (~110°, 3–7m). Cảm biến HC-SR501 xuất tín hiệu digital đơn giản — HIGH khi phát hiện chuyển động, LOW khi không — nên rất dễ đọc bằng GPIO.

Trong dự án này, bạn sẽ ghép PIR với một LED và một buzzer để tạo báo động chuyển động cơ bản, đồng thời học hai cách xử lý tín hiệu GPIO phổ biến: polling (vòng lặp liên tục kiểm tra trạng thái chân) và interrupt/event detection (chỉ chạy code khi có sự kiện xảy ra, tiết kiệm CPU hơn nhiều).

2. Linh kiện cần có

Linh kiện

Số lượng

Giá tham khảo

Raspberry Pi 4 Model B (đã cài Raspberry Pi OS)

1

~1.500.000đ

Cảm biến PIR HC-SR501

1

~20.000đ

Active Buzzer (còi tích cực)

1

~10.000đ

Module LED đơn (có điện trở hạn dòng onboard)

1

~5.000đ

Breadboard + dây jumper

1 bộ

~30.000đ

3. Sơ đồ kết nối

Module

Raspberry Pi 4

PIR VCC

5V (Pin 2)

PIR GND

GND

PIR OUT

GPIO17 (Pin 11) — logic ra 3.3V, nối thẳng không cần chia áp

Buzzer VCC

3V3 (Pin 1)

Buzzer GND

GND

Buzzer IO

GPIO27 (Pin 13)

LED VCC

3V3 (Pin 1)

LED GND

GND

LED IN

GPIO22 (Pin 15)

Lưu ý quan trọng khi lắp đặt:

Cảm biến PIR HC-SR501 cần thời gian "khởi động ổn định" (warm-up) khoảng 30–60 giây sau khi cấp nguồn trước khi cho kết quả đọc chính xác — trong thời gian này cảm biến có thể báo động giả.

Luôn chờ đủ thời gian này trước khi tin vào kết quả đọc đầu tiên. Biến trở trên board PIR cho phép chỉnh độ nhạy (Sx) và thời gian giữ mức HIGH (Tx) — vặn theo chiều kim đồng hồ để tăng độ nhạy/thời gian giữ.

4. Code ví dụ #1 — Báo động cơ bản (polling)

Vòng lặp đơn giản nhất: liên tục đọc trạng thái chân PIR, bật LED + buzzer khi phát hiện chuyển động, tắt khi hết.

#!/usr/bin/env python3
"""
Raspberry Pi + PIR HC-SR501 - Bao dong chuyen dong co ban (LED + Buzzer)
Wiring:
  PIR OUT    -> GPIO17
  Buzzer IO  -> GPIO27
  LED IN     -> GPIO22
"""
import RPi.GPIO as GPIO
import time

PIR_PIN = 17
BUZZER_PIN = 27
LED_PIN = 22


def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(PIR_PIN, GPIO.IN)
    GPIO.setup(BUZZER_PIN, GPIO.OUT)
    GPIO.setup(LED_PIN, GPIO.OUT)
    GPIO.output(BUZZER_PIN, False)
    GPIO.output(LED_PIN, False)
    print("[BOOT] PIR motion alarm ready. Khoi dong on dinh cam bien...")
    time.sleep(2)  # rut gon cho demo; PIR that can 30-60s de on dinh


def main():
    setup()
    try:
        while True:
            motion = GPIO.input(PIR_PIN)
            if motion:
                print("[ALERT] Phat hien chuyen dong!")
                GPIO.output(LED_PIN, True)
                GPIO.output(BUZZER_PIN, True)
            else:
                GPIO.output(LED_PIN, False)
                GPIO.output(BUZZER_PIN, False)
            time.sleep(0.2)
    except KeyboardInterrupt:
        print("\n[EXIT] Dung boi nguoi dung")
    finally:
        GPIO.cleanup()


if __name__ == "__main__":
    main()

5. Code ví dụ #2 — Xử lý bằng GPIO interrupt (event detection) + cooldown

Thay vì liên tục polling (tốn CPU), bản này dùng GPIO.add_event_detect() để chỉ chạy callback khi có cạnh lên (RISING) trên chân PIR. Thêm cơ chế cooldown để tránh báo động dồn dập liên tục khi có chuyển động kéo dài.

#!/usr/bin/env python3
"""
Raspberry Pi + PIR - Bao dong bang GPIO interrupt (event detection) + cooldown
"""
import RPi.GPIO as GPIO
import time

PIR_PIN = 17
BUZZER_PIN = 27
LED_PIN = 22
ALARM_DURATION_S = 3.0
COOLDOWN_S = 1.0

last_trigger = 0.0


def motion_callback(channel):
    global last_trigger
    now = time.time()
    if now - last_trigger < COOLDOWN_S:
        return  # bo qua trigger lap trong thoi gian cooldown
    last_trigger = now
    print(f"[ALERT] Chuyen dong luc {time.strftime('%H:%M:%S')}")
    GPIO.output(LED_PIN, True)
    GPIO.output(BUZZER_PIN, True)
    time.sleep(ALARM_DURATION_S)
    GPIO.output(LED_PIN, False)
    GPIO.output(BUZZER_PIN, False)


def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(PIR_PIN, GPIO.IN)
    GPIO.setup(BUZZER_PIN, GPIO.OUT)
    GPIO.setup(LED_PIN, GPIO.OUT)
    GPIO.output(BUZZER_PIN, False)
    GPIO.output(LED_PIN, False)
    GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=motion_callback, bouncetime=200)
    print("[BOOT] PIR interrupt-based alarm ready (event detection)")


def main():
    setup()
    try:
        while True:
            time.sleep(1)  # main loop ranh, xu ly hoan toan qua callback
    except KeyboardInterrupt:
        print("\n[EXIT] Dung boi nguoi dung")
    finally:
        GPIO.cleanup()


if __name__ == "__main__":
    main()

6. Code ví dụ #3 — Ứng dụng thực tế: hệ thống có lịch arm/disarm + telemetry

Giống thiết bị an ninh gia đình thực tế: chỉ báo động trong khung giờ đã đặt (ví dụ ban đêm 22h–6h), tự ghi log JSON định kỳ, và tự báo trạng thái "degraded" nếu PIR không có tín hiệu nào trong thời gian dài (dấu hiệu cảm biến hỏng hoặc mất kết nối).

#!/usr/bin/env python3
"""
Raspberry Pi + PIR - He thong bao dong co lich arm/disarm + telemetry JSON
Ung dung thuc te: chi bao dong trong khung gio da dat (vd ban dem),
tu ghi log JSON dinh ky, bao "degraded" neu PIR khong phan hoi qua lau.
"""
import RPi.GPIO as GPIO
import time
import json
from datetime import datetime

PIR_PIN = 17
BUZZER_PIN = 27
LED_PIN = 22
ARM_HOUR_START = 22   # 22:00
ARM_HOUR_END = 6      # 06:00
TELEMETRY_INTERVAL_S = 5.0
PIR_SILENT_TIMEOUT_S = 120.0


def is_armed(now=None):
    now = now or datetime.now()
    h = now.hour
    if ARM_HOUR_START > ARM_HOUR_END:
        return h >= ARM_HOUR_START or h < ARM_HOUR_END
    return ARM_HOUR_START <= h < ARM_HOUR_END


def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(PIR_PIN, GPIO.IN)
    GPIO.setup(BUZZER_PIN, GPIO.OUT)
    GPIO.setup(LED_PIN, GPIO.OUT)
    GPIO.output(BUZZER_PIN, False)
    GPIO.output(LED_PIN, False)
    print("[BOOT] Scheduled PIR alarm service starting")
    print(f"[BOOT] Armed window: {ARM_HOUR_START}:00 -> {ARM_HOUR_END}:00")


def main():
    setup()
    last_motion_ts = time.time()
    last_telemetry = 0.0

    try:
        while True:
            armed = is_armed()
            motion = GPIO.input(PIR_PIN)
            now = time.time()

            if motion:
                last_motion_ts = now

            alarm_on = armed and motion
            GPIO.output(LED_PIN, alarm_on)
            GPIO.output(BUZZER_PIN, alarm_on)

            if now - last_telemetry >= TELEMETRY_INTERVAL_S:
                pir_silent = (now - last_motion_ts) > PIR_SILENT_TIMEOUT_S
                telemetry = {
                    "sensor": "pir_hcsr501",
                    "armed": armed,
                    "motion": bool(motion) if not pir_silent else None,
                    "status": "degraded" if pir_silent else "ok",
                }
                print(json.dumps(telemetry))
                last_telemetry = now
                if pir_silent:
                    print("[WARN] Khong co tin hieu PIR trong thoi gian dai - kiem tra cam bien")

            time.sleep(0.2)
    except KeyboardInterrupt:
        print("\n[EXIT] Dung boi nguoi dung")
    finally:
        GPIO.cleanup()


if __name__ == "__main__":
    main()

7. Lỗi thường gặp

Lỗi

Nguyên nhân

Cách sửa

PIR liên tục báo động giả ngay sau khi bật nguồn

Cảm biến chưa qua thời gian warm-up (30–60s)

Chờ ít nhất 30–60s sau khi cấp nguồn trước khi tin vào kết quả đọc

PIR không phát hiện chuyển động dù có người đi qua

Biến trở độ nhạy (Sx) vặn quá thấp, hoặc vật thể ở góc ngoài vùng quét ~110°

Vặn biến trở Sx theo chiều kim đồng hồ để tăng độ nhạy; kiểm tra góc lắp đặt cảm biến

Buzzer/LED không phản ứng dù PIR đã lên HIGH

Sai chân GPIO trong code so với sơ đồ, hoặc thiếu GPIO.setup() cho chân OUT

Đối chiếu lại số chân trong code với bảng sơ đồ mục 3

Callback interrupt bị gọi nhiều lần liên tục cho 1 lần chuyển động

PIR giữ mức HIGH dao động nhẹ (rung tín hiệu) hoặc thiếu debounce

Dùng bouncetime trong add_event_detect() và thêm cooldown thủ công như code ví dụ #2

Báo động không tắt vào ban ngày dù đã cấu hình lịch

Giờ hệ thống (system time) của Raspberry Pi sai lệch

Đồng bộ giờ bằng sudo timedatectl set-ntp true hoặc kiểm tra kết nối Internet cho NTP

8. Tổng kết

Dự án này minh họa hai kỹ thuật xử lý GPIO cốt lõi: đọc trạng thái bằng polling (đơn giản, dễ hiểu, tốn CPU) và bằng interrupt/event detection (hiệu quả hơn, phù hợp ứng dụng thực tế chạy nền dài hạn).

Kết hợp với logic lịch arm/disarm và telemetry JSON, bạn đã có một khung sườn gần giống hệ thống an ninh gia đình thực tế. Hướng mở rộng tiếp theo: gửi thông báo qua Telegram Bot hoặc MQTT khi phát hiện chuyển động, hoặc ghép thêm camera để chụp ảnh lúc báo động.