1 HTTP简单介绍
HTTP协议是互联网上应用最为广泛的一种网络协议,它的全称是Hypertext Transfer Protocol(超文本传输协议),默认的端口号为80。HTTP协议是一种用于由客户端向服务器传输超文本(例如HTML)的协议,它基于TCP/IP通信协议进行数据传输。
HTTP协议的工作流程大致如下:
- 客户端(通常是浏览器)与服务器建立连接。这个连接是通过TCP/IP协议建立的,通常需要指定服务器的IP地址和端口号。
- 客户端向服务器发送一个HTTP请求。HTTP请求通常包括一个请求行、一些请求头和请求体。请求行包括请求方法(如GET、POST等)、请求的资源路径和HTTP协议的版本。请求头包含一些额外的信息,如请求的资源类 型、请求的认证信息等。请求体则包含客户端发送给服务器的实际数据。
- 服务器接收到HTTP请求后,根据请求方法和路径查找并返回相应的文件作为应答。这个文件可以是HTML页面、图片、视频等资源。
- 客户端与服务器关闭连接。在完成数据传输后,客户端和服务器会关闭连接,释放网络资源。
以上就是HTTP协议的基本工作流程。通过这个协议,客户端可以向服务器请求和接收各种类型的资源,从而实现互联网上的信息共享和交互
2 利用心知天气API获取天气
心知天气提供免费的API,为方便演示,注册账号从控制面板可以获得服务的私钥,正式产品中不建议使用这种方式。

利用LWIP实现HTTP GET,通过HTTP通讯从服务器获取带有天气信息的数据。
- 首先通过LWIP获取主机(也就是域名)的IP地址。
- 然后TCP通过80端口连接这个IP地址。
- 向主机发送数据。
- 接受主机返回的数据。
2.1 添加代码
【每周分享】【瑞萨RA8D1 LVGL/LWIP评测】6、基于lwip实现sntp功能和RTC时间设置
https://bbs.21ic.com/icview-3505453-1-1.html
(出处: 21ic电子技术开发论坛)
导入之前帖子中的 RA8D1_06_LWIP_SNTP_RTC工程,FSP中修改lwip模块内部的sys timeout内存块数量并生成代码。

