ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于树莓派与DS18B20的物联网温度监控系统实战指南

基于树莓派与DS18B20的物联网温度监控系统实战指南 1. 项目概述用树莓派打造一个温度追踪器最近在整理工作室的设备发现一个老款的树莓派4B闲置着琢磨着给它找个活儿干。正好工作室里有个小温室里面养了些对温度比较敏感的植物还有一台3D打印机打印ABS材料时对腔体温度也有要求。手动拿温度计去测不仅麻烦数据还不连续。于是我就想能不能用这个树莓派做个持续工作的温度追踪器这个想法听起来简单但真做起来从传感器选型、数据采集、到后端存储和前段展示每一步都有不少门道。这个“Temperature Tracker using Raspberry Pi”项目本质上是一个物联网IoT数据采集与监控系统。它的核心功能是让树莓派这个微型电脑连接上一个或多个温度传感器以设定的频率比如每分钟一次读取环境温度数据然后将这些数据保存下来并通过一个网页界面实时展示和历史查询。它解决的痛点就是低成本、自动化、可远程访问的持续环境监测。无论是想监控家里的地下室是否潮湿阴冷还是想记录鱼缸的水温变化甚至是观测服务器机柜的内部温度这个方案都特别合适。你不需要是个电子或编程专家只要跟着步骤一步步来花上一个下午的时间就能拥有一个属于自己的、24小时不间断工作的温度监控站。2. 核心硬件选型与电路连接解析2.1 传感器选型DS18B20 vs DHT11/DHT22做温度监测第一步就是挑传感器。市面上常见的、适合树莓派的有两类单总线的DS18B20和数字温湿度一体的DHT11/DHT22。DS18B20是我这次项目的首选原因有几个。首先它的精度足够高典型精度在±0.5°C对于绝大多数环境监测场景完全够用。其次它采用独特的“单总线”协议只需要一根数据线外加电源和地线就能与树莓派通信这意味着你可以用树莓派的一个GPIO引脚通过并联的方式连接多个传感器给不同房间或位置编号实现多点监测布线非常简洁。最后它本身是防水的有封装好的探头版本可以直接扔进水里测水温应用场景更广。DHT11/DHT22这类传感器能同时提供温度和湿度数据对于需要温湿度共同监测的场景如温室、仓库是很好的选择。DHT22的精度和量程比DHT11更好。但它们使用的是自定义的单线通信协议对时序要求严格编程稍复杂且响应速度不如DS18B20在需要快速或高密度采样的场景下可能不太合适。注意如果你只需要温度数据且可能有多点监测需求DS18B20是更清晰、更可靠的选择。如果需要温湿度且采样频率不高如每分钟一次DHT22是性价比之选。2.2 所需物料清单与电路连接硬件部分非常简单除了树莓派任何型号均可推荐3B或4B性能更充裕你还需要DS18B20温度传感器建议购买已焊接好引线和上拉电阻的模块省事。4.7kΩ - 10kΩ的电阻一枚用于单总线的上拉如果模块已集成则不需要。母对母杜邦线若干用于连接。一个面包板非必须但方便测试和扩展。连接电路是电子项目的“Hello World”。树莓派有40个GPIO针脚我们需要用到3.3V电源、接地GND和一个数据引脚。接线步骤以DS18B20为例电源将传感器的VCC或红色线连接到树莓派的3.3V引脚例如物理引脚1。切记不要接到5V引脚虽然DS18B20兼容5V但为了保险和统一使用3.3V更安全。接地将传感器的GND或黑色线连接到树莓派的任意GND引脚例如物理引脚6。数据将传感器的DATA或黄色线连接到树莓派的一个GPIO引脚例如我习惯用GPIO4它对应物理引脚7。上拉电阻在传感器的DATA线和3.3V电源线之间连接一个4.7kΩ的电阻。这个电阻至关重要它用于在总线空闲时将数据线稳定在高电平确保通信可靠。很多成品模块已经集成了这个电阻。连接好后你的电路看起来应该非常简洁三根线从传感器出来分别接到树莓派的3.3V、GND和GPIO4然后在GPIO4和3.3V之间多了一个电阻如果模块没有的话。2.3 树莓派系统准备与基础配置在连接硬件之前我们需要确保树莓派的系统就绪。我使用的是官方的Raspberry Pi OS以前叫Raspbian用SD卡刷好系统后首次启动完成基础设置。有几个关键的系统配置需要提前做启用SSH和VNC为了方便后续无头无显示器操作在系统首选项的“Raspberry Pi Configuration”工具中找到“Interfaces”标签页将SSH和VNC都启用。这样你就可以用另一台电脑通过命令行或远程桌面来控制树莓派了。更新系统打开终端首先运行sudo apt update和sudo apt upgrade -y确保所有软件包都是最新的。一个更新的系统能避免很多奇怪的兼容性问题。启用单总线1-Wire接口这是让DS18B20工作的关键。同样在“Raspberry Pi Configuration”的“Interfaces”标签页找到“1-Wire”选项将其启用。你也可以通过命令行操作sudo raspi-config然后依次选择Interface Options-1-Wire-Yes完成后重启。重启后你可以验证1-Wire是否启用成功。在终端输入lsmod | grep w1如果看到w1_gpio和w1_therm模块就说明驱动加载成功了。3. 软件架构设计与核心组件部署一个完整的温度追踪器不仅仅是读一下传感器数据。我们需要一个稳定、可扩展的软件架构来负责数据采集、存储和展示。我设计的架构分为三层数据采集层、数据服务层和数据展示层。3.1 数据采集层Python脚本与定时任务采集层的工作是用Python脚本与DS18B20传感器通信读取温度值。得益于Linux内核的支持DS18B20的数据会以文件形式出现在/sys/bus/w1/devices/目录下。每个传感器都有一个唯一的ID如28-xxxx对应一个文件夹里面的w1_slave文件就包含了温度数据。我写了一个简单的Python脚本read_temperature.pyimport os import time import glob from datetime import datetime # 1-Wire设备的基础路径 base_dir /sys/bus/w1/devices/ # 找到所有以“28-”开头的设备文件夹即DS18B20传感器 device_folders glob.glob(base_dir 28-*) def read_temp_raw(device_file): 读取传感器原始数据文件 with open(device_file, r) as f: lines f.readlines() return lines def read_temp(device_folder): 从原始数据中解析出摄氏度温度值 device_file device_folder /w1_slave lines read_temp_raw(device_file) # 检查数据是否有效CRC校验 while lines[0].strip()[-3:] ! YES: time.sleep(0.2) lines read_temp_raw(device_file) equals_pos lines[1].find(t) if equals_pos ! -1: temp_string lines[1][equals_pos2:] temp_c float(temp_string) / 1000.0 # 原始数据是毫摄氏度 return temp_c return None if __name__ __main__: for device_folder in device_folders: sensor_id device_folder.split(/)[-1] # 提取传感器ID temperature read_temp(device_folder) if temperature is not None: timestamp datetime.now().strftime(%Y-%m-%d %H:%M:%S) # 打印到控制台后续会改为写入数据库 print(f{timestamp} - Sensor {sensor_id}: {temperature:.2f}°C) else: print(fFailed to read from sensor {sensor_id})这个脚本的核心逻辑是定位传感器、读取文件、校验数据、解析温度。现在它只是打印出来我们需要让它定期运行。这里我使用Linux系统自带的Cron定时任务。在终端输入crontab -e然后在文件末尾添加一行*/5 * * * * /usr/bin/python3 /home/pi/temperature_tracker/read_temperature.py /home/pi/temperature.log 21这行配置的意思是每5分钟用python3运行一次我们的脚本并将输出包括错误信息追加到temperature.log日志文件中。这样数据采集的自动化就完成了。3.2 数据服务层轻量级数据库选型与表设计将数据打印到日志文件不是长久之计不利于查询和分析。我们需要一个数据库。对于树莓派这种资源有限的设备SQLite是绝佳选择。它无需安装独立的数据库服务整个数据库就是一个文件零配置通过Python标准库sqlite3就能直接操作。我们在采集脚本里加入数据库操作。首先创建一个数据库和表import sqlite3 DB_PATH /home/pi/temperature_data.db def init_database(): conn sqlite3.connect(DB_PATH) c conn.cursor() # 创建数据表包含时间戳、传感器ID和温度值 c.execute(CREATE TABLE IF NOT EXISTS temperature_readings (timestamp DATETIME, sensor_id TEXT, temperature REAL)) # 创建索引以提高按时间和传感器查询的速度 c.execute(CREATE INDEX IF NOT EXISTS idx_time ON temperature_readings(timestamp)) c.execute(CREATE INDEX IF NOT EXISTS idx_sensor ON temperature_readings(sensor_id)) conn.commit() conn.close()然后修改主循环将读取到的数据插入数据库def save_to_database(sensor_id, temperature): conn sqlite3.connect(DB_PATH) c conn.cursor() timestamp datetime.now().strftime(%Y-%m-%d %H:%M:%S) c.execute(INSERT INTO temperature_readings VALUES (?, ?, ?), (timestamp, sensor_id, temperature)) conn.commit() conn.close() # 在主循环中调用 # temperature read_temp(device_folder) # save_to_database(sensor_id, temperature)使用SQLite我们几乎不增加系统负担就能实现数据的结构化存储。表结构设计简单明了时间、传感器ID、温度值。加上索引后即使数据积累到几十万条查询速度也依然很快。3.3 数据展示层使用Flask构建Web仪表盘数据存好了我们还需要一个直观的方式来看。在树莓派上搭建一个轻量的Web服务器是最佳选择。Python的Flask框架简单易用非常适合这个任务。首先安装Flaskpip install flask。然后创建一个新的Python文件app.pyfrom flask import Flask, render_template, jsonify import sqlite3 from datetime import datetime, timedelta import json app Flask(__name__) DB_PATH /home/pi/temperature_data.db app.route(/) def index(): 主页面展示图表 return render_template(index.html) app.route(/api/current) def get_current_temp(): API接口获取所有传感器的最新温度 conn sqlite3.connect(DB_PATH) c conn.cursor() # 获取每个传感器最近的一条记录 c.execute(SELECT sensor_id, temperature, timestamp FROM temperature_readings WHERE timestamp (SELECT MAX(timestamp) FROM temperature_readings AS tr2 WHERE tr2.sensor_id temperature_readings.sensor_id) ORDER BY sensor_id) data [{sensor_id: row[0], temperature: row[1], timestamp: row[2]} for row in c.fetchall()] conn.close() return jsonify(data) app.route(/api/history/sensor_id) def get_history(sensor_id): API接口获取指定传感器过去24小时的数据用于绘图 conn sqlite3.connect(DB_PATH) c conn.cursor() time_threshold (datetime.now() - timedelta(hours24)).strftime(%Y-%m-%d %H:%M:%S) c.execute(SELECT timestamp, temperature FROM temperature_readings WHERE sensor_id ? AND timestamp ? ORDER BY timestamp, (sensor_id, time_threshold)) data [{time: row[0], temperature: row[1]} for row in c.fetchall()] conn.close() return jsonify(data) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 生产环境务必设置debugFalse这个Flask应用提供了两个核心API/api/current返回当前最新温度/api/history/sensor_id返回某个传感器过去24小时的历史数据。前端页面通过调用这些API来获取JSON格式的数据。4. 前端可视化与系统集成实战4.1 使用Chart.js绘制动态温度曲线有了后端API前端页面就负责把数据漂亮地展示出来。我在项目根目录下创建了一个templates文件夹在里面新建index.html。为了绘制曲线图我选择了Chart.js这个轻量而强大的JavaScript图表库。index.html的核心部分如下!DOCTYPE html html head title树莓派温度监控仪表盘/title script srchttps://cdn.jsdelivr.net/npm/chart.js/script style body { font-family: sans-serif; margin: 20px; } .dashboard { display: flex; flex-wrap: wrap; } .sensor-card { border: 1px solid #ccc; border-radius: 5px; padding: 15px; margin: 10px; min-width: 300px; } .current-temp { font-size: 2em; font-weight: bold; } .chart-container { width: 100%; height: 300px; } /style /head body h1️ 树莓派温度监控系统/h1 div idcurrentDashboard classdashboard !-- 这里会动态插入每个传感器的当前温度卡片 -- /div div idhistoryCharts classdashboard !-- 这里会动态插入每个传感器的历史曲线图 -- /div script // 1. 获取并显示当前温度 fetch(/api/current) .then(response response.json()) .then(data { const dashboard document.getElementById(currentDashboard); dashboard.innerHTML ; // 清空 data.forEach(sensor { const card document.createElement(div); card.className sensor-card; card.innerHTML h3传感器: ${sensor.sensor_id}/h3 p最新温度: span classcurrent-temp${sensor.temperature.toFixed(2)}°C/span/p p更新时间: ${sensor.timestamp}/p ; dashboard.appendChild(card); }); }); // 2. 获取并绘制历史曲线 // 假设我们已知传感器ID或者可以从第一个API响应中获取 const sensorIds [28-xxxxxxxxxxxx, 28-xxxxxxxxxxxy]; // 这里需要替换成你的实际传感器ID sensorIds.forEach(sensorId { fetch(/api/history/${sensorId}) .then(response response.json()) .then(data { const container document.getElementById(historyCharts); const chartDiv document.createElement(div); chartDiv.className sensor-card; chartDiv.innerHTML h3${sensorId} - 24小时趋势/h3canvas idchart-${sensorId}/canvas; container.appendChild(chartDiv); const ctx document.getElementById(chart-${sensorId}).getContext(2d); new Chart(ctx, { type: line, data: { labels: data.map(d d.time.substring(11, 16)), // 只取时分 datasets: [{ label: 温度 (°C), data: data.map(d d.temperature), borderColor: rgb(75, 192, 192), tension: 0.1 }] }, options: { responsive: true, maintainAspectRatio: false } }); }); }); /script /body /html这个页面会先向/api/current请求数据动态生成显示当前温度的卡片。然后针对每个已知的传感器ID这里需要你手动替换成实际的ID请求其24小时历史数据并用Chart.js绘制出漂亮的折线图。图表是交互式的可以缩放、平移方便查看细节。4.2 系统服务化与开机自启动我们不希望每次重启树莓派后都要手动去启动Flask应用和Cron任务Cron本身是系统服务无需额外处理。我们需要将Flask应用设置为系统服务实现开机自启和后台稳定运行。创建一个系统服务文件sudo nano /etc/systemd/system/temperature-tracker.service[Unit] DescriptionTemperature Tracker Web Interface Afternetwork.target [Service] Userpi WorkingDirectory/home/pi/temperature_tracker ExecStart/usr/bin/python3 /home/pi/temperature_tracker/app.py Restartalways RestartSec10 StandardOutputsyslog StandardErrorsyslog SyslogIdentifiertemp_tracker [Install] WantedBymulti-user.target这个配置文件告诉系统以用户pi的身份在指定目录下运行我们的app.py。如果程序意外崩溃系统会在10秒后自动重启它Restartalways。保存后执行以下命令启用并启动服务sudo systemctl daemon-reload # 重新加载服务配置 sudo systemctl enable temperature-tracker.service # 启用开机自启 sudo systemctl start temperature-tracker.service # 立即启动服务 sudo systemctl status temperature-tracker.service # 检查服务状态看到状态显示为“active (running)”就大功告成了。现在你的温度追踪Web界面已经作为一个守护进程在后台运行即使树莓派重启也会自动拉起。4.3 内网穿透与远程访问配置到目前为止我们的仪表盘只能在家庭局域网内通过http://树莓派IP:5000访问。如果你想在外面比如在公司也能查看家里的温度就需要内网穿透。这里我使用Cloudflare Tunnel以前叫 Argo Tunnel作为例子因为它免费、安全且配置相对简单。首先你需要在Cloudflare上注册账号并添加你的域名可以是一个免费的子域名。在树莓派上安装Cloudflare的客户端sudo apt install cloudflared运行认证命令cloudflared tunnel login它会打开一个浏览器页面让你授权。创建隧道cloudflared tunnel create temperature-tracker记下返回的隧道ID一个UUID。创建配置文件在~/.cloudflared/config.yml中写入tunnel: 你的隧道ID credentials-file: /home/pi/.cloudflared/隧道ID.json ingress: - hostname: temp.yourdomain.com # 你的子域名 service: http://localhost:5000 - service: http_status:404在Cloudflare DNS面板为你指定的子域名如temp创建一条CNAME记录指向隧道ID.cfargotunnel.com。运行隧道cloudflared tunnel run temperature-tracker。为了让它也在后台运行同样可以将其配置为系统服务。配置成功后你就可以通过https://temp.yourdomain.com这个地址在任何有网络的地方访问你的温度监控仪表盘了。5. 项目优化、问题排查与扩展思路5.1 性能优化与数据管理随着系统长时间运行数据库会越来越大。虽然SQLite很轻量但无限制增长也会影响查询效率。我们需要一个数据清理策略。一个常见的做法是只保留一定时间内的详细数据比如过去30天。对于更早的数据可以按小时或天进行聚合计算平均值、最大值、最小值然后删除原始数据只保留聚合结果。这可以通过一个额外的Cron任务来实现# 编辑crontab: crontab -e # 每天凌晨3点执行数据清理和聚合脚本 0 3 * * * /usr/bin/python3 /home/pi/temperature_tracker/cleanup_db.pycleanup_db.py脚本的内容可以包括删除超过30天的原始数据。将30天前、昨天之前的数据按传感器ID和小时进行聚合存入另一张temperature_summary表。在Flask应用中可以为“历史数据”API增加一个参数比如?periodweek时查询汇总表periodday时查询详细表。5.2 常见问题与排查技巧在实际部署中你可能会遇到以下问题传感器读数为85°C或-127°C等异常值原因这是DS18B20的典型错误值通常表示通信失败或传感器接触不良。排查首先检查物理连接确保杜邦线插紧没有虚接。然后检查上拉电阻是否连接正确4.7kΩ电阻是否接在数据线和3.3V之间。最后在终端输入ls /sys/bus/w1/devices/看是否能列出你的传感器28-开头的文件夹。如果看不到说明1-Wire驱动未正确加载或传感器未识别尝试重启树莓派或重新插拔传感器。Flask网页无法访问原因防火墙阻止了5000端口或者Flask服务没有运行。排查首先sudo systemctl status temperature-tracker.service查看服务状态。如果是inactive检查app.py是否有语法错误或者日志文件sudo journalctl -u temperature-tracker.service中的错误信息。如果服务是active则检查防火墙sudo ufw status。如果防火墙开启需要允许5000端口sudo ufw allow 5000。网页图表不显示或数据为空原因前端JavaScript无法从后端API获取到数据可能是API路径错误、CORS问题如果前后端分离部署或数据库为空。排查打开浏览器的开发者工具F12切换到“网络”(Network)标签页刷新页面查看对/api/current等接口的请求是否成功状态码应为200。如果失败查看控制台(Console)是否有错误信息。确保Flask应用运行在正确的host (0.0.0.0) 和端口上。系统运行一段时间后变卡原因可能是内存或CPU占用过高。树莓派资源有限如果同时运行太多服务或者Python脚本有内存泄漏会导致问题。排查使用htop命令查看系统资源占用。检查Cron任务是否过于频繁不要低于1分钟一次Flask的debug模式是否在生产环境中被意外开启会非常耗资源。5.3 功能扩展与进阶玩法基础系统搭建完成后你可以根据需求进行丰富扩展多传感器与位置映射连接多个DS18B20在数据库中不仅记录传感器ID还记录一个“位置”字段如“客厅”、“鱼缸”、“机房机柜上”。在前端展示时用位置名称代替难记的ID。阈值告警在数据采集脚本中加入逻辑判断。如果某个传感器的温度超过或低于预设的阈值就触发告警。告警方式可以很多样发送电子邮件使用Python的smtplib库。发送App推送使用Pushover、Bark等服务。发送短信使用Twilio等API通常收费。控制继电器如果温度过高自动控制一个连接在树莓派上的继电器打开风扇或关闭加热器实现简单的闭环控制。数据导出与分析在Flask应用中增加一个路由允许用户以CSV格式导出选定时间范围的数据方便用Excel或专业软件进行更深入的分析。集成到智能家居平台如果你使用Home Assistant可以将树莓派上的温度数据通过MQTT协议发布出去。在采集脚本中使用paho-mqtt库将读取到的温度值发布到像home/sensor/temperature/room1这样的主题Home Assistant就能自动发现并集成这个传感器实现更复杂的自动化联动。这个项目就像一颗种子从最简单的读取一个温度值开始可以生长出各种有趣的应用。它的价值不在于用了多高深的技术而在于提供了一个完整的、可落地的物联网原型让你亲手触摸到从物理信号到数字数据再到可视化图表和网络服务的全过程。
返回列表