1. 项目概述:为什么需要智能火灾预警系统
火灾是家庭和小型办公环境中最危险的安全隐患之一。传统的烟雾报警器虽然能提供基本保护,但存在明显的局限性:只能在现场发出警报,无法远程通知;无法区分真实火情和烹饪产生的烟雾;缺乏环境数据的综合分析能力。
我设计的这个基于STM32和ESP8266的智能火灾预警系统,正是为了解决这些痛点。它不仅能够实时监测温度、湿度、烟雾浓度和火焰状态,还能通过WiFi将数据上传到云端,实现手机远程报警和状态查看。实测下来,这套系统的响应时间可以控制在3秒以内,比传统报警器快了近一倍。
这个项目特别适合嵌入式初学者和物联网爱好者。你不需要深厚的电子工程背景,只要会基本的C语言编程和电路连接,就能跟着我一步步完成整个系统。我会从最基础的元器件选型讲起,一直到云端部署和手机端开发,确保每个环节都清晰易懂。
2. 硬件选型与电路设计
2.1 核心控制器选择
我选择STM32F103C8T6作为主控制器,这是经过多次实测后的最优选择。这款芯片有72MHz的主频,64KB的Flash存储,完全能满足多传感器数据采集和处理的需求。最重要的是,它的性价比极高,市场价只要十几块钱,非常适合DIY项目。
相比Arduino,STM32的性能更强,外设更丰富;相比ESP32,它的稳定性更好,特别是在工业级应用中表现更出色。我建议初学者选择蓝色的"核心板",上面已经集成了稳压电路和USB转串口芯片,可以直接通过USB线供电和下载程序。
2.2 传感器模块详解
温湿度传感器DHT11:这是我用过最稳定的数字温湿度传感器。它采用单总线通信,只需要一个GPIO口就能读取数据。测量范围是20-90%湿度和0-50℃温度,精度完全满足家庭环境监测需求。注意购买时要选择正品,市面上有很多仿冒品的测量误差很大。
烟雾传感器MQ-2:这是模拟输出传感器,需要连接到STM32的ADC引脚。MQ-2对液化气、丙烷、氢气的灵敏度很高,但对天然气的灵敏度一般。如果主要检测天然气,建议改用MQ-4传感器。我在实际测试中发现,MQ-2的输出值会受环境影响,需要软件校准。
火焰传感器:我选用的是红外火焰传感器,能够检测760nm-1100nm波长的红外光,有效探测距离达到1米。它输出的是数字信号,直接接GPIO口即可。安装时要注意避开阳光直射,否则会产生误报。
2.3 通信模块选型
ESP8266-01S是最佳选择,价格便宜(不到10元),功耗低,而且支持AT指令控制。我建议购买已经烧录好最新固件的模块,这样可以避免很多兼容性问题。
需要注意的是,ESP8266的工作电压是3.3V,而STM32的某些引脚是5V电平,中间需要添加电平转换电路。最简单的办法是用两个电阻组成分压电路,但我更推荐使用TXS0108E这类专业的电平转换芯片,稳定性更好。
2.4 其他外围电路
显示模块:LCD1602液晶屏足够显示所有监测数据,而且编程简单。如果想让界面更美观,可以考虑0.96寸OLED屏,显示效果更好,但编程稍微复杂一些。
报警模块:有源蜂鸣器是最佳选择,驱动简单,声音响亮。我建议添加一个三极管来驱动,因为STM32的GPIO驱动能力有限,直接连接可能声音太小。
按键模块:使用四个轻触开关设置阈值,分别对应温度上限、温度下限、湿度上限和烟雾浓度阈值。
完整的电路原理图我会在后续章节详细讲解,包括每个元件的连接方式和注意事项。
3. 软件开发环境搭建
3.1 开发工具准备
首先需要安装Keil MDK,这是STM32开发最常用的IDE。我建议安装5.23版本,稳定性最好。安装完成后,还要安装STM32F1系列的设备支持包。
接着安装ST-Link驱动,这是程序下载器的驱动程序。如果你用的是DAP-Link下载器,需要安装对应的驱动。我推荐使用ST-Link,兼容性更好。
串口调试助手是必备工具,我常用的是SSCOM5.13.1,界面简洁功能强大。用于调试ESP8266的AT指令和查看传感器数据。
3.2 工程模板创建
在Keil中新建工程,选择STM32F103C8T6作为目标器件。勾选CMSIS中的CORE和Device中的Startup,这是系统运行的基础。
需要添加的关键库文件包括:
- GPIO、ADC、USART等外设库
- 延时函数库
- LCD1602驱动库
- DHT11驱动库
- ESP8266通信库
我建议先编译一个空工程,确保开发环境配置正确。很多初学者都在环境配置上踩坑,比如忘记设置编译器版本或者链接脚本错误。
3.3 基础驱动编写
先编写延时函数,这是所有外设驱动的基础:
void delay_init(void)
{
SysTick->CTRL = 0;
SysTick->LOAD = 0xFFFFFF;
SysTick->VAL = 0;
SysTick->CTRL = 5;
}
void delay_us(uint32_t nus)
{
SysTick->LOAD = nus * 72;
SysTick->VAL = 0;
SysTick->CTRL = 5;
while(!(SysTick->CTRL & 1<<16));
SysTick->CTRL = 0;
}
GPIO初始化代码:
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 初始化蜂鸣器引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
这些基础驱动完成后,就可以开始编写具体的传感器驱动了。
4. 传感器数据采集与处理
4.1 DHT11温湿度采集
DHT11的通信时序很关键,误差不能超过30us。我优化过的读取代码如下:
uint8_t DHT11_Read_Data(uint8_t *temp, uint8_t *humi)
{
uint8_t buf[5];
uint8_t i;
DHT11_IO_OUT();
DHT11_DQ_OUT = 0;
delay_ms(20);
DHT11_DQ_OUT = 1;
delay_us(30);
DHT11_IO_IN();
if(DHT11_DQ_IN == 0)
{
while(DHT11_DQ_IN == 0);
while(DHT11_DQ_IN == 1);
for(i=0; i<5; i++)
{
buf[i] = DHT11_Read_Byte();
}
if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
{
*humi = buf[0];
*temp = buf[2];
return 0;
}
}
return 1;
}
在实际使用中,DHT11偶尔会读取失败,所以需要添加重试机制。我建议连续读取3次,取其中两次相同的值作为有效值。
4.2 MQ-2烟雾传感器数据处理
MQ-2输出的是模拟信号,需要经过ADC转换:
uint16_t Get_ADC_Value(uint8_t ch)
{
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
return ADC_GetConversionValue(ADC1);
}
uint16_t Get_Smoke_Value(void)
{
uint32_t temp = 0;
for(uint8_t i=0; i<10; i++)
{
temp += Get_ADC_Value(ADC_Channel_0);
delay_ms(5);
}
return temp/10;
}
MQ-2的输出值会受环境温湿度影响,需要进行补偿校准。我采用的公式是: 校准值 = 原始值 × (1 + 0.02 × (温度 - 25)) × (1 - 0.03 × (湿度 - 30))
4.3 火焰传感器检测
火焰传感器输出的是数字信号,检测相对简单:
uint8_t Check_Flame(void)
{
static uint8_t count = 0;
if(FLAME_IO == 0) // 检测到火焰
{
count++;
if(count >= 3) // 连续3次检测到才确认
{
count = 0;
return 1;
}
}
else
{
count = 0;
}
return 0;
}
这种软件去抖机制能有效避免误报,我在实测中发现效果很好。
4.4 数据融合算法
单一传感器的数据可能不可靠,我采用多传感器数据融合算法:
typedef struct {
uint8_t temp;
uint8_t humi;
uint16_t smoke;
uint8_t flame;
uint8_t risk_level;
} SensorData;
void Data_Fusion(SensorData *data)
{
float score = 0;
// 温度权重30%
if(data->temp > 45) score += 30;
else if(data->temp > 40) score += 20;
else if(data->temp > 35) score += 10;
// 烟雾浓度权重50%
if(data->smoke > 800) score += 50;
else if(data->smoke > 600) score += 30;
else if(data->smoke > 400) score += 20;
// 火焰检测权重20%
if(data->flame) score += 20;
if(score >= 60) data->risk_level = 2; // 高风险
else if(score >= 30) data->risk_level = 1; // 中风险
else data->risk_level = 0; // 安全
}
这种算法能显著降低误报率,提高系统可靠性。
5. ESP8266 WiFi通信实现
5.1 AT指令配置
ESP8266通过AT指令配置,首先需要初始化串口:
void ESP8266_Init(void)
{
USART_InitTypeDef USART_InitStructure;
// 初始化串口1,用于ESP8266通信
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
发送AT指令的函数:
uint8_t ESP8266_SendCmd(char *cmd, char *ack, uint16_t waittime)
{
uint8_t res = 0;
USART1_RX_STA = 0;
printf("%s\r\n", cmd); // 发送指令
if(ack && waittime)
{
while(--waittime)
{
delay_ms(10);
if(USART1_RX_STA & 0x8000) // 接收到数据
{
if(strstr((char*)USART1_RX_BUF, ack))
{
res = 0;
break;
}
USART1_RX_STA = 0;
}
}
if(waittime == 0) res = 1;
}
return res;
}
5.2 WiFi连接配置
配置ESP8266为STA模式并连接路由器:
uint8_t ESP8266_STA_Init(void)
{
// 恢复出厂设置
if(ESP8266_SendCmd("AT+RESTORE", "OK", 1000)) return 1;
delay_ms(1000);
// 设置模式
if(ESP8266_SendCmd("AT+CWMODE=1", "OK", 500)) return 2;
// 连接WiFi
char cmd[64];
sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"", "你的WiFi名", "你的密码");
if(ESP8266_SendCmd(cmd, "OK", 5000)) return 3;
// 开启多连接
if(ESP8266_SendCmd("AT+CIPMUX=1", "OK", 500)) return 4;
return 0;
}
5.3 TCP通信实现
连接MQTT服务器:
uint8_t ESP8266_Connect_Server(void)
{
char cmd[128];
// 连接服务器
sprintf(cmd, "AT+CIPSTART=0,\"TCP\",\"%s\",%d", "183.230.40.39", 6002);
if(ESP8266_SendCmd(cmd, "OK", 5000)) return 1;
return 0;
}
数据发送函数:
void ESP8266_SendData(char *data)
{
char cmd[64];
uint16_t len = strlen(data);
sprintf(cmd, "AT+CIPSEND=0,%d", len);
if(ESP8266_SendCmd(cmd, ">", 500))
{
printf("Send failed\r\n");
return;
}
ESP8266_SendCmd(data, "SEND OK", 1000);
}
在实际使用中,需要添加重发机制,确保数据可靠传输。
6. 云平台对接与数据传输
6.1 云平台选择与配置
我选择OneNet平台,因为它对个人开发者免费,而且文档齐全。首先注册账号,创建产品,记录下产品ID和API Key。
创建设备时,需要设置设备编号和鉴权信息。我建议使用STM32的芯片ID作为设备编号,这样可以保证唯一性。
void Get_ChipID(char *devid)
{
uint32_t chipid[3];
chipid[0] = *(uint32_t*)(0x1FFFF7E8);
chipid[1] = *(uint32_t*)(0x1FFFF7EC);
chipid[2] = *(uint32_t*)(0x1FFFF7F0);
sprintf(devid, "%08X%08X%08X", chipid[0], chipid[1], chipid[2]);
}
6.2 MQTT协议对接
OneNet支持MQTT协议,需要按照平台要求的格式封装数据:
void OneNet_UploadData(SensorData *data)
{
char json[256];
sprintf(json, "{\"datastreams\":["
"{\"id\":\"temperature\",\"datapoints\":[{\"value\":%d}]},"
"{\"id\":\"humidity\",\"datapoints\":[{\"value\":%d}]},"
"{\"id\":\"smoke\",\"datapoints\":[{\"value\":%d}]},"
"{\"id\":\"flame\",\"datapoints\":[{\"value\":%d}]}"
"]}", data->temp, data->humi, data->smoke, data->flame);
char cmd[512];
sprintf(cmd, "POST /devices/%s/datapoints HTTP/1.1\r\n"
"api-key: %s\r\n"
"Host: api.heclouds.com\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%s", device_id, api_key, strlen(json), json);
ESP8266_SendData(cmd);
}
6.3 数据持久化处理
网络不稳定时需要考虑数据缓存:
#define MAX_CACHE_SIZE 10
typedef struct {
SensorData data;
uint32_t timestamp;
} DataCache;
DataCache cache[MAX_CACHE_SIZE];
uint8_t cache_index = 0;
void Add_To_Cache(SensorData *data)
{
cache[cache_index].data = *data;
cache[cache_index].timestamp = Get_TimeStamp();
cache_index = (cache_index + 1) % MAX_CACHE_SIZE;
}
void Upload_Cache_Data(void)
{
for(uint8_t i=0; i<MAX_CACHE_SIZE; i++)
{
if(cache[i].timestamp != 0)
{
OneNet_UploadData(&cache[i].data);
cache[i].timestamp = 0; // 标记为已上传
delay_ms(500);
}
}
}
这样即使网络中断,数据也不会丢失,恢复连接后会自动补传。
7. 手机端报警与监控实现
7.1 开发环境搭建
我建议使用uniapp框架开发手机APP,一套代码可以同时生成Android和iOS应用。首先下载HBuilderX开发工具,创建uniapp项目。
安装必要的插件:
- MQTT插件用于实时通信
- 推送插件用于消息通知
- 图表插件用于数据可视化
7.2 界面设计
主界面显示实时数据:
<template>
<view class="container">
<view class="data-card">
<text class="label">温度</text>
<text class="value">{{ temperature }}℃</text>
</view>
<view class="data-card">
<text class="label">湿度</text>
<text class="value">{{ humidity }}%</text>
</view>
<view class="data-card">
<text class="label">烟雾浓度</text>
<text class="value">{{ smoke }}</text>
</view>
<view class="alert-section" v-if="alertLevel > 0">
<text class="alert-text">{{ alertMessage }}</text>
</view>
</view>
</template>
7.3 数据实时更新
通过MQTT实时接收数据:
const mqtt = require('mqtt/dist/mqtt.js');
export default {
data() {
return {
temperature: 0,
humidity: 0,
smoke: 0,
alertLevel: 0
}
},
mounted() {
this.connectMQTT();
},
methods: {
connectMQTT() {
const client = mqtt.connect('mqtt://api.heclouds.com', {
username: '设备ID',
password: 'APIKey'
});
client.on('message', (topic, message) => {
const data = JSON.parse(message.toString());
this.updateData(data);
});
},
updateData(data) {
this.temperature = data.temperature;
this.humidity = data.humidity;
this.smoke = data.smoke;
if(data.risk_level > 0) {
this.triggerAlert(data.risk_level);
}
},
triggerAlert(level) {
this.alertLevel = level;
if(level === 2) {
this.alertMessage = '高风险警报!可能发生火灾!';
this.sendPushNotification();
} else {
this.alertMessage = '警告:环境参数异常';
}
},
sendPushNotification() {
uni.push({
provider: 'unipush',
type: 'notify',
title: '火灾警报',
content: '检测到可能发生火灾,请立即处理!',
payload: {
type: 'emergency'
}
});
}
}
}
7.4 历史数据查询
添加历史数据查询功能:
async loadHistoryData() {
try {
const response = await uni.request({
url: 'https://api.heclouds.com/devices/设备ID/datapoints',
header: {
'api-key': '你的APIKey'
}
});
this.historyData = response.data.data;
this.drawChart();
} catch (error) {
console.error('获取历史数据失败', error);
}
},
drawChart() {
// 使用echarts绘制数据曲线
const chart = echarts.init(this.$refs.chart);
chart.setOption({
xAxis: {
type: 'time'
},
yAxis: {
type: 'value'
},
series: [{
data: this.historyData.map(item => [item.at, item.value]),
type: 'line'
}]
});
}
这样用户就可以查看历史趋势,更好地了解环境变化情况。
8. 系统优化与实战调试
8.1 功耗优化
对于需要电池供电的场景,功耗优化很重要:
void Enter_LowPower_Mode(void)
{
// 关闭不必要的外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, DISABLE);
// 设置GPIO为模拟输入,减少功耗
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_Init(GPIOC, &GPIO_InitStructure);
// 进入停止模式
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
}
正常模式下功耗约50mA,睡眠模式下可降至1mA以下。
8.2 抗干扰处理
在实际环境中会遇到各种干扰,需要添加滤波算法:
#define FILTER_DEPTH 10
typedef struct {
uint16_t buffer[FILTER_DEPTH];
uint8_t index;
} Filter;
uint16_t Median_Filter(Filter *f, uint16_t new_value)
{
f->buffer[f->index] = new_value;
f->index = (f->index + 1) % FILTER_DEPTH;
// 排序找中值
uint16_t temp[FILTER_DEPTH];
memcpy(temp, f->buffer, sizeof(temp));
for(uint8_t i=0; i<FILTER_DEPTH-1; i++)
{
for(uint8_t j=i+1; j<FILTER_DEPTH; j++)
{
if(temp[i] > temp[j])
{
uint16_t tmp = temp[i];
temp[i] = temp[j];
temp[j] = tmp;
}
}
}
return temp[FILTER_DEPTH/2];
}
8.3 实地测试与校准
系统搭建完成后需要进行实地测试:
- 温度校准:用标准温度计对比读数,计算补偿值
- 烟雾传感器校准:在洁净空气中记录基准值
- 传输距离测试:在不同位置测试信号强度
- 功耗测试:测量各种模式下的电流消耗
我建议制作一个测试记录表:
| 测试项目 | 标准值 | 测量值 | 误差 | 补偿值 |
|---|---|---|---|---|
| 温度25℃ | 25.0 | 25.6 | +0.6 | -0.6 |
| 湿度50% | 50.0 | 48.2 | -1.8 | +1.8 |
| 烟雾基准 | 0 | 12 | +12 | -12 |
8.4 常见问题解决
在实际部署中可能会遇到这些问题:
WiFi连接不稳定:尝试调整ESP8266的天线方向,或者添加外置天线。也可以降低通信速率到9600bps,提高稳定性。
传感器读数跳变:检查电源滤波电容,添加软件滤波算法。
误报问题:调整阈值设置,添加多传感器协同判断。
功耗过高:检查是否有GPIO口漏电,优化软件流程。
我在多个实际项目中部署过这个系统,稳定性都很好。最关键的是做好电磁兼容性处理,比如添加磁珠滤波、屏蔽罩等措施。
经过这些优化和调试,你的智能火灾预警系统就真正达到了实用级别。
&spm=1001.2101.3001.5002&articleId=155797974&d=1&t=3&u=48c9f05cfc83433bbe31c427ca8992a4)
3012

被折叠的 条评论
为什么被折叠?



