The Direct Answer: Sizing and Parsing JsonArray in ArduinoJson
To successfully use an arduino json jsonarray in modern sketches, you must understand that ArduinoJson v7 shifted from static document types to a unified JsonDocument that allocates dynamically. However, to prevent heap fragmentation on memory-constrained microcontrollers, you should still pre-calculate your payload and use the reserve() method. The code and tables in this guide target the ESP32 DevKit V1 (30-pin variant, ESP32-WROOM-32E module) running Arduino core v3.x.
A JsonArray is not a standalone container; it is a reference to a sequence of nodes inside a JsonDocument. If you attempt to create an array without a parent document, or if you under-allocate the document's memory pool, you will hit null references or crash your ESP32's MQTT stack. Below, we break down the exact hardware, memory math, and C++ implementation required to build a robust sensor array node.
Hardware & Pin Mapping for the Sensor Array Node
For this build, we are creating a multi-sensor node that reads three analog light sensors (LDRs) and packages their values into a JSON array for serial debugging and eventual MQTT transmission. We use the ESP32's ADC1 pins because they remain stable even when WiFi is active (unlike ADC2).
Parts List
- MCU: ESP32 DevKit V1 (30-pin, ESP32-WROOM-32E)
- Sensors: 3x GL5528 Photoresistors (LDRs)
- Resistors: 3x 10kΩ pull-down resistors (1/4W, 1% tolerance)
- Prototyping: Half-size breadboard, 22 AWG solid copper jumper wires
Pin Mapping Table
| Component | ESP32 Pin (DevKit V1) | GPIO Number | Notes |
|---|---|---|---|
| LDR 1 (Signal) | VP / GPIO 36 | 36 | ADC1_CH0. Input only, no internal pull-up. |
| LDR 2 (Signal) | VN / GPIO 39 | 39 | ADC1_CH3. Input only. |
| LDR 3 (Signal) | GPIO 34 | 34 | ADC1_CH6. Input only. |
| LDR VCC (All) | 3V3 | - | Do not use 5V; ESP32 ADC max input is ~3.1V. |
| LDR GND (All) | GND | - | Connect via 10kΩ pull-down resistors. |
Memory Budgeting: ArduinoJson Capacity Table
While ArduinoJson v7 handles dynamic resizing, relying on automatic reallocation inside a loop() function causes severe heap fragmentation. On an ESP32, this eventually starves the WiFi stack, causing WiFi.disconnect() events. You must estimate your payload and use doc.reserve(). Use this table to budget your RAM and serialized MQTT buffer sizes.
| Array Payload Type | Element Count | Est. RAM Nodes (v7) | Max Serialized Bytes | Safe MQTT Buffer Size |
|---|---|---|---|---|
| Array of 12-bit Integers (ADC) | 3 | ~96 bytes | 24 bytes | 64 bytes |
| Array of Floats (Temperature) | 5 | ~160 bytes | 45 bytes | 128 bytes |
| Array of Strings (MAC Addresses) | 4 (17 chars each) | ~256 bytes | 88 bytes | 192 bytes |
| Nested Objects in Array (Sensor Data) | 3 objects (2 keys each) | ~384 bytes | 140 bytes | 256 bytes |
Note: RAM node estimates include the array overhead plus the individual element nodes. Always add a 20% margin for string null-terminators and JSON syntax characters (brackets, commas). For deeper math on node sizing, refer to the official ArduinoJson capacity documentation.
Complete Code: Generating and Parsing JSON Arrays
This sketch reads the three LDRs, builds a JsonArray, serializes it to a string, and then immediately parses a hardcoded JSON array to demonstrate bidirectional handling. It includes explicit error handling for deserialization failures.
#include <ArduinoJson.h>
// --- Pin Definitions ---
const int LDR_PINS[3] = {36, 39, 34};
const int ADC_RESOLUTION = 4095; // ESP32 default 12-bit
void setup() {
Serial.begin(115200);
delay(1000); // Allow serial monitor to connect
// Analog pins on ESP32 default to input, but explicit declaration is good practice
for(int i=0; i<3; i++) {
pinMode(LDR_PINS[i], INPUT);
}
Serial.println("ESP32 JsonArray Node Initialized.");
}
void loop() {
// ==========================================
// PART 1: Generating a JsonArray
// ==========================================
JsonDocument docOut;
// Reserve memory to prevent heap fragmentation (3 integers + array overhead)
// JSON_ARRAY_SIZE(3) is roughly 96 bytes in v7
docOut.reserve(JSON_ARRAY_SIZE(3));
JsonArray sensorArray = docOut.to<JsonArray>();
for(int i=0; i<3; i++) {
int rawADC = analogRead(LDR_PINS[i]);
sensorArray.add(rawADC);
}
String outputString;
serializeJson(docOut, outputString);
Serial.print("Generated Array: ");
Serial.println(outputString);
// ==========================================
// PART 2: Parsing a JsonArray
// ==========================================
String incomingPayload = "[{\"id\":1,\"val\":45.2},{\"id\":2,\"val\":88.9}]";
JsonDocument docIn;
DeserializationError error = deserializeJson(docIn, incomingPayload);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
delay(5000);
return;
}
// Extract the root as a JsonArray
JsonArray parsedArray = docIn.as<JsonArray>();
if (parsedArray.isNull()) {
Serial.println("Error: Root is not a valid JsonArray.");
} else {
Serial.println("Parsing successful. Iterating elements:");
for (JsonObject obj : parsedArray) {
int id = obj["id"] | 0; // Default to 0 if missing
float val = obj["val"] | 0.0f;
Serial.printf(" -> ID: %d, Value: %.2f\n", id, val);
}
}
delay(3000);
}
Debugging: Exact Error Strings and Null Arrays
When working with the arduino json jsonarray structure, you will inevitably encounter memory or type-mismatch errors. Here is how to diagnose the two most common failure modes on the bench.
Error 1: DeserializationError::NoMemory
The Symptom: Your serial monitor prints deserializeJson() failed: NoMemory when parsing an incoming MQTT payload.
Ranked Causes:
- Capacity Exhaustion (v6 legacy or fixed buffers): If you are passing a fixed-size buffer to
deserializeJsonand the incoming string exceeds it. - Heap Starvation: The ESP32's heap is too fragmented to allocate the dynamic pool required by the
JsonDocument. This happens when you create and destroy large JSON objects insideloop()without usingreserve()or reusing a global document. - Runaway Nested Arrays: The incoming payload contains deeply nested arrays that exceed the default nesting limit (usually 10 levels deep).
Error 2: JsonArray is null
The Symptom: deserializeJson reports Ok, but calling doc["data"].as<JsonArray>() returns a null array, and your for loop skips entirely.
- Check the Root Brackets: Is the incoming string actually an array (
[ ... ])? If the payload is an object ({ "data": [ ... ] }), you must extract the object key first:doc["data"].as<JsonArray>(). If you callas<JsonArray>()on the root document when the root is an object, it returns null. - Check Key Spelling and Case: JSON keys are strictly case-sensitive.
doc["Data"]will fail if the payload contains"data". Print the raw string before parsing to verify the exact key names. - Check Iterator Invalidation: Are you adding or removing elements from the
JsonArraywhile iterating over it with a range-basedforloop? This invalidates the internal pointers. Always build a new array or use index-basedfor(int i=0; i<arr.size(); i++)loops if you must modify the array on the fly.
Extending the Build: MQTT and Dynamic Payloads
Once you have the serial output working, the next logical step is pushing this JsonArray to a broker like Mosquitto or AWS IoT.
How to Extend (Add MQTT)
To send this array over the network, integrate the PubSubClient library. The critical trap here is the MQTT buffer size. By default, PubSubClient limits payloads to 256 bytes. If your JsonArray of nested objects exceeds this, client.publish() will silently fail and return false.
The Fix: Add #define MQTT_MAX_PACKET_SIZE 512 at the very top of your sketch, before including <PubSubClient.h>. Then, serialize directly to the client stream to save RAM:
// Stream serialization saves creating an intermediate String object
client.beginPublish("sensors/light", measureJson(docOut), false);
serializeJson(docOut, client);
client.endPublish();
How to Simplify (Drop the Math)
If you are building a low-frequency logger (e.g., reading sensors once every 60 seconds and deep sleeping), heap fragmentation is no longer a threat because the ESP32 reboots or clears the heap entirely between cycles. In this scenario, you can safely delete the docOut.reserve() line. Let ArduinoJson v7 handle the dynamic allocation automatically. This simplifies your code and removes the need to maintain the capacity budgeting table as your payload schema evolves.
For more details on ESP32 ADC behavior and why we avoid ADC2 when WiFi is active, consult the Espressif ADC API Reference. Understanding the hardware limitations ensures your JSON data is accurate before it ever hits the parser.






