发布时间:2024-10-05 16:01
目录
一、项目描述
二、原理图
三、实物
四、代码
六、论文
七、资料清单
1.通过串口获取电表数据,并在OLED液晶上显示;
2. 通过ESP8266WIFI模块将电表数据上传到云平台和手机APP上
手机APP
云平台
ESP8266代码
#include \"bsp_esp8266.h\"
#include \"common.h\"
#include
#include
#include
#include \"bsp_SysTick.h\"
#include \"test.h\"
#include \"bsp_usart1.h\"
/*
管脚连接关系
ESP8266 单片机
VCC ---------- 5V
GND ---------- GND
TXD ---------- PB11 (RX)
RXD ---------- PB10 (TX)
RST ---------- PA8 (可以不连接)
IO_0 ---------- 未连接
模块的 RST引脚和IO_0引脚 默认为高电平, 所以RST引脚不连接也可以正常使用。
模块必须5V供电,3.3V供电时不能正常工作。
*/
static void ESP8266_GPIO_Config ( void );
static void ESP8266_USART_Config ( void );
static void ESP8266_USART_NVIC_Configuration ( void );
struct STRUCT_USARTx_Fram strEsp8266_Fram_Record = { 0 }; //ESP8266接收数据结构体
/**
* @brief ESP8266初始化函数
* @param 无
* @retval 无
*/
void ESP8266_Init ( void )
{
ESP8266_GPIO_Config (); //ESP8266模块使用到的IO口初始化
ESP8266_USART_Config (); //与ESP8266模块连接的串口初始化
macESP8266_RST_HIGH_LEVEL(); //ESP8266模块 复位脚设置为高电平
macESP8266_CH_DISABLE(); //片选引脚不选中
}
/**
* @brief 初始化ESP8266用到的GPIO引脚
* @param 无
* @retval 无
*/
static void ESP8266_GPIO_Config ( void )
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/* 配置 CH_PD 引脚*/
macESP8266_CH_PD_APBxClock_FUN ( macESP8266_CH_PD_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = macESP8266_CH_PD_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init ( macESP8266_CH_PD_PORT, & GPIO_InitStructure );
/* 配置 RST 引脚*/
macESP8266_RST_APBxClock_FUN ( macESP8266_RST_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = macESP8266_RST_PIN;
GPIO_Init ( macESP8266_RST_PORT, & GPIO_InitStructure );
}
/**
* @brief 初始化ESP8266用到的 USART
* @param 无
* @retval 无
*/
static void ESP8266_USART_Config ( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* config USART clock */
macESP8266_USART_APBxClock_FUN ( macESP8266_USART_CLK, ENABLE );
macESP8266_USART_GPIO_APBxClock_FUN ( macESP8266_USART_GPIO_CLK, ENABLE );
/* USART GPIO config */
/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = macESP8266_USART_TX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( macESP8266_USART_TX_PORT, &GPIO_InitStructure );
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Pin = macESP8266_USART_RX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( macESP8266_USART_RX_PORT, &GPIO_InitStructure );
/* USART1 mode config */
USART_InitStructure.USART_BaudRate = macESP8266_USART_BAUD_RATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init( macESP8266_USARTx, &USART_InitStructure );
/* 中断配置 */
USART_ITConfig ( macESP8266_USARTx, USART_IT_RXNE, ENABLE ); //使能串口接收中断
USART_ITConfig ( macESP8266_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断
ESP8266_USART_NVIC_Configuration ();
USART_Cmd( macESP8266_USARTx, ENABLE );
}
/**
* @brief 配置 ESP8266 USART 的 NVIC 中断
* @param 无
* @retval 无
*/
static void ESP8266_USART_NVIC_Configuration ( void )
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig ( macNVIC_PriorityGroup_x );
/* Enable the USART2 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = macESP8266_USART_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
}
/**
* @brief ESP8266 USART 中断函数
* @param 无
* @retval 无
*/
void macESP8266_USART_INT_FUN ( void )
{
// char * pCh;
// char cStr [ 100 ] = { 0 };
uint8_t ucCh;
if ( USART_GetITStatus ( macESP8266_USARTx, USART_IT_RXNE ) != RESET ) //若串口中有数据进来,存储接收到的数据
{
ucCh = USART_ReceiveData( macESP8266_USARTx );
if ( strEsp8266_Fram_Record .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) ) //预留1个字节写结束符
strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ++ ] = ucCh;
}
//进入空闲中断,说明一组数据接收完成
if ( USART_GetITStatus( macESP8266_USARTx, USART_IT_IDLE ) == SET ) //数据帧接收完毕
{
strEsp8266_Fram_Record .InfBit .FramFinishFlag = 1; //设置数据接收完成标志
ucCh = USART_ReceiveData( macESP8266_USARTx ); //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)
USART_ClearITPendingBit( macESP8266_USARTx, USART_IT_IDLE );
//检测与服务器的连接是否断开 只在Station模式下使用
ucTcpClosedFlag = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"CLOSED\\r\\n\" ) ? 1 : 0;
}
}
/*
* 函数名:ESP8266_Rst
* 描述 :重启WF-ESP8266模块
* 输入 :无
* 返回 : 无
* 调用 :被 ESP8266_AT_Test 调用
*/
void ESP8266_Rst ( void )
{
#if 1
ESP8266_Cmd ( \"AT+RST\", \"OK\", \"ready\", 2500 );
#else
macESP8266_RST_LOW_LEVEL();
Delay_ms ( 500 );
macESP8266_RST_HIGH_LEVEL();
#endif
}
/*
* 函数名:ESP8266_Cmd
* 描述 :对WF-ESP8266模块发送AT指令
* 输入 :cmd,待发送的指令
* reply1,reply2,期待的响应,为NULL表不需响应,两者为或逻辑关系
* waittime,等待响应的时间
* 返回 : 1,指令发送成功
* 0,指令发送失败
* 调用 :被外部调用
*/
bool ESP8266_Cmd ( char * cmd, char * reply1, char * reply2, u32 waittime )
{
strEsp8266_Fram_Record .InfBit .FramLength = 0; //接收缓冲区数据长度清0
macESP8266_Usart ( \"%s\\r\\n\", cmd ); //向模块发送命令
if ( ( reply1 == 0 ) && ( reply2 == 0 ) ) //不需要接收数据
return true;
Delay_ms ( waittime ); //延时 等待接收模块返回的指令
strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ] = \'\\0\'; //接收到的字符串末尾加入 结束符
macPC_Usart ( \"%s\", strEsp8266_Fram_Record .Data_RX_BUF ); //调试口打印接收到模块的指令
if ( ( reply1 != 0 ) && ( reply2 != 0 ) )
return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) ||
( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) );
else if ( reply1 != 0 )
return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) );
else
return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) );
}
/*
* 函数名:ESP8266_AT_Test
* 描述 :对WF-ESP8266模块进行AT测试启动
* 输入 :无
* 返回 : 无
* 调用 :被外部调用
*/
//void ESP8266_AT_Test ( void )
//{
// macESP8266_RST_HIGH_LEVEL();
//
// Delay_ms ( 1000 );
//
// while ( ! ESP8266_Cmd ( \"AT\", \"OK\", NULL, 200 ) ) ESP8266_Rst ();
//}
void ESP8266_AT_Test ( void )
{
char count = 0;
macESP8266_RST_HIGH_LEVEL();
Delay_ms ( 1000 );
while ( count < 10 )
{
if( ESP8266_Cmd ( \"AT\", \"OK\", NULL, 500 ) ) return; //如果接收到模块返回的OK指令,就直接返回
ESP8266_Rst(); //否则复位模块,重新发送AT测试指令
++ count;
//Main_Uart2();
}
}
/*
* 函数名:ESP8266_Net_Mode_Choose
* 描述 :选择WF-ESP8266模块的工作模式
* 输入 :enumMode,工作模式
* 返回 : 1,选择成功
* 0,选择失败
* 调用 :被外部调用
*/
bool ESP8266_Net_Mode_Choose ( ENUM_Net_ModeTypeDef enumMode )
{
switch ( enumMode )
{
case STA:
return ESP8266_Cmd ( \"AT+CWMODE=1\", \"OK\", \"no change\", 2500 );
case AP:
return ESP8266_Cmd ( \"AT+CWMODE=2\", \"OK\", \"no change\", 2500 );
case STA_AP:
return ESP8266_Cmd ( \"AT+CWMODE=3\", \"OK\", \"no change\", 2500 );
default:
return false;
}
}
/*
* 函数名:ESP8266_JoinAP
* 描述 :WF-ESP8266模块连接外部WiFi
* 输入 :pSSID,WiFi名称字符串
* :pPassWord,WiFi密码字符串
* 返回 : 1,连接成功
* 0,连接失败
* 调用 :被外部调用
*/
bool ESP8266_JoinAP ( char * pSSID, char * pPassWord )
{
char cCmd [120];
sprintf ( cCmd, \"AT+CWJAP=\\\"%s\\\",\\\"%s\\\"\", pSSID, pPassWord );
return ESP8266_Cmd ( cCmd, \"OK\", NULL, 5000 );
}
/*
* 函数名:ESP8266_BuildAP
* 描述 :WF-ESP8266模块创建WiFi热点
* 输入 :pSSID,WiFi名称字符串
* :pPassWord,WiFi密码字符串
* :enunPsdMode,WiFi加密方式代号字符串
* 返回 : 1,创建成功
* 0,创建失败
* 调用 :被外部调用
*/
bool ESP8266_BuildAP ( char * pSSID, char * pPassWord, ENUM_AP_PsdMode_TypeDef enunPsdMode )
{
char cCmd [120];
sprintf ( cCmd, \"AT+CWSAP=\\\"%s\\\",\\\"%s\\\",1,%d\", pSSID, pPassWord, enunPsdMode );//名称、密码、通道号(默认为1)、加密方式
return ESP8266_Cmd ( cCmd, \"OK\", 0, 1000 );
}
/*
* 函数名:ESP8266_Enable_MultipleId
* 描述 :WF-ESP8266模块启动多连接
* 输入 :enumEnUnvarnishTx,配置是否多连接
* 返回 : 1,配置成功
* 0,配置失败
* 调用 :被外部调用
*/
bool ESP8266_Enable_MultipleId ( FunctionalState enumEnUnvarnishTx )
{
char cStr [20];
sprintf ( cStr, \"AT+CIPMUX=%d\", ( enumEnUnvarnishTx ? 1 : 0 ) );
return ESP8266_Cmd ( cStr, \"OK\", 0, 500 );
}
/*
* 函数名:ESP8266_Link_Server
* 描述 :WF-ESP8266模块连接外部服务器
* 输入 :enumE,网络协议
* :ip,服务器IP字符串
* :ComNum,服务器端口字符串
* :id,模块连接服务器的ID
* 返回 : 1,连接成功
* 0,连接失败
* 调用 :被外部调用
*/
bool ESP8266_Link_Server ( ENUM_NetPro_TypeDef enumE, char * ip, char * ComNum, ENUM_ID_NO_TypeDef id )
{
char cStr [100] = { 0 }, cCmd [120];
switch ( enumE )
{
case enumTCP:
sprintf ( cStr, \"\\\"%s\\\",\\\"%s\\\",%s\", \"TCP\", ip, ComNum );
break;
case enumUDP:
sprintf ( cStr, \"\\\"%s\\\",\\\"%s\\\",%s\", \"UDP\", ip, ComNum );
break;
default:
break;
}
if ( id < 5 )
sprintf ( cCmd, \"AT+CIPSTART=%d,%s\", id, cStr );
else
sprintf ( cCmd, \"AT+CIPSTART=%s\", cStr );
return ESP8266_Cmd ( cCmd, \"OK\", \"ALREAY CONNECT\", 4000 );
}
/*
* 函数名:ESP8266_StartOrShutServer
* 描述 :WF-ESP8266模块开启或关闭服务器模式
* 输入 :enumMode,开启/关闭
* :pPortNum,服务器端口号字符串
* :pTimeOver,服务器超时时间字符串,单位:秒
* 返回 : 1,操作成功
* 0,操作失败
* 调用 :被外部调用
*/
bool ESP8266_StartOrShutServer ( FunctionalState enumMode, char * pPortNum, char * pTimeOver )
{
char cCmd1 [120], cCmd2 [120];
if ( enumMode ) //开启服务
{
sprintf ( cCmd1, \"AT+CIPSERVER=%d,%s\", 1, pPortNum );
sprintf ( cCmd2, \"AT+CIPSTO=%s\", pTimeOver );
return ( ESP8266_Cmd ( cCmd1, \"OK\", 0, 500 ) &&
ESP8266_Cmd ( cCmd2, \"OK\", 0, 500 ) );
}
else //关闭服务
{
sprintf ( cCmd1, \"AT+CIPSERVER=%d,%s\", 0, pPortNum );
return ESP8266_Cmd ( cCmd1, \"OK\", 0, 500 );
}
}
/*
* 函数名:ESP8266_Get_LinkStatus
* 描述 :获取 WF-ESP8266 的连接状态,较适合单端口时使用
* 输入 :无
* 返回 : 2,获得ip
* 3,建立连接
* 3,失去连接
* 0,获取状态失败
* 调用 :被外部调用
*/
uint8_t ESP8266_Get_LinkStatus ( void )
{
if ( ESP8266_Cmd ( \"AT+CIPSTATUS\", \"OK\", 0, 500 ) )
{
if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"STATUS:2\\r\\n\" ) ) //获得ip
return 2;
else if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"STATUS:3\\r\\n\" ) ) //建立连接
return 3;
else if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"STATUS:4\\r\\n\" ) ) //失去连接
return 4;
}
return 0;
}
/*
* 函数名:ESP8266_Get_IdLinkStatus
* 描述 :获取 WF-ESP8266 的端口(Id)连接状态,较适合多端口时使用
* 输入 :无
* 返回 : 端口(Id)的连接状态,低5位为有效位,分别对应Id5~0,某位若置1表该Id建立了连接,若被清0表该Id未建立连接
* 调用 :被外部调用
*/
uint8_t ESP8266_Get_IdLinkStatus ( void )
{
uint8_t ucIdLinkStatus = 0x00;
if ( ESP8266_Cmd ( \"AT+CIPSTATUS\", \"OK\", 0, 500 ) )
{
if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"+CIPSTATUS:0,\" ) )
ucIdLinkStatus |= 0x01;
else
ucIdLinkStatus &= ~ 0x01;
if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"+CIPSTATUS:1,\" ) )
ucIdLinkStatus |= 0x02;
else
ucIdLinkStatus &= ~ 0x02;
if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"+CIPSTATUS:2,\" ) )
ucIdLinkStatus |= 0x04;
else
ucIdLinkStatus &= ~ 0x04;
if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"+CIPSTATUS:3,\" ) )
ucIdLinkStatus |= 0x08;
else
ucIdLinkStatus &= ~ 0x08;
if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"+CIPSTATUS:4,\" ) )
ucIdLinkStatus |= 0x10;
else
ucIdLinkStatus &= ~ 0x10;
}
return ucIdLinkStatus;
}
/*
* 函数名:ESP8266_Inquire_ApIp
* 描述 :获取 F-ESP8266 的 AP IP
* 输入 :pApIp,存放 AP IP 的数组的首地址
* ucArrayLength,存放 AP IP 的数组的长度
* 返回 : 0,获取失败
* 1,获取成功
* 调用 :被外部调用
*/
uint8_t ESP8266_Inquire_ApIp ( char * pApIp, uint8_t ucArrayLength )
{
char uc;
char * pCh;
ESP8266_Cmd ( \"AT+CIFSR\", \"OK\", 0, 500 );
pCh = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"APIP,\\\"\" );
if ( pCh )
pCh += 6;
else
return 0;
for ( uc = 0; uc < ucArrayLength; uc ++ )
{
pApIp [ uc ] = * ( pCh + uc );
if ( pApIp [ uc ] == \'\\\"\' )
{
pApIp [ uc ] = \'\\0\';
break;
}
}
return 1;
}
/*
* 函数名:ESP8266_UnvarnishSend
* 描述 :配置WF-ESP8266模块进入透传发送
* 输入 :无
* 返回 : 1,配置成功
* 0,配置失败
* 调用 :被外部调用
*/
bool ESP8266_UnvarnishSend ( void )
{
if ( ! ESP8266_Cmd ( \"AT+CIPMODE=1\", \"OK\", 0, 500 ) )
return false;
return
ESP8266_Cmd ( \"AT+CIPSEND\", \"OK\", \">\", 500 );
}
/*
* 函数名:ESP8266_ExitUnvarnishSend
* 描述 :配置WF-ESP8266模块退出透传模式
* 输入 :无
* 返回 : 无
* 调用 :被外部调用
*/
void ESP8266_ExitUnvarnishSend ( void )
{
Delay_ms ( 1000 );
macESP8266_Usart ( \"+++\" );
Delay_ms ( 500 );
}
/*
* 函数名:ESP8266_SendString
* 描述 :WF-ESP8266模块发送字符串
* 输入 :enumEnUnvarnishTx,声明是否已使能了透传模式
* :pStr,要发送的字符串
* :ulStrLength,要发送的字符串的字节数
* :ucId,哪个ID发送的字符串
* 返回 : 1,发送成功
* 0,发送失败
* 调用 :被外部调用
*/
bool ESP8266_SendString ( FunctionalState enumEnUnvarnishTx, char * pStr, u32 ulStrLength, ENUM_ID_NO_TypeDef ucId )
{
char cStr [20];
bool bRet = false;
if ( enumEnUnvarnishTx )
{
macESP8266_Usart ( \"%s\", pStr );
bRet = true;
}
else
{
if ( ucId < 5 )
sprintf ( cStr, \"AT+CIPSEND=%d,%d\", ucId, ulStrLength + 2 );
else
sprintf ( cStr, \"AT+CIPSEND=%d\", ulStrLength + 2 );
ESP8266_Cmd ( cStr, \"> \", 0, 1000 );
bRet = ESP8266_Cmd ( pStr, \"SEND OK\", 0, 1000 );
}
return bRet;
}
/*
* 函数名:ESP8266_ReceiveString
* 描述 :WF-ESP8266模块接收字符串
* 输入 :enumEnUnvarnishTx,声明是否已使能了透传模式
* 返回 : 接收到的字符串首地址
* 调用 :被外部调用
*/
char * ESP8266_ReceiveString ( FunctionalState enumEnUnvarnishTx )
{
char * pRecStr = 0;
strEsp8266_Fram_Record .InfBit .FramLength = 0;
strEsp8266_Fram_Record .InfBit .FramFinishFlag = 0;
while ( ! strEsp8266_Fram_Record .InfBit .FramFinishFlag );
strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ] = \'\\0\';
if ( enumEnUnvarnishTx )
pRecStr = strEsp8266_Fram_Record .Data_RX_BUF;
else
{
if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"+IPD\" ) )
pRecStr = strEsp8266_Fram_Record .Data_RX_BUF;
}
return pRecStr;
}
/*
* 函数名:ESP8266_CWLIF
* 描述 :查询已接入设备的IP
* 输入 :pStaIp,存放已接入设备的IP
* 返回 : 1,有接入设备
* 0,无接入设备
* 调用 :被外部调用
*/
uint8_t ESP8266_CWLIF ( char * pStaIp )
{
uint8_t uc, ucLen;
char * pCh, * pCh1;
ESP8266_Cmd ( \"AT+CWLIF\", \"OK\", 0, 100 );
pCh = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \",\" );
if ( pCh )
{
pCh1 = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, \"AT+CWLIF\\r\\r\\n\" ) + 11;
ucLen = pCh - pCh1;
}
else
return 0;
for ( uc = 0; uc < ucLen; uc ++ )
pStaIp [ uc ] = * ( pCh1 + uc );
pStaIp [ ucLen ] = \'\\0\';
return 1;
}
/*
* 函数名:ESP8266_CIPAP
* 描述 :设置模块的 AP IP
* 输入 :pApIp,模块的 AP IP
* 返回 : 1,设置成功
* 0,设置失败
* 调用 :被外部调用
*/
uint8_t ESP8266_CIPAP ( char * pApIp )
{
char cCmd [ 30 ];
sprintf ( cCmd, \"AT+CIPAP=\\\"%s\\\"\", pApIp );
if ( ESP8266_Cmd ( cCmd, \"OK\", 0, 5000 ) )
return 1;
else
return 0;
}
chrome浏览器91版本SameSite by default cookies被移除后的解决方案,Chrome中跨域POST请求无法携带Cookie的解决方案
Vue2.7正式发布!代号为:Naruto(火影忍者),原生支持 Composition API +终于可以在Vue2项目中使用Vue3的新特性了,真香~
物联网之MQTT3.1.1和MQTT5协议 (5) PUBACK报文
redis——列表操作、事务(管道)、其他操作、django集成redis、celery介绍安装、celery快速使用
DEVOPS架构师 -- 05从零开始构建基于Kubernetes的DevOps平台
SpringCloud微服务实战——搭建企业级开发框架(四十三):多租户可配置的电子邮件发送系统设计与实现