(代码易懂实现)新stm32+esp8266/01s+dht11+mqtt onenet上报温湿度和远程控制——STM32代码实现篇

 

(代码完美实现)stm32 + 新版 onenet + mqtt物联网(保姆级教程)
地址:↓↓👇👇👇👇👇👇👇👇👇👇👇👇👇

(代码完美实现)stm32 + 新版 onenet + mqtt物联网(保姆级教程) https://blog.csdn.net/Wang2869902214/article/details/142501323

注                👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆

STM32代码实现

注:代码和文章内容创作不易,使用或分享代码时请标注来源

读者们也可以加入STM32链接OneNET的群聊 自助获取资料,交流一些遇到的问题也可以互相探讨:        自助获取资料  Q:983362248

最终效果

第一个视频:详细展示STM32上电后初始化ESP8266链接OneNET MQTT设备,再进行订阅操作,发送DHT11温湿度数据和接收数据

(已验证,完美实现)stm32+esp8266连接新版onenet上报与下发数据

微信小程序 :温湿度数据上报,数据下发控制单片机

微信小程序stm32+esp8266+onenet显示温湿度和控制单片机

 

读者们也可以加入STM32链接OneNET的群聊 自助获取资料,交流一些遇到的问题也可以互相探讨:           自助获取资料     Q:983362248 

开启本章节需要完成下方的前置任务:
点击跳转:

 

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总 https://blog.csdn.net/Wang2869902214/article/details/142501323

目标

1.连接OneNET:STM32使用串口与ESP8266/01s连接发送AT指令连接上OneNET MQTT设备

2.数据上报OneNET:将STM32采集的数据上传到OneNET

3.接收OneNET下发数据:STM32接收OneNET下发的指令并进行对应的操作

 

代码主要逻辑

 

无线模块状态结构体
 

typedef struct {
    uint8_t receiveDataFlag;				//接收数据标志位
    uint8_t sendDataFlag;					//发送数据标志位
	uint8_t wirelessInitFlag;				//无线模块初始化完成标志位
    uint16_t wirelessInitErrorCode;			//无线模块初始化错误代码
}Wireless_TypeDef;

extern Wireless_TypeDef WirelessStatus;


无线模块连接OneNET MQTT设备需要发送的AT指令

全部使用宏定义,方便其他的用户直接修改设备等相关信息即可直接使用

#include "wireless.h"

/**
  *CSDN文章:(代码完美实现)stm32 + 新版 onenet + mqtt物联网(保姆级教程)
  *CSDN博主:送外卖的CV工程师
  *版权声明:本文件为博主原创代码,遵循 CC 4.0 BY-SA 版权协议,转载使用请附上原代码出处和本声明。
  *本文博客链接:https://blog.csdn.net/Wang2869902214/article/details/142501323
  *gitee地址:	 https://gitee.com/Wang2869902214/stm32-onenet-mqtt.git
  *更新日期:2024-9-25
  *QQ群聊:STM连接onenet交流群983362248
  *所需资料全部可以在群里下载
  */
  
  
  
/*********代码中包含大量的【调试】语句,可以打开或者屏蔽查看运行状态*********/


/*
	只需更新这些信息
*/
#define ControlEnable 0 //是否使用OneNET下发数据,	【如需使用则置 1 并添加Keil.Jansson库 ps:可以进群查看】
#define JsonSendEnable 0    //是否使用json组装数据,需要在启动文件中设置更大的堆内存 【如需使用则置 1 并添加Keil.Jansson库 ps:可以进群查看】


Wireless_TypeDef WirelessStatus = {0, 0, 0, 0};

#define WIFI_SSID "207"							//WIFI用户名

#define WIFI_PASSWORD "12345678"				//WIFI密码

#define ONENET_MQTT_PRODUCT_ID "YqRZ5hrM6p"		//OneNET MQTT产品ID

#define ONENET_MQTT_DEVICE_NAME "CSDN"			//OneNET MQTT设备名称

#define ONENET_MQTT_TOKEN "version=2018-10-31&res=products%2FYqRZ5hrM6p%2Fdevices%2FCSDN&et=2028715245&method=md5&sign=G4I0xqIYmYUtCdTTo2t%2FqQ%3D%3D"		//token
#if JsonSendEnable
	#include "jansson.h"
