基于stm32和esp8266的智能家居设计,连接onenet云平台

本文所需硬件:STM32F103C8T6、LED、ESP01s、DHT11、面包板、杜邦线,实现功能:温湿度上传和远程开关灯的基本功能。使用的是AT指令MQTT版本固件的ESP01s,基于此固件版本进行说明。

其他说明(本文主要介绍代码的实现,具体全过程会给附上视频链接)

onenet云平台的创建和AT固件烧录的详细内容请看这个视频⬇⬇⬇⬇⬇⬇

记录onenet平台 mqtt产品和设备信息_哔哩哔哩_bilibili

DHT11驱动代码参考⬇⬇⬇⬇⬇⬇

【STM32】驱动DHT11(CubeMX配置)(HAL库)_dht11驱动代码-优快云博客

成果演示

智能家居

主要的AT指令

AT指令(Attention Command)是一种基于文本的交互协议,用于通过串口(如UART)控制模块执行特定操作(如连接Wi-Fi、发送数据等)。它是模块与主控设备(如MCU、PC)通信的桥梁,具有简单、通用的特点。

AT

意义:这个指令是用来测试串口通信是否畅通的最基本指令。发送AT应该返回OK,代表通信没有问题。
作用:验证ESP8266-01s模块已经正确连接并且准备接收更多的AT指令。


AT+CWMODE=1

意义:该指令用于设置ESP8266的Wi-Fi工作模式。CWMODE=1将模块设置为Station模式(STA模式),这意味着模块可以连到一个已存在的Wi-Fi网络。
作用:配置ESP8266作为Wi-Fi客户端,让其可以连接到无线路由器。


AT+CWDHCP=1,1

意义:此指令用于设置DHCP(动态主机配置协议)。CWDHCP=1,1的设置使ESP8266在Station模式下启用DHCP客户端,这样它可以自动从网络路由器获取IP地址。
作用:确保ESP8266在连接到Wi-Fi网络时能够自动获得IP地址。


AT+CWJAP="wifi名称","wifi密码"

意义:此指令用于连接到一个Wi-Fi热点。
作用:使ESP8266连接到指定的Wi-Fi热点。

AT+MQTTUSERCFG=0,1,"设备名称","产品ID","token",0,0,""

意义:这个指令用于配置MQTT客户端的用户参数。参数0指的是客户端ID,1为MQTT版本,最后的两个0表示是否清理会话和是否启用遗嘱消息。
作用:设置了客户端的认证信息和其他MQTT连接选项,这对于与MQTT代理进行通信是必要的。


AT+MQTTCONN=0,"mqtts.heclouds.com",1883,1

意义:此指令用于发起到MQTT服务器的连接。参数0是客户端ID,"mqtts.heclouds.com"是MQTT服务器地址,1883是服务器端口号,最后的1表示启用清理会话。
作用:根据提供的服务器详情和客户端配置信息,指令会尝试建立MQTT连接。


AT+MQTTSUB=0,"$sys/产品ID/设备名称/thing/property/post/reply",0

意义:指令用于订阅MQTT主题。0是客户端ID,中间的内容是是MQTT主题,最后的0是请求的服务质量(QoS)等级。
作用:向MQTT服务器表明客户端想要接收与该主题相关的消息。


AT+MQTTPUB=0,"$sys/产品ID/设备名称/thing/property/post","{\"id\":\"123\"\,\"params\":{\"temp\":{\"value\":23.6\}}}",0,0

意义:通过此指令将消息发布到MQTT主题。0是客户端ID,"$sys/产品ID/设备名称/thing/property/post"是要发布消息的主题。紧接着的是要发布的消息内容,temp是你设置的变量名,23.6是发送数值,0,0分别表示消息的QoS等级和是否保留消息。
作用:发送一条消息到MQTT服务器,服务器则将该消息转发给订阅了对应主题的所有客户端。


AT+MQTTSUB=0,"$sys/产品ID/设备名称/thing/property/set",0

意义:该指令用于指示ESP8266模块订阅一个指定的MQTT主题。
作用:使ESP8266模块能够监听并接收到任何发送到这个主题的消息,通常这些消息是针对设备设置或控制指令。

