13. ESP32-HTTPClient(Arduino): 从基础GET到JSON交互实战

📅 发布时间:2026/7/5 10:07:54 👁️ 浏览次数:
13. ESP32-HTTPClient(Arduino): 从基础GET到JSON交互实战
1. 从零开始为什么ESP32需要HTTPClient如果你玩过ESP32肯定知道它最厉害的本事就是能联网。不管是做个天气站、智能开关还是把传感器数据传到云端都离不开网络通信。而网络通信里HTTP协议又是最常用、最通用的“语言”。想象一下你的ESP32就像一个迷你浏览器它需要从网上的服务器比如一个提供天气信息的网站API获取数据或者把自己的数据比如房间温湿度提交给服务器。这个“获取”和“提交”的过程就是通过HTTP请求完成的。在Arduino框架下ESP32官方提供了一个超级好用的库叫做HTTPClient。它把复杂的网络通信细节都封装好了你只需要调用几个简单的函数就能像在电脑上用浏览器访问网页一样让ESP32和互联网上的任何服务“对话”。我刚开始用的时候也觉得挺神奇这么个小玩意儿几行代码就能跟全世界的服务器打交道这才是物联网开发的魅力所在。所以这篇文章就是带你手把手从最基础的HTTP GET请求开始一直深入到如何构造和解析复杂的JSON数据。无论你是想做一个能显示最新头条新闻的电子相框还是一个能向服务器报告状态的智能设备掌握HTTPClient都是你的必修课。别担心跟着我的步骤走你会发现这一切比想象中简单得多。2. 万事开头难搭建你的开发环境工欲善其事必先利其器。在写代码之前我们得先把“战场”布置好。这一步看似简单但却是后续所有操作的基础很多新手卡住就是因为环境没弄对。2.1 安装必需的库HTTPClient和ArduinoJson首先确保你已经在电脑上安装好了Arduino IDE。打开它我们第一步不是写代码而是去“图书馆”借两本重要的“工具书”。点击顶部菜单栏的“工具”-“管理库…”。这会打开库管理器里面汇集了成千上万的第三方库。在搜索框里输入“HTTPClient”。你会发现排在第一位的很可能就是由“espressif”提供的官方库。这个库是ESP32核心框架的一部分通常已经内置了但为了保险起见你可以点一下“安装”确认。它的作用就是帮我们处理所有HTTP协议的底层通信。接着再次搜索“ArduinoJson”。找到由“Benoit Blanchon”开发的这个库点击安装。这个库是我们的“JSON翻译官”服务器返回的复杂JSON数据或者我们要发送的JSON格式数据都得靠它来“编码”和“解码”。我强烈建议安装较新的版本如v6.x或v7.x新版本功能更强大API也更友好。安装完成后你的Arduino IDE就具备了进行网络HTTP通信和JSON数据处理的所有能力。这两个库是我们今天实战的绝对核心。2.2 连接Wi-Fi让ESP32“上网”HTTPClient再厉害也得在联网的基础上才能工作。这就好比你的手机功能再强没连上Wi-Fi或移动网络也上不了网。所以在发起任何HTTP请求之前我们必须先让ESP32连接到你的本地路由器。这里我们需要用到ESP32的WiFi库。下面是一个最经典的连接模板代码我几乎在每个网络项目里都会用上它。你需要修改的只有两个地方你的Wi-Fi名字SSID和密码。#include WiFi.h // 替换成你家的Wi-Fi信息 const char* ssid Your_WiFi_Name; const char* password Your_WiFi_Password; void setup() { Serial.begin(115200); // 启动串口监视器方便看调试信息 delay(1000); Serial.println(); Serial.print(正在连接Wi-Fi: ); Serial.println(ssid); WiFi.begin(ssid, password); // 开始连接 // 等待连接成功这个循环会一直检查连接状态 while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } // 连接成功后的提示 Serial.println(); Serial.println(Wi-Fi连接成功); Serial.print(本地IP地址: ); Serial.println(WiFi.localIP()); // 打印ESP32获取到的IP地址 } void loop() { // 主循环暂时空着 }把这段代码上传到你的ESP32然后打开串口监视器波特率设为115200。如果一切正常你会先看到一串“.....”最后显示“Wi-Fi连接成功”以及一个IP地址。看到这个恭喜你你的ESP32已经成功“冲浪”了这是所有后续操作的前提务必确保这一步成功。3. 初试锋芒发起你的第一个GET请求好了热身完毕现在进入正题。GET请求是最简单、最常用的HTTP方法它的作用就是“获取”。比如从服务器读取一篇文章、获取最新的股票价格、查询天气预报等等。我们来实战一下从一个免费的公共测试API获取数据。3.1 GET请求的完整四步曲发起一个GET请求可以分解为四个清晰的步骤我把它叫做“四步曲”。我们用一个获取随机用户信息的公开APIhttps://randomuser.me/api/来举例。#include WiFi.h #include HTTPClient.h // 引入HTTPClient库 // Wi-Fi信息同上此处省略 const char* ssid Your_WiFi_Name; const char* password Your_WiFi_Password; // 要请求的服务器地址 const char* serverUrl https://randomuser.me/api/; void setup() { Serial.begin(115200); // 连接Wi-Fi代码同上此处省略 // ... [Wi-Fi连接代码] // **第一步创建HTTPClient对象** HTTPClient http; // **第二步指定目标URL开始连接** Serial.print(正在请求URL: ); Serial.println(serverUrl); http.begin(serverUrl); // 这里就是告诉http对象我们要和哪个服务器对话 // **第三步发送GET请求并获取响应代码** int httpCode http.GET(); // 关键的一步发出GET指令 // **第四步处理服务器的回应** if (httpCode 0) { // httpCode 就是HTTP状态码200表示成功404表示未找到等等 Serial.printf(HTTP响应代码: %d\n, httpCode); if (httpCode HTTP_CODE_OK) { // 等同于 httpCode 200 // 获取服务器返回的响应体就是数据内容 String payload http.getString(); Serial.println(服务器返回的数据); Serial.println(payload); // 你会看到一串复杂的JSON文本 } } else { // 如果httpCode 0说明请求本身失败了比如网络断开、域名解析失败 Serial.printf(GET请求失败错误: %s\n, http.errorToString(httpCode).c_str()); } // **最后务必结束连接释放资源** http.end(); Serial.println(连接已关闭); } void loop() { // 空循环 }上传这段代码并运行如果网络通畅你会在串口监视器里看到一大堆JSON格式的文本里面包含了随机生成的用户信息如姓名、邮箱、住址等。虽然现在看起来是一团乱码但这证明你的ESP32已经成功地与互联网服务器进行了一次对话3.2 理解关键细节与常见“坑点”第一次成功固然兴奋但想玩得转还得理解背后的细节。我踩过不少坑这里分享给你。HTTP状态码是“晴雨表”http.GET()的返回值httpCode至关重要。200OK是成功。常见的还有404未找到、500服务器内部错误。一定要检查这个码而不是直接去读数据。http.getString()的局限这个方法会把整个响应体读成一个String对象非常方便。但是如果服务器返回的数据量非常大比如几MB可能会撑爆ESP32的内存。对于大数据可以使用http.getStream()来流式读取像接水管一样一点一点处理。资源释放不能忘http.end()这行代码就像你打完电话要挂断一样重要。它负责关闭TCP连接释放网络资源。如果忘了写多次请求后可能会导致内存泄漏或连接数耗尽。好习惯是begin()和end()成对出现。HTTPS请求现在的API大多使用HTTPS加密的HTTP。对于简单的请求HTTPClient库可以处理。但如果遇到证书验证问题你可能需要调用http.begin(serverUrl, rootCA)来指定根证书或者使用http.setInsecure()跳过证书验证仅用于测试生产环境不安全。4. 进阶交互构造并发送POST请求与JSONGET是从服务器“拿”数据而POST是向服务器“送”数据。这在物联网中极其常见比如你的温湿度传感器需要定时把数据上报到云端数据库。而且现在几乎所有的Web API都使用JSON格式来交换数据因为它结构清晰、易读易解析。4.1 构建一个JSON请求体假设我们要创建一个智能家居设备需要向服务器https://api.your-iot-platform.com/data上报当前的温度和湿度。我们要发送的数据格式可能是这样的JSON{ device_id: esp32_room_01, temperature: 25.6, humidity: 60.2, timestamp: 1234567890 }在ESP32上我们借助之前安装的ArduinoJson库来轻松生成这个字符串。#include ArduinoJson.h // 引入JSON库 void buildJsonPayload() { // 1. 创建一个JsonDocument对象200是预估的JSON字符串大小字节 // 对于复杂JSON这个值要估大一些否则会序列化失败。 StaticJsonDocument200 jsonDoc; // 2. 向这个文档对象中添加键值对就像操作一个字典 jsonDoc[device_id] esp32_room_01; jsonDoc[temperature] 25.6; jsonDoc[humidity] 60.2; jsonDoc[timestamp] 1234567890; // 3. 将JsonDocument对象序列化转换成一个String字符串 String requestBody; serializeJson(jsonDoc, requestBody); // 4. 看看我们构建的JSON字符串长什么样 Serial.print(构建的JSON请求体); Serial.println(requestBody); // 输出{device_id:esp32_room_01,temperature:25.6,humidity:60.2,timestamp:1234567890} }这个过程就像是在填写一张电子表格JsonDocument填好后用一个工具serializeJson把它打印成标准格式的文本requestBody准备寄出去。4.2 发送POST请求并携带JSON数据有了JSON字符串我们就可以发起POST请求了。和GET相比POST需要多设置一个请求头Header告诉服务器“我发给你的数据是JSON格式的哦”。#include WiFi.h #include HTTPClient.h #include ArduinoJson.h const char* ssid Your_WiFi_Name; const char* password Your_WiFi_Password; const char* serverUrl https://api.your-iot-platform.com/data; // 替换为你的真实API地址 void sendSensorData() { if (WiFi.status() WL_CONNECTED) { // 确保Wi-Fi还连着 HTTPClient http; http.begin(serverUrl); // 第一步指定目标 // **关键步骤添加请求头** http.addHeader(Content-Type, application/json); // 这个头信息至关重要它明确告诉服务器请求体的内容类型是JSON。 // 构建JSON请求体使用上一节的函数 StaticJsonDocument200 jsonDoc; jsonDoc[device_id] esp32_room_01; jsonDoc[temperature] 25.6; jsonDoc[humidity] 60.2; String requestBody; serializeJson(jsonDoc, requestBody); Serial.println(正在发送POST请求...); // 发送POST请求并将JSON字符串作为请求体发送出去 int httpResponseCode http.POST(requestBody); if (httpResponseCode 0) { Serial.printf(HTTP响应代码: %d\n, httpResponseCode); String response http.getString(); Serial.print(服务器响应: ); Serial.println(response); // 通常服务器成功处理后会返回一个确认信息比如 {status: success, id: 12345} } else { Serial.printf(POST请求失败错误: %s\n, http.errorToString(httpResponseCode).c_str()); } http.end(); // 关闭连接 } else { Serial.println(Wi-Fi已断开无法发送数据); } } void setup() { Serial.begin(115200); // 连接Wi-Fi... // 连接成功后调用发送函数 sendSensorData(); } void loop() { // 可以在这里定时发送数据例如每10分钟发送一次 // delay(600000); // 10分钟 600000毫秒 // sendSensorData(); }这段代码模拟了一个物联网设备上报数据的完整流程。注意http.POST(requestBody)这一行它把我们的JSON字符串requestBody作为参数传了进去库会自动处理数据发送。5. 高手过招解析复杂的JSON响应发送数据只是成功了一半我们往往还需要处理服务器返回的响应。服务器通常会返回一个JSON告诉我们操作是否成功或者返回我们查询的数据。比如我们上报数据后服务器可能返回{success: true, message: Data received, data_id: 789}。我们需要从中提取出data_id等信息。5.1 使用ArduinoJson解析响应数据接上面的例子假设服务器对我们的POST请求返回了上面的JSON。我们来解析它// ... [接上一段代码在收到服务器响应 response 之后] if (httpResponseCode HTTP_CODE_OK) { // 或者 httpResponseCode 200 String response http.getString(); // 1. 准备一个用于解析的JsonDocument大小要足够容纳响应JSON StaticJsonDocument256 responseDoc; // 根据响应大小调整这里给256字节 // 2. 反序列化将JSON字符串解析到responseDoc对象中 DeserializationError error deserializeJson(responseDoc, response); // 3. 检查解析是否成功 if (error) { Serial.print(JSON解析失败: ); Serial.println(error.c_str()); return; // 解析失败就退出 } // 4. 从解析后的对象中提取数据 bool success responseDoc[success]; // 读取布尔值 const char* message responseDoc[message]; // 读取字符串 int dataId responseDoc[data_id]; // 读取整数 // 5. 使用提取到的数据 if (success) { Serial.printf(操作成功消息%s, 数据ID%d\n, message, dataId); // 你可以把dataId保存起来或者做其他逻辑处理 } else { Serial.printf(操作失败%s\n, message); } }解析JSON的核心就是deserializeJson函数它把字符串“翻译”成程序可以方便访问的内存结构。之后你就可以像访问数组或结构体一样用键名如[success]来获取对应的值。这里最容易出错的就是StaticJsonDocument的大小如果预留空间不足解析会失败。对于不确定大小的响应可以先打印出来看看或者使用DynamicJsonDocument更灵活但稍慢。5.2 处理嵌套JSON和数组真实的API响应往往更复杂。比如一个天气预报API返回的数据{ city: Beijing, forecast: [ {day: Monday, temp_high: 22, temp_low: 12}, {day: Tuesday, temp_high: 24, temp_low: 14} ] }解析这种嵌套了数组的数据就需要用到ArduinoJson提供的数组迭代功能。// 假设 response 是上面的JSON字符串 StaticJsonDocument512 doc; // 复杂JSON需要更大空间 DeserializationError error deserializeJson(doc, response); if (!error) { const char* city doc[city]; Serial.printf(城市%s\n, city); // 获取 forecast 这个数组 JsonArray forecastArray doc[forecast]; // 遍历数组中的每一个元素每一个元素都是一个JsonObject for (JsonObject forecastDay : forecastArray) { const char* day forecastDay[day]; int tempHigh forecastDay[temp_high]; int tempLow forecastDay[temp_low]; Serial.printf( %s: 最高%d°C, 最低%d°C\n, day, tempHigh, tempLow); } }通过doc[forecast]获取到的是一个JsonArray对象然后我们可以用for循环来遍历它。循环中的forecastDay就是数组里的每一个对象可以继续用键名来访问其内部数据。掌握了这个你就能应对绝大多数API返回的JSON数据了。6. 实战优化让你的HTTP通信更健壮到这一步基本的GET/POST和JSON交互你已经会了。但想把代码用于实际项目还需要考虑更多现实问题网络会不稳定、服务器可能会暂时无响应、设备需要长期稳定运行。我分享几个从实际项目中总结出来的优化技巧。6.1 添加超时与重试机制网络请求没有100%的成功率。默认情况下HTTPClient可能会等待很久。我们需要设置超时并在失败时重试。HTTPClient http; http.begin(serverUrl); // 设置连接超时单位毫秒 http.setConnectTimeout(5000); // 5秒连接不上就放弃 // 设置响应超时单位毫秒 http.setTimeout(10000); // 等待服务器响应的最长时间为10秒 // 实现一个简单的重试逻辑 int maxRetries 3; int retryCount 0; int httpCode -1; while (retryCount maxRetries httpCode 0) { Serial.printf(尝试第 %d 次请求...\n, retryCount 1); httpCode http.GET(); // 或 http.POST(payload) if (httpCode 0) { Serial.printf(请求失败错误%s。等待1秒后重试。\n, http.errorToString(httpCode).c_str()); delay(1000); retryCount; } } if (httpCode 0) { // 处理成功响应 } else { Serial.println(多次重试后仍失败请检查网络或服务器。); } http.end();setConnectTimeout和setTimeout是两个非常实用的方法能防止程序因为网络问题而“卡死”。配合重试循环可以大大提高单次请求的可靠性。6.2 连接复用与长连接频繁地建立和断开TCP连接即频繁调用begin()和end()是有开销的。对于需要高频向同一服务器发送请求的场景比如每5秒上报一次传感器数据可以使用连接复用。HTTPClient库在底层支持HTTP/1.1的持久连接Keep-Alive。一个简单的优化方式是不要在每次请求后都立即调用http.end()。你可以将HTTPClient对象声明为全局变量或静态变量在setup()中begin()一次然后在loop()中反复进行GET/POST操作。但要注意如果长时间没有请求连接可能会被服务器断开你需要处理这种断开重连的情况。更常见的做法是对于定时任务每次循环都完整地执行begin()-GET/POST-end()代码结构更清晰而将连接复用交给库和底层协议去优化。6.3 错误处理与日志记录完善的错误处理是项目稳定的基石。不要只处理成功的情况。void makeHttpRequest() { HTTPClient http; if (!http.begin(serverUrl)) { Serial.println(HTTP连接初始化失败URL可能格式错误。); return; } int httpCode http.GET(); if (httpCode 0) { Serial.printf([HTTP] GET... 状态码: %d\n, httpCode); if (httpCode HTTP_CODE_OK) { String payload http.getString(); // 成功处理payload } else { // 服务器返回了错误状态码如404 500 String errorPayload http.getString(); Serial.printf(服务器返回错误内容%s\n, errorPayload.c_str()); } } else { // 网络层错误 Serial.printf([HTTP] GET... 失败错误: %s\n, http.errorToString(httpCode).c_str()); // 可以根据不同的错误码进行不同处理如 // if(httpCode HTTPC_ERROR_CONNECTION_REFUSED) { ... } // if(httpCode HTTPC_ERROR_CONNECTION_TIMEDOUT) { ... } } http.end(); }把http.errorToString(httpCode)返回的错误信息打印出来对于调试网络问题有巨大帮助。同时将重要的操作如连接状态、请求结果、解析的关键数据通过串口打印出来或者写入SPIFFS文件系统形成运行日志便于后期排查问题。把这些技巧融入到你的代码中你的ESP32 HTTP通信程序就会从一个“玩具” demo进化成一个可以应对真实复杂网络环境的“战士”。记住物联网开发不仅仅是让功能跑通更是要保证它在各种意外情况下都能稳定、可靠地运行。多测试多思考边界情况你的项目成功率会高很多。