diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 29a0330be..8cf8686fd 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -72,6 +72,7 @@ This release is based on the latest Espressif/Arduino core version 3. It brings - writing a negative custom entity value was always 1 too high because float-to-int rounding always added +0.5. Negative values now round the same way as positive ones [#3297](https://github.com/emsesp/EMS-ESP32/issues/3297) - after a cold boot, SoftAP fallback no longer abandons STA forever. If a home WiFi SSID is configured, EMS-ESP keeps retrying STA while the captive portal is up so a late-booting router (e.g. after a power outage) can still be joined; SoftAP stops once STA gets an IP - NTP time zone strings updated to IANA tz 2026e. About 32 zones had the wrong UTC offset now or within the next year (Mexico no longer uses DST, Egypt does again, Morocco is permanent UTC+0, Kazakhstan, and from 1 November 2026 western Canada stays on permanent offsets). On boot EMS-ESP rewrites the stored POSIX string from the saved IANA label so existing installs pick up the new rules without re-selecting the zone [#3308](https://github.com/emsesp/EMS-ESP32/pull/3308) +- MQTT with TLS and a CA certificate crashed on every connect (a boot loop) on boards with PSRAM when the broker's certificate chain is EC, such as Let's Encrypt ISRG Root X2: verifying it needs about 6 KB of stack and the MQTT task only had 5120 bytes. The MQTT task now gets an 8 KB stack when TLS verifies a CA certificate, and keeps 5120 bytes for insecure TLS and plain MQTT ## Changed diff --git a/lib/espMqttClient/src/MqttClient.cpp b/lib/espMqttClient/src/MqttClient.cpp index 625300a49..1e4780225 100644 --- a/lib/espMqttClient/src/MqttClient.cpp +++ b/lib/espMqttClient/src/MqttClient.cpp @@ -13,7 +13,7 @@ using espMqttClientInternals::PacketType; using espMqttClientTypes::DisconnectReason; using espMqttClientTypes::Error; -MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority, uint8_t core) +MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority, uint8_t core, uint32_t stackSize) : _useInternalTask(useInternalTask) , _transport(nullptr) , _onConnectCallback(nullptr) @@ -55,6 +55,7 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint , _disconnectReason(DisconnectReason::TCP_DISCONNECTED) #if defined(ARDUINO_ARCH_ESP32) && ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO , _highWaterMark(4294967295) +, _stackSize(stackSize) #endif { EMC_GENERATE_CLIENTID(_generatedClientId); @@ -64,12 +65,12 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { #ifdef CONFIG_FREERTOS_UNICORE // no core to pin to on single-core targets (S2, C3, C6) - xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle); + xTaskCreate((TaskFunction_t)_loop, "mqttclient", stackSize, this, priority, &_taskHandle); #else if (core > 1) { - xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle); + xTaskCreate((TaskFunction_t)_loop, "mqttclient", stackSize, this, priority, &_taskHandle); } else { - xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core); + xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", stackSize, this, priority, &_taskHandle, core); } #endif } @@ -77,6 +78,7 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint (void) useInternalTask; (void) priority; (void) core; + (void) stackSize; #endif _clientId = _generatedClientId; _core = core; @@ -320,7 +322,7 @@ void MqttClient::loop() { size_t waterMark = uxTaskGetStackHighWaterMark(NULL); if (waterMark < _highWaterMark) { _highWaterMark = waterMark; - emc_log_i("Stack usage: %zu/%i", EMC_TASK_STACK_SIZE - _highWaterMark, EMC_TASK_STACK_SIZE); + emc_log_i("Stack usage: %zu/%zu", _stackSize - _highWaterMark, _stackSize); } #endif } diff --git a/lib/espMqttClient/src/MqttClient.h b/lib/espMqttClient/src/MqttClient.h index 5fe7b11a9..49e3ca72e 100644 --- a/lib/espMqttClient/src/MqttClient.h +++ b/lib/espMqttClient/src/MqttClient.h @@ -81,7 +81,7 @@ class MqttClient { } protected: - explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1); + explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE); espMqttClientTypes::UseInternalTask _useInternalTask; espMqttClientInternals::Transport* _transport; @@ -207,6 +207,7 @@ class MqttClient { #if defined(ARDUINO_ARCH_ESP32) #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO size_t _highWaterMark; + size_t _stackSize; #endif #endif }; diff --git a/lib/espMqttClient/src/MqttClientSetup.h b/lib/espMqttClient/src/MqttClientSetup.h index 67f46a079..159ba56d0 100644 --- a/lib/espMqttClient/src/MqttClientSetup.h +++ b/lib/espMqttClient/src/MqttClientSetup.h @@ -208,8 +208,8 @@ class MqttClientSetup : public MqttClient { */ protected: - explicit MqttClientSetup(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1) - : MqttClient(useInternalTask, priority, core) { + explicit MqttClientSetup(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE) + : MqttClient(useInternalTask, priority, core, stackSize) { #if EMC_MULTIPLE_CALLBACKS _onConnectCallback = [this](bool sessionPresent) { for (auto callback : _onConnectCallbacks) if (callback.first) callback.first(sessionPresent); diff --git a/lib/espMqttClient/src/espMqttClient.cpp b/lib/espMqttClient/src/espMqttClient.cpp index c2c12118f..104f5ae3d 100644 --- a/lib/espMqttClient/src/espMqttClient.cpp +++ b/lib/espMqttClient/src/espMqttClient.cpp @@ -15,8 +15,8 @@ espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask _transport = &_client; } -espMqttClient::espMqttClient(uint8_t priority, uint8_t core) - : MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core) +espMqttClient::espMqttClient(uint8_t priority, uint8_t core, uint32_t stackSize) + : MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core, stackSize) , _client() { _transport = &_client; } @@ -27,8 +27,8 @@ espMqttClientSecure::espMqttClientSecure(espMqttClientTypes::UseInternalTask use _transport = &_client; } -espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core) - : MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core) +espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core, uint32_t stackSize) + : MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core, stackSize) , _client() { _transport = &_client; } diff --git a/lib/espMqttClient/src/espMqttClient.h b/lib/espMqttClient/src/espMqttClient.h index 41d2c516b..b8b3fd9bf 100644 --- a/lib/espMqttClient/src/espMqttClient.h +++ b/lib/espMqttClient/src/espMqttClient.h @@ -48,7 +48,7 @@ class espMqttClientSecure : public MqttClientSetup { class espMqttClient : public MqttClientSetup { public: explicit espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask); - explicit espMqttClient(uint8_t priority = 1, uint8_t core = 1); + explicit espMqttClient(uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE); protected: espMqttClientInternals::ClientSync _client; @@ -57,7 +57,7 @@ class espMqttClient : public MqttClientSetup { class espMqttClientSecure : public MqttClientSetup { public: explicit espMqttClientSecure(espMqttClientTypes::UseInternalTask useInternalTask); - explicit espMqttClientSecure(uint8_t priority = 1, uint8_t core = 1); + explicit espMqttClientSecure(uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE); espMqttClientSecure & setInsecure(); espMqttClientSecure & setCACert(const char * rootCA); espMqttClientSecure & setCertificate(const char * clientCa); diff --git a/src/ESP32React/MqttSettingsService.cpp b/src/ESP32React/MqttSettingsService.cpp index c2102aed8..50f61ff9a 100644 --- a/src/ESP32React/MqttSettingsService.cpp +++ b/src/ESP32React/MqttSettingsService.cpp @@ -32,10 +32,13 @@ void MqttSettingsService::begin() { } void MqttSettingsService::startClient() { - static bool isSecure = false; + static bool isSecure = false; + static uint32_t stackSize = EMC_TASK_STACK_SIZE; + // only verifying a CA chain needs the larger stack, insecure TLS and plain MQTT fit in the default + const uint32_t wantStack = (_state.enableTLS && _state.rootCA != "insecure") ? EMSESP_MQTT_STACKSIZE_CA : EMC_TASK_STACK_SIZE; if (_mqttClient) { // do we need to change the client? - if ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS)) { + if (isSecure == _state.enableTLS && stackSize == wantStack) { return; } @@ -43,12 +46,14 @@ void MqttSettingsService::startClient() { _mqttClient = nullptr; } + stackSize = wantStack; + if (_state.enableTLS) { isSecure = true; if (emsesp::EMSESP::system_.PSram() == 0) { _mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO); } else { - _mqttClient = new espMqttClientSecure(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE); + _mqttClient = new espMqttClientSecure(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE, wantStack); } if (!_mqttClient) { diff --git a/src/ESP32React/MqttSettingsService.h b/src/ESP32React/MqttSettingsService.h index c286c3544..fe0cd4f1a 100644 --- a/src/ESP32React/MqttSettingsService.h +++ b/src/ESP32React/MqttSettingsService.h @@ -60,6 +60,11 @@ #define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE #endif +// MQTT task stack when TLS verifies the broker against a CA. BearSSL needs ~6 KB to verify an EC (P-384, P-521) chain +#ifndef EMSESP_MQTT_STACKSIZE_CA +#define EMSESP_MQTT_STACKSIZE_CA 8192 +#endif + #ifndef EMSESP_MQTT_PRIORITY #define EMSESP_MQTT_PRIORITY 1 #endif