皋陶 发表于 2020-8-28 11:38:12

RISC-V单片机快速入门06-控制ESP8266启动Http Server

本帖最后由 皋陶 于 2020-8-28 17:16 编辑

RISC-V单片机快速入门01-开发环境搭建
RISC-V单片机快速入门02-移植RT_Thread Nano
RISC-V单片机快速入门03-基于RT_Thread Nano添加控制台
RISC-V单片机快速入门04-基于RT_Thread Nano添加FinSH
RISC-V单片机快速入门05-串口助手发送AT指令启动TCP Server
RISC-V单片机快速入门06-控制ESP8266启动Http Server
RISC-V单片机快速入门07-板载LCD显示ESP8266数据


前言:上一节,我们使用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如果不再增加,说明一帧数据接收完成。
static void timeout1(void *parameter)
{
    int sock_id = -1;
    char buff = { 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;
    }
}
上述代码为上一节的代码,根据串口收到的数据内容,将数据分为三类:连接通知、断开通知、数据传输通知,
本节使用http client访问,会同时收到"CONNECT“和"+IPD",因此需要将代码做如下修改:
增加同时收到"CONNECT"和"+IPD"判断,方便主程序检测到网页访问。
static void timeout1(void *parameter)
{
    int sock_id = -1;
    char buff = { 0x00 };
    int len = 0;
    sys_event_e event = 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;

            // http data include both "CONNECT" and "IPD"
            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;
            }
            // 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;
    }
}
2.2 事件处理事件处理模块主要包含应用程序大循环,大循环中检测系统事件状态,根据事件状态再大循环中做出响应;
上一节此处做的处理比较简单,将收到的数据原路返回,本节需要判断网页发送的数据内容,返回不同的数据给网页,网页访问http://192.168.4.1:8089/getdata 返回{“pm25”:2},网页访问http://192.168.4.1:8089/index 返回 index
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<h1>index</h1>";

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();
    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;
}
三、运行
下载程序完毕后,重启设备,ESP01S启动一个WIFI热点,并启动TCP Server,log如下:


电脑连接热点,网页访问http://192.168.4.1:8089/index



网页访问http://192.168.4.1:8089/getdata



从上文可知,ESP-01S已经启动了HTTP Server并能接收网页的访问。

四、结语一叶孤沙出品:一沙一世界,一叶一菩提
页: [1]
查看完整版本: RISC-V单片机快速入门06-控制ESP8266启动Http Server