ESP8266WiFi模块实现TCP连接服务器
在前面的博客里(STM32-ESP8266wifi模块实现)说到了通过AT命令配置ESP8266实现TCP连接。 他的流程是, 1,使能串口,及中断。 2,发送AT指令到ESP8266WiFi模块,进行配置一些参数。
串口配置
串口初始化
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
HAL_UART_Receive_IT(&huart1 , &uart1_receive_rx, 1);
}
void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
HAL_UART_Receive_IT(&huart2 , &uart2_receive_rx, 1);
}
HAL_UART_Receive_IT(&huart1 , uint8_t *pData, uint16_t Size);
-
功能:串口中断接收,以中断方式接收指定长度数据。大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)
-
参数:
UART_HandleTypeDef *huart 操作句柄 *pData 接收到的数据存放地址 Size 接收的字节数
GPIO配置及开启中断
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
配置GPIO
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
else if(uartHandle->Instance==USART2)
{
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
}
}
初始化之后的引脚恢复成默认的状态–各个寄存器复位时的值
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_DISABLE();
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
HAL_NVIC_DisableIRQ(USART1_IRQn);
}
else if(uartHandle->Instance==USART2)
{
__HAL_RCC_USART2_CLK_DISABLE();
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);
HAL_NVIC_DisableIRQ(USART2_IRQn);
}
}
串口中断服务函数
串口产生中断,就会自动调用USART1_IRQHandler,USART2_IRQHandler中断服务函数,以串口1USART1为例;
void USART1_IRQHandler(void)
{
uint32_t timeout=0;
uint32_t maxDelay=0x1FFFF;
#if SYSTEM_SUPPORT_OS
OSIntEnter();
#endif
HAL_UART_IRQHandler(&huart1);
timeout=0;
while (HAL_UART_GetState(&huart1)!=HAL_UART_STATE_READY)
{
timeout++;超时处理
if(timeout>maxDelay)
break;
}
timeout=0;
while(HAL_UART_Receive_IT(&huart1,uart1_receive_rx, 1)!=HAL_OK)
{
timeout++;
if(timeout>maxDelay)
break;
}
#if SYSTEM_SUPPORT_OS
OSIntExit();
#endif
}
这个函数中,首先调用HAL_UART_IRQHandler(&huart1); 其主要用于判断中断类型。通过这个中断处理函数会调用UART_Receive_IT(huart); 该函数是将接收到的数据保存到指定的串口缓冲区,也就是变量uart1_receive_rx;。该函数每执行一次(接受一个字符),pRxBufferPtr指针加1,并保存接收到的字符到 uart1_receive_rx ,同时RxXferCount数据减1;接着再判断,RxXferCount自减后是否等于0.如果等于0 ,则关闭接收完成中断,进行中断回调函数。如果需要再一次开启时。需要再一次开启中断,也就是调用 HAL_UART_Receive_IT()函数。
定义接收buffer
我们在usart.c 文件中要定义相关中断接收buffer。
static uint8_t uart1_receive_rx;
char buf1[256];
uint8_t uart1_b;
static uint8_t uart2_receive_rx;
char buf2[256];
uint8_t uart2_b;
中断接收回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
if( uart1_b< sizeof(uart1_receive_buf) )
{
buf2[uart1_b++] = uart1_receive_rx;
}
HAL_UART_Receive_IT(&huart1 , &uart1_receive_rx, 1);
}
if (huart->Instance == USART2)
{
if( uart1_b< sizeof(buf2) )
{
buf2[uart2_b++] = uart2_receive_rx;
}
HAL_UART_Receive_IT(&huart2 , &uart2_receive_rx, 1);
}
}
下面一张流程图也可以很好的理解串口中断接收: 函数流程图:
HAL_UART_Receive_IT(中断接收函数) -> USART_IRQHandler(void)(中断服务函数) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) -> UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) -> HAL_UART_RxCpltCallback(huart);(中断回调函数)
串口发送数据:
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。 参数:
UART_HandleTypeDef *huart 转发huart1/huart2的句柄 *pData 需要发送的数据 Size 发送的字节数 Timeout 最大发送时间,发送数据超过该时间退出发送
HAL_UART_Transmit(&huart1, uart1_receive_rx, uart1_b, 0xffff);
串口的转发函数:
void uart_forward(void)
{
if ( strstr(buf1, "/r/n") )
{
HAL_UART_Transmit(&huart2 ,(uint8_t *)buf1, uart1_b, 0xFF);
clear_buf1();
}
if ( uart2_b > 0 )
{
HAL_UART_Transmit(&huart1, (uint8_t *)buf2, uart2_b, 0xFF);
clear_buf2();
}
}
extern char buf1[256];
extern uint8_t uart1_b;
#define clear_buf1() do { memset( buf1, 0, sizeof(buf1)); \
uart1_b=0; } while(0)
extern char buf2[256];
extern uint8_t uart2_b;
#define clear_buf2() do { memset(buf2, 0, sizeof(buf2)); \
uart2_b=0; } while(0)
这样我们可以用AT命令从串口接收数据到转发数据来配置esp8266wifi模块。
esp8266.c
接下我们写一下通过AT命令连接WiFi和连接服务器的代码;
一、ESP8266WiFi模块初始化 1, AT+RST 重启、复位ESP8266命令 2, AT 确认模块是否正常工作 3, AT+GMR 获取WiFi模块的软件版本信息 4, AT+CWMODE_CUR=1 设置WiFi为Station模式 5, AT+CWDHCP_CUR=1,1 设置使能Station模式的DHCP服务; 二、连网 1, AT+CWJAP_CUR=“路由器的ID”,“密码” 连接无线路由器 2, AT+CIPSTA_CUR? 查询是否获取到IP地址 三、TCP连接 3, AT+CIPSTART=“TCP”,“192.168.2.172”,12345 IP和端口 4,AT+CIPSEND=bytes (等收到模块返回 > 后再输入bytes个字节数据将会通过WiFi模块发送出去;) 5, AT+CIPCLOSE //关闭TCP连接;
但是,由于这个流程是我们自己需要每次使用AT指令去实现,比较麻烦。所以我们自己需要编写程序代码来自动实现这些功能。 这样,我们就可以直接连接好服务器来接收ESP8266WiFi模块发送到服务器的数据了。
在写代码之前我们做一个简单的梳理,我们现在可以通过桥接好的串口1,2给esp8266wifi模块发送AT命令了,具体流程是:pc通过串口1发送AT命令到串口转发程序(通信猫测试软件;)然后通过串口2转发AT命令到esp8266wifi模块; 每当我们发送一个AT命令完成时,esp8266WiFi模块都会做相应的reply,正确执行会reply “AT命令加OK”;否则会reply "At命令加ERROR "; 比如:
void esp8266_wifi_init(void)
{
AT_send("AT+RST\r\n", 500);
clear_buf();
HAL_Delay(500);
AT_send("AT\r\n");
clear_buf();
AT_send("AT+GMR"\r\n, 500);
clear_buf();
AT_send("AT+CWMODE_CUR=1", 500);
clear_buf();
AT_send("AT+CWDHCP_CUR=1,1", 500);
clear_buf();
}
int AT_send(char *at_buf2, unsigned int timeout)
{
int rv = -1;
unsigned int i;
if(!at_buf2 || strlen(at_buf2) <= 0)
{
return -1;
esp8266wifi_print("error to get data\r\n");
}
esp8266wifi_print("Start to send AT command to esp8266\n");
HAL_UART_Transmit(wifi_huart2, (uint_8 *)at_buf2, strlen(at_buf2), 1000);
for(i=0; i<timeout; i++)
{
if (strstr(at_buf2, "OK\r\n"))
{
rv = 0;
esp8266wifi_print("\r\nget data%s", at_buf2);
return 0;
}
if (strstr(at_buf2, "ERROR\r\n"))
{
rv = 1;
esp8266wifi_print("\r\n error to get data:%s", at_buf2);
return 1;
}
if (strstr(at_buf2, "FAIL\r\n"))
{
rv = 1;
esp8266wifi_print("\r\n error to connecte WIFI");
}
HAL_Delay(5);
return 0;
}
}
int esp8266_enter_network(char *id, char *password)
{
int rv ;
char at_buf2[256];
if (!id || strlen(password) <= 0)
{
esp8266wifi_print("\r\n error to get data");
return -1;
}
memset(at_buf2, 0, sizeof(at_buf2));
snprintf(at_buf2, sizeof(at_buf2), "AT+CWJAP_CUR="%s","%s"\r\n", id, password);
AT_send(at_buf2, 8000);
chear_buf();
return 0;
}
int esp8266_socket_connecte(char *serverip, int port)
{
char at_buf2[256];
int rv = -1;
if (!serverip || !port)
{
esp8266wifi_print("\r\n error to get data");
return rv;
}
AT_send("AT+CIPMUX=0\r\n", 500);
memset(at_buf2, 0, sizeof(at_buf2));
snprintf(at_buf2, sizeof(at_buf2), "AT+CIPSTART="%s","%d"\r\n", serverip, port);
AT_send(at_buf2,500);
clear_buf();
}
int esp8266_socket_disconnecte(void)
{
int rv=0;
AT_send("AT+CIPCLOSE\r\n", 500);
return rv;
}
int sock_send_data(unsigned char *data, int at_uart2_bytes)
{
int sta;
char at_buf2[64];
memset(at_buf2, 0, sizeof(at_buf2));
snprintf(at_buf2, sizeof(at_buf2), "AT+CIPSEND="%d"\r\n", at_uart2_bytes);
if (strstr(at_buf2, ">\r\n"))
{
sta = 1;
esp8266wifi_print("send data to server\r\n");
}
else
{
sta = 0;
esp8266wifi_print("send data to server error\r\n");
}
if (sta != 0)
{
AT_send_data(data, at_uart2_bytes, 800);
return at_uart2_bytes;
}
}
int AT_send_data(unsigned char *data, int at_uart2_bytes, unsigned char timeout)
{
int rv = -1;
unsigned int i;
if (!data || at_uart2_bytes <= 0)
{
esp8266wifi_print("ERROR %s:%d %s(): Invalid input arguments\r\n", __FILE__, __LINE__, __FUNCTION__);
return -1;
}
esp8266wifi_print(">>>Start to send data \r\n");
clear_buf();
HAL_UART_Transmit(wifi_huart2, (uint8_t *)data, at_uart2_bytes, 1000);
for (i=0; i<timeout; i++)
{
if (strstr(at_buf2, "OK\r\n"))
{
rv = 0;
esp8266wifi_print(">>>Start to send data :%s\r\n", at_buf2);
return rv;
}
if (strstr(at_buf2, "ERROR\r\n"))
{
rv =1;
esp8266wifi_print(">>>Start to send data error:%s\r\n", at_buf2);
return rv;
}
HAL_Delay(5);
}
}
esp8266.h
#ifndef _ESP8266_H_
#define _ESP8266_H_
#define at_buf2 buf2
#define at_uart2_bytes uart2_b
#define wifi_huart2 huart2
#ifdef CONFIG_DEBUG_WIFI
#define esp8266wifiprint(format,args...) printf(format, ##args)
#else
#define esp8266wifiprint(format,args...) do{} while(0)
#endif
extern void esp8266_wifi_init(void);
extern int AT_send(char *at_buf2, unsigned int timeout);
extern int AT_send_data(unsigned char *data, int at_uart2_bytes, unsigned int timeout);
extern int esp8266_enter_network(char *id, char *password);
extern int esp8266_socket_connecte(char *serverip, int port);
extern int int esp8266_socket_disconnecte(void);
main.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"esp8266.h"
//添加WiFi ID PASSORD , serverip, port ,
#define DEF_ROUTER_SSID "Router_wifi"
#define DEF_ROUTER_PWD "Router@wifi"
#define DEF_ROUTER_IP "192.168.101.5"
#define SOCKET_SERVER_IP "192.168.101.5"
#define SOCKET_SERVER_PORT 12345
//初始化,连接WiFi,连接服务器。
esp8266_wifi_init();
esp8266_enter_network(DEF_ROUTER_SSID, DEF_ROUTER_PWD);
esp8266_socket_connecte(SOCKET_SERVER_IP, SOCKET_SERVER_PORT);
|