这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【树莓派Zero2W】——完成一个环境监控器

共1条 1/1 1 跳转至

【树莓派Zero2W】——完成一个环境监控器

助工
2025-12-28 20:45:01     打赏

硬件:树莓派Zero 2 W、Unit ENV-III 环境传感器、Gravity: PM2.5空气质量传感器、微雪3寸墨水屏模块红黄黑白400×168分辨率墨水屏。

软件:Vscode+python。

image.png


使用Zero2W,结合传感器制作一个环境传感器。SHT30是一个温湿度传感器。其中一个传感器是基于热电阻原理的温度传感器,另一个是基于电容原理的湿度传感器。传感器通过I2C接口与微控制器通信,提供温度和湿度的数字读数。Gravity: PM2.5空气质量传感器是一个基于激光散射原理的数字式通用颗粒物传感器,可连续采集并计算单位体积内空气中不同粒径的悬浮颗粒物个数,即颗粒物浓度分布,进而换算成为质量浓度,并利用I2C接口输出相关数据。这两个传感器都是通过I2C总线与Zero2W连接,接在I2C1上(3、5脚)。墨水屏使用SPI总线与树莓派连接,这个墨水屏是四色墨水屏,无法做局刷,全屏刷新一次需要10秒左右,所以这里使用50秒进行一次更新。每50秒读取一次温湿度和空气质量信息,然后在屏幕上进行显示。

image.png

编程使用vscode+python。vscode的ssh插件,可以很方便地连接到Zero2W上,直接编辑python脚本。很方便。python编程也是很简单,有很多成熟的包可以直接调用。

image.png

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys
import os
import time
import smbus

from dfrobot_airqualitysensor.dfrobot_airqualitysensor import DFRobot_AirQualitySensor
picdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pic')
libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')
if os.path.exists(libdir):
    sys.path.append(libdir)

import logging
from waveshare_epd import epd3in0g
import time
from PIL import Image, ImageDraw, ImageFont
import traceback
import RPi.GPIO as GPIO
from time import sleep


airinfo={'temperature': 20.682078278782328, 'humidity': 57.39375906004425, 'pm': [36, 56, 58]}
# I2C配置
ADDR = 0x44
BUS_NUM = 1
# SHT30命令
CMD_MEASURE = 0x2C06  # 高精度测量,无时钟拉伸
CMD_RESET = 0x30A2    # 软复位

bus = smbus.SMBus(BUS_NUM)
def crc8(data):
    """计算SHT30的CRC8校验码"""
    crc = 0xFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x80:
                crc = (crc << 1) ^ 0x31
            else:
                crc <<= 1
        crc &= 0xFF
    return crc

def read_sht30():
    """
    读取温湿度
    返回: (温度, 湿度) 或 (None, None) 如果失败
    """
    try:
        # 发送测量命令
        bus.write_i2c_block_data(ADDR, CMD_MEASURE >> 8, [CMD_MEASURE & 0xFF])
       
        # 等待测量完成 (约15.5ms)
        time.sleep(0.016)
       
        # 读取6字节数据 [TMSB, TLSB, TCRC, HMSB, HLSB, HCRC]
        data = bus.read_i2c_block_data(ADDR, 0x00, 6)
       
        # CRC校验
        if crc8(data[0:2]) != data[2]:
            print("温度数据CRC错误")
            return None, None
        if crc8(data[3:5]) != data[5]:
            print("湿度数据CRC错误")
            return None, None
       
        # 计算温度
        raw_temp = (data[0] << 8) | data[1]
        temperature = -45 + (175.0 * raw_temp / 65535.0)
       
        # 计算湿度
        raw_hum = (data[3] << 8) | data[4]
        humidity = 100.0 * raw_hum / 65535.0
       
        return temperature, humidity
       
    except OSError as e:
        print(f"I2C通信失败: {e}")
        return None, None

def reset_sensor():
    """复位传感器"""
    try:
        bus.write_i2c_block_data(ADDR, CMD_RESET >> 8, [CMD_RESET & 0xFF])
        time.sleep(0.001)
        return True
    except OSError as e:
        print(f"复位失败: {e}")
        return False

