Skip to content

akkoyun/JSON

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

6 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

JSON Library v2.0.1

Ultra-low-RAM, zero-copy JSON parser for AVR microcontrollers.

Designed for ATmega devices parsing GSM server responses. A JSON instance occupies 7 bytes of RAM regardless of the payload size β€” no heap allocation, no internal buffer copy, no dynamic memory.


Features

  • Zero-copy β€” stores only a pointer to your buffer; the original string is never duplicated
  • No heap β€” every instance is 7 bytes on the stack; getObject() child instances are also 7 bytes each
  • PROGMEM keys β€” all key lookups use F("key"), keeping key strings in Flash rather than RAM
  • Single-pass validation β€” locate and validate the JSON block in one scan (no double pass, no duplicate CPI instructions on AVR)
  • Top-level key isolation β€” nested objects with identically-named keys never shadow each other
  • GSM prefix handling β€” AT+CMQTTRXPAYLOAD and similar headers before { are skipped automatically
  • Full type coverage β€” int32_t, float, bool, string, nested object, array
  • Pay-as-you-go Flash β€” float library (~1 880 B) is only linked when getFloat() is called

RAM / Flash β€” vs ArduinoJson v7 (ATmega328P)

Scenario Our RAM ArduinoJson RAM Our Flash ArduinoJson Flash
Flat integers 231 B 330 B 2 674 B 8 880 B
Bool + string 247 B 354 B 2 762 B 9 086 B
GSM prefix + string 243 B 342 B 2 908 B 9 070 B
Float + array + typeOf 295 B 388 B 5 308 B 10 060 B
Heavy server config (19 keys + nested) 529 B 740 B 5 710 B 11 590 B

ArduinoJson static RAM shown; at runtime JsonDocument adds ~200–600 B from the heap (not reflected above). This library uses zero heap.


Installation

Copy the JSON folder into your Arduino libraries/ directory, or install via the Arduino Library Manager / PlatformIO registry.

; platformio.ini
lib_deps = akkoyun/JSON

Quick Start

#include <JSON.h>

char buf[] = "{\"Event\":900,\"Temp\":23.75,\"Online\":true}";
JSON json(buf);  // buf must stay alive as long as json is used

json.isValid();                      // true
json.getInt(F("Event"));             // 900
json.getFloat(F("Temp"));            // 23.75
json.getBool(F("Online"));           // 1

API Reference

Construction

JSON json(const char * buffer);

The caller owns buffer and must keep it alive for the lifetime of the JSON object. No copy is made.


Structural

Method Returns Description
isValid() bool true when the JSON block is structurally valid
size() uint16_t Character count from { to } inclusive
hasKey(F("key")) bool true when the key exists as a direct child
typeOf(F("key")) Type Value type (see enum below)

Type enum values

Constant Value Meaning
JSON::NONE 0 Key not found
JSON::INT 1 Integer number
JSON::FLOAT 2 Decimal number
JSON::STRING 3 Quoted string
JSON::BOOL 4 true / false
JSON::NUL 5 null
JSON::OBJECT 6 Nested { }
JSON::ARRAY 7 Array [ ]

Scalar access

int32_t getInt   (F("key"))                        // 0 when absent
int32_t operator[](F("key"))                       // alias for getInt
float   getFloat (F("key"))                        // 0.0 when absent
int8_t  getBool  (F("key"))                        // 1=true  0=false  -1=absent
bool    getString(F("key"), char* buf, uint8_t len) // true on success

getString decodes \\, \", \/, \n, \r, \t escape sequences and always null-terminates buf.


Nested object access

JSON child = json.getObject(F("key"));
JSON child = json.getObject(F("key"), uint8_t index);  // object inside array

The child JSON shares the parent buffer β€” zero additional RAM beyond the 7-byte instance. Returns an invalid JSON (isValid() == false) when the key is absent or the value is not an object. Nesting depth is unlimited.

// {"Pressure":{"Settings":{"P_Min":0.5,"P_Max":9.5}}}
JSON pressure = json.getObject(F("Pressure"));
JSON settings = pressure.getObject(F("Settings"));
float pMin = settings.getFloat(F("P_Min"));  // 0.5

Array access

uint8_t arraySize(F("key"))                                         // 0 when absent
int32_t getInt   (F("key"), uint8_t index)
float   getFloat (F("key"), uint8_t index)
int8_t  getBool  (F("key"), uint8_t index)
bool    getString(F("key"), uint8_t index, char* buf, uint8_t len)
JSON    getObject(F("key"), uint8_t index)

Examples

Basic β€” integer, string, bool

#include <JSON.h>

char buf[] = "{\"Event\":900,\"DeviceID\":\"ABC-001\",\"Online\":true}";
JSON json(buf);

json.getInt(F("Event"));              // 900
json.getBool(F("Online"));            // 1
json.hasKey(F("Foo"));                // 0

char id[8];
json.getString(F("DeviceID"), id, sizeof(id));  // "ABC-001"

Float + type detection

char buf[] = "{\"Temp\":23.75,\"RSSI\":-87,\"Label\":\"OK\"}";
JSON json(buf);

json.typeOf(F("Temp"));   // JSON::FLOAT (2)
json.typeOf(F("RSSI"));   // JSON::INT   (1)
json.typeOf(F("Label"));  // JSON::STRING (3)

json.getFloat(F("Temp")); // 23.75
json.getInt(F("RSSI"));   // -87

Integer and string arrays

char buf[] = "{\"Codes\":[10,20,30],\"Tags\":[\"v1\",\"v2\"]}";
JSON json(buf);

json.arraySize(F("Codes"));       // 3
json.getInt(F("Codes"), 0);       // 10
json.getInt(F("Codes"), 2);       // 30

char tag[4];
json.getString(F("Tags"), 1, tag, sizeof(tag));  // "v2"

Heavy server config with nested object

char buf[] =
    "{\"ServerIP\":\"176.235.232.121\","
    "\"OnInterval\":5,\"VRMS_Min\":192.0,\"VRMS_Max\":253.0,"
    "\"Pressure\":{\"Settings\":{\"P_Min\":0.5,\"P_Max\":9.5}}}";

JSON json(buf);

char ip[20];
json.getString(F("ServerIP"), ip, sizeof(ip));  // "176.235.232.121"
json.getFloat(F("VRMS_Min"));                   // 192.0

JSON settings = json.getObject(F("Pressure")).getObject(F("Settings"));
settings.getFloat(F("P_Min"));   // 0.5
settings.getFloat(F("P_Max"));   // 9.5

GSM AT-command prefix (auto-skipped)

// Module output: +CMQTTRXPAYLOAD: 0,42\r\n{"Temp":23.75}
char buf[] = "+CMQTTRXPAYLOAD: 0,42\r\n{\"Temp\":23.75}";
JSON json(buf);   // prefix before '{' is ignored automatically
json.getFloat(F("Temp"));  // 23.75

Design Notes

Why F("key")?
On AVR, string literals live in RAM by default. F() marks them PROGMEM so they stay in Flash. Every key in this library is read with pgm_read_byte(), keeping your RAM budget for actual data.

Why no StaticJsonDocument equivalent?
This library does not pre-allocate a scratch buffer at all. Parsing is done by scanning the source string in-place. Memory cost is O(1) β€” always 7 bytes per instance, independent of payload size.

Depth of getObject() chains
Each getObject() call returns a 7-byte stack instance. Chains of any depth are safe as long as the parent buffer remains alive.


License

Copyright (C) 2014-2025 Mehmet Gunce Akkoyun. All rights reserved.
Cannot be copied and/or distributed without the express permission of the author.

E-Mail

About

πŸ“Ÿ JSON library for Arduino and embedded C++. Simple and efficient. Light version.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors