声明:本项目为自研,(前端是小伙伴帮忙做的)开发不易,大家点点赞支持一下主播吧(目前网上没有看到基于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;
    }
}

系统特点

  1. 实时监测:持续监控环境参数

  2. 双重报警:本地声光报警 + 远程状态推送

  3. 远程控制:支持通过MQTT修改报警阈值

  4. 稳定可靠:采用工业级传感器和成熟的通信协议

  5. 低成本:基于廉价硬件组件

应用场景

  • 家庭火灾预警

  • 仓库环境监控

  • 实验室安全监测

  • 工业生产环境监控

总结

本系统成功实现了基于51单片机和ESP8266的智能火灾报警功能,展示了如何将传统单片机与现代物联网技术相结合。系统具有良好的实用性和扩展性,为小型化、低成本的智能安防系统开发提供了参考方案。


网盘链接: https://pan.baidu.com/s/1cfd2bHt5SNVlaVzUCct9XQ提取码: sdjw
技术交流:849498477  欢迎在评论区或群留言讨论!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