基于ESP32S3的智能终端项目--5.显示时间和天气功能

项目演示:

tq

rl

目录

项目演示:

时间和天气功能实现保姆级教程

[1. 准备工作:注册心知天气并获取API密钥](#1. 准备工作:注册心知天气并获取API密钥)

[2. 添加必要的库](#2. 添加必要的库)

[3. 编写代码:data.c 深度解析](#3. 编写代码:data.c 深度解析)

[3.1 包含头文件和定义全局变量](#3.1 包含头文件和定义全局变量)

[3.2 获取时间函数 time_get()](#3.2 获取时间函数 time_get())

[3.3 获取天气函数 weather_get() (核心逻辑)](#3.3 获取天气函数 weather_get() (核心逻辑))

[3.4 数据更新任务函数 data_update()](#3.4 数据更新任务函数 data_update())

[3.5 创建独立的FreeRTOS任务](#3.5 创建独立的FreeRTOS任务)

[4. 总结与保姆级操作步骤](#4. 总结与保姆级操作步骤)

[5. 常见问题与调试](#5. 常见问题与调试)


时间和天气功能实现保姆级教程

本章的目标是让你的ESP32-S3智能终端能够:

  1. 从NTP服务器获取准确时间,并在屏幕上动态更新。

  2. 从心知天气API获取实时天气和未来天气预报,并显示在UI上。

  3. 通过 FreeRTOS任务,让这些网络请求和数据处理在后台自动运行,不影响界面刷新。

1. 准备工作:注册心知天气并获取API密钥

  • 第一步:注册账号

    访问心知天气官网,完成注册和登录。个人开发者通常有免费试用额度,足够学习使用。

  • 第二步:找到你的私钥

    登录后,在控制台或产品管理页面,找到你的"API密钥"(通常是一串字母和数字的组合,例如 SHdGnMJ4MzuCuy0-L)。这个密钥是你的程序访问天气数据的凭证,需要保密

  • 第三步:了解API文档(可选)

    教程中使用的API地址是官方提供的:

    • 实时天气:https://api.seniverse.com/v3/weather/now.json

    • 逐日天气预报:https://api.seniverse.com/v3/weather/daily.json

    • 你访问这些链接返回403是正常的,因为它们需要携带你的密钥作为参数才能获取数据。代码中正是通过?key=将你的密钥附加在URL后面。

2. 添加必要的库

在PlatformIO项目配置文件 platformio.inilib_deps 中,确保已经添加了 ArduinoJson 库,用于解析API返回的JSON数据。

cs 复制代码
ini

lib_deps =
    bblanchon/ArduinoJson@^6.21.4
    # ... 其他依赖

3. 编写代码:data.c 深度解析

这份代码是你提供的 data.c,我将逐段解释它的作用。

3.1 包含头文件和定义全局变量
cs 复制代码
c

#include "data.h"
#include "lvgl.h"
#include "ui/src/ui.h"
#include <HTTPClient.h>
#include <ArduinoJson.h>

struct tm timeinfo; // 标准时间结构体,用于存储获取到的时间

// --- 心知天气API配置 ---
String url = "https://api.seniverse.com/v3/weather/now.json";
String key = "Sq9D2GX9Rie6Gljzv"; // 请务必替换成你自己的API密钥!
String locations = "深圳";        // 查询的城市,可以修改
String language = "zh-Hans";
String unit = "c";

String url_2 = "https://api.seniverse.com/v3/weather/daily.json";
String start = "0";
String days = "3";                // 获取未来几天的天气?原文档是7,这里改为了3
#define get_days 3                 // 保持与days一致

// --- 用于存储解析结果的全局变量 ---
String city, temp, text, humi, last_update;
String data, date, text_day, low, high, precip;
String week_weather[7]={"周日","周一","周二","周三","周四","周五","周六"};
  • 要点key 变量必须替换为你自己的心知天气私钥。locationsdays 可以根据需要修改。
3.2 获取时间函数 time_get()
cs 复制代码
c

void time_get(void)
{
    // configTime(时区偏移秒, 夏令时秒, NTP服务器1, 服务器2, 服务器3);
    // 28800秒 = 8小时,即东八区(北京时间)
    configTime(28800, 0, "ntp1.aliyun.com", "ntp2.aliyun.com", "ntp3.aliyun.com");
}
  • 作用:这个函数配置并启动NTP时间同步。它告诉ESP32去连接阿里云的NTP服务器,获取网络时间,并自动处理时区转换。调用一次后,系统会定期后台同步。
3.3 获取天气函数 weather_get() (核心逻辑)

这个函数负责发起HTTP请求,并解析返回的JSON数据,最后更新UI。

cpp 复制代码
c

void weather_get(void)
{
    // 1. 创建HTTP客户端对象
    HTTPClient http;
    HTTPClient http_2;

    // 2. 构造完整的请求URL并发起GET请求
    http.begin(url+"?key="+key+"&location="+locations+"&language="+language+"&unit="+unit);
    http_2.begin(url_2+"?key="+key+"&location="+locations+"&language="+language+"&unit="+unit+"&start="+start+"&days="+days);

    int httpCode = http.GET();          // 执行请求,获取返回码
    int httpCoed_2 = http_2.GET();

    // 3. 读取服务器返回的JSON字符串
    String response = http.getString();
    String response_2 = http_2.getString();

    http.end();   // 关闭连接
    http_2.end();

    // 4. 使用ArduinoJson解析JSON数据
    // 创建DynamicJsonDocument对象,大小根据返回数据量预估
    DynamicJsonDocument doc(1024);
    DynamicJsonDocument doc_2(512 * get_days); // 根据天数动态分配内存

    deserializeJson(doc, response);      // 解析实时天气
    deserializeJson(doc_2, response_2);  // 解析天气预报

    // 5. 从解析后的文档中提取数据,并赋值给全局变量
    city = doc["results"][0]["location"]["name"].as<String>();
    temp = doc["results"][0]["now"]["temperature"].as<String>();
    text = doc["results"][0]["now"]["text"].as<String>();
    humi = doc["results"][0]["now"]["humidity"].as<String>();
    last_update = doc["results"][0]["last_update"].as<String>();

    // 注意:这里取的是"明天"的天气 (daily[1])
    text_day = doc_2["results"][0]["daily"][1]["text_day"].as<String>();
    low = doc_2["results"][0]["daily"][1]["low"].as<String>();
    high = doc_2["results"][0]["daily"][1]["high"].as<String>();
    precip = doc_2["results"][0]["daily"][1]["precip"].as<String>();

    // 6. 格式化未来几天的天气数据,用于滚动列表
    data = ""; // 初始化空字符串
    for(int i = 0; i < get_days; ++i)
    {
        if(i == 0) date = "今天";
        // 根据当前星期几,计算未来i天是星期几
        else date = week_weather[(timeinfo.tm_wday+i<=6) ? (timeinfo.tm_wday+i) : (timeinfo.tm_wday+i-7)];
        data += date +
                " " + doc_2["results"][0]["daily"][i]["text_day"].as<String>() +
                " 温度:" + doc_2["results"][0]["daily"][i]["low"].as<String>() +
                "~" + doc_2["results"][0]["daily"][i]["high"].as<String>() +
                " 降水:" + doc_2["results"][0]["daily"][i]["precip"].as<String>() +
                "\n";
    }

    // 7. 更新LVGL界面上各个部件的文本
    lv_label_set_text_fmt( ui_LabelLocation, "地点:%s", city.c_str());
    lv_label_set_text_fmt( ui_LabelTemp, "温度:%s度", temp.c_str()); // 原文档有湿度,这里简化了
    lv_label_set_text_fmt( ui_LabelCode, "天气:%s", text.c_str());
    lv_label_set_text_fmt( ui_LabelUpdateTime, "上次更新时间:%s", last_update.c_str());

    // 更新"明天"天气预报的专用标签
    lv_label_set_text_fmt( ui_LabelTomorrowCode, "明天%s,天气%s,温度%s~%s度,降水概率%s",
                            week_weather[(timeinfo.tm_wday+1<=6) ? (timeinfo.tm_wday+1) : (timeinfo.tm_wday+1-7)],
                            text_day.c_str(), low.c_str(), high.c_str(), precip.c_str());

    // 更新未来天气的滚动列表
    lv_roller_set_options( ui_RollerFuture, data.c_str(), LV_ROLLER_MODE_NORMAL );
}
  • 要点

    • JSON解析:deserializeJson() 是ArduinoJson库的核心函数。你需要根据API的实际返回结构,用 doc["key"][index]["subkey"].as<类型>() 的方式一层层取出数据。

    • UI更新:所有 lv_label_set_text_fmt 函数都是在操作你在SquareLine Studio里设计的、名称以 ui_ 开头的UI部件。

3.4 数据更新任务函数 data_update()

这个函数会被FreeRTOS任务周期性调用,负责更新时间和天气。

cs 复制代码
c

void data_update()
{
    if(Data.time_flag) // Data.time_flag 应该是一个全局标志,当需要更新时间时置1
    {
        if(getLocalTime(&timeinfo)) // 尝试获取本地时间
        {
            // 更新时间显示
            const char* week[7]={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
            lv_label_set_text_fmt(ui_LabelTime1,"%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
            lv_label_set_text_fmt(ui_LabelTime2,"%d/%02d/%02d %s", timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday, week[timeinfo.tm_wday]);

            if(Data.weather_flag) // 如果需要更新天气
            {
                weather_get(); // 调用天气获取函数(这个函数可能耗时)
                // 设置日历部件的高亮日期
                lv_calendar_set_today_date(ui_Calendar, timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday);
                lv_calendar_set_showed_date(ui_Calendar, timeinfo.tm_year+1900, timeinfo.tm_mon+1);
                Data.weather_flag = 0; // 清除天气更新标志
            }
        }
    }
}
  • 要点 :这个函数本身不能 包含阻塞性的长延时,尤其是不能在里面直接调用 weather_get() 而不采取措施,因为HTTP请求很慢。在实际项目中,通常会用另一个专门的任务去执行 weather_get(),或者在这里使用非阻塞的方式。你文档最后提到的创建 data_task 就是为了解决这个问题。
3.5 创建独立的FreeRTOS任务

教学文档的最后,给出了创建任务的示例:

cs 复制代码
c

xTaskCreatePinnedToCore(data_task, "data_task", 1024*5, NULL, 1, NULL, 0);

你需要实现 data_task 函数,让它在循环中调用 data_update() 并添加合适的延时:

cs 复制代码
c

void data_task(void *pvParameters) {
    while(1) {
        data_update();  // 检查标志并更新UI
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 每秒执行一次
    }
}

这样,时间可以每秒更新一次,而耗时的 weather_get() 只在 Data.weather_flag 被置1时才触发。

4. 总结与保姆级操作步骤

  1. 注册并获取密钥:去心知天气网站,注册账号,找到你的API密钥,复制它。

  2. 修改代码 :在你项目的 data.c(或类似文件)中,将 String key = "..." 替换为你自己的密钥。

  3. 添加依赖 :确认 platformio.ini 中包含了 bblanchon/ArduinoJson

  4. 检查UI变量名 :确保代码中使用的 ui_LabelLocation, ui_LabelTemp 等变量名,与你SquareLine Studio项目中实际定义的部件名称完全一致(包括大小写)。

  5. 集成到主程序

    • setup() 函数中调用 time_get() 启动NTP同步。

    • 创建并启动 data_task FreeRTOS任务。

    • 当WiFi连接成功后(或根据需要),将 Data.time_flagData.weather_flag 置1,触发时间和天气的首次更新。

  6. 编译并烧录,查看效果。

5. 常见问题与调试

  • 时间显示为1970年或错误 :通常是NTP服务器连接失败或时区设置不对。检查WiFi是否正常,configTime的第一个参数(时区偏移)是否正确。

  • 天气数据不更新

    • 首先确认WiFi已连接。

    • 检查API密钥是否有效,账户是否有剩余查询次数。

    • weather_get() 中添加串口打印,输出 responseresponse_2 字符串,看API是否返回了错误信息(如 "status_code" 非0)。

    • 检查JSON解析路径是否和API返回的结构完全匹配(可以用在线JSON解析器验证)。

  • LVGL界面卡死 :如果直接在LVGL刷新任务或高优先级任务中调用 weather_get(),会因网络阻塞导致界面卡顿。务必将其放在一个低优先级的独立任务中执行。

相关推荐
白云偷星子1 小时前
云原生笔记2
运维·笔记·云原生
蒸蒸yyyyzwd2 小时前
cpp后端学习笔记
c++·笔记
学生信的大叔2 小时前
qiime2-2025.10安装与使用
笔记
强子感冒了2 小时前
JavaWeb学习笔记:动静态Web、URL、HTTP
前端·笔记·学习
Aliex_git2 小时前
Dockerfile 优化实践笔记
笔记·学习·gitlab
xhyu612 小时前
【学习笔记】推荐系统 (7.特征交叉:FM、DCN、LHUC、SENet、Bilinear Cross、FiBiNet)
笔记·学习
芝士雪豹只抽瑞克五2 小时前
HAProxy 七层负载均衡器笔记
运维·笔记·负载均衡
芝士雪豹只抽瑞克五2 小时前
Linux Virtual Server (LVS) 负载均衡集群笔记
linux·笔记·负载均衡·lvs
嵌入式×边缘AI:打怪升级日志2 小时前
基于ESP32S3的智能终端项目--4.1 FreeRTOS 任务调度&&设置屏幕亮度
freertos·屏幕亮度