STM32+ESP01S(WIFI)上传温湿度(DHT11)至巴法云+手机app

前言

翻东西时发现一个ESP01S模块,想着玩一下
本篇学习ESP01S连接巴法云,AT指令参考:https://espressif-docs.readthedocs-hosted.com/projects/esp-at/zh-cn/release-v2.2.0.0_esp8266/AT_Command_Set/index.html

设计目标

  • STM32通过DHT11获取温湿度,通过ESP01S连接巴法云发布温湿度,手机app订阅巴法云获取温湿度
  • STM32连接LED灯,实现通过手机app控制LED灯的状态

硬件介绍

  • STM32F103C8T6最小系统板
  • ESP01S模块
  • DHT11温湿度传感器

手机app

手机app使用App Inventor平台开发,可视化编程,简单方便

软件设计

连接ESP01S

STM32通过USART2连接ESP01S

STM32ESP01S
PA2(TX)RX
PA3(RX)TX
VCCRST
VCCEN
3.33V3
GNDGND

注意:根据EPS01S规格书可查,3V3供电引脚需要外部供电电源输出电流在 500mA 以上,如果上电后不稳定(自动重启),可能是供电不足,单片机连接的电源电流有限,建议更换或增加电源(最小系统板尾部有USB-mrico连接手机充电器即可,建议先连接充电器再连接ST-LINK,防止电脑USB口电压不稳导致电脑出问题)

代码部分

串口

USART1连接电脑(使用串口工具)用于调试显示,USART2连接ESP01S

USART使用两组中断,接收中断和空闲中断实现“接收”和“接收完成”的功能
用数组接收串口数据,初始化时把外部指针指向数组的地址,获取接收数据标志位时,直接对外部指针进行处理即可(不通过extern使用全局变量,便于移植),不需要调用函数传出接收数据(第二次接收数据时直接覆盖,因此需要及时处理)

Usart.h

#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

#define USART_BUF_SIZE 100 /* USART一次收发数据长度 */

void Usart1_Init(unsigned int baud);                                      /* 串口1初始化 */
void Usart2_Init(unsigned int baud);                                      /* 串口2初始化 */
void Usart_SendString(USART_TypeDef *USARTx, uint8_t *str, uint16_t len); /* 串口数据发送 */
void UsartPrintf(USART_TypeDef *USARTx, char *fmt, ...);                  /* 串口格式化打印 */
void USART1_IRQHandler_Callback(uint8_t data, FlagStatus idle);           /* 串口1中断回调函数 */
void USART2_IRQHandler_Callback(uint8_t data, FlagStatus idle);           /* 串口2中断回调函数 */

#endif

Usart.c

#include "Usart.h"

/*
************************************************************
*	函数名称:	Usart1_Init
*	函数功能:	串口1初始化
*	入口参数:	baud:设定的波特率
*	返回参数:	无
*	说明:
************************************************************
*/
void Usart1_Init(unsigned int baud)
{
    GPIO_InitTypeDef gpioInitStruct;
    USART_InitTypeDef usartInitStruct;
    NVIC_InitTypeDef nvicInitStruct;

    //开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    // PA9	TXD
    gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpioInitStruct.GPIO_Pin = GPIO_Pin_9;
    gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpioInitStruct);

    // PA10	RXD
    gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpioInitStruct.GPIO_Pin = GPIO_Pin_10;
    gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpioInitStruct);

    //初始化USART1
    usartInitStruct.USART_BaudRate = baud;
    usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
    usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 // 接收和发送
    usartInitStruct.USART_Parity = USART_Parity_No;                             // 无校验
    usartInitStruct.USART_StopBits = USART_StopBits_1;                          // 1位停止位
    usartInitStruct.USART_WordLength = USART_WordLength_8b;                     // 8位数据位
    USART_Init(USART1, &usartInitStruct);

    USART_Cmd(USART1, ENABLE);                     // 使能串口
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能接收中断
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能串口总线空闲中断

    nvicInitStruct.NVIC_IRQChannel = USART1_IRQn;
    nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
    nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    nvicInitStruct.NVIC_IRQChannelSubPriority = 2;
    NVIC_Init(&nvicInitStruct);
}

/*
************************************************************
*	函数名称:	Usart2_Init
*	函数功能:	串口2初始化
*	入口参数:	baud:设定的波特率
*	返回参数:	无
*	说明:		TX-PA2		RX-PA3
************************************************************
*/
void Usart2_Init(unsigned int baud)
{
    GPIO_InitTypeDef gpioInitStruct;
    USART_InitTypeDef usartInitStruct;
    NVIC_InitTypeDef nvicInitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    // PA2	TXD
    gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpioInitStruct.GPIO_Pin = GPIO_Pin_2;
    gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpioInitStruct);

    // PA3	RXD
    gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpioInitStruct.GPIO_Pin = GPIO_Pin_3;
    gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpioInitStruct);

    usartInitStruct.USART_BaudRate = baud;
    usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
    usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 // 接收和发送
    usartInitStruct.USART_Parity = USART_Parity_No;                             // 无校验
    usartInitStruct.USART_StopBits = USART_StopBits_1;                          // 1位停止位
    usartInitStruct.USART_WordLength = USART_WordLength_8b;                     // 8位数据位
    USART_Init(USART2, &usartInitStruct);

    USART_Cmd(USART2, ENABLE);                     // 使能串口
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // 使能接收中断
    USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // 使能串口总线空闲中断

    nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
    nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
    nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&nvicInitStruct);
}

/*
************************************************************
*	函数名称:	Usart_SendString
*	函数功能:	串口数据发送
*	入口参数:	USARTx:串口组
*				str:要发送的数据
*				len:数据长度
*	返回参数:	无
*	说明:
************************************************************
*/
void Usart_SendString(USART_TypeDef *USARTx, uint8_t *str, uint16_t len)
{
    uint16_t count = 0;

    for (; count < len; count++)
    {
        while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET)
            ;                           /* 等待发送完成 */
        USART_SendData(USARTx, *str++); /* 发送数据 */
    }
}

