51单片机+ESP8266连接mqtt的智能火灾报警系统设计与实现
声明:本项目为自研,(前端是小伙伴帮忙做的)开发不易,大家点点赞支持一下主播吧(目前网上没有看到基于51的mqtt相关项目,感觉51搞这些太容易超内存了,并且大佬都在搞32)
本博客不包含emqx的搭建教程,不会搭建emqx的可以先看这个博主的视频: https://b23.tv/fuq6wbk
结尾附网盘链接
项目概述
本项目基于STC89C52RC单片机,结合ESP8266 WiFi模块、DHT11温湿度传感器、MQ-2气体传感器等硬件,设计并实现了一个智能火灾报警系统。系统能够实时监测环境温湿度和可燃气体浓度,通过MQTT协议将数据上传到云平台,并支持远程报警和阈值设置。

系统架构
硬件组成
-
主控芯片:STC89C52RC单片机
-
通信模块:ESP8266 WiFi模块(ESP-01S)
-
传感器:
-
DHT11温湿度传感器
-
MQ-2气体浓度传感器
-
-
AD/DA转换模块:PCF8591转换模块
-
显示模块:LCD1602液晶显示屏
-
报警模块:蜂鸣器、LED报警灯
软件架构
-
开发环境:Keil C51
-
通信协议:MQTT
-
数据格式:JSON
PCB设计
1.原理图

2.PCB

核心功能实现
1. 传感器数据采集
1.mq2烟雾传感器
#include "mq2.h"
#include "i2c.h"
#define PCF8591_ADDR 0x90 // PCF8591 I2C地址
// 读取PCF8591指定通道的模拟值(MQ2接通道0)
unsigned char MQ2_ReadByte(unsigned char Channel)
{
unsigned char Data;
I2C_Start();
I2C_SendByte(PCF8591_ADDR); // 写地址
I2C_ReceiveAck();
I2C_SendByte(0x40 | Channel); // 控制字:开启模拟输入+指定通道
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(PCF8591_ADDR | 0x01); // 读地址
I2C_ReceiveAck();
Data = I2C_ReceiveByte(); // 读取数据
I2C_SendAck(1); // 发送非应答
I2C_Stop();
return Data;
}
#ifndef __MQ2_H__
#define __MQ2_H__
#include <reg52.h>
// PCF8591
#define PCF8591_CH0 0x00 // MQ2接通道0
#define PCF8591_CH1 0x01
#define PCF8591_CH2 0x02
#define PCF8591_CH3 0x03
unsigned char MQ2_ReadByte(unsigned char Channel);
#endif
2.dht11温湿度传感器
#include "dht11.h"
#include <intrins.h>
// 30us延时
void DHT11_Delay30us(void)
{
unsigned char i;
i = 12;
while (--i);
}
// 40us延时
void DHT11_Delay40us(void)
{
unsigned char i;
i = 17;
while (--i);
}
uint8_t DHT11_Init(void)
{
DHT11_DQ = 1;
DHT11_DQ = 0;
Delay_xms(20); // 至少给18ms
DHT11_DQ = 1;
DHT11_Delay30us();
if(DHT11_DQ == 0)
{
while(DHT11_DQ == 0);
while(DHT11_DQ == 1);
return 0;
}
return 1;
}
uint8_t DHT11_ReceiveByte(void)
{
uint8_t i, Byte = 0x00;
for(i=0; i<8; i++)
{
Byte <<= 1;
while(DHT11_DQ == 0);
DHT11_Delay40us();
if(DHT11_DQ == 1) Byte |= 0x01;
while(DHT11_DQ == 1);
}
return Byte;
}
uint8_t DHT11_GetData(uint8_t *Humi_H, uint8_t *Humi_L, uint8_t *Temp_H, uint8_t *Temp_L)
{
uint8_t i, Buffer[5];
if(DHT11_Init() == 0)
{
for(i=0; i<5; i++) Buffer[i] = DHT11_ReceiveByte();
if(Buffer[0] + Buffer[1] + Buffer[2] + Buffer[3] == Buffer[4])
{
*Humi_H = Buffer[0];
*Humi_L = Buffer[1];
*Temp_H = Buffer[2];
*Temp_L = Buffer[3];
return 0;
}
}
return 1;
}
#ifndef __DHT11_H__
#define __DHT11_H__
#include <reg52.h>
#include "delay.h"
typedef unsigned char uint8_t;
sbit DHT11_DQ = P2^0; // 引脚设置
uint8_t DHT11_Init(void);
uint8_t DHT11_ReceiveByte(void);
uint8_t DHT11_GetData(uint8_t *Humi_H, uint8_t *Humi_L, uint8_t *Temp_H, uint8_t *Temp_L);
#endif
2. ESP8266 MQTT通信
注意:客户端ID随意但是必须唯一,用户名称和密码就是自己在emqx上设置的

