本文记录在「悦聆」多媒体中枢(见第六篇《网络收音机》)上新增天气预报页面的过程:Open-Meteo 逐小时预报 + SNTP 网络校时 + LVGL 图形化天气图标,定位广西资源县。不需要 TLS、不需要 API key,裸 socket 即可。

1. 方案选型
嵌入式裸 socket(无 TLS)场景下选天气 API 的约束:纯 HTTP、免 key、数据格式简单。Open-Meteo 完美符合:
GET http://api.open-meteo.com/v1/forecast?latitude=26.03&longitude=110.64\ &hourly=temperature_2m,weathercode&timezone=Asia%2FShanghai&forecast_days=2
返回今明两天 48 条逐小时数据(ISO8601 时间、温度、WMO 天气码),JSON 约 5KB。WMO 天气码是整数枚举,比文字描述好解析得多。
2. 实现要点
HTTP 一次性拉取(weather.c):DNS 解析 → 连接 → 发 GET → 读至 EOF → 按 \r\n\r\n 分离头和体。
JSON 解析:Zephyr 自带 JSON 库对字符串数组支持繁琐,最终全部手写解析——时间数组按引号切,数字数组用 strtof/strtol 逐个取,格式固定的场景下比描述符驱动简单可控:
/* "temperature_2m":[14.8,14.5,...] 逐个取 */
while (n < max_n && *p != ']') {
while (*p == ' ' || *p == ',') p++;
if (*p == ']') break;
out[n++] = strtof(p, &end);
if (end == p) return -EINVAL;
p = end;
}SNTP 校时(time_sync.c):联网拿到 IP 后自动同步(ntp.aliyun.com),本地按 uptime 推算,UTC+8。天气列表从当前小时开始显示,当前时段带"现在"标记,07:00 行高亮(早起看预报的场景)。
/* Zephyr SNTP 库的 ts.seconds 已是 Unix epoch(1970 起), * 库内部已转好 1900 偏移,只需加 UTC+8,别再减一次 */ base_epoch_utc8 = (int64_t)ts.seconds + UTC8_OFFSET;
天气图标:LVGL 没有天气符号,用基础图形(圆/圆角矩形)拼:晴=黄太阳,多云=叠云,雨=云+蓝色雨线,雪=云+白点,雷=深色云+黄色闪电块,雾=灰云+横线。行内 40×40 小图标按 WMO 码分发。
页面结构:顶部状态栏(返回键 + 标题 + 实时时钟 + 网络状态)+ 可滚动逐小时列表。
3. 踩坑记录
| URL 里写 %%2F | 400 Bad Request——%% 只在格式串里转义,%s 插入时原样发出 | 字面量里直接写 %2F |
| Zephyr JSON 库的 JSON_TOK_FLOAT | 温度全 0.0——它不是"float 值"而是不透明 token | 解码浮点要用 JSON_TOK_FLOAT_FP/JSON_TOK_DOUBLE_FP(最终干脆手写解析) |
| SNTP 二次减偏移 | 时间变成负数——库已转 Unix epoch | 只加 UTC+8 |
| picolibc 的 %f | 温度显示 *float* | CONFIG_PICOLIBC_IO_FLOAT=y |
| 字库漏字 | 方框:预报/更多应用/高亮/现在 | lv_font_conv 子集要按 UI 文案收全 |
| 多页状态栏只更新一页 | 新页面停在 "Waiting" | 每页的状态控件登记到数组,刷新时遍历同步 |
4. 效果
进入"天气预报"页:自动拉取(首次)→ 列表从当前小时起显示 48 条,格式 MM-DD HH:00 | 图标 | 温度°C | 天气,当前小时标绿"现在",每天 07:00 行亮蓝底高亮。右上角"刷新"手动重取。串口日志:
time_sync: time synced via ntp.aliyun.com: local 11-11 21:08 (raw ...) weather: fetching forecast... weather: parsed times=48 temps=48 codes=48 weather: forecast parsed: 48 hours
我要赚赏金