#endif
#if ControlEnable
	#include "jansson.h"
	#define ONENET_MQTT_CMD_IDENTIFIER "cmd"			//OneNET MQTT命令标识符
#endif

const char *floatIdentifiers[] = {"temp", "humi"};	//修改自己设备中的属性标识符
float floatValues[] = {0, 0};						//对应上方数组下标的数据
const char *stringIdentifiers[] = {0};					
const char *stringValues[] = {0};	

/*

			只需更新这些信息

*/
#define WIRELESS_WIFI_INFO "AT+CWJAP=\"" WIFI_SSID "\",\"" WIFI_PASSWORD "\"\r\n"
#define WIRELESS_RECEIVE_CMD "\"" ONENET_MQTT_CMD_IDENTIFIER "\":"

#define ONENET_MQTT_SERVER_INFO "AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1\r\n"
#define ONENET_MQTT_USERCFG_INFO "AT+MQTTUSERCFG=0,1,\"" ONENET_MQTT_DEVICE_NAME "\",\"" ONENET_MQTT_PRODUCT_ID "\",\"" ONENET_MQTT_TOKEN "\",0,0,\"\"\r\n"
#define ONENET_MQTT_SET_TOPIC "AT+MQTTSUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set\",0\r\n"

#if JsonSendEnable
	#if ControlEnable
		#define ONENET_MQTT_SET_MQTTPUBRAW "AT+MQTTPUBRAW=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set_reply\""
	#endif
	#define ONENET_MQTT_PUB_MQTTPUBRAW "AT+MQTTPUBRAW=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post\""
#else
	#if ControlEnable
		#define ONENET_MQTT_PUB_SET "AT+MQTTPUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set_reply\""
	#endif
	#define ONENET_MQTT_PUBTOPIC "AT+MQTTPUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post\""
#endif

1.初始化

由前置任务可以知道,每次发送AT指令后,无线模块总是会回应我们,例如“OK”又或者其他的内容,这里初始化发送的每条AT指令都必须要得到无线模块的回应,这样防止无线模块未按照预期模式运行,如果初始化出现错误时,需要即使去处理对应的问题

这里如果不了解为什么要这样写代码的逻辑,放个传送门:
物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——ESP8266/01s AT指令连接OneNET MQTT篇
 

/**  
  * @简要  无线模块初始化函数
  * @参数  无
  * @注意事项 	如果函数初始化失败,会进入错误函数
  * @返回值 无  
  */
