文在基于 STM32F103 + ESP8266 实现 DHT11 温湿度数据上传华为云 IoT 平台基础上移植了FreeRTOS操作系统,相比于裸机,系统通过任务隔离与优先级调度、阻塞式同步与互斥、可靠的任务间通信和更好的可观测性,显著提升了实时性、稳定性与可扩展性。

DHT11温湿度传感器

如果想要使用DHT11我们需要发送开始信号,即主机先拉低信号,然后再拉高信号,并等待DHT的响应。DHT11响应则会先拉低信号然后再拉高,并准备传输数据。

DHT11响应后会发送数据,40bit为一组,分别为8bit的湿度整数+8bit的湿度小数+8bit的温度度整数+8bit的温度小数

DHT11在传输数据时,会先拉低信号,然后再拉高信号,用高电平的维持时间来表示0和1.

DHT11.h

#ifndef __DHT11_H__
#define __DHT11_H__

#include "sys.h"
#include "delay.h"

#define err 1


#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_Pin_15

u8 DHT11_Init(void);
u8 DHT11_Read_Data(u8 *temp, u8 *hum);
u8 DHT11_Read_Bit(void);
u8 DHT11_Read_Byte(void);
u8 DHT11_Check(void);
void DHT11_Rst(void);
void DHT11_IO_OUT(void);
void DHT11_IO_IN(void);
#endif

DHT11.c

#include "dht11.h"

void DHT11_IO_OUT(void)
{
    //设置为输出端口,用于发送开始信号
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

void DHT11_IO_IN(void)
{
    //设置为输入端口,用于接收数据
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

void DHT11_Rst(void)
{
    //发送开始信号
    DHT11_IO_OUT(); //设置为输出端口
    GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
    delay_ms(20);
    GPIO_SetBits(DHT11_PORT, DHT11_PIN);
    delay_us(30);
}

u8 DHT11_Check(void)
{
    //DHT11响应信号检测
    u8 retry = 0;
    DHT11_IO_IN(); //设置为输入端口

    while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) && retry < 100)
    {
        retry++;
        delay_ms(1);
    }

    if(retry >= 100) return err;
    else retry = 0;

    while(!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) && retry < 100)
    {
        retry++;
        delay_ms(1);
    }
    if(retry >= 100) return err;
    return 0;
}

u8 DHT11_Read_Bit(void)
{
    //等待DHT11拉低信号
    u8 retry = 0;
    while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) && retry < 100)
    {
        retry ++;
        delay_us(1);
    }

    //等待DHT11拉高信号
    retry = 0;
    while (!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) && retry < 100)
    {
        retry ++;
        delay_us(1);
    }
    //延时40us后读取数据线电平状态,判断是0还是1
    delay_us(40);
    if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)) return 1;
    else return 0;
}

u8 DHT11_Read_Byte(void)
{
    u8 i, dat;
    dat = 0;
    for(i = 0; i < 8; i++)
    {
        dat <<= 1;
        dat |= DHT11_Read_Bit();
    }
    return dat;
}

u8 DHT11_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //使能时钟
    DHT11_Rst();
    return DHT11_Check();
}

u8 DHT11_Read_Data(u8 *temp, u8 *hum)
{
    u8 rData[5];
    DHT11_Rst();
    if(!DHT11_Check())
    {
        for(int i = 0; i < 5; i++)
            rData[i] = DHT11_Read_Byte();
    }

    if(rData[0] + rData[1] + rData[2] + rData[3] == rData[4])
    {
        hum = rData[0];
        temp = rData[2];
    }
    else return err;
    return 0;
}


LED显示屏与I2C

在获取到温湿度后可以将其显示在OLED屏幕上。

I2C主要包含SDA和SCL,其中SCL为时钟线,负责同步,而SDA则负责发送数据。

通信流程:

  • 准备:SDA、SCL 线接好上拉电阻,主机和从机通电就绪,总线空闲时两线均为高电平
  • 启动:主机发起启动条件(SCL 高电平时,SDA 从高变低),告知从机通信开始
  • 寻址:主机发送 7 位从机地址 + 1 位读写方向(0 = 写、1 = 读),等待从机应答
  • 应答:从机地址匹配则拉低 SDA 发应答(ACK),不匹配则无应答,主机需终止通信
  • 传数据:
  • 写操作:主机逐字节发数据,每字节后等从机应答
  • 读操作:从机逐字节发数据,主机前 N-1 字节发应答(让从机继续发),最后 1 字节发非应答(NACK)
  • 先写后读(常用):主机先写指令 / 寄存器地址,再发重复启动(不停止),切换读方向收数据
  • 停止:主机发起停止条件(SCL 高电平时,SDA 从低变高),通信结束,总线回归空闲