依次发送以上AT指令就可以实现esp8266连接onenet云平台,可以先使用串口助手测试一下能不能正常的连接(如果不可以,仔细检查一下配置,有的AT指令很长,容易出错),如果可以正常连接再进行下一步,使用stm32连接esp8266上传云端数据。

cubemx的配置

主要就是打开串口1(调试串口)、串口2(发送AT指令)、定时器(每隔一段时间上传一次数据,降低功耗)、配置DHT11的GPIO引脚和控制led灯的GPIO引脚。还有一些基本配置我就不多说了(开启时钟、配置时钟树、debug设置)。

接线图

这里最好用一个稳压模块,因为esp01s模块对电压要求特别高,刚开始博主因为没有稳压模块,一直找不出错误,很恼火。还有就是esp01s和stm32的TX和RX引脚别接错,一定是RX接TX。

代码部分

主函数

主函数很简单,就是查看各种标志位,做出响应。

还有一些cubemx自动生成的初始化配置我没有列出来,只要配置没问题就肯定没问题。

	ONENET_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		//定时器设置时间到,设置标志位,向云端传输数据
		if(ledflag==1){
			
			DHT_Read();						//获取温湿度
			ledflag =0;						//标志位清零
			cntnum=Data[0];				//获取湿度值
			ESP_Senddata("S",cntnum);//发送湿度数据
			//必须延时,不然第一次数据发完没接收到响应,直接再次发送会发送失败
			HAL_Delay(80);					
			cntnum=Data[2];					//获取温度值
			ESP_Senddata("T",cntnum);//发送温度数据
			
		}
		//接收led亮数据后,发送led灭给云端
		if(ledflag2==1){
			ledflag2=0;
			sprintf(cmdcmd,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"led\\\":{\\\"value\\\":true\\}}}\",0,0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdcmd);
		}
		//接收led灭数据后,发送led灭给云端
		if(ledflag3==1){
			ledflag3=0;
			sprintf(cmdcmd,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"led\\\":{\\\"value\\\":false\\}}}\",0,0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdcmd);
		}
  }
串口发送数据函数(主要是发送AT指令)
void ESP8266_SendCmd(const char* command) {
	
    HAL_UART_Transmit(&huart2, (uint8_t*)command, strlen(command), HAL_MAX_DELAY);
	
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, HAL_MAX_DELAY);
	
}
发送AT指令连接onenet云平台,AT指令具体作用上面有讲解,这里不再解释

这里有一些宏定义:WIFI_SSID->wifi名称,WIFI_PASSWD->wifi密码,

MQTT_ID->产品ID,MQTT_NAME->设备名称,MQTT_TOKEN->你的token值。

void ONENET_Init(void){
			//初始化时获取一次温湿度
			DHT_Read();
			//等待esp8266上电稳定
			HAL_Delay(1000);
			char cmdBuf[256];
	
			ESP8266_SendCmd("AT+RST"); 
			HAL_Delay(2000);
			ESP8266_SendCmd("AT");
			HAL_Delay(1000);
			ESP8266_SendCmd("AT+CWMODE=1");
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+CWDHCP=1,1");
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+CWJAP=\"%s\",\"%s\"",WIFI_SSID,WIFI_PASSWD);
			ESP8266_SendCmd(cmdBuf);

			HAL_Delay(4000);
			sprintf(cmdBuf, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"", MQTT_NAME, MQTT_ID, MQTT_TOKEN);
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf, "AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1");
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+MQTTSUB=0,\"$sys/%s/%s/thing/property/post/reply\",0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+MQTTSUB=0,\"$sys/%s/%s/thing/property/set\",0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"led\\\":{\\\"value\\\":false\\}}}\",0,0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdBuf);
			//开启数据接收中断
			HAL_UART_Receive_IT(&huart2, &uart_rx_data, 1);
			//开启定时器中断  一定要在连接完云平台之后再开启
			HAL_TIM_Base_Start_IT(&htim2);
			
}
向云平台发送数据函数,需要传入变量名和数值(变量名必须和云平台上设置的一致)
void ESP_Senddata(const char *name,const char cnt){
	char cmd[256];
	sprintf(cmd,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"%s\\\":{\\\"value\\\":%d\\}}}\",0,0",MQTT_ID,MQTT_NAME,name,cnt);
	ESP8266_SendCmd(cmd);
}
定时器中断回调函数,主要设置标志位,不能在中断里运行太长时间
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim->Instance == TIM2) {
    ledflag=1;
		
  }
}
串口接收函数,主要是接收云端发来的数据和指令,做出响应,并且给出回应,接收数据成功。