void Wireless_Init(void)
{
	const uint8_t sendDataCount = 10;
    printf("\r\nStart MQTT service\r\n");

	printf("1. AT+RST\r\n");    										//需要延迟最少500ms,否则下一句指令发送无效
	if(Wireless_Send_Command("AT+RST\r\n\r\n", "", sendDataCount) == 1) 		WirelessStatus.wirelessInitErrorCode |= 1 << 0;
	HAL_Delay(500);
	
	printf("2. ATE0\r\n");												//关闭回显
	if(Wireless_Send_Command("ATE0\r\n", "OK", sendDataCount) == 1)				WirelessStatus.wirelessInitErrorCode |= 1 << 1;
	HAL_Delay(100);
	
	printf("3. AT+CWAUTOCONN=0\r\n");									//上电不自动连接 AP
	if(Wireless_Send_Command("AT+CWAUTOCONN=0\r\n", "OK", sendDataCount) == 1) 	WirelessStatus.wirelessInitErrorCode |= 1 << 2;
	HAL_Delay(100);
	
	printf("4. AT+CWMODE=1\r\n");										//设置 Station 模式
	if(Wireless_Send_Command("AT+CWMODE=1\r\n", "OK", sendDataCount) == 1)		WirelessStatus.wirelessInitErrorCode |= 1 << 3;
	HAL_Delay(100);
	
	printf("5. AT+CWDHCP=1,0\r\n");										//启用DHCP
	if(Wireless_Send_Command("AT+CWDHCP=1,0\r\n", "OK", sendDataCount) == 1)	WirelessStatus.wirelessInitErrorCode |= 1 << 4;
	HAL_Delay(100);
	
	printf("6. AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI_SSID, WIFI_PASSWORD);			//连接AP
	if(Wireless_Send_Command(WIRELESS_WIFI_INFO, "GOT IP", 8) == 1) 			WirelessStatus.wirelessInitErrorCode |= 1 << 5;
	HAL_Delay(100);

	printf("7. ESP8266_USERCFG_INFO=%s\r\n", ONENET_MQTT_USERCFG_INFO); 		//设置 MQTT 用户属性
	if(Wireless_Send_Command(ONENET_MQTT_USERCFG_INFO, "OK", sendDataCount) == 1)	WirelessStatus.wirelessInitErrorCode |= 1 << 6;
	HAL_Delay(100);
	
	printf("8. ESP8266_ONENET_INFO=%s\r\n",ONENET_MQTT_SERVER_INFO);   				//连接 MQTT Broker
	if(Wireless_Send_Command(ONENET_MQTT_SERVER_INFO, "OK", sendDataCount) == 1) 				WirelessStatus.wirelessInitErrorCode |= 1 << 7;
	HAL_Delay(100);
	
	printf("9. SET_TOPIC=%s\r\n",ONENET_MQTT_SET_TOPIC);							//设置 MQTT 用户属性
	if(Wireless_Send_Command(ONENET_MQTT_SET_TOPIC, "OK", sendDataCount) == 1) 				WirelessStatus.wirelessInitErrorCode |= 1 << 8;
	HAL_Delay(100);

	if(WirelessStatus.wirelessInitErrorCode == 0) 
	{
		WirelessStatus.wirelessInitFlag = 1;
		printf("MQTT service started successfully\r\n");
	}
	else 
	{
		WirelessStatus.wirelessInitFlag = 0;
		printf("MQTT service failed to start,ERROR CODE:%X\r\n", WirelessStatus.wirelessInitErrorCode);
		Wireless_Error_Handler(WirelessStatus.wirelessInitErrorCode);  //错误处理函数
	}
		
}

 在上方代码中:

if(Wireless_Send_Command("AT+RST\r\n\r\n", "", sendDataCount) == 1)

这个函数原型:
 

/**  
  * @简要  无线模块发送指令并且等待回应数据
  * @参数  cmd:需要发送的指令字符串地址
  * @参数  res:需要回应的指令
  * @参数  sendCount:最大发送指令次数
  * @返回值 无  
  */
uint8_t Wireless_Send_Command(char *cmd, char *res, uint8_t sendCount)
{
	uint8_t status = 1;
	while(sendCount--)
	{
		Wireless_Usart_Send(cmd);
		//printf("cmd :%s",cmd);

		if(WirelessStatus.receiveDataFlag == 1)
		{
			if(strstr((const char *)WirelessRx.RxBuffer, res) != NULL)		//若找到关键字
			{		
				status = 0;
				WirelessStatus.receiveDataFlag = 0;
				Wireless_Buffer_Clear();
				break;
			}
			Wireless_Buffer_Clear();
			WirelessStatus.receiveDataFlag = 0;
		}
		HAL_Delay(200);
	}
	return status;
}

这里还需要修改明白一个底层的发送函数:

需要将里面的串口发送修改为自己的
这里面的DMA配置也有文章可以参考:
STM32基于HAL库使用串口+DMA 不定长接收数据 学习记录

/**  
  * @简要  用来适配无线模块发送字符的函数
  * @参数  cmd: 传入需要发送字符指针
  * @返回值 无  
  */
void Wireless_Usart_Send(char *cmd)
{
    if (cmd == NULL) return; // 确保指针不为空

    // 发送字符串
    // 参数依次为:USART句柄指针、发送数据缓冲区指针、数据字节长度
	HAL_UART_Transmit_DMA(&huart2, (uint8_t *)cmd, strlen(cmd));
}

串口接收采用了DMA空闲接收回调函数处理(可以参考上方连接配置):

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	
    if (huart->Instance == USART1)
    {
        Uart1Rx.RxDataCnt = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
		if(Uart1Rx.RxBuffer[Uart1Rx.RxDataCnt - 2] == '\r' && Uart1Rx.RxBuffer[Uart1Rx.RxDataCnt - 1] == '\n')
		{
			HAL_UART_Transmit_DMA(&huart1, Uart1Rx.RxBuffer, Uart1Rx.RxDataCnt);
		}
    }
	if (huart->Instance == USART2)
    {
        WirelessRx.RxDataCnt = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
		if(WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 2] == '\r' && WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 1] == '\n')
		{
			WirelessStatus.receiveDataFlag = 1;
			if(WirelessStatus.wirelessInitFlag == 1) printf("Receive\r\n");
			//HAL_UART_Transmit_DMA(&huart1, WirelessRx.RxBuffer, WirelessRx.RxDataCnt);
		}
    }
}


 

