基于mqtt的环境监测系统-方案设计

238 阅读11分钟

关键词:

物联网;环境监测系统;STM32F103C8T6;MQTT协议

硬件:stm32及相关环境采集传感器

软件:keil5 Hbuilder 串口调试助手

本系统使用阿里云物联网云平台进行消息流转。

总体概述:

基于mqtt的环境监测系统包括硬件设备采集、传输和后台软件控制两部分。

其中硬件部分采用传感器+STM32单片机的方式,利用温湿度传感器、火焰传感器、氨气浓度传感器、二氧化碳传感器、光敏传感器和压力传感器等采集环境数据,将采集的数据经由STM32F103C8T6单片机,通过无线网络WIFI的方式传送至上位机(后台软件)。

后台软件使用到了兼容性和易用性都较为良好的微信小程序进行对于种植环境数据的实时展示和变化趋势分析,便于用户远程查看当前环境的状态。用户还可以通过小程序或硬件外设实现对于环境的手动调控。

方案设计

  • STM32固件开发:使用适当的开发工具和语言(如C/C++)编写STM32单片机的固件程序,包括数据采集、数据处理、控制算法;
  • ESP8266WIFI模块:需要进行固件烧录,使用AT指令与物联网云平台连接通信。
  • 客户端(小程序设计):使用微信小程序uniapp开发框架,编写小程序前端界面和后台逻辑。微信小程序能够实时显示传感器数据,并及时向用户发送通知和警报。
  • 双端数据通信:微信小程序需要实现与STM32单片机的数据交互和远程监控。因此需要使用用于在Web浏览器和Node.js环境中实现MQTT通信的mqtt.js客户端库,连接云平台进行通信。双端连接后,由云平台进行消息转发,实现STM32和微信小程序之间的数据交互。
  • 测试和调试:最后需要对整个系统进行测试和调试,确保功能能够正常运行。

技术介绍

  1. MQTT(Message Queuing Telemetry Transport)是一种轻量级的、基于发布-订阅模式的通信协议,多用于物联网技术中。MQTT技术的最大优点在于,使用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。
  2. STM32单片机是一系列由STMicroelectronics开发的32位ARM Cortex-M微控制器,具备高性能的处理能力和较大的存储容量,能够处理复杂的算法和任务,同时支持多种外设和通信接口。使用STM32作为核心元件能够实现实时数据采集和控制、多任务处理、硬件扩展能力和低功耗设计,提高系统的性能、可靠性和灵活性。
  3. 传感器节点:
    • 温湿度监测模块:温湿度传感器DHT11
    • 高温报警器模块:热敏电阻+蜂鸣器
    • 二氧化碳浓度监测模块:二氧化碳传感器JW01-CO2-V2.2+风扇
    • 光照强度监测模块:光敏电阻+补光灯
    • 自动灌溉模块:土壤湿度检测传感器+水泵
    • RFID模块:
    • ESP8266WIFI信息传输模块:在本系统中,ESP8266 WiFi模块的主要作用是通过连接WIFI接入互联网,以便将单片机采集到的数据发送至云平台。在编写程序之前,我们需要为模块烧录用于发送MQTT通讯的固件库。本系统使用的是(1471)ESP8266-AT_MQTT-1M固件库。
    • OLED显示模块:液晶显示屏主要用于提供环境内温度、湿度、光照强度和二氧化碳浓度等信息的实时显示。当系统启动并连接客户端后,OLED屏幕上显示提示信息表示系统已经启动,同时系统开始实时展示采集到的环境信息数值。
  4. 移动端MQTT通讯程序:使用微信小程序开源库mqtt.js客户端库,发送mqtt请求进行主题的发布和订阅。当客户端订阅主题后,开启消息监听,可以实时监听服务端发送的信息,无需手动调用接口请求数据。 使用mqtt.js客户端库发送消息的代码如下所示:
                    // sendCommond 发布事件
			toControl: function() {
				uni.$on('customEvent', this.testNum);
				wx.navigateTo({
					url: '../charts/controlData',
					success: function(res) {
						console.log('success', res);
					},
				});
			},
			testNum: function(data) {
				this.sendCommond(data);
			},
			// 与服务器建立连接
			connectWIFI: function() {
				let that = this;
				let clientOpt = aliyunOpt.getAliyunIotMqttClient({
					productKey: this.aliyunInfo.productKey,
					deviceName: this.aliyunInfo.deviceName,
					deviceSecret: this.aliyunInfo.deviceSecret,
					regionId: this.aliyunInfo.regionId,
					port: this.aliyunInfo.port,
				});
				let host = 'wxs://' + clientOpt.host;
				this.options.clientId = clientOpt.clientId;
				this.options.password = clientOpt.password;
				this.options.username = clientOpt.username;
				//访问服务器
				this.client = mqtt.connect(host, this.options);
				this.client.on('connect', function(connack) {
					wx.showToast({
						title: '连接成功'
					})
					console.log("连接成功");
					// 改变显示的状态
					// this.deviceStatus="系统正常,wifi已连接";
				})
				//接收消息监听
				this.client.on("message", function(topic, payload) {
					//message是一个16进制的字节流
					let dataFromALY = {};
					try {
						// 获取单片机传入的数据并进行存储
						dataFromALY = JSON.parse(payload);	
					} catch (error) {
						console.log(error);
					}
				})
				})
	}
  1. 移动端小程序实现:

