【树莓派5】环境监测仪
本文介绍了树莓派 5 单板计算机结合 SPI 通信协议驱动 LCD 彩色显示屏、结合 IIC 协议驱动 AHT20 和 BMP280 传感器,进而实现环境状态实时监测的项目设计。
项目介绍
项目包括驱动 LCD 彩屏、驱动 AHT20 & BMP280 模块、综合搭建环境监测仪三部分。
LCD 彩屏:模块介绍、硬件连接、环境搭建、关键代码、效果演示;
AHT20 和 BMP20:模块介绍、硬件连接、软件包部署、代码、效果演示;
环境监测仪:硬件连接、工程代码、效果演示;
LCD 彩屏
结合 SPI 接口驱动 ST7735 协议的 LCD 彩色显示屏。
LCD 模块
使用 OpenMV LCD 彩色屏幕模块,基于 ST7735R 通信协议。


引脚定义


LCD 模块:LCD Shield – OpenMV .
原理图


驱动库:adafruit/Adafruit_CircuitPython_RGB_Display: Drivers for RGB displays for Adafruit CircuitPython.
硬件连接
| RaspberryPi | LCD | Note |
| GND | GND | Ground |
| 3.3V | Vin | Power |
| Pin 22 | RST | Reset |
| Pin 18 | DC/RS | Command |
| Pin 24 | CS | Chip Select |
| Pin 19 | MOSI | Data |
| Pin 23 | SCLK | Clock |
树莓派40Pin引脚定义: Raspberry Pi GPIO Pinout .
安装软件包
创建虚拟环境,安装所需 ST7735 RGB LCD 屏幕驱动软件包
python3 -m venv .venv source .venv/bin/activate pip install adafruit-circuitpython-rgb-display
详见:adafruit-circuitpython-rgb-display · PyPI .
代码
终端执行指令 touch lcd_display.py 新建程序文件,并添加如下代码
import board
import digitalio
from PIL import Image, ImageDraw
from adafruit_rgb_display import st7735
# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.CE0)
dc_pin = digitalio.DigitalInOut(board.D24)
reset_pin = digitalio.DigitalInOut(board.D25)
# Config for display baudrate (default max is 24mhz):
BAUDRATE = 24000000
# Setup SPI bus using hardware SPI:
spi = board.SPI()
# Create the display:
disp = st7735.ST7735R(
spi,
rotation=90,
cs=cs_pin,
dc=dc_pin,
rst=reset_pin,
baudrate=BAUDRATE,
)
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
if disp.rotation % 180 == 90:
height = disp.width # we swap height/width to rotate it to landscape!
width = disp.height
else:
width = disp.width # we swap height/width to rotate it to landscape!
height = disp.height
image = Image.new("RGB", (width, height))
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
disp.image(image)
image = Image.open("blinka.jpg")
# Scale the image to the smaller screen dimension
image_ratio = image.width / image.height
screen_ratio = width / height
if screen_ratio < image_ratio:
scaled_width = image.width * height // image.height
scaled_height = height
else:
scaled_width = width
scaled_height = image.height * width // image.width
image = image.resize((scaled_width, scaled_height), Image.BICUBIC)
# Crop and center the image
x = scaled_width // 2 - width // 2
y = scaled_height // 2 - height // 2
image = image.crop((x, y, x + width, y + height))
# Display image.
disp.image(image)保存代码。
效果
终端执行指令 python lcd_diaplay.py 运行程序;
LCD 显示图片


AHT20 & BMP280 传感器
使用 IIC 协议驱动 AHT20 温湿度传感器和 BMP280 气压传感器,并终端打印数据。
模块
该模块由奥松数字温湿度传感器 AHT20 与数字气压传感器 BMP280 组成。
直流 3.3V 供电,体积小巧,便于集成。
两个传感器的通信引脚接至同一 IIC 总线, 板载 4.7K 上拉电阻。


