从传感器到可视化构建树莓派温湿度监控系统的完整实践书房里的绿植总是不明原因枯萎小型温室的环境波动难以掌控这些场景背后往往隐藏着温湿度变化的秘密。本文将带你超越基础传感器读数用树莓派和DHT11构建一个带历史数据记录的本地监控系统把原始数据转化为可交互的视觉信息。1. 硬件配置与基础代码优化在开始系统搭建前确保你已准备好以下组件树莓派任何型号均可推荐使用3B或4BDHT11温湿度传感器模块3根杜邦线建议使用母对母连线方案DHT11 VCC → 树莓派 3.3V (引脚1或17) DHT11 DATA → 树莓派 GPIO12 (引脚32) DHT11 GND → 树莓派 GND (引脚6或9)原始代码中的轮询检测方式虽然有效但存在几个明显问题缺乏异常处理、代码复用性差、采样间隔不精确。以下是优化后的传感器读取类import RPi.GPIO as GPIO import time class DHT11: def __init__(self, pin): self.pin pin GPIO.setmode(GPIO.BOARD) GPIO.setup(self.pin, GPIO.OUT) self.last_read_time 0 def read(self): # 确保采样间隔≥2秒 if time.time() - self.last_read_time 2: time.sleep(2 - (time.time() - self.last_read_time)) try: # 发送开始信号 GPIO.output(self.pin, 0) time.sleep(0.02) GPIO.output(self.pin, 1) GPIO.setup(self.pin, GPIO.IN) # 等待传感器响应 if not self._wait_for_pin(0, 0.1) or not self._wait_for_pin(1, 0.1): raise Exception(传感器响应超时) # 读取40位数据 data self._read_bits() # 验证校验和 if not self._validate_checksum(data): raise Exception(校验失败) # 解析温湿度 humidity, temperature self._parse_data(data) self.last_read_time time.time() return humidity, temperature except Exception as e: print(f读取失败: {str(e)}) return None, None def _wait_for_pin(self, state, timeout): start time.time() while GPIO.input(self.pin) state: if time.time() - start timeout: return False return True def _read_bits(self): bits [] for _ in range(40): if not self._wait_for_pin(0, 0.0001): break start time.time() if not self._wait_for_pin(1, 0.0001): break duration time.time() - start bits.append(1 if duration 0.00003 else 0) return bits def _validate_checksum(self, data): if len(data) ! 40: return False checksum sum(data[i] for i in range(32, 40)) calculated sum(data[i] for i in range(0, 32)) return checksum calculated def _parse_data(self, data): humidity data[0] * 128 data[1] * 64 data[2] * 32 data[3] * 16 \ data[4] * 8 data[5] * 4 data[6] * 2 data[7] * 1 temperature data[16] * 128 data[17] * 64 data[18] * 32 data[19] * 16 \ data[20] * 8 data[21] * 4 data[22] * 2 data[23] * 1 return humidity, temperature提示DHT11的精度有限温度±2℃湿度±5%如需更高精度可考虑DHT22或BME280传感器2. 数据持久化方案比较与实现单纯的实时读数价值有限历史数据记录才能揭示环境变化规律。以下是三种常见存储方案的对比方案优点缺点适用场景CSV文件简单直观无需额外依赖查询效率低无并发支持小型项目数据量小SQLite轻量级支持SQL查询需要基本数据库知识大多数本地应用InfluxDB专为时序数据优化高效需要单独安装维护高频采样大数据量SQLite实现示例import sqlite3 from datetime import datetime class DataLogger: def __init__(self, db_pathsensor_data.db): self.conn sqlite3.connect(db_path) self._init_db() def _init_db(self): cursor self.conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS readings ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, temperature REAL, humidity REAL ) ) self.conn.commit() def log_reading(self, temperature, humidity): cursor self.conn.cursor() cursor.execute( INSERT INTO readings (timestamp, temperature, humidity) VALUES (datetime(now), ?, ?) , (temperature, humidity)) self.conn.commit() def get_readings(self, hours24): cursor self.conn.cursor() cursor.execute( SELECT timestamp, temperature, humidity FROM readings WHERE timestamp datetime(now, ?) ORDER BY timestamp , (f-{hours} hours,)) return cursor.fetchall() def close(self): self.conn.close()结合之前的DHT11类可以创建定时记录任务from time import sleep from threading import Timer class ScheduledLogger: def __init__(self, sensor, logger, interval300): self.sensor sensor self.logger logger self.interval interval self._timer None def start(self): self._run() def _run(self): humidity, temperature self.sensor.read() if humidity is not None and temperature is not None: self.logger.log_reading(temperature, humidity) self._timer Timer(self.interval, self._run) self._timer.start() def stop(self): if self._timer: self._timer.cancel()3. Web可视化界面搭建数据存储后我们需要一个直观的展示方式。Flask是Python轻量级Web框架的绝佳选择from flask import Flask, render_template import json from datetime import datetime, timedelta app Flask(__name__) app.route(/) def dashboard(): # 获取最近24小时数据 readings logger.get_readings(24) # 准备图表数据 timestamps [r[0] for r in readings] temperatures [r[1] for r in readings] humidities [r[2] for r in readings] return render_template(dashboard.html, current_temptemperatures[-1] if temperatures else None, current_humidhumidities[-1] if humidities else None, timestampsjson.dumps(timestamps), temperaturesjson.dumps(temperatures), humiditiesjson.dumps(humidities))配套的HTML模板templates/dashboard.html!DOCTYPE html html head title环境监控面板/title script srchttps://cdn.jsdelivr.net/npm/chart.js/script style .container { max-width: 1000px; margin: 0 auto; } .current-readings { display: flex; margin-bottom: 20px; } .reading-box { flex: 1; text-align: center; padding: 15px; } .temp-box { background: #ffebee; } .humid-box { background: #e3f2fd; } .reading-value { font-size: 2.5em; font-weight: bold; } .reading-label { color: #666; } /style /head body div classcontainer h1环境监控面板/h1 div classcurrent-readings div classreading-box temp-box div classreading-label当前温度/div div classreading-value{{ current_temp|round(1) }}°C/div /div div classreading-box humid-box div classreading-label当前湿度/div div classreading-value{{ current_humid|round(1) }}%/div /div /div canvas idhistoryChart/canvas /div script const ctx document.getElementById(historyChart).getContext(2d); const chart new Chart(ctx, { type: line, data: { labels: JSON.parse({{ timestamps|tojson }}), datasets: [ { label: 温度 (°C), data: JSON.parse({{ temperatures|tojson }}), borderColor: rgb(255, 99, 132), backgroundColor: rgba(255, 99, 132, 0.1), tension: 0.1 }, { label: 湿度 (%), data: JSON.parse({{ humidities|tojson }}), borderColor: rgb(54, 162, 235), backgroundColor: rgba(54, 162, 235, 0.1), tension: 0.1 } ] }, options: { responsive: true, scales: { x: { type: time, time: { unit: hour, tooltipFormat: MMM D, HH:mm } } } } }); /script /body /html4. 系统集成与部署优化将各个组件整合为完整的系统服务import signal import sys def shutdown_handler(signum, frame): print(\n正在关闭服务...) scheduler.stop() logger.close() sys.exit(0) if __name__ __main__: # 初始化组件 sensor DHT11(pin12) logger DataLogger() scheduler ScheduledLogger(sensor, logger, interval300) # 注册关机信号处理 signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler) # 启动服务 scheduler.start() app.run(host0.0.0.0, port8080)部署优化建议系统服务化sudo nano /etc/systemd/system/environment_monitor.service添加以下内容[Unit] DescriptionEnvironment Monitoring Service Afternetwork.target [Service] ExecStart/usr/bin/python3 /path/to/your/script.py WorkingDirectory/path/to/your/ Userpi Grouppi Restartalways [Install] WantedBymulti-user.target开机自启sudo systemctl enable environment_monitor sudo systemctl start environment_monitor性能优化调整采样频率温室监控可设为5分钟实验室可能需1分钟定期归档历史数据每月将旧数据移至单独数据库添加简单的访问控制如Flask-HTTPAuth常见问题排查传感器无响应检查连线是否松动尝试更换GPIO引脚Web页面无法访问确认防火墙允许8080端口sudo ufw allow 8080数据不更新检查定时器线程是否正常运行查看系统日志journalctl -u environment_monitor -f