2.上报数据

这里开启了一个定时器,用一个LED灯去显示当前wifi模块状态,同时也在后台计时,准备设置发送数据标志位

这个定时器的存在只是为了加强用户交互,显示当前WiFi模块的工作状态,例如连接OneNET初始化失败,正在连接,成功连接等等,如果不需要的话完全可以删除,然后发送计时可以在主函数用一个变量累加到一定数量时触发发送

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static uint16_t countTimer_SendData_OneNET = 0;
	static uint16_t countTimer_LED_Toggle = 0;
    if (htim == (&htim1))
    {
       if(countTimer_SendData_OneNET++ > 4000)
	   {
		   countTimer_SendData_OneNET = 0;
		   WirelessStatus.sendDataFlag = 1;
	   }
	   
	   if(countTimer_LED_Toggle++ >= 1000)
	   {
		   countTimer_LED_Toggle = 0;
			if(WirelessStatus.wirelessInitErrorCode == 0 && WirelessStatus.wirelessInitFlag == 0)
				HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); //正在初始化
			else if(WirelessStatus.wirelessInitErrorCode == 0 && WirelessStatus.wirelessInitFlag == 1)
				HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);		//初始化成功
			else if(WirelessStatus.wirelessInitErrorCode != 0)
				HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);	//初始化失败

	   }
    }
}

这里是整个无线模块的运行函数

/**  
  * @简要  无线模块运行函数	
  * @参数  无
  * @注意事项	一直轮询,可以设置为定时触发,但是回应下发的数据会变慢
  * @返回值 无  
  */
void Wireless_Loop(void)
{
	if(WirelessStatus.receiveDataFlag == 1)
	{
		WirelessStatus.receiveDataFlag = 0;	//清除接收数据标志位
		Wireless_Receive_Command_Respond((char *)WirelessRx.RxBuffer);		//接收到指令后进行回应操作
		Wireless_Buffer_Clear();     //执行完接收数据的所有操作后再清除缓存
	}
	else if(WirelessStatus.sendDataFlag == 1 && WirelessStatus.wirelessInitFlag == 1) 
	{
		WirelessStatus.sendDataFlag = 0;		//清除发送数据标志位
		Wireless_Publish_Data(2, floatIdentifiers, floatValues, 0, stringIdentifiers, stringValues);	//上报数据
	}
}

这里代码是放在主函数的while循环中一直执行

在这个代码中可以看到,上报数据和接收数据是互斥的,接收数据处理优先级比发送大,这里这样互斥处理的原因是:如果在wifi模块正在接收OneNET下发的数据时,又正好发送数据,无线模块接收的数据通过串口发送到STM32中,数据中可能会连带两种状态的数据(1:OneNET下发的数据,2:STM32上报数据时发送的AT指令的应答)这样解析接收的数据时出错率会大大提升

 

上报数据的核心代码:

这里代码逻辑不清楚可以参考下方两个文章:

这两个文章中有说明需要发送的数据json格式和数据类型

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——ESP8266/01s AT指令连接OneNET MQTT篇

OneNET官方文档:MQTT协议接入 最佳实践

手动拼装json格式数据

