概要
本部分内容介绍如何使用STM32配置ESP8266使用STA模式,这个是使用MQTT与上层服务器链接的必要前提,笔者也是第一次使用ESP8266,通过AT指令来配置,查找了很多资料与代码,借鉴了正点原子ESP8266库和例程,在其基础上修改而来,这篇博客也是我边学边写的记录。
准备
硬件准备
- 带有stm32主控的硬件一套
- esp8266模块一个
- 路由器一个
软件准备
- CubeMx用来配置工程
- MDK5用于编写调试和下载代码
配置ESP8266的步骤
配置ESP8266的目的是为了进入透传模式从而使用MQTT链接网络,我们需要使用AT指令,使ESP8266进入透传模式,步骤如下
1.AT+CWMODE=1 设置模块为STA模式
2.AT+CWAUTOCONN=0
[tr]按顺序要执行指令执行指令的意义[/tr]
+++ | 退出透传模式 |
AE0 | 关闭回显 |
AT+CWMODE=1 | 设置模块为STA模式 |
AT+RST | 重启生效 |
等待三秒 | 等待三秒 |
AT+CWAUTOCONN=0 | 取消自动连接 |
AT+ CWJAP = ,< password> | 连接路由器 |
AT+CIPMUX=0 | 关闭多链接 |
AT+CIPSTAR,"IP号",端口号 | 连接到服务器 |
AT+CIPMODE=1 | 设置为透传模式 |
AT+CIPSEND | 开启透传模式 |
ESP8266的发送命令函数由正点原子的函数改进而来
STM32与ESP8266通过串口连接,需要配置的模块有
- TIM2,定时器中断,用来判断接收命令是否超时,如果超时就按照接受命令,中断优先级为1
- USART1,用于向电脑发送数据,便于调试
- USART3,用于向ESP发送数据,包括发送和接受,采用中断方式接收,查询方式发送,中断优先级为2
- 整个发送命令并接收的流程比较复杂,用到了定时器中断来设置发送指令,接收到的回复是否完成,并在定时器中断中将接收指令的回复接受完成的标志位置为1,并且与预期回复进行比较。
- 这样发送命令并接收的整个流程的好处在于,可以保证发送指令的质量,得到准确的反馈。
- 就好像你向一个人说,你帮我倒杯水好吗,然后要等待他的回复,如果听到他说:好的。说明他听到了我们的命令,这个时候我们才能放心。
实际操作部分
CubeMx配置工程
配置工程就像我上面所述的步骤,需要配置相应的外设,两个串口和一个定时器
1.打开CubeMx,选择对应型号的芯片,始终选择外部晶振
2.查看原理图,选择相应的引脚
配置UART1,UART3,并设置波特率
设置定时器2,并开启定时器中断,分频为7200,重装值为500,这样,接收信息的缓冲时间为50ms
设置中断分组与中断优先级
配置时钟树,使主频最高为72MHZ
配置工程并生成代码
编写ESP8266函数并添加配套函数
首先添加两个uart重定向printf函数和uart接收中断处理函数
void u3_printf(char* fmt,...) { uint8_t i,j; va_list ap; va_start(ap,fmt); vsprintf((char*)USART3_TX_BUF,fmt,ap); va_end(ap); i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度 for(j=0;j
SR&0X40)==0); //循环发送,直到发送完毕 USART3->DR=USART3_TX_BUF[j]; } }1234567891011121314void u1_printf(char fmt,...)
{
uint8_t i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char)USART1_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART1_TX_BUF); //此次发送数据的长度
for(j=0;j
{
while((USART1->SR&0X40)==0); //循环发送,直到发送完毕
USART3->DR=USART1_TX_BUF[j];
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->InstanceUSART3)
{
if((USART3_RX_STA&(1<<15))0)//接收完的一批数据,还没有被处理,则不再接收其他数据
{
if(USART3_RX_STA
{
__HAL_TIM_SET_COUNTER(&htim2,0); //计数器清空
if(USART3_RX_STA==0) //使能定时器7的中断
{
__HAL_TIM_ENABLE(&htim2); //使能定时器2
}
USART3_RX_BUF[USART3_RX_STA++]=temp_rx; //记录接收到的值
}else
{
USART3_RX_STA|=1<<15; //强制标记接收完成
}
}
}
}
编写发送和检查命令核心函数
- 创建esp8266.c esp8266.h并按照之前的流程图编写函数如下
uint8_t esp8266_send_cmd(uint8_t *cmd,uint8_t *ack,uint16_t waittime){uint8_t res = 0;USART3_RX_STA = 0;u3_printf("%srn",cmd);if(ack&&waittime) { while(--waittime) { HAL_Delay(10); if(USART3_RX_STA&0X8000) { if(esp8266_check_cmd(ack)) { u1_printf("ack:%srn",(uint8_t*)ack); break; } USART3_RX_STA=0; } } if(waittime==0)res=1; }return res;12345678910111213141516171819202112}
uint8_t esp8266_check_cmd(uint8_t str)
{
char *strx=0;if(USART3_RX_STA&0X8000) { USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0; u1_printf("%srn",(char*)USART3_RX_BUF); strx=strstr((const char*)USART3_RX_BUF,(const char*)str);} return (uint8_t*)strx;123456789}
在定时器文件中编写定时器中断服务函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim->Instance == TIM2) { USART3_RX_STA|=1<<15; __HAL_TIM_CLEAR_FLAG(&htim2,TIM_EVENTSOURCE_UPDATE ); __HAL_TIM_DISABLE(&htim2); }12341234}
在esp8266.c中按照上面的表格编写esp8266的初始化函数
uint8_t esp8266_Connect_IOTServer(void){ u1_printf("准备配置模块rn"); HAL_Delay(100);u1_printf("准备退出透传模式n");if(esp8266_quit_trans()){ u1_printf("退出透传模式失败,准备重启rn"); return 6;}else u1_printf("退出透传模式成功rn");u1_printf("准备关闭回显rn");if(esp8266_send_cmd("ATE0","OK",50)){ u1_printf("关闭回显失败准备重启rn"); return 1;}else u1_printf("关闭回显成功rn");u1_printf("查询模块是否在线rn");if(esp8266_send_cmd("AT","OK",50)){ u1_printf("模块不在线准备重启rn"); return 1;}else u1_printf("设置查询在线成功rn");u1_printf("准备设置STA模式rn");if(esp8266_send_cmd("AT+CWMODE=1","OK",50)){ u1_printf("设置STA模式失败准备重启rn"); return 1;}else u1_printf("设置STA模式成功rn");u1_printf("准备重启rn");if(esp8266_send_cmd("AT+RST","OK",50)){ u1_printf("重启失败,准备重启rn"); return 2;}else u1_printf("重启成功,等待三秒rn");HAL_Delay(1000);HAL_Delay(1000);HAL_Delay(1000);HAL_Delay(1000);u1_printf("准备取消自动连接rn");if(esp8266_send_cmd("AT+CWAUTOCONN=0","OK",50)){ u1_printf("取消自动连接失败,准备重启rn"); return 3;}else u1_printf("取消自动连接成功rn");u1_printf("准备链接路由器rn");if(esp8266_Connect_AP()){ u1_printf("连接路由器失败,等待重启rn"); return 4;}else u1_printf("连接路由器成功rn");HAL_Delay(1000);HAL_Delay(1000);u1_printf("设置为关闭多路连接rn");if(esp8266_send_cmd("AT+CIPMUX=0","OK",100)){ u1_printf("关闭多路连接失败,准备重启rn"); return 7;}else u1_printf("设置关闭多路连接成功rn");u1_printf("准备链接服务器rn");if(esp8266_Connect_Server()){ u1_printf("连接服务器失败,等待重启rn"); return 8;}else u1_printf("连接服务器成功rn");u1_printf("准备退出透传模式n");if(esp8266_quit_trans()){ u1_printf("退出透传模式失败,准备重启rn"); return 6;}else u1_printf("退出透传模式成功rn");u1_printf("设置为透传模式rn");if(esp8266_send_cmd("AT+CIPMODE=1","OK",50)){ u1_printf("设置透传失败,准备重启rn"); return 6;}else u1_printf("设置透传成功rn");u1_printf("设置开启透传模式rn");if(esp8266_send_cmd("AT+CIPSEND","OK",1000)){ u1_printf("开启透传失败,准备重启rn"); return 9;}else u1_printf("开启透传成功rn");while(1){ HAL_Delay(1000); u3_printf("This is a messagern");}return 0;1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
在主函数中初始化相应模块并调用esp8266的初始化函数
while (1) { /* USER CODE END WHILE */ esp8266_Connect_IOTServer();123123}
观察实验现象
- 打开网络调试助手,调整到TCP服务器模式,并记下此时的ip地址和端口号
- 打开串口调试助手
- 打开开发板观察现象
下面是串口调试助手的消息


上面的图就是进入透传模式的过程,整个过程的AT指令是按照本文章最上面的表格来设置的,观察网络调试助手观察现象

发现进入透传模式之后的数据成功发送到服务器端,试验成功。
总结
本部分内容介绍了如何使STM32使用串口与ESP8266进行通讯,并且配置ESP8266进入TCP客户端的透传模式链接服务器,下一部分内容不出意外的话应该是介绍如何使STM32使用paho的开源MQTT库连接到百度云天工服务器
概要
本部分内容介绍如何使用STM32配置ESP8266使用STA模式,这个是使用MQTT与上层服务器链接的必要前提,笔者也是第一次使用ESP8266,通过AT指令来配置,查找了很多资料与代码,借鉴了正点原子ESP8266库和例程,在其基础上修改而来,这篇博客也是我边学边写的记录。
准备
硬件准备
- 带有stm32主控的硬件一套
- esp8266模块一个
- 路由器一个
软件准备
- CubeMx用来配置工程
- MDK5用于编写调试和下载代码
配置ESP8266的步骤
配置ESP8266的目的是为了进入透传模式从而使用MQTT链接网络,我们需要使用AT指令,使ESP8266进入透传模式,步骤如下
1.AT+CWMODE=1 设置模块为STA模式
2.AT+CWAUTOCONN=0
[tr]按顺序要执行指令执行指令的意义[/tr]
+++ | 退出透传模式 |
AE0 | 关闭回显 |
AT+CWMODE=1 | 设置模块为STA模式 |
AT+RST | 重启生效 |
等待三秒 | 等待三秒 |
AT+CWAUTOCONN=0 | 取消自动连接 |
AT+ CWJAP = ,< password> | 连接路由器 |
AT+CIPMUX=0 | 关闭多链接 |
AT+CIPSTAR,"IP号",端口号 | 连接到服务器 |
AT+CIPMODE=1 | 设置为透传模式 |
AT+CIPSEND | 开启透传模式 |
ESP8266的发送命令函数由正点原子的函数改进而来
STM32与ESP8266通过串口连接,需要配置的模块有
- TIM2,定时器中断,用来判断接收命令是否超时,如果超时就按照接受命令,中断优先级为1
- USART1,用于向电脑发送数据,便于调试
- USART3,用于向ESP发送数据,包括发送和接受,采用中断方式接收,查询方式发送,中断优先级为2
- 整个发送命令并接收的流程比较复杂,用到了定时器中断来设置发送指令,接收到的回复是否完成,并在定时器中断中将接收指令的回复接受完成的标志位置为1,并且与预期回复进行比较。
- 这样发送命令并接收的整个流程的好处在于,可以保证发送指令的质量,得到准确的反馈。
- 就好像你向一个人说,你帮我倒杯水好吗,然后要等待他的回复,如果听到他说:好的。说明他听到了我们的命令,这个时候我们才能放心。
实际操作部分
CubeMx配置工程
配置工程就像我上面所述的步骤,需要配置相应的外设,两个串口和一个定时器
1.打开CubeMx,选择对应型号的芯片,始终选择外部晶振
2.查看原理图,选择相应的引脚
配置UART1,UART3,并设置波特率
设置定时器2,并开启定时器中断,分频为7200,重装值为500,这样,接收信息的缓冲时间为50ms
设置中断分组与中断优先级
配置时钟树,使主频最高为72MHZ
配置工程并生成代码
编写ESP8266函数并添加配套函数
首先添加两个uart重定向printf函数和uart接收中断处理函数
void u3_printf(char* fmt,...) { uint8_t i,j; va_list ap; va_start(ap,fmt); vsprintf((char*)USART3_TX_BUF,fmt,ap); va_end(ap); i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度 for(j=0;j
SR&0X40)==0); //循环发送,直到发送完毕 USART3->DR=USART3_TX_BUF[j]; } }1234567891011121314void u1_printf(char fmt,...)
{
uint8_t i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char)USART1_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART1_TX_BUF); //此次发送数据的长度
for(j=0;j
{
while((USART1->SR&0X40)==0); //循环发送,直到发送完毕
USART3->DR=USART1_TX_BUF[j];
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->InstanceUSART3)
{
if((USART3_RX_STA&(1<<15))0)//接收完的一批数据,还没有被处理,则不再接收其他数据
{
if(USART3_RX_STA
{
__HAL_TIM_SET_COUNTER(&htim2,0); //计数器清空
if(USART3_RX_STA==0) //使能定时器7的中断
{
__HAL_TIM_ENABLE(&htim2); //使能定时器2
}
USART3_RX_BUF[USART3_RX_STA++]=temp_rx; //记录接收到的值
}else
{
USART3_RX_STA|=1<<15; //强制标记接收完成
}
}
}
}
编写发送和检查命令核心函数
- 创建esp8266.c esp8266.h并按照之前的流程图编写函数如下
uint8_t esp8266_send_cmd(uint8_t *cmd,uint8_t *ack,uint16_t waittime){uint8_t res = 0;USART3_RX_STA = 0;u3_printf("%srn",cmd);if(ack&&waittime) { while(--waittime) { HAL_Delay(10); if(USART3_RX_STA&0X8000) { if(esp8266_check_cmd(ack)) { u1_printf("ack:%srn",(uint8_t*)ack); break; } USART3_RX_STA=0; } } if(waittime==0)res=1; }return res;12345678910111213141516171819202112}
uint8_t esp8266_check_cmd(uint8_t str)
{
char *strx=0;if(USART3_RX_STA&0X8000) { USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0; u1_printf("%srn",(char*)USART3_RX_BUF); strx=strstr((const char*)USART3_RX_BUF,(const char*)str);} return (uint8_t*)strx;123456789}
在定时器文件中编写定时器中断服务函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim->Instance == TIM2) { USART3_RX_STA|=1<<15; __HAL_TIM_CLEAR_FLAG(&htim2,TIM_EVENTSOURCE_UPDATE ); __HAL_TIM_DISABLE(&htim2); }12341234}
在esp8266.c中按照上面的表格编写esp8266的初始化函数
uint8_t esp8266_Connect_IOTServer(void){ u1_printf("准备配置模块rn"); HAL_Delay(100);u1_printf("准备退出透传模式n");if(esp8266_quit_trans()){ u1_printf("退出透传模式失败,准备重启rn"); return 6;}else u1_printf("退出透传模式成功rn");u1_printf("准备关闭回显rn");if(esp8266_send_cmd("ATE0","OK",50)){ u1_printf("关闭回显失败准备重启rn"); return 1;}else u1_printf("关闭回显成功rn");u1_printf("查询模块是否在线rn");if(esp8266_send_cmd("AT","OK",50)){ u1_printf("模块不在线准备重启rn"); return 1;}else u1_printf("设置查询在线成功rn");u1_printf("准备设置STA模式rn");if(esp8266_send_cmd("AT+CWMODE=1","OK",50)){ u1_printf("设置STA模式失败准备重启rn"); return 1;}else u1_printf("设置STA模式成功rn");u1_printf("准备重启rn");if(esp8266_send_cmd("AT+RST","OK",50)){ u1_printf("重启失败,准备重启rn"); return 2;}else u1_printf("重启成功,等待三秒rn");HAL_Delay(1000);HAL_Delay(1000);HAL_Delay(1000);HAL_Delay(1000);u1_printf("准备取消自动连接rn");if(esp8266_send_cmd("AT+CWAUTOCONN=0","OK",50)){ u1_printf("取消自动连接失败,准备重启rn"); return 3;}else u1_printf("取消自动连接成功rn");u1_printf("准备链接路由器rn");if(esp8266_Connect_AP()){ u1_printf("连接路由器失败,等待重启rn"); return 4;}else u1_printf("连接路由器成功rn");HAL_Delay(1000);HAL_Delay(1000);u1_printf("设置为关闭多路连接rn");if(esp8266_send_cmd("AT+CIPMUX=0","OK",100)){ u1_printf("关闭多路连接失败,准备重启rn"); return 7;}else u1_printf("设置关闭多路连接成功rn");u1_printf("准备链接服务器rn");if(esp8266_Connect_Server()){ u1_printf("连接服务器失败,等待重启rn"); return 8;}else u1_printf("连接服务器成功rn");u1_printf("准备退出透传模式n");if(esp8266_quit_trans()){ u1_printf("退出透传模式失败,准备重启rn"); return 6;}else u1_printf("退出透传模式成功rn");u1_printf("设置为透传模式rn");if(esp8266_send_cmd("AT+CIPMODE=1","OK",50)){ u1_printf("设置透传失败,准备重启rn"); return 6;}else u1_printf("设置透传成功rn");u1_printf("设置开启透传模式rn");if(esp8266_send_cmd("AT+CIPSEND","OK",1000)){ u1_printf("开启透传失败,准备重启rn"); return 9;}else u1_printf("开启透传成功rn");while(1){ HAL_Delay(1000); u3_printf("This is a messagern");}return 0;1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
在主函数中初始化相应模块并调用esp8266的初始化函数
while (1) { /* USER CODE END WHILE */ esp8266_Connect_IOTServer();123123}
观察实验现象
- 打开网络调试助手,调整到TCP服务器模式,并记下此时的ip地址和端口号
- 打开串口调试助手
- 打开开发板观察现象
下面是串口调试助手的消息


上面的图就是进入透传模式的过程,整个过程的AT指令是按照本文章最上面的表格来设置的,观察网络调试助手观察现象

发现进入透传模式之后的数据成功发送到服务器端,试验成功。
总结
本部分内容介绍了如何使STM32使用串口与ESP8266进行通讯,并且配置ESP8266进入TCP客户端的透传模式链接服务器,下一部分内容不出意外的话应该是介绍如何使STM32使用paho的开源MQTT库连接到百度云天工服务器
举报