1
0
mirror of https://github.com/esphome/esphome.git synced 2025-04-08 20:00:27 +01:00
This commit is contained in:
Tomasz Duda 2025-02-23 10:19:10 +01:00
parent 8e5926e843
commit 0cdaf0b090

View File

@ -8,10 +8,17 @@
#include <type_traits>
#include <vector>
#include <limits>
#include <array>
#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
@ -22,8 +29,6 @@
#elif defined(USE_LIBRETINY)
#include <FreeRTOS.h>
#include <semphr.h>
#elif defined(USE_ZEPHYR)
#include <zephyr/kernel.h>
#endif
#define HOT __attribute__((hot))
@ -158,7 +163,7 @@ template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max
return (value - min) * (max_out - min_out) / (max - min) + min_out;
}
/// Calculate a CRC-8 checksum of \p data with size \p len.
/// Calculate a CRC-8 checksum of \p data with size \p len using the CRC-8-Dallas/Maxim polynomial.
uint8_t crc8(const uint8_t *data, uint8_t len);
/// Calculate a CRC-16 checksum of \p data with size \p len.
@ -423,6 +428,14 @@ template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::stri
return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T));
}
/// Format the byte array \p data of length \p len in binary.
std::string format_bin(const uint8_t *data, size_t length);
/// Format an unsigned integer in binary, starting with the most significant byte.
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_bin(T val) {
val = convert_big_endian(val);
return format_bin(reinterpret_cast<uint8_t *>(&val), sizeof(T));
}
/// Return values for parse_on_off().
enum ParseOnOffState {
PARSE_NONE = 0,
@ -549,6 +562,7 @@ class Mutex {
public:
Mutex();
Mutex(const Mutex &) = delete;
~Mutex();
void lock();
bool try_lock();
void unlock();
@ -556,10 +570,11 @@ class Mutex {
Mutex &operator=(const Mutex &) = delete;
private:
#if defined(USE_ZEPHYR)
k_mutex handle_;
#elif defined(USE_ESP32) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
SemaphoreHandle_t handle_;
#else
// d-pointer to store private data on new platforms
void *handle_; // NOLINT(clang-diagnostic-unused-private-field)
#endif
};
@ -641,6 +656,14 @@ std::string get_mac_address_pretty();
void set_mac_address(uint8_t *mac);
#endif
/// Check if a custom MAC address is set (ESP32 & variants)
/// @return True if a custom MAC address is set (ESP32 & variants), else false
bool has_custom_mac_address();
/// Check if the MAC address is not all zeros or all ones
/// @return True if MAC is valid, else false
bool mac_address_is_valid(const uint8_t *mac);
/// Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
void delay_microseconds_safe(uint32_t us);
@ -649,35 +672,48 @@ void delay_microseconds_safe(uint32_t us);
/// @name Memory management
///@{
/** An STL allocator that uses SPI RAM.
/** An STL allocator that uses SPI or internal RAM.
* Returns `nullptr` in case no memory is available.
*
* By setting flags, it can be configured to don't try main memory if SPI RAM is full or unavailable, and to return
* `nulllptr` instead of aborting when no memory is available.
* By setting flags, it can be configured to:
* - perform external allocation falling back to main memory if SPI RAM is full or unavailable
* - perform external allocation only
* - perform internal allocation only
*/
template<class T> class ExternalRAMAllocator {
template<class T> class RAMAllocator {
public:
using value_type = T;
enum Flags {
NONE = 0,
REFUSE_INTERNAL = 1 << 0, ///< Refuse falling back to internal memory when external RAM is full or unavailable.
ALLOW_FAILURE = 1 << 1, ///< Don't abort when memory allocation fails.
NONE = 0, // Perform external allocation and fall back to internal memory
ALLOC_EXTERNAL = 1 << 0, // Perform external allocation only.
ALLOC_INTERNAL = 1 << 1, // Perform internal allocation only.
ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
};
ExternalRAMAllocator() = default;
ExternalRAMAllocator(Flags flags) : flags_{flags} {}
template<class U> constexpr ExternalRAMAllocator(const ExternalRAMAllocator<U> &other) : flags_{other.flags_} {}
RAMAllocator() = default;
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
ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
if (this->flags_ & Flags::ALLOC_EXTERNAL) {
ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
}
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
ptr = static_cast<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
#endif
if (ptr == nullptr && (this->flags_ & Flags::REFUSE_INTERNAL) == 0)
ptr = static_cast<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
if (ptr == nullptr && (this->flags_ & Flags::ALLOW_FAILURE) == 0)
abort();
return ptr;
}
@ -685,10 +721,50 @@ template<class T> class ExternalRAMAllocator {
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:
Flags flags_{Flags::NONE};
uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL};
};
template<class T> using ExternalRAMAllocator = RAMAllocator<T>;
/// @}
/// @name Internal functions