广州网站建设流程,建设旅游网站,珠海市企业网站建设,网页无法访问是怎么回事前言 上一节我们学习了串口的简单使用#xff0c;本节我们增加难度#xff0c;做一个demo通过AT指令控制ESP8266#xff0c;使用DMA方式接收ESP8266发来的数据#xff0c;后续我们便开始通过ESP8266连接物联网云平台#xff0c;敬请关注。 一、准备 1. ESP8266硬件准备 准…前言 上一节我们学习了串口的简单使用本节我们增加难度做一个demo通过AT指令控制ESP8266使用DMA方式接收ESP8266发来的数据后续我们便开始通过ESP8266连接物联网云平台敬请关注。 一、准备 1. ESP8266硬件准备 准备ESP8266模块本实验使用便宜好用的ESP-01s模块某宝只需要5RMB左右很适合新手测试使用ESP-01S默认支持AT指令所谓AT指令是说WIFI厂商把复杂的TCP/IP协议坐在了ESP8266芯片内部了单片机只需要通过串口发送ATCMD形式的指令即可达到调用TCP/IP协议栈的功能极大的降低了学习和开发成本。
2. ESP8266固件准备 更新AT MQTT固件进入安信可官网下载MQTT透传固件固件号1471参考之前文章即可轻松完成烧录。
3. 硬件连接 注意串口2连接ESP01S模块串口1连接串口工具
二、实例 1. 建立工程 将上节串口实验代码复制并修改名字为7.WiFi通过CubeMx关闭Usart2打开Usart3并设置DMA方法方式接收数据。 取消勾选USART3 global interrupt中断回调打开DMA1 channel3 global interrupt。 Keil打开工程查看stm32f1xx_it.c中相关配置如下
/*** brief This function handles DMA1 channel3 global interrupt.*/
void DMA1_Channel3_IRQHandler(void)
{/* USER CODE BEGIN DMA1_Channel3_IRQn 0 *//* USER CODE END DMA1_Channel3_IRQn 0 */HAL_DMA_IRQHandler(hdma_usart3_rx);/* USER CODE BEGIN DMA1_Channel3_IRQn 1 *//* USER CODE END DMA1_Channel3_IRQn 1 */
}按照下图顺序创建Application/User/Driver文件夹。
同时在本地根目录中创建User文件夹文件夹中创建esp8266.c和esp8266.h两个文件。
在Keil中Application/User/Driver处右击选择Add Existing Files to Group ‘Application/user/Driver’…选择上述创建的esp8266.c文件将文件加入到工程。
将/User文件路径按照下图方式添加到工程以便能访问到esp8266.h文件
编辑esp8266.h内容如下增加#ifndef #define #endif格式文件防止因为重复包含头文件导致错误重点关注Uart_Frame_Record_t结构体该结构体内部访问共用体希望结构体直接访问共用体内元素需要增加#pragma anon_unions。
#ifndef __ESP8266_H
#define __ESP8266_H #include main.h
#include stdio.h
#include string.h
#include stdbool.h#pragma anon_unions// ESP8266 printf
#define ESP8266_USART(fmt, ...) USART_printf (fmt, ##__VA_ARGS__)
#define PC_USART(fmt, ...) printf(fmt, ##__VA_ARGS__) //这是串口打印函数串口1执行printf后会自动执行fput函数重定向了printf。#define RX_BUF_MAX_LEN 1024 //最大字节数//ESP8266模式选择
typedef enum
{STA,AP,STA_AP
}ENUM_Net_ModeTypeDef;//网络传输层协议枚举类型
typedef enum{enumTCP,enumUDP,
} ENUM_NetPro_TypeDef;
//连接号指定为该连接号可以防止其他计算机访问同一端口而发生错误
typedef enum{Multiple_ID_0 0,Multiple_ID_1 1,Multiple_ID_2 2,Multiple_ID_3 3,Multiple_ID_4 4,Single_ID_0 5,
} ENUM_ID_NO_TypeDef;//网络状态
typedef enum{WIFI_DISCONNECT 0,WIFI_CONNECTED 1,WIFI_GOT_IP 2,WIFI_CLOUD_CONNECTED 3,WIFI_CLOUD_FAILED 4,
} ENUM_WIFI_STATUS_TypeDef;typedef struct _Uart_Frame_Record_t //数据帧结构体
{char Data_RX_BUF[RX_BUF_MAX_LEN];union {__IO uint16_t InfAll;__IO uint16_t FramLength;}; }Uart_Frame_Record_t;extern Uart_Frame_Record_t Uart_Frame_Record;extern volatile int wifi_connect_status;//初始化和TCP功能函数
void ESP8266_Init(uint32_t bound);
void ESP8266_ATE0(void);
bool ESP8266_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,uint32_t time);
bool ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode);
bool ESP8266_JoinAP( char * pSSID, char * pPassWord );
bool ESP8266_Enable_MultipleId ( FunctionalState enumEnUnvarnishTx );
bool ESP8266_Link_Server(ENUM_NetPro_TypeDef enumE, char * ip, char * ComNum, ENUM_ID_NO_TypeDef id);
bool ESP8266_UnvarnishSend ( void );
void ESP8266_ExitUnvarnishSend ( void );
uint8_t ESP8266_Get_LinkStatus ( void );uint8_t AT_Printf(char* fmt,...);
uint8_t ESP8266_SendCMD(uint8_t *cmd, uint8_t *ack, uint16_t waittime);
uint8_t ESP8266_SendData(uint8_t *tbuf, uint16_t len);
uint8_t ESP8266_APInit(char *name, char *password);
uint8_t ESP8266_STAInit(void);
uint8_t ESP8266_STAConnect(char *name, char *password);#endifesp8266.c中内容如下所示
#include esp8266.h
#include usart.hvolatile int wifi_connect_status 0; Uart_Frame_Record_t Uart_Frame_Record { 0 }; //定义了一个数据帧结构体//对ESP8266模块发送AT指令
// cmd 待发送的指令
// ack1,ack2;期待的响应为NULL表不需响应两者为或逻辑关系
// time 等待响应时间
//返回1发送成功 0失败
bool ESP8266_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,uint32_t time)
{Uart_Frame_Record.FramLength 0; //重新接收新的数据包AT_Printf(%s\r\n, cmd);if(ack10ack20) //不需要接收数据{return true;}delay_ms(time); //延时Uart_Frame_Record.Data_RX_BUF[Uart_Frame_Record.FramLength ] \0;printf(------ %s, Uart_Frame_Record.Data_RX_BUF);if(ack1!0ack2!0){return ( ( bool ) strstr ( Uart_Frame_Record.Data_RX_BUF, ack1 ) || ( bool ) strstr ( Uart_Frame_Record.Data_RX_BUF, ack2 ) );}else if( ack1 ! 0 ) //strstr(s1,s2);检测s2是否为s1的一部分是返回该位置否则返回false它强制转换为bool类型了return ( ( bool ) strstr ( Uart_Frame_Record.Data_RX_BUF, ack1 ) );elsereturn ( ( bool ) strstr ( Uart_Frame_Record.Data_RX_BUF, ack2 ) );
}//发送恢复出厂默认设置指令将模块恢复成出厂设置
void ESP8266_ATE0(void)
{char count0;delay_ms(1000); while(count 10){if(ESP8266_Send_AT_Cmd(ATE0,OK,NULL,500)) {printf(OK\r\n);return;} count;}
}//发送恢复出厂默认设置指令将模块恢复成出厂设置
void ESP8266_AT_Restore(void)
{char count0;delay_ms(1000); while(count 10){if(ESP8266_Send_AT_Cmd(ATRESTORE,OK,NULL,500)) {printf(OK\r\n);return;} count;}
}//选择ESP8266的工作模式
// enumMode 模式类型
//成功返回true失败返回false
bool ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode)
{switch ( enumMode ){case STA:return ESP8266_Send_AT_Cmd ( ATCWMODE1, OK, no change, 2500 ); case AP:return ESP8266_Send_AT_Cmd ( ATCWMODE2, OK, no change, 2500 ); case STA_AP:return ESP8266_Send_AT_Cmd ( ATCWMODE3, OK, no change, 2500 ); default:return false;}
}//ESP8266连接外部的WIFI
//pSSID WiFi帐号
//pPassWord WiFi密码
//设置成功返回true 反之false
bool ESP8266_JoinAP( char * pSSID, char * pPassWord)
{char cCmd [120];sprintf ( cCmd, ATCWJAP\%s\,\%s\, pSSID, pPassWord );return ESP8266_Send_AT_Cmd( cCmd, OK, NULL, 5000 );
}//ESP8266 透传使能
//enumEnUnvarnishTx 是否多连接bool类型
//设置成功返回true反之false
bool ESP8266_Enable_MultipleId (FunctionalState enumEnUnvarnishTx )
{char cStr [20];sprintf ( cStr, ATCIPMUX%d, ( enumEnUnvarnishTx ? 1 : 0 ) );return ESP8266_Send_AT_Cmd ( cStr, OK, 0, 500 );}//ESP8266 连接服务器
//enumE 网络类型
//ip 服务器IP
//ComNum 服务器端口
//id连接号确保通信不受外界干扰
//设置成功返回true反之fasle
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, ATCIPSTART%d,%s, id, cStr);elsesprintf ( cCmd, ATCIPSTART%s, cStr );return ESP8266_Send_AT_Cmd ( cCmd, OK, ALREAY CONNECT, 4000 );}//透传使能
//设置成功返回true 反之false
bool ESP8266_UnvarnishSend ( void )
{if (!ESP8266_Send_AT_Cmd ( ATCIPMODE1, OK, 0, 500 ))return false;return ESP8266_Send_AT_Cmd( ATCIPSEND, OK, , 500 );}//ESP8266 检测连接状态
//返回0获取状态失败
//返回2获得ip
//返回3建立连接
//返回4失去连接
uint8_t ESP8266_Get_LinkStatus ( void )
{if (ESP8266_Send_AT_Cmd( ATCIPSTATUS, OK, 0, 500 ) ){if ( strstr ( Uart_Frame_Record.Data_RX_BUF, STATUS:2\r\n ) )return 2;else if ( strstr ( Uart_Frame_Record.Data_RX_BUF, STATUS:3\r\n ) )return 3;else if ( strstr ( Uart_Frame_Record.Data_RX_BUF, STATUS:4\r\n ) )return 4; }return 0;
}//检测应答命令
static uint8_t* ESP8266_CheckCMD(uint8_t *str)
{
// char *strx 0;
// if(Uart_Frame_Record.FramLength0x8000)
// {
// Uart_Frame_Record.Data_RX_BUF[Uart_Frame_Record.FramLength0x7FFF] 0;//添加结束符
// strx strstr((const char*)Uart_Frame_Record.Data_RX_BUF,(const char*)str);
// }
// return (uint8_t*)strx;return (uint8_t *)strstr((const char*)Uart_Frame_Record.Data_RX_BUF,(const char*)str);
}// 串口2 PA2 TX PA3 RX
void Dev_UART3SendStr(uint8_t* tbuf, uint16_t tlen, uint8_t tByte){ uint16_t i 0,j 0;if(tlen 0)j tlen;elsej strlen((const char*)tbuf);for( i 0; i j; i){if((tByte0)(i2)){HAL_UART_Transmit(huart3, tByte, 1, 10);}HAL_UART_Transmit(huart3, tbuf[i], 1, 10);}
}//cmd:发送的命令字符串
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//返回值:0,发送成功 1,发送失败
uint8_t ESP8266_SendCMD(uint8_t *cmd, uint8_t *ack,uint16_t waittime)
{Uart_Frame_Record.FramLength 0;memset((void *)Uart_Frame_Record.Data_RX_BUF, 0, sizeof(Uart_Frame_Record.Data_RX_BUF));Dev_UART3SendStr(cmd, 0, 0);if(ackwaittime){while(--waittime){delay_ms(10);if(Uart_Frame_Record.FramLength0x8000){if(ESP8266_CheckCMD(ack)){printf(AT: %s, ack: %s\r\n, cmd, Uart_Frame_Record.Data_RX_BUF);Uart_Frame_Record.FramLength 0; return 0; }else {Uart_Frame_Record.FramLength 0; return 1;}}}if(waittime0) {return 1;}}return 1;
}uint8_t AT_Printf(char* fmt,...)
{uint8_t tbuf[300] {0};uint16_t j 0;va_list ap;va_start(ap, fmt);vsprintf((char*)tbuf, fmt, ap);va_end(ap);j strlen((const char*)tbuf);HAL_UART_Transmit(huart3, tbuf, j, 10);return 0;
}uint8_t ESP8266_SendData(uint8_t *tbuf, uint16_t len)
{HAL_UART_Transmit(huart3, tbuf, len, 10);return 0;
}// STA模式下获取本地IP
void ESP8266_GetLocalIP(uint8_t* ipbuf)
{uint8_t *p,*p1;if(ESP8266_SendCMD( (uint8_t *)ATCIFSR\r\n, (uint8_t *)OK, 50)){ipbuf[0] 0;return;}p ESP8266_CheckCMD((uint8_t *)\);p1 (uint8_t *)strstr((const char*)(p1),\);*p10;sprintf((char*)ipbuf,%s,p1);
}//退出透传模式 0,退出成功; 1,退出失败
uint8_t ESP8266_QuitTrans(void)
{ ESP8266_SendData((uint8_t *), 3);delay_ms(500); //等待500msreturn ESP8266_SendCMD((uint8_t *)AT\r\n,(uint8_t *)OK,20);//退出透传判断
}//获取连接状态 0,未连接;1,连接成功.
uint8_t ESP8266_ConstaCheck(void)
{while(ESP8266_SendCMD((uint8_t *)ATCIPSTATUS\r\n,(uint8_t *)OK,50)); return 0;
}uint8_t ESP8266_APInit(char *name, char *password)
{uint8_t Sbuf[60] {0};while(ESP8266_SendCMD((uint8_t *)AT\r\n,(uint8_t *)OK,200)){//退出透传ESP8266_QuitTrans();//关闭透传模式 ESP8266_SendCMD((uint8_t *)ATCIPMODE0\r\n,(uint8_t *)OK,200);delay_ms(1000);}// 关闭回显while(ESP8266_SendCMD((uint8_t *)ATE0\r\n,(uint8_t *)OK,200)); // 设置波特率while(ESP8266_SendCMD((uint8_t *)ATUART115200,8,1,0,0\r\n,(uint8_t *)OK,200)); delay_ms(10);// 设置WIFI AP模式while(ESP8266_SendCMD((uint8_t *)ATCWMODE2\r\n,(uint8_t *)OK,200));while(ESP8266_SendCMD((uint8_t *)ATRST\r\n,(uint8_t *)OK,200)); // 延时4秒等待重启成功 delay_ms(1000); delay_ms(1000);delay_ms(1000); delay_ms(1000);memset(Sbuf, 0 , sizeof(Sbuf));sprintf((char*)Sbuf, ATCWSAP\%s\,\%s\,1,4\r\n, name, password);while(ESP8266_SendCMD(Sbuf, (uint8_t *)OK, 1000));#ifdef UDP_Modewhile(ESP8266_SendCMD((uint8_t *)ATCIPMUX0\r\n,(uint8_t *)OK,200)); while(ESP8266_SendCMD((uint8_t *)ATCIPSTART\UDP\,\255.255.255.255\,60156,42254,0\r\n,(uint8_t *)OK,500));while(ESP8266_SendCMD((uint8_t *)ATCIPMODE1\r\n,(uint8_t *)OK,300));while(ESP8266_SendCMD((uint8_t *)ATCIPSEND\r\n,(uint8_t *)OK,200));#endif Uart_Frame_Record.FramLength 0;memset((void *)Uart_Frame_Record.Data_RX_BUF, 0, sizeof(Uart_Frame_Record.Data_RX_BUF)); return 0;
}uint8_t ESP8266_STAConnect(char *name, char *password)
{uint8_t Sbuf[60] {0};memset(Sbuf, 0 , sizeof(Sbuf));sprintf((char*)Sbuf,ATCWJAP\%s\,\%s\\r\n, name, password); if(ESP8266_SendCMD( Sbuf, (uint8_t *)WIFI GOT IP, 3000)){return 1;}return 0;
}uint8_t ESP8266_STAInit(void)
{ // 延时2秒等待串口初始化完成 while(ESP8266_SendCMD((uint8_t *)AT\r\n,(uint8_t *)OK,200)){//退出透传ESP8266_QuitTrans();//关闭透传模式 ESP8266_SendCMD((uint8_t *)ATCIPMODE0\r\n,(uint8_t *)OK,200);delay_ms(800);}// 关闭回显while(ESP8266_SendCMD((uint8_t *)ATE0\r\n,(uint8_t *)OK,200)); // // 设置波特率
// while(ESP8266_SendCMD((uint8_t *)ATUART115200,8,1,0,0\r\n,(uint8_t *)OK,200));delay_ms(10);// 设置WIFI STA模式while(ESP8266_SendCMD((uint8_t *)ATCWMODE1\r\n,(uint8_t *)OK,200));
// while(ESP8266_SendCMD((uint8_t *)ATRST\r\n,(uint8_t *)OK,200)); delay_ms(1000);
// delay_ms(1000);
// delay_ms(1000);return 1;
}2. 核心函数 main函数内容如下调用__HAL_UART_ENABLE_IT(huart3, UART_IT_IDLE)启动串口空闲中断使用HAL_UART_Receive_DMA函数将Uart_Frame_Record.Data_RX_BUF设置为DMA接收存储区。
/* USER CODE BEGIN PV */uint8_t Wssid[20] ioter;
uint8_t Wpassword[20] 1234567980;
/* USER CODE END PV */int main(void)
{/* USER CODE BEGIN 1 /int ret 0;/ USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals /MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();MX_USART3_UART_Init();/ USER CODE BEGIN 2 */printf(start application\r\n);// 使能空闲中断__HAL_UART_ENABLE_IT(huart3, UART_IT_IDLE);HAL_UART_Receive_DMA(huart3, (uint8_t *)Uart_Frame_Record.Data_RX_BUF, RX_BUF_MAX_LEN); // 启动DMA接收// 连接路由器ESP8266_STAInit();delay_ms(1000);if (wifi_connect_status ! WIFI_GOT_IP){while(ESP8266_STAConnect((char *)Wssid, (char *)Wpassword));}/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);printf(reset gpio!\r\n);HAL_Delay(1000); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);printf(set gpio!\r\n);HAL_Delay(1000); }return ret;/* USER CODE END 3 */
}USART3_IRQHandler函数如下 当串口接收完一帧数据后会触发空闲中断中断回调函数检测到空闲中断被触发停止DMA接收数据置位Uart_Frame_Record.FramLength然后对接收的数据处理通过wifi_event_cb函数将WIFI连接状态传送出去。
void USART3_IRQHandler(void)
{if(__HAL_UART_GET_FLAG(huart3, UART_FLAG_IDLE) ! RESET) // 空闲中断标记被置位{__HAL_UART_CLEAR_IDLEFLAG(huart3); // 清除中断标记HAL_UART_DMAStop(huart3); // 停止DMA接收Uart_Frame_Record.FramLength RX_BUF_MAX_LEN - __HAL_DMA_GET_COUNTER(huart3.hdmarx); // 总数据量减去未接收到的数据量为已经接收到的数据量Uart_Frame_Record.Data_RX_BUF[Uart_Frame_Record.FramLength] 0; // 添加结束符 Uart_Frame_Record.FramLength | 1 15;if (strstr(Uart_Frame_Record.Data_RX_BUF, WIFI CONNECTED)){wifi_event_cb(WIFI_CONNECTED);}else if(strstr(Uart_Frame_Record.Data_RX_BUF, WIFI DISCONNECT)){wifi_event_cb(WIFI_DISCONNECT);}else if(strstr(Uart_Frame_Record.Data_RX_BUF, WIFI GOT IP)){wifi_event_cb(WIFI_GOT_IP);}HAL_UART_Receive_DMA(huart3, (uint8_t *)Uart_Frame_Record.Data_RX_BUF, RX_BUF_MAX_LEN); // 重新启动DMA接收}
}接着我们看下ESP8266_SendCMD函数该函数实现发送自定义AT指令并根据结果判断AT指令是否执行成功该函数为本节核心内容函数首先清空缓存然后调用串口发送函数将AT指令发送出去然后进入while(–waittime)等待超时如果正常收到数据Uart_Frame_Record.FramLength最高位会被置位判断是否接收到ack数据。
//cmd:发送的命令字符串
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//返回值:0,发送成功 1,发送失败
uint8_t ESP8266_SendCMD(uint8_t *cmd, uint8_t *ack,uint16_t waittime)
{Uart_Frame_Record.FramLength 0;memset((void *)Uart_Frame_Record.Data_RX_BUF, 0, sizeof(Uart_Frame_Record.Data_RX_BUF));Dev_UART3SendStr(cmd, 0, 0);if(ackwaittime){while(--waittime){delay_ms(10);if(Uart_Frame_Record.FramLength0x8000){if(ESP8266_CheckCMD(ack)){printf(AT: %s, ack: %s\r\n, cmd, Uart_Frame_Record.Data_RX_BUF);Uart_Frame_Record.FramLength 0; return 0; }else {Uart_Frame_Record.FramLength 0; return 1;}}}if(waittime0) {return 1;}}return 1;
}3. 程序执行 编译后下载程序打开路由器或者设置WIFI热点ioter:1234567980 注意不要选择5GESP8266仅2.4G串口1打印数据如下所示看到WIFI GOT IP说明设备联网成功并且得到了DHCP分配的IP地址。
三、小结 如您在使用过程中有任何问题请加QQ群进一步交流。
QQ交流群573122190 (备注物联网项目交流)
小叶老师出品种一棵树最好的时间是十年前其次是现在
完整代码获取 https://download.csdn.net/download/weixin_45006076/89472819