esp32学习笔记:http-request
在esp-idf中的\examples\protocols\http_server\simple
文件夹的例程中简单介绍了esp32作为http客户端请求服务器。
http-request的过程是建立在socket的tcp协议上,通过访问服务器域名的形式来从服务器来获取数据。
大体流程如下:
首先初始化相关硬件,其次连接路由器,最后esp32与服务器建立tcp连接并获取信息。下面内容将介绍相关API。
1 硬件初始化
初始化的硬件主要是NVS
,用来保存路由器WIFI的账户信息。还要初始化网络相关的内容。
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
2 连接路由器
在esp-idf中中已经封装好连接WIFI的API,其具体如何工作将在以后笔记详述。
ESP_ERROR_CHECK(example_connect());
3 建立TCP连接,并通过http-request获取数据
通过freeRTOS操作系统,创建一个建立数据传输的任务。
xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL);
3.1 域名解析
服务器通常是以域名的形式给出的,我们需要利用getaddrinfo()
函数解析为IP地址。
int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);
WEB_SERVER
:服务器域名;WEB_PORT
:端口号。
3.2 建立tcp连接
根据解析到的IP地址信息连建立TCP连接。
创建socket s = socket(res->ai_family, res->ai_socktype, 0);
连接服务器 connect(s, res->ai_addr, res->ai_addrlen)
使用write发送get请求 write(s, REQUEST, strlen(REQUEST)
其中REQUEST
:是get请求的内容
读取服务器响应数据 read(s, recv_buf, sizeof(recv_buf)-1)
4 连接天气服务器获取数据示例
4.1程序源码
//http组包宏,获取天气的http接口参数
#define WEB_SERVER "api.seniverse.com"
#define WEB_PORT "80"
#define WEB_URL "/v3/weather/now.json?key="
#define APIKEY "修改为自己"
#define CITY "wuxi"
#define LANGUAGE "en"
//http请求包
static const char *REQUEST = "GET https://WEB_SERVER/v3/weather/now.json?key=APIKEY&location=CITY&language=LANGUAGE&unit=c\n\n";
static const char *TAG = "example";
//天气解析结构体
typedef struct
{
char cit[20];
char weather_text[20];
char weather_code[2];
char temperatur[3];
}weather_info;
weather_info weathe;
void cjson_to_struct_info(char *text)
{
cJSON *root,*psub;
cJSON *arrayItem;
//截取有效json
char *index=strchr(text,'{');
strcpy(text,index);
root = cJSON_Parse(text);
if(root!=NULL)
{
psub = cJSON_GetObjectItem(root, "results");
arrayItem = cJSON_GetArrayItem(psub,0);
cJSON *locat = cJSON_GetObjectItem(arrayItem, "location");
cJSON *now = cJSON_GetObjectItem(arrayItem, "now");
if((locat!=NULL)&&(now!=NULL))
{
psub=cJSON_GetObjectItem(locat,"name");
sprintf(weathe.cit,"%s",psub->valuestring);
ESP_LOGI(TAG,"city:%s",weathe.cit);
psub=cJSON_GetObjectItem(now,"text");
sprintf(weathe.weather_text,"%s",psub->valuestring);
ESP_LOGI(TAG,"weather:%s",weathe.weather_text);
psub=cJSON_GetObjectItem(now,"code");
sprintf(weathe.weather_code,"%s",psub->valuestring);
//ESP_LOGI(HTTP_TAG,"%s",weathe.weather_code);
psub=cJSON_GetObjectItem(now,"temperature");
sprintf(weathe.temperatur,"%s",psub->valuestring);
ESP_LOGI(TAG,"temperatur:%s",weathe.temperatur);
//ESP_LOGI(HTTP_TAG,"--->city %s,weather %s,temperature %s<---\r\n",weathe.cit,weathe.weather_text,weathe.temperatur);
}
}
//ESP_LOGI(HTTP_TAG,"%s 222",__func__);
cJSON_Delete(root);
}
void https_get_task(void *pvParameters)
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
struct in_addr *addr;
int s, r;
char recv_buf[1024];
char mid_buf[1024];
int index;
while(1) {
//DNS域名解析
int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);
if(err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p\r\n", err, res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
//打印获取的IP
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
//ESP_LOGI(HTTP_TAG, "DNS lookup succeeded. IP=%s\r\n", inet_ntoa(*addr));
//新建socket
s = socket(res->ai_family, res->ai_socktype, 0);
if(s < 0) {
ESP_LOGE(TAG, "... Failed to allocate socket.\r\n");
close(s);
freeaddrinfo(res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
//连接ip
if(connect(s, res->ai_addr, res->ai_addrlen) != 0) {
ESP_LOGE(TAG, "... socket connect failed errno=%d\r\n", errno);
close(s);
freeaddrinfo(res);
vTaskDelay(4000 / portTICK_PERIOD_MS);
continue;
}
freeaddrinfo(res);
//发送http包
if (write(s, REQUEST, strlen(REQUEST)) < 0) {
ESP_LOGE(TAG, "... socket send failed\r\n");
close(s);
vTaskDelay(4000 / portTICK_PERIOD_MS);
continue;
}
//清缓存
memset(mid_buf,0,sizeof(mid_buf));
//获取http应答包
do {
bzero(recv_buf, sizeof(recv_buf));
r = read(s, recv_buf, sizeof(recv_buf)-1);
strcat(mid_buf,recv_buf);
} while(r > 0);
//json解析
cjson_to_struct_info(mid_buf);
//关闭socket,http是短连接
close(s);
//延时一会
for(int countdown = 10; countdown >= 0; countdown--) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
}
void app_main(void)
{
ESP_ERROR_CHECK( nvs_flash_init() );
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL);
}
4.2 实验结果
I (3724) example: city:无锡
I (3724) example: weather:多云
I (3724) example: temperatur:27