1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-22 19:53:46 +01:00

Merge branch 'esp32_ble_server_unique_ptr_mfr_data' into integration

This commit is contained in:
J. Nick Koston
2025-10-05 17:10:24 -05:00
13 changed files with 89 additions and 36 deletions

View File

@@ -68,6 +68,10 @@ void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
}
void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
this->advertising_set_manufacturer_data(std::span<const uint8_t>(data));
}
void ESP32BLE::advertising_set_manufacturer_data(std::span<const uint8_t> data) {
this->advertising_init_();
this->advertising_->set_manufacturer_data(data);
this->advertising_start();

View File

@@ -118,6 +118,7 @@ class ESP32BLE : public Component {
void advertising_start();
void advertising_set_service_data(const std::vector<uint8_t> &data);
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
void advertising_set_manufacturer_data(std::span<const uint8_t> data);
void advertising_set_appearance(uint16_t appearance) { this->appearance_ = appearance; }
void advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name);
void advertising_add_service_uuid(ESPBTUUID uuid);

View File

@@ -59,6 +59,10 @@ void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
}
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
this->set_manufacturer_data(std::span<const uint8_t>(data));
}
void BLEAdvertising::set_manufacturer_data(std::span<const uint8_t> data) {
delete[] this->advertising_data_.p_manufacturer_data;
this->advertising_data_.p_manufacturer_data = nullptr;
this->advertising_data_.manufacturer_len = data.size();

View File

@@ -35,6 +35,7 @@ class BLEAdvertising {
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
void set_manufacturer_data(const std::vector<uint8_t> &data);
void set_manufacturer_data(std::span<const uint8_t> data);
void set_appearance(uint16_t appearance) { this->advertising_data_.appearance = appearance; }
void set_service_data(const std::vector<uint8_t> &data);
void set_service_data(std::span<const uint8_t> data);

View File

@@ -99,7 +99,8 @@ bool BLEServer::can_proceed() { return this->is_running() || !this->parent_->is_
void BLEServer::restart_advertising_() {
if (this->is_running()) {
this->parent_->advertising_set_manufacturer_data(this->manufacturer_data_);
this->parent_->advertising_set_manufacturer_data(
std::span<const uint8_t>(this->manufacturer_data_.get(), this->manufacturer_data_length_));
}
}

View File

@@ -35,7 +35,11 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
bool is_running();
void set_manufacturer_data(const std::vector<uint8_t> &data) {
this->manufacturer_data_ = data;
this->manufacturer_data_length_ = data.size();
this->manufacturer_data_.reset(data.empty() ? nullptr : new uint8_t[data.size()]);
if (!data.empty()) {
memcpy(this->manufacturer_data_.get(), data.data(), data.size());
}
this->restart_advertising_();
}
@@ -87,24 +91,27 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
void remove_client_(uint16_t conn_id);
void dispatch_callbacks_(CallbackType type, uint16_t conn_id);
// 4-byte aligned (pointers and vectors on 32-bit)
std::vector<CallbackEntry> callbacks_;
std::vector<uint8_t> manufacturer_data_{};
esp_gatt_if_t gatts_if_{0};
bool registered_{false};
uint16_t clients_[USE_ESP32_BLE_MAX_CONNECTIONS]{};
uint8_t client_count_{0};
std::vector<ServiceEntry> services_{};
std::vector<BLEService *> services_to_start_{};
std::unique_ptr<uint8_t[]> manufacturer_data_{};
BLEService *device_information_service_{};
// 2-byte aligned
uint16_t clients_[USE_ESP32_BLE_MAX_CONNECTIONS]{};
// 1-byte aligned
uint8_t manufacturer_data_length_{0};
uint8_t client_count_{0};
esp_gatt_if_t gatts_if_{0};
enum State : uint8_t {
INIT = 0x00,
REGISTERING,
STARTING_SERVICE,
RUNNING,
} state_{INIT};
bool registered_{false};
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -79,7 +79,7 @@ void MDNSComponent::compile_records_() {
#ifdef USE_API
if (api::global_api_server != nullptr) {
auto &service = this->services_[this->services_.count()++];
auto &service = this->services_.emplace_next();
service.service_type = MDNS_STR(SERVICE_ESPHOMELIB);
service.proto = MDNS_STR(SERVICE_TCP);
service.port = api::global_api_server->get_port();
@@ -158,14 +158,14 @@ void MDNSComponent::compile_records_() {
#endif // USE_API
#ifdef USE_PROMETHEUS
auto &prom_service = this->services_[this->services_.count()++];
auto &prom_service = this->services_.emplace_next();
prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS);
prom_service.proto = MDNS_STR(SERVICE_TCP);
prom_service.port = USE_WEBSERVER_PORT;
#endif
#ifdef USE_WEBSERVER
auto &web_service = this->services_[this->services_.count()++];
auto &web_service = this->services_.emplace_next();
web_service.service_type = MDNS_STR(SERVICE_HTTP);
web_service.proto = MDNS_STR(SERVICE_TCP);
web_service.port = USE_WEBSERVER_PORT;
@@ -174,7 +174,7 @@ void MDNSComponent::compile_records_() {
#if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_WEBSERVER) && !defined(USE_MDNS_EXTRA_SERVICES)
// Publish "http" service if not using native API or any other services
// This is just to have *some* mDNS service so that .local resolution works
auto &fallback_service = this->services_[this->services_.count()++];
auto &fallback_service = this->services_.emplace_next();
fallback_service.service_type = "_http";
fallback_service.proto = "_tcp";
fallback_service.port = USE_WEBSERVER_PORT;

View File

@@ -39,7 +39,7 @@ class MDNSComponent : public Component {
float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; }
#ifdef USE_MDNS_EXTRA_SERVICES
void add_extra_service(MDNSService service) { this->services_[this->services_.count()++] = std::move(service); }
void add_extra_service(MDNSService service) { this->services_.emplace_next() = std::move(service); }
#endif
const StaticVector<MDNSService, MDNS_SERVICE_COUNT> &get_services() const { return this->services_; }

View File

@@ -52,6 +52,20 @@ DefaultHeaders &DefaultHeaders::Instance() { return default_headers_instance; }
namespace {
// Non-blocking send function to prevent watchdog timeouts when TCP buffers are full
/**
* Sends data on a socket in non-blocking mode.
*
* @param hd HTTP server handle (unused).
* @param sockfd Socket file descriptor.
* @param buf Buffer to send.
* @param buf_len Length of buffer.
* @param flags Flags for send().
* @return
* - Number of bytes sent on success.
* - HTTPD_SOCK_ERR_INVALID if buf is nullptr.
* - HTTPD_SOCK_ERR_TIMEOUT if the send buffer is full (EAGAIN/EWOULDBLOCK).
* - HTTPD_SOCK_ERR_FAIL for other errors.
*/
int nonblocking_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags) {
if (buf == nullptr) {
return HTTPD_SOCK_ERR_INVALID;
@@ -319,8 +333,8 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const std::string &name) {
}
}
// Don't cache misses to prevent memory exhaustion from malicious requests
// with thousands of non-existent parameter lookups
// Don't cache misses to avoid wasting memory when handlers check for
// optional parameters that don't exist in the request
if (!val.has_value()) {
return nullptr;
}

View File

@@ -172,7 +172,8 @@ class AsyncWebServerRequest {
AsyncWebServerResponse *rsp_{};
// Use vector instead of map/unordered_map: most requests have 0-3 params, so linear search
// is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid
// duplicate storage. Only successful lookups are cached to prevent memory exhaustion attacks.
// duplicate storage. Only successful lookups are cached to prevent cache pollution when
// handlers check for optional parameters that don't exist.
std::vector<AsyncWebParameter *> params_;
std::string post_query_;
AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}

View File

@@ -365,7 +365,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str());
ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str());
ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str());
#if defined(USE_ESP32) && ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
#if defined(USE_ESP32) && defined(USE_WIFI_WPA2_EAP) && ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGV(TAG, " TTLS Phase 2: " LOG_SECRET("'%s'"), eap_phase2_to_str(eap_config.ttls_phase_2));
#endif
bool ca_cert_present = eap_config.ca_cert != nullptr && strlen(eap_config.ca_cert);

View File

@@ -33,12 +33,22 @@ static const char *const TAG = "component";
// Using namespace-scope static to avoid guard variables (saves 16 bytes total)
// This is safe because ESPHome is single-threaded during initialization
namespace {
struct ComponentErrorMessage {
const Component *component;
const char *message;
};
struct ComponentPriorityOverride {
const Component *component;
float priority;
};
// Error messages for failed components
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::unique_ptr<std::vector<std::pair<const Component *, const char *>>> component_error_messages;
std::unique_ptr<std::vector<ComponentErrorMessage>> component_error_messages;
// Setup priority overrides - freed after setup completes
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::unique_ptr<std::vector<std::pair<const Component *, float>>> setup_priority_overrides;
std::unique_ptr<std::vector<ComponentPriorityOverride>> setup_priority_overrides;
} // namespace
namespace setup_priority {
@@ -134,9 +144,9 @@ void Component::call_dump_config() {
// Look up error message from global vector
const char *error_msg = nullptr;
if (component_error_messages) {
for (const auto &pair : *component_error_messages) {
if (pair.first == this) {
error_msg = pair.second;
for (const auto &entry : *component_error_messages) {
if (entry.component == this) {
error_msg = entry.message;
break;
}
}
@@ -306,17 +316,17 @@ void Component::status_set_error(const char *message) {
if (message != nullptr) {
// Lazy allocate the error messages vector if needed
if (!component_error_messages) {
component_error_messages = std::make_unique<std::vector<std::pair<const Component *, const char *>>>();
component_error_messages = std::make_unique<std::vector<ComponentErrorMessage>>();
}
// Check if this component already has an error message
for (auto &pair : *component_error_messages) {
if (pair.first == this) {
pair.second = message;
for (auto &entry : *component_error_messages) {
if (entry.component == this) {
entry.message = message;
return;
}
}
// Add new error message
component_error_messages->emplace_back(this, message);
component_error_messages->emplace_back(ComponentErrorMessage{this, message});
}
}
void Component::status_clear_warning() {
@@ -356,9 +366,9 @@ float Component::get_actual_setup_priority() const {
// Check if there's an override in the global vector
if (setup_priority_overrides) {
// Linear search is fine for small n (typically < 5 overrides)
for (const auto &pair : *setup_priority_overrides) {
if (pair.first == this) {
return pair.second;
for (const auto &entry : *setup_priority_overrides) {
if (entry.component == this) {
return entry.priority;
}
}
}
@@ -367,21 +377,21 @@ float Component::get_actual_setup_priority() const {
void Component::set_setup_priority(float priority) {
// Lazy allocate the vector if needed
if (!setup_priority_overrides) {
setup_priority_overrides = std::make_unique<std::vector<std::pair<const Component *, float>>>();
setup_priority_overrides = std::make_unique<std::vector<ComponentPriorityOverride>>();
// Reserve some space to avoid reallocations (most configs have < 10 overrides)
setup_priority_overrides->reserve(10);
}
// Check if this component already has an override
for (auto &pair : *setup_priority_overrides) {
if (pair.first == this) {
pair.second = priority;
for (auto &entry : *setup_priority_overrides) {
if (entry.component == this) {
entry.priority = priority;
return;
}
}
// Add new override
setup_priority_overrides->emplace_back(this, priority);
setup_priority_overrides->emplace_back(ComponentPriorityOverride{this, priority});
}
bool Component::has_overridden_loop() const {

View File

@@ -130,6 +130,16 @@ template<typename T, size_t N> class StaticVector {
}
}
// Return reference to next element and increment count (with bounds checking)
T &emplace_next() {
if (count_ >= N) {
// Should never happen with proper size calculation
// Return reference to last element to avoid crash
return data_[N - 1];
}
return data_[count_++];
}
size_t size() const { return count_; }
bool empty() const { return count_ == 0; }