I2C.h

#ifndef __I2C_H
#define __I2C_H	 
#include "sys.h"


#define I2CPORT		GPIOB	//定义IO接口
#define I2C_SCL		GPIO_Pin_6	//定义IO接口
#define I2C_SDA		GPIO_Pin_7	//定义IO接口

#define HostAddress	0xc0	//总线主机的器件地址
#define BusSpeed	200000	//总线速度


void I2C_Configuration(void);
void I2C_Send_Buffer(u8 SlaveAddr, u8 WriteAddr, u8* pBuffer, u16 NumByteToWrite);
void I2C_Send_Byte(u8 SlaveAddr,u8 writeAddr,u8 pBuffer);
void I2C_Read_Buffer(u8 SlaveAddr,u8 readAddr,u8* pBuffer,u16 NumByteToRead);
u8 I2C_Read_Byte(u8 SlaveAddr,u8 readAddr);
		 				    

#endif

I2C.c:

I2C首先需要初始化GPIO端口进行复用,并启动I2C使能,然后我们还需要配置一下I2C。

#include "i2c.h"

void I2C_GPIO_Init(void){ //I2C接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); //启动i2c使能
    GPIO_InitStructure.GPIO_Pin = I2C_SCL | I2C_SDA; //选择端口号              
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //设置工作方式    
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
	GPIO_Init(I2CPORT, &GPIO_InitStructure);
}

void I2C_Configuration(void){ //I2C初始化
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_GPIO_Init(); 
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//设置为I2C模式
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_OwnAddress1 = HostAddress; //主机地址
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//允许应答
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位地址模式
	I2C_InitStructure.I2C_ClockSpeed = BusSpeed; //总线速度	
	I2C_Init(I2C1,&I2C_InitStructure);
	I2C_Cmd(I2C1,ENABLE);//开启I2C			
}

void I2C_Send_Buffer(u8 SlaveAddr, u8 WriteAddr, u8* pBuffer, u16 NumByteToWrite)
{
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); // 等待总线空闲

    I2C_GenerateSTART(I2C1, ENABLE);//产生开始信号
    while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除ev5

    I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Transmitter); //发送器件地址
    while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //清除ev6

    I2C_SendData(I2C1,WriteAddr); //内部功能地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//等待发送结束

	while(NumByteToWrite--){ //循环发送数据
		I2C_SendData(I2C1,*pBuffer); 
		pBuffer++; 
		while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除ev8
	}
	I2C_GenerateSTOP(I2C1,ENABLE);//产生停止信号
}


void I2C_Send_Byte(u8 SlaveAddr, u8 writeAddr, u8 pBuffer)
{
    // 1. 产生起始条件
    I2C_GenerateSTART(I2C1, ENABLE);
    // 等待起始条件发送完成并清除EV5事件(SB标志)
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    // 2. 发送从设备地址(写方向)
    I2C_Send7bitAddress(I2C1, SlaveAddr, I2C_Direction_Transmitter);
    // 等待地址发送完成并收到ACK,清除EV6事件
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    // 3. 发送寄存器地址
    I2C_SendData(I2C1, writeAddr);
    // 等待该字节(含ACK)在总线上传输完毕,清除TXE+BTF标志
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    // 4. 发送要写入的数据
    I2C_SendData(I2C1, pBuffer);
    // 等待数据字节传输完成
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    // 5. 产生停止条件
    I2C_GenerateSTOP(I2C1, ENABLE);
}


void I2C_Read_Buffer(u8 SlaveAddr, u8 readAddr, u8* pBuffer, u16 NumByteToRead)
{
    // 等待总线空闲(如果总线忙则一直等待)
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

    // === 第一次发送:写寄存器地址 ===
    I2C_GenerateSTART(I2C1, ENABLE);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, SlaveAddr, I2C_Direction_Transmitter);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    I2C_Cmd(I2C1, ENABLE);   // 确保I2C外设使能(实际可省略)
    I2C_SendData(I2C1, readAddr);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    // === 第二次发送:重启起始条件,切换为读方向 ===
    I2C_GenerateSTART(I2C1, ENABLE);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, SlaveAddr, I2C_Direction_Receiver);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    // 循环读取NumByteToRead个字节
    while(NumByteToRead)
    {
        // 如果只剩最后一个字节,需要禁止从机应答并提前产生停止条件
        if(NumByteToRead == 1)
        {
            I2C_AcknowledgeConfig(I2C1, DISABLE);   // 失能应答(主机不发送ACK)
            I2C_GenerateSTOP(I2C1, ENABLE);         // 提前产生停止条件
        }

        // 检查是否已收到一个字节(EV7事件)
        if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        {
            *pBuffer = I2C_ReceiveData(I2C1);  // 从数据寄存器读取字节
            pBuffer++;
            NumByteToRead--;
        }
    }

    // 恢复应答配置(为下次通信准备)
    I2C_AcknowledgeConfig(I2C1, ENABLE);
}

