IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> ESP8266WiFi模块实现代码 -> 正文阅读

[网络协议]ESP8266WiFi模块实现代码

ESP8266WiFi模块实现TCP连接服务器

在前面的博客里(STM32-ESP8266wifi模块实现)说到了通过AT命令配置ESP8266实现TCP连接。
他的流程是,
1,使能串口,及中断。
2,发送AT指令到ESP8266WiFi模块,进行配置一些参数。

串口配置

串口初始化

// //配置串口,串口初始化。(usart1/usart2)
void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;//设置波特率
  huart1.Init.WordLength = UART_WORDLENGTH_8B;//字长为8位数据格式
  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();
  } 
 //在这个函数里使能usart1的接收中断,中断接收的每个字节存储在 uart1_receive_rx 中
  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;//字长为8位数据格式
  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();
  }
 //使能usart2的接收中断,中断接收的每个字节存储在 uart2_receive_rx 变量中
//每接收一个字节就会进入一次中断USART1_IRQHandler
//处理数据的函数,读取接收寄存器,将数据赋值给结构体huart的pRxBuffer并清除中断标志,
  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)
  {
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();//使能串口USART1时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIO时钟
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
   
    */配置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);
	//根据GPIO_INITTypeDef结构体中Pin来找到对应的要操作的引脚位置
	//初始化我们需要用到的引脚的工作模式,包括具体引脚的工作速度、是否复用模式、上下拉等等参数。
    
    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  
  }
  else if(uartHandle->Instance==USART2)
  {
  
    /* USART2 clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART2 GPIO Configuration
    PA2     ------> USART2_TX
    PA3     ------> USART2_RX
    */
    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);
    //根据GPIO_INITTypeDef结构体中Pin来找到对应的要操作的引脚位置
    //初始化我们需要用到的引脚的工作模式,包括具体引脚的工作速度、是否复用模式、上下拉等等参数。

    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
  
  }
}

初始化之后的引脚恢复成默认的状态–各个寄存器复位时的值

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
    //将初始化之后的引脚恢复成默认的状态–各个寄存器复位时的值
	
    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
 
  }
  else if(uartHandle->Instance==USART2)
  {
  
    /* Peripheral clock disable */
    __HAL_RCC_USART2_CLK_DISABLE();

    /**USART2 GPIO Configuration
    PA2     ------> USART2_TX
    PA3     ------> USART2_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);
	//将初始化之后的引脚恢复成默认的状态–各个寄存器复位时的值

    /* USART2 interrupt Deinit */
    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	 	//使用OS
	OSIntEnter();    
#endif
	
	HAL_UART_IRQHandler(&huart1);	//调用HAL库中断处理函数,通过这个中断处理函数会调用UART_Receive_IT(huart);
	
	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)//一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
        timeout++; //超时处理
        if(timeout>maxDelay) 
        	break;	
	}
#if SYSTEM_SUPPORT_OS	 	//使用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。

//USART1
static uint8_t  uart1_receive_rx;
char	        buf1[256];
uint8_t			uart1_b;
//USART2
static uint8_t	uart2_receive_rx;
char			buf2[256];
uint8_t			uart2_b;

中断接收回调函数

//将uart1,uart2接收数据加进buf中。
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);   //串口发送uart1_b字节数据,最大传输时间0xffff

串口的转发函数

// 这个函数在我们来手动测试AT命令时需要用到串口转发函数,下篇博文会自动完成相关AT命令的操作;
void uart_forward(void)
{
	if ( strstr(buf1, "/r/n") )
	{
			HAL_UART_Transmit(&huart2 ,(uint8_t *)buf1, uart1_b, 0xFF);//最大传输时间0xFF
			clear_buf1();//这是一个宏用来清理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 ";
比如:
在这里插入图片描述

// 我们将实现的功能按模块来封装,每一个函数模块来执行相应地功能,最后将其连贯成一个功能;
//WiFi模块初始化,这个函数是用来初始化WiFi模块的一些配置,如设置station模式,使能DHCP自动获取IP
//这个函数是一个无参函数,相对于连接WiFi和连接服务器函数来说,只需要输入我们的所配置的AT指令,不需要变化;
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();
}

//发送AT指令函数,将AT指令发送到串口2。
//之前我们实现了AT指令从串口1一接收,发送给串口2,串口2转发到esp8266.
int AT_send(char *at_buf2, unsigned int timeout)//timeout:
{
		int 			rv = -1;
	   unsigned int 			i;// 0 - 255
			
		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);
		//当我们发送一个数据到esp8266WiFi模块,它会reply;
		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;	
		}	
}
//接下来我们初始化完成之后就可以去加入网络了,连接WiFi;
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;
}

//下面就可以发送数据给server了。
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;
	}
}
//发送我们编写数据data的函数,用来发送我们想要给server的信息;
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);


  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 13:30:27  更:2021-07-28 13:30:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:17:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码