bblanchon / ArduinoJson

📟 JSON library for Arduino and embedded C++. Simple and efficient.
https://arduinojson.org
MIT License
6.76k stars 1.12k forks source link

Change code from v6.19 to v7.latest #2142

Open hasenradball opened 3 days ago

hasenradball commented 3 days ago

Hi,

having changed the lib from actual v6.19 to v7.latest and see some points to update. is there a general example how to setup code for Arduino JSON for v7. to be able to do a review with v7 in mind?

hasenradball commented 2 days ago

Hi Benoit,

watched your video, great stuff.

I want to buy the paperback version! Do you have one for me?

What is your suggestion working on the ESP-01? Stay on 6.19.4 or switch to 7?

bblanchon commented 2 days ago

Hi @hasenradball,

I'm overwhelmed these days, so I don't know when I'll be able to work on the paperback version.

For every 32-bit microcontroller, including ESP-01, I strongly recommend switching to ArduinoJson 7.

Best regards, Benoit

hasenradball commented 2 days ago

Maybee one final Question.

With v6 I used this code. 2 Functions for Websocket and Server. There I use json_str as a buffer is there actually a better choice to store the json? Or is it actually needed?

void sendMessageJson(int id, bool state_relais, bool state_tor) {
  char json_str[300];
  // Reserve DynamicJsonDocument doc(500); --> Heap
  // Reserve StaticJsonDocument<500> doc; --> Stack
  StaticJsonDocument<280> doc;

  // Uhrzeit
  char timestrbuffer[6];
  uhr.get_timeformatted(timestrbuffer, 6, "%R");

  // Betriebszeit
  char durationstrbuffer[29];
  snprintf(durationstrbuffer, 29, "%02lld:%02u:%02u", esp_duration.get_hh(), esp_duration.get_mm(), esp_duration.get_ss());

  doc["Version"] = webIf.version;
  doc["Relais"] = state_relais;
  doc["Tor_offen"] = state_tor;
  doc["Temperature"] = sensor_BME280.Data.Messwert2;
  doc["Humidity"] = sensor_BME280.Data.Messwert3;
  doc["Pressure"] = sensor_BME280.Data.Messwert4;
  doc["Time"] = timestrbuffer;
  doc["Duration"] = durationstrbuffer;
  serializeJson(doc, json_str);
  // serializeJson(doc, Serial);
  // DBG__PRINT("\n\tjson_str length:", "");
  // DBG__PRINT(" ", String(measureJson(doc)));
  // DBG__PRINT("\tdoc - Memory usage:", "");
  // DBG__PRINT(" ", String(doc.memoryUsage()));
  if (id >= 0) {
    webSocket.sendTXT(id, json_str, measureJson(doc));
  }
  else {
    webSocket.broadcastTXT(json_str, measureJson(doc));
  }

}

void sendMessageJson_WifiConfig(bool wlan_user_req) {
  // --> Funktion zum Senden der Daten an die Webside
  char json_str[300];

  //const size_t capacity = JSON_OBJECT_SIZE(12);
  // Reserve DynamicJsonDocument doc(500); --> Heap
  // Reserve StaticJsonDocument<500> doc; --> Stack
  StaticJsonDocument<280> doc;

  doc["Version"] = webIf.version;
  doc["WLAN_USER_INPUT_Requested"] = wlan_user_req;
  doc["MAC"] = WiFi.macAddress();
  doc["Hostname"] = wlan.getHostname();

  serializeJson(doc, json_str);
  // serializeJson(doc, Serial);
  // DBG__PRINT("json_str length: ", measureJson(doc));
  // DBG__PRINT(" doc - Memory usage: ", doc.memoryUsage());

  // wichtig! damit Daten nicht aus dem Browser cache kommen
  server.sendHeader(F("Cache-Control"), "no-cache");
  server.send(200, "application/json", json_str);
}
hasenradball commented 1 day ago

I replace the above code by this.

It would be nice if you can give a final confirm ;-)

