Hướng dẫn từ cơ bản đến nâng cao về GPIO trên Raspberry Pi

Môi trường phần cứng: Raspberry Pi 4 Model B Rev 1.5 · Debian 12 Bookworm · Nhân 6.12.25
Ngày kiểm thử: Tháng 4 năm 2026
Tác giả: zeruns (https://blog.zeruns.com/)
Nội dung này do AI viết, chỉ mang tính chất tham khảo (Hermes Agent + DeepSeek V4 Flash)


Mục lục

  1. Khái niệm cơ bản về GPIO
  2. Chuẩn bị môi trường và công cụ
  3. GPIO Input/Output (bằng Shell)
  4. GPIO Input/Output (bằng Python)
  5. Giao tiếp I2C
  6. Giao tiếp SPI
  7. Ví dụ tổng hợp
  8. Lưu ý và xử lý sự cố

1. Khái niệm cơ bản về GPIO

1.1 GPIO là gì?

GPIO (General Purpose Input/Output - Ngõ vào/ra mục đích chung) là một nhóm chân có thể lập trình được trên bộ vi xử lý. Thông qua phần mềm, bạn có thể cấu hình mỗi chân thành:

  • Chế độ xuất (Output): Phát mức cao (3.3V) hoặc thấp (0V), dùng để điều khiển LED, rơle, còi, v.v.
  • Chế độ nhập (Input): Đọc trạng thái điện áp bên ngoài (cao/thấp), dùng để đọc tín hiệu từ nút nhấn, cảm biến
  • Chức năng thay thế (Alternate function): Các giao thức truyền thông chuyên dụng như I2C, SPI, UART, PWM, v.v.

1.2 Bố trí chân của Raspberry Pi 4B

Raspberry Pi 4B sử dụng thanh chân mở rộng 40 chân, có ba cách đánh số chân:

Cách đánh số Mô tả Thư viện mẫu
Chân vật lý (Board) Đánh số từ 1 đến 40, bắt đầu từ góc trái trên là 1 RPi.GPIO setmode(GPIO.BOARD)
Số BCM (Broadcom) Số theo chân GPIO trên chip, ví dụ GPIO17 RPi.GPIO setmode(GPIO.BCM), libgpiod
Số wiringPi Số do wiringPi tự định nghĩa (đã lỗi thời) Thư viện wiringPi

Bảng chức năng 40 chân

                    ┌─────────────────────────────┐
                    │  🥝 Sơ đồ chân Raspberry Pi 4B 40 chân   │
  3.3V  (01) ──●  ●── (02)  5V                    │
 GPIO2  (03) ──●  ●── (04)  5V                    │
 GPIO3  (05) ──●  ●── (06)  GND                   │
 GPIO4  (07) ──●  ●── (08)  GPIO14 (UART TX)      │
   GND  (09) ──●  ●── (10)  GPIO15 (UART RX)      │
GPIO17  (11) ──●  ●── (12)  GPIO18 (PWM0)         │
GPIO27  (13) ──●  ●── (14)  GND                   │
GPIO22  (15) ──●  ●── (16)  GPIO23                │
  3.3V  (17) ──●  ●── (18)  GPIO24                │
GPIO10 (19) ──●  ●── (20)  GND  (SPI_MOSI)        │
 GPIO9 (21) ──●  ●── (22)  GPIO25 (SPI_MISO)      │
GPIO11 (23) ──●  ●── (24)  GPIO8  (SPI_SCLK)      │
   GND (25) ──●  ●── (26)  GPIO7  (SPI_CE0)       │
 GPIO0 (27) ──●  ●── (28)  GPIO1  (ID_SDA / I2C)  │
 GPIO5 (29) ──●  ●── (30)  GND                    │
 GPIO6 (31) ──●  ●── (32)  GPIO12 (PWM0)          │
GPIO13 (33) ──●  ●── (34)  GND                    │
GPIO19 (35) ──●  ●── (36)  GPIO16                 │
GPIO26 (37) ──●  ●── (38)  GPIO20                 │
   GND (39) ──●  ●── (40)  GPIO21                 │
                    └─────────────────────────────┘

Bảng chức năng chính của Raspberry Pi 4B

Chân vật lý BCM Chức năng Chân vật lý BCM Chức năng
1 3.3V 2 5V
3 GPIO2 I2C1 SDA 4 5V
5 GPIO3 I2C1 SCL 6 GND
7 GPIO4 8 GPIO14 UART TX
9 GND 10 GPIO15 UART RX
11 GPIO17 12 GPIO18 PCM_CLK / PWM0
13 GPIO27 14 GND
15 GPIO22 16 GPIO23
17 3.3V 18 GPIO24
19 GPIO10 SPI0 MOSI 20 GND
21 GPIO9 SPI0 MISO 22 GPIO25
23 GPIO11 SPI0 SCLK 24 GPIO8 SPI0 CE0
25 GND 26 GPIO7 SPI0 CE1
27 GPIO0 ID_SDA (EEPROM) 28 GPIO1 ID_SCL (EEPROM)
29 GPIO5 30 GND
31 GPIO6 32 GPIO12 PWM0
33 GPIO13 PWM1 34 GND
35 GPIO19 PCM_FS / PWM1 36 GPIO16
37 GPIO26 38 GPIO20 PCM_DIN
39 GND 40 GPIO21 PCM_DOUT

1.3 Lưu ý quan trọng

  • Mức logic: GPIO của Raspberry Pi là mức 3.3V, không thể nối trực tiếp với tín hiệu 5V
  • Giới hạn dòng điện: Mỗi chân GPIO xuất tối đa khoảng 16mA, tổng tất cả các chân không quá 50mA
  • Trạng thái mặc định: Hầu hết các chân GPIO mặc định ở chế độ nhập, một số có điện trở kéo lên/kéo xuống trong
  • Chân 3.3V (01/17): Dòng điện xuất tối đa khoảng 500mA
  • Chân 5V (02/04): Lấy điện trực tiếp từ cổng USB-C, dòng điện phụ thuộc vào bộ nguồn

2. Chuẩn bị môi trường và công cụ

2.1 Danh sách công cụ đã cài đặt

Hướng dẫn này được kiểm thử trên hệ thống Debian 12 Bookworm, các công cụ sau đã được cài sẵn:

Công cụ/Thư viện Phiên bản Mục đích
libgpiod / gpioset / gpioget 1.6.3 Điều khiển GPIO bằng Shell
python3-libgpiod 1.6.3 Liên kết Python cho libgpiod
RPi.GPIO 0.7.2 Thư viện GPIO Python cổ điển
smbus2 Đã cài Giao tiếp I2C bằng Python
spidev Đã cài Giao tiếp SPI bằng Python
pigpiod 1.79 Dịch vụ GPIO (PWM/điều khiển từ xa)
i2c-tools 4.3 Quét thiết bị I2C

2.2 Cài đặt công cụ cần thiết

Nếu thiếu một số công cụ, có thể cài theo lệnh sau:

# Công cụ GPIO cơ bản
sudo apt install gpiod libgpiod-dev python3-libgpiod

# Công cụ I2C
sudo apt install i2c-tools

# Thư viện Python
pip install RPi.GPIO smbus2 spidev gpiozero

# Dịch vụ pigpio (PWM phần cứng, khuyến nghị)
sudo apt install pigpio pigpiod
sudo systemctl enable pigpiod --now

2.3 Bật giao diện I2C / SPI

Sử dụng raspi-config để bật:

sudo raspi-config

Đường dẫn menu:
Interface OptionsI2CEnableYes
Interface OptionsSPIEnableYes

Hoặc trực tiếp sửa tệp /boot/firmware/config.txt (Debian 12):

# Thêm thủ công
sudo tee -a /boot/firmware/config.txt <<EOF
dtparam=i2c_arm=on
dtparam=spi=on
EOF

# Khởi động lại để áp dụng
sudo reboot

Kiểm tra việc bật thành công:

# Kiểm tra I2C
ls /dev/i2c*
# Đầu ra: /dev/i2c-20  /dev/i2c-21

# Kiểm tra SPI (sau khi bật)
ls /dev/spi*
# Đầu ra: /dev/spidev0.0  /dev/spidev0.1

2.4 Xem tất cả gpiochip

$ gpioinfo
gpiochip0 - 58 lines:
        line   0:     "ID_SDA"       unused   input  active-high
        line   1:     "ID_SCL"       unused   input  active-high
        line   2:      "GPIO2"       unused   input  active-high
        ...

Con chip BCM2711 trên Raspberry Pi 4B cung cấp một gpiochip0, tổng cộng 58 chân GPIO, nhưng chỉ có GPIO0-GPIO27 được đưa ra ngoài thông qua thanh chân 40Pin.


3. Nhập/xuất GPIO (bằng Shell)

3.1 Bộ công cụ libgpiod

Nên sử dụng các công cụ của libgpiod, vì nó không phụ thuộc wiringPi và là giải pháp phổ biến hiện nay.

Các lệnh chính:

Lệnh Chức năng Ví dụ
gpioinfo Xem trạng thái tất cả GPIO gpioinfo
gpioset Thiết lập mức điện áp ngõ ra GPIO gpioset 0 17=1
gpioget Đọc mức điện áp ngõ vào GPIO gpioget 0 17
gpiomon Theo dõi sự kiện GPIO gpiomon 0 17

Giải thích định dạng: gpioset <chip> <pin >= <va> chipthường là 0 (gpiochip0),pin` là số theo định danh BCM

3.2 Đầu ra: Bật đèn LED

Nối cực dương của đèn LED (chân dài) qua điện trở 330Ω đến GPIO17 (chân vật lý 11), cực âm (chân ngắn) nối đến GND (chân vật lý 9).

# Đặt mức cao cho GPIO17 → Bật LED
gpioset 0 17=1

# Đặt mức thấp cho GPIO17 → Tắt LED
gpioset 0 17=0

3.3 Đầu vào: Đọc trạng thái nút nhấn

Nối một đầu của nút nhấn vào GPIO18 (chân vật lý 12), đầu còn lại nối GND (chân vật lý 14).

# Đọc mức điện áp GPIO18 (yêu cầu kéo lên ngoài hoặc kích hoạt kéo lên nội)
gpioget 0 18
# Đầu ra: 1 (chưa nhấn, mức cao)
# Đầu ra: 0 (khi nhấn, mức thấp)

3.4 Xem trạng thái tất cả chân

# Xem tất cả các chân
gpioinfo

# Xem chân cụ thể (BCM 17)
gpioinfo | grep "GPIO17"
# Đầu ra: line  17:      "GPIO17"       "myapp"   output  active-high

3.5 Dùng gpioset thiết lập kéo lên/kéo xuống

libgpiod phiên bản 1.6 hỗ trợ thiết lập thiên vị (bias) khi yêu cầu:

# Đặt GPIO17 thành đầu ra, mức cao ban đầu, có kéo lên
gpioset --bias=enable 0 17=1

4. GPIO Đầu vào/Đầu ra (bằng Python)

4.1 Sử dụng RPi.GPIO (thư viện cổ điển)

RPi.GPIO là thư viện GPIO bằng Python được dùng phổ biến nhất, đơn giản và dễ hiểu.

4.1.1 Đầu ra: LED nhấp nháy

import RPi.GPIO as GPIO
import time

# Dùng định danh theo BCM
GPIO.setmode(GPIO.BCM)

# Đặt GPIO17 làm đầu ra
GPIO.setup(17, GPIO.OUT)

# Nhấp nháy LED 5 lần
for _ in range(5):
    GPIO.output(17, GPIO.HIGH)  # Bật
    time.sleep(0.5)
    GPIO.output(17, GPIO.LOW)   # Tắt
    time.sleep(0.5)

# Dọn dẹp tài nguyên
GPIO.cleanup()

4.1.2 Đầu vào: Đọc nút nhấn

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

# GPIO18 làm đầu vào, bật điện trở kéo lên nội
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

try:
    while True:
        if GPIO.input(18) == GPIO.LOW:
            print("Nút được nhấn")
        else:
            print("Nút được nhả")
        time.sleep(0.1)
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.PUD_UP = kéo lên nội (mặc định mức cao, nhấn xuống mức thấp)
GPIO.PUD_DOWN = kéo xuống nội (mặc định mức thấp, nhấn lên mức cao)

4.1.3 Đầu ra PWM: Đèn kiểu thở

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)  # GPIO18 hỗ trợ PWM phần cứng

pwm = GPIO.PWM(18, 1000)  # Tần số 1kHz
pwm.start(0)  # Chu kỳ hoạt động 0% ban đầu

try:
    while True:
        # Dần sáng lên
        for duty in range(0, 101, 5):
            pwm.ChangeDutyCycle(duty)
            time.sleep(0.05)
        # Dần mờ đi
        for duty in range(100, -1, -5):
            pwm.ChangeDutyCycle(duty)
            time.sleep(0.05)
except KeyboardInterrupt:
    pwm.stop()
    GPIO.cleanup()

4.1.4 Phát hiện sự kiện: Đọc nút bằng ngắt

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def button_callback(channel):
    print(f"Nút được nhấn! (chân {channel})")

# Kích hoạt ngắt theo cạnh xuống
GPIO.add_event_detect(18, GPIO.FALLING,
                      callback=button_callback,
                      bouncetime=200)

try:
    print("Đang chờ nhấn nút...")
    time.sleep(60)
except KeyboardInterrupt:
    GPIO.cleanup()

4.2 Sử dụng gpiozero (tầng bao cao cấp)

gpiozero cung cấp lớp bao hướng đối tượng cao cấp, phù hợp để phát triển nhanh:

# Cài đặt
pip install gpiozero
from gpiozero import LED, Button, Buzzer
from signal import pause

# Đèn LED: Tự động nhấp nháy
led = LED(17)
led.blink()

# Nút nhấn: Khi nhấn, bật đèn LED
btn = Button(18, pull_up=True)

# Cách 1: Gán trực tiếp
btn.when_pressed = led.on
btn.when_released = led.off

# Cách 2: Hàm gọi lại tùy chỉnh
btn.when_pressed = lambda: print("Nhấn rồi!")

# Giữ chương trình chạy
pause()

gpiozero có các thành phần tích hợp:

Thành phần Tên lớp Mô tả
LED LED(pin) Điều khiển bật/tắt, nhấp nháy, hiệu ứng thở
Nút Button(pin, pull_up=True) Đọc trạng thái nút, phát hiện nhấn giữ
Buzzer Buzzer(pin) Điều khiển còi tích cực
Cảm biến khoảng cách DistanceSensor(echo, trigger) Cảm biến siêu âm HC-SR04
Động cơ servo Servo(pin) Điều khiển góc quay servo
Động cơ Motor(forward, backward) Điều khiển động cơ một chiều

4.3 Sử dụng liên kết Python của libgpiod (thao tác cấp thấp)

import gpiod
import time

# Mở gpiochip0
chip = gpiod.Chip('gpiochip0')

# Lấy dòng GPIO17 và yêu cầu làm đầu ra
line = chip.get_line(17)
line.request(consumer='myapp', type=gpiod.LINE_REQ_DIR_OUT)

# Đặt mức cao
line.set_value(1)
time.sleep(1)

# Đặt mức thấp
line.set_value(0)

# Giải phóng
line.release()

Chế độ đầu vào:

import gpiod

chip = gpiod.Chip('gpiochip0')
line = chip.get_line(18)

# Yêu cầu làm đầu vào, kéo lên nội
line.request(consumer='myapp',
             type=gpiod.LINE_REQ_DIR_IN,
             flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP)

value = line.get_value()  # 0 hoặc 1
print(f"GPIO18 = {value}")

line.release()

5. Giao tiếp I2C

5.1 Nguyên lý cơ bản I2C

I2C (Inter-Integrated Circuit) là một giao diện nối tiếp sử dụng hai đường dây:

Tín hiệu Chân Chức năng
SCL GPIO3 (chân vật lý 5) Đường đồng hồ
SDA GPIO2 (chân vật lý 3) Đường dữ liệu

Đặc điểm:

  • Cấu trúc chủ-tớ: Raspberry Pi là thiết bị chủ (Master), cảm biến là thiết bị tớ (Slave)
  • Địa chỉ 7 bit: Mỗi thiết bị tớ có địa chỉ duy nhất (ví dụ: 0x44)
  • Chia sẻ bus: Nhiều thiết bị có thể nối trên một bus I2C
  • Tốc độ: 100kHz (chuẩn), 400kHz (nhanh)

5.2 Xem bus I2C

# Xem danh sách bus I2C
$ i2cdetect -l
i2c-20  i2c        bcm2711_i2c1                   I2C adapter
i2c-21  i2c        bcm2711_i2c0                   I2C adapter
  • i2c-20: Ứng với chân 3(SDA)/5(SCL) — bus I2C chính
  • i2c-21: Ứng với ID_EEPROM (chân 27/28), thông thường không dùng cho thiết bị ngoài

5.3 Quét các thiết bị I2C

# Quét thiết bị trên i2c-20
$ i2cdetect -y 20
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- 44 -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Có thiết bị tại địa chỉ 0x44 — đó chính là cảm biến nhiệt độ/độ ẩm SHT30 (địa chỉ I2C 0x44).

Nếu không thấy thiết bị nào (toàn hiển thị --), nghĩa là bus chưa nối thiết bị.

5.4 Đọc/ghi I2C bằng smbus2

smbus2 là thư viện I2C bằng Python phổ biến nhất.

5.4.1 Đọc cảm biến nhiệt độ/độ ẩm SHT30

import smbus2
import time

# Địa chỉ I2C của SHT30
SHT30_ADDR = 0x44

# Mở bus I2C 20
bus = smbus2.SMBus(20)

# Gửi lệnh đo: chế độ độ chính xác cao
bus.write_i2c_block_data(SHT30_ADDR, 0x2C, [0x06])

time.sleep(0.015)  # Chờ phép đo hoàn tất (15ms)

# Đọc 6 byte dữ liệu
data = bus.read_i2c_block_data(SHT30_ADDR, 0x00, 6)

# Tính nhiệt độ và độ ẩm
temp_raw = ((data[0] << 8) | data[1])
temp = -45 + 175 * temp_raw / 65535.0

hum_raw = ((data[3] << 8) | data[4])
hum = 100 * hum_raw / 65535.0

print(f"Nhiệt độ: {temp:.2f}°C")
print(f"Độ ẩm: {hum:.2f}%")

5.4.2 Đóng gói thành hàm

import smbus2
import time

class SHT30:
    def __init__(self, bus_id=20, addr=0x44):
        self.bus = smbus2.SMBus(bus_id)
        self.addr = addr

    def read(self):
        """Đọc nhiệt độ, độ ẩm, trả về (nhiệt độ °C, độ ẩm %)"""
        self.bus.write_i2c_block_data(self.addr, 0x2C, [0x06])
        time.sleep(0.015)
        data = self.bus.read_i2c_block_data(self.addr, 0x00, 6)

        temp_raw = (data[0] << 8) | data[1]
        temp = -45 + 175 * temp_raw / 65535.0

        hum_raw = (data[3] << 8) | data[4]
        hum = 100 * hum_raw / 65535.0

        return round(temp, 2), round(hum, 2)

# Sử dụng
sensor = SHT30()
t, h = sensor.read()
print(f"{t}°C, {h}%RH")

5.4.3 Các thao tác đọc/ghi I2C tổng quát

import smbus2

bus = smbus2.SMBus(20)
addr = 0x44  # Địa chỉ I2C thiết bị

# Ghi một byte vào thanh ghi
bus.write_byte_data(addr, 0x2C, 0x06)

# Đọc một byte
value = bus.read_byte_data(addr, 0x00)

# Đọc nhiều byte
data = bus.read_i2c_block_data(addr, 0x00, 6)

# Ghi nhiều byte
bus.write_i2c_block_data(addr, 0x2C, [0x06])

# Ghi một byte (không có địa chỉ thanh ghi)
bus.write_byte(addr, 0x06)

# Đọc một byte (không có địa chỉ thanh ghi)
value = bus.read_byte(addr)

bus.close()

5.5 Dùng lệnh i2cget / i2cset từ dòng lệnh

# Đọc thanh ghi trạng thái của SHT30 (1 byte)
$ i2cget -y 20 0x44 0x00
0x65

# Đọc 6 byte dữ liệu gốc
$ i2cget -y 20 0x44 0x00 b 6

6. Giao tiếp SPI

6.1 Nguyên lý cơ bản SPI

SPI (Serial Peripheral Interface) là một bus nối tiếp đồng bộ song công, dùng bốn dây:

Tín hiệu Chân Raspberry Pi Chức năng
MOSI GPIO10 (chân vật lý 19) Chủ xuất, tớ nhận
MISO GPIO9 (chân vật lý 21) Chủ nhận, tớ xuất
SCLK GPIO11 (chân vật lý 23) Tín hiệu đồng hồ
CE0 GPIO8 (chân vật lý 24) Chọn thiết bị 0
CE1 GPIO7 (chân vật lý 26) Chọn thiết bị 1

Đặc điểm:

  • Song công: Đồng thời gửi và nhận
  • Chủ-tớ: Raspberry Pi làm chủ, có thể nối nhiều tớ (mỗi tớ một chân chọn)
  • Tốc độ cao: Có thể đạt hàng chục MHz
  • Không có địa chỉ thiết bị: Chọn thiết bị qua chân CE (Chip Enable)

6.2 Bật SPI và kiểm tra

# Sau khi đã bật SPI
$ ls /dev/spi*
/dev/spidev0.0  /dev/spidev0.1
  • /dev/spidev0.0: bus SPI0, CE0
  • /dev/spidev0.1: bus SPI0, CE1

6.3 Đọc/ghi SPI bằng spidev

pip install spidev

6.3.1 Đọc ADC MCP3008 (ADC 8 kênh)

import spidev

# Mở SPI
spi = spidev.Spi

#### 6.3.2 Truyền và nhận dữ liệu SPI phổ dụng

```python
import spidev

spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 500000
spi.mode = 0

# Gửi và nhận (song công, gửi 0xFF đồng thời đọc)
data_out = [0xFF, 0x00, 0x55]
data_in = spi.xfer2(data_out)  # Gửi 3 byte, nhận 3 byte
print(f"Gửi: {[hex(x) for x in data_out]}")
print(f"Nhận: {[hex(x) for x in data_in]}")

# Chỉ gửi (bỏ qua kết quả nhận)
spi.writebytes([0x01, 0x02, 0x03])

spi.close()

7. Ví dụ tổng hợp

7.1 Ví dụ 1: Đèn LED hiệu ứng thở + điều khiển bằng nút bấm

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

LED_PIN = 17      # BCM 17
BTN_PIN = 18      # BCM 18

GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.setup(BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

pwm = GPIO.PWM(LED_PIN, 500)
pwm.start(0)

running = True  # Trạng thái LED

try:
    while True:
        if GPIO.input(BTN_PIN) == GPIO.LOW:
            running = not running
            time.sleep(0.3)  # Khử nhiễu

        if running:
            # Hiệu ứng thở
            for duty in range(0, 101, 2):
                pwm.ChangeDutyCycle(duty)
                time.sleep(0.01)
            for duty in range(100, -1, -2):
                pwm.ChangeDutyCycle(duty)
                time.sleep(0.01)
        else:
            pwm.ChangeDutyCycle(0)

except KeyboardInterrupt:
    pwm.stop()
    GPIO.cleanup()

7.2 Ví dụ 2: Đọc cảm biến nhiệt độ và độ ẩm SHT30 + ghi dữ liệu tự động

import smbus2
import time
import csv
from datetime import datetime

class SHT30:
    def __init__(self, bus_id=20, addr=0x44):
        self.bus = smbus2.SMBus(bus_id)
        self.addr = addr

    def read(self):
        self.bus.write_i2c_block_data(self.addr, 0x2C, [0x06])
        time.sleep(0.015)
        data = self.bus.read_i2c_block_data(self.addr, 0x00, 6)
        t = -45 + 175 * ((data[0] \u003c\u003c 8) | data[1]) / 65535.0
        h = 100 * ((data[3] \u003c\u003c 8) | data[4]) / 65535.0
        return round(t, 2), round(h, 2)

sensor = SHT30()

# Kiểm tra đọc dữ liệu
t, h = sensor.read()
print(f"[{datetime.now()}] Nhiệt độ: {t}°C, Độ ẩm: {h}%")

# Ghi dữ liệu liên tục vào CSV (mỗi phút một lần)
with open('sensor_log.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['Thời gian', 'Nhiệt độ (C)', 'Độ ẩm (%)'])
    for i in range(10):  # Ghi 10 lần
        t, h = sensor.read()
        now = datetime.now().strftime('%H:%M:%S')
        writer.writerow([now, t, h])
        print(f"[{now}] {t}°C, {h}%")
        time.sleep(60)

7.3 Ví dụ 3: gpiozero - một dòng hoàn tất

from gpiozero import LED, Button
from signal import pause
import time

led = LED(17)
btn = Button(18, pull_up=True)

# Ấn xuống bật, nhả ra tắt
btn.when_pressed = led.on
btn.when_released = led.off

# Nhấn kép chuyển sang chế độ nhấp nháy
press_count = 0

def double_click():
    global press_count
    press_count += 1
    time.sleep(0.3)
    if press_count == 2:
        led.blink()
        print("Nhấn kép! LED chuyển sang chế độ nhấp nháy")
        press_count = 0

btn.when_pressed = double_click

pause()

8. Lưu ý và xử lý sự cố

8.1 Chuyển đổi mức điện áp (3.3V so với 5V)

\u003e :warning: GPIO Raspberry Pi sử dụng mức logic 3.3V, cấm kết nối trực tiếp với thiết bị 5V!

Tình huống Cách xử lý
Ngõ ra cảm biến 5V → Ngõ vào Raspberry Pi Sử dụng mạch chuyển đổi mức (ví dụ: TXS0108E) hoặc phân áp bằng điện trở
Ngõ ra Raspberry Pi → Ngõ vào thiết bị 5V Một số thiết bị 5V có thể nhận 3.3V là mức cao (ví dụ: WS2812B)
Cảm biến 3.3V → Raspberry Pi Có thể kết nối trực tiếp :white_check_mark:

Phân áp (5V → 3.3V):

    5V ──┬── R1 (1.8kΩ) ──┬── GPIO Raspberry Pi
         │                │
        GND ── R2 (3.3kΩ) ─┘

8.2 Điện trở kéo lên/kéo xuống

  • Kéo lên nội bộ: Dùng pull_up_down=GPIO.PUD_UP trong RPi.GPIO
  • Kéo xuống nội bộ: Dùng pull_up_down=GPIO.PUD_DOWN trong RPi.GPIO
  • libgpiod: gpiod.LINE_REQ_FLAG_BIAS_PULL_UP
  • Kéo lên ngoài: Dùng điện trở 4.7kΩ ~ 10kΩ nối với 3.3V

8.3 Giới hạn dòng điện

Thông số Giới hạn
Dòng tối đa mỗi chân GPIO 16mA
Tổng dòng tất cả các chân GPIO 50mA
Tổng dòng các chân 3.3V ~500mA
Tổng dòng các chân 5V Tùy thuộc nguồn cấp

\u003e :light_bulb: Khi cấp trực tiếp cho LED phải nối tiếp điện trở giới hạn dòng 220Ω ~ 330Ω!

8.4 Các lỗi thường gặp

Lỗi Nguyên nhân Cách khắc phục
RuntimeError: No access to /dev/mem Thiếu quyền sudo python3 script.py
RuntimeError: Pin already in use Chân đã bị tiến trình khác chiếm dụng Kiểm tra /sys/class/gpio/, giải phóng hoặc khởi động lại
[Errno 13] Permission denied + i2c Chưa bật I2C hoặc thiếu quyền Thêm người dùng vào nhóm i2c
Could not open /dev/spidev0.0 Chưa bật SPI Kiểm tra /boot/config.txt
LED không sáng Đấu ngược cực Chân dài nối GPIO, chân ngắn nối GND
Nút bấm kích hoạt sai Chưa khử nhiễu Thêm bouncetime trong code hoặc dùng độ trễ khử rung

8.5 Thiết lập quyền

# Thêm người dùng hiện tại vào nhóm i2c, gpio
sudo usermod -aG i2c,gpio $USER

# Đăng nhập lại để có hiệu lực
exec su - $USER

8.6 Tài liệu tham khảo trực tuyến


\u003e Tác giả: zeruns
\u003e Hướng dẫn này được kiểm tra và viết dựa trên Raspberry Pi 4B + Debian 12 Bookworm