mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[helpers] Provide calls to get free heap and largest available block. (#7978)
This commit is contained in:
		| @@ -1,38 +1,20 @@ | ||||
| #include "json_util.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #ifdef USE_ESP8266 | ||||
| #include <Esp.h> | ||||
| #endif | ||||
| #ifdef USE_ESP32 | ||||
| #include <esp_heap_caps.h> | ||||
| #endif | ||||
| #ifdef USE_RP2040 | ||||
| #include <Arduino.h> | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace json { | ||||
|  | ||||
| static const char *const TAG = "json"; | ||||
|  | ||||
| static std::vector<char> global_json_build_buffer;  // NOLINT | ||||
| static const auto ALLOCATOR = RAMAllocator<uint8_t>(RAMAllocator<uint8_t>::ALLOC_INTERNAL); | ||||
|  | ||||
| std::string build_json(const json_build_t &f) { | ||||
|   // Here we are allocating up to 5kb of memory, | ||||
|   // with the heap size minus 2kb to be safe if less than 5kb | ||||
|   // as we can not have a true dynamic sized document. | ||||
|   // The excess memory is freed below with `shrinkToFit()` | ||||
| #ifdef USE_ESP8266 | ||||
|   const size_t free_heap = ESP.getMaxFreeBlockSize();  // NOLINT(readability-static-accessed-through-instance) | ||||
| #elif defined(USE_ESP32) | ||||
|   const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); | ||||
| #elif defined(USE_RP2040) | ||||
|   const size_t free_heap = rp2040.getFreeHeap(); | ||||
| #elif defined(USE_LIBRETINY) | ||||
|   const size_t free_heap = lt_heap_get_free(); | ||||
| #endif | ||||
|  | ||||
|   auto free_heap = ALLOCATOR.get_max_free_block_size(); | ||||
|   size_t request_size = std::min(free_heap, (size_t) 512); | ||||
|   while (true) { | ||||
|     ESP_LOGV(TAG, "Attempting to allocate %u bytes for JSON serialization", request_size); | ||||
| @@ -67,20 +49,12 @@ bool parse_json(const std::string &data, const json_parse_t &f) { | ||||
|   // with the heap size minus 2kb to be safe if less than that | ||||
|   // as we can not have a true dynamic sized document. | ||||
|   // The excess memory is freed below with `shrinkToFit()` | ||||
| #ifdef USE_ESP8266 | ||||
|   const size_t free_heap = ESP.getMaxFreeBlockSize();  // NOLINT(readability-static-accessed-through-instance) | ||||
| #elif defined(USE_ESP32) | ||||
|   const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); | ||||
| #elif defined(USE_RP2040) | ||||
|   const size_t free_heap = rp2040.getFreeHeap(); | ||||
| #elif defined(USE_LIBRETINY) | ||||
|   const size_t free_heap = lt_heap_get_free(); | ||||
| #endif | ||||
|   auto free_heap = ALLOCATOR.get_max_free_block_size(); | ||||
|   size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); | ||||
|   while (true) { | ||||
|     DynamicJsonDocument json_document(request_size); | ||||
|     if (json_document.capacity() == 0) { | ||||
|       ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size, | ||||
|       ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %zu bytes, free heap: %zu", request_size, | ||||
|                free_heap); | ||||
|       return false; | ||||
|     } | ||||
|   | ||||
| @@ -80,15 +80,7 @@ bool OnlineImage::resize_(int width_in, int height_in) { | ||||
|     this->width_ = width; | ||||
|     ESP_LOGD(TAG, "New size: (%d, %d)", width, height); | ||||
|   } else { | ||||
| #if defined(USE_ESP8266) | ||||
|     // NOLINTNEXTLINE(readability-static-accessed-through-instance) | ||||
|     int max_block = ESP.getMaxFreeBlockSize(); | ||||
| #elif defined(USE_ESP32) | ||||
|     int max_block = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); | ||||
| #else | ||||
|     int max_block = -1; | ||||
| #endif | ||||
|     ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %d Bytes", max_block); | ||||
|     ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %zu Bytes", this->allocator_.get_max_free_block_size()); | ||||
|     this->end_connection_(); | ||||
|     return false; | ||||
|   } | ||||
|   | ||||
| @@ -11,6 +11,14 @@ | ||||
|  | ||||
| #include "esphome/core/optional.h" | ||||
|  | ||||
| #ifdef USE_ESP8266 | ||||
| #include <Esp.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_RP2040 | ||||
| #include <Arduino.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
| #include <esp_heap_caps.h> | ||||
| #endif | ||||
| @@ -684,20 +692,23 @@ template<class T> class RAMAllocator { | ||||
|   }; | ||||
|  | ||||
|   RAMAllocator() = default; | ||||
|   RAMAllocator(uint8_t flags) : flags_{flags} {} | ||||
|   RAMAllocator(uint8_t flags) { | ||||
|     // default is both external and internal | ||||
|     flags &= ALLOC_INTERNAL | ALLOC_EXTERNAL; | ||||
|     if (flags != 0) | ||||
|       this->flags_ = flags; | ||||
|   } | ||||
|   template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {} | ||||
|  | ||||
|   T *allocate(size_t n) { | ||||
|     size_t size = n * sizeof(T); | ||||
|     T *ptr = nullptr; | ||||
| #ifdef USE_ESP32 | ||||
|     // External allocation by default or if explicitely requested | ||||
|     if ((this->flags_ & Flags::ALLOC_EXTERNAL) || ((this->flags_ & Flags::ALLOC_INTERNAL) == 0)) { | ||||
|     if (this->flags_ & Flags::ALLOC_EXTERNAL) { | ||||
|       ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); | ||||
|     } | ||||
|     // Fallback to internal allocation if explicitely requested or no flag is specified | ||||
|     if (ptr == nullptr && ((this->flags_ & Flags::ALLOC_INTERNAL) || (this->flags_ & Flags::ALLOC_EXTERNAL) == 0)) { | ||||
|       ptr = static_cast<T *>(malloc(size));  // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) | ||||
|     if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) { | ||||
|       ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); | ||||
|     } | ||||
| #else | ||||
|     // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported | ||||
| @@ -710,8 +721,46 @@ template<class T> class RAMAllocator { | ||||
|     free(p);  // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Return the total heap space available via this allocator | ||||
|    */ | ||||
|   size_t get_free_heap_size() const { | ||||
| #ifdef USE_ESP8266 | ||||
|     return ESP.getFreeHeap();  // NOLINT(readability-static-accessed-through-instance) | ||||
| #elif defined(USE_ESP32) | ||||
|     auto max_internal = | ||||
|         this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0; | ||||
|     auto max_external = | ||||
|         this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0; | ||||
|     return max_internal + max_external; | ||||
| #elif defined(USE_RP2040) | ||||
|     return ::rp2040.getFreeHeap(); | ||||
| #elif defined(USE_LIBRETINY) | ||||
|     return lt_heap_get_free(); | ||||
| #else | ||||
|     return 100000; | ||||
| #endif | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Return the maximum size block this allocator could allocate. This may be an approximation on some platforms | ||||
|    */ | ||||
|   size_t get_max_free_block_size() const { | ||||
| #ifdef USE_ESP8266 | ||||
|     return ESP.getMaxFreeBlockSize();  // NOLINT(readability-static-accessed-through-instance) | ||||
| #elif defined(USE_ESP32) | ||||
|     auto max_internal = | ||||
|         this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0; | ||||
|     auto max_external = | ||||
|         this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0; | ||||
|     return std::max(max_internal, max_external); | ||||
| #else | ||||
|     return this->get_free_heap_size(); | ||||
| #endif | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   uint8_t flags_{Flags::ALLOW_FAILURE}; | ||||
|   uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL}; | ||||
| }; | ||||
|  | ||||
| template<class T> using ExternalRAMAllocator = RAMAllocator<T>; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user