1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 00:31:58 +00:00

[wifi] Avoid jump tables in LOG_STR switch statements to save ESP8266 RAM (#13799)

This commit is contained in:
J. Nick Koston
2026-02-06 18:20:46 +01:00
committed by GitHub
parent 2917057da8
commit f9192b5f75
2 changed files with 81 additions and 99 deletions

View File

@@ -236,25 +236,23 @@ static const char *const TAG = "wifi";
/// │ - Roaming fail (RECONNECTING→IDLE): counter preserved (ping-pong) │
/// └──────────────────────────────────────────────────────────────────────┘
// Use if-chain instead of switch to avoid jump table in RODATA (wastes RAM on ESP8266)
static const LogString *retry_phase_to_log_string(WiFiRetryPhase phase) {
switch (phase) {
case WiFiRetryPhase::INITIAL_CONNECT:
return LOG_STR("INITIAL_CONNECT");
if (phase == WiFiRetryPhase::INITIAL_CONNECT)
return LOG_STR("INITIAL_CONNECT");
#ifdef USE_WIFI_FAST_CONNECT
case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS:
return LOG_STR("FAST_CONNECT_CYCLING");
if (phase == WiFiRetryPhase::FAST_CONNECT_CYCLING_APS)
return LOG_STR("FAST_CONNECT_CYCLING");
#endif
case WiFiRetryPhase::EXPLICIT_HIDDEN:
return LOG_STR("EXPLICIT_HIDDEN");
case WiFiRetryPhase::SCAN_CONNECTING:
return LOG_STR("SCAN_CONNECTING");
case WiFiRetryPhase::RETRY_HIDDEN:
return LOG_STR("RETRY_HIDDEN");
case WiFiRetryPhase::RESTARTING_ADAPTER:
return LOG_STR("RESTARTING");
default:
return LOG_STR("UNKNOWN");
}
if (phase == WiFiRetryPhase::EXPLICIT_HIDDEN)
return LOG_STR("EXPLICIT_HIDDEN");
if (phase == WiFiRetryPhase::SCAN_CONNECTING)
return LOG_STR("SCAN_CONNECTING");
if (phase == WiFiRetryPhase::RETRY_HIDDEN)
return LOG_STR("RETRY_HIDDEN");
if (phase == WiFiRetryPhase::RESTARTING_ADAPTER)
return LOG_STR("RESTARTING");
return LOG_STR("UNKNOWN");
}
bool WiFiComponent::went_through_explicit_hidden_phase_() const {

View File

@@ -416,75 +416,65 @@ PROGMEM_STRING_TABLE(OpModeStrings, "OFF", "STA", "AP", "AP+STA", "UNKNOWN");
const LogString *get_op_mode_str(uint8_t mode) { return OpModeStrings::get_log_str(mode, OpModeStrings::LAST_INDEX); }
// Use if-chain instead of switch to avoid jump tables in RODATA (wastes RAM on ESP8266).
// A single switch would generate a sparse lookup table with ~175 default entries, wasting 700 bytes of RAM.
// Even split switches still generate smaller jump tables in RODATA.
const LogString *get_disconnect_reason_str(uint8_t reason) {
/* If this were one big switch statement, GCC would generate a lookup table for it. However, the values of the
* REASON_* constants aren't continuous, and GCC will fill in the gap with the default value -- wasting 4 bytes of RAM
* per entry. As there's ~175 default entries, this wastes 700 bytes of RAM.
*/
if (reason <= REASON_CIPHER_SUITE_REJECTED) { // This must be the last constant with a value <200
switch (reason) {
case REASON_AUTH_EXPIRE:
return LOG_STR("Auth Expired");
case REASON_AUTH_LEAVE:
return LOG_STR("Auth Leave");
case REASON_ASSOC_EXPIRE:
return LOG_STR("Association Expired");
case REASON_ASSOC_TOOMANY:
return LOG_STR("Too Many Associations");
case REASON_NOT_AUTHED:
return LOG_STR("Not Authenticated");
case REASON_NOT_ASSOCED:
return LOG_STR("Not Associated");
case REASON_ASSOC_LEAVE:
return LOG_STR("Association Leave");
case REASON_ASSOC_NOT_AUTHED:
return LOG_STR("Association not Authenticated");
case REASON_DISASSOC_PWRCAP_BAD:
return LOG_STR("Disassociate Power Cap Bad");
case REASON_DISASSOC_SUPCHAN_BAD:
return LOG_STR("Disassociate Supported Channel Bad");
case REASON_IE_INVALID:
return LOG_STR("IE Invalid");
case REASON_MIC_FAILURE:
return LOG_STR("Mic Failure");
case REASON_4WAY_HANDSHAKE_TIMEOUT:
return LOG_STR("4-Way Handshake Timeout");
case REASON_GROUP_KEY_UPDATE_TIMEOUT:
return LOG_STR("Group Key Update Timeout");
case REASON_IE_IN_4WAY_DIFFERS:
return LOG_STR("IE In 4-Way Handshake Differs");
case REASON_GROUP_CIPHER_INVALID:
return LOG_STR("Group Cipher Invalid");
case REASON_PAIRWISE_CIPHER_INVALID:
return LOG_STR("Pairwise Cipher Invalid");
case REASON_AKMP_INVALID:
return LOG_STR("AKMP Invalid");
case REASON_UNSUPP_RSN_IE_VERSION:
return LOG_STR("Unsupported RSN IE version");
case REASON_INVALID_RSN_IE_CAP:
return LOG_STR("Invalid RSN IE Cap");
case REASON_802_1X_AUTH_FAILED:
return LOG_STR("802.1x Authentication Failed");
case REASON_CIPHER_SUITE_REJECTED:
return LOG_STR("Cipher Suite Rejected");
}
}
switch (reason) {
case REASON_BEACON_TIMEOUT:
return LOG_STR("Beacon Timeout");
case REASON_NO_AP_FOUND:
return LOG_STR("AP Not Found");
case REASON_AUTH_FAIL:
return LOG_STR("Authentication Failed");
case REASON_ASSOC_FAIL:
return LOG_STR("Association Failed");
case REASON_HANDSHAKE_TIMEOUT:
return LOG_STR("Handshake Failed");
case REASON_UNSPECIFIED:
default:
return LOG_STR("Unspecified");
}
if (reason == REASON_AUTH_EXPIRE)
return LOG_STR("Auth Expired");
if (reason == REASON_AUTH_LEAVE)
return LOG_STR("Auth Leave");
if (reason == REASON_ASSOC_EXPIRE)
return LOG_STR("Association Expired");
if (reason == REASON_ASSOC_TOOMANY)
return LOG_STR("Too Many Associations");
if (reason == REASON_NOT_AUTHED)
return LOG_STR("Not Authenticated");
if (reason == REASON_NOT_ASSOCED)
return LOG_STR("Not Associated");
if (reason == REASON_ASSOC_LEAVE)
return LOG_STR("Association Leave");
if (reason == REASON_ASSOC_NOT_AUTHED)
return LOG_STR("Association not Authenticated");
if (reason == REASON_DISASSOC_PWRCAP_BAD)
return LOG_STR("Disassociate Power Cap Bad");
if (reason == REASON_DISASSOC_SUPCHAN_BAD)
return LOG_STR("Disassociate Supported Channel Bad");
if (reason == REASON_IE_INVALID)
return LOG_STR("IE Invalid");
if (reason == REASON_MIC_FAILURE)
return LOG_STR("Mic Failure");
if (reason == REASON_4WAY_HANDSHAKE_TIMEOUT)
return LOG_STR("4-Way Handshake Timeout");
if (reason == REASON_GROUP_KEY_UPDATE_TIMEOUT)
return LOG_STR("Group Key Update Timeout");
if (reason == REASON_IE_IN_4WAY_DIFFERS)
return LOG_STR("IE In 4-Way Handshake Differs");
if (reason == REASON_GROUP_CIPHER_INVALID)
return LOG_STR("Group Cipher Invalid");
if (reason == REASON_PAIRWISE_CIPHER_INVALID)
return LOG_STR("Pairwise Cipher Invalid");
if (reason == REASON_AKMP_INVALID)
return LOG_STR("AKMP Invalid");
if (reason == REASON_UNSUPP_RSN_IE_VERSION)
return LOG_STR("Unsupported RSN IE version");
if (reason == REASON_INVALID_RSN_IE_CAP)
return LOG_STR("Invalid RSN IE Cap");
if (reason == REASON_802_1X_AUTH_FAILED)
return LOG_STR("802.1x Authentication Failed");
if (reason == REASON_CIPHER_SUITE_REJECTED)
return LOG_STR("Cipher Suite Rejected");
if (reason == REASON_BEACON_TIMEOUT)
return LOG_STR("Beacon Timeout");
if (reason == REASON_NO_AP_FOUND)
return LOG_STR("AP Not Found");
if (reason == REASON_AUTH_FAIL)
return LOG_STR("Authentication Failed");
if (reason == REASON_ASSOC_FAIL)
return LOG_STR("Association Failed");
if (reason == REASON_HANDSHAKE_TIMEOUT)
return LOG_STR("Handshake Failed");
return LOG_STR("Unspecified");
}
// TODO: This callback runs in ESP8266 system context with limited stack (~2KB).
@@ -645,21 +635,15 @@ void WiFiComponent::wifi_pre_setup_() {
WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
station_status_t status = wifi_station_get_connect_status();
switch (status) {
case STATION_GOT_IP:
return WiFiSTAConnectStatus::CONNECTED;
case STATION_NO_AP_FOUND:
return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND;
;
case STATION_CONNECT_FAIL:
case STATION_WRONG_PASSWORD:
return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED;
case STATION_CONNECTING:
return WiFiSTAConnectStatus::CONNECTING;
case STATION_IDLE:
default:
return WiFiSTAConnectStatus::IDLE;
}
if (status == STATION_GOT_IP)
return WiFiSTAConnectStatus::CONNECTED;
if (status == STATION_NO_AP_FOUND)
return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND;
if (status == STATION_CONNECT_FAIL || status == STATION_WRONG_PASSWORD)
return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED;
if (status == STATION_CONNECTING)
return WiFiSTAConnectStatus::CONNECTING;
return WiFiSTAConnectStatus::IDLE;
}
bool WiFiComponent::wifi_scan_start_(bool passive) {
static bool first_scan = false;