本文即将为你示例如何使用esp32的wifi,http组件以及百度在线语音合成等功能,实现获取天气数据并通过语音播放。
效果演示
esp32语音播放天气预报
一,请求天气数据
1.1 思路
利用esp32的http组件,向Web API请求天气数据,我使用的是万维易源提供的API接口,这个接口的调用比较简单,而且提供了1000次的额度,还是比较良心的。
请求的方式也很简单,使用GET方式向天气接口发起请求,服务端会返回json格式的当地城市的天气数据。后续我们只要解析出json中的数据,提取我们想要的天气数据。
1.2 注册账号
在官网注册账号,进入控制台,找到接口使用者-》我的应用
,系统默认创建了一个应用,just_test_app
,将这个应用的showapi_appid
,和sercret
保存下来,我们需要这两个数据作为请求参数。
然后进入天气api接口文档,购买0.00元档的套餐,到这一步,你已经可用使用它的接口了。
在接口文档中,可以选择根据不同的参数来获取天气数据,这里我选择ip查询。当ip的参数为空时,会默认返回客户端的ip地址的城市的信息。
1.3 获取数据
通过ip获取天气的url是https://route.showapi.com/9-4
,我们只需要在url后加入showapi_appid
和showapi_sign
两个参数(showapi_sign就是上文的sercret),然后发起GET请求,就能接收到返回的json字符串。
例如我的url是:
https://route.showapi.com/9-4?showapi_appid=421387&showapi_sign=f5f3b508fd6c43e09457be788b251a32
http请求代码,具体逻辑见代码注释
全局变量
static char http_data[MX_HTTP_BUFF]; //! 需要定义为全局静态变量,否则会导致任务的堆栈溢出
#define WEATHER_URL "https://route.showapi.com/9-4?showapi_appid=421387&showapi_sign=f5f3b508fd6c43e09457be788b251a32"
esp_err_t err;
esp_http_client_config_t config = {
.url = WEATHER_URL, //http请求地址
.event_handler = _http_event_handle, //http请求回调函数
.method = HTTP_METHOD_GET, //GET方式
};
esp_http_client_handle_t client = esp_http_client_init(&config);
AUDIO_MEM_CHECK(TAG, client, return NULL);
//发起请求
if(esp_http_client_open(client,0)!=ESP_OK)
{
return -1;
}
//获取返回头
esp_http_client_fetch_headers(client);
ESP_LOGI(TAG, "Status = %d, content_length = %d",esp_http_client_get_status_code(client),esp_http_client_get_content_length(client));
while(1)
{
//读取返回的数据到http_data
int read_len = esp_http_client_read(client, http_data + index, max_len - index);
//读取完毕
if(read_len <= 0)
{
break;
}
index += read_len;
total += read_len;
http_data[index] = 0;
}
if(total<0)
{
ESP_LOGI(TAG,"read error");
return -1;
}
esp_http_client_close(client);
esp_http_client_cleanup(client);
}
二,解析JSON字符串
2.1 天气数据
通过将返回的json数据格式化,可得到下面的json数据:
{
"showapi_res_error": "",
"showapi_res_id": "5fa415b48d57ba1617db793e",
"showapi_res_code": 0,
"showapi_res_body": {
"time": "20201105180000",
"ret_code": 0,
"now": {
"weather_code": "00",
"aqiDetail": {
"num": "95",
"co": "0.5",
"so2": "6",
"area": "深圳",
"o3": "84",
"no2": "32",
"aqi": "51",
"quality": "良好",
"pm10": "52",
"pm2_5": "25",
"o3_8h": "98",
"primary_pollutant": "颗粒物(PM10)"
},
"wind_direction": "东风",
"temperature_time": "23:00",
"wind_power": "1级",
"aqi": "51",
"sd": "78%",
"weather_pic": "http://app1.showapi.com/weather/icon/night/00.png",
"weather": "晴",
"temperature": "22"
},
"cityInfo": {
"c6": "guangdong",
"c5": "深圳",
"c4": "shenzhen",
"c3": "宝安",
"c9": "中国",
"c8": "china",
"c7": "广东",
"c17": "+8",
"c16": "AZ9755",
"c1": "101280605",
"c2": "baoan",
"c0": "440306",
"longitude": 113.878,
"c11": "0755",
"c10": "3",
"latitude": 22.556,
"c12": "518001",
"c15": "1"
},
"showapi_fee_code": 0,
"f1": {
"night_weather_code": "00",
"day_weather": "晴",
"night_weather": "晴",
"jiangshui": "0%",
"air_press": "1005 hPa",
"night_wind_power": "0-3级 <5.4m/s",
"day_wind_power": "0-3级 <5.4m/s",
"day_weather_code": "00",
"sun_begin_end": "06:25|16:20",
"ziwaixian": "很强",
"day_weather_pic": "http://app1.showapi.com/weather/icon/day/00.png",
"weekday": 4,
"night_air_temperature": "20",
"day_air_temperature": "27",
"day_wind_direction": "北风",
"day": "20201105",
"night_weather_pic": "http://app1.showapi.com/weather/icon/night/00.png",
"night_wind_direction": "无持续风向"
},
"f3": {
"night_weather_code": "07",
"day_weather": "小雨",
"night_weather": "小雨",
"jiangshui": "70%",
"air_press": "1005 hPa",
"night_wind_power": "0-3级 <5.4m/s",
"day_wind_power": "0-3级 <5.4m/s",
"day_weather_code": "07",
"sun_begin_end": "06:28|16:17",
"ziwaixian": "最弱",
"day_weather_pic": "http://app1.showapi.com/weather/icon/day/07.png",
"weekday": 6,
"night_air_temperature": "21",
"day_air_temperature": "28",
"day_wind_direction": "东北风",
"day": "20201107",
"night_weather_pic": "http://app1.showapi.com/weather/icon/night/07.png",
"night_wind_direction": "东北风"
},
"f2": {
"night_weather_code": "07",
"day_weather": "晴",
"night_weather": "小雨",
"jiangshui": "0%",
"air_press": "1005 hPa",
"night_wind_power": "0-3级 <5.4m/s",
"day_wind_power": "0-3级 <5.4m/s",
"day_weather_code": "00",
"sun_begin_end": "06:27|16:18",
"ziwaixian": "很强",
"day_weather_pic": "http://app1.showapi.com/weather/icon/day/00.png",
"weekday": 5,
"night_air_temperature": "21",
"day_wind_direction": "无持续风向",
"day_air_temperature": "28",
"night_wind_direction": "东北风",
"night_weather_pic": "http://app1.showapi.com/weather/icon/night/07.png",
"day": "20201106"
}
}
}
2.2 使用cJSON解析出字符串
cJSON是一个用C语言写的专门用于处理json数据的库。官方地址:http://www.json.org/。idf中已经集成了cjson组件。
以下代码示例展示了如何使用cjson解析出json字符串里的数据,并将字符串拼接成一句流畅的句子。简单的展示,只读取json中的天气和温度数据。
#include "cJSON.h"
char data[1024]={0};
void demo()
{
//解析整个json
cJSON *root = cJSON_Parse(http_data);
if(!root)
{
ESP_LOGI(TAG, "error,json parse error");
return -1;
}
//获取showapi_res_body
cJSON *showapi_res_body = cJSON_GetObjectItem(root,"showapi_res_body");
/*---------今天天气数据----------------------------*/
cJSON *f1 = cJSON_GetObjectItem(showapi_res_body, "f1");
//白天的天气
cJSON *item = cJSON_GetObjectItem(f1, "day_weather");
char *day_weather = cJSON_GetStringValue(item);
//白天的气温
item = cJSON_GetObjectItem(f1, "day_air_temperature");
char *day_air_temperature = cJSON_GetStringValue(item);
//拼接成一句话,如:今天,天气晴,气温26度
snprintf(data, 50, "今天,天气%s,气温%s度", day_weather,day_air_temperature);
cJSON_Delete(root);
}
几个重要的函数:
将json字符串转化成json类型的对象,这个函数必须在其他函数之前执行。
cJSON_Parse(const char *value)
根据键值获取cjson对象的一个成员,这个成员可以也是一个json对象
cJSON_GetObjectItem(const cJSON * const object, const char * const string)
获取json对象数组中的指定成员。
cJSON_GetArrayItem(const cJSON *array, int index);
获取json对象里的字符串
cJSON_GetStringValue(cJSON *item);
三,语音合成
使用百度语音合成,将拼接的字符串转换成语音,通过喇叭播放出来。
如何将文字转换成语音,请参考另一篇博客:ESP32使用百度语音合成 实现文字转语音播放
四,小结
天气API只是一个例子,网上有许多开放的API可玩,各位有什么好玩的API也可以在评论留言