Explorar o código

improv-wifi initial commit

Sebastien L %!s(int64=2) %!d(string=hai) anos
pai
achega
1390e258db

+ 10 - 0
.gitignore

@@ -18,3 +18,13 @@ components/wifi-manager/UML-State-Machine-in-C
 envfile.txt
 artifacts
 web-installer
+
+squeezelite-esp32.code-workspace
+
+esp-idf-vscode-generated.gdb
+
+debug.log
+
+components/wifi-manager/esp32_improv.cpp.txt
+
+components/wifi-manager/esp32_improv.h.txt

+ 1 - 0
components/platform_console/CMakeLists.txt

@@ -5,6 +5,7 @@ idf_component_register( SRCS
 							cmd_system.c
 							cmd_wifi.c
 							platform_console.c
+							improv_console.c
 							cmd_config.c
 						INCLUDE_DIRS .   
 						REQUIRES nvs_flash 

+ 66 - 0
components/platform_console/cmd_wifi.c

@@ -33,6 +33,7 @@
 #include "esp_netif.h"
 #include "esp_event.h"
 #include "led.h"
+#include "improv.h"
 extern bool bypass_network_manager;
 #define JOIN_TIMEOUT_MS (10000)
 #include "platform_console.h"
@@ -48,6 +49,15 @@ static struct {
     struct arg_str *password;
     struct arg_end *end;
 } join_args;
+static struct {
+    struct arg_lit * conect;
+    struct arg_lit * state;
+    struct arg_lit * info;
+    struct arg_lit * list;
+    struct arg_str *ssid;
+    struct arg_str *password;
+    struct arg_end *end;
+} improv_args;
 
 
 
@@ -185,6 +195,44 @@ static int connect(int argc, char **argv)
 
     return 0;
 }
+extern bool on_improv_command(ImprovCommandStruct_t *command); // command callback 
+static int do_improv(int argc, char **argv)
+{
+    ImprovCommandStruct_t command;
+	int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&improv_args);
+    if (nerrors != 0) {
+        return 1;
+    }
+    if(improv_args.info->count>0){
+        memset(&command,0x00,sizeof(command));
+        command.command = IMPROV_CMD_GET_DEVICE_INFO;
+        on_improv_command(&command);
+    }    
+    if(improv_args.conect->count>0){
+        if(improv_args.ssid->count == 0){
+            ESP_LOGE(__func__,"Parameter ssid is required to connect");
+            return 1;
+        }
+        command.ssid = improv_args.ssid->sval[0];
+        if(improv_args.password->count == 0){
+            command.password= improv_args.password->sval[0];
+        }
+        command.command = IMPROV_CMD_WIFI_SETTINGS;
+        on_improv_command(&command);
+    } 
+    if(improv_args.state->count>0){
+        memset(&command,0x00,sizeof(command));
+        command.command = IMPROV_CMD_GET_CURRENT_STATE;
+        on_improv_command(&command);
+    }
+    if(improv_args.list->count>0){
+        memset(&command,0x00,sizeof(command));
+        command.command = IMPROV_CMD_GET_WIFI_NETWORKS;
+        on_improv_command(&command);
+    }
+
+    return 0;
+}
 void register_wifi_join()
 {
     join_args.timeout = arg_int0(NULL, "timeout", "<t>", "Connection timeout, ms");
@@ -201,10 +249,28 @@ void register_wifi_join()
     };
     ESP_ERROR_CHECK( esp_console_cmd_register(&join_cmd) );
 }
