Arduino a JSON

Zápisník experimentátora

Hierarchy: Internet vecí

JSON je dátový formát, s ktorým sa určite stretnete, ak budete chcieť odoslať údaje do cloudu. Obvykle je vyžadované, aby ste do HTTP požiadavky doplnili údaje práve v tomto formáte. JSON je síce na prvý pohľad jednoduchý a ľahko pochopiteľný textový formát, môže ale na niektorých miestach prekvapiť. V tomto článku sa pozrieme na generovanie a parsovanie tohoto formátu pomocou Arduina.

Príklady v tomto článku budú robené na doske MKR1000. Tam máme dostatok RAM, takže nie je problém s generovaním alebo parsovaním aj väčších údajov. Pokiaľ vystačíte s menšou pamäťou, mali by rovnaké príklady fungovať aj na bežnom Arduine Uno.

JSON

Takto vyzerá typický príklad údajov v tomto formáte. Môže byť pekne formátovaný, alebo nahustený v jednom riadku. Oba príklady sú dátovo rovnocenné. Najprv ukážka formátovaného kódu, ktorý je určený ľudským očiam.

{
  "fld_text": "gps",
  "fld_integer": 1351824120,
  "fld_bool": true,
  "fld_float1": 4.12,
  "fld_float2": 4.123457,
  "fld_array": [
    0,
    1,
    2,
    3,
    4
  ],
  "fld_object": {
    "x": 10,
    "y": 20
  }
}

A ukážka nahusteného kódu, ktorý je určený pre počítačové spracovanie.

{"fld_text":"gps","fld_integer":1351824120,"fld_bool":true,"fld_float1":4.12,"fld_float2":4.123457,"fld_array":[0,1,2,3,4],"fld_object":{"x":10,"y":20}}

Oba kódy v sebe nesú rovnakú informáciu. Na prvý pohľad je vidno, že sa jedná o súbor údajových párov, kde prvá položka obsahuje názov položky a druhá zase hodnotu. Danú dvojicu môže nazývať ako atribút. Preto ľahko uhádnete, že premenná fld_integer = 1351824120 sa vo formáte JSON zapisuje takto.

"fld_integer": 1351824120

V tomto texte nebudeme zabiehať do ďalších podrobností o formáte JSON, tie si môžete pozrieť napríklad na stránke Wikipédie.

Ako vytvárať JSON

V prípade jednoduchšieho obsahu JSON bez problémov vytvoríte aj ručne. Zložitejšie dátové údaje ale môžu predstavovať zdroj chýb a preto je rozumné na to použiť hotovú knižnicu. V prípade Arduina takáto knižnica existuje a volá sa ArduinoJson. Nainštalujete ju pomocou Správcu knižníc.

Príklad údajov v predchádzajúcich odstavcoch bol vytvorený pomocou nasledovného programu. Definovaním StaticJsonBuffer<400> jsonBuffer; si vytvoríme priestor v RAM, ktorý sa bude využívať pri generovaní. V tomto prípade som vyčlenil 400 bajtov pamäte. Aby sme do pamäte mohli zapisovať, potrebujeme koreňový uzol. Ten získame príkazom JsonObject& root = jsonBuffer.createObject();. Obvyklé je mať v JSON ako koreňový uzol objekt, ktorý umožňuje pridávať ľubovoľné množstvo atribútov.

Pridávanie rôznych typov atribútov je jasné zo zdrojového textu. Pozastavím sa iba pri atribútoch typu float. Implicitne je reálne číslo formátované na dve desatinne miesta. Ak potrebujeme viac, musíme to povedať pomocou funkcie double_with_n_digits.

Príkazom JsonArray& data = root.createNestedArray("fld_array"); pridáme vnorené pole a príkazom JsonObject &oin = root.createNestedObject("fld_object"); pridáme vnorený objekt.