注意:mqtt的IP地址用ipconfig查看不了,可以用网络调试助手等软件看,不知道是哪个可以先用串口调试ESP8266,发送AT指令连接尝试:(串口发送AT指令记得末尾加上换行)
AT+CWMODE=1
AT+CWJAP="WiFi名","WiFi密码"
AT+MQTTUSERCFG=0,1,"客户端ID","用户名","密码",0,0,""
AT+MQTTCONN=0,"mqtt的IP地址",1883,0
网络调试助手:

esp8266.c
#include "esp8266.h"
#include <reg52.h>
#include "lcd1602.h"
#include "usart.h"
#include "delay.h"
#include "time.h"
// 硬件引脚定义
sbit WIFI_RST = P1^7; // ESP8266复位
sbit Bee = P1^6; // 蜂鸣器
sbit ALARM_LED= P1^5; // 报警灯
// 全局变量:传感器数据
unsigned char Humi_H, Temp_H, Gas;
unsigned char Threshold = 29; // 温度报警阈值
// ESP8266初始化连接MQTT(使用AT指令)
void ESP_conect_sev(void)
{
// ESP8266复位
WIFI_RST = 0;
Delay_xms(1000);
WIFI_RST = 1;
Delay_xms(1000);
// 本地MQTT AT指令配置
uart1_sendstring("AT+CWMODE=1\r\n");
Delay_xms(1000);
uart1_sendstring("AT+CWJAP=\"WiFi名\",\"密码\"\r\n"); // 连接WiFi
Delay_xms(2000);
uart1_sendstring("AT+MQTTUSERCFG=0,1,\"客户端ID\",\"用户名\",\"密码\",0,0,\"\"\r\n"); // MQTT用户配置
Delay_xms(1000);
uart1_sendstring("AT+MQTTCONN=0,\"IP地址\",1883,0\r\n"); // 连接MQTT服务器
Delay_xms(2000);
}
// 数字转字符串(发送数据用)
void num_to_str(unsigned char num, unsigned char *num_str1)
{
if(num != 0)
{
unsigned int num_temp = num;
unsigned char cnt = 0;
while(num_temp)
{
cnt++;
num_temp /= 10;
}
num_temp = num;
while(cnt)
{
cnt--;
num_str1[cnt] = (num_temp % 10) + 48;
num_temp /= 10;
}
}
else
{
num_str1[0] = '0';
}
}
// 发送第一个传感器数据(湿度)
void first_data(unsigned char *str2, unsigned char num)
{
unsigned char str1[3] = {0};
// MQTT发布主题:testtopic/1,保留JSON数据格式
uart1_sendstring("AT+MQTTPUB=0,\"testtopic/1\",\"{\\\"");
uart1_sendstring(str2);
uart1_sendstring("\\\":");
num_to_str(num, str1);
uart1_sendstring(str1);
}
// 追加传感器数据(温度、气体浓度)
void add_data(unsigned char *str2, unsigned char num)
{
unsigned char str1[3] = {0};
uart1_sendstring("\\,\\\"");
uart1_sendstring(str2);
uart1_sendstring("\\\":");
num_to_str(num, str1);
uart1_sendstring(str1);
}
// 每秒发送传感器数据到MQTT
void ESP_send_data(void)
{
if(time0_ms > 1000) // 1秒发送一次
{
first_data(APP_Humi, Humi_H); // 湿度
add_data(APP_Temp, Temp_H); // 温度
add_data(APP_Gas, Gas); // 气体浓度
uart1_sendstring("}\",0,0\r\n");
Delay_xms(200);
uart1_sendstring("AT+MQTTPUB=0,\"订阅主题名\",\"");
if(Gas > 50 || Temp_H > Threshold)
{
uart1_sendstring("fire"); // 报警发送fire
}
else
{
uart1_sendstring("normal");// 正常发送normal
}
uart1_sendstring("\",0,0\r\n");
Delay_xms(200);
time0_ms = 0; // 重置定时器
}
}
// 字符串比较
bit str_cmp(char *str1, char *str2, char len)
{
char *str3 = str1;
char *str4 = str2;
while(len)
{
if(*str3 != *str4) return 0;
len--;
str3++;
str4++;
}
return 1;
}
// 字符串转数字
int str_to_num(char *str1)
{
unsigned char num = 0;
while(*str1 >= '0' && *str1 <= '9')
{
num += (*str1) - 48;
str1++;
if(!(*str1 >= '0' && *str1 <= '9')) break;
num *= 10;
}
return num;
}
// 处理本地MQTT下发指令
unsigned char Deliver_num;
void Proces_distribut_data(void)
{
unsigned char i; // C89:提前声明循环变量,修复语法错误
if(RX_ok) // 接收完成
{
Deliver_num = str_to_num(Deliver_data); // 解析指令数值
// 仅保留温度阈值修改逻辑(可扩展其他本地指令)
if(str_cmp(Deliver_identifier, APP_Threshold, 8))
{
Threshold = Deliver_num;
}
// 清空接收缓存
for(i=0; i<6; i++)
{
Deliver_identifier[i] = 0;
}
for(i=0; i<4; i++)
{
Deliver_data[i] = 0;
}
Deliver_cnt = 0;
Deliver_data_cnt = 0;
RX_ok = 0;
}
}
// 传感器报警逻辑(气体浓度/温度过高)
void Alarm_Check(void)
{
if(Gas > 50 || Temp_H > Threshold) // 气体浓度>阈值 或 温度>阈值
{
LCD_ShowString(2, 7, "fire!");
Bee = 0; // 蜂鸣器响
ALARM_LED = 1; // 报警灯亮
}
else
{
LCD_ShowString(2, 7, " ");
Bee = 1; // 蜂鸣器关
ALARM_LED = 0; // 报警灯灭
}
}
esp8266.h
#ifndef _ESP8266_H_
#define _ESP8266_H_
#include <reg52.h>
#include "usart.h"
#include "time.h"
// 传感器数据宏定义
#define APP_Humi "Humi" // 湿度
#define APP_Temp "Temp" // 温度
#define APP_Gas "Gas" // 气体浓度
#define APP_Threshold "Threshold" // 温度阈值
extern unsigned char Humi_H, Temp_H, Gas;
extern unsigned char Threshold;
void ESP_conect_sev(void);
void ESP_send_data(void);
void num_to_str(unsigned char num, unsigned char *num_str1);
bit str_cmp(char *str1, char *str2, char len);
int str_to_num(char *str1);
void Proces_distribut_data(void);
void Alarm_Check(void);
void first_data(unsigned char *str2, unsigned char num);
void add_data(unsigned char *str2, unsigned char num);
#endif
关键技术点
1. 数据格式转换
由于51的时钟频率差异(11.0592MHz相比32性能相差数十倍,所以数字转换算法必须极度优化,我在这个问题上卡了两天,后面查到一种比较好的轻量级数字转换算法
void num_to_str(unsigned char num, unsigned char *num_str1)
{
if(num != 0)
{
unsigned int num_temp = num;
unsigned char cnt = 0;
while(num_temp)
{
cnt++;
num_temp /= 10;
}
num_temp = num;
while(cnt)
{
cnt--;
num_str1[cnt] = (num_temp % 10) + 48;
num_temp /= 10;
}
}
else
{
num_str1[0] = '0';
}
}
2. JSON数据构造
由于51单片机内存限制,无法使用标准库,需要采用手动构造JSON格式数据:
void first_data(unsigned char *str2, unsigned char num)
{
unsigned char str1[3] = {0};
// MQTT发布主题:testtopic/1,保留JSON数据格式
uart1_sendstring("AT+MQTTPUB=0,\"testtopic/1\",\"{\\\"");
uart1_sendstring(str2);
uart1_sendstring("\\\":");
num_to_str(num, str1);
uart1_sendstring(str1);
}
3. 中断处理的能力限制
51单片机中断向量少,响应慢。32则有着丰富的中断源,NVIC优先级管理
void UART1_IRQ_Handle(void) interrupt 4 using 1
{
if (TI) {
TI = 0;
uart1_busy = 0; // 只做标志位操作
}
if (RI) {
Pro_deliver_data(); // 立即处理,不缓存
RI = 0;
}
}
系统特点
-
实时监测:持续监控环境参数
-
双重报警:本地声光报警 + 远程状态推送
-
远程控制:支持通过MQTT修改报警阈值
-
稳定可靠:采用工业级传感器和成熟的通信协议
-
低成本:基于廉价硬件组件
应用场景
-
家庭火灾预警
-
仓库环境监控
-
实验室安全监测
-
工业生产环境监控
总结
本系统成功实现了基于51单片机和ESP8266的智能火灾报警功能,展示了如何将传统单片机与现代物联网技术相结合。系统具有良好的实用性和扩展性,为小型化、低成本的智能安防系统开发提供了参考方案。
网盘链接: https://pan.baidu.com/s/1cfd2bHt5SNVlaVzUCct9XQ提取码: sdjw
技术交流:849498477 欢迎在评论区或群留言讨论!
更多推荐
所有评论(0)