/**  
	  * @简要  无线模块上报数据	
	  * @参数  floatCount:	需要发送的float数据个数
	  * @参数  *floatIdentifiers[]:float标识符数组
	  * @参数  floatValues[]:float数据数组
	  * @参数  stringCount:需要发送的string数据个数
	  * @参数  *stringIdentifiers[]:string标识符数组
	  * @参数  const char *stringValues[]:string数据数组
	  * @注意事项	这里需要二重转义:第一次转义字符是软件上字符串要求格式,第二次是因为AT指令发送字符串,
					需要再转义一次,故不方便使用json组装数据
	  * @返回值 无  
	  */
	void Wireless_Publish_Data(unsigned char floatCount, const char *floatIdentifiers[], float floatValues[], unsigned char stringCount, const char *stringIdentifiers[], const char *stringValues[]) 
	{  
		// 初始化缓冲区位置  
		size_t bufferPos = 0;  
		const uint8_t BUFFER_SIZE = 255;
		char globalBuffer[BUFFER_SIZE];
		// 拼接主题和JSON开头  
		bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "%s,\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{", PUBTOPIC);  
		  
		// 处理浮点数数据  
		for (unsigned char i = 0; i < floatCount; ++i) {  
			bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  
								  "\\\"%s\\\":{\\\"value\\\":%.2f\\}%s",  
								  floatIdentifiers[i], floatValues[i],  
								  (i < floatCount - 1 || stringCount > 0) ? "\\," : "");  
		}  
		  
		// 处理字符串数据  
		for (unsigned char i = 0; i < stringCount; ++i) {  
			bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  
								  "\\\"%s\\\":{\\\"value\\\":\\\"%s\\\"\\}%s",  
								  stringIdentifiers[i], stringValues[i],  
								  (i < stringCount - 1) ? "\\," : "");  
		}  
		  
		// 拼接JSON结尾  
		bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "}}\",0,0\r\n");  
		  
		// 确保我们没有超出缓冲区  
		if (bufferPos >= BUFFER_SIZE) {  
			// 处理错误,例如通过日志记录  
			return;  
		}  
		Wireless_Send_Command(globalBuffer,"OK",1);
	}

使用keil.Jansson库组装

需要注意使用jansson需要调大启动文件中的堆空间大小(方法可参考下方链接中)

这里放个keil.Jansson使用方法:

STM32在Keil5中利用Jansson库处理和组装JSON数据【详细版】

	/**  
	  * @简要  无线模块上报数据	
	  * @参数  floatCount:	需要发送的float数据个数
	  * @参数  *floatIdentifiers[]:float标识符数组
	  * @参数  floatValues[]:float数据数组
	  * @参数  stringCount:需要发送的string数据个数
	  * @参数  *stringIdentifiers[]:string标识符数组
	  * @参数  const char *stringValues[]:string数据数组
	  * @注意事项	这里使用json组装,需要动态分配大量的内容,需要将堆大小设置最小0x600
	  * @返回值 无  
	  */
	void Wireless_Publish_Data(unsigned char floatCount, const char *floatIdentifiers[], float floatValues[], unsigned char stringCount, const char *stringIdentifiers[], const char *stringValues[]) {  
		json_t *root, *id_obj, *version_obj, *params_obj;  
		char *json_str;  
		size_t json_str_len;  
	  
		// 创建JSON对象  
		root = json_object();  
		id_obj = json_string("123");  
		version_obj = json_string("1.0");  
		params_obj = json_object();  
	  
		// 添加id和version到root对象  
		json_object_set_new(root, "id", id_obj);  
		json_object_set_new(root, "version", version_obj);  
		  
		// 添加浮点数数据到params对象  
		for (unsigned char i = 0; i < floatCount; ++i) {  
			json_t *float_val_obj = json_object();  
			json_object_set_new(float_val_obj, "value", json_real(floatValues[i]));  
			json_object_set_new(params_obj, floatIdentifiers[i], float_val_obj);  
		}  
	  
		// 添加字符串数据到params对象  
		for (unsigned char i = 0; i < stringCount; ++i) {  
			json_t *string_val_obj = json_object();  
			json_object_set_new(string_val_obj, "value", json_string(stringValues[i]));  
			json_object_set_new(params_obj, stringIdentifiers[i], string_val_obj);  
		}  
	  
		// 将params对象添加到root对象  
		json_object_set_new(root, "params", params_obj);  
	  
		// 将JSON对象转换为字符串  
		json_str = json_dumps(root, JSON_INDENT(0) | JSON_REAL_PRECISION(4));  
		json_str_len = strlen(json_str);  
	  
		// printf("json_str = %s\r\n",json_str);
		
		// 为MQTT发布准备最终的消息字符串  
		char globalBuffer[256];  // 确保缓冲区足够大  
		snprintf(globalBuffer, sizeof(globalBuffer), "%s,%d,0,0\r\n", ONENET_MQTT_PUB_MQTTPUBRAW, json_str_len + 2);  //添加\r\n的长度
		
		// 发送命令  
		Wireless_Send_Command(globalBuffer, "OK", 1);  
		strcat(json_str, "\r\n");
		printf("%s%s",globalBuffer, json_str);
		Wireless_Send_Command(json_str, "+MQTTPUB:OK", 1);  
		// 释放JSON对象  
		json_decref(root);  
		free(json_str);  // 注意:json_dumps分配的内存需要手动释放  
	}  