这里没有使用json库处理数据,因为我还不会。(求教)

void HandleCloudMsg(char *msg) {
		
	
	  char req_id[32] = {0};
    char *id_start = strstr(msg, "\"id\":\"");
    if(id_start) {
        id_start += 6; // 跳过"id\":\""
        char *id_end = strchr(id_start, '"');
        if(id_end) {
            int id_len = id_end - id_start;
            strncpy(req_id, id_start, id_len);
            req_id[id_len] = '\0';
        }
    }
   
    // 查找关键字段(避免使用复杂库)
    char *led_pos = strstr(msg, "\"led\":");
    if(led_pos != NULL) {
        // 提取状态值
        char state = *(led_pos + 6); // 冒号后的第一个字符
        if(state == 't') { // true
            led1_ON();    //自己写的控制led亮灭的函数,这个都会吧
						
            MQTT_SendResponse(req_id);
						ledflag2=1;
						
        } else if(state == 'f') { // false
            led1_OFF();    //自己写的控制led亮灭的函数,这个都会吧
						MQTT_SendResponse(req_id);
						ledflag3=1;
						
            
        }
    }
}


// 发送指令响应到云平台
void MQTT_SendResponse(char* request_id) {
  // 构造响应JSON
  char response_json[128];
  sprintf(response_json, 
          "{\\\"id\\\":\\\"%s\\\"\\,\\\"code\\\":200\\,\\\"msg\\\":\\\"success\\\"}", 
          request_id);
  
  // 发布到响应主题
  char topic[128];
  sprintf(topic, "$sys/%s/%s/thing/property/set_reply", 
          MQTT_ID, MQTT_NAME);
  
  // 发送MQTT消息
	char mqtt_send_buf[256];
  sprintf(mqtt_send_buf, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n", 
          topic, response_json);
  ESP8266_SendCmd(mqtt_send_buf);
}



// 串口接收完成中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if(huart->Instance == USART2) { // 来自ESP模块的数据
        // 保存接收到的字节
        msg_buf[msg_len++] = uart_rx_data;
        
        // 检测消息结束(根据实际情况调整)
        if(uart_rx_data == '}' || msg_len >= sizeof(msg_buf)-1) {
            msg_buf[msg_len] = '\0'; // 添加字符串结束符
            
            // 检测是否为控制消息
            if(strstr(msg_buf, "thing/property/set")) {
                HandleCloudMsg(msg_buf); // 处理消息
            }
            
            msg_len = 0; // 重置缓冲区
        }
        
        // 重新启用接收中断
        HAL_UART_Receive_IT(&huart2, &uart_rx_data, 1);
    }
}

完整代码

esp8266代码
//esp8266.h文件

#ifndef __ESP8266_H
#define __ESP8266_H

#include "head.h"


//这些宏定义更换成自己的
#define WIFI_SSID        "wifi名称"    
#define WIFI_PASSWD      "wifi密码"
#define MQTT_ID     	"产品id"
#define MQTT_NAME     		"设备名称"         
#define MQTT_TOKEN    	"token值"                
     
void ESP8266_SendCmd(const char* command);
void ONENET_Init(void);

void HandleCloudMsg(char *msg);

// 添加在文件末尾
void MQTT_SendResponse(char* request_id);

#endif

//esp8266.c文件

#include "head.h"

char ledflag=0;
char ledflag2=0;
char ledflag3=0;

