diff --git a/src/helpers/CommonCLI_Observer.cpp b/src/helpers/CommonCLI_Observer.cpp index 37982da4..12c403dc 100644 --- a/src/helpers/CommonCLI_Observer.cpp +++ b/src/helpers/CommonCLI_Observer.cpp @@ -405,6 +405,34 @@ bool CommonCLI::handleObserverSetCmd(uint32_t sender_timestamp, const char* conf } else { sprintf(reply, "OK - slot %d preset: %s", slot + 1, preset_name); } + // Warn when this slot won't actually connect on this hardware. The set + // is never blocked — prefs persist so the config carries over if the + // device is moved to a board with more slots — but flag it, or the + // operator waits for a connection that never comes. Mirrors the bridge + // setup loop: slots past the runtime array (RUNTIME_MQTT_SLOTS) are + // never iterated; within it, only the first getMaxActiveSlots() + // *enabled* slots connect (each WSS/TLS link costs ~40 KB heap). + if (strcmp(preset_name, MQTT_PRESET_NONE) != 0) { + size_t used = strlen(reply); + if (slot >= RUNTIME_MQTT_SLOTS) { + if (used < 158) { + snprintf(reply + used, 160 - used, " (slot inactive on this hardware)"); + } + } else { + const int max_active = MQTTBridge::getMaxActiveSlots(); + int rank = 0; // this slot's position among enabled slots, by index + for (int s = 0; s <= slot; s++) { + if (_mqtt_prefs.mqtt_slot_preset[s][0] != '\0' && + strcmp(_mqtt_prefs.mqtt_slot_preset[s], MQTT_PRESET_NONE) != 0) { + rank++; + } + } + if (rank > max_active && used < 158) { + snprintf(reply + used, 160 - used, + " (won't connect: %d-slot limit on this hardware)", max_active); + } + } + } } } else { strcpy(reply, "Error: unknown preset. Use 'get mqtt.presets'"); diff --git a/src/helpers/bridges/MQTTBridge.cpp b/src/helpers/bridges/MQTTBridge.cpp index 2066f9c6..39cd494d 100644 --- a/src/helpers/bridges/MQTTBridge.cpp +++ b/src/helpers/bridges/MQTTBridge.cpp @@ -337,6 +337,18 @@ void MQTTBridge::formatMqttStatsReply(char* buf, size_t bufsize) { uint8_t MQTTBridge::getLastWifiDisconnectReason() { return s_wifi_disconnect_reason; } unsigned long MQTTBridge::getLastWifiDisconnectTime() { return s_wifi_disconnect_time; } +// Each WSS/TLS connection needs ~40KB for mbedTLS buffers. Without PSRAM even 3 +// concurrent connections would exhaust internal heap, so cap at 2; with PSRAM +// cap at 5 (6 configurable but 5 active max). Static so the CLI can report, at +// config time, that a slot beyond this cap won't connect. +int MQTTBridge::getMaxActiveSlots() { +#if defined(ESP_PLATFORM) && defined(BOARD_HAS_PSRAM) + return psramFound() ? 5 : 2; +#else + return 2; +#endif +} + unsigned long MQTTBridge::getSlotCurrentOutageStartMs(int slot_index) const { if (slot_index < 0 || slot_index >= RUNTIME_MQTT_SLOTS) return 0; return _slots[slot_index].current_outage_started_ms; @@ -627,15 +639,9 @@ void MQTTBridge::begin() { MQTT_DEBUG_PRINTLN("PSRAM: not configured for this board (no BOARD_HAS_PSRAM)"); #endif - // Limit active slots based on available memory. - // Each WSS/TLS connection needs ~40KB for mbedTLS buffers. - // Without PSRAM, even 3 concurrent connections would exhaust internal heap. - // With PSRAM, cap at 5 for safety (6 configurable but 5 active max). - #if defined(ESP_PLATFORM) && defined(BOARD_HAS_PSRAM) - _max_active_slots = psramFound() ? 5 : 2; - #else - _max_active_slots = 2; - #endif + // Limit active slots based on available memory (single source of truth in + // getMaxActiveSlots(), which the CLI also uses to warn about inactive slots). + _max_active_slots = getMaxActiveSlots(); MQTT_DEBUG_PRINTLN("Max active slots: %d", _max_active_slots); // Check if WiFi credentials are configured first diff --git a/src/helpers/bridges/MQTTBridge.h b/src/helpers/bridges/MQTTBridge.h index 5e7e2d01..17346c43 100644 --- a/src/helpers/bridges/MQTTBridge.h +++ b/src/helpers/bridges/MQTTBridge.h @@ -498,6 +498,10 @@ public: static void formatSlotDiagReply(char* buf, size_t bufsize, int slot_index); static uint8_t getLastWifiDisconnectReason(); static unsigned long getLastWifiDisconnectTime(); + /** Max slots that can be connected at once on this hardware (each WSS/TLS link + * needs ~40 KB internal heap): 5 with PSRAM, 2 without. Note this is below + * RUNTIME_MQTT_SLOTS, so more slots can be configured than will connect. */ + static int getMaxActiveSlots(); #if defined(WITH_MQTT_NEIGHBORS) void requestPublishNeighbors(const char* json, size_t len);