From d71996e58d57d70446d9eb4d6cc023263e9c779c Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 23 Aug 2021 10:43:54 +0200 Subject: [PATCH] Reduce static RAM usage (#2140) --- esphome/components/light/light_call.cpp | 37 +++--- esphome/components/ota/ota_component.cpp | 11 +- esphome/components/sntp/sntp_component.cpp | 5 +- esphome/components/time/real_time_clock.cpp | 5 +- .../wifi/wifi_component_esp8266.cpp | 112 ++++++++++-------- esphome/core/log.h | 25 ++++ 6 files changed, 113 insertions(+), 82 deletions(-) diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index 6945d37ded..d979b13368 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -8,26 +8,23 @@ namespace light { static const char *const TAG = "light"; static const char *color_mode_to_human(ColorMode color_mode) { - switch (color_mode) { - case ColorMode::UNKNOWN: - return "Unknown"; - case ColorMode::WHITE: - return "White"; - case ColorMode::COLOR_TEMPERATURE: - return "Color temperature"; - case ColorMode::COLD_WARM_WHITE: - return "Cold/warm white"; - case ColorMode::RGB: - return "RGB"; - case ColorMode::RGB_WHITE: - return "RGBW"; - case ColorMode::RGB_COLD_WARM_WHITE: - return "RGB + cold/warm white"; - case ColorMode::RGB_COLOR_TEMPERATURE: - return "RGB + color temperature"; - default: - return ""; - } + if (color_mode == ColorMode::UNKNOWN) + return "Unknown"; + if (color_mode == ColorMode::WHITE) + return "White"; + if (color_mode == ColorMode::COLOR_TEMPERATURE) + return "Color temperature"; + if (color_mode == ColorMode::COLD_WARM_WHITE) + return "Cold/warm white"; + if (color_mode == ColorMode::RGB) + return "RGB"; + if (color_mode == ColorMode::RGB_WHITE) + return "RGBW"; + if (color_mode == ColorMode::RGB_COLD_WARM_WHITE) + return "RGB + cold/warm white"; + if (color_mode == ColorMode::RGB_COLOR_TEMPERATURE) + return "RGB + color temperature"; + return ""; } void LightCall::perform() { diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 71f8101704..ac72befb9e 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -178,28 +178,29 @@ void OTAComponent::handle_() { #endif if (!Update.begin(ota_size, U_FLASH)) { + uint8_t error = Update.getError(); StreamString ss; Update.printError(ss); #ifdef ARDUINO_ARCH_ESP8266 - if (ss.indexOf("Invalid bootstrapping") != -1) { + if (error == UPDATE_ERROR_BOOTSTRAP) { error_code = OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; goto error; } - if (ss.indexOf("new Flash config wrong") != -1 || ss.indexOf("new Flash config wsong") != -1) { + if (error == UPDATE_ERROR_NEW_FLASH_CONFIG) { error_code = OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; goto error; } - if (ss.indexOf("Flash config wrong real") != -1 || ss.indexOf("Flash config wsong real") != -1) { + if (error == UPDATE_ERROR_FLASH_CONFIG) { error_code = OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; goto error; } - if (ss.indexOf("Not Enough Space") != -1) { + if (error == UPDATE_ERROR_SPACE) { error_code = OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; goto error; } #endif #ifdef ARDUINO_ARCH_ESP32 - if (ss.indexOf("Bad Size Given") != -1) { + if (error == UPDATE_ERROR_SIZE) { error_code = OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; goto error; } diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index ff176b1d4e..895c775b19 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -56,9 +56,8 @@ void SNTPComponent::loop() { if (!time.is_valid()) return; - char buf[128]; - time.strftime(buf, sizeof(buf), "%c"); - ESP_LOGD(TAG, "Synchronized time: %s", buf); + ESP_LOGD(TAG, "Synchronized time: %d-%d-%d %d:%d:%d", time.year, time.month, time.day_of_month, time.hour, + time.minute, time.second); this->time_sync_callback_.call(); this->has_time_ = true; } diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index c2a93b5191..27a2d84da6 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -35,9 +35,8 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) { } auto time = this->now(); - char buf[128]; - time.strftime(buf, sizeof(buf), "%c"); - ESP_LOGD(TAG, "Synchronized time: %s", buf); + ESP_LOGD(TAG, "Synchronized time: %d-%d-%d %d:%d:%d", time.year, time.month, time.day_of_month, time.hour, + time.minute, time.second); this->time_sync_callback_.call(); } diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index ab68f421a8..ad1a64d1f4 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -366,65 +366,75 @@ const char *get_op_mode_str(uint8_t mode) { return "UNKNOWN"; } } +// Note that this method returns PROGMEM strings, so use LOG_STR_ARG() to access them. const char *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_AUTH_EXPIRE: - return "Auth Expired"; - case REASON_AUTH_LEAVE: - return "Auth Leave"; - case REASON_ASSOC_EXPIRE: - return "Association Expired"; - case REASON_ASSOC_TOOMANY: - return "Too Many Associations"; - case REASON_NOT_AUTHED: - return "Not Authenticated"; - case REASON_NOT_ASSOCED: - return "Not Associated"; - case REASON_ASSOC_LEAVE: - return "Association Leave"; - case REASON_ASSOC_NOT_AUTHED: - return "Association not Authenticated"; - case REASON_DISASSOC_PWRCAP_BAD: - return "Disassociate Power Cap Bad"; - case REASON_DISASSOC_SUPCHAN_BAD: - return "Disassociate Supported Channel Bad"; - case REASON_IE_INVALID: - return "IE Invalid"; - case REASON_MIC_FAILURE: - return "Mic Failure"; - case REASON_4WAY_HANDSHAKE_TIMEOUT: - return "4-Way Handshake Timeout"; - case REASON_GROUP_KEY_UPDATE_TIMEOUT: - return "Group Key Update Timeout"; - case REASON_IE_IN_4WAY_DIFFERS: - return "IE In 4-Way Handshake Differs"; - case REASON_GROUP_CIPHER_INVALID: - return "Group Cipher Invalid"; - case REASON_PAIRWISE_CIPHER_INVALID: - return "Pairwise Cipher Invalid"; - case REASON_AKMP_INVALID: - return "AKMP Invalid"; - case REASON_UNSUPP_RSN_IE_VERSION: - return "Unsupported RSN IE version"; - case REASON_INVALID_RSN_IE_CAP: - return "Invalid RSN IE Cap"; - case REASON_802_1X_AUTH_FAILED: - return "802.1x Authentication Failed"; - case REASON_CIPHER_SUITE_REJECTED: - return "Cipher Suite Rejected"; case REASON_BEACON_TIMEOUT: - return "Beacon Timeout"; + return LOG_STR("Beacon Timeout"); case REASON_NO_AP_FOUND: - return "AP Not Found"; + return LOG_STR("AP Not Found"); case REASON_AUTH_FAIL: - return "Authentication Failed"; + return LOG_STR("Authentication Failed"); case REASON_ASSOC_FAIL: - return "Association Failed"; + return LOG_STR("Association Failed"); case REASON_HANDSHAKE_TIMEOUT: - return "Handshake Failed"; + return LOG_STR("Handshake Failed"); case REASON_UNSPECIFIED: default: - return "Unspecified"; + return LOG_STR("Unspecified"); } } @@ -448,7 +458,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); } else { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, - format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); + format_mac_addr(it.bssid).c_str(), LOG_STR_ARG(get_disconnect_reason_str(it.reason))); } break; } diff --git a/esphome/core/log.h b/esphome/core/log.h index 0eec28101f..fbaaf14408 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -7,6 +7,7 @@ #include "WString.h" #endif +#include "esphome/core/macros.h" // avoid esp-idf redefining our macros #include "esphome/core/esphal.h" @@ -162,4 +163,28 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #define ONOFF(b) ((b) ? "ON" : "OFF") #define TRUEFALSE(b) ((b) ? "TRUE" : "FALSE") +#ifdef USE_STORE_LOG_STR_IN_FLASH +#define LOG_STR(s) PSTR(s) + +// From Arduino 2.5 onwards, we can pass a PSTR() to printf(). For previous versions, emulate support +// by copying the message to a local buffer first. String length is limited to 63 characters. +// https://github.com/esp8266/Arduino/commit/6280e98b0360f85fdac2b8f10707fffb4f6e6e31 +#include +#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 5, 0) +#define LOG_STR_ARG(s) \ + ({ \ + char __buf[64]; \ + __buf[63] = '\0'; \ + strncpy_P(__buf, s, 63); \ + __buf; \ + }) +#else +#define LOG_STR_ARG(s) (s) +#endif + +#else +#define LOG_STR(s) (s) +#define LOG_STR_ARG(s) (s) +#endif + } // namespace esphome