这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 开源硬件 » 【树莓派5】环境监测仪

共8条 1/1 1 跳转至

【树莓派5】环境监测仪

工程师
2026-03-26 17:59:34   被打赏 50 分(兑奖)     打赏

【树莓派5】环境监测仪

本文介绍了树莓派 5 单板计算机结合 SPI 通信协议驱动 LCD 彩色显示屏、结合 IIC 协议驱动 AHT20 和 BMP280 传感器,进而实现环境状态实时监测的项目设计。

项目介绍

项目包括驱动 LCD 彩屏、驱动 AHT20 & BMP280 模块、综合搭建环境监测仪三部分。

  • LCD 彩屏:模块介绍、硬件连接、环境搭建、关键代码、效果演示;

  • AHT20 和 BMP20:模块介绍、硬件连接、软件包部署、代码、效果演示;

  • 环境监测仪:硬件连接、工程代码、效果演示;

LCD 彩屏

结合 SPI 接口驱动 ST7735 协议的 LCD 彩色显示屏。

LCD 模块

使用 OpenMV LCD 彩色屏幕模块,基于 ST7735R 通信协议。

lcd_st7735r.jpg

引脚定义

openmv_lcd_pinout.jpg

LCD 模块:LCD Shield – OpenMV .

原理图

SCH_lcd.jpg

驱动库:adafruit/Adafruit_CircuitPython_RGB_Display: Drivers for RGB displays for Adafruit CircuitPython.

硬件连接


RaspberryPiLCDNote
GNDGNDGround
3.3VVinPower
Pin 22RSTReset
Pin 18DC/RSCommand
Pin 24CSChip Select
Pin 19MOSIData
Pin 23SCLKClock

详见:Python Wiring and Setup | Adafruit 1.14" 240x135 Color TFT Breakout LCD Display | Adafruit Learning System .

树莓派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 显示图片

openmv_lcd_display.jpg


AHT20 & BMP280 传感器

使用 IIC 协议驱动 AHT20 温湿度传感器和 BMP280 气压传感器,并终端打印数据。

模块

该模块由奥松数字温湿度传感器 AHT20 与数字气压传感器 BMP280 组成。

  • 直流 3.3V 供电,体积小巧,便于集成。

  • 两个传感器的通信引脚接至同一 IIC 总线, 板载 4.7K 上拉电阻。

aht20_bmp280_module.jpg

参数特点

AHT20


ParameterValue
Supply Voltage2.0V to 5.5V
Operating Current0.25 mA (average)
Temperature Range-40°C to 85°C
Temperature Accuracy±0.3°C
Humidity Range0% to 100% RH
Humidity Accuracy±2% RH
Communication ProtocolI²C


BMP280



ParameterValue
Supply Voltage1.71V to 3.6V
Operating Current2.7 µA (in normal mode)
Pressure Range300 hPa to 1100 hPa
Pressure Accuracy±1 hPa
Temperature Range-40°C to 85°C
Temperature Accuracy±1°C
Communication ProtocolI²C or SPI

硬件连接


RaspberryPiAHT20&BMP280 moduleNote
GNDGNDGround
3.3VVccPower
Pin3SDASerial Data
Pin5SCLSerial 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 获取传感器地址

i2cdetect_aht20_bmp280.jpg

其中 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_print.jpg

环境监测仪

结合前面驱动 AHT20 和 BMP280 模块的程序以及 LCD 屏幕驱动程序,实现 LCD 显示实时温湿度和气压数据的环境监测仪。

硬件连接

  • 保持 LCD 屏幕和树莓派 4 连接。

  • 保持 AHT20 & BMP280 模块和树莓派 4 连接。

流程图

flowchart_lcd_env.jpg

代码

终端执行指令 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 显示实时温湿度和气压数据,同时终端连续打印温湿度和气压数据;

lcd_aht20_bmp280.jpg

总结

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






关键词: 树莓派     传感器     驱动     显示屏     模块    

专家
2026-03-29 09:00:24     打赏
2楼

谢谢分享


高工
2026-03-29 19:26:14     打赏
3楼

这个python操作外设的库是树莓派自带的嘛 


高工
2026-03-30 12:06:16     打赏
4楼

界面挺好看的!


专家
2026-03-31 14:59:49     打赏
5楼

这个屏幕是特制的,还是买现成的?适用于树莓派4、5?


专家
2026-03-31 15:02:32     打赏
6楼

楼主的帖子写的很好!程序代码、处理流程都贴上来了,学习了!谢谢!


高工
2026-04-01 08:31:05     打赏
7楼

谢谢分享!!!


高工
2026-04-02 12:31:30     打赏
8楼

你这个BMP280看起来精度是比较不错的,我在淘宝上购买过多款的BMP280,精度都很糟糕。文章写的很好。


共8条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]