extern UART_HandleTypeDef huart2; // 声明外部串口
uint8_t uart_rx_data; // 串口接收缓存
char msg_buf[256];    // 消息缓冲区
uint16_t msg_len = 0; // 当前消息长度



void ESP8266_SendCmd(const char* command) {
	
    HAL_UART_Transmit(&huart2, (uint8_t*)command, strlen(command), HAL_MAX_DELAY);
	
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, HAL_MAX_DELAY);
	
}
void ONENET_Init(void){
			//初始化时获取一次温湿度
			DHT_Read();
			//等待esp8266上电稳定
			HAL_Delay(1000);
			char cmdBuf[256];
	
			ESP8266_SendCmd("AT+RST"); 
			HAL_Delay(2000);
			ESP8266_SendCmd("AT");
			HAL_Delay(1000);
			ESP8266_SendCmd("AT+CWMODE=1");
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+CWDHCP=1,1");
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+CWJAP=\"%s\",\"%s\"",WIFI_SSID,WIFI_PASSWD);
			ESP8266_SendCmd(cmdBuf);

			HAL_Delay(4000);
			sprintf(cmdBuf, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"", MQTT_NAME, MQTT_ID, MQTT_TOKEN);
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf, "AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1");
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+MQTTSUB=0,\"$sys/%s/%s/thing/property/post/reply\",0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+MQTTSUB=0,\"$sys/%s/%s/thing/property/set\",0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdBuf);
			HAL_Delay(2000);
			sprintf(cmdBuf,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"led\\\":{\\\"value\\\":false\\}}}\",0,0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdBuf);
			//开启数据接收中断
			HAL_UART_Receive_IT(&huart2, &uart_rx_data, 1);
			//开启定时器中断  一定要在连接完云平台之后再开启
			HAL_TIM_Base_Start_IT(&htim2);
			
}


void ESP_Senddata(const char *name,const char cnt){
	char cmd[256];
	sprintf(cmd,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"%s\\\":{\\\"value\\\":%d\\}}}\",0,0",MQTT_ID,MQTT_NAME,name,cnt);
	ESP8266_SendCmd(cmd);
}


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim->Instance == TIM2) {
    ledflag=1;
		
  }
}




void HandleCloudMsg(char *msg) {
		
	
	  char req_id[32] = {0};
    char *id_start = strstr(msg, "\"id\":\"");
    if(id_start) {
        id_start += 6; // 跳过"id\":\""
        char *id_end = strchr(id_start, '"');
        if(id_end) {
            int id_len = id_end - id_start;
            strncpy(req_id, id_start, id_len);
            req_id[id_len] = '\0';
        }
    }
   
    // 查找关键字段(避免使用复杂库)
    char *led_pos = strstr(msg, "\"led\":");
    if(led_pos != NULL) {
        // 提取状态值
        char state = *(led_pos + 6); // 冒号后的第一个字符
        if(state == 't') { // true
            led1_ON();
						
            MQTT_SendResponse(req_id);
						ledflag2=1;
						
        } else if(state == 'f') { // false
            led1_OFF();
						MQTT_SendResponse(req_id);
						ledflag3=1;
						
            
        }
    }
}


// 发送指令响应到云平台
void MQTT_SendResponse(char* request_id) {
  // 构造响应JSON
  char response_json[128];
  sprintf(response_json, 
          "{\\\"id\\\":\\\"%s\\\"\\,\\\"code\\\":200\\,\\\"msg\\\":\\\"success\\\"}", 
          request_id);
  
  // 发布到响应主题
  char topic[128];
  sprintf(topic, "$sys/%s/%s/thing/property/set_reply", 
          MQTT_ID, MQTT_NAME);
  
  // 发送MQTT消息
	char mqtt_send_buf[256];
  sprintf(mqtt_send_buf, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n", 
          topic, response_json);
  ESP8266_SendCmd(mqtt_send_buf);
}



