1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 15:12:06 +00:00

[wifi] Store credentials in PROGMEM on ESP8266 to reduce RAM usage

This commit is contained in:
J. Nick Koston
2025-10-27 20:55:32 -05:00
parent 285e006637
commit 5917444e9f
4 changed files with 120 additions and 22 deletions

View File

@@ -140,17 +140,21 @@ EAP_AUTH_SCHEMA = cv.All(
cv.has_at_least_one_key(CONF_IDENTITY, CONF_CERTIFICATE),
)
CONF_AP_TIMEOUT = "ap_timeout"
CONF_SSID_DATA_ID = "ssid_data_id"
CONF_PASSWORD_DATA_ID = "password_data_id"
WIFI_NETWORK_BASE = cv.Schema(
{
cv.GenerateID(): cv.declare_id(WiFiAP),
cv.GenerateID(CONF_SSID_DATA_ID): cv.declare_id(cg.uint8),
cv.GenerateID(CONF_PASSWORD_DATA_ID): cv.declare_id(cg.uint8),
cv.Optional(CONF_SSID): cv.ssid,
cv.Optional(CONF_PASSWORD): validate_password,
cv.Optional(CONF_CHANNEL): validate_channel,
cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
}
)
CONF_AP_TIMEOUT = "ap_timeout"
WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend(
{
cv.Optional(
@@ -352,9 +356,23 @@ def manual_ip(config):
def wifi_network(config, ap, static_ip):
if CONF_SSID in config:
cg.add(ap.set_ssid(config[CONF_SSID]))
ssid = config[CONF_SSID]
if CORE.is_esp8266:
# On ESP8266, store SSID in PROGMEM to save RAM
prog_arr = cg.progmem_array(config[CONF_SSID_DATA_ID], ssid)
cg.add(ap.set_ssid_flash(prog_arr))
else:
# On ESP32/RP2040, string literals are already in rodata (flash)
cg.add(ap.set_ssid_flash(ssid))
if CONF_PASSWORD in config:
cg.add(ap.set_password(config[CONF_PASSWORD]))
password = config[CONF_PASSWORD]
if CORE.is_esp8266:
# On ESP8266, store password in PROGMEM to save RAM
prog_arr = cg.progmem_array(config[CONF_PASSWORD_DATA_ID], password)
cg.add(ap.set_password_flash(prog_arr))
else:
# On ESP32/RP2040, string literals are already in rodata (flash)
cg.add(ap.set_password_flash(password))
if CONF_EAP in config:
cg.add(ap.set_eap(eap_auth(config[CONF_EAP])))
cg.add_define("USE_WIFI_WPA2_EAP")

View File

@@ -888,19 +888,68 @@ void WiFiComponent::save_fast_connect_settings_() {
}
#endif
void WiFiAP::set_ssid(const std::string &ssid) { this->ssid_ = ssid; }
void WiFiAP::set_ssid(const std::string &ssid) {
this->ssid_storage_ = ssid;
this->ssid_ = this->ssid_storage_->c_str();
}
void WiFiAP::set_bssid(bssid_t bssid) { this->bssid_ = bssid; }
void WiFiAP::set_bssid(optional<bssid_t> bssid) { this->bssid_ = bssid; }
void WiFiAP::set_password(const std::string &password) { this->password_ = password; }
void WiFiAP::set_password(const std::string &password) {
this->password_storage_ = password;
this->password_ = this->password_storage_->c_str();
}
#ifdef USE_ESP8266
void WiFiAP::set_ssid_flash(const uint8_t *ssid) {
this->ssid_storage_.reset();
this->ssid_ = reinterpret_cast<const char *>(ssid);
}
void WiFiAP::set_password_flash(const uint8_t *password) {
this->password_storage_.reset();
this->password_ = reinterpret_cast<const char *>(password);
}
#else
void WiFiAP::set_ssid_flash(const char *ssid) {
this->ssid_storage_.reset();
this->ssid_ = ssid;
}
void WiFiAP::set_password_flash(const char *password) {
this->password_storage_.reset();
this->password_ = password;
}
#endif
#ifdef USE_WIFI_WPA2_EAP
void WiFiAP::set_eap(optional<EAPAuth> eap_auth) { this->eap_ = std::move(eap_auth); }
#endif
void WiFiAP::set_channel(optional<uint8_t> channel) { this->channel_ = channel; }
void WiFiAP::set_manual_ip(optional<ManualIP> manual_ip) { this->manual_ip_ = manual_ip; }
void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; }
const std::string &WiFiAP::get_ssid() const { return this->ssid_; }
std::string WiFiAP::get_ssid() const {
#ifdef USE_ESP8266
if (!this->ssid_storage_.has_value()) {
// Flash storage - read from PROGMEM
size_t len = strlen_P(this->ssid_);
std::string result;
result.resize(len);
memcpy_P(&result[0], this->ssid_, len);
return result;
}
#endif
return std::string(this->ssid_);
}
const optional<bssid_t> &WiFiAP::get_bssid() const { return this->bssid_; }
const std::string &WiFiAP::get_password() const { return this->password_; }
std::string WiFiAP::get_password() const {
#ifdef USE_ESP8266
if (!this->password_storage_.has_value()) {
// Flash storage - read from PROGMEM
size_t len = strlen_P(this->password_);
std::string result;
result.resize(len);
memcpy_P(&result[0], this->password_, len);
return result;
}
#endif
return std::string(this->password_);
}
#ifdef USE_WIFI_WPA2_EAP
const optional<EAPAuth> &WiFiAP::get_eap() const { return this->eap_; }
#endif

View File

@@ -135,6 +135,18 @@ class WiFiAP {
void set_bssid(bssid_t bssid);
void set_bssid(optional<bssid_t> bssid);
void set_password(const std::string &password);
/// Set SSID from flash string (PROGMEM on ESP8266, rodata on ESP32)
#ifdef USE_ESP8266
void set_ssid_flash(const uint8_t *ssid);
#else
void set_ssid_flash(const char *ssid);
#endif
/// Set password from flash string (PROGMEM on ESP8266, rodata on ESP32)
#ifdef USE_ESP8266
void set_password_flash(const uint8_t *password);
#else
void set_password_flash(const char *password);
#endif
#ifdef USE_WIFI_WPA2_EAP
void set_eap(optional<EAPAuth> eap_auth);
#endif // USE_WIFI_WPA2_EAP
@@ -142,9 +154,9 @@ class WiFiAP {
void set_priority(float priority) { priority_ = priority; }
void set_manual_ip(optional<ManualIP> manual_ip);
void set_hidden(bool hidden);
const std::string &get_ssid() const;
std::string get_ssid() const;
const optional<bssid_t> &get_bssid() const;
const std::string &get_password() const;
std::string get_password() const;
#ifdef USE_WIFI_WPA2_EAP
const optional<EAPAuth> &get_eap() const;
#endif // USE_WIFI_WPA2_EAP
@@ -154,8 +166,10 @@ class WiFiAP {
bool get_hidden() const;
protected:
std::string ssid_;
std::string password_;
const char *ssid_{nullptr};
const char *password_{nullptr};
optional<std::string> ssid_storage_;
optional<std::string> password_storage_;
optional<bssid_t> bssid_;
#ifdef USE_WIFI_WPA2_EAP
optional<EAPAuth> eap_;

View File

@@ -236,16 +236,25 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
struct station_config conf {};
memset(&conf, 0, sizeof(conf));
if (ap.get_ssid().size() > sizeof(conf.ssid)) {
std::string ssid = ap.get_ssid();
std::string password = ap.get_password();
size_t ssid_len = ssid.length();
size_t password_len = password.length();
if (ssid_len > sizeof(conf.ssid)) {
ESP_LOGE(TAG, "SSID too long");
return false;
}
if (ap.get_password().size() > sizeof(conf.password)) {
if (password_len > sizeof(conf.password)) {
ESP_LOGE(TAG, "Password too long");
return false;
}
memcpy(reinterpret_cast<char *>(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size());
memcpy(reinterpret_cast<char *>(conf.password), ap.get_password().c_str(), ap.get_password().size());
memcpy(conf.ssid, ssid.c_str(), ssid_len);
memcpy(conf.password, password.c_str(), password_len);
if (ap.get_bssid().has_value()) {
conf.bssid_set = 1;
@@ -791,27 +800,35 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
return false;
struct softap_config conf {};
if (ap.get_ssid().size() > sizeof(conf.ssid)) {
std::string ssid = ap.get_ssid();
std::string password = ap.get_password();
size_t ssid_len = ssid.length();
size_t password_len = password.length();
if (ssid_len > sizeof(conf.ssid)) {
ESP_LOGE(TAG, "AP SSID too long");
return false;
}
memcpy(reinterpret_cast<char *>(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size());
conf.ssid_len = static_cast<uint8>(ap.get_ssid().size());
memcpy(conf.ssid, ssid.c_str(), ssid_len);
conf.ssid_len = static_cast<uint8>(ssid_len);
conf.channel = ap.get_channel().value_or(1);
conf.ssid_hidden = ap.get_hidden();
conf.max_connection = 5;
conf.beacon_interval = 100;
if (ap.get_password().empty()) {
if (password_len == 0) {
conf.authmode = AUTH_OPEN;
*conf.password = 0;
} else {
conf.authmode = AUTH_WPA2_PSK;
if (ap.get_password().size() > sizeof(conf.password)) {
if (password_len > sizeof(conf.password)) {
ESP_LOGE(TAG, "AP password too long");
return false;
}
memcpy(reinterpret_cast<char *>(conf.password), ap.get_password().c_str(), ap.get_password().size());
memcpy(conf.password, password.c_str(), password_len);
}
ETS_UART_INTR_DISABLE();