Ver Fonte

taming the memory monster

Sebastien há 5 anos atrás
pai
commit
5ab1f04ea5

+ 1 - 1
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 
 

+ 5 - 11
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();
 }
 

+ 35 - 23
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=&top;
 	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;
 
 }

+ 4 - 2
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);

+ 40 - 2
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, &current.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	

+ 1 - 1
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");

+ 1 - 1
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);

+ 93 - 0
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 <stdio.h>
+#include <string.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <http_parser.h>
+#include <sdkconfig.h>
+#include <esp_err.h>
+
+#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_ */

+ 373 - 0
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 <string.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <assert.h>
+
+#include <esp_http_server.h>
+#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;
+}
+

+ 20 - 1
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;

+ 1 - 1
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
 
 

+ 3 - 1
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 <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -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");
     }

+ 1 - 1
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)

+ 1 - 1
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 := .
+COMPONENT_ADD_INCLUDEDIRS := .  

+ 4 - 0
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");
 }