美文网首页物联网相关技术研究物联网loT从业者
ESP8266学习笔记(6)——简易HTTP客户端

ESP8266学习笔记(6)——简易HTTP客户端

作者: Leung_ManWah | 来源:发表于2018-11-27 18:12 被阅读3次

    一、背景


    已知智能网关固定IP地址192.168.100.1,智能插座连上智能网关AP热点后,向网关发起标定设备请求,因此需要ESP8266作为HTTP客户端角色。

    Post请求和Get请求:


    二、流程

    2.1 定义相关结构体

    struct espconn http_client_conn;            // 定义一个TCP连接结构体
    LOCAL os_timer_t http_send_identify_timer;  // 定义一个定时器,因为连接上服务器瞬间发送消息会收不到
    

    2.2 初始化HTTP客户端

    /**
     @brief 初始化HTTP客户端
     @param remote_port 远程端口
     @return 无
    */
    void ICACHE_FLASH_ATTR
    user_httpclient_init(uint32 remote_port)
    {
        struct ip_info info;
        struct ip_addr remote_ip;
        char buffer[68] = "192.168.100.1";
        remote_ip.addr = ipaddr_addr(buffer);                       // 点分十进制写入IP结构体
        // 配置
        http_client_conn.type = ESPCONN_TCP;
        http_client_conn.state = ESPCONN_NONE;
        http_client_conn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp));
        wifi_get_ip_info(STATION_IF, &info);
        os_memcpy(http_client_conn.proto.tcp->local_ip, &info.ip, 4);
        os_memcpy(http_client_conn.proto.tcp->remote_ip, &remote_ip, 4);
        http_client_conn.proto.tcp->local_port = espconn_port();    // 获取可用端口作为本地端口
        http_client_conn.proto.tcp->remote_port = remote_port;
        // 注册连接成功和重连的回调函数
        espconn_regist_connectcb(&http_client_conn, http_client_connect_cb);
        espconn_regist_reconcb(&http_client_conn, http_client_recon_cb);
        // 连接服务器
        espconn_connect(&http_client_conn);
    }
    

    2.3 定义相关回调函数

    /**
     @brief 成功接收到服务器返回数据函数
     @param arg 指向传入参数的指针
     @param pdata 字符串数组
     @param len 字符串数组长度
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_client_recv_cb(void *arg, char *pdata, unsigned short len)
    {
        os_printf("httpclient_recvdata:\t%s\n", pdata);
    }
    
    /**
     @brief 发送数据到服务器成功的回调函数
     @param arg 指向传入参数的指针
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_client_send_cb(void *arg)
    {
        os_printf("httpclient_send succeed!\n");
    }
    
    /**
     @brief 断开服务器成功的回调函数
     @param arg 指向传入参数的指针
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_client_discon_cb(void *arg)
    {
        os_printf("httpclient_disconnect!\n");
    }
    
    /**
     @brief 连接失败的回调函数,err为错误代码
     @param arg 指向传入参数的指针
     @param err 为错误代码
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_client_recon_cb(void *arg, sint8 err)
    {
        os_printf("httpclient connect error, error number:%d\r\n", err);
        espconn_disconnect((struct espconn *) arg);  // 连接失败后断开
    }
    
    /**
     @brief 成功连接到服务器的回调函数
     @param arg 指向传入参数的指针
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_client_connect_cb(void *arg)
    {
        struct espconn *pespconn = arg;
        espconn_regist_recvcb(pespconn, http_client_recv_cb);
        espconn_regist_sentcb(pespconn, http_client_send_cb);
        espconn_regist_disconcb(pespconn, http_client_discon_cb);
    
        os_timer_disarm(&http_send_identify_timer);
        os_timer_setfn(&http_send_identify_timer, (os_timer_func_t *) http_send_identify_callback, NULL);
        os_timer_arm(&http_send_identify_timer, 2000, false);  // 开启发送设备认证请求的定时器
    }
    

    2.4 定时器到时后进入回调函数

    /**
     @brief 发送设备认证定时器的回调函数
     @param 无
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_send_identify_callback(void)
    {
        http_identify_device();   // 向核心路由器发送设备认证
    }
    
    /**
     @brief 发送设备认证
     @param 无
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_identify_device(void)
    {
        char URL[32] = "192.168.100.1";
        unsigned short port = 8080;
        char postdata[128] = {0};
        char host[32] = {0};
        char filename[32] = {0};
        uint8 id_buf[20] = {0};
        unsigned int nId = 0;
            
        // 创建JSON数据包
        cJSON *Result = cJSON_CreateObject();
        cJSON * array = cJSON_CreateArray();
        cJSON_AddStringToObject(Result, "Module", "ServiceObject");
        cJSON_AddStringToObject(Result, "Func", "append");
        // 添加 Info 数组
        cJSON_AddItemToObject(Result,"Info",array);
        cJSON * array_item = cJSON_CreateObject();
        cJSON_AddItemToArray(array, array_item);
        os_sprintf(id_buf, "%x", system_get_chip_id());
        nId = HexStrToDec(id_buf);
        cJSON_AddNumberToObject(array_item, "id", nId);
        cJSON_AddNumberToObject(array_item, "type", 2);
    
        char *succeedData = cJSON_Print(Result);
        os_sprintf(postdata, "%s", succeedData);
        //os_printf("data:%s\n", postdata);
    
        http_post_request(URL, host, filename, &port, postdata);  // 发送POST请求
        cJSON_Delete(Result);
    }
    

    2.5 发送POST请求

    #define GET "GET /%s HTTP/1.1\r\nContent-Type: text/html;charset=utf-8\r\nAccept: */*\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n"
    #define POST "POST /%s HTTP/1.1\r\nAccept: */*\r\nContent-Length: %d\r\nContent-Type: application/json\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n%s"
    
    /**
     @brief 解析URL
     @param tbd
     @return 无
    */
    LOCAL void ICACHE_FLASH_ATTR
    http_parse_request_url(char *URL, char *host, char *filename, unsigned short *port)
    {
        char *PA;
        char *PB;
    
        memset(host, 0, sizeof(host));
        memset(filename, 0, sizeof(filename));
    
        *port = 0;
    
        if (!(*URL))
        {
            return;
        }
    
        PA = URL;
    
        if (!strncmp(PA, "http://", strlen("http://")))
        {
            PA = URL + strlen("http://");
        }
        else if (!strncmp(PA, "https://", strlen("https://")))
        {
            PA = URL + strlen("https://");
        }
    
        PB = strchr(PA, '/');
    
        if (PB)
        {
            memcpy(host, PA, strlen(PA) - strlen(PB));
            if (PB + 1)
            {
                memcpy(filename, PB + 1, strlen(PB - 1));
                filename[strlen(PB) - 1] = 0;
            }
            host[strlen(PA) - strlen(PB)] = 0;
        }
        else
        {
            memcpy(host, PA, strlen(PA));
            host[strlen(PA)] = 0;
        }
    
        PA = strchr(host, ':');
    
        if (PA)
        {
            *port = atoi(PA + 1);
        }
        else
        {
            *port = 80;
        }
    }
    
    /**
     @brief 发送POST请求
     @param tbd
     @return 无
    */
    void ICACHE_FLASH_ATTR
    http_post_request(char *URL, char *host, char *filename, unsigned short *port, char *postdata)
    {
        char buffer[1024] = {0};
        http_parse_request_url(URL, host, filename, port);
        os_sprintf(buffer, POST, filename, strlen(postdata), host, postdata);
        os_printf("httpclient_data:\r\n%s\n", buffer);
        espconn_sent(&http_client_conn, buffer, strlen(buffer));
    }
    

    • 由 Leung 写于 2018 年 11 月 27 日

    • 参考:[ESP8266 Non-OS SDK API参考]
        Esp8266学习之旅⑤ 8266原生乐鑫SDK高级使用之封装Post与Get请求云端,拿到“天气预报信息”

    相关文章

      网友评论

        本文标题:ESP8266学习笔记(6)——简易HTTP客户端

        本文链接:https://www.haomeiwen.com/subject/tzqnqqtx.html