mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Merge branch 'integration' into memory_api
This commit is contained in:
		@@ -61,10 +61,14 @@ static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.rssi) == sizeof(es
 | 
				
			|||||||
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t),
 | 
					static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t),
 | 
				
			||||||
              "remote_addr must follow rssi in read_rssi_cmpl");
 | 
					              "remote_addr must follow rssi in read_rssi_cmpl");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Maximum size for inline storage of GATTC/GATTS data
 | 
				
			||||||
 | 
					// This value is chosen to fit within the 80-byte union while maximizing inline storage
 | 
				
			||||||
 | 
					static constexpr size_t INLINE_DATA_SIZE = 68;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
 | 
					// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
 | 
				
			||||||
// This class stores each event with minimal memory usage.
 | 
					// This class stores each event with minimal memory usage.
 | 
				
			||||||
// GAP events (99% of traffic) don't have the heap allocation overhead.
 | 
					// GAP events (99% of traffic) don't have the heap allocation overhead.
 | 
				
			||||||
// GATTC/GATTS events use heap allocation for their param and data.
 | 
					// GATTC/GATTS events use heap allocation for their param and inline storage for small data.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Event flow:
 | 
					// Event flow:
 | 
				
			||||||
// 1. ESP-IDF BLE stack calls our static handlers in the BLE task context
 | 
					// 1. ESP-IDF BLE stack calls our static handlers in the BLE task context
 | 
				
			||||||