该函数使用示例:

Wireless_Publish_Data(2, floatIdentifiers, floatValues, 0, stringIdentifiers, stringValues);	

//代表发送两个浮点数据,0个字符串数据

 

 

3.接收OneNET下发的指令数据

这里下发数据部分可以查看该文章:

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——ESP8266/01s AT指令连接OneNET MQTT篇

在OneNET的API调试:物模型设置:设备属性设置,填入对应的json格式数据和参数
如果上文宏定义中的命令标识符是什么,那么OneNET API调试中就应该填写对应的

这是串口接收函数

	if (huart->Instance == USART2)
    {
        WirelessRx.RxDataCnt = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
		if(WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 2] == '\r' && WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 1] == '\n')
		{
			WirelessStatus.receiveDataFlag = 1;
			if(WirelessStatus.wirelessInitFlag == 1) printf("Receive\r\n");
			//HAL_UART_Transmit_DMA(&huart1, WirelessRx.RxBuffer, WirelessRx.RxDataCnt);
		}
    }

这里可以看出,如果wifi模块接收到OneNET下发的数据后,接收标志位会被置1,这里不影响无线模块函数初始化

这里是整个无线模块的运行函数

/**  
  * @简要  无线模块运行函数	
  * @作者 [CSDN:送外卖的CV工程师,博客地址:https://blog.csdn.net/Wang2869902214/article/details/142501323] 
  * @参数  无
  * @注意事项	一直轮询,可以设置为定时触发,但是回应下发的数据会变慢
  * @返回值 无  
  */
void Wireless_Loop(void)
{
	if(WirelessStatus.receiveDataFlag == 1)
	{
		WirelessStatus.receiveDataFlag = 0;	//清除接收数据标志位
		Wireless_Receive_Command_Respond((char *)WirelessRx.RxBuffer);		//接收到指令后进行回应操作
		Wireless_Buffer_Clear();     //执行完接收数据的所有操作后再清除缓存
	}
	else if(WirelessStatus.sendDataFlag == 1 && WirelessStatus.wirelessInitFlag == 1) 
	{
		DHT11_Read_Data();					//读取DHT11的数据
		floatValues[0] = DHT11_Get_Temp(); printf("tmep:%.1f\r\n",floatValues[0]);	//赋值
		floatValues[1] = DHT11_Get_Humi(); printf("humi:%.1f\r\n",floatValues[1]);	//赋值
		
		WirelessStatus.sendDataFlag = 0;		//清除发送数据标志位
		Wireless_Publish_Data(2, floatIdentifiers, floatValues, 0, stringIdentifiers, stringValues);	//上报数据
	}
}

这里代码是放在主函数的while循环中一直执行

可以看出如果接受标志位被置1后,会执行这个函数

/**  
  * @简要  无线模块接收到下发的数据后进行回应	
  * @参数  str:无线模块接收到的数据
  * @注意事项	这里判断下发的指令中是否包含关键字: "+MQTTSUBRECV"
				里面的printf可以打印接收到的数据和转换后的数据
  * @返回值 无  
  */