# 绘制温度信息
def drawTemprute():
    draw.rectangle((0, 0, 160, 55), fill=epd.WHITE, outline=epd.BLACK)  # 温湿度
    draw.rectangle((0, 0, 78, 16), fill=epd.BLACK)  # 温湿度
    draw.rectangle((81, 0, 160, 16), fill=epd.BLACK)  # 温湿度
    draw.text((20, 1), '温度', font=font12, fill=epd.WHITE)
    draw.text((102, 1), '湿度', font=font12, fill=epd.WHITE)
    # 温度按舒适范围显示 17~27.1℃ 黑色,11度以下 31度以上为红色
    if airinfo["temperature"] >= 17 and airinfo["temperature"] <= 27.1:
        draw.text((8, 21), "%.1f" % (airinfo["temperature"]), font=font26, fill=epd.BLACK)
        draw.text((94, 21), "%.0f%%" % (airinfo["humidity"]), font=font26, fill=epd.BLACK)
    elif (airinfo["temperature"] > 27.1 and airinfo["temperature"] < 31) or (
            airinfo["temperature"] < 17 and airinfo["temperature"] >= 11):
        draw.text((8, 21), "%.1f" % (airinfo["temperature"]), font=font26, fill=epd.YELLOW)
        draw.text((94, 21), "%.0f%%" % (airinfo["humidity"]), font=font26, fill=epd.YELLOW)
    else:
        draw.text((8, 21), "%.1f" % (airinfo["temperature"]), font=font26, fill=epd.RED)
        draw.text((94, 21), "%.0f%%" % (airinfo["humidity"]), font=font26, fill=epd.RED)

# 绘制空气颗粒物信息 包括标准颗粒物下PM1.0  PM2.5  PM10 的颗粒物浓度 显示使用最大值
def drawPM():
    draw.rectangle((0, 56, 160, 111), fill=epd.WHITE, outline=epd.YELLOW)  # 空气质量
    draw.rectangle((0, 56, 160, 72), fill=epd.BLACK)
    draw.text((10, 57), '标准颗粒物浓度(ug/m3)', font=font12, fill=epd.WHITE)
    pmval=max(airinfo["pm"])
    if pmval <=110:
        draw.text((10, 78), "%3d 空气干净" % (pmval), font=font26, fill=epd.BLACK)
    elif pmval<=210:
        draw.text((10, 78), "%3d 中度污染" % (pmval), font=font26, fill=epd.YELLOW)
    else:
        draw.text((10, 78), "%3d 重度污染" % (pmval), font=font26, fill=epd.RED)

# 绘制墨水屏,进行信息展示
def inofDisp():
    drawTemprute()
    drawPM()
    epd.display(epd.getbuffer(Himage))


sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))

airqualitysensor = DFRobot_AirQualitySensor()
epd = epd3in0g.EPD()        #初始化墨水屏
logging.info("init and Clear")
font12 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 12)
font18 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 18)
font20 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 20)
font24 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 24)
font26 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 26)
# Drawing on the image
epd.init()      #墨水屏初始化
epd.Clear()
Himage = Image.new('RGB', (epd.height, epd.width), 0xffffff)
draw = ImageDraw.Draw(Himage)

if __name__ == '__main__':
    if not reset_sensor():
        print("无法与传感器通信,请检查硬件连接和I2C配置。")
        exit(1)
    print(airqualitysensor.gain_version())
    while True:
        airinfo["pm"]=airqualitysensor.read_standard()
        airinfo["temperature"],airinfo["humidity"] = read_sht30()
        print(airinfo)
        # 收集完成传感器信息,开始绘制屏幕 提供信息展示
        inofDisp()
        time.sleep(50)

效果演示:

市内空气正常状况。

image.png

点燃一张纸,对空气进行污染。可以看见空气质量信息使用红色显示。

image.png

等待一会,等待烟尘散去,空气质量污染程度下降,黄色显示状态。

image.png

恢复正常状态。

image.png

7921c123adfa6d349045f5a29d4d1e7f.png

源码:zero2w.tar.zip



共1条 1/1 1 跳转至

回复

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