/*
************************************************************
*	函数名称:	UsartPrintf
*	函数功能:	格式化打印
*	入口参数:	USARTx:串口组
*				fmt:不定长参
*	返回参数:	无
*	说明:
************************************************************
*/
void UsartPrintf(USART_TypeDef *USARTx, char *fmt, ...)
{
    uint8_t Buf[USART_BUF_SIZE]; /* 接收输入变量数组,如果数组不够大,可以修改一下 */
    va_list ap;
    uint8_t *pStr = Buf;

    va_start(ap, fmt);
    vsnprintf((char *)Buf, sizeof(Buf), fmt, ap);
    va_end(ap);

    while (*pStr != 0)
    {
        while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET)
            ;                            /* 等待发送完成 */
        USART_SendData(USARTx, *pStr++); /* 发送数据 */
    }
}

//==========================================================
//	函数名称:	USART1_IRQHandler
//	函数功能:	串口1收发中断
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
void USART1_IRQHandler(void)
{
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) /* 串口1接收中断 */
    {
        USART1_IRQHandler_Callback(USART_ReceiveData(USART1), RESET); /* 返回数据 */
        USART_ClearFlag(USART1, USART_FLAG_RXNE);                     /* 清除中断标志位 */
    }

    if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) /* 数据帧接收完毕,串口1空闲中断 */
    {
        USART1_IRQHandler_Callback(USART_ReceiveData(USART1), SET); /* 返回接收完毕信号 */
        USART_ClearFlag(USART1, USART_IT_IDLE);                     /* 清除中断标志位 */
    }
}

//==========================================================
//	函数名称:	USART2_IRQHandler
//	函数功能:	串口2收发中断
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
void USART2_IRQHandler(void)
{
    if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) /* 串口2接收中断 */
    {
        USART2_IRQHandler_Callback(USART_ReceiveData(USART2), RESET); /* 返回数据 */
        USART_ClearFlag(USART2, USART_FLAG_RXNE);                     /* 清除中断标志位 */
    }

    if (USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) /*  数据帧接收完毕,串口2空闲中断 */
    {
        USART2_IRQHandler_Callback(USART_ReceiveData(USART2), SET); /* 返回接收完毕信号 */
        USART_ClearFlag(USART2, USART_IT_IDLE);                     /* 清除中断标志位 */
    }
}

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H

#include "Usart.h"

#define SERIAL_USART USART1                                       /* 打印串口连接的串口 */
#define SERIAL_BUF_SIZE USART_BUF_SIZE                            /* 打印串口一次收发数据长度 */
#define SERIAL_USART_INIT(x) Usart1_Init(x)                       /* 打印串口连接串口初始化 */
#define Serial_Printf(...) UsartPrintf(SERIAL_USART, __VA_ARGS__) /* 打印串口发送数据 */

void Serial_Init(uint8_t **buf);  /* 打印串口初始化 */
FlagStatus Serial_Rec_Flag(void); /* 串口接收到数据标志位 */

#endif

Serial.c

#include "Serial.h"

static FlagStatus Serial_Flag = RESET;      /* 接收到数据标志位 */
static uint8_t Serial_Buf[SERIAL_BUF_SIZE]; /* 接收到的数据数组 */

//==========================================================
//	函数名称:	Serial_Init
//	函数功能:	打印串口初始化
//	入口参数:	buf:存储获取数据的指针地址
//	返回参数:	无
//	说明:
//==========================================================
void Serial_Init(uint8_t **buf)
{
    SERIAL_USART_INIT(9600);
    *buf = Serial_Buf;
}

//==========================================================
//	函数名称:	Serial_Rec_Flag
//	函数功能:	Serial接收到数据标志位
//	入口参数:	无
//	返回参数:	SET:接收完成    RESET:未完成接收
//	说明:
//==========================================================
FlagStatus Serial_Rec_Flag(void)
{
    if (Serial_Flag == SET)
    {
        Serial_Flag = RESET;
        return SET;
    }
    return RESET;
}

//==========================================================
//	函数名称:	USART1_IRQHandler_Callback
//	函数功能:	串口1中断回调函数
//	入口参数:	data:获取数据  idle:空闲,接收完成
//	返回参数:	无
//	说明:
//==========================================================
void USART1_IRQHandler_Callback(uint8_t data, FlagStatus idle)
{
    static uint8_t RxState = 0;       /* 状态变量 */
    static uint8_t RxData_length = 0; /* 接收数据数量 */

    if (idle == SET)
    {
        RxState = 0;                      /* 状态置0 */
        Serial_Buf[RxData_length] = '\0'; /* 结尾 */
        Serial_Flag = SET;                /* 接收完成 */
    }
    else if (RxState == 0) /* 未接收 */
    {
        RxState = 1;          /* 状态置1 */
        RxData_length = 0;    /* 接收数据数量清零 */
        Serial_Buf[0] = data; /* 获取接收数据 */
        RxData_length++;      /* 接收数据数量加1 */
    }
    else if (RxState == 1) /* 接收中 */
    {
        Serial_Buf[RxData_length] = data;          /* 获取接收数据 */
        if (RxData_length < (SERIAL_BUF_SIZE - 1)) /* 限制RxData_length长度,尾部空一位置用于'\0' */
            RxData_length++;                       /* 接收数据数量加1 */
    }
}

ESP01S

ESP01S通过WiFi连接巴法云
根据巴法云接入文档提示,可根据主题后三位判断主题类型,有主题类型的设备可以在巴法云手机app上直接查看和控制设备,如果不需要巴法云手机app可不需要加后三位数字

ESP01S.h

#ifndef _ESP01S_H
#define _ESP01S_H

#include <stdlib.h>
#include "Usart.h"
#include "Serial.h"
#include "Delay.h"

#define ESP01S_USART USART2                                           /* ESP01S连接的串口 */
#define ESP01S_BUF_SIZE USART_BUF_SIZE                                /* ESP01S一次收发数据长度 */
#define ESP01S_USART_INIT(x) Usart2_Init(x)                           /* ESP01S连接串口初始化 */
#define ESP01S_Printf(...) UsartPrintf(ESP01S_USART, __VA_ARGS__)     /* ESP01S发送数据 */
#define ESP01S_MQTT_INFO "AT+CIPSTART=\"TCP\",\"bemfa.com\",8344\r\n" /* 连接巴法云的AT指令 */
#define MQTT_PING_CMD "cmd=0&msg=ping\r\n"                            /* 心跳命令 */
#define MQTT_PING_REC "cmd=0&res=1"                                   /* 心跳返回信息 */
#define MQTT_PING_TIME_MAX 30                                         /* MQTT心跳最长时间(秒) */

