前语:
上一节,咱们运用串口东西发送AT指令操作ESP-01S,本节,运用GD32VF103替代传偶东西完结和ESP-01S模块的交互进程。
一、基础知识
1.交互流程简介
(1)设备上电,先操控8266的复位引脚为低电平,让模块复位
(2)发送指令:ATE0,撤销回显
(3)发送指令:AT+CWMODE=2,设置ESP01S为AP形式
(4)发送指令:AT+CIPMUX=1,设置多路衔接,AP形式最多支撑5个设备衔接
(5)发送指令:AT+CWSAP="ESP01S_test","12345678",1,3,发动一个WIFI热门
(6)发送指令:AT+CIPSERVER=1,8089,发动TCP Server
(7)发送指令:AT+CIPSERVER=1,8089,发动TCP Server
(8)大循环中检测是否收到ESP01S数据,收到数据后马上回来。
2.程序结构简介
程序首要包含如下4个功用模块:ESP01S初始化、串口处理、Event回调函数、事情处理;串口处理模块包含串口接纳和定时器判别一帧数据是否接纳完结功用,Event回调函数首要用来告诉应用层体系的状况,便利应用层做出相应,比方设备检测到其他TCP
Client客户端接入模块,能够操控LED状况,事情处理模块首要包含应用程序大循环,大循环中检测体系事情状况,依据事情状况再大循环中做出呼应。
二、体系功用模块胪陈
1.Event回调函数
本程序运用了函数指针,应用层将事情处理函数传到hal_common.c中int hal_sys_contex_init(sys_status_fun fun, void *user_data)函数
void system_status_callback(int sock, int event)
{
system_context->sock_id = sock;
system_context->event = event;
switch (event)
{
case STA_CONNECTED:
rt_kprintf("Sock %d connected!\r\n", sock);
break;
case STA_CLOSED:
rt_kprintf("Sock %d closed!\r\n", sock);
break;
case STA_DATA_ARRIVED:
rt_kprintf("Sock %d data arrived!\r\n", sock);
break;
default:
break;
}
}
typedef enum {
STA_CONNECTED,
STA_CLOSED,
STA_DATA_ARRIVED, // clients send data to wifi
STA_EVENT_MAX,
}sys_event_e;
typedef void (*sys_status_fun)(int sock, int event);
typedef struct sys_ctx{
int sock_id;
sys_event_e event;
char data_buf[SYS_CTX_UART_RECV_SIZE];
sys_status_fun sys_status_cb;
void *user_data;
}sys_ctx_t;
int hal_sys_contex_init(sys_status_fun fun, void *user_data)
{
sys_contex.sys_status_cb = fun;
sys_contex.user_data = user_data;
return 0;
}
int main(void)
{
hal_sys_contex_init(system_status_callback, RT_NULL);
while(1)
{
}
}
2.串口处理
串口处理模块包含串口接纳和定时器判别一帧数据是否接纳完结功用,串口接纳函数代码如下:
#define RX_BUF_MAX_LEN 1024 //最大接纳缓存字节数
struct STRUCT_USART_Fram_S //串口数据帧的处理结构体
{
char Data_RX_BUF [ RX_BUF_MAX_LEN ];
uint16_t FramLength;
struct {
uint8_t FramStartFlag;
uint8_t FramFinishFlag;
} InfBit;
} ;
struct STRUCT_USART_Fram_S Esp8266_Frame_Record;
void USART2_IRQHandler()
{
uint8_t ch = -1;
if(RESET != usart_interrupt_flag_get(EVAL_COM2, USART_INT_FLAG_RBNE))
{
ch = usart_data_receive(EVAL_COM2);
// if ( Esp8266_Frame_Record.FramLength < ( RX_BUF_MAX_LEN - 1 ) ) //预留1个字节写结束符
// {
Esp8266_Frame_Record .Data_RX_BUF [ Esp8266_Frame_Record.FramLength ] = ch;
// }
Esp8266_Frame_Record.FramLength ++;
if (Esp8266_Frame_Record.FramLength >= 1024)
{
Esp8266_Frame_Record.FramLength = 0;
}
cnt = Esp8266_Frame_Record.FramLength;
// rt_kprintf(".......uart recv : %c, count is %d\r\n", ch, cnt);
Esp8266_Frame_Record.InfBit.FramStartFlag = 1;
}
}
中止处理函数中,将接纳的数据放到Esp8266_Frame_Record .Data_RX_BUF中,然后将
Esp8266_Frame_Record.InfBit.FramStartFlag置1,这个标志位再定时器中会用到,能够用来判别接纳一帧数据是否完结。
一帧数据接纳是否完结的判别逻辑是:定时器会定时检测,假如FramStartFlag为1,阐明串口正在接纳数据,没接纳一个数据,FramLength加1,因而,当进入定时器中止函数,判别FramStartFlag为1情况下FrameLength假如不再添加,阐明一帧数据接纳完结。
static void timeout1(void *parameter)
{
int sock_id = -1;
char buff[128] = { 0x00 };
int len = 0;
sys_event_e event = STA_EVENT_MAX;
// rt_kprintf("timer's cnt is %d, FrameLength is %d\r\n", cnt, Esp8266_Frame_Record.FramLength);
if (1 == Esp8266_Frame_Record.InfBit.FramStartFlag)
{
if (cnt == Esp8266_Frame_Record.FramLength && cnt != 0)
{
cnt = 0;
Esp8266_Frame_Record .Data_RX_BUF [ Esp8266_Frame_Record.FramLength ] = 0x00;
rt_kprintf("timer --------> data %s\r\n", Esp8266_Frame_Record.Data_RX_BUF);
if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, "CONNECT"))
{
sscanf(Esp8266_Frame_Record.Data_RX_BUF, "%d,%s", &sock_id, buff);
event = STA_CONNECTED;
}else if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, "CLOSED"))
{
sscanf(Esp8266_Frame_Record.Data_RX_BUF, "%d,%s", &sock_id, buff);
event = STA_CLOSED;
}else if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, "+IPD"))
{
rt_memset(hal_sys_contex_get()->data_buf, 0x00, SYS_CTX_UART_RECV_SIZE);
sscanf(Esp8266_Frame_Record.Data_RX_BUF, "%*[^+]+IPD,%d,%d:%[^\r]", &sock_id, &len, hal_sys_contex_get()->data_buf);
event = STA_DATA_ARRIVED;
rt_kprintf("parsed +IPD :%s\r\n", hal_sys_contex_get()->data_buf);
}
// call sys_status_cb
if (hal_sys_contex_get()->sys_status_cb)
{
hal_sys_contex_get()->sys_status_cb(sock_id, event);
}
Esp8266_Frame_Record.InfBit.FramFinishFlag = 1;
Esp8266_Frame_Record.InfBit.FramStartFlag = 0;
}else
{
cnt = Esp8266_Frame_Record.FramLength;
}
}else
{
cnt = 0;
Esp8266_Frame_Record.FramLength = 0;
}
}
留意:事情处理本质上是在此调用hal_sys_contex_get()->sys_status_cb(sock_id, event)映射到应用层的void system_status_callback(int sock, int event)函数。
3.事情处理
事情处理的中心再while(1)中,依据体系当时事情状况做出呼应,本节是检测到事情为数据类型时分,将数据原路回来。
int main(void)
{
/* enable the LED clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* configure LED GPIO port */
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
gpio_bit_reset(GPIOA, GPIO_PIN_1);
// create iwdt_thread
dynamic_thread = rt_thread_create("led_thread", led_process_thread_entry,
RT_NULL, 512, 2, 10);
rt_thread_startup(dynamic_thread);
// init sys_ctx
hal_sys_contex_init(system_status_callback, RT_NULL);
system_context = hal_sys_contex_get();
hal_timer_init();
ESP8266_Init();
rt_thread_mdelay(1000);
ESP8266_Ate0();
tcp_server_init();
tcp_server_start();
while(1)
{
if (STA_DATA_ARRIVED == system_context->event)
{
// send back
ESP8266_SendString ( DISABLE, system_context->data_buf, rt_strlen(system_context->data_buf), system_context->sock_id );
}
rt_thread_mdelay(10);
}
return 0;
}
三、运转
下载程序结束后,重启设备,ESP01S发动一个WIFI热门,并发动TCP Server,log如下:
电脑衔接热门,运用网络帮手衔接192.168.4.1:8089
网络帮手发送数据给ESP01S
封闭网络帮手,应用程序也能够检测到,如下Log所示