⚠️ CAUTION: SLIPPERY FLOOR ⚠️The Arduino Library Manager installs the ArduinoJson version 6 by default.
However, using version 5 is highly recommended because version 6 is still in beta stage.
Open the Arduino Library Manager and make sure that ArduinoJson version 5.13.4 is installed.
Why shouldn’t I use a global
DynamicJsonBuffer are monotonic allocators: they can allocate memory, but they cannot release it.
The advantages are speed and code size; they are ultra-fast and ultra small.
The drawback is that you can only use them once; they are throwaways.
It’s like drawing on a piece of paper: when you want to make a new drawing, you start with a new blank sheet. It would be crazy to erase an old sheet of paper so you can reuse it, right?
Similarly, when you need to use ArduinoJson again, you must create a new
A global variable, by definition, is never destroyed, so a global
JsonBuffer can only be used once (or a very few times) until it’s full and unusable.
That is why you should never use a global
it would be like having a single sheet of paper for your whole life!
Why such a terrible limitation?
ArduinoJson is designed to do one thing and to do it well: the JSON serialization.
So before trying to use a global
JsonBuffer, ask yourself first:
“Am I using ArduinoJson for serialization, or am I pushing it beyond its initial intent?”
In particular, you should not use
JsonArray to store the internal state of your program as this would be terribly inefficient. Instead, write your own data structure and use ArduinoJson only in serialization functions, as shown in What’s the best way to use the library?
The “global JsonObject configuration” anti-pattern
Many projects use a JSON file to store their configuration: the file is read at startup, and the content is kept in memory during the execution of the program.
In that situation, it’s tempting to use a global
JsonObject attached to a global
In theory, it’s OK to use a global read-only
JsonObject, because the
JsonBuffer won’t grow.
But in practice, it’s a huge waste of memory and processor time.
The best way to implement a global configuration object is to use custom data structures as demonstrated in the example JsonConfigFile.ino.
This is a win on four levels:
- Faster code (the object tree is walked only once at boot time)
- Smaller RAM footprint (
JsonVariant… have significant overhead)
- Smaller code
- It decouples the memory representation from the file format, which will be very handy when the file format evolves
Reducing the size of global variables is crucial as they reduce the RAM remaining for the rest. Moreover, the JSON object tree consumes memory even if the program doesn’t use it. For example, if someone adds values in the JSON settings that are not actually used by the program, the memory will be used anyway. One could even get to a situation where the program is unable to run because all the memory will be consumed by the settings.
.data segment anti-pattern
One might think that a legitimate usage of a global buffer is to have a
StaticJsonBuffer in the
.data segment so that the compiler will issue an error if there is not enough memory.
While this seems like a good idea, it wastes a lot of RAM as it reserves memory that is used only a fraction of the time.
Do you still believe that global variables are your only option?
In particular, have a look at the case study named “Configuration in SPIFFS”. It shows how to load and save a complex JSON configuration on an ESP8266.