extern char ESP01S_MQTT_TOPIC_LED[]; /* 连接巴法云的主题LED */

typedef enum
{
    MQTT_Cmd_Add = 1,       /* 添加订阅 */
    MQTT_Cmd_Send = 2,      /* 发布 */
    MQTT_Cmd_AddAndRec = 3, /* 订阅并获取一次历史信息 */
    MQTT_Cmd_RecTime = 7,   /* 获取时间 */
    MQTT_Cmd_RecSend = 9,   /* 获取一次已发消息 */
} _MQTT_Cmd;                /* MQTT命令 */

void ESP01S_Init(uint8_t **buf);                                                          /* 初始化ESP01S */
FlagStatus ESP01S_Rec_Flag(void);                                                         /* ESP01S接收到数据标志位 */
void ESP01S_ALL(void);                                                                    /* ESP01S循环执行部分 */
void ESP01S_Loop_1s(void);                                                                /* ESP01S定时执行部分 */
void MQTT_Set_LED(FunctionalState state, uint8_t num);                                    /* LED控制 */
void MQTT_Send_humi_temp(uint8_t humi, uint8_t humi_dec, uint8_t temp, uint8_t temp_dec); /* 发送温湿度 */
void ESP01S_On_Line_Feedback(void);                                                       /* ESP01S在线反馈 */
void ESP01S_Send_String(uint8_t *string);                                                 /* 发送数据到ESP01S */

#endif

ESP01S.c

#include "ESP01S.h"

