diff --git a/esphome/analyze_memory/const.py b/esphome/analyze_memory/const.py index 78af82059fe..8dd6664bc00 100644 --- a/esphome/analyze_memory/const.py +++ b/esphome/analyze_memory/const.py @@ -88,6 +88,77 @@ SYMBOL_PATTERNS = { "sys_mbox_new", "sys_arch_mbox_tryfetch", ], + # LibreTiny/Beken BK7231 radio calibration + "bk_radio_cal": [ + "bk7011_", + "calibration_main", + "gcali_", + "rwnx_cal", + ], + # LibreTiny/Beken WiFi MAC layer + "bk_wifi_mac": [ + "rxu_", # RX upper layer + "txu_", # TX upper layer + "txl_", # TX lower layer + "rxl_", # RX lower layer + "scanu_", # Scan unit + "mm_hw_", # MAC management hardware + "mm_bcn", # MAC management beacon + "mm_tim", # MAC management TIM + "mm_check", # MAC management checks + "sm_connect", # Station management + "me_beacon", # Management entity beacon + "me_build", # Management entity build + "hapd_", # Host AP daemon + "chan_pre_", # Channel management + "handle_probe_", # Probe handling + ], + # LibreTiny/Beken system control + "bk_system": [ + "sctrl_", # System control + "icu_ctrl", # Interrupt control unit + "gdma_ctrl", # DMA control + "mpb_ctrl", # MPB control + "uf2_", # UF2 OTA + "bkreg_", # Beken registers + ], + # LibreTiny/Beken BLE stack + "bk_ble": [ + "gapc_", # GAP client + "gattc_", # GATT client + "attc_", # ATT client + "attmdb_", # ATT database + "atts_", # ATT server + "l2cc_", # L2CAP + "prf_env", # Profile environment + ], + # LibreTiny/Beken scheduler + "bk_scheduler": [ + "sch_plan_", # Scheduler plan + "sch_prog_", # Scheduler program + "sch_arb_", # Scheduler arbiter + ], + # LibreTiny/Beken DMA descriptors + "bk_dma": [ + "rx_payload_desc", + "rx_dma_hdrdesc", + "tx_hw_desc", + "host_event_data", + "host_cmd_data", + ], + # ARM EABI compiler runtime (LibreTiny uses ARM Cortex-M) + "arm_runtime": [ + "__aeabi_", + "__adddf3", + "__subdf3", + "__muldf3", + "__divdf3", + "__addsf3", + "__subsf3", + "__mulsf3", + "__divsf3", + "__gnu_unwind", + ], "xtensa": ["xt_", "_xt_", "xPortEnterCriticalTimeout"], "heap": ["heap_", "multi_heap"], "spi_flash": ["spi_flash"], @@ -782,7 +853,22 @@ SYMBOL_PATTERNS = { "math_internal": ["__mdiff", "__lshift", "__mprec_tens", "quorem"], "character_class": ["__chclass"], "camellia": ["camellia_", "camellia_feistel"], - "crypto_tables": ["FSb", "FSb2", "FSb3", "FSb4"], + "crypto_tables": [ + "FSb", + "FSb2", + "FSb3", + "FSb4", + "Te0", # AES encryption table + "Td0", # AES decryption table + "crc32_table", # CRC32 lookup table + "crc_tab", # CRC lookup table + ], + "crypto_hash": [ + "SHA1Transform", # SHA1 hash function + "MD5Transform", # MD5 hash function + "SHA256", + "SHA512", + ], "event_buffer": ["g_eb_list_desc", "eb_space"], "base_node": ["base_node_", "base_node_add_handler"], "file_descriptor": ["s_fd_table"], diff --git a/esphome/components/alpha3/alpha3.h b/esphome/components/alpha3/alpha3.h index 7189ecbc335..19d8e99331a 100644 --- a/esphome/components/alpha3/alpha3.h +++ b/esphome/components/alpha3/alpha3.h @@ -15,10 +15,8 @@ namespace alpha3 { namespace espbt = esphome::esp32_ble_tracker; static const espbt::ESPBTUUID ALPHA3_GENI_SERVICE_UUID = espbt::ESPBTUUID::from_uint16(0xfe5d); -static const espbt::ESPBTUUID ALPHA3_GENI_CHARACTERISTIC_UUID = - espbt::ESPBTUUID::from_raw({static_cast(0xa9), 0x7b, static_cast(0xb8), static_cast(0x85), 0x0, - 0x1a, 0x28, static_cast(0xaa), 0x2a, 0x43, 0x6e, 0x3, static_cast(0xd1), - static_cast(0xff), static_cast(0x9c), static_cast(0x85)}); +static const espbt::ESPBTUUID ALPHA3_GENI_CHARACTERISTIC_UUID = espbt::ESPBTUUID::from_raw( + {0xa9, 0x7b, 0xb8, 0x85, 0x00, 0x1a, 0x28, 0xaa, 0x2a, 0x43, 0x6e, 0x03, 0xd1, 0xff, 0x9c, 0x85}); static const int16_t GENI_RESPONSE_HEADER_LENGTH = 13; static const size_t GENI_RESPONSE_TYPE_LENGTH = 8; diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index ab88df8121b..ae593955a44 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -31,7 +31,9 @@ class ESPBTUUID { static ESPBTUUID from_raw(const char *data, size_t length); static ESPBTUUID from_raw(const char *data) { return from_raw(data, strlen(data)); } static ESPBTUUID from_raw(const std::string &data) { return from_raw(data.c_str(), data.length()); } - static ESPBTUUID from_raw(std::initializer_list data) { return from_raw(data.begin(), data.size()); } + static ESPBTUUID from_raw(std::initializer_list data) { + return from_raw(reinterpret_cast(data.begin()), data.size()); + } static ESPBTUUID from_uuid(esp_bt_uuid_t uuid); diff --git a/esphome/components/mopeka_ble/mopeka_ble.cpp b/esphome/components/mopeka_ble/mopeka_ble.cpp index 07c8ac5d712..bd3ecbeecb3 100644 --- a/esphome/components/mopeka_ble/mopeka_ble.cpp +++ b/esphome/components/mopeka_ble/mopeka_ble.cpp @@ -62,7 +62,8 @@ bool MopekaListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) const bool sync_button_pressed = (manu_data.data[3] & 0x80) != 0; if (this->show_sensors_without_sync_ || sync_button_pressed) { - ESP_LOGI(TAG, "MOPEKA STD (CC2540) SENSOR FOUND: %s", device.address_str().c_str()); + char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + ESP_LOGI(TAG, "MOPEKA STD (CC2540) SENSOR FOUND: %s", device.address_str_to(addr_buf)); } // Is the device maybe a Mopeka Pro (NRF52) sensor. @@ -78,7 +79,8 @@ bool MopekaListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) const bool sync_button_pressed = (manu_data.data[2] & 0x80) != 0; if (this->show_sensors_without_sync_ || sync_button_pressed) { - ESP_LOGI(TAG, "MOPEKA PRO (NRF52) SENSOR FOUND: %s", device.address_str().c_str()); + char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + ESP_LOGI(TAG, "MOPEKA PRO (NRF52) SENSOR FOUND: %s", device.address_str_to(addr_buf)); } } diff --git a/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp b/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp index 42d61f81a38..9bc9900a5ac 100644 --- a/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +++ b/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp @@ -31,7 +31,8 @@ bool MopekaProCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) return false; } - ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str_to(addr_buf)); const auto &manu_datas = device.get_manufacturer_datas(); diff --git a/esphome/components/mopeka_std_check/mopeka_std_check.cpp b/esphome/components/mopeka_std_check/mopeka_std_check.cpp index 231d09b909a..6322b550c94 100644 --- a/esphome/components/mopeka_std_check/mopeka_std_check.cpp +++ b/esphome/components/mopeka_std_check/mopeka_std_check.cpp @@ -35,15 +35,17 @@ void MopekaStdCheck::dump_config() { * update the sensor state data. */ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { - { - // Validate address. - if (device.address_uint64() != this->address_) { - return false; - } - - ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + // Validate address. + if (device.address_uint64() != this->address_) { + return false; } + // Stack buffer for MAC address formatting - reused throughout function + char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + const char *addr_str = device.address_str_to(addr_buf); + + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", addr_str); + { // Validate service uuid const auto &service_uuids = device.get_service_uuids(); @@ -59,7 +61,7 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) const auto &manu_datas = device.get_manufacturer_datas(); if (manu_datas.size() != 1) { - ESP_LOGE(TAG, "[%s] Unexpected manu_datas size (%d)", device.address_str().c_str(), manu_datas.size()); + ESP_LOGE(TAG, "[%s] Unexpected manu_datas size (%d)", addr_str, manu_datas.size()); return false; } @@ -68,11 +70,11 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE char hex_buf[format_hex_pretty_size(MOPEKA_MAX_LOG_BYTES)]; #endif - ESP_LOGVV(TAG, "[%s] Manufacturer data: %s", device.address_str().c_str(), + ESP_LOGVV(TAG, "[%s] Manufacturer data: %s", addr_str, format_hex_pretty_to(hex_buf, manu_data.data.data(), manu_data.data.size())); if (manu_data.data.size() != MANUFACTURER_DATA_LENGTH) { - ESP_LOGE(TAG, "[%s] Unexpected manu_data size (%d)", device.address_str().c_str(), manu_data.data.size()); + ESP_LOGE(TAG, "[%s] Unexpected manu_data size (%d)", addr_str, manu_data.data.size()); return false; } @@ -82,21 +84,21 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) const u_int8_t hardware_id = mopeka_data->data_1 & 0xCF; if (static_cast(hardware_id) != STANDARD && static_cast(hardware_id) != XL && static_cast(hardware_id) != ETRAILER && static_cast(hardware_id) != STANDARD_ALT) { - ESP_LOGE(TAG, "[%s] Unsupported Sensor Type (0x%X)", device.address_str().c_str(), hardware_id); + ESP_LOGE(TAG, "[%s] Unsupported Sensor Type (0x%X)", addr_str, hardware_id); return false; } - ESP_LOGVV(TAG, "[%s] Sensor slow update rate: %d", device.address_str().c_str(), mopeka_data->slow_update_rate); - ESP_LOGVV(TAG, "[%s] Sensor sync pressed: %d", device.address_str().c_str(), mopeka_data->sync_pressed); + ESP_LOGVV(TAG, "[%s] Sensor slow update rate: %d", addr_str, mopeka_data->slow_update_rate); + ESP_LOGVV(TAG, "[%s] Sensor sync pressed: %d", addr_str, mopeka_data->sync_pressed); for (u_int8_t i = 0; i < 3; i++) { - ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 1, - mopeka_data->val[i].value_0, mopeka_data->val[i].time_0); - ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 2, - mopeka_data->val[i].value_1, mopeka_data->val[i].time_1); - ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 3, - mopeka_data->val[i].value_2, mopeka_data->val[i].time_2); - ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 4, - mopeka_data->val[i].value_3, mopeka_data->val[i].time_3); + ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", addr_str, (i * 4) + 1, mopeka_data->val[i].value_0, + mopeka_data->val[i].time_0); + ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", addr_str, (i * 4) + 2, mopeka_data->val[i].value_1, + mopeka_data->val[i].time_1); + ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", addr_str, (i * 4) + 3, mopeka_data->val[i].value_2, + mopeka_data->val[i].time_2); + ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", addr_str, (i * 4) + 4, mopeka_data->val[i].value_3, + mopeka_data->val[i].time_3); } // Get battery level first @@ -163,12 +165,12 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) } } - ESP_LOGV(TAG, "[%s] Found %u values with best data %u time %u.", device.address_str().c_str(), - number_of_usable_values, best_value, best_time); + ESP_LOGV(TAG, "[%s] Found %u values with best data %u time %u.", addr_str, number_of_usable_values, best_value, + best_time); if (number_of_usable_values < 1 || best_value < 2 || best_time < 2) { // At least two measurement values must be present. - ESP_LOGW(TAG, "[%s] Poor read quality. Setting distance to 0.", device.address_str().c_str()); + ESP_LOGW(TAG, "[%s] Poor read quality. Setting distance to 0.", addr_str); if (this->distance_ != nullptr) { this->distance_->publish_state(0); } @@ -177,7 +179,7 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) } } else { float lpg_speed_of_sound = this->get_lpg_speed_of_sound_(temp_in_c); - ESP_LOGV(TAG, "[%s] Speed of sound in current fluid %f m/s", device.address_str().c_str(), lpg_speed_of_sound); + ESP_LOGV(TAG, "[%s] Speed of sound in current fluid %f m/s", addr_str, lpg_speed_of_sound); uint32_t distance_value = lpg_speed_of_sound * best_time / 100.0f; diff --git a/esphome/components/whirlpool/whirlpool.cpp b/esphome/components/whirlpool/whirlpool.cpp index 1ac32f30daf..6fe735362dc 100644 --- a/esphome/components/whirlpool/whirlpool.cpp +++ b/esphome/components/whirlpool/whirlpool.cpp @@ -163,6 +163,7 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) { } uint8_t remote_state[WHIRLPOOL_STATE_LENGTH] = {0}; + bool skip_footer = false; // Read all bytes. for (int i = 0; i < WHIRLPOOL_STATE_LENGTH; i++) { // Read bit @@ -170,6 +171,13 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) { if (!data.expect_item(WHIRLPOOL_BIT_MARK, WHIRLPOOL_GAP)) return false; } + if (i == 14 && !data.is_valid()) { + // Remote control only sent 14 bytes, nothing more to read, not even the footer + ESP_LOGV(TAG, "Remote control only sent %d bytes", i); + skip_footer = true; + break; + } + for (int j = 0; j < 8; j++) { if (data.expect_item(WHIRLPOOL_BIT_MARK, WHIRLPOOL_ONE_SPACE)) { remote_state[i] |= 1 << j; @@ -183,7 +191,7 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) { ESP_LOGVV(TAG, "Byte %d %02X", i, remote_state[i]); } // Validate footer - if (!data.expect_mark(WHIRLPOOL_BIT_MARK)) { + if (!data.expect_mark(WHIRLPOOL_BIT_MARK) && !skip_footer) { ESP_LOGV(TAG, "Footer fail"); return false; } @@ -196,7 +204,7 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) { for (uint8_t i = 14; i < 20; i++) checksum20 ^= remote_state[i]; - if (checksum13 != remote_state[13] || checksum20 != remote_state[20]) { + if (checksum13 != remote_state[13] || (!skip_footer && checksum20 != remote_state[20])) { ESP_LOGVV(TAG, "Checksum fail"); return false; } diff --git a/script/ci_memory_impact_extract.py b/script/ci_memory_impact_extract.py index 77d59417e35..dd91fa861ce 100755 --- a/script/ci_memory_impact_extract.py +++ b/script/ci_memory_impact_extract.py @@ -92,18 +92,23 @@ def run_detailed_analysis(build_dir: str) -> dict | None: print(f"Build directory not found: {build_dir}", file=sys.stderr) return None - # Find firmware.elf + # Find firmware.elf (or raw_firmware.elf for LibreTiny) elf_path = None for elf_candidate in [ build_path / "firmware.elf", build_path / ".pioenvs" / build_path.name / "firmware.elf", + # LibreTiny uses raw_firmware.elf + build_path / "raw_firmware.elf", + build_path / ".pioenvs" / build_path.name / "raw_firmware.elf", ]: if elf_candidate.exists(): elf_path = str(elf_candidate) break if not elf_path: - print(f"firmware.elf not found in {build_dir}", file=sys.stderr) + print( + f"firmware.elf/raw_firmware.elf not found in {build_dir}", file=sys.stderr + ) return None # Find idedata.json - check multiple locations diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 5cc3f2570aa..44e8e4b5ab7 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -89,6 +89,7 @@ class Platform(StrEnum): ESP32_C6_IDF = "esp32-c6-idf" ESP32_S2_IDF = "esp32-s2-idf" ESP32_S3_IDF = "esp32-s3-idf" + BK72XX_ARD = "bk72xx-ard" # LibreTiny BK7231N # Memory impact analysis constants @@ -120,6 +121,7 @@ PLATFORM_SPECIFIC_COMPONENTS = frozenset( # fastest build times, most sensitive to code size changes # 3. ESP32 IDF - Primary ESP32 platform, most representative of modern ESPHome # 4-6. Other ESP32 variants - Less commonly used but still supported +# 7. BK72XX - LibreTiny platform (good for detecting LibreTiny-specific changes) MEMORY_IMPACT_PLATFORM_PREFERENCE = [ Platform.ESP32_C6_IDF, # ESP32-C6 IDF (newest, supports Thread/Zigbee) Platform.ESP8266_ARD, # ESP8266 Arduino (most memory constrained, fastest builds) @@ -127,6 +129,7 @@ MEMORY_IMPACT_PLATFORM_PREFERENCE = [ Platform.ESP32_C3_IDF, # ESP32-C3 IDF Platform.ESP32_S2_IDF, # ESP32-S2 IDF Platform.ESP32_S3_IDF, # ESP32-S3 IDF + Platform.BK72XX_ARD, # LibreTiny BK7231N ] @@ -404,7 +407,7 @@ def _detect_platform_hint_from_filename(filename: str) -> Platform | None: - wifi_component_esp_idf.cpp, *_idf.h -> ESP32 IDF variants - wifi_component_esp8266.cpp, *_esp8266.h -> ESP8266_ARD - *_esp32*.cpp -> ESP32 IDF (generic) - - *_libretiny.cpp, *_retiny.* -> LibreTiny (not in preference list) + - *_libretiny.cpp, *_bk72*.* -> BK72XX (LibreTiny) - *_pico.cpp, *_rp2040.* -> RP2040 (not in preference list) Args: @@ -438,10 +441,11 @@ def _detect_platform_hint_from_filename(filename: str) -> Platform | None: if "esp32" in filename_lower: return Platform.ESP32_IDF - # LibreTiny and RP2040 are not in MEMORY_IMPACT_PLATFORM_PREFERENCE - # so we don't return them as hints - # if "retiny" in filename_lower or "libretiny" in filename_lower: - # return None # No specific LibreTiny platform preference + # LibreTiny (via 'libretiny' pattern or BK72xx-specific files) + if "libretiny" in filename_lower or "bk72" in filename_lower: + return Platform.BK72XX_ARD + + # RP2040 is not in MEMORY_IMPACT_PLATFORM_PREFERENCE # if "pico" in filename_lower or "rp2040" in filename_lower: # return None # No RP2040 platform preference