参数特点
AHT20
| Parameter | Value |
| Supply Voltage | 2.0V to 5.5V |
| Operating Current | 0.25 mA (average) |
| Temperature Range | -40°C to 85°C |
| Temperature Accuracy | ±0.3°C |
| Humidity Range | 0% to 100% RH |
| Humidity Accuracy | ±2% RH |
| Communication Protocol | I²C |
BMP280
| Parameter | Value |
| Supply Voltage | 1.71V to 3.6V |
| Operating Current | 2.7 µA (in normal mode) |
| Pressure Range | 300 hPa to 1100 hPa |
| Pressure Accuracy | ±1 hPa |
| Temperature Range | -40°C to 85°C |
| Temperature Accuracy | ±1°C |
| Communication Protocol | I²C or SPI |
硬件连接
| RaspberryPi | AHT20&BMP280 module | Note |
| GND | GND | Ground |
| 3.3V | Vcc | Power |
| Pin3 | SDA | Serial Data |
| Pin5 | SCL | Serial Clock |
安装软件包
激活虚拟环境,安装所需传感器驱动包
sudo apt install python3-smbus2 python3-smbus source .venv/bin/activate pip install adafruit-circuitpython-ahtx0 pip install adafruit-circuitpython-bmp280
终端执行指令 i2cdetect -y 1 获取传感器地址


其中 0x38 对应 aht20,0x77 对应 bmp280 .
代码
终端执行指令 touch aht20_bmp280_print.py 新建程序文件,并添加如下代码
import time
import board
import adafruit_ahtx0
import adafruit_bmp280
# Create sensor object, communicating over the board's default I2C bus
i2c = board.I2C() # uses board.SCL and board.SDA
sensor = adafruit_ahtx0.AHTx0(i2c)
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)
# change this to match the location's pressure (hPa) at sea level
bmp280.sea_level_pressure = 1019.85
try:
while True:
print("\nTemperature: %0.1f C" % sensor.temperature)
print("Humidity: %0.1f %%" % sensor.relative_humidity)
print("\nTemperature: %0.1f C" % bmp280.temperature)
print("Pressure: %0.1f hPa" % bmp280.pressure)
print("Altitude = %0.2f meters" % bmp280.altitude)
time.sleep(2)
except KeyboardInterrupt:
pass
finally:
print("Exiting....")保存代码。
当前位置的海平面气压查询:天气雷达地图 | MSN 天气 .
效果
终端执行指令 python aht20_bmp280_print.py 运行程序;
终端打印温湿度和气压数据;


环境监测仪
结合前面驱动 AHT20 和 BMP280 模块的程序以及 LCD 屏幕驱动程序,实现 LCD 显示实时温湿度和气压数据的环境监测仪。
硬件连接
保持 LCD 屏幕和树莓派 4 连接。
保持 AHT20 & BMP280 模块和树莓派 4 连接。
流程图

