From 5ab1f04ea534e541dfbbc601f2f98fc6ff82d8b2 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 2 Mar 2020 18:03:47 -0500 Subject: [PATCH] taming the memory monster --- Makefile | 2 +- components/cmd_system/cmd_system.c | 16 +- components/services/messaging.c | 58 +-- components/services/messaging.h | 6 +- components/services/monitor.c | 42 +- components/squeezelite-ota/squeezelite-ota.c | 2 +- components/telnet/telnet.c | 2 +- components/wifi-manager/_esp_http_server.h | 93 +++++ components/wifi-manager/_esp_httpd_main.c | 373 ++++++++++++++++++ components/wifi-manager/code.js | 21 +- components/wifi-manager/component.mk | 2 +- .../wifi-manager/wifi_manager_http_server.c | 4 +- main/CMakeLists.txt | 2 +- main/component.mk | 2 +- main/esp_app_main.c | 4 + 15 files changed, 583 insertions(+), 46 deletions(-) create mode 100644 components/wifi-manager/_esp_http_server.h create mode 100644 components/wifi-manager/_esp_httpd_main.c diff --git a/Makefile b/Makefile index 9639bad0..bfbb0778 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ #recovery: EXTRA_CPPFLAGS+=-DRECOVERY_APPLICATION=1 PROJECT_NAME?=squeezelite -EXTRA_CPPFLAGS+= -I$(PROJECT_PATH)/main +EXTRA_CPPFLAGS+= -I$(PROJECT_PATH)/main -I$(IDF_PATH)/components/esp_http_server/src -I$(IDF_PATH)/components/esp_http_server/src/port/esp32 -I$(IDF_PATH)/components/esp_http_server/src/util #/-Wno-error=maybe-uninitialized include $(IDF_PATH)/make/project.mk diff --git a/components/cmd_system/cmd_system.c b/components/cmd_system/cmd_system.c index dd452e41..7e9dc015 100644 --- a/components/cmd_system/cmd_system.c +++ b/components/cmd_system/cmd_system.c @@ -106,8 +106,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); return ESP_OK; } @@ -117,8 +116,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); return ESP_OK; } @@ -166,8 +164,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); } } @@ -181,8 +178,7 @@ static int restart(int argc, char **argv) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); return 0; } @@ -193,9 +189,7 @@ void simple_restart() if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); } diff --git a/components/services/messaging.c b/components/services/messaging.c index f8841baa..3d1d9ff6 100644 --- a/components/services/messaging.c +++ b/components/services/messaging.c @@ -27,7 +27,7 @@ typedef struct { RingbufHandle_t buf_handle; } messaging_list_t; static messaging_list_t top; - +#define MSG_LENGTH_AVG 201 messaging_list_t * get_struct_ptr(messaging_handle_t handle){ return (messaging_list_t *)handle; @@ -35,12 +35,14 @@ messaging_list_t * get_struct_ptr(messaging_handle_t handle){ messaging_handle_t get_handle_ptr(messaging_list_t * handle){ return (messaging_handle_t )handle; } + RingbufHandle_t messaging_create_ring_buffer(uint8_t max_count){ RingbufHandle_t buf_handle = NULL; StaticRingbuffer_t *buffer_struct = malloc(sizeof(StaticRingbuffer_t)); if (buffer_struct != NULL) { - size_t buf_size = (size_t )(sizeof(single_message_t)+8)*(size_t )(max_count>0?max_count:5); // no-split buffer requires an additional 8 bytes - uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + size_t buf_size = (size_t )(sizeof(single_message_t)+8+MSG_LENGTH_AVG)*(size_t )(max_count>0?max_count:5); // no-split buffer requires an additional 8 bytes + buf_size = buf_size - (buf_size % 4); + uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_32BIT); if (buffer_storage== NULL) { ESP_LOGE(tag,"buff alloc failed"); } @@ -62,9 +64,9 @@ void messaging_fill_messages(messaging_list_t * target_subscriber){ message= messaging_retrieve_message(top.buf_handle); if(message){ //re-post to original queue so it is available to future subscribers - messaging_post_to_queue(get_handle_ptr(&top), message, sizeof(single_message_t)); + messaging_post_to_queue(get_handle_ptr(&top), message, message->msg_size); // post to new subscriber - messaging_post_to_queue(get_handle_ptr(target_subscriber) , message, sizeof(single_message_t)); + messaging_post_to_queue(get_handle_ptr(target_subscriber) , message, message->msg_size); FREE_AND_NULL(message); } } @@ -116,6 +118,7 @@ const char * messaging_get_class_desc(messaging_classes msg_class){ switch (msg_class) { CASE_TO_STR(MESSAGING_CLASS_OTA); CASE_TO_STR(MESSAGING_CLASS_SYSTEM); + CASE_TO_STR(MESSAGING_CLASS_STATS); default: return "Unknown"; break; @@ -156,14 +159,9 @@ single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle){ vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting); if(uxItemsWaiting>0){ message = (single_message_t *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(50)); - if(item_size!=sizeof(single_message_t)){ - ESP_LOGE(tag,"Invalid message length!"); - } - else { - message_copy = heap_caps_malloc(item_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if(message_copy){ - memcpy(message_copy,message,item_size); - } + message_copy = heap_caps_malloc(item_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(message_copy){ + memcpy(message_copy,message,item_size); } vRingbufferReturnItem(buf_handle, (void *)message); } @@ -171,15 +169,22 @@ single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle){ } esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_message_t * message, size_t message_size){ - UBaseType_t uxItemsWaiting=0; size_t item_size=0; messaging_list_t * subscriber=get_struct_ptr(subscriber_handle); if(!subscriber->buf_handle){ ESP_LOGE(tag,"post failed: null buffer for %s", str_or_unknown(subscriber->subscriber_name)); return ESP_FAIL; } - vRingbufferGetInfo(subscriber->buf_handle,NULL,NULL,NULL,NULL,&uxItemsWaiting); - if(uxItemsWaiting>=subscriber->max_count){ + void * pItem=NULL; + int passes=0; + UBaseType_t res=pdFALSE; + while(passes++<3){ + res = xRingbufferSendAcquire(subscriber->buf_handle, &pItem, message_size, pdMS_TO_TICKS(100)); + if(res == pdTRUE && pItem){ + memcpy(pItem,message,message_size); + xRingbufferSendComplete(subscriber->buf_handle, pItem); + break; + } ESP_LOGD(tag,"messaged dropped for %s",str_or_unknown(subscriber->subscriber_name)); single_message_t * dummy = (single_message_t *)xRingbufferReceive(subscriber->buf_handle, &item_size, pdMS_TO_TICKS(50)); if (dummy== NULL) { @@ -189,7 +194,6 @@ esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_m vRingbufferReturnItem(subscriber->buf_handle, (void *)dummy); } } - UBaseType_t res = xRingbufferSend(subscriber->buf_handle, message, message_size, pdMS_TO_TICKS(1000)); if (res != pdTRUE) { ESP_LOGE(tag,"post to %s failed",str_or_unknown(subscriber->subscriber_name)); return ESP_FAIL; @@ -197,19 +201,27 @@ esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_m return ESP_OK; } void messaging_post_message(messaging_types type,messaging_classes msg_class, char *fmt, ...){ - single_message_t message={}; + single_message_t * message=NULL; + size_t msg_size=0; + size_t ln =0; messaging_list_t * cur=⊤ va_list va; va_start(va, fmt); - vsnprintf(message.message, sizeof(message.message), fmt, va); + ln = vsnprintf(NULL, 0, fmt, va)+1; + msg_size = sizeof(single_message_t)+ln; + message = (single_message_t *)heap_caps_malloc(msg_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + vsprintf(message->message, fmt, va); va_end(va); - message.type = type; - message.msg_class = msg_class; - message.sent_time = esp_timer_get_time() / 1000; + message->msg_size = msg_size; + message->type = type; + message->msg_class = msg_class; + message->sent_time = esp_timer_get_time() / 1000; + ESP_LOGI(tag,"Post: %s",message->message); while(cur){ - messaging_post_to_queue(get_handle_ptr(cur), &message, sizeof(single_message_t)); + messaging_post_to_queue(get_handle_ptr(cur), message, msg_size); cur = get_struct_ptr(cur->next); } + FREE_AND_NULL(message); return; } diff --git a/components/services/messaging.h b/components/services/messaging.h index fb0ca0a0..f9ecbca6 100644 --- a/components/services/messaging.h +++ b/components/services/messaging.h @@ -9,7 +9,8 @@ typedef enum { } messaging_types; typedef enum { MESSAGING_CLASS_OTA, - MESSAGING_CLASS_SYSTEM + MESSAGING_CLASS_SYSTEM, + MESSAGING_CLASS_STATS } messaging_classes; typedef struct messaging_list_t *messaging_handle_t; @@ -18,7 +19,8 @@ typedef struct { time_t sent_time; messaging_types type; messaging_classes msg_class; - char message[151]; + size_t msg_size; + char message[]; } single_message_t; cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle); diff --git a/components/services/monitor.c b/components/services/monitor.c index 4b56dbe5..04199895 100644 --- a/components/services/monitor.c +++ b/components/services/monitor.c @@ -21,6 +21,9 @@ #include "globdefs.h" #include "config.h" #include "accessors.h" +#include "messaging.h" +#include "cJSON.h" +#include "trace.h" #define MONITOR_TIMER (10*1000) @@ -49,10 +52,12 @@ static void task_stats( void ) { TaskStatus_t *tasks; uint32_t total, n; } current, previous; - + cJSON * top=cJSON_CreateObject(); + cJSON * tlist=cJSON_CreateArray(); current.n = uxTaskGetNumberOfTasks(); current.tasks = malloc( current.n * sizeof( TaskStatus_t ) ); current.n = uxTaskGetSystemState( current.tasks, current.n, ¤t.total ); + cJSON_AddNumberToObject(top,"ntasks",current.n); static EXT_RAM_ATTR char scratch[128+1]; *scratch = '\0'; @@ -66,6 +71,20 @@ static void task_stats( void ) { n += sprintf(scratch + n, "%16s %2u%% s:%5u", current.tasks[i].pcTaskName, 100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed, current.tasks[i].usStackHighWaterMark); + cJSON * t=cJSON_CreateObject(); + cJSON_AddNumberToObject(t,"cpu",100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed); + cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark); + cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority); + cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority); + cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName); + cJSON_AddNumberToObject(t,"st",current.tasks[i].eCurrentState); + cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber); + cJSON_AddItemToArray(tlist,t); + char * topsts = cJSON_PrintUnformatted(t); + if(topsts){ + ESP_LOGI(TAG,"task detail: %s",topsts); + FREE_AND_NULL(topsts); + } if (i % 3 == 2 || i == current.n - 1) { ESP_LOGI(TAG, "%s", scratch); n = 0; @@ -77,13 +96,32 @@ static void task_stats( void ) { #else for (int i = 0, n = 0; i < current.n; i ++) { n += sprintf(scratch + n, "%16s s:%5u\t", current.tasks[i].pcTaskName, current.tasks[i].usStackHighWaterMark); + cJSON * t=cJSON_CreateObject(); + cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark); + cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority); + cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority); + cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName); + cJSON_AddStringToObject(t,"st",current.tasks[i].eCurrentState); + cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber); + cJSON_AddItemToArray(tlist,t); + char * topsts = cJSON_PrintUnformatted(t); + if(topsts){ + ESP_LOGI(TAG,"task detail: %s",topsts); + FREE_AND_NULL(topsts); + } if (i % 3 == 2 || i == current.n - 1) { ESP_LOGI(TAG, "%s", scratch); n = 0; } } #endif - + cJSON_AddItemToObject(top,"tasks",tlist); + char * top_a= cJSON_PrintUnformatted(top); + if(top_a){ + messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_STATS,top_a); + FREE_AND_NULL(top_a); + } + cJSON_free(top); if (previous.tasks) free(previous.tasks); previous = current; #endif diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c index 6202c051..ea7bd719 100644 --- a/components/squeezelite-ota/squeezelite-ota.c +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -616,7 +616,7 @@ void ota_task(void *pvParameter) { esp_err_t err = ESP_OK; int data_read = 0; - GDS_TextSetFont(display,2,&Font_droid_sans_fallback_15x17,-2); + GDS_TextSetFont(display,2,GDS_GetHeight(display)>32?&Font_droid_sans_fallback_15x17:&Font_droid_sans_fallback_11x13,-2); GDS_ClearExt(display, true); GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Firmware update"); GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Initializing"); diff --git a/components/telnet/telnet.c b/components/telnet/telnet.c index 0f603ae4..526ed468 100644 --- a/components/telnet/telnet.c +++ b/components/telnet/telnet.c @@ -144,7 +144,7 @@ void init_telnet(){ void start_telnet(void * pvParameter){ static bool isStarted=false; StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); - StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE,(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)); if(!isStarted && bIsEnabled) { xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_MAIN_PRIO , xStack, xTaskBuffer); diff --git a/components/wifi-manager/_esp_http_server.h b/components/wifi-manager/_esp_http_server.h new file mode 100644 index 00000000..bde57c65 --- /dev/null +++ b/components/wifi-manager/_esp_http_server.h @@ -0,0 +1,93 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_HTTP_SERVER_H_ +#define __ESP_HTTP_SERVER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +/** + * @brief Starts the web server + * + * Create an instance of HTTP server and allocate memory/resources for it + * depending upon the specified configuration. + * + * Example usage: + * @code{c} + * + * //Function for starting the webserver + * httpd_handle_t start_webserver(void) + * { + * // Generate default configuration + * httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + * + * // Empty handle to http_server + * httpd_handle_t server = NULL; + * + * // Start the httpd server + * if (httpd_start(&server, &config) == ESP_OK) { + * // Register URI handlers + * httpd_register_uri_handler(server, &uri_get); + * httpd_register_uri_handler(server, &uri_post); + * } + * // If server failed to start, handle will be NULL + * return server; + * } + * + * @endcode + * + * @param[in] config Configuration for new instance of the server + * @param[out] handle Handle to newly created instance of the server. NULL on error + * @return + * - ESP_OK : Instance created successfully + * - ESP_ERR_INVALID_ARG : Null argument(s) + * - ESP_ERR_HTTPD_ALLOC_MEM : Failed to allocate memory for instance + * - ESP_ERR_HTTPD_TASK : Failed to launch server task + */ +esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config); +static inline int __httpd_os_thread_create_static(TaskHandle_t *thread, + const char *name, uint16_t stacksize, int prio, + void (*thread_routine)(void *arg), void *arg, + BaseType_t core_id) +{ + + + StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + StackType_t *xStack = heap_caps_malloc(stacksize,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)); + + + // + *thread = xTaskCreateStaticPinnedToCore(thread_routine, name, stacksize, arg, prio, xStack,xTaskBuffer,core_id); + if (*thread) { + return ESP_OK; + } + return ESP_FAIL; +} +#ifdef __cplusplus +} +#endif + +#endif /* ! _ESP_HTTP_SERVER_H_ */ diff --git a/components/wifi-manager/_esp_httpd_main.c b/components/wifi-manager/_esp_httpd_main.c new file mode 100644 index 00000000..af6faefa --- /dev/null +++ b/components/wifi-manager/_esp_httpd_main.c @@ -0,0 +1,373 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include <_esp_http_server.h> +#include "../src/esp_httpd_priv.h" +#include "../src/util/ctrl_sock.h" + +static const char *TAG = "_httpd"; + + +struct httpd_ctrl_data { + enum httpd_ctrl_msg { + HTTPD_CTRL_SHUTDOWN, + HTTPD_CTRL_WORK, + } hc_msg; + httpd_work_fn_t hc_work; + void *hc_work_arg; +}; + + +static esp_err_t _httpd_server_init(struct httpd_data *hd) +{ + int fd = socket(PF_INET6, SOCK_STREAM, 0); + if (fd < 0) { + ESP_LOGE(TAG, LOG_FMT("error in socket (%d)"), errno); + return ESP_FAIL; + } + + struct in6_addr inaddr_any = IN6ADDR_ANY_INIT; + struct sockaddr_in6 serv_addr = { + .sin6_family = PF_INET6, + .sin6_addr = inaddr_any, + .sin6_port = htons(hd->config.server_port) + }; + + /* Enable SO_REUSEADDR to allow binding to the same + * address and port when restarting the server */ + int enable = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) { + /* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But + * it does not affect the normal working of the HTTP Server */ + ESP_LOGW(TAG, LOG_FMT("error enabling SO_REUSEADDR (%d)"), errno); + } + + int ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if (ret < 0) { + ESP_LOGE(TAG, LOG_FMT("error in bind (%d)"), errno); + close(fd); + return ESP_FAIL; + } + + ret = listen(fd, hd->config.backlog_conn); + if (ret < 0) { + ESP_LOGE(TAG, LOG_FMT("error in listen (%d)"), errno); + close(fd); + return ESP_FAIL; + } + + int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port); + if (ctrl_fd < 0) { + ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno); + close(fd); + return ESP_FAIL; + } + + int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (msg_fd < 0) { + ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno); + close(fd); + close(ctrl_fd); + return ESP_FAIL; + } + + hd->listen_fd = fd; + hd->ctrl_fd = ctrl_fd; + hd->msg_fd = msg_fd; + return ESP_OK; +} + +static void _httpd_process_ctrl_msg(struct httpd_data *hd) +{ + struct httpd_ctrl_data msg; + int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0); + if (ret <= 0) { + ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno); + return; + } + if (ret != sizeof(msg)) { + ESP_LOGW(TAG, LOG_FMT("incomplete msg")); + return; + } + + switch (msg.hc_msg) { + case HTTPD_CTRL_WORK: + if (msg.hc_work) { + ESP_LOGD(TAG, LOG_FMT("work")); + (*msg.hc_work)(msg.hc_work_arg); + } + break; + case HTTPD_CTRL_SHUTDOWN: + ESP_LOGD(TAG, LOG_FMT("shutdown")); + hd->hd_td.status = THREAD_STOPPING; + break; + default: + break; + } +} + +static esp_err_t _httpd_accept_conn(struct httpd_data *hd, int listen_fd) +{ + /* If no space is available for new session, close the least recently used one */ + if (hd->config.lru_purge_enable == true) { + if (!httpd_is_sess_available(hd)) { + /* Queue asynchronous closure of the least recently used session */ + return httpd_sess_close_lru(hd); + /* Returning from this allowes the main server thread to process + * the queued asynchronous control message for closing LRU session. + * Since connection request hasn't been addressed yet using accept() + * therefore _httpd_accept_conn() will be called again, but this time + * with space available for one session + */ + } + } + + struct sockaddr_in addr_from; + socklen_t addr_from_len = sizeof(addr_from); + int new_fd = accept(listen_fd, (struct sockaddr *)&addr_from, &addr_from_len); + if (new_fd < 0) { + ESP_LOGW(TAG, LOG_FMT("error in accept (%d)"), errno); + return ESP_FAIL; + } + ESP_LOGD(TAG, LOG_FMT("newfd = %d"), new_fd); + + struct timeval tv; + /* Set recv timeout of this fd as per config */ + tv.tv_sec = hd->config.recv_wait_timeout; + tv.tv_usec = 0; + setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); + + /* Set send timeout of this fd as per config */ + tv.tv_sec = hd->config.send_wait_timeout; + tv.tv_usec = 0; + setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); + + if (ESP_OK != httpd_sess_new(hd, new_fd)) { + ESP_LOGW(TAG, LOG_FMT("session creation failed")); + close(new_fd); + return ESP_FAIL; + } + ESP_LOGD(TAG, LOG_FMT("complete")); + return ESP_OK; +} +/* Manage in-coming connection or data requests */ +static esp_err_t _httpd_server(struct httpd_data *hd) +{ + fd_set read_set; + FD_ZERO(&read_set); + if (hd->config.lru_purge_enable || httpd_is_sess_available(hd)) { + /* Only listen for new connections if server has capacity to + * handle more (or when LRU purge is enabled, in which case + * older connections will be closed) */ + FD_SET(hd->listen_fd, &read_set); + } + FD_SET(hd->ctrl_fd, &read_set); + + int tmp_max_fd; + httpd_sess_set_descriptors(hd, &read_set, &tmp_max_fd); + int maxfd = MAX(hd->listen_fd, tmp_max_fd); + tmp_max_fd = maxfd; + maxfd = MAX(hd->ctrl_fd, tmp_max_fd); + + ESP_LOGD(TAG, LOG_FMT("doing select maxfd+1 = %d"), maxfd + 1); + int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL); + if (active_cnt < 0) { + ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno); + httpd_sess_delete_invalid(hd); + return ESP_OK; + } + + /* Case0: Do we have a control message? */ + if (FD_ISSET(hd->ctrl_fd, &read_set)) { + ESP_LOGD(TAG, LOG_FMT("processing ctrl message")); + _httpd_process_ctrl_msg(hd); + if (hd->hd_td.status == THREAD_STOPPING) { + ESP_LOGD(TAG, LOG_FMT("stopping thread")); + return ESP_FAIL; + } + } + + /* Case1: Do we have any activity on the current data + * sessions? */ + int fd = -1; + while ((fd = httpd_sess_iterate(hd, fd)) != -1) { + if (FD_ISSET(fd, &read_set) || (httpd_sess_pending(hd, fd))) { + ESP_LOGD(TAG, LOG_FMT("processing socket %d"), fd); + if (httpd_sess_process(hd, fd) != ESP_OK) { + ESP_LOGD(TAG, LOG_FMT("closing socket %d"), fd); + close(fd); + /* Delete session and update fd to that + * preceding the one being deleted */ + fd = httpd_sess_delete(hd, fd); + } + } + } + + /* Case2: Do we have any incoming connection requests to + * process? */ + if (FD_ISSET(hd->listen_fd, &read_set)) { + ESP_LOGD(TAG, LOG_FMT("processing listen socket %d"), hd->listen_fd); + if (_httpd_accept_conn(hd, hd->listen_fd) != ESP_OK) { + ESP_LOGW(TAG, LOG_FMT("error accepting new connection")); + } + } + return ESP_OK; +} + +static void _httpd_close_all_sessions(struct httpd_data *hd) +{ + int fd = -1; + while ((fd = httpd_sess_iterate(hd, fd)) != -1) { + ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), fd); + httpd_sess_delete(hd, fd); + close(fd); + } +} +/* The main HTTPD thread */ +static void _httpd_thread(void *arg) +{ + int ret; + struct httpd_data *hd = (struct httpd_data *) arg; + hd->hd_td.status = THREAD_RUNNING; + + ESP_LOGD(TAG, LOG_FMT("web server started")); + while (1) { + ret = _httpd_server(hd); + if (ret != ESP_OK) { + break; + } + } + + ESP_LOGD(TAG, LOG_FMT("web server exiting")); + close(hd->msg_fd); + cs_free_ctrl_sock(hd->ctrl_fd); + _httpd_close_all_sessions(hd); + close(hd->listen_fd); + hd->hd_td.status = THREAD_STOPPED; + httpd_os_thread_delete(); +} +static struct httpd_data *__httpd_create(const httpd_config_t *config) +{ + /* Allocate memory for httpd instance data */ + struct httpd_data *hd = calloc(1, sizeof(struct httpd_data)); + if (!hd) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance")); + return NULL; + } + hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *)); + if (!hd->hd_calls) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers")); + free(hd); + return NULL; + } + hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db)); + if (!hd->hd_sd) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data")); + free(hd->hd_calls); + free(hd); + return NULL; + } + struct httpd_req_aux *ra = &hd->hd_req_aux; + ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr)); + if (!ra->resp_hdrs) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers")); + free(hd->hd_sd); + free(hd->hd_calls); + free(hd); + return NULL; + } + hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t)); + if (!hd->err_handler_fns) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers")); + free(ra->resp_hdrs); + free(hd->hd_sd); + free(hd->hd_calls); + free(hd); + return NULL; + } + /* Save the configuration for this instance */ + hd->config = *config; + return hd; +} +static void _httpd_delete(struct httpd_data *hd) +{ + struct httpd_req_aux *ra = &hd->hd_req_aux; + /* Free memory of httpd instance data */ + free(hd->err_handler_fns); + free(ra->resp_hdrs); + free(hd->hd_sd); + + /* Free registered URI handlers */ + httpd_unregister_all_uri_handlers(hd); + free(hd->hd_calls); + free(hd); +} +esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config) +{ + if (handle == NULL || config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + /* Sanity check about whether LWIP is configured for providing the + * maximum number of open sockets sufficient for the server. Though, + * this check doesn't guarantee that many sockets will actually be + * available at runtime as other processes may use up some sockets. + * Note that server also uses 3 sockets for its internal use : + * 1) listening for new TCP connections + * 2) for sending control messages over UDP + * 3) for receiving control messages over UDP + * So the total number of required sockets is max_open_sockets + 3 + */ + if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) { + ESP_LOGE(TAG, "Configuration option max_open_sockets is too large (max allowed %d)\n\t" + "Either decrease this or configure LWIP_MAX_SOCKETS to a larger value", + CONFIG_LWIP_MAX_SOCKETS - 3); + return ESP_ERR_INVALID_ARG; + } + + struct httpd_data *hd = __httpd_create(config); + if (hd == NULL) { + /* Failed to allocate memory */ + return ESP_ERR_HTTPD_ALLOC_MEM; + } + + if (_httpd_server_init(hd) != ESP_OK) { + _httpd_delete(hd); + return ESP_FAIL; + } + + httpd_sess_init(hd); + if (__httpd_os_thread_create_static(&hd->hd_td.handle, "httpd", + hd->config.stack_size, + hd->config.task_priority, + _httpd_thread, hd, + hd->config.core_id) != ESP_OK) { + /* Failed to launch task */ + _httpd_delete(hd); + return ESP_ERR_HTTPD_TASK; + } + + *handle = (httpd_handle_t *)hd; + return ESP_OK; +} + diff --git a/components/wifi-manager/code.js b/components/wifi-manager/code.js index 55fcb8e0..58ca2f01 100644 --- a/components/wifi-manager/code.js +++ b/components/wifi-manager/code.js @@ -24,6 +24,14 @@ var nvs_type_t = { NVS_TYPE_ANY : 0xff /*!< Must be last */ } ; +var task_state_t = { + 0 : "eRunning", /*!< A task is querying the state of itself, so must be running. */ + 1 : "eReady", /*!< The task being queried is in a read or pending ready list. */ + 2 : "eBlocked", /*!< The task being queried is in the Blocked state. */ + 3 : "eSuspended", /*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + 4 : "eDeleted" + +} var releaseURL = 'https://api.github.com/repos/sle118/squeezelite-esp32/releases'; var recovery = false; var enableAPTimer = true; @@ -649,7 +657,9 @@ function refreshAPHTML(data){ function getMessages() { $.getJSON("/messages.json", function(data) { data.forEach(function(msg) { - var msg_age = msg["current_time"] - msg["sent_time"]; + var msg_age = msg["current_time"] - msg["sent_time"]; + var msg_time = new Date( ); + msg_time.setTime( msg_time .getTime() - msg_age ); switch (msg["class"]) { case "MESSAGING_CLASS_OTA": //message: "{"ota_dsc":"Erasing flash complete","ota_pct":0}" @@ -668,6 +678,15 @@ function getMessages() { } } break; + case "MESSAGING_CLASS_STATS": + // for task states, check structure : task_state_t + var stats_data = JSON.parse(msg["message"]); + console.log(msg_time + " - Number of tasks on the ESP32: " + stats_data["ntasks"]); + var stats_tasks = stats_data["tasks"]; + stats_tasks.forEach(function(task) { + console.log(msg_time + " - " + task["nme"] + ' - '+ task["cpu"]); + }); + break; case "MESSAGING_CLASS_SYSTEM": showMessage(msg["message"], msg["type"],msg_age); lastMsg = msg; diff --git a/components/wifi-manager/component.mk b/components/wifi-manager/component.mk index 1800f23c..4a5d5006 100644 --- a/components/wifi-manager/component.mk +++ b/components/wifi-manager/component.mk @@ -7,7 +7,7 @@ # please read the SDK documents if you need to do this. # COMPONENT_EMBED_FILES := style.css code.js index.html bootstrap.min.css.gz jquery.min.js.gz popper.min.js.gz bootstrap.min.js.gz -COMPONENT_ADD_INCLUDEDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . $(IDF_PATH)/components/esp_http_server/src $(IDF_PATH)/components/esp_http_server/src/port/esp32 $(IDF_PATH)/components/esp_http_server/src/util CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO diff --git a/components/wifi-manager/wifi_manager_http_server.c b/components/wifi-manager/wifi_manager_http_server.c index 373cad3f..deb91352 100644 --- a/components/wifi-manager/wifi_manager_http_server.c +++ b/components/wifi-manager/wifi_manager_http_server.c @@ -22,6 +22,7 @@ #include "http_server_handlers.h" #include "esp_log.h" #include "esp_http_server.h" +#include "_esp_http_server.h" #include #include #include @@ -112,6 +113,7 @@ void register_regular_handlers(httpd_handle_t server){ } + esp_err_t http_server_start() { ESP_LOGI(TAG, "Initializing HTTP Server"); @@ -131,7 +133,7 @@ esp_err_t http_server_start() // config.open_fn ESP_LOGI(TAG, "Starting HTTP Server"); - esp_err_t err= httpd_start(&_server, &config); + esp_err_t err= __httpd_start(&_server, &config); if(err != ESP_OK){ ESP_LOGE_LOC(TAG,"Start server failed"); } diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 71b1bcdd..6cf0111e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_ADD_INCLUDEDIRS . ) +set(COMPONENT_ADD_INCLUDEDIRS . ${IDF_PATH}/components/esp_http_server/src ${IDF_PATH}/components/esp_http_server/src/port/esp32 ${IDF_PATH}/components/esp_http_server/src/util) set(COMPONENT_SRCS "esp_app_main.c" "platform_esp32.c" "cmd_wifi.c" "console.c" "nvs_utilities.c" "cmd_squeezelite.c" "config.c") set(REQUIRES esp_common) diff --git a/main/component.mk b/main/component.mk index 8edcf630..175f015e 100644 --- a/main/component.mk +++ b/main/component.mk @@ -9,4 +9,4 @@ CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO -DMODEL_NAME=SqueezeESP32 LDFLAGS += -s COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/github.pem -COMPONENT_ADD_INCLUDEDIRS := . \ No newline at end of file +COMPONENT_ADD_INCLUDEDIRS := . \ No newline at end of file diff --git a/main/esp_app_main.c b/main/esp_app_main.c index 10234c45..8ccb6018 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -47,6 +47,7 @@ #include "config.h" #include "audio_controls.h" #include "telnet.h" +#include "messaging.h" static const char certs_namespace[] = "certificates"; static const char certs_key[] = "blob"; @@ -73,12 +74,14 @@ const char * str_or_unknown(const char * str) { return (str?str:unknown_string_p /* brief this is an exemple of a callback that you can setup in your own app to get notified of wifi manager event */ void cb_connection_got_ip(void *pvParameter){ ESP_LOGI(TAG, "I have a connection!"); + messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"Wifi connected"); xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); bWifiConnected=true; led_unpush(LED_GREEN); } void cb_connection_sta_disconnected(void *pvParameter){ led_blink_pushed(LED_GREEN, 250, 250); + messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"Wifi disconnected"); bWifiConnected=false; xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); } @@ -427,4 +430,5 @@ void app_main() #endif free(fwurl); } + messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"System started"); }