STM32f103增加ESP8266模块,通过Wifi用手机TCP服务端远程与STM32通信

您所在的位置:网站首页 手机app源码怎么用 STM32f103增加ESP8266模块,通过Wifi用手机TCP服务端远程与STM32通信

STM32f103增加ESP8266模块,通过Wifi用手机TCP服务端远程与STM32通信

2023-06-13 02:27| 来源: 网络整理| 查看: 265

STM32f103增加ESP8266模块,通过Wifi用手机TCP服务端远程与STM32通信

提示:esp8266开启透传模式,连上路由器Wifi(电脑热点),接入STM32串口引脚。另外手机TCP服务端是手机IP地址+串口号(大于8000滴)

文章目录 STM32f103增加ESP8266模块,通过Wifi用手机TCP服务端远程与STM32通信前言一、硬件1.硬件部件2.接线 二、代码实现过程1.电脑串口调试ESP82662.初次实验在STM32写好程序3.串口接收来自8266的数据(串口中断服务)4.主函数 心得

前言

最近对ESP8266模块进行小了解,知道只要对8266通过串口发送AT指令进行配置其工作模式,就能够通过手机调试APP创建什么TCP/UDP服务端/客户端和8266进行无线通信。于是,我想把8266装到STM32单片机上。 STM32如何给8266发送AT指令,让8266进入透传模式这就是程序代码问题。我需要给STM32一上电8266就自动联网、连接TCP服务端、再开启透传模式。如何通过手机TCP服务端发送数据包让STM32执行一些有趣的动作。 但是,因为经验有限,我在以为很简单的STM32给8266发送AT指令的程序部分花了不少天时间去研究和学习。经过查找大量各路网友的文章,也是道路坎坷,终于功夫不负有心人。以下是我的关于这方面的一下见解与程序

提示:以下是本篇文章正文内容,下面案例可供参考

一、硬件 1.硬件部件 STM32f103c6核心板esp8266(ESP-01)OLED显示屏两个LED小灯ST-LINK V2 器和 USB to TTL器面包板+杜邦线若干 2.接线

对于ESP8266模块 . . . …用到其引脚:TX、RX、EN、RST、Vcc3.3、GND 提示:8266的EN通过串连1k欧姆的电阻接入Vcc,我这里RST也接到了Vcc 对于STM32: . … . . 用到其引脚:A9+A10 (Tx和Rx)分别连接8266的Rx和Tx, B8+B9(连接OLED显示屏的SLC和SDA), A1+A2(接LED1和LED2)

二、代码实现过程 1.电脑串口调试ESP8266

AT指令(示例):------------------- AT—————————//AT指令 AT+CWMODE=3 —— //模式配置,选择3 AT+RST" ——————//重启 AT+CIFER"———— //查8266本机IP地址 AT+CWJAP=“热点名称”,“热点密码”————————//8266联网 AT+CIPMUX=0"— //开启单链接 AT+CIPSTART=“TCP”,“手机IP地址”————————//8266接入手机TCP服务端 AT+CIPMODE=1” ——//开启透传模式 AT+CIPSEND" ————//开始透传接发 在这里插入图片描述