void Wireless_Receive_Command_Respond(const char * str)
{
	char *jsonData;
	char id[5] = {0};
	int command = 0;
		
	if(strstr(str, "+MQTTSUBRECV") != NULL)			//利用AT指令的特性去判断接受的内容是否包含关键字以验证数据来源
	  {

			//printf("str = %s",str);						//【调试】
			jsonData = strchr((const char*)str, '{');		//提取json格式数据,通常是以 “{”开始    以“}”结尾
			//printf("jsonData = %s",jsonData);				//【调试】
			Remove_Trailing_Crlf(jsonData);					//这里需要去除末尾的换行回车符
			Wireless_Extract_Receive_Command(jsonData, id, &command);	//这里使用keil.Jansson去提取下发数据的id和command
			Wireless_Receive_Ack_CloudPlatform((char *)str, id);		//这里回应OneNET,表示接收到了数据
			Wireless_Receive_Command_Control(command);		//这里对接收到的command执行对应的操作
			
			
	  }
}

这个函数会先判断数据来源,再利用Keil.Jansson库解析数据,这个库的使用方法:

STM32在Keil5中利用Jansson库处理和组装JSON数据【详细版】

 

这个函数就是通过OneNET下发的数据去执行对应的操作


/**  
  * @简要  执行无线模块接收到下发指令对应的操作	
  * @参数  cmdValue:提取后的指令数据
  * @注意事项	这里的指令可以更改为比int更大类型,需要在提取Wireless_Extract_Receive_Command函数中修改提取的数据大小
  * @返回值 无  
  */
void Wireless_Receive_Command_Control(int cmdValue)
{
	//1000 1 0 :1000灯 2 代表2号灯 0代表灭 
	if(cmdValue == 10021)  	 HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin,GPIO_PIN_SET);
	else if(cmdValue == 10020)  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin,GPIO_PIN_RESET); 
	
	if(cmdValue == 10031)  	 HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin,GPIO_PIN_SET);
	else if(cmdValue == 10030)  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin,GPIO_PIN_RESET); 
}

 

代码下载

文章和代码创作不易,为了防止代码被不当使用或者其他人员下载代码后收费,需要源码或者相关资料的读者可以在评论区留下邮箱联系方式 或者 加入下方的交流群自助下载资料,博主看到了会第一时间发送噢!

看到这里的哥哥姐姐们,如果感觉此文章对你们有帮助的话,麻烦点个赞,支持一下!

补充

后续有什么内容也会及时在交流群里补充

读者们也可以加入STM32链接OneNET的群聊,交流一些遇到的问题也可以互相探讨:

自助获取资料,Q:983362248

 

 

 

