diff --git a/backend/app/services/esp-idf-template/sdkconfig.defaults.in b/backend/app/services/esp-idf-template/sdkconfig.defaults.in index f147302d..0128b07c 100644 --- a/backend/app/services/esp-idf-template/sdkconfig.defaults.in +++ b/backend/app/services/esp-idf-template/sdkconfig.defaults.in @@ -80,3 +80,18 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BTDM_CTRL_MODE_BR_EDR_BLE=y CONFIG_BT_BLE_ENABLED=y + +# ── mbedTLS: PSK key-exchange ciphersuites ─────────────────────────────── +# arduino-esp32's WiFiClientSecure/ssl_client.cpp wraps its ENTIRE body +# (start_ssl_client, ssl_init, send_ssl_data, ...) in +# #if !defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) ... #else #endif +# so with no PSK key-exchange enabled the object compiles empty and ANY sketch +# that uses WiFiClientSecure — including HTTPClient.begin(url), which links the +# secure client even for http:// — fails to link with "undefined reference to +# start_ssl_client". ESP-IDF's mbedtls defaults these OFF; arduino-esp32's own +# sdkconfig turns them ON. Match arduino-esp32 so TLS/HTTPS sketches link. +CONFIG_MBEDTLS_PSK_MODES=y +CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=y diff --git a/backend/app/services/espidf_compiler.py b/backend/app/services/espidf_compiler.py index c116f052..66038595 100644 --- a/backend/app/services/espidf_compiler.py +++ b/backend/app/services/espidf_compiler.py @@ -1920,9 +1920,21 @@ class ESPIDFCompiler: # template tree. Doing this BEFORE cmake configure means the new # CONFIG_* lines reach kconfig on its first read. rendered_sdkconfig = self._render_sdkconfig(board_options, _TEMPLATE_DIR) - (project_dir / 'sdkconfig.defaults').write_text( - rendered_sdkconfig, encoding='utf-8', + defaults_path = project_dir / 'sdkconfig.defaults' + prev_defaults = ( + defaults_path.read_text(encoding='utf-8') if defaults_path.exists() else None ) + defaults_path.write_text(rendered_sdkconfig, encoding='utf-8') + + # ESP-IDF only SEEDS sdkconfig from sdkconfig.defaults when sdkconfig is + # ABSENT. Persistent build dirs live in the build volume and keep a + # stale sdkconfig across image rebuilds, so a defaults change (a new + # CONFIG_* shipped in the template, or different board options) would + # otherwise never reach kconfig. Drop the generated sdkconfig when the + # rendered defaults change so kconfig re-seeds from them on configure. + if prev_defaults is not None and prev_defaults != rendered_sdkconfig: + (project_dir / 'sdkconfig').unlink(missing_ok=True) + (project_dir / 'sdkconfig.old').unlink(missing_ok=True) # Generate partitions.csv per the selected scheme. partition_csv = self._render_partition_csv(board_options['partitionScheme']) diff --git a/backend/tests/test_espidf_options.py b/backend/tests/test_espidf_options.py index d3040771..2c1d9e3a 100644 --- a/backend/tests/test_espidf_options.py +++ b/backend/tests/test_espidf_options.py @@ -183,6 +183,18 @@ def test_render_sdkconfig_cpu_freq(compiler: ESPIDFCompiler) -> None: assert 'CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160' in text +def test_render_sdkconfig_enables_mbedtls_psk(compiler: ESPIDFCompiler) -> None: + # arduino-esp32's WiFiClientSecure/ssl_client.cpp guards its whole body on a + # PSK key-exchange being enabled. Without it the object compiles empty and + # any WiFiClientSecure / HTTPClient.begin() sketch fails to link with + # "undefined reference to start_ssl_client". + from app.services.espidf_compiler import _TEMPLATE_DIR + opts = compiler._normalize_options(None, idf_target='esp32') + text = compiler._render_sdkconfig(opts, _TEMPLATE_DIR) + assert 'CONFIG_MBEDTLS_PSK_MODES=y' in text + assert 'CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y' in text + + def test_render_sdkconfig_debug_level_verbose(compiler: ESPIDFCompiler) -> None: from app.services.espidf_compiler import _TEMPLATE_DIR opts = compiler._normalize_options(