![在这里插入图片描述](https://img-blog.csdnimg.cn/37df4dec4864458599015d3f20600125.png#pic_center)

如此,知道通过串口给8266发送AT指令时,它会返回什么应答的消息。

我发现8266回复的并不是单纯的 OK或者ready… 它会重新回复AT指令内容、未知的换行符数、或者匹配时等待或不成功时的消息、接线不稳定时干扰信息等等

所有我就针对串口上传送过来的一大串数据如何判断并保存真正需要的数据包做出一些代码上的思想与验证。

2.初次实验在STM32写好程序

(2-1)、创建全局变量:

两个部分重点变量: * 处理AT指令配置8266时,其用串口回复应答时数据缓冲数组+标志+数据累计指针 * 处理透传模式通信时,8266向STM32串口转发时数据缓冲数组+标志

#ifndef __WIFISERIAL_H #define __WIFISERIAL_H #define USART1_MAX_RECV_LEN 100 //最大接收缓存字节数 #define USART1_MAX_SEND_LEN 100 //最大发送缓存字节数 //针对向8266发送AT指令时用的变量 extern uint8_t wifi_SaveAble; //对8266通过串口回复数据的保存能力(即决定是否保存来自串口的数据) extern uint8_t Wifi_answerOver; //8266回复完毕置1(即已经接收应答数据状态),要手动清零 extern uint8_t Wifi_cnt; //8266回复时保存数据的累计指针,自动清零 extern char WIFI_Reply[USART1_MAX_RECV_LEN];//8266回复应答数据保存的地方 //针对手机TCP服务端与8266通信再通过串口传递到STM32时用的变量 extern uint8_t USART1_RX_BUF[USART1_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN字节 extern uint8_t USART1_TX_BUF[USART1_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节 extern vu16 USART1_RX_STA; //接收数据状态

(2-2). STM32串口发送(A9引脚)

//通信时接收数据包格式:@xxxxxx!! //发送脚PA9——接模块RX、接收脚PA10——接模块TX //波特率:115200. #include #include "WifiSerial.h" void WifiSerial_Init(uint32_t bound) { 省略————————STM32串口,引脚的外设配置 } void Serial_SendByte(uint8_t Byte) { USART_SendData(USART1, Byte); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } void Serial_SendString(char *String) { uint8_t i; for (i = 0; String[i] != '\0'; i ++) { Serial_SendByte(String[i]); } } void WifiSerial_Printf(char *format, ...) { va_list ap; va_start(ap,format); vsprintf((char*)USART1_TX_BUF,format,ap); va_end(ap); Serial_SendString((char*)USART1_TX_BUF); } void Wifi_Serial_SendString(uint8_t *String , uint8_t leng) { uint8_t i; for (i = 0; i WifiSerial_Printf(AT); //————————向8266发送AT指令 delay_ms(1000); delay_ms(1000); WifiSerial_Printf(CWMODE);//———————向8266发送CWMODE指令 delay_ms(1000); delay_ms(1000); delay_ms(1000); WifiSerial_Printf(CWJAP); //———————向8266发送CWJAP指令,让它联网 delay_ms(1000); delay_ms(1000); WifiSerial_Printf(RST); //———————向8266发送RST指令,复位 delay_ms(1000); delay_ms(1000); delay_ms(1000); delay_ms(1000); WifiSerial_Printf(CIPSTART);//——————向8266发送CIPSTART指令,让它与(创建的服务器IP地址和串口)连接 delay_ms(1000); delay_ms(1000); delay_ms(1000); delay_ms(1000); delay_ms(1000); //——————如果8266与手机调试APP应用创建的TCP服务端连接成功,则TCP界面变成可以发送的状态。 WifiSerial_Printf(CIPMODE);//——————向8266发送CIPMODE指令,透传模式 delay_ms(1000); delay_ms(1000); WifiSerial_Printf(CIPSEND);//——————向8266发送CIPSEND指令,发送数据模式 delay_ms(1000); delay_ms(1000); }

(2-3.3).ESP8266.C 程序: 其次,串口接收到有效的应答后,才发送下一条AT指令。 得到8266反馈信号就退出当前指令发送的循环,否则不断地按时重复发送指令。 提示:路由器WIFI提前开好,手机TCP服务端提前创建好!

//*********ESP8266模块,模式3,进入透传模式,与手机(TCP服务端)通信***************** //闭环式发送AT+指令,STM32通过串口直接向ESP8266模块发送指令,在while(),等待8266的回复消息,如果成功配置进行发送下一条指令。 void ESP8266_start_trans(void) { while(_esp8266s_endcmd((uint8_t*)AT,(uint8_t*)answer1_OK,10)) //向8266发送AT指令,不成功持续发送,成功则退出循环 delay_ms(1000); while(_esp8266s_endcmd((uint8_t*)CWMODE,(uint8_t*)answer1_OK,10)) //向8266发送AT指令,不成功持续发送,成功则退出循环 delay_ms(1000); while(_esp8266s_endcmd((uint8_t*)CWJAP,(uint8_t*)answer1_OK,20)) //向8266发送AT指令,不成功持续发送,成功则退出循环 delay_ms(500); while(_esp8266s_endcmd((uint8_t*)RST,(uint8_t*)"ready"),20) //向8266发送AT指令,不成功持续发送,成功则退出循环 delay_ms(500); while(_esp8266s_endcmd((uint8_t*)CIPSTART,(uint8_t*)answer1_OK,10)) //向8266发送AT指令,不成功持续发送,成功则退出循环 delay_ms(500); while(_esp8266s_endcmd((uint8_t*)CIPMODE,(uint8_t*)answer1_OK,10)) //向8266发送AT指令,不成功持续发送,成功则退出循环 delay_ms(500); while(_esp8266s_endcmd((uint8_t*)CIPSEND,(uint8_t*)answer1_OK,10)) //向8266发送AT指令,不成功持续发送,成功则退出循环 delay_ms(500); OLED_SHOW_stage(7,(uint8_t*)"AT+CIPSEND ok"); } //—————————————————————————————————— //--函数作用: 向ESP8266发送指令。 //--参数: cmd:发送的指令字符串; ack:期待的应答结果,如果为空,则表示不需要等待应答; waittime:等待时间(单位:100ms加倍) //--返回值: 0:匹配成功(得到了期待的应答结果); 1:匹配失败,(可以设置直接跳过8266的匹配函数); 2:重新发送指令 //--关键(全局)变量: 表示有8266通过串口应答了:Wifi_answerOver=1、 保存8266的回复信息:WIFI_Reply[]。 //——————————————————————————————————————— extern uint8_t _esp8266s_endcmd(uint8_t *cmd,uint8_t *ack,uint8_t waittime) { WifiSerial_Printf((char *)cmd); //向8266发送指令 while(waittime--) //若没有成功收到8266回复,重复发送指令的时间计数是:waittime*100ms (即:waittime倍的100ms) { if(Wifi_answerOver == 1) //如果8266发送完毕就进入 { if(strcmp((char *)WIFI_Reply,(char *)ack) == NULL) //关键字判断,如果串口读取到Wifi回复内容与关键字一样则成功,返回值0 { ESP8266_Clear(); return Wifi_OK; //返回:0 会使调用这个函数的循环退出; } if(strcmp((char *)WIFI_Reply,(char *)ack) == NULL) //关键字判断,如果串口读取到Wifi回复内容与关键字一样则成功,返回值0 { OLED_SHOW_wait((uint8_t *)WIFI_Reply,(uint8_t *)"OK"); ESP8266_Clear(); return Wifi_error; //返回:1 会使调用这个函数的循环退出; } ESP8266_Clear(); //必要的清零和放在该位置 } Delay_ms(100); } //如果在循环给定时间能没有完成,或者Wifi回复内容与关键字匹配不成功,则失败,将再次发送指令,返回值:1 return Wifi_wait; //返回:2 会使调用这个函数的循环继续重复执行; }

结论:

3.串口接收来自8266的数据(串口中断服务)

3-1. 串口中断服务函数

//串口中断服务函数 void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) { Serial_RxPData = USART_ReceiveData(USART1); //从串口读缓冲寄存器里拿数据 Receive_CommunicationData(); //通信时的接收数据函数 // _1_Wifi_Signal_Proces(); //初代ESP8266配置指令时接收应答函数 // _2_Screening_Wifi_Signal(); //二代ESP8266配置指令时接收应答函数 _3_Screening_Useful_Wifi_Message();//三代ESP8266配置指令时接收应答函数 USART_ClearITPendingBit(USART1, USART_IT_RXNE); //告诉串口已经读取完数据 } }

3-2. 初代接收8266应答中断部分 //——函数作用:如果可以,在串口上8266发送的数据中没有得到结束标志之前,所有的数据都保存,统统都收下。 //——参数: 无 //——返回值: 无 //——关键(全局)变量:保存使能标志(wifi_SaveAble),接收结束标志(Wifi_answerOver),数据累计指针(Wifi_cnt),保存数据的数组(WIFI_Reply[]), //——优点: 除了保存所有数据,毫无意义 //——缺点: 除非串口发来确定的信息数据,无法获取需要的应答信息,结束标志位置1后无法回头读取。

void _1_Wifi_Signal_Proces(void) { //只为配置8266时有效使用的 if(wifi_SaveAble == 0 && Wifi_answerOver == 0) { if(Serial_RxPData == '\r') { wifi_SaveAble = 1; } else { WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt++; } } else if(wifi_SaveAble == 1 && Wifi_answerOver == 0) { if(Serial_RxPData == '\n') { WIFI_Reply[Wifi_cnt] = '\0'; Wifi_answerOver = 1; //置1 串口收发接收 } } }

3-3. 第二代接收8266应答的中断部分 //——函数作用:如果可以,找到串口上8266发送的数据中的几个有用字节并保存,判断保存数据是否为需要的应答信息。 //——参数: 无 //——返回值: 无 //——关键(全局)变量:保存使能标志(wifi_SaveAble),接收结束标志(Wifi_answerOver),数据累计指针(Wifi_cnt),保存数据的数组(WIFI_Reply[]), //——优点: 直接对每一个字节筛选,在串口数据流上可以获得存在的需要的字节数据 //——缺点: 无法判断完整的一帧数据,比如:如果要获得OK的应答,则 OJ8K、CONNECTK2、OK…都会当成 OK。

void _2_Screening_Wifi_Signal(void) { if (wifi_SaveAble == 0 && Wifi_answerOver == 0) { if ((Serial_RxPData == 'O' || Serial_RxPData == 'r')) { WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; wifi_SaveAble = 1; } } else if (wifi_SaveAble == 1 && Wifi_answerOver == 0) { switch(Serial_RxPData) { case '\r': wifi_SaveAble = 2; break; case 'K': WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; wifi_SaveAble = 2; break; case 'e': WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; break; case 'a': WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; break; case 'd': WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; break; case 'y': WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; wifi_SaveAble = 2; break; } } else if (wifi_SaveAble == 2 && Wifi_answerOver == 0) { if (Serial_RxPData == '\n') { WIFI_Reply[Wifi_cnt] = '\0'; Wifi_cnt = 0; wifi_SaveAble = 0; /*———*——Wifi_answerOver = 1;——*——*/ } } //--*---*---*---*---判断接收到的数据是否为需要的应答信息。---*---*---*---*---*---*// //———*———*———如果是就置1结束标志位,从此停止接收——*————*———// if(Serial_RxPData == '\n' && (strcmp((char *)WIFI_Reply,"OK") == NULL || strcmp((char *)WIFI_Reply,"ready") == NULL)) { Wifi_answerOver = 1; } }

3-4. 第三代接收8266应答的中断部分 //——函数作用:如果可以,保存串口上8266发送的数据的一帧数据,判断该数据是否为需要的应答信息。 //——参数: 无 //——返回值: 无 //——关键(全局)变量:保存使能标志(wifi_SaveAble),接收结束标志(Wifi_answerOver),数据累计指针(Wifi_cnt),保存数据的数组(WIFI_Reply[]), //——优点: 未得到有效应答信息则一直寻找一帧数据,在串口数据流上可以获得一帧连续完整的数据。 比如:要获取应答OK,则对应 OJ8K、CONNECTK2、OK…只会保存OK //——缺点: 在同一次发送中,如果获得了一个有效应答,其后面的有效应答无法接收。

void _3_Screening_Useful_Wifi_Message(void) { if (wifi_SaveAble == 0 && Wifi_answerOver == 0) { if (Serial_RxPData == 'O' || Serial_RxPData == 'r' || Serial_RxPData == 'E') //对8266发来的数据是否开始保存的条件 { WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; wifi_SaveAble = 1; } } else if (wifi_SaveAble == 1 && Wifi_answerOver == 0) { if (Serial_RxPData == '\r') { wifi_SaveAble = 2; } else { WIFI_Reply[Wifi_cnt] = Serial_RxPData; Wifi_cnt ++; } } else if (wifi_SaveAble == 2 && Wifi_answerOver == 0) { if (Serial_RxPData == '\n') { wifi_SaveAble = 0; WIFI_Reply[Wifi_cnt] = '\0'; Wifi_cnt = 0; } } //--*---*---*---*---判断接收到的数据是否为需要的应答信息。---*---*---*---*---*---*// //———*———*———如果是就置1结束标志位,从此停止接收——*————*———// if(Serial_RxPData == '\n' && (strcmp((char *)WIFI_Reply,"OK") == NULL || strcmp((char *)WIFI_Reply,"ready") == NULL || strcmp((char *)WIFI_Reply,"ERROR") == NULL)) { Wifi_answerOver = 1; } }

3-5.通信时接收数据的中断部分 //——函数作用:如果数据接发协议正确,保存串口上发送的数据,按照协议:包头+数据+包尾 //——参数: 无 //——返回值: 无 //——关键(全局)变量:接收结束标志(USART1_RX_STA),保存数据的数组(USART1_RX_BUF[])。

void Receive_CommunicationData() { static uint8_t RxState = 0; static uint8_t cont = 0; if (RxState == 0) { if (Serial_RxPData == '@' && USART1_RX_STA == 0) { wifi_SaveAble = 4; //-----------在8266配置成功情况下,传送有效数据时,不保存在回复数据的数组(WIFI_Reply[])里 cont = 0; RxState = 1; USART_ClearITPendingBit(USART1, USART_IT_RXNE); } } else if (RxState == 1) { if (Serial_RxPData != '!') { USART1_RX_BUF[cont] = Serial_RxPData; cont ++; } else { RxState = 2; } } else if (RxState == 2) { if (Serial_RxPData == '!') { RxState = 0; USART1_RX_BUF[cont] = '\0'; USART1_RX_STA = 1; cont = 0; } else { RxState = 1; USART1_RX_BUF[cont] = Serial_RxPData; cont ++; } } } 4.主函数 #include "stm32f10x.h" #include "WifiSerial.h" #include "esp8266.h" #include ~~~~~~~~~~~~等等 void Main_DoSomething(void); int main(void) { OLED_Init(); LED_Init(); Key_Init(); WifiSerial_Init(115200); OLED_ShowString(1, 1, "TxPacket"); OLED_ShowString(3, 1, "RxPacket"); // delay_ms(500); // ESP8266_Pow_Init(); // delay_ms(500); delay_ms(500); ESP8266_start_trans(); while (1) { Main_DoSomething(); } }

(main.c) 接下来就是按照自己设计的通信协议,发送很多很多不同的数据。 每个数据包在主函数里设计好执行任务,去实现不同的项目工作。 这里:通过手机TCP服务端发送数据包“@LED1_ON!!“ 或"@LED2_ON!!",ESP8266接收数据包并通过串口转发给STM32,STM32解包以后执行点亮LED1或LED2。 如此,可以远程操控STM32去实现亮灭LED、改变PWM输出、发射红外通信、以及反馈STM32上外设模块的数据(若接入其他传感器) 这样一来,是否无线遥控,无线控制舵机、电机转速…都在程序设计的问题上了。

void Main_DoSomething(void) { if (USART1_RX_STA == 1) { WifiSerial_Printf("\r\nCommand : \r\n"); WifiSerial_Printf((char*)USART1_RX_BUF); OLED_ShowString(4, 1, " "); OLED_ShowString(4, 1, (char*)USART1_RX_BUF); if (strcmp((char*)USART1_RX_BUF, "LED1_ON") == 0) { LED1_ON(); OLED_ShowString(2, 1, " "); OLED_ShowString(2, 1, "LED1_ON_OK"); WifiSerial_Printf("\r\nResult : \r\n"); WifiSerial_Printf((char*)USART1_RX_BUF); WifiSerial_Printf(" is OK !\r\n"); } else if (strcmp((char*)USART1_RX_BUF, "LED1_OFF") == 0) { LED1_OFF(); OLED_ShowString(2, 1, " "); OLED_ShowString(2, 1, "LED1_OFF_OK"); WifiSerial_Printf("\r\nResult : \r\n"); WifiSerial_Printf((char*)USART1_RX_BUF); WifiSerial_Printf(" is OK !\r\n"); } else if(strcmp((char*)USART1_RX_BUF, "LED2_ON") == 0) { LED2_ON(); OLED_ShowString(2, 1, " "); OLED_ShowString(2, 1, "LED2_ON_OK"); WifiSerial_Printf("\r\nResult : \r\n"); WifiSerial_Printf((char*)USART1_RX_BUF); WifiSerial_Printf(" is OK !\r\n"); } else if (strcmp((char*)USART1_RX_BUF, "LED2_OFF") == 0) { LED2_OFF(); OLED_ShowString(2, 1, " "); OLED_ShowString(2, 1, "LED2_OFF_OK"); WifiSerial_Printf("\r\nResult : \r\n"); WifiSerial_Printf((char*)USART1_RX_BUF); WifiSerial_Printf(" is OK !\r\n"); } else { OLED_ShowString(4,1,(char*)USART1_RX_BUF); OLED_ShowString(2, 1, " "); OLED_ShowString(2, 1, "ERROR_COMMAND"); WifiSerial_Printf("\r\nResult : \r\n"); WifiSerial_Printf("ERROR_COMMAND\r\n"); } USART1_RX_STA = 0; } }

在这里插入图片描述

STM32+8266+TCP服务端,WIFI无线通信

心得

其非常有意思的存在。 常觉世物无联系,今晓石木皆有灵。 不去揭晓不自知,非下功夫不可得。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3