IoTLabs

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

Series: Lập trình Raspberry Pi – Bài 8: Đọc DHT22/BME280 và lưu log dữ liệu (nhiệt độ/độ ẩm) theo thời gian

Series: Lập trình Raspberry Pi & Ứng dụng thực tế Phần 2 — Lập trình GPIO & phần cứng Bài 8: Đọc DHT22/BME280 và lưu log dữ liệu (nhiệt độ/độ ẩm) theo thời gian


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

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

  • Đọc sensor DHT22 hoặc BME280 trên Raspberry Pi.
  • Chuẩn hoá dữ liệu: ts, temperature, humidity (BME280 có thêm pressure).
  • Lưu log ra CSV + in console để debug.
  • Chạy ổn định theo chu kỳ (ví dụ mỗi 10 giây).

Gợi ý: nếu bạn có BME280 (I2C) thì ưu tiên BME280 vì ổn định hơn, dễ scan địa chỉ (bài 7).


2) Phần cứng & wiring

A) BME280 (I2C) — khuyến nghị

  • VCC → 3.3V (Pin 1)
  • GND → GND (Pin 6)
  • SDA → GPIO2 (Pin 3)
  • SCL → GPIO3 (Pin 5)

Kiểm tra địa chỉ:

sudo i2cdetect -y 1

Thường sẽ thấy 0x76 hoặc 0x77.

B) DHT22 (1-wire kiểu GPIO)

  • VCC → 3.3V
  • GND → GND
  • DATA → GPIO4 (Pin 7) (gợi ý)
  • Nếu module rời (không có board), thường cần điện trở kéo 4.7k–10k từ DATA lên 3.3V.

3) Cài thư viện

Nếu dùng BME280

sudo apt update
sudo apt -y install python3-pip python3-smbus i2c-tools
pip3 install adafruit-circuitpython-bme280

Nếu dùng DHT22

sudo apt update
sudo apt -y install python3-pip
pip3 install adafruit-circuitpython-dht
sudo apt -y install libgpiod2

4) Code mẫu: logger ra CSV (dùng BME280)

Tạo thư mục:

mkdir -p ~/apps/sensor-logger
cd ~/apps/sensor-logger
nano bme280_logger.py

Dán code:

import time
import csv
from datetime import datetime

import board
import adafruit_bme280

LOG_FILE = "bme280_log.csv"
INTERVAL_SEC = 10

def init_sensor():
    i2c = board.I2C()
    sensor = adafruit_bme280.Adafruit_BME280_I2C(i2c)
    # Nếu sensor báo sai, bạn có thể set sea_level_pressure cho đúng khu vực
    # sensor.sea_level_pressure = 1013.25
    return sensor

def ensure_csv_header():
    try:
        with open(LOG_FILE, "x", newline="") as f:
            w = csv.writer(f)
            w.writerow(["ts", "temperature_c", "humidity_pct", "pressure_hpa"])
    except FileExistsError:
        pass

def append_row(ts, t, h, p):
    with open(LOG_FILE, "a", newline="") as f:
        w = csv.writer(f)
        w.writerow([ts, f"{t:.2f}", f"{h:.2f}", f"{p:.2f}"])

def main():
    sensor = init_sensor()
    ensure_csv_header()

    print("BME280 logger started. Writing to", LOG_FILE)
    while True:
        ts = datetime.now().isoformat(timespec="seconds")
        t = sensor.temperature
        h = sensor.humidity
        p = sensor.pressure

        print(f"{ts} | T={t:.2f}°C | H={h:.2f}% | P={p:.2f}hPa")
        append_row(ts, t, h, p)

        time.sleep(INTERVAL_SEC)

if __name__ == "__main__":
    main()

Chạy:

python3 bme280_logger.py

Xem file CSV:

tail -n 5 bme280_log.csv

5) Code mẫu: logger ra CSV (dùng DHT22)

Tạo file:

nano dht22_logger.py

Dán code:

import time
import csv
from datetime import datetime

import board
import adafruit_dht

LOG_FILE = "dht22_log.csv"
INTERVAL_SEC = 10

def ensure_csv_header():
    try:
        with open(LOG_FILE, "x", newline="") as f:
            w = csv.writer(f)
            w.writerow(["ts", "temperature_c", "humidity_pct"])
    except FileExistsError:
        pass

def append_row(ts, t, h):
    with open(LOG_FILE, "a", newline="") as f:
        w = csv.writer(f)
        w.writerow([ts, f"{t:.2f}", f"{h:.2f}"])

def main():
    # GPIO4 (Pin 7) - bạn có thể đổi nếu cần
    dht = adafruit_dht.DHT22(board.D4)
    ensure_csv_header()

    print("DHT22 logger started. Writing to", LOG_FILE)

    while True:
        ts = datetime.now().isoformat(timespec="seconds")
        try:
            t = dht.temperature
            h = dht.humidity

            if t is not None and h is not None:
                print(f"{ts} | T={t:.2f}°C | H={h:.2f}%")
                append_row(ts, t, h)
            else:
                print(f"{ts} | read failed (None)")
        except RuntimeError as e:
            # DHT hay bị lỗi đọc lặt vặt, xử lý bằng retry
            print(f"{ts} | read error: {e}")
        time.sleep(INTERVAL_SEC)

if __name__ == "__main__":
    main()

Chạy:

python3 dht22_logger.py

6) Lỗi thường gặp & cách xử lý

BME280

  • i2cdetect không thấy: quay lại Bài 7 (enable I2C + wiring SDA/SCL + GND chung)
  • Thấy 0x76/0x77 nhưng code lỗi: kiểm tra dây nguồn 3.3V, đổi module khác

DHT22

  • Lỗi RuntimeError thường xuyên: bình thường (DHT “khó chiều”), tăng INTERVAL_SEC lên 15–30 giây
  • Humidity/temperature None: wiring DATA hoặc thiếu pull-up

7) Bài tập nâng cấp

  1. Thêm cột device_id vào CSV (ví dụ hostname).
  2. Log thêm uptime_sec (tính từ lúc start).
  3. Chạy logger bằng systemd service (áp dụng Bài 3) để tự chạy khi boot.