ESPHome  2024.11.0
json_util.cpp
Go to the documentation of this file.
1 #include "json_util.h"
2 #include "esphome/core/log.h"
3 
4 #ifdef USE_ESP8266
5 #include <Esp.h>
6 #endif
7 #ifdef USE_ESP32
8 #include <esp_heap_caps.h>
9 #endif
10 #ifdef USE_RP2040
11 #include <Arduino.h>
12 #endif
13 
14 namespace esphome {
15 namespace json {
16 
17 static const char *const TAG = "json";
18 
19 static std::vector<char> global_json_build_buffer; // NOLINT
20 
21 std::string build_json(const json_build_t &f) {
22  // Here we are allocating up to 5kb of memory,
23  // with the heap size minus 2kb to be safe if less than 5kb
24  // as we can not have a true dynamic sized document.
25  // The excess memory is freed below with `shrinkToFit()`
26 #ifdef USE_ESP8266
27  const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
28 #elif defined(USE_ESP32)
29  const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
30 #elif defined(USE_RP2040)
31  const size_t free_heap = rp2040.getFreeHeap();
32 #elif defined(USE_LIBRETINY)
33  const size_t free_heap = lt_heap_get_free();
34 #endif
35 
36  size_t request_size = std::min(free_heap, (size_t) 512);
37  while (true) {
38  ESP_LOGV(TAG, "Attempting to allocate %u bytes for JSON serialization", request_size);
39  DynamicJsonDocument json_document(request_size);
40  if (json_document.capacity() == 0) {
41  ESP_LOGE(TAG,
42  "Could not allocate memory for JSON document! Requested %u bytes, largest free heap block: %u bytes",
43  request_size, free_heap);
44  return "{}";
45  }
46  JsonObject root = json_document.to<JsonObject>();
47  f(root);
48  if (json_document.overflowed()) {
49  if (request_size == free_heap) {
50  ESP_LOGE(TAG, "Could not allocate memory for JSON document! Overflowed largest free heap block: %u bytes",
51  free_heap);
52  return "{}";
53  }
54  request_size = std::min(request_size * 2, free_heap);
55  continue;
56  }
57  json_document.shrinkToFit();
58  ESP_LOGV(TAG, "Size after shrink %u bytes", json_document.capacity());
59  std::string output;
60  serializeJson(json_document, output);
61  return output;
62  }
63 }
64 
65 bool parse_json(const std::string &data, const json_parse_t &f) {
66  // Here we are allocating 1.5 times the data size,
67  // with the heap size minus 2kb to be safe if less than that
68  // as we can not have a true dynamic sized document.
69  // The excess memory is freed below with `shrinkToFit()`
70 #ifdef USE_ESP8266
71  const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
72 #elif defined(USE_ESP32)
73  const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
74 #elif defined(USE_RP2040)
75  const size_t free_heap = rp2040.getFreeHeap();
76 #elif defined(USE_LIBRETINY)
77  const size_t free_heap = lt_heap_get_free();
78 #endif
79  size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5));
80  while (true) {
81  DynamicJsonDocument json_document(request_size);
82  if (json_document.capacity() == 0) {
83  ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size,
84  free_heap);
85  return false;
86  }
87  DeserializationError err = deserializeJson(json_document, data);
88  json_document.shrinkToFit();
89 
90  JsonObject root = json_document.as<JsonObject>();
91 
92  if (err == DeserializationError::Ok) {
93  return f(root);
94  } else if (err == DeserializationError::NoMemory) {
95  if (request_size * 2 >= free_heap) {
96  ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
97  return false;
98  }
99  ESP_LOGV(TAG, "Increasing memory allocation.");
100  request_size *= 2;
101  continue;
102  } else {
103  ESP_LOGE(TAG, "JSON parse error: %s", err.c_str());
104  return false;
105  }
106  };
107  return false;
108 }
109 
110 } // namespace json
111 } // namespace esphome
bool parse_json(const std::string &data, const json_parse_t &f)
Parse a JSON string and run the provided json parse function if it&#39;s valid.
Definition: json_util.cpp:65
std::function< void(JsonObject)> json_build_t
Callback function typedef for building JsonObjects.
Definition: json_util.h:20
std::function< bool(JsonObject)> json_parse_t
Callback function typedef for parsing JsonObjects.
Definition: json_util.h:17
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition: json_util.cpp:21
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7