基于FreeRTOS的温湿度监测系统
文在基于 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 */
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)