基于树莓派与MQ-7传感器构建物联网一氧化碳监测报警系统

发布时间:2026/6/4 14:18:55

基于树莓派与MQ-7传感器构建物联网一氧化碳监测报警系统 1. 项目概述从零构建一个会“报警”的智能一氧化碳监测器一氧化碳这个无色无味的“隐形杀手”在密闭的车库、使用不当的燃气热水器旁或者通风不畅的室内都可能悄然积聚威胁生命安全。传统的独立报警器功能单一数据无法远程查看报警后若无人现场响应风险依然存在。作为一名长期折腾智能家居和嵌入式开发的爱好者我一直想做一个能联网、能远程通知、还能查看历史数据的智能监测终端。正好手头有闲置的树莓派和几个传感器这个想法便落地成了今天要分享的“基于树莓派的物联网一氧化碳监测系统”。这个项目的核心目标很简单实时监测环境中的一氧化碳浓度并在浓度超标时通过多种方式网页、短信、邮件立即通知用户。它不仅仅是一个报警器更是一个数据节点。你可以把它放在老家的厨房、自家的车库或者改装进房车里通过手机就能随时查看家里的空气质量心里会踏实很多。整个系统以树莓派作为“大脑”负责数据采集、处理和网络通信MQ-7传感器是“鼻子”专门捕捉一氧化碳再通过Python编写的服务将数据呈现在网页上并通过Twilio和Gmail API发送警报。无论你是物联网的初学者还是有一定Python和硬件基础的开发者这个项目都能带你走通从传感器信号采集到云端服务集成的完整链路理解物联网系统最核心的“感知-处理-传输-应用”逻辑。2. 核心硬件选型与电路设计思路硬件是项目的基石选型直接决定了系统的稳定性、精度和成本。我的选型原则是在满足核心功能的前提下优先选择社区支持好、文档丰富、性价比高的组件这样在开发中遇到问题更容易找到解决方案。2.1 主控与传感器为什么是树莓派和MQ-7树莓派3 Model B是这个项目的主控制器。选择它而非更便宜的微控制器如Arduino主要基于三点考虑完整的操作系统树莓派运行Raspbian现为Raspberry Pi OS本质上是一台微型Linux电脑。这意味着我们可以直接用Python这种高级语言开发复杂逻辑轻松运行Flask Web服务器并利用系统级的包管理工具安装各种依赖开发效率极高。强大的网络能力板载Wi-Fi和蓝牙无需额外模块即可接入互联网是实现“物联网”中“联网”功能的关键。我们可以通过SSH远程登录调试程序也可以直接调用网络API发送警报。丰富的外设接口40Pin的GPIO排针提供了通用输入输出、SPI、I2C、UART等接口能灵活连接各种传感器和模数转换器。对于一氧化碳传感器我选择了经典的MQ-7。这是一个半导体气敏传感器它的核心是一个二氧化锡SnO2的敏感元件。在清洁空气中二氧化锡的导电率很低。当存在一氧化碳时CO气体会在传感器表面发生化学反应导致传感器的导电率增加。通过测量这个导电率的变化就能间接推算出气体的浓度。选择MQ-7的原因在于其成本低廉、对一氧化碳灵敏度高且市面上资料和配套电路非常成熟。但必须注意这类半导体传感器容易受温度、湿度及其他可燃性气体的交叉干扰因此它更适合用于定性或半定量的报警场景而非需要极高精度的工业检测。2.2 信号调理的关键模数转换器ADCMCP3008树莓派的GPIO口只能读取数字信号高电平或低电平而MQ-7输出的是一个模拟电压信号浓度越高电压通常越高。因此我们需要一个模数转换器ADC来将这个连续的模拟电压转换为树莓派可以理解的数字值。我选用的是MCP3008一款8通道、10位精度的ADC芯片采用SPI接口通信。10位精度意味着它可以将0到参考电压例如3.3V的范围划分为2^101024个等级。如果参考电压为3.3V那么每个数字等级LSB代表的电压大约是3.3V / 1024 ≈ 0.0032V3.2mV。这个精度对于我们的报警应用来说完全足够。注意树莓派GPIO的工作电压是3.3V严禁接入5V电压否则会损坏主板。因此整个电路的逻辑电压需统一为3.3V。MQ-7模块通常有5V供电版本务必确认其信号输出电平是否兼容3.3V或者选择支持3.3V的模块。2.3 电路连接详解与安全规范电路搭建是硬件部分最需要细心的一环。下图是完整的连接示意图但我会逐一解释每个连接的作用和背后的原理树莓派 (3.3V GPIO) MCP3008 (ADC) MQ-7 传感器 分压电路 ───────────────────────────────────────────────────────────────────────────── 3.3V Pin 1 ───────────── VDD (Pin 16) ─── 为ADC和传感器供电 3.3V Pin 1 ────────────────────────────────────────┐ │ GND Pin 6 ───────────── VSS (Pin 15) │ GND Pin 6 ────────────────────────────────────────┼── 共地确保电压参考基准一致 │ GPIO 11 (SPI0 SCLK) ──── CLK (Pin 13) │ GPIO 10 (SPI0 MOSI) ──── D_in (Pin 11) │ GPIO 9 (SPI0 MISO) ──── D_out(Pin 12) │ GPIO 8 (SPI0 CE0) ───── CS (Pin 10) │ │ │ MQ-7加热丝回路 │ 5V Pin 2 ───┐ │ │ │ [ ] 加热丝 │ │ │ GND Pin 6 ──┘ │ │ MQ-7信号输出回路 │ AOUT Pin ────┐ │ │ │ [1KΩ] ─── 至 ADC CH0 │ │ │ [470Ω] ─── GND │ │ │ GND Pin ────┘连接步骤与原理分析电源与地线首先将树莓派的3.3V引脚Pin 1连接到MCP3008的VDDPin 16和MQ-7的VCC如果支持3.3V。同时将树莓派的GNDPin 6连接到MCP3008的VSSPin 15和MQ-7的GND。共地至关重要它确保了所有器件都以同一个“零电位”作为参考电压测量才会准确。SPI总线连接MCP3008通过SPI协议与树莓派通信。SPI需要四根线SCLK (Serial Clock)时钟线由树莓派主机提供同步数据位传输。MOSI (Master Out Slave In)主输出、从机输入线树莓派通过它向MCP3008发送控制命令例如选择哪个通道进行转换。MISO (Master In Slave Out)主机输入、从机输出线树莓派通过它读取MCP3008转换完成的数字值。CS (Chip Select)片选线低电平有效。当树莓派将CE0引脚拉低时MCP3008知道自己被“选中”开始响应通信。这允许总线上挂载多个SPI设备。MQ-7加热器供电MQ-7内部有一个需要5V供电的加热丝用于将敏感材料加热到最佳工作温度。务必使用树莓派的5V引脚Pin 2或4为其单独供电切勿使用3.3V否则加热不足会导致传感器无法正常工作或灵敏度极低。信号分压与采集MQ-7的AOUT引脚输出一个模拟电压。我们通过一个简单的分压电路将其连接到MCP3008的通道0CH0。分压电路由1KΩ和470Ω电阻串联组成。MQ-7的输出端接在1KΩ电阻上470Ω电阻接地两个电阻之间的连接点接到MCP3008的CH0。这个电路有两个作用一是作为负载将传感器输出的电流信号转化为电压信号二是起到一定的限流和保护作用。根据欧姆定律CH0测量到的电压 V_ch0 V_aout * (470 / (1000 470))。我们需要在后续代码中根据这个关系反推出传感器端的原始电压。实操心得在面包板上搭建电路时建议先断开电源按照“电源-芯片-传感器”的顺序连接并再三检查特别是电源和地线不要接反。连接SPI线时可以借助颜色区分例如黑线GND红线3.3V/5V黄线时钟绿线数据能有效降低接错线的概率。通电前用手触摸一下主要芯片如果发现有异常发热立即断电检查。3. 软件环境配置与核心代码解析硬件搭好了接下来就是让系统“活”起来。软件部分分为三层驱动层读取传感器数据、服务层处理数据、提供API、应用层Web界面、报警逻辑。我们使用Python作为统一语言极大简化了开发流程。3.1 系统准备与SPI驱动使能首先确保你的树莓派系统是最新的并通过sudo raspi-config工具启用SPI接口。sudo raspi-config # 选择 Interface Options - SPI - Yes 启用。重启后可以通过以下命令检查SPI设备是否出现ls /dev/spi* # 应该能看到类似 /dev/spidev0.0 的设备文件3.2 传感器数据读取与MCP3008通信我们使用Python的spidev库来操作SPI设备。首先安装它sudo pip3 install spidev。下面是一个读取MCP3008指定通道电压值的核心函数import spidev import time class MQ7Sensor: def __init__(self, channel0, vref3.3): self.spi spidev.SpiDev() self.spi.open(0, 0) # 打开SPI总线0设备0 (对应CE0) self.spi.max_speed_hz 1350000 # 设置SPI时钟频率1.35MHz是稳妥值 self.channel channel self.vref vref # ADC参考电压接的是树莓派的3.3V def read_adc(self): 读取MCP3008指定通道的原始值0-1023 # MCP3008的通信协议要求先发送一个起始位(1)然后是单端/差分模式选择位(1表示单端) # 接着是3位通道选择位最后补足4个时钟脉冲用于读取数据。 cmd 0b11 6 # 起始位单端模式 0b11000000 cmd | (self.channel 0x07) 3 # 通道选择位放到第3-5位 # 发送3个字节第一个是命令后两个是占位符用于接收数据 resp self.spi.xfer2([cmd, 0x00, 0x00]) # 返回的数据中后两个字节包含了10位结果 # resp[1]的低2位和resp[2]的8位组成10位数据 value ((resp[1] 0x03) 8) | resp[2] return value def read_voltage(self): 将ADC原始值转换为实际电压值单位伏特 raw self.read_adc() voltage (raw * self.vref) / 1023.0 return voltage def calculate_ppm(self, voltage): 将电压值估算为一氧化碳浓度PPM。 注意这是一个简化的估算实际浓度需要根据传感器数据手册进行校准。 MQ-7的灵敏度特性曲线是非线性的通常需要分段拟合或查表。 这里提供一个非常粗略的线性估算示例仅用于演示报警逻辑。 # 假设传感器在清洁空气中的电压为V0在100ppm CO中的电压为V100 # 本例假设V0 0.2V, V100 2.0V (这些值必须通过实际校准获得) v0 0.2 v100 2.0 if voltage v0: return 0.0 # 线性插值 ppm (voltage - v0) * 100 / (v100 - v0) return round(ppm, 2) def cleanup(self): self.spi.close() # 使用示例 if __name__ __main__: sensor MQ7Sensor(channel0) try: while True: v sensor.read_voltage() ppm sensor.calculate_ppm(v) print(f电压: {v:.2f}V, 估算浓度: {ppm} ppm) time.sleep(2) except KeyboardInterrupt: sensor.cleanup() print(程序退出。)这段代码的关键在于spi.xfer2()函数和数据处理。MCP3008的通信需要一次发送和接收同时完成。我们发送一个包含命令的字节列表并接收同样长度的字节列表作为响应再从响应中解析出我们需要的10位数据。重要提示calculate_ppm函数中的v0和v100是占位值绝对不能直接用于真实环境MQ-7的电阻比Rs/R0与气体浓度PPM在对数坐标下近似呈线性关系。真正的校准需要在已知浓度的标准气体中进行或者至少要在清洁空气中长时间通电预热通常需要24-48小时后测量其稳定输出作为R0然后根据数据手册中的灵敏度曲线进行换算。对于报警应用你可以通过实验确定一个安全的电压阈值例如对应50ppm的电压当电压超过该阈值时就触发报警这比直接估算PPM值更可靠。3.3 构建Web服务与数据APIFlask框架的应用为了让数据能在网络上被访问我们使用轻量级的Flask框架创建一个Web服务器。它主要做两件事一是提供一个网页显示实时浓度和历史图表二是提供RESTful API接口供前端或其他系统获取数据。首先安装依赖pip3 install flask flask-cors如果前端与后端不同源需要cors支持。from flask import Flask, jsonify, render_template, request from flask_cors import CORS import threading import time import json import os from datetime import datetime # 假设上面的MQ7Sensor类在同一个文件或已导入 app Flask(__name__) CORS(app) # 允许跨域请求 # 全局变量存储最新数据 current_data { voltage: 0.0, ppm: 0.0, timestamp: , alert: False } data_history [] # 用于存储历史数据简单起见用列表生产环境应用数据库 HISTORY_LIMIT 500 # 保存最近500条数据 ALERT_THRESHOLD_PPM 50 # 报警阈值单位ppm sensor MQ7Sensor(channel0) def sensor_reading_loop(): 后台线程持续读取传感器数据 global current_data while True: try: v sensor.read_voltage() ppm sensor.calculate_ppm(v) ts datetime.now().strftime(%Y-%m-%d %H:%M:%S) is_alert ppm ALERT_THRESHOLD_PPM current_data.update({ voltage: v, ppm: ppm, timestamp: ts, alert: is_alert }) # 保存历史数据 data_history.append({ ppm: ppm, time: ts }) if len(data_history) HISTORY_LIMIT: data_history.pop(0) # 移除最旧的数据 # 如果触发报警调用报警函数后续集成 if is_alert: trigger_alert(ppm, ts) time.sleep(5) # 每5秒读取一次 except Exception as e: print(f读取传感器数据时出错: {e}) time.sleep(10) app.route(/) def index(): 主页面显示实时监控数据 return render_template(index.html) # 需要创建一个templates/index.html文件 app.route(/api/current, methods[GET]) def get_current_data(): API接口获取当前最新数据 return jsonify(current_data) app.route(/api/history, methods[GET]) def get_history_data(): API接口获取历史数据 # 可以接受参数例如 ?hours6 返回最近6小时的数据 hours request.args.get(hours, default24, typeint) # 这里简化处理返回所有历史数据。实际应根据时间过滤。 return jsonify(data_history[-hours*12:]) # 假设5秒一条12条/分钟 app.route(/api/alert_threshold, methods[GET, POST]) def handle_threshold(): API接口获取或设置报警阈值 global ALERT_THRESHOLD_PPM if request.method POST: new_threshold request.json.get(threshold) if new_threshold is not None and 0 new_threshold 1000: # 简单验证 ALERT_THRESHOLD_PPM new_threshold return jsonify({message: f报警阈值已更新为 {new_threshold} ppm}), 200 else: return jsonify({error: 无效的阈值}), 400 else: return jsonify({alert_threshold: ALERT_THRESHOLD_PPM}) def trigger_alert(ppm, timestamp): 触发报警的统一入口函数 print(f[警报] {timestamp} - 一氧化碳浓度超标: {ppm} ppm) # 这里后续会集成发送短信和邮件的逻辑 # send_sms_alert(ppm, timestamp) # send_email_alert(ppm, timestamp) if __name__ __main__: # 启动传感器数据读取线程 sensor_thread threading.Thread(targetsensor_reading_loop, daemonTrue) sensor_thread.start() print(传感器数据读取线程已启动。) # 启动Flask Web服务监听所有网络接口方便同一局域网内访问 app.run(host0.0.0.0, port5000, debugFalse) # 生产环境务必设置debugFalse这个Flask应用创建了几个核心端点/用于提供监控页面/api/current返回实时数据/api/history返回历史数据/api/alert_threshold允许动态调整报警阈值。数据读取在一个独立的后台线程中运行不会阻塞Web服务器的响应。4. 双通道报警系统集成Twilio短信与Gmail API本地报警网页显示还不够我们需要在浓度超标时能立即通知到不在现场的家人。这里我集成了两种最通用的通知方式短信和邮件。4.1 使用Twilio发送报警短信Twilio是一家云通信平台提供了简单的API来发送短信和拨打电话。它有为开发者提供的免费试用额度足够我们进行测试和轻量使用。配置步骤注册账号访问Twilio官网注册验证手机号后会获得一个试用账户包含少量余额和一个虚拟的试用电话号码。获取凭证在控制台首页找到你的Account SID和Auth Token这是调用API的钥匙。安装SDK在树莓派上安装Twilio的Python库pip3 install twilio。购买号码可选试用账户会分配一个号码但只能向已验证的手机号发短信。如果需要向任意号码发需购买一个Twilio电话号码使用试用金即可。集成代码from twilio.rest import Client import os class SMSAlert: def __init__(self): # 强烈建议将敏感信息存储在环境变量中而非硬编码在代码里 self.account_sid os.environ.get(TWILIO_ACCOUNT_SID, 你的ACCOUNT_SID) self.auth_token os.environ.get(TWILIO_AUTH_TOKEN, 你的AUTH_TOKEN) self.from_number os.environ.get(TWILIO_PHONE_NUMBER, 你的Twilio电话号码) # 格式如 1234567890 self.to_number os.environ.get(ALERT_PHONE_NUMBER, 接收警报的手机号) # 格式如 8613901234567 self.client Client(self.account_sid, self.auth_token) def send(self, ppm, timestamp, location家中): 发送报警短信 if not all([self.account_sid, self.auth_token, self.from_number, self.to_number]): print(Twilio配置信息不完整跳过短信发送。) return False try: message_body f[一氧化碳警报] 位于 {location} 的监测器检测到浓度超标当前浓度: {ppm} ppm 时间: {timestamp}。请立即通风并检查气源 message self.client.messages.create( bodymessage_body, from_self.from_number, toself.to_number ) print(f短信警报已发送消息SID: {message.sid}) return True except Exception as e: print(f发送短信失败: {e}) return False # 在trigger_alert函数中调用 sms_sender SMSAlert() def trigger_alert(ppm, timestamp): print(f[警报] {timestamp} - 一氧化碳浓度超标: {ppm} ppm) # 发送短信 sms_sender.send(ppm, timestamp) # 后续会添加发送邮件的调用注意事项Twilio试用账户发送的短信会带有“Sent from your Twilio trial account”的标识。务必保管好你的Auth Token如同保管密码一样。建议通过export TWILIO_ACCOUNT_SIDyour_sid的方式在系统环境变量中设置代码中通过os.environ.get读取避免将密钥提交到代码仓库。4.2 使用Gmail API发送报警邮件对于更详细的报警信息或者需要通知多个邮箱邮件是更好的选择。我们通过Google Cloud的Gmail API来实现。配置步骤关键且稍复杂创建Google Cloud项目访问Google Cloud Console创建一个新项目例如“CO-Monitor”。启用Gmail API在项目内的“API和服务”-“库”中搜索并启用“Gmail API”。创建服务账号凭证在“API和服务”-“凭据”页面点击“创建凭据”选择“服务账号”。填写服务账号名称和ID角色可以暂时选择“Project - Editor”为安全起见生产环境应遵循最小权限原则。创建完成后进入该服务账号详情页在“密钥”选项卡中点击“添加密钥”-“创建新密钥”选择JSON格式。密钥文件会自动下载到你的电脑上请妥善保管。启用域范围委派关键步骤为了让服务账号能以某个真实用户你的邮箱的身份发送邮件需要进行域范围委派。记下服务账号详情页中的“唯一ID”通常是一串邮箱格式的字符。如果你使用Google Workspace原G Suite管理员可以按照Google官方文档进行委派。对于个人Gmail这个过程比较复杂更推荐使用OAuth 2.0 for Desktop Apps流程。但为了简化我们可以使用一个“变通”方法使用yagmail库仅适用于个人邮箱且需开启“安全性较低的应用的访问权限”不推荐或使用SMTP协议。这里我们采用更通用的SMTP方式。使用SMTP协议发送邮件替代Gmail API由于Gmail API配置对新手有一定门槛且服账号发送邮件需要复杂的域委派我们可以退而求其次使用标准的SMTP协议。这需要你开启Gmail的“两步验证”并创建一个“应用专用密码”。import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import os class EmailAlert: def __init__(self): self.smtp_server smtp.gmail.com self.smtp_port 587 # TLS端口 self.sender_email os.environ.get(GMAIL_SENDER, 你的Gmail地址gmail.com) # 注意这里不是你的Gmail登录密码而是“应用专用密码” self.app_password os.environ.get(GMAIL_APP_PASSWORD, 你的16位应用专用密码) self.receiver_emails os.environ.get(ALERT_EMAILS, 接收邮箱1,接收邮箱2).split(,) def send(self, ppm, timestamp, location家中): 通过SMTP发送报警邮件 if not all([self.sender_email, self.app_password, self.receiver_emails]): print(邮件配置信息不完整跳过邮件发送。) return False subject f【紧急】一氧化碳浓度超标警报 - {location} body f 一氧化碳监测系统检测到危险浓度 详细信息 - 监测位置{location} - 检测浓度{ppm} ppm - 超标时间{timestamp} - 报警阈值{ALERT_THRESHOLD_PPM} ppm 请立即采取以下措施 1. 立即打开所有门窗保持通风。 2. 迅速撤离到户外空气新鲜处。 3. 检查可能的泄漏源燃气灶、热水器、汽车尾气等。 4. 如有人感到头晕、恶心、乏力请立即就医。 此邮件由自动监测系统发送请勿直接回复。 msg MIMEMultipart() msg[From] self.sender_email msg[To] , .join(self.receiver_emails) msg[Subject] subject msg.attach(MIMEText(body, plain, utf-8)) try: server smtplib.SMTP(self.smtp_server, self.smtp_port) server.starttls() # 启用TLS加密 server.login(self.sender_email, self.app_password) server.sendmail(self.sender_email, self.receiver_emails, msg.as_string()) server.quit() print(邮件警报已发送。) return True except Exception as e: print(f发送邮件失败: {e}) return False # 应用专用密码生成步骤 # 1. 登录你的Google账号进入“安全性”设置。 # 2. 在“登录Google”部分确保已开启“两步验证”。 # 3. 两步验证开启后同一页面会出现“应用专用密码”选项。 # 4. 生成一个用于“邮件”或“其他自定义名称”的密码得到一个16位的密码将其填入环境变量。现在更新trigger_alert函数集成两种报警方式sms_sender SMSAlert() email_sender EmailAlert() alert_cooldown 300 # 报警冷却时间5分钟防止短时间内重复报警 last_alert_time 0 def trigger_alert(ppm, timestamp): global last_alert_time current_time time.time() if current_time - last_alert_time alert_cooldown: print(f距离上次报警不足 {alert_cooldown} 秒本次跳过。) return print(f[警报] {timestamp} - 一氧化碳浓度超标: {ppm} ppm) # 发送短信 sms_sender.send(ppm, timestamp) # 发送邮件 email_sender.send(ppm, timestamp) last_alert_time current_time增加了冷却时间机制避免传感器波动导致一分钟内发送数十条警报的“轰炸”情况。5. 系统部署、优化与故障排查实录将代码部署到树莓派上并让它稳定运行是项目的最后一步也是最考验耐心的一步。5.1 完整部署流程与开机自启文件传输在本地开发完成后使用scp命令将整个项目文件夹上传到树莓派。scp -r ./co_monitor_project pi你的树莓派IP:~/co_monitor安装依赖SSH登录树莓派进入项目目录安装所有Python依赖。cd ~/co_monitor pip3 install -r requirements.txt # 如果没创建requirements.txt可以手动安装pip3 install flask spidev twilio设置环境变量创建一个配置文件如.env或直接在~/.bashrc中设置敏感信息。# 编辑 ~/.bashrc nano ~/.bashrc # 在文件末尾添加 export TWILIO_ACCOUNT_SID你的sid export TWILIO_AUTH_TOKEN你的token export TWILIO_PHONE_NUMBER1234567890 export ALERT_PHONE_NUMBER8613901234567 export GMAIL_SENDER你的邮箱gmail.com export GMAIL_APP_PASSWORD你的应用密码 export ALERT_EMAILS邮箱1gmail.com,邮箱2qq.com # 保存退出后使配置生效 source ~/.bashrc配置开机自启使用systemd这是保证系统重启后服务能自动运行的最佳方式。创建服务文件sudo nano /etc/systemd/system/co-monitor.service写入以下内容[Unit] DescriptionCO Monitor Service Afternetwork.target [Service] Typesimple Userpi WorkingDirectory/home/pi/co_monitor EnvironmentPATH/usr/bin # 加载环境变量文件如果配置在.bashrc里可能需要用source加载 ExecStart/usr/bin/python3 /home/pi/co_monitor/app.py Restarton-failure RestartSec10 [Install] WantedBymulti-user.target启用并启动服务sudo systemctl daemon-reload sudo systemctl enable co-monitor.service sudo systemctl start co-monitor.service # 检查状态和日志 sudo systemctl status co-monitor.service sudo journalctl -u co-monitor.service -f5.2 性能优化与数据持久化当前的实现将历史数据存储在内存列表中树莓派断电后数据会丢失。对于长期监测需要将数据持久化。方案一使用SQLite数据库轻量级推荐import sqlite3 from contextlib import closing import os DB_PATH /home/pi/co_monitor/sensor_data.db def init_db(): 初始化数据库创建表 with closing(sqlite3.connect(DB_PATH)) as conn: cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS sensor_readings ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, voltage REAL NOT NULL, ppm REAL NOT NULL, alert INTEGER DEFAULT 0 ) ) # 创建索引以提高按时间查询的速度 cursor.execute(CREATE INDEX IF NOT EXISTS idx_timestamp ON sensor_readings(timestamp)) conn.commit() def save_reading(voltage, ppm, is_alert): 保存一条读数到数据库 with closing(sqlite3.connect(DB_PATH)) as conn: cursor conn.cursor() cursor.execute(INSERT INTO sensor_readings (voltage, ppm, alert) VALUES (?, ?, ?), (voltage, ppm, 1 if is_alert else 0)) conn.commit() # 在sensor_reading_loop中替换内存列表操作 # data_history.append(...) 改为 save_reading(v, ppm, is_alert)方案二定时写入文件最简单如果不想引入数据库可以定期将数据追加到CSV文件中。import csv from datetime import datetime LOG_FILE /home/pi/co_monitor/sensor_log.csv def log_to_csv(timestamp, voltage, ppm, alert): 将数据记录到CSV文件 file_exists os.path.isfile(LOG_FILE) with open(LOG_FILE, a, newline) as f: writer csv.writer(f) if not file_exists: writer.writerow([timestamp, voltage, ppm, alert]) # 写入表头 writer.writerow([timestamp, voltage, ppm, alert])5.3 常见问题与排查技巧在开发和运行过程中你几乎一定会遇到下面这些问题。这里是我踩过坑后总结的排查清单问题现象可能原因排查步骤与解决方案网页无法访问 (Connection refused)1. Flask服务未启动。2. 防火墙阻止了5000端口。3. 树莓派IP地址变化。1.sudo systemctl status co-monitor检查服务状态查看日志journalctl -u co-monitor -f。2. 树莓派默认防火墙一般关闭。检查命令sudo ufw status。3. 使用hostname -I查看当前IP或在路由器后台查看树莓派的DHCP分配地址。传感器读数始终为0或固定值1. 电路连接错误或松动。2. SPI未启用。3. MCP3008通道选择错误。4. MQ-7加热丝未供电5V。1.断电后用万用表通断档检查所有连接线特别是GND和3.3V。2. 确认/dev/spidev0.0存在并检查Python代码中spi.open(0,0)是否正确。3. 检查代码中channel参数是否与物理连接CH0一致。4.最关键用万用表测量MQ-7模块的VCC和GND引脚之间是否有5V电压。没有则检查5V供电线路。ADC读数波动剧烈1. 电源噪声。2. 信号线过长或未使用屏蔽线引入干扰。3. 传感器预热不足。1. 尝试给树莓派使用质量好的电源适配器避免和电机等大功率设备共用插座。2. 尽量缩短传感器到ADC的连接线或使用双绞线。3. MQ-7需要长时间预热24-48小时才能稳定。刚通电时读数漂移是正常的。可以在软件中做滑动平均滤波smoothed_value 0.8 * last_value 0.2 * current_read。短信/邮件发送失败1. Twilio/邮箱账号凭证错误。2. 网络连接问题。3. Twilio试用账户限制如未验证接收号。4. Gmail应用专用密码错误或未开启“安全性较低的应用访问”。1. 仔细核对环境变量中的SID、Token、电话号码格式必须包含国家代码如86。2.ping google.com检查树莓派网络是否通畅。3. 登录Twilio控制台查看Debugger日志会有详细的错误信息。4. 确认Gmail两步验证已开且生成的是16位“应用专用密码”不是登录密码。可尝试先用Python脚本单独测试邮件发送功能。树莓派运行一段时间后卡死1. 电源功率不足推荐5V/2.5A以上。2. SD卡读写频繁导致损坏或过热。3. 内存泄漏代码问题。1. 使用官方电源或质量可靠的5V/3A电源。2. 将日志、数据库写入操作优化为批量写入减少SD卡写入频率。考虑使用USB SSD或高质量Class 10 SD卡。3. 检查代码确保在异常情况下资源被正确释放如spi.close()。使用htop命令监控内存使用情况。报警阈值不准确1. MQ-7未校准估算PPM公式不准。2. 环境温湿度影响传感器灵敏度。1.放弃精确PPM采用电压阈值报警。在已知安全的环境和疑似有泄漏的环境注意安全切勿在真实危险环境测试下记录传感器的稳定电压输出设定一个安全的电压阈值。2. 如果条件允许购买一个商用的一氧化碳报警器作为参考进行对比校准。或者将设备放在通风良好的室外记录其“清洁空气”下的基准电压值。最后的个人体会做物联网项目硬件连接是基础一定要耐心细致软件逻辑是骨架结构清晰很重要而稳定性和可靠性才是灵魂。这个系统我跑了大概一个月期间因为电源接触不良重启过一次因为SD卡质量差导致日志文件系统损坏一次。我的建议是对于这种需要长期运行的安全相关设备供电和存储一定要用好的。另外报警功能一定要有“防骚扰”机制比如我上面加的冷却时间以及可以考虑“持续超标N次才报警”的逻辑避免因瞬时干扰误报。未来如果想升级可以加上TFT小屏幕本地显示或者把数据上传到更专业的物联网平台如Home Assistant、ThingsBoard做更漂亮的可视化和智能联动比如浓度超标自动打开智能风扇。

相关新闻