// 串口接收完成中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if(huart->Instance == USART2) { // 来自ESP模块的数据
        // 保存接收到的字节
        msg_buf[msg_len++] = uart_rx_data;
        
        // 检测消息结束(根据实际情况调整)
        if(uart_rx_data == '}' || msg_len >= sizeof(msg_buf)-1) {
            msg_buf[msg_len] = '\0'; // 添加字符串结束符
            
            // 检测是否为控制消息
            if(strstr(msg_buf, "thing/property/set")) {
                HandleCloudMsg(msg_buf); // 处理消息
            }
            
            msg_len = 0; // 重置缓冲区
        }
        
        // 重新启用接收中断
        HAL_UART_Receive_IT(&huart2, &uart_rx_data, 1);
    }
}














DHT11代码(包含需要的微秒级延时函数)

这里是移植的开头那位大佬的代码。

//delay.h文件

#ifndef DELAY_DELAY_H_
#define DELAY_DELAY_H_

#include "head.h" 

void Delay_us(uint32_t us);

#endif 


//delay.c文件

#include "head.h"

#define LOOPS_PER_US 9  // 72MHz下典型值

void Delay_us(uint32_t us)
{
    uint32_t count = us * LOOPS_PER_US;
    while(count--);
}


//DHT11.h文件

#ifndef __DHT11_H
#define __DHT11_H

#include "head.h"

void DHT_GPIO_SET_OUTPUT(void);
void DHT_GPIO_SET_INPUT(void);
uint8_t DHT_Read_Byte(void);
uint8_t DHT_Read(void);

#endif

//DHT11.c

#include "head.h"

uint8_t Data[5]={0x00,0x00,0x00,0x00,0x00};   //Data存储读取的温湿度信息
		

/*------------------------------*/
void DHT_GPIO_SET_OUTPUT(void)     //设置GPIOx为输出模式(MCU的IO口向DHT11发激活信号)
{
	GPIO_InitTypeDef GPIO_InitStructure;    //在GPIO_InitTypeDef结构体中修改IO口参数(结构体成员)
	GPIO_InitStructure.Pin=DHT11_DA_Pin;      //设置的格式必须严格遵循注释,比如GPIO_PIN_define
	GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}


void DHT_GPIO_SET_INPUT(void)     //设置GPIOx为输入模式(DHT11向MUC的IO发电平信号,信号里包含了温湿度信息)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin=DHT11_DA_Pin;
	GPIO_InitStructure.Mode=GPIO_MODE_INPUT;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}

/*------------------------------*/

/*
     uint8_t DHT_Read_Byte(void)用于转译采集DHT11发送给IO口的电平信号(8位)。
*/
uint8_t DHT_Read_Byte(void)  //从DHT11读取一位(8字节)信号
{
	 uint8_t ReadData=0;  //ReadData用于存放8bit数据,即8个单次读取的1bit数据的组合
	 uint8_t temp;      //临时存放信号电平(0或1)
	 uint8_t retry=0;   //retry用于防止卡死
	 uint8_t i;    
	 for(i=0; i<8; i++)   //一次温湿度信号读取八位
	 {
			while(HAL_GPIO_ReadPin(GPIOA,DHT11_DA_Pin)==0 && retry<100)  
			//等待直到DHT11输出高电平:当PA5=1,上升沿,表示开始接受数据,可以判断0 or 1,跳出循环,执行后续判断(若PA5=0,将一直循环等待)
			{
					Delay_us(1);
				  retry++;             //retry防止PA5读取不到数据卡死在这一步,当经历100us后retry自增到100,跳出循环。
			}
			retry=0;
			
			Delay_us(40);    //延时30us
			//根据时序图,DHT传回高电平信号维持26us~28us表示0,	维持70us表示1
		  //延时30us后,如果IO读取到仍是高电平,说明采集到1;如果IO读取到低电平,说明采集到0
			//读取电平信号暂存temp内,随后会压入ReadData中
			if(HAL_GPIO_ReadPin(GPIOA,DHT11_DA_Pin)==1)   temp=1;
			 else   temp=0;

			 while(HAL_GPIO_ReadPin(GPIOA,DHT11_DA_Pin)==1 && retry<100)
			//等待直到DHT11输出低电平,表示退出。本轮1bit信号接收完毕。
			 {
				 Delay_us(1);
				 retry++;
			 }
			 retry=0;
			 
			 ReadData<<=1;    //ReadData内信号先全部左移一位,空出末尾位置
			 ReadData |= temp;        //将temp写入ReadData
	 }

		return ReadData;
}