Ostáva už iba posledný krok. Premeniť internú dátovú štruktúru na text, ktorý je možné odoslať do cloudu. Príkazom root.printTo(Serial); vytvoríme zhustený text a príkazom root.prettyPrintTo(Serial); vytvoríme pekne formátovaný text.

#include <ArduinoJson.h>

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    // wait serial port initialization
  }

  Serial.println("MKR1000 JSON Generator");
  Serial.println();

  StaticJsonBuffer<400> jsonBuffer;

  JsonObject& root = jsonBuffer.createObject();

  root["fld_text"] = "gps";
  root["fld_integer"] = 1351824120;
  root["fld_bool"] = true;
  root["fld_float1"] = 4.12345678;
  root["fld_float2"] = double_with_n_digits(4.12345678, 6);
  root["fld_null"] = NULL;

  JsonArray& data = root.createNestedArray("fld_array");
  for (int i = 0; i < 5; i++)
    data.add(i);

  JsonObject &oin = root.createNestedObject("fld_object");
  oin["x"] = 10;
  oin["y"] = 20;

  root.printTo(Serial);

  Serial.println();
  Serial.println("-----");

  root.prettyPrintTo(Serial);
}

Ako parsovať JSON

Parsovanie JSON je opačný proces ako v predchádzajúcom odstavci. Z textu získame priamo premenné v C++, ktoré budú obsahovať dané hodnoty. Predchádzajúci JSON text nám poslúži na parsovanie. Obsah premenných naplníme pomocou nasledovného programu.

Postupujeme presne opačne. V premennej json mám obsah predchádzajúceho výstupu. Pretože sa v texte nachádzajú úvodzovky a nové riadky, musím všetko prepísať do takej podoby, aby zodpovedala požiadavkám na reťazce v C++. Príkazom JsonObject& root = jsonBuffer.parseObject(json); rozparsujem reťazec. Je vhodné aj skontrolovať, či sa parsovanie podarilo.

A potom už len priradíme obsah do premenných. Napríklad long fld_integer = root["fld_integer"]; nám naplní do premennej fld_integer hodnozu z JSON reťazca. Pomocou makier DUMP sa môžeme presvedčiť, že to funguje naozaj dobre. Mierne komplikovanejšie to je pri poliach. Ale aj tam máme nástroje, ako zistiť počet prvkov v poli a do lokálnej premennej si to priradiť iba vtedy, keď máme vyhradený dostatok miesta.

Posledné riadky ukazujú, ako je možné naplniť položky štruktúry z vnoreného objektu.

#include <ArduinoJson.h>
#include "dump.h"

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    // wait serial port initialization
  }

  Serial.println("MKR1000 JSON Parser");
  Serial.println();

  StaticJsonBuffer<400> jsonBuffer;

  char json[] =
    "{\n"
    "  \"fld_text\": \"gps\",\n"
    "  \"fld_integer\": 1351824120,\n"
    "  \"fld_bool\": true,\n"
    "  \"fld_float1\": 4.12,\n"
    "  \"fld_float2\": 4.123457,\n"
    "  \"fld_null\": 0,\n"
    "  \"fld_array\": [\n"
    "    0,\n"
    "    1,\n"
    "    2,\n"
    "    3,\n"
    "    4\n"
    "  ],\n"
    "  \"fld_object\": {\n"
    "    \"x\": 10,\n"
    "    \"y\": 20\n"
    "  }\n"
    "}";

  Serial.println(json);
  DUMP(json)

  JsonObject& root = jsonBuffer.parseObject(json);

  // Test if parsing succeeds.
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }

  const char* fld_text = root["fld_text"];
  long fld_integer = root["fld_integer"];
  bool fld_bool = root["fld_bool"];
  float fld_float = root["fld_float2"];
  DUMPVAL(fld_text)
  DUMPVAL(fld_integer)
  DUMPVAL(fld_bool)
  DUMPVAL(fld_float)

  JsonArray& fld_array = root["fld_array"];
  int a[10];
  memset(a,-1,sizeof(a));
  int poc = fld_array.size();
  if(poc<10)
    fld_array.copyTo(a);
  DUMP(a); 

  struct fobj {
    int x;
    int y;
  };
  fobj fld_object;
  fld_object.x = root["fld_object"]["x"];
  fld_object.y = root["fld_object"]["y"];
  DUMP(fld_object)
  DUMPVAL(fld_object.x)
  DUMPVAL(fld_object.y)
}

