简介面向ESP32与物联网初学者的HTTPS天气获取Demo演示如何基于mbedtls库建立安全连接、发起天气API请求并通过cJSON解析温度、湿度等JSON数据。资源为完整ESP-IDF工程共1682个文件以.o/.d编译中间文件、.h头文件、.a静态库及.mk构建脚本为主另有sdkconfig配置与Makefile整体19.36MB便于直接查看工程结构与编译产物免去手动搭建依赖的麻烦。已有5186人学习下载适合想快速上手HTTPS、TLS和JSON解析的开发者。需要留意的是示例为简化流程跳过了证书验证读者可在此基础上补充CA证书链校验体会真实项目中的安全要点。通过阅读源代码并结合mbedtls与cJSON的调用流程可清晰理解ESP32从联网到请求、解析、输出的完整链路。1. 这个DEMO到底在做什么从零搭一个能用的天气获取链路打开天气预报App看到的是“晴、25℃、湿度40%”这几个字背后却是一条完整的数据链路。我这个DEMO所做的事情就是把这套链路最核心的一段——通过HTTPS协议请求远程天气服务并解析返回结果——在ESP32这块开发板上完整地跑通。先明确一下这个DEMO到底解决了什么问题。对于刚接触嵌入式联网开发的人来说最常见的困惑有两个第一WiFi连上之后怎么真正“拿到”互联网上的数据而不是只停留在连接成功的阶段第二HTTP协议大家都在用但为什么实际项目中服务商都强制要求HTTPS两者之间到底差了些什么。这个DEMO就是用一套最简的代码把WiFi连接、HTTPS请求、JSON解析这三件事串起来跑通之后你就具备向任意的HTTPS API服务发起请求并处理响应结果的能力了不只是天气股票行情、地图定位、传感器上报都是同一套逻辑。适合谁来参考刚入手ESP32、想从点灯进阶到联网取数据的开发者搞过Python或后端、但对嵌入式环境的网络栈不熟悉的转行者还有做课程设计或毕设需要一套电路简单又能演示联网交互的学生。整个DEMO硬件成本极低一块ESP32开发板加一条USB线就能跑。软件上我用的是Arduino框架不碰ESP-IDF的复杂配置把精力集中在网络请求和数据处理上。在动笔写代码之前先把这个链路在脑子里过一遍。从开发板视角出发整个流程是上电 → 初始化WiFi → 连接到路由器 → 建立TCP连接 → 通过TLS握手建立HTTPS加密通道 → 发送HTTP GET请求 → 接收服务器返回的JSON数据 → 解析出温度、湿度、天气描述字段 → 通过串口打印出来。每一步都相互依赖但拆开看每一环又不复杂这就是为什么我说这是一个“简单但完整”的DEMO。2. 选型与设计思路为什么是ESP32、HTTPS和Arduino框架2.1 为什么选ESP32而不是其他开发板当初选型的时候我也想过用ESP8266毕竟它更便宜资料也多。但最终还是选了ESP32原因很直接ESP8266只有一个Tensilica Xtensa LX106单核CPU时钟只有80MHzRAM只有80KB可用做HTTPS请求时W5500这类方案还要另接以太网模块而ESP32是双核240MHz、520KB SRAM加4MB Flash在跑TLS握手这种对CPU和内存都有一定压力的任务时余量明显更大。更关键的一点是ESP32原生支持TLS 1.2协议栈这在Arduino环境下直接对应着WiFiClientSecure库用起来就是几行代码的事情。如果是ESP8266虽然也有对应的库支持但需要手动管理堆内存的空间更大而且芯片内部的加密引擎弱一些大证书握手时很容易出现半路断连的情况。说句实在的ESP32多出来的那十几块钱成本换来的开发体验和故障排查难度下降是绝对值的。还有一个容易被忽略的点ESP32的Flash容量大意味着能放下更完整的CA证书文件。后面讲HTTPS证书校验时你会看到要把服务器的证书链完整存进Flash小容量芯片是很吃力的但ESP32在这方面就比较从容。2.2 HTTPS和HTTP的本质区别以及为什么服务商都强制HTTPS在很多初学者眼里HTTPS就是一个“更安全”的HTTP具体怎么安全、安全到什么程度并没有清晰的概念。这里用一个日常场景类比HTTP像是在明信片上写信寄出去沿途经过的每一个邮局工作人员都能看到内容HTTPS则是把信装进一个上了锁的保险箱再交给快递员运输只有收件人手里的钥匙才能打开。这个“钥匙”体系在技术上就是TLS协议干的事情。细拆下来TLS握手是一个三步走的流程第一步是服务器把它的数字证书拿给客户端看相当于出示工作证第二步是客户端验证这个证书是不是由可信的证书颁发机构CA签发的并且确保证书上的域名和客户端请求的域名一致第三步是双方协商出一个对称加密密钥之后的通信全部用这个密钥做对称加密因为非对称加密的计算开销太大了不适合大流量场景。那么回到国内常见的天气数据服务商为什么全部要求HTTPS而不是继续用HTTP最表层的原因是政策合规用户数据必须加密传输。更深层的原因有两个一是防止数据在链路中被运营商或中间代理注入广告脚本这种情况在HTTP下是真实存在的而且很难追责二是防止请求参数被篡改比如说攻击者把你请求中的城市编码换掉让你拿到错误数据这种“中间人攻击”在HTTP下几乎零成本。对我们做嵌入式开发的人而言直接后果就是如果还用早年那些HTTP接口大多数服务商要么已经下架要么返回的响应被强制重定向到HTTPS。所以这个DEMO只用HTTPS不是炫技而是客观要求。2.3 Arduino框架和ESP-IDF怎么选刚开始做ESP32开发第一个摆在面前的选择就是框架用Arduino还是用乐鑫官方的ESP-IDF我的建议很明确除非你要做量产级产品且对低功耗、实时性有非常极限的要求或者需要用到蓝牙Mesh、自研协议栈等Arduino封装不到位的特性否则优先用Arduino。理由也很务实。第一Arduino生态中有大量现成库这个DEMO用到的WiFiClientSecure和ArduinoJson都是水土相符的成熟方案第二Arduino平台对TLS的处理做了很好的封装ESP-IDF自己实现HTTPS客户端时要手动管理一整套事件循环和内存分配写起来非常痛苦第三调试手段简单——串口打印就能解决问题而ESP-IDF下的错误排查往往要翻以太网日志和分析异常回调。当然Arduino也有它的坑最典型的就是库版本碎片化。ESP32的Arduino核心库更新频率很快不同版本之间的API有细微差别后面我会专门讲版本怎么锁定这个坑我是实实在在踩过的。3. 准备工作环境搭建、API选型与证书处理3.1 开发环境搭建与Arduino IDE配置环境搭建这块我默认你用的是最主流的组合Arduino IDE加ESP32开发板支持包。安装完成之后要做两件关键的事这两件事决定了你后续是否会被各种编译错误折腾到崩溃。第一件事是配置开发板管理器地址。打开Arduino IDE进入“文件 → 首选项 → 附加开发板管理器网址”添加乐鑫官方或镜像的JSON索引地址。注意这里有个大坑如果你直接访问国外源在部分网络环境下很容易下载到一半就中断一个稳妥的替代方案是使用国内镜像加速。具体做法是把这个JSON地址换成国内镜像站提供的对应链接然后在“工具 → 开发板 → 开发板管理器”中搜索“esp32”并安装。第二件事是锁定固件版本。我建议你安装 release 2.x 系列的 2.0.17 版本左右不要直接追最新版。原因是最新版的Arduino-ESP32核心库在一些API上做过修改很多网上的历史教程和库文件适配的是2.0.x系列版本不匹配会出现各种“reference is ambiguous”之类的编译错误排查起来非常头疼。开发板选择上如果你用的是最常见的ESP32 DevKitC系列直接选“ESP32 Dev Module”就行Flash Mode选QIOFlash Size保持默认即可。3.2 天气数据API的选型与注册API选型是整个DEMO中比较个性化的一环。目前主流的中文天气服务大概有这么几条路线一是国际级的OpenWeatherMap接口稳定、文档清晰但免费额度每天只有60次调用而且返回的是英文描述二是国内的和风天气中文体验好有免费开发版但需要实名认证三是心知天气接口简单注册即可用免费版每天1000次调用对学习DEMO来说完全够用。我最终选的是心知天气理由有三个注册流程极简不需要审核免费版支撑一个DEMO绰绰有余返回的JSON结构不算复杂对初学者解析起来更友好。注册后在控制台里创建一个“天气天气”类型的应用你会拿到一个私钥这个私钥就是API请求的钥匙。3.3 HTTPS证书处理两种方案的权衡与选择这是整个DEMO中最让人困惑也最容易踩坑的地方。ESP32通过HTTPS访问服务器时WiFiClientSecure默认会校验服务器的CA证书。如果你不做任何证书配置直接跑代码大概率会看到这样的报错ssl_client: crt verify failed。这是因为ESP32的固件里没有内置你服务器的根证书它无法确认这个服务器是可信的。处理这个问题的思路有两个一个是把服务器的CA证书硬编码进代码让ESP32用这份CA去校验链上所有证书另一个是使用ESP32的setInsecure()方法跳过证书校验只建立加密通道但不验证服务器身份。对于DEMO来说我强烈建议使用第一种方案虽然多了一步证书获取但你能顺便理解TLS的核心机制。第二种方案只适合测试临时用因为跳过校验等于放弃了防中间人攻击的能力这就和整个HTTPS的意义背道而驰了。那么怎么下载服务器的CA证书有两个途径一是直接去心知天气官网找他们的安全文档一般会提供证书下载二是用浏览器打开API地址点击地址栏左侧的小锁图标查看证书详情并导出根证书。导出后用文本编辑器打开你会看到以-----BEGIN CERTIFICATE-----开头的一长串Base64内容这就是我们要用的CA证书。这里有一个非常重要的坑必须提醒JSON格式中直接把证书字符串放在引号里没问题但要注意这个字符串里不能有额外的换行转义符否则TLS握手会被打断错误提示还非常隐晦。我在第一次跑的时候就因为这个折腾了半个晚上后来换了一种写法把证书从代码中拿出来放到源文件的单独头文件里这才让报错信息一下子变得友好清晰了。4. 完整代码编写与实操过程4.1 核心代码从WiFi连接到HTTPS请求理清了方案逻辑下面直接跑代码。先把整体结构说一下整个Demo用Arduino框架编写核心模块只有三个WiFiClientSecure负责HTTPS通信ArduinoJson负责解析返回数据串口负责把结果显示出来。先看初始化部分也就是WiFi连接和HTTPS客户端配置的代码。我们用ESP32开发板内置的WiFi库建立网络连接并为后续的加密通信做好准备#include WiFi.h #include WiFiClientSecure.h #include ArduinoJson.h // 配置信息 const char* ssid 你的WiFi名称; const char* password 你的WiFi密码; const char* host api.seniverse.com; const char* privateKey 你的API私钥; const char* city shenzhen; const char* rootCACert REOF(-----BEGIN CERTIFICATE----- 这里放你的CA证书内容 -----END CERTIFICATE-----)EOF; // 把证书挂载到secure client上 WiFiClientSecure client; void setup() { Serial.begin(115200); // 1. 连接WiFi WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\r\nWiFi连接成功); // 2. 配置HTTPS证书校验 client.setCACert(rootCACert); Serial.print(本地IP: ); Serial.println(WiFi.localIP()); }这段代码里最核心的一行是client.setCACert(rootCACert)它把之前下载的CA证书加载进了WiFiClientSecure对象。这一步做不好后面的所有HTTPS请求都会在TLS握手阶段失败。我个人的经验是为了确保验证这些内容过程中的证书条目在加载时不会出错建议把普通主体文本与证书之间做明确区分避免拼写和格式上的混乱。接着看主循环中如何构造请求并接收响应。这里的核心是通过client.connect(host, 443)连接到目标服务器的443端口然后发送标准HTTP GET请求头等待并读取响应void loop() { if (!client.connected()) { if (!client.connect(host, 443)) { Serial.println(HTTPS连接失败); delay(3000); return; } } // 拼凑GET请求URL String url /v3/weather/now.json?key String(privateKey) location String(city) languagezh-Hansunitc; Serial.println(请求URL: url); client.print(String(GET ) url HTTP/1.1\r\n Host: host \r\n Connection: close\r\n\r\n); // 跳过HTTP响应头直接读取body String line client.readStringUntil(\n); // 等待响应结束并拼接数据 String response ; while (client.connected()) { String chunk client.readStringUntil(\n); if (chunk.length() 0) { response chunk; } } Serial.println(原始响应: ); Serial.println(response); delay(60000); // 每60秒请求一次 }这里面有个细节很多人不懂为什么要先发Connection: close因为在HTTP/1.1协议中默认是保持长连接的。如果不主动声明关闭服务器返回完数据后并不立即断开TCP那client.connected()会一直为真代码就会一直卡在读数据阶段给人一种“程序假死”的错觉。加了这个头服务器发完数据就断开连接这段代码才能正常走完。4.2 JSON解析与数据显示收到响应之后body是一个JSON字符串心知天气返回的结构大致长这样{ results: [ { location: { name: 深圳 }, now: { temperature: 28, text: 多云 } } ] }用ArduinoJson库解析需要专门说明一下API版本。ArduinoJson目前主流的版本是5.x和6.x两者API不兼容尤其6.x版本的deserializeJson返回值判断方式和5.x的parseObject完全不同。我这里用的是6.x语法这也是当前建议的版本。解析的核心代码如下StaticJsonDocument512 doc; DeserializationError err deserializeJson(doc, response); if (err) { Serial.print(JSON解析失败: ); Serial.println(err.c_str()); return; } const char* temperature doc[results][0][now][temperature]; const char* weatherText doc[results][0][now][text]; const char* locationName doc[results][0][location][name]; Serial.printf(城市: %s 温度: %s℃ 天气: %s\r\n, locationName, temperature, weatherText);解析这段代码时要特别注意内存分配问题。我用的StaticJsonDocument512表示静态分配512字节的内存空间用于存放JSON解析树。这是ArduinoJson文档里强调的关键内存管理问题如果服务器返回的JSON比你预想的大解析会失败并返回NoMemory错误。对心知天气的接口来说512字节足够如果你换了更大返回体的API建议改成DynamicJsonDocument或者按量调大静态内存。还有一个经验之谈doc[results][0][now][temperature]这种嵌套访问方式如果JSON里某个中间层不存在返回的不是null而是Undefined值在6.x版本中取它的asconst char*会得到空指针。所以严谨的写法是先判断doc.containsKey或直接判断temperature ! nullptr。4.3 完整运行效果把上面的代码合并到一起编译上传后打开串口监视器波特率设成115200你会发现程序按照预期的流程逐步执行最终会看到类似下面的输出............. WiFi连接成功 本地IP: 192.168.1.102 请求URL: /v3/weather/now.json?keyxxxlocationshenzhen... HTTP/1.1 200 OK ... 城市: 深圳 温度: 28℃ 天气: 多云这个输出看起来很简单但它背后验证了整个链路的完整可靠ESP32成功连接了WiFi、完成了TLS证书校验、通过加密通道发起了HTTP请求、拿到了服务端的响应数据、正确解析了JSON格式并提取了关键字段。走完这一遍流程你对嵌入式设备联网取数这件事就有了整体的掌控感。如果这一步有问题请先别急着往下查代码逻辑优先检查串口输出里有没有crt verify failed、connection refused这类关键词这两个是最高频的失败信号。5. 调试排错我在实际开发中撞过的坑5.1 常见错误对照表把这段时间折腾的实际问题整理成一张速查表按错误现象对号入座排查就行报错现象根本原因解决办法crt verify failedCA证书不正确或证书链不完整更新CA证书检查证书字符串是否有多余转义connection refusedAPI域名拼写错误或端口不是443检查host变量确认服务商API地址NoMemory(JSON解析失败)缓冲区设置过小增大StaticJsonDocument的内存大小中文显示乱码串口监视器编码不是UTF-8调整串口监视器右下角编码为UTF-8一直打印“.”WiFi密码错误或路由器信号问题确认SSID与密码检查2.4GHz频段是否开启编译报client is not a member of WiFiClientSecure库版本过低或未引入正确头文件确认安装的ESP32核心库版本在2.0.x以上5.2 最让我头疼的两个问题第一个坑是证书格式问题。我把证书从浏览器导出后直接粘贴到代码里。Arduino IDE在编译时会自动处理字符串看起来没问题但运行到TLS握手阶段就报证书校验失败。排查了很久发现证书内容中间有Windows环境特有的换行符\r\n在某些底层库的处理流程中会破坏证书字符串的完整性。解决办法很简单把证书写到单独的头文件里手动确保它是Unix风格的标准换行。第二个坑是断电重连问题。如果ESP32上电时路由器还没有完全就绪WiFi库的自动重连机制在某些版本下会失效表现为一直打印点号。解决方案是在while (WiFi.status() ! WL_CONNECTED)循环里加一个超时计数器比如超过20秒就重启ESP.restart()。这种场景在Demo中不常发生但一旦你用电池供电做离线项目这是个必现的问题。5.3 排查HTTPS问题的万能思路在嵌入式设备上排查HTTPS问题遵循“从底层往上排查”的原则会节省大量时间。先看TCP层是否连通再检查TLS握手是否成功最后才看HTTP应用层。如果每次都从HTTP层的报错信息开始猜很容易陷入证书或URL这类次级问题的泥潭。具体操作时可以先写一个最简的测试代码只建立TCP连接并发送一个HTTP请求完全不涉及TLS证书校验。确认TCP通了之后再逐步添加TLS证书校验逻辑缩小问题范围。这个思路听起来朴素但实际调试效率非常高。6. 进一步扩展从天气DEMO到实用设备跑通这个DEMO之后只把它停留在读取天气的层面就太可惜了。实际上这套代码的核心骨架——WiFi连接、HTTPS请求、JSON解析——是所有IoT联网应用的基础设施。你可以把天气数据和硬件外设联动起来做成一个桌面气象站在代码里加入温湿度传感器DHT11或BME280的数据读取把室内测量值和云端天气数据同时显示在一块OLED屏幕上。这种软硬结合的小项目会带来一个质变你不再是单纯接收网络数据而是把两种数据源融合进同一个决策逻辑中。比如说当云端预报下雨且室内湿度超过75%时自动打开继电器控制的除湿机——这种场景才是物联网真正有价值的应用。另外一个扩展方向是把数据上报方向调转让ESP32把传感器数据通过HTTPS POST到云平台。心知天气、各种时序数据库平台都提供了HTTP API代码改造只需要把GET请求换成POST请求并在请求体中填入JSON格式的数据。这会让你理解嵌入式设备成为“数据生产者”而不是单纯的“消费者”这种角色的转换对职业发展也有实际帮助。还有一个很经典的扩展思路是OTA远程升级。既然ESP32能通过HTTPS从服务器下载数据那同样可以通过HTTPS下载固件包并写入新的Flash分区。配合一个MQTT或HTTP心跳机制就能实现设备的远程固件更新完全不用再插USB线。把这个功能加进去之后你的设备才算真正有了“产品形态”。最后提醒一句如果你准备把网络请求的间隔调得非常短比如每秒请求一次要留意心知天气这些免费套餐有每日调用次数限制超限后接口会返回错误码。调试时最好加一个手动触发按钮只在按键时请求一次这样既不浪费额度也能随时验证功能。我在做Demo调试时就经常开着自动循环字面意义上“烧掉”了当天的配额第二天怎么调都不出数据排查了半天才发现是这个问题。本文还有配套的精品资源点击获取