代码
终端执行指令 touch lcd_aht20_bmp280.py 新建程序文件,并添加如下代码
import time
import board
import digitalio
import busio
from PIL import Image, ImageDraw, ImageFont
from adafruit_rgb_display import st7735 # 导入显示驱动
import adafruit_ahtx0 # 导入 AHT20 传感器库
import adafruit_bmp280 # 导入 BMP280 传感器库
# ========== 配置 ==========
# 屏幕配置
BORDER = 5
FONTSIZE_TITLE = 16
FONTSIZE_DATA = 20
FONTSIZE_SMALL = 12
# 引脚配置 (BCM编码)
cs_pin = digitalio.DigitalInOut(board.CE0) # Pin 24
dc_pin = digitalio.DigitalInOut(board.D24) # Pin 18
reset_pin = digitalio.DigitalInOut(board.D25) # Pin 22
# SPI配置
BAUDRATE = 24000000
# 根据当地气象站实时数据调整
SEA_LEVEL_PRESSURE = 1016.0
# ========== 初始化显示 ==========
spi = board.SPI()
# 初始化 ST7735 1.8寸屏幕 (128x160分辨率)
disp = st7735.ST7735R(
spi,
rotation=90, # 横屏显示
cs=cs_pin,
dc=dc_pin,
rst=reset_pin,
baudrate=BAUDRATE,
width=128,
height=160,
x_offset=0,
y_offset=0,
)
# 根据旋转方向确定画布尺寸
if disp.rotation % 180 == 90:
width = disp.height # 160
height = disp.width # 128
else:
width = disp.width # 128
height = disp.height # 160
print(f"屏幕尺寸: {width}x{height}")
# 创建图像缓冲区
image = Image.new("RGB", (width, height))
draw = ImageDraw.Draw(image)
# ========== 初始化传感器 ==========
try:
i2c = board.I2C()
# AHT20 温湿度传感器 (地址0x38)
aht20 = adafruit_ahtx0.AHTx0(i2c)
print("AHT20 传感器初始化成功")
# BMP280 气压传感器 (地址0x77)
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x77)
bmp280.sea_level_pressure = SEA_LEVEL_PRESSURE
print(f"BMP280 传感器初始化成功,海平面气压设置为: {SEA_LEVEL_PRESSURE} hPa")
sensors_ready = True
except Exception as e:
print(f"传感器初始化失败: {e}")
sensors_ready = False
# ========== 加载字体 ==========
try:
# 尝试加载系统中文字体,如果没有则使用默认字体
font_title = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", FONTSIZE_TITLE)
font_data = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", FONTSIZE_DATA)
font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FONTSIZE_SMALL)
font_cn = font_title # 如果没有中文字体,用英文替代
except OSError:
print("字体加载失败,使用默认字体")
font_title = ImageFont.load_default()
font_data = font_title
font_small = font_title
font_cn = font_title
# ========== 颜色定义 ==========
COLOR_BG = (0, 0, 0) # 黑色背景
COLOR_TITLE_BG = (31, 31, 31) # 深灰标题栏
COLOR_TEMP = (255, 140, 0) # 橙色 - 温度
COLOR_HUM = (0, 206, 209) # 青色 - 湿度
COLOR_PRESS = (144, 238, 144) # 浅绿 - 气压
COLOR_ALT = (255, 105, 180) # 粉红 - 海拔
COLOR_TEXT = (255, 255, 255) # 白色
COLOR_WARN = (255, 0, 0) # 红色
# ========== 绘制函数 ==========
def draw_rounded_rectangle(draw, xy, radius, fill, outline=None):
"""绘制圆角矩形"""
x1, y1, x2, y2 = xy
draw.rounded_rectangle(xy, radius=radius, fill=fill, outline=outline)
def get_text_size(draw, text, font):
"""获取文字尺寸"""
bbox = draw.textbbox((0, 0), text, font=font)
return bbox[2] - bbox[0], bbox[3] - bbox[1]
def draw_sensor_card(draw, x, y, w, h, title, value, unit, color, font_title, font_data, font_small):
"""绘制传感器数据卡片"""
# 绘制卡片背景
draw_rounded_rectangle(draw, (x, y, x+w, y+h), 5, (40, 40, 40), color)
# 标题
draw.text((x+5, y+3), title, font=font_title, fill=color)
# 数值
(tw, th) = get_text_size(draw, value, font_data)
draw.text((x+w//2 - tw//2, y+h//2 - th//2 + 2), value, font=font_data, fill=COLOR_TEXT)
# 单位
draw.text((x+w-13, y+h-16), unit, font=font_small, fill=color)
# ========== 主循环 ==========
print("环境监测仪启动...")
print("按 Ctrl+C 停止")
# 清屏
draw.rectangle((0, 0, width, height), fill=COLOR_BG)
disp.image(image)
update_interval = 2 # 更新间隔秒数
last_update = 0
try:
while True:
current_time = time.time()
# 每隔 update_interval 秒更新一次数据
if current_time - last_update >= update_interval:
last_update = current_time
# 读取传感器数据
if sensors_ready:
try:
# AHT20 数据
temp_aht = aht20.temperature
humidity = aht20.relative_humidity
# BMP280 数据
temp_bmp = bmp280.temperature
pressure = bmp280.pressure
altitude = bmp280.altitude
# 计算平均温度
avg_temp = (temp_aht + temp_bmp) / 2
# 控制台输出
print(f"\n[{time.strftime('%H:%M:%S')}]")
print(f"温度: {avg_temp:.1f}°C | 湿度: {humidity:.1f}%")
print(f"气压: {pressure:.1f} hPa | 海拔: {altitude:.1f}m")
data_valid = True
except Exception as e:
print(f"传感器读取错误: {e}")
data_valid = False
else:
data_valid = False
# 绘制界面
draw.rectangle((0, 0, width, height), fill=COLOR_BG)
# 标题栏
draw.rectangle((0, 0, width, 22), fill=COLOR_TITLE_BG)
title_text = "ENV Monitor"
(tw, th) = get_text_size(draw, title_text, font_title)
draw.text((width//2 - tw//2, 3), title_text, font=font_title, fill=COLOR_TEXT)
if data_valid:
# 布局参数
card_w = (width - 15) // 2
card_h = 48
margin = 5
# 第1行:温度卡片 (左) + 湿度卡片 (右)
draw_sensor_card(draw, 5, 25, card_w, card_h,
"TEMP", f"{avg_temp:.1f}", "C",
COLOR_TEMP, font_small, font_data, font_small)
draw_sensor_card(draw, 10 + card_w, 25, card_w, card_h,
"HUM", f"{humidity:.1f}", "%",
COLOR_HUM, font_small, font_data, font_small)
# 第2行:气压卡片 (全宽)
draw.rectangle((5, 25 + card_h + margin, width-5, 25 + card_h*2 + margin),
fill=(40, 40, 40), outline=COLOR_PRESS, width=2)
draw.text((10, 25 + card_h + margin + 2), "PRESSURE", font=font_small, fill=COLOR_PRESS)
press_text = f"{pressure:.1f} hPa"
(tw, th) = get_text_size(draw, press_text, font_data)
draw.text((width//2 - tw//2, 25 + card_h + margin + 18), press_text,
font=font_data, fill=COLOR_TEXT)
# 第3行:海拔 + 更新时间
draw.text((10, height - 35), f"Altitude: {altitude:.1f} m",
font=font_small, fill=COLOR_ALT)
# 更新时间
time_str = time.strftime("%H:%M:%S")
(tw, th) = get_text_size(draw, time_str, font_small)
draw.text((width - tw - 5, height - 15), time_str, font=font_small, fill=(128, 128, 128))
# 状态指示器 (绿色圆点)
draw.ellipse((5, height-12, 12, height-5), fill=(0, 255, 0))
else:
# 传感器错误提示
draw.text((10, height//2 - 10), "Sensor Error!", font=font_data, fill=COLOR_WARN)
draw.text((10, height//2 + 15), "Check I2C connection", font=font_small, fill=COLOR_TEXT)
# 推送显示
disp.image(image)
# 短暂休眠,避免CPU占用过高
time.sleep(0.1)
except KeyboardInterrupt:
print("\n程序被用户中断")
except Exception as e:
print(f"\n程序错误: {e}")
finally:
# 退出时显示停止信息
try:
draw.rectangle((0, 0, width, height), fill=COLOR_BG)
draw.text((width//2 - 40, height//2 - 10), "STOPPED", font=font_data, fill=COLOR_WARN)
disp.image(image)
print("显示已关闭")
except:
pass保存代码。
效果
终端执行指令 python lcd_aht20_bmp280.py 运行程序;
LCD 显示实时温湿度和气压数据,同时终端连续打印温湿度和气压数据;


总结
本文介绍了树莓派 5 单板计算机结合 SPI 通信协议驱动 LCD 彩色显示屏、结合 IIC 协议驱动 AHT20 和 BMP280 传感器,进而实现环境状态实时监测的项目设计,为相关产品在物联网领域的快速开发和应用设计提供了参考。
我要赚赏金