本系统设计了一款微信小程序,方便用户实现环境监测。

  • 在小程序中可以观察到当前环境的数据趋势,同时在告警模块中会提示用户当前是否存在异常模块需要用户进行处理。
  • 当用户进入小程序,监测界面会提示设备连接状态,连接完成后可以展示实时测量的环境数据。
  • 系统中使用到了echarts图表库制作可视化图表。

image.png

系统架构图

image.png

本次课题研究的环境监测系统包括硬件设备采集、传输和后台软件控制两部分。

功能测试

编号****测试项****操作步骤****预期结果****实际结果****
1****WIFI连接模块打开WIFI,尝试连接设备小程序提示WIFI连接成功成功
2********温湿度检测模块室温下监测种植环境温湿度信息收集室内温度湿度与实际相符;OLED显示数据与小程序界面数据无出入,延迟小于5s成功
3****高温报警器模块提高环境温度。在高温情况下观察蜂鸣器状态和小程序预警情况温度过高时蜂鸣器发出预警蜂鸣。用户可以在小程序界面看到温度告警信息。点击小程序按钮或硬件的按键外设停止蜂鸣器警报成功 
4****二氧化碳浓度监测模块将系统置于二氧化碳浓度较高的环境,观察采集数据显示及风扇运作情况小风扇开始转动。当系统移动至正常环境,风扇停止转动成功
5****光照强度监测模块将系统放置在正常环境和过暗环境,观察其LED补光灯情况环境光强过低时LED开启,自动补光成功
6****自动灌溉模块将系统放置在土壤湿度过低的环境下,观察水泵运作情况水泵开始运作成功
7****液晶显示模块当更换环境时,观察OLED显示屏上的数据能否正常显示数据以0.5s的频率进行刷新,展示无异常成功

物联网云平台-双端调试

使用C++,AT固件指令进行云平台连接:

/*****************************************************
*函数名称:void ESP8266_Init(void)
*函数功能:ESP8266参数初始化
*入口参数:void
*出口参数:void
*****************************************************/
void ESP8266_Init(void)
{	
	esp8266_send_cmd("AT+RST","ready",500);
	esp8266_send_cmd("AT+CIOBAUD=115200","OK",50);
	esp8266_send_cmd("AT+CWMODE=3","OK",50);	//配置WiFi工作模式为Station模式:就是ESP8266模块去连接自己家的wifi,													//手机也连接自己家的wifi然后实现了手机和WiFi模块的通信,(自己家wifi相当于一个中介)
	//让Wifi模块重启的命令
//	esp8266_send_cmd("AT+RST","ready",500);
	esp8266_send_cmd("ATE0","OK",50);
	Delay_ms(1000);
	//让模块连接上自己的路由
	esp8266_send_cmd("AT+CWJAP=\"【账号】\",\"【密码】\"","WIFI GOT IP",300);
	printf("22222222222222222222");
	//AT+MQTTUSERCFG - 配置 MQTT 用户属性
	esp8266_send_cmd(MQTT_set,"OK",100);
	esp8266_send_cmd(MQTT_set,"OK",100);
	//配置 MQTT 客户端 ID
	esp8266_send_cmd(MQTT_Client,"OK",100);
	//连接指定 MQTT协议
	esp8266_send_cmd(MQTT_Pass,"OK",300);
}

//向ESP8266发送命令
//cmd:发送的命令字符串;ack:期待的应答结果,如果为空,则表示不需要等待应答;waittime:等待时间(单位:10ms)
//返回值:0,发送成功(得到了期待的应答结果);1,发送失败
u8 esp8266_send_cmd(char *cmd,char *ack,u16 waittime)
{
	u8 res=0; 
	USART2_RX_STA=0;
	u2_printf("%s\r\n",cmd);
//	printf("222:\r\n");USART
	if(ack&&waittime)		
	{
		while(--waittime)	
		{
			Delay_ms(10);
//			printf("333:%d\r\n",USART2_RX_STA&0X8000);
			if(USART2_RX_STA&0X8000)
			{
//				printf("333:%d\r\n",(const char*)USART2_RX_BUF);
				char *strx=0;
//				printf("???????%s\r\n",(const char*)USART2_RX_BUF);
				if(USART2_RX_STA&0X8000)		//接收到一次数据了
					
				{ 
					USART2_RX_BUF[USART2_RX_STA&0X7FFF]=0;//添加结束符
					//	readReceivedData();

					//    // 打印接收到的字符串
					//    printf("Received string: %s\n", receivedString);
//					printf("?%s\r\n",(const char*)USART2_RX_BUF);
					strx=strstr((const char*)USART2_RX_BUF,(const char*)ack);
				} 
//				printf("333:%c333%c1111111111111111\r\n",(const char*)USART2_RX_BUF,(const char*)ack);
				//	readReceivedData();

				//    // 打印接收到的字符串
				//    printf("Received string: %s\n", receivedString);
				   
				if((u8*)strx)
				{
//					printf("ack:%s\r\n",(u8*)ack);
					break;
				}
					USART2_RX_STA=0;
			}
		}
		if(waittime==0)res=1; 
	}
	return res;
} 
//ESP8266发送命令后,检测接收到的应答
//str:期待的应答结果
//返回值:0,没有得到期待的应答结果;其他,期待应答结果的位置(str的位置)

