diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 2aa63b87cc..3f0b61b60c 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -439,13 +439,25 @@ def safe_ip(ip): def manual_ip(config): if config is None: return None + fields = { + "static_ip": CONF_STATIC_IP, + "gateway": CONF_GATEWAY, + "subnet": CONF_SUBNET, + "dns1": CONF_DNS1, + "dns2": CONF_DNS2, + } + if CORE.is_esp8266: + # On ESP8266, .rodata is mapped to RAM. Using StructInitializer with all + # compile-time constant fields causes the compiler to place a const blob + # in .rodata, silently consuming ~20 bytes of RAM. Field-by-field assignment + # encodes the values as immediate operands in flash instructions instead. + cg.add(cg.RawExpression("wifi::ManualIP manual_ip{}")) + for member, key in fields.items(): + cg.add(cg.RawExpression(f"manual_ip.{member} = {safe_ip(config.get(key))}")) + return cg.RawExpression("manual_ip") return cg.StructInitializer( ManualIP, - ("static_ip", safe_ip(config[CONF_STATIC_IP])), - ("gateway", safe_ip(config[CONF_GATEWAY])), - ("subnet", safe_ip(config[CONF_SUBNET])), - ("dns1", safe_ip(config.get(CONF_DNS1))), - ("dns2", safe_ip(config.get(CONF_DNS2))), + *((member, safe_ip(config.get(key))) for member, key in fields.items()), ) diff --git a/tests/components/wifi/test.esp8266-ard.yaml b/tests/components/wifi/test.esp8266-ard.yaml index 709a639ad6..034b74b3fb 100644 --- a/tests/components/wifi/test.esp8266-ard.yaml +++ b/tests/components/wifi/test.esp8266-ard.yaml @@ -1,6 +1,12 @@ wifi: min_auth_mode: WPA2 post_connect_roaming: true + manual_ip: + static_ip: 192.168.1.100 + gateway: 192.168.1.1 + subnet: 255.255.255.0 + dns1: 1.1.1.1 + dns2: 8.8.8.8 packages: - !include common.yaml