@@ -135,27 +139,36 @@ class BLEEvent {
 | 
				
			|||||||
  ~BLEEvent() { this->release(); }
 | 
					  ~BLEEvent() { this->release(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Default constructor for pre-allocation in pool
 | 
					  // Default constructor for pre-allocation in pool
 | 
				
			||||||
  BLEEvent() : type_(GAP) {}
 | 
					  BLEEvent() : event_{}, type_(GAP) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Invoked on return to EventPool - clean up any heap-allocated data
 | 
					  // Invoked on return to EventPool - clean up any heap-allocated data
 | 
				
			||||||
  void release() {
 | 
					  void release() {
 | 
				
			||||||
    if (this->type_ == GAP) {
 | 
					    switch (this->type_) {
 | 
				
			||||||
      return;
 | 
					      case GAP:
 | 
				
			||||||
    }
 | 
					        // GAP events don't have heap allocations
 | 
				
			||||||
    if (this->type_ == GATTC) {
 | 
					        break;
 | 
				
			||||||
      delete this->event_.gattc.gattc_param;
 | 
					      case GATTC:
 | 
				
			||||||
      delete[] this->event_.gattc.data;
 | 
					        delete this->event_.gattc.gattc_param;
 | 
				
			||||||
      this->event_.gattc.gattc_param = nullptr;
 | 
					        // Only delete heap data if it was heap-allocated
 | 
				
			||||||
      this->event_.gattc.data = nullptr;
 | 
					        if (!this->event_.gattc.is_inline && this->event_.gattc.data.heap_data != nullptr) {
 | 
				
			||||||
      this->event_.gattc.data_len = 0;
 | 
					          delete[] this->event_.gattc.data.heap_data;
 | 
				
			||||||
      return;
 | 
					        }
 | 
				
			||||||
    }
 | 
					        // Clear critical fields to prevent issues if type changes
 | 
				
			||||||
    if (this->type_ == GATTS) {
 | 
					        this->event_.gattc.gattc_param = nullptr;
 | 
				
			||||||
      delete this->event_.gatts.gatts_param;
 | 
					        this->event_.gattc.is_inline = false;
 | 
				
			||||||
      delete[] this->event_.gatts.data;
 | 
					        this->event_.gattc.data.heap_data = nullptr;
 | 
				
			||||||
      this->event_.gatts.gatts_param = nullptr;
 | 
					        break;
 | 
				
			||||||
      this->event_.gatts.data = nullptr;
 | 
					      case GATTS:
 | 
				
			||||||
      this->event_.gatts.data_len = 0;
 | 
					        delete this->event_.gatts.gatts_param;
 | 
				
			||||||
 | 
					        // Only delete heap data if it was heap-allocated
 | 
				
			||||||
 | 
					        if (!this->event_.gatts.is_inline && this->event_.gatts.data.heap_data != nullptr) {
 | 
				
			||||||
 | 
					          delete[] this->event_.gatts.data.heap_data;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Clear critical fields to prevent issues if type changes
 | 
				
			||||||
 | 
					        this->event_.gatts.gatts_param = nullptr;
 | 
				
			||||||
 | 
					        this->event_.gatts.is_inline = false;
 | 
				
			||||||
 | 
					        this->event_.gatts.data.heap_data = nullptr;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -207,22 +220,30 @@ class BLEEvent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
					    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
				
			||||||
    struct gattc_event {
 | 
					    struct gattc_event {
 | 
				
			||||||
      esp_gattc_cb_event_t gattc_event;
 | 
					      esp_ble_gattc_cb_param_t *gattc_param;  // Heap-allocated (4 bytes)
 | 
				
			||||||
      esp_gatt_if_t gattc_if;
 | 
					      esp_gattc_cb_event_t gattc_event;       // 4 bytes
 | 
				
			||||||
      esp_ble_gattc_cb_param_t *gattc_param;  // Heap-allocated
 | 
					      union {
 | 
				
			||||||
      uint8_t *data;                          // Heap-allocated raw buffer (manually managed)
 | 
					        uint8_t *heap_data;                     // 4 bytes when heap-allocated
 | 
				
			||||||
      uint16_t data_len;                      // Track size separately
 | 
					        uint8_t inline_data[INLINE_DATA_SIZE];  // INLINE_DATA_SIZE bytes when stored inline
 | 
				
			||||||
    } gattc;
 | 
					      } data;                                   // INLINE_DATA_SIZE bytes total
 | 
				
			||||||
 | 
					      uint16_t data_len;                        // 2 bytes
 | 
				
			||||||
 | 
					      esp_gatt_if_t gattc_if;                   // 1 byte
 | 
				
			||||||
 | 
					      bool is_inline;                           // 1 byte - true when data is stored inline
 | 
				
			||||||
 | 
					    } gattc;                                    // Total: 80 bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
					    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
				
			||||||
    struct gatts_event {
 | 
					    struct gatts_event {
 | 
				
			||||||
      esp_gatts_cb_event_t gatts_event;
 | 
					      esp_ble_gatts_cb_param_t *gatts_param;  // Heap-allocated (4 bytes)
 | 
				
			||||||
      esp_gatt_if_t gatts_if;
 | 
					      esp_gatts_cb_event_t gatts_event;       // 4 bytes
 | 
				
			||||||
      esp_ble_gatts_cb_param_t *gatts_param;  // Heap-allocated
 | 
					      union {
 | 
				
			||||||
      uint8_t *data;                          // Heap-allocated raw buffer (manually managed)
 | 
					        uint8_t *heap_data;                     // 4 bytes when heap-allocated
 | 
				
			||||||
      uint16_t data_len;                      // Track size separately
 | 
					        uint8_t inline_data[INLINE_DATA_SIZE];  // INLINE_DATA_SIZE bytes when stored inline
 | 
				
			||||||
    } gatts;
 | 
					      } data;                                   // INLINE_DATA_SIZE bytes total
 | 
				
			||||||
  } event_;  // 80 bytes
 | 
					      uint16_t data_len;                        // 2 bytes
 | 
				
			||||||
 | 
					      esp_gatt_if_t gatts_if;                   // 1 byte
 | 
				
			||||||
 | 
					      bool is_inline;                           // 1 byte - true when data is stored inline
 | 
				
			||||||
 | 
					    } gatts;                                    // Total: 80 bytes
 | 
				
			||||||
 | 
					  } event_;                                     // 80 bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ble_event_t type_;
 | 
					  ble_event_t type_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -236,6 +257,29 @@ class BLEEvent {
 | 
				
			|||||||
  const esp_ble_sec_t &security() const { return event_.gap.security; }
 | 
					  const esp_ble_sec_t &security() const { return event_.gap.security; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
 | 
					  // Helper to copy data with inline storage optimization
 | 
				
			||||||
 | 
					  template<typename EventStruct>
 | 
				
			||||||
 | 
					  void copy_data_with_inline_storage_(EventStruct &event, const uint8_t *src_data, uint16_t len,
 | 
				
			||||||
 | 
					                                      uint8_t **param_value_ptr) {
 | 
				
			||||||
 | 
					    event.data_len = len;
 | 
				
			||||||
 | 
					    if (len > 0) {
 | 
				
			||||||
 | 
					      if (len <= INLINE_DATA_SIZE) {
 | 
				
			||||||
 | 
					        event.is_inline = true;
 | 
				
			||||||
 | 
					        memcpy(event.data.inline_data, src_data, len);
 | 
				
			||||||
 | 
					        *param_value_ptr = event.data.inline_data;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        event.is_inline = false;
 | 
				
			||||||
 | 
					        event.data.heap_data = new uint8_t[len];
 | 
				
			||||||
 | 
					        memcpy(event.data.heap_data, src_data, len);
 | 
				
			||||||
 | 
					        *param_value_ptr = event.data.heap_data;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      event.is_inline = false;
 | 
				
			||||||
 | 
					      event.data.heap_data = nullptr;
 | 
				
			||||||
 | 
					      *param_value_ptr = nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Initialize GAP event data
 | 
					  // Initialize GAP event data
 | 
				
			||||||
  void init_gap_data_(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
 | 
					  void init_gap_data_(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
 | 
				
			||||||
    this->event_.gap.gap_event = e;
 | 
					    this->event_.gap.gap_event = e;
 | 
				
			||||||
@@ -321,12 +365,13 @@ class BLEEvent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (p == nullptr) {
 | 
					    if (p == nullptr) {
 | 
				
			||||||
      this->event_.gattc.gattc_param = nullptr;
 | 
					      this->event_.gattc.gattc_param = nullptr;
 | 
				
			||||||
      this->event_.gattc.data = nullptr;
 | 
					      this->event_.gattc.is_inline = false;
 | 
				
			||||||
 | 
					      this->event_.gattc.data.heap_data = nullptr;
 | 
				
			||||||
      this->event_.gattc.data_len = 0;
 | 
					      this->event_.gattc.data_len = 0;
 | 
				
			||||||
      return;  // Invalid event, but we can't log in header file
 | 
					      return;  // Invalid event, but we can't log in header file
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Heap-allocate param and data
 | 
					    // Heap-allocate param
 | 
				
			||||||
    // Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
 | 
					    // Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
 | 
				
			||||||
    // while GAP events (99%) are stored inline to minimize memory usage
 | 
					    // while GAP events (99%) are stored inline to minimize memory usage
 | 
				
			||||||
    // IMPORTANT: This heap allocation provides clear ownership semantics:
 | 
					    // IMPORTANT: This heap allocation provides clear ownership semantics:
 | 
				
			||||||
@@ -340,28 +385,17 @@ class BLEEvent {
 | 
				
			|||||||
    // We must copy this data to ensure it remains valid when the event is processed later.
 | 
					    // We must copy this data to ensure it remains valid when the event is processed later.
 | 
				
			||||||
    switch (e) {
 | 
					    switch (e) {
 | 
				
			||||||
      case ESP_GATTC_NOTIFY_EVT:
 | 
					      case ESP_GATTC_NOTIFY_EVT:
 | 
				
			||||||
        this->event_.gattc.data_len = p->notify.value_len;
 | 
					        copy_data_with_inline_storage_(this->event_.gattc, p->notify.value, p->notify.value_len,
 | 
				
			||||||
        if (p->notify.value_len > 0) {
 | 
					                                       &this->event_.gattc.gattc_param->notify.value);
 | 
				
			||||||
          this->event_.gattc.data = new uint8_t[p->notify.value_len];
 | 
					 | 
				
			||||||
          memcpy(this->event_.gattc.data, p->notify.value, p->notify.value_len);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          this->event_.gattc.data = nullptr;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data;
 | 
					 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case ESP_GATTC_READ_CHAR_EVT:
 | 
					      case ESP_GATTC_READ_CHAR_EVT:
 | 
				
			||||||
      case ESP_GATTC_READ_DESCR_EVT:
 | 
					      case ESP_GATTC_READ_DESCR_EVT:
 | 
				
			||||||
        this->event_.gattc.data_len = p->read.value_len;
 | 
					        copy_data_with_inline_storage_(this->event_.gattc, p->read.value, p->read.value_len,
 | 
				
			||||||
        if (p->read.value_len > 0) {
 | 
					                                       &this->event_.gattc.gattc_param->read.value);
 | 
				
			||||||
          this->event_.gattc.data = new uint8_t[p->read.value_len];
 | 
					 | 
				
			||||||
          memcpy(this->event_.gattc.data, p->read.value, p->read.value_len);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          this->event_.gattc.data = nullptr;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this->event_.gattc.gattc_param->read.value = this->event_.gattc.data;
 | 
					 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        this->event_.gattc.data = nullptr;
 | 
					        this->event_.gattc.is_inline = false;
 | 
				
			||||||
 | 
					        this->event_.gattc.data.heap_data = nullptr;
 | 
				
			||||||
        this->event_.gattc.data_len = 0;
 | 
					        this->event_.gattc.data_len = 0;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -374,12 +408,13 @@ class BLEEvent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (p == nullptr) {
 | 
					    if (p == nullptr) {
 | 
				
			||||||
      this->event_.gatts.gatts_param = nullptr;
 | 
					      this->event_.gatts.gatts_param = nullptr;
 | 
				
			||||||
      this->event_.gatts.data = nullptr;
 | 
					      this->event_.gatts.is_inline = false;
 | 
				
			||||||
 | 
					      this->event_.gatts.data.heap_data = nullptr;
 | 
				
			||||||
      this->event_.gatts.data_len = 0;
 | 
					      this->event_.gatts.data_len = 0;
 | 
				
			||||||
      return;  // Invalid event, but we can't log in header file
 | 
					      return;  // Invalid event, but we can't log in header file
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Heap-allocate param and data
 | 
					    // Heap-allocate param
 | 
				
			||||||
    // Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
 | 
					    // Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
 | 
				
			||||||
    // while GAP events (99%) are stored inline to minimize memory usage
 | 
					    // while GAP events (99%) are stored inline to minimize memory usage
 | 
				
			||||||
    // IMPORTANT: This heap allocation provides clear ownership semantics:
 | 
					    // IMPORTANT: This heap allocation provides clear ownership semantics:
 | 
				
			||||||
@@ -393,17 +428,12 @@ class BLEEvent {
 | 
				
			|||||||
    // We must copy this data to ensure it remains valid when the event is processed later.
 | 
					    // We must copy this data to ensure it remains valid when the event is processed later.
 | 
				
			||||||
    switch (e) {
 | 
					    switch (e) {
 | 
				
			||||||
      case ESP_GATTS_WRITE_EVT:
 | 
					      case ESP_GATTS_WRITE_EVT:
 | 
				
			||||||
        this->event_.gatts.data_len = p->write.len;
 | 
					        copy_data_with_inline_storage_(this->event_.gatts, p->write.value, p->write.len,
 | 
				
			||||||
        if (p->write.len > 0) {
 | 
					                                       &this->event_.gatts.gatts_param->write.value);
 | 
				
			||||||
          this->event_.gatts.data = new uint8_t[p->write.len];
 | 
					 | 
				
			||||||
          memcpy(this->event_.gatts.data, p->write.value, p->write.len);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          this->event_.gatts.data = nullptr;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this->event_.gatts.gatts_param->write.value = this->event_.gatts.data;
 | 
					 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        this->event_.gatts.data = nullptr;
 | 
					        this->event_.gatts.is_inline = false;
 | 
				
			||||||
 | 
					        this->event_.gatts.data.heap_data = nullptr;
 | 
				
			||||||
        this->event_.gatts.data_len = 0;
 | 
					        this->event_.gatts.data_len = 0;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -414,6 +444,15 @@ class BLEEvent {
 | 
				
			|||||||
// The gap member in the union should be 80 bytes (including the gap_event enum)
 | 
					// The gap member in the union should be 80 bytes (including the gap_event enum)
 | 
				
			||||||
static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)) <= 80, "gap_event struct has grown beyond 80 bytes");
 | 
					static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)) <= 80, "gap_event struct has grown beyond 80 bytes");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Verify GATTC and GATTS structs don't exceed GAP struct size
 | 
				
			||||||
 | 
					// This ensures the union size is determined by GAP (the most common event type)
 | 
				
			||||||
 | 
					static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gattc)) <=
 | 
				
			||||||
 | 
					                  sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)),
 | 
				
			||||||
 | 
					              "gattc_event struct exceeds gap_event size - union size would increase");
 | 
				
			||||||
 | 
					static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gatts)) <=
 | 
				
			||||||
 | 
					                  sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)),
 | 
				
			||||||
 | 
					              "gatts_event struct exceeds gap_event size - union size would increase");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Verify esp_ble_sec_t fits within our union
 | 
					// Verify esp_ble_sec_t fits within our union
 | 
				
			||||||
static_assert(sizeof(esp_ble_sec_t) <= 73, "esp_ble_sec_t is larger than BLEScanResult");
 | 
					static_assert(sizeof(esp_ble_sec_t) <= 73, "esp_ble_sec_t is larger than BLEScanResult");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user