HTTP GET获取的返回数据格式为JSON数据,拉取cJSON代码仓库,将其中的 cJSON.c\h和 cJSON_Utils.c\h文件添加到工程的src文件夹中,调用其中的接口函数用于数据解析。
定义用于连接心知天气服务器、存储和解析返回的报文的宏和变量。
xSemaphoreHandle wait_dns_sem;
ip_addr_t sntp_server_ip;
ip_addr_t weather_server_ip;
#define WEATHER_HOST_NAME "api.seniverse.com"
#define WEATHER_URL "GET /v3/weather/now.json?key=<private_key>&location=wuxi&language=zh-Hans&unit=c\r\n\r\n"
uint8_t net_buf[2048];
char weather_str[100];
uint32_t data_len = 0;
uint8_t *ret_data = NULL;
定义以下函数用于心知天气网站的DNS解析,获取对应的IP地址。
static void dns_found2(const char *name, const ip_addr_t *addr, void *arg)
{
LWIP_UNUSED_ARG(arg);
printf("%s: %s\n", name, addr ? ipaddr_ntoa(addr) : "<not found>");
weather_server_ip.addr=addr->addr;
xSemaphoreGive(wait_dns_sem);
}
static void dns_dorequest2(ip_addr_t *dnsresp)
{
const char* dnsname = "api.seniverse.com";
if (dns_gethostbyname(dnsname, dnsresp, dns_found2, NULL) == ERR_OK) {
}
xSemaphoreTake(wait_dns_sem,portMAX_DELAY);
}
void lwip_app_thread_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
....
ret_data=net_buf;
wait_dns_sem=xSemaphoreCreateBinary();
...
dns_dorequest2(&weather_server_ip);
}
获取到IP地址,使用以下函数连接到服务器,发送请求数据的HTTP GET数据,并接收返回的数据
/**
* @brief http get request
*
* @param host_name host name
* @param url url,resource path
* @param ret_str the http get return
* @param ret_str_len the content returned by HTTP GET
* @param ret_str_max_len the content length returned by HTTP GET
* @return int return execution result
*/
fsp_err_t ln_drv_http_get(uint8_t *host_name,uint8_t *url,uint8_t *ret_str,uint32_t *ret_str_len,uint32_t ret_str_max_len)
{
//通过Socket创建TCP连接
FSP_PARAMETER_NOT_USED (host_name);
int ret = 0;
struct sockaddr_in client_addr;
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
printf("Socket init failed\n");
return FSP_ERR_ASSERTION;
}
memset(&client_addr,0,sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(80);
client_addr.sin_addr.s_addr = weather_server_ip.addr;
memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));
for(int i = 0; i < 10; i ++){
ret = connect(sock,(struct sockaddr *)&client_addr,sizeof(struct sockaddr));
if(ret != -1)
break;
vTaskDelay(50);
}
if(ret == -1){
printf("TCP Connect failed!\n");
closesocket(sock);
vTaskDelay(10);
return FSP_ERR_ASSERTION;
}else{
//向主机发送GET数据
ret = write(sock,url,strlen((char*)url));
if(ret != strlen((char*)url)){
printf("TCP write data failed!\n");
closesocket(sock);
return FSP_ERR_ASSERTION;
}
//接受主机返回的数据
ret = recv(sock, ret_str,ret_str_max_len,0);
closesocket(sock);
if(ret <= 0){
printf("TCP read data failed!\n");
closesocket(sock);
return FSP_ERR_ASSERTION;
}else{
*ret_str_len = ret;
}
}
closesocket(sock);
return FSP_SUCCESS;
}
void lwip_app_thread_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
....
ret_data=net_buf;
wait_dns_sem=xSemaphoreCreateBinary();
...
dns_dorequest2(&weather_server_ip);
ln_drv_http_get((uint8_t*)&WEATHER_HOST_NAME,(uint8_t*)&WEATHER_URL,(uint8_t*)ret_data,&data_len,2048);
printf("HTTP GET RET:%s",(uint8_t *)ret_data);
...
}
通过调用cJSON的接口函数,对返回的数据进行解析,提取其中的天气信息。
// 将天气预报信息,构建位需要播报的语音字符串
static bool parse_weather_json(char *analysis_buf, char *tts_str)
{
char tts_str_temp[100];
if (analysis_buf == NULL)
return false;
cJSON *json_root = cJSON_Parse(analysis_buf);
if (json_root != NULL)
{
cJSON *cjson_arr = cJSON_GetObjectItem(json_root, "results");
cJSON *cjosn_location = cJSON_GetObjectItem(cJSON_GetArrayItem(cjson_arr, 0), "location");
printf("\r\n城市 -> %s\r\n", cJSON_GetObjectItem(cjosn_location, "name")->valuestring);
sprintf(tts_str_temp, "%s", cJSON_GetObjectItem(cjosn_location, "name")->valuestring);
strcat(tts_str, tts_str_temp);
// cJSON *cjson_days = cJSON_GetObjectItem(cJSON_GetArrayItem(cjson_arr, 0), "daily");
cJSON *cjson_today = cJSON_GetObjectItem(cJSON_GetArrayItem(cjson_arr, 0), "now");
printf("今天天气 -> %s\r\n", cJSON_GetObjectItem(cjson_today, "text")->valuestring);
printf("温度 -> %s\r\n", cJSON_GetObjectItem(cjson_today, "temperature")->valuestring);
sprintf(tts_str_temp, "今天天气%s;温度%s摄氏度;",
cJSON_GetObjectItem(cjson_today, "text")->valuestring,
cJSON_GetObjectItem(cjson_today, "temperature")->valuestring);
strcat(tts_str, tts_str_temp);
printf("%s\r\n", tts_str);
cJSON_Delete(json_root);
}
return true;
}
void lwip_app_thread_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
....
ret_data=net_buf;
wait_dns_sem=xSemaphoreCreateBinary();
...
dns_dorequest2(&weather_server_ip);
ln_drv_http_get((uint8_t*)&WEATHER_HOST_NAME,(uint8_t*)&WEATHER_URL,(uint8_t*)ret_data,&data_len,2048);
printf("HTTP GET RET:%s",(uint8_t *)ret_data);
parse_weather_json(ret_data,weather_str);
...
}
2.2 运行效果
编译并烧录程序到开发板,可以得到获取天气信息并输出到串口终端。

附件:RA8D1_07_LWIP_SNTP_RTC_HTTP.zip