static char ESP01S_MQTT_UID[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; /* 连接巴法云的秘钥 */
char ESP01S_MQTT_TOPIC_LED[] = "led002";                            /* 连接巴法云的主题LED */
char ESP01S_MQTT_TOPIC_HUMI[] = "humi004";                          /* 连接巴法云的主题湿度 */
char ESP01S_MQTT_TOPIC_TEMP[] = "temp004";                          /* 连接巴法云的主题温度 */

static char ESP01S_WIFI_SSID[] = "xxxxxxx";      /* 连接wifi的名称 */
static char ESP01S_WIFI_PASSWORD[] = "xxxxxxxx"; /* 连接wifi的密码 */

static uint8_t ESP01S_buf[ESP01S_BUF_SIZE];    /* 接收ESP-01s的返回数据数组 */
static FlagStatus ESP01S_buf_continue = RESET; /* 是否允许接收多组数据 */
static FlagStatus Recive_end_Flag = RESET;     /* ESP01S数据接收完成标志位 */
static FlagStatus Recive_Busy = RESET;         /* ESP01S数据接收中标志位 */
static FlagStatus MQTT_On_Line_Flag = RESET;   /* MQTT在线标志位 */
static FlagStatus MQTT_Ping_Flag = RESET;      /* MQTT发送心跳标志位 */
static uint8_t MQTT_Ping_Time_s = 0;           /* MQTT心跳时间(s) */
static uint16_t RxData_length = 0;             /* 接收数据长度 */

//==========================================================
//	函数名称:	ESP01S_Recive_Clean
//	函数功能:	接收信息清除
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
static void ESP01S_Recive_Clean(void)
{
    RxData_length = 0;       /* 接收数据数量清零 */
    ESP01S_buf[0] = '\0';    /* 清除接收数据 */
    Recive_Busy = RESET;     /* 清除数据接收中标志位 */
    Recive_end_Flag = RESET; /* 清除ESP01S接收数据标志位 */
}

//==========================================================
//	函数名称:	ESP01S_SendCmd_Printf
//	函数功能:	发送命令
//	入口参数:	cmd:命令
//			   fmt:输入信息
//	返回参数:	SET-成功	RESET-失败
//	说明:
//==========================================================
static FlagStatus ESP01S_SendCmd_Printf(char *res, char *fmt, ...)
{
    char String[ESP01S_BUF_SIZE]; /* 接收输入变量数组,如果数组不够大,可以修改一下 */
    uint8_t times = 20;           /* 检查返回数据次数 */
    va_list arg;
    va_start(arg, fmt);
    vsprintf(String, fmt, arg);

    ESP01S_buf_continue = SET; /* 允许接收多组数据 */
    do
    {
        while (Recive_Busy == SET) /* 等待串口空闲 */
        {
            Delay_ms(1);
        }
        ESP01S_Recive_Clean(); /* 接收信息清零 */
        ESP01S_Printf(String); /* 发送命令 */
        Delay_ms(5);
    } while ((strstr((const char *)ESP01S_buf, "busy s...") != NULL) || (strstr((const char *)ESP01S_buf, "busy p...") != NULL)); /* 输入正常 */

    while (times--)
    {
        while (Recive_Busy == SET) /* 等待接收完成 */
        {
            Delay_ms(1);
        }
        if (strstr((const char *)ESP01S_buf, res) != NULL) /* 如果检索到关键词 */
        {
            ESP01S_buf_continue = RESET; /* 禁止接收多组数据 */
            Recive_end_Flag = RESET;     /* 清除ESP01S接收数据标志位 */
            return SET;
        }
        Delay_ms(10);
    }

    Recive_end_Flag = RESET;     /* 清除ESP01S接收数据标志位 */
    ESP01S_buf_continue = RESET; /* 禁止接收多组数据 */
    return RESET;
}

//==========================================================
//	函数名称:	ESP01S_EXIT_CIPMODE
//	函数功能:	ESP01S退出穿透模式
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
static void ESP01S_EXIT_CIPMODE(void)
{
    if (ESP01S_SendCmd_Printf("OK", "AT\r\n") == SET)
        return;

    Serial_Printf("退出透传模式\r\n");
    Delay_ms(40);
    while (Recive_Busy == SET)
        ;                 /* 等待串口空闲 */
    ESP01S_Printf("+++"); /* 退出透传发送 */
    Delay_ms(40);
    ESP01S_SendCmd_Printf("OK", "AT+CIPMODE=0\r\n"); /* 退出透传模式 */
    ESP01S_SendCmd_Printf("OK", "AT+CIPCLOSE\r\n");  /* 断开连接 */
}

//==========================================================
//	函数名称:	ESP01S_Status
//	函数功能:	ESP01S状态
//	入口参数:	无
//	返回参数:	状态
//	说明:   0: ESP station 为未初始化状态
//          1: ESP station 为已初始化状态,但还未开始 Wi-Fi 连接
//          2: ESP station 已连接 AP,获得 IP 地址
//          3: ESP station 已建立 TCP、UDP 或 SSL 传输
//          4: ESP 设备所有的 TCP、UDP 和 SSL 均断开
//          5: ESP station 开始过 Wi-Fi 连接,但尚未连接上 AP 或从 AP 断开
//==========================================================
static uint8_t ESP01S_Status(void)
{
    uint8_t status = 0;
    ESP01S_EXIT_CIPMODE(); /* 退出透传模式 */

    if (ESP01S_SendCmd_Printf("OK", "AT+CIPSTATUS\r\n") == SET) /* 查询连接状态和信息 */
    {
        status = atoi(strstr((char *)ESP01S_buf, "STATUS:") + strlen("STATUS:")); /* 获取STATUS:后一位数字 */
        Serial_Printf("ESP01S连接模式为:%d\r\n", status);                        /* 串口打印 */
    }
    return status;
}

//==========================================================
//	函数名称:	MQTT_SendCmd
//	函数功能:	MQTT发送命令
//	入口参数:	cmd:命令
//             topic:主题
//	返回参数:	无
//	说明:
//==========================================================
static void MQTT_SendCmd(_MQTT_Cmd cmd, char *topic)
{
    ESP01S_SendCmd_Printf("cmd", "cmd=%d&uid=%s&topic=%s\r\n", cmd, ESP01S_MQTT_UID, topic);
}

//==========================================================
//	函数名称:	WIFI_Connect
//	函数功能:	WIFI设置
//	入口参数:	ssid:WIFI名称
//	           pwd:WIFI密码
//	返回参数:	是否连接成功
//	说明:
//==========================================================
static FlagStatus WIFI_Connect(char *ssid, char *pwd)
{
    Serial_Printf("\r\nWIFI连接\r\n");                                                      /* 串口打印 */
    if (ESP01S_SendCmd_Printf("OK", "AT+CWMODE=1\r\n") == SET)                              /* 1 station模式 2 AP路由器模式 3 station+AP混合模式 */
        return ESP01S_SendCmd_Printf("CONNECTED", "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, pwd); /* 连接WIFI */
    return RESET;
}

//==========================================================
//	函数名称:	MQTT_Connect
//	函数功能:	连接MQTT
//	入口参数:	是否连接成功
//	返回参数:	无
//	说明:
//==========================================================
static FlagStatus MQTT_Connect(void)
{
    Serial_Printf("MQTT连接\r\n");                              /* 串口打印 */
    if (ESP01S_SendCmd_Printf("OK", "AT+CIPMODE=1\r\n") == SET) /* 设置为透传模式 */
    {
        if (ESP01S_SendCmd_Printf("OK", ESP01S_MQTT_INFO) == SET) /* 连接到MQTT服务器 */
        {
            if ((ESP01S_SendCmd_Printf(">", "AT+CIPSEND\r\n")) == SET) /* 进入透传模式,下面发的都会无条件传输 */
            {
                Delay_ms(200);
                MQTT_SendCmd(MQTT_Cmd_Add, ESP01S_MQTT_TOPIC_LED);                       /* 订阅主题 */
                MQTT_SendCmd(MQTT_Cmd_Add, ESP01S_MQTT_TOPIC_HUMI);                      /* 订阅主题 */
                MQTT_SendCmd(MQTT_Cmd_Add, ESP01S_MQTT_TOPIC_TEMP);                      /* 订阅主题 */
                MQTT_On_Line_Flag = ESP01S_SendCmd_Printf(MQTT_PING_REC, MQTT_PING_CMD); /* 发送MATT心跳,判断是否连接 */

                if (MQTT_On_Line_Flag == SET)
                    Serial_Printf("MQTT订阅成功\r\n"); /* 串口打印 */

                return MQTT_On_Line_Flag;
            }
        }
    }
    return RESET;
}

//==========================================================
//	函数名称:	ESP01S_Init
//	函数功能:	初始化ESP01S
//	入口参数:	buf:存储获取数据的指针地址
//	返回参数:	无
//	说明:
//==========================================================
void ESP01S_Init(uint8_t **buf)
{
    uint8_t status; /* ESP01S连接状态 */

    ESP01S_USART_INIT(115200);             /* ESP01S连接串口初始化 */
    *buf = ESP01S_buf;                     /* 传递参数 */
    Serial_Printf("\r\nESP01S初始化\r\n"); /* 串口打印 */
    Delay_ms(10);
    MQTT_On_Line_Flag = ESP01S_SendCmd_Printf(MQTT_PING_REC, MQTT_PING_CMD); /* 心跳连接,cpu重启,ESP01S保持上电 */
    if (MQTT_On_Line_Flag == SET)
    {
        MQTT_SendCmd(MQTT_Cmd_Add, ESP01S_MQTT_TOPIC_LED);  /* 订阅主题 */
        MQTT_SendCmd(MQTT_Cmd_Add, ESP01S_MQTT_TOPIC_HUMI); /* 订阅主题 */
        MQTT_SendCmd(MQTT_Cmd_Add, ESP01S_MQTT_TOPIC_TEMP); /* 订阅主题 */
        Serial_Printf("\r\nMQTT在线\r\n");                  /* 串口打印 */
        return;
    }

    status = ESP01S_Status();       /* 获取ESP01S连接状态 */
    if (status == 1 || status == 5) /* 未连接或连接后断开 */
    {
        WIFI_Connect(ESP01S_WIFI_SSID, ESP01S_WIFI_PASSWORD); /* 连接FIWI */
        MQTT_Connect();                                       /* 连接MQTT */
    }
    else
        MQTT_Connect(); /* 连接MQTT */
}

//==========================================================
//	函数名称:	ESP01S_On_Line_Feedback
//	函数功能:	ESP01S在线反馈
//	入口参数:	无
//	返回参数:	无
//	说明:100ms执行一次
//==========================================================
void ESP01S_On_Line_Feedback(void)
{
    MQTT_Ping_Time_s = 0;    /* 心跳时间清理 */
    MQTT_On_Line_Flag = SET; /* MQTT在线 */
}

//==========================================================
//	函数名称:	ESP01S_ALL
//	函数功能:	ESP01S循环执行部分
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
void ESP01S_ALL(void)
{
    uint8_t status; /* ESP01S连接状态 */

    if (MQTT_Ping_Flag == SET) /* MQTT发送心跳标志位 */
    {
        MQTT_On_Line_Flag = ESP01S_SendCmd_Printf(MQTT_PING_REC, MQTT_PING_CMD); /* 发送MATT心跳,判断是否连接 */
        if (MQTT_On_Line_Flag == RESET)
        {
            Serial_Printf("MQTT离线\r\n"); /* 串口打印 */
        }
        MQTT_Ping_Flag = RESET; /* 清除标志位 */
    }

    if (MQTT_On_Line_Flag == RESET) /* MQTT不在线 */
    {
        status = ESP01S_Status();                      /* 获取ESP01S连接状态 */
        if (status == 0 || status == 1 || status == 5) /* 未连接或连接后断开 */
        {
            WIFI_Connect(ESP01S_WIFI_SSID, ESP01S_WIFI_PASSWORD); /* 连接FIWI */
            MQTT_Connect();                                       /* 连接MQTT */
        }
        else
            MQTT_Connect(); /* 连接MQTT */
    }
}

//==========================================================
//	函数名称:	ESP01S_Loop_1s
//	函数功能:	ESP01S定时执行部分
//	入口参数:	无
//	返回参数:	无
//	说明:1s执行一次
//==========================================================
void ESP01S_Loop_1s(void)
{
    MQTT_Ping_Time_s++;

    if (MQTT_Ping_Time_s >= MQTT_PING_TIME_MAX) /* 心跳 */
    {
        MQTT_Ping_Flag = SET; /* MQTT心跳 */
        MQTT_Ping_Time_s = 0;
    }
}

//==========================================================
//	函数名称:	USART2_IRQHandler_Callback
//	函数功能:	串口2中断回调函数
//	入口参数:	data:获取数据  idle:空闲,接收完成
//	返回参数:	无
//	说明:
//==========================================================
void USART2_IRQHandler_Callback(uint8_t data, FlagStatus idle)
{
    Recive_Busy = SET;          /* 数据接收中标志位 */
    static uint8_t RxState = 0; /* 接收状态 */

    if (idle == SET) /* 接收完成 */
    {
        RxState = 0;                          /* 状态置0 */
        Recive_end_Flag = SET;                /* 接收完成 */
        Recive_Busy = RESET;                  /* 清除数据接收中标志位 */
        if (ESP01S_buf_continue == RESET)     /* 禁止接收多组数据状态下,接收完成后加'\0'结尾 */
            ESP01S_buf[RxData_length] = '\0'; /* 结尾 */
    }
    else if (RxState == 0) /* 未接收 */
    {
        Recive_end_Flag = RESET; /* 未接收 */
        RxState = 1;             /* 状态置1 */

        if (ESP01S_buf_continue == RESET)          /* 禁止接收多组数据状态下,接收第一个data时,RxData_length置0 */
            RxData_length = 0;                     /* 接收数据数量清零 */
        ESP01S_buf[RxData_length] = data;          /* 获取接收数据 */
        if (RxData_length < (ESP01S_BUF_SIZE - 2)) /* 限制RxData_length长度,尾部空一位置用于'\0' */
            RxData_length++;                       /* 接收数据数量加1 */
    }
    else if (RxState == 1) /* 接收中 */
    {
        ESP01S_buf[RxData_length] = data;          /* 获取接收数据 */
        if (RxData_length < (ESP01S_BUF_SIZE - 2)) /* 限制RxData_length长度,尾部空一位置用于'\0' */
            RxData_length++;                       /* 接收数据数量加1 */
    }
}

//==========================================================
//	函数名称:	ESP01S_Rec_Flag
//	函数功能:	ESP01S接收到数据标志位
//	入口参数:	无
//	返回参数:	SET:接收完成    RESET:未完成接收
//	说明:
//==========================================================
FlagStatus ESP01S_Rec_Flag(void)
{
    if (Recive_end_Flag == SET) /* 接收完成 */
    {
        Recive_end_Flag = RESET; /* 清除ESP01S接收数据标志位 */
        return SET;
    }

    return RESET;
}

//==========================================================
//	函数名称:	MQTT_Set_LED
//	函数功能:	LED控制
//	入口参数:	state:开关
//             num:亮度
//	返回参数:	无
//	说明:
//==========================================================
void MQTT_Set_LED(FunctionalState state, uint8_t num)
{
    if (state == DISABLE)
        ESP01S_SendCmd_Printf("cmd=2", "cmd=2&uid=%s&topic=%s&msg=off\r\n", ESP01S_MQTT_UID, ESP01S_MQTT_TOPIC_LED);
    else
        ESP01S_SendCmd_Printf("cmd=2", "cmd=2&uid=%s&topic=%s&msg=on#%d\r\n", ESP01S_MQTT_UID, ESP01S_MQTT_TOPIC_LED, num);
}

//==========================================================
//	函数名称:	MQTT_Send_humi_temp
//	函数功能:	发送温湿度
//	入口参数:	humi:       湿度整数位
//             humi_dec:   湿度小数位
//	           temp:       温度整数位
//             temp_dec:   温度小数位
//	返回参数:	无
//	说明:
//==========================================================
void MQTT_Send_humi_temp(uint8_t humi, uint8_t humi_dec, uint8_t temp, uint8_t temp_dec)
{
    ESP01S_SendCmd_Printf("cmd=2", "cmd=2&uid=%s&topic=%s&msg=%d.%d\r\n", ESP01S_MQTT_UID, ESP01S_MQTT_TOPIC_HUMI, humi, humi_dec); /* 发送湿度 */
    ESP01S_SendCmd_Printf("cmd=2", "cmd=2&uid=%s&topic=%s&msg=%d.%d\r\n", ESP01S_MQTT_UID, ESP01S_MQTT_TOPIC_TEMP, temp, temp_dec); /* 发送温度 */
}

//==========================================================
//	函数名称:	ESP01S_Send_String
//	函数功能:	发送数据到ESP01S
//	入口参数:	string:输入数据
//	返回参数:	无
//	说明:     发送"to_esp01s:on"后,后面发送的信息直接转发给ESP01S,
//            全部发送结束后,发送"to_esp01s:off",后面发送的信息不再转发
//            可用于测试,手动初始化等
//==========================================================
void ESP01S_Send_String(uint8_t *string)
{
    static FunctionalState start = DISABLE;
    if (start == DISABLE)
    {
        if (strstr((const char *)string, "to_esp01s:on") != NULL) /* 开启 */
        {
            start = ENABLE;
            Serial_Printf("开启串口转发到ESP01S\r\n");
        }
    }
    else
    {
        while (Recive_Busy == SET)
            ;                                                      /* 等待串口空闲 */
        if (strstr((const char *)string, "to_esp01s:off") != NULL) /* 关闭 */
        {
            start = DISABLE;
            Serial_Printf("关闭串口转发到ESP01S\r\n");
        }
        else if (strstr((const char *)string, "+++") != NULL) /* 退出透传 */
            ESP01S_Printf("%s", string);                      /* 串口接收数据发送给ESP01S */
        else
            ESP01S_Printf("%s\r\n", string); /* 串口接收数据发送给ESP01S */
    }
}

上文中xxxxx的部分替换成自己的数据
ESP01S_Send_String函数可用于前期对ESP01S进行测试,也可直接使用ESP01S直接转接电脑的方式进行测试,但是需要多接几次线,嫌麻烦没试,该函数还可用于后期功能拓展(如:更换wifi、更换巴法云秘钥或主题,连接上位机进行控制显示等)

定时器

为了保障连接稳定,需要定时发送心跳信号,所以还需要一个定时器

Timer.h

#ifndef __TIMER_H
#define __TIMER_H

#include "stm32f10x.h"

void Timer2_Init(void);          /* 定时器2初始化 */
void TIM2_IRQHandler_Loop(void); /* 定时器2中断回调函数,中断间隔1ms */

#endif

Timer.c

#include "Timer.h"

//==========================================================
//	函数名称:	Timer2_Init
//	函数功能:	初始化定时器2
//	入口参数:	无
//	返回参数:	无
//	说明:      设定为1ms中断1次
//==========================================================
void Timer2_Init(void)
{
    // 开启时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // 选择时基单元的时钟
    TIM_InternalClockConfig(TIM2);

    // 配置时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period = 1000 - 1;
    TIM_TimeBaseInitStruct.TIM_Prescaler = SystemCoreClock / 1000 / 1000 - 1;
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

    // 清除TIM2的挂起标志
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);

    // 使能中断
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    // 配置NVIC
    // 优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // NVIC初始化
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStruct);

    // 启动定时器
    TIM_Cmd(TIM2, ENABLE);
}