+static void register_improv_debug(){
+    improv_args.conect = arg_lit0(NULL,"connect","Connects to the specified wifi ssid and password");
+    improv_args.ssid = arg_str0(NULL, NULL, "<ssid>", "SSID of AP");
+    improv_args.password = arg_str0(NULL, NULL, "<pass>", "Password of AP");
+    improv_args.info = arg_lit0(NULL,"info","Request the info packet");
+    improv_args.list = arg_lit0(NULL,"list","Request the wifi list packet");
+    improv_args.state = arg_lit0(NULL,"state","Requests the state packet");
 
+    improv_args.end = arg_end(2);
+     const esp_console_cmd_t improv_cmd = {
+        .command = "improv",
+        .help = "Send an improv-wifi serial command to the system",
+        .hint = NULL,
+        .func = &do_improv,
+        .argtable = &improv_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&improv_cmd) );
+}
 void register_wifi()
 {
     register_wifi_join();
+    register_improv_debug();
     if(bypass_network_manager){
     	initialise_wifi();
     }

+ 162 - 0
components/platform_console/improv_console.c

@@ -0,0 +1,162 @@
+#include "platform_console.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "esp_log.h"
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+#include "driver/uart.h"
+#include "linenoise/linenoise.h"
+#include "argtable3/argtable3.h"
+#include "nvs.h" 
+#include "nvs_flash.h"
+#include "pthread.h"
+#include "platform_esp32.h"
+#include "cmd_decl.h"
+#include "trace.h"
+#include "platform_config.h"
+#include "telnet.h" 
+#include "tools.h"
+#include "improv.h"
+#include "messaging.h"
+#include "config.h"
+#include "improv_console.h"
+#include "network_status.h"
+
+static const char * TAG ="improv_console";
+const time_t improv_timeout_ms = 50;
+TickType_t improv_timeout_tick = pdMS_TO_TICKS(improv_timeout_ms);
+ImprovState_t improv_state = IMPROV_STATE_READY_AUTHORIZED; 
+const size_t improv_buffer_size = 121;
+size_t improv_buffer_len = 0;
+uint8_t * improv_buffer_data = NULL;
+TickType_t improv_delay = portMAX_DELAY;
+
+
+void cb_improv_got_ip(nm_state_t new_state, int sub_state){
+	if(improv_state == IMPROV_STATE_PROVISIONING){
+		char * url = network_status_alloc_get_system_url();
+		ESP_LOGI(TAG,"Signaling improv state connected state with url: %s",STR_OR_BLANK(url));
+		improv_send_device_url(IMPROV_CMD_WIFI_SETTINGS,url);
+		FREE_AND_NULL(url);
+	}
+}
+void cb_improv_disconnected(nm_state_t new_state, int sub_state){
+	if(improv_state == IMPROV_STATE_PROVISIONING){
+		ESP_LOGI(TAG,"Signalling improv connect failure ");
+		improv_state = IMPROV_STATE_READY_AUTHORIZED;
+		improv_send_error(IMPROV_ERROR_UNABLE_TO_CONNECT);
+	}
+	
+}
+bool on_improv_command(ImprovCommandStruct_t *command){
+	esp_err_t err = ESP_OK;
+	wifi_connect_state_t wifi_state = network_wifi_get_connect_state();
+	const esp_app_desc_t* desc = esp_ota_get_app_description();
+	improv_buffer_len = 0;
+	char * url=NULL;
+	char * host_name = NULL;
+	ESP_LOGI(TAG, "Processing improv command %s",improv_get_command_desc(command->command));
+	if(!command){
+		return false;
+	}
+	switch (command->command)
+	{
+		case IMPROV_CMD_WIFI_SETTINGS:
+			// attempt to connect to the provided SSID+password
+            improv_state = IMPROV_STATE_PROVISIONING;
+			ESP_LOGI(TAG,"Improv connect to %s",command->ssid );
+			network_async_connect(command->ssid, command->password);
+			FREE_AND_NULL(command->ssid);
+			FREE_AND_NULL(command->password);
+
+			break;
+		case IMPROV_CMD_GET_CURRENT_STATE:
+			if(wifi_state !=NETWORK_WIFI_STATE_CONNECTING){
+				network_async_scan();
+			}
+			switch (wifi_state)
+			{
+				case NETWORK_WIFI_STATE_CONNECTING:
+					ESP_LOGI(TAG,"Signaling improv state " );
+  					return improv_send_current_state(improv_state);
+					break;
+				case NETWORK_WIFI_STATE_INVALID_CONFIG:
+					improv_state = IMPROV_STATE_READY_AUTHORIZED;
+					ESP_LOGW(TAG,"Signaling improv state IMPROV_ERROR_UNABLE_TO_CONNECT" );
+					return improv_send_error(IMPROV_ERROR_UNABLE_TO_CONNECT);
+					break;
+				case NETWORK_WIFI_STATE_FAILED:
+					ESP_LOGW(TAG,"Signaling improv state IMPROV_ERROR_NOT_AUTHORIZED" );
+					network_async_scan();
+					improv_state = IMPROV_STATE_READY_AUTHORIZED;
+					return improv_send_error(IMPROV_ERROR_NOT_AUTHORIZED);
+					break;
+				case NETWORK_WIFI_STATE_CONNECTED:
+					network_async_scan();
+					url = network_status_alloc_get_system_url();
+					ESP_LOGI(TAG,"Signaling improv state connected state with url: %s",STR_OR_BLANK(url));
+					improv_state = IMPROV_STATE_PROVISIONED;
+					improv_send_current_state(improv_state);
+					// also send url
+					improv_send_device_url(IMPROV_CMD_GET_CURRENT_STATE,url);
+					FREE_AND_NULL(url);
+					break;
+			default:
+				ESP_LOGI(TAG,"Signaling improv state " );
+				return improv_send_current_state(improv_state);
+				break;
+			}
+		break;
+		case IMPROV_CMD_GET_DEVICE_INFO:
+			ESP_LOGI(TAG,"Signaling improv with device info. Firmware Name: %s, Version: %s  ",desc->project_name,desc->version );
+			host_name = config_alloc_get_str("host_name",NULL,"Squeezelite");
+			improv_send_device_info(desc->project_name,desc->version,"ESP32",host_name);
+			FREE_AND_NULL(host_name);
+		break;
+		case IMPROV_CMD_GET_WIFI_NETWORKS:
+		ESP_LOGI(TAG,"Signaling improv with list of wifi networks " );
+		improv_wifi_list_send();
+		break;
+		default:
+			ESP_LOGE(TAG,"Signaling improv with invalid RPC call received" );
+			improv_send_error(IMPROV_ERROR_INVALID_RPC);
+			break;
+	}
+	return false;
+}
+void on_improv_error(ImprovError_t error){
+	improv_send_error(error);
+	ESP_LOGE(TAG,"Error processing improv-wifi packet : %s", improv_get_error_desc(error));
+
+}
+
+#if BUFFER_DEBUG
+void dump_buffer(const char * prefix, const char * buff, size_t len){
+
+	printf("\n%s (%d): ",prefix, len);
+	for(int i=0;i<len;i++){
+		printf("    %c ",isprint(buff[i])?buff[i]:'.');
+	}
+	printf("\n%s (%d): ",prefix, len);
+	for(int i=0;i<len;i++){
+		printf("0x%03x ",buff[i]);
+	}
+	printf("\n");
+
+}
+#else
+#define dump_buffer(prefix,buff,size)
+#endif
+bool improv_send_callback(uint8_t * buffer, size_t length){
+	dump_buffer("send", (const char *) buffer, length);
+	uart_write_bytes(CONFIG_ESP_CONSOLE_UART_NUM,buffer,length );
+	return true;
+}
+void improv_console_init(){
+	ESP_LOGI(TAG,"Initializing improv callbacks");
+    network_register_state_callback(NETWORK_WIFI_ACTIVE_STATE,WIFI_CONNECTED_STATE, "improv_got_ip", &cb_improv_got_ip);
+    network_register_state_callback(NETWORK_WIFI_ACTIVE_STATE,WIFI_CONNECTING_NEW_FAILED_STATE, "improv_disconnect", &cb_improv_disconnected);
+    network_register_state_callback(NETWORK_WIFI_CONFIGURING_ACTIVE_STATE,WIFI_CONFIGURING_CONNECT_FAILED_STATE, "improv_disconnect", &cb_improv_disconnected);  
+}

+ 23 - 0
components/platform_console/improv_console.h

@@ -0,0 +1,23 @@
+#pragma once
+#include "network_manager.h"
+#include "improv.h"
+#include "freertos/freertos.h"
+#include <stdio.h>
+#include <stdlib.h>
+#define BUFFER_DEBUG 0
+
+
+extern TickType_t improv_timeout_tick;
+extern const size_t improv_buffer_size;
+extern size_t improv_buffer_len;
+extern uint8_t * improv_buffer_data;
+extern const time_t improv_timeout_ms;
+extern TickType_t improv_delay;
+
+
+void cb_improv_got_ip(nm_state_t new_state, int sub_state);
+bool on_improv_command(ImprovCommandStruct_t *command);
+void on_improv_error(ImprovError_t error);
+void dump_buffer(const char * prefix, const char * buff, size_t len);
+bool improv_send_callback(uint8_t * buffer, size_t length);
+void improv_console_init();

+ 69 - 18
components/platform_console/platform_console.c

@@ -27,17 +27,18 @@
 #include "platform_config.h"
 #include "telnet.h" 
 #include "tools.h"
-
+#include "improv.h"
 #include "messaging.h"
-
+#include "network_manager.h"
 #include "config.h"
+#include "improv_console.h"
 static pthread_t thread_console;
 static void * console_thread();
 void console_start();
 static const char * TAG = "console";
 extern bool bypass_network_manager;
 extern void register_squeezelite();
-
+bool improv=false;
 static EXT_RAM_ATTR QueueHandle_t uart_queue;
 static EXT_RAM_ATTR struct {
 		uint8_t _buf[512];
@@ -249,31 +250,77 @@ void process_autoexec(){
 	}
 }
 
+
+#define BUFFERDEBUG 0
 static ssize_t stdin_read(int fd, void* data, size_t size) {
 	size_t bytes = -1;
+	uint32_t improv_next_timeout = 0;
+	if(!improv_buffer_data){
+		improv_buffer_data = (uint8_t * )malloc_init_external(improv_buffer_size);
+		memset(improv_buffer_data,0x00,improv_buffer_size);
+		improv_set_send_callback(improv_send_callback);
+	}
+	size_t read_size = 0;
 	
 	while (1) {
-		QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, portMAX_DELAY);
-	
-		if (activated == uart_queue) {
-			uart_event_t event;
-			
-			xQueueReceive(uart_queue, &event, 0);
-	
-			if (event.type == UART_DATA) {
-				bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < event.size ? size : event.size, 0);
-				// we have to do our own line ending translation here 
-				for (int i = 0; i < bytes; i++) if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';
-				break;
-			}	
-		} else if (xRingbufferCanRead(stdin_redir.handle, activated)) {
+		QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, improv_delay);
+		uint32_t now = esp_timer_get_time() / 1000;			uart_event_t event;
+		xQueueReceive(uart_queue, &event, 0);
+		//esp_rom_printf(".");
+		//esp_rom_printf("\n********Activated: 0x%6X, type: %d\n",(unsigned int)activated, event.type);
+
+		if (event.type == UART_DATA) {
+			//esp_rom_printf("uart.");
+			#if BUFFERDEBUG
+			printf("\n********event: %d, read: %d\n", event.size,bytes);
+			#endif
+			bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, improv_buffer_data+improv_buffer_len,1, 0);
+			while(bytes>0){
+				//esp_rom_printf("rb[%c]\n",*(bufferdata+buffer_len));
+				improv_buffer_len++;
+				if(!improv_parse_serial_byte(improv_buffer_len-1,improv_buffer_data[improv_buffer_len-1],improv_buffer_data,on_improv_command,on_improv_error)){
+					#if BUFFERDEBUG
+					//dump_buffer("improv invalid",(const char *)bufferdata,buffer_len);
+					#endif
+					if(improv_buffer_len>0){
+						//esp_rom_printf("not improv\n");
+						xRingbufferSend(stdin_redir.handle, improv_buffer_data,improv_buffer_len, pdMS_TO_TICKS(100));
+					}
+					improv_buffer_len=0;
+				}
+				bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, improv_buffer_data+improv_buffer_len,1, 0);
+			}
+			improv_next_timeout = esp_timer_get_time() / 1000+improv_timeout_ms;
+			#if BUFFERDEBUG
+			//dump_buffer("after event",(const char *)bufferdata,buffer_len);
+			#endif
+		} 
+		if ( xRingbufferCanRead(stdin_redir.handle, activated)) {
+			//esp_rom_printf("\n********rbr!\n");
 			char *p = xRingbufferReceiveUpTo(stdin_redir.handle, &bytes, 0, size);
 			// we might receive strings, replace null by \n
+			#if BUFFERDEBUG
+			//dump_buffer("Ringbuf read",p,bytes);
+			#endif
 			for (int i = 0; i < bytes; i++) if (p[i] == '\0' || p[i] == '\r') p[i] = '\n';						
 			memcpy(data, p, bytes);
 			vRingbufferReturnItem(stdin_redir.handle, p);
 			break;
 		}
+		if(improv_buffer_len>0){
+			improv_delay = improv_timeout_tick;
+		}
+		else {
+			improv_delay = portMAX_DELAY;
+		}
+		if (now > improv_next_timeout && improv_buffer_len > 0) {
+			#if BUFFERDEBUG
+			//dump_buffer("improv timeout",(const char *)bufferdata,buffer_len);
+			#endif
+			//esp_rom_printf("\n********QueueSent\n");
+			xRingbufferSendFromISR(stdin_redir.handle, improv_buffer_data, improv_buffer_len, pdMS_TO_TICKS(100));
+			improv_buffer_len = 0;
+		}
 	}	
 	
 	return bytes;
