diff --git a/components/esp_websocket_client/Kconfig b/components/esp_websocket_client/Kconfig index 289c1725d8..bf9d842303 100644 --- a/components/esp_websocket_client/Kconfig +++ b/components/esp_websocket_client/Kconfig @@ -20,4 +20,27 @@ menu "ESP WebSocket client" default 2000 help Timeout for acquiring the TX lock when using separate TX lock. + + config ESP_WS_CLIENT_ALLOC_IN_EXT_RAM + bool "Allocate esp_websocket_client structure and config storage in PSRAM" + default n + depends on SPIRAM + help + Enable this option to allocate the esp_websocket_client structure and its + internal configuration storage in external RAM (PSRAM). This is useful for + freeing up internal RAM in memory-constrained applications. + If PSRAM allocation fails, esp_websocket_client_init() returns NULL. + There is no fallback to internal RAM. + + config ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM + bool "Allocate websocket task stack in PSRAM" + default n + depends on SPIRAM + depends on FREERTOS_SUPPORT_STATIC_ALLOCATION + help + Enable this option to allocate the WebSocket task stack in external RAM (PSRAM) + using xTaskCreateStaticPinnedToCore. Only the task stack is placed in PSRAM; + the TCB (Task Control Block) remains in internal RAM as required by the API. + If PSRAM stack allocation fails, esp_websocket_client_start() returns ESP_FAIL. + There is no fallback to internal RAM. endmenu diff --git a/components/esp_websocket_client/esp_websocket_client.c b/components/esp_websocket_client/esp_websocket_client.c index a7246e5cf1..59a3f2f342 100644 --- a/components/esp_websocket_client/esp_websocket_client.c +++ b/components/esp_websocket_client/esp_websocket_client.c @@ -159,6 +159,10 @@ struct esp_websocket_client { int close_status_code; /*!< Status code from the last received CLOSE frame (0 = none / client-initiated) */ esp_transport_keep_alive_t keep_alive_cfg; struct ifreq *if_name; +#if CONFIG_ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM + StackType_t *task_stack_buffer; + StaticTask_t *task_buffer; +#endif }; static uint64_t _tick_get_ms(void) @@ -527,6 +531,16 @@ static void destroy_and_free_resources(esp_websocket_client_handle_t client) vEventGroupDelete(client->status_bits); client->status_bits = NULL; } +#if CONFIG_ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM + if (client->task_stack_buffer) { + heap_caps_free(client->task_stack_buffer); + client->task_stack_buffer = NULL; + } + if (client->task_buffer) { + heap_caps_free(client->task_buffer); + client->task_buffer = NULL; + } +#endif free(client); client = NULL; } @@ -546,7 +560,6 @@ static esp_err_t stop_wait_task(esp_websocket_client_handle_t client) client->state = WEBSOCKET_STATE_UNKNOW; return ESP_OK; } - #if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT static void websocket_header_hook(void * client, const char * line, int line_len) { @@ -777,7 +790,11 @@ static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_hand esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config) { + #if CONFIG_ESP_WS_CLIENT_ALLOC_IN_EXT_RAM + esp_websocket_client_handle_t client = heap_caps_calloc(1, sizeof(struct esp_websocket_client), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +#else esp_websocket_client_handle_t client = calloc(1, sizeof(struct esp_websocket_client)); +#endif ESP_WS_CLIENT_MEM_CHECK(TAG, client, return NULL); esp_event_loop_args_t event_args = { @@ -812,7 +829,11 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie ESP_WS_CLIENT_MEM_CHECK(TAG, client->tx_lock, goto _websocket_init_fail); #endif + #if CONFIG_ESP_WS_CLIENT_ALLOC_IN_EXT_RAM + client->config = heap_caps_calloc(1, sizeof(websocket_config_storage_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +#else client->config = calloc(1, sizeof(websocket_config_storage_t)); +#endif ESP_WS_CLIENT_MEM_CHECK(TAG, client->config, goto _websocket_init_fail); if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) { @@ -1429,6 +1450,7 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client) if (client == NULL) { return ESP_ERR_INVALID_ARG; } + if (client->state >= WEBSOCKET_STATE_INIT) { ESP_LOGE(TAG, "The client has started"); return ESP_FAIL; @@ -1442,8 +1464,42 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client) } } - if (xTaskCreatePinnedToCore(esp_websocket_client_task, client->config->task_name ? client->config->task_name : "websocket_task", - client->config->task_stack, client, client->config->task_prio, &client->task_handle, client->config->task_core_id) != pdTRUE) { + BaseType_t res = pdPASS; +#if CONFIG_ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM + if (client->task_stack_buffer == NULL) { + client->task_stack_buffer = (StackType_t *)heap_caps_calloc(1, client->config->task_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + // TCB must be in internal RAM for xTaskCreateStaticPinnedToCore + if (client->task_buffer == NULL) { + client->task_buffer = (StaticTask_t *)heap_caps_calloc(1, sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + } + + if (client->task_stack_buffer && client->task_buffer) { + ESP_LOGI(TAG, "Allocated %d bytes stack in PSRAM for WebSocket task", client->config->task_stack); + client->task_handle = xTaskCreateStaticPinnedToCore( + esp_websocket_client_task, + client->config->task_name ? client->config->task_name : "websocket_task", + client->config->task_stack, + client, + client->config->task_prio, + client->task_stack_buffer, + client->task_buffer, + client->config->task_core_id + ); + if (client->task_handle == NULL) { + res = pdFAIL; + } + } else { + ESP_LOGE(TAG, "Failed to allocate PSRAM stack for WebSocket task"); + res = pdFAIL; + } +#else + res = xTaskCreatePinnedToCore(esp_websocket_client_task, client->config->task_name ? client->config->task_name : "websocket_task", + client->config->task_stack, client, client->config->task_prio, &client->task_handle, client->config->task_core_id); +#endif + + if (res != pdPASS || client->task_handle == NULL) { + client->task_handle = NULL; ESP_LOGE(TAG, "Error create websocket task"); return ESP_FAIL; }