mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +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 "json_util.h" | ||||||
| #include "esphome/core/log.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 esphome { | ||||||
| namespace json { | namespace json { | ||||||
|  |  | ||||||
| static const char *const TAG = "json"; | static const char *const TAG = "json"; | ||||||
|  |  | ||||||
| static std::vector<char> global_json_build_buffer;  // NOLINT | 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) { | std::string build_json(const json_build_t &f) { | ||||||
|   // Here we are allocating up to 5kb of memory, |   // Here we are allocating up to 5kb of memory, | ||||||
|   // with the heap size minus 2kb to be safe if less than 5kb |   // with the heap size minus 2kb to be safe if less than 5kb | ||||||
|   // as we can not have a true dynamic sized document. |   // as we can not have a true dynamic sized document. | ||||||
|   // The excess memory is freed below with `shrinkToFit()` |   // The excess memory is freed below with `shrinkToFit()` | ||||||
| #ifdef USE_ESP8266 |   auto free_heap = ALLOCATOR.get_max_free_block_size(); | ||||||
|   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 |  | ||||||
|  |  | ||||||
|   size_t request_size = std::min(free_heap, (size_t) 512); |   size_t request_size = std::min(free_heap, (size_t) 512); | ||||||
|   while (true) { |   while (true) { | ||||||
|     ESP_LOGV(TAG, "Attempting to allocate %u bytes for JSON serialization", request_size); |     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 |   // with the heap size minus 2kb to be safe if less than that | ||||||
|   // as we can not have a true dynamic sized document. |   // as we can not have a true dynamic sized document. | ||||||
|   // The excess memory is freed below with `shrinkToFit()` |   // The excess memory is freed below with `shrinkToFit()` | ||||||
| #ifdef USE_ESP8266 |   auto free_heap = ALLOCATOR.get_max_free_block_size(); | ||||||
|   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 |  | ||||||
|   size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); |   size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); | ||||||
|   while (true) { |   while (true) { | ||||||
|     DynamicJsonDocument json_document(request_size); |     DynamicJsonDocument json_document(request_size); | ||||||
|     if (json_document.capacity() == 0) { |     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); |                free_heap); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -80,15 +80,7 @@ bool OnlineImage::resize_(int width_in, int height_in) { | |||||||
|     this->width_ = width; |     this->width_ = width; | ||||||
|     ESP_LOGD(TAG, "New size: (%d, %d)", width, height); |     ESP_LOGD(TAG, "New size: (%d, %d)", width, height); | ||||||
|   } else { |   } else { | ||||||
| #if defined(USE_ESP8266) |     ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %zu Bytes", this->allocator_.get_max_free_block_size()); | ||||||
|     // 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); |  | ||||||
|     this->end_connection_(); |     this->end_connection_(); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -11,6 +11,14 @@ | |||||||
|  |  | ||||||
| #include "esphome/core/optional.h" | #include "esphome/core/optional.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP8266 | ||||||
|  | #include <Esp.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_RP2040 | ||||||
|  | #include <Arduino.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
| #include <esp_heap_caps.h> | #include <esp_heap_caps.h> | ||||||
| #endif | #endif | ||||||
| @@ -684,20 +692,23 @@ template<class T> class RAMAllocator { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   RAMAllocator() = default; |   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_} {} |   template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {} | ||||||
|  |  | ||||||
|   T *allocate(size_t n) { |   T *allocate(size_t n) { | ||||||
|     size_t size = n * sizeof(T); |     size_t size = n * sizeof(T); | ||||||
|     T *ptr = nullptr; |     T *ptr = nullptr; | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|     // External allocation by default or if explicitely requested |     if (this->flags_ & Flags::ALLOC_EXTERNAL) { | ||||||
|     if ((this->flags_ & Flags::ALLOC_EXTERNAL) || ((this->flags_ & Flags::ALLOC_INTERNAL) == 0)) { |  | ||||||
|       ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); |       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) { | ||||||
|     if (ptr == nullptr && ((this->flags_ & Flags::ALLOC_INTERNAL) || (this->flags_ & Flags::ALLOC_EXTERNAL) == 0)) { |       ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); | ||||||
|       ptr = static_cast<T *>(malloc(size));  // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) |  | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
|     // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported |     // 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) |     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: |  private: | ||||||
|   uint8_t flags_{Flags::ALLOW_FAILURE}; |   uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| template<class T> using ExternalRAMAllocator = RAMAllocator<T>; | template<class T> using ExternalRAMAllocator = RAMAllocator<T>; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user