一、天气API
1. 心知天气的产品简介
HyperData 是心知天气的高精度气象数据产品,通过标准的 Restful API 接口,提供标准化的数据访问。无论是 APP、智能硬件还是企业级系统都可以轻松接入心知的精细化天气数据。
HyperData API V4版是当前的最新版本。相比 V3 版的 API 服务,V4 版 API 通过技术架构的重新设计和底层代码的重构重写,在产品使用方式、接口性能、服务稳定性和开发效率上得到大幅改进。新的数据都会直接发布 V4 版接口,老数据会从 V3 版逐步迁移到 V4 版。
以下为V4版和V3版API的主要区别对比:
V4版 | V3版 | |
产品简介 | 心知基于全新架构开发的数据API产品,在产品使用方式、接口性能、服务稳定性和开发效率上得到大幅改进 | 心知老版数据API产品,将逐步迁移升级到V4版 |
产品定位 | 以公里级网格等专业气象数据为主(V3全部迁移后将支持城市级数据) | 以城市级基础天气数据为主 |
鉴权方式 | 需要公钥私钥配合使用,更安全, | 仅需要私钥,使用简单,但泄露后可能造成盗用 |
如何试用 | 目前需要 联系我们 人工申请 | 在官网控制台自助开通试用 |
产品文档 | 心知天气 API 使用手册(V4版) · 心知科技 | 心知天气 API 使用手册(V3版) · 心知科技 |
我们这里使用的是心知天气的V3免费版,可以在心知天气的控制台去免费申请,这个免费版不限制使用次数,只限制访问频率(最大1次/秒)。
2. 心知天气API使用
(1) 首先要获取你的账号密钥,查看/修改你的API密钥 · 心知科技,我这里直接使用私钥查询天 气,优点是方便,缺点是不安全,容易泄露私钥。
(2)然后通过HTTP GET获取天气信息,URL为:
http://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=ip&language=zh-Hans&unit=c
只需要将URL里面的your_api_key替换为你自己的私钥,就可以自动获取你IP所在地的天气了。如果想获取其他城市的天气只需要将location的参数替换下就可以了。例如:
http://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c
可以在你的浏览器来测试下这个链接,我的结果如下:
我的浏览器有JSON解析插件,如果没有的话应该是一行JSON数据
二、LWIP实现简单的HTTP GET请求
1. HTTP简单介绍
HTTP协议是互联网上应用最为广泛的一种网络协议,它的全称是Hypertext Transfer Protocol(超文本传输协议),默认的端口号为80。HTTP协议是一种用于由客户端向服务器传输超文本(例如HTML)的协议,它基于TCP/IP通信协议进行数据传输。
HTTP协议的工作流程大致如下:
- 客户端(通常是浏览器)与服务器建立连接。这个连接是通过TCP/IP协议建立的,通常需要指定服务器的IP地址和端口号。
- 客户端向服务器发送一个HTTP请求。HTTP请求通常包括一个请求行、一些请求头和请求体。请求行包括请求方法(如GET、POST等)、请求的资源路径和HTTP协议的版本。请求头包含一些额外的信息,如请求的资源类型、请求的认证信息等。请求体则包含客户端发送给服务器的实际数据。
- 服务器接收到HTTP请求后,根据请求方法和路径查找并返回相应的文件作为应答。这个文件可以是HTML页面、图片、视频等资源。
- 客户端与服务器关闭连接。在完成数据传输后,客户端和服务器会关闭连接,释放网络资源。
以上就是HTTP协议的基本工作流程。通过这个协议,客户端可以向服务器请求和接收各种类型的资源,从而实现互联网上的信息共享和交互。
2. LWIP实现HTTP GET步骤
- 首先通过LWIP获取主机(也就是域名)的IP地址。
- 然后TCP通过80端口连接这个IP地址。
- 向主机发送数据。
- 接受主机返回的数据。
下面是具体的代码:
(1)通过LWIP的DNS接口获取主机的IP地址
static OS_Semaphore_t wait_dns_sem;
static ip_addr_t my_http_ip;
static void dns_found(const char *name, ip_addr_t *host_ip, void *callback_arg)
{
//获取到DNS的回调函数
OS_SemaphoreRelease(&wait_dns_sem); //释放信号量
my_http_ip.addr = host_ip->addr; //保存获取到的IP地址
}
/**
* @brief get ip address by lwip dns
*
* @param hostname host name
* @param ip host ip address
* @param cb dns found call back function
* @return int return execution result
*/
static int ln_drv_get_ip_by_dns(char *hostname,ip_addr_t *ip,dns_found_callback cb)
{
//创建FreeRTOS的二值信号量
OS_Status ret_sta = OS_SemaphoreCreateBinary(&wait_dns_sem);
if (ret_sta != OS_OK) {
LOG(LOG_LVL_ERROR, "Wait DNS sem creat fail\r\n");
return HAL_ERROR;
}
//通过DNS获取主机地址IP
err_t ret = dns_gethostbyname(hostname, ip, cb,NULL);;
if (ret == ERR_INPROGRESS){
//需要通过向dns服务器发送dns请求数据来获取hostname对应的IP地址
}else if (ret == ERR_OK){
//从dns缓存表寻找hostname对应的IP地址
OS_SemaphoreRelease(&wait_dns_sem);
}else if(ret == ERR_OK){
//获取IP失败
LOG(LOG_LVL_ERROR, "Get dns result err.Error code:%d\n",ret);
return HAL_ERROR;
}
//等待信号量释放
OS_SemaphoreWait(&wait_dns_sem, 30000);
if(OS_SemaphoreGetCount(&wait_dns_sem) == 0){
//信号量被释放,保存IP地址
ip->addr = my_http_ip.addr;
return HAL_OK;
}else{
//信号量未被释放,等待超时
LOG(LOG_LVL_ERROR, "Get dns result timeout.\n");
return HAL_ERROR;
}
}
(2)LWIP通过TCP Socket连接目标IP地址,且发送和接收数据
/**
* @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
*/
int 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)
{
//首先通过DNS获取主机IP
ip_addr_t host_ip;
host_ip.addr = 0;
if(ln_drv_get_ip_by_dns((char*)host_name,&host_ip,(dns_found_callback)dns_found) != HAL_OK){
LOG(LOG_LVL_ERROR, "Get HOST IP failed.\n");
return HAL_ERROR;
}else{
LOG(LOG_LVL_INFO, "HOST IP:%s.\n",ip4addr_ntoa(&(host_ip.addr)));
}
//通过Socket创建TCP连接
int ret = 0;
struct sockaddr_in client_addr;
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
LOG(LOG_LVL_ERROR, "Socket init failed\n");
return HAL_ERROR;
}
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 = host_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;
OS_MsDelay(50);
}
if(ret == -1){
LOG(LOG_LVL_ERROR, "TCP Connect failed!\n");
closesocket(sock);
OS_MsDelay(10);
return HAL_ERROR;
}else{
//向主机发送GET数据
ret = write(sock,url,strlen((char*)url));
if(ret != strlen((char*)url)){
LOG(LOG_LVL_ERROR, "TCP write data failed!\n");
closesocket(sock);
return HAL_ERROR;
}
//接受主机返回的数据
ret = recv(sock, ret_str,ret_str_max_len,0);
closesocket(sock);
if(ret <= 0){
LOG(LOG_LVL_ERROR, "TCP read data failed!\n");
closesocket(sock);
return HAL_ERROR;
}else{
*ret_str_len = ret;
}
}
closesocket(sock);
return HAL_OK;
}
测试代码:
#define WEATHER_HOST_NAME "api.seniverse.com"
#define WEATHER_URL "GET /v3/weather/now.json?key=your_key=ip&language=zh-Hans&unit=c\r\n\r\n"
//记得替换里面的your_key
#define GET_NET_DATA_LEN 2048
uint32_t data_len = 0;
uint8_t *ret_data = NULL;
data_len = 0;
ret_data = OS_Malloc(GET_NET_DATA_LEN);
memset(ret_data,0,GET_NET_DATA_LEN);
ln_drv_http_get((uint8_t*)&WEATHER_HOST_NAME,(uint8_t*)&WEATHER_URL,(uint8_t*)ret_data,&data_len,GET_NET_DATA_LEN);
LOG(LOG_LVL_INFO,"HTTP GET RET:%s",(uint8_t *)ret_data);
OS_Free(ret_data);
测试结果:
分析返回数据里面天气以及位置信息:
因为我只需要城市名字、天气代码和温度这三个比较见的信息,所以就只是简单的查询字符串来查找数据,正常应该用CJSON来解析的
static int find_information(uint8_t *data,uint8_t *weather_code,int8_t *weather_tem,uint8_t *position_str)
{
char *token = NULL;
char *endptr = NULL;
char delimiters[] = ",";
token = strtok((char*)data,delimiters);
while(token != NULL){
token = strtok(NULL,delimiters);
if(memcmp("name",token+1,strlen("name")) == 0){
strcpy((char*)position_str,token+8);
position_str[strlen(position_str)-1] = 0x0;//delete '"'
}
if(memcmp("code",token+1,strlen("code")) == 0){
my_weather_code = strtol(token+8,&endptr,0);
}
if(memcmp("temperature",token+1,strlen("temperature")) == 0){
my_weather_tem = strtol(token+15,&endptr,0);
return HAL_OK;
}
}
return HAL_ERROR;
}
3. 完整代码
先调用 ln_drv_get_net_data_init 这个函数,这个函数会创建一个Task,每隔几秒定时获取天气信息,在其它任务中通过 ln_drv_get_net_data 这个函数获取天气信息。weather_code,weather_tem,position_str,这三个参数分别是天气代码,温度,城市名字。天气代码说明见:V3天气现象代码说明 · 心知科技
上层封装函数:
#define WEATHER_HOST_NAME "api.seniverse.com"
#define WEATHER_URL "GET /v3/weather/now.json?key=yout_key&location=ip&language=zh-Hans&unit=c\r\n\r\n"
#define WEATHER_HOST_IP "116.62.81.138"
#define GET_NET_DATA_LEN 2048
static OS_Thread_t g_get_net_data_thread;
static uint8_t my_weather_code = 0;
static int8_t my_weather_tem = 0;
static uint8_t my_position_str[50] = {0};
static uint8_t cur_status = 0;
static void get_net_data_task_entry(void *params);
int ln_drv_get_net_data_init(void)
{
if(OS_OK != OS_ThreadCreate(&g_get_net_data_thread, "Get net data Task", get_net_data_task_entry, NULL, OS_PRIORITY_BELOW_NORMAL, 2*1024)) {
return HAL_ERROR;
}
return HAL_OK;
}
static int find_information(uint8_t *data,uint8_t *weather_code,int8_t *weather_tem,uint8_t *position_str)
{
char *token = NULL;
char *endptr = NULL;
char delimiters[] = ",";
token = strtok((char*)data,delimiters);
while(token != NULL){
token = strtok(NULL,delimiters);
if(memcmp("name",token+1,strlen("name")) == 0){
strcpy((char*)position_str,token+8);
position_str[strlen(position_str)-1] = 0x0;//delete '"'
}
if(memcmp("code",token+1,strlen("code")) == 0){
my_weather_code = strtol(token+8,&endptr,0);
}
if(memcmp("temperature",token+1,strlen("temperature")) == 0){
my_weather_tem = strtol(token+15,&endptr,0);
return HAL_OK;
}
}
return HAL_ERROR;
}
static void get_net_data_task_entry(void *params)
{
uint32_t data_len = 0;
uint8_t *ret_data = NULL;
while (1)
{
data_len = 0;
ret_data = OS_Malloc(GET_NET_DATA_LEN);
memset(ret_data,0,GET_NET_DATA_LEN);
if(HAL_OK == ln_drv_http_get((uint8_t*)&WEATHER_HOST_NAME,(uint8_t*)&WEATHER_URL,(uint8_t*)ret_data,&data_len,GET_NET_DATA_LEN)){
LOG(LOG_LVL_INFO,"HTTP GET RET:%s",(uint8_t *)ret_data);
if(HAL_OK == find_information(ret_data,&my_weather_code,&my_weather_tem,my_position_str)){
cur_status = 1;
//LOG(LOG_LVL_INFO,"my_weather_code:%d\my_weather_tem:%d\my_position_str:%s\n",(uint8_t *)ret_data);
}
}else{
LOG(LOG_LVL_INFO,"Get weather information failed.");
}
OS_Free(ret_data);
//OS_MsDelay(1000 * 60 * 60);
OS_MsDelay(2000);
}
}
int ln_drv_get_net_data(uint8_t *weather_code,int8_t *weather_tem,uint8_t *position_str)
{
if(cur_status == 0){
return HAL_BUSY;
}else{
*weather_code = my_weather_code;
*weather_tem = my_weather_tem;
memcpy(position_str,my_position_str,strlen((char*)my_position_str));
cur_status = 0;
}
return HAL_OK;
}
http get代码:
static OS_Semaphore_t wait_dns_sem;
static ip_addr_t my_http_ip;
static void dns_found(const char *name, ip_addr_t *host_ip, void *callback_arg)
{
OS_SemaphoreRelease(&wait_dns_sem);
my_http_ip.addr = host_ip->addr;
}
/**
* @brief get ip address by lwip dns
*
* @param hostname host name
* @param ip host ip address
* @param cb dns found call back function
* @return int return execution result
*/
static int ln_drv_get_ip_by_dns(char *hostname,ip_addr_t *ip,dns_found_callback cb)
{
OS_Status ret_sta = OS_SemaphoreCreateBinary(&wait_dns_sem);
if (ret_sta != OS_OK) {
LOG(LOG_LVL_ERROR, "Wait DNS sem creat fail\r\n");
return HAL_ERROR;
}
err_t ret = dns_gethostbyname(hostname, ip, cb,NULL);;
if (ret == ERR_INPROGRESS){
//需要通过向dns服务器发送dns请求数据来获取hostname对应的IP地址
}else if (ret == ERR_OK){
//从dns缓存表寻找hostname对应的IP地址
OS_SemaphoreRelease(&wait_dns_sem);
}else if(ret == ERR_OK){
LOG(LOG_LVL_ERROR, "Get dns result err.Error code:%d\n",ret);
return HAL_ERROR;
}
OS_SemaphoreWait(&wait_dns_sem, 30000);
if(OS_SemaphoreGetCount(&wait_dns_sem) == 0){
ip->addr = my_http_ip.addr;
return HAL_OK;
}else{
LOG(LOG_LVL_ERROR, "Get dns result timeout.\n");
return HAL_ERROR;
}
}
/**
* @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
*/
int 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)
{
//get host ip by dns
ip_addr_t host_ip;
host_ip.addr = 0;
if(ln_drv_get_ip_by_dns((char*)host_name,&host_ip,(dns_found_callback)dns_found) != HAL_OK){
LOG(LOG_LVL_ERROR, "Get HOST IP failed.\n");
return HAL_ERROR;
}else{
LOG(LOG_LVL_INFO, "HOST IP:%s.\n",ip4addr_ntoa(&(host_ip.addr)));
}
//tcp connect by lwip socket
int ret = 0;
struct sockaddr_in client_addr;
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
LOG(LOG_LVL_ERROR, "Socket init failed\n");
return HAL_ERROR;
}
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 = host_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;
OS_MsDelay(50);
}
if(ret == -1){
LOG(LOG_LVL_ERROR, "TCP Connect failed!\n");
closesocket(sock);
OS_MsDelay(10);
return HAL_ERROR;
}else{
ret = write(sock,url,strlen((char*)url));
if(ret != strlen((char*)url)){
LOG(LOG_LVL_ERROR, "TCP write data failed!\n");
closesocket(sock);
return HAL_ERROR;
}
ret = recv(sock, ret_str,ret_str_max_len,0);
closesocket(sock);
if(ret <= 0){
LOG(LOG_LVL_ERROR, "TCP read data failed!\n");
closesocket(sock);
return HAL_ERROR;
}else{
*ret_str_len = ret;
}
}
closesocket(sock);
return HAL_OK;
}
测试结果:
因为本人也在学习,所以文中难免会有错误的地方,欢迎指出。如果本文对你的学习或工作有一定帮助,麻烦帮忙点赞收藏,谢谢!