前言:
上一节,我们使用GD32VF103控制ESP-01S启动TCP Server,然后让多个网络调试助手连接ESP-01S并与之通信,本节我们在上一节基础上完成控制ESP-01S启动HTTP Server的功能,使用网页访问ESP-01S。
一、基础知识
1.HTTP简介
HTTP是Hypertext Transfer Protocol的缩写,Hypertext(超文本)是可以根据客户端请求而跳转的结构化信息。HTTP协议的请求及相应方式设计如下图所示:
从图中可以看出,服务器端响应客户端请求后立刻断开连接,连接不会维持很久,即使同一个客户端再次发送请求,服务端也无法辨认出是否是原先的那个客户端发出的请求,会以相同的方式处理新的请求。
2.HTTP请求
HTTP请求是客户端向服务端发送请求消息,请求消息可以分为请求行、消息头、消息体三个部分;请求行含有请求方式信息(GET/POST等),GET用于请求数据,POST主要用于传输数据;消息头包括一些访问的域名、用户代理、Cookie等信息;消息体就是请求的数据,仅在POST方式请求时候输入。
3.HTTP响应
HTTP响应是指服务端根据客户端发送的请求中的动作要求做出具体的动作,然后将结果返回给客户端。 HTTP响应消息可以分为状态行、头信息、消息体三个部分;状态行含有请求的状态信息,这是其与请求消息相比最大的区别。
4.交互流程简介
(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) 大循环中检测是否收到ESP01S数据,收到数据后判断,如果是网页发来数据,返回HTTP数据。
二、程序说明
程序主要包括如下4个功能模块:ESP-01S初始化、串口处理、Event回调函数、事件处理;Http协议是基于TCP协议的,本节是在上一节基础上进行的,Event回调函数代码和上一节保持一致即可,ESP-01S初始化部分改动也较小,修改TCP server超时时间,设置1秒超时,即客户端访问数据后,服务器返回数据后主动断开连接,ESP8266_StartOrShutServer ( 1, "8089", "1" )。
2.1 串口 处理
串口处理模块包括串口接收和定时器判断一帧数据是否接收完成功能,一帧数据接收是否完成的判断逻辑是:定时器会定期检测,如果FramStartFlag为1,说明串口正在接收数据,没接收一个数据,FramLength加1,因此,当进入定时器中断函数,判断FramStartFlag为1情况下FrameLength如果不再增加,说明一帧数据接收完成。
<code>static
void
timeout1
(void
*parameter) {int
sock_id =-1
;char
buff[128
] = {0x00
};int
len =0
; sys_event_eevent
= STA_EVENT_MAX;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); }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
; } }/<code>
上述代码为上一节的代码,根据串口收到的数据内容,将数据分为三类:连接通知、断开通知、数据传输通知,本节使用http client访问,会同时收到"CONNECT“和"+IPD",因此需要将代码做如下修改:增加同时收到"CONNECT"和"+IPD"判断,方便主程序检测到网页访问。
<code>static
void
timeout1
(void
*parameter) {int
sock_id =-1
;char
buff[128
] = {0x00
};int
len =0
; sys_event_eevent
= STA_EVENT_MAX;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
;if
(rt_strstr(Esp8266_Frame_Record.Data_RX_BUF,"CONNECT"
) && 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; }else
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; }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
; } }/<code>
2.2 事件处理
事件处理模块主要包含应用程序大循环,大循环中检测系统事件状态,根据事件状态再大循环中做出响应;上一节此处做的处理比较简单,将收到的数据原路返回,本节需要判断网页发送的数据内容,返回不同的数据给网页,网页访问http://192.168.4.1:8089/getdata 返回{"pm25":2},网页访问http://192.168.4.1:8089/index 返回
index
,<code>char
*response ="HTTP/1.1 200 OK \r\n\r\n{"pm25":2}"
;char
*response_index ="HTTP/1.1 200 OK \r\n\r\n
index
"; int main(void) { gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1); gpio_bit_reset(GPIOA, GPIO_PIN_1); dynamic_thread = rt_thread_create("led_thread"
, led_process_thread_entry, RT_NULL,512
,2
,10
); rt_thread_startup(dynamic_thread); 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(); http_server_init(); http_server_start();while
(1
) {if
(STA_DATA_ARRIVED == system_context->event) {if
(rt_strstr(system_context->data_buf,"/getdata"
)) { rt_kprintf("ready to send data………. %s\r\n"
, response); ESP8266_SendString ( DISABLE, response, rt_strlen(response), system_context->sock_id ); system_context->event = STA_CONNECTED; }else
if
(rt_strstr(system_context->data_buf,"/index"
)) { rt_kprintf("ready to send data………. %s\r\n"
, response_index); ESP8266_SendString ( DISABLE, response_index, rt_strlen(response_index), system_context->sock_id ); system_context->event = STA_CONNECTED; } } rt_thread_mdelay(10
); }return
0
; }/<code>
三、运行
下载程序完毕后,重启设备,ESP01S启动一个WIFI热点,并启动TCP Server,log如下:
电脑连接热点,网页访问http://192.168.4.1:8089/index
网页访问http://192.168.4.1:8089/getdata
从上文可知,ESP-01S已经启动了HTTP Server并能接收网页的访问。