u8* esp8266_check_cmd(char *str)
{
	char *strx=0;
//	printf("???????%s\r\n",(const char*)USART2_RX_BUF);
	if(USART2_RX_STA&0X8000)		//接收到一次数据了
	{ 
		USART2_RX_BUF[USART2_RX_STA&0X7FFF]=0;//添加结束符
//		printf("?%s\r\n",(const char*)USART2_RX_BUF);
		strx=strstr((const char*)USART2_RX_BUF,(const char*)str);
	} 
//	memset(USART2_RX_BUF, 0, sizeof(USART2_RX_BUF)); 
	return (u8*)strx;
}

/*****************************************************
*函数名称:ESP8266_Send(char *property,int Data)
*函数功能:向云端发送数据
*入口参数:char *property,int Data (属性)(数据)
*出口参数:void
*****************************************************/
void ESP8266_Send(char *property,int Data,char *property1,int Data1,char *property2,int Data2,char *property3,int Data3,char *property4,int Data4)
{
	USART2_RX_STA=0;
//	OLED_ShowNum(3,11,99,2);
//	char json[100];  // 定义存储 JSON 字符串的缓冲区

// 构建 JSON 对象的键值对
//snprintf(json, sizeof(json), "{\"%s\": %d, \"%s\": %d}", property1, Data1, property2, Data2);
	u2_printf("AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":%d}\",1,0\r\n",post,property,Data);
	
	
//	u2_printf("AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":%d},{\\\"%s\\\":%d}\",1,0\r\n",post,property,Data,property1,Data1);
	
//	u2_printf("AT+MQTTPUB=0,\"%s\",\"%s\",1,0\r\n",post,json);
	
	
	
	
	Delay_ms(400);
	u2_printf("AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":%d}\",1,0\r\n",post,property1,Data1);
	Delay_ms(400);
	u2_printf("AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":%d}\",1,0\r\n",post,property2,Data2);
	Delay_ms(400);
	u2_printf("AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":%d}\",1,0\r\n",post,property3,Data3);
	Delay_ms(400);
	u2_printf("AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":%d}\",1,0\r\n",post,property4,Data4);
	Delay_ms(400);

}

/*****************************************************
*函数名称:ESP8266_Received(void)
*函数功能:云端接收数据
*入口参数:char *property,int Data (属性)(数据)
*出口参数:void
*****************************************************/
void ESP8266_Received(char *PRO)
{
	
	unsigned char *ret = 0;
	char *property = 0;
	unsigned char i;
	if(PRO == NULL);
	else if(USART2_RX_STA&0x8000)			//串口2接收完一帧数据
	{
		ret = USART2_RX_BUF;
		if(ret!=0)
		{
			property = strstr((const char *)ret,(const char *)PRO);
//			OLED_ShowString(4,1,(char *)ret);
			if(property!=NULL)
			{
				for(i=0;i<6;i++)
				{
					Property_Data[i] = *(property+13+i);
				}
				if(Property_Data[1] == '}'){
					for(i=0;i<6;i++)
					{
						Property_Data[i] = '0';
					}
				}
				USART2_RX_STA = 0;
			}
			else
			{
				USART2_RX_STA = 0;
			}
		}
		else
		{
			USART2_RX_STA = 0;	
		}
	}
}

当双端均连接至云平台时,登录阿里云物联网云平台,可以看到用户设备界面提示微信端及WIFI模块端都在线,表示双端都已经准备完成,可以进行通讯。

image.png 当单片机开始发送数据时,可以在物联网平台的日志服务一栏看到消息的接入和转发情况。单击某一条数据,可以看到该行为是阿里云平台向微信端进行数据转发,转发消息的内容是发送单片机采集到的环境温度具体数值。

image.png

未来展望:

(1)添加可读写的RFID门禁设备:由于RFID磁卡是固定录入程序中的,所以在对磁卡进行录入删除时候需要改变后台程序,这一操作比较麻烦。

(2)优化mqtt通讯方式:由于本系统中涉及到的设备较少,故在设计中使用到的mqtt通讯方式为一对一通讯。在日后为了能够一次性控制多个设备,可以使用发布订阅模式进行优化,发布者发布通知后所有的订阅者都能够接收命令并实施,有效提高管理效率。