JSON消息构建
JSON消息构建
Section titled “JSON消息构建”本节介绍使用ArduinoJson库在ESP32上创建和解析JSON消息。通过本节学习,你将能够:
- 安装和配置ArduinoJson库
- 为MQTT发布构建JSON对象
- 解析来自Node-RED或其他MQTT客户端的JSON消息
- 使用ArduinoJson Assistant工具生成正确的代码
- 已完成 ESP32 基础编程环境配置
- 掌握基础草图架构
为什么物联网中使用JSON
Section titled “为什么物联网中使用JSON”JSON(JavaScript对象表示法)是物联网通信的标准数据格式,因为:
- 人类可读:易于检查和调试
- 自描述:字段名说明数据内容
- 语言无关:适用于ESP32、Node-RED、Python、JavaScript等
- 结构灵活:轻松添加或删除字段而不破坏解析器
- 广泛支持:每个MQTT客户端库都处理JSON
示例JSON负载:
{ "device": "ESP32", "temperature": 25.5, "humidity": 65.2, "lux": 312, "rssi": -67}ArduinoJson库
Section titled “ArduinoJson库”ArduinoJson是最流行的Arduino/ESP32 JSON库。关键特性:
- 反序列化:将JSON字符串解析为程序可访问的对象
- 序列化:从程序变量构建JSON字符串
- 内存高效:通过
StaticJsonDocument或DynamicJsonDocument进行可配置分配 - Assistant工具:基于Web的代码生成器,位于 arduinojson.org/v7/assistant/
ArduinoJson v7(当前版本):
| 类 | 描述 |
|---|---|
JsonDocument | JSON文档存储的基类 |
StaticJsonDocument<N> | 固定大小分配(栈)——大小已知时使用 |
DynamicJsonDocument<N> | 堆分配——用于可变大小 |
JsonObject | 命名键值对 |
JsonArray | 值的有序列表 |
第一步:安装ArduinoJson
Section titled “第一步:安装ArduinoJson”PlatformIO(platformio.ini):
lib_deps = bblanchon/ArduinoJson @ ^7.0Arduino IDE:库管理器 → 搜索”ArduinoJson” → 安装
第二步:使用ArduinoJson Assistant
Section titled “第二步:使用ArduinoJson Assistant”ArduinoJson Assistant 为你的特定JSON结构生成正确的代码:
- 访问 https://arduinojson.org/v7/assistant/
- 选择你的开发板:ESP32
- 选择操作:Deserialize(接收)或 Serialize(发送)
- 粘贴示例JSON文档
- Assistant生成所需的确切代码
- 将代码复制到你的草图中
这消除了手动内存计算和语法猜测。
第三步:序列化JSON(ESP32 → MQTT发布)
Section titled “第三步:序列化JSON(ESP32 → MQTT发布)”构建要通过MQTT发布的JSON负载:
#include <ArduinoJson.h>
void sendMQTTValues() { // 计算所需文档大小 // 使用Assistant获取精确值:https://arduinojson.org/v7/assistant/ StaticJsonDocument<200> doc;
// 填充JSON文档 doc["device"] = "ESP32"; doc["temperature"] = 25.5; doc["humidity"] = 65; doc["lux"] = 312; doc["online"] = true;
// 序列化到字符串缓冲区 char buffer[256]; size_t len = serializeJson(doc, buffer);
// 通过MQTT发布 if (mqttClient.publish("esp32/data", buffer, len)) { Serial.print("Published JSON: "); Serial.println(buffer); }}使用真实的传感器数据:
void sendSensorData(float temperature, float humidity, int lux) { StaticJsonDocument<200> doc;
doc["device"] = "sensor-01"; doc["temperature"] = temperature; doc["humidity"] = humidity; doc["lux"] = lux; doc["rssi"] = WiFi.RSSI(); doc["timestamp"] = millis() / 1000; // 运行秒数
char buffer[256]; size_t len = serializeJson(doc, buffer);
mqttClient.publish("esp32/data", buffer, len);}嵌套JSON对象:
void sendComplexData() { StaticJsonDocument<300> doc;
// 设备信息 JsonObject device = doc.createNestedObject("device"); device["id"] = "ESP32-001"; device["firmware"] = "2.1.0";
// 传感器读数 JsonObject sensors = doc.createNestedObject("sensors"); sensors["temperature"] = 25.5; sensors["humidity"] = 65.2;
// 值数组 JsonArray history = doc.createNestedArray("history"); history.add(25.1); history.add(25.3); history.add(25.5);
char buffer[512]; serializeJsonPretty(doc, Serial); // 格式化打印用于调试 size_t len = serializeJson(doc, buffer); mqttClient.publish("esp32/data", buffer, len);}输出:
{ "device": { "id": "ESP32-001", "firmware": "2.1.0" }, "sensors": { "temperature": 25.5, "humidity": 65.2 }, "history": [25.1, 25.3, 25.5]}第四步:反序列化JSON(MQTT接收 → ESP32)
Section titled “第四步:反序列化JSON(MQTT接收 → ESP32)”解析从Node-RED接收的JSON消息:
void mqttCallback(char* topic, byte* payload, unsigned int length) { // 将负载转换为字符串 char json[length + 1]; memcpy(json, payload, length); json[length] = '\0';
Serial.print("Received JSON: "); Serial.println(json);
// 解析JSON StaticJsonDocument<256> doc; DeserializationError error = deserializeJson(doc, json);
if (error) { Serial.print("JSON parse failed: "); Serial.println(error.c_str()); return; }
// 提取值 const char* command = doc["command"]; int value = doc["value"]; bool enabled = doc["enabled"];
Serial.print("Command: "); Serial.println(command ? command : "null"); Serial.print("Value: "); Serial.println(value); Serial.print("Enabled: "); Serial.println(enabled ? "true" : "false");
// 根据解析的数据执行操作 if (strcmp(command, "LED_ON") == 0) { digitalWrite(2, HIGH); } else if (strcmp(command, "LED_OFF") == 0) { digitalWrite(2, LOW); } else if (strcmp(command, "SET_THRESHOLD") == 0) { temperatureThreshold = value; }}第五步:处理JSON数组
Section titled “第五步:处理JSON数组”// 传入的JSON:{"channels": [1, 3, 5], "mode": "auto"}void parseArrayExample(char* json) { StaticJsonDocument<256> doc; DeserializationError error = deserializeJson(doc, json);
if (error) return;
// 访问数组 JsonArray channels = doc["channels"]; const char* mode = doc["mode"];
Serial.print("Mode: "); Serial.println(mode); Serial.print("Active channels: ");
for (int channel : channels) { Serial.print(channel); Serial.print(" "); // 激活每个通道 } Serial.println();}第六步:内存管理
Section titled “第六步:内存管理”最常见的ArduinoJson陷阱是错误的内存分配:
// 太小——会静默溢出StaticJsonDocument<64> doc; // 对大多数负载来说太小doc["temperature"] = 25.5; // 数据可能损坏doc["humidity"] = 65.2; // 溢出!
// 正确——使用Assistant计算StaticJsonDocument<200> doc; // 典型传感器负载的正确大小
// 对于未知大小,使用DynamicJsonDocumentDynamicJsonDocument doc(2048); // 堆分配,2048字节内存估算:
- 每个键:~(键长度 + 1)字节
- 每个字符串值:~(字符串长度 + 1)字节
- 每个数字:8字节
- 每个布尔值:2字节
- JSON开销:~ 30-40字节
使用 ArduinoJson Assistant 精确计算。
- 通过MQTT发布的JSON负载是有效的JSON(用JSON验证器验证)
- Node-RED能解析发布的JSON负载
- ESP32正确解析来自Node-RED的JSON命令
- 数组和嵌套对象处理正常工作
- 内存分配足以容纳最大的预期负载
序列化的JSON被截断或乱码
Section titled “序列化的JSON被截断或乱码”原因:缓冲区大小不足以容纳JSON文档。
解决方案:
- 增加
StaticJsonDocument<N>的大小 - 增加
char buffer[N]的大小(必须大于文档) - 使用
serializeJson(doc, buffer, sizeof(buffer))防止溢出
deserializeJson() 返回”NoMemory”
Section titled “deserializeJson() 返回”NoMemory””原因:JsonDocument 对于传入的JSON太小。
解决方案:
- 使用Assistant计算正确的大小
- 对于可变大小负载,切换到
DynamicJsonDocument - 增加文档大小
编译错误:“JsonDocument is too large for stack”
Section titled “编译错误:“JsonDocument is too large for stack””原因:StaticJsonDocument 在栈上分配;非常大的文档超过栈限制。
解决方案:
- 切换到
DynamicJsonDocument(堆分配) - 通过简化JSON结构减小文档大小
- 通过
build_flags = -DCONFIG_ARDUINO_LOOP_STACK_SIZE=16384增加ESP32栈大小
- 始终检查反序列化错误:
if (deserializeJson(doc, json))捕获格式错误的JSON - 使用Assistant:它消除了内存分配的猜测
- 已知大小优先使用
StaticJsonDocument:更快且避免堆碎片 - 开发期间使用
serializeJsonPretty():可读输出有助于调试 - 在关键路径中避免动态字段名:为键创建字符串比文字慢
- 对复杂结构使用
JsonObject和JsonArray:它们是类型安全且高效的
- ArduinoJson处理JSON序列化(ESP32 → MQTT)和反序列化(MQTT → ESP32)
- ArduinoJson Assistant Web工具为任何JSON结构生成正确的代码
- 内存管理至关重要:使用正确的
JsonDocument大小 - 解析传入消息时始终检查
DeserializationError - 完全支持复杂的嵌套对象和数组
- JSON是本课程中ESP32与Node-RED通信的标准数据格式