智能火焰联动报警与远程监管系统——基于STM32+K210+ESP8266(OneNet平台)
若该文为原创文章,转载请注明原文出处。
一、项目背景与意义
火灾是危害公共安全的主要灾害之一。传统火灾报警器通常只依赖单一传感器(烟雾或温度),误报率高,且无法远程监控。随着AI视觉和物联网技术的发展,将图像识别与多传感器融合,再结合云平台,可以实现精准识别、快速响应、远程可视、历史可溯的智能消防系统。
本项目设计了一套增强型智能火焰联动报警与远程监管系统,以STM32F103C8T6为主控,K210通过YOLOv2实现火焰视觉检测,结合MQ-2烟雾传感器和DS18B20温度传感器进行融合判断,通过ESP8266(ESP-01S)接入中国移动OneNet云平台,实现实时数据上报、远程控制、历史记录查询等功能。系统还具备本地声光报警、JQ8900语音播报、OLED信息显示、SD卡日志存储等完整本地交互能力。
二、系统总体架构
2.1 系统分层
| 层级 | 功能模块 | 硬件设备 |
|---|---|---|
| 感知层 | 火焰视觉检测、烟雾浓度采集、温度采集 | K210+摄像头、MQ-2、DS18B20 |
| 控制层 | 逻辑决策、外设控制、通信处理 | STM32F103C8T6 |
| 执行层 | 声光报警、联动控制、语音播报 | 蜂鸣器/LED、继电器、JQ8900语音模块 |
| 交互层 | 本地信息显示、按键操作 | OLED屏、按键 |
| 存储层 | 报警日志记录 | MicroSD卡(SPI接口) |
| 通信层 | WiFi联网、数据上报、远程控制 | ESP-01S (ESP8266) |
| 云平台 | 数据展示、历史记录、远程控制 | OneNet物联网平台 |
2.2 系统框图

