一、需求分析
1、获取温湿度显示在OLED和微信小程序
2、获取光照度显示在OLED和微信小程序
3、远程开关灯(使用MQTT+Cjson)
4、远程开关蜂鸣器(使用MQTT+Cjson)
5、蜂鸣器自动报警(光照度 < 5000 && 湿度 < 80 &&温度 < 38)
6、按键外部中断控制LED+蜂鸣器
硬件:
1.GY-30光照度传感器
2.DHT11温湿度传感器
3.蜂鸣器
4.LED灯
5.8266-01s
6.OLED显示屏
7.MCU
二、引脚定义
LED | PA0 |
---|---|
GY-30 | PB14 PB15 |
OLED | PB8 PB9 |
DHT11 | PA7 |
BEEP | PB12 |
USART1 | PA9(TX) PA10 (RX) 调试串口 |
USART2 | PA2(TX) PA3 (RX) 8266-01s发送AT指令 |
按键: | PB0:LED PB10:蜂鸣器 (按键中断中别加入延时) |
内部定时器 | TIM4 温湿度需要(也可不用,可用Dealy驱动) |
ESP8266-01S+MQTT: 1、烧录安信可8266-01S模块 ESP8266-01S烧录模式: EN:高电平 RST:按键控制转低电平,低电平有效 IO0:低电平 IO2:低电平 2、运行模式时,只需要接VCC+GND+RXD+TXD即可
三、源代码
因代码过多,因此需要源码请私信我或者有问题私信我交流
以下代码包含了MCU驱动所有的主要程序
光照度传感器GY-30
#include "GY30.h"
#include "TIM.h"
void GPIOConfig( void )
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = sda | scl;
GPIO_Init( GPIOB, &GPIO_InitStruct );
}
/*启动:SDA/SCL为低
停止:SDA/SCL为高*/
void BH1750_Start()
{
GPIO_SetBits( GPIOB, sda ); /*SDA拉高*/
GPIO_SetBits( GPIOB, scl ); /*SCL拉高*/
TIM4_Delayms(5); /*等待时间*/
GPIO_ResetBits( GPIOB, sda ); /* SDA拉低 */
TIM4_Delayms(5); /* 等待时间*/
GPIO_ResetBits( GPIOB, scl ); /* SCL拉低 */
}
void BH1750_Stop()
{
GPIO_ResetBits( GPIOB, sda ); /*SDA拉低*/
GPIO_SetBits( GPIOB, scl ); /* SCL拉高 */
TIM4_Delayms(5); /* */
GPIO_SetBits( GPIOB, sda ); /* SDA拉高*/
TIM4_Delayms(5); /* */
}
void BH1750_SendACK( unsigned int ack )
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = sda;
GPIO_Init( GPIOA, &GPIO_InitStruct );
if ( ack == 1 ) /* */
GPIO_SetBits( GPIOB, sda );
else if ( ack == 0 )
GPIO_ResetBits( GPIOB, sda );
else
return;
GPIO_SetBits( GPIOB, scl ); /* */
TIM4_Delayms(5); /* */
GPIO_ResetBits( GPIOB, scl ); /* */
TIM4_Delayms(5); /* */
}
unsigned int BH1750_RecvACK()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = sda;
GPIO_Init( GPIOA, &GPIO_InitStruct );
GPIO_SetBits( GPIOB, scl );
TIM4_Delayms(5);
if ( GPIO_ReadInputDataBit( GPIOB, sda ) == 1 )
mcy = 1;
else
mcy = 0;
GPIO_ResetBits( GPIOB, scl );
TIM4_Delayms(5);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init( GPIOA, &GPIO_InitStruct );
return(mcy);
}
void BH1750_SendByte( unsigned char dat )
{
unsigned char i;
for ( i = 0; i < 8; i++ )
{
if ( 0X80 & dat )
GPIO_SetBits( GPIOB, sda );
else
GPIO_ResetBits( GPIOB, sda );
dat <<= 1;
GPIO_SetBits( GPIOB, scl );
TIM4_Delayms(5);
GPIO_ResetBits( GPIOB, scl );
TIM4_Delayms(5);
}
BH1750_RecvACK();
}
unsigned char BH1750_RecvByte()
{
unsigned char i;
unsigned char dat = 0;
unsigned char bit;
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = sda;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOB, &GPIO_InitStruct );
GPIO_SetBits( GPIOB, sda );
for ( i = 0; i < 8; i++ )
{
dat <<= 1;
GPIO_SetBits( GPIOB, scl );
TIM4_Delayms(5);
if ( SET == GPIO_ReadInputDataBit( GPIOB, sda ) )
bit = 0X01;
else
bit = 0x00;
dat |= bit;
GPIO_ResetBits( GPIOB, scl );
TIM4_Delayms(5);
}
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init( GPIOB, &GPIO_InitStruct );
return(dat);
}
void Single_Write_BH1750( unsigned char REG_Address )
{
BH1750_Start();
BH1750_SendByte( SlaveAddress );
BH1750_SendByte( REG_Address );
//BH1750_SendByte(REG_data);
BH1750_Stop();
}
void Init_BH1750()
{
GPIOConfig();
Single_Write_BH1750( 0x01 );
}
void mread( void )
{
unsigned char i;
BH1750_Start();
BH1750_SendByte( SlaveAddress + 1 );
for ( i = 0; i < 3; i++ )
{
BUF[i] = BH1750_RecvByte();
if ( i == 3 )
{
BH1750_SendACK( 1 );
}else {
BH1750_SendACK( 0 );
}
}
BH1750_Stop();
TIM4_Delayms(5);
}
GY-30.h
#ifndef __BH1750_H__
#define __BH1750_H__
#include "stm32f10x.h" // Device header
#define sda GPIO_Pin_14 /* SDA */
#define scl GPIO_Pin_15 /* SCL */
#define SlaveAddress 0x46 /*当ADD接地时,地址为0X46*/
extern unsigned char BUF[8];
extern int dis_data;
extern int mcy;
void Init_BH1750( void );
void conversion( unsigned int temp_data );
void Single_Write_BH1750( unsigned char REG_Address );
unsigned char Single_Read_BH1750( unsigned char REG_Address );
void mread( void );
void GPIOConfig( void );
#endif
TIM.c
#include "stm32f10x.h" // Device header // Device header
void TIM4_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); ///使能TIM4时钟
TIM_TimeBaseInitStructure.TIM_Period = 50000-1; //自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = 60-1; //定时器分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//初始化TIM4
}
//微秒级延时
void TIM4_Delayus(uint16_t xus)
{
TIM_Cmd(TIM4,ENABLE); //启动定时器
while(TIM4->CNT < xus);
TIM4->CNT = 0;
TIM_Cmd(TIM4,DISABLE); //关闭定时器
}
//毫秒级延时
void TIM4_Delayms(uint16_t xms)
{
int i;
for(i=0;i<xms;i++)
{
TIM4_Delayus(1000);
}
}
DHT11.c
/**
* ************************************************************************
*
* @file My_DHT11.c
* @author zxr
* @brief
*
* ************************************************************************
* @copyright Copyright (c) 2024 zxr
* ************************************************************************
*/
#include "DHT11.h"
#include "TIM.h"
/**
* ************************************************************************
* @brief 将DHT11配置为推挽输出模式
* ************************************************************************
*/
void DHT11_Init(void)
{
//RC开启GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//初始化GPIO
GPIO_InitTypeDef Beep;
Beep.GPIO_Mode = GPIO_Mode_Out_PP;
Beep.GPIO_Pin = GPIO_Pin_7;
Beep.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&Beep);
GPIO_SetBits(GPIOA,GPIO_Pin_7 );
}
static void DHT11_PP_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = DHT11_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
}
/**
* ************************************************************************
* @brief 将DHT11配置为上拉输入模式
* ************************************************************************
*/
static void DHT11_UP_IN(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = DHT11_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
}
/**
* ************************************************************************
* @brief 读取字节
* @return temp
* ************************************************************************
*/
uint8_t DHT11_ReadByte(void)
{
uint8_t i, temp = 0;
for (i = 0; i < 8; i++)
{
while (DHT11_ReadPin == 0); // 等待低电平结束
TIM4_Delayus(40); // 延时 40 微秒
if (DHT11_ReadPin == 1)
{
while (DHT11_ReadPin == 1); // 等待高电平结束
temp |= (uint8_t)(0X01 << (7 - i)); // 先发送高位
}
else
{
temp &= (uint8_t)~(0X01 << (7 - i));
}
}
return temp;
}
/**
* ************************************************************************
* @brief 读取一次数据
* @param[in] DHT11_Data 定义的结构体变量
* @return 0或1(数据校验是否成功)
* @note 它首先向DHT11发送启动信号,然后等待DHT11的应答。如果DHT11正确应答,
* 则继续读取湿度整数、湿度小数、温度整数、温度小数和校验和数据,
* 并计算校验和以进行数据校验
* ************************************************************************
*/
uint8_t DHT11_ReadData(DHT11_Data_TypeDef *DHT11_Data)
{
DHT11_PP_OUT(); // 主机输出,主机拉低
DHT11_PULL_0;
TIM4_Delayms(18); // 延时 18 ms
DHT11_PULL_1; // 主机拉高,延时 30 us
TIM4_Delayus(30);
DHT11_UP_IN(); // 主机输入,获取 DHT11 数据
if (DHT11_ReadPin == 0) // 收到从机应答
{
while (DHT11_ReadPin == 0); // 等待从机应答的低电平结束
while (DHT11_ReadPin == 1); // 等待从机应答的高电平结束
/*开始接收数据*/
DHT11_Data->humi_int = DHT11_ReadByte();
DHT11_Data->humi_dec = DHT11_ReadByte();
DHT11_Data->temp_int = DHT11_ReadByte();
DHT11_Data->temp_dec = DHT11_ReadByte();
DHT11_Data->check_sum = DHT11_ReadByte();
DHT11_PP_OUT(); // 读取结束,主机拉高
DHT11_PULL_1;
// 数据校验
if (DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_dec + DHT11_Data->temp_int + DHT11_Data->temp_dec)
{
return 1;
}
else
{
return 0;
}
}
else // 未收到从机应答
{
return 0;
}
}
DHT11.h
/**
* ************************************************************************
*
* @file My_DHT11.h
* @author zxr
* @brief
*
* ************************************************************************
* @copyright Copyright (c) 2024 zxr
* ************************************************************************
*/
#ifndef __MY_DHT11_H
#define __MY_DHT11_H
#include "stm32f10x.h" // Device header
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_Pin_7
#define DHT11_PULL_1 GPIO_SetBits(DHT11_PORT, DHT11_PIN)
#define DHT11_PULL_0 GPIO_ResetBits(DHT11_PORT, DHT11_PIN)
#define DHT11_ReadPin GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)
/**
* ************************************************************************
* @brief 存储传感器数据的结构体
*
*
* ************************************************************************
*/
typedef struct
{
uint8_t humi_int; // 湿度的整数部分
uint8_t humi_dec; // 湿度的小数部分
uint8_t temp_int; // 温度的整数部分
uint8_t temp_dec; // 温度的小数部分
uint8_t check_sum; // 校验和
} DHT11_Data_TypeDef;
uint8_t DHT11_ReadData(DHT11_Data_TypeDef* DHT11_Data);
void DHT11_Init(void);
#endif
USART.c
/**
************************************************************
************************************************************
************************************************************
* 文件名: usart.c
*
* 作者: 张继瑞
*
* 日期: 2016-11-23
*
* 版本: V1.0
*
* 说明: 单片机串口外设初始化,格式化打印
*
* 修改记录:
************************************************************
************************************************************
************************************************************
**/
//硬件驱动
#include "usart.h"
#include "delay.h"
//C库
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
/*
************************************************************
* 函数名称: Usart1_Init
*
* 函数功能: 串口1初始化
*
* 入口参数: baud:设定的波特率
*
* 返回参数: 无
*
* 说明: TX-PA9 RX-PA10
************************************************************
*/
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);
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); //使能接收中断
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); //使能接收中断
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, unsigned char *str, unsigned short len)
{
unsigned short count = 0;
for(; count < len; count++)
{
USART_SendData(USARTx, *str++); //发送数据
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成
}
}
/*
************************************************************
* 函数名称: UsartPrintf
*
* 函数功能: 格式化打印
*
* 入口参数: USARTx:串口组
* fmt:不定长参
*
* 返回参数: 无
*
* 说明:
************************************************************
*/
void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...)
{
unsigned char UsartPrintfBuf[296];
va_list ap;
unsigned char *pStr = UsartPrintfBuf;
va_start(ap, fmt);
vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化
va_end(ap);
while(*pStr != 0)
{
USART_SendData(USARTx, *pStr++);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
}
/*
************************************************************
* 函数名称: USART1_IRQHandler
*
* 函数功能: 串口1收发中断
*
* 入口参数: 无
*
* 返回参数: 无
*
* 说明:
************************************************************
*/
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
USART_ClearFlag(USART1, USART_FLAG_RXNE);
}
}
USART.h
#ifndef _USART_H_
#define _USART_H_
#include "stm32f10x.h"
#define USART_DEBUG USART1 //调试打印所使用的串口组
void Usart1_Init(unsigned int baud);
void Usart2_Init(unsigned int baud);
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len);
void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...);
#endif
8266.c
8266
需要修改的地方有: #define ESP8266_WIFI_INFO "AT+CWJAP=\"WIFI名称\",\"WIFI密码\"\r\n" #define ESP8266_ONENET_INFO "AT+CIPSTART=\"TCP\",\"broker.emqx.io\",1883\r\n"
/**
************************************************************
************************************************************
************************************************************
* 文件名: esp8266.c
*
* 作者: 张继瑞
*
* 日期: 2017-05-08
*
* 版本: V1.0
*
* 说明: ESP8266的简单驱动
*
* 修改记录:
************************************************************
************************************************************
************************************************************
**/
//单片机头文件
#include "stm32f10x.h"
//网络设备驱动
#include "esp8266.h"
//硬件驱动
#include "Delay.h"
#include "usart.h"
//C库
#include <string.h>
#include <stdio.h>
#define ESP8266_WIFI_INFO "AT+CWJAP=\"WIFI名称\",\"密码\"\r\n"
#define ESP8266_ONENET_INFO "AT+CIPSTART=\"TCP\",\"broker.emqx.io\",1883\r\n"
unsigned char esp8266_buf[128];
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
//==========================================================
// 函数名称: ESP8266_Clear
//
// 函数功能: 清空缓存
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Clear(void)
{
memset(esp8266_buf, 0, sizeof(esp8266_buf));
esp8266_cnt = 0;
}
//==========================================================
// 函数名称: ESP8266_WaitRecive
//
// 函数功能: 等待接收完成
//
// 入口参数: 无
//
// 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成
//
// 说明: 循环调用检测是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(void)
{
if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
return REV_WAIT;
if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
{
esp8266_cnt = 0; //清0接收计数
return REV_OK; //返回接收完成标志
}
esp8266_cntPre = esp8266_cnt; //置为相同
return REV_WAIT; //返回接收未完成标志
}
//==========================================================
// 函数名称: ESP8266_SendCmd
//
// 函数功能: 发送命令
//
// 入口参数: cmd:命令
// res:需要检查的返回指令
//
// 返回参数: 0-成功 1-失败
//
// 说明:
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res)
{
unsigned char timeOut = 200;
Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
while(timeOut--)
{
if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
{
if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词
{
ESP8266_Clear(); //清空缓存
return 0;
}
}
Delay_ms(10);
}
return 1;
}
//==========================================================
// 函数名称: ESP8266_SendData
//
// 函数功能: 发送数据
//
// 入口参数: data:数据
// len:长度
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_SendData(unsigned char *data, unsigned short len)
{
char cmdBuf[32];
ESP8266_Clear(); //清空接收缓存
sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len); //发送命令
if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据
{
Usart_SendString(USART2, data, len); //发送设备连接请求数据
}
}
//==========================================================
// 函数名称: ESP8266_GetIPD
//
// 函数功能: 获取平台返回的数据
//
// 入口参数: 等待的时间(乘以10ms)
//
// 返回参数: 平台返回的原始数据
//
// 说明: 不同网络设备返回的格式不同,需要去调试
// 如ESP8266的返回格式为 "+IPD,x:yyy" x代表数据长度,yyy是数据内容
//==========================================================
unsigned char *ESP8266_GetIPD(unsigned short timeOut)
{
char *ptrIPD = NULL;
do
{
if(ESP8266_WaitRecive() == REV_OK) //如果接收完成
{
ptrIPD = strstr((char *)esp8266_buf, "IPD,"); //搜索“IPD”头
if(ptrIPD == NULL) //如果没找到,可能是IPD头的延迟,还是需要等待一会,但不???超过设定的时间
{
//UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");
}
else
{
ptrIPD = strchr(ptrIPD, ':'); //找到':'
if(ptrIPD != NULL)
{
ptrIPD++;
return (unsigned char *)(ptrIPD);
}
else
return NULL;
}
}
Delay_ms(5);
timeOut--; //延时等待
} while(timeOut>0);
return NULL; //超时还未找到,返回空指针
}
//==========================================================
// 函数名称: ESP8266_Init
//
// 函数功能: 初始化ESP8266
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//ESP8266复位引脚
GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Initure.GPIO_Pin = GPIO_Pin_14; //GPIOC14-复位
GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_Initure);
GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_RESET);
Delay_ms(250);
GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_SET);
Delay_ms(500);
ESP8266_Clear();
UsartPrintf(USART_DEBUG, "1. AT\r\n");
while(ESP8266_SendCmd("AT\r\n", "OK"))
Delay_ms(500);
UsartPrintf(USART_DEBUG, "1. RST\r\n");
ESP8266_SendCmd("AT+RST\r\n", "");
Delay_ms(500);
ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
Delay_ms(500);
UsartPrintf(USART_DEBUG, "2. CWMODE\r\n");
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
Delay_ms(500);
UsartPrintf(USART_DEBUG, "3. AT+CWDHCP\r\n");
while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
Delay_ms(500);
UsartPrintf(USART_DEBUG, "4. CWJAP\r\n");
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
Delay_ms(500);
UsartPrintf(USART_DEBUG, "5. CIPSTART\r\n");
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
Delay_ms(500);
UsartPrintf(USART_DEBUG, "6. ESP8266 Init OK\r\n");
}
//==========================================================
// 函数名称: USART2_IRQHandler
//
// 函数功能: 串口2收发中断
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
{
if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆
esp8266_buf[esp8266_cnt++] = USART2->DR;
USART_ClearFlag(USART2, USART_FLAG_RXNE);
}
}
8266.h
#ifndef _ESP8266_H_
#define _ESP8266_H_
#define REV_OK 0 //接收完成标志
#define REV_WAIT 1 //接收未完成标志
void ESP8266_Init(void);
void ESP8266_Clear(void);
void ESP8266_SendData(unsigned char *data, unsigned short len);
unsigned char *ESP8266_GetIPD(unsigned short timeOut);
#endif
MQTT.c
/**
************************************************************
************************************************************
************************************************************
* 文件名: MqttKit.c
*
* 作者: 张继瑞
*
* 日期: 2018-04-27
*
* 版本: V1.6
*
* 说明: MQTT协议
*
* 修改记录: V1.1:解决MQTT_PacketSubscribe订阅不为2个topic
* 个数时协议错误的bug
* V1.2:修复MQTT_PacketCmdResp的bug
* V1.3:将strncpy替换为memcpy,解决潜在bug
* V1.4:修复 MQTT_PacketPublishAck
* MQTT_PacketPublishRel
* 函数封包错误的bug
* V1.5:增加 MQTT_UnPacketCmd
* MQTT_UnPacketPublish
* 接口对消息内容长度的提取参数
* V1.6:增加二进制文件上传接口
************************************************************
************************************************************
************************************************************
**/
//协议头文件
#include "MqttKit.h"
//C库
#include <string.h>
#include <stdio.h>
#define CMD_TOPIC_PREFIX "$creq"
//==========================================================
// 函数名称: EDP_NewBuffer
//
// 函数功能: 申请内存
//
// 入口参数: edpPacket:包结构体
// size:大小
//
// 返回参数: 无
//
// 说明: 1.可使用动态分配来分配内存
// 2.可使用局部或全局数组来指定内存
//==========================================================
void MQTT_NewBuffer(MQTT_PACKET_STRUCTURE *mqttPacket, uint32 size)
{
uint32 i = 0;
if(mqttPacket->_data == NULL)
{
mqttPacket->_memFlag = MEM_FLAG_ALLOC;
mqttPacket->_data = (uint8 *)MQTT_MallocBuffer(size);
if(mqttPacket->_data != NULL)
{
mqttPacket->_len = 0;
mqttPacket->_size = size;
for(; i < mqttPacket->_size; i++)
mqttPacket->_data[i] = 0;
}
}
else
{
mqttPacket->_memFlag = MEM_FLAG_STATIC;
for(; i < mqttPacket->_size; i++)
mqttPacket->_data[i] = 0;
mqttPacket->_len = 0;
if(mqttPacket->_size < size)
mqttPacket->_data = NULL;
}
}
//==========================================================
// 函数名称: MQTT_DeleteBuffer
//
// 函数功能: 释放数据内存
//
// 入口参数: edpPacket:包结构体
//
// 返回参数: 无
//
// 说明:
//==========================================================
void MQTT_DeleteBuffer(MQTT_PACKET_STRUCTURE *mqttPacket)
{
if(mqttPacket->_memFlag == MEM_FLAG_ALLOC)
MQTT_FreeBuffer(mqttPacket->_data);
mqttPacket->_data = NULL;
mqttPacket->_len = 0;
mqttPacket->_size = 0;
mqttPacket->_memFlag = MEM_FLAG_NULL;
}
int32 MQTT_DumpLength(size_t len, uint8 *buf)
{
int32 i = 0;
for(i = 1; i <= 4; ++i)
{
*buf = len % 128;
len >>= 7;
if(len > 0)
{
*buf |= 128;
++buf;
}
else
{
return i;
}
}
return -1;
}
int32 MQTT_ReadLength(const uint8 *stream, int32 size, uint32 *len)
{
int32 i;
const uint8 *in = stream;
uint32 multiplier = 1;
*len = 0;
for(i = 0; i < size; ++i)
{
*len += (in[i] & 0x7f) * multiplier;
if(!(in[i] & 0x80))
{
return i + 1;
}
multiplier <<= 7;
if(multiplier >= 2097152) //128 * *128 * *128
{
return -2; // error, out of range
}
}
return -1; // not complete
}
//==========================================================
// 函数名称: MQTT_UnPacketRecv
//
// 函数功能: MQTT数据接收类型判断
//
// 入口参数: dataPtr:接收的数据指针
//
// 返回参数: 0-成功 其他-失败原因
//
// 说明:
//==========================================================
uint8 MQTT_UnPacketRecv(uint8 *dataPtr)
{
uint8 status = 255;
uint8 type = dataPtr[0] >> 4; //类型检查
if(type < 1 || type > 14)
return status;
if(type == MQTT_PKT_PUBLISH)
{
uint8 *msgPtr;
uint32 remain_len = 0;
msgPtr = dataPtr + MQTT_ReadLength(dataPtr + 1, 4, &remain_len) + 1;
if(remain_len < 2 || dataPtr[0] & 0x01) //retain
return 255;
if(remain_len < ((uint16)msgPtr[0] << 8 | msgPtr[1]) + 2)
return 255;
if(strstr((int8 *)msgPtr + 2, CMD_TOPIC_PREFIX) != NULL) //如果是命令下发
status = MQTT_PKT_CMD;
else
status = MQTT_PKT_PUBLISH;
}
else
status = type;
return status;
}
//==========================================================
// 函数名称: MQTT_PacketConnect
//
// 函数功能: 连接消息组包
//
// 入口参数: user:用户名:产品ID
// password:密码:鉴权信息或apikey
// devid:设备ID
// cTime:连接保持时间
// clean_session:离线消息清除标志
// qos:重发标志
// will_topic:异常离线topic
// will_msg:异常离线消息
// will_retain:消息推送标志
// mqttPacket:包指针
//
// 返回参数: 0-成功 其他-失败
//
// 说明:
//==========================================================
uint8 MQTT_PacketConnect(const int8 *user, const int8 *password, const int8 *devid,
uint16 cTime, uint1 clean_session, uint1 qos,
const int8 *will_topic, const int8 *will_msg, int32 will_retain,
MQTT_PACKET_STRUCTURE *mqttPacket)
{
uint8 flags = 0;
uint8 will_topic_len = 0;
uint16 total_len = 15;
int16 len = 0, devid_len = strlen(devid);
if(!devid)
return 1;
total_len += devid_len + 2;
//断线后,是否清理离线消息:1-清理 0-不清理--------------------------------------------
if(clean_session)
{
flags |= MQTT_CONNECT_CLEAN_SESSION;
}
//异常掉线情况下,服务器发布的topic------------------------------------------------------
if(will_topic)
{
flags |= MQTT_CONNECT_WILL_FLAG;
will_topic_len = strlen(will_topic);
total_len += 4 + will_topic_len + strlen(will_msg);
}
//qos级别--主要用于PUBLISH(发布态)消息的,保证消息传递的次数-----------------------------
switch((unsigned char)qos)
{
case MQTT_QOS_LEVEL0:
flags |= MQTT_CONNECT_WILL_QOS0; //最多一次
break;
case MQTT_QOS_LEVEL1:
flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS1); //最少一次
break;
case MQTT_QOS_LEVEL2:
flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS2); //只有一次
break;
default:
return 2;
}
//主要用于PUBLISH(发布态)的消息,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它。如果不设那么推送至当前订阅的就释放了
if(will_retain)
{
flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_RETAIN);
}
//账号为空 密码为空---------------------------------------------------------------------
if(!user || !password)
{
return 3;
}
flags |= MQTT_CONNECT_USER_NAME | MQTT_CONNECT_PASSORD;
total_len += strlen(user) + strlen(password) + 4;
//分配内存-----------------------------------------------------------------------------
MQTT_NewBuffer(mqttPacket, total_len);
if(mqttPacket->_data == NULL)
return 4;
memset(mqttPacket->_data, 0, total_len);
/*************************************固定头部***********************************************/
//固定头部----------------------连接请求类型---------------------------------------------
mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_CONNECT << 4;
//固定头部----------------------剩余长度值-----------------------------------------------
len = MQTT_DumpLength(total_len - 5, mqttPacket->_data + mqttPacket->_len);
if(len < 0)
{
MQTT_DeleteBuffer(mqttPacket);
return 5;
}
else
mqttPacket->_len += len;
/*************************************可变头部***********************************************/
//可变头部----------------------协议名长度 和 协议名--------------------------------------
mqttPacket->_data[mqttPacket->_len++] = 0;
mqttPacket->_data[mqttPacket->_len++] = 4;
mqttPacket->_data[mqttPacket->_len++] = 'M';
mqttPacket->_data[mqttPacket->_len++] = 'Q';
mqttPacket->_data[mqttPacket->_len++] = 'T';
mqttPacket->_data[mqttPacket->_len++] = 'T';
//可变头部----------------------protocol level 4-----------------------------------------
mqttPacket->_data[mqttPacket->_len++] = 4;
//可变头部----------------------连接标志(该函数开头处理的数据)-----------------------------
mqttPacket->_data[mqttPacket->_len++] = flags;
//可变头部----------------------保持连接的时间(秒)----------------------------------------
mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(cTime);
mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(cTime);
/*************************************消息体************************************************/
//消息体----------------------------devid长度、devid-------------------------------------
mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(devid_len);
mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(devid_len);
strncat((int8 *)mqttPacket->_data + mqttPacket->_len, devid, devid_len);
mqttPacket->_len += devid_len;