u8 I2C_Read_Byte(u8 SlaveAddr, u8 readAddr)
{
    u8 a;

    // 等待总线空闲
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

    // 写寄存器地址
    I2C_GenerateSTART(I2C1, ENABLE);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, SlaveAddr, I2C_Direction_Transmitter);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    I2C_Cmd(I2C1, ENABLE);
    I2C_SendData(I2C1, readAddr);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    // 重启起始条件,切换为读
    I2C_GenerateSTART(I2C1, ENABLE);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, SlaveAddr, I2C_Direction_Receiver);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    // 只读一个字节:禁止应答,产生停止条件
    I2C_AcknowledgeConfig(I2C1, DISABLE);
    I2C_GenerateSTOP(I2C1, ENABLE);

    // 读取数据
    a = I2C_ReceiveData(I2C1);
    return a;
}


STM32 使用I2C对OLED屏进行控制,并传输数据。关于OLED屏的操作网上有很多,就不再赘述。

ESP8266

ESP8266在该项目当中只作为STM32的一个wifi 模块进行使用,因此在这里也就进行简单的介绍。(参考https://blog.csdn.net/qq_44016222/article/details/142739842)

 ESP8266是一款嵌入式系统级芯片,它集成了Wi-Fi功能和微控制器能力于一身,常用于物联网(IoT)项目中。这款芯片支持TCP/IP协议栈,能够连接到WiFi网络,并通过AT命令或者更高级的API与主控设备进行通信。

ESP8266常见的AT指令

AT指令:全称是Attention指令,起源于 Hayes 公司,最初用于控制调制解调器,如今已成为控制各类通信模块(如Wi-Fi、蓝牙、GSM)的标准语言。

我们使用AT指令通过串口来与ESP8266进行通信。

我们需要用到的AT指令包括:

  • AT测试是否OK
  • AT+CWMODE=1,设置模块为Station模式
  • AT+CWJAP="你的WiFi名称","你的WiFi密码"   连接Wi-Fi
  • AT+MQTTUSERCFG=0,1,"NULL","MQTT用户名","MQTT密码" 设置MQTT用户属性
  • AT+MQTTCLIENTID=0   设置MQTT客户端ID
  • AT+MQTTCONN=0,"华为云IoT平台接入地址",1883,1   连接华为云MQTT服务器
  • AT+MQTTPUB=0,"$oc/devices/{设备ID}/sys/properties/report","{\"services\":[{\"service_id\":\"你的服务ID\",\"properties\":{\"温度\":25.5}}]}",0,0

ESP8266的基础工作模式

有三种工作模式:AP、STA、AP+STA。其中AP模式就是我们常说的热点模式,而STA则可以连接WIFI,AP+STA就是讲两种功能进行结合。

wifi模块的代码:
 

#include "wifi.h"

int flag = 0;

u8 ESP8266_SendAT(char *cmd, char *response, u16 timeout)
{
    USART2_ClearBuffer();
    Usart2_SendString(cmd);
    delay_ms(1000);
    if(!flag)
    {
        flag = 0;
        delay_ms(3000);
    }
    while(timeout--)
    {
        if(strstr(WiFi_RxBuffer, response))
            return 0;
        delay_ms(10);
    }
    return 1;

}


u8 ESP8266_Init()
{
    //格式化
    if (ESP8266_SendAT("AT+RESTORE\r\n", "OK", 1000)) {
        printf("%s\r\n", WiFi_RxBuffer);
        printf("格式化异常\r\n");
        return 1;
    }
    printf("格式化成功\r\n"); 

    //发送AT
     if (ESP8266_SendAT("AT\r\n", "OK", 1000)) {
        printf("基本响应测试异常\r\n");
        return 1;
    }
    printf("基本响应测试成功\r\n");

    //配置STA
    if (ESP8266_SendAT("AT+CWMODE=1\r\n", "OK", 1000)) {
        printf("配置Station模式失败\r\n");
        return 2;
    }
    printf("配置Station模式成功\r\n");

    //取消自动连接
    if (ESP8266_SendAT("AT+CWAUTOCONN=0\r\n", "OK", 1000)) {
        printf("取消自动连接失败\r\n");
        return 2;
    }
    printf("取消自动连接成功\r\n");

    //连接指定WiFi
    char connectWiFiCmd[64];
    sprintf(connectWiFiCmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", WiFi_Id, WiFi_Password); // AT+CWJAP="TP-LINK_5EB1","15139104099"
    if (ESP8266_SendAT(connectWiFiCmd, "WIFI CONNECTED", 5000)) {
        printf("WiFi连接失败\r\n");
        return 3;
    }
    printf("WiFi连接成功\r\n");
    return 0;
}


u8 ESP8266_ConnectHuaweiCloud()
{
    char cmd[256];
    char cmd2[256];

    // 配置用户参数(使用 Mqtt_Client_Id, Mqtt_Username, Mqtt_Password)
    sprintf(cmd, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\r\n",
            Mqtt_Client_Id, Mqtt_Username, Mqtt_Password);

    // 连接服务器(端口使用 Mqtt_Port,注意是整数)
    sprintf(cmd2, "AT+MQTTCONN=0,\"%s\",%d,0\r\n", Mqtt_Server, Mqtt_Port);

    delay_ms(1000);

    if (ESP8266_SendAT(cmd, "OK", 2000)) {
        printf("MQTT CFG FAIL\r\n");
        return 1;
    }
    printf("MQTT CFG SUCCESS\r\n");

    // 设置 Client ID(同样使用 Mqtt_Client_Id)
    sprintf(cmd, "AT+MQTTCLIENTID=0,\"%s\"\r\n", Mqtt_Client_Id);
    if (ESP8266_SendAT(cmd, "OK", 2000)) {
        printf("%s\r\n", WiFi_RxBuffer);
        printf("MQTT ClientID FAIL\r\n");
        return 1;
    }
    printf("MQTT ClientID SUCCESS\r\n");

    flag = 1;

    if (ESP8266_SendAT(cmd2, "MQTTCONNECTED", 2000)) {
        printf("%s\r\n", WiFi_RxBuffer);
        printf("MQTT CONNECT FAIL\r\n");
        return 2;
    }
    printf("MQTT CONNECT SUCCESS\r\n");

    ESP8266_SendAT("AT+MQTTCONN?\r\n", "OK", 2000);
    printf("%s\r\n", WiFi_RxBuffer);
    return 0;
}

void ESP8266_SendDataToHuawei(float temp, float humi)
{
    // 华为云数据格式:JSON,主题固定
    char data[128];
    sprintf((char*)data,
            "{\\\"services\\\":[{\\\"service_id\\\":\\\"Tem_Hum_Data\\\"\\,\\\"properties\\\":{\\\"Temperature\\\":%.1f\\,\\\"Humidity\\\":%.1f}}]}",
            temp, humi);

    // 发布数据到指定主题
    char cmd[512];
    sprintf((char*)cmd,
            "AT+MQTTPUB=0,\"$oc/devices/%s/sys/properties/report\",\"%s\",0,0\r\n",
            Mqtt_Client_Id, data);
    //char cmd[256] = "AT+MQTTPUB=0,\"$oc/devices/690028b2e41f2979315c5cd2_esp8266-01/sys/properties/report\",\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Tem_Hum_Data\\\"\\,\\\"properties\\\":{\\\"Temperature\\\":26.8\\,\\\"Humidity\\\":58.0}}]}\",0,0\r\n";
    //char cmd[256];
    //sprintf(cmd, "AT+MQTTPUB=0,\"$oc/devices/690028b2e41f2979315c5cd2_esp8266-01/sys/properties/report\",\"{\"services\":[{\"service_id\":\"Tem_Hum_Data\",\"properties\":{\"Temperature\":%.1f,\"Humidity\":%.1f}}]}\",0,0\r\n", temp, humi);
    if (ESP8266_SendAT(cmd, "OK", 2000)) {
        printf("%s\r\n", WiFi_RxBuffer);
        printf("向华为云发送数据失败\r\n");
    } 
    else
    {
        printf("向华为云发送数据成功\r\n");
        printf("Tem:%.1f,Hum:%.1f",temp,humi);
    }
};

main.c


#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "wifi.h"
#include "oled0561.h"


int main (void){//主程序
	RCC_Configuration(); //时钟设置
	I2C_Configuration();
	OLED0561_Init();
	USART1_Init();
	Usart2_Init();

	//连接wifi
	while(!ESP8266_Init()) delay_ms(100);

	//连接华为云
	whlie(!ESP8266_ConnectHuaweiCloud()) delay_ms(100);

	//发送数据
	float temp, hum;
	while(1)
	{
		if(! DHT11_Read_Data(&temp, &hum))
		ESP8266_SendDataToHuawei(temp, hum);
		delay_s(5);
	}
	

}



RTOS版本

以上是直接在裸机上进行开发的,可以看到使用到了大量的delay函数,对CPU资源造成了浪费。同时全部的代码都写在了main中,不利于后期的维护。

修改思路:先将任务划分为三大部分:WiFi_Task、Dht11_Senor、Data_Upload。并且将程序中的delay函数换成vTaskDelay。再加一个显示任务Oled_Show。

/* USER CODE BEGIN Header */
#include "driver_timer.h"
#include "driver_dht11.h"
#include "driver_ir_receiver.h"
#include "driver_ir_sender.h"
#include "driver_key.h"
#include "driver_uart.h"
#include "driver_oled.h"

/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "wifi.h"
#include  <stdio.h> 
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
static TaskHandle_t Data_Upload_TaskHandle;  // 数据上传任务句柄
static TaskHandle_t Dht11_Senor_TaskHandle;  // DHT11传感器任务句柄
static TaskHandle_t Oled_show_TaskHandle;  // OLED显示任务句柄

static int hum, temp; // 全局变量,用于存储温湿度数据

/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
void WiFi_Task(void *argument);
void Dht11_Senor_Task(void *argument);
void Data_Upload(void *argument);
void Oled_Show(void *argument);
/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void *argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
  xTaskCreate(WiFi_Task, "WiFi_Task", 256, NULL, osPriorityNormal + 2, NULL);
  xTaskCreate(Dht11_Senor_Task, "Dht11_Senor_Task", 256, NULL, osPriorityNormal + 1, &Dht11_Senor_TaskHandle);
  xTaskCreate(Data_Upload, "Data_Upload", 256, NULL, osPriorityNormal, &Data_Upload_TaskHandle);
  xTaskCreate(Oled_Show, "Oled_Show", 256, NULL, osPriorityNormal, &Oled_show_TaskHandle);
  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

}

/* USER CODE BEGIN Header_StartDefaultTask */

/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  
  for(;;)
  {
	  DS18B20_Test();
  }
  /* USER CODE END StartDefaultTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
void WiFi_Task(void *argument)
{
  
  //检查WiFi连接状态
  while(1)
  {
    if(ESP8266_CheckWiFi() == pdFAIL)
    {
      // 初始化ESP8266模块
      if(ESP8266_Init() == pdFAIL) 
      {
        printf("ESP8266初始化失败\r\n");
        vTaskDelete(NULL); // 删除任务
      }
      // 连接到华为云
      if(ESP8266_ConnectHuaweiCloud() == pdFAIL) 
      {
        printf("连接华为云失败\r\n");
        vTaskDelete(NULL); // 删除任务
      }
      //任务通知 只有连接完才能够传输数据
      xTaskNotifyGive(Dht11_Senor_TaskHandle); // 通知数据上传任务可以开始传输数据
    }
    vTaskDelay(pdMS_TO_TICKS(5000)); // 每5秒检查一次WiFi连接状态
  }
}


void Dht11_Senor_Task(void *argument)
{
  //等待WiFi连接成功
  ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待通知,阻塞直到收到通知
  while(1)
  {
    if(DHT11_Read(&hum, &temp) == 0)
    {
      printf("温度: %d°C, 湿度: %d%%\r\n", temp, hum);
      vTaskDelay(pdMS_TO_TICKS(3000)); // 每3秒读取一次数据
      xTaskNotifyGive(Data_Upload_TaskHandle); // 通知数据上传任务可以开始传输数据
      xTaskNotifyGive(Oled_show_TaskHandle); // 通知OLED显示任务可以开始显示数据
    }
  }
  
}

void Data_Upload(void *argument)
{
  
  while(1)
  {
    //等待DHT11传感器任务通知
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待通知,阻塞直到收到通知
    ESP8266_SendDataToHuawei((float)temp, (float)hum);
  }
}

void Oled_Show(void *argument)
{
  OLED_Clear();
  while(1)
  {
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待通知,阻塞直到收到通知
    char buffer[32];
    sprintf(buffer, "Temp: %dC  Hum: %d%%", temp, hum);
    OLED_PrintString(0, 0, buffer);
  }
}

/* USER CODE END Application */

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