
1. 为什么用ESP32-IDF做天气获取而不是Arduino拿到这个标题第一反应是都2025年了ESP32获取天气信息这种玩法早就被玩烂了。但仔细一看后缀挂着“IDF”这就不一样了。市面上90%以上的ESP32联网教程都是基于Arduino框架的用Arduino写HTTP请求、解析JSON确实简单几行代码就能跑通但很多人在Arduino上把项目调通之后想往产品级、量产级的方向走就会发现处处碰壁。Arduino框架封得太死底层细节全被藏起来了。你想看TCP连接的状态、想自己控制重连策略、想精确管理内存分配都很难下手。而ESP-IDF是乐鑫官方维护的物联网开发框架基于FreeRTOS实时操作系统提供完整的事件循环机制、TCP/IP协议栈接口以及内存管理、日志系统、看门狗等一整套嵌入式基础设施。我当时选择用ESP-IDF来做这个温度天气获取项目核心原因有三个第一ESP-IDF的WiFi事件驱动模型比Arduino的轮询方式要优雅得多。Arduino里你通常是在loop()里不断检查WiFi状态而IDF是注册事件回调函数系统在WiFi连接成功、断开、获取IP等关键节点主动通知你。这种异步事件驱动的思路才是做真实物联网产品的正确姿势。第二IDF对JSON解析、HTTP客户端的封装虽然比Arduino繁琐但可控性强。比如你可以精确控制HTTP请求的超时时间、可以通过流式解析减少内存占用、可以复用TCP连接。换句话说同样的功能IDF写出来的代码更底层、更扎实也更容易排查问题。第三也是很多人忽略的一点ESP-IDF的组件管理机制让代码结构更清晰。你可以把WiFi连接、HTTP请求、JSON解析、业务逻辑拆成独立组件后续想在这个项目上扩展功能比如显示在OLED屏上、上传到MQTT服务器完全不用推倒重来。别误会我不是说Arduino不好。如果你只是做个课设或者快速验证想法Arduino绝对是最快的路径。但如果想在嵌入式这个方向走得更深或者项目有后续迭代的打算从IDF开始是完全值得的。2. 整体设计从硬件到云端的链路梳理2.1 这个项目到底要做哪些事拆解一下标题就清楚了ESP32获取温度和天气信息。完整的功能链路是这样的ESP32开发板通过板载WiFi模块连接路由器获取局域网IP然后通过HTTP协议访问天气API服务商提供的接口把返回的JSON数据解析出来提取温度、天气现象、湿度、风力这些信息最后通过串口打印或者显示屏展示。听起来简单但里面涉及的知识点并不少WiFi连接管理、TCP/IP协议、HTTP请求格式、JSON数据解析、以及ESP-IDF特有的任务创建和内存管理。任何一个环节出问题整个项目都跑不起来。我见过太多人在WiFi连接这关就卡住了所以后面会重点讲。2.2 为什么选和风天气API市面上免费的天气API其实不少国内的知心天气心知天气、和风天气QWeather国外的OpenWeatherMap还有各种聚合数据平台提供的接口。我最后选了和风天气原因如下访问速度快服务器在国内不需要额外处理网络延迟问题注册即送免费开发版个人学习和DIY完全够用接口返回的数据结构清晰国内城市数据精度高支持城市ID、经纬度、IP定位等多种查询方式当然你也可以选其他服务商但要注意一点不同API的返回字段结构和单位标准不一样。比如温度有的是摄氏度有的是华氏度有的用temp字段有的用temperature字段。代码里的JSON解析部分需要对应调整这一点在移植的时候特别容易踩坑。和风天气的API也有升级迭代的问题。老的api.heweather.com域名的v6版本以及目前推荐的新版devapi.qweather.com的v7版本返回的数据结构有差异。写代码前先看清楚你用的哪个版本的接口文档别拿着一份旧代码去请求新接口返回的JSON解析不出来就开始怀疑人生。注意免费的API Key有调用频率限制通常QPS限制在个位数开发调试阶段没问题但如果是做成产品给多个人用就得考虑用量配额或者自建代理服务。2.3 硬件选型和接线这个项目的硬件门槛几乎为零。你需要的东西硬件数量说明ESP32开发板1块推荐ESP32-DevKitC或NodeMCU-32SUSB数据线1根必须是数据线不能是充电线电脑1台用于编译烧录和查看串口日志可选OLED显示屏1块I2C接口的SSD1306用于本地显示接线方面如果只是通过串口看数据那就只需要用USB线连电脑不用接任何外围器件。如果加OLED屏SDA接GPIO21SCL接GPIO22这是ESP32的默认I2C引脚不同板子可能有差异以自己手头板子的丝印为准。ESP32的价格现在已经被打到10块钱以内了加上一个几块钱的OLED屏这套学习方案的硬件成本不超过20块。相比动不动就要买开发板的其他方案这个项目的性价比是真的高。3. 环境搭建ESP-IDF从零到能编译3.1 安装ESP-IDF环境这里只讲最省事的路线毕竟折腾环境的痛苦大家都懂。官方提供的ESP-IDF Tools Installer是Windows和macOS用户的首选它会自动帮你装好编译器、调试工具、Python环境、以及IDF本身。Linux用户直接用官方脚本mkdir -p ~/esp cd ~/esp git clone --recursive https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh esp32注意--recursive参数不能省ESP-IDF用了大量的子模块少了这个参数后面编译时会缺头文件报一些莫名其妙的错误。安装完成后每次打开新终端都要先执行环境变量导入source $IDF_PATH/export.sh我建议在~/.bashrc里加上这行省得每次都要手动执行。当然前提是已经设置了IDF_PATH环境变量指向esp-idf的目录。提示如果下载速度慢可以先安装idf-github-client工具来加速GitHub访问或者直接用乐鑫官方提供的IDF镜像仓库。这个后面编译的时候还会遇到。3.2 创建并配置工程IDF提供了工程模板不用从零手写CMakeLists.txtcp -r $IDF_PATH/examples/get-started/hello_world ./weather_project cd weather_project然后要手动添加两个组件esp_http_client和cjson。在IDF 4.x及以上的版本中编辑main/CMakeLists.txtidf_component_register(SRCS main.c INCLUDE_DIRS .)实际上如果只用esp_http_client和cJSON不需要额外声明组件依赖因为在代码里include了对应头文件后构建系统会自动解析。但如果你用了其他自定义组件就要在这里显式声明。主要说下工程里几个关键配置文件sdkconfig项目的核心配置文件WiFi相关配置、Flash大小、CPU频率都在这里main/main.c主程序入口CMakeLists.txt构建脚本用idf.py menuconfig可以打开图形化配置界面里面有几个推荐改的地方Component config → LWIP → Enable SO_REUSEADDR → 开启 Component config → ESP32-specific → Main task stack size → 建议改到4096前者能避免WiFi重连时端口复用出错后者是给主任务留足栈空间防止运行到一半栈溢出重启。4. WiFi连接实战事件驱动的正确姿势4.1 初始化与事件循环ESP-IDF连接WiFi的思路和Arduino那种阻塞式的WiFi.begin()完全不同。IDF把WiFi的整个生命周期都设计成了事件驱动——你发出连接指令后系统在后台异步处理每个关键节点都会触发事件回调。这种模型的好处是主任务不会被WiFi连接过程卡死程序在等待连接期间可以去做别的事情。看代码#include esp_wifi.h #include esp_event.h static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_START) { ESP_LOGI(TAG, WiFi STA started, connecting...); esp_wifi_connect(); } else if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGI(TAG, Disconnected from AP, retrying...); esp_wifi_connect(); // 自动重连 } else if (event_base IP_EVENT event_id IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event (ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, Got IP: IPSTR, IP2STR(event-ip_info.ip)); // 在这里通知其他任务可以开始干活了 } }这段代码里最重要的就是WIFI_EVENT_STA_DISCONNECTED分支的处理。路由器重启、信号波动、WiFi密码错改任何原因导致连接断开都会触发这个事件。如果不在这里重新调用esp_wifi_connect()设备断开一次就永远连不回去了只能重启。很多人的板子跑一段时间就掉线就是因为没处理重连逻辑。4.2 配置并启动WiFivoid wifi_init_sta(void) { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(cfg)); esp_event_handler_instance_t instance_any; ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, instance_any)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, instance_any)); wifi_config_t wifi_config { .sta { .ssid CONFIG_WIFI_SSID, .password CONFIG_WIFI_PASSWORD, .threshold.authmode WIFI_AUTH_WPA2_PSK, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); }几个容易出错的地方第一esp_netif_init()和esp_event_loop_create_default()必须在esp_wifi_init()之前调用顺序错了直接报错。第二.threshold.authmode WIFI_AUTH_WPA2_PSK这个字段是设置连接的最低安全等级要求。如果路由器的认证方式是WPA3且设置为仅WPA3模式那这个配置会导致连接失败。比较稳妥的做法是设成WIFI_AUTH_OPEN表示不限制。第三用CONFIG_WIFI_SSID这种方式是在menuconfig里配置的WiFi信息。好处是代码里不硬编码敏感信息改WiFi不用重新编译。我把SSID和密码放在了menuconfig → Example Connection Configuration里这样每次换WiFi环境只需要改配置重新烧录固件就行。4.3 任务同步连接成功后再发请求WiFi连接是异步的但HTTP请求必须在拿到IP地址之后才能发起。这中间就需要一个同步机制。最简单的做法是用xEventGroupSync或者FreeRTOS的队列在IP_EVENT_STA_GOT_IP事件回调里置一个标志位主任务等待这个标志位后再往下走。我习惯用事件组#define WIFI_CONNECTED_BIT BIT0 static EventGroupHandle_t wifi_event_group; // 在 wifi_init_sta 里初始化 wifi_event_group xEventGroupCreate(); // 事件回调里 xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); // 主任务里 xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);这里解释一下为什么xEventGroupWaitBits的第三个参数要设为pdFALSE设为pdFALSE表示等待标志位时不清除它这样即使多个任务同时等待这个事件也不会出现事件被抢走的问题。如果设为pdTRUE一个任务等到之后标志位就清零了另一个任务可能就永远等不到。5. 获取天气数据HTTP请求与JSON解析5.1 构造请求URL和风天气的API域名和路径格式比较稳定以城市ID查询为例https://devapi.qweather.com/v7/weather/now?location101010100key你的API_Key参数的说明location城市ID101010100是北京的ID可以在天气API官方的城市列表里查到自己所在城市对应的IDkey注册后获取的API密钥如果不想查城市ID也可以用经纬度坐标格式是location116.41,39.92经度在前纬度在后。我个人推荐用经纬度定位更灵活而且不需要维护城市ID库。为了让请求更规范再加两个参数langzh // 返回中文描述 unitm // 公制单位温度用摄氏度完整URL长度也不长ESP32的HTTP客户端完全能处理。5.2 用esp_http_client发起请求ESP-IDF封装了esp_http_client组件API设计得比原生的socket请求要友好很多。看一个GET请求的完整流程#include esp_http_client.h esp_http_client_config_t config { .url url, .method HTTP_METHOD_GET, .timeout_ms 5000, .user_data response_buffer, // 存放响应内容的缓冲区 }; esp_http_client_handle_t client esp_http_client_init(config); esp_err_t err esp_http_client_perform(client); if (err ESP_OK) { // 请求成功response_buffer 里是完整的响应体 } esp_http_client_cleanup(client);这里有两个非常重要的细节第一user_data字段用来传递自定义数据。在事件回调里可以通过http_response_data拿到服务端返回的完整内容static esp_err_t http_event_handler(esp_http_client_event_t *evt) { if (evt-event_id HTTP_EVENT_ON_DATA) { // 数据分块到达追加到缓冲区 } }如果不设置user_data默认是拿不到响应体的你需要自己处理HTTP_EVENT_ON_DATA事件把分块到达的数据拼接起来。第二服务器返回的数据大小不确定。我建议先给缓冲区分配足够大的内存比如2KB。和风天气的weather/now接口返回的JSON一般不到1KB2KB绰绰有余。5.3 用cJSON解析天气数据需要把返回的JSON字符串解析成结构体。ESP-IDF自带cJSON库这是一套老牌的C语言JSON解析库API设计得很直观。天气返回的JSON长这样{ code: 200, now: { obsTime: 2025-01-15T10:3008:00, temp: 23, feelsLike: 24, icon: 101, text: 多云, wind360: 135, windDir: 东南风, windScale: 3, windSpeed: 15, humidity: 48, precip: 0.0, pressure: 1009, vis: 25, cloud: 20, dew: 16 } }注意temp字段是字符串类型不是数字类型。用cJSON_GetObjectItem取到值之后还得用-valuestring去拿字符串再用atoi转成整数。很多教程在这一点上含糊带过结果新手照着敲代码一解析就崩。void parse_weather_data(const char *json_string, weather_info_t *weather) { cJSON *root cJSON_Parse(json_string); if (root NULL) { ESP_LOGE(TAG, JSON parse error); return; } // 检查返回码 cJSON *code cJSON_GetObjectItem(root, code); if (code strcmp(code-valuestring, 200) ! 0) { ESP_LOGE(TAG, API returned error code: %s, code-valuestring); cJSON_Delete(root); return; } cJSON *now cJSON_GetObjectItem(root, now); if (now NULL) { ESP_LOGE(TAG, No now field found); cJSON_Delete(root); return; } cJSON *temp cJSON_GetObjectItem(now, temp); cJSON *text cJSON_GetObjectItem(now, text); cJSON *humidity cJSON_GetObjectItem(now, humidity); if (temp text humidity) { weather-temperature atoi(temp-valuestring); snprintf(weather-description, sizeof(weather-description), %s, text-valuestring); weather-humidity atoi(humidity-valuestring); ESP_LOGI(TAG, Temperature: %d°C, Weather: %s, Humidity: %d%%, weather-temperature, weather-description, weather-humidity); } cJSON_Delete(root); // 这句千万别忘了防止内存泄漏 }5.4 HTTP返回码和状态判断很多初学者踩过一个坑HTTP请求成功了但返回的数据是错的。原因是服务端返回的HTTP 200不代表业务逻辑正确——和风天气用的是应用层的code字段比如API Key无效时返回401或者城市ID不存在时返回404。所以解析JSON之前一定要先检查code字段是否为200。这其实是个好习惯。无论对接什么API都不要只依赖HTTP状态码判断请求是否成功要检查应用层返回的业务状态码。我之前对接过某个硬件云平台的APIHTTP 200的情况下返回的业务错误有十几种只用HTTP状态码会漏掉绝大多数异常情况。6. 完整代码与编译烧录6.1 完整代码mqtt分支简化版把上面几个模块串起来完整的main.c代码大概长这样#include stdio.h #include string.h #include freertos/FreeRTOS.h #include freertos/task.h #include freertos/event_groups.h #include esp_system.h #include esp_wifi.h #include esp_event.h #include esp_log.h #include esp_netif.h #include esp_http_client.h #include cJSON.h #include nvs_flash.h #define WIFI_CONNECTED_BIT BIT0 #define TAG WEATHER typedef struct { int temperature; int humidity; char description[32]; } weather_info_t; static EventGroupHandle_t wifi_event_group; // 在menuconfig中配置 #define WIFI_SSID CONFIG_WIFI_SSID #define WIFI_PASS CONFIG_WIFI_PASSWORD #define WEATHER_API_KEY CONFIG_WEATHER_API_KEY #define WEATHER_CITY CONFIG_WEATHER_CITY static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGI(TAG, WiFi disconnected, retrying...); esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT); } else if (event_base IP_EVENT event_id IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event (ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, Got IP: IPSTR, IP2STR(event-ip_info.ip)); xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); } } static void wifi_init_sta(void) { wifi_event_group xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(cfg)); ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL)); wifi_config_t wifi_config { .sta { .ssid WIFI_SSID, .password WIFI_PASS, .threshold.authmode WIFI_AUTH_WPA2_PSK, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); } static void fetch_weather(weather_info_t *weather) { char url[256]; snprintf(url, sizeof(url), https://devapi.qweather.com/v7/weather/now?location%skey%slangzhunitm, WEATHER_CITY, WEATHER_API_KEY); char response[2048] {0}; esp_http_client_config_t config { .url url, .method HTTP_METHOD_GET, .timeout_ms 5000, .user_data response, .event_handler NULL, // 使用默认处理方式 }; esp_http_client_handle_t client esp_http_client_init(config); esp_err_t err esp_http_client_perform(client); if (err ESP_OK) { int status_code esp_http_client_get_status_code(client); ESP_LOGI(TAG, HTTP status: %d, response: %s, status_code, response); parse_weather_data(response, weather); } else { ESP_LOGE(TAG, HTTP request failed: %s, esp_err_to_name(err)); } esp_http_client_cleanup(client); } void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); wifi_init_sta(); // 等待WiFi连接 xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY); weather_info_t weather {0}; while (1) { fetch_weather(weather); vTaskDelay(pdMS_TO_TICKS(60000)); // 每60秒刷新一次 } }有一点要说明上面的代码为了展示核心逻辑做了一定程度的简化比如parse_weather_data函数的实现省略了在上一节有完整版HTTP事件处理也用了默认模式。实际项目中建议把parse_weather_data函数单独放到一个.c文件里方便后期维护。6.2 menuconfig配置项在main/Kconfig.projbuild里定义自定义配置项menu Weather Configuration config WEATHER_API_KEY string API Key default config WEATHER_CITY string City ID default 101010100 config WIFI_SSID string WiFi SSID default config WIFI_PASSWORD string WiFi Password default endmenu这样在idf.py menuconfig里就能直接看到并修改这些配置非常方便。这个文件的格式是Kconfig语法和Linux内核的配置系统一样上手很轻松。6.3 编译烧录idf.py build idf.py -p /dev/ttyUSB0 flash monitor第一次编译会下载一些依赖包速度可能比较慢。编译成功后烧录到ESP32里打开串口监视器就能看到类似这样的输出I (1234) WEATHER: WiFi connected, SSID: MyWiFi I (1234) WEATHER: Got IP: 192.168.1.100 I (2234) WEATHER: HTTP status: 200, response: {code:200,now:{temp:23,...}} I (2234) WEATHER: Temperature: 23°C, Weather: 多云, Humidity: 48%看到这个输出恭喜你整个链路已经打通了。7. 常见问题与排查技巧实录7.1 问题速查表问题现象可能原因排查和解决方式编译报错找不到cJSON.h组件依赖未声明在CMakeLists.txt里加REQUIRES cjsonWiFi一直连接不上密码错误、认证方式不匹配检查menuconfig里配置确认路由器认证方式连接上但掉线反复重连路由器信号弱/电源供电不足检查供电电流是否足够至少500mAHTTP请求超时API域名解析DNS失败或网络不通先用ping命令测试连通性或者缩短timeout_ms观察报错JSON解析返回NULL请求返回非预期结构用串口打印原始响应对照API文档检查字段运行一段时间后重启看门狗超时或者内存泄漏检查任务栈大小排查是否有未释放的cJSON节点温度为负数显示乱码atoi转换后格式化字符问题用%d格式化即可检查是否用了%u无符号输出7.2 避坑心得内存管理ESP32虽然有520KB的SRAM但实际可用的Heap内存也没有想象中多。特别是cJSON_Parse这类操作解析一个稍大的JSON对象一次性就要分配十几KB的动态内存。如果频繁请求每次又不释放用不了多久就会内存爆掉系统崩溃重启。所以我强烈建议在代码里加上内存使用统计方便调试ESP_LOGI(TAG, Free heap: %d bytes, esp_get_free_heap_size());放在HTTP请求前后各打一次如果两次差值越来越大说明有内存泄漏重点检查cJSON_Delete有没有遗漏。7.3 避坑心得HTTP请求用HTTPS还是HTTP和风天气的API同时支持HTTP和HTTPS。我在代码里用的是HTTPS好处是数据加密传输Key不会明文暴露在局域网里被截获。但代价是ESP32需要处理TLS握手会增加一些内存和耗时。如果你用的是HTTP版本代码里需要把URL改为http://devapi.qweather.com/...并且不需要做TLS相关配置。但要注意两点第一部分API服务商对HTTP请求的频率限制更严格第二局域网内抓包工具比如Wireshark能直接看到你的API Key。个人DIY项目无所谓但如果想做得正规一些建议还是用HTTPS。使用HTTPS时ESP-IDF需要加载证书。和风天气的证书是权威CA签发的IDF默认带了cacert_bundle一般不用额外配置。但如果你用的API服务商用的是自签名证书就得手动导入证书这种情况下建议先用HTTP模式调通再做HTTPS升级。7.4 断网自动恢复策略物联网设备最怕的不是一开始连不上网而是运行过程中网络出问题。我在这个项目里做了三层保障第一WiFi层在WIFI_EVENT_STA_DISCONNECTED事件里调用esp_wifi_connect()自动重连。但我加了一个退避逻辑连续重连3次失败后暂停5分钟再重试避免高频重连导致路由器负载异常。第二HTTP层请求超时时间设为5秒超时后本次请求失败但程序不退出进入下一次循环时重新请求。第三业务层如果连续10次请求都失败判定WiFi可能彻底断开了主动调用esp_wifi_disconnect()和esp_wifi_connect()强制重新走一遍连接流程。这套策略实测下来在路由器重启的场景下设备能在1分钟之内自动恢复不需要人工干预。8. 项目扩展与后续想法天气获取这个项目本身不大但它是一个非常好的“样板工程”。如果把WiFi连接、HTTP请求、JSON解析这套链路玩熟了后面很多物联网项目都能吃上这波红利。我个人踩过不少坑之后觉得这个项目最适合往下面几个方向扩展OLED屏显示加一块0.96寸的SSD1306屏幕把温度、天气图标、湿度都画上去放在桌面当电子台历用。I2C接口就两根线代码量也不大。顺手还能把时间也同步了做成一个桌面天气时钟。接入智能家居用获取到的天气数据做决策比如下雨了自动关窗、温度过低自动开启加热器。ESP32本身就支持多种物联网协议往这个方向扩展很顺手。多城市监控在代码里定义城市数组循环请求多个城市的天气数据然后把信息通过MQTT上报到服务器做一个简单的多地点天气监控系统。这个场景适合放在办公室、家里、父母家多个地方部署。本地数据记录加上microSD卡模块或NVS存储每小时记录一次天气数据跑上一个月可以把数据导出出来分析当地的气候变化规律。这个玩法有意思但要注意NVS存储有擦写寿命限制建议用外置SD卡。工程代码我放在了自己的仓库里需要的朋友可以拉下来参考。遇到任何问题欢迎在评论区或者私信交流。做嵌入式开发就是这样没有谁能一次跑通都是靠反复试错一点点调出来的。希望这篇笔记能帮你少走点弯路。