void sendMessageJson(int id, bool state_relais, bool state_tor) {
  char json_str[JSON_MAX_OUTPUT_SIZE];
  // v6.x
  // Reserve DynamicJsonDocument doc(500); --> Heap
  // Reserve StaticJsonDocument<500> doc; --> Stack
  // v7.x
  JsonDocument doc;

  // Uhrzeit
  char timestrbuffer[6];
  uhr.get_timeformatted(timestrbuffer, 6, "%R");

  // Betriebszeit
  char durationstrbuffer[29];
  snprintf(durationstrbuffer, 29, "%02lld:%02u:%02u", esp_duration.get_hh(), esp_duration.get_mm(), esp_duration.get_ss());

  doc["Version"] = webIf.version;
  doc["Relais"] = state_relais;
  doc["Tor_offen"] = state_tor;
  doc["Temperature"] = static_cast<float>(ESP.getFreeHeap());
  //doc["Temperature"] = sensor_BME280.Data.Messwert2;
  doc["Humidity"] = sensor_BME280.Data.Messwert3;
  doc["Pressure"] = sensor_BME280.Data.Messwert4;
  doc["Time"] = timestrbuffer;
  doc["Duration"] = durationstrbuffer;

  doc.shrinkToFit();
  serializeJson(doc, json_str);
  // serializeJson(doc, Serial);
  // DBG__PRINT("\n\tjson_str length:", "");
  // DBG__PRINT(" ", String(measureJson(doc)));
  // DBG__PRINT("\tdoc - Memory usage:", "");
  // DBG__PRINT(" ", String(doc.memoryUsage()));
  if (id >= 0) {
    webSocket.sendTXT(id, json_str, measureJson(doc));
  }
  else {
    webSocket.broadcastTXT(json_str, measureJson(doc));
  }

}
bblanchon commented 9 hours ago

My review:

  1. Small stack buffers like timestrbuffer and durationstrbuffer are appropriate; however, for larger buffers (let's say >128B), you should allocate on the heap to prevent stack overflows (you can also use String for simplicity).

  2. serializeJson() returns the length of the JSON document, so you can use the return value in place of measureJson(), which will significantly improve speed and code size.

  3. Calling shrinkToFit() is unnecessary since you discard the JsonDocument right after the serialization.

hasenradball commented 2 hours ago

Dear Benoit,

thank you so much for the review and the hints.

For the return of serialize function I was unsure, looked up in the API but was not explizitly mentioned as length of JsonDocument.

Indeed in the past I prevented as much as possible to use the heap. I took nearly everything on stack. Is it good putting things on heap which don‘ t last long? An how to see a StackOverflow comming?

In the example below would it the also better to use std::Strings for the char arrays like timezone, password, etc. And directly do it with std:String?


StaticJsonDocument<JSON_DOC_SIZE> doc;

  // Deserialize the JSON document
  DeserializationError error = deserializeJson(doc, file);
  if (error) {
    DBG__PRINT(F(">>> ERROR: deserializeJson() of >config.txt< failed:"), "");
    DBG__PRINT(error.f_str(), "!\n");
    return false;
  }
  else {
    // Ausgabe => Serial
    //serializeJson(doc, Serial);
    DBG__PRINT("\n");
  }

  // Copy values from the JsonDocument to the Config
  strlcpy(wlan_host, doc["wifi"]["Hostname"], sizeof(wlan_host));
  strlcpy(wlan_ssid, doc["wifi"]["SSID"], sizeof(wlan_ssid));
  strlcpy(wlan_passwd, doc["wifi"]["Password"], sizeof(wlan_passwd));
  strlcpy(time_zone, doc["time"]["TimeZone"], sizeof(time_zone));
  strlcpy(time_server1, doc["time"]["TimeServer1"], sizeof(time_server1));
  strlcpy(time_server2, doc["time"]["TimeServer2"], sizeof(time_server2));
  strlcpy(time_server3, doc["time"]["TimeServer3"], sizeof(time_server3));
  strlcpy(time_zone, doc["time"]["TimeZone"], sizeof(time_zone));
  licht_schwelle = doc["licht"]["Schwellwert"];
  licht_min = doc["licht"]["Min"];
  licht_max = doc["licht"]["Max"];
  farbe_r = doc["farbe"]["RGB-R"];
  farbe_g = doc["farbe"]["RGB-G"];
  farbe_b = doc["farbe"]["RGB-B"];
  sprach_einstellung = doc["sprache"]["Wert"];

  tim_wlan.active = doc["timerWLAN"]["active"];
  strlcpy(tim_wlan.on_time, doc["timerWLAN"]["On"], sizeof(tim_wlan.on_time));
  setTimer_struct(tim_wlan.on_time, &tim_wlan.on_time_struct);
  strlcpy(tim_wlan.off_time, doc["timerWLAN"]["Off"], sizeof(tim_wlan.off_time));
  setTimer_struct(tim_wlan.off_time, &tim_wlan.off_time_struct);
  return true;
}

Can you please put me on the list for the book?