//==========================================================
//	函数名称:	TIM2_IRQHandler
//	函数功能:	定时器2中断函数,中断间隔1ms
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) /* 判断中断标志位 */
    {
        TIM2_IRQHandler_Loop();                     /* 中断回调函数,中断间隔1ms */
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update); /* 清除标志位 */
    }
}

DHT11温湿度传感器

DHT11网上资料很多,就不解释了

DHT11.h

#ifndef __DHT11_H
#define __DHT11_H

#include "stm32f10x.h"
#include "Delay.h"

#define DHT11_DATA_IN GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) /* 获取STH11数据引脚状态 */
#define DHT11_DATA_OUT_H GPIO_SetBits(GPIOB, GPIO_Pin_14)       /* 置STH11数据引脚为高电平 */
#define DHT11_DATA_OUT_L GPIO_ResetBits(GPIOB, GPIO_Pin_14)     /* 置STH11数据引脚为低电平 */

void DHT11_Init(void);             /* DHT11初始化 */
uint8_t DHT11_Read(uint8_t *data); /* 获取DHT11数据 */

#endif

DHT11.c

#include "DHT11.h"

/*********************************************************
函数名称:DHT11_Init
函数功能:初始化DHT11
入口参数:无
返回结果:无
函数说明:
**********************************************************/
void DHT11_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /* 开启GPIOB外设时钟 */
}