三、硬件选型与设计
3.1 核心硬件清单
| 模块 | 型号 | 数量 | 接口 | 作用 |
|---|---|---|---|---|
| 主控MCU | STM32F103C8T6 | 1 | - | 系统逻辑核心 |
| AI视觉 | 正点原子K210 + OV2640 | 1 | UART | 火焰YOLO检测,输出置信度 |
| WiFi模块 | ESP-01S (ESP8266) | 1 | UART | 连接OneNet,MQTT通信 |
| 烟雾传感器 | MQ-2 | 1 | ADC | 烟雾/可燃气体浓度检测 |
| 温度传感器 | DS18B20 | 1 | 1-Wire | 环境温度检测 |
| 语音播报 | JQ8900 MP3模块 | 1 | UART/触发IO | 播放预先存入TF卡的语音文件 |
| OLED显示屏 | 0.96寸 SSD1306 | 1 | I2C | 显示系统状态、传感器数值 |
| MicroSD卡模块 | SPI接口 | 1 | SPI | 存储报警日志(CSV格式) |
| 有源蜂鸣器 | 5V | 1 | GPIO | 声音报警 |
| 高亮LED | 红/蓝双色 | 1 | GPIO | 光报警 |
| 继电器模块 | 5V 2路 | 1 | GPIO | 控制排烟扇、喷淋电磁阀 |
| 按键 | 轻触开关 | 3 | GPIO | 复位/消音、自检、页面切换 |
| 电源 | 5V/2A适配器 | 1 | DC口 | 系统供电 |
3.2 引脚连接表(STM32端)
| STM32引脚 | 外设 | 说明 |
|---|---|---|
| PA9 (USART1_TX) | K210 (RX) | 发送查询指令 |
| PA10 (USART1_RX) | K210 (TX) | 接收火焰检测结果 |
| PA2 (USART2_TX) | ESP-01S (RX) | 发送AT/MQTT数据 |
| PA3 (USART2_RX) | ESP-01S (TX) | 接收WiFi/MQTT数据 |
| PA9 (或其它UART) | JQ8900 (RX) | 语音播报控制(若UART复用,可采用IO触发模式) |
| PB0 | 蜂鸣器 | 高电平驱动 |
| PB1 | LED(红) | 报警指示灯 |
| PB2 | LED(蓝) | 运行指示灯 |
| PB3 | 继电器1(排烟) | 控制排烟风扇 |
| PB4 | 继电器2(喷淋) | 控制喷淋电磁阀 |
| PB5 | KEY1(复位/消音) | 输入,下拉 |
| PB6 | KEY2(自检) | 输入,下拉 |
| PB7 | KEY3(页面切换) | 输入,下拉 |
| PB8 | OLED SCL | I2C时钟 |
| PB9 | OLED SDA | I2C数据 |
| PA4 | SD_CS | SPI片选 |
| PA5 | SD_SCK | SPI时钟 |
| PA6 | SD_MISO | SPI主入从出 |
| PA7 | SD_MOSI | SPI主出从入 |
| PA0 | MQ-2输出 | ADC输入 |
| PA1 | DS18B20 | 1-Wire数据 |
说明:JQ8900支持串口指令播放(如“播放第1首”、“音量调节”等),也可通过IO触发播放指定编号的音频。为节省UART资源,可采用IO触发模式,将JQ8900的触发引脚连接到STM32的GPIO,通过电平脉冲选择播放不同的音频文件(预先烧录到JQ8900的TF卡中)。
四、功能模块详细设计
4.1 火焰视觉检测(K210)
-
模型:YOLOv2,输入尺寸224×224,训练数据集包含火焰和干扰物(如红色灯光、太阳光等),输出两个类别:
no(无火)、fire(有火)。 -
通信协议:K210通过UART向STM32发送数据,格式:
FIRE:0.87(检测到火焰,置信度0.87)或NONE(未检测到火焰)。 -
帧率:约10~15 FPS,满足实时性要求。
4.2 多传感器融合与分级报警策略
STM32每200ms采集一次三路数据,综合判断火警等级:
| 等级 | 火焰置信度 | 烟雾浓度(ADC值) | 温度(℃) | 动作 |
|---|---|---|---|---|
| 0(正常) | <0.5 | <100 | <40 | 无动作,LED蓝灯闪烁 |
| 1(预警) | ≥0.5且<0.7 | ≥100或≥40 | ≥40 | 蜂鸣器慢速鸣叫(1Hz),OLED显示“预警” |
| 2(一般火警) | ≥0.7 | ≥200或≥45 | ≥45 | 蜂鸣器中速(2Hz),启动排烟继电器,语音播报“请注意,检测到火情”,上报OneNet |
| 3(紧急火警) | ≥0.8 | ≥350或≥60 | ≥60 | 蜂鸣器快速(4Hz),启动排烟+喷淋,语音播报“紧急火警,请立即疏散”,上报OneNet紧急事件 |
4.3 本地人机交互
-
OLED显示:默认页面显示实时传感器值、火警等级、WiFi状态;按KEY3可切换至日志查看页面(显示最近5条报警记录)。
-
按键功能:
-
KEY1(短按):消音/复位当前报警;
-
KEY1(长按3秒):进入系统自检模式(依次测试蜂鸣器、LED、继电器、语音);
-
KEY2:手动触发一次模拟火警(用于功能演示);
-
KEY3:切换OLED显示页面。
-
4.4 语音播报(JQ8900)
-
音频准备:将语音文件(MP3格式)按序号命名(如
001.mp3为“系统启动”、002.mp3为“检测到火情”、003.mp3为“紧急火警,请立即疏散”等)存入JQ8900的TF卡。 -
播放控制:STM32通过IO触发方式,拉低对应的触发引脚(如PLAY1、PLAY2等)即可播放指定音频。若使用串口指令,则通过UART发送“播放第X首”指令。
-
音量调节:可通过指令设定音量大小。
4.5 远程通信与OneNet云平台
-
WiFi模块:ESP-01S(AT固件),STM32通过AT指令控制连接WiFi、建立TCP/MQTT连接。
-
协议:MQTT(基于OneNet的MQTT旧版协议)。
-
数据上传格式:JSON格式,包含:
{
"fire_level": 2,
"smoke": 250,
"temp": 42.5,
"confidence": 0.85,
"timestamp": 1691234567
}
-
上传触发:火警等级变化时立即上传,正常状态下每30秒上传一次心跳(包含当前传感器数值,等级=0)。
-
下行指令:订阅OneNet的“命令”Topic,可接收:
-
{"cmd":"reset"}:复位报警 -
{"cmd":"fan_on"}:强制启动排烟 -
{"cmd":"fan_off"}:关闭排烟 -
{"cmd":"sprinkler_on"}:启动喷淋 -
{"cmd":"sprinkler_off"}:关闭喷淋 -
{"cmd":"set_threshold","smoke":300,"temp":50}:修改报警阈值
-
4.7 OneNet平台与历史记录查询
-
OneNet产品创建:创建MQTT产品,设备注册后获取设备ID和API密钥。
-
数据流:定义多个数据流(fire_level, smoke, temp, confidence)。
-
可视化:使用OneNet的“可视化”组件创建仪表盘,展示实时数据及历史曲线。
-
历史查询:OneNet默认保存数据流的历史数据(可通过API或控制台查询)。手机APP可通过OneNet官方APP或自定义APP(调用OneNet RESTful API)查询历史记录。
手机APP查询流程:
-
用户打开APP(或微信小程序);
-
调用OneNet API:
GET /devices/{device_id}/datapoints?datastream_id=smoke&start=...&end=... -
获取指定时间段的历史数据并列表展示,同时可绘制曲线图。
五、软件流程
5.1 主程序流程图
系统初始化(时钟、GPIO、UART、I2C、SPI、ADC、1-Wire、FATFS)
加载默认阈值(从SD卡/Flash读取,若不存在则使用默认值)
初始化OLED显示“系统启动”
初始化JQ8900播放“系统启动”语音
ESP-01S初始化(AT测试、连接WiFi、连接OneNet MQTT)
进入主循环:
1. 读取K210串口数据(非阻塞),更新火焰状态
2. 读取MQ-2 ADC值 → 滤波 → 换算浓度
3. 读取DS18B20温度
4. 综合评估火警等级
5. 如果等级变化:
记录日志到SD卡
触发声光报警(等级对应频率)
控制继电器(排烟/喷淋)
播放对应语音
上传数据到OneNet
6. 如果等级=0且之前有报警且已持续正常30秒,则自动复位(或等待手动KEY1)
7. 刷新OLED(每200ms更新数值,按KEY3切换页面)
8. 检测按键(消音、自检、页面切换)
9. 处理ESP-01S接收的MQTT消息(解析指令并执行)
10. 定时(每30秒)上传一次心跳数据(包含所有传感器值)
11. 延时100ms,循环
5.2 K210端程序(框架)
K210端使用正点原子提供的YOLO例程,仅需修改主循环,通过UART发送检测结果:
# YOLO 火灾检测 - 适配正点原子 K210 (支持 Flash/SD 加载 + 串口输出)
import sensor, image, lcd, time
from maix import KPU
import gc, sys
from machine import UART
from fpioa_manager import fm
from board import board_info
input_size = (224, 224)
labels = ['no', 'fire']
anchors = [6.94, 4.56, 2.44, 2.12, 0.34, 0.41, 0.56, 0.78, 1.34, 1.22]
anchor_num = len(anchors) // 2 # 5
# ========== 配置区域 ==========
USE_FLASH = True # True=从Flash加载,False=从SD卡加载
MODEL_ADDR = 0x300000 # Flash地址(使用前请确认实际烧录地址)
MODEL_SIZE = 1906856 # 模型大小(字节),请根据实际文件大小填写
MODEL_PATH = "/sd/model-56191.kmodel" # SD卡路径
# LCD/摄像头旋转设置(180度)
LCD_ROTATION = 2 # 0:0°, 1:90°, 2:180°, 3:270°
SENSOR_HMIRROR = True # 水平翻转
SENSOR_VFLIP = True # 垂直翻转
# YOLO参数
CONFIDENCE_THRESHOLD = 0.5
NMS_THRESHOLD = 0.3
# ================================
def lcd_show_except(e):
import uio
err_str = uio.StringIO()
sys.print_exception(e, err_str)
err_str = err_str.getvalue()
img = image.Image(size=input_size)
img.draw_string(0, 10, err_str, scale=1, color=(0xff,0x00,0x00))
lcd.display(img)
class Comm:
def __init__(self, uart):
self.uart = uart
def send_detect_result(self, objects, labels):
"""发送检测结果,格式: x:y:w:h:class_id:confidence:label, ..."""
msg = ""
for obj in objects:
x, y, w, h, class_id, confidence = obj
label = labels[class_id]
msg += "{}:{}:{}:{}:{}:{:.2f}:{}, ".format(x, y, w, h, class_id, confidence, label)
if msg:
msg = msg[:-2] + "\n" # 去掉末尾空格和逗号,加换行
self.uart.write(msg.encode())
def init_uart():
"""初始化UART1,优先使用board_info定义的引脚,否则使用固定引脚10/11"""
try:
fm.register(board_info.EX_UART1_TX, fm.fpioa.UART1_TX, force=True)
fm.register(board_info.EX_UART1_RX, fm.fpioa.UART1_RX, force=True)
uart = UART(UART.UART1, 115200, 8, 0, 0, timeout=1000, read_buf_len=256)
print("UART1 configured with board_info")
return uart
except Exception:
pass
fm.register(10, fm.fpioa.UART1_TX, force=True)
fm.register(11, fm.fpioa.UART1_RX, force=True)
uart = UART(UART.UART1, 115200, 8, 0, 0, timeout=1000, read_buf_len=256)
print("UART1 configured with fixed pins (10,11)")
return uart
def main(anchors, labels=None, model_addr=MODEL_ADDR, model_size=MODEL_SIZE,
model_path=MODEL_PATH, use_flash=USE_FLASH,
sensor_window=input_size, lcd_rotation=LCD_ROTATION,
sensor_hmirror=SENSOR_HMIRROR, sensor_vflip=SENSOR_VFLIP,
conf_threshold=CONFIDENCE_THRESHOLD, nms_threshold=NMS_THRESHOLD):
# 1. 初始化传感器
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing(sensor_window)
sensor.set_hmirror(sensor_hmirror)
sensor.set_vflip(sensor_vflip)
sensor.run(1)
# 2. 初始化LCD
lcd.init(type=1)
lcd.rotation(lcd_rotation)
lcd.clear(lcd.WHITE)
# 3. 加载标签
if not labels:
try:
with open('labels.txt','r') as f:
exec(f.read())
except Exception:
print("no labels.txt, using default labels")
labels = ['no', 'fire']
if not labels:
print("no labels")
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "no labels", color=(255, 0, 0), scale=2)
lcd.display(img)
return 1
# 4. 显示启动画面
try:
img = image.Image("startup.jpg")
lcd.display(img)
except Exception:
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "loading model...", color=(255, 255, 255), scale=2)
lcd.display(img)
# 5. 初始化UART
uart = init_uart()
comm = Comm(uart)
print("UART ready, system start...")
# 6. 创建KPU对象并加载模型
task = None
try:
task = KPU()
if use_flash:
print("Loading model from Flash: 0x{:06X}, size: {}".format(model_addr, model_size))
task.load_kmodel(model_addr, model_size)
else:
print("Loading model from SD: {}".format(model_path))
task.load_kmodel(model_path)
print("Model loaded successfully!")
# 7. 初始化YOLO2
net_w, net_h = sensor_window
layer_w = net_w // 32
layer_h = net_h // 32
print("YOLO2 init: layer_w={}, layer_h={}".format(layer_w, layer_h))
task.init_yolo2(
anchors,
anchor_num=anchor_num,
img_w=net_w,
img_h=net_h,
net_w=net_w,
net_h=net_h,
layer_w=layer_w,
layer_h=layer_h,
threshold=conf_threshold,
nms_value=nms_threshold,
classes=len(labels)
)
print("YOLO2 initialized successfully!")
# 8. 主循环
while True:
img = sensor.snapshot()
t = time.ticks_ms()
# 推理
task.run_with_output(img)
objects = task.regionlayer_yolo2()
t = time.ticks_ms() - t
# 绘制检测结果并串口发送
if objects:
for obj in objects:
x, y, w, h, class_id, confidence = obj
# 画框
img.draw_rectangle(x, y, w, h, color=(0, 255, 0))
# 显示标签和置信度
label_text = "{}:{:.2f}".format(labels[class_id], confidence)
img.draw_string(x+2, y+2, label_text, scale=2, color=(255, 0, 0))
# 串口发送
comm.send_detect_result(objects, labels)
# 显示FPS等信息
img.draw_string(0, 200, "t:%dms" % t, scale=2, color=(255, 0, 0))
img.draw_string(0, 2, "Fire Detector", scale=1.5, color=(0, 255, 255))
if use_flash:
load_info = "Flash:0x{:06X}".format(model_addr)
else:
load_info = "SD:{}".format(model_path.split('/')[-1])
img.draw_string(0, 30, load_info, scale=1, color=(128, 128, 128))
lcd.display(img)
gc.collect()
except Exception as e:
print("Error:", e)
raise e
finally:
if task is not None:
try:
task.deinit()
except Exception:
pass
gc.collect()
if __name__ == "__main__":
try:
main(anchors=anchors, labels=labels,
model_addr=MODEL_ADDR,
model_size=MODEL_SIZE,
model_path=MODEL_PATH,
use_flash=USE_FLASH,
sensor_window=input_size,
lcd_rotation=LCD_ROTATION,
sensor_hmirror=SENSOR_HMIRROR,
sensor_vflip=SENSOR_VFLIP)
except Exception as e:
sys.print_exception(e)
lcd_show_except(e)
finally:
gc.collect()
5.3 OneNet平台配置简要步骤
-
登录OneNet平台,创建“MQTT”产品;
-
注册设备,获取设备ID、鉴权信息(API Key);
-
在设备详情中创建多个数据流(
fire_level,smoke,temp,confidence); -
下载OneNet提供的MQTT接入示例,根据文档修改
clientId、username、password等参数; -
编写STM32端MQTT封装代码(参考OneNet官方C SDK或使用AT指令+自定义封装);
-
开发手机APP(可使用OneNet官方APP直接绑定设备,或基于API开发微信小程序)。
六、项目实施步骤
| 阶段 | 任务 | 时间估算 |
|---|---|---|
| 硬件搭建 | 焊接/插接各模块,检查供电和连接 | 1天 |
| 驱动调试 | 分别调试各外设(OLED、SD卡、传感器、语音等) | 2天 |
| K210模型烧录与通信 | 将火焰检测模型烧录到K210,调试UART数据格式 | 1天 |
| STM32逻辑开发 | 实现主状态机、融合算法、本地交互 | 3天 |
| ESP-01S网络通信 | 实现AT指令控制、MQTT连接、数据收发 | 2天 |
| OneNet平台配置 | 创建产品、设备、数据流,测试数据上传 | 1天 |
| APP历史查询 | 调用OneNet API开发简易查询页面 | 2天 |
| 系统联调与优化 | 联合调试,调整阈值,测试稳定性 | 2天 |
七、创新点与技术特色
-
视觉-传感融合:将AI视觉与常规传感器结合,提高了火灾识别的准确性和抗干扰能力。
-
分级报警机制:不同风险等级对应不同响应策略,避免单一报警导致的过度反应或漏报。
-
语音交互:JQ8900语音播报让报警信息更直观,适用于嘈杂环境。
-
低成本、易部署:所有硬件模块均为市面常见型号,成本低廉,可快速复制。
八、项目展示效果
-
本地端:OLED实时显示传感器数值和系统状态;报警时蜂鸣器LED声光齐鸣,继电器动作,语音播报。
-
云端端:OneNet平台显示实时数据曲线,历史报警事件可查询;手机APP可随时查看设备状态和报警记录。
-
远程控制:管理员可通过APP下发指令,实现远程复位、启停排烟喷淋等操作
九、常见问题与解决
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| K210不发送数据 | 串口波特率不匹配或接线错误 | 检查波特率(115200)和TX/RX交叉连接 |
| ESP-01S连接WiFi失败 | SSID/密码错误或信号弱 | 检查AT指令,靠近路由器测试 |
| 语音不播报 | JQ8900供电不足或音频文件格式不对 | 确保5V供电,音频为MP3格式,命名正确 |
| SD卡写入失败 | 初始化失败或文件系统未格式化 | 确保SD卡为FAT32格式,检查SPI接线 |
| OneNet连接超时 | MQTT参数错误或网络延迟 | 确认设备ID和API Key正确,检查防火墙 |
十、总结与展望
本项目实现了从火焰检测到本地联动、云端监控的全链路智能消防系统。通过引入AI视觉和多传感器融合,有效降低了误报率;结合OneNet云平台,实现了远程监管和历史数据追溯。未来可进一步扩展功能:
-
增加人脸识别,实现报警时抓拍现场图片并上传;
-
接入多个K210摄像头,实现多区域监控;
-
增加GPS定位,支持移动式火情追踪;
-
采用低功耗设计,适配野外或无人值守场景。
如有侵权,或需要完整代码,请及时联系博主。
更多推荐

所有评论(0)