赞
踩
stm32f103c8t6 单片机
esp8266 wift 模块
实现esp8266 模块的通讯(客户端)
这个是我所使用的模块ESP-01S 类型的,连线图要参考使用手册。本实验采用的是上图的连线方式。
下面是其他模块的原理图。
本实验的连线方式:
端口 | esp8266引脚 |
---|---|
VCC | 3.3V |
GND | GND |
PA2 | MCU_TXD |
PA3 | MCU_RXD |
#include "delay.h" #include "sys.h" #include "string.h" #include "tcp.h" #include "esp8266.h" volatile uint8_t UartRxData; uint8_t UartTxbuf[512]={1,2,3,4,5,6,7,8,9,10}; uint8_t UartRxbuf[512],UartIntRxbuf[512]; uint16_t UartRxIndex=0,UartRxFlag,UartRxLen=0,UartRxTimer,UartRxOKFlag,UartIntRxLen; //串口清除 uint8_t UartRecv_Clear(void) { UartRxOKFlag=0; UartRxLen=0; UartIntRxLen=0; UartRxIndex=0; return 1; } //接收标志函数,返回0说明没收据接收,返回1说明有数据收到 uint8_t Uart_RecvFlag(void) { if(UartRxOKFlag==0x55) { UartRxOKFlag=0; UartRxLen=UartIntRxLen; memcpy(UartRxbuf,UartIntRxbuf,UartIntRxLen);//把缓冲区的数据,放入需要解析的数组 UartIntRxLen=0; TcpClosedFlag = strstr ((const char *)UartRxbuf, "CLOSED\r\n" ) ? 1 : 0; return 1; } return 0; } //串口2在1字节接收完成回调函数,USART2_IRQHandler中断服务程序中调用 void HAL_UART_RxCpltCallback(void) { UartRxFlag=0x55;//接收标志置位 UartIntRxbuf[UartRxIndex]= USART_ReceiveData(USART2);//数据写入缓冲区 UartRxIndex++;//记载数目加1 if(UartRxIndex>=512)//缓冲区是512字节,如果存满,归零 { UartRxIndex=0; } USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//接收中断打开 } //1ms调用一次,用来判断是否收完一帧 void UART_RecvDealwith(void) { if(UartRxFlag==0x55) { if(UartIntRxLen<UartRxIndex)//UartIntRxLen小于UartRxIndex,说明有收到新的数据,把接收长度增加 { UartIntRxLen=UartRxIndex; }else { UartRxTimer++; if(UartRxTimer>=50)//50ms,等待,没收到新数据,说明已经收完一帧 { UartRxTimer=0; UartRxFlag=0; UartRxOKFlag=0x55; UartRxIndex=0; } } } } //USART2管脚初始化 void GPIO_Config_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; /* Configure USART2 Rx as input floating */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure USART2 Tx as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); } void RCC_Configuration(void) { /* Enable GPIO clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); /* Enable USART2 Clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); } void USART_Config_Init(void) { USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 115200; 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(USART2, &USART_InitStructure); /* Enable USARTy Receive interrupts */ USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); /* Enable the USART2 */ USART_Cmd(USART2, ENABLE); } void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Configure the NVIC Preemption Priority Bits */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); /* Enable the USART2 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //主函数,采用外部8M晶振,72M系统主频,可以在void SetSysClock(void)函数中选择主频率设置 int main(void) { delay_init(); //延时函数初始化,通过Systick中断实现1ms延时功能 RCC_Configuration();//串口2时钟初始化 NVIC_Configuration();//串口2中断配置 GPIO_Config_Init(); //USART2管脚PA2,PA3初始化 USART_Config_Init();//串口2配置,115200波特率,无校验,1位停止,8位数据 ESP8266_Init();//ESP8266复位初始化 while(1) { ESP8266_STA_TCPClient_Test();//测试TCP通讯 } }
#include "esp8266.h" #include "stm32f10x.h" #include <stdarg.h> #include "string.h" extern uint8_t UartRxbuf[512]; extern uint16_t UartRxLen; extern uint8_t UartRecv_Clear(void); struct STRUCT_USART_Fram ESP8266_Fram_Record_Struct = { 0 }; //定义了一个数据帧结构体 void ESP8266_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = ESP8266_RST_Pin; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(ESP8266_RST_Pin_Port, &GPIO_InitStructure);//复位配置成输出 ESP8266_RST_Pin_Periph_Clock(); ESP8266_Rst(); } //对ESP8266模块发送AT指令 // cmd 待发送的指令 // ack1,ack2;期待的响应,为NULL表不需响应,两者为或逻辑关系 // time 等待响应时间 //返回1发送成功, 0失败 bool ESP8266_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,u32 time) { UartRecv_Clear(); //重新接收新的数据包 ESP8266_USART("%s\r\n", cmd); if(ack1==0&&ack2==0) //不需要接收数据 { return true; } delay_ms(time); //延时 delay_ms(1000); if(Uart_RecvFlag()==1) { UartRxbuf[UartRxLen]='\0'; } // printf("%s",UartRxbuf); if(ack1!=0&&ack2!=0) { return ( ( bool ) strstr ( (const char*)UartRxbuf, ack1 ) || ( bool ) strstr ( (const char*)UartRxbuf, ack2 ) ); } else if( ack1 != 0 ) //strstr(s1,s2);检测s2是否为s1的一部分,是返回该位置,否则返回false,它强制转换为bool类型了 return ( ( bool ) strstr ( (const char*)UartRxbuf, ack1 ) ); else return ( ( bool ) strstr ( (const char*)UartRxbuf, ack2 ) ); } //复位重启 void ESP8266_Rst(void) { ESP8266_RST_Pin_SetL; delay_ms(500); ESP8266_RST_Pin_SetH; } //发送恢复出厂默认设置指令将模块恢复成出厂设置 void ESP8266_AT_Test(void) { char count=0; delay_ms(1000); while(count < 10) { if(ESP8266_Send_AT_Cmd("AT+RESTORE","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 ( "AT+CWMODE=1", "OK", "no change", 2500 ); case AP: return ESP8266_Send_AT_Cmd ( "AT+CWMODE=2", "OK", "no change", 2500 ); case STA_AP: return ESP8266_Send_AT_Cmd ( "AT+CWMODE=3", "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, "AT+CWJAP=\"%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, "AT+CIPMUX=%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, "AT+CIPSTART=%d,%s", id, cStr); else sprintf ( cCmd, "AT+CIPSTART=%s", cStr ); return ESP8266_Send_AT_Cmd ( cCmd, "OK", "ALREAY CONNECT", 4000 ); } //透传使能 //设置成功返回true, 反之false bool ESP8266_UnvarnishSend ( void ) { if (!ESP8266_Send_AT_Cmd ( "AT+CIPMODE=1", "OK", 0, 500 )) return false; return ESP8266_Send_AT_Cmd( "AT+CIPSEND", "OK", ">", 500 ); } //ESP8266发送字符串 //enumEnUnvarnishTx是否使能透传模式 //pStr字符串 //ulStrLength字符串长度 //ucId 连接号 //设置成功返回true, 反之false bool ESP8266_SendString(FunctionalState enumEnUnvarnishTx, char * pStr, u32 ulStrLength, ENUM_ID_NO_TypeDef ucId ) { char cStr [20]; bool bRet = false; if ( enumEnUnvarnishTx ) { ESP8266_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_Send_AT_Cmd ( cStr, "> ", 0, 1000 ); bRet = ESP8266_Send_AT_Cmd ( pStr, "SEND OK", 0, 1000 ); } return bRet; } //ESP8266退出透传模式 void ESP8266_ExitUnvarnishSend ( void ) { delay_ms(1000); ESP8266_USART( "+++" ); delay_ms( 500 ); } //ESP8266 检测连接状态 //返回0:获取状态失败 //返回2:获得ip //返回3:建立连接 //返回4:失去连接 u8 ESP8266_Get_LinkStatus ( void ) { if (ESP8266_Send_AT_Cmd( "AT+CIPSTATUS", "OK", 0, 500 ) ) { if ( strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "STATUS:2\r\n" ) ) return 2; else if ( strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "STATUS:3\r\n" ) ) return 3; else if ( strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "STATUS:4\r\n" ) ) return 4; } return 0; } static char *itoa( int value, char *string, int radix ) { int i, d; int flag = 0; char *ptr = string; /* This implementation only works for decimal numbers. */ if (radix != 10) { *ptr = 0; return string; } if (!value) { *ptr++ = 0x30; *ptr = 0; return string; } /* if this is a negative value insert the minus sign. */ if (value < 0) { *ptr++ = '-'; /* Make the value positive. */ value *= -1; } for (i = 10000; i > 0; i /= 10) { d = value / i; if (d || flag) { *ptr++ = (char)(d + 0x30); value -= (d * i); flag = 1; } } /* Null terminate the string. */ *ptr = 0; return string; } /* NCL_Itoa */ void USART_printf ( USART_TypeDef* USARTx, char * Data, ... ) { const char *s; int d; char buf[16]; unsigned char TempData; va_list ap; va_start(ap, Data); while ( * Data != 0 ) // 判断数据是否到达结束符 { if ( * Data == 0x5c ) //'\' { switch ( *++Data ) { case 'r': //回车符 TempData=0x0d; USART_SendData(USARTx, TempData); while (RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC));//发送完成判断 Data ++; break; case 'n': //换行符 TempData=0x0a; USART_SendData(USARTx, TempData); while (RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC));//发送完成判断 Data ++; break; default: Data ++; break; } } else if ( * Data == '%') { switch ( *++Data ) { case 's': //字符串 s = va_arg(ap, const char *); for ( ; *s; s++) { TempData=*s; USART_SendData(USARTx, TempData); while (RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC));//发送完成判断 } Data++; break; case 'd': //十进制 d = va_arg(ap, int); itoa(d, buf, 10); for (s = buf; *s; s++) { TempData=*s; USART_SendData(USARTx, TempData); while (RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC));//发送完成判断 } Data++; break; default: Data++; break; } } else { TempData=*Data++; USART_SendData(USARTx, TempData); while (RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC));//发送完成判断 } } } //下面为ESP8266MQTT功能指令 /* *MQTT配置用户属性 *LinkID 连接ID,目前只支持0 *scheme 连接方式,这里选择MQTT over TCP,这里设置为1 *client_id MQTTclientID 用于标志client身份 *username 用于登录 MQTT 服务器 的 username *password 用于登录 MQTT 服务器 的 password *cert_key_ID 证书 ID, 目前支持一套 cert 证书, 参数为 0 *CA_ID 目前支持一套 CA 证书, 参数为 0 *path 资源路径,这里设置为"" *设置成功返回true 反之false */ bool ESP8266_MQTTUSERCFG( char * pClient_Id, char * pUserName,char * PassWord) { char cCmd [120]; sprintf ( cCmd, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"", pClient_Id,pUserName,PassWord ); return ESP8266_Send_AT_Cmd( cCmd, "OK", NULL, 500 ); } /* *连接指定的MQTT服务器 *LinkID 连接ID,目前只支持0 *IP:MQTT服务器上对应的IP地址 *ComNum MQTT服务器上对应的端口号,一般为1883 *设置成功返回true 反之false */ bool ESP8266_MQTTCONN( char * Ip, int Num) { char cCmd [120]; sprintf ( cCmd,"AT+MQTTCONN=0,\"%s\",%d,0", Ip,Num); return ESP8266_Send_AT_Cmd( cCmd, "OK", NULL, 500 ); } /* *订阅指定连接的 MQTT 主题, 可重复多次订阅不同 topic *LinkID 连接ID,目前只支持0 *Topic 订阅的主题名字,这里设置为Topic *Qos值:一般为0,这里设置为1 *设置成功返回true 反之false */ bool ESP8266_MQTTSUB(char * Topic) { char cCmd [120]; sprintf ( cCmd, "AT+MQTTSUB=0,\"%s\",1",Topic ); return ESP8266_Send_AT_Cmd( cCmd, "OK", NULL, 500 ); } /* *在LinkID上通过 topic 发布数据 data, 其中 data 为字符串消息 *LinkID 连接ID,目前只支持0 *Topic 订阅的主题名字,这里设置为Topic *data:字符串信息 *设置成功返回true 反之false */ bool ESP8266_MQTTPUB( char * Topic,char *temp) { char cCmd [120]; sprintf (cCmd, "AT+MQTTPUB=0,\"%s\",\"%s\",1,0", Topic ,temp); return ESP8266_Send_AT_Cmd( cCmd, "OK", NULL, 1000 ); } /* *关闭 MQTT Client 为 LinkID 的连接, 并释放内部占用的资源 *LinkID 连接ID,目前只支持0 *Topic 订阅的主题名字,这里设置为Topic *data:字符串信息 *设置成功返回true 反之false */ bool ESP8266_MQTTCLEAN(void) { char cCmd [120]; sprintf ( cCmd, "AT+MQTTCLEAN=0"); return ESP8266_Send_AT_Cmd( cCmd, "OK", NULL, 500 ); } //ESP8266发送字符串 //enumEnUnvarnishTx是否使能透传模式 //pStr字符串 //ulStrLength字符串长度 //ucId 连接号 //设置成功返回true, 反之false bool MQTT_SendString(char * pTopic,char *temp2) { bool bRet = false; ESP8266_MQTTPUB(pTopic,temp2); delay_ms(1000); bRet = true; return bRet; }
···
#ifndef __ESP8266_H
#define __ESP8266_H
#include “stm32f10x.h”
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#if defined ( __CC_ARM )
#pragma anon_unions
#endif
//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;
#define ESP8266_RST_Pin GPIO_Pin_3 //复位管脚
#define ESP8266_RST_Pin_Port GPIOC //复位
#define ESP8266_RST_Pin_Periph_Clock() RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //PC时钟
#define ESP8266_RST_Pin_SetH GPIO_SetBits(ESP8266_RST_Pin_Port, ESP8266_RST_Pin)
#define ESP8266_RST_Pin_SetL GPIO_ResetBits(ESP8266_RST_Pin_Port, ESP8266_RST_Pin)
#define ESP8266_USART(fmt, …) USART_printf (USART2, fmt, ##VA_ARGS)
#define PC_USART(fmt, …) printf(fmt, ##VA_ARGS) //这是串口打印函数,串口2,执行printf后会自动执行fput函数,重定向了printf。
#define RX_BUF_MAX_LEN 1024 //最大字节数
extern struct STRUCT_USART_Fram //数据帧结构体
{
char Data_RX_BUF[RX_BUF_MAX_LEN];
union
{
__IO u16 InfAll;
struct
{
__IO u16 FramLength :15; // 14:0
__IO u16 FramFinishFlag :1; // 15
}InfBit;
};
}ESP8266_Fram_Record_Struct;
//初始化和TCP功能函数
void ESP8266_Init(void);
void ESP8266_AT_Test(void);
bool ESP8266_Send_AT_Cmd(char *cmd,char *ack1,char ack2,u32 time);
void ESP8266_Rst(void);
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_SendString(FunctionalState enumEnUnvarnishTx, char * pStr, u32 ulStrLength, ENUM_ID_NO_TypeDef ucId );
bool ESP8266_UnvarnishSend ( void );
void ESP8266_ExitUnvarnishSend ( void );
u8 ESP8266_Get_LinkStatus ( void );
void USART_printf ( USART_TypeDef USARTx, char * Data, … );
//MQTT功能函数
bool ESP8266_MQTTUSERCFG( char * pClient_Id, char * pUserName,char * PassWord);
bool ESP8266_MQTTCONN( char * Ip, int Num);
bool ESP8266_MQTTSUB(char * Topic);
bool ESP8266_MQTTPUB( char * Topic,char *temp);
bool ESP8266_MQTTCLEAN(void);
bool MQTT_SendString(char * pTopic,char *temp2);
extern void delay_ms(__IO uint32_t nTime);
extern uint8_t Uart_RecvFlag(void);
#endif
···
#include "tcp.h" #include "esp8266.h" #include "stdio.h" #include "string.h" #include "stm32f10x.h" volatile u8 TcpClosedFlag = 0; extern uint8_t Uart_RecvFlag(void); void ESP8266_STA_TCPClient_Test(void) { u8 res; char str[100]={0}; ESP8266_AT_Test(); ESP8266_Net_Mode_Choose(STA); while(!ESP8266_JoinAP(User_ESP8266_SSID, User_ESP8266_PWD)); ESP8266_Enable_MultipleId ( DISABLE ); while(!ESP8266_Link_Server(enumTCP, User_ESP8266_TCPServer_IP, User_ESP8266_TCPServer_PORT, Single_ID_0)); while(!ESP8266_UnvarnishSend()); while ( 1 ) { sprintf (str,"傻逼校园网\r\n" );//格式化发送字符串到TCP服务器 ESP8266_SendString ( ENABLE, str, 0, Single_ID_0 ); delay_ms(200); Uart_RecvFlag();//接收数据 if(TcpClosedFlag) //判断是否失去连接 { ESP8266_ExitUnvarnishSend(); //退出透传模式 do { res = ESP8266_Get_LinkStatus(); //获取连接状态 } while(!res); if(res == 4) //确认失去连接,重连 { while (!ESP8266_JoinAP(User_ESP8266_SSID, User_ESP8266_PWD ) ); while (!ESP8266_Link_Server(enumTCP, User_ESP8266_TCPServer_IP, User_ESP8266_TCPServer_PORT, Single_ID_0 ) ); } while(!ESP8266_UnvarnishSend()); } } }
#ifndef __TCP_H #define __TCP_H #include "stm32f10x.h" /* *以下参数需要用户自行修改才能测试用过 */ #define User_ESP8266_SSID "shiyan" //wifi名 #define User_ESP8266_PWD "12345678" //wifi密码 #define User_ESP8266_TCPServer_IP "192.168.137.1" //服务器IP #define User_ESP8266_TCPServer_PORT "8888" //服务器端口号 extern volatile uint8_t TcpClosedFlag; //连接状态标志 void ESP8266_STA_TCPClient_Test(void); extern void delay_ms(__IO uint32_t nTime); #endif
该部分的参数需要自己进行修改, 可以通过反复断开和连接确认自己的设备IP。
我第一次测试程序的时候,代码没有问题。但就是接受不到信息。开始觉得可能是防火墙的关系,测试之后,与防火墙没有关系。
最后发现是校园网的关系,具体的影响方式不太了解。
千万不要用校园网做测试。
如果需要源代码,评论区留邮箱!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。