/*------------------------------*/

/*
     uint8_t DHT_Read(void)表达完整时序
*/
uint8_t DHT_Read(void)
{
	 uint8_t retry=0;
	 uint8_t i;
		
	 DHT_GPIO_SET_OUTPUT();    //IO设置为输出模式。在传输的最开始,MCU要向DHT11发送信号
	 HAL_GPIO_WritePin(GPIOA,DHT11_DA_Pin,GPIO_PIN_RESET);   //IO->DHT11:先拉低电平18ms(应时序要求)
	 HAL_Delay(18);
	 HAL_GPIO_WritePin(GPIOA,DHT11_DA_Pin,GPIO_PIN_SET);   //IO->DHT11:随后拉高电平20us
	 Delay_us(20);
	
	//MCU通过IO向DHT11发送请求完毕。接下来DHT11向IO发送响应,IO转为输入模式。在这之后就开始信号的转译读取。
	DHT_GPIO_SET_INPUT();
	Delay_us(20);
	if(HAL_GPIO_ReadPin(GPIOA,DHT11_DA_Pin)==0) //DHT11发回低电平响应(读取到低电平,说明DHT11有响应)
	{
		//接下来,DHT11拉低电平一段时间后拉高电平一段时间
		while(HAL_GPIO_ReadPin(GPIOA,DHT11_DA_Pin)==0 && retry<100)  
		{
		   Delay_us(1);
			 retry++;
		}
		retry=0;
		while(HAL_GPIO_ReadPin(GPIOA,DHT11_DA_Pin)==1 && retry<100) 
		{
		   Delay_us(1);
			 retry++;
		}
		retry=0;
		
		//一共传输40位,一次DHT_Read_Byte返回8位,共读取5次。存储在Data[]中。(Data[]定义为全局)
		for(i=0; i<5; i++)
		{
			 Data[i] = DHT_Read_Byte();  //每次读取一字节(8位)
		}
		Delay_us(50);
		//说明:Data[0]湿度, Data[2]温度。Data[1]和Data[3]分别为湿度和温度的小数位。Data[4]用于校验。
	}
	
	 uint32_t sum=Data[0]+Data[1]+Data[2]+Data[3];  //校验
	 if((sum)==Data[4])    return 1;  
	   else   return 0;
	
}
主函数

里面有一些cubemx配置自动生成的代码部分我没有列出来,我只列出来了主要部分


extern char ledflag;
extern char ledflag2;
extern char ledflag3;
extern uint8_t Data[5];
char cmdcmd[256];
char cntnum=0;

int main(void)
{

    while(1){

            if(ledflag==1){
			
			DHT_Read();						//获取温湿度
			ledflag =0;						//标志位清零
			cntnum=Data[0];				//获取湿度值
			ESP_Senddata("S",cntnum);//发送湿度数据
			//必须延时,不然第一次数据发完没接收到响应,直接再次发送会发送失败
			HAL_Delay(80);					
			cntnum=Data[2];					//获取温度值
			ESP_Senddata("T",cntnum);//发送温度数据
			
		}
		//接收led亮数据后,发送led灭给云端
		if(ledflag2==1){
			ledflag2=0;
			sprintf(cmdcmd,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"led\\\":{\\\"value\\\":true\\}}}\",0,0",MQTT_ID,MQTT_NAME);
			ESP8266_SendCmd(cmdcmd);
		}
		//接收led灭数据后,发送led灭给云端
		if(ledflag3==1){
			ledflag3=0;
			sprintf(cmdcmd,"AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"            {\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"led\\\":                                    {\\\"value\\\":false\\}}}\",0,0",MQTT_ID,MQTT_NAME);
			    ESP8266_SendCmd(cmdcmd);
		}

    }
}

总结

以上代码,只能实现基本的数据上传和数据处理,如果需要更复杂的场景,还需要更多的优化,欢迎大家提出建议和优化方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值