@@ -304,6 +351,9 @@ void initialize_console() {
 		
 	/* re-direct stdin to our own driver so we can gather data from various sources */
 	stdin_redir.queue_set = xQueueCreateSet(2);
+	if(!stdin_redir.queue_set){
+		ESP_LOGE(TAG,"Serial event queue set could not be created");
+	}
 	stdin_redir.handle = xRingbufferCreateStatic(sizeof(stdin_redir._buf), RINGBUF_TYPE_BYTEBUF, stdin_redir._buf, &stdin_redir._ringbuf);
 	xRingbufferAddToQueueSetRead(stdin_redir.handle, stdin_redir.queue_set);
 	xQueueAddToSet(uart_queue, stdin_redir.queue_set);
@@ -312,7 +362,6 @@ void initialize_console() {
 	vfs.flags = ESP_VFS_FLAG_DEFAULT;
 	vfs.open = stdin_dummy;
 	vfs.read = stdin_read;
-
 	ESP_ERROR_CHECK(esp_vfs_register("/dev/console", &vfs, NULL));
 	freopen("/dev/console", "r", stdin);
 
@@ -352,6 +401,7 @@ bool console_push(const char *data, size_t size) {
 void console_start() {
 	/* we always run console b/c telnet sends commands to stdin */
 	initialize_console();
+	improv_console_init();
 
 	/* Register commands */
 	MEMTRACE_PRINT_DELTA_MESSAGE("Registering help command");
@@ -449,6 +499,7 @@ static esp_err_t run_command(char * line){
 }
 
 static void * console_thread() {
+	
 	if(!is_recovery_running){
 		MEMTRACE_PRINT_DELTA_MESSAGE("Running autoexec");
 		process_autoexec();

+ 2 - 2
components/wifi-manager/CMakeLists.txt

@@ -3,8 +3,8 @@ set( WEBPACK_DIR webapp/webpack/dist )
 
 idf_component_register( SRC_DIRS . webapp UML-State-Machine-in-C/src
 						INCLUDE_DIRS . webapp UML-State-Machine-in-C/src
-						REQUIRES squeezelite-ota json mdns 
-						PRIV_REQUIRES tools services platform_config esp_common json newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant platform_console esp_http_server console driver_bt
+						REQUIRES squeezelite-ota json mdns  bt
+						PRIV_REQUIRES  tools services platform_config esp_common json newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant platform_console esp_http_server console driver_bt
 )
 
 include(webapp/webapp.cmake)

+ 431 - 0
components/wifi-manager/improv.c

@@ -0,0 +1,431 @@
+#include "esp_system.h"
+#include "esp_log.h"
+#include "improv.h"
+#include "tools.h"
+#include "string.h"
+static ImprovCommandStruct_t last_command;
+
+static callback_table_t callbacks[] = {
+    {IMPROV_CMD_UNKNOWN, NULL},
+    {IMPROV_CMD_WIFI_SETTINGS, NULL},
+    {IMPROV_CMD_GET_CURRENT_STATE, NULL},
+    {IMPROV_CMD_GET_DEVICE_INFO, NULL},
+    {IMPROV_CMD_GET_WIFI_NETWORKS, NULL},
+    {IMPROV_CMD_BAD_CHECKSUM, NULL},
+    {-1, NULL}};
+const char *improv_get_error_desc(ImprovError_t error)
+{
+  switch (error)
+  {
+    ENUM_TO_STRING(IMPROV_ERROR_NONE)
+    ENUM_TO_STRING(IMPROV_ERROR_INVALID_RPC)
+    ENUM_TO_STRING(IMPROV_ERROR_UNKNOWN_RPC)
+    ENUM_TO_STRING(IMPROV_ERROR_UNABLE_TO_CONNECT)
+    ENUM_TO_STRING(IMPROV_ERROR_NOT_AUTHORIZED)
+    ENUM_TO_STRING(IMPROV_ERROR_UNKNOWN)
+  }
+  return "";
+}
+const char *improv_get_command_desc(ImprovCommand_t command)
+{
+  switch (command)
+  {
+    ENUM_TO_STRING(IMPROV_CMD_UNKNOWN)
+    ENUM_TO_STRING(IMPROV_CMD_WIFI_SETTINGS)
+    ENUM_TO_STRING(IMPROV_CMD_GET_CURRENT_STATE)
+    ENUM_TO_STRING(IMPROV_CMD_GET_DEVICE_INFO)
+    ENUM_TO_STRING(IMPROV_CMD_GET_WIFI_NETWORKS)
+    ENUM_TO_STRING(IMPROV_CMD_BAD_CHECKSUM)
+  }
+  return "";
+};
+static improv_send_callback_t send_callback = NULL;
+const uint8_t improv_prefix[] = {'I', 'M', 'P', 'R', 'O', 'V', IMPROV_SERIAL_VERSION};
+typedef struct __attribute__((__packed__))
+{
+  uint8_t prefix[6];
+  uint8_t version;
+  uint8_t packet_type;
+  uint8_t data_len;
+} improv_packet_t;
+#define PACKET_CHECKSUM_SIZE sizeof(uint8_t)
+#define PACKET_PAYLOAD(packet) ((uint8_t *)packet) + sizeof(improv_packet_t)
+
+static ImprovAPListStruct_t *ap_list = NULL;
+static size_t ap_list_size = 0;
+static size_t ap_list_actual = 0;
+void improv_wifi_list_free()
+{
+  ap_list_actual = 0;
+  ImprovAPListStruct_t *current = ap_list;
+  for (int i = 0; i < ap_list_actual; i++)
+  {
+    if (!current)
+    {
+      break;
+    }
+    FREE_AND_NULL(current->rssi);
+    FREE_AND_NULL(current->ssid);
+    FREE_AND_NULL(current->auth_req);
+    current++;
+  }
+  FREE_AND_NULL(ap_list);
+}
+bool improv_wifi_list_allocate(size_t num_entries)
+{
+  improv_wifi_list_free();
+  ap_list = malloc_init_external(num_entries * sizeof(ImprovAPListStruct_t) + 1); // last byte will always be null
+  ap_list_size = num_entries;
+  ap_list_actual = 0;
+  return ap_list != NULL;
+}
+
+bool improv_wifi_list_add(const char *ssid, int8_t rssi, bool auth_req)
+{
+  const size_t yes_no_length = 4;
+  ImprovAPListStruct_t *current = ap_list + ap_list_actual;
+  if (ap_list_actual > ap_list_size || !current)
+  {
+    return false;
+  }
+  current->ssid = strdup_psram(ssid);
+  size_t length = snprintf(NULL, 0, "%02d", rssi) + 1;
+  current->auth_req = malloc_init_external(yes_no_length); // enough for YES/NO to fit
+  current->rssi = (char *)malloc_init_external(length);
+  if (!current->rssi || !current->auth_req)
+  {
+    return false;
+  }
+  snprintf(current->rssi, length, "%02d", rssi);
+  snprintf(current->auth_req, yes_no_length, "%s", auth_req ? "YES" : "NO");
+  ap_list_actual++;
+  return true;
+}
+
+void improv_parse_data(ImprovCommandStruct_t *improv_command, const uint8_t *data, size_t length, bool check_checksum)
+{
+
+  ImprovCommand_t command = (ImprovCommand_t)data[0];
+  uint8_t data_length = data[1];
+
+  if (data_length != length - 2 - check_checksum)
+  {
+    improv_command->command = IMPROV_CMD_UNKNOWN;
+    return;
+  }
+
+  if (check_checksum)
+  {
+    uint8_t checksum = data[length - 1];
+
+    uint32_t calculated_checksum = 0;
+    for (uint8_t i = 0; i < length - 1; i++)
+    {
+      calculated_checksum += data[i];
+    }
+
+    if ((uint8_t)calculated_checksum != checksum)
+    {
+      improv_command->command = IMPROV_CMD_BAD_CHECKSUM;
+      return;
+    }
+  }
+
+  if (command == IMPROV_CMD_WIFI_SETTINGS)
+  {
+    uint8_t ssid_length = data[2];
+    uint8_t ssid_start = 3;
+    size_t ssid_end = ssid_start + ssid_length;
+
+    uint8_t pass_length = data[ssid_end];
+    size_t pass_start = ssid_end + 1;
+    size_t pass_end = pass_start + pass_length;
+
+    improv_command->ssid = malloc(ssid_length + 1);
+    memset(improv_command->ssid, 0x00, ssid_length + 1);
+    memcpy(improv_command->ssid, &data[ssid_start], ssid_length);
+    improv_command->password = NULL;
+    if (pass_length > 0)
+    {
+      improv_command->password = malloc(pass_length + 1);
+      memset(improv_command->password, 0x00, pass_length + 1);
+      memcpy(improv_command->password, &data[pass_start], pass_length);
+    }
+  }
+
+  improv_command->command = command;
+}
+
+bool improv_parse_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,
+                              improv_command_callback_t callback, on_error_callback_t on_error)
+{
+  ImprovCommandStruct_t command = {0};
+  if (position < 7)
+    return byte == improv_prefix[position];
+  if (position <= 8)
+    return true;
+
+  uint8_t command_type = buffer[7];
+  uint8_t data_len = buffer[8];
+
+  if (position <= 8 + data_len)
+    return true;
+
+  if (position == 8 + data_len + 1)
+  {
+    uint8_t checksum = 0x00;
+    for (size_t i = 0; i < position; i++)
+      checksum += buffer[i];
+
+    if (checksum != byte)
+    {
+      on_error(IMPROV_ERROR_INVALID_RPC);
+      return false;
+    }
+
+    if (command_type == IMPROV_PACKET_TYPE_RPC)
+    {
+
+      improv_parse_data(&command, &buffer[9], data_len, false);
+      callback(&command);
+    }
+  }
+
+  return false;
+}
+
+void improv_set_send_callback(improv_send_callback_t callback)
+{
+  send_callback = callback;
+}
+
+bool improv_set_callback(ImprovCommand_t command, improv_command_callback_t callback)
+{
+  callback_table_t *pCt = &callbacks;
+  while (pCt->index > -1)
+  {
+    if (pCt->index == command)
+    {
+      pCt->callback = callback;
+      return true;
+    }
+  }
+  return false;
+}
+bool improv_handle_callback(ImprovCommandStruct_t *command)
+{
+  const callback_table_t *pCt = &callbacks;
+  while (pCt->index > -1)
+  {
+    if (pCt->index == command->command)
+    {
+      return pCt->callback && pCt->callback(command);
+    }
+  }
+  return false;
+}
+bool improv_send_packet(uint8_t *packet, size_t msg_len)
+{
+  bool result = false;
+  if (send_callback && packet && msg_len > 0)
+  {
+    result = send_callback(packet, msg_len);
+  }
+  return result;
+}
+bool improv_send_byte(ImprovSerialType_t packet_type, uint8_t data)
+{
+  size_t msg_len;
+  uint8_t *packet = improv_build_response(packet_type, (const char *)&data, 1, &msg_len);
+  bool result = improv_send_packet(packet, msg_len);
+  FREE_AND_NULL(packet);
+  return result;
+}
+
+bool improv_send_current_state(ImprovState_t state)
+{
+  return improv_send_byte(IMPROV_PACKET_TYPE_CURRENT_STATE, (uint8_t)state);
+}
+bool improv_send_error(ImprovError_t error)
+{
+  return improv_send_byte(IMPROV_PACKET_TYPE_ERROR_STATE, (uint8_t)error);
+}
+size_t improv_wifi_get_wifi_list_count(){
+  return ap_list_actual;
+}
+bool improv_wifi_list_send()
+{
+  size_t msglen = 0;
+  bool result = true;
+  if (ap_list_actual == 0)
+  {
+    return false;
+  }
+  for (int i = 0; i < ap_list_actual && result; i++)
+  {
+    uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_WIFI_NETWORKS,(const char **) &ap_list[i], IMPROV_AP_STRUCT_NUM_STR, &msglen);
+    result = improv_send_packet(packet, msglen);
+    FREE_AND_NULL(packet);
+  }
+  uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_WIFI_NETWORKS, NULL, 0, &msglen);
+  result = improv_send_packet(packet, msglen);
+  FREE_AND_NULL(packet);
+  return result;
+}
+bool improv_send_device_url(ImprovCommand_t from_command, const char *url)
+{
+  size_t msglen = 0;
+  uint8_t *packet = NULL;
+  bool result = false;
+  if (url && strlen(url))
+  {
+    packet = improv_build_rpc_response(from_command, &url, 1, &msglen);
+    if (!packet)
+      return false;
+    result = improv_send_packet(packet, msglen);
+      FREE_AND_NULL(packet);
+  }
+  packet = improv_build_rpc_response(from_command, "", 0, &msglen);
+  if (!packet)
+    return false;
+  result = improv_send_packet(packet, msglen);
+
+  
+
+  return result;
+}
+bool improv_send_device_info(const char *firmware_name, const char *firmware_version, const char *hardware_chip_variant, const char *device_name)
+{
+  ImprovDeviceInfoStruct_t device_info;
+  size_t msglen = 0;
+  device_info.device_name = device_name;
+  device_info.firmware_name = firmware_name;
+  device_info.firmware_version = firmware_version;
+  device_info.hardware_chip_variant = hardware_chip_variant;
+  device_info.nullptr = NULL;
+  uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_DEVICE_INFO, &device_info, IMPROV_DEVICE_INFO_NUM_STRINGS, &msglen);
+  if (!packet)
+    return false;
+  bool result = improv_send_packet(packet, msglen);
+  FREE_AND_NULL(packet);
+  return true;
+}
+bool parse_improv_serial_line(const uint8_t *buffer)
+{
+  const uint8_t *b = buffer;
+  const uint8_t *p = improv_prefix;
+  const uint8_t *data = NULL;
+  uint8_t checksum = 0x00;
+  uint8_t rec_checksum = 0x00;
+
+  while (*p != '\0' && *b != '\0')
+  {
+    // check if line prefix matches the standard
+    if (*p++ != *b++)
+    {
+      return false;
+    }
+  }
+
+  uint8_t command_type = *p++;
+  if (command_type == 0)
+    return false;
+  uint8_t data_len = *p++;
+  data = p;
+  rec_checksum = buffer[sizeof(improv_prefix) + data_len];
+  for (size_t i = 0; i < sizeof(improv_prefix) + data_len; i++)
+  {
+    checksum += buffer[i];
+  }
+
+  if (checksum != rec_checksum)
+  {
+    improv_send_error(IMPROV_ERROR_INVALID_RPC);
+    return false;
+  }
+
+  if (command_type == IMPROV_PACKET_TYPE_RPC)
+  {
+    improv_parse_data(&last_command, &data, data_len, false);
+    return improv_handle_callback(&last_command);
+  }
+
+  return false;
+}
+// Improv packet format
+// 1-6	Header will equal IMPROV
+// 7	Version CURRENT VERSION = 1
+// 8	Type (see below)
+// 9	Length
+// 10...X	Data
+// X + 10	Checksum
+improv_packet_t *improv_alloc_prefix(size_t data_len, ImprovSerialType_t packet_type, size_t *out_len)
+{
+  size_t buffer_len = sizeof(improv_packet_t) + data_len + 1; // one byte for checksum
+  if (out_len)
+  {
+    *out_len = buffer_len;
+  }
+  improv_packet_t *out = (improv_packet_t *)malloc_init_external(buffer_len + 1);
+  memcpy(out, improv_prefix, sizeof(improv_prefix));
+  out->packet_type = (uint8_t)packet_type;
+  out->data_len = (uint8_t)data_len;
+  return out;
+}
+uint8_t improv_set_checksum(improv_packet_t *data, size_t buffer_len)
+{
+  uint32_t calculated_checksum = 0;
+  for (int b = 0; b < buffer_len - 1; b++)
+  {
+    calculated_checksum += ((uint8_t *)data)[b];
+  }
+  calculated_checksum = calculated_checksum & 0xFF;
+  ((uint8_t *)data)[buffer_len - 1] = (uint8_t)calculated_checksum;
+  return calculated_checksum;
+}
+uint8_t *improv_build_response(ImprovSerialType_t packet_type, const char *datum, size_t len, size_t *out_len)
+{
+  size_t buffer_len = 0;
+  improv_packet_t *improv_packet = improv_alloc_prefix(len, packet_type, &buffer_len);
+  if (out_len)
+  {
+    *out_len = buffer_len;
+  }
+  uint8_t *p = PACKET_PAYLOAD(improv_packet);
+  for (int i = 0; i < len; i++)
+  {
+    *p++ = datum[i]; // string 1
+  }
+  improv_set_checksum(improv_packet, buffer_len);
+  return (uint8_t *)improv_packet;
+}
+uint8_t *improv_build_rpc_response(ImprovCommand_t command, const char **results, size_t num_strings, size_t *out_len)
+{
+  size_t buffer_len = 0;
+  size_t total_string_len = 0;
+  size_t string_buffer_len = 0;
+  for (int i = 0; i < num_strings && (results[i] && (results[i])[0] != '\0'); i++)
+  {
+    size_t l = strlen(results[i]);
+    total_string_len += l;
+    string_buffer_len += l + 1; // length of the string plus byte for length
+  }
+
+  improv_packet_t *improv_packet = improv_alloc_prefix(string_buffer_len + 2, IMPROV_PACKET_TYPE_RPC_RESPONSE, &buffer_len); // 2 bytes for command and length of all strings
+  if (out_len)
+  {
+    *out_len = buffer_len;
+  }
+  uint8_t *p = PACKET_PAYLOAD(improv_packet);
+  *p++ = (uint8_t)command;           // command being responded to
+  *p++ = (uint8_t)string_buffer_len; //
+  for (int i = 0; i < num_strings && results[i]; i++)
+  {
+    uint8_t curlel = (uint8_t)strlen(results[i]);
+    *p++ = curlel;
+    memcpy(p, results[i], curlel);
+    p += curlel;
+  }
+  improv_set_checksum(improv_packet, buffer_len);
+  return (uint8_t *)improv_packet;
+}

+ 279 - 0
components/wifi-manager/improv.h

@@ -0,0 +1,279 @@
+#pragma once
+// This is the description of the Improv Wi-Fi protocol using a serial port.
+// The device needs to be connected to the computer via a USB/UART serial port.
+// The protocol has two actors: the Improv service running on the gadget and the Improv client.
+// The Improv service will receive Wi-Fi credentials from the client via the serial connection.
+// The Improv client asks for the current state and sends the Wi-Fi credentials.
+
+// =========================================================================================
+// Packet format
+// ======================================
+// Byte	  Purpose
+// ----   -------------------------------
+// 1-6	  Header will equal IMPROV
+// 7	    Version CURRENT VERSION = 1
+// 8	    Type (see below)
+// 9	    Length
+// 10...X	Data
+// X + 10	Checksum
+
+// =========================================================================================
+// Packet types
+// ======================================
+// Type	Description	    Direction
+// ---- ------------    -----------------
+// 0x01	Current state   Device to Client
+// 0x02	Error state	    Device to Client
+// 0x03	RPC Command	    Device to Client
+// 0x04	RPC Result	    Client to Device
+typedef enum  {
+    IMPROV_PACKET_TYPE_CURRENT_STATE = 0x01,
+    IMPROV_PACKET_TYPE_ERROR_STATE = 0x02,
+    IMPROV_PACKET_TYPE_RPC = 0x03,
+    IMPROV_PACKET_TYPE_RPC_RESPONSE = 0x04
+} ImprovSerialType_t;
+
+// =========================================================================================
+// Packet: Current State
+// ======================================
+// Type: 0x01
+// Direction: Device to Client
+// --------------------------------------
+// The data of this packet is a single byte and contains the current status of the provisioning
+// service. It is to be written to any listening clients for instant feedback.
+
+// Byte	Description
+// 1	current state
+// The current state can be the following values:
+
+// Value	State	              Purpose
+// -----  ------------------  -----------------------------------------
+// 0x02	  Ready (Authorized)	Ready to accept credentials.
+// 0x03	  Provisioning	      Credentials received, attempt to connect.
+// 0x04	  Provisioned	        Connection successful.
+typedef enum   {
+  IMPROV_STATE_READY_AUTHORIZED = 0x02,
+  IMPROV_STATE_PROVISIONING = 0x03,
+  IMPROV_STATE_PROVISIONED = 0x04,
+} ImprovState_t;
+
+// =========================================================================================
+// Packet: Error state
+// ======================================
+// Type: 0x02
+// Direction: Device to client
+// --------------------------------------
+// The data of this packet is a single byte and contains the current status of the 
+// provisioning service. Whenever it changes the device needs to sent it to any listening 
+// clients for instant feedback.
+
+// Byte	Description
+// 1	error state
+// Error state can be the following values:
+
+// Value	State	              Purpose
+// -----  ------------------  -----------------------------------------
+// 0x00	  No error	          This shows there is no current error state.
+// 0x01	  Invalid RPC packet	RPC packet was malformed/invalid.
+// 0x02	  Unknown RPC command	The command sent is unknown.
+// 0x03	  Unable to connect	  The credentials have been received and an attempt to connect 
+//                            to the network has failed.
+// 0xFF	  Unknown Error	
+typedef enum  {
+  IMPROV_ERROR_NONE = 0x00,
+  IMPROV_ERROR_INVALID_RPC = 0x01,
+  IMPROV_ERROR_UNKNOWN_RPC = 0x02,
+  IMPROV_ERROR_UNABLE_TO_CONNECT = 0x03,
+  IMPROV_ERROR_NOT_AUTHORIZED = 0x04,
+  IMPROV_ERROR_UNKNOWN = 0xFF,
+} ImprovError_t;
+
+
+// =========================================================================================
+// Packet: RPC Command
+// Type: 0x03
+// Direction: Client to device
+// --------------------------------------
+// This packet type is used for the client to send commands to the device. When an RPC 
+// command is sent, the device should sent an update to the client to set the error state to 
+// 0 (no error). The response will either be an RPC result packet or an error state update.
+
+// Byte	  Description
+// -----  ---------------------
+// 1	    Command (see below)
+// 2	    Data length
+// 3...X	Data
+typedef enum   {
+  IMPROV_CMD_UNKNOWN = 0x00,
+  IMPROV_CMD_WIFI_SETTINGS = 0x01,
+  IMPROV_CMD_GET_CURRENT_STATE = 0x02,
+  IMPROV_CMD_GET_DEVICE_INFO = 0x03,
+  IMPROV_CMD_GET_WIFI_NETWORKS = 0x04,
+  IMPROV_CMD_BAD_CHECKSUM = 0xFF,
+} ImprovCommand_t;
+
+// ======================================
+// RPC Command: Send Wi-Fi settings
+// Submit Wi-Fi credentials to the Improv Service to attempt to connect to.
+// Type: 0x03
+// Command ID: 0x01
+
+// Byte	  Description
+// -----  ----------------
+// 1	    command (0x01)
+// 2	    data length
+// 3	    ssid length
+// 4...X	ssid bytes
+// X	    password length
+// X...Y	password bytes
+
+// Example: SSID = MyWirelessAP, Password = mysecurepassword
+// 01 1E 0C {MyWirelessAP} 10 {mysecurepassword}
+// This command will generate an RPC result. The first entry in the list is an URL to 
+// redirect the user to. 
+// If there is no URL, omit the entry or add an empty string.
+
+// ======================================
+// RPC Command: Request current state
+// Sends a request for the device to send the current state of improv to the client.
+
+// Type: 0x03
+// Command ID: 0x02
+
+// Byte	  Description
+// -----  ----------------
+// 1	    command (0x02)
+// 2	    data length (0)
+// This command will trigger at least one packet, the Current State (see above) and if 
+// already provisioned, 
+// the same response you would get if device provisioning was successful (see below).
+
+// ======================================
+// RPC Command: Request device information
+// Sends a request for the device to send information about itself.
+
+// Type: 0x03
+// Command ID: 0x03
+
+// Byte	  Description
+// -----  ----------------
+// 1	    command (0x02)
+// 2	    data length (0)
+
+// This command will trigger one packet, the Device Information formatted as a RPC result. 
+// This result will contain at least 4 strings.
+
+// Order of strings: Firmware name, firmware version, hardware chip/variant, device name.
+
+// Example: ESPHome, 2021.11.0, ESP32-C3, Temperature Monitor.
+
+// ======================================
+// RPC Command: Request scanned Wi-Fi networks
+// Sends a request for the device to send the Wi-Fi networks it sees.
+
+// Type: 0x03
+// Command ID: 0x04
+
+// Byte	  Description
+// -----  ----------------
+// 1	    command (0x02)
+// 2	    data length (0)
+// This command will trigger at least one RPC Response. Each response will contain at 
+// least 3 strings.
+
+// Order of strings: Wi-Fi SSID, RSSI, Auth required.
+
+// Example: MyWirelessNetwork, -60, YES.
+
+// The final response (or the first if no networks are found) will have 0 strings in the body.
+
+// =========================================================================================
+// Packet: RPC Result
+// ======================================
+// Type: 0x04
+// Direction: Device to client
+// --------------------------------------
+// This packet type contains the response to an RPC command. Results are returned as a list 
+// of strings. An empty list is allowed.
+
+// Byte	  Description
+// -----  ----------------
+// 1	    Command being responded to (see above)
+// 2	    Data length
+// 3	    Length of string 1
+// 4...X	String 1
+// X	    Length of string 2
+// X...Y	String 2
+// ...	  etc
+
+
+
+
+
+
+
+
+
+static const uint8_t CAPABILITY_IDENTIFY = 0x01;
+static const uint8_t IMPROV_SERIAL_VERSION = 1;
+
+#ifndef FREE_AND_NULL
+#define FREE_AND_NULL(x) if(x) { free(x); x=NULL; }
+#endif
+#ifndef ENUM_TO_STRING
+#define ENUM_TO_STRING(g) 	\
+    case g:    				\
+        return STR(g);    	\
+        break;
+#endif
+
+
+typedef struct  {
+  ImprovCommand_t command;
+  char * ssid;
+  char * password;
+} ImprovCommandStruct_t;
+typedef struct {
+  char * ssid;
+  char * rssi;
+  char * auth_req; // YES/NO
+} ImprovAPListStruct_t;
+#define IMPROV_AP_STRUCT_NUM_STR 3
+typedef struct {
+  char * firmware_name;
+  char * firmware_version;
+  char * hardware_chip_variant;
+  char * device_name;
+  char * nullptr;
+} ImprovDeviceInfoStruct_t;
+#define IMPROV_DEVICE_INFO_NUM_STRINGS 4
+
+typedef bool (*improv_command_callback_t)(ImprovCommandStruct_t *cmd);
+typedef void (*on_error_callback_t)(ImprovError_t error);
+typedef bool (*improv_send_callback_t)(uint8_t * buffer, size_t length);
+typedef struct {
+  int index ;
+  improv_command_callback_t callback ;
+} callback_table_t;
+
+void improv_parse_data(ImprovCommandStruct_t * improv_command, const uint8_t *data, size_t length, bool check_checksum) ;
+bool improv_parse_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,improv_command_callback_t callback, on_error_callback_t on_error);
+bool parse_improv_serial_line( const uint8_t *buffer);
+void improv_set_send_callback(improv_send_callback_t callback );
+
+bool improv_set_callback(ImprovCommand_t command, improv_command_callback_t callback  );
+bool improv_wifi_list_allocate(size_t num_entries);
+bool improv_wifi_list_add(const char * ssid, int8_t rssi, bool auth_req );
+bool improv_wifi_list_send(  );
+size_t improv_wifi_get_wifi_list_count();
+bool improv_send_device_info(  const char * firmware_name, const char * firmware_version,  const char * hardware_chip_variant,  const char * device_name);
+uint8_t * improv_build_response(ImprovSerialType_t command, const char * datum, size_t len, size_t * out_len);
+uint8_t * improv_build_rpc_response(ImprovCommand_t command, const char ** results, size_t num_strings, size_t * out_len);
+bool improv_send_current_state(ImprovState_t state);
+bool improv_send_error(ImprovError_t error);
+const char * improv_get_error_desc(ImprovError_t error);
+const char * improv_get_command_desc(ImprovCommand_t command);
+bool improv_send_device_url( ImprovCommand_t from_command, const char * url);
+// Improv Wi-Fi   –   Contact   –   GitHub
+// Improv is an initiative by ESPHome & Home Assistant.
+// Development funded by Nabu Casa.

+ 53 - 30
components/wifi-manager/network_manager.c

@@ -62,20 +62,23 @@ typedef struct network_callback {
     SLIST_ENTRY(network_callback)
     next;  //!< next callback
 } network_callback_t;
-
+static wifi_connect_state_t wifi_connect_state = NETWORK_WIFI_STATE_INIT;
 /** linked list of command structures */
 static SLIST_HEAD(cb_list, network_callback) s_cb_list;
-
 network_t NM;
 
-
 //! Create and initialize the array of state machines.
 state_machine_t* const SM[] = {(state_machine_t*)&NM};
 static void network_timer_cb(void* timer_id);
 int get_root_id(const state_t *  state);
 const state_t* get_root( const state_t* const state);
 static void network_task(void* pvParameters);
-
+void network_wifi_set_connect_state(wifi_connect_state_t state){
+    wifi_connect_state = state;
+}
+wifi_connect_state_t network_wifi_get_connect_state(){
+    return wifi_connect_state;
+}
 void network_start_stop_dhcp_client(esp_netif_t* netif, bool start) {
     tcpip_adapter_dhcp_status_t status;
     esp_err_t err = ESP_OK;
@@ -195,17 +198,21 @@ void network_start_stop_dhcps(esp_netif_t* netif, bool start) {
 #define ADD_LEAF(name,...) CASE_TO_STR(name);
 #define ADD_EVENT(name) CASE_TO_STR(name);
 #define ADD_FIRST_EVENT(name) CASE_TO_STR(name);
-static const char* state_to_string(const  state_t * state) {
-    if(!state) {
-        return "";
-    }
-    switch (state->Parent?state->Parent->Id:state->Id) {
+static const char* nm_state_to_string(nm_state_t  state) {
+    switch (state) {
         ALL_NM_STATE
         default:
             break;
     }
     return "Unknown";
 }
+static const char* state_to_string(const  state_t * state) {
+    if(!state) {
+        return "";
+    }
+    return nm_state_to_string(state->Parent?state->Parent->Id:state->Id);
+   
+}
 static const char* wifi_state_to_string(mn_wifi_active_state_t state) {
     switch (state) {
         ALL_WIFI_STATE(,)
@@ -230,25 +237,27 @@ static const char* wifi_configuring_state_to_string(mn_wifi_configuring_state_t
     }
     return "Unknown";
 }
-static const char* sub_state_to_string(const state_t * state) {
-    if(!state) {
-        return "N/A";
-    }
-    int root_id = get_root_id(state);
-    switch (root_id)
+static const char* sub_state_id_to_string(nm_state_t state, int substate) {
+    switch (state)
     {
     case NETWORK_ETH_ACTIVE_STATE:
-        return eth_state_to_string(state->Id);
+        return eth_state_to_string(substate);
         break;
     case NETWORK_WIFI_ACTIVE_STATE:
-        return wifi_state_to_string(state->Id);
+        return wifi_state_to_string(substate);
     case NETWORK_WIFI_CONFIGURING_ACTIVE_STATE:
-        return wifi_configuring_state_to_string(state->Id);
+        return wifi_configuring_state_to_string(substate);
     default:
         break;
     }
     return "*";
 }
+static const char* sub_state_to_string(const state_t * state) {
+    if(!state) {
+        return "N/A";
+    }
+    return sub_state_id_to_string(get_root_id(state), state->Id);
+}
 
 static const char* event_to_string(network_event_t state) {
     switch (state) {
@@ -274,8 +283,7 @@ static const max_sub_states_t state_max[] = {
 { .parent_state = NETWORK_INSTANTIATED_STATE, .sub_state_last = 0 },
 {.parent_state = NETWORK_ETH_ACTIVE_STATE, .sub_state_last = TOTAL_ETH_ACTIVE_STATE-1 },
 {.parent_state = NETWORK_WIFI_ACTIVE_STATE, .sub_state_last = TOTAL_WIFI_ACTIVE_STATE-1 },
-{.parent_state = WIFI_CONFIGURING_STATE, .sub_state_last = TOTAL_WIFI_CONFIGURING_STATE-1 },
-{.parent_state = WIFI_CONFIGURING_STATE, .sub_state_last = TOTAL_WIFI_CONFIGURING_STATE-1 },
+{.parent_state = NETWORK_WIFI_CONFIGURING_ACTIVE_STATE, .sub_state_last = TOTAL_WIFI_CONFIGURING_STATE-1 },
 {.parent_state =-1}
 };
 
@@ -345,23 +353,27 @@ static void network_task(void* pvParameters) {
      return -1;
  }
 esp_err_t network_register_state_callback(nm_state_t state,int sub_state, const char* from, network_status_reached_cb cb) {
+    const char * error_prefix = "Error registering callback for State" ;
     network_callback_t* item = NULL;
     if (!cb) {
         return ESP_ERR_INVALID_ARG;
     }
     item = calloc(1, sizeof(*item));
     if (item == NULL) {
+        ESP_LOGE(TAG,"%s %s. Memory allocation failed",error_prefix, nm_state_to_string(state));
         return ESP_ERR_NO_MEM;
     }
     if(sub_state != -1 && sub_state>get_max_substate(state)){
         // sub state has to be valid
+        ESP_LOGE(TAG,"%s %s. Substate %d/%d %s",error_prefix, nm_state_to_string(state), sub_state,get_max_substate(state), sub_state>get_max_substate(state)?"out of boundaries":"invalid");
         return ESP_ERR_INVALID_ARG;
     }
-
+    ESP_LOGI(TAG,"Registering callback for State %s, substate %s: %s", nm_state_to_string(state), sub_state_id_to_string(state,sub_state), from);
     item->state = state;
     item->cb = cb;
     item->from = from;
     item->sub_state=sub_state;
+    
     network_callback_t* last = SLIST_FIRST(&s_cb_list);
     if (last == NULL) {
         SLIST_INSERT_HEAD(&s_cb_list, item, next);
@@ -389,19 +401,26 @@ static bool is_root_state(const state_t *  state){
 static bool is_current_state(const state_t*  state, nm_state_t  state_id, int sub_state_id){
     return get_root(state)->Id == state_id && (sub_state_id==-1 || (!is_root_state(state) && state->Id == sub_state_id) );
 }
+
 void network_execute_cb(state_machine_t* const state_machine, const char * caller) {
     network_callback_t* it;
+    bool found=false;
+    ESP_LOGI(TAG,"Checking if we need to invoke callbacks. ");
     SLIST_FOREACH(it, &s_cb_list, next) {
         if (is_current_state(state_machine->State,it->state, it->sub_state)) {
             char * cb_prefix= messaging_alloc_format_string("BEGIN Executing Callback %s", it->from) ;
             NETWORK_DEBUG_STATE_MACHINE(true,STR_OR_BLANK(cb_prefix),state_machine,false, STR_OR_BLANK(caller));    
             FREE_AND_NULL(cb_prefix);
             it->cb((nm_state_t)get_root(state_machine->State)->Id, is_root_state(state_machine->State)?-1:state_machine->State->Id);
+            found = true;
             cb_prefix= messaging_alloc_format_string("END Executing Callback %s", it->from) ;
             NETWORK_DEBUG_STATE_MACHINE(false,STR_OR_BLANK(cb_prefix),state_machine,false, STR_OR_BLANK(caller));    
             FREE_AND_NULL(cb_prefix);
         }
     }
+    if(!found){
+        NETWORK_DEBUG_STATE_MACHINE(true,"No Callback found ",state_machine,false, STR_OR_BLANK(caller));    
+    }
 }
 
 bool network_is_wifi_prioritized() {
@@ -465,7 +484,7 @@ void network_manager_format_from_to_states(esp_log_level_t level, const char* pr
         source_sub_state = sub_state_to_string(from_state);
     }
     if (show_source) {
-        ESP_LOG_LEVEL(level, TAG, "%s %s %s(%s)->%s(%s) [%s]",
+        ESP_LOG_LEVEL(level, TAG, "%s %s %s.%s->%s.%s [evt:%s]",
                       STR_OR_BLANK(caller),
                       prefix,
                       source_state,
@@ -724,19 +743,23 @@ void network_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t ev
             break;
     }
 }
-void network_set_hostname(esp_netif_t* interface) {
-    esp_err_t err;
+char * alloc_get_host_name(){
     ESP_LOGD(TAG, "Retrieving host name from nvs");
     char* host_name = (char*)config_alloc_get(NVS_TYPE_STR, "host_name");
     if (host_name == NULL) {
         ESP_LOGE(TAG, "Could not retrieve host name from nvs");
-    } else {
-        ESP_LOGD(TAG, "Setting host name to : %s", host_name);
-        if ((err = esp_netif_set_hostname(interface, host_name)) != ESP_OK) {
-            ESP_LOGE(TAG, "Unable to set host name. Error: %s", esp_err_to_name(err));
-        }
-        free(host_name);
+    } 
+    return host_name;
+}
+void network_set_hostname(esp_netif_t* interface) {
+    esp_err_t err;
+    char * host_name = alloc_get_host_name();
+    if(!host_name) return;
+    ESP_LOGD(TAG, "Setting host name to : %s", host_name);
+    if ((err = esp_netif_set_hostname(interface, host_name)) != ESP_OK) {
+        ESP_LOGE(TAG, "Unable to set host name. Error: %s", esp_err_to_name(err));
     }
+    free(host_name);
 }
 #define LOCAL_MAC_SIZE 20
 char* network_manager_alloc_get_mac_string(uint8_t mac[6]) {

+ 13 - 1
components/wifi-manager/network_manager.h

@@ -10,6 +10,7 @@
 #include "hsm.h"
 #include "esp_log.h"
 #include "network_services.h"
+#include "improv.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -257,10 +258,20 @@ typedef enum update_reason_code_t {
 	UPDATE_LOST_CONNECTION = 3,
 	UPDATE_FAILED_ATTEMPT_AND_RESTORE = 4,
 	UPDATE_ETHERNET_CONNECTED = 5
-
 }update_reason_code_t;
 
+typedef enum {
+    NETWORK_WIFI_STATE_INIT,
+    NETWORK_WIFI_STATE_CONNECTING,
+    NETWORK_WIFI_STATE_DOWN,
+    NETWORK_WIFI_STATE_INVALID_CONFIG,
+    NETWORK_WIFI_STATE_FAILED,
+    NETWORK_WIFI_STATE_CONNECTED
+
+} wifi_connect_state_t;
 
+void network_wifi_set_connect_state(wifi_connect_state_t state);
+wifi_connect_state_t network_wifi_get_connect_state();
 
 
 
@@ -312,6 +323,7 @@ void network_manager_initialise_mdns();
 bool network_is_wifi_prioritized();
 void network_set_timer(uint16_t duration, const char * tag);
 void network_set_hostname(esp_netif_t * netif);
+char * alloc_get_host_name();
 esp_err_t network_get_ip_info_for_netif(esp_netif_t* netif, tcpip_adapter_ip_info_t* ipInfo);
 void network_start_stop_dhcp_client(esp_netif_t* netif, bool start);
 void network_start_stop_dhcps(esp_netif_t* netif, bool start);

+ 78 - 10
components/wifi-manager/network_manager_handlers.c

@@ -1,7 +1,10 @@
 #ifdef NETWORK_HANDLERS_LOG_LEVEL
 #define LOG_LOCAL_LEVEL NETWORK_HANDLERS_LOG_LEVEL
+    #pragma message("Log Level overwritten to " LOG_LOCAL_LEVEL)
+#else
+    #pragma message("Log Level set to " LOG_LOCAL_LEVEL)
 #endif
-#include "network_manager.h"
+#include "network_manager.h" 
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -42,6 +45,7 @@
 #include "tools.h"
 #include "http_server_handlers.h"
 #include "network_manager.h"
+#include "improv.h"
 
 static const char TAG[]="network_handlers";
 
@@ -387,6 +391,7 @@ static state_machine_result_t NETWORK_ETH_ACTIVE_STATE_handler(state_machine_t*
         case EN_SCAN:
             ESP_LOGW(TAG,"Wifi  scan cannot be executed in this state");
             network_wifi_built_known_ap_list();
+            
             result = EVENT_HANDLED;
             break;
         case EN_DELETE: {
@@ -418,7 +423,9 @@ static state_machine_result_t ETH_CONNECTING_NEW_STATE_entry_handler(state_machi
     network_t* const nm = (network_t *)State_Machine;
     network_handler_entry_print(State_Machine,true);
     network_start_stop_dhcp_client(nm->wifi_netif, true);
-    network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password);
+    if(network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password) == ESP_ERR_INVALID_ARG){
+        network_async_fail();
+    }
     FREE_AND_NULL(nm->event_parameters->ssid);
     FREE_AND_NULL(nm->event_parameters->password);
     NETWORK_EXECUTE_CB(State_Machine);
@@ -430,6 +437,11 @@ static state_machine_result_t ETH_CONNECTING_NEW_STATE_handler(state_machine_t*
     network_handler_print(State_Machine,true);
     state_machine_result_t result = EVENT_HANDLED;
     switch (State_Machine->Event) {
+        case EN_FAIL:
+            ESP_LOGW(TAG,"Error connecting to access point");
+            network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
+            result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
+            break;
         case EN_GOT_IP:
             result= local_traverse_state(State_Machine, &network_states[WIFI_CONNECTED_STATE],__FUNCTION__);
             break;
@@ -621,6 +633,7 @@ static state_machine_result_t NETWORK_WIFI_CONFIGURING_ACTIVE_STATE_entry_handle
     nm->wifi_ap_netif = network_wifi_config_ap();
     dns_server_start(nm->wifi_ap_netif);
     network_wifi_start_scan();
+    NETWORK_EXECUTE_CB(State_Machine);
     network_handler_entry_print(State_Machine,false);
     return EVENT_HANDLED;
 }
@@ -649,6 +662,9 @@ static state_machine_result_t NETWORK_WIFI_CONFIGURING_ACTIVE_STATE_handler(stat
         case EN_ETH_GOT_IP:
             network_interface_coexistence(State_Machine);
             break;
+        case EN_TIMER:
+            result= EVENT_HANDLED;
+            break;
         default:
             result =EVENT_UN_HANDLED;
     }
@@ -696,7 +712,9 @@ static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_entry_handler(state
     network_t* const nm = (network_t *)State_Machine;
     network_handler_entry_print(State_Machine,true);
     network_start_stop_dhcp_client(nm->wifi_netif, true);
-    network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password);
+    if(network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password) == ESP_ERR_INVALID_ARG){
+        network_async_fail();
+    }
     FREE_AND_NULL(nm->event_parameters->ssid);
     FREE_AND_NULL(nm->event_parameters->password);
     NETWORK_EXECUTE_CB(State_Machine);
@@ -718,13 +736,16 @@ static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_handler(state_machi
             network_status_update_ip_info(UPDATE_CONNECTION_OK); 
             result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_SUCCESS_STATE],__FUNCTION__);
             break;
+        case EN_FAIL:
+            ESP_LOGW(TAG,"Error connecting to access point");
+            result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_FAILED_STATE],__FUNCTION__);
+            break;
         case EN_LOST_CONNECTION:
             if(nm->event_parameters->disconnected_event->reason == WIFI_REASON_ASSOC_LEAVE) {
                 ESP_LOGI(TAG,"Wifi was disconnected from previous access point. Waiting to connect.");
             }
             else {
-                network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
-                result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
+                result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_FAILED_STATE],__FUNCTION__);
             }
             break;
         case EN_TIMER:
@@ -747,6 +768,43 @@ static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_exit_handler(state_
     return EVENT_HANDLED;
 }
 
+/********************************************************************************************* 
+ * WIFI_CONFIGURING_CONNECT_FAILED_STATE
+ */
+static state_machine_result_t WIFI_CONFIGURING_CONNECT_FAILED_STATE_entry_handler(state_machine_t* const State_Machine) {
+    network_handler_entry_print(State_Machine,true);
+    network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
+    ESP_LOGE(TAG, "Connecting Failed.");
+    NETWORK_EXECUTE_CB(State_Machine);
+    network_async_fail();
+    network_handler_entry_print(State_Machine,false);
+    return EVENT_HANDLED;
+}
+static state_machine_result_t WIFI_CONFIGURING_CONNECT_FAILED_STATE_handler(state_machine_t* const State_Machine) {
+    network_handler_print(State_Machine,true);
+    state_machine_result_t result = EVENT_HANDLED;
+    network_t* const nm = (network_t *)State_Machine;
+    switch (State_Machine->Event) {
+         case EN_FAIL:
+            result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
+            break;
+        default:
+            result= EVENT_HANDLED;
+    }
+    // Process global handler at the end, since we want to overwrite
+    // UPDATE_STATUS with our own logic above
+    HANDLE_GLOBAL_EVENT(State_Machine);
+    network_handler_print(State_Machine,false);
+    return result;
+}
+static state_machine_result_t WIFI_CONFIGURING_CONNECT_FAILED_STATE_exit_handler(state_machine_t* const State_Machine) {
+    network_exit_handler_print(State_Machine,true);
+    network_set_timer(0,NULL);
+    network_exit_handler_print(State_Machine,false);
+    return EVENT_HANDLED;
+}
+
+
 /********************************************************************************************* 
  * WIFI_CONFIGURING_CONNECT_SUCCESS_STATE
  */
@@ -827,6 +885,7 @@ static state_machine_result_t WIFI_CONNECTING_STATE_handler(state_machine_t* con
             }
             else if(nm->event_parameters->disconnected_event->reason != WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT) {
                 network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
+                network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
                 result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
             }
             break;
@@ -838,6 +897,7 @@ static state_machine_result_t WIFI_CONNECTING_STATE_handler(state_machine_t* con
 }
 static state_machine_result_t WIFI_CONNECTING_STATE_exit_handler(state_machine_t* const State_Machine) {
     network_exit_handler_print(State_Machine,true);
+    network_set_timer(0,NULL);
     network_exit_handler_print(State_Machine,false);
     return EVENT_HANDLED;
 }
@@ -849,7 +909,9 @@ static state_machine_result_t WIFI_CONNECTING_NEW_STATE_entry_handler(state_mach
     network_t* const nm = (network_t *)State_Machine;
     network_handler_entry_print(State_Machine,true);
     network_start_stop_dhcp_client(nm->wifi_netif, true);
-    network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password);
+    if(network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password) == ESP_ERR_INVALID_ARG){
+        network_async_fail();
+    }
     FREE_AND_NULL(nm->event_parameters->ssid);
     FREE_AND_NULL(nm->event_parameters->password);
     NETWORK_EXECUTE_CB(State_Machine);
@@ -859,8 +921,13 @@ static state_machine_result_t WIFI_CONNECTING_NEW_STATE_entry_handler(state_mach
 static state_machine_result_t WIFI_CONNECTING_NEW_STATE_handler(state_machine_t* const State_Machine) {
     HANDLE_GLOBAL_EVENT(State_Machine);
     network_handler_print(State_Machine,true);
+    network_t* const nm = (network_t *)State_Machine;
     state_machine_result_t result = EVENT_HANDLED;
     switch (State_Machine->Event) {
+        case EN_FAIL:
+            ESP_LOGW(TAG,"Error connecting to access point");
+            result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONNECTING_NEW_FAILED_STATE],__FUNCTION__);
+            break;
         case EN_GOT_IP:
             network_status_update_ip_info(UPDATE_CONNECTION_OK); 
             result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_CONNECTED_STATE],__FUNCTION__);
@@ -901,6 +968,7 @@ static state_machine_result_t WIFI_CONNECTING_NEW_STATE_exit_handler(state_machi
 static state_machine_result_t WIFI_CONNECTING_NEW_FAILED_STATE_entry_handler(state_machine_t* const State_Machine) {
     network_t* const nm = (network_t *)State_Machine;
     network_handler_entry_print(State_Machine,true);
+    network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
     if (nm->wifi_connected ) {
         // Wifi was already connected to an existing access point. Restore connection
         network_connect_active_ssid(State_Machine);      
@@ -1140,18 +1208,18 @@ static state_machine_result_t ETH_ACTIVE_CONNECTED_STATE_exit_handler(state_mach
 static state_machine_result_t local_switch_state(state_machine_t* state_machine,
                                                  const state_t* const target_state, const char * caller) {
     const state_t* source = state_machine->State;
-    NETWORK_PRINT_TRANSITION(true, "BEGIN SWITCH", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
+    NETWORK_PRINT_TRANSITION(true, "switch.begin", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
     state_machine_result_t result = switch_state(state_machine, target_state);
-    NETWORK_PRINT_TRANSITION( false,"BEGIN SWITCH", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
+    NETWORK_PRINT_TRANSITION( false,"switch.end", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
     ((network_t *)state_machine)->source_state = source;
     return result;
 }
 static state_machine_result_t local_traverse_state(state_machine_t* const state_machine,
                                                    const state_t* const target_state, const char * caller) {
     const state_t *  source = state_machine->State;
-    NETWORK_PRINT_TRANSITION( true,"BEGIN TRAVERSE", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true, caller);
+    NETWORK_PRINT_TRANSITION( true,"traverse.begin", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true, caller);
     state_machine_result_t result = traverse_state(state_machine, target_state);
-    NETWORK_PRINT_TRANSITION( false,"END TRAVERSE", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
+    NETWORK_PRINT_TRANSITION( false,"traverse.end", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
     ((network_t *)state_machine)->source_state = source;
     return result;
 }

+ 7 - 4
components/wifi-manager/network_services.h

@@ -36,20 +36,23 @@ extern "C" {
 #define ALL_WIFI_CONFIGURING_STATE(PARENT, LEVEL)\
     ADD_LEAF(WIFI_CONFIGURING_STATE,PARENT,LEVEL)\
     ADD_LEAF(WIFI_CONFIGURING_CONNECT_STATE,PARENT,LEVEL)\
+    ADD_LEAF(WIFI_CONFIGURING_CONNECT_FAILED_STATE,PARENT,LEVEL)\
     ADD_LEAF(WIFI_CONFIGURING_CONNECT_SUCCESS_STATE,PARENT,LEVEL)
 
+
+
 typedef enum {
     ALL_NM_STATE
     TOTAL_NM_STATE
 } nm_state_t;
-typedef enum {
-    ALL_WIFI_STATE(,)
-    TOTAL_WIFI_ACTIVE_STATE
-} mn_wifi_active_state_t;
 typedef enum {
     ALL_ETH_STATE(,)
     TOTAL_ETH_ACTIVE_STATE
 } mn_eth_active_state_t;
+typedef enum {
+    ALL_WIFI_STATE(,)
+    TOTAL_WIFI_ACTIVE_STATE
+} mn_wifi_active_state_t;
 typedef enum {
     ALL_WIFI_CONFIGURING_STATE(,)
     TOTAL_WIFI_CONFIGURING_STATE

+ 13 - 1
components/wifi-manager/network_status.c

@@ -14,6 +14,7 @@
 #include "platform_esp32.h"
 #include "tools.h"
 #include "trace.h"
+#include "messaging.h"
 #ifndef CONFIG_SQUEEZELITE_ESP32_RELEASE_URL
 #pragma message "Defaulting release url"
 #define CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
@@ -33,7 +34,11 @@ static uint16_t lms_server_cport = 0;
 static void (*chained_notify)(in_addr_t, u16_t, u16_t);
 static void connect_notify(in_addr_t ip, u16_t hport, u16_t cport);
 #define STA_IP_LEN sizeof(char) * IP4ADDR_STRLEN_MAX
-
+static update_reason_code_t last_reason_code = -1;
+void * get_http_server(int *port);
+update_reason_code_t get_last_reason_code(){
+    return last_reason_code;
+}
 void init_network_status() {
     chained_notify = server_notify;
     server_notify = connect_notify;
@@ -169,6 +174,11 @@ void network_status_safe_reset_sta_ip_string() {
 char* network_status_get_sta_ip_string() {
     return network_status_ip_address;
 }
+char * network_status_alloc_get_system_url(){
+    int port=0;
+    void * server = get_http_server(&port);
+    return messaging_alloc_format_string("http://%s:%d/",network_status_ip_address,port);
+}
 void set_lms_server_details(in_addr_t ip, u16_t hport, u16_t cport) {
     strncpy(lms_server_ip, inet_ntoa(ip), sizeof(lms_server_ip));
     lms_server_ip[sizeof(lms_server_ip) - 1] = '\0';
@@ -297,11 +307,13 @@ void network_status_update_address(cJSON* root, esp_netif_ip_info_t* ip_info) {
         ESP_LOGE(TAG, "Cannor update IP address. JSON structure or ip_info is null");
         return;
     }
+    network_status_safe_update_sta_ip_string(&ip_info->ip);
     network_update_cjson_string(&root, "ip", ip4addr_ntoa((ip4_addr_t*)&ip_info->ip));
     network_update_cjson_string(&root, "netmask", ip4addr_ntoa((ip4_addr_t*)&ip_info->netmask));
     network_update_cjson_string(&root, "gw", ip4addr_ntoa((ip4_addr_t*)&ip_info->gw));
 }
 void network_status_update_ip_info(update_reason_code_t update_reason_code) {
+    last_reason_code = update_reason_code;
     ESP_LOGV(TAG, "network_status_update_ip_info called");
     esp_netif_ip_info_t ip_info;
     if (network_status_lock_json_buffer(portMAX_DELAY)) {

+ 2 - 0
components/wifi-manager/network_status.h

@@ -54,6 +54,8 @@ cJSON* network_status_get_basic_info(cJSON** old);
 void network_status_update_basic_info();
 void network_status_clear_ip();
 void network_status_safe_reset_sta_ip_string();
+update_reason_code_t get_last_reason_code();
+char * network_status_alloc_get_system_url();
 #ifdef __cplusplus
 }
 #endif

+ 11 - 0
components/wifi-manager/network_wifi.c

@@ -739,6 +739,7 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i
             wifi_event_sta_connected_t* s = (wifi_event_sta_connected_t*)event_data;
             char* bssid = network_manager_alloc_get_mac_string(s->bssid);
             char* ssid = strdup_psram((char*)s->ssid);
+            network_wifi_set_connect_state(NETWORK_WIFI_STATE_CONNECTED);
             if (bssid && ssid) {
                 ESP_LOGD(TAG, "WIFI_EVENT_STA_CONNECTED. Channel: %d, Access point: %s, BSSID: %s ", s->channel, STR_OR_BLANK(ssid), (bssid));
             }
@@ -773,6 +774,7 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i
                 ESP_LOGI(TAG, "WiFi Roaming to new access point");
             } else {
                 network_async_lost_connection((wifi_event_sta_disconnected_t*)event_data);
+                network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
             }
         } break;
 
@@ -841,8 +843,10 @@ void network_wifi_generate_access_points_json(cJSON** ap_list) {
     known_access_point_t* it;
     if (*ap_list == NULL)
         return;
+    improv_wifi_list_allocate(ap_num);
     for (int i = 0; i < ap_num; i++) {
         network_wifi_add_access_point_json(*ap_list, &accessp_records[i]);
+        improv_wifi_list_add(ap_ssid_string(&accessp_records[i]),accessp_records[i].rssi, accessp_records[i].authmode!=WIFI_AUTH_OPEN);
     }
     SLIST_FOREACH(it, &s_ap_list, next) {
         if (!network_wifi_was_ssid_seen(it->ssid)) {
@@ -1126,10 +1130,12 @@ esp_err_t network_wifi_connect(const char* ssid, const char* password) {
     ESP_LOGD(TAG, "network_wifi_connect");
     if (!is_wifi_up()) {
         messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Wifi not started. Cannot connect");
+        network_wifi_set_connect_state(NETWORK_WIFI_STATE_DOWN);
         return ESP_FAIL;
     }
     if (!ssid || !password || strlen(ssid) == 0) {
         ESP_LOGE(TAG, "Cannot connect wifi. wifi config is null!");
+        network_wifi_set_connect_state(NETWORK_WIFI_STATE_INVALID_CONFIG);
         return ESP_ERR_INVALID_ARG;
     }
 
@@ -1159,12 +1165,17 @@ esp_err_t network_wifi_connect(const char* ssid, const char* password) {
 
     config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
     if ((err = esp_wifi_set_config(WIFI_IF_STA, &config)) != ESP_OK) {
+        network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
         ESP_LOGE(TAG, "Failed to set STA configuration. Error %s", esp_err_to_name(err));
     }
     if (err == ESP_OK) {
         ESP_LOGI(TAG, "Wifi Connecting to %s...", ssid);
         if ((err = esp_wifi_connect()) != ESP_OK) {
             ESP_LOGE(TAG, "Failed to initiate wifi connection. Error %s", esp_err_to_name(err));
+            network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
+        }
+        else{
+            network_wifi_set_connect_state(NETWORK_WIFI_STATE_CONNECTING);
         }
     }
     return err;

+ 1 - 0
components/wifi-manager/network_wifi.h

@@ -70,6 +70,7 @@ size_t network_wifi_get_known_count_in_range();
 esp_err_t network_wifi_built_known_ap_list();
 esp_err_t network_wifi_connect_next_in_range();
 const wifi_sta_config_t* network_wifi_load_active_config();
+
 #ifdef __cplusplus
 }
 #endif

+ 0 - 1
components/wifi-manager/wifi_manager_http_server.c

@@ -130,7 +130,6 @@ void register_regular_handlers(httpd_handle_t server){
 
 }
 
-
 esp_err_t http_server_start()
 {
 	

+ 1 - 1
main/CMakeLists.txt

@@ -1,5 +1,5 @@
 idf_component_register(SRC_DIRS . 
-					PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets
+					PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets driver_bt
                     	EMBED_FILES ../server_certs/github.pem
 					LDFRAGMENTS "linker.lf"
                     	)

BIN=BIN
otadata.bin


+ 1 - 0
squeezelite.cmake

@@ -143,6 +143,7 @@ add_custom_command(
 			COMMAND xtensa-esp32-elf-objcopy  --globalize-symbol find_command_by_name ${build_dir}/esp-idf/console/libconsole.a
 	        VERBATIM
 )
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/otadata.bin ${build_dir}/ota_data_initial.bin COPYONLY)
 add_custom_command(
 			TARGET squeezelite.elf
 			PRE_LINK