<think>嗯,用户让我介绍一个智能楼道安全系统,包含实时监控、智能分析、紧急预警、自动灭火信息发布这几个功能。首先,我需要理解每个部分的具体需求。实时监控部分涉及多种传感器,比如烟雾、温度红外传感器,可能还需要夜视功能。这部分需要解释传感器如何工作,以及如何集成到嵌入式系统中。然后,智能分析需要处理采集的数据,用算法判断异常,比如烟雾超标或外人闯入。这里可能需要提到数据处理算法预设的逻辑。 紧急预警部分包括声光报警发送信息到手机消防中心。这里要说明触发报警的条件通知机制,可能需要涉及网络模块的选择,比如Wi-Fi或4G。自动灭火部分需要模拟喷淋系统,可能需要描述启动条件执行机构,比如电磁阀控制。信息发布方面,用户提到手机APP查看实时数据云平台日志,这里需要考虑数据上传到云端的实现方式,以及APP的设计。 接下来,我需要确保每个部分的技术细节准确,比如传感器的型号选择,例如MQ-2烟雾传感器DHT11温度传感器。红外传感器可能需要说明其工作原理,比如热释电红外传感器。夜视功能可能需要用到红外摄像头,补充可见光不足的情况。 在智能分析部分,需要提到微控制器如何处理数据,比如使用阈值比较或机器学习模型,但考虑到嵌入式系统的资源限制,可能更倾向于简单的阈值判断。紧急预警的联网功能可能需要使用SIM模块或Wi-Fi模块,比如ESP8266,来发送短信或推送通知。 自动灭火的喷淋系统模拟,可能需要一个水泵或电磁阀的控制电路,当检测到火灾时触发。这里要强调安全性,比如防止误触发,可能需要双重验证,比如同时检测温度烟雾才启动。 信息发布部分,云平台可以选择阿里云或AWS,数据存储实时显示需要考虑数据API的设计。手机APP可能需要开发一个简单的界面,显示传感器数据报警状态。 需要检查是否有遗漏的功能点,比如用户提到的所有五个部分是否都被覆盖。同时,要确保语言通俗易懂,避免过于专业的术语,但又要保持技术准确性。可能还需要考虑系统的可靠性实时性,比如传感器的响应时间,系统的稳定性。 最后,组织内容结构,分点详细说明每个模块,从硬件到软件,再到工作流程,确保用户能够逐步理解整个系统的工作原理实现方式。还要注意使用正确的中文术语,避免翻译腔,保持自然流畅。</think>我将按照您提出的5个功能模块,分步骤说明该智能楼道安全系统的设计与实现: 一、实时监控模块 $$硬件组成 = \begin{cases} MQ-2烟雾传感器 & 检测CO/CH4浓度\\ DHT11温湿度传感器 & 温度检测范围0-50℃\\ HC-SR501热释电红外 & 探测距离7米(夜视用940nm红外补光)\\ OV2640摄像头 & 支持1080P@30fps带IR-CUT昼夜切换 \end{cases}$$ ▶ 工作流程:所有传感器通过I2C总线连接STM32F407主控,采样频率设置为: $$f_{s}=\frac{1}{T_s}=10Hz \quad (T_s=100ms)$$ 二、智能分析模块 1. 数据预处理: - 滑动窗口滤波:$y[n] = \frac{1}{N}\sum_{k=0}^{N-1}x[n-k]$(N=5) - 特征提取:温度变化率$\Delta T = \frac{T_{current}-T_{prev}}{\Delta t}$ 2. 安全算法: ```python if (smoke > 1000ppm) or (ΔT > 3℃/min) : fire_risk = True elif PIR_detect & (not RFID_authorized): intruder_alert = True ``` 三、紧急预警模块 $$报警响应时间 \leq 300ms$$ - 声光报警电路:采用SGM8557运放驱动120dB蜂鸣器 + 3W LED爆闪 - 短信报警:SIM800C模块通过AT指令发送: ``` AT+CMGS="13800138000"\r > ALERT: Fire detected at Staircase B2!\r ``` 四、自动灭火模块 $$\text{启动条件} = \begin{cases} T > 70℃ & \text{且}\\ 烟雾浓度 > 1500ppm & \text{持续10s} \end{cases}$$ ▶ 喷淋系统模拟: ```c void activate_sprinkler(){ HAL_GPIO_Write(GPIOB, SOLENOID_PIN, HIGH); // 打开电磁阀 HAL_Delay(5000); // 持续喷水5秒 HAL_GPIO_Write(GPIOB, SOLENOID_PIN, LOW); } ``` 五、信息发布系统 $$数据流:传感器 \xrightarrow{SPI} MCU \xrightarrow{MQTT} 阿里云IoT \xrightarrow{API} APP$$ - 云平台数据结构: ```json { "timestamp": "2023-08-20T15:30:00Z", "temperature": 25.6, "smoke": 432, "PIR_status": 0, "GPS": "31.2304°N,121.4737°E" } ``` - APP功能:通过WebSocket实现实时数据刷,刷率1Hz 系统特点: 1. 低功耗设计:采用STM32的Stop Mode,待机功耗<1mA 2. 双模通信:同时支持4G(中国移动OneNETLoRa无线传输 3. 防火设计:所有电路板涂覆三防漆,满足IP65防护等级 实际部署建议: - 每层楼安装2组传感器节点(间距≤15米) - 灭火喷头覆盖半径按$r = \sqrt{\frac{Q}{K\sqrt{P}}}$计算(Q流量系数,K材料系数) - 定期校准:温度传感器每6个月用黑体炉校准,烟雾传感器用标准气体标定 需要进一步讨论的工程问题包括:防误报算法优化、多节点组网策略、以及电池供电时的能量管理方案。
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

送外卖的CV工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值