一、背景
已知智能网关固定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请求云端,拿到“天气预报信息”
网友评论