ESP32实战:从心知天气API到智能家居气象站(附完整代码)
1. 为什么选择ESP32和心知天气打造气象站每次看到手机上的天气预报不准我就特别想自己做个靠谱的气象站。ESP32这块开发板简直是为物联网而生自带Wi-Fi和蓝牙价格还特别亲民几十块钱就能搞定。而心知天气的API是我用过最稳定的免费天气接口数据更新快不说返回的JSON格式也特别规整。说到实际应用场景这个气象站可以放在阳台上实时监测室外温湿度配合家里的智能空调自动调节室温。我去年夏天就靠这个方案省了不少电费比单纯用定时器控制空调智能多了。ESP32的低功耗特性让它完全可以靠太阳能电池板供电实现真正的无线部署。相比其他天气API心知天气有几点特别吸引我首先是免费版完全够用20次/分钟的调用频率对个人项目绰绰有余其次是文档非常规范新手也能快速上手最重要的是数据准确度很高我对比过几个主流平台心知天气的实时数据最接近实际观测值。2. 硬件准备与开发环境搭建2.1 必备硬件清单我建议初学者直接购买ESP32开发板套装通常包含以下组件ESP32-WROOM-32开发板建议选择带CP2102芯片的版本驱动兼容性好Micro USB数据线既能供电也能烧录程序杜邦线若干建议准备20根以上公对公、公对母各半0.96寸OLED显示屏SSD1306驱动用来显示天气信息面包板方便临时搭建电路如果想让气象站更专业可以加配这些传感器BME280环境传感器温湿度气压三合一光敏电阻模块监测光照强度雨滴检测模块放在室外监测降雨2.2 软件环境配置我习惯用Arduino IDE开发ESP32项目配置步骤如下安装最新版Arduino IDE1.8.x以上在首选项中添加ESP32开发板管理地址https://dl.espressif.com/dl/package_esp32_index.json打开开发板管理器搜索安装esp32选择2.0.x版本安装必要的库文件ArduinoJson6.x以上版本HTTPClient通常随ESP32开发板包自带WiFi系统自带Adafruit_SSD1306如果使用OLED屏这里有个坑要注意不同版本的ArduinoJson库API可能不兼容我建议锁定使用6.19.4版本这个版本在ESP32上最稳定。安装方法是在库管理器中搜索ArduinoJson点击版本选择下拉菜单指定版本号。3. 心知天气API申请与配置3.1 获取API密钥实战打开心知天气官网(www.seniverse.com)注册流程很简单用邮箱验证就能激活账号。登录后进入控制台在我的产品里找到免费版天气API点击立即申请。申请成功后系统会生成一个类似SKmg2uxKdER08RUk-的API密钥。这里要特别注意密钥中的横线-是有效字符复制时千万不能漏掉我就曾经因为漏掉这个横线调试了半天。免费版API的限制是每分钟20次调用每天1万次上限支持国内3000城市返回数据包含温度、天气状况、风力等基础信息3.2 API调用参数详解心知天气提供了多种API接口我们主要用天气实况这个接口。完整请求URL格式如下String url https://api.seniverse.com/v3/weather/now.json?key你的API密钥location城市名languagezh-Hansunitc关键参数说明location支持城市名如北京、拼音beijing、IDWX4FBXXFKE4F或经纬度39.93:116.40languagezh-Hans返回简体中文unitc表示摄氏温度f表示华氏温度测试时可以直接把构造好的URL粘贴到浏览器地址栏看看返回的JSON数据是否正常。我建议先用Postman测试接口确认无误再写代码。4. ESP32连接Wi-Fi与HTTP请求4.1 稳定连接Wi-Fi的技巧ESP32连接Wi-Fi的代码虽然简单但实际使用中会遇到各种问题。这是我优化过的连接方案void connectWiFi() { WiFi.disconnect(true); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.print(正在连接WiFi); int retries 0; while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); if(retries 30) { Serial.println(\n连接超时重启ESP32); ESP.restart(); } } Serial.println(\n连接成功); Serial.print(IP地址: ); Serial.println(WiFi.localIP()); }这段代码增加了几个实用功能连接前先断开现有连接避免冲突设置30秒超时超时后自动重启显示获取到的IP地址方便调试如果遇到频繁断连的问题可以尝试在路由器设置中将2.4GHz频段的信道固定在1、6或11避开拥挤的信道。4.2 优化HTTP请求的稳定性ESP32的HTTPClient库用起来简单但在实际项目中要考虑网络不稳定的情况。这是我总结的最佳实践String httpGETRequest(String url) { HTTPClient http; String payload {}; // 默认返回空JSON http.begin(url); http.setTimeout(10000); // 设置10秒超时 int httpCode http.GET(); if (httpCode HTTP_CODE_OK) { payload http.getString(); } else { Serial.printf(HTTP请求失败错误码: %d\n, httpCode); } http.end(); return payload; }这个封装函数有以下几个优点设置了合理的超时时间对错误码进行了处理始终保证返回有效的JSON字符串正确释放了HTTP连接资源5. JSON数据解析与显示优化5.1 使用ArduinoJson解析天气数据心知天气返回的JSON数据结构比较规整但解析时还是有些细节要注意。这是我的解析代码void parseWeather(String json) { DynamicJsonDocument doc(2048); // 分配足够大的内存 DeserializationError error deserializeJson(doc, json); if (error) { Serial.print(JSON解析失败: ); Serial.println(error.c_str()); return; } JsonObject results_0 doc[results][0]; String location results_0[location][name]; String weatherText results_0[now][text]; int temperature results_0[now][temperature]; int humidity results_0[now][humidity]; String windDir results_0[now][wind_direction]; int windSpeed results_0[now][wind_speed]; Serial.println(位置: location); Serial.println(天气: weatherText); Serial.println(温度: String(temperature) °C); Serial.println(湿度: String(humidity) %); Serial.println(风向: windDir); Serial.println(风速: String(windSpeed) km/h); }关键点说明内存分配要足够大心知天气的返回数据通常在1KB左右一定要检查解析错误使用完整路径访问嵌套的JSON字段更安全数值型数据建议直接转为int类型5.2 OLED屏幕显示优化如果使用0.96寸OLED屏显示天气信息要注意以下几点分页显示信息避免屏幕拥挤添加图标更直观可以使用U8g2库内置的天气图标设置合理的刷新频率建议30秒更新一次这是我的显示函数示例void displayWeather() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); // 第一行显示城市和天气状况 display.setCursor(0,0); display.print(city weatherText); // 第二行显示温度大字体 display.setTextSize(2); display.setCursor(0, 20); display.print(temperature); display.print(°C); // 第三行显示其他信息 display.setTextSize(1); display.setCursor(0, 45); display.print(湿度: String(humidity) % ); display.print(风速: String(windSpeed) km/h); display.display(); }6. 完整项目代码与部署建议6.1 项目完整源码以下是整合了所有功能的完整代码我已经在实际项目中验证过稳定性#include WiFi.h #include HTTPClient.h #include ArduinoJson.h #include Wire.h #include Adafruit_SSD1306.h #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, OLED_RESET); const char* ssid 你的WiFi名称; const char* password 你的WiFi密码; const char* apiKey 你的心知天气API密钥; const char* city 北京; String weatherText; int temperature; int humidity; String windDir; int windSpeed; void setup() { Serial.begin(115200); // 初始化OLED if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(OLED初始化失败); while(1); } display.display(); delay(2000); display.clearDisplay(); connectWiFi(); fetchWeather(); displayWeather(); } void loop() { static unsigned long lastUpdate 0; if (millis() - lastUpdate 300000) { // 每5分钟更新一次 lastUpdate millis(); fetchWeather(); displayWeather(); } } void connectWiFi() { // ...WiFi连接代码同前... } String httpGETRequest(String url) { // ...HTTP请求代码同前... } void fetchWeather() { String url https://api.seniverse.com/v3/weather/now.json?key String(apiKey) location String(city) languagezh-Hansunitc; String json httpGETRequest(url); parseWeather(json); } void parseWeather(String json) { // ...JSON解析代码同前... } void displayWeather() { // ...显示代码同前... }6.2 部署与优化建议在实际部署时我有几个实用建议电源管理如果使用电池供电建议启用ESP32的深度睡眠模式只在需要时唤醒外壳选择3D打印一个防水外壳或者用现成的防水盒改装安装位置避免阳光直射否则温度读数会偏高错误恢复添加看门狗定时器遇到死机自动重启数据记录可以定期将天气数据保存到SD卡或上传到物联网平台对于进阶开发者可以考虑这些扩展功能添加MQTT支持将数据发布到Home Assistant实现OTA升级功能方便远程更新固件开发微信小程序随时查看气象站数据添加语音播报功能做成会说话的天气站