void loop() {
  // not used in this example
}

Dump premenných na sériový port. O tom ako funngujú makrá v súbore dump.h si môžete prečítať v samostatnom článku.

MKR1000 JSON Parser

{
  "fld_text": "gps",
  "fld_integer": 1351824120,
  "fld_bool": true,
  "fld_float1": 4.12,
  "fld_float2": 4.123457,
  "fld_null": 0,
  "fld_array": [
    0,
    1,
    2,
    3,
    4
  ],
  "fld_object": {
    "x": 10,
    "y": 20
  }
}
Dump: json
20007D44 - 7B 0A 20 20 22 66 6C 64 5F 74 65 78 74 22 3A 20 {.  "fld_text": 
20007D54 - 22 67 70 73 22 2C 0A 20 20 22 66 6C 64 5F 69 6E "gps",.  "fld_in
20007D64 - 74 65 67 65 72 22 3A 20 31 33 35 31 38 32 34 31 teger": 13518241
20007D74 - 32 30 2C 0A 20 20 22 66 6C 64 5F 62 6F 6F 6C 22 20,.  "fld_bool"
20007D84 - 3A 20 74 72 75 65 2C 0A 20 20 22 66 6C 64 5F 66 : true,.  "fld_f
20007D94 - 6C 6F 61 74 31 22 3A 20 34 2E 31 32 2C 0A 20 20 loat1": 4.12,.  
20007DA4 - 22 66 6C 64 5F 66 6C 6F 61 74 32 22 3A 20 34 2E "fld_float2": 4.
20007DB4 - 31 32 33 34 35 37 2C 0A 20 20 22 66 6C 64 5F 6E 123457,.  "fld_n
20007DC4 - 75 6C 6C 22 3A 20 30 2C 0A 20 20 22 66 6C 64 5F ull": 0,.  "fld_
20007DD4 - 61 72 72 61 79 22 3A 20 5B 0A 20 20 20 20 30 2C array": [.    0,
20007DE4 - 0A 20 20 20 20 31 2C 0A 20 20 20 20 32 2C 0A 20 .    1,.    2,. 
20007DF4 - 20 20 20 33 2C 0A 20 20 20 20 34 0A 20 20 5D 2C    3,.    4.  ],
20007E04 - 0A 20 20 22 66 6C 64 5F 6F 62 6A 65 63 74 22 3A .  "fld_object":
20007E14 - 20 7B 0A 20 20 20 20 22 78 22 3A 20 31 30 2C 0A  {.    "x": 10,.
20007E24 - 20 20 20 20 22 79 22 3A 20 32 30 0A 20 20 7D 0A     "y": 20.  }.
20007E34 - 7D 00 }.
Dump: fld_text=gps
20007CE8 - 4D 7D 00 20 M}. 
Dump: fld_integer=1351824120
20007CEC - F8 32 93 50 .2.P
Dump: fld_bool=1
20007CE7 - 01 .
Dump: fld_float=4.12
20007CF0 - 5C F3 83 40 \..@
Dump: a
20007D1C - 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 ................
20007D2C - 04 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF ................
20007D3C - FF FF FF FF FF FF FF FF ........
Dump: fld_object
20007CF4 - 0A 00 00 00 14 00 00 00 ........
Dump: fld_object.x=10
20007CF4 - 0A 00 00 00 ....
Dump: fld_object.y=20
20007CF8 - 14 00 00 00 ....

Zdrojové texty

Zdrojové texty sú na GitHub:


01.12.2016


Menu