/*********************************************************
函数名称:DHT11_GPIO_OUT
函数功能:初始化数据引脚为输出
入口参数:无
返回结果:无
函数说明:
**********************************************************/
static void DHT11_GPIO_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*********************************************************
函数名称:DHT11_GPIO_IN
函数功能:初始化数据引脚为输入
入口参数:无
返回结果:无
函数说明:
**********************************************************/
static void DHT11_GPIO_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*********************************************************
函数名称:DHT11_Start
函数功能:开启
入口参数:无
返回结果:无
函数说明:
**********************************************************/
static void DHT11_Start(void)
{
    DHT11_GPIO_OUT(); /* 输出模式 */
    DHT11_DATA_OUT_H; /* 拉高 */
    Delay_us(2);      /* 2us */
    DHT11_DATA_OUT_L; /* 拉低 */
    Delay_ms(20);     /* 至少18ms */
    DHT11_DATA_OUT_H; /* 拉高 */
    Delay_us(30);     /* 20-40us */
    DHT11_GPIO_IN();  /* 输入模式 */
}

/*********************************************************
函数名称:DHT11_Ask
函数功能:应答
入口参数:无
返回结果:1:成功       0:失败
函数说明:开始信号结束,DHT11发送80us低电平响应信号,发送响应信号后,再把总线拉高80us
**********************************************************/
static uint8_t DHT11_Ask(void)
{
    uint8_t retry = 0;

    if (DHT11_DATA_IN == 0)
    {
        while (DHT11_DATA_IN == 0 && retry < 100) /* 等待80us低电平消失 */
        {
            Delay_us(1);
            retry++;
            if (retry >= 100) //超过100us退出,以免卡死
                return 0;
        }
        retry = 0;
        while (DHT11_DATA_IN == 1 && retry < 100) /* 等待80us高电平消失 */
        {
            Delay_us(1);
            retry++;
            if (retry >= 100) //超过100us退出,以免卡死
                return 0;
        }
    }
    return 1;
}

/*********************************************************
函数名称:DHT11_Read_Byte
函数功能:读一个字节
入口参数:无
返回结果:获取的字节
函数说明:
**********************************************************/
static uint8_t DHT11_Read_Byte(void)
{
    uint8_t i, retry;
    uint8_t data = 0;

    for (i = 0; i < 8; i++)
    {
        retry = 0;
        while (DHT11_DATA_IN == 0 && retry < 100) /* 等待开始信号结束,50us */
        {
            Delay_us(1);
            retry++; //超过100us退出,以免卡死
        }
        retry = 0;
        Delay_us(35); /* 数字0信号高电平持续26-28us,数字1信号高电平70us,延时35us以确认数字0或1 */

        data <<= 1;            /* 左移一位 */
        data |= DHT11_DATA_IN; /* 获取一位 */

        while (DHT11_DATA_IN == 1 && retry < 100) /* 等待高电平消失 */
        {
            Delay_us(1);
            retry++; //超过100us退出,以免卡死
        }
    }
    return data;
}

/*********************************************************
函数名称:DHT11_Read
函数功能:读DHT11检测数据
入口参数:data:存放数据的地址
返回结果:1:读取成功       0:读取失败
函数说明:
**********************************************************/
uint8_t DHT11_Read(uint8_t *data)
{
    uint8_t i;
    uint8_t buf[5];

    DHT11_Start();   /* 开始 */
    Delay_us(20);    /* 延时20us检测 */
    if (DHT11_Ask()) /* 有应答 */
    {
        for (i = 0; i < 5; i++)
        {
            buf[i] = DHT11_Read_Byte(); /* 接收5位8字节数据 */
        }

        Delay_us(55);
        DHT11_DATA_OUT_H; /* DHT11拉低总线50us作为结束信号,这里等待55us后拉高 */

        if (buf[4] == buf[0] + buf[1] + buf[2] + buf[3]) /* 校验 */
        {
            for (i = 0; i < 4; i++)
            {
                data[i] = buf[i];
            }
            return 1;
        }
    }
    return 0;
}

LED

增加返回控制信号,控制LED的状态,实现获取消息的功能

LED.h

#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"

#define LEDNums 2 // LED总数量

typedef enum
{
    LED1,
    LED2
} LEDNumber; // LED代号

typedef enum
{
    LED_OFF,
    LED_ON,
    LED_Change
} LEDState; // LED状态设置

// LED对应GPIO
typedef struct
{
    GPIO_TypeDef *GPIOx;
    uint16_t GPIO_Pin;
} LEDPort;

// LED初始化
void LED_Init(void);

FunctionalState LED_State(LEDNumber LED_num);    /* 获取LED状态 */
void LED_Set(LEDNumber LED_num, LEDState State); /* LED状态设置 */

#endif

LED.c

#include "LED.h"

// LED声明
LEDPort LEDNum[LEDNums];

//==========================================================
//	函数名称:	LED_Init
//	函数功能:	LED初始化
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
void LED_Init(void)
{
    // GPIC外设时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // GPIO模式配置
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 默认关闭
    GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_0);

    // 配置各个LED的GPIO
    LEDNum[0].GPIOx = GPIOA;
    LEDNum[0].GPIO_Pin = GPIO_Pin_0;
    LEDNum[1].GPIOx = GPIOA;
    LEDNum[1].GPIO_Pin = GPIO_Pin_1;
}

//==========================================================
//	函数名称:	LED_State
//	函数功能:	获取LED状态
//	入口参数:	LED_num:LED代号
//	返回参数:	ENABLE:开启    DISABLE:熄灭
//	说明:
//==========================================================
FunctionalState LED_State(LEDNumber LED_num)
{
    return (FunctionalState)GPIO_ReadOutputDataBit(LEDNum[LED_num].GPIOx, LEDNum[LED_num].GPIO_Pin);
}

//==========================================================
//	函数名称:	LED_Set
//	函数功能:	设置LED状态
//	入口参数:	LED_num:LED代号     State:设置的状态
//	返回参数:	无
//	说明:
//==========================================================
void LED_Set(LEDNumber LED_num, LEDState State)
{
    if (State == LED_ON)
        GPIO_SetBits(LEDNum[LED_num].GPIOx, LEDNum[LED_num].GPIO_Pin);
    else if (State == LED_OFF)
        GPIO_ResetBits(LEDNum[LED_num].GPIOx, LEDNum[LED_num].GPIO_Pin);
    // 取反
    else if (GPIO_ReadOutputDataBit(LEDNum[LED_num].GPIOx, LEDNum[LED_num].GPIO_Pin))
        GPIO_ResetBits(LEDNum[LED_num].GPIOx, LEDNum[LED_num].GPIO_Pin);
    else
        GPIO_SetBits(LEDNum[LED_num].GPIOx, LEDNum[LED_num].GPIO_Pin);
}

OLED

OLED可用于测试,根据需要选择,本篇直接使用B站江协科技教程的代码,源码就不放了,不用的话直接把相关代码删了就行

按键

按键从单片机端控制LED,同时发送LED状态至巴法云

Key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"

#define KEY1_READ GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) /* 获取按键1的状态 */
#define KEY2_READ GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) /* 获取按键2的状态 */

void Key_Init(void);      /* 按键初始化 */
void Key_Loop_5ms(void);  /* 按键值获取,定时器调用,消抖 */
uint8_t Key_GetNum(void); /* 获取按键值 */

#endif

Key.c

#include "Key.h"

static uint8_t key_read = 0;      /* 按键值 */
static uint8_t key_read_lsat = 0; /* 上一次按键值 */

void Key_Init(void)
{
    // GPIOB外设时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // GPIO模式配置
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
}

/*********************************************************
函数名称:Key_Loop_5ms
函数功能:按键值检测
入口参数:无
返回结果:无
函数说明:定时器5ms调用一次,消抖
**********************************************************/
void Key_Loop_5ms(void)
{
    static uint8_t count = 0;
    count++;

    if (count >= 3) /* 消抖时间(15ms) */
    {
        count = 0;
        key_read_lsat = key_read; /* 保存上次按键值 */

        if (!KEY1_READ)
            key_read = 1; /* 按键1按下 */
        else if (!KEY2_READ)
            key_read = 2; /* 按键2按下 */
        else
            key_read = 0;
    }
}

/*********************************************************
函数名称:Key_GetNum
函数功能:获取按键值
入口参数:无
返回结果:按键值
函数说明:
**********************************************************/
uint8_t Key_GetNum(void)
{
    // if (!key_read_lsat)
    //     return key_read; /* 检测按键按下动作 */

    if (!key_read)
        return key_read_lsat; /* 检测按键抬起动作 */

    return 0;
}

主函数main

#include "LED.h"
#include "OLED.h"
#include "Key.h"
#include "Serial.h"
#include "Timer.h"
#include "ESP01S.h"
#include "DHT11.h"

uint8_t *pSerial_Recive = NULL;      /* 从串口获取到数据的指针 */
uint8_t *pESP01S_Recive = NULL;      /* 从ESP01S获取到数据的指针 */
uint8_t LED_brightness;              /* LED亮度 */
uint8_t key_num = 0;                 /* 按键值 */
FlagStatus DHT11_Renew_Flag = RESET; /* DHT11更新数据标志位 */
uint8_t DHT11_data[4];               /* DHT11数据 */

int main()
{
    OLED_Init();
    LED_Init();
    Key_Init();
    Serial_Init(&pSerial_Recive);
    ESP01S_Init(&pESP01S_Recive);
    Timer2_Init();
    DHT11_Init();

    LED_Set(LED2, LED_ON);
    OLED_ShowString(1, 1, "LED2:ON    ");
    OLED_ShowString(2, 1, "Key_Num:");
    OLED_ShowString(3, 1, "humi:  .");
    OLED_ShowString(4, 1, "temp:  .");

    while (1)
    {
        key_num = Key_GetNum(); /* 更新按键值 */
        if (key_num)
        {
            OLED_ShowNum(2, 9, key_num, 3);
            if (LED_State(LED2) == DISABLE)
            {
                MQTT_Set_LED(ENABLE, 100);
                OLED_ShowString(1, 1, "LED2:ON#100");
                LED_Set(LED2, LED_ON);
            }
            else
            {
                MQTT_Set_LED(DISABLE, 0);
                LED_Set(LED2, LED_OFF);
                OLED_ShowString(1, 1, "LED2:OFF   ");
            }
            key_num = 0;
        }

        ESP01S_ALL();                 /* ESP01S循环执行部分 */
        if (ESP01S_Rec_Flag() == SET) /* ESP01S接收到数据 */
        {
            Serial_Printf("ESP01S_Recive:%s\r\n", pESP01S_Recive);                  /* 串口打印接收数据 */
            if (strstr((const char *)pESP01S_Recive, ESP01S_MQTT_TOPIC_LED) != NULL) /* 巴法云的主题LED */
            {
                if (strstr((const char *)pESP01S_Recive, "msg=on") != NULL) /* 开灯信号 */
                {
                    OLED_ShowString(1, 1, "LED2:ON    ");
                    LED_Set(LED2, LED_ON);

                    if (strstr((const char *)pESP01S_Recive, "msg=on#") != NULL) /* 开灯亮度信号 */
                    {
                        LED_brightness = atoi(strstr((const char *)pESP01S_Recive, "msg=on#") + strlen("msg=on#")); /* 获取开灯亮度信号 */
                        OLED_ShowNum(1, 9, LED_brightness, 3);
                    }
                }
                else if (strstr((const char *)pESP01S_Recive, "msg=off") != NULL) /* 关灯信号 */
                {
                    LED_Set(LED2, LED_OFF);
                    OLED_ShowString(1, 1, "LED2:OFF   ");
                }
            }
        }

        if (Serial_Rec_Flag() == SET) /* 串口接收到数据 */
        {
            Serial_Printf("Serial_Recive:%s\r\n", pSerial_Recive); /* 串口打印接收数据 */
            ESP01S_Send_String(pSerial_Recive);
        }

        if (DHT11_Renew_Flag == SET)
        {
            if (DHT11_Read(DHT11_data))
            {
                OLED_ShowNum(3, 6, DHT11_data[0], 2);
                OLED_ShowNum(3, 9, DHT11_data[1], 2);
                OLED_ShowNum(4, 6, DHT11_data[2], 2);
                OLED_ShowNum(4, 9, DHT11_data[3], 2);
            }
            MQTT_Send_humi_temp(DHT11_data[0], DHT11_data[1], DHT11_data[2], DHT11_data[3]);
            DHT11_Renew_Flag = RESET;
        }
    }
}

//==========================================================
//	函数名称:	TIM2_IRQHandler_Loop
//	函数功能:	定时器2中断回调函数,中断间隔1ms
//	入口参数:	无
//	返回参数:	无
//	说明:
//==========================================================
void TIM2_IRQHandler_Loop(void)
{
    static uint16_t MQTT_cnt = 0; /* 计数 */
    MQTT_cnt++;
    MQTT_cnt %= 60000;

    if (MQTT_cnt % 5 == 0) /* 5ms执行一次 */
        Key_Loop_5ms();    /* 按键检测 */

    if (MQTT_cnt % 1000 == 1) /* 1s执行一次 */
        ESP01S_Loop_1s();     /* ESP01S定时执行部分 */

    if (MQTT_cnt % 2000 == 2)   /* 2s执行一次 */
        DHT11_Renew_Flag = SET; /* 更新DHT11数值 */
}

App Inventor

本篇只是为了学习测试,实现简单功能即可,有兴趣的可以自行深入学习,打开时需要手机联网
项目文件和apk文件链接:百度网盘蓝奏云(密码在链接里)
有需要可根据项目文件自行更改,自己使用的话可以把保存私钥的功能删了,直接默认自己的私钥即可,我是想着做一个通用且不需要调整的版本可以供多人直接使用才增加了保存私钥到本地的功能
三个主题实际使用时不需要全部使用,只用其中一两个也是可以的
安装时有风险提示!!!
主页
简洁风的主页(这不是重点)在这里插入图片描述

第一次打开会有报错弹窗,点开右上角设置按钮,在下方私钥输入框输入自己巴法云的私钥,再点测试按钮,如果连接成功,会获取温湿度和LED状态,连接成功后点击保存按钮,会自动生成一个文件保存私钥,下次打开APP自动获取,再次设置按钮隐藏输入框部分,如果连接失败或连接成功但没有返回数据状态,对比私钥是否正确,主题是否相同;
其中LED主题、温度主题、湿度主题为默认,可更改,但是更改后不会保存到本地,建议巴法云新建主题时参考app上默认主题名称;
app上温湿度的值不可手动调整,单片机推送更新巴法云上数值时app自动同步更新,灯的图标可以点击,每次点击可更改状态,并发送状态数据至巴法云,单片机获取状态数据后改变LED灯状态

至此完成全部测试

结语

本篇为学习总结,只为学习测试基础功能,没有进行太多测试,可能隐藏一些暂未发现的问题,欢迎大佬们指正。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值