Obsługa formatu JSON w Arduino
W dzisiejszym artykule dowiemy się w jaki sposób parsować oraz tworzyć własne obiekty JSON w programach Arduino. Pomocna okaże się nam w tym biblioteka o nazwie ArduinoJson, której nazwa może być nieco myląca, gdyż sprawdzi się ona nie tylko w połączeniu z płytkami Arduino, ale także ESP8266, Teensy, a nawet w programie kompilowanym na komputer klasy PC.
Instalacja biblioteki
Bibliotekę możemy pobrać za pomocą Menedżera płytek w Arduino IDE. Wystarczy, że wpiszemy w wyszukiwarce nazwę ArduinoJson. Należy jednak tutaj zaznaczyć, że zanim wciśniemy przycisk Instaluj musimy sprawdzić czy wersja, którą pobieramy jest stabilna. W momencie pisania artykułu ostatnia stabilna wersja to 5.13.1. Jednak jeżeli nie zmienimy wersji to menedżer domyślnie zainstaluje nam wersję 6.4.0-beta.
Kiedy już zainstalujemy bibliotekę możemy zacząć z niej korzystać dołączając do naszego programu plik nagłówkowy ArduinoJson.h
#include <ArduinoJson.h>
Alokacja pamięci
Aby móc tworzyć nowe obiekty lub parsować ciągi znaków musimy zaalokować odpowiednią ilość pamięci. W tym celu posłużymy się klasą StaticJsonBuffer lub DynamicJsonBuffer. Zanim jednak wybierzemy, którą chcemu użyć musimy wiedzieć czym się różnią. Mówiąc najprościej bufor statyczny alokowany jest na stosie, a dynamiczny na stercie. O różnicy pomiędzy stosem a stertą można poczytać tutaj. W skrócie mając do dyspozycji niewiele RAMu tak jak w Arduino lepiej wykorzystać bufor statyczny.
Pamięć zaalokować możemy w ten sposób:
StaticJsonBuffer<JSON_BUFFER_CAPACITY> jsonBuffer;
Teraz wykorzystując ten bufor możemy utworzyć lub sparsować obiekt:
Parsowanie:
JsonObject& root = jsonBuffer.parseObject(jsonString);
Tworzenie obiektu:
JsonObject& root = jsonBuffer.createObject();
Skąd jednak wiedzieć jaki rozmiar buforu? Wystarczy, że wiemy jakie obiekty chcemy parsować/tworzyć. Twórca biblioteki udostępnił nam specjalny kalkulator pod adresem https://arduinojson.org/v5/assistant/. Wystarczy, że wkleimy tam obiekt, który chcemy przetwarzać i voila! W wyniku dostajemy rozmiar buforu oraz przykładowe kody :)
Poza wyrażeniem, które możemy podstawić pod stałą reprezentującą rozmiar buforu widizmy też jaki rozmiar będzie miał bufor dla podanych danych na różnych platformach.
Parsowanie obiektów
W moim przykłądowym kodzie pokażę jak odczytać, a następnie sparsować string przez port szeregowy:
#include <ArduinoJson.h> #define JSON_BUFFER_CAPACITY 2 * JSON_OBJECT_SIZE(3) + 40; #define MAX_COMMAND_SIZE 200 #define TIMEOUT 100 void setup() { Serial.begin(9600); } void loop() { StaticJsonBuffer<JSON_BUFFER_CAPACITY> jsonBuffer; unsigned long startTime = millis(); char command[MAX_COMMAND_SIZE] = ""; char temp; int charPosition = 0; bool gotLine = false; while(millis() - startTime <= TIMEOUT) { while(Serial.available() > 0 && !gotLine) { temp = Serial.read(); startTime = millis(); if(temp == '\r' || temp == '\n') { gotLine = true; } command[charPosition++] = temp; } } if (gotLine) { JsonObject& root = jsonBuffer.parseObject(command); gotLine = false; processJsonCommand(root); } } void processJsonCommand(JsonObject& jsonRoot) { // Tu możemy przetworzyć sparsowany obiekt }
Wewnątrz funkcji processJsonCommand mamy już gotowy sparsowany obiekt, który odebraliśmy przez port szeregowy (możemy do niego podłączyć np. moduł bluetooth HC-05).
Przykładowo, aby odczytać wartość pola o nazwie name należy napisać taki oto kod:
const char* name = root["name"];
Wystarczy, że przypiszemy wartość do zmiennej odpowiedniego typu i już. Biblioteka zrobi za nas całą robotę ;)
Tworzenie obiektów
Jeżeli chcemy utworzyć obiekt/tablicę należy wywołąć metodę createObject lub createArray na obiekcie bufora.
JsonObject& root = jsonBuffer.createObject();
Aby przypisać dane do odpowiedniego pola wystarczy, że odwołamy się do niego jak do tablicy asocjacyjnej:
root["name"] = "Wartosc pola name"; root["id"] = 10;
Jeżeli chcemy dodać do naszego obiektu zagnieżdżenie wystarczy, że wywołamy na nim metodę createNestedObject lub createNestedArray:
JsonObject& data = root.createNestedObject("data"); data["r"] = 120; data["g"] = 50; data["b"] = 255;
Tym sposobem stworzyliśmy obiekt JSON o takiej strukturze:
{ "name": "Wartosc pola name", "id": 10, "data": { "r": 120, "g": 50, "b": 255 } }
Teraz możemy wypisać go na port szeregowy jedną z 2 metod:
root.printTo(Serial); // Wypisywanie stringu JSON bez wcięć i w jednej linii root.prettyPrintTo(Serial); // Wypisywanie ładnie sformatowanego stringu JSON (z wcięciami i nowymi liniami)
Ograniczenia
Niezależnie czy używamy statycznego, czy dynamicznego buforu JSON należy pamiętać, że nie powinniśmy współdzielić go pomiędzy różnymi obiektami. Może to spowodować niekontrolowane zachowanie programu w przypadku skończenia się buforu. Za każdym razem, kiedy musimy stworzyć nowy JSON należy zaalokować nowy bufor, a już na pewno wystrzegać się deklarowania bufru jako globalnej zmiennej.
Należy też pamiętać, że pomimo wygody formatu JSON, nie zawsze będzie on dobrym wyborem. Szczególnie w przypadku Arduino Uno, które ma tylko 2 kb pamięci RAM. Jeżeli będziemy przetwarzali małe dane to jak najbardziej możemy sobie ułtawić życie wykorzystaniem opisanej biblioteki. W przeciwnym wypadku powinniśmy rozważyć wprowadzenie autorskiego, minimalistycznego formatu lub zmianę procesora na jakiś z większą ilością pamięci (np. Teensy lub któryś z rodziny ESP).
ok to jak to sparsowac?
{
„result” : [
{
„LastUpdate” : „2019-01-08 12:42:01”,
„Name” : „IloscParamDoPrzetw”,
„Type” : „0”,
„Value” : „0”,
„idx” : „7”
}
],
„status” : „OK”,
„title” : „GetUserVariable”
}
Polecam skorzystać z asystenta: https://arduinojson.org/v5/assistant/
Mniej więcej powinien Cię nakierować na to jak parsować te dane.