From 2e11e66db473cf454b93f6dbd115d53e5e4f3b28 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Jun 2025 03:11:13 +0200 Subject: [PATCH] Optimize bluetooth_proxy memory usage on ESP32 (#9114) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../bluetooth_proxy/bluetooth_connection.h | 11 +++++-- .../bluetooth_proxy/bluetooth_proxy.h | 12 +++++-- .../esp32_ble_client/ble_client_base.h | 33 +++++++++++++------ .../esp32_ble_tracker/esp32_ble_tracker.h | 12 ++++--- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.h b/esphome/components/bluetooth_proxy/bluetooth_connection.h index fd83f8dd00..73c034d93b 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.h +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.h @@ -26,10 +26,17 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase { protected: friend class BluetoothProxy; - bool seen_mtu_or_services_{false}; - int16_t send_service_{-2}; + // Memory optimized layout for 32-bit systems + // Group 1: Pointers (4 bytes each, naturally aligned) BluetoothProxy *proxy_; + + // Group 2: 2-byte types + int16_t send_service_{-2}; // Needs to handle negative values and service count + + // Group 3: 1-byte types + bool seen_mtu_or_services_{false}; + // 1 byte used, 1 byte padding }; } // namespace bluetooth_proxy diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 16db0a0a11..f0632350e0 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -134,11 +134,17 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com BluetoothConnection *get_connection_(uint64_t address, bool reserve); - bool active_; - - std::vector connections_{}; + // Memory optimized layout for 32-bit systems + // Group 1: Pointers (4 bytes each, naturally aligned) api::APIConnection *api_connection_{nullptr}; + + // Group 2: Container types (typically 12 bytes on 32-bit) + std::vector connections_{}; + + // Group 3: 1-byte types grouped together + bool active_; bool raw_advertisements_{false}; + // 2 bytes used, 2 bytes padding }; extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index 814a9664d9..bf3b589b1b 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -96,21 +96,34 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { void set_state(espbt::ClientState st) override; protected: - int gattc_if_; - esp_bd_addr_t remote_bda_; - esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC}; - uint16_t conn_id_{UNSET_CONN_ID}; + // Memory optimized layout for 32-bit systems + // Group 1: 8-byte types uint64_t address_{0}; - bool auto_connect_{false}; + + // Group 2: Container types (grouped for memory optimization) std::string address_str_{}; - uint8_t connection_index_; - int16_t service_count_{0}; - uint16_t mtu_{23}; - bool paired_{false}; - espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; std::vector services_; + + // Group 3: 4-byte types + int gattc_if_; esp_gatt_status_t status_{ESP_GATT_OK}; + // Group 4: Arrays (6 bytes) + esp_bd_addr_t remote_bda_; + + // Group 5: 2-byte types + uint16_t conn_id_{UNSET_CONN_ID}; + uint16_t mtu_{23}; + + // Group 6: 1-byte types and small enums + esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC}; + espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; + uint8_t connection_index_; + uint8_t service_count_{0}; // ESP32 has max handles < 255, typical devices have < 50 services + bool auto_connect_{false}; + bool paired_{false}; + // 6 bytes used, 2 bytes padding + void log_event_(const char *name); }; diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 490ed19645..414c9f4b48 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -129,7 +129,7 @@ class ESPBTDeviceListener { ESP32BLETracker *parent_{nullptr}; }; -enum class ClientState { +enum class ClientState : uint8_t { // Connection is allocated INIT, // Client is disconnecting @@ -165,7 +165,7 @@ enum class ScannerState { STOPPED, }; -enum class ConnectionType { +enum class ConnectionType : uint8_t { // The default connection type, we hold all the services in ram // for the duration of the connection. V1, @@ -193,15 +193,19 @@ class ESPBTClient : public ESPBTDeviceListener { } } ClientState state() const { return state_; } - int app_id; + + // Memory optimized layout + uint8_t app_id; // App IDs are small integers assigned sequentially protected: + // Group 1: 1-byte types ClientState state_{ClientState::INIT}; // want_disconnect_ is set to true when a disconnect is requested // while the client is connecting. This is used to disconnect the // client as soon as we get the connection id (conn_id_) from the // ESP_GATTC_OPEN_EVT event. bool want_disconnect_{false}; + // 2 bytes used, 2 bytes padding }; class ESP32BLETracker : public Component, @@ -262,7 +266,7 @@ class ESP32BLETracker : public Component, /// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed. void set_scanner_state_(ScannerState state); - int app_id_{0}; + uint8_t app_id_{0}; /// Vector of addresses that have already been printed in print_bt_device_info std::vector already_discovered_;