From 63fbc2f64573fdf5a69fa5d2254d0a095bba3691 Mon Sep 17 00:00:00 2001 From: Sebastien L Date: Fri, 10 Dec 2021 13:07:27 -0500 Subject: [PATCH] Network manager implemented and relatively stable --- CMakeLists.txt | 26 +- components/cpp-stateless | 1 - components/driver_bt/bt_app_core.c | 4 +- components/driver_bt/bt_app_source.c | 7 +- components/platform_config/CMakeLists.txt | 2 +- components/platform_config/nvs_utilities.c | 192 ++- components/platform_config/nvs_utilities.h | 5 + components/platform_config/platform_config.c | 63 +- components/platform_console/CMakeLists.txt | 2 +- .../app_squeezelite/CMakeLists.txt | 2 +- .../app_squeezelite/cmd_squeezelite.c | 5 +- components/platform_console/cmd_config.c | 410 ++++- components/platform_console/cmd_i2ctools.c | 4 +- components/platform_console/cmd_nvs.c | 97 +- components/platform_console/cmd_system.c | 63 +- components/platform_console/cmd_wifi.c | 12 +- .../platform_console/platform_console.c | 22 +- components/raop/raop_sink.c | 2 + components/services/CMakeLists.txt | 2 +- components/services/accessors.c | 71 +- components/services/accessors.h | 5 +- components/services/globdefs.h | 4 +- components/services/messaging.c | 35 +- components/services/messaging.h | 1 + components/services/monitor.c | 14 +- components/services/services.c | 40 + components/squeezelite-ota/squeezelite-ota.c | 21 +- components/telnet/telnet.c | 4 +- components/tools/CMakeLists.txt | 3 +- components/tools/trace.c | 120 ++ components/tools/trace.h | 11 + components/wifi-manager/CMakeLists.txt | 6 +- components/wifi-manager/README.md | 41 - .../wifi-manager/UML-State-Machine-in-C | 1 + components/wifi-manager/_esp_httpd_main.c | 11 +- components/wifi-manager/dns_server.c | 19 +- components/wifi-manager/dns_server.h | 3 +- .../wifi-manager/http_server_handlers.c | 114 +- .../wifi-manager/http_server_handlers.h | 31 +- .../wifi-manager/network_driver_DM9051.c | 44 +- .../wifi-manager/network_driver_LAN8720.c | 49 +- .../wifi-manager/network_driver_W5500.c | 62 +- components/wifi-manager/network_ethernet.c | 153 +- components/wifi-manager/network_ethernet.h | 15 +- components/wifi-manager/network_manager.c | 740 +++++++- components/wifi-manager/network_manager.h | 416 +++-- .../wifi-manager/network_manager_handlers.c | 1155 +++++++++++++ components/wifi-manager/network_status.c | 395 +++-- components/wifi-manager/network_status.h | 24 +- components/wifi-manager/network_wifi.c | 1483 ++++++++++------- components/wifi-manager/network_wifi.h | 43 +- components/wifi-manager/settings.png | Bin 901 -> 0 bytes components/wifi-manager/state_machine.cpp | 793 --------- components/wifi-manager/state_machine.h | 108 -- components/wifi-manager/webapp/mock/ap.json | 22 +- .../wifi-manager/webapp/mock/messages.json | 2 +- .../wifi-manager/webapp/src/js/custom.js | 7 +- components/wifi-manager/wifi0.png | Bin 605 -> 0 bytes components/wifi-manager/wifi1.png | Bin 613 -> 0 bytes components/wifi-manager/wifi2.png | Bin 615 -> 0 bytes components/wifi-manager/wifi24.png | Bin 605 -> 0 bytes components/wifi-manager/wifi3.png | Bin 656 -> 0 bytes .../wifi-manager/wifi_manager_http_server.c | 21 +- main/Kconfig.projbuild | 44 +- main/esp_app_main.c | 136 +- sdkconfig | 19 +- 66 files changed, 4528 insertions(+), 2679 deletions(-) delete mode 160000 components/cpp-stateless create mode 100644 components/tools/trace.c delete mode 100644 components/wifi-manager/README.md create mode 160000 components/wifi-manager/UML-State-Machine-in-C create mode 100644 components/wifi-manager/network_manager_handlers.c delete mode 100644 components/wifi-manager/settings.png delete mode 100644 components/wifi-manager/state_machine.cpp delete mode 100644 components/wifi-manager/state_machine.h delete mode 100644 components/wifi-manager/wifi0.png delete mode 100644 components/wifi-manager/wifi1.png delete mode 100644 components/wifi-manager/wifi2.png delete mode 100644 components/wifi-manager/wifi24.png delete mode 100644 components/wifi-manager/wifi3.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c0046d3..517d109d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,17 @@ cmake_minimum_required(VERSION 3.5) set(EXTRA_COMPONENT_DIRS components/platform_console/app_recovery components/platform_console/app_squeezelite ) include($ENV{IDF_PATH}/tools/cmake/project.cmake) add_definitions(-DMODEL_NAME=SqueezeESP32) +# Uncomment line below to get memory usage trace details +#add_definitions(-DENABLE_MEMTRACE=1) +#uncomment line below to get network ethernet debug logs +#add_definitions(-DNETWORK_ETHERNET_LOG_LEVEL=ESP_LOG_DEBUG) +#uncomment line below to get network status debug logs +#add_definitions(-DNETWORK_STATUS_LOG_LEVEL=ESP_LOG_DEBUG) +#add_definitions(-DNETWORK_HANDLERS_LOG_LEVEL=ESP_LOG_DEBUG) +#add_definitions(-DNETWORK_WIFI_LOG_LEVEL=ESP_LOG_DEBUG) +#add_definitions(-DNETWORK_MANAGER_LOG_LEVEL=ESP_LOG_DEBUG) + + if(NOT DEFINED DEPTH) set(DEPTH "16") endif() @@ -11,10 +22,16 @@ set_property(TARGET recovery.elf PROPERTY RECOVERY_PREFIX app_recovery ) include(squeezelite.cmake) set(PROJECT_VER $ENV{PROJECT_VER}) -#target_compile_definitions(__idf_wifi-manager PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO) -target_compile_definitions(__idf_esp_wifi PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO) -target_compile_definitions(__idf_app_recovery PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO) + +#target_compile_definitions(__idf_services PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) +#target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) +#target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) +#target_compile_definitions(__idf_wifi-manager PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO) +#target_compile_definitions(__idf_esp_wifi PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) +#target_compile_definitions(__idf_platform_console PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) + +#target_compile_definitions(__idf_app_recovery PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO) # target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO) # target_compile_definitions(__idf_esp_event PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO) # target_compile_definitions(__idf_esp_netif PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) @@ -84,13 +101,12 @@ target_compile_definitions(__idf_app_recovery PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_ # target_compile_definitions(__idf_openssl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_perfmon PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_platform_config PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) -# target_compile_definitions(__idf_platform_console PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_protobuf-c PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_protocomm PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_pthread PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_raop PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_sdmmc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) -# target_compile_definitions(__idf_services PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) + # target_compile_definitions(__idf_soc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_spiffs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) # target_compile_definitions(__idf_spi_flash PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG) diff --git a/components/cpp-stateless b/components/cpp-stateless deleted file mode 160000 index 9e7138f8..00000000 --- a/components/cpp-stateless +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9e7138f8fe164d64deb73b2b23994eeb9abe3e0b diff --git a/components/driver_bt/bt_app_core.c b/components/driver_bt/bt_app_core.c index df77604f..e35a8337 100644 --- a/components/driver_bt/bt_app_core.c +++ b/components/driver_bt/bt_app_core.c @@ -17,6 +17,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" +#include "globdefs.h" static const char * TAG = "btappcore"; @@ -41,8 +42,7 @@ bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, i if (param_len == 0) { return bt_app_send_msg(&msg); } else if (p_params && param_len > 0) { - if ((msg.param = malloc(param_len)) != NULL) { - memcpy(msg.param, p_params, param_len); + if ((msg.param = clone_obj_psram(p_params, param_len)) != NULL) { /* check if caller has provided a copy callback to do the deep copy */ if (p_copy_cback) { p_copy_cback(&msg, msg.param, p_params); diff --git a/components/driver_bt/bt_app_source.c b/components/driver_bt/bt_app_source.c index 302ea39d..246085fd 100644 --- a/components/driver_bt/bt_app_source.c +++ b/components/driver_bt/bt_app_source.c @@ -20,6 +20,7 @@ #include "trace.h" #include "messaging.h" #include "cJSON.h" +#include "globdefs.h" static const char * TAG = "bt_app_source"; static const char * BT_RC_CT_TAG="RCCT"; @@ -226,8 +227,8 @@ void hal_bluetooth_init(const char * options) squeezelite_args.end = arg_end(2); ESP_LOGD(TAG,"Copying parameters"); - char * opts = strdup(options); - char **argv = malloc(sizeof(char**)*15); + char * opts = strdup_psram(options); + char **argv = malloc_init_external(sizeof(char**)*15); size_t argv_size=15; @@ -254,7 +255,7 @@ void hal_bluetooth_init(const char * options) ESP_LOGW(TAG,"Unable to retrieve the a2dp sink name from nvs."); } } else { - squeezelite_conf.sink_name=strdup(squeezelite_args.sink_name->sval[0]); + squeezelite_conf.sink_name=strdup_psram(squeezelite_args.sink_name->sval[0]); // sync with NVS esp_err_t err=ESP_OK; if((err= config_set_value(NVS_TYPE_STR, "a2dp_sink_name", squeezelite_args.sink_name->sval[0]))!=ESP_OK){ diff --git a/components/platform_config/CMakeLists.txt b/components/platform_config/CMakeLists.txt index 117dbbff..4090bbc2 100644 --- a/components/platform_config/CMakeLists.txt +++ b/components/platform_config/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register( SRC_DIRS . INCLUDE_DIRS . - PRIV_REQUIRES tools newlib console esp_common freertos + PRIV_REQUIRES tools newlib console esp_common freertos services REQUIRES nvs_flash json ) diff --git a/components/platform_config/nvs_utilities.c b/components/platform_config/nvs_utilities.c index 90152069..e3ce3c64 100644 --- a/components/platform_config/nvs_utilities.c +++ b/components/platform_config/nvs_utilities.c @@ -1,4 +1,3 @@ -//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #include "nvs_utilities.h" #include @@ -15,6 +14,7 @@ #include "nvs_flash.h" #include "nvs_utilities.h" #include "platform_config.h" +#include "globdefs.h" const char current_namespace[] = "config"; const char settings_partition[] = "settings"; @@ -69,6 +69,11 @@ const char *type_to_str(nvs_type_t type) return "Unknown"; } +void erase_settings_partition(){ + ESP_LOGW(TAG, "Erasing nvs on partition %s",settings_partition); + ESP_ERROR_CHECK(nvs_flash_erase_partition(settings_partition)); + nvs_flash_init_partition(settings_partition); +} void initialize_nvs() { ESP_LOGI(TAG, "Initializing flash nvs "); esp_err_t err = nvs_flash_init(); @@ -95,62 +100,89 @@ void initialize_nvs() { ESP_LOGD(TAG, "nvs init completed"); } -esp_err_t nvs_load_config(){ - nvs_entry_info_t info; - esp_err_t err = ESP_OK; - size_t malloc_int = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); - size_t malloc_spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); +esp_err_t nvs_load_config() { + nvs_entry_info_t info; + esp_err_t err = ESP_OK; + size_t malloc_int = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); + size_t malloc_spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); - nvs_iterator_t it = nvs_entry_find(settings_partition, NULL, NVS_TYPE_ANY); - if(it == NULL) { - ESP_LOGW(TAG, "empty nvs partition %s, namespace %s",settings_partition,current_namespace ); - } - while (it != NULL) { - nvs_entry_info(it, &info); + nvs_iterator_t it = nvs_entry_find(settings_partition, NULL, NVS_TYPE_ANY); + if (it == NULL) { + ESP_LOGW(TAG, "empty nvs partition %s, namespace %s", settings_partition, current_namespace); + } + while (it != NULL) { + nvs_entry_info(it, &info); - if(strstr(info.namespace_name, current_namespace)) { - void * value = get_nvs_value_alloc(info.type,info.key); - if(value==NULL) - { - ESP_LOGE(TAG, "nvs read failed."); - return ESP_FAIL; + if (strstr(info.namespace_name, current_namespace)) { + if (strlen(info.key) == 0) { + ESP_LOGW(TAG, "empty key name in namespace %s. Removing it.", current_namespace); + nvs_handle_t nvs_handle; + err = nvs_open(settings_partition, NVS_READWRITE, &nvs_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "nvs_open failed. %s", esp_err_to_name(err)); + } else { + if ((err = nvs_erase_key(nvs_handle, info.key)) != ESP_OK) { + ESP_LOGE(TAG, "nvs_erase_key failed. %s", esp_err_to_name(err)); + } else { + nvs_commit(nvs_handle); + } + nvs_close(nvs_handle); + if (err == ESP_OK) { + ESP_LOGW(TAG, "nvs_erase_key completed on empty key. Restarting system to apply changes."); + esp_restart(); + } + } + if (err != ESP_OK) { + ESP_LOGW(TAG, "nvs_erase_key failed on empty key. Configuration partition should be erased. %s", esp_err_to_name(err)); + err = ESP_OK; + } + } + else { + void* value = get_nvs_value_alloc(info.type, info.key); + if (value == NULL) { + ESP_LOGE(TAG, "nvs read failed."); + return ESP_FAIL; + } + config_set_value(info.type, info.key, value); + free(value); } - config_set_value(info.type, info.key, value); - free(value ); - } - it = nvs_entry_next(it); - } - char * json_string= config_alloc_get_json(false); - if(json_string!=NULL) { - ESP_LOGD(TAG, "config json : %s\n", json_string); - free(json_string); - } - ESP_LOGD(TAG,"Config memory usage. Heap internal:%zu (min:%zu) (used:%zu) external:%zu (min:%zu) (used:%zd)", - heap_caps_get_free_size(MALLOC_CAP_INTERNAL), - heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), - malloc_int-heap_caps_get_free_size(MALLOC_CAP_INTERNAL), - heap_caps_get_free_size(MALLOC_CAP_SPIRAM), - heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), - malloc_spiram -heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); - return err; -} + } + it = nvs_entry_next(it); + } + char* json_string = config_alloc_get_json(false); + if (json_string != NULL) { + ESP_LOGD(TAG, "config json : %s\n", json_string); + free(json_string); + } + ESP_LOGW(TAG, "Configuration memory usage. Heap internal:%zu (min:%zu) (used:%zu) external:%zu (min:%zu) (used:%zd)", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), + malloc_int - heap_caps_get_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + malloc_spiram - heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); + return err; +} esp_err_t store_nvs_value(nvs_type_t type, const char *key, void * data) { if (type == NVS_TYPE_BLOB) return ESP_ERR_NVS_TYPE_MISMATCH; return store_nvs_value_len(type, key, data,0); } -esp_err_t store_nvs_value_len(nvs_type_t type, const char *key, void * data, - size_t data_len) { +esp_err_t store_nvs_value_len_for_partition(const char * partition,const char * namespace,nvs_type_t type, const char *key, const void * data,size_t data_len) { esp_err_t err; nvs_handle nvs; - + if(!key || key[0]=='\0'){ + ESP_LOGE(TAG, "Cannot store value to nvs: key is empty"); + return ESP_ERR_INVALID_ARG; + } + if (type == NVS_TYPE_ANY) { return ESP_ERR_NVS_TYPE_MISMATCH; } - err = nvs_open_from_partition(settings_partition, current_namespace, NVS_READWRITE, &nvs); + err = nvs_open_from_partition(partition, namespace, NVS_READWRITE, &nvs); if (err != ESP_OK) { return err; } @@ -185,53 +217,65 @@ esp_err_t store_nvs_value_len(nvs_type_t type, const char *key, void * data, nvs_close(nvs); return err; } -void * get_nvs_value_alloc(nvs_type_t type, const char *key) { +esp_err_t store_nvs_value_len(nvs_type_t type, const char *key, void * data, + size_t data_len) { + return store_nvs_value_len_for_partition(settings_partition,current_namespace,type,key,data,data_len); +} +void * get_nvs_value_alloc_for_partition(const char * partition,const char * namespace,nvs_type_t type, const char *key, size_t * size){ nvs_handle nvs; esp_err_t err; void * value=NULL; - - err = nvs_open_from_partition(settings_partition, current_namespace, NVS_READONLY, &nvs); + if(size){ + *size=0; + } + err = nvs_open_from_partition(partition, namespace, NVS_READONLY, &nvs); if (err != ESP_OK) { ESP_LOGE(TAG, "Could not open the nvs storage."); return NULL; } if (type == NVS_TYPE_I8) { - value=malloc(sizeof(int8_t)); + value=malloc_init_external(sizeof(int8_t)); err = nvs_get_i8(nvs, key, (int8_t *) value); } else if (type == NVS_TYPE_U8) { - value=malloc(sizeof(uint8_t)); + value=malloc_init_external(sizeof(uint8_t)); err = nvs_get_u8(nvs, key, (uint8_t *) value); } else if (type == NVS_TYPE_I16) { - value=malloc(sizeof(int16_t)); + value=malloc_init_external(sizeof(int16_t)); err = nvs_get_i16(nvs, key, (int16_t *) value); } else if (type == NVS_TYPE_U16) { - value=malloc(sizeof(uint16_t)); + value=malloc_init_external(sizeof(uint16_t)); err = nvs_get_u16(nvs, key, (uint16_t *) value); } else if (type == NVS_TYPE_I32) { - value=malloc(sizeof(int32_t)); + value=malloc_init_external(sizeof(int32_t)); err = nvs_get_i32(nvs, key, (int32_t *) value); } else if (type == NVS_TYPE_U32) { - value=malloc(sizeof(uint32_t)); + value=malloc_init_external(sizeof(uint32_t)); err = nvs_get_u32(nvs, key, (uint32_t *) value); } else if (type == NVS_TYPE_I64) { - value=malloc(sizeof(int64_t)); + value=malloc_init_external(sizeof(int64_t)); err = nvs_get_i64(nvs, key, (int64_t *) value); } else if (type == NVS_TYPE_U64) { - value=malloc(sizeof(uint64_t)); + value=malloc_init_external(sizeof(uint64_t)); err = nvs_get_u64(nvs, key, (uint64_t *) value); } else if (type == NVS_TYPE_STR) { size_t len=0; err = nvs_get_str(nvs, key, NULL, &len); if (err == ESP_OK) { - value=malloc(len); + value=malloc_init_external(len+1); err = nvs_get_str(nvs, key, value, &len); - } + if(size){ + *size=len; + } + } } else if (type == NVS_TYPE_BLOB) { size_t len; err = nvs_get_blob(nvs, key, NULL, &len); if (err == ESP_OK) { - value=malloc(len+1); + value=malloc_init_external(len+1); + if(size){ + *size=len; + } err = nvs_get_blob(nvs, key, value, &len); } } @@ -244,6 +288,9 @@ void * get_nvs_value_alloc(nvs_type_t type, const char *key) { nvs_close(nvs); return value; } +void * get_nvs_value_alloc(nvs_type_t type, const char *key) { + return get_nvs_value_alloc_for_partition(settings_partition, current_namespace,type,key,NULL); +} esp_err_t get_nvs_value(nvs_type_t type, const char *key, void*value, const uint8_t buf_size) { nvs_handle nvs; esp_err_t err; @@ -296,11 +343,10 @@ esp_err_t get_nvs_value(nvs_type_t type, const char *key, void*value, const uint nvs_close(nvs); return err; } -esp_err_t erase_nvs(const char *key) +esp_err_t erase_nvs_for_partition(const char * partition, const char * namespace,const char *key) { nvs_handle nvs; - - esp_err_t err = nvs_open(current_namespace, NVS_READWRITE, &nvs); + esp_err_t err = nvs_open_from_partition(partition,namespace, NVS_READWRITE, &nvs); if (err == ESP_OK) { err = nvs_erase_key(nvs, key); if (err == ESP_OK) { @@ -311,7 +357,35 @@ esp_err_t erase_nvs(const char *key) } nvs_close(nvs); } - + else { + ESP_LOGE(TAG,"Could not erase key %s from partition %s namespace %s : %s", key,partition,namespace, esp_err_to_name(err)); + } return err; } +esp_err_t erase_nvs(const char *key) +{ + return erase_nvs_for_partition(NVS_DEFAULT_PART_NAME, current_namespace,key); +} +esp_err_t erase_nvs_partition(const char * partition, const char * namespace){ + nvs_handle nvs; + const char * step = "Opening"; + ESP_LOGD(TAG,"%s partition %s, namespace %s ",step,partition,namespace); + esp_err_t err = nvs_open_from_partition(partition,namespace, NVS_READWRITE, &nvs); + if (err == ESP_OK) { + step = "Erasing"; + ESP_LOGD(TAG,"%s namespace %s ",step,partition); + err = nvs_erase_all(nvs); + if (err == ESP_OK) { + step = "Committing"; + ESP_LOGD(TAG,"%s",step); + err = nvs_commit(nvs); + } + } + if(err !=ESP_OK){ + ESP_LOGE(TAG,"%s partition %s, name space %s : %s",step,partition,namespace,esp_err_to_name(err)); + } + ESP_LOGD(TAG,"Closing %s ",namespace); + nvs_close(nvs); + return err; +} \ No newline at end of file diff --git a/components/platform_config/nvs_utilities.h b/components/platform_config/nvs_utilities.h index 9877b8d5..e789fd46 100644 --- a/components/platform_config/nvs_utilities.h +++ b/components/platform_config/nvs_utilities.h @@ -13,10 +13,15 @@ esp_err_t store_nvs_value_len(nvs_type_t type, const char *key, void * data, siz esp_err_t store_nvs_value(nvs_type_t type, const char *key, void * data); esp_err_t get_nvs_value(nvs_type_t type, const char *key, void*value, const uint8_t buf_size); void * get_nvs_value_alloc(nvs_type_t type, const char *key); +void * get_nvs_value_alloc_for_partition(const char * partition,const char * namespace,nvs_type_t type, const char *key, size_t * size); +esp_err_t erase_nvs_for_partition(const char * partition, const char * namespace,const char *key); +esp_err_t store_nvs_value_len_for_partition(const char * partition,const char * namespace,nvs_type_t type, const char *key, const void * data,size_t data_len); esp_err_t erase_nvs(const char *key); void print_blob(const char *blob, size_t len); const char *type_to_str(nvs_type_t type); nvs_type_t str_to_type(const char *type); +esp_err_t erase_nvs_partition(const char * partition, const char * namespace); +void erase_settings_partition(); #ifdef __cplusplus } #endif diff --git a/components/platform_config/platform_config.c b/components/platform_config/platform_config.c index 10bcc121..b5dc863d 100644 --- a/components/platform_config/platform_config.c +++ b/components/platform_config/platform_config.c @@ -18,7 +18,6 @@ * along with this program. If not, see . * */ -//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #include "platform_config.h" #include "nvs_utilities.h" #include "platform_esp32.h" @@ -39,18 +38,18 @@ #include "cJSON.h" #include "freertos/timers.h" #include "freertos/event_groups.h" - +#include "globdefs.h" #define CONFIG_COMMIT_DELAY 1000 #define LOCK_MAX_WAIT 20*CONFIG_COMMIT_DELAY static const char * TAG = "config"; -static cJSON * nvs_json=NULL; -static TimerHandle_t timer; -static SemaphoreHandle_t config_mutex = NULL; -static EventGroupHandle_t config_group; +EXT_RAM_ATTR static cJSON * nvs_json=NULL; +EXT_RAM_ATTR static TimerHandle_t timer; +EXT_RAM_ATTR static SemaphoreHandle_t config_mutex = NULL; +EXT_RAM_ATTR static EventGroupHandle_t config_group; /* @brief indicate that the ESP32 is currently connected. */ -static const int CONFIG_NO_COMMIT_PENDING = BIT0; -static const int CONFIG_LOAD_BIT = BIT1; +EXT_RAM_ATTR static const int CONFIG_NO_COMMIT_PENDING = BIT0; +EXT_RAM_ATTR static const int CONFIG_LOAD_BIT = BIT1; bool config_lock(TickType_t xTicksToWait); void config_unlock(); @@ -62,7 +61,7 @@ cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key,const void * static void vCallbackFunction( TimerHandle_t xTimer ); void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag); #define IMPLEMENT_SET_DEFAULT(t,nt) void config_set_default_## t (const char *key, t value){\ - void * pval = malloc(sizeof(value));\ + void * pval = malloc_init_external(sizeof(value));\ *((t *) pval) = value;\ config_set_default(nt, key,pval,0);\ free(pval); } @@ -72,7 +71,7 @@ void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag); return ESP_FAIL;} static void * malloc_fn(size_t sz){ - void * ptr = is_recovery_running?malloc(sz):heap_caps_malloc(sz, MALLOC_CAP_SPIRAM); + void * ptr = is_recovery_running?malloc(sz):heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if(ptr==NULL){ ESP_LOGE(TAG,"malloc_fn: unable to allocate memory!"); } @@ -85,20 +84,28 @@ void init_cJSON(){ } void config_init(){ ESP_LOGD(TAG, "Creating mutex for Config"); + MEMTRACE_PRINT_DELTA(); config_mutex = xSemaphoreCreateMutex(); + MEMTRACE_PRINT_DELTA(); ESP_LOGD(TAG, "Creating event group"); + MEMTRACE_PRINT_DELTA(); config_group = xEventGroupCreate(); + MEMTRACE_PRINT_DELTA(); ESP_LOGD(TAG, "Loading config from nvs"); init_cJSON(); + MEMTRACE_PRINT_DELTA(); if(nvs_json !=NULL){ cJSON_Delete(nvs_json); } nvs_json = cJSON_CreateObject(); config_set_group_bit(CONFIG_LOAD_BIT,true); + MEMTRACE_PRINT_DELTA(); nvs_load_config(); + MEMTRACE_PRINT_DELTA(); config_set_group_bit(CONFIG_LOAD_BIT,false); + MEMTRACE_PRINT_DELTA(); config_start_timer(); } @@ -318,28 +325,28 @@ void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry){ return NULL; } if (nvs_type == NVS_TYPE_I8) { - value=malloc(sizeof(int8_t)); + value=malloc_init_external(sizeof(int8_t)); *(int8_t *)value = (int8_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_U8) { - value=malloc(sizeof(uint8_t)); + value=malloc_init_external(sizeof(uint8_t)); *(uint8_t *)value = (uint8_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_I16) { - value=malloc(sizeof(int16_t)); + value=malloc_init_external(sizeof(int16_t)); *(int16_t *)value = (int16_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_U16) { - value=malloc(sizeof(uint16_t)); + value=malloc_init_external(sizeof(uint16_t)); *(uint16_t *)value = (uint16_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_I32) { - value=malloc(sizeof(int32_t)); + value=malloc_init_external(sizeof(int32_t)); *(int32_t *)value = (int32_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_U32) { - value=malloc(sizeof(uint32_t)); + value=malloc_init_external(sizeof(uint32_t)); *(uint32_t *)value = (uint32_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_I64) { - value=malloc(sizeof(int64_t)); + value=malloc_init_external(sizeof(int64_t)); *(int64_t *)value = (int64_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_U64) { - value=malloc(sizeof(uint64_t)); + value=malloc_init_external(sizeof(uint64_t)); *(uint64_t *)value = (uint64_t)entry_value->valuedouble; } else if (nvs_type == NVS_TYPE_STR) { if(!cJSON_IsString(entry_value)){ @@ -361,8 +368,7 @@ void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry){ } else { size_t len=strlen(cJSON_GetStringValue(entry_value)); - value=(void *)heap_caps_malloc(len+1, MALLOC_CAP_DMA); - memset(value,0x00,len+1); + value=(void *)malloc_init_external(len+1); memcpy(value,cJSON_GetStringValue(entry_value),len); if(value==NULL){ char * entry_str = cJSON_PrintUnformatted(entry); @@ -406,12 +412,11 @@ void config_commit_to_nvs(){ void * value = config_safe_alloc_get_entry_value(type, entry); if(value!=NULL){ size_t len=strlen(entry->string); - char * key=(void *)heap_caps_malloc(len+1, MALLOC_CAP_DMA); - memset(key,0x00,len+1); + char * key=(void *)malloc_init_external(len+1); memcpy(key,entry->string,len); esp_err_t err = store_nvs_value(type,key,value); - free(key); - free(value); + FREE_AND_NULL(key); + FREE_AND_NULL(value); if(err!=ESP_OK){ char * entry_str = cJSON_PrintUnformatted(entry); @@ -617,11 +622,11 @@ void * config_alloc_get(nvs_type_t nvs_type, const char *key) { } void * config_alloc_get_str(const char *key, char *lead, char *fallback) { - if (lead && *lead) return strdup(lead); + if (lead && *lead) return strdup_psram(lead); char *value = config_alloc_get_default(NVS_TYPE_STR, key, NULL, 0); if ((!value || !*value) && fallback) { if (value) free(value); - value = strdup(fallback); + value = strdup_psram(fallback); } return value; } @@ -673,7 +678,7 @@ char * config_alloc_get_json(bool bFormatted){ char * json_buffer = NULL; if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){ ESP_LOGE(TAG, "Unable to lock config after %d ms",LOCK_MAX_WAIT); - return strdup("{\"error\":\"Unable to lock configuration object.\"}"); + return strdup_psram("{\"error\":\"Unable to lock configuration object.\"}"); } if(bFormatted){ json_buffer= cJSON_Print(nvs_json); @@ -686,6 +691,10 @@ char * config_alloc_get_json(bool bFormatted){ } esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, const void * value){ esp_err_t result = ESP_OK; + if(!key ||!key[0]){ + ESP_LOGW(TAG,"Empty key passed. Ignoring entry!"); + return ESP_ERR_INVALID_ARG; + } if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){ ESP_LOGE(TAG, "Unable to lock config after %d ms",LOCK_MAX_WAIT); result = ESP_FAIL; diff --git a/components/platform_console/CMakeLists.txt b/components/platform_console/CMakeLists.txt index 6d6e2cf8..b46cdced 100644 --- a/components/platform_console/CMakeLists.txt +++ b/components/platform_console/CMakeLists.txt @@ -8,6 +8,6 @@ idf_component_register( SRCS cmd_config.c INCLUDE_DIRS . REQUIRES nvs_flash - PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite) + PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite services) target_link_libraries(${COMPONENT_LIB} "-Wl,--undefined=GDS_DrawPixelFast") target_link_libraries(${COMPONENT_LIB} ${build_dir}/esp-idf/$/lib$.a ) diff --git a/components/platform_console/app_squeezelite/CMakeLists.txt b/components/platform_console/app_squeezelite/CMakeLists.txt index 8f72e589..10899949 100644 --- a/components/platform_console/app_squeezelite/CMakeLists.txt +++ b/components/platform_console/app_squeezelite/CMakeLists.txt @@ -1,7 +1,7 @@ idf_build_get_property(idf_path IDF_PATH) idf_component_register( SRCS cmd_squeezelite.c INCLUDE_DIRS . - PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display ) + PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display services) target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=feof") diff --git a/components/platform_console/app_squeezelite/cmd_squeezelite.c b/components/platform_console/app_squeezelite/cmd_squeezelite.c index 5d1cdfba..036ec691 100644 --- a/components/platform_console/app_squeezelite/cmd_squeezelite.c +++ b/components/platform_console/app_squeezelite/cmd_squeezelite.c @@ -12,6 +12,8 @@ #include "platform_esp32.h" #include "platform_config.h" #include "esp_app_format.h" +#include "globdefs.h" + extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length); static const char * TAG = "squeezelite_cmd"; #define SQUEEZELITE_THREAD_STACK_SIZE (4*1024) @@ -112,8 +114,7 @@ static int launchsqueezelite(int argc, char **argv) ESP_LOGV(TAG,"Saving args in thread structure"); thread_parms.argc=0; - thread_parms.argv = malloc(sizeof(char**)*(argc+ADDITIONAL_SQUEEZELITE_ARGS)); - memset(thread_parms.argv,'\0',sizeof(char**)*(argc+ADDITIONAL_SQUEEZELITE_ARGS)); + thread_parms.argv = malloc_init_external(sizeof(char**)*(argc+ADDITIONAL_SQUEEZELITE_ARGS)); for(int i=0;i #include "cmd_config.h" #include "argtable3/argtable3.h" @@ -18,14 +17,19 @@ #include "trace.h" #include "messaging.h" #include "accessors.h" +#include "adac.h" +#include "globdefs.h" +#include "cJSON.h" const char * desc_squeezelite ="Squeezelite Options"; const char * desc_dac= "DAC Options"; +const char * desc_preset= "Preset Options"; const char * desc_spdif= "SPDIF Options"; const char * desc_audio= "General Audio Options"; const char * desc_bt_source= "Bluetooth Audio Output Options"; const char * desc_rotary= "Rotary Control"; +extern const struct adac_s *dac_set[]; #define CODECS_BASE "flac|pcm|mp3|ogg" #if NO_FAAD @@ -44,6 +48,11 @@ const char * desc_rotary= "Rotary Control"; #define CODECS_DSD "" #endif #define CODECS_MP3 "|mad|mpg" +#ifdef CONFIG_SQUEEZEAMP +static const char * known_configs = ""; +#else +static const char * known_configs_string = "[{\"name\":\"ESP32A1S Old Model config 1 (AC101)\",\"config\":[{\"dac_config\":\"AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32\"},{\"dac_controlset\":\"\"},{\"set_GPIO\":\"\"},{\"spdif_config\":\"21=amp,22=green:0,39=jack:0\"}]},{\"name\":\"ESP32A1S Old Model config 2 (AC101)\",\"config\":[{\"dac_config\":\"AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32\"},{\"dac_controlset\":\"\"},{\"set_GPIO\":\"\"},{\"spdif_config\":\"21=amp,22=green:0,5=jack:0\"}]},{\"name\":\"ESP32A1S V2.2+ variant 1 (ES8388)\",\"config\":[{\"dac_config\":\"model=ES8388,bck=27,ws=25,do=26,sda=33,scl=32,di=35,i2c=16\"},{\"dac_controlset\":\"\"},{\"set_GPIO\":\"21=amp,22=green:0,39=jack:0\"},{\"spdif_config\":\"\"}]},{\"name\":\"ESP32A1S V2.2+ variant 2 (ES8388)\",\"config\":[{\"dac_config\":\"model=ES8388,bck=5,ws=25,do=26,sda=18,scl=23,i2c=16\"},{\"dac_controlset\":\"\"},{\"set_GPIO\":\"21=amp,22=green:0,39=jack:0\"},{\"spdif_config\":\"\"}]}]"; +#endif #if !defined(MODEL_NAME) @@ -58,6 +67,7 @@ const char * desc_rotary= "Rotary Control"; #define MODEL_NAME_STRING STR(MODEL_NAME) #endif + #define CODECS CODECS_BASE CODECS_AAC CODECS_FF CODECS_DSD CODECS_MP3 #define NOT_OUTPUT "has input capabilities only" #define NOT_GPIO "is not a GPIO" @@ -85,7 +95,10 @@ static struct { struct arg_lit *clear; struct arg_end *end; } i2s_args; - +static struct { + struct arg_str *model_name; + struct arg_end *end; +} known_model_args; static struct { struct arg_rem * rem; struct arg_int * A; @@ -189,9 +202,8 @@ int check_missing_parm(struct arg_int * int_parm, FILE * f){ } char * strip_bt_name(char * opt_str) { - char *result = malloc(strlen(opt_str)+1); - memset(result, 0x00, strlen(opt_str)+1); - char *str = strdup(opt_str); + char *result = malloc_init_external(strlen(opt_str)+1); + char *str = strdup_psram(opt_str); const char * output_marker=" -o"; if(!result ){ @@ -555,7 +567,18 @@ static int do_rotary_cmd(int argc, char **argv){ FREE_AND_NULL(buf); return (nerrors==0 && err==ESP_OK)?0:1; } - +static int is_valid_gpio_number(int gpio, const char * name, FILE *f, bool mandatory, struct arg_int * target, bool output){ + bool invalid = (!GPIO_IS_VALID_GPIO(gpio) ||(output && !GPIO_IS_VALID_OUTPUT_GPIO(gpio))) ; + if(invalid && mandatory && gpio!=-1){ + fprintf(f,"Error: Invalid mandatory gpio %d for %s\n",gpio,name); + return 1; + } + if(target && !invalid){ + target->count=1; + target->ival[0]=gpio; + } + return 0; +} static int do_i2s_cmd(int argc, char **argv) { i2s_platform_config_t i2s_dac_pin = { @@ -570,6 +593,7 @@ static int do_i2s_cmd(int argc, char **argv) return 1; } strcpy(i2s_dac_pin.model, "I2S"); + ESP_LOGD(TAG,"Processing i2s command %s with %d parameters",argv[0],argc); esp_err_t err=ESP_OK; int nerrors = arg_parse(argc, argv,(void **)&i2s_args); @@ -583,39 +607,39 @@ static int do_i2s_cmd(int argc, char **argv) size_t buf_size = 0; FILE *f = open_memstream(&buf, &buf_size); if (f == NULL) { + ESP_LOGE(TAG, "do_i2s_cmd: Failed to open memstream"); cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n"); return 1; } if(nerrors >0){ + ESP_LOGE(TAG,"do_i2s_cmd: %d errors parsing arguments",nerrors); arg_print_errors(f,i2s_args.end,desc_dac); - fclose(f); - return 1; } - nerrors+=is_output_gpio(i2s_args.clock, f, &i2s_dac_pin.pin.bck_io_num, true); - nerrors+=is_output_gpio(i2s_args.wordselect, f, &i2s_dac_pin.pin.ws_io_num, true); - nerrors+=is_output_gpio(i2s_args.data, f, &i2s_dac_pin.pin.data_out_num, true); - nerrors+=is_output_gpio(i2s_args.mute_gpio, f, &i2s_dac_pin.mute_gpio, false); - if(i2s_dac_pin.mute_gpio>0){ - i2s_dac_pin.mute_level = i2s_args.mute_level->count>0?1:0; - } - if(i2s_args.dac_sda->count>0 && i2s_args.dac_sda->ival[0]>=0){ - // if SDA specified, then SDA and SCL are both mandatory - nerrors+=is_output_gpio(i2s_args.dac_sda, f, &i2s_dac_pin.sda, false); - nerrors+=is_output_gpio(i2s_args.dac_scl, f, &i2s_dac_pin.scl, false); - } - if(i2s_args.dac_sda->count==0&& i2s_args.dac_i2c->count>0){ - fprintf(f,"warning: ignoring i2c address, since dac i2c gpios config is incomplete\n"); - } - else if(i2s_args.dac_i2c->count>0){ - i2s_dac_pin.i2c_addr = i2s_args.dac_i2c->ival[0]; - } - if(i2s_args.model_name->count>0 && strlen(i2s_args.model_name->sval[0])>0){ + else { strncpy(i2s_dac_pin.model,i2s_args.model_name->sval[0],sizeof(i2s_dac_pin.model)); i2s_dac_pin.model[sizeof(i2s_dac_pin.model) - 1] = '\0'; - } - if(!nerrors ){ - fprintf(f,"Storing i2s parameters.\n"); - nerrors+=(config_i2s_set(&i2s_dac_pin, "dac_config")!=ESP_OK); + nerrors += is_output_gpio(i2s_args.clock, f, &i2s_dac_pin.pin.bck_io_num, true); + nerrors += is_output_gpio(i2s_args.wordselect, f, &i2s_dac_pin.pin.ws_io_num, true); + nerrors += is_output_gpio(i2s_args.data, f, &i2s_dac_pin.pin.data_out_num, true); + nerrors += is_output_gpio(i2s_args.mute_gpio, f, &i2s_dac_pin.mute_gpio, false); + if (i2s_dac_pin.mute_gpio >= 0) { + i2s_dac_pin.mute_level = i2s_args.mute_level->count > 0 ? 1 : 0; + } + if (i2s_args.dac_sda->count > 0 && i2s_args.dac_sda->ival[0] >= 0) { + // if SDA specified, then SDA and SCL are both mandatory + nerrors += is_output_gpio(i2s_args.dac_sda, f, &i2s_dac_pin.sda, false); + nerrors += is_output_gpio(i2s_args.dac_scl, f, &i2s_dac_pin.scl, false); + } + if (i2s_args.dac_sda->count == 0 && i2s_args.dac_i2c->count > 0) { + fprintf(f, "warning: ignoring i2c address, since dac i2c gpios config is incomplete\n"); + } else if (i2s_args.dac_i2c->count > 0) { + i2s_dac_pin.i2c_addr = i2s_args.dac_i2c->ival[0]; + } + + if (!nerrors) { + fprintf(f, "Storing i2s parameters.\n"); + nerrors += (config_i2s_set(&i2s_dac_pin, "dac_config") != ESP_OK); + } } if(!nerrors ){ fprintf(f,"Done.\n"); @@ -647,40 +671,58 @@ cJSON * example_cb(){ return values; } -//const i2s_pin_config_t * config_get_spdif_pin_struct( ); +cJSON * known_model_cb(){ + const char * key="board_model"; + cJSON * values = cJSON_CreateObject(); + if(!values){ + ESP_LOGE(TAG,"known_model_cb: Failed to create JSON object"); + return NULL; + } + char * name = config_alloc_get_default(NVS_TYPE_STR,key,"",0); + if(!name){ + ESP_LOGE(TAG,"Failed to get board model from nvs key %s ",key); + } + else { + cJSON_AddStringToObject(values,known_model_args.model_name->hdr.longopts,name); + } + + return values; +} cJSON * i2s_cb(){ cJSON * values = cJSON_CreateObject(); + const i2s_platform_config_t * i2s_conf= config_dac_get( ); + if(i2s_conf->pin.bck_io_num>0 ) { - cJSON_AddNumberToObject(values,"clock",i2s_conf->pin.bck_io_num); + cJSON_AddNumberToObject(values,i2s_args.clock->hdr.longopts,i2s_conf->pin.bck_io_num); } if(i2s_conf->pin.ws_io_num>=0 ) { - cJSON_AddNumberToObject(values,"wordselect",i2s_conf->pin.ws_io_num); + cJSON_AddNumberToObject(values,i2s_args.wordselect->hdr.longopts,i2s_conf->pin.ws_io_num); } if(i2s_conf->pin.data_out_num>=0 ) { - cJSON_AddNumberToObject(values,"data",i2s_conf->pin.data_out_num); + cJSON_AddNumberToObject(values,i2s_args.data->hdr.longopts,i2s_conf->pin.data_out_num); } if(i2s_conf->sda>=0 ) { - cJSON_AddNumberToObject(values,"dac_sda",i2s_conf->sda); + cJSON_AddNumberToObject(values,i2s_args.dac_sda->hdr.longopts,i2s_conf->sda); } if(i2s_conf->scl>=0 ) { - cJSON_AddNumberToObject(values,"dac_scl",i2s_conf->scl); + cJSON_AddNumberToObject(values,i2s_args.dac_scl->hdr.longopts,i2s_conf->scl); } if(i2s_conf->i2c_addr>=0 ) { - cJSON_AddNumberToObject(values,"dac_i2c",i2s_conf->i2c_addr); + cJSON_AddNumberToObject(values,i2s_args.dac_i2c->hdr.longopts,i2s_conf->i2c_addr); } if(i2s_conf->mute_gpio>=0 ) { - cJSON_AddNumberToObject(values,"mute_gpio",i2s_conf->mute_gpio); + cJSON_AddNumberToObject(values,i2s_args.mute_gpio->hdr.longopts,i2s_conf->mute_gpio); } if(i2s_conf->mute_level>=0 ) { - cJSON_AddBoolToObject(values,"mute_level",i2s_conf->mute_level>0); + cJSON_AddBoolToObject(values,i2s_args.mute_level->hdr.longopts,i2s_conf->mute_level>0); } if(strlen(i2s_conf->model)>0){ - cJSON_AddStringToObject(values,"model_name",i2s_conf->model); + cJSON_AddStringToObject(values,i2s_args.model_name->hdr.longopts,i2s_conf->model); } else { - cJSON_AddStringToObject(values,"model_name","I2S"); + cJSON_AddStringToObject(values,i2s_args.model_name->hdr.longopts,"I2S"); } return values; @@ -862,17 +904,288 @@ static char * get_log_level_options(const char * longname){ char * options = NULL; int len = snprintf(NULL,0,template,longname,longname,longname); if(len>0){ - options = malloc(len+1); + options = malloc_init_external(len+1); snprintf(options,len,template,longname,longname,longname); } return options; } + +// loop through dac_set and concatenate model name separated with | +static char * get_dac_list(){ + const char * ES8388_MODEL_NAME = "ES8388|"; + char * dac_list=NULL; + size_t total_len=0; + for(int i=0;dac_set[i];i++){ + if(dac_set[i]->model && strlen(dac_set[i]->model)>0){ + total_len+=strlen(dac_set[i]->model)+1; + } + else { + break; + } + } + total_len+=strlen(ES8388_MODEL_NAME); + dac_list = malloc_init_external(total_len+1); + if(dac_list){ + for(int i=0;dac_set[i];i++){ + if(dac_set[i]->model && strlen(dac_set[i]->model)>0){ + strcat(dac_list,dac_set[i]->model); + strcat(dac_list,"|"); + } + else { + break; + } + } + strcat(dac_list,ES8388_MODEL_NAME); + } + return dac_list; +} +void replace_char_in_string(char * str, char find, char replace){ + for(int i=0;str[i];i++){ + if(str[i]==find){ + str[i]=replace; + } + } +} + +static cJSON * get_known_configurations(FILE * f){ + #define err1_msg "Failed to parse known_configs json. %s\nError at:\n%s" + #define err2_msg "Known configs should be an array and it is not: \n%s" + if(!known_configs_string || strlen(known_configs_string)==0){ + return NULL; + } + cJSON * known_configs_json = cJSON_Parse(known_configs_string); + if(!known_configs_json){ + if(f){ + fprintf(f,err1_msg,known_configs_string,STR_OR_BLANK(cJSON_GetErrorPtr())); + } + else { + ESP_LOGE(TAG,err1_msg,known_configs_string,STR_OR_BLANK(cJSON_GetErrorPtr())); + } + return NULL; + } + else { + if(!cJSON_IsArray(known_configs_json)){ + if(f){ + fprintf(f,err2_msg,STR_OR_BLANK(cJSON_GetErrorPtr())); + } + else { + ESP_LOGE(TAG,err2_msg,STR_OR_BLANK(cJSON_GetErrorPtr())); + } + cJSON_Delete(known_configs_json); + return NULL; + } + + } + return known_configs_json; + +} + +static cJSON * find_known_model_name(cJSON * root,const char * name, FILE * f, bool * found){ + if(found){ + *found = false; + } + if(!root){ + return NULL; + } + cJSON * item; + cJSON_ArrayForEach(item, root){ + if(cJSON_IsObject(item)){ + cJSON * model = cJSON_GetObjectItem(item,"name"); + if(model && cJSON_IsString(model) && strcmp(cJSON_GetStringValue(model),name)==0){ + if(found){ + *found = true; + } + return item; + } + } + } + return NULL; +} +static esp_err_t is_known_model_name(const char * name, FILE * f, bool * found){ + esp_err_t err = ESP_OK; + if(found){ + *found = false; + } + if(!known_configs_string || strlen(known_configs_string)==0){ + return err; + } + cJSON * known_configs_json = get_known_configurations(f); + if(known_configs_json){ + cJSON * known_item = find_known_model_name(known_configs_json,name,f,found); + if(known_item && found){ + *found = true; + } + cJSON_Delete(known_configs_json); + } + return err; +} + +static esp_err_t save_known_config(const char * name, FILE * f){ + esp_err_t err = ESP_OK; + char * json_string=NULL; + if(!known_configs_string || strlen(known_configs_string)==0){ + return err; + } + cJSON * known_configs_json = get_known_configurations(f); + if(known_configs_json){ + bool found = false; + cJSON * known_item = find_known_model_name(known_configs_json,name,f,&found); + if(known_item && found){ + json_string = cJSON_Print(known_item); + ESP_LOGD(TAG,"known_item_string: %s",STR_OR_BLANK(json_string)); + FREE_AND_NULL(json_string); + cJSON * kvp=NULL; + cJSON * config_array = cJSON_GetObjectItem(known_item,"config"); + if(config_array && cJSON_IsArray(config_array)){ + json_string = cJSON_Print(config_array); + ESP_LOGD(TAG,"config_array: %s",STR_OR_BLANK(json_string)); + FREE_AND_NULL(json_string); + cJSON_ArrayForEach(kvp, config_array){ + cJSON * kvp_value=kvp->child; + if(!kvp_value){ + ESP_LOGE(TAG,"config entry is not an object!"); + err=ESP_FAIL; + continue; + } + char * key = kvp_value->string; + char * value = kvp_value->valuestring; + if(!key || !value || strlen(key)==0){ + ESP_LOGE(TAG,"Invalid config entry %s:%s",STR_OR_BLANK(key),STR_OR_BLANK(value)); + err=ESP_FAIL; + continue; + } + + fprintf(f,"Storing %s=%s\n",key,value); + err = config_set_value(NVS_TYPE_STR,key,value); + if(err){ + fprintf(f,"Failed: %s\n",esp_err_to_name(err)); + break; + } + } + } + else { + json_string = cJSON_Print(config_array); + char * known_item_string = cJSON_Print(known_item); + fprintf(f,"Failed to parse config array. %s\n%s\nKnown item found: %s\n",config_array?cJSON_IsArray(config_array)?"":"NOT AN ARRAY":"config entry not found",STR_OR_BLANK(json_string),STR_OR_BLANK(known_item_string)); + FREE_AND_NULL(json_string); + FREE_AND_NULL(known_item_string); + err = ESP_FAIL; + } + } + if(err==ESP_OK){ + err = config_set_value(NVS_TYPE_STR,"board_model",name); + if(err!=ESP_OK){ + fprintf(f,"Failed to save board model %s\n",name); + } + } + cJSON_Delete(known_configs_json); + } + return err; +} + +char * config_dac_alloc_print_known_config(){ + cJSON * item=NULL; + char * dac_list=NULL; + size_t total_len=0; + cJSON * object = get_known_configurations(NULL); + if(!object){ + return strdup_psram(""); + } + // loop through all items, and concatenate model name separated with | + + cJSON_ArrayForEach(item, object){ + if(cJSON_IsObject(item)){ + cJSON * model = cJSON_GetObjectItem(item,"name"); + if(model && cJSON_IsString(model)){ + total_len+=strlen(model->valuestring)+1; + } + } + } + if(total_len==0){ + ESP_LOGI(TAG,"No known configs found"); + cJSON_Delete(object); + return NULL; + } + dac_list = malloc_init_external(total_len+1); + if(dac_list){ + cJSON_ArrayForEach(item, object){ + if(cJSON_IsObject(item)){ + cJSON * model = cJSON_GetObjectItem(item,"name"); + if(model && cJSON_IsString(model)){ + strcat(dac_list,model->valuestring); + strcat(dac_list,"|"); + } + } + } + } + dac_list[strlen(dac_list)-1]='\0'; + cJSON_Delete(object); + return dac_list; +} +static int do_register_known_templates_config(int argc, char **argv){ + esp_err_t err=ESP_OK; + int nerrors = arg_parse(argc, argv,(void **)&known_model_args); + char *buf = NULL; + size_t buf_size = 0; + FILE *f = open_memstream(&buf, &buf_size); + if (f == NULL) { + cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n"); + return 1; + } + if(nerrors >0){ + arg_print_errors(f,known_model_args.end,desc_preset); + } + else { + bool found=false; + + if(nerrors +=(is_known_model_name(known_model_args.model_name->sval[0],f,&found)!=ESP_OK)){ + fprintf(f,"Error registering known config %s. The model was not found.\n",known_model_args.model_name->sval[0]); + } + if(nerrors==0 && found){ + fprintf(f,"Appling template configuration for %s\n",known_model_args.model_name->sval[0]); + nerrors+=((err=save_known_config(known_model_args.model_name->sval[0],f))!=ESP_OK); + } + + if(err!=ESP_OK){ + nerrors++; + fprintf(f,"Error registering known config %s.\n",known_model_args.model_name->sval[0]); + } + else { + fprintf(f,"Registered known config %s.\n",known_model_args.model_name->sval[0]); + } + } + + if(!nerrors ){ + fprintf(f,"Done.\n"); + } + fflush (f); + cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf); + fclose(f); + FREE_AND_NULL(buf); + return (nerrors==0 && err==ESP_OK)?0:1; +} +static void register_known_templates_config(){ + char * known_models = config_dac_alloc_print_known_config(); + known_model_args.model_name = arg_str1(NULL,"model_name",known_models,"Known Board Name.\nFor known boards, several systems parameters will be updated"); + known_model_args.end = arg_end(1); + const esp_console_cmd_t cmd = { + .command = CFG_TYPE_HW("preset"), + .help = desc_preset, + .hint = NULL, + .func = &do_register_known_templates_config, + .argtable = &known_model_args + }; + cmd_to_json_with_cb(&cmd,&known_model_cb); + ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); + FREE_AND_NULL(known_models); + +} static void register_i2s_config(void){ - i2s_args.model_name = arg_str1(NULL,"model_name","TAS57xx|TAS5713|AC101|WM8978|I2S","DAC Model Name"); + i2s_args.model_name = arg_str1(NULL,"model_name",STR_OR_BLANK(get_dac_list()),"DAC Model Name"); i2s_args.clear = arg_lit0(NULL, "clear", "Clear configuration"); - i2s_args.clock = arg_int1(NULL,"clock","","Clock GPIO. e.g. 33"); - i2s_args.wordselect = arg_int1(NULL,"wordselect","","Word Select GPIO. e.g. 25"); - i2s_args.data = arg_int1(NULL,"data","","Data GPIO. e.g. 32"); + i2s_args.clock = arg_int0(NULL,"clock","","Clock GPIO. e.g. 33"); + i2s_args.wordselect = arg_int0(NULL,"wordselect","","Word Select GPIO. e.g. 25"); + i2s_args.data = arg_int0(NULL,"data","","Data GPIO. e.g. 32"); i2s_args.mute_gpio = arg_int0(NULL,"mute_gpio", "", "Mute GPIO. e.g. 14"); i2s_args.mute_level = arg_lit0(NULL,"mute_level","Mute GPIO level. Checked=HIGH, Unchecked=LOW"); i2s_args.dac_sda = arg_int0(NULL,"dac_sda", "", "SDA GPIO. e.g. 27"); @@ -1005,6 +1318,9 @@ static void register_squeezelite_config(void){ } void register_config_cmd(void){ + if(!is_dac_config_locked()){ + register_known_templates_config(); + } register_audio_config(); // register_squeezelite_config(); register_bt_source_config(); diff --git a/components/platform_console/cmd_i2ctools.c b/components/platform_console/cmd_i2ctools.c index 7537a03a..f6509a0c 100644 --- a/components/platform_console/cmd_i2ctools.c +++ b/components/platform_console/cmd_i2ctools.c @@ -6,7 +6,6 @@ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ -//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include #include "cmd_i2ctools.h" #include "argtable3/argtable3.h" @@ -21,6 +20,7 @@ #include "messaging.h" #include "display.h" #include "config.h" +#include "globdefs.h" #define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ #define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ @@ -754,7 +754,7 @@ static int do_i2cget_cmd(int argc, char **argv) } i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); - uint8_t *data = malloc(len); + uint8_t *data = malloc_init_external(len); if (data_addr != -1) { i2c_master_write_byte(cmd, chip_addr << 1 | WRITE_BIT, ACK_CHECK_EN); i2c_master_write_byte(cmd, data_addr, ACK_CHECK_EN); diff --git a/components/platform_console/cmd_nvs.c b/components/platform_console/cmd_nvs.c index ccb5ffa1..2690e4af 100644 --- a/components/platform_console/cmd_nvs.c +++ b/components/platform_console/cmd_nvs.c @@ -6,7 +6,6 @@ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ -//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #ifdef __cplusplus extern "C" { #endif @@ -27,41 +26,50 @@ extern "C" { #include "nvs_utilities.h" #include "platform_console.h" #include "messaging.h" +#include "globdefs.h" +#include "trace.h" +extern esp_err_t network_wifi_erase_legacy(); +extern esp_err_t network_wifi_erase_known_ap(); static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob"; static const char * TAG = "cmd_nvs"; -static struct { +EXT_RAM_ATTR static struct { struct arg_str *key; struct arg_str *type; struct arg_str *value; struct arg_end *end; } set_args; -static struct { +EXT_RAM_ATTR static struct { struct arg_str *key; struct arg_str *type; struct arg_end *end; } get_args; -static struct { +EXT_RAM_ATTR static struct { struct arg_str *key; struct arg_end *end; } erase_args; -static struct { +EXT_RAM_ATTR static struct { struct arg_str *namespace; struct arg_end *end; } erase_all_args; -static struct { +EXT_RAM_ATTR static struct { struct arg_str *partition; struct arg_str *namespace; struct arg_str *type; struct arg_end *end; } list_args; +EXT_RAM_ATTR static struct { + struct arg_lit *legacy; + struct arg_lit *ap_list; + struct arg_end *end; +} wifi_erase_args; static esp_err_t store_blob(nvs_handle nvs, const char *key, const char *str_values) @@ -75,7 +83,7 @@ static esp_err_t store_blob(nvs_handle nvs, const char *key, const char *str_val return ESP_ERR_NVS_TYPE_MISMATCH; } - char *blob = (char *)malloc(blob_len); + char *blob = (char *)malloc_init_external(blob_len); if (blob == NULL) { return ESP_ERR_NO_MEM; } @@ -261,7 +269,7 @@ static esp_err_t get_value_from_nvs(const char *key, const char *str_type) } else if (type == NVS_TYPE_STR) { size_t len=0; if ( (err = nvs_get_str(nvs, key, NULL, &len)) == ESP_OK) { - char *str = (char *)malloc(len); + char *str = (char *)malloc_init_external(len); if ( (err = nvs_get_str(nvs, key, str, &len)) == ESP_OK) { log_send_messaging(MESSAGING_INFO,"String associated with key '%s' is %s \n", key, str); } @@ -270,7 +278,7 @@ static esp_err_t get_value_from_nvs(const char *key, const char *str_type) } else if (type == NVS_TYPE_BLOB) { size_t len; if ( (err = nvs_get_blob(nvs, key, NULL, &len)) == ESP_OK) { - char *blob = (char *)malloc(len); + char *blob = (char *)malloc_init_external(len); if ( (err = nvs_get_blob(nvs, key, blob, &len)) == ESP_OK) { log_send_messaging(MESSAGING_INFO,"Blob associated with key '%s' is %d bytes long: \n", key, len); print_blob(blob, len); @@ -399,7 +407,7 @@ static int erase_namespace(int argc, char **argv) return 0; } -static int erase_wifi_manager(int argc, char **argv) +static int erase_network_manager(int argc, char **argv) { nvs_handle nvs; esp_err_t err = nvs_open("config", NVS_READWRITE, &nvs); @@ -411,15 +419,49 @@ static int erase_wifi_manager(int argc, char **argv) } nvs_close(nvs); if (err != ESP_OK) { - cmd_send_messaging(argv[0],MESSAGING_ERROR, "wifi manager configuration was not erase. %s", esp_err_to_name(err)); + cmd_send_messaging(argv[0],MESSAGING_ERROR, "System configuration was not erased. %s", esp_err_to_name(err)); return 1; } else { - cmd_send_messaging(argv[0],MESSAGING_WARNING, "Wifi manager configuration was erased"); + cmd_send_messaging(argv[0],MESSAGING_WARNING, "system configuration was erased. Please reboot."); } return 0; } +static int wifi_erase_config(int argc, char **argv) +{ + esp_err_t err=ESP_OK; + esp_err_t err_ap_list=ESP_OK; + bool done = false; + int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&wifi_erase_args); + if (nerrors != 0) { + return 1; + } + if(wifi_erase_args.ap_list->count>0){ + err_ap_list = network_wifi_erase_known_ap(); + if (err_ap_list != ESP_OK) { + cmd_send_messaging(argv[0],MESSAGING_ERROR, "Could not erase legacy wifi configuration: %s", esp_err_to_name(err)); + } + else { + cmd_send_messaging(argv[0],MESSAGING_ERROR, "Legacy wifi configuration was erased"); + } + done = true; + } + if(wifi_erase_args.legacy->count>0){ + err = network_wifi_erase_legacy(); + if (err != ESP_OK) { + cmd_send_messaging(argv[0],MESSAGING_ERROR, "Could not erase known ap list : %s", esp_err_to_name(err)); + } + else { + cmd_send_messaging(argv[0],MESSAGING_ERROR, "Known access point list was erased"); + } + done = true; + } + if(!done){ + cmd_send_messaging(argv[0],MESSAGING_WARNING, "Please specify at least one configuration type to erase.", esp_err_to_name(err)); + } + return (err_ap_list==ESP_OK && err==ESP_OK)?0:1; +} static int list(const char *part, const char *name, const char *str_type) { @@ -476,6 +518,10 @@ void register_nvs() erase_all_args.namespace = arg_str1(NULL, NULL, "", "namespace to be erased"); erase_all_args.end = arg_end(2); + wifi_erase_args.ap_list = arg_lit0("a","ap_list","Erases Known access points list"); + wifi_erase_args.legacy = arg_lit0("l","legacy","Erases legacy access point storage"); + wifi_erase_args.end = arg_end(1); + list_args.partition = arg_str1(NULL, NULL, "", "partition name"); list_args.namespace = arg_str0("n", "namespace", "", "namespace name"); list_args.type = arg_str0("t", "type", "", ARG_TYPE_STR); @@ -516,11 +562,19 @@ void register_nvs() .func = &erase_namespace, .argtable = &erase_all_args }; - const esp_console_cmd_t erase_wifimanager_cmd = { - .command = "nvs_erase_wifi_manager", - .help = "Erases wifi_manager's config", + const esp_console_cmd_t erase_config_cmd = { + .command = "wifi_erase_config", + .help = "Erases all stored access points from flash", .hint = NULL, - .func = &erase_wifi_manager, + .func = &wifi_erase_config, + .argtable = &wifi_erase_args + }; + + const esp_console_cmd_t erase_networkmanager_cmd = { + .command = "nvs_erase_configuration", + .help = "Erases system's configuration", + .hint = NULL, + .func = &erase_network_manager, .argtable = NULL }; @@ -535,12 +589,21 @@ void register_nvs() .func = &list_entries, .argtable = &list_args }; + MEMTRACE_PRINT_DELTA_MESSAGE("registering list_entries_cmd"); ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd)); + MEMTRACE_PRINT_DELTA_MESSAGE("registering set_cmd"); ESP_ERROR_CHECK(esp_console_cmd_register(&set_cmd)); + MEMTRACE_PRINT_DELTA_MESSAGE("registering get_cmd"); ESP_ERROR_CHECK(esp_console_cmd_register(&get_cmd)); + MEMTRACE_PRINT_DELTA_MESSAGE("registering erase_cmd"); ESP_ERROR_CHECK(esp_console_cmd_register(&erase_cmd)); + MEMTRACE_PRINT_DELTA_MESSAGE("registering erase_namespace_cmd"); ESP_ERROR_CHECK(esp_console_cmd_register(&erase_namespace_cmd)); - ESP_ERROR_CHECK(esp_console_cmd_register(&erase_wifimanager_cmd)); + MEMTRACE_PRINT_DELTA_MESSAGE("registering erase_config_cmd"); + ESP_ERROR_CHECK(esp_console_cmd_register(&erase_networkmanager_cmd)); + MEMTRACE_PRINT_DELTA_MESSAGE("registering erase_config_cmd"); + ESP_ERROR_CHECK(esp_console_cmd_register(&erase_config_cmd)); + MEMTRACE_PRINT_DELTA_MESSAGE("Done"); } #ifdef __cplusplus diff --git a/components/platform_console/cmd_system.c b/components/platform_console/cmd_system.c index 248cae21..f07af71e 100644 --- a/components/platform_console/cmd_system.c +++ b/components/platform_console/cmd_system.c @@ -32,21 +32,23 @@ #include "messaging.h" #include "platform_console.h" #include "trace.h" +#include "globdefs.h" + #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS #pragma message("Runtime stats enabled") #define WITH_TASKS_INFO 1 #else #pragma message("Runtime stats disabled") #endif -static struct { +EXT_RAM_ATTR static struct { struct arg_str *scanmode; struct arg_end *end; } wifi_parms_arg; -static struct { +EXT_RAM_ATTR static struct { struct arg_str *name; struct arg_end *end; } name_args; -static struct { +EXT_RAM_ATTR static struct { struct arg_lit *btspeaker; struct arg_lit *airplay; struct arg_str *telnet; @@ -61,6 +63,7 @@ static const char * TAG = "cmd_system"; static void register_free(); static void register_setdevicename(); static void register_heap(); +static void register_dump_heap(); static void register_version(); static void register_restart(); static void register_deep_sleep(); @@ -73,7 +76,7 @@ static void register_set_wifi_parms(); #if WITH_TASKS_INFO static void register_tasks(); #endif -extern BaseType_t wifi_manager_task; +extern BaseType_t network_manager_task; void register_system() { register_set_wifi_parms(); @@ -81,6 +84,7 @@ void register_system() register_free(); register_set_services(); register_heap(); + register_dump_heap(); register_setdevicename(); register_version(); register_restart(); @@ -297,12 +301,35 @@ static void register_free() cmd_to_json(&cmd); ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); } - +static int dump_heap(int argc, char **argv) +{ + ESP_LOGD(TAG, "Dumping heap"); + heap_caps_dump_all(); + return 0; +} /* 'heap' command prints minumum heap size */ static int heap_size(int argc, char **argv) { - uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT); - cmd_send_messaging(argv[0],MESSAGING_INFO, "min heap size: %u", heap_size); + ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL), + heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM), + heap_caps_get_free_size(MALLOC_CAP_DMA), + heap_caps_get_minimum_free_size(MALLOC_CAP_DMA), + heap_caps_get_largest_free_block(MALLOC_CAP_DMA)); + cmd_send_messaging(argv[0],MESSAGING_INFO,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL), + heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM), + heap_caps_get_free_size(MALLOC_CAP_DMA), + heap_caps_get_minimum_free_size(MALLOC_CAP_DMA), + heap_caps_get_largest_free_block(MALLOC_CAP_DMA)); return 0; } cJSON * setdevicename_cb(){ @@ -341,10 +368,9 @@ int set_squeezelite_player_name(FILE * f,const char * name){ if(nvs_config && strlen(nvs_config)>0){ // allocate enough memory to hold the new command line size_t cmdLength = strlen(nvs_config) + strlen(cleaned_name) + strlen(parm) +1 ; - newCommandLine = malloc(cmdLength); - memset(newCommandLine,0x00, cmdLength); - ESP_LOGD(TAG,"Parsing command %s",nvs_config); - argv = (char **) calloc(22, sizeof(char *)); + newCommandLine = malloc_init_external(cmdLength); + ESP_LOGD(TAG,"Parsing command %s",nvs_config); + argv = (char **) malloc_init_external(22* sizeof(char *)); if (argv == NULL) { FREE_AND_NULL(nvs_config); return 1; @@ -400,7 +426,7 @@ static int setdevicename(int argc, char **argv) /* Check "--name" option */ if (name_args.name->count) { - name=strdup(name_args.name->sval[0]); + name=strdup_psram(name_args.name->sval[0]); } else { cmd_send_messaging(argv[0],MESSAGING_ERROR,"Name must be specified."); @@ -448,6 +474,17 @@ static void register_heap() } +static void register_dump_heap() +{ + const esp_console_cmd_t heap_cmd = { + .command = "dump_heap", + .help = "Dumps the content of the heap to serial output", + .hint = NULL, + .func = &dump_heap, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) ); + +} static void register_setdevicename() { @@ -470,7 +507,7 @@ static void register_setdevicename() static int tasks_info(int argc, char **argv) { const size_t bytes_per_task = 40; /* see vTaskList description */ - char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task); + char *task_list_buffer = malloc_init_external(uxTaskGetNumberOfTasks() * bytes_per_task); if (task_list_buffer == NULL) { cmd_send_messaging(argv[0],MESSAGING_ERROR, "failed to allocate buffer for vTaskList output"); return 1; diff --git a/components/platform_console/cmd_wifi.c b/components/platform_console/cmd_wifi.c index 9e165ddf..3f352c29 100644 --- a/components/platform_console/cmd_wifi.c +++ b/components/platform_console/cmd_wifi.c @@ -32,12 +32,12 @@ #include "esp_netif.h" #include "esp_event.h" #include "led.h" -extern bool bypass_wifi_manager; +extern bool bypass_network_manager; #define JOIN_TIMEOUT_MS (10000) #include "platform_console.h" -extern EventGroupHandle_t wifi_event_group; +extern EventGroupHandle_t network_event_group; extern const int CONNECTED_BIT; //static const char * TAG = "cmd_wifi"; /** Arguments used by 'join' function */ @@ -65,10 +65,10 @@ static void event_handler(void* arg, esp_event_base_t event_base, if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { led_blink_pushed(LED_GREEN, 250, 250); esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + xEventGroupClearBits(network_event_group, CONNECTED_BIT); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { led_unpush(LED_GREEN); - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + xEventGroupSetBits(network_event_group, CONNECTED_BIT); } } //bool wait_for_wifi(){ @@ -127,7 +127,7 @@ static bool wifi_join(const char *ssid, const char *pass, int timeout_ms) ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); ESP_ERROR_CHECK( esp_wifi_connect() ); - int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + int bits = xEventGroupWaitBits(network_event_group, CONNECTED_BIT, pdFALSE, pdTRUE, timeout_ms / portTICK_PERIOD_MS); return (bits & CONNECTED_BIT) != 0; } @@ -202,7 +202,7 @@ void register_wifi_join() void register_wifi() { register_wifi_join(); - if(bypass_wifi_manager){ + if(bypass_network_manager){ initialise_wifi(); } } diff --git a/components/platform_console/platform_console.c b/components/platform_console/platform_console.c index 0d491d05..9afbe2b6 100644 --- a/components/platform_console/platform_console.c +++ b/components/platform_console/platform_console.c @@ -34,7 +34,7 @@ pthread_t thread_console; static void * console_thread(); void console_start(); static const char * TAG = "console"; -extern bool bypass_wifi_manager; +extern bool bypass_network_manager; extern void register_squeezelite(); /* Prompt to be printed before each line. @@ -188,8 +188,8 @@ void process_autoexec(){ uint8_t autoexec_flag=0; char * str_flag = config_alloc_get(NVS_TYPE_STR, "autoexec"); - if(!bypass_wifi_manager){ - ESP_LOGW(TAG, "Processing autoexec commands while wifi_manager active. Wifi related commands will be ignored."); + if(!bypass_network_manager){ + ESP_LOGW(TAG, "Processing autoexec commands while network manager active. Wifi related commands will be ignored."); } if(is_recovery_running){ ESP_LOGD(TAG, "Processing autoexec commands in recovery mode. Squeezelite commands will be ignored."); @@ -203,7 +203,7 @@ void process_autoexec(){ ESP_LOGD(TAG,"Getting command name %s", autoexec_name); autoexec_value= config_alloc_get(NVS_TYPE_STR, autoexec_name); if(autoexec_value!=NULL ){ - if(!bypass_wifi_manager && strstr(autoexec_value, "join ")!=NULL ){ + if(!bypass_network_manager && strstr(autoexec_value, "join ")!=NULL ){ ESP_LOGW(TAG,"Ignoring wifi join command."); } else if(is_recovery_running && !strstr(autoexec_value, "squeezelite " ) ){ @@ -298,18 +298,26 @@ void console_start() { ESP_ERROR_CHECK(esp_console_init(&console_config)); } /* Register commands */ + MEMTRACE_PRINT_DELTA_MESSAGE("Registering help command"); esp_console_register_help_command(); + MEMTRACE_PRINT_DELTA_MESSAGE("Registering system commands"); register_system(); + MEMTRACE_PRINT_DELTA_MESSAGE("Registering config commands"); register_config_cmd(); + MEMTRACE_PRINT_DELTA_MESSAGE("Registering nvs commands"); register_nvs(); + MEMTRACE_PRINT_DELTA_MESSAGE("Registering wifi commands"); register_wifi(); if(!is_recovery_running){ + MEMTRACE_PRINT_DELTA_MESSAGE("Registering squeezelite commands"); register_squeezelite(); } else { + MEMTRACE_PRINT_DELTA_MESSAGE("Registering recovery commands"); register_ota_cmd(); } + MEMTRACE_PRINT_DELTA_MESSAGE("Registering i2c commands"); register_i2ctools(); if(!is_serial_suppressed()){ @@ -358,14 +366,16 @@ void console_start() { prompt = recovery_prompt; cfg.stack_size = 4096 ; } + MEMTRACE_PRINT_DELTA_MESSAGE("Creating console thread with stack size of 4096 bytes"); esp_pthread_set_cfg(&cfg); pthread_attr_t attr; pthread_attr_init(&attr); - pthread_create(&thread_console, &attr, console_thread, NULL); pthread_attr_destroy(&attr); + MEMTRACE_PRINT_DELTA_MESSAGE("Console thread created"); } else if(!is_recovery_running){ + MEMTRACE_PRINT_DELTA_MESSAGE("Running autoexec"); process_autoexec(); } @@ -392,7 +402,9 @@ esp_err_t run_command(char * line){ } static void * console_thread() { if(!is_recovery_running){ + MEMTRACE_PRINT_DELTA_MESSAGE("Running autoexec"); process_autoexec(); + MEMTRACE_PRINT_DELTA_MESSAGE("Autoexec done"); } /* Main loop */ while (1) { diff --git a/components/raop/raop_sink.c b/components/raop/raop_sink.c index d14f27d9..52f1cd0f 100644 --- a/components/raop/raop_sink.c +++ b/components/raop/raop_sink.c @@ -164,6 +164,8 @@ static bool raop_sink_start(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) { tcpip_adapter_if_t ifs[] = { TCPIP_ADAPTER_IF_ETH, TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_IF_AP }; // get various IP info + // it is possible to get the currently active interface with the following call: + // network_get_active_interface() for (int i = 0; i < sizeof(ifs) / sizeof(tcpip_adapter_if_t); i++) if (tcpip_adapter_get_ip_info(ifs[i], &ipInfo) == ESP_OK && ipInfo.ip.addr != IPADDR_ANY) { tcpip_adapter_get_hostname(ifs[i], &hostname); diff --git a/components/services/CMakeLists.txt b/components/services/CMakeLists.txt index 6c2e80e5..7617ef6e 100644 --- a/components/services/CMakeLists.txt +++ b/components/services/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register(SRC_DIRS . INCLUDE_DIRS . ${IDF_PATH}/components/driver - REQUIRES json tools platform_config display + REQUIRES json tools platform_config display wifi-manager PRIV_REQUIRES soc esp32 squeezelite ) diff --git a/components/services/accessors.c b/components/services/accessors.c index 871c31d0..5940271d 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -32,6 +32,7 @@ #include "trace.h" #include "monitor.h" #include "messaging.h" +#include "network_ethernet.h" static const char *TAG = "services"; @@ -62,6 +63,7 @@ static char * config_spdif_get_string(){ ",ws=" STR(CONFIG_SPDIF_WS_IO) ",do=" STR(CONFIG_SPDIF_DO_IO)); } + /**************************************************************************************** * */ @@ -122,7 +124,6 @@ const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){ set_i2s_pin(dac_config, &i2s_dac_pin.pin); strcpy(i2s_dac_pin.model, "i2s"); char * p=NULL; - if ((p = strcasestr(dac_config, "i2c")) != NULL) i2s_dac_pin.i2c_addr = atoi(strchr(p, '=') + 1); if ((p = strcasestr(dac_config, "sda")) != NULL) i2s_dac_pin.sda = atoi(strchr(p, '=') + 1); if ((p = strcasestr(dac_config, "scl")) != NULL) i2s_dac_pin.scl = atoi(strchr(p, '=') + 1); @@ -143,9 +144,8 @@ const eth_config_t * config_get_eth_from_str(char * eth_config ){ char * p=NULL; static EXT_RAM_ATTR eth_config_t eth_pin; memset(ð_pin, 0xFF, sizeof(eth_pin)); - memset(ð_pin.model, 0xFF, sizeof(eth_pin.model)); - eth_pin.spi = false; - eth_pin.rmii = false; + memset(ð_pin.model, 0x00, sizeof(eth_pin.model)); + eth_pin.valid = true; if ((p = strcasestr(eth_config, "model")) != NULL) sscanf(p, "%*[^=]=%15[^,]", eth_pin.model); if ((p = strcasestr(eth_config, "mdc")) != NULL) eth_pin.mdc = atoi(strchr(p, '=') + 1); @@ -158,15 +158,32 @@ const eth_config_t * config_get_eth_from_str(char * eth_config ){ if ((p = strcasestr(eth_config, "speed")) != NULL) eth_pin.speed = atoi(strchr(p, '=') + 1); if ((p = strcasestr(eth_config, "clk")) != NULL) eth_pin.clk = atoi(strchr(p, '=') + 1); if ((p = strcasestr(eth_config, "host")) != NULL) eth_pin.host = atoi(strchr(p, '=') + 1); - eth_pin.valid = eth_pin.model && strlen(eth_pin.model)>0 && GPIO_IS_VALID_GPIO(eth_pin.mdio) && GPIO_IS_VALID_GPIO(eth_pin.mdc); - - if(strcasestr(eth_pin.model, "LAN8720")){ - eth_pin.rmii = true; + + if(!eth_pin.model || strlen(eth_pin.model)==0){ + eth_pin.valid = false; + return ð_pin; } - else { - eth_pin.spi = true; - /* here we must also check that we have at least a CS gpio */ - eth_pin.valid = eth_pin.valid && GPIO_IS_VALID_GPIO(eth_pin.cs); + network_ethernet_driver_t* network_driver = network_ethernet_driver_autodetect(eth_pin.model); + if(!network_driver || !network_driver->valid){ + messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: model %s %s",eth_pin.model,network_driver?"was not compiled in":"was not found"); + eth_pin.valid = false; + } + if(network_driver){ + eth_pin.rmii = network_driver->rmii; + eth_pin.spi = network_driver->spi; + + if(network_driver->rmii){ + if(!GPIO_IS_VALID_GPIO(eth_pin.mdio) || !GPIO_IS_VALID_GPIO(eth_pin.mdc)){ + messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: %s %s",!GPIO_IS_VALID_GPIO(eth_pin.mdc)?"Invalid MDC":"",!GPIO_IS_VALID_GPIO(eth_pin.mdio)?"Invalid mdio":""); + eth_pin.valid = false; + } + } + else if(network_driver->spi){ + if(!GPIO_IS_VALID_GPIO(eth_pin.cs)){ + messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: invalid CS pin"); + return false; + } + } } return ð_pin; } @@ -216,10 +233,12 @@ const eth_config_t * config_eth_get( ){ #endif #endif ",mdc=" STR(CONFIG_ETH_MDC_IO) ",mdio=" STR(CONFIG_ETH_MDIO_IO)) ; + if(config && strlen(config)>0){ + ESP_LOGD(TAG,"Parsing ethernet configuration %s", config); + } static EXT_RAM_ATTR eth_config_t eth_config; - ESP_LOGD(TAG, "Ethernet config string %s", config); memcpy(ð_config, config_get_eth_from_str(config), sizeof(eth_config)); - free(config); + FREE_AND_NULL(config); return ð_config; } /**************************************************************************************** @@ -236,7 +255,7 @@ void config_eth_init( eth_config_t * target ){ esp_err_t config_i2c_set(const i2c_config_t * config, int port){ int buffer_size=255; esp_err_t err=ESP_OK; - char * config_buffer=calloc(buffer_size,1); + char * config_buffer=malloc_init_external(buffer_size); if(config_buffer) { snprintf(config_buffer,buffer_size,"scl=%u,sda=%u,speed=%u,port=%u",config->scl_io_num,config->sda_io_num,config->master.clk_speed,port); log_send_messaging(MESSAGING_INFO,"Updating I2C configuration to %s",config_buffer); @@ -255,8 +274,8 @@ esp_err_t config_i2c_set(const i2c_config_t * config, int port){ esp_err_t config_rotary_set(rotary_struct_t * config){ int buffer_size=512; esp_err_t err=ESP_OK; - char * config_buffer=calloc(buffer_size,1); - char * config_buffer2=calloc(buffer_size,1); + char * config_buffer=malloc_init_external(buffer_size); + char * config_buffer2=malloc_init_external(buffer_size); if(config_buffer && config_buffer2) { snprintf(config_buffer,buffer_size,"A=%i,B=%i",config->A, config->B); if(config->SW >=0 ){ @@ -296,8 +315,8 @@ esp_err_t config_rotary_set(rotary_struct_t * config){ esp_err_t config_display_set(const display_config_t * config){ int buffer_size=512; esp_err_t err=ESP_OK; - char * config_buffer=calloc(buffer_size,1); - char * config_buffer2=calloc(buffer_size,1); + char * config_buffer=malloc_init_external(buffer_size); + char * config_buffer2=malloc_init_external(buffer_size); if(config_buffer && config_buffer2) { snprintf(config_buffer,buffer_size,"%s,width=%i,height=%i",config->type,config->width,config->height); if(strcasecmp("I2C",config->type)==0){ @@ -348,8 +367,8 @@ esp_err_t config_display_set(const display_config_t * config){ esp_err_t config_i2s_set(const i2s_platform_config_t * config, const char * nvs_name){ int buffer_size=255; esp_err_t err=ESP_OK; - char * config_buffer=calloc(buffer_size,1); - char * config_buffer2=calloc(buffer_size,1); + char * config_buffer=malloc_init_external(buffer_size); + char * config_buffer2=malloc_init_external(buffer_size); if(config_buffer && config_buffer2) { snprintf(config_buffer,buffer_size,"model=%s,bck=%u,ws=%u,do=%u",config->model,config->pin.bck_io_num,config->pin.ws_io_num,config->pin.data_out_num); if(config->mute_gpio>=0){ @@ -384,7 +403,7 @@ esp_err_t config_i2s_set(const i2s_platform_config_t * config, const char * nvs_ esp_err_t config_spdif_set(const i2s_platform_config_t * config){ int buffer_size=255; esp_err_t err=ESP_OK; - char * config_buffer=calloc(buffer_size,1); + char * config_buffer=malloc_init_external(buffer_size); if(config_buffer ) { snprintf(config_buffer,buffer_size,"bck=%u,ws=%u,do=%u",config->pin.bck_io_num,config->pin.ws_io_num,config->pin.data_out_num); log_send_messaging(MESSAGING_INFO,"Updating SPDIF configuration to %s",config_buffer); @@ -406,7 +425,7 @@ esp_err_t config_spdif_set(const i2s_platform_config_t * config){ esp_err_t config_spi_set(const spi_bus_config_t * config, int host, int dc){ int buffer_size=255; esp_err_t err = ESP_OK; - char * config_buffer=calloc(buffer_size,1); + char * config_buffer=malloc_init_external(buffer_size); if(config_buffer) { snprintf(config_buffer,buffer_size,"data=%u,clk=%u,dc=%u,host=%u,miso=%d",config->mosi_io_num,config->sclk_io_num,dc,host,config->miso_io_num); log_send_messaging(MESSAGING_INFO,"Updating SPI configuration to %s",config_buffer); @@ -855,7 +874,7 @@ cJSON * get_Rotary_GPIO(cJSON * list){ */ esp_err_t get_gpio_structure(cJSON * gpio_entry, gpio_entry_t ** gpio){ esp_err_t err = ESP_OK; - *gpio = malloc(sizeof(gpio_entry_t)); + *gpio = malloc_init_external(sizeof(gpio_entry_t)); cJSON * val = cJSON_GetObjectItem(gpio_entry,"gpio"); if(val){ (*gpio)->gpio= (int)val->valuedouble; @@ -865,14 +884,14 @@ esp_err_t get_gpio_structure(cJSON * gpio_entry, gpio_entry_t ** gpio){ } val = cJSON_GetObjectItem(gpio_entry,"name"); if(val){ - (*gpio)->name= strdup(cJSON_GetStringValue(val)); + (*gpio)->name= strdup_psram(cJSON_GetStringValue(val)); } else { ESP_LOGE(TAG,"gpio name value not found"); err=ESP_FAIL; } val = cJSON_GetObjectItem(gpio_entry,"group"); if(val){ - (*gpio)->group= strdup(cJSON_GetStringValue(val)); + (*gpio)->group= strdup_psram(cJSON_GetStringValue(val)); } else { ESP_LOGE(TAG,"gpio group value not found"); err=ESP_FAIL; diff --git a/components/services/accessors.h b/components/services/accessors.h index 8f720b8a..2c03c996 100644 --- a/components/services/accessors.h +++ b/components/services/accessors.h @@ -12,6 +12,8 @@ #include "driver/i2c.h" #include "driver/i2s.h" #include "driver/spi_master.h" +#include "freertos/queue.h" + extern const char *i2c_name_type; extern const char *spi_name_type; typedef struct { @@ -30,7 +32,7 @@ typedef struct { bool rotate; } display_config_t; -typedef struct { +typedef struct eth_config_struct { char model[16]; bool valid; bool rmii; @@ -98,6 +100,7 @@ const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host); void parse_set_GPIO(void (*cb)(int gpio, char *value)); const i2s_platform_config_t * config_dac_get(); const i2s_platform_config_t * config_spdif_get( ); +const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ); esp_err_t config_spdif_set(const i2s_platform_config_t * config); bool is_spdif_config_locked(); esp_err_t free_gpio_entry( gpio_entry_t ** gpio); diff --git a/components/services/globdefs.h b/components/services/globdefs.h index 8b5139a5..79be0033 100644 --- a/components/services/globdefs.h +++ b/components/services/globdefs.h @@ -21,7 +21,6 @@ typedef struct { int timer, base_channel, max; } pwm_system_t; extern pwm_system_t pwm_system; - #ifdef CONFIG_SQUEEZEAMP #define ADAC dac_tas57xx #elif defined(CONFIG_A1S) @@ -29,3 +28,6 @@ extern pwm_system_t pwm_system; #else #define ADAC dac_external #endif +void * malloc_init_external(size_t sz); +void * clone_obj_psram(void * source, size_t source_sz); +char * strdup_psram(const char * source); \ No newline at end of file diff --git a/components/services/messaging.c b/components/services/messaging.c index 968d9c46..bcdc7077 100644 --- a/components/services/messaging.c +++ b/components/services/messaging.c @@ -15,6 +15,7 @@ #include "platform_esp32.h" #include "messaging.h" #include "trace.h" +#include "globdefs.h" /************************************ * Globals */ @@ -38,7 +39,7 @@ messaging_handle_t get_handle_ptr(messaging_list_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)); + StaticRingbuffer_t *buffer_struct = malloc_init_external(sizeof(StaticRingbuffer_t)); if (buffer_struct != NULL) { 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); @@ -76,7 +77,7 @@ messaging_handle_t messaging_register_subscriber(uint8_t max_count, char * name) while(cur->next){ cur = get_struct_ptr(cur->next); } - cur->next=heap_caps_malloc(sizeof(messaging_list_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + cur->next=malloc_init_external(sizeof(messaging_list_t)); if(!cur->next){ ESP_LOGE(tag,"subscriber alloc failed"); return NULL; @@ -84,7 +85,7 @@ messaging_handle_t messaging_register_subscriber(uint8_t max_count, char * name) memset(cur->next,0x00,sizeof(messaging_list_t)); cur = get_struct_ptr(cur->next); cur->max_count=max_count; - cur->subscriber_name=strdup(name); + cur->subscriber_name=strdup_psram(name); cur->buf_handle = messaging_create_ring_buffer(max_count); if(cur->buf_handle){ messaging_fill_messages(cur); @@ -99,7 +100,7 @@ void messaging_service_init(){ } else { top.max_count = max_count; - top.subscriber_name = strdup("messaging"); + top.subscriber_name = strdup_psram("messaging"); } return; } @@ -161,10 +162,7 @@ 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)); - message_copy = heap_caps_malloc(item_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if(message_copy){ - memcpy(message_copy,message,item_size); - } + message_copy = clone_obj_psram(message,item_size); vRingbufferReturnItem(buf_handle, (void *)message); } return message_copy; @@ -231,7 +229,7 @@ void messaging_post_message(messaging_types type,messaging_classes msg_class, co va_start(va, fmt); 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); + message = (single_message_t *)malloc_init_external(msg_size); vsprintf(message->message, fmt, va); va_end(va); message->msg_size = msg_size; @@ -256,11 +254,25 @@ void messaging_post_message(messaging_types type,messaging_classes msg_class, co return; } +char * messaging_alloc_format_string(const char *fmt, ...) { + va_list va; + va_start(va, fmt); + size_t ln = vsnprintf(NULL, 0, fmt, va)+1; + char * message_txt = malloc_init_external(ln); + if(message_txt){ + vsprintf(message_txt, fmt, va); + va_end(va); + } + else{ + ESP_LOGE(tag, "Memory allocation failed while sending message"); + } + return message_txt; +} void log_send_messaging(messaging_types msgtype,const char *fmt, ...) { va_list va; va_start(va, fmt); size_t ln = vsnprintf(NULL, 0, fmt, va)+1; - char * message_txt = malloc(ln); + char * message_txt = malloc_init_external(ln); if(message_txt){ vsprintf(message_txt, fmt, va); va_end(va); @@ -272,12 +284,13 @@ void log_send_messaging(messaging_types msgtype,const char *fmt, ...) { ESP_LOGE(tag, "Memory allocation failed while sending message"); } } + void cmd_send_messaging(const char * cmdname,messaging_types msgtype, const char *fmt, ...){ va_list va; va_start(va, fmt); size_t cmd_len = strlen(cmdname)+1; size_t ln = vsnprintf(NULL, 0, fmt, va)+1; - char * message_txt = malloc(ln+cmd_len); + char * message_txt = malloc_init_external(ln+cmd_len); if(message_txt){ strcpy(message_txt,cmdname); strcat(message_txt,"\n"); diff --git a/components/services/messaging.h b/components/services/messaging.h index baa5a193..7d0cb273 100644 --- a/components/services/messaging.h +++ b/components/services/messaging.h @@ -39,6 +39,7 @@ single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle); void log_send_messaging(messaging_types msgtype,const char *fmt, ...); void cmd_send_messaging(const char * cmdname,messaging_types msgtype, const char *fmt, ...); esp_err_t messaging_type_to_err_type(messaging_types type); +char * messaging_alloc_format_string(const char *fmt, ...) ; void messaging_service_init(); #define REALLOC_CAT(e,n) e=realloc(e,strlen(n)); e=strcat(e,n) diff --git a/components/services/monitor.c b/components/services/monitor.c index 550c52c3..3aeabfa9 100644 --- a/components/services/monitor.c +++ b/components/services/monitor.c @@ -54,7 +54,7 @@ static void task_stats( cJSON* top ) { } current, previous; cJSON * tlist=cJSON_CreateArray(); current.n = uxTaskGetNumberOfTasks(); - current.tasks = malloc( current.n * sizeof( TaskStatus_t ) ); + current.tasks = malloc_init_external( current.n * sizeof( TaskStatus_t ) ); current.n = uxTaskGetSystemState( current.tasks, current.n, ¤t.total ); cJSON_AddNumberToObject(top,"ntasks",current.n); @@ -126,11 +126,13 @@ static void monitor_callback(TimerHandle_t xTimer) { cJSON_AddNumberToObject(top,"free_spiram",heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); cJSON_AddNumberToObject(top,"min_free_spiram",heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); - ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu)", + ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)", heap_caps_get_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_free_size(MALLOC_CAP_SPIRAM), - heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_free_size(MALLOC_CAP_DMA), + heap_caps_get_minimum_free_size(MALLOC_CAP_DMA)); task_stats(top); char * top_a= cJSON_PrintUnformatted(top); @@ -248,11 +250,13 @@ void monitor_svc_init(void) { } FREE_AND_NULL(p); - ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu)", + ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)", heap_caps_get_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_free_size(MALLOC_CAP_SPIRAM), - heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_free_size(MALLOC_CAP_DMA), + heap_caps_get_minimum_free_size(MALLOC_CAP_DMA)); } /**************************************************************************************** diff --git a/components/services/services.c b/components/services/services.c index c0c11ce2..3633f4ba 100644 --- a/components/services/services.c +++ b/components/services/services.c @@ -18,6 +18,9 @@ #include "globdefs.h" #include "accessors.h" #include "messaging.h" +#include "esp_heap_caps.h" +#include "esp_log.h" + extern void battery_svc_init(void); extern void monitor_svc_init(void); @@ -35,6 +38,43 @@ pwm_system_t pwm_system = { static const char *TAG = "services"; + +void * malloc_init_external(size_t sz){ + void * ptr=NULL; + ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(ptr==NULL){ + ESP_LOGE(TAG,"malloc_init_external: unable to allocate %d bytes of PSRAM!",sz); + } + else { + memset(ptr,0x00,sz); + } + return ptr; +} +void * clone_obj_psram(void * source, size_t source_sz){ + void * ptr=NULL; + ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(ptr==NULL){ + ESP_LOGE(TAG,"clone_obj_psram: unable to allocate %d bytes of PSRAM!",source_sz); + } + else { + memcpy(ptr,source,source_sz); + } + return ptr; +} +char * strdup_psram(const char * source){ + void * ptr=NULL; + size_t source_sz = strlen(source)+1; + ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(ptr==NULL){ + ESP_LOGE(TAG,"strdup_psram: unable to allocate %d bytes of PSRAM! Cannot clone string %s",source_sz,source); + } + else { + memset(ptr,0x00,source_sz); + strcpy(ptr,source); + } + return ptr; +} + /**************************************************************************************** * */ diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c index c5e20000..96109c92 100644 --- a/components/squeezelite-ota/squeezelite-ota.c +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -38,7 +38,7 @@ #include "gds_draw.h" #include "platform_esp32.h" #include "lwip/sockets.h" - +#include "globdefs.h" extern const char * get_certificate(); #define IF_DISPLAY(x) if(display) { x; } @@ -95,11 +95,13 @@ static esp_http_client_config_t http_client_config; void _printMemStats(){ - ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)", + ESP_LOGD(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)", heap_caps_get_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_free_size(MALLOC_CAP_SPIRAM), - heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_free_size(MALLOC_CAP_DMA), + heap_caps_get_minimum_free_size(MALLOC_CAP_DMA)); } uint8_t ota_get_pct_complete(){ return ota_status->total_image_len==0?0: @@ -183,7 +185,7 @@ void sendMessaging(messaging_types type,const char * fmt, ...){ va_start(args, fmt); str_len = vsnprintf(NULL,0,fmt,args)+1; if(str_len>0){ - msg_str = malloc(str_len); + msg_str = malloc_init_external(str_len); vsnprintf(msg_str,str_len,fmt,args); if(type == MESSAGING_WARNING){ ESP_LOGW(TAG,"%s",msg_str); @@ -237,7 +239,7 @@ esp_err_t handle_http_on_data(esp_http_client_event_t *evt){ ota_status->total_image_len=esp_http_client_get_content_length(evt->client); ota_status->downloaded_image_len = 0; ota_status->newdownloadpct = 0; - ota_status->bin_data= malloc(ota_status->total_image_len); + ota_status->bin_data= malloc_init_external(ota_status->total_image_len); if(ota_status->bin_data==NULL){ sendMessaging(MESSAGING_ERROR,"Error: buffer alloc error"); return ESP_FAIL; @@ -351,7 +353,7 @@ esp_err_t init_config(ota_thread_parms_t * p_ota_thread_parms){ http_client_config.event_handler = _http_event_handler; http_client_config.disable_auto_redirect=false; http_client_config.skip_cert_common_name_check = false; - http_client_config.url = strdup(p_ota_thread_parms->url); + http_client_config.url = strdup_psram(p_ota_thread_parms->url); http_client_config.max_redirection_count = 4; // buffer size below is for http read chunks http_client_config.buffer_size = 8192; //1024 ; @@ -680,12 +682,11 @@ esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t ESP_LOGE(TAG,"OTA Already started. "); return ESP_FAIL; } - ota_status = heap_caps_malloc(sizeof(ota_status_t) , (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); - memset(ota_status, 0x00, sizeof(ota_status_t)); + ota_status = malloc_init_external(sizeof(ota_status_t)); ota_status->bOTAThreadStarted=true; if(bin_url){ - ota_thread_parms.url =strdup(bin_url); + ota_thread_parms.url =strdup_psram(bin_url); ESP_LOGD(TAG, "Starting ota on core %u for : %s", OTA_CORE,ota_thread_parms.url); } else { @@ -751,7 +752,7 @@ in_addr_t discover_ota_server(int max) { do { - ESP_LOGI(TAG,"sending LMS discovery"); + ESP_LOGI(TAG,"sending LMS discovery for OTA Update"); memset(&s, 0, sizeof(s)); if (sendto(disc_sock, buf, len, 0, (struct sockaddr *)&d, sizeof(d)) < 0) { diff --git a/components/telnet/telnet.c b/components/telnet/telnet.c index 42b3c064..0c96071e 100644 --- a/components/telnet/telnet.c +++ b/components/telnet/telnet.c @@ -58,7 +58,7 @@ static size_t log_buf_size=2000; //32-bit aligned size static bool bIsEnabled=false; static int partnerSocket=0; static telnet_t *tnHandle; -extern bool bypass_wifi_manager; +extern bool bypass_network_manager; /************************************ * Forward declarations @@ -92,7 +92,7 @@ void init_telnet(){ // if wifi manager is bypassed, there will possibly be no wifi available // bMirrorToUART = (strcasestr("D",val)!=NULL); - if(!bMirrorToUART && bypass_wifi_manager){ + if(!bMirrorToUART && bypass_network_manager){ // This isn't supposed to happen, as telnet won't start if wifi manager isn't // started. So this is a safeguard only. ESP_LOGW(TAG,"Wifi manager is not active. Forcing console on Serial output."); diff --git a/components/tools/CMakeLists.txt b/components/tools/CMakeLists.txt index 07b3ba12..0cfb6056 100644 --- a/components/tools/CMakeLists.txt +++ b/components/tools/CMakeLists.txt @@ -1,6 +1,7 @@ -idf_component_register(SRCS operator.cpp utf8.c +idf_component_register(SRCS operator.cpp utf8.c trace.c REQUIRES esp_common pthread INCLUDE_DIRS . + PRIV_REQUIRES services freertos pthread esp_common ) #doing our own implementation of new operator for some pre-compiled binaries diff --git a/components/tools/trace.c b/components/tools/trace.c new file mode 100644 index 00000000..679fd4c3 --- /dev/null +++ b/components/tools/trace.c @@ -0,0 +1,120 @@ +#include +#include "esp_system.h" +#include +#include +#include "esp_log.h" +#include "freertos/xtensa_api.h" +#include "freertos/FreeRTOSConfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "globdefs.h" +#include "esp_event.h" +#include "trace.h" + + +static const char TAG[] = "TRACE"; +// typedef struct mem_usage_trace_for_thread { +// TaskHandle_t task; +// size_t malloc_int_last; +// size_t malloc_spiram_last; +// size_t malloc_dma_last; +// const char *name; +// SLIST_ENTRY(mem_usage_trace_for_thread) next; +// } mem_usage_trace_for_thread_t; + +// static EXT_RAM_ATTR SLIST_HEAD(memtrace, mem_usage_trace_for_thread) s_memtrace; + +// mem_usage_trace_for_thread_t* memtrace_get_thread_entry(TaskHandle_t task) { +// if(!task) { +// ESP_LOGE(TAG, "memtrace_get_thread_entry: task is NULL"); +// return NULL; +// } +// ESP_LOGD(TAG,"Looking for task %s",STR_OR_ALT(pcTaskGetName(task ), "unknown")); +// mem_usage_trace_for_thread_t* it; +// SLIST_FOREACH(it, &s_memtrace, next) { +// if ( it->task == task ) { +// ESP_LOGD(TAG,"Found task %s",STR_OR_ALT(pcTaskGetName(task ), "unknown")); +// return it; +// } +// } +// return NULL; +// } +// void memtrace_add_thread_entry(TaskHandle_t task) { +// if(!task) { +// ESP_LOGE(TAG, "memtrace_get_thread_entry: task is NULL"); +// return ; +// } +// mem_usage_trace_for_thread_t* it = memtrace_get_thread_entry(task); +// if (it) { +// ESP_LOGW(TAG, "memtrace_add_thread_entry: thread already in list"); +// return; +// } +// it = (mem_usage_trace_for_thread_t*)malloc_init_external(sizeof(mem_usage_trace_for_thread_t)); +// if (!it) { +// ESP_LOGE(TAG, "memtrace_add_thread_entry: malloc failed"); +// return; +// } +// it->task = task; +// it->malloc_int_last = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); +// it->malloc_spiram_last = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); +// it->malloc_dma_last = heap_caps_get_free_size(MALLOC_CAP_DMA); +// it->name = pcTaskGetName(task); +// SLIST_INSERT_HEAD(&s_memtrace, it, next); +// return; +// } +// void memtrace_print_delta(){ +// TaskHandle_t task = xTaskGetCurrentTaskHandle(); +// mem_usage_trace_for_thread_t* it = memtrace_get_thread_entry(task); +// if (!it) { +// memtrace_add_thread_entry(task); +// ESP_LOGW(TAG, "memtrace_print_delta: added new entry for task %s",STR_OR_ALT(pcTaskGetName(task ), "unknown")); +// return; +// } +// size_t malloc_int_delta = heap_caps_get_free_size(MALLOC_CAP_INTERNAL) - it->malloc_int_last; +// size_t malloc_spiram_delta = heap_caps_get_free_size(MALLOC_CAP_SPIRAM) - it->malloc_spiram_last; +// size_t malloc_dma_delta = heap_caps_get_free_size(MALLOC_CAP_DMA) - it->malloc_dma_last; +// ESP_LOG(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)", +// heap_caps_get_free_size(MALLOC_CAP_INTERNAL), +// heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), +// heap_caps_get_free_size(MALLOC_CAP_SPIRAM), +// heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), +// heap_caps_get_free_size(MALLOC_CAP_DMA), +// heap_caps_get_minimum_free_size(MALLOC_CAP_DMA)); +// ESP_LOGW(TAG, "memtrace_print_delta: %s: malloc_int_delta=%d, malloc_spiram_delta=%d, malloc_dma_delta=%d", +// STR_OR_ALT(it->name, "unknown"), +// malloc_int_delta, +// malloc_spiram_delta, +// malloc_dma_delta); +// it->malloc_int_last = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); +// it->malloc_spiram_last = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); +// it->malloc_dma_last = heap_caps_get_free_size(MALLOC_CAP_DMA); + +// } +size_t malloc_int = 0; +size_t malloc_spiram =0; +size_t malloc_dma = 0; +void memtrace_print_delta(const char * msg, const char * tag, const char * function){ + size_t malloc_int_delta = heap_caps_get_free_size(MALLOC_CAP_INTERNAL) - malloc_int; + size_t malloc_spiram_delta = heap_caps_get_free_size(MALLOC_CAP_SPIRAM) - malloc_spiram; + size_t malloc_dma_delta = heap_caps_get_free_size(MALLOC_CAP_DMA) - malloc_dma; + ESP_LOGW(TAG, "Heap internal:%zu (min:%zu)(chg:%d)/external:%zu (min:%zu)(chg:%d)/dma:%zu (min:%zu)(chg:%d) : %s%s%s%s%s", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), + malloc_int_delta, + heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + malloc_spiram_delta, + heap_caps_get_free_size(MALLOC_CAP_DMA), + heap_caps_get_minimum_free_size(MALLOC_CAP_DMA), + malloc_dma_delta, + STR_OR_BLANK(tag), + tag?" ":"", + STR_OR_BLANK(function), + function?" ":"", + STR_OR_BLANK(msg) + ); + malloc_int = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); + malloc_spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); + malloc_dma = heap_caps_get_free_size(MALLOC_CAP_DMA); +} diff --git a/components/tools/trace.h b/components/tools/trace.h index 5550c427..3379a44f 100644 --- a/components/tools/trace.h +++ b/components/tools/trace.h @@ -21,6 +21,9 @@ #ifndef STR_OR_ALT #define STR_OR_ALT(str,alt) (str?str:alt) #endif +#ifndef STR_OR_BLANK +#define STR_OR_BLANK(p) p == NULL ? "" : p +#endif #define ENUM_TO_STRING(g) \ case g: \ return STR(g); \ @@ -28,6 +31,14 @@ extern const char unknown_string_placeholder[]; extern const char * str_or_unknown(const char * str); extern const char * str_or_null(const char * str); +void memtrace_print_delta(const char * msg, const char * TAG, const char * function); +#ifdef ENABLE_MEMTRACE +#define MEMTRACE_PRINT_DELTA() memtrace_print_delta(NULL,TAG,__FUNCTION__); +#define MEMTRACE_PRINT_DELTA_MESSAGE(x) memtrace_print_delta(x,TAG,__FUNCTION__); +#else +#define MEMTRACE_PRINT_DELTA() +#define MEMTRACE_PRINT_DELTA_MESSAGE(x) ESP_LOGD(TAG,"%s",x); +#endif #ifndef FREE_AND_NULL #define FREE_AND_NULL(x) if(x) { free(x); x=NULL; } diff --git a/components/wifi-manager/CMakeLists.txt b/components/wifi-manager/CMakeLists.txt index c15fc2cd..b6e1a80a 100644 --- a/components/wifi-manager/CMakeLists.txt +++ b/components/wifi-manager/CMakeLists.txt @@ -1,9 +1,9 @@ set( WEBPACK_DIR webapp/webpack/dist ) -idf_component_register( SRC_DIRS . webapp - INCLUDE_DIRS . webapp ${IDF_PATH}/components/esp_http_server/src ${IDF_PATH}/components/esp_http_server/src/port/esp32 ${IDF_PATH}/components/esp_http_server/src/util ${IDF_PATH}/components/esp_http_server/src/ +idf_component_register( SRC_DIRS . webapp UML-State-Machine-in-C/src + INCLUDE_DIRS . webapp UML-State-Machine-in-C/src ${IDF_PATH}/components/esp_http_server/src ${IDF_PATH}/components/esp_http_server/src/port/esp32 ${IDF_PATH}/components/esp_http_server/src/util ${IDF_PATH}/components/esp_http_server/src/ REQUIRES squeezelite-ota json mdns - PRIV_REQUIRES tools cpp-stateless services platform_config esp_common json newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant platform_console esp_http_server console driver_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 ) diff --git a/components/wifi-manager/README.md b/components/wifi-manager/README.md deleted file mode 100644 index d1652c15..00000000 --- a/components/wifi-manager/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# What is esp32-wifi-manager? -*esp32-wifi-manager* is an esp32 program that enables easy management of wifi networks through a web application. - -*esp32-wifi-manager* is **lightweight** (8KB of task stack in total) and barely uses any CPU power through a completely event driven architecture. It's an all in one wifi scanner, http server & dns daemon living in the least amount of RAM possible. - -For real time constrained applications, *esp32-wifi-manager* can live entirely on PRO CPU, leaving the entire APP CPU untouched for your own needs. - -*esp32-wifi-manager* will automatically attempt to re-connect to a previously saved network on boot, and it will start its own wifi access point through which you can manage wifi networks if a saved network cannot be found and/or if the connection is lost. - -*esp32-wifi-manager* is an esp-idf project that compiles successfully with the esp-idf 3.2 release. You can simply copy the project and start adding your own code to it. - -# Demo -[![esp32-wifi-manager demo](http://img.youtube.com/vi/hxlZi15bym4/0.jpg)](http://www.youtube.com/watch?v=hxlZi15bym4) - -# Look and Feel -![esp32-wifi-manager on an mobile device](https://idyl.io/wp-content/uploads/2017/11/esp32-wifi-manager-password.png "esp32-wifi-manager") ![esp32-wifi-manager on an mobile device](https://idyl.io/wp-content/uploads/2017/11/esp32-wifi-manager-connected-to.png "esp32-wifi-manager") - -# Adding esp32-wifi-manager to your code -Ther are effectively three different ways you can embed esp32-wifi-manager with your code: -* Just forget about it and poll in your code for wifi connectivity status -* Use event callbacks -* Modify esp32-wifi-manager code directly to fit your needs - -**Event callbacks** are the cleanest way to use the wifi manager and that's the recommended way to do it. A typical use-case would be to get notified when wifi manager finally gets a connection an access point. In order to do this you can simply define a callback function: - -```c -void cb_connection_ok(void *pvParameter){ - ESP_LOGI(TAG, "I have a connection!"); -} -``` - -Then just register it by calling: - -```c -wifi_manager_set_callback(EVENT_STA_GOT_IP, &cb_connection_ok); -``` - -That's it! Now everytime the event is triggered it will call this function. - -# License -*esp32-wifi-manager* is MIT licensed. As such, it can be included in any project, commercial or not, as long as you retain original copyright. Please make sure to read the license file. diff --git a/components/wifi-manager/UML-State-Machine-in-C b/components/wifi-manager/UML-State-Machine-in-C new file mode 160000 index 00000000..96264241 --- /dev/null +++ b/components/wifi-manager/UML-State-Machine-in-C @@ -0,0 +1 @@ +Subproject commit 96264241ad7a247ee143acbf81005ba82802d1fa diff --git a/components/wifi-manager/_esp_httpd_main.c b/components/wifi-manager/_esp_httpd_main.c index 255f6d78..156879f1 100644 --- a/components/wifi-manager/_esp_httpd_main.c +++ b/components/wifi-manager/_esp_httpd_main.c @@ -24,6 +24,7 @@ #include <_esp_http_server.h> #include "esp_httpd_priv.h" #include "ctrl_sock.h" +#include "globdefs.h" static const char *TAG = "_httpd"; @@ -269,18 +270,18 @@ static void _httpd_thread(void *arg) 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)); + struct httpd_data *hd = malloc_init_external(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 *)); + hd->hd_calls = malloc_init_external(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)); + hd->hd_sd = malloc_init_external(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); @@ -288,7 +289,7 @@ static struct httpd_data *__httpd_create(const httpd_config_t *config) return NULL; } struct httpd_req_aux *ra = &hd->hd_req_aux; - ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr)); + ra->resp_hdrs = malloc_init_external(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); @@ -296,7 +297,7 @@ static struct httpd_data *__httpd_create(const httpd_config_t *config) free(hd); return NULL; } - hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t)); + hd->err_handler_fns = malloc_init_external(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); diff --git a/components/wifi-manager/dns_server.c b/components/wifi-manager/dns_server.c index f5ce722b..932970be 100644 --- a/components/wifi-manager/dns_server.c +++ b/components/wifi-manager/dns_server.c @@ -59,8 +59,8 @@ static const char TAG[] = "dns_server"; static TaskHandle_t task_dns_server = NULL; int socket_fd; -void dns_server_start() { - xTaskCreate(&dns_server, "dns_server", 3072, NULL, WIFI_MANAGER_TASK_PRIORITY-1, &task_dns_server); +void dns_server_start(esp_netif_t * netif) { + xTaskCreate(&dns_server, "dns_server", 3072, (void *)netif, WIFI_MANAGER_TASK_PRIORITY-1, &task_dns_server); } void dns_server_stop(){ @@ -75,7 +75,8 @@ void dns_server_stop(){ void dns_server(void *pvParameters) { struct sockaddr_in sa, ra; - + esp_err_t esp_err = ESP_OK; + esp_netif_t * netif = (esp_netif_t * )pvParameters; /* Set redirection DNS hijack to the access point IP */ ip4_addr_t ip_resolved; inet_pton(AF_INET, DEFAULT_AP_IP, &ip_resolved); @@ -90,10 +91,14 @@ void dns_server(void *pvParameters) { memset(&sa, 0, sizeof(struct sockaddr_in)); /* Bind to port 53 (typical DNS Server port) */ - tcpip_adapter_ip_info_t ip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); + esp_netif_ip_info_t ip_info; + esp_err = esp_netif_get_ip_info(netif,&ip_info); + if(esp_err!=ESP_OK) { + ESP_LOGE(TAG, "Failed to get adapter info for udp: %s", esp_err_to_name(esp_err)); + exit(1); + } ra.sin_family = AF_INET; - ra.sin_addr.s_addr = ip.ip.addr; + ra.sin_addr.s_addr = ip_info.ip.addr; ra.sin_port = htons(53); if (bind(socket_fd, (struct sockaddr *)&ra, sizeof(struct sockaddr_in)) == -1) { ESP_LOGE(TAG, "Failed to bind to 53/udp"); @@ -149,7 +154,7 @@ void dns_server(void *pvParameters) { for(char* c=domain; *c != '\0'; c++){ if(*c < ' ' || *c > 'z') *c = '.'; /* technically we should test if the first two bits are 00 (e.g. if( (*c & 0xC0) == 0x00) *c = '.') but this makes the code a lot more readable */ } - ESP_LOGI(TAG, "Replying to DNS request for %s from %s", domain, ip_address); + ESP_LOGD(TAG, "Replying to DNS request for %s from %s", domain, ip_address); /* create DNS answer at the end of the query*/ diff --git a/components/wifi-manager/dns_server.h b/components/wifi-manager/dns_server.h index 81367169..63246f08 100644 --- a/components/wifi-manager/dns_server.h +++ b/components/wifi-manager/dns_server.h @@ -35,6 +35,7 @@ Contains the freeRTOS task for the DNS server that processes the requests. #include #include #include "squeezelite-ota.h" +#include "esp_netif.h" #ifdef __cplusplus @@ -128,7 +129,7 @@ typedef struct __attribute__((__packed__)) dns_answer_t{ }dns_answer_t; void dns_server(void *pvParameters); -void dns_server_start(); +void dns_server_start(esp_netif_t * netif); void dns_server_stop(); diff --git a/components/wifi-manager/http_server_handlers.c b/components/wifi-manager/http_server_handlers.c index 212b23e6..11d51867 100644 --- a/components/wifi-manager/http_server_handlers.c +++ b/components/wifi-manager/http_server_handlers.c @@ -1,34 +1,5 @@ /* -Copyright (c) 2017-2019 Tony Pottier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -@file http_server.c -@author Tony Pottier -@brief Defines all functions necessary for the HTTP server to run. - -Contains the freeRTOS task for the HTTP listener and all necessary support -function to process requests, decode URLs, serve files, etc. etc. - -@note http_server task cannot run without the wifi_manager task! -@see https://idyl.io -@see https://github.com/tonyp7/esp32-wifi-manager +Copyright (c) 2017-2021 Sebastien L */ #include "http_server_handlers.h" @@ -57,7 +28,8 @@ function to process requests, decode URLs, serve files, etc. etc. #include "webapp/webpack.h" #include "network_wifi.h" #include "network_status.h" - +#include "globdefs.h" + #define HTTP_STACK_SIZE (5*1024) const char str_na[]="N/A"; #define STR_OR_NA(s) s?s:str_na @@ -104,7 +76,7 @@ char * alloc_get_http_header(httpd_req_t * req, const char * key){ * extra byte for null termination */ buf_len = httpd_req_get_hdr_value_len(req, key) + 1; if (buf_len > 1) { - buf = malloc(buf_len); + buf = malloc_init_external(buf_len); /* Copy null terminated value string into buffer */ if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) { ESP_LOGD_LOC(TAG, "Found header => %s: %s",key, buf); @@ -120,16 +92,14 @@ char * http_alloc_get_socket_address(httpd_req_t *req, u8_t local, in_port_t * p union sockaddr_aligned addr; len = sizeof(addr); ip_addr_t * ip_addr=NULL; - char * ipstr = malloc(INET6_ADDRSTRLEN); - memset(ipstr,0x0,INET6_ADDRSTRLEN); - + char * ipstr = malloc_init_external(INET6_ADDRSTRLEN); typedef int (*getaddrname_fn_t)(int s, struct sockaddr *name, socklen_t *namelen); getaddrname_fn_t get_addr = NULL; int s = httpd_req_to_sockfd(req); if(s == -1) { free(ipstr); - return strdup("httpd_req_to_sockfd error"); + return strdup_psram("httpd_req_to_sockfd error"); } ESP_LOGV_LOC(TAG,"httpd socket descriptor: %u", s); @@ -192,7 +162,7 @@ bool is_captive_portal_host_name(httpd_req_t *req){ ESP_LOGD_LOC(TAG, "Soft AP Host name is %s",ap_host_name); } - ap_ip_address = malloc(IP4ADDR_STRLEN_MAX); + ap_ip_address = malloc_init_external(IP4ADDR_STRLEN_MAX); memset(ap_ip_address, 0x00, IP4ADDR_STRLEN_MAX); if(ap_ip_address){ ESP_LOGD_LOC(TAG, "Converting soft ip address to string"); @@ -233,8 +203,7 @@ session_context_t* get_session_context(httpd_req_t *req){ bool newConnection=false; if (! req->sess_ctx) { ESP_LOGD(TAG,"New connection context. Allocating session buffer"); - req->sess_ctx = malloc(sizeof(session_context_t)); - memset(req->sess_ctx,0x00,sizeof(session_context_t)); + req->sess_ctx = malloc_init_external(sizeof(session_context_t)); req->free_ctx = free_ctx_func; newConnection = true; // get the remote IP address only once per session @@ -256,11 +225,13 @@ bool is_user_authenticated(httpd_req_t *req){ return true; } - ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)", - heap_caps_get_free_size(MALLOC_CAP_INTERNAL), - heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), - heap_caps_get_free_size(MALLOC_CAP_SPIRAM), - heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); + ESP_LOGD(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), + heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_free_size(MALLOC_CAP_DMA), + heap_caps_get_minimum_free_size(MALLOC_CAP_DMA)); // todo: ask for user to authenticate return false; @@ -439,7 +410,7 @@ esp_err_t ap_scan_handler(httpd_req_t *req){ // todo: redirect to login page // return ESP_OK; } - network_manager_async_scan(); + network_async_scan(); esp_err_t err = set_content_type_from_req(req); if(err == ESP_OK){ httpd_resp_send(req, (const char *)empty, HTTPD_RESP_USE_STRLEN); @@ -529,9 +500,9 @@ esp_err_t ap_get_handler(httpd_req_t *req){ } /* if we can get the mutex, write the last version of the AP list */ esp_err_t err = set_content_type_from_req(req); - if( err == ESP_OK && wifi_manager_lock_json_buffer(( TickType_t ) 200/portTICK_PERIOD_MS)){ - char *buff = wifi_manager_alloc_get_ap_list_json(); - wifi_manager_unlock_json_buffer(); + if( err == ESP_OK && network_status_lock_json_buffer(( TickType_t ) 200/portTICK_PERIOD_MS)){ + char *buff = network_status_alloc_get_ap_list_json(); + network_status_unlock_json_buffer(); if(buff!=NULL){ httpd_resp_send(req, (const char *)buff, HTTPD_RESP_USE_STRLEN); free(buff); @@ -683,7 +654,7 @@ esp_err_t config_post_handler(httpd_req_t *req){ else { // we're getting a request to do an OTA from that URL ESP_LOGW_LOC(TAG, "Found OTA request!"); - otaURL=strdup(val); + otaURL=strdup_psram(val); bOTA=true; } } @@ -730,7 +701,7 @@ esp_err_t config_post_handler(httpd_req_t *req){ ESP_LOGW_LOC(TAG, "Restarting system to process OTA for url %s",otaURL); } - network_manager_reboot_ota(otaURL); + network_reboot_ota(otaURL); free(otaURL); } return err; @@ -766,15 +737,15 @@ esp_err_t connect_post_handler(httpd_req_t *req){ cJSON * ssid_object = cJSON_GetObjectItem(root, "ssid"); if(ssid_object !=NULL){ - ssid = strdup(ssid_object->valuestring); + ssid = strdup_psram(ssid_object->valuestring); } cJSON * password_object = cJSON_GetObjectItem(root, "pwd"); if(password_object !=NULL){ - password = strdup(password_object->valuestring); + password = strdup_psram(password_object->valuestring); } cJSON * host_name_object = cJSON_GetObjectItem(root, "host_name"); if(host_name_object !=NULL){ - host_name = strdup(host_name_object->valuestring); + host_name = strdup_psram(host_name_object->valuestring); } cJSON_Delete(root); @@ -785,14 +756,7 @@ esp_err_t connect_post_handler(httpd_req_t *req){ } if(ssid !=NULL && strlen(ssid) <= MAX_SSID_SIZE && strlen(password) <= MAX_PASSWORD_SIZE ){ - wifi_config_t* config = wifi_manager_get_wifi_sta_config(); - memset(config, 0x00, sizeof(wifi_config_t)); - strlcpy((char *)config->sta.ssid, ssid, sizeof(config->sta.ssid)+1); - if(password){ - strlcpy((char *)config->sta.password, password, sizeof(config->sta.password)+1); - } - ESP_LOGD_LOC(TAG, "http_server_netconn_serve: network_manager_async_scan() call, with ssid: %s, password: %s", config->sta.ssid, config->sta.password); - network_manager_async_scan(); + network_async_connect(ssid, password); httpd_resp_send(req, (const char *)success, strlen(success)); } else { @@ -816,7 +780,7 @@ esp_err_t connect_delete_handler(httpd_req_t *req){ return err; } httpd_resp_send(req, (const char *)success, strlen(success)); - network_manager_async_disconnect(); + network_async_delete(); return ESP_OK; } @@ -833,7 +797,7 @@ esp_err_t reboot_ota_post_handler(httpd_req_t *req){ } httpd_resp_send(req, (const char *)success, strlen(success)); - network_manager_async_reboot(OTA); + network_async_reboot(OTA); return ESP_OK; } esp_err_t reboot_post_handler(httpd_req_t *req){ @@ -848,7 +812,7 @@ esp_err_t reboot_post_handler(httpd_req_t *req){ return err; } httpd_resp_send(req, (const char *)success, strlen(success)); - network_manager_async_reboot(RESTART); + network_async_reboot(RESTART); return ESP_OK; } esp_err_t recovery_post_handler(httpd_req_t *req){ @@ -863,7 +827,7 @@ esp_err_t recovery_post_handler(httpd_req_t *req){ return err; } httpd_resp_send(req, (const char *)success, strlen(success)); - network_manager_async_reboot(RECOVERY); + network_async_reboot(RECOVERY); return ESP_OK; } @@ -881,7 +845,7 @@ esp_err_t flash_post_handler(httpd_req_t *req){ if(err != ESP_OK){ return err; } - char * binary_buffer = malloc(req->content_len); + char * binary_buffer = malloc_init_external(req->content_len); if(binary_buffer == NULL){ ESP_LOGE(TAG, "File too large : %d bytes", req->content_len); /* Respond with 400 Bad Request */ @@ -981,11 +945,11 @@ esp_err_t process_redirect(httpd_req_t *req, const char * status){ remote_ip = http_alloc_get_socket_address(req,0, &port); size_t buf_size = strlen(redirect_payload1) +strlen(redirect_payload2) + strlen(redirect_payload3) +2*(strlen(location_prefix)+strlen(ap_ip_address))+1; - char * redirect=malloc(buf_size); + char * redirect=malloc_init_external(buf_size); if(strcasestr(status,"302")){ size_t url_buf_size = strlen(location_prefix) + strlen(ap_ip_address)+1; - redirect_url = malloc(url_buf_size); + redirect_url = malloc_init_external(url_buf_size); memset(redirect_url,0x00,url_buf_size); snprintf(redirect_url, buf_size,"%s%s/",location_prefix, ap_ip_address); ESP_LOGW_LOC(TAG, "Redirecting host [%s] to %s (from uri %s)",remote_ip, redirect_url,req->uri); @@ -1034,9 +998,9 @@ esp_err_t redirect_processor(httpd_req_t *req, httpd_err_code_t error){ remote_ip = http_alloc_get_socket_address(req,0, &port); ESP_LOGW_LOC(TAG, "%s requested invalid URL: [%s]",remote_ip, req->uri); - if(wifi_manager_lock_sta_ip_string(portMAX_DELAY)){ - sta_ip_address = strdup(wifi_manager_get_sta_ip_string()); - wifi_manager_unlock_sta_ip_string(); + if(network_status_lock_sta_ip_string(portMAX_DELAY)){ + sta_ip_address = strdup_psram(network_status_get_sta_ip_string()); + network_status_unlock_sta_ip_string(); } else { ESP_LOGE(TAG,"Unable to obtain local IP address from WiFi Manager."); @@ -1149,9 +1113,9 @@ esp_err_t status_get_handler(httpd_req_t *req){ return err; } - if(wifi_manager_lock_json_buffer(( TickType_t ) 200/portTICK_PERIOD_MS)) { - char *buff = wifi_manager_alloc_get_ip_info_json(); - wifi_manager_unlock_json_buffer(); + if(network_status_lock_json_buffer(( TickType_t ) 200/portTICK_PERIOD_MS)) { + char *buff = network_status_alloc_get_ip_info_json(); + network_status_unlock_json_buffer(); if(buff) { httpd_resp_send(req, (const char *)buff, strlen(buff)); free(buff); @@ -1164,7 +1128,7 @@ esp_err_t status_get_handler(httpd_req_t *req){ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR , "Error retrieving status object"); } // update status for next status call - network_manager_async_update_status(); + network_async_update_status(); return ESP_OK; } diff --git a/components/wifi-manager/http_server_handlers.h b/components/wifi-manager/http_server_handlers.h index 53797b4c..85151370 100644 --- a/components/wifi-manager/http_server_handlers.h +++ b/components/wifi-manager/http_server_handlers.h @@ -1,34 +1,5 @@ /* -Copyright (c) 2017-2019 Tony Pottier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -@file http_server.h -@author Tony Pottier -@brief Defines all functions necessary for the HTTP server to run. - -Contains the freeRTOS task for the HTTP listener and all necessary support -function to process requests, decode URLs, serve files, etc. etc. - -@note http_server task cannot run without the wifi_manager task! -@see https://idyl.io -@see https://github.com/tonyp7/esp32-wifi-manager +Copyright (c) 2017-2021 Sebastien L */ #ifndef HTTP_SERVER_H_INCLUDED diff --git a/components/wifi-manager/network_driver_DM9051.c b/components/wifi-manager/network_driver_DM9051.c index 74088c33..b70b6df8 100644 --- a/components/wifi-manager/network_driver_DM9051.c +++ b/components/wifi-manager/network_driver_DM9051.c @@ -1,38 +1,40 @@ #include "esp_eth.h" #include "network_ethernet.h" - -static esp_eth_mac_t* mac_new(spi_device_handle_t spi_handle, eth_config_t* ethernet_config) { +static EXT_RAM_ATTR network_ethernet_driver_t DM9051; +static esp_err_t start(spi_device_handle_t spi_handle, eth_config_t* ethernet_config) { #ifdef CONFIG_ETH_SPI_ETHERNET_DM9051 + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); eth_dm9051_config_t eth_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle); // we assume that isr has been installed already eth_config.int_gpio_num = ethernet_config->intr; - return esp_eth_mac_new_dm9051(ð_config, &mac_config); -#else - return NULL; -#endif -} -static esp_eth_phy_t* phy_new(eth_config_t* ethernet_config) { -#ifdef CONFIG_ETH_SPI_ETHERNET_DM9051 - eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); - phy_config.phy_addr = 1; + phy_config.phy_addr = -1; phy_config.reset_gpio_num = ethernet_config->rst; - return esp_eth_phy_new_dm9051(&phy_config); + + esp_eth_mac_t* mac = esp_eth_mac_new_dm9051(ð_config, &mac_config); + esp_eth_phy_t* phy = esp_eth_phy_new_dm9051(&phy_config); + esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); + return esp_eth_driver_install(&config, &DM9051.handle); #else - return NULL; + return ESP_ERR_NOT_SUPPORTED; #endif } -static void init_config(eth_config_t* ethernet_config) { -} -static network_ethernet_driver_t DM9051 = { - .mac_new = mac_new, - .phy_new = phy_new, - .init_config = init_config, - .valid = true, -}; +static void init_config(eth_config_t* ethernet_config) { + DM9051.start = start; + DM9051.rmii = true; + DM9051.spi = false; + DM9051.valid = true; +} + network_ethernet_driver_t* DM9051_Detect(char* Driver) { if (!strcasestr(Driver, "DM9051")) return NULL; +#ifdef CONFIG_ETH_SPI_ETHERNET_DM9051 + DM9051.valid = true; +#else + DM9051.valid = false; +#endif + DM9051.init_config = init_config; return &DM9051; } diff --git a/components/wifi-manager/network_driver_LAN8720.c b/components/wifi-manager/network_driver_LAN8720.c index f77c29e2..7c164e74 100644 --- a/components/wifi-manager/network_driver_LAN8720.c +++ b/components/wifi-manager/network_driver_LAN8720.c @@ -1,47 +1,40 @@ #include "esp_eth.h" #include "network_ethernet.h" -static esp_eth_mac_t* mac_new(spi_device_handle_t spi_handle, eth_config_t* ethernet_config) { +static EXT_RAM_ATTR network_ethernet_driver_t LAN8720; +static esp_err_t start(spi_device_handle_t spi_handle, eth_config_t* ethernet_config) { #ifdef CONFIG_ETH_PHY_INTERFACE_RMII + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac_config.smi_mdc_gpio_num = ethernet_config->mdc; mac_config.smi_mdio_gpio_num = ethernet_config->mdio; - mac_config.sw_reset_timeout_ms = 400; - return esp_eth_mac_new_esp32(&mac_config); -#else - return NULL; -#endif -} -static esp_eth_phy_t* phy_new(eth_config_t* ethernet_config) { -#ifdef CONFIG_ETH_PHY_INTERFACE_RMII - eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); phy_config.phy_addr = 1; phy_config.reset_gpio_num = ethernet_config->rst; - return esp_eth_phy_new_lan8720(&phy_config); + + esp_eth_mac_t* mac = esp_eth_mac_new_esp32(&mac_config); + esp_eth_phy_t* phy = esp_eth_phy_new_lan8720(&phy_config); + esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); + return esp_eth_driver_install(&config, &LAN8720.handle); #else - return NULL; -#endif + return ESP_ERR_NOT_SUPPORTED; +#endif } + static void init_config(eth_config_t* ethernet_config) { -#ifdef CONFIG_ETH_PHY_INTERFACE_RMII -#else - return NULL; -#endif + LAN8720.start = start; + LAN8720.rmii = true; + LAN8720.spi = false; } -static network_ethernet_driver_t LAN8720 = { - - .mac_new = mac_new, - .phy_new = phy_new, - .init_config = init_config, -#ifdef CONFIG_ETH_PHY_INTERFACE_RMII - .valid = true, -#else - .valid = false, -#endif -}; network_ethernet_driver_t* LAN8720_Detect(char* Driver) { if (!strcasestr(Driver, "LAN8720")) return NULL; +#ifdef CONFIG_ETH_PHY_INTERFACE_RMII + LAN8720.valid = true; +#else + LAN8720.valid = false; +#endif + LAN8720.init_config = init_config; return &LAN8720; } diff --git a/components/wifi-manager/network_driver_W5500.c b/components/wifi-manager/network_driver_W5500.c index 39ae65dd..c4b0873c 100644 --- a/components/wifi-manager/network_driver_W5500.c +++ b/components/wifi-manager/network_driver_W5500.c @@ -1,43 +1,55 @@ #include "esp_eth.h" +#include "globdefs.h" #include "network_ethernet.h" +static EXT_RAM_ATTR network_ethernet_driver_t W5500; +static EXT_RAM_ATTR spi_device_interface_config_t devcfg; +static EXT_RAM_ATTR esp_netif_config_t cfg_spi; +static EXT_RAM_ATTR esp_netif_inherent_config_t esp_netif_config; -static esp_eth_mac_t* mac_new(spi_device_handle_t spi_handle, eth_config_t* ethernet_config) { +static esp_err_t start(spi_device_handle_t spi_handle, eth_config_t* ethernet_config) { #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 eth_w5500_config_t eth_config = ETH_W5500_DEFAULT_CONFIG(spi_handle); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - // we assume that isr has been installed already - eth_config.int_gpio_num = ethernet_config->intr; - return esp_eth_mac_new_w5500(ð_config, &mac_config); -#else - return NULL; -#endif -} -static esp_eth_phy_t* phy_new(eth_config_t* ethernet_config) { -#ifdef CONFIG_ETH_SPI_ETHERNET_W5500 eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); - phy_config.phy_addr = 1; + + eth_config.int_gpio_num = ethernet_config->intr; + phy_config.phy_addr = -1; // let the system automatically find out the phy address phy_config.reset_gpio_num = ethernet_config->rst; - return esp_eth_phy_new_w5500(&phy_config); + + esp_eth_mac_t* mac = esp_eth_mac_new_w5500(ð_config, &mac_config); + esp_eth_phy_t* phy = esp_eth_phy_new_w5500(&phy_config); + esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); + return esp_eth_driver_install(&config, &W5500.handle); #else - return NULL; + return ESP_ERR_NOT_SUPPORTED; #endif } - static void init_config(eth_config_t* ethernet_config) { -} + esp_netif_inherent_config_t loc_esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); + devcfg.command_bits = 16; // Actually it's the address phase in W5500 SPI frame + devcfg.address_bits = 8; // Actually it's the control phase in W5500 SPI frame + devcfg.mode = 0; + devcfg.clock_speed_hz = ethernet_config->speed > 0 ? ethernet_config->speed : SPI_MASTER_FREQ_20M; // default speed + devcfg.queue_size = 20; + devcfg.spics_io_num = ethernet_config->cs; + memcpy(&esp_netif_config, &loc_esp_netif_config, sizeof(loc_esp_netif_config)); + cfg_spi.base = &esp_netif_config, + cfg_spi.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH; + W5500.cfg_netif = &cfg_spi; + W5500.devcfg = &devcfg; + W5500.start = start; + W5500.spi = true; + W5500.rmii = false; -static network_ethernet_driver_t W5500 = { - .mac_new = mac_new, - .phy_new = phy_new, - .init_config = init_config, -#ifdef CONFIG_ETH_SPI_ETHERNET_W5500 - .valid = true, -#else - .valid = false, -#endif -}; +} network_ethernet_driver_t* W5500_Detect(char* Driver, network_ethernet_driver_t* Device) { if (!strcasestr(Driver, "W5500")) return NULL; + W5500.init_config = init_config; +#ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + W5500.valid = true; +#else + W5500.valid = false; +#endif return &W5500; } diff --git a/components/wifi-manager/network_ethernet.c b/components/wifi-manager/network_ethernet.c index 07cfcbfa..4e572035 100644 --- a/components/wifi-manager/network_ethernet.c +++ b/components/wifi-manager/network_ethernet.c @@ -1,3 +1,6 @@ +#ifdef NETWORK_ETHERNET_LOG_LEVEL +#define LOG_LOCAL_LEVEL NETWORK_ETHERNET_LOG_LEVEL +#endif #include "network_ethernet.h" #include "freertos/timers.h" #include "globdefs.h" @@ -5,14 +8,13 @@ #include "network_status.h" #include "platform_config.h" #include "trace.h" -#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#include "accessors.h" #include "esp_log.h" //#include "dnserver.h" static char TAG[] = "network_ethernet"; TimerHandle_t ETH_timer; -esp_eth_handle_t eth_handle = NULL; esp_netif_t* eth_netif = NULL; EventGroupHandle_t ethernet_event_group; const int LINK_UP_BIT = BIT0; @@ -22,7 +24,6 @@ static network_ethernet_driver_t* network_driver = NULL; extern network_ethernet_detect_func_t DM9051_Detect, W5500_Detect, LAN8720_Detect; static network_ethernet_detect_func_t* drivers[] = {DM9051_Detect, W5500_Detect, LAN8720_Detect, NULL}; #define ETH_TIMEOUT_MS (30 * 1000) -static void network_manager_ethernet_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); /**************************************************************************************** * @@ -67,7 +68,7 @@ bool network_ethernet_is_up() { return (xEventGroupGetBits(ethernet_event_group) & LINK_UP_BIT)!=0; } bool network_ethernet_enabled() { - return eth_handle != NULL; + return network_driver !=NULL && network_driver->handle != NULL; } bool network_ethernet_wait_for_link(uint16_t max_wait_ms){ if(!network_ethernet_enabled()) return false; @@ -89,75 +90,33 @@ bool network_ethernet_wait_for_link(uint16_t max_wait_ms){ static void ETH_Timeout(void* timer_id); void destroy_network_ethernet() { } -static void set_host_name() { - ESP_LOGE(TAG, "TODO: Find a way to set the host name here!"); - // esp_err_t err; - // 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 { - // if (!network_ethernet_enabled()) { - // ESP_LOGE(TAG, "Cannot set name on a disabled interface"); - // } else { - // ESP_LOGD(TAG, "Setting host name to : %s", host_name); - // if ((err = esp_netif_set_hostname(eth_handle, host_name)) != ESP_OK) { - // ESP_LOGE(TAG, "Unable to set host name. Error: %s", esp_err_to_name(err)); - // } - // ESP_LOGD(TAG, "Done setting host name to : %s", host_name); - // } - // FREE_AND_NULL(host_name); - // } -} static void network_ethernet_print_config(const eth_config_t* eth_config) { - // #if defined(CONFIG_ETH_PHY_INTERFACE_RMII) - // if(eth_config->) - // ESP_LOGI(TAG, - // "Model: %s, rst=%d, mdc=%d, mdio=%d, host=%d, cs=%d, mosi=%d, miso=%d, intr=%d, clk=%d, speed=%d, tx_en=%d, tx0=%d, tx1=%d, rx0=%d, rx1=%d, crs_dv=%d", - // eth_config->model, eth_config->rst, eth_config->mdc, eth_config->mdio, eth_config->host, eth_config->cs, - // eth_config->mosi, eth_config->miso, eth_config->intr, eth_config->clk, eth_config->speed, - // eth_config->tx_en, eth_config->tx0, eth_config->tx1, eth_config->rx0, eth_config->rx1, eth_config->crs_dv); - // #else - // ESP_LOGI(TAG, "Model: %s, rst=%d, mdc=%d, mdio=%d, host=%d, cs=%d, mosi=%d, miso=%d, intr=%d, clk=%d, speed=%d ", - // eth_config->model, eth_config->rst, eth_config->mdc, eth_config->mdio, eth_config->host, eth_config->cs, - // eth_config->mosi, eth_config->miso, eth_config->intr, eth_config->clk, eth_config->speed); - // : - // #endif + ESP_LOGI(TAG,"Ethernet config: \n model: %s, valid: %s, type: %s, mdc:%d, mdio:%d, rst:%d, mosi:%d, miso:%d, intr:%d, cs:%d, speed:%d, clk:%d, host:%s(%d)", + eth_config->model, eth_config->valid?"YES":"NO",eth_config->spi?"SPI":"RMII", eth_config->mdc, eth_config->mdio, eth_config->rst, eth_config->mosi, eth_config->miso, eth_config->intr, eth_config->cs, eth_config->speed, eth_config->clk, eth_config->host==0?"SPI1":eth_config->host==1?"SPI2":eth_config->host==2?"SPI3":"",eth_config->host); } + void init_network_ethernet() { esp_err_t err = ESP_OK; - esp_eth_mac_t* mac; - esp_eth_phy_t* phy; eth_config_t eth; + ESP_LOGI(TAG, "Attempting to initialize Ethernet"); config_eth_init(ð); - ESP_LOGD(TAG, "Attempting to initialize Ethernet"); - // quick check if we have a valid ethernet configuration - if (!eth.valid) { - ESP_LOGI(TAG, "No ethernet"); - return; - } - network_driver = network_ethernet_driver_autodetect(eth.model); - if (!network_driver) { - messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Invalid ethernet Ethernet chip %s", eth.model); - return; - } - if (!network_driver->valid) { - messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Code not compiled for Ethernet chip %s", eth.model); + if(!eth.valid){ + ESP_LOGI(TAG,"No Ethernet configuration, or configuration invalid"); return; } + network_driver->init_config(ð); network_ethernet_print_config(ð); - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); - eth_netif = esp_netif_new(&cfg); + + eth_netif = esp_netif_new(network_driver->cfg_netif); esp_eth_set_default_handlers(eth_netif); esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL); - esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL); ethernet_event_group = xEventGroupCreate(); xEventGroupClearBits(ethernet_event_group, LINK_UP_BIT); spi_device_handle_t spi_handle = NULL; - if (network_driver->eth_config.spi) { + if (network_driver->spi) { spi_host_device_t host = SPI3_HOST; if (eth.host != -1) { @@ -169,12 +128,18 @@ void init_network_ethernet() { .quadwp_io_num = -1, .quadhd_io_num = -1, }; - + // can't use SPI0 - if (eth.host == 1) + if (eth.host == 0) + { + ESP_LOGW(TAG,"Cannot use SPI1 host. Defaulting to SPI2"); host = SPI2_HOST; - ESP_LOGI(TAG, "Initializing SPI bus on host %d with mosi %d and miso %d", host, eth.mosi, eth.miso); - err = spi_bus_initialize(host, &buscfg, 1); + } + else { + host = eth.host; + } + ESP_LOGI(TAG, "Initializing SPI bus on host %d (SPI%d) with mosi %d and miso %d", host,host+1, eth.mosi, eth.miso); + err = spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO); if (err != ESP_OK) { ESP_LOGE(TAG, "SPI bus init failed : %s", esp_err_to_name(err)); } @@ -184,38 +149,39 @@ void init_network_ethernet() { host = spi_system_host; } if (err == ESP_OK) { - spi_device_interface_config_t devcfg = { - .command_bits = 1, - .address_bits = 7, - .mode = 0, - .clock_speed_hz = eth.speed, - .spics_io_num = eth.cs, - .queue_size = 20}; - ESP_LOGI(TAG, "Adding ethernet SPI on host %d with mosi %d and miso %d", host, eth.mosi, eth.miso); - err = spi_bus_add_device(host, &devcfg, &spi_handle); + ESP_LOGI(TAG, "Adding ethernet SPI on host %d (SPI%d) with mosi %d and miso %d", host,host+1, eth.mosi, eth.miso); + err = spi_bus_add_device(host, network_driver->devcfg, &spi_handle); } if (err != ESP_OK) { ESP_LOGE(TAG, "SPI host failed : %s", esp_err_to_name(err)); } } if (err == ESP_OK) { - ESP_LOGD(TAG, "Setting up ethernet driver"); - mac = network_driver->mac_new(spi_handle, ð); - phy = network_driver->phy_new(ð); - esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); - err = esp_eth_driver_install(&config, ð_handle); + err = network_driver->start(spi_handle,ð); } + if(err == ESP_OK){ + uint8_t mac_address[6]; + esp_read_mac(mac_address,ESP_MAC_ETH); + char * mac_string=network_manager_alloc_get_mac_string(mac_address); + ESP_LOGD(TAG,"Assigning mac address %s to ethernet interface", STR_OR_BLANK(mac_string)); + FREE_AND_NULL(mac_string); + esp_eth_ioctl(network_driver->handle, ETH_CMD_S_MAC_ADDR, mac_address); + } if (err == ESP_OK) { ESP_LOGD(TAG, "Attaching ethernet to network interface"); - err = esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)); + err = esp_netif_attach(eth_netif, esp_eth_new_netif_glue(network_driver->handle)); } if (err == ESP_OK) { ESP_LOGI(TAG, "Starting ethernet network"); - err = esp_eth_start(eth_handle); + err = esp_eth_start(network_driver->handle); + } if (err != ESP_OK) { messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Configuring Ethernet failed: %s", esp_err_to_name(err)); - eth_handle = NULL; + if(spi_handle) { + spi_bus_remove_device(spi_handle); + } + network_driver->handle = NULL; } } @@ -235,18 +201,17 @@ static void eth_event_handler(void* arg, esp_event_base_t event_base, int32_t ev esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); ESP_LOGI(TAG, ""); ESP_LOGI(TAG, "Ethernet Link Up, HW Addr %02x:%02x:%02x:%02x:%02x:%02x", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); - ESP_LOGD(TAG, "Sending EVENT_ETH_LINK_UP message to network manager"); - network_manager_async_link_up(); + network_async_link_up(); break; case ETHERNET_EVENT_DISCONNECTED: ESP_LOGI(TAG, "Ethernet Link Down"); xEventGroupClearBits(ethernet_event_group, LINK_UP_BIT); - ESP_LOGD(TAG, "Sending EVENT_ETH_LINK_DOWN message to network manager"); - network_manager_async_link_down(); + network_async_link_down(); break; case ETHERNET_EVENT_START: ESP_LOGI(TAG, "Ethernet Started. Setting host name"); - set_host_name(); + network_set_hostname(eth_netif); + network_async_success(); break; case ETHERNET_EVENT_STOP: ESP_LOGI(TAG, "Ethernet Stopped"); @@ -254,30 +219,10 @@ static void eth_event_handler(void* arg, esp_event_base_t event_base, int32_t ev default: break; } - } else if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) { - network_manager_ethernet_ip_event_handler(arg, event_base, event_id, event_data); - } + } } static void ETH_Timeout(void* timer_id) { - network_manager_async_fail(); + network_async_fail(); } -static void network_manager_ethernet_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { - if (event_base != IP_EVENT) - return; - ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data; - ip_event_got_ip_t* s = event_data; - tcpip_adapter_if_t index = s->if_index; - esp_netif_ip_info_t* ip_info = &s->ip_info; - - ESP_LOGI(TAG, "Got an IP address from Ethernet interface #%i. IP=" IPSTR ", Gateway=" IPSTR ", NetMask=" IPSTR ", %s", - index, - IP2STR(&ip_info->ip), - IP2STR(&ip_info->gw), - IP2STR(&ip_info->netmask), - s->ip_changed ? "Address was changed" : "Address unchanged"); - ip_event_got_ip_t* parm = malloc(sizeof(ip_event_got_ip_t)); - memcpy(parm, event_data, sizeof(ip_event_got_ip_t)); - network_manager_async_got_ip(parm); -} \ No newline at end of file diff --git a/components/wifi-manager/network_ethernet.h b/components/wifi-manager/network_ethernet.h index e4513a51..6eac02ef 100644 --- a/components/wifi-manager/network_ethernet.h +++ b/components/wifi-manager/network_ethernet.h @@ -3,19 +3,26 @@ #include "network_manager.h" #include "accessors.h" #include +#include "esp_netif_defaults.h" #ifdef __cplusplus extern "C" { #endif + typedef struct { bool valid; - eth_config_t eth_config; - esp_eth_mac_t* (*mac_new)(spi_device_handle_t spi_handle, eth_config_t * eth_config); - esp_eth_phy_t *(*phy_new)( eth_config_t* eth_config); + bool rmii; + bool spi; + esp_eth_handle_t handle; + esp_netif_config_t * cfg_netif; + spi_device_interface_config_t * devcfg; + // esp_eth_mac_t* (*mac_new)(spi_device_handle_t spi_handle, eth_config_t * eth_config); + // esp_eth_phy_t *(*phy_new)( eth_config_t* eth_config); + esp_err_t (*start)(spi_device_handle_t spi_handle,eth_config_t *ethernet_config); void (*init_config)(eth_config_t * eth_config); } network_ethernet_driver_t; typedef network_ethernet_driver_t* network_ethernet_detect_func_t(const char* Driver); - +network_ethernet_driver_t* network_ethernet_driver_autodetect(const char* Driver); void destroy_network_ethernet(); void init_network_ethernet(); bool network_ethernet_wait_for_link(uint16_t max_wait_ms); diff --git a/components/wifi-manager/network_manager.c b/components/wifi-manager/network_manager.c index d8681547..d701d4be 100644 --- a/components/wifi-manager/network_manager.c +++ b/components/wifi-manager/network_manager.c @@ -1,34 +1,10 @@ /* -Copyright (c) 2017-2019 Tony Pottier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -@file wifi_manager.c -@author Tony Pottier -@brief Defines all functions necessary for esp32 to connect to a wifi/scan wifis - -Contains the freeRTOS task and all necessary support - -@see https://idyl.io -@see https://github.com/tonyp7/esp32-wifi-manager +Copyright (c) 2017-2021 Sebastien L */ +#ifdef NETWORK_MANAGER_LOG_LEVEL +#define LOG_LOCAL_LEVEL NETWORK_MANAGER_LOG_LEVEL +#endif #include "network_manager.h" #include #include @@ -37,16 +13,15 @@ Contains the freeRTOS task and all necessary support #include "network_ethernet.h" #include "network_status.h" #include "network_wifi.h" -#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG + +#include "dns_server.h" #include "esp_log.h" -#include "platform_esp32.h" - - #include "esp_system.h" #include "freertos/FreeRTOS.h" +#include "platform_esp32.h" -#include "freertos/task.h" #include "esp_netif.h" +#include "freertos/task.h" #include "cJSON.h" #include "cmd_system.h" @@ -62,59 +37,696 @@ Contains the freeRTOS task and all necessary support #include "lwip/netdb.h" #include "mdns.h" #include "messaging.h" -#include "state_machine.h" #include "platform_config.h" #include "trace.h" #include "accessors.h" +#include "esp_err.h" #include "globdefs.h" #include "http_server_handlers.h" +#include "network_manager.h" + +QueueHandle_t network_queue; +BaseType_t network_task_handle; +static const char TAG[] = "network"; +static TaskHandle_t task_network_manager = NULL; +RTC_NOINIT_ATTR static bool s_wifi_prioritized = false; +extern esp_reset_reason_t xReason; +typedef struct network_callback { + network_status_reached_cb cb; + nm_state_t state; + int sub_state; + const char* from; + SLIST_ENTRY(network_callback) + next; //!< next callback +} network_callback_t; + +/** 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_start_stop_dhcp(esp_netif_t* netif, bool start) { + tcpip_adapter_dhcp_status_t status; + esp_err_t err = ESP_OK; + ESP_LOGD(TAG, "Checking if DHCP client for STA interface is running"); + if (!netif) { + ESP_LOGE(TAG, "Invalid adapter. Cannot start/stop dhcp. "); + return; + } + if((err=esp_netif_dhcpc_get_status(netif, &status))!=ESP_OK){ + ESP_LOGE(TAG,"Error retrieving dhcp status : %s", esp_err_to_name(err)); + return; + } + switch (status) + { + case ESP_NETIF_DHCP_STARTED: + if(start){ + ESP_LOGD(TAG, "DHCP client already started"); + } + else { + ESP_LOGI(TAG, "Stopping DHCP client"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcpc_stop(netif)); + } + break; + case ESP_NETIF_DHCP_STOPPED: + if(start){ + ESP_LOGI(TAG, "Starting DHCP client"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcpc_start(netif)); + } + else { + ESP_LOGI(TAG, "DHCP client already started"); + } + break; + case ESP_NETIF_DHCP_INIT: + if(start){ + ESP_LOGI(TAG, "Starting DHCP client"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcpc_start(netif)); + } + else { + ESP_LOGI(TAG, "Stopping DHCP client"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcpc_stop(netif)); + } + break; + + default: + ESP_LOGW(TAG,"Unknown DHCP status"); + break; + } +} +void network_start_stop_dhcps(esp_netif_t* netif, bool start) { + tcpip_adapter_dhcp_status_t status; + esp_err_t err = ESP_OK; + ESP_LOGD(TAG, "Checking if DHCP server is running"); + if (!netif) { + ESP_LOGE(TAG, "Invalid adapter. Cannot start/stop dhcp server. "); + return; + } + if((err=esp_netif_dhcps_get_status(netif, &status))!=ESP_OK){ + ESP_LOGE(TAG,"Error retrieving dhcp server status : %s", esp_err_to_name(err)); + return; + } + switch (status) + { + case ESP_NETIF_DHCP_STARTED: + if(start){ + ESP_LOGD(TAG, "DHCP server already started"); + } + else { + ESP_LOGI(TAG, "Stopping DHCP server"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(netif)); + } + break; + case ESP_NETIF_DHCP_STOPPED: + if(start){ + ESP_LOGI(TAG, "Starting DHCP server"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(netif)); + } + else { + ESP_LOGI(TAG, "DHCP server already stopped"); + } + break; + case ESP_NETIF_DHCP_INIT: + if(start){ + ESP_LOGI(TAG, "Starting DHCP server"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(netif)); + } + else { + ESP_LOGI(TAG, "Stopping DHCP server"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(netif)); + } + break; + + default: + ESP_LOGW(TAG,"Unknown DHCP status"); + break; + } +} +/********************************************************************************************* + * String conversion routines + */ -#ifndef STR_OR_BLANK -#define STR_OR_BLANK(p) p == NULL ? "" : p -#endif +#define ADD_ROOT(name,...) CASE_TO_STR(name); +#define ADD_ROOT_LEAF(name,...) CASE_TO_STR(name); +#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) { + ALL_NM_STATE + default: + break; + } + return "Unknown"; +} +static const char* wifi_state_to_string(mn_wifi_active_state_t state) { + switch (state) { + ALL_WIFI_STATE(,) + default: + break; + } + return "Unknown"; +} +static const char* eth_state_to_string(mn_eth_active_state_t state) { + switch (state) { + ALL_ETH_STATE(,) + default: + break; + } + return "Unknown"; +} +static const char* wifi_configuring_state_to_string(mn_wifi_configuring_state_t state) { + switch (state) { + ALL_WIFI_CONFIGURING_STATE(,) + default: + break; + } + 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) + { + case NETWORK_ETH_ACTIVE_STATE: + return eth_state_to_string(state->Id); + break; + case NETWORK_WIFI_ACTIVE_STATE: + return wifi_state_to_string(state->Id); + case NETWORK_WIFI_CONFIGURING_ACTIVE_STATE: + return wifi_configuring_state_to_string(state->Id); + default: + break; + } + return "*"; +} -//EventGroupHandle_t wifi_manager_event_group; -void (**cb_ptr_arr)(void*) = NULL; +static const char* event_to_string(network_event_t state) { + switch (state) { + ALL_NM_EVENTS -/* @brief tag used for ESP serial console messages */ -//static const char TAG[] = "network_manager"; + default: + break; + } + return "Unknown"; +} -/* @brief indicate that the ESP32 is currently connected. */ -const int WIFI_MANAGER_WIFI_CONNECTED_BIT = BIT0; -const int WIFI_MANAGER_AP_STA_CONNECTED_BIT = BIT1; -/* @brief Set automatically once the SoftAP is started */ -const int WIFI_MANAGER_AP_STARTED_BIT = BIT2; -/* @brief When set, means a client requested to connect to an access point.*/ -const int WIFI_MANAGER_REQUEST_STA_CONNECT_BIT = BIT3; -/* @brief This bit is set automatically as soon as a connection was lost */ -const int WIFI_MANAGER_STA_DISCONNECT_BIT = BIT4; -/* @brief When set, means the wifi manager attempts to restore a previously saved connection at startup. */ -const int WIFI_MANAGER_REQUEST_RESTORE_STA_BIT = BIT5; -/* @brief When set, means a client requested to disconnect from currently connected AP. */ -const int WIFI_MANAGER_REQUEST_WIFI_DISCONNECT_BIT = BIT6; -/* @brief When set, means a scan is in progress */ -const int WIFI_MANAGER_SCAN_BIT = BIT7; -/* @brief When set, means user requested for a disconnect */ -const int WIFI_MANAGER_REQUEST_DISCONNECT_BIT = BIT8; -/* @brief When set, means user requested connecting to a new network and it failed */ -const int WIFI_MANAGER_REQUEST_STA_CONNECT_FAILED_BIT = BIT9; +#undef ADD_EVENT +#undef ADD_FIRST_EVENT +#undef ADD_ROOT +#undef ADD_ROOT_LEAF +#undef ADD_LEAF -/* @brief task handle for the main wifi_manager task */ +typedef struct { + int parent_state; + int sub_state_last ; +} max_sub_states_t; +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 =-1} +}; +void network_start() { + + if(xReason == ESP_RST_POWERON ){ + ESP_LOGD(TAG, "Power on reset, initializing wifi priotitized to false"); + s_wifi_prioritized = false; + } + ESP_LOGD(TAG, " Creating message queue"); + network_queue = xQueueCreate(3, sizeof(queue_message)); + ESP_LOGD(TAG, " Creating network manager task"); + network_task_handle = xTaskCreate(&network_task, "network", 4096, NULL, WIFI_MANAGER_TASK_PRIORITY, &task_network_manager); +} + +static void event_logger(uint32_t state_machine, uint32_t state, uint32_t event) { + ESP_LOGI(TAG, "Handling network manager event state Id %d->[%s]", state, event_to_string(event)); +} +static const char * get_state_machine_result_string(state_machine_result_t result) { + switch(result) { + case EVENT_HANDLED: + return "EVENT_HANDLED"; + case EVENT_UN_HANDLED: + return "EVENT_UN_HANDLED"; + case TRIGGERED_TO_SELF: + return "TRIGGERED_TO_SELF"; + } + return "Unknown"; +} +static void result_logger(uint32_t state, state_machine_result_t result) { + ESP_LOGD(TAG, "Network Manager Result: %s, New State id: %d", get_state_machine_result_string(result) , state); +} + +static void network_task(void* pvParameters) { + queue_message msg; + BaseType_t xStatus; + initialize_network_handlers((state_machine_t*)&NM); + network_async(EN_START); + + /* main processing loop */ + for (;;) { + xStatus = xQueueReceive(network_queue, &msg, portMAX_DELAY); + + if (xStatus == pdPASS) { + // pass the event to the sync processor + NM.event_parameters = &msg; + NM.Machine.Event = msg.trigger; + if (dispatch_event(SM, 1, event_logger, result_logger) == EVENT_UN_HANDLED) { + network_manager_format_from_to_states(ESP_LOG_ERROR,"Unhandled Event",NULL,NM.Machine.State,msg.trigger,false,"network manager"); + } + } /* end of if status=pdPASS */ + } /* end of for loop */ + + vTaskDelete(NULL); +} + int get_max_substate(nm_state_t state){ + for(int i=0;state_max[i].parent_state!=-1;i++){ + if(state_max[i].parent_state == state){ + return state_max[i].sub_state_last; + } + } + return -1; + } +esp_err_t network_register_state_callback(nm_state_t state,int sub_state, const char* from, network_status_reached_cb cb) { + network_callback_t* item = NULL; + if (!cb) { + return ESP_ERR_INVALID_ARG; + } + item = calloc(1, sizeof(*item)); + if (item == NULL) { + return ESP_ERR_NO_MEM; + } + if(sub_state != -1 && sub_state>get_max_substate(state)){ + // sub state has to be valid + return ESP_ERR_INVALID_ARG; + } + 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); + } else { + network_callback_t* it; + while ((it = SLIST_NEXT(last, next)) != NULL) { + last = it; + } + SLIST_INSERT_AFTER(last, item, next); + } + return ESP_OK; +} +const state_t* get_root( const state_t* const state){ + if(!state) return NULL; + return state->Parent==NULL?state: get_root(state->Parent); +} +int get_root_id(const state_t * state){ + if(!state) return -1; + return state->Parent==NULL?state->Id: get_root_id(state->Parent); +} -void wifi_manager_set_callback(message_code_t message_code, void (*func_ptr)(void*)) { - if (cb_ptr_arr && message_code < MESSAGE_CODE_COUNT) { - cb_ptr_arr[message_code] = func_ptr; +static bool is_root_state(const state_t * state){ + return state->Parent==NULL; +} +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; + 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); + 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); + } } } +bool network_is_wifi_prioritized() { + eth_config_t eth_config; + config_eth_init(ð_config); + // char* prioritize = (char*)config_alloc_get_default(NVS_TYPE_STR, "prio_wifi", "N", 0); + // bool result = strcasecmp("N", prioritize); + bool result = s_wifi_prioritized; + if(result){ + result = network_wifi_get_known_count()>0 || !eth_config.valid; + ESP_LOGD(TAG,"Wifi is prioritized with %d known access points.%s %s",network_wifi_get_known_count(),eth_config.valid?" And a valid ethernet adapter":"",result?"Wifi prioritized":"Ethernet prioritized"); + } + return result; +} + +void network_prioritize_wifi(bool activate) { + if(s_wifi_prioritized == activate) return; + s_wifi_prioritized = activate; + ESP_LOGI(TAG,"Wifi is %s prioritized",activate?"":"not"); + // if (network_is_wifi_prioritized() != activate) { + // ESP_LOGW(TAG, "Wifi will %s be prioritized on next boot", activate ? "" : "NOT"); + // config_set_value(NVS_TYPE_STR, "prio_wifi", activate ? "Y" : "N"); + // } +} + + +void network_manager_format_state_machine(esp_log_level_t level, const char* prefix, state_machine_t* state_machine, bool show_source, const char * caller) { + state_t const* source_state = NULL; + state_t const* current_state = NULL; + network_event_t event = -1; + MEMTRACE_PRINT_DELTA(); + if (state_machine) { + source_state = ((network_t *)state_machine)->source_state; + current_state = state_machine->State; + event = state_machine->Event; + network_manager_format_from_to_states(level, prefix, source_state, current_state, event, show_source,caller); + } + else { + ESP_LOG_LEVEL(level, TAG, "%s - %s -> [%s]", + STR_OR_BLANK(caller), + prefix, + event_to_string(event)); + } + + +} +void network_manager_format_from_to_states(esp_log_level_t level, const char* prefix, const state_t * from_state,const state_t * current_state, network_event_t event,bool show_source, const char * caller) { + const char* source_state = ""; + const char* source_sub_state = ""; + const char* state = "N/A"; + const char* sub_state = "N/A"; + + if (current_state) { + state = state_to_string(current_state); + sub_state = sub_state_to_string(current_state); + } + if (!from_state) { + source_state = "N/A"; + } else { + source_state = state_to_string(from_state); + source_sub_state = sub_state_to_string(from_state); + } + if (show_source) { + ESP_LOG_LEVEL(level, TAG, "%s %s %s(%s)->%s(%s) [%s]", + STR_OR_BLANK(caller), + prefix, + source_state, + source_sub_state, + state, + sub_state, + event_to_string(event)); + } else { + ESP_LOG_LEVEL(level, TAG, "%s %s %s(%s) [%s]", + STR_OR_BLANK(caller), + prefix, + state, + sub_state, + event_to_string(event)); + } +} +void network_async(network_event_t trigger) { + queue_message msg; + memset(&msg,0x00,sizeof(msg)); + msg.trigger = trigger; + ESP_LOGD(TAG, "Posting event %s directly", event_to_string(trigger)); + xQueueSendToBack(network_queue, &msg, portMAX_DELAY); +} +void network_async_fail() { + network_async(EN_FAIL); +} +void network_async_success() { + network_async(EN_SUCCESS); +} +void network_async_connected(){ + network_async(EN_CONNECTED); +} +void network_async_link_up() { + network_async(EN_LINK_UP); +} +void network_async_link_down() { + network_async(EN_LINK_DOWN); +} +void network_async_configure() { + network_async(EN_CONFIGURE); +} +void network_async_got_ip() { + network_async(EN_GOT_IP); +} +void network_async_eth_got_ip() { + network_async(EN_ETH_GOT_IP); +} +void network_async_timer() { + network_async(EN_TIMER); +} +void network_async_start() { + network_async(EN_START); +} +void network_async_scan() { + network_async(EN_SCAN); +} + +void network_async_update_status() { + network_async(EN_UPDATE_STATUS); +} + +void network_async_delete() { + network_async(EN_DELETE); +} + +void network_async_scan_done() { + network_async(EN_SCAN_DONE); +} +void network_async_connect(const char * ssid, const char * password) { + queue_message msg; + memset(&msg,0x00,sizeof(msg)); + msg.trigger = EN_CONNECT_NEW; + msg.ssid = strdup_psram(ssid); + if(password && strlen(password) >0){ + msg.password = strdup_psram(password); + } + ESP_LOGD(TAG, "Posting event %s", event_to_string(msg.trigger)); + xQueueSendToBack(network_queue, &msg, portMAX_DELAY); +} +void network_async_lost_connection(wifi_event_sta_disconnected_t* disconnected_event) { + queue_message msg; + memset(&msg,0x00,sizeof(msg)); + msg.trigger = EN_LOST_CONNECTION; + ESP_LOGD(TAG, "Posting event %s", event_to_string(msg.trigger)); + msg.disconnected_event = malloc_init_external(sizeof(wifi_event_sta_disconnected_t)); + if(msg.disconnected_event){ + memcpy(msg.disconnected_event, disconnected_event,sizeof(wifi_event_sta_disconnected_t)); + xQueueSendToBack(network_queue, &msg, portMAX_DELAY); + } + else { + ESP_LOGE(TAG,"Unable to post lost connection event."); + } +} +void network_async_reboot(reboot_type_t rtype) { + queue_message msg; + memset(&msg,0x00,sizeof(msg)); + msg.trigger = EN_REBOOT; + msg.rtype = rtype; + ESP_LOGD(TAG, "Posting event %s - type %d", event_to_string(msg.trigger),rtype); + xQueueSendToBack(network_queue, &msg, portMAX_DELAY); +} + +void network_reboot_ota(char* url) { + queue_message msg; + memset(&msg,0x00,sizeof(msg)); + + if (url == NULL) { + msg.trigger = EN_REBOOT; + msg.rtype = OTA; + ESP_LOGD(TAG, "Posting event %s - type %d", event_to_string(msg.trigger),msg.rtype); + } else { + msg.trigger = EN_REBOOT_URL; + ESP_LOGD(TAG, "Posting event %s - type reboot URL", event_to_string(msg.trigger)); + msg.strval = strdup_psram(url); + } + + xQueueSendToBack(network_queue, &msg, portMAX_DELAY); +} + +network_t* network_get_state_machine() { + return &NM; +} + +static void network_timer_cb(void* timer_id) { + network_async_timer(); +} +esp_netif_t* network_get_active_interface() { + if (NM.wifi_ap_netif && (network_wifi_is_ap_mode() || network_wifi_is_ap_sta_mode())) { + return NM.wifi_ap_netif; + } else if (NM.wifi_netif && network_wifi_is_sta_mode()) { + return NM.wifi_netif; + } + return NM.eth_netif; +} +bool network_is_interface_connected(esp_netif_t* interface) { + esp_err_t err = ESP_OK; + tcpip_adapter_ip_info_t ipInfo; + if(!interface){ + return false; + } + err = network_get_ip_info_for_netif(interface, &ipInfo); + if(err != ESP_OK){ + ESP_LOGD(TAG,"network_get_ip_info_for_netif returned %s", esp_err_to_name(err)); + } + return ((err == ESP_OK) && (ipInfo.ip.addr != IPADDR_ANY)); +} +static esp_netif_t* get_connected_interface() { + esp_netif_t* interface = NULL; + for (int i = 0; i < 4; i++) { + switch (i) { + case 0: + // try the active interface + interface = network_get_active_interface(); + break; + case 1: + interface = NM.wifi_ap_netif; + break; + case 2: + interface = NM.wifi_netif; + break; + case 3: + interface = NM.eth_netif; + break; + default: + break; + } + if (interface && network_is_interface_connected(interface)) { + ESP_LOGD(TAG,"Found connected interface in iteration #%d",i); + return interface; + } + } + ESP_LOGD(TAG,"No connected interface found"); + return NULL; +} +esp_err_t network_get_ip_info_for_netif(esp_netif_t* netif, tcpip_adapter_ip_info_t* ipInfo) { + esp_netif_ip_info_t loc_ip_info; + if (!ipInfo ) { + ESP_LOGE(TAG, "Invalid pointer for ipInfo"); + return ESP_ERR_INVALID_ARG; + } + if (!netif) { + ESP_LOGE(TAG, "Invalid pointer for netif"); + return ESP_ERR_INVALID_ARG; + } + memset(ipInfo,0x00,sizeof(tcpip_adapter_ip_info_t)); + esp_err_t err= esp_netif_get_ip_info(netif, &loc_ip_info); + if(err==ESP_OK){ + ip4_addr_set(&(ipInfo->ip),&loc_ip_info.ip); + ip4_addr_set(&(ipInfo->gw),&loc_ip_info.gw); + ip4_addr_set(&(ipInfo->netmask),&loc_ip_info.netmask); + } + return err; +} +esp_err_t network_get_ip_info(tcpip_adapter_ip_info_t* ipInfo) { + esp_netif_t* netif= get_connected_interface(); + if(netif){ + return network_get_ip_info_for_netif(netif,ipInfo); + } + return ESP_FAIL; +} + +esp_err_t network_get_hostname(const char** hostname) { + return esp_netif_get_hostname(get_connected_interface(), hostname); +} + +void network_set_timer(uint16_t duration) { + if (duration > 0) { + if (!NM.state_timer) { + ESP_LOGD(TAG, "Starting new pulse check timer with period of %u ms.", duration); + NM.state_timer = xTimerCreate("background STA", pdMS_TO_TICKS(duration), pdFALSE, NULL, network_timer_cb); + } else { + ESP_LOGD(TAG, "Changing the pulse timer period to %u ms.", duration); + xTimerChangePeriod(NM.state_timer, pdMS_TO_TICKS(duration), portMAX_DELAY); + } + xTimerStart(NM.state_timer, portMAX_DELAY); + } else if (NM.state_timer) { + ESP_LOGD(TAG, "Stopping timer"); + xTimerStop(NM.state_timer, portMAX_DELAY); + } +} +void network_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + ip_event_got_ip_t* s = NULL; + esp_netif_ip_info_t* ip_info = NULL; + + if (event_base != IP_EVENT) + return; + switch (event_id) { + case IP_EVENT_ETH_GOT_IP: + case IP_EVENT_STA_GOT_IP: + s = (ip_event_got_ip_t*)event_data; + ip_info = &s->ip_info; + ESP_LOGI(TAG, "Got an IP address from interface %s. IP=" IPSTR ", Gateway=" IPSTR ", NetMask=" IPSTR ", %s", + event_id == IP_EVENT_ETH_GOT_IP ? "Eth" : event_id == IP_EVENT_STA_GOT_IP ? "Wifi" + : "Unknown", + IP2STR(&ip_info->ip), + IP2STR(&ip_info->gw), + IP2STR(&ip_info->netmask), + s->ip_changed ? "Address was changed" : "Address unchanged"); + network_async(event_id == IP_EVENT_ETH_GOT_IP ? EN_ETH_GOT_IP : EN_GOT_IP); + break; + case IP_EVENT_STA_LOST_IP: + ESP_LOGD(TAG, "IP_EVENT_STA_LOST_IP"); + break; + case IP_EVENT_AP_STAIPASSIGNED: + ESP_LOGD(TAG, "IP_EVENT_AP_STAIPASSIGNED"); + break; + case IP_EVENT_GOT_IP6: + ESP_LOGD(TAG, "IP_EVENT_GOT_IP6"); + break; + default: + break; + } +} +void network_set_hostname(esp_netif_t* interface) { + esp_err_t err; + 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); + } +} +#define LOCAL_MAC_SIZE 20 +char* network_manager_alloc_get_mac_string(uint8_t mac[6]) { + char* macStr = malloc_init_external(LOCAL_MAC_SIZE); + if(macStr){ + snprintf(macStr, LOCAL_MAC_SIZE, MACSTR, MAC2STR(mac)); + } + return macStr; +} + + diff --git a/components/wifi-manager/network_manager.h b/components/wifi-manager/network_manager.h index b2b0e96a..f2ccfb11 100644 --- a/components/wifi-manager/network_manager.h +++ b/components/wifi-manager/network_manager.h @@ -1,41 +1,5 @@ -/* -Copyright (c) 2017-2019 Tony Pottier +#pragma once -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -@file network_manager.h -@author Tony Pottier -@brief Defines all functions necessary for esp32 to connect to a wifi/scan wifis - -Contains the freeRTOS task and all necessary support - -@see https://idyl.io -@see https://github.com/tonyp7/esp32-wifi-manager -*/ - -#ifndef WIFI_MANAGER_H_INCLUDED -#define WIFI_MANAGER_H_INCLUDED - - -#ifdef __cplusplus -extern "C" { -#endif #include "esp_system.h" #include "esp_wifi.h" #include "esp_wifi_types.h" @@ -43,8 +7,192 @@ extern "C" { #include "cJSON.h" #include "esp_eth.h" #include "freertos/event_groups.h" -#include "state_machine.h" -#include "state_machine.h" +#include "hsm.h" +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define STA_POLLING_MIN (15 * 1000) +#define STA_POLLING_MAX (10 * 60 * 1000) +#define ETH_LINK_DOWN_REBOOT (2 * 1000) +#define ETH_DHCP_FAIL (6 * 1000) +#define WIFI_DHCP_FAIL (6 * 1000) + +/* + * --------------------- ENUMERATION --------------------- + */ +//#define ADD_ROOT(NAME, HANDLER, ENTRY, EXIT, CHILD) +//#define ADD_ROOT(NAME, HANDLER, ENTRY, EXIT, CHILD) +//#define ADD_LEAF(NAME, HANDLER, ENTRY, EXIT, PARENT, LEVEL) + +#define ALL_NM_STATE \ + ADD_ROOT_LEAF(NETWORK_INSTANTIATED_STATE)\ + ADD_ROOT_LEAF(NETWORK_INITIALIZING_STATE)\ + ADD_ROOT(NETWORK_ETH_ACTIVE_STATE, Eth_Active_State)\ + ADD_ROOT(NETWORK_WIFI_ACTIVE_STATE, Wifi_Active_State)\ + ADD_ROOT(NETWORK_WIFI_CONFIGURING_ACTIVE_STATE, Wifi_Configuring_State) + +#define ALL_ETH_STATE(PARENT, LEVEL)\ + ADD_LEAF(ETH_STARTING_STATE,PARENT,LEVEL)\ + ADD_LEAF(ETH_ACTIVE_LINKUP_STATE,PARENT,LEVEL)\ + ADD_LEAF(ETH_ACTIVE_LINKDOWN_STATE,PARENT,LEVEL)\ + ADD_LEAF(ETH_ACTIVE_CONNECTED_STATE,PARENT,LEVEL)\ + ADD_LEAF(ETH_CONNECTING_NEW_STATE,PARENT,LEVEL) + +#define ALL_WIFI_STATE(PARENT, LEVEL)\ + ADD_LEAF(WIFI_INITIALIZING_STATE,PARENT,LEVEL)\ + ADD_LEAF(WIFI_CONNECTING_STATE,PARENT,LEVEL)\ + ADD_LEAF(WIFI_CONNECTING_NEW_STATE,PARENT,LEVEL)\ + ADD_LEAF(WIFI_CONNECTING_NEW_FAILED_STATE,PARENT,LEVEL)\ + ADD_LEAF(WIFI_CONNECTED_STATE,PARENT,LEVEL)\ + ADD_LEAF(WIFI_USER_DISCONNECTED_STATE,PARENT,LEVEL)\ + ADD_LEAF(WIFI_LOST_CONNECTION_STATE,PARENT,LEVEL) + +#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_SUCCESS_STATE,PARENT,LEVEL)\ + ADD_LEAF(WIFI_CONFIGURING_CONNECT_SUCCESS_GOTOSTA_STATE,PARENT,LEVEL) + + +#define ADD_ROOT(name, ...) name, +#define ADD_ROOT_LEAF(name, ...) name, +#define ADD_LEAF(name, ...) name, +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_CONFIGURING_STATE(,) + TOTAL_WIFI_CONFIGURING_STATE +} mn_wifi_configuring_state_t; + +#undef ADD_STATE +#undef ADD_ROOT +#undef ADD_ROOT_LEAF +#undef ADD_LEAF + +typedef void (*network_status_reached_cb)(nm_state_t state_id, int sub_state ); +//! List of oven events +#define ALL_NM_EVENTS \ + ADD_FIRST_EVENT(EN_LINK_UP) \ + ADD_EVENT(EN_LINK_DOWN)\ + ADD_EVENT(EN_CONFIGURE)\ + ADD_EVENT(EN_GOT_IP)\ + ADD_EVENT(EN_ETH_GOT_IP)\ + ADD_EVENT(EN_DELETE)\ + ADD_EVENT(EN_TIMER)\ + ADD_EVENT(EN_START)\ + ADD_EVENT(EN_SCAN)\ + ADD_EVENT(EN_FAIL)\ + ADD_EVENT(EN_SUCCESS)\ + ADD_EVENT(EN_SCAN_DONE)\ + ADD_EVENT(EN_CONNECT)\ + ADD_EVENT(EN_CONNECT_NEW)\ + ADD_EVENT(EN_REBOOT)\ + ADD_EVENT(EN_REBOOT_URL)\ + ADD_EVENT(EN_LOST_CONNECTION)\ + ADD_EVENT(EN_ETHERNET_FALLBACK)\ + ADD_EVENT(EN_UPDATE_STATUS)\ + ADD_EVENT(EN_CONNECTED) +#define ADD_EVENT(name) name, +#define ADD_FIRST_EVENT(name) name=1, +typedef enum { + ALL_NM_EVENTS +} network_event_t; +#undef ADD_EVENT +#undef ADD_FIRST_EVENT + +typedef enum { + OTA, + RECOVERY, + RESTART, +} reboot_type_t; + +typedef struct { + network_event_t trigger; + char * ssid; + char * password; + reboot_type_t rtype; + char* strval; + wifi_event_sta_disconnected_t* disconnected_event; + esp_netif_t *netif; +} queue_message; + +typedef struct +{ + state_machine_t Machine; //!< Abstract state machine + const state_t* source_state; + bool ethernet_connected; + TimerHandle_t state_timer; + uint32_t STA_duration; + int32_t total_connected_time; + int64_t last_connected; + uint16_t num_disconnect; + uint16_t retries; + bool wifi_connected; + esp_netif_t *wifi_netif; + esp_netif_t *eth_netif; + esp_netif_t *wifi_ap_netif; + queue_message * event_parameters; +} network_t; + + +/* + * --------------------- External function prototype --------------------- + */ + +void network_start(); +network_t * network_get_state_machine(); +void network_event_simple(network_event_t trigger); +void network_event(network_event_t trigger, void* param); +void network_async_event(network_event_t trigger, void* param); +void network_async(network_event_t trigger); +void network_async_fail(); +void network_async_success(); +void network_async_link_up(); +void network_async_link_down(); +void network_async_configure(); +void network_async_got_ip(); +void network_async_timer(); +void network_async_start(); +void network_async_scan(); +void network_async_scan_done(); +void network_async_connect(const char * ssid, const char * password); +void network_async_lost_connection(wifi_event_sta_disconnected_t * disconnected_event); +void network_async_reboot(reboot_type_t rtype); +void network_reboot_ota(char* url); +void network_async_delete(); +void network_async_update_status(); +void network_async_eth_got_ip(); +void network_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); +bool network_is_interface_connected(esp_netif_t * interface); + + + + + + +/* + * --------------------- Inline functions --------------------- + */ + + + + + + /** * @brief Defines the maximum size of a SSID name. 32 is IEEE standard. * @warning limit is also hard coded in wifi_config_t. Never extend this value. @@ -149,96 +297,23 @@ extern "C" { */ #define DEFAULT_STA_POWER_SAVE WIFI_PS_MIN_MODEM -/** - * @brief Defines the maximum length in bytes of a JSON representation of an access point. - * - * maximum ap string length with full 32 char ssid: 75 + \\n + \0 = 77\n - * example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","chan":12,"rssi":-100,"auth":4},\n - * BUT: we need to escape JSON. Imagine a ssid full of \" ? so it's 32 more bytes hence 77 + 32 = 99.\n - * this is an edge case but I don't think we should crash in a catastrophic manner just because - * someone decided to have a funny wifi name. - */ -#define JSON_ONE_APP_SIZE 99 -/** - * @brief Defines the complete list of all messages that the wifi_manager can process. - * - * Some of these message are events ("EVENT"), and some of them are action ("ORDER") - * Each of these messages can trigger a callback function and each callback function is stored - * in a function pointer array for convenience. Because of this behavior, it is extremely important - * to maintain a strict sequence and the top level special element 'MESSAGE_CODE_COUNT' - * - * @see wifi_manager_set_callback - */ -typedef enum message_code_t { - NONE = 0, - ORDER_START_HTTP_SERVER = 1, - ORDER_STOP_HTTP_SERVER = 2, - ORDER_START_DNS_SERVICE = 3, - ORDER_STOP_DNS_SERVICE = 4, - ORDER_START_WIFI_SCAN = 5, - ORDER_LOAD_AND_RESTORE_STA = 6, - ORDER_CONNECT_STA = 7, - ORDER_DISCONNECT_STA = 8, - ORDER_START_AP = 9, - ORDER_START_HTTP = 10, - ORDER_START_DNS_HIJACK = 11, - EVENT_STA_DISCONNECTED = 12, - EVENT_SCAN_DONE = 13, - EVENT_GOT_IP = 14, - ORDER_RESTART_OTA = 15, - ORDER_RESTART_RECOVERY = 16, - ORDER_RESTART_OTA_URL = 17, - ORDER_RESTART = 18, - ORDER_UPDATE_STATUS = 19, - EVENT_ETH_GOT_IP = 20, - EVENT_ETH_TIMEOUT = 21, - EVENT_ETH_LINK_UP = 22, - EVENT_ETH_LINK_DOWN = 23, - MESSAGE_CODE_COUNT = 24 /* important for the callback array */ -}message_code_t; - - - -/* @brief indicate that the ESP32 is currently connected. */ -extern const int WIFI_MANAGER_WIFI_CONNECTED_BIT; - -extern const int WIFI_MANAGER_AP_STA_CONNECTED_BIT; - -/* @brief Set automatically once the SoftAP is started */ -extern const int WIFI_MANAGER_AP_STARTED_BIT; - -/* @brief When set, means a client requested to connect to an access point.*/ -extern const int WIFI_MANAGER_REQUEST_STA_CONNECT_BIT; - -/* @brief This bit is set automatically as soon as a connection was lost */ -extern const int WIFI_MANAGER_STA_DISCONNECT_BIT ; - -/* @brief When set, means the wifi manager attempts to restore a previously saved connection at startup. */ -extern const int WIFI_MANAGER_REQUEST_RESTORE_STA_BIT ; - -/* @brief When set, means a client requested to disconnect from currently connected AP. */ -extern const int WIFI_MANAGER_REQUEST_WIFI_DISCONNECT_BIT ; - -/* @brief When set, means a scan is in progress */ -extern const int WIFI_MANAGER_SCAN_BIT ; - -/* @brief When set, means user requested for a disconnect */ -extern const int WIFI_MANAGER_REQUEST_DISCONNECT_BIT ; - -/* @brief When set, means user requested connecting to a new network and it failed */ -extern const int WIFI_MANAGER_REQUEST_STA_CONNECT_FAILED_BIT ; - -void network_manager_reboot_ota(char * url); +void network_reboot_ota(char * url); /** * @brief simplified reason codes for a lost connection. * * esp-idf maintains a big list of reason codes which in practice are useless for most typical application. + * UPDATE_CONNECTION_OK - Web UI expects this when attempting to connect to a new access point succeeds + * UPDATE_FAILED_ATTEMPT - Web UI expects this when attempting to connect to a new access point fails + * UPDATE_USER_DISCONNECT = 2, + * UPDATE_LOST_CONNECTION = 3, + * UPDATE_FAILED_ATTEMPT_AND_RESTORE - Web UI expects this when attempting to connect to a new access point fails and previous connection is restored + * UPDATE_ETHERNET_CONNECTED = 5 */ typedef enum update_reason_code_t { - UPDATE_CONNECTION_OK = 0, + UPDATE_CONNECTION_OK = 0, // expected when UPDATE_FAILED_ATTEMPT = 1, UPDATE_USER_DISCONNECT = 2, UPDATE_LOST_CONNECTION = 3, @@ -247,33 +322,6 @@ typedef enum update_reason_code_t { }update_reason_code_t; -typedef enum connection_request_made_by_code_t{ - CONNECTION_REQUEST_NONE = 0, - CONNECTION_REQUEST_USER = 1, - CONNECTION_REQUEST_AUTO_RECONNECT = 2, - CONNECTION_REQUEST_RESTORE_CONNECTION = 3, - CONNECTION_REQUEST_MAX_FAILED = 4, - CONNECTION_REQUEST_MAX = 0x7fffffff /*force the creation of this enum as a 32 bit int */ -}connection_request_made_by_code_t; - -/** - * The wifi manager settings in use - */ -//struct wifi_settings_t{ -// bool sta_only; -// bool sta_static_ip; -// wifi_ps_type_t sta_power_save; -// tcpip_adapter_ip_info_t sta_static_ip_config; -//}; -//extern struct wifi_settings_t wifi_settings; - -// /** -// * @brief Structure used to store one message in the queue. -// */ -// typedef struct{ -// message_code_t code; -// void *param; -// } queue_message; @@ -284,59 +332,95 @@ typedef enum connection_request_made_by_code_t{ /** * Frees up all memory allocated by the wifi_manager and kill the task. */ -void network_manager_destroy(); +void network_destroy(); /** * Filters the AP scan list to unique SSIDs */ void filter_unique( wifi_ap_record_t * aplist, uint16_t * ap_num); -/** - * Main task for the wifi_manager - */ -void network_manager( void * pvParameters ); - -char* wifi_manager_alloc_get_ap_list_json(); -cJSON * wifi_manager_clear_ap_list_json(cJSON **old); +char* network_status_alloc_get_ap_list_json(); +cJSON * network_manager_clear_ap_list_json(cJSON **old); /** * @brief A standard wifi event handler as recommended by Espressif */ -esp_err_t wifi_manager_event_handler(void *ctx, system_event_t *event); +esp_err_t network_manager_event_handler(void *ctx, system_event_t *event); /** * @brief Clears the connection status json. - * @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful. + * @note This is not thread-safe and should be called only if network_status_lock_json_buffer call is successful. */ -cJSON * wifi_manager_clear_ip_info_json(cJSON **old); -cJSON * wifi_manager_get_new_json(cJSON **old); +cJSON * network_status_clear_ip_info_json(cJSON **old); +cJSON * network_status_get_new_json(cJSON **old); /** * @brief Start the mDNS service */ -void wifi_manager_initialise_mdns(); +void network_manager_initialise_mdns(); /** - * @brief Register a callback to a custom function when specific event message_code happens. + * @brief Register a callback to a custom function when specific network manager states are reached. */ -void wifi_manager_set_callback(message_code_t message_code, void (*func_ptr)(void*) ); +esp_err_t network_register_state_callback(nm_state_t state, int sub_state, const char* from, network_status_reached_cb cb); +bool network_is_wifi_prioritized(); +esp_netif_t * network_get_active_interface(); +esp_err_t network_get_hostname( const char **hostname); +esp_err_t network_get_ip_info(tcpip_adapter_ip_info_t* ipInfo); +void network_set_timer(uint16_t duration); +void network_set_hostname(esp_netif_t * netif); +esp_err_t network_get_ip_info_for_netif(esp_netif_t* netif, tcpip_adapter_ip_info_t* ipInfo); +void network_start_stop_dhcp(esp_netif_t* netif, bool start); +void network_start_stop_dhcps(esp_netif_t* netif, bool start); +void network_prioritize_wifi(bool activate); +#define ADD_ROOT_FORWARD_DECLARATION(name, ...) ADD_STATE_FORWARD_DECLARATION_(name) +#define ADD_ROOT_LEAF_FORWARD_DECLARATION(name, ...) ADD_STATE_FORWARD_DECLARATION_(name) +#define ADD_LEAF_FORWARD_DECLARATION(name, ...) ADD_STATE_FORWARD_DECLARATION_(name) +#define ADD_STATE_FORWARD_DECLARATION_(name) \ + static state_machine_result_t name##_handler(state_machine_t* const State_Machine); \ + static state_machine_result_t name##_entry_handler(state_machine_t* const State_Machine); \ + static state_machine_result_t name##_exit_handler(state_machine_t* const State_Machine); +void initialize_network_handlers(state_machine_t* state_machine); +void network_manager_format_from_to_states(esp_log_level_t level, const char* prefix, state_t const * from_state, state_t const* current_state, network_event_t event,bool show_source, const char * caller ); +void network_manager_format_state_machine(esp_log_level_t level, const char* prefix, state_machine_t * state_machine, bool show_source, const char * caller) ; +char* network_manager_alloc_get_mac_string(uint8_t mac[6]); -bool network_manager_is_flag_set(EventBits_t bit); -void network_manager_set_flag(EventBits_t bit); -void network_manager_clear_flag(EventBits_t bit); +#if defined(LOG_LOCAL_LEVEL) && LOG_LOCAL_LEVEL >=ESP_LOG_VERBOSE +#define NETWORK_PRINT_TRANSITION(begin, prefix, source,target, event, print_source,caller ) network_manager_format_from_to_states(ESP_LOG_VERBOSE, prefix, source,target, event, print_source,caller ) +#define NETWORK_DEBUG_STATE_MACHINE(begin, cb_prefix,state_machine,print_from,caller) network_manager_format_state_machine(ESP_LOG_DEBUG,cb_prefix,state_machine,print_from,caller) +#define NETWORK_EXECUTE_CB(mch) network_execute_cb(mch,__FUNCTION__); +#define network_handler_entry_print(State_Machine, begin) network_manager_format_state_machine(ESP_LOG_DEBUG,begin?"ENTRY START":"ENTRY END",State_Machine,false,__FUNCTION__) +#define network_exit_handler_print(State_Machine, begin) network_manager_format_state_machine(ESP_LOG_DEBUG,begin?"EXIT START":"END END",State_Machine,false,__FUNCTION__) +#define network_handler_print(State_Machine, begin) network_manager_format_state_machine(ESP_LOG_DEBUG,begin?"HANDLER START":"HANDLER END",State_Machine,false,__FUNCTION__) + +#elif defined(LOG_LOCAL_LEVEL) && LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG +#define network_handler_entry_print(State_Machine, begin) if(begin) network_manager_format_state_machine(ESP_LOG_DEBUG,begin?"BEGIN ENTRY":"END ENTRY",State_Machine,false,__FUNCTION__) +#define network_exit_handler_print(State_Machine, begin) if(begin) network_manager_format_state_machine(ESP_LOG_DEBUG,begin?"BEGIN EXIT":"END EXIT",State_Machine,false,__FUNCTION__) +#define network_handler_print(State_Machine, begin) if(begin) network_manager_format_state_machine(ESP_LOG_DEBUG,begin?"HANDLER START":"HANDLER END",State_Machine,false,__FUNCTION__) + +#define NETWORK_PRINT_TRANSITION(begin, prefix, source,target, event, print_source,caller ) if(begin) network_manager_format_from_to_states(ESP_LOG_DEBUG, prefix, source,target, event, print_source,caller )#define NETWORK_EXECUTE_CB(mch) network_execute_cb(mch,__FUNCTION__); +#define NETWORK_DEBUG_STATE_MACHINE(begin, cb_prefix,state_machine,print_from,caller) if(begin) network_manager_format_state_machine(ESP_LOG_DEBUG,cb_prefix,state_machine,print_from,caller) +#else +#define network_exit_handler_print(nm, begin) +#define network_handler_entry_print(State_Machine, begin) +#define network_handler_print(State_Machine, begin) +#define NETWORK_EXECUTE_CB(mch) network_execute_cb(mch,NULL) +#define NETWORK_PRINT_TRANSITION(prefix, source,target, event, print_source,caller ) +#define NETWORK_DEBUG_STATE_MACHINE(begin, cb_prefix,state_machine,print_from,caller) +#endif #ifdef __cplusplus } #endif -#endif /* WIFI_MANAGER_H_INCLUDED */ + diff --git a/components/wifi-manager/network_manager_handlers.c b/components/wifi-manager/network_manager_handlers.c new file mode 100644 index 00000000..540c02dc --- /dev/null +++ b/components/wifi-manager/network_manager_handlers.c @@ -0,0 +1,1155 @@ +#ifdef NETWORK_HANDLERS_LOG_LEVEL +#define LOG_LOCAL_LEVEL NETWORK_HANDLERS_LOG_LEVEL +#endif +#include "network_manager.h" +#include +#include +#include +#include +#include "network_ethernet.h" +#include "network_status.h" +#include "network_wifi.h" + +#include "dns_server.h" +#include "esp_log.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "platform_esp32.h" + +#include "esp_netif.h" +#include "freertos/task.h" + +#include "cJSON.h" +#include "cmd_system.h" +#include "esp_app_format.h" + +#include "esp_event.h" +#include "esp_ota_ops.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "lwip/api.h" +#include "lwip/err.h" +#include "lwip/ip4_addr.h" +#include "lwip/netdb.h" +#include "mdns.h" +#include "messaging.h" + +#include "platform_config.h" +#include "trace.h" + +#include "accessors.h" +#include "esp_err.h" +#include "globdefs.h" +#include "http_server_handlers.h" +#include "network_manager.h" + +static const char TAG[]="network_handlers"; + +EXT_RAM_ATTR static state_t network_states[TOTAL_NM_STATE]; +EXT_RAM_ATTR static state_t Wifi_Active_State[TOTAL_WIFI_ACTIVE_STATE]; +EXT_RAM_ATTR static state_t Eth_Active_State[TOTAL_ETH_ACTIVE_STATE]; +EXT_RAM_ATTR static state_t Wifi_Configuring_State[TOTAL_WIFI_CONFIGURING_STATE]; + +static void network_interface_coexistence(state_machine_t* state_machine); +static state_machine_result_t local_traverse_state(state_machine_t* const state_machine, + const state_t* const target_state, const char * caller) ; +static state_machine_result_t local_switch_state(state_machine_t* state_machine, + const state_t* const target_state, const char * caller); +void network_execute_cb(state_machine_t* const state_machine, const char * caller); +int get_root_id(const state_t * state); +const state_t* get_root( const state_t* const state); + +#define ADD_ROOT(name,...) ADD_ROOT_FORWARD_DECLARATION(name,...) +#define ADD_ROOT_LEAF(name,...) ADD_ROOT_LEAF_FORWARD_DECLARATION(name,...) +#define ADD_LEAF(name,...) ADD_LEAF_FORWARD_DECLARATION(name,...) +ALL_NM_STATE +ALL_ETH_STATE(, ) +ALL_WIFI_STATE(, ) +ALL_WIFI_CONFIGURING_STATE(, ) +#undef ADD_ROOT +#undef ADD_ROOT_LEAF +#undef ADD_LEAF + +/* + * --------------------- Global variables --------------------- + */ +#define ADD_ROOT(NAME, CHILD) ADD_ROOT_(NAME, CHILD) +#define ADD_LEAF(NAME, PARENT, LEVEL) ADD_LEAF_(NAME, PARENT, LEVEL) +#define ADD_ROOT_LEAF(NAME) ADD_ROOT_LEAF_(NAME) + +#define ADD_ROOT_(NAME, CHILD) \ + [NAME] = { \ + .Handler = NAME##_handler, \ + .Entry = NAME##_entry_handler, \ + .Exit = NAME##_exit_handler, \ + .Level = 0, \ + .Parent = NULL, \ + .Id = NAME, \ + .Node = CHILD, \ + }, + +#define ADD_ROOT_LEAF_(NAME) \ + [NAME] = { \ + .Handler = NAME##_handler, \ + .Entry = NAME##_entry_handler, \ + .Exit = NAME##_exit_handler, \ + .Id = NAME, \ + .Parent = NULL, \ + }, + +#define ADD_LEAF_(NAME, PARENT, LEVEL) \ + [NAME] = { \ + .Handler = NAME##_handler, \ + .Entry = NAME##_entry_handler, \ + .Exit = NAME##_exit_handler, \ + .Id = NAME, \ + .Level = LEVEL, \ + .Parent = PARENT, \ + }, + +static void network_initialize_state_machine_globals(){ + static state_t loc_network_states[] = + { + ALL_NM_STATE}; + + static state_t loc_Wifi_Active_State[] = { + ALL_WIFI_STATE(&network_states[NETWORK_WIFI_ACTIVE_STATE], 1) + }; + static state_t loc_Eth_Active_State[]={ + ALL_ETH_STATE(&network_states[NETWORK_ETH_ACTIVE_STATE], 1) + + }; + static state_t loc_Wifi_Configuring_State[]={ + ALL_WIFI_CONFIGURING_STATE(&network_states[NETWORK_WIFI_CONFIGURING_ACTIVE_STATE], 1) + }; + memcpy(&network_states,&loc_network_states,sizeof(network_states)); + memcpy(&Eth_Active_State,&loc_Eth_Active_State,sizeof(Eth_Active_State)); + memcpy(&Wifi_Active_State,&loc_Wifi_Active_State,sizeof(Wifi_Active_State)); + memcpy(&Wifi_Configuring_State,&loc_Wifi_Configuring_State,sizeof(Wifi_Configuring_State)); +} +#undef ADD_ROOT +#undef ADD_ROOT_LEAF +#undef ADD_LEAF + +#define HANDLE_GLOBAL_EVENT(m) \ + if (handle_global_event(m) == EVENT_HANDLED) \ + return EVENT_HANDLED; + +static void network_connect_active_ssid(state_machine_t* const State_Machine) { + network_t* const nm = (network_t*)State_Machine; + if (network_wifi_connect_active_ssid() != ESP_OK) { + ESP_LOGE(TAG, "Oups. Something went wrong!"); + nm->wifi_connected = false; + ESP_LOGD(TAG, "Checking if ethernet interface is connected"); + if (network_is_interface_connected(nm->eth_netif)) { + ESP_LOGD(TAG, "Ethernet connection is found. Try to fallback there"); + network_async(EN_ETHERNET_FALLBACK); + } else { + // returning to AP mode + nm->STA_duration = STA_POLLING_MIN; + ESP_LOGD(TAG, "No ethernet and no wifi configured. Go to configuration mode"); + network_async_configure(); + } + } +} + +void initialize_network_handlers(state_machine_t* state_machine){ + MEMTRACE_PRINT_DELTA(); + network_initialize_state_machine_globals(); + MEMTRACE_PRINT_DELTA(); + NETWORK_INSTANTIATED_STATE_handler(state_machine); + MEMTRACE_PRINT_DELTA(); +} +static state_machine_result_t handle_global_event(state_machine_t* state_machine) { + network_t * net_sm= ((network_t *)state_machine); + switch (net_sm->Machine.Event) { + case EN_UPDATE_STATUS: + // handle the event, but don't swicth + network_status_update_basic_info(); + return EVENT_HANDLED; + /* code */ + break; + case EN_REBOOT: + ESP_LOGD(TAG,"Called for reboot type %d",net_sm->event_parameters->rtype); + switch (net_sm->event_parameters->rtype) { + case OTA: + ESP_LOGD(TAG, " Calling guided_restart_ota."); + guided_restart_ota(); + break; + case RECOVERY: + ESP_LOGD(TAG, " Calling guided_factory."); + guided_factory(); + break; + case RESTART: + ESP_LOGD(TAG, " Calling simple_restart."); + simple_restart(); + break; + + default: + break; + } + return EVENT_UN_HANDLED; + break; + case EN_REBOOT_URL: + if (net_sm->event_parameters->strval) { + start_ota(net_sm->event_parameters->strval, NULL, 0); + FREE_AND_NULL(net_sm->event_parameters->strval); + } + return EVENT_UN_HANDLED; + break; + + default: + break; + } + return EVENT_UN_HANDLED; +} + + + + +/********************************************************************************************* + * INSTANTIATED_STATE + */ +static state_machine_result_t NETWORK_INSTANTIATED_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t NETWORK_INSTANTIATED_STATE_handler(state_machine_t* const State_Machine) { + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_UN_HANDLED; + State_Machine->State = &network_states[NETWORK_INSTANTIATED_STATE]; + State_Machine->Event = EN_START; + HANDLE_GLOBAL_EVENT(State_Machine); + if (State_Machine->Event == EN_START) { + result= local_traverse_state(State_Machine, &network_states[NETWORK_INITIALIZING_STATE],__FUNCTION__); + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t NETWORK_INSTANTIATED_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * INITIALIZING_STATE + */ +static state_machine_result_t NETWORK_INITIALIZING_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + + MEMTRACE_PRINT_DELTA_MESSAGE(" Initializing tcp_ip adapter"); + esp_netif_init(); + MEMTRACE_PRINT_DELTA_MESSAGE(" Creating the default event loop"); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + MEMTRACE_PRINT_DELTA_MESSAGE("Initializing network status"); + init_network_status(); + MEMTRACE_PRINT_DELTA_MESSAGE("Loading wifi global configuration"); + network_wifi_global_init(); + MEMTRACE_PRINT_DELTA_MESSAGE(" Registering event handler"); + esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &network_ip_event_handler, NULL); + MEMTRACE_PRINT_DELTA_MESSAGE(" Initializing network done. Starting http server"); + // send a message to start the connections + http_server_start(); + MEMTRACE_PRINT_DELTA_MESSAGE("Executing Callback"); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} + +static state_machine_result_t NETWORK_INITIALIZING_STATE_handler(state_machine_t* const State_Machine) { + + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_UN_HANDLED; + HANDLE_GLOBAL_EVENT(State_Machine); + switch (State_Machine->Event) { + case EN_START: + if (network_is_wifi_prioritized()) { + ESP_LOGI(TAG, "WiFi connection is prioritized. Starting WiFi"); + result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_INITIALIZING_STATE],__FUNCTION__); + } + else { + result= local_traverse_state(State_Machine, &Eth_Active_State[ETH_STARTING_STATE],__FUNCTION__); + } + break; + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t NETWORK_INITIALIZING_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * ETH_STARTING_STATE + */ +static state_machine_result_t ETH_STARTING_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + ESP_LOGD(TAG, "Looking for ethernet Interface"); + network_t* const nm = (network_t *)State_Machine; + init_network_ethernet(); + if (!network_ethernet_enabled()) { + network_async_fail(); + } else { + nm->eth_netif = network_ethernet_get_interface(); + } + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} + +static state_machine_result_t ETH_STARTING_STATE_handler(state_machine_t* const State_Machine) { + state_machine_result_t result = EVENT_HANDLED; + network_handler_print(State_Machine,true); + HANDLE_GLOBAL_EVENT(State_Machine); + switch (State_Machine->Event) { + case EN_FAIL: + result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_INITIALIZING_STATE],__FUNCTION__); + break; + case EN_SUCCESS: + result= local_traverse_state(State_Machine, &network_states[NETWORK_ETH_ACTIVE_STATE],__FUNCTION__); + break; + default: + ESP_LOGE(TAG, "No handler"); + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t ETH_STARTING_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * NETWORK_ETH_ACTIVE_STATE + */ +static state_machine_result_t NETWORK_ETH_ACTIVE_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + network_set_timer(ETH_LINK_DOWN_REBOOT); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t NETWORK_ETH_ACTIVE_STATE_handler(state_machine_t* const State_Machine) { + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_UN_HANDLED; + switch (State_Machine->Event) { + case EN_CONNECT_NEW: + result= local_traverse_state(State_Machine, &Eth_Active_State[ETH_CONNECTING_NEW_STATE],__FUNCTION__); + break; + case EN_LINK_UP: + result= local_traverse_state(State_Machine, &Eth_Active_State[ETH_ACTIVE_LINKUP_STATE],__FUNCTION__); + break; + case EN_LINK_DOWN: + result= local_traverse_state(State_Machine, &Eth_Active_State[ETH_ACTIVE_LINKDOWN_STATE],__FUNCTION__); + break; + case EN_ETH_GOT_IP: + result= local_traverse_state(State_Machine, &Eth_Active_State[ETH_ACTIVE_CONNECTED_STATE],__FUNCTION__); + break; + case EN_ETHERNET_FALLBACK: + result= local_traverse_state(State_Machine, &Eth_Active_State[ETH_ACTIVE_CONNECTED_STATE],__FUNCTION__); + break; + case EN_TIMER: + ESP_LOGW(TAG, "Ethernet link timeout. Rebooting to wifi"); + network_prioritize_wifi(true); + simple_restart(); + //result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_INITIALIZING_STATE],__FUNCTION__); + break; + case EN_SCAN: + ESP_LOGW(TAG,"Wifi scan cannot be executed in this state"); + network_wifi_built_known_ap_list(); + break; + case EN_DELETE: { + ESP_LOGD(TAG, "WiFi disconnected by user"); + network_wifi_clear_config(); + network_status_update_ip_info(UPDATE_USER_DISCONNECT); + result= EVENT_HANDLED; + } break; + default: + HANDLE_GLOBAL_EVENT(State_Machine); + result=EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t NETWORK_ETH_ACTIVE_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_set_timer(0); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + + + +/********************************************************************************************* + * ETH_CONNECTING_NEW_STATE + */ +static state_machine_result_t ETH_CONNECTING_NEW_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_start_stop_dhcp(nm->wifi_netif, true); + network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password); + FREE_AND_NULL(nm->event_parameters->ssid); + FREE_AND_NULL(nm->event_parameters->password); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t ETH_CONNECTING_NEW_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_GOT_IP: + result= local_traverse_state(State_Machine, &network_states[WIFI_CONNECTED_STATE],__FUNCTION__); + break; + case EN_LOST_CONNECTION: + network_status_update_ip_info(UPDATE_FAILED_ATTEMPT); + messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Unable to connect to new WiFi access point."); + // no existing configuration, or wifi wasn't the active connection when connection + // attempt was made + network_async(EN_ETHERNET_FALLBACK); + result = EVENT_HANDLED; + break; + + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t ETH_CONNECTING_NEW_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_set_timer(0); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * ETH_ACTIVE_LINKDOWN + */ +static state_machine_result_t ETH_ACTIVE_LINKDOWN_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + network_set_timer(ETH_LINK_DOWN_REBOOT); + NETWORK_EXECUTE_CB(State_Machine); + messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Ethernet link down."); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t ETH_ACTIVE_LINKDOWN_STATE_handler(state_machine_t* const State_Machine) { + network_handler_print(State_Machine,true); + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,false); + return EVENT_UN_HANDLED; +} +static state_machine_result_t ETH_ACTIVE_LINKDOWN_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_set_timer(0); + network_exit_handler_print(State_Machine,false); + + return EVENT_HANDLED; +} + +/********************************************************************************************* + * ETH_ACTIVE_LINKUP_STATE + */ +static state_machine_result_t ETH_ACTIVE_LINKUP_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + network_set_timer(ETH_DHCP_FAIL); + NETWORK_EXECUTE_CB(State_Machine); + messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_SYSTEM, "Ethernet link up."); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t ETH_ACTIVE_LINKUP_STATE_handler(state_machine_t* const State_Machine) { + state_machine_result_t result = EVENT_UN_HANDLED; + network_handler_print(State_Machine,true); + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t ETH_ACTIVE_LINKUP_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * WIFI_UP_STATE + */ +static state_machine_result_t NETWORK_WIFI_ACTIVE_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t NETWORK_WIFI_ACTIVE_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_UN_HANDLED; + switch (State_Machine->Event) + { + case EN_LINK_UP: + ESP_LOGW(TAG, "Ethernet link up in wifi mode"); + break; + case EN_ETH_GOT_IP: + network_interface_coexistence(State_Machine); + 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__); + break; + case EN_SCAN: + if (network_wifi_start_scan() == ESP_OK) { + result= EVENT_HANDLED; + } + break; + case EN_SCAN_DONE: + if(wifi_scan_done() == ESP_OK) { + result= EVENT_HANDLED; + } + break; + case EN_CONNECT_NEW: + result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_CONNECTING_NEW_STATE],__FUNCTION__); + break; + case EN_DELETE: + result= local_traverse_state(State_Machine,&Wifi_Active_State[WIFI_USER_DISCONNECTED_STATE],__FUNCTION__); + break; + case EN_ETHERNET_FALLBACK: + result= local_traverse_state(State_Machine, &Eth_Active_State[ETH_ACTIVE_CONNECTED_STATE],__FUNCTION__); + break; + default: + break; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t NETWORK_WIFI_ACTIVE_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * WIFI_INITIALIZING_STATE + */ +static state_machine_result_t WIFI_INITIALIZING_STATE_entry_handler(state_machine_t* const State_Machine) { + network_t* const nm = (network_t *)State_Machine; + network_handler_entry_print(State_Machine,true); + if(!nm->wifi_netif){ + nm->wifi_netif = network_wifi_start(); + } + if (!is_wifi_up()) { + messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Wifi not started. Load Configuration"); + return EVENT_UN_HANDLED; + } + if (network_wifi_get_known_count()>0) { + ESP_LOGI(TAG, "Existing wifi config found. Attempting to connect."); + network_async_success(); + } else { + /* no wifi saved: start soft AP! This is what should happen during a first run */ + ESP_LOGW(TAG, "No saved wifi. Starting AP configuration mode."); + network_async_configure(); + } + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} + +static state_machine_result_t WIFI_INITIALIZING_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_CONFIGURE: + result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__); + break; + case EN_SUCCESS: + result= local_switch_state(State_Machine, &Wifi_Active_State[WIFI_CONNECTING_STATE],__FUNCTION__); + break; + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_INITIALIZING_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + + +/********************************************************************************************* + * WIFI_CONFIGURING_ACTIVE_STATE + */ +static state_machine_result_t NETWORK_WIFI_CONFIGURING_ACTIVE_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + network_t* const nm = (network_t *)State_Machine; + nm->wifi_ap_netif = network_wifi_config_ap(); + dns_server_start(nm->wifi_ap_netif); + network_wifi_start_scan(); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t NETWORK_WIFI_CONFIGURING_ACTIVE_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_SCAN: + if (network_wifi_start_scan() == ESP_OK) { + result= EVENT_HANDLED; + } + break; + case EN_SCAN_DONE: + ESP_LOGD(TAG,"Network configuration active, wifi scan completed"); + if(wifi_scan_done() == ESP_OK) { + result= EVENT_HANDLED; + } + break; + case EN_CONNECT_NEW: + result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_STATE],__FUNCTION__); + break; + case EN_LINK_UP: + ESP_LOGW(TAG, "Ethernet link up in wifi mode"); + break; + case EN_ETH_GOT_IP: + network_interface_coexistence(State_Machine); + break; + default: + result =EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t NETWORK_WIFI_CONFIGURING_ACTIVE_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine, true); + /* bring down DNS hijack */ + ESP_LOGD(TAG, " Stopping DNS."); + dns_server_stop(); + ESP_LOGD(TAG, "Changing wifi mode to STA."); + network_wifi_set_sta_mode(); + network_exit_handler_print(State_Machine, false); + + return EVENT_HANDLED; +} + + +/********************************************************************************************* + * WIFI_CONFIGURING_STATE + */ +static state_machine_result_t WIFI_CONFIGURING_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_CONFIGURING_STATE_handler(state_machine_t* const State_Machine) { + network_handler_print(State_Machine,true); + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,false); + return EVENT_UN_HANDLED; +} +static state_machine_result_t WIFI_CONFIGURING_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * WIFI_CONFIGURING_CONNECT_STATE + */ +static state_machine_result_t WIFI_CONFIGURING_CONNECT_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_start_stop_dhcp(nm->wifi_netif, true); + network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password); + FREE_AND_NULL(nm->event_parameters->ssid); + FREE_AND_NULL(nm->event_parameters->password); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_CONNECTED: + result=EVENT_HANDLED; + ESP_LOGD(TAG,"Wifi was connected. Waiting for IP address"); + network_set_timer(WIFI_DHCP_FAIL); + break; + case EN_GOT_IP: + 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_LOST_CONNECTION: + network_status_update_ip_info(UPDATE_FAILED_ATTEMPT); + result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__); + break; + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + + FREE_AND_NULL(((network_t *)State_Machine)->event_parameters->disconnected_event); + network_set_timer(0); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * WIFI_CONFIGURING_CONNECT_SUCCESS_STATE + */ +static state_machine_result_t WIFI_CONFIGURING_CONNECT_SUCCESS_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + network_status_update_ip_info(UPDATE_CONNECTION_OK); + ESP_LOGD(TAG, "Saving wifi configuration."); + network_wifi_save_sta_config(); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_CONFIGURING_CONNECT_SUCCESS_STATE_handler(state_machine_t* const State_Machine) { + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_UPDATE_STATUS: + network_status_update_basic_info(); + result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_SUCCESS_GOTOSTA_STATE],__FUNCTION__); + break; + default: + result= EVENT_UN_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_SUCCESS_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + + + +/********************************************************************************************* + * WIFI_CONFIGURING_CONNECT_SUCCESS_GOTOSTA_STATE + */ +static state_machine_result_t WIFI_CONFIGURING_CONNECT_SUCCESS_GOTOSTA_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + ESP_LOGD(TAG, "Waiting for next status update event to turn off AP."); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_CONFIGURING_CONNECT_SUCCESS_GOTOSTA_STATE_handler(state_machine_t* const State_Machine) { + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_UPDATE_STATUS: + network_status_update_basic_info(); + result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_CONNECTED_STATE],__FUNCTION__); + break; + default: + result= EVENT_UN_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_SUCCESS_GOTOSTA_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + + +/********************************************************************************************* + * WIFI_CONNECTING_STATE + */ +static state_machine_result_t WIFI_CONNECTING_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_start_stop_dhcp(nm->wifi_netif, true); + network_connect_active_ssid(State_Machine); + nm->STA_duration = STA_POLLING_MIN; + /* create timer for background STA connection */ + network_set_timer(nm->STA_duration); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_CONNECTING_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + state_machine_result_t result = EVENT_HANDLED; + network_handler_print(State_Machine,true); + switch (State_Machine->Event) { + case EN_CONNECTED: + // nothing to do here. Let's wait for IP address + break; + case EN_TIMER: + // try connecting again. + // todo: implement multi-ap logic + network_connect_active_ssid(State_Machine); + break; + default: + result = EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_CONNECTING_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * WIFI_CONNECTING_NEW_STATE + */ +static state_machine_result_t WIFI_CONNECTING_NEW_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_start_stop_dhcp(nm->wifi_netif, true); + network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password); + FREE_AND_NULL(nm->event_parameters->ssid); + FREE_AND_NULL(nm->event_parameters->password); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +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); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + 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__); + break; + case EN_CONNECTED: + ESP_LOGD(TAG,"Successfully connected to the new access point. Waiting for IP Address"); + result = EVENT_HANDLED; + break; + case EN_LOST_CONNECTION: + if(((network_t *)State_Machine)->event_parameters->disconnected_event->reason == WIFI_REASON_ASSOC_LEAVE){ + ESP_LOGD(TAG,"Successfully disconnected from the existing access point. "); + return EVENT_HANDLED; + } + ESP_LOGW(TAG,"Trying to connect failed"); + result = local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_CONNECTING_NEW_FAILED_STATE],__FUNCTION__); + break; + + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_CONNECTING_NEW_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_set_timer(0); + FREE_AND_NULL(((network_t *)State_Machine)->event_parameters->disconnected_event); + network_exit_handler_print(State_Machine,false); + + return EVENT_HANDLED; +} + + + +/********************************************************************************************* + * WIFI_CONNECTING_NEW_FAILED_STATE + */ +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); + if (nm->wifi_connected ) { + // Wifi was already connected to an existing access point. Restore connection + network_connect_active_ssid(State_Machine); + } + + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_CONNECTING_NEW_FAILED_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_GOT_IP: + network_status_update_ip_info(UPDATE_FAILED_ATTEMPT_AND_RESTORE); + result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_CONNECTED_STATE],__FUNCTION__); + break; + case EN_CONNECTED: + ESP_LOGD(TAG,"Successfully connected to the previous access point. Waiting for IP Address"); + result = EVENT_HANDLED; + break; + case EN_LOST_CONNECTION: + network_status_update_ip_info(UPDATE_FAILED_ATTEMPT); + messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Unable to fall back to previous access point."); + result = EVENT_HANDLED; + break; + + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_CONNECTING_NEW_FAILED_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_set_timer(0); + FREE_AND_NULL(((network_t *)State_Machine)->event_parameters->disconnected_event); + network_exit_handler_print(State_Machine,false); + + return EVENT_HANDLED; +} + + + +/********************************************************************************************* + * WIFI_CONNECTED_STATE + */ +static state_machine_result_t WIFI_CONNECTED_STATE_entry_handler(state_machine_t* const State_Machine) { + network_t* const nm = (network_t *)State_Machine; + network_handler_entry_print(State_Machine,true); + nm->last_connected = esp_timer_get_time(); + // cancel timeout pulse + network_set_timer(0); + ESP_LOGD(TAG, "Checking if wifi config changed."); + if (network_wifi_sta_config_changed()) { + ESP_LOGD(TAG, "Wifi Config changed. Saving it."); + network_wifi_save_sta_config(); + } + ESP_LOGD(TAG, "Updating the ip info json."); + network_interface_coexistence(State_Machine); + nm->wifi_connected = true; + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_CONNECTED_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_LOST_CONNECTION: + result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_LOST_CONNECTION_STATE],__FUNCTION__); + break; + default: + result = EVENT_UN_HANDLED; + break; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_CONNECTED_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + FREE_AND_NULL(((network_t *)State_Machine)->event_parameters->disconnected_event); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + + +/********************************************************************************************* + * WIFI_USER_DISCONNECTED_STATE + */ +static state_machine_result_t WIFI_USER_DISCONNECTED_STATE_entry_handler(state_machine_t* const State_Machine) { + network_handler_entry_print(State_Machine,true); + ESP_LOGD(TAG, " WiFi disconnected by user"); + network_wifi_clear_config(); + network_status_update_ip_info(UPDATE_USER_DISCONNECT); + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_USER_DISCONNECTED_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + network_handler_print(State_Machine,true); + state_machine_result_t result = EVENT_HANDLED; + switch (State_Machine->Event) { + case EN_LOST_CONNECTION: + // this is a success! we're actually asking to disconnect + result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__); + break; + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_USER_DISCONNECTED_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +/********************************************************************************************* + * WIFI_LOST_CONNECTION_STATE + */ +static state_machine_result_t WIFI_LOST_CONNECTION_STATE_entry_handler(state_machine_t* const State_Machine) { + network_t* const nm = (network_t *)State_Machine; + network_handler_entry_print(State_Machine,true); + ESP_LOGE(TAG, " WiFi Connection lost."); + messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "WiFi Connection lost"); + network_status_update_ip_info(UPDATE_LOST_CONNECTION); + network_status_safe_reset_sta_ip_string(); + if (nm->last_connected > 0) + nm->total_connected_time += ((esp_timer_get_time() - nm->last_connected) / (1000 * 1000)); + nm->last_connected = 0; + nm->num_disconnect++; + ESP_LOGW(TAG, " Wifi disconnected. Number of disconnects: %d, Average time connected: %d", nm->num_disconnect, nm->num_disconnect > 0 ? (nm->total_connected_time / nm->num_disconnect) : 0); + if (nm->retries < WIFI_MANAGER_MAX_RETRY) { + nm->retries++; + ESP_LOGD(TAG, " Retrying connection connection, %d/%d.", nm->retries, WIFI_MANAGER_MAX_RETRY); + network_connect_active_ssid( State_Machine); + } else { + /* In this scenario the connection was lost beyond repair */ + nm->retries = 0; + ESP_LOGD(TAG,"Checking if ethernet interface is connected"); + if (network_is_interface_connected(nm->eth_netif)) { + ESP_LOGW(TAG, "Cannot connect to Wifi. Falling back to Ethernet "); + network_async(EN_ETHERNET_FALLBACK); + } else { + network_status_update_ip_info(UPDATE_LOST_CONNECTION); + wifi_mode_t mode; + ESP_LOGW(TAG, " All connect retry attempts failed."); + + /* put us in softAP mode first */ + esp_wifi_get_mode(&mode); + if (WIFI_MODE_APSTA != mode) { + nm->STA_duration = STA_POLLING_MIN; + network_async_configure(); + } else if (nm->STA_duration < STA_POLLING_MAX) { + nm->STA_duration *= 1.25; + } + + /* keep polling for existing connection */ + network_set_timer(nm->STA_duration); + ESP_LOGD(TAG, " STA search slow polling of %d", nm->STA_duration); + } + } + + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t WIFI_LOST_CONNECTION_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + state_machine_result_t result = EVENT_HANDLED; + network_handler_print(State_Machine,true); + switch (State_Machine->Event) { + case EN_CONFIGURE: + result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__); + break; + case EN_TIMER: + result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_LOST_CONNECTION_STATE],__FUNCTION__); + break; + case EN_CONNECT: + result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONNECTING_STATE],__FUNCTION__); + break; + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t WIFI_LOST_CONNECTION_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + + +/********************************************************************************************* + * ETH_ACTIVE_CONNECTED_STATE + */ +static state_machine_result_t ETH_ACTIVE_CONNECTED_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_status_update_ip_info(UPDATE_ETHERNET_CONNECTED); + nm->ethernet_connected = true; + // start a wifi Scan so web ui is populated with available entries + NETWORK_EXECUTE_CB(State_Machine); + network_handler_entry_print(State_Machine,false); + return EVENT_HANDLED; +} +static state_machine_result_t ETH_ACTIVE_CONNECTED_STATE_handler(state_machine_t* const State_Machine) { + HANDLE_GLOBAL_EVENT(State_Machine); + state_machine_result_t result = EVENT_HANDLED; + network_handler_print(State_Machine,true); + switch (State_Machine->Event) { + case EN_TIMER: + ESP_LOGD(TAG, "Ignoring ethernet link up timer check"); + result= EVENT_HANDLED; + break; + default: + result= EVENT_UN_HANDLED; + } + network_handler_print(State_Machine,false); + return result; +} +static state_machine_result_t ETH_ACTIVE_CONNECTED_STATE_exit_handler(state_machine_t* const State_Machine) { + network_exit_handler_print(State_Machine,true); + network_exit_handler_print(State_Machine,false); + return EVENT_HANDLED; +} + +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); + 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_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); + 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_t *)state_machine)->source_state = source; + return result; +} + +static void network_interface_coexistence(state_machine_t* state_machine) { + // this function is called whenever both wifi and ethernet are + // found to be active at the same time + network_t* nm = (network_t *)state_machine; + if (nm->wifi_connected && state_machine->Event == EN_ETH_GOT_IP) { + char* eth_reboot = config_alloc_get_default(NVS_TYPE_STR, "eth_boot", "N", 0); + network_prioritize_wifi(false); + if (strcasecmp(eth_reboot, "N")) { + ESP_LOGW(TAG, "Option eth_reboot set to reboot when ethernet is connected. Rebooting"); + simple_restart(); + } else { + ESP_LOGW(TAG, "Option eth_reboot set to not reboot when ethernet is connected. Using Wifi interface until next reboot"); + } + FREE_AND_NULL(eth_reboot); + } else if (get_root(state_machine->State)->Id == NETWORK_ETH_ACTIVE_STATE){ + messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Wifi Connected with Ethernet active. System reload needed"); + simple_restart(); + } +} \ No newline at end of file diff --git a/components/wifi-manager/network_status.c b/components/wifi-manager/network_status.c index ebee7c82..edc9d862 100644 --- a/components/wifi-manager/network_status.c +++ b/components/wifi-manager/network_status.c @@ -1,3 +1,7 @@ +#ifdef NETWORK_STATUS_LOG_LEVEL +#define LOG_LOCAL_LEVEL NETWORK_STATUS_LOG_LEVEL +#endif + #include "network_status.h" #include #include "bt_app_core.h" @@ -16,10 +20,12 @@ #define CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases" #endif static const char TAG[] = "network_status"; -SemaphoreHandle_t wifi_manager_json_mutex = NULL; -SemaphoreHandle_t wifi_manager_sta_ip_mutex = NULL; +SemaphoreHandle_t network_status_json_mutex = NULL; +static TaskHandle_t network_json_locked_task = NULL; +SemaphoreHandle_t network_status_ip_address_mutex = NULL; +static TaskHandle_t network_status_ip_address_locked_task = NULL; char* release_url = NULL; -char* wifi_manager_sta_ip = NULL; +char* network_status_ip_address = NULL; char* ip_info_json = NULL; cJSON* ip_info_cjson = NULL; static char lms_server_ip[IP4ADDR_STRLEN_MAX] = {0}; @@ -33,11 +39,11 @@ void init_network_status() { chained_notify = server_notify; server_notify = connect_notify; ESP_LOGD(TAG, "init_network_status. Creating mutexes"); - wifi_manager_json_mutex = xSemaphoreCreateMutex(); - wifi_manager_sta_ip_mutex = xSemaphoreCreateMutex(); + network_status_json_mutex = xSemaphoreCreateMutex(); + network_status_ip_address_mutex = xSemaphoreCreateMutex(); ip_info_json = NULL; ESP_LOGD(TAG, "init_network_status. Creating status json structure"); - ip_info_cjson = wifi_manager_clear_ip_info_json(&ip_info_cjson); + ip_info_cjson = network_status_clear_ip_info_json(&ip_info_cjson); ESP_LOGD(TAG, "Getting release url "); char* release_url = (char*)config_alloc_get_default(NVS_TYPE_STR, "release_url", QUOTE(CONFIG_SQUEEZELITE_ESP32_RELEASE_URL), 0); if (release_url == NULL) { @@ -46,57 +52,73 @@ void init_network_status() { ESP_LOGD(TAG, "Found release url %s", release_url); } ESP_LOGD(TAG, "About to set the STA IP String to 0.0.0.0"); - wifi_manager_sta_ip = (char*)malloc(STA_IP_LEN); - wifi_manager_safe_update_sta_ip_string(NULL); + network_status_ip_address = (char*)malloc_init_external(STA_IP_LEN); + network_status_safe_update_sta_ip_string(NULL); } void destroy_network_status() { FREE_AND_NULL(release_url); FREE_AND_NULL(ip_info_json); - FREE_AND_NULL(wifi_manager_sta_ip); + FREE_AND_NULL(network_status_ip_address); cJSON_Delete(ip_info_cjson); - vSemaphoreDelete(wifi_manager_json_mutex); - wifi_manager_json_mutex = NULL; - vSemaphoreDelete(wifi_manager_sta_ip_mutex); - wifi_manager_sta_ip_mutex = NULL; + vSemaphoreDelete(network_status_json_mutex); + network_status_json_mutex = NULL; + vSemaphoreDelete(network_status_ip_address_mutex); + network_status_ip_address_mutex = NULL; ip_info_cjson = NULL; } -cJSON* wifi_manager_get_new_json(cJSON** old) { - ESP_LOGV(TAG, "wifi_manager_get_new_json called"); +cJSON* network_status_get_new_json(cJSON** old) { + ESP_LOGV(TAG, "network_status_get_new_json called"); cJSON* root = *old; if (root != NULL) { cJSON_Delete(root); *old = NULL; } - ESP_LOGV(TAG, "wifi_manager_get_new_json done"); + ESP_LOGV(TAG, "network_status_get_new_json done"); return cJSON_CreateObject(); } -cJSON* wifi_manager_clear_ip_info_json(cJSON** old) { - ESP_LOGV(TAG, "wifi_manager_clear_ip_info_json called"); - cJSON* root = wifi_manager_get_basic_info(old); - ESP_LOGV(TAG, "wifi_manager_clear_ip_info_json done"); +cJSON* network_status_clear_ip_info_json(cJSON** old) { + ESP_LOGV(TAG, "network_status_clear_ip_info_json called"); + cJSON* root = network_status_get_basic_info(old); + cJSON_DeleteItemFromObjectCaseSensitive(root, "ip"); + cJSON_DeleteItemFromObjectCaseSensitive(root, "netmask"); + cJSON_DeleteItemFromObjectCaseSensitive(root, "gw"); + cJSON_DeleteItemFromObjectCaseSensitive(root, "rssi"); + cJSON_DeleteItemFromObjectCaseSensitive(root, "ssid"); + cJSON_DeleteItemFromObjectCaseSensitive(root, "eth"); + + ESP_LOGV(TAG, "network_status_clear_ip_info_json done"); return root; } void network_status_clear_ip() { - if (wifi_manager_lock_json_buffer(portMAX_DELAY)) { - ip_info_cjson = wifi_manager_clear_ip_info_json(&ip_info_cjson); - wifi_manager_unlock_json_buffer(); + if (network_status_lock_json_buffer(portMAX_DELAY)) { + ip_info_cjson = network_status_clear_ip_info_json(&ip_info_cjson); + network_status_unlock_json_buffer(); } } -char* wifi_manager_alloc_get_ip_info_json() { +char* network_status_alloc_get_ip_info_json() { return cJSON_PrintUnformatted(ip_info_cjson); } -void wifi_manager_unlock_json_buffer() { +void network_status_unlock_json_buffer() { ESP_LOGV(TAG, "Unlocking json buffer!"); - xSemaphoreGive(wifi_manager_json_mutex); + network_json_locked_task = NULL; + xSemaphoreGive(network_status_json_mutex); } -bool wifi_manager_lock_json_buffer(TickType_t xTicksToWait) { +bool network_status_lock_json_buffer(TickType_t xTicksToWait) { ESP_LOGV(TAG, "Locking json buffer"); - if (wifi_manager_json_mutex) { - if (xSemaphoreTake(wifi_manager_json_mutex, xTicksToWait) == pdTRUE) { + + TaskHandle_t calling_task = xTaskGetCurrentTaskHandle(); + if (calling_task == network_json_locked_task) { + ESP_LOGV(TAG, "json buffer already locked to current task"); + return true; + } + + if (network_status_json_mutex) { + if (xSemaphoreTake(network_status_json_mutex, xTicksToWait) == pdTRUE) { ESP_LOGV(TAG, "Json buffer locked!"); + network_json_locked_task = calling_task; return true; } else { ESP_LOGE(TAG, "Semaphore take failed. Unable to lock json buffer mutex"); @@ -108,9 +130,15 @@ bool wifi_manager_lock_json_buffer(TickType_t xTicksToWait) { } } -bool wifi_manager_lock_sta_ip_string(TickType_t xTicksToWait) { - if (wifi_manager_sta_ip_mutex) { - if (xSemaphoreTake(wifi_manager_sta_ip_mutex, xTicksToWait) == pdTRUE) { +bool network_status_lock_sta_ip_string(TickType_t xTicksToWait) { + TaskHandle_t calling_task = xTaskGetCurrentTaskHandle(); + if (calling_task == network_status_ip_address_locked_task) { + ESP_LOGD(TAG, "json buffer already locked to current task "); + return true; + } + if (network_status_ip_address_mutex) { + if (xSemaphoreTake(network_status_ip_address_mutex, xTicksToWait) == pdTRUE) { + network_status_ip_address_locked_task = calling_task; return true; } else { return false; @@ -120,26 +148,27 @@ bool wifi_manager_lock_sta_ip_string(TickType_t xTicksToWait) { } } -void wifi_manager_unlock_sta_ip_string() { - xSemaphoreGive(wifi_manager_sta_ip_mutex); +void network_status_unlock_sta_ip_string() { + network_status_ip_address_locked_task = NULL; + xSemaphoreGive(network_status_ip_address_mutex); } -void wifi_manager_safe_update_sta_ip_string(esp_ip4_addr_t* ip4) { - if (wifi_manager_lock_sta_ip_string(portMAX_DELAY)) { - strcpy(wifi_manager_sta_ip, ip4 != NULL ? ip4addr_ntoa(ip4) : "0.0.0.0"); - ESP_LOGD(TAG, "Set STA IP String to: %s", wifi_manager_sta_ip); - wifi_manager_unlock_sta_ip_string(); +void network_status_safe_update_sta_ip_string(esp_ip4_addr_t* ip4) { + if (network_status_lock_sta_ip_string(portMAX_DELAY)) { + strcpy(network_status_ip_address, ip4 != NULL ? ip4addr_ntoa((ip4_addr_t*)ip4) : "0.0.0.0"); + ESP_LOGD(TAG, "Set STA IP String to: %s", network_status_ip_address); + network_status_unlock_sta_ip_string(); } } -void wifi_manager_safe_reset_sta_ip_string() { - if (wifi_manager_lock_sta_ip_string(portMAX_DELAY)) { - strcpy(wifi_manager_sta_ip, "0.0.0.0"); - ESP_LOGD(TAG, "Set STA IP String to: %s", wifi_manager_sta_ip); - wifi_manager_unlock_sta_ip_string(); +void network_status_safe_reset_sta_ip_string() { + if (network_status_lock_sta_ip_string(portMAX_DELAY)) { + strcpy(network_status_ip_address, "0.0.0.0"); + ESP_LOGD(TAG, "Set STA IP String to: %s", network_status_ip_address); + network_status_unlock_sta_ip_string(); } } -char* wifi_manager_get_sta_ip_string() { - return wifi_manager_sta_ip; +char* network_status_get_sta_ip_string() { + return network_status_ip_address; } 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)); @@ -152,98 +181,47 @@ static void connect_notify(in_addr_t ip, u16_t hport, u16_t cport) { set_lms_server_details(ip, hport, cport); if (chained_notify) (*chained_notify)(ip, hport, cport); - network_manager_async_update_status(); + network_async_update_status(); } -void wifi_manager_update_basic_info() { - int32_t total_connected_time = 0; - int64_t last_connected = 0; - uint16_t num_disconnect = 0; - network_wifi_get_stats(&total_connected_time, &last_connected, &num_disconnect); - if (wifi_manager_lock_json_buffer(portMAX_DELAY)) { - monitor_gpio_t* mgpio = get_jack_insertion_gpio(); - cJSON* voltage = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "Voltage"); - if (voltage) { - cJSON_SetNumberValue(voltage, battery_value_svc()); - } - cJSON* bt_status = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "bt_status"); - if (bt_status) { - cJSON_SetNumberValue(bt_status, bt_app_source_get_a2d_state()); - } - cJSON* bt_sub_status = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "bt_sub_status"); - if (bt_sub_status) { - cJSON_SetNumberValue(bt_sub_status, bt_app_source_get_media_state()); - } - cJSON* jack = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "Jack"); - if (jack) { - jack->type = mgpio->gpio >= 0 && jack_inserted_svc() ? cJSON_True : cJSON_False; - } - cJSON* disconnect_count = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "disconnect_count"); - if (disconnect_count) { - cJSON_SetNumberValue(disconnect_count, num_disconnect); - } - cJSON* avg_conn_time = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "avg_conn_time"); - if (avg_conn_time) { - cJSON_SetNumberValue(avg_conn_time, num_disconnect > 0 ? (total_connected_time / num_disconnect) : 0); - } - if (lms_server_cport > 0) { - cJSON* value = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "lms_cport"); - if (value) { - cJSON_SetNumberValue(value, lms_server_cport); - } else { - cJSON_AddNumberToObject(ip_info_cjson, "lms_cport", lms_server_cport); - } - } - - if (lms_server_port > 0) { - cJSON* value = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "lms_port"); - if (value) { - cJSON_SetNumberValue(value, lms_server_port); - } else { - cJSON_AddNumberToObject(ip_info_cjson, "lms_port", lms_server_port); - } - } - - if (strlen(lms_server_ip) > 0) { - cJSON* value = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "lms_ip"); - if (!value) { - // only create if it does not exist. Since we're creating a reference - // to a char buffer, updates to cJSON aren't needed - cJSON_AddItemToObject(ip_info_cjson, "lms_ip", cJSON_CreateStringReference(lms_server_ip)); - } - } - if (network_ethernet_enabled()) { - cJSON* eth = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "eth_up"); - if (eth) { - eth->type = network_ethernet_is_up() ? cJSON_True : cJSON_False; - } - } - wifi_manager_unlock_json_buffer(); - } +void network_status_update_basic_info() { + // locking happens below this level + network_status_get_basic_info(&ip_info_cjson); } cJSON* network_status_update_string(cJSON** root, const char* key, const char* value) { - if (*root == NULL) { - *root = cJSON_CreateObject(); - } + if (network_status_lock_json_buffer(portMAX_DELAY)) { + if (*root == NULL) { + *root = cJSON_CreateObject(); + if (*root == NULL) { + ESP_LOGE(TAG, "Error creating cJSON object!"); + } + } - if (!key || !value || strlen(key) == 0) - return *root; - cJSON* cjsonvalue = cJSON_GetObjectItemCaseSensitive(*root, key); - if (cjsonvalue && strcasecmp(cJSON_GetStringValue(cjsonvalue), value) != 0) { - ESP_LOGD(TAG, "Value %s changed from %s to %s", key, cJSON_GetStringValue(cjsonvalue), value); - cJSON_SetValuestring(cjsonvalue, value); + if (!key || !value || strlen(key) == 0) { + ESP_LOGE(TAG, "network_status_update_string. Invalid key or value passed! key: %s, value: %s", STR_OR_ALT(key, ""), STR_OR_ALT(value, "")); + network_status_unlock_json_buffer(); + return *root; + } + cJSON* cjsonvalue = cJSON_GetObjectItemCaseSensitive(*root, key); + if (cjsonvalue && strcasecmp(cJSON_GetStringValue(cjsonvalue), value) != 0) { + ESP_LOGD(TAG, "Value %s changed from %s to %s", key, cJSON_GetStringValue(cjsonvalue), value); + cJSON_SetValuestring(cjsonvalue, value); + } else { + cJSON_AddItemToObject(*root, key, cJSON_CreateString(value)); + } + network_status_unlock_json_buffer(); } else { - cJSON_AddItemToObject(*root, key, cJSON_CreateString(value)); + ESP_LOGW(TAG, "Unable to lock status json buffer. "); } return *root; } cJSON* network_status_update_number(cJSON** root, const char* key, int value) { - if (wifi_manager_lock_json_buffer(portMAX_DELAY)) { + if (network_status_lock_json_buffer(portMAX_DELAY)) { if (*root == NULL) { *root = cJSON_CreateObject(); } - if (key && value && strlen(key) != 0) { + if (key && strlen(key) != 0) { cJSON* cjsonvalue = cJSON_GetObjectItemCaseSensitive(*root, key); if (cjsonvalue) { cJSON_SetNumberValue(cjsonvalue, value); @@ -251,14 +229,14 @@ cJSON* network_status_update_number(cJSON** root, const char* key, int value) { cJSON_AddNumberToObject(*root, key, value); } } - wifi_manager_unlock_json_buffer(); + network_status_unlock_json_buffer(); } else { ESP_LOGW(TAG, "Unable to lock status json buffer. "); } return *root; } cJSON* network_status_update_float(cJSON** root, const char* key, float value) { - if (wifi_manager_lock_json_buffer(portMAX_DELAY)) { + if (network_status_lock_json_buffer(portMAX_DELAY)) { if (*root == NULL) { *root = cJSON_CreateObject(); } @@ -271,14 +249,14 @@ cJSON* network_status_update_float(cJSON** root, const char* key, float value) { cJSON_AddNumberToObject(*root, key, value); } } - wifi_manager_unlock_json_buffer(); + network_status_unlock_json_buffer(); } else { ESP_LOGW(TAG, "Unable to lock status json buffer. "); } return *root; } cJSON* network_status_update_bool(cJSON** root, const char* key, bool value) { - if (wifi_manager_lock_json_buffer(portMAX_DELAY)) { + if (network_status_lock_json_buffer(portMAX_DELAY)) { if (*root == NULL) { *root = cJSON_CreateObject(); } @@ -291,91 +269,110 @@ cJSON* network_status_update_bool(cJSON** root, const char* key, bool value) { cJSON_AddBoolToObject(*root, key, value); } } - wifi_manager_unlock_json_buffer(); + network_status_unlock_json_buffer(); } else { ESP_LOGW(TAG, "Unable to lock status json buffer. "); } return *root; } -cJSON* wifi_manager_get_basic_info(cJSON** old) { - int32_t total_connected_time = 0; - int64_t last_connected = 0; - uint16_t num_disconnect = 0; - network_wifi_get_stats(&total_connected_time, &last_connected, &num_disconnect); +cJSON* network_status_get_basic_info(cJSON** old) { + if (network_status_lock_json_buffer(portMAX_DELAY)) { + network_t* nm = network_get_state_machine(); + monitor_gpio_t* mgpio = get_jack_insertion_gpio(); + const esp_app_desc_t* desc = esp_ota_get_app_description(); - monitor_gpio_t* mgpio = get_jack_insertion_gpio(); - const esp_app_desc_t* desc = esp_ota_get_app_description(); - ESP_LOGV(TAG, "wifi_manager_get_basic_info called"); - cJSON* root = network_status_update_string(&root, "project_name", desc->project_name); + *old = network_status_update_string(old, "project_name", desc->project_name); #ifdef CONFIG_FW_PLATFORM_NAME - root = network_status_update_string(&root, "platform_name", CONFIG_FW_PLATFORM_NAME); + *old = network_status_update_string(old, "platform_name", CONFIG_FW_PLATFORM_NAME); #endif - root = network_status_update_string(&root, "version", desc->version); - if (release_url != NULL) - root = network_status_update_string(&root, "release_url", release_url); - root = network_status_update_number(&root, "recovery", is_recovery_running ? 1 : 0); - root = network_status_update_bool(&root, "Jack", mgpio->gpio >= 0 && jack_inserted_svc()); - root = network_status_update_float(&root, "Voltage", battery_value_svc()); - root = network_status_update_number(&root, "disconnect_count", num_disconnect); - root = network_status_update_float(&root, "avg_conn_time", num_disconnect > 0 ? (total_connected_time / num_disconnect) : 0); - root = network_status_update_number(&root, "bt_status", bt_app_source_get_a2d_state()); - root = network_status_update_number(&root, "bt_sub_status", bt_app_source_get_media_state()); - + *old = network_status_update_string(old, "version", desc->version); + if (release_url != NULL) + *old = network_status_update_string(old, "release_url", release_url); + *old = network_status_update_number(old, "recovery", is_recovery_running ? 1 : 0); + *old = network_status_update_bool(old, "Jack", mgpio->gpio >= 0 && jack_inserted_svc()); + *old = network_status_update_float(old, "Voltage", battery_value_svc()); + *old = network_status_update_number(old, "disconnect_count", nm->num_disconnect); + *old = network_status_update_float(old, "avg_conn_time", nm->num_disconnect > 0 ? (nm->total_connected_time / nm->num_disconnect) : 0); + *old = network_status_update_number(old, "bt_status", bt_app_source_get_a2d_state()); + *old = network_status_update_number(old, "bt_sub_status", bt_app_source_get_media_state()); #if CONFIG_I2C_LOCKED - root = network_status_update_bool(&root, "is_i2c_locked", true); + *old = network_status_update_bool(old, "is_i2c_locked", true); #else - root = network_status_update_bool(&root, "is_i2c_locked", false); + *old = network_status_update_bool(old, "is_i2c_locked", false); #endif - if (network_ethernet_enabled()) { - root = network_status_update_bool(&root, "eth_up", network_ethernet_is_up()); - } - ESP_LOGV(TAG, "wifi_manager_get_basic_info done"); - return root; -} + if (network_ethernet_enabled()) { + *old = network_status_update_bool(old, "eth_up", network_ethernet_is_up()); + } + if (lms_server_cport > 0) { + *old = network_status_update_number(old, "lms_cport", lms_server_cport); + } -void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code) { - ESP_LOGD(TAG, "wifi_manager_generate_ip_info_json called"); + if (lms_server_port > 0) { + *old = network_status_update_number(old, "lms_port", lms_server_port); + } - if (wifi_manager_lock_json_buffer(portMAX_DELAY)) { - /* generate the connection info with success */ - - ip_info_cjson = wifi_manager_get_basic_info(&ip_info_cjson); - wifi_manager_unlock_json_buffer(); + if (strlen(lms_server_ip) > 0) { + *old = network_status_update_string(old, "lms_ip", lms_server_ip); + } + ESP_LOGV(TAG, "network_status_get_basic_info done"); + network_status_unlock_json_buffer(); } else { ESP_LOGW(TAG, "Unable to lock status json buffer. "); } - ip_info_cjson = network_status_update_number(&ip_info_cjson, "urc", update_reason_code); - if (update_reason_code == UPDATE_CONNECTION_OK || update_reason_code == UPDATE_ETHERNET_CONNECTED) { - /* rest of the information is copied after the ssid */ - tcpip_adapter_ip_info_t ip_info; - esp_netif_get_ip_info(network_wifi_get_interface(), &ip_info); - - network_status_update_string(&ip_info_cjson, "ip", ip4addr_ntoa((ip4_addr_t*)&ip_info.ip)); - network_status_update_string(&ip_info_cjson, "netmask", ip4addr_ntoa((ip4_addr_t*)&ip_info.netmask)); - network_status_update_string(&ip_info_cjson, "gw", ip4addr_ntoa((ip4_addr_t*)&ip_info.gw)); - wifi_mode_t mode; - if (esp_wifi_get_mode(&mode) == ESP_OK && (mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA)) { - /* wifi is active, and associated to an AP */ - wifi_ap_record_t ap; - esp_wifi_sta_get_ap_info(&ap); - network_status_update_string(&ip_info_cjson, "ssid", ((char*)ap.ssid)); - network_status_update_number(&ip_info_cjson, "rssi", ap.rssi); - } - if (network_ethernet_is_up()) { - esp_netif_get_ip_info(network_ethernet_get_interface(), &ip_info); - cJSON* ethernet_ip = cJSON_CreateObject(); - cJSON_AddItemToObject(ethernet_ip, "ip", cJSON_CreateString(ip4addr_ntoa((ip4_addr_t*)&ip_info.ip))); - cJSON_AddItemToObject(ethernet_ip, "netmask", cJSON_CreateString(ip4addr_ntoa((ip4_addr_t*)&ip_info.netmask))); - cJSON_AddItemToObject(ethernet_ip, "gw", cJSON_CreateString(ip4addr_ntoa((ip4_addr_t*)&ip_info.gw))); - cJSON_AddItemToObject(ip_info_cjson, "eth", ethernet_ip); - } - } else { - cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "ip"); - cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "netmask"); - cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "gw"); - cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "rssi"); - cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "ssid"); - cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "eth"); - } - ESP_LOGV(TAG, "wifi_manager_generate_ip_info_json done"); + return *old; +} +void network_status_update_address(cJSON* root, esp_netif_ip_info_t* ip_info) { + if (!root || !ip_info) { + ESP_LOGE(TAG, "Cannor update IP address. JSON structure or ip_info is null"); + return; + } + network_status_update_string(&root, "ip", ip4addr_ntoa((ip4_addr_t*)&ip_info->ip)); + network_status_update_string(&root, "netmask", ip4addr_ntoa((ip4_addr_t*)&ip_info->netmask)); + network_status_update_string(&root, "gw", ip4addr_ntoa((ip4_addr_t*)&ip_info->gw)); +} +void network_status_update_ip_info(update_reason_code_t 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)) { + /* generate the connection info with success */ + + ip_info_cjson = network_status_get_basic_info(&ip_info_cjson); + ip_info_cjson = network_status_update_number(&ip_info_cjson, "urc", update_reason_code); + ESP_LOGD(TAG,"Updating ip info with reason code %d. Checking if Wifi interface is connected",update_reason_code); + if (network_is_interface_connected(network_wifi_get_interface())) { + + esp_netif_get_ip_info(network_wifi_get_interface(), &ip_info); + network_status_update_address(ip_info_cjson, &ip_info); + if (!network_wifi_is_ap_mode()) { + /* wifi is active, and associated to an AP */ + wifi_ap_record_t ap; + esp_wifi_sta_get_ap_info(&ap); + network_status_update_string(&ip_info_cjson, "ssid", ((char*)ap.ssid)); + network_status_update_number(&ip_info_cjson, "rssi", ap.rssi); + } + + } else { + cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "ip"); + cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "netmask"); + cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "gw"); + cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "rssi"); + cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "ssid"); + } + ESP_LOGD(TAG,"Checking if ethernet interface is connected"); + if (network_is_interface_connected(network_ethernet_get_interface())) { + cJSON* ethernet_ip = cJSON_GetObjectItem(ip_info_cjson, "eth"); + if (!ethernet_ip) { + ethernet_ip = cJSON_CreateObject(); + cJSON_AddItemToObject(ip_info_cjson, "eth", ethernet_ip); + } + esp_netif_get_ip_info(network_ethernet_get_interface(), &ip_info); + network_status_update_address(ethernet_ip, &ip_info); + } else { + cJSON_DeleteItemFromObjectCaseSensitive(ip_info_cjson, "eth"); + } + network_status_unlock_json_buffer(); + } else { + ESP_LOGW(TAG, "Unable to lock status json buffer. "); + } + ESP_LOGV(TAG, "wifi_status_generate_ip_info_json done"); } diff --git a/components/wifi-manager/network_status.h b/components/wifi-manager/network_status.h index 25f46cf4..19a7dfb4 100644 --- a/components/wifi-manager/network_status.h +++ b/components/wifi-manager/network_status.h @@ -5,7 +5,7 @@ extern "C" { #endif -char* wifi_manager_alloc_get_ip_info_json(); +char* network_status_alloc_get_ip_info_json(); /** * @brief Tries to get access to json buffer mutex. * @@ -21,39 +21,39 @@ char* wifi_manager_alloc_get_ip_info_json(); * @param xTicksToWait The time in ticks to wait for the semaphore to become available. * @return true in success, false otherwise. */ -bool wifi_manager_lock_json_buffer(TickType_t xTicksToWait); +bool network_status_lock_json_buffer(TickType_t xTicksToWait); /** * @brief Releases the json buffer mutex. */ -void wifi_manager_unlock_json_buffer(); +void network_status_unlock_json_buffer(); -bool wifi_manager_lock_sta_ip_string(TickType_t xTicksToWait); -void wifi_manager_unlock_sta_ip_string(); +bool network_status_lock_sta_ip_string(TickType_t xTicksToWait); +void network_status_unlock_sta_ip_string(); /** * @brief gets the string representation of the STA IP address, e.g.: "192.168.1.69" */ -char* wifi_manager_get_sta_ip_string(); +char* network_status_get_sta_ip_string(); /** * @brief thread safe char representation of the STA IP update */ -void wifi_manager_safe_update_sta_ip_string(esp_ip4_addr_t * ip4); +void network_status_safe_update_sta_ip_string(esp_ip4_addr_t * ip4); /** * @brief Generates the connection status json: ssid and IP addresses. - * @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful. + * @note This is not thread-safe and should be called only if network_status_lock_json_buffer call is successful. */ -void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code); +void network_status_update_ip_info(update_reason_code_t update_reason_code); void init_network_status(); void destroy_network_status(); -cJSON* wifi_manager_get_basic_info(cJSON** old); +cJSON* network_status_get_basic_info(cJSON** old); -void wifi_manager_update_basic_info(); +void network_status_update_basic_info(); void network_status_clear_ip(); -void wifi_manager_safe_reset_sta_ip_string(); +void network_status_safe_reset_sta_ip_string(); #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/components/wifi-manager/network_wifi.c b/components/wifi-manager/network_wifi.c index b4de4489..392ac6e4 100644 --- a/components/wifi-manager/network_wifi.c +++ b/components/wifi-manager/network_wifi.c @@ -1,3 +1,6 @@ +#ifdef NETWORK_WIFI_LOG_LEVEL +#define LOG_LOCAL_LEVEL NETWORK_WIFI_LOG_LEVEL +#endif #include "network_wifi.h" #include #include "cJSON.h" @@ -13,215 +16,639 @@ #include "network_status.h" #include "nvs.h" #include "nvs_flash.h" +#include "nvs_utilities.h" #include "platform_config.h" #include "platform_esp32.h" #include "tools.h" #include "trace.h" - static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); -static void network_manager_wifi_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) ; static char* get_disconnect_code_desc(uint8_t reason); - +esp_err_t network_wifi_get_blob(void* target, size_t size, const char* key); +static inline const char* ssid_string(const wifi_sta_config_t* sta); +static inline const char* password_string(const wifi_sta_config_t* sta); cJSON* accessp_cjson = NULL; -wifi_config_t* wifi_manager_config_sta = NULL; + static const char TAG[] = "network_wifi"; -const char wifi_manager_nvs_namespace[] = "config"; +const char network_wifi_nvs_namespace[] = "config"; +const char ap_list_nsv_namespace[] = "aplist"; +/* rrm ctx */ +//Roaming support - int rrm_ctx = 0; -uint16_t ap_num = MAX_AP_NUM; +uint16_t ap_num = 0; - - - -esp_netif_t *wifi_netif; +esp_netif_t* wifi_netif; +esp_netif_t* wifi_ap_netif; wifi_ap_record_t* accessp_records = NULL; -/* wifi scanner config */ -wifi_scan_config_t scan_config = { - .ssid = 0, - .bssid = 0, - .channel = 0, - .show_hidden = true}; -#ifndef STR_OR_BLANK -#define STR_OR_BLANK(p) p == NULL ? "" : p -#endif +#define UINT_TO_STRING(val) \ + static char loc[sizeof(val) + 1]; \ + memset(loc, 0x00, sizeof(loc)); \ + strlcpy(loc, (char*)val, sizeof(loc)); \ + return loc; -esp_netif_t *network_wifi_get_interface(){ - return wifi_netif; +static inline const char* ssid_string(const wifi_sta_config_t* sta) { + UINT_TO_STRING(sta->ssid); } - -void init_network_wifi() { - ESP_LOGD(TAG, "WIFI Starting."); - accessp_cjson = NULL; - accessp_cjson = wifi_manager_clear_ap_list_json(&accessp_cjson); - wifi_manager_config_sta = (wifi_config_t*)malloc(sizeof(wifi_config_t)); - memset(wifi_manager_config_sta, 0x00, sizeof(wifi_config_t)); - ESP_LOGD(TAG, "Init. "); - wifi_netif = esp_netif_create_default_wifi_sta(); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - ESP_LOGD(TAG, "Handlers"); - //wifi_manager_register_handlers(); - ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - ESP_EVENT_ANY_ID, - &network_wifi_event_handler, - NULL, - NULL)); - esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &network_manager_wifi_ip_event_handler, NULL); - ESP_LOGD(TAG, "Storage"); - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_LOGD(TAG, "Set Mode"); - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); - ESP_LOGD(TAG, "Start"); - ESP_ERROR_CHECK(esp_wifi_start()); +static inline const char* password_string(const wifi_sta_config_t* sta) { + UINT_TO_STRING(sta->password); } -void destroy_network_wifi() { - cJSON_Delete(accessp_cjson); - accessp_cjson = NULL; - FREE_AND_NULL(wifi_manager_config_sta); -} -bool network_wifi_sta_config_changed() { - bool changed = true; - wifi_config_t wifi_manager_config_sta_existing; - if(wifi_manager_config_sta && wifi_manager_load_wifi_sta_config(&wifi_manager_config_sta_existing )){ - changed = strcmp( (char *)wifi_manager_config_sta_existing.sta.ssid,(char *)wifi_manager_config_sta->sta.ssid ) !=0 || - strcmp((char *) wifi_manager_config_sta_existing.sta.password,(char *)wifi_manager_config_sta->sta.password ) !=0; - } - return changed; - +static inline const char* ap_ssid_string(const wifi_ap_record_t* ap) { + UINT_TO_STRING(ap->ssid); } +typedef struct known_access_point { + char* ssid; + char* password; + bool found; + uint8_t bssid[6]; /**< MAC address of AP */ + uint8_t primary; /**< channel of AP */ + wifi_auth_mode_t authmode; /**< authmode of AP */ + uint32_t phy_11b : 1; /**< bit: 0 flag to identify if 11b mode is enabled or not */ + uint32_t phy_11g : 1; /**< bit: 1 flag to identify if 11g mode is enabled or not */ + uint32_t phy_11n : 1; /**< bit: 2 flag to identify if 11n mode is enabled or not */ + uint32_t phy_lr : 1; /**< bit: 3 flag to identify if low rate is enabled or not */ + time_t next_try; + SLIST_ENTRY(known_access_point) + next; //!< next callback +} known_access_point_t; -esp_err_t network_wifi_save_sta_config() { - nvs_handle handle; - esp_err_t esp_err; - ESP_LOGD(TAG, "Config Save"); +/** linked list of command structures */ +static EXT_RAM_ATTR SLIST_HEAD(ap_list, known_access_point) s_ap_list; +known_access_point_t* network_wifi_get_ap_entry(const char* ssid) { + known_access_point_t* it; - if (wifi_manager_config_sta) { - esp_err = nvs_open(wifi_manager_nvs_namespace, NVS_READWRITE, &handle); - if (esp_err != ESP_OK) { - ESP_LOGE(TAG, "%s failure. Error %s", wifi_manager_nvs_namespace, esp_err_to_name(esp_err)); - return esp_err; - } - - esp_err = nvs_set_blob(handle, "ssid", wifi_manager_config_sta->sta.ssid, sizeof(wifi_manager_config_sta->sta.ssid)); - if (esp_err != ESP_OK) { - ESP_LOGE(TAG, "ssid (%s). Error %s", wifi_manager_nvs_namespace, esp_err_to_name(esp_err)); - return esp_err; - } - - esp_err = nvs_set_blob(handle, "password", wifi_manager_config_sta->sta.password, sizeof(wifi_manager_config_sta->sta.password)); - if (esp_err != ESP_OK) { - ESP_LOGE(TAG, "pass (%s). Error %s", wifi_manager_nvs_namespace, esp_err_to_name(esp_err)); - return esp_err; - } - - esp_err = nvs_commit(handle); - if (esp_err != ESP_OK) { - ESP_LOGE(TAG, "Commit error: %s", esp_err_to_name(esp_err)); - messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Unable to save wifi credentials. %s", esp_err_to_name(esp_err)); - return esp_err; - } - nvs_close(handle); - - ESP_LOGD(TAG, "saved: ssid:%s password:%s", wifi_manager_config_sta->sta.ssid, wifi_manager_config_sta->sta.password); + if (!ssid || strlen(ssid) == 0) { + ESP_LOGW(TAG, "network_wifi_get_ap_entry Invalid SSID %s", !ssid ? "IS NULL" : "IS BLANK"); + return NULL; } + SLIST_FOREACH(it, &s_ap_list, next) { + ESP_LOGD(TAG, "Looking for SSID %s = %s ?", ssid, it->ssid); + if (strcmp(it->ssid, ssid) == 0) { + ESP_LOGD(TAG, "network_wifi_get_ap_entry SSID %s found! ", ssid); + return it; + } + } + return NULL; +} +void network_wifi_remove_ap_entry(const char* ssid) { + if (!ssid || strlen(ssid) == 0) { + ESP_LOGE(TAG, "network_wifi_remove_ap_entry error empty SSID"); + } + known_access_point_t* it = network_wifi_get_ap_entry(ssid); + if (it) { + ESP_LOGW(TAG, "Removing %s from known list of access points", ssid); + FREE_AND_NULL(it->ssid); + FREE_AND_NULL(it->password); + SLIST_REMOVE(&s_ap_list, it, known_access_point, next); + FREE_AND_NULL(it); + } +} +void network_wifi_empty_known_list() { + known_access_point_t* it; + while ((it = SLIST_FIRST(&s_ap_list)) != NULL) { + network_wifi_remove_ap_entry(it->ssid); + } +} + +const wifi_sta_config_t* network_wifi_get_active_config() { + static wifi_config_t config; + esp_err_t err = ESP_OK; + memset(&config, 0x00, sizeof(config)); + if ((err = esp_wifi_get_config(WIFI_IF_STA, &config)) == ESP_OK) { + return &config.sta; + } else { + ESP_LOGD(TAG, "Could not get wifi STA config: %s", esp_err_to_name(err)); + } + return NULL; +} + +size_t network_wifi_get_known_count() { + size_t count = 0; + known_access_point_t* it; + SLIST_FOREACH(it, &s_ap_list, next) { + count++; + } + return count; +} +esp_err_t network_wifi_add_ap(known_access_point_t* item) { + known_access_point_t* last = SLIST_FIRST(&s_ap_list); + if (last == NULL) { + SLIST_INSERT_HEAD(&s_ap_list, item, next); + } else { + known_access_point_t* it; + while ((it = SLIST_NEXT(last, next)) != NULL) { + last = it; + } + SLIST_INSERT_AFTER(last, item, next); + } return ESP_OK; } -bool wifi_manager_load_wifi_sta_config(wifi_config_t* config ){ - nvs_handle handle; - esp_err_t esp_err; - - ESP_LOGD(TAG, "Fetching wifi sta config."); - esp_err = nvs_open(wifi_manager_nvs_namespace, NVS_READONLY, &handle); - if (esp_err == ESP_OK) { - - - /* ssid */ - ESP_LOGD(TAG, "Fetching value for ssid."); - size_t sz = sizeof(config->sta.ssid); - uint8_t* buff = (uint8_t*)malloc(sizeof(uint8_t) * sz); - memset(buff, 0x00, sizeof(uint8_t) * sz); - esp_err = nvs_get_blob(handle, "ssid", buff, &sz); - if (esp_err != ESP_OK) { - ESP_LOGD(TAG, "No ssid found in nvs."); - FREE_AND_NULL(buff); - nvs_close(handle); - return false; +esp_err_t network_wifi_add_ap_copy(const known_access_point_t* known_ap) { + known_access_point_t* item = NULL; + esp_err_t err = ESP_OK; + + if (!known_ap) { + ESP_LOGE(TAG, "Invalid access point entry"); + return ESP_ERR_INVALID_ARG; + } + if (!known_ap->ssid || strlen(known_ap->ssid) == 0) { + ESP_LOGE(TAG, "Invalid access point ssid"); + return ESP_ERR_INVALID_ARG; + } + item = malloc_init_external(sizeof(known_access_point_t)); + if (item == NULL) { + ESP_LOGE(TAG, "Memory allocation failed"); + return ESP_ERR_NO_MEM; + } + item->ssid = strdup_psram(known_ap->ssid); + item->password = strdup_psram(known_ap->password); + memcpy(&item->bssid, known_ap->bssid, sizeof(item->bssid)); + item->primary = known_ap->primary; + item->authmode = known_ap->authmode; + item->phy_11b = known_ap->phy_11b; + item->phy_11g = known_ap->phy_11g; + item->phy_11n = known_ap->phy_11n; + item->phy_lr = known_ap->phy_lr; + err = network_wifi_add_ap(item); + return err; +} +const wifi_ap_record_t* network_wifi_get_ssid_info(const char* ssid) { + if (!accessp_records) + return NULL; + for (int i = 0; i < ap_num; i++) { + if (strcmp(ap_ssid_string(&accessp_records[i]), ssid) == 0) { + return &accessp_records[i]; } - memcpy(config->sta.ssid, buff, sizeof(config->sta.ssid)); - FREE_AND_NULL(buff); - ESP_LOGD(TAG, "wifi_manager_fetch_wifi_sta_config: ssid:%s ", config->sta.ssid); + } + return NULL; +} +esp_err_t network_wifi_add_ap_from_sta_copy(const wifi_sta_config_t* sta) { + known_access_point_t* item = NULL; + esp_err_t err = ESP_OK; + if (!sta) { + ESP_LOGE(TAG, "Invalid access point entry"); + return ESP_ERR_INVALID_ARG; + } + if (!sta->ssid || strlen((char*)sta->ssid) == 0) { + ESP_LOGE(TAG, "Invalid access point ssid"); + return ESP_ERR_INVALID_ARG; + } + item = malloc_init_external(sizeof(known_access_point_t)); + if (item == NULL) { + ESP_LOGE(TAG, "Memory allocation failed"); + return ESP_ERR_NO_MEM; + } + item->ssid = strdup_psram(ssid_string(sta)); + item->password = strdup_psram(password_string(sta)); + memcpy(&item->bssid, sta->bssid, sizeof(item->bssid)); + item->primary = sta->channel; + const wifi_ap_record_t* seen = network_wifi_get_ssid_info(item->ssid); + if (seen) { + item->authmode = seen->authmode; + item->phy_11b = seen->phy_11b; + item->phy_11g = seen->phy_11g; + item->phy_11n = seen->phy_11n; + item->phy_lr = seen->phy_lr; + } + err = network_wifi_add_ap(item); + return err; +} - /* password */ - sz = sizeof(config->sta.password); - buff = (uint8_t*)malloc(sizeof(uint8_t) * sz); - memset(buff, 0x00, sizeof(uint8_t) * sz); - esp_err = nvs_get_blob(handle, "password", buff, &sz); - if (esp_err != ESP_OK) { - // Don't take this as an error. This could be an opened access point? - ESP_LOGW(TAG, "No wifi password found in nvs"); +bool network_wifi_is_known_ap(const char* ssid) { + return network_wifi_get_ap_entry(ssid) != NULL; +} + +static bool network_wifi_was_ssid_seen(const char* ssid) { + if (!accessp_records || ap_num == 0 || ap_num == MAX_AP_NUM) { + return false; + } + for (int i = 0; i < ap_num; i++) { + if (strcmp(ap_ssid_string(&accessp_records[i]), ssid) == 0) { + return true; + } + } + return false; +} +void network_wifi_set_found_ap() { + known_access_point_t* it; + SLIST_FOREACH(it, &s_ap_list, next) { + if (network_wifi_was_ssid_seen(it->ssid)) { + it->found = true; } else { - memcpy(config->sta.password, buff, sizeof(config->sta.password)); - ESP_LOGD(TAG, "wifi_manager_fetch_wifi_sta_config: password:%s", config->sta.password); + it->found = false; } - FREE_AND_NULL(buff); - nvs_close(handle); - config->sta.scan_method = WIFI_ALL_CHANNEL_SCAN; - return config->sta.ssid[0] != '\0'; + } +} + +esp_err_t network_wifi_alloc_ap_json(known_access_point_t* item, char** json_string) { + esp_err_t err = ESP_OK; + if (!item || !json_string) { + return ESP_ERR_INVALID_ARG; + } + cJSON* cjson_item = cJSON_CreateObject(); + if (!cjson_item) { + ESP_LOGE(TAG, "Memory allocation failure. Cannot save ap json"); + return ESP_ERR_NO_MEM; + } + cJSON_AddStringToObject(cjson_item, "ssid", item->ssid); + cJSON_AddStringToObject(cjson_item, "pass", item->password); + cJSON_AddNumberToObject(cjson_item, "chan", item->primary); + cJSON_AddNumberToObject(cjson_item, "auth", item->authmode); + char* bssid = network_manager_alloc_get_mac_string(item->bssid); + if (bssid) { + cJSON_AddItemToObject(cjson_item, "bssid", cJSON_CreateString(STR_OR_BLANK(bssid))); + } + FREE_AND_NULL(bssid); + cJSON_AddNumberToObject(cjson_item, "b", item->phy_11b ? 1 : 0); + cJSON_AddNumberToObject(cjson_item, "g", item->phy_11g ? 1 : 0); + cJSON_AddNumberToObject(cjson_item, "n", item->phy_11n ? 1 : 0); + cJSON_AddNumberToObject(cjson_item, "low_rate", item->phy_lr ? 1 : 0); + + *json_string = cJSON_PrintUnformatted(cjson_item); + if (!*json_string) { + ESP_LOGE(TAG, "Memory allocaiton failed. Cannot save ap entry."); + err = ESP_ERR_NO_MEM; + } + cJSON_Delete(cjson_item); + return err; +} +bool network_wifi_str2mac(const char* mac, uint8_t* values) { + if (6 == sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &values[0], &values[1], &values[2], &values[3], &values[4], &values[5])) { + return true; } else { - ESP_LOGW(TAG, "wifi manager has no previous configuration. %s", esp_err_to_name(esp_err)); return false; } } -bool wifi_manager_fetch_wifi_sta_config() { - if (wifi_manager_config_sta == NULL) { - ESP_LOGD(TAG, "Allocating memory for structure."); - wifi_manager_config_sta = (wifi_config_t*)malloc(sizeof(wifi_config_t)); +esp_err_t network_wifi_add_json_entry(const char* json_text) { + esp_err_t err = ESP_OK; + known_access_point_t known_ap; + if (!json_text || strlen(json_text) == 0) { + ESP_LOGE(TAG, "Invalid access point json"); + return ESP_ERR_INVALID_ARG; } - memset(wifi_manager_config_sta, 0x00, sizeof(wifi_config_t)); - return wifi_manager_load_wifi_sta_config(wifi_manager_config_sta); + cJSON* cjson_item = cJSON_Parse(json_text); + if (!cjson_item) { + ESP_LOGE(TAG, "Invalid JSON %s", json_text); + return ESP_ERR_INVALID_ARG; + } + cJSON* value = cJSON_GetObjectItemCaseSensitive(cjson_item, "ssid"); + if (!value || !cJSON_IsString(value) || strlen(cJSON_GetStringValue(value)) == 0) { + ESP_LOGE(TAG, "Missing ssid in : %s", json_text); + err = ESP_ERR_INVALID_ARG; + } else { + if (!network_wifi_get_ap_entry(cJSON_GetStringValue(value))) { + known_ap.ssid = strdup_psram(cJSON_GetStringValue(value)); + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "pass"); + if (value && cJSON_IsString(value) && strlen(cJSON_GetStringValue(value)) > 0) { + known_ap.password = strdup_psram(cJSON_GetStringValue(value)); + } + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "chan"); + if (value) { + known_ap.primary = value->valueint; + } + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "auth"); + if (value) { + known_ap.authmode = value->valueint; + } + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "b"); + if (value) { + known_ap.phy_11b = value->valueint; + } + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "g"); + if (value) { + known_ap.phy_11g = value->valueint; + } + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "n"); + if (value) { + known_ap.phy_11n = value->valueint; + } + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "low_rate"); + if (value) { + known_ap.phy_lr = value->valueint; + } + value = cJSON_GetObjectItemCaseSensitive(cjson_item, "bssid"); + if (value && cJSON_IsString(value) && strlen(cJSON_GetStringValue(value)) > 0) { + network_wifi_str2mac(cJSON_GetStringValue(value), known_ap.bssid); + } + err = network_wifi_add_ap_copy(&known_ap); + } else { + ESP_LOGE(TAG, "Duplicate ssid %s found in storage", cJSON_GetStringValue(value)); + } + } + cJSON_Delete(cjson_item); + return err; +} +esp_err_t network_wifi_delete_ap(const char* key) { + esp_err_t esp_err = ESP_OK; + if (!key || strlen(key) == 0) { + ESP_LOGE(TAG, "SSID Empty. Cannot remove "); + return ESP_ERR_INVALID_ARG; + } + + known_access_point_t* it = network_wifi_get_ap_entry(key); + if (!it) { + ESP_LOGE(TAG, "Unknown AP entry"); + return ESP_ERR_INVALID_ARG; + } + + /* + * Check if we're deleting the active network + */ + ESP_LOGD(TAG, "Deleting AP %s. Checking if this is the active AP", key); + const wifi_sta_config_t* config = network_wifi_load_active_config(); + if (config && strlen(ssid_string(config)) > 0 && strcmp(ssid_string(config), it->ssid) == 0) { + ESP_LOGD(TAG, "Confirmed %s to be the active network. Removing it from flash.", key); + esp_err = network_wifi_erase_legacy(); + if (esp_err != ESP_OK) { + ESP_LOGW(TAG, "Legacy network details could not be removed from flash : %s", esp_err_to_name(esp_err)); + } + } + ESP_LOGD(TAG, "Removing network %s from the flash AP list", key); + esp_err = erase_nvs_for_partition(NVS_DEFAULT_PART_NAME, ap_list_nsv_namespace, it->ssid); + if (esp_err != ESP_OK) { + messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Deleting network entry %s error (%s). Error %s", key, ap_list_nsv_namespace, esp_err_to_name(esp_err)); + } + ESP_LOGD(TAG, "Removing network %s from the known AP list", key); + network_wifi_remove_ap_entry(it->ssid); + return esp_err; } -wifi_config_t* wifi_manager_get_wifi_sta_config() { - return wifi_manager_config_sta; +esp_err_t network_wifi_erase_legacy() { + esp_err_t err = erase_nvs_partition(NVS_DEFAULT_PART_NAME, network_wifi_nvs_namespace); + if (err == ESP_OK) { + ESP_LOGW(TAG, "Erased wifi configuration. Disconnecting from network"); + if ((err = esp_wifi_disconnect()) != ESP_OK) { + ESP_LOGW(TAG, "Could not disconnect from deleted network : %s", esp_err_to_name(err)); + } + } + return err; } -// void wifi_manager_register_handlers() { -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_WIFI_READY, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_AUTHMODE_CHANGE, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_START, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STOP, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_PROBEREQRECVED, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_WPS_ER_SUCCESS, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_WPS_ER_FAILED, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_WPS_ER_TIMEOUT, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_WPS_ER_PIN, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_START, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_STOP, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &network_wifi_event_handler, NULL)); -// ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &network_wifi_event_handler, NULL)); -// } -#define LOCAL_MAC_SIZE 20 -char* get_mac_string(uint8_t mac[6]) { - char* macStr = malloc(LOCAL_MAC_SIZE); - memset(macStr, 0x00, LOCAL_MAC_SIZE); - snprintf(macStr, LOCAL_MAC_SIZE, MACSTR, MAC2STR(mac)); - return macStr; + +esp_err_t network_wifi_erase_known_ap() { + network_wifi_empty_known_list(); + esp_err_t err = erase_nvs_partition(NVS_DEFAULT_PART_NAME, ap_list_nsv_namespace); + return err; } + +esp_err_t network_wifi_write_ap(const char* key, const char* value, size_t size) { + size_t size_override = size > 0 ? size : strlen(value) + 1; + esp_err_t esp_err = store_nvs_value_len_for_partition(NVS_DEFAULT_PART_NAME, ap_list_nsv_namespace, NVS_TYPE_BLOB, key, value, size_override); + if (esp_err != ESP_OK) { + messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "%s (%s). Error %s", key, network_wifi_nvs_namespace, esp_err_to_name(esp_err)); + } + return esp_err; +} +esp_err_t network_wifi_write_nvs(const char* key, const char* value, size_t size) { + size_t size_override = size > 0 ? size : strlen(value) + 1; + esp_err_t esp_err = store_nvs_value_len_for_partition(NVS_DEFAULT_PART_NAME, network_wifi_nvs_namespace, NVS_TYPE_BLOB, key, value, size_override); + if (esp_err != ESP_OK) { + messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "%s (%s). Error %s", key, network_wifi_nvs_namespace, esp_err_to_name(esp_err)); + } + return esp_err; +} + +esp_err_t network_wifi_store_ap_json(known_access_point_t* item) { + esp_err_t err = ESP_OK; + size_t size = 0; + char* json_string = NULL; + const wifi_sta_config_t* sta = network_wifi_get_active_config(); + + if ((err = network_wifi_alloc_ap_json(item, &json_string)) == ESP_OK) { + // get any existing entry from the nvs and compare + char* existing = get_nvs_value_alloc_for_partition(NVS_DEFAULT_PART_NAME, ap_list_nsv_namespace, NVS_TYPE_BLOB, item->ssid, &size); + if (!existing || strncmp(existing, json_string, strlen(json_string)) != 0) { + ESP_LOGI(TAG, "SSID %s was changed or is new. Committing to flash", item->ssid); + err = network_wifi_write_ap(item->ssid, json_string, 0); + if (sta && strlen(ssid_string(sta)) > 0 && strcmp(ssid_string(sta), item->ssid) == 0) { + ESP_LOGI(TAG, "Committing active access point"); + err = network_wifi_write_nvs("ssid", ssid_string(sta), 0); + if (err == ESP_OK) { + err = network_wifi_write_nvs("password", STR_OR_BLANK(password_string(sta)), 0); + } + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error committing active access point : %s", esp_err_to_name(err)); + } + } + } + FREE_AND_NULL(existing); + FREE_AND_NULL(json_string); + } + return err; +} + +esp_netif_t* network_wifi_get_interface() { + return wifi_netif; +} +esp_netif_t* network_wifi_get_ap_interface() { + return wifi_ap_netif; +} +esp_err_t network_wifi_set_sta_mode() { + if (!wifi_netif) { + ESP_LOGE(TAG, "Wifi not initialized. Cannot set sta mode"); + return ESP_ERR_INVALID_STATE; + } + ESP_LOGD(TAG, "Set Mode to STA"); + esp_err_t err = esp_wifi_set_mode(WIFI_MODE_STA); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error setting mode to STA: %s", esp_err_to_name(err)); + } else { + ESP_LOGI(TAG, "Starting wifi"); + err = esp_wifi_start(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error starting wifi: %s", esp_err_to_name(err)); + } + } + return err; +} +esp_netif_t* network_wifi_start() { + MEMTRACE_PRINT_DELTA_MESSAGE( "Starting wifi interface as STA mode"); + accessp_cjson = network_manager_clear_ap_list_json(&accessp_cjson); + if (!wifi_netif) { + MEMTRACE_PRINT_DELTA_MESSAGE("Init STA mode - creating default interface. "); + wifi_netif = esp_netif_create_default_wifi_sta(); + MEMTRACE_PRINT_DELTA_MESSAGE("Initializing Wifi. "); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_init(&cfg)); + MEMTRACE_PRINT_DELTA_MESSAGE("Registering wifi Handlers"); + //network_wifi_register_handlers(); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &network_wifi_event_handler, + NULL, + NULL)); + MEMTRACE_PRINT_DELTA_MESSAGE("Setting up wifi Storage"); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + } + MEMTRACE_PRINT_DELTA_MESSAGE("Setting up wifi mode as STA"); + network_wifi_set_sta_mode(); + MEMTRACE_PRINT_DELTA_MESSAGE("Setting hostname"); + network_set_hostname(wifi_netif); + MEMTRACE_PRINT_DELTA_MESSAGE("Done starting wifi interface"); + return wifi_netif; +} +void destroy_network_wifi() { + cJSON_Delete(accessp_cjson); + accessp_cjson = NULL; +} + +bool network_wifi_sta_config_changed() { + bool changed = true; + const wifi_sta_config_t* sta = network_wifi_get_active_config(); + if (!sta || strlen(ssid_string(sta)) == 0) + return false; + + known_access_point_t* known = network_wifi_get_ap_entry(ssid_string(sta)); + if (known && strcmp(known->ssid, ssid_string(sta)) == 0 && + strcmp((char*)known->password, password_string(sta)) == 0) { + changed = false; + } else { + ESP_LOGI(TAG, "New network configuration found"); + } + return changed; +} + +esp_err_t network_wifi_save_sta_config() { + esp_err_t esp_err = ESP_OK; + known_access_point_t* item = NULL; + MEMTRACE_PRINT_DELTA_MESSAGE("Config Save"); + + const wifi_sta_config_t* sta = network_wifi_get_active_config(); + if (sta && strlen(ssid_string(sta)) > 0) { + MEMTRACE_PRINT_DELTA_MESSAGE("Checking if current SSID is known"); + item = network_wifi_get_ap_entry(ssid_string(sta)); + if (!item) { + ESP_LOGD(TAG,"New SSID %s found", ssid_string(sta)); + // this is a new access point. First add it to the end of the AP list + esp_err = network_wifi_add_ap_from_sta_copy(sta); + } + } + // now traverse the list and commit + MEMTRACE_PRINT_DELTA_MESSAGE("Saving all known ap as json strings"); + known_access_point_t* it; + SLIST_FOREACH(it, &s_ap_list, next) { + if ((esp_err = network_wifi_store_ap_json(it)) != ESP_OK) { + ESP_LOGW(TAG, "Error saving wifi ap entry %s : %s", it->ssid, esp_err_to_name(esp_err)); + break; + } + } + return esp_err; +} + +void network_wifi_load_known_access_points() { + esp_err_t esp_err; + size_t size = 0; + if (network_wifi_get_known_count() > 0) { + ESP_LOGW(TAG, "Access points already loaded"); + return; + } + nvs_iterator_t it = nvs_entry_find(NVS_DEFAULT_PART_NAME, ap_list_nsv_namespace, NVS_TYPE_ANY); + if (it == NULL) { + ESP_LOGW(TAG, "No known access point found"); + return; + } + do { + nvs_entry_info_t info; + nvs_entry_info(it, &info); + if (strstr(info.namespace_name, ap_list_nsv_namespace)) { + void* value = get_nvs_value_alloc_for_partition(NVS_DEFAULT_PART_NAME, ap_list_nsv_namespace, info.type, info.key, &size); + if (value == NULL) { + ESP_LOGE(TAG, "nvs read failed for %s.", info.key); + } else if ((esp_err = network_wifi_add_json_entry(value)) != ESP_OK) { + ESP_LOGE(TAG, "Invalid entry or error for %s.", (char*)value); + } + FREE_AND_NULL(value); + } + it = nvs_entry_next(it); + } while (it != NULL); + + return; +} + +esp_err_t network_wifi_get_blob(void* target, size_t size, const char* key) { + esp_err_t esp_err = ESP_OK; + size_t found_size = 0; + if (!target) { + ESP_LOGE(TAG, "%s invalid target pointer", __FUNCTION__); + return ESP_ERR_INVALID_ARG; + } + memset(target, 0x00, size); + char* value = (char*)get_nvs_value_alloc_for_partition(NVS_DEFAULT_PART_NAME, network_wifi_nvs_namespace, NVS_TYPE_BLOB, key, &found_size); + if (!value) { + ESP_LOGD(TAG,"nvs key %s not found.", key); + esp_err = ESP_FAIL; + } else { + memcpy((char*)target, value, size > found_size ? found_size : size); + FREE_AND_NULL(value); + ESP_LOGD(TAG,"Successfully loaded key %s", key); + } + return esp_err; +} +const wifi_sta_config_t* network_wifi_load_active_config() { + static wifi_sta_config_t config; + esp_err_t esp_err = ESP_OK; + memset(&config, 0x00, sizeof(config)); + config.scan_method = WIFI_ALL_CHANNEL_SCAN; + MEMTRACE_PRINT_DELTA_MESSAGE("Fetching wifi sta config - ssid."); + esp_err = network_wifi_get_blob(&config.ssid, sizeof(config.ssid), "ssid"); + if (esp_err == ESP_OK && strlen((char*)config.ssid) > 0) { + ESP_LOGD(TAG,"network_wifi_load_active_config: ssid:%s. Fetching password (if any) ", ssid_string(&config)); + if (network_wifi_get_blob(&config.password, sizeof(config.password), "password") != ESP_OK) { + ESP_LOGW(TAG, "No wifi password found in nvs"); + } + } else { + if(network_wifi_get_known_count() > 0) { + ESP_LOGW(TAG, "No wifi ssid found in nvs, but known access points found. Using first known access point."); + known_access_point_t* ap = SLIST_FIRST(&s_ap_list); + if (ap) { + strncpy((char*)&config.ssid, ap->ssid, sizeof(config.ssid)); + strncpy((char*)&config.password, ap->password, sizeof(config.password)); + } + esp_err = ESP_OK; + } else { + ESP_LOGW(TAG, "network manager has no previous configuration. %s", esp_err_to_name(esp_err)); + return NULL; + } + } + return &config; +} +bool network_wifi_load_wifi_sta_config() { + network_wifi_load_known_access_points(); + const wifi_sta_config_t* config = network_wifi_load_active_config(); + if (config) { + known_access_point_t* item = network_wifi_get_ap_entry(ssid_string(config)); + if (!item) { + ESP_LOGI(TAG, "Adding legacy/active wifi connection to the known list"); + network_wifi_add_ap_from_sta_copy(config); + } + } + return config && config->ssid[0] != '\0'; +} +bool network_wifi_get_config_for_ssid(wifi_config_t* config, const char* ssid) { + known_access_point_t* item = network_wifi_get_ap_entry(ssid); + if (!item) { + ESP_LOGE(TAG, "Unknown ssid %s", ssid); + return false; + } + memset(&config->ap, 0x00, sizeof(config->ap)); + strncpy((char*)config->ap.ssid, item->ssid, sizeof(config->ap.ssid)); + strncpy((char*)config->ap.password, item->password, sizeof(config->ap.ssid)); + config->sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + return true; +} + static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base != WIFI_EVENT) return; switch (event_id) { + case WIFI_EVENT_WIFI_READY: ESP_LOGD(TAG, "WIFI_EVENT_WIFI_READY"); break; case WIFI_EVENT_SCAN_DONE: ESP_LOGD(TAG, "WIFI_EVENT_SCAN_DONE"); - network_manager_async_scan_done(); + network_async_scan_done(); break; case WIFI_EVENT_STA_AUTHMODE_CHANGE: @@ -237,20 +664,11 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i break; case WIFI_EVENT_AP_PROBEREQRECVED: { - // wifi_event_ap_probe_req_rx_t - // Argument structure for WIFI_EVENT_AP_PROBEREQRECVED event - // - // Public Members - // - // int rssi - // Received probe request signal strength - // - // uint8_t mac[6] - // MAC address of the station which send probe request - wifi_event_ap_probe_req_rx_t* s = (wifi_event_ap_probe_req_rx_t*)event_data; - char* mac = get_mac_string(s->mac); - ESP_LOGD(TAG, "WIFI_EVENT_AP_PROBEREQRECVED. RSSI: %d, MAC: %s", s->rssi, STR_OR_BLANK(mac)); + char* mac = network_manager_alloc_get_mac_string(s->mac); + if (mac) { + ESP_LOGD(TAG, "WIFI_EVENT_AP_PROBEREQRECVED. RSSI: %d, MAC: %s", s->rssi, STR_OR_BLANK(mac)); + } FREE_AND_NULL(mac); } break; case WIFI_EVENT_STA_WPS_ER_SUCCESS: @@ -267,8 +685,10 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i break; case WIFI_EVENT_AP_STACONNECTED: { wifi_event_ap_staconnected_t* stac = (wifi_event_ap_staconnected_t*)event_data; - char* mac = get_mac_string(stac->mac); - ESP_LOGD(TAG, "WIFI_EVENT_AP_STACONNECTED. aid: %d, mac: %s", stac->aid, STR_OR_BLANK(mac)); + char* mac = network_manager_alloc_get_mac_string(stac->mac); + if (mac) { + ESP_LOGD(TAG, "WIFI_EVENT_AP_STACONNECTED. aid: %d, mac: %s", stac->aid, STR_OR_BLANK(mac)); + } FREE_AND_NULL(mac); } break; case WIFI_EVENT_AP_STADISCONNECTED: @@ -284,35 +704,16 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i break; case WIFI_EVENT_STA_CONNECTED: { - // structwifi_event_sta_connected_t - // Argument structure for WIFI_EVENT_STA_CONNECTED event - // - // Public Members - // - // uint8_t ssid[32] - // SSID of connected AP - // - // uint8_t ssid_len - // SSID length of connected AP - // - // uint8_t bssid[6] - // BSSID of connected AP - // - // uint8_t channel - // channel of connected AP - // - // wifi_auth_mode_tauthmode - // authentication mode used by AP - //, get_mac_string(EVENT_HANDLER_ARG_FIELD(wifi_event_ap_probe_req_rx_t, mac))); - ESP_LOGD(TAG, "WIFI_EVENT_STA_CONNECTED. "); wifi_event_sta_connected_t* s = (wifi_event_sta_connected_t*)event_data; - char* bssid = get_mac_string(s->bssid); - char* ssid = strdup((char*)s->ssid); - ESP_LOGD(TAG, "WIFI_EVENT_STA_CONNECTED. Channel: %d, Access point: %s, BSSID: %s ", s->channel, STR_OR_BLANK(ssid), (bssid)); + char* bssid = network_manager_alloc_get_mac_string(s->bssid); + char* ssid = strdup_psram((char*)s->ssid); + if (bssid && ssid) { + ESP_LOGD(TAG, "WIFI_EVENT_STA_CONNECTED. Channel: %d, Access point: %s, BSSID: %s ", s->channel, STR_OR_BLANK(ssid), (bssid)); + } FREE_AND_NULL(bssid); FREE_AND_NULL(ssid); - network_manager_async_success(); + network_async(EN_CONNECTED); } break; @@ -334,16 +735,14 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i // uint8_t reason // reason of disconnection wifi_event_sta_disconnected_t* s = (wifi_event_sta_disconnected_t*)event_data; - char* bssid = get_mac_string(s->bssid); + char* bssid = network_manager_alloc_get_mac_string(s->bssid); ESP_LOGD(TAG, "WIFI_EVENT_STA_DISCONNECTED. From BSSID: %s, reason code: %d (%s)", STR_OR_BLANK(bssid), s->reason, get_disconnect_code_desc(s->reason)); FREE_AND_NULL(bssid); - - - /* if a DISCONNECT message is posted while a scan is in progress this scan will NEVER end, causing scan to never work again. For this reason SCAN_BIT is cleared too */ - // todo: check for implementation of this: network_manager_clear_flag(WIFI_MANAGER_WIFI_CONNECTED_BIT | WIFI_MANAGER_SCAN_BIT); - wifi_event_sta_disconnected_t * disconnected_event = malloc(sizeof(wifi_event_sta_disconnected_t)); - memcpy(disconnected_event, event_data, sizeof(wifi_event_sta_disconnected_t)); - network_manager_async_lost_connection(disconnected_event); + if (s->reason == WIFI_REASON_ROAMING) { + ESP_LOGI(TAG, "WiFi Roaming to new access point"); + } else { + network_async_lost_connection((wifi_event_sta_disconnected_t*)event_data); + } } break; default: @@ -351,53 +750,75 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i } } - -cJSON* wifi_manager_get_new_array_json(cJSON** old) { - ESP_LOGV(TAG, "wifi_manager_get_new_array_json called"); +cJSON* network_wifi_get_new_array_json(cJSON** old) { + ESP_LOGV(TAG, "network_wifi_get_new_array_json called"); cJSON* root = *old; if (root != NULL) { cJSON_Delete(root); *old = NULL; } - ESP_LOGV(TAG, "wifi_manager_get_new_array_json done"); + ESP_LOGV(TAG, "network_wifi_get_new_array_json done"); return cJSON_CreateArray(); } -void wifi_manager_generate_access_points_json(cJSON** ap_list) { - *ap_list = wifi_manager_get_new_array_json(ap_list); +void network_wifi_global_init() { + network_wifi_get_new_array_json(&accessp_cjson); + ESP_LOGD(TAG, "Loading existing wifi configuration (if any)"); + network_wifi_load_wifi_sta_config(); +} +void network_wifi_add_access_point_json(cJSON* ap_list, wifi_ap_record_t* ap_rec) { + cJSON* ap = cJSON_CreateObject(); + if (ap == NULL) { + ESP_LOGE(TAG, "Unable to allocate memory for access point %s", ap_rec->ssid); + return; + } + cJSON* radio = cJSON_CreateObject(); + if (radio == NULL) { + ESP_LOGE(TAG, "Unable to allocate memory for access point %s", ap_rec->ssid); + cJSON_Delete(ap); + return; + } + cJSON_AddItemToObject(ap, "ssid", cJSON_CreateString(ap_ssid_string(ap_rec))); + cJSON_AddBoolToObject(ap, "known", network_wifi_is_known_ap(ap_ssid_string(ap_rec))); + if (ap_rec->rssi != 0) { + // only add the rest of the details when record doesn't come from + // "known" access points that aren't in range + cJSON_AddNumberToObject(ap, "chan", ap_rec->primary); + cJSON_AddNumberToObject(ap, "rssi", ap_rec->rssi); + cJSON_AddNumberToObject(ap, "auth", ap_rec->authmode); + char* bssid = network_manager_alloc_get_mac_string(ap_rec->bssid); + if (bssid) { + cJSON_AddItemToObject(ap, "bssid", cJSON_CreateString(STR_OR_BLANK(bssid))); + } + FREE_AND_NULL(bssid); + cJSON_AddNumberToObject(radio, "b", ap_rec->phy_11b ? 1 : 0); + cJSON_AddNumberToObject(radio, "g", ap_rec->phy_11g ? 1 : 0); + cJSON_AddNumberToObject(radio, "n", ap_rec->phy_11n ? 1 : 0); + cJSON_AddNumberToObject(radio, "low_rate", ap_rec->phy_lr ? 1 : 0); + cJSON_AddItemToObject(ap, "radio", radio); + } + cJSON_AddItemToArray(ap_list, ap); + char* ap_json = cJSON_PrintUnformatted(ap); + if (ap_json != NULL) { + ESP_LOGD(TAG, "New access point found: %s", ap_json); + free(ap_json); + } +} +void network_wifi_generate_access_points_json(cJSON** ap_list) { + *ap_list = network_wifi_get_new_array_json(ap_list); + wifi_ap_record_t known_ap; + known_access_point_t* it; if (*ap_list == NULL) return; for (int i = 0; i < ap_num; i++) { - cJSON* ap = cJSON_CreateObject(); - if (ap == NULL) { - ESP_LOGE(TAG, "Unable to allocate memory for access point entry #%d", i); - return; - } - cJSON* radio = cJSON_CreateObject(); - if (radio == NULL) { - ESP_LOGE(TAG, "Unable to allocate memory for access point entry #%d", i); - cJSON_Delete(ap); - return; - } - wifi_ap_record_t ap_rec = accessp_records[i]; - cJSON_AddNumberToObject(ap, "chan", ap_rec.primary); - cJSON_AddNumberToObject(ap, "rssi", ap_rec.rssi); - cJSON_AddNumberToObject(ap, "auth", ap_rec.authmode); - cJSON_AddItemToObject(ap, "ssid", cJSON_CreateString((char*)ap_rec.ssid)); - - char* bssid = get_mac_string(ap_rec.bssid); - cJSON_AddItemToObject(ap, "bssid", cJSON_CreateString(STR_OR_BLANK(bssid))); - FREE_AND_NULL(bssid); - cJSON_AddNumberToObject(radio, "b", ap_rec.phy_11b ? 1 : 0); - cJSON_AddNumberToObject(radio, "g", ap_rec.phy_11g ? 1 : 0); - cJSON_AddNumberToObject(radio, "n", ap_rec.phy_11n ? 1 : 0); - cJSON_AddNumberToObject(radio, "low_rate", ap_rec.phy_lr ? 1 : 0); - cJSON_AddItemToObject(ap, "radio", radio); - cJSON_AddItemToArray(*ap_list, ap); - char* ap_json = cJSON_PrintUnformatted(ap); - if (ap_json != NULL) { - ESP_LOGD(TAG, "New access point found: %s", ap_json); - free(ap_json); + network_wifi_add_access_point_json(*ap_list, &accessp_records[i]); + } + SLIST_FOREACH(it, &s_ap_list, next) { + if (!network_wifi_was_ssid_seen(it->ssid)) { + memset(&known_ap, 0x00, sizeof(known_ap)); + strlcpy((char*)known_ap.ssid, it->ssid, sizeof(known_ap.ssid)); + ESP_LOGD(TAG, "Adding known access point that is not in range: %s", it->ssid); + network_wifi_add_access_point_json(*ap_list, &known_ap); } } char* ap_list_json = cJSON_PrintUnformatted(*ap_list); @@ -406,7 +827,7 @@ void wifi_manager_generate_access_points_json(cJSON** ap_list) { free(ap_list_json); } } -void wifi_manager_set_ipv4val(const char* key, char* default_value, ip4_addr_t * target) { +void network_wifi_set_ipv4val(const char* key, char* default_value, ip4_addr_t* target) { char* value = config_alloc_get_default(NVS_TYPE_STR, key, default_value, 0); if (value != NULL) { ESP_LOGD(TAG, "%s: %s", key, value); @@ -414,8 +835,8 @@ void wifi_manager_set_ipv4val(const char* key, char* default_value, ip4_addr_t * } FREE_AND_NULL(value); } -void wifi_manager_config_ap() { - tcpip_adapter_ip_info_t info; +esp_netif_t* network_wifi_config_ap() { + esp_netif_ip_info_t info; esp_err_t err = ESP_OK; char* value = NULL; wifi_config_t ap_config = { @@ -424,33 +845,24 @@ void wifi_manager_config_ap() { }, }; ESP_LOGI(TAG, "Configuring Access Point."); - wifi_netif = esp_netif_create_default_wifi_ap(); + if (!wifi_ap_netif) { + wifi_ap_netif = esp_netif_create_default_wifi_ap(); + } + network_wifi_set_ipv4val("ap_ip_address", DEFAULT_AP_IP, (ip4_addr_t*)&info.ip); + network_wifi_set_ipv4val("ap_ip_gateway", CONFIG_DEFAULT_AP_GATEWAY, (ip4_addr_t*)&info.gw); + network_wifi_set_ipv4val("ap_ip_netmask", CONFIG_DEFAULT_AP_NETMASK, (ip4_addr_t*)&info.netmask); /* In order to change the IP info structure, we have to first stop * the DHCP server on the new interface */ - esp_netif_dhcps_stop(wifi_netif); - - // tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); - // if (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED) { - // ESP_LOGD(TAG, "Stopping DHCP on interface so we can "); - // if ((err = tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP)) != ESP_OK) /* stop AP DHCP server */ - // { - // ESP_LOGW(TAG, "Stopping DHCP failed. Error %s", esp_err_to_name(err)); - // } - // } - /* - * Set access point mode IP adapter configuration - */ - - wifi_manager_set_ipv4val("ap_ip_address", DEFAULT_AP_IP, &info.ip); - wifi_manager_set_ipv4val("ap_ip_gateway", CONFIG_DEFAULT_AP_GATEWAY, &info.gw); - wifi_manager_set_ipv4val("ap_ip_netmask", CONFIG_DEFAULT_AP_NETMASK, &info.netmask); - ESP_LOGD(TAG, "Setting tcp_ip info for interface TCPIP_ADAPTER_IF_AP"); - if ((err = esp_netif_set_ip_info(wifi_netif, &info)) != ESP_OK) { + network_start_stop_dhcps(wifi_ap_netif, false); + ESP_LOGD(TAG, "Setting tcp_ip info for access point"); + if ((err = esp_netif_set_ip_info(wifi_ap_netif, &info)) != ESP_OK) { ESP_LOGE(TAG, "Setting tcp_ip info for interface TCPIP_ADAPTER_IF_AP. Error %s", esp_err_to_name(err)); - return; + return wifi_ap_netif; } + network_start_stop_dhcps(wifi_ap_netif, true); + /* * Set Access Point configuration */ @@ -486,17 +898,17 @@ void wifi_manager_config_ap() { ESP_LOGD(TAG, "Beacon interval: %d", ap_config.ap.beacon_interval); const char* msg = "Setting wifi mode as WIFI_MODE_APSTA"; - ESP_LOGD(TAG, "%s",msg); + ESP_LOGD(TAG, "%s", msg); if ((err = esp_wifi_set_mode(WIFI_MODE_APSTA)) != ESP_OK) { - ESP_LOGE(TAG, "%s. Error %s",msg, esp_err_to_name(err)); - return; + ESP_LOGE(TAG, "%s. Error %s", msg, esp_err_to_name(err)); + return wifi_ap_netif; } msg = "Setting wifi AP configuration for WIFI_IF_AP"; ESP_LOGD(TAG, "%s", msg); if ((err = esp_wifi_set_config(WIFI_IF_AP, &ap_config)) != ESP_OK) /* stop AP DHCP server */ { ESP_LOGE(TAG, "%s . Error %s", msg, esp_err_to_name(err)); - return; + return wifi_ap_netif; } msg = "Setting wifi bandwidth"; @@ -504,7 +916,7 @@ void wifi_manager_config_ap() { if ((err = esp_wifi_set_bandwidth(WIFI_IF_AP, DEFAULT_AP_BANDWIDTH)) != ESP_OK) /* stop AP DHCP server */ { ESP_LOGE(TAG, "%s failed. Error %s", msg, esp_err_to_name(err)); - return; + return wifi_ap_netif; } msg = "Setting wifi power save"; @@ -513,14 +925,14 @@ void wifi_manager_config_ap() { if ((err = esp_wifi_set_ps(DEFAULT_STA_POWER_SAVE)) != ESP_OK) /* stop AP DHCP server */ { ESP_LOGE(TAG, "%s failed. Error %s", msg, esp_err_to_name(err)); - return; + return wifi_ap_netif; } - esp_netif_dhcps_start(wifi_netif); + ESP_LOGD(TAG, "Done configuring Soft Access Point"); - dns_server_start(); + return wifi_ap_netif; } -void wifi_manager_filter_unique(wifi_ap_record_t* aplist, uint16_t* aps) { +void network_wifi_filter_unique(wifi_ap_record_t* aplist, uint16_t* aps) { int total_unique; wifi_ap_record_t* first_free; total_unique = *aps; @@ -574,417 +986,216 @@ void wifi_manager_filter_unique(wifi_ap_record_t* aplist, uint16_t* aps) { *aps = total_unique; } -char* wifi_manager_alloc_get_ap_list_json() { +char* network_status_alloc_get_ap_list_json() { return cJSON_PrintUnformatted(accessp_cjson); } -cJSON* wifi_manager_clear_ap_list_json(cJSON** old) { - ESP_LOGV(TAG, "wifi_manager_clear_ap_list_json called"); - cJSON* root = wifi_manager_get_new_array_json(old); - ESP_LOGV(TAG, "wifi_manager_clear_ap_list_json done"); +cJSON* network_manager_clear_ap_list_json(cJSON** old) { + ESP_LOGV(TAG, "network_manager_clear_ap_list_json called"); + cJSON* root = network_wifi_get_new_array_json(old); + ESP_LOGV(TAG, "network_manager_clear_ap_list_json done"); return root; } -esp_err_t wifi_scan_done(queue_message* msg) { + +esp_err_t network_wifi_built_known_ap_list() { + if (network_status_lock_json_buffer(pdMS_TO_TICKS(1000))) { + ESP_LOGD(TAG,"Building known AP list"); + accessp_cjson = network_manager_clear_ap_list_json(&accessp_cjson); + network_wifi_generate_access_points_json(&accessp_cjson); + network_status_unlock_json_buffer(); + ESP_LOGD(TAG, "Done building ap JSON list"); + } else { + ESP_LOGE(TAG, "Failed to lock json buffer"); + return ESP_FAIL; + } + return ESP_OK; +} + +esp_err_t wifi_scan_done() { esp_err_t err = ESP_OK; /* As input param, it stores max AP number ap_records can hold. As output param, it receives the actual AP number this API returns. * As a consequence, ap_num MUST be reset to MAX_AP_NUM at every scan */ ESP_LOGD(TAG, "Getting AP list records"); + ap_num = MAX_AP_NUM; if ((err = esp_wifi_scan_get_ap_num(&ap_num)) != ESP_OK) { ESP_LOGE(TAG, "Failed to retrieve scan results count. Error %s", esp_err_to_name(err)); return err; } - + FREE_AND_NULL(accessp_records); if (ap_num > 0) { - accessp_records = (wifi_ap_record_t*)malloc(sizeof(wifi_ap_record_t) * ap_num); + accessp_records = (wifi_ap_record_t*)malloc_init_external(sizeof(wifi_ap_record_t) * ap_num); if ((err = esp_wifi_scan_get_ap_records(&ap_num, accessp_records)) != ESP_OK) { ESP_LOGE(TAG, "Failed to retrieve scan results list. Error %s", esp_err_to_name(err)); return err; } /* make sure the http server isn't trying to access the list while it gets refreshed */ ESP_LOGD(TAG, "Preparing to build ap JSON list"); - if (wifi_manager_lock_json_buffer(pdMS_TO_TICKS(1000))) { + if (network_status_lock_json_buffer(pdMS_TO_TICKS(1000))) { /* Will remove the duplicate SSIDs from the list and update ap_num */ - wifi_manager_filter_unique(accessp_records, &ap_num); - wifi_manager_generate_access_points_json(&accessp_cjson); - wifi_manager_unlock_json_buffer(); + network_wifi_filter_unique(accessp_records, &ap_num); + network_wifi_set_found_ap(); + network_wifi_generate_access_points_json(&accessp_cjson); + network_status_unlock_json_buffer(); ESP_LOGD(TAG, "Done building ap JSON list"); - } else { ESP_LOGE(TAG, "could not get access to json mutex in wifi_scan"); err = ESP_FAIL; } - free(accessp_records); } else { // ESP_LOGD(TAG, "No AP Found. Emptying the list."); - accessp_cjson = wifi_manager_get_new_array_json(&accessp_cjson); + accessp_cjson = network_wifi_get_new_array_json(&accessp_cjson); } return err; } -bool is_wifi_up(){ - return wifi_netif!=NULL; +bool is_wifi_up() { + return wifi_netif != NULL; } -esp_err_t network_wifi_start_scan(queue_message* msg) { +esp_err_t network_wifi_start_scan() { + wifi_scan_config_t scan_config = { + .ssid = 0, + .bssid = 0, + .channel = 0, + .scan_type = WIFI_SCAN_TYPE_ACTIVE, + .show_hidden = true}; esp_err_t err = ESP_OK; - ESP_LOGD(TAG, "MESSAGE: ORDER_START_WIFI_SCAN"); - if(!is_wifi_up()) { + ESP_LOGI(TAG, "Initiating wifi network scan"); + if (!is_wifi_up()) { messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Wifi not started. Cannot scan"); return ESP_FAIL; } /* if a scan is already in progress this message is simply ignored thanks to the WIFI_MANAGER_SCAN_BIT uxBit */ - if (!network_manager_is_flag_set(WIFI_MANAGER_SCAN_BIT)) { - if ((err = esp_wifi_scan_start(&scan_config, false)) != ESP_OK) { - ESP_LOGW(TAG, "Unable to start scan; %s ", esp_err_to_name(err)); - // set_status_message(WARNING, "Wifi Connecting. Cannot start scan."); - messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Scanning failed: %s", esp_err_to_name(err)); - } else { - network_manager_set_flag(WIFI_MANAGER_SCAN_BIT); - } - } else { - ESP_LOGW(TAG, "Scan already in progress!"); + if ((err = esp_wifi_scan_start(&scan_config, false)) != ESP_OK) { + ESP_LOGW(TAG, "Unable to start scan; %s ", esp_err_to_name(err)); + // set_status_message(WARNING, "Wifi Connecting. Cannot start scan."); + messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Scanning failed: %s", esp_err_to_name(err)); } - return err; } -static void polling_STA(void* timer_id) { - network_manager_async_connect(wifi_manager_get_wifi_sta_config()); -} -void set_host_name() { - esp_err_t err; - 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 = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, host_name)) != ESP_OK) { - ESP_LOGE(TAG, "Unable to set host name. Error: %s", esp_err_to_name(err)); - } - // if((err=tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_AP, host_name)) !=ESP_OK){ - // ESP_LOGE(TAG, "Unable to set host name. Error: %s",esp_err_to_name(err)); - // } - free(host_name); - } -} - -esp_err_t network_wifi_connect(wifi_config_t * cfg){ - esp_err_t err = ESP_OK; - ESP_LOGD(TAG, "network_wifi_connect"); - if(!is_wifi_up()) { - messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Wifi not started. Cannot connect"); - return ESP_FAIL; - } - tcpip_adapter_dhcp_status_t status; - ESP_LOGD(TAG, "wifi_manager: Checking if DHCP client for STA interface is running"); - ESP_ERROR_CHECK_WITHOUT_ABORT(tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &status)); - if (status != TCPIP_ADAPTER_DHCP_STARTED) { - ESP_LOGD(TAG, "wifi_manager: Start DHCP client for STA interface"); - ESP_ERROR_CHECK_WITHOUT_ABORT(tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA)); - } +bool network_wifi_is_ap_mode() { wifi_mode_t mode; /* update config to latest and attempt connection */ - esp_wifi_get_mode(&mode); - if (WIFI_MODE_APSTA != mode && WIFI_MODE_STA != mode) { - // the soft ap is not started, so let's set the WiFi mode to STA - ESP_LOGD(TAG, "MESSAGE: network_wifi_connect_existing - setting mode WIFI_MODE_STA"); - if ((err = esp_wifi_set_mode(WIFI_MODE_STA)) != ESP_OK) { - ESP_LOGE(TAG, "Failed to set wifi mode to STA. Error %s", esp_err_to_name(err)); + return esp_wifi_get_mode(&mode) == ESP_OK && mode == WIFI_MODE_AP; +} +bool network_wifi_is_sta_mode() { + wifi_mode_t mode; + /* update config to latest and attempt connection */ + return esp_wifi_get_mode(&mode) == ESP_OK && mode == WIFI_MODE_STA; +} +bool network_wifi_is_ap_sta_mode() { + wifi_mode_t mode; + /* update config to latest and attempt connection */ + return esp_wifi_get_mode(&mode) == ESP_OK && mode == WIFI_MODE_APSTA; +} + +esp_err_t network_wifi_connect(const char* ssid, const char* password) { + esp_err_t err = ESP_OK; + wifi_config_t config; + memset(&config, 0x00, sizeof(config)); + ESP_LOGD(TAG, "network_wifi_connect"); + if (!is_wifi_up()) { + messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Wifi not started. Cannot connect"); + return ESP_FAIL; + } + if (!ssid || !password || strlen(ssid) == 0) { + ESP_LOGE(TAG, "Cannot connect wifi. wifi config is null!"); + return ESP_ERR_INVALID_ARG; + } + + wifi_mode_t wifi_mode; + err = esp_wifi_get_mode(&wifi_mode); + if (err == ESP_ERR_WIFI_NOT_INIT) { + ESP_LOGW(TAG, "Wifi not initialized. Attempting to start sta mode"); + network_wifi_start(); + } else if (err != ESP_OK) { + ESP_LOGE(TAG, "Could not retrieve wifi mode : %s", esp_err_to_name(err)); + } else if (wifi_mode != WIFI_MODE_STA && wifi_mode != WIFI_MODE_APSTA) { + ESP_LOGD(TAG, "Changing wifi mode to STA"); + err = network_wifi_set_sta_mode(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Could not set mode to STA. Cannot connect to SSID %s", ssid); return err; } } - - if ((err = esp_wifi_set_config(WIFI_IF_STA, cfg)) != ESP_OK) { - ESP_LOGE(TAG, "Failed to set STA configuration. Error %s", esp_err_to_name(err)); - return err; + // copy configuration and connect + strlcpy((char*)config.sta.ssid, ssid, sizeof(config.sta.ssid)); + if (password) { + strlcpy((char*)config.sta.password, password, sizeof(config.sta.password)); } - set_host_name(); - ESP_LOGI(TAG, "Wifi Connecting..."); - if ((err = esp_wifi_connect()) != ESP_OK) { - ESP_LOGE(TAG, "Failed to initiate wifi connection. Error %s", esp_err_to_name(err)); - return err; - } + // First Disconnect + esp_wifi_disconnect(); + + config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + if ((err = esp_wifi_set_config(WIFI_IF_STA, &config)) != ESP_OK) { + 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)); + } + } return err; } -void network_wifi_clear_config(){ - - /* erase configuration */ - if (wifi_manager_config_sta) { - ESP_LOGI(TAG, "Erasing WiFi Configuration."); - memset(wifi_manager_config_sta, 0x00, sizeof(wifi_config_t)); - /* save NVS memory */ - network_wifi_save_sta_config(); +esp_err_t network_wifi_connect_ssid(const char* ssid) { + known_access_point_t* item = network_wifi_get_ap_entry(ssid); + if (item) { + return network_wifi_connect(item->ssid, item->password); + } + return ESP_FAIL; +} +esp_err_t network_wifi_connect_active_ssid() { + const wifi_sta_config_t* config = network_wifi_load_active_config(); + if (config) { + return network_wifi_connect(ssid_string(config), password_string(config)); + } + return ESP_FAIL; +} +void network_wifi_clear_config() { + /* erase configuration */ + const wifi_sta_config_t* sta = network_wifi_get_active_config(); + network_wifi_delete_ap(ssid_string(sta)); + esp_err_t err = ESP_OK; + if ((err = esp_wifi_disconnect()) != ESP_OK) { + ESP_LOGW(TAG, "Could not disconnect from deleted network : %s", esp_err_to_name(err)); } - } - - -// esp_err_t network_wifi_disconnected(queue_message* msg) { -// esp_err_t err = ESP_OK; -// wifi_event_sta_disconnected_t disc_event; - -// // ESP_LOGD(TAG, "MESSAGE: EVENT_STA_DISCONNECTED"); -// // if (msg->param == NULL) { -// // ESP_LOGE(TAG, "MESSAGE: EVENT_STA_DISCONNECTED - expected parameter not found!"); -// // } else { -// // memcpy(&disc_event, (wifi_event_sta_disconnected_t*)msg->param, sizeof(disc_event)); -// // free(msg->param); -// // ESP_LOGD(TAG, "MESSAGE: EVENT_STA_DISCONNECTED with Reason code: %d (%s)", disc_event.reason, get_disconnect_code_desc(disc_event.reason)); -// // } - -// /* this even can be posted in numerous different conditions -// * -// * 1. SSID password is wrong -// * 2. Manual disconnection ordered -// * 3. Connection lost -// * -// * Having clear understand as to WHY the event was posted is key to having an efficient wifi manager -// * -// * With wifi_manager, we determine: -// * If WIFI_MANAGER_REQUEST_STA_CONNECT_BIT is set, We consider it's a client that requested the connection. -// * When SYSTEM_EVENT_STA_DISCONNECTED is posted, it's probably a password/something went wrong with the handshake. -// * -// * If WIFI_MANAGER_REQUEST_STA_CONNECT_BIT is set, it's a disconnection that was ASKED by the client (clicking disconnect in the app) -// * When SYSTEM_EVENT_STA_DISCONNECTED is posted, saved wifi is erased from the NVS memory. -// * -// * If WIFI_MANAGER_REQUEST_STA_CONNECT_BIT and WIFI_MANAGER_REQUEST_STA_CONNECT_BIT are NOT set, it's a lost connection -// * -// * In this version of the software, reason codes are not used. They are indicated here for potential future usage. -// * -// * REASON CODE: -// * 1 UNSPECIFIED -// * 2 AUTH_EXPIRE auth no longer valid, this smells like someone changed a password on the AP -// * 3 AUTH_LEAVE -// * 4 ASSOC_EXPIRE -// * 5 ASSOC_TOOMANY too many devices already connected to the AP => AP fails to respond -// * 6 NOT_AUTHED -// * 7 NOT_ASSOCED -// * 8 ASSOC_LEAVE -// * 9 ASSOC_NOT_AUTHED -// * 10 DISASSOC_PWRCAP_BAD -// * 11 DISASSOC_SUPCHAN_BAD -// * 12 -// * 13 IE_INVALID -// * 14 MIC_FAILURE -// * 15 4WAY_HANDSHAKE_TIMEOUT wrong password! This was personnaly tested on my home wifi with a wrong password. -// * 16 GROUP_KEY_UPDATE_TIMEOUT -// * 17 IE_IN_4WAY_DIFFERS -// * 18 GROUP_CIPHER_INVALID -// * 19 PAIRWISE_CIPHER_INVALID -// * 20 AKMP_INVALID -// * 21 UNSUPP_RSN_IE_VERSION -// * 22 INVALID_RSN_IE_CAP -// * 23 802_1X_AUTH_FAILED wrong password? -// * 24 CIPHER_SUITE_REJECTED -// * 200 BEACON_TIMEOUT -// * 201 NO_AP_FOUND -// * 202 AUTH_FAIL -// * 203 ASSOC_FAIL -// * 204 HANDSHAKE_TIMEOUT -// * -// * */ - -// /* reset saved sta IP */ -// wifi_manager_safe_reset_sta_ip_string(); - -// if (network_manager_is_flag_set(WIFI_MANAGER_REQUEST_STA_CONNECT_BIT)) { -// network_manager_clear_flag(WIFI_MANAGER_REQUEST_STA_CONNECT_BIT); -// ESP_LOGW(TAG, "WiFi Disconnected while processing user connect request. Wrong password?"); -// /* there are no retries when it's a user requested connection by design. This avoids a user hanging too much -// * in case they typed a wrong password for instance. Here we simply clear the request bit and move on */ - - -// wifi_mode_t mode; -// esp_wifi_get_mode(&mode); -// if (WIFI_MODE_STA == mode) { -// network_manager_set_flag(WIFI_MANAGER_REQUEST_STA_CONNECT_FAILED_BIT); -// // if wifi was STA, attempt to reload the previous network connection -// ESP_LOGW(TAG, "Attempting to restore previous network"); -// wifi_manager_send_message(ORDER_LOAD_AND_RESTORE_STA, NULL); -// } -// } else if (network_manager_is_flag_set(WIFI_MANAGER_REQUEST_DISCONNECT_BIT)) { -// // ESP_LOGD(TAG, "WiFi disconnected by user"); -// // /* user manually requested a disconnect so the lost connection is a normal event. Clear the flag and restart the AP */ -// // network_manager_clear_flag(WIFI_MANAGER_REQUEST_DISCONNECT_BIT); -// // wifi_manager_generate_ip_info_json(UPDATE_USER_DISCONNECT, wifi_netif,false); -// // /* erase configuration */ -// // if (wifi_manager_config_sta) { -// // ESP_LOGI(TAG, "Erasing WiFi Configuration."); -// // memset(wifi_manager_config_sta, 0x00, sizeof(wifi_config_t)); -// // /* save NVS memory */ -// // network_wifi_save_sta_config(); -// // } -// // /* start SoftAP */ -// // ESP_LOGD(TAG, "Disconnect processing complete. Ordering an AP start."); -// // wifi_manager_send_message(ORDER_START_AP, NULL); -// } else { -// /* lost connection ? */ -// // ESP_LOGE(TAG, "WiFi Connection lost."); -// // messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "WiFi Connection lost"); -// // wifi_manager_generate_ip_info_json(UPDATE_LOST_CONNECTION, wifi_netif,false); - -// // if (retries < WIFI_MANAGER_MAX_RETRY) { -// // ESP_LOGD(TAG, "Issuing ORDER_CONNECT_STA to retry connection."); -// // retries++; -// // wifi_manager_send_message(ORDER_CONNECT_STA, (void*)CONNECTION_REQUEST_AUTO_RECONNECT); -// // } else { -// // /* In this scenario the connection was lost beyond repair: kick start the AP! */ -// // retries = 0; -// // wifi_mode_t mode; -// // ESP_LOGW(TAG, "All connect retry attempts failed."); - -// // /* put us in softAP mode first */ -// // esp_wifi_get_mode(&mode); -// // /* if it was a restore attempt connection, we clear the bit */ -// // network_manager_clear_flag(WIFI_MANAGER_REQUEST_RESTORE_STA_BIT); - -// // if (WIFI_MODE_APSTA != mode) { -// // STA_duration = STA_POLLING_MIN; -// // wifi_manager_send_message(ORDER_CONNECT_STA, (void*)CONNECTION_REQUEST_MAX_FAILED); -// // } else if (STA_duration < STA_POLLING_MAX) { -// // STA_duration *= 1.25; -// // } - -// // xTimerChangePeriod(STA_timer, pdMS_TO_TICKS(STA_duration), portMAX_DELAY); -// // xTimerStart(STA_timer, portMAX_DELAY); -// // ESP_LOGD(TAG, "STA search slow polling of %d", STA_duration); -// // } -// } -// return err; -// } - char* get_disconnect_code_desc(uint8_t reason) { switch (reason) { - case 1: - return "UNSPECIFIED"; - break; - case 2: - return "AUTH_EXPIRE"; - break; - case 3: - return "AUTH_LEAVE"; - break; - case 4: - return "ASSOC_EXPIRE"; - break; - case 5: - return "ASSOC_TOOMANY"; - break; - case 6: - return "NOT_AUTHED"; - break; - case 7: - return "NOT_ASSOCED"; - break; - case 8: - return "ASSOC_LEAVE"; - break; - case 9: - return "ASSOC_NOT_AUTHED"; - break; - case 10: - return "DISASSOC_PWRCAP_BAD"; - break; - case 11: - return "DISASSOC_SUPCHAN_BAD"; - break; - case 12: - return ""; - break; - case 13: - return "IE_INVALID"; - break; - case 14: - return "MIC_FAILURE"; - break; - case 15: - return "4WAY_HANDSHAKE_TIMEOUT"; - break; - case 16: - return "GROUP_KEY_UPDATE_TIMEOUT"; - break; - case 17: - return "IE_IN_4WAY_DIFFERS"; - break; - case 18: - return "GROUP_CIPHER_INVALID"; - break; - case 19: - return "PAIRWISE_CIPHER_INVALID"; - break; - case 20: - return "AKMP_INVALID"; - break; - case 21: - return "UNSUPP_RSN_IE_VERSION"; - break; - case 22: - return "INVALID_RSN_IE_CAP"; - break; - case 23: - return "802_1X_AUTH_FAILED"; - break; - case 24: - return "CIPHER_SUITE_REJECTED"; - break; - case 200: - return "BEACON_TIMEOUT"; - break; - case 201: - return "NO_AP_FOUND"; - break; - case 202: - return "AUTH_FAIL"; - break; - case 203: - return "ASSOC_FAIL"; - break; - case 204: - return "HANDSHAKE_TIMEOUT"; - break; - default: - return "UNKNOWN"; - break; + ENUM_TO_STRING(WIFI_REASON_UNSPECIFIED); + ENUM_TO_STRING(WIFI_REASON_AUTH_EXPIRE); + ENUM_TO_STRING(WIFI_REASON_AUTH_LEAVE); + ENUM_TO_STRING(WIFI_REASON_ASSOC_EXPIRE); + ENUM_TO_STRING(WIFI_REASON_ASSOC_TOOMANY); + ENUM_TO_STRING(WIFI_REASON_NOT_AUTHED); + ENUM_TO_STRING(WIFI_REASON_NOT_ASSOCED); + ENUM_TO_STRING(WIFI_REASON_ASSOC_LEAVE); + ENUM_TO_STRING(WIFI_REASON_ASSOC_NOT_AUTHED); + ENUM_TO_STRING(WIFI_REASON_DISASSOC_PWRCAP_BAD); + ENUM_TO_STRING(WIFI_REASON_DISASSOC_SUPCHAN_BAD); + ENUM_TO_STRING(WIFI_REASON_IE_INVALID); + ENUM_TO_STRING(WIFI_REASON_MIC_FAILURE); + ENUM_TO_STRING(WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT); + ENUM_TO_STRING(WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT); + ENUM_TO_STRING(WIFI_REASON_IE_IN_4WAY_DIFFERS); + ENUM_TO_STRING(WIFI_REASON_GROUP_CIPHER_INVALID); + ENUM_TO_STRING(WIFI_REASON_PAIRWISE_CIPHER_INVALID); + ENUM_TO_STRING(WIFI_REASON_AKMP_INVALID); + ENUM_TO_STRING(WIFI_REASON_UNSUPP_RSN_IE_VERSION); + ENUM_TO_STRING(WIFI_REASON_INVALID_RSN_IE_CAP); + ENUM_TO_STRING(WIFI_REASON_802_1X_AUTH_FAILED); + ENUM_TO_STRING(WIFI_REASON_CIPHER_SUITE_REJECTED); + ENUM_TO_STRING(WIFI_REASON_INVALID_PMKID); + ENUM_TO_STRING(WIFI_REASON_BEACON_TIMEOUT); + ENUM_TO_STRING(WIFI_REASON_NO_AP_FOUND); + ENUM_TO_STRING(WIFI_REASON_AUTH_FAIL); + ENUM_TO_STRING(WIFI_REASON_ASSOC_FAIL); + ENUM_TO_STRING(WIFI_REASON_HANDSHAKE_TIMEOUT); + ENUM_TO_STRING(WIFI_REASON_CONNECTION_FAIL); + ENUM_TO_STRING(WIFI_REASON_AP_TSF_RESET); + ENUM_TO_STRING(WIFI_REASON_ROAMING); } return ""; } - - -static void network_manager_wifi_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { - ip_event_got_ip_t* s = NULL; - tcpip_adapter_if_t index; - esp_netif_ip_info_t* ip_info = NULL; - - if (event_base != IP_EVENT) - return; - switch (event_id) { - case IP_EVENT_ETH_GOT_IP: - case IP_EVENT_STA_GOT_IP: - s = (ip_event_got_ip_t*)event_data; - //tcpip_adapter_if_t index = s->if_index; - network_manager_async_got_ip(); - ip_info = &s->ip_info; - index = s->if_index; - - ESP_LOGI(TAG, "Got an IP address from interface #%i. IP=" IPSTR ", Gateway=" IPSTR ", NetMask=" IPSTR ", %s", - index, - IP2STR(&ip_info->ip), - IP2STR(&ip_info->gw), - IP2STR(&ip_info->netmask), - s->ip_changed ? "Address was changed" : "Address unchanged"); - - break; - case IP_EVENT_STA_LOST_IP: - ESP_LOGD(TAG, "IP_EVENT_STA_LOST_IP"); - break; - case IP_EVENT_AP_STAIPASSIGNED: - ESP_LOGD(TAG, "IP_EVENT_AP_STAIPASSIGNED"); - break; - case IP_EVENT_GOT_IP6: - ESP_LOGD(TAG, "IP_EVENT_GOT_IP6"); - break; - default: - break; - } -} \ No newline at end of file diff --git a/components/wifi-manager/network_wifi.h b/components/wifi-manager/network_wifi.h index 1ab44074..8eda6020 100644 --- a/components/wifi-manager/network_wifi.h +++ b/components/wifi-manager/network_wifi.h @@ -4,7 +4,7 @@ extern "C" { #endif -void init_network_wifi(); +esp_netif_t * network_wifi_start(); void destroy_network_wifi(); /** * @brief saves the current STA wifi config to flash ram storage. @@ -16,46 +16,59 @@ esp_err_t network_wifi_save_sta_config(); * @brief fetch a previously STA wifi config in the flash ram storage. * @return true if a previously saved config was found, false otherwise. */ -bool wifi_manager_fetch_wifi_sta_config(); +bool network_wifi_load_wifi_sta_config(); -wifi_config_t* wifi_manager_get_wifi_sta_config(); /** * @brief Registers handler for wifi and ip events */ -void wifi_manager_register_handlers(); +void network_wifi_register_handlers(); /** * @brief Generates the list of access points after a wifi scan. - * @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful. + * @note This is not thread-safe and should be called only if network_status_lock_json_buffer call is successful. */ -void wifi_manager_generate_access_points_json(cJSON ** ap_list); +void network_wifi_generate_access_points_json(cJSON ** ap_list); /** * @brief Clear the list of access points. - * @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful. + * @note This is not thread-safe and should be called only if network_status_lock_json_buffer call is successful. */ -void wifi_manager_clear_access_points_json(); +void network_wifi_clear_access_points_json(); -void wifi_manager_config_ap(); +esp_netif_t * network_wifi_config_ap(); -void wifi_manager_filter_unique( wifi_ap_record_t * aplist, uint16_t * aps); -esp_err_t wifi_scan_done(queue_message *msg); -esp_err_t network_wifi_start_scan(queue_message *msg); +void network_wifi_filter_unique( wifi_ap_record_t * aplist, uint16_t * aps); +esp_err_t wifi_scan_done(); +esp_err_t network_wifi_start_scan(); esp_err_t network_wifi_load_restore(queue_message *msg); esp_err_t network_wifi_order_connect(queue_message *msg); esp_err_t network_wifi_disconnected(queue_message *msg); esp_err_t network_wifi_start_ap(queue_message *msg); -bool wifi_manager_load_wifi_sta_config(wifi_config_t* config ); +bool network_wifi_get_config_for_ssid(wifi_config_t* config, const char * ssid); esp_err_t network_wifi_handle_event(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); -esp_err_t network_wifi_connect(wifi_config_t * cfg); bool is_wifi_up(); +wifi_config_t* network_wifi_set_wifi_sta_config(const char * ssid, const char * password) ; void network_wifi_clear_config(); esp_netif_t *network_wifi_get_interface(); +esp_netif_t *network_wifi_get_ap_interface(); +bool network_wifi_is_ap_sta_mode(); +bool network_wifi_is_sta_mode(); +bool network_wifi_is_ap_mode(); bool network_wifi_sta_config_changed(); -void network_wifi_get_stats( int32_t * ret_total_connected_time, int64_t * ret_last_connected, uint16_t * ret_num_disconnect); +void network_wifi_global_init(); +bool network_wifi_is_known_ap(const char * ssid); +esp_err_t network_wifi_connect(const char * ssid, const char * password); +esp_err_t network_wifi_erase_legacy(); +esp_err_t network_wifi_connect_ssid(const char * ssid); +esp_err_t network_wifi_connect_active_ssid(); +esp_err_t network_wifi_erase_known_ap(); +esp_err_t network_wifi_set_sta_mode(); +size_t network_wifi_get_known_count(); +esp_err_t network_wifi_built_known_ap_list(); +const wifi_sta_config_t* network_wifi_load_active_config(); #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/components/wifi-manager/settings.png b/components/wifi-manager/settings.png deleted file mode 100644 index 1828d4cf94ad881b133eeeae388b6244cd5d50fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 901 zcmV;01A6?4P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^XO*0N*(00Q+%L_t(oN9~tCXcJ)=#-r)wud!5VqM(S0 zgIggGp(QQp=3=D|P7V%&=oTT-4lMySU^fv%+!Xu+DvIEs;-Ew^C?>(^C<;n4h+tEp z38L{O#2)yv z3UDgGk4WI${A&Ef_OGzM>f?lPCW{kT&nF1sOaR>xz_yGNe@=+Lq`<2;m>ZCDGI$^6 z_kmx-sobEp+d~+*gwW6MGx)mz>w0{AoMp3Fwz#;+=H}+u^z<~toiFW$e}VOecsD6Z zsu48&)(x5*kH=ZLTxK%W>vc9YH6@+X`47C0@{B^^l&;a_R4T=mmzOC{u1=@JW@l%m z^EzLoNTX1AEHri_~DDijKBNAaG}tU8y=5#FAP#iEHp zeuStIEm1B7O`n7zR;$$rZBP7ai45F?-@sq}M)VdM=f&Zbl@&tU*XZb|fdOuTBCLhR zv-&~q0iD2Aim(oQsi=@@q#$Wy|A!AXnQIY3MK~m zE^?RLbE-&TEP^3dNIRv)+@FlPcUw?RJ~Z%*;sV zbv{p#CJm$55}FMj8yjP#Qis#Gd$Vq!u%r!)U4-b;CAp~&xnHZIc_Y&)r&ZR3_4_b?RDY!?%26zB)+z%Oe+fZ|^pOfLDm(b=869rO brd+Q7CIFdJVy1Sa00000NkvXXu0mjf - -#include -#include -#include -#include -#include "cmd_system.h" -#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG -#include "esp_log.h" -#include "esp_wifi_types.h" -#include "http_server_handlers.h" -#include "messaging.h" -#include "network_ethernet.h" -#include "network_status.h" -#include "network_wifi.h" -#include "state_machine.h" -#include "trace.h" -#include "dns_server.h" -BaseType_t network_manager_task; -/* objects used to manipulate the main queue of events */ -QueueHandle_t network_manager_queue; - -using namespace stateless; -using namespace std::placeholders; -static const char TAG[] = "network_manager"; -static TaskHandle_t task_network_manager = NULL; -TimerHandle_t STA_timer = NULL; -uint32_t STA_duration; - -static int32_t total_connected_time = 0; -static int64_t last_connected = 0; -static uint16_t num_disconnect = 0; -void network_wifi_get_stats(int32_t* ret_total_connected_time, int64_t* ret_last_connected, uint16_t* ret_num_disconnect) { - *ret_total_connected_time = total_connected_time; - *ret_last_connected = last_connected; - *ret_num_disconnect = num_disconnect; -} -namespace { - -std::ostream& operator<<(std::ostream& os, const state_t s) { - //static const char* name[] = { "idle", "stopped", "started", "running" }; - static const char* name[] = { - - "initializing", - "global", - "eth_starting", - "eth_active", - "eth_active_linkup", - "eth_active_connected", - "eth_active_linkdown", - "wifi_up", - "wifi_initializing", - "network_manager_async", - "wifi_connecting_scanning", - "wifi_connecting", - "wifi_connected", - "wifi_disconnecting", - "wifi_user_disconnected", - "wifi_connected_waiting_for_ip", - "wifi_connected_scanning", - "wifi_lost_connection", - "wifi_ap_mode", - "wifi_ap_mode_scanning", - "wifi_ap_mode_scan_done", - "wifi_ap_mode_connecting", - "wifi_ap_mode_connected", - "system_rebooting"}; - os << name[(int)s]; - return os; -} - -const char* trigger_to_string(trig_t trigger) { - switch (trigger) { - ENUM_TO_STRING(t_link_up); - ENUM_TO_STRING(t_link_down); - ENUM_TO_STRING(t_configure); - ENUM_TO_STRING(t_got_ip); - ENUM_TO_STRING(t_next); - ENUM_TO_STRING(t_start); - ENUM_TO_STRING(t_scan); - ENUM_TO_STRING(t_fail); - ENUM_TO_STRING(t_success); - ENUM_TO_STRING(t_scan_done); - ENUM_TO_STRING(t_connect); - ENUM_TO_STRING(t_reboot); - ENUM_TO_STRING(t_reboot_url); - ENUM_TO_STRING(t_lost_connection); - ENUM_TO_STRING(t_update_status); - ENUM_TO_STRING(t_disconnect); - default: - - break; - } - return "Unknown trigger"; -} -std::ostream& operator<<(std::ostream& os, const trig_t & t) { - //static const char* name[] = { "start", "stop", "set_speed", "halt" }; - os << trigger_to_string(t); - return os; -} - -} // namespace - -// namespace stateless -// { - -// template<> void print_state(std::ostream& os, const state& s) -// { os << s; } - -// template<> void print_trigger 0) - total_connected_time += ((esp_timer_get_time() - last_connected) / (1000 * 1000)); - last_connected = 0; - num_disconnect++; - ESP_LOGW(TAG, "Wifi disconnected. Number of disconnects: %d, Average time connected: %d", num_disconnect, num_disconnect > 0 ? (total_connected_time / num_disconnect) : 0); - //network_manager_async_connect(wifi_manager_get_wifi_sta_config()); - if (retries < WIFI_MANAGER_MAX_RETRY) { - ESP_LOGD(TAG, "Issuing ORDER_CONNECT_STA to retry connection."); - retries++; - network_manager_async_connect(wifi_manager_get_wifi_sta_config()); - free(disconnected_event); - } else { - /* In this scenario the connection was lost beyond repair: kick start the AP! */ - retries = 0; - wifi_mode_t mode; - ESP_LOGW(TAG, "All connect retry attempts failed."); - - /* put us in softAP mode first */ - esp_wifi_get_mode(&mode); - if (WIFI_MODE_APSTA != mode) { - STA_duration = STA_POLLING_MIN; - network_manager_async_lost_connection(disconnected_event); - } else if (STA_duration < STA_POLLING_MAX) { - STA_duration *= 1.25; - free(disconnected_event); - } - /* keep polling for existing connection */ - xTimerChangePeriod(STA_timer, pdMS_TO_TICKS(STA_duration), portMAX_DELAY); - xTimerStart(STA_timer, portMAX_DELAY); - ESP_LOGD(TAG, "STA search slow polling of %d", STA_duration); - } - }) - .on_entry([=](const TTransition& t) { - wifi_manager_safe_reset_sta_ip_string(); - }) - .permit(trig_t::t_connect, state_t::wifi_connecting) - .permit(trig_t::t_lost_connection, state_t::wifi_ap_mode); - // Register a callback for state transitions (the default does nothing). - sm_.on_transition([](const TTransition& t) { - std::cout << "transition from [" << t.source() << "] to [" - << t.destination() << "] via trigger [" << t.trigger() << "]" - << std::endl; - }); - - // Override the default behaviour of throwing when a trigger is unhandled. - sm_.on_unhandled_trigger([](const state_t s, const trig_t t) { - std::cerr << "ignore unhandled trigger [" << t << "] in state [" << s - << "]" << std::endl; - }); - } - - public: - bool - Allowed(trig_t trigger) { - if (!sm_.can_fire(trigger)) { - ESP_LOGW(TAG, "Network manager might not be able to process trigger %s", trigger_to_string(trigger)); - return false; - } - return true; - } - bool Fire(trig_t trigger) { - try { - sm_.fire(trigger); - return true; - } catch (const std::exception& e) { - std::cerr << e.what() << '\n'; - } - return false; - } - bool Event(queue_message& msg) { - trig_t trigger = msg.trigger; - try { - if (trigger == trig_t::t_connect) { - sm_.fire(connect_trigger_, msg.wifi_config); - } else if (trigger == trig_t::t_reboot) { - sm_.fire(reboot_trigger_, msg.rtype); - } else if (trigger == trig_t::t_reboot_url) { - sm_.fire(reboot_ota_, msg.strval); - } else if (trigger == trig_t::t_lost_connection) { - sm_.fire(disconnected_, msg.disconnected_event); - } else { - sm_.fire(trigger); - } - return true; - } catch (const std::exception& e) { - std::cerr << e.what() << '\n'; - return false; - } - } - - private: - uint8_t reconnect_attempt = 0; - bool existing_connection = false; - uint8_t retries = 0; - TStateMachine sm_; - TConnectTrigger connect_trigger_; - TRebootTrigger reboot_trigger_; - TRebootOTATrigger reboot_ota_; - TDisconnectTrigger disconnected_; -}; - -NetworkManager nm; -void network_manager_start() { - nm.Fire(trig_t::t_start); -} - -bool network_manager_async(trig_t trigger) { - queue_message msg; - msg.trigger = trigger; - if (nm.Allowed(trigger)) { - return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); - } - return false; -} -bool network_manager_async_fail() { - return network_manager_async(trig_t::t_fail); -} -bool network_manager_async_success() { - return network_manager_async(trig_t::t_success); -} - -bool network_manager_async_link_up() { - return network_manager_async(trig_t::t_link_up); -} -bool network_manager_async_link_down() { - return network_manager_async(trig_t::t_link_down); -} -bool network_manager_async_configure() { - return network_manager_async(trig_t::t_configure); -} -bool network_manager_async_got_ip() { - return network_manager_async(trig_t::t_got_ip); -} -bool network_manager_async_next() { - return network_manager_async(trig_t::t_next); -} -bool network_manager_async_start() { - return network_manager_async(trig_t::t_start); -} -bool network_manager_async_scan() { - return network_manager_async(trig_t::t_scan); -} - -bool network_manager_async_update_status() { - return network_manager_async(trig_t::t_update_status); -} - -bool network_manager_async_disconnect() { - return network_manager_async(trig_t::t_disconnect); -} - -bool network_manager_async_scan_done() { - return network_manager_async(trig_t::t_scan_done); -} -bool network_manager_async_connect(wifi_config_t* wifi_config) { - queue_message msg; - msg.trigger = trig_t::t_connect; - msg.wifi_config = wifi_config; - if (nm.Allowed(msg.trigger)) { - return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); - } - return false; -} -bool network_manager_async_lost_connection(wifi_event_sta_disconnected_t* disconnected_event) { - queue_message msg; - msg.trigger = trig_t::t_lost_connection; - msg.disconnected_event = disconnected_event; - if (nm.Allowed(msg.trigger)) { - return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); - } - return false; -} -bool network_manager_async_reboot(reboot_type_t rtype) { - queue_message msg; - msg.trigger = trig_t::t_reboot; - msg.rtype = rtype; - if (nm.Allowed(msg.trigger)) { - return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); - } - return false; -} - -void network_manager_reboot_ota(char* url) { - queue_message msg; - - if (url == NULL) { - msg.trigger = trig_t::t_reboot; - msg.rtype = reboot_type_t::OTA; - } else { - msg.trigger = trig_t::t_reboot_url; - msg.strval = strdup(url); - } - if (nm.Allowed(msg.trigger)) { - xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); - } - return; -} - -// switch (msg.code) { -// case EVENT_SCAN_DONE: -// err = wifi_scan_done(&msg); -// /* callback */ -// if (cb_ptr_arr[msg.code]) { -// ESP_LOGD(TAG, "Invoking SCAN DONE callback"); -// (*cb_ptr_arr[msg.code])(NULL); -// ESP_LOGD(TAG, "Done Invoking SCAN DONE callback"); -// } -// break; -// case ORDER_START_WIFI_SCAN: -// err = network_wifi_start_scan(&msg); -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); -// break; - -// case ORDER_LOAD_AND_RESTORE_STA: -// err = network_wifi_load_restore(&msg); -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); - -// break; - -// case ORDER_CONNECT_STA: -// err = network_wifi_order_connect(&msg); -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); - -// break; - -// case EVENT_STA_DISCONNECTED: -// err = network_wifi_disconnected(&msg); -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); -// break; - -// case ORDER_START_AP: -// err = network_wifi_start_ap(&msg); -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); -// break; - -// case EVENT_GOT_IP: -// ESP_LOGD(TAG, "MESSAGE: EVENT_GOT_IP"); -// /* save IP as a string for the HTTP server host */ -// //s->ip_info.ip.addr -// ip_event_got_ip_t* event = (ip_event_got_ip_t*)msg.param; -// wifi_manager_safe_update_sta_ip_string(&(event->ip_info.ip)); -// wifi_manager_generate_ip_info_json(network_manager_is_flag_set(WIFI_MANAGER_REQUEST_STA_CONNECT_FAILED_BIT)? UPDATE_FAILED_ATTEMPT_AND_RESTORE : UPDATE_CONNECTION_OK, event->esp_netif, event->ip_changed); -// free(msg.param); -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); -// break; -// case UPDATE_CONNECTION_OK: -// messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "UPDATE_CONNECTION_OK not implemented"); -// break; -// case ORDER_DISCONNECT_STA: -// ESP_LOGD(TAG, "MESSAGE: ORDER_DISCONNECT_STA. Calling esp_wifi_disconnect()"); - -// /* precise this is coming from a user request */ -// xEventGroupSetBits(wifi_manager_event_group, WIFI_MANAGER_REQUEST_DISCONNECT_BIT); - -// /* order wifi discconect */ -// ESP_ERROR_CHECK(esp_wifi_disconnect()); - -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); - -// break; - -// case ORDER_RESTART_OTA_URL: -// ESP_LOGD(TAG, "Calling start_ota."); -// start_ota(msg.param, NULL, 0); -// free(msg.param); -// break; - -// case ORDER_RESTART_RECOVERY: - -// break; -// case ORDER_RESTART: -// ESP_LOGD(TAG, "Calling simple_restart."); -// simple_restart(); -// break; -// case ORDER_UPDATE_STATUS: -// ; -// break; -// case EVENT_ETH_TIMEOUT: -// ESP_LOGW(TAG, "Ethernet connection timeout. Rebooting with WiFi Active"); -// network_manager_reboot(RESTART); -// break; -// case EVENT_ETH_LINK_UP: -// /* callback */ -// ESP_LOGD(TAG,"EVENT_ETH_LINK_UP message received"); -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); -// break; -// case EVENT_ETH_LINK_DOWN: -// /* callback */ -// if (cb_ptr_arr[msg.code]) -// (*cb_ptr_arr[msg.code])(NULL); -// break; -// default: -// break; - -// } /* end of switch/case */ - -// if (!network_ethernet_wait_for_link(500)) { -// if(network_ethernet_enabled()){ -// ESP_LOGW(TAG, "Ethernet not connected. Starting Wifi"); -// } -// init_network_wifi(); -// wifi_manager_send_message(ORDER_LOAD_AND_RESTORE_STA, NULL); -// } - -void network_manager(void* pvParameters) { - queue_message msg; - esp_err_t err = ESP_OK; - BaseType_t xStatus; - network_manager_async(trig_t::t_start); - - /* main processing loop */ - for (;;) { - xStatus = xQueueReceive(network_manager_queue, &msg, portMAX_DELAY); - - if (xStatus == pdPASS) { - // pass the event to the sync processor - nm.Event(msg); - } /* end of if status=pdPASS */ - } /* end of for loop */ - - vTaskDelete(NULL); -} - -void network_manager_destroy() { - vTaskDelete(task_network_manager); - task_network_manager = NULL; - /* heap buffers */ - destroy_network_status(); - destroy_network_wifi(); - destroy_network_status(); - /* RTOS objects */ - vQueueDelete(network_manager_queue); - network_manager_queue = NULL; -} \ No newline at end of file diff --git a/components/wifi-manager/state_machine.h b/components/wifi-manager/state_machine.h deleted file mode 100644 index dd09f39a..00000000 --- a/components/wifi-manager/state_machine.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once -#ifdef __cplusplus -extern "C" { - -#endif - -#include "esp_wifi.h" -#include "esp_wifi_types.h" - - -#define STA_POLLING_MIN (15 * 1000) -#define STA_POLLING_MAX (10 * 60 * 1000) - -//enum class state { idle, stopped, started, running }; - -//enum class trigger { start, stop, set_speed, halt }; -typedef enum { - t_link_up, - t_link_down, - t_configure, - t_got_ip, - t_disconnect, - t_next, - t_start, - t_scan, - t_fail, - t_success, - t_scan_done, - t_connect, - t_reboot, - t_reboot_url, - t_lost_connection, - t_update_status -} trig_t; - -typedef enum { - instantiated, - initializing, - global, - eth_starting, - eth_active, - eth_active_linkup, - eth_active_connected, - eth_active_linkdown, - wifi_up, - wifi_initializing, - wifi_connecting_scanning, - wifi_connecting, - wifi_connected, - wifi_disconnecting, - wifi_user_disconnected, - wifi_connected_waiting_for_ip, - wifi_connected_scanning, - wifi_lost_connection, - wifi_ap_mode, - wifi_ap_mode_scanning, - wifi_ap_mode_scan_done, - wifi_ap_mode_connecting, - wifi_ap_mode_connected, - system_rebooting -} state_t; -typedef enum { - OTA, - RECOVERY, - RESTART, -} reboot_type_t; -typedef struct { - trig_t trigger; - union - { - wifi_config_t* wifi_config; - reboot_type_t rtype; - char * strval; - wifi_event_sta_disconnected_t * disconnected_event; - } ; - - -} queue_message; - -bool network_manager_event_simple(trig_t trigger); -bool network_manager_event(trig_t trigger, void* param); -bool network_t_connect_event(wifi_config_t* wifi_config); -bool network_t_link_event(bool link_up); -bool network_manager_async_event(trig_t trigger, void* param); -bool network_manager_async(trig_t trigger); -bool network_manager_async_fail(); -bool network_manager_async_success(); -bool network_manager_async_link_up(); -bool network_manager_async_link_down(); -bool network_manager_async_configure(); -bool network_manager_async_got_ip(); -bool network_manager_async_next(); -bool network_manager_async_start(); -bool network_manager_async_scan(); -bool network_manager_async_scan_done(); -bool network_manager_async_connect(wifi_config_t* wifi_config); -bool network_manager_async_lost_connection(wifi_event_sta_disconnected_t * disconnected_event); -bool network_manager_async_reboot(reboot_type_t rtype); -void network_manager_reboot_ota(char* url); -bool network_manager_async_disconnect(); -bool network_manager_async_update_status(); -/** - * Allocate heap memory for the wifi manager and start the wifi_manager RTOS task - */ -void network_manager_start(); -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/components/wifi-manager/webapp/mock/ap.json b/components/wifi-manager/webapp/mock/ap.json index cf27ca60..49ffe0fb 100644 --- a/components/wifi-manager/webapp/mock/ap.json +++ b/components/wifi-manager/webapp/mock/ap.json @@ -1,13 +1,13 @@ [ -{"ssid":"Pantum-AP-A6D49F","chan":11,"rssi":-55,"auth":4}, -{"ssid":"a0308","chan":1,"rssi":-56,"auth":3}, -{"ssid":"dlink-D9D8","chan":11,"rssi":-82,"auth":4}, -{"ssid":"Linksys06730","chan":7,"rssi":-85,"auth":3}, -{"ssid":"SINGTEL-5171","chan":9,"rssi":-88,"auth":4}, -{"ssid":"1126-1","chan":11,"rssi":-89,"auth":4}, -{"ssid":"The Shah 5GHz-2","chan":1,"rssi":-90,"auth":3}, -{"ssid":"SINGTEL-1D28 (2G)","chan":11,"rssi":-91,"auth":3}, -{"ssid":"dlink-F864","chan":1,"rssi":-92,"auth":4}, -{"ssid":"dlink-74F0","chan":1,"rssi":-93,"auth":4}, -{"ssid":"MyTestSSID","chan":2,"rssi":-53,"auth":4} +{"ssid":"Pantum-AP-A6D49F","chan":11,"rssi":-55,"auth":4, "known":false}, +{"ssid":"a0308","chan":1,"rssi":-56,"auth":3, "known":false}, +{"ssid":"dlink-D9D8","chan":11,"rssi":-82,"auth":4, "known":false}, +{"ssid":"Linksys06730","chan":7,"rssi":-85,"auth":3, "known":false}, +{"ssid":"SINGTEL-5171","chan":9,"rssi":-88,"auth":4, "known":false}, +{"ssid":"1126-1","chan":11,"rssi":-89,"auth":4, "known":false}, +{"ssid":"The Shah 5GHz-2","chan":1,"rssi":-90,"auth":3, "known":false}, +{"ssid":"SINGTEL-1D28 (2G)","chan":11,"rssi":-91,"auth":3, "known":false}, +{"ssid":"dlink-F864","chan":1,"rssi":-92,"auth":4, "known":false}, +{"ssid":"dlink-74F0","chan":1,"rssi":-93,"auth":4, "known":false}, +{"ssid":"MyTestSSID","chan":2,"rssi":-53,"auth":4, "known":true} ] \ No newline at end of file diff --git a/components/wifi-manager/webapp/mock/messages.json b/components/wifi-manager/webapp/mock/messages.json index c90985b8..654c940f 100644 --- a/components/wifi-manager/webapp/mock/messages.json +++ b/components/wifi-manager/webapp/mock/messages.json @@ -54,7 +54,7 @@ "current_time": 147319 }, { - "message": "Wifi connected", + "message": "Network connected", "type": "MESSAGING_INFO", "class": "MESSAGING_CLASS_SYSTEM", "sent_time": 141256, diff --git a/components/wifi-manager/webapp/src/js/custom.js b/components/wifi-manager/webapp/src/js/custom.js index 321a5502..71a7d4e3 100644 --- a/components/wifi-manager/webapp/src/js/custom.js +++ b/components/wifi-manager/webapp/src/js/custom.js @@ -1098,7 +1098,7 @@ function rssiToIcon(rssi) { } function refreshAP() { - $.getJSON('/scan.json', async function() { + $.getJSON('/scan.json', async function() { await sleep(2000); $.getJSON('/ap.json', function(data) { if (data.length > 0) { @@ -1335,6 +1335,7 @@ function handleRecoveryMode(data) { $('#boot-form').attr('action', '/recovery.json'); } } + function hasConnectionChanged(data){ // gw: "192.168.10.1" // ip: "192.168.10.225" @@ -1366,10 +1367,10 @@ function handleWifiDialog(data){ $('.connecting-status').show(); } if(SystemConfig.ap_ssid){ - $('#apName').text(SystemConfig.ap_ssid); + $('#apName').text(SystemConfig.ap_ssid.value); } if(SystemConfig.ap_pwd){ - $('#apPass').text(SystemConfig.ap_pwd); + $('#apPass').text(SystemConfig.ap_pwd.value); } if(!data) { diff --git a/components/wifi-manager/wifi0.png b/components/wifi-manager/wifi0.png deleted file mode 100644 index a34274b6a2b2e088c890d44c13cc1a21e4ec2f47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 605 zcmV-j0;2tiP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf5&!@T5&_cPe*6Fc02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^Ag1Z(U700GTOL_t(YOXZYJC`3^d$KO1WhBQ9%6|tBi zOrb2q!p6daElXt<7WR{krr5BNuVN$3LReZ@XiAMTMM|2Ljns%(DBoEOzw_Rm=EazK zgVp%guRHhLbN=(*edk^+>y-=b;B`10)3B_VBsL6d0rL&;fjqgGSUabIoE63@ zbDEH~pJ@htjKpI|_2Fepg5(cLjZtYTM~z8UK+!t9D3f%^S$KjeYHe+;o8*uxjGJ76 zyp6>7;S{WgP;$4Zm4jwWl@Orlno&BT1Eg49U7k|*w+j}@d0?CeY2S=g4szFgIh7VU zp&JT~*}qpm`z;?j%x}5Ezr8hoqB@Kt5bv50u_ANnZ`6Y6rR5Kz#B8^u9l02gzg=c1`=x0g=|_@HfG!jC00000NkvXXu0mjfZ`cB( diff --git a/components/wifi-manager/wifi1.png b/components/wifi-manager/wifi1.png deleted file mode 100644 index 7aa0f6f0f8d0c678c6a01448a1626c379e3dba17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 613 zcmV-r0-F7aP)N2bZe?^J zG%heMF*ZbLuk`=`0pdwSK~zXf<~sw_ZK_eSg32=i%{~ zEm7&Zo)Moa-htnW+qB4n7rNi?AIH2hN~;fbup{2A;cG(>?fZ_q*G%yPhVflZ8X zMjn|P#Y#Q8*_%KZH?Yn|+24RiZZPv@^PJugnvtVku!O}K7krpx_THLBZ&(i0usBf7 z6V^@0Qu8SnGdbuX<4%Py!6h5QLo>v+Le{G6s6;zPG)-Ga7<;T&5QGmdXfU&C$y(rA zZe*#ZFy3cXc50BYLqzbFz&*A&-zzgRsInaD`soI;FO|p8GG0_jGEqEXjMwlEfvyvh(_=$h*=S|Gy?$WK|0R?Kqdf$KDikye$U z4%x;ke&wPK`NrzsR~OG){?K7Qdtto$nW}1nPhh?^JM;=-TbZdwLN6^hxS*HvE160; z3EhJ6zrlJANxulwtx~=|2GGxEXRP)2%Vv8An6!$_VEubk00000NkvXXu0mjfqJIwT diff --git a/components/wifi-manager/wifi2.png b/components/wifi-manager/wifi2.png deleted file mode 100644 index 8ae5619e62d5cda7662033ba4c6f04f99ebf77df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 615 zcmV-t0+{`YP)N2bZe?^J zG%heMF*ZbLuk`=`0pv+UK~zXf-PB7cL{S*P@wp??ki7DWSSa!+l!aK>SXi)Ssoa?? z>}4axhK;<6jbtG#Ei9xIMUj%MY@~=-DDNzW|2K2abY|uvxk^s`y7zqN`@Z{_bH`)b z_OEgby2&wUNJJfMzu!NC8D)r27s}y?GY+H?L%I4=*bDC!VlO&n$g`76PY6f_9E|43ZZJ$v^|fsJ%0L5gU+%5+?g9c+9*8vU_41 zeMN4jgC4i4`8+c8Vl}}9js zk@z-_V19&>yF#seXi@#5CWbsLP;|v{0QHb!d33o;dB2UABIlmt1Z4Zh^@~dZQD-?BU6w~+^Z^{<2wD6KUql%^%#s$uaWrmmB z8R<0LKu|8gd>YB26&FXY*X!*;H|Oc$(ElGE&pV1eRBM20l868R002ovPDHLkV1iqB B3)27q diff --git a/components/wifi-manager/wifi24.png b/components/wifi-manager/wifi24.png deleted file mode 100644 index a34274b6a2b2e088c890d44c13cc1a21e4ec2f47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 605 zcmV-j0;2tiP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf5&!@T5&_cPe*6Fc02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^Ag1Z(U700GTOL_t(YOXZYJC`3^d$KO1WhBQ9%6|tBi zOrb2q!p6daElXt<7WR{krr5BNuVN$3LReZ@XiAMTMM|2Ljns%(DBoEOzw_Rm=EazK zgVp%guRHhLbN=(*edk^+>y-=b;B`10)3B_VBsL6d0rL&;fjqgGSUabIoE63@ zbDEH~pJ@htjKpI|_2Fepg5(cLjZtYTM~z8UK+!t9D3f%^S$KjeYHe+;o8*uxjGJ76 zyp6>7;S{WgP;$4Zm4jwWl@Orlno&BT1Eg49U7k|*w+j}@d0?CeY2S=g4szFgIh7VU zp&JT~*}qpm`z;?j%x}5Ezr8hoqB@Kt5bv50u_ANnZ`6Y6rR5Kz#B8^u9l02gzg=c1`=x0g=|_@HfG!jC00000NkvXXu0mjfZ`cB( diff --git a/components/wifi-manager/wifi3.png b/components/wifi-manager/wifi3.png deleted file mode 100644 index b366cae559c3217cabf8ca93eeeba75de7de2898..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 zcmV;B0&o3^P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^MlX|MGF00IC>L_t(YOSRNLNE=}o$MICDh!#m&@gJ3T z5GvY$3J#)!i-Uu%bu9D}9J))FAm~yTX;sig#6h%U2L}lV5fqey6sImyh*;_%v{j2k z^!tSO4ewo^_=jGH4|nhHectDCm-jB>dES4ehG9}e$3t|%OC%ESF>g!|y+$j_psmdd z22$LNK`hef1Xnl6;TyFd(2vJ>fI?5Vc~vr*9Am*gE=_~`_=8P)(8{Z@eQoo0CU^1I z6#t8JtkLs{y}$s$p;KUGv4L@Fv5+106tX+U zng0xV9NUanF$VeNatH3tc|pwr$3_|3&^9cbvm4%0_ysrp@$yL0)Q<$#m)mn2xT;yS zf(z5+Hu5fCoTuEveCN&I?7r6!!$2EG-fY?p|0000base_path, "/res/", sizeof(rest_context->base_path)); httpd_config_t config = HTTPD_DEFAULT_CONFIG(); @@ -141,15 +146,17 @@ esp_err_t http_server_start() //todo: use the endpoint below to configure session token? // config.open_fn - ESP_LOGD(TAG, "Starting HTTP Server"); + MEMTRACE_PRINT_DELTA_MESSAGE( "Starting HTTP Server"); esp_err_t err= __httpd_start(&_server, &config); if(err != ESP_OK){ ESP_LOGE_LOC(TAG,"Start server failed"); } else { - + MEMTRACE_PRINT_DELTA_MESSAGE( "HTTP Server started. Registering common handlers"); register_common_handlers(_server); + MEMTRACE_PRINT_DELTA_MESSAGE("Registering regular handlers"); register_regular_handlers(_server); + MEMTRACE_PRINT_DELTA_MESSAGE("HTTP Server regular handlers registered"); } return err; diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index be5c3f04..3e22539a 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -1,20 +1,20 @@ menu "Squeezelite-ESP32" menu "Logging" config LOGGING_SLIMPROTO - string "logging level for slimproto" - default "info" - help - Set logging level info|debug|sdebug + string "logging level for slimproto" + default "info" + help + Set logging level info|debug|sdebug config LOGGING_STREAM - string "logging level for stream" - default "info" - help - Set logging level info|debug|sdebug - config LOGGING_DECODE - string "logging level for decode" - default "info" - help - Set logging level info|debug|sdebug + string "logging level for stream" + default "info" + help + Set logging level info|debug|sdebug + config LOGGING_DECODE + string "logging level for decode" + default "info" + help + Set logging level info|debug|sdebug config LOGGING_OUTPUT string "logging level for output" default "info" @@ -102,11 +102,22 @@ menu "Squeezelite-ESP32" config DAC_CONTROLSET string default '{ "init": [ {"reg":41, "val":128}, {"reg":18, "val":255} ], "poweron": [ {"reg":18, "val":64, "mode":"or"} ], "poweroff": [ {"reg":18, "val":191, "mode":"and" } ] }' if TWATCH2020 - default "" + default "" # AGGREGATES - end endmenu menu "Audio settings" + menu "Known Configurations" + visible if BASIC_I2C_BT + config DAC_KNOWN_CONFIGURATIONS + string "Known DAC configurations" + default "ESP32A1S Old (AC101)|ESP32A1S V2.2+ (ES8388)|Xiaomi Gateway" if BASIC_I2C_BT + default "" + config DAC_KNOWN_CONFIGURATIONS_GPIOS + string "GPIOs for known DAC configurations" + default "model=AC101,sda=33,scl=32,bck=27,ws=26,di=35,do=25|model=ES8388,bck=5,ws=25,do=26,sda=18,scl=23,i2c=16|model=I2S,bck=12,ws=26,do=25,i2c=106,sda=18,scl=23" if BASIC_I2C_BT + default "" + endmenu menu "DAC settings" visible if BASIC_I2C_BT menu "I2S settings" @@ -198,7 +209,8 @@ menu "Squeezelite-ESP32" int "Control loop delay." default 500 help - Decreasing this will lead to a more responsive BT control, but might lead to noisy log files if debug is enabled. + Decreasing this will lead to a more responsive BT control, + but might lead to noisy log files if debug is enabled. config A2DP_CONNECT_TIMEOUT_MS int "Time out duration when trying to connect to an A2DP audio sink" default 1000 @@ -351,5 +363,5 @@ menu "Squeezelite-ESP32" string "Default command line to execute" default "squeezelite -o I2S -b 500:2000 -d all=info -C 30" help - This is the command to run when starting the device + This is the command to run when starting the device endmenu \ No newline at end of file diff --git a/main/esp_app_main.c b/main/esp_app_main.c index c355a94e..22724c3f 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -45,14 +45,16 @@ #include "display.h" #include "accessors.h" #include "cmd_system.h" +#include "globdefs.h" + static const char certs_namespace[] = "certificates"; static const char certs_key[] = "blob"; static const char certs_version[] = "version"; const char unknown_string_placeholder[] = "unknown"; const char null_string_placeholder[] = "null"; -EventGroupHandle_t wifi_event_group; +EventGroupHandle_t network_event_group; -bool bypass_wifi_manager=false; +bool bypass_network_manager=false; const int CONNECTED_BIT = BIT0; #define JOIN_TIMEOUT_MS (10000) #define LOCAL_MAC_SIZE 20 @@ -61,7 +63,7 @@ static const char TAG[] = "esp_app_main"; char * fwurl = NULL; RTC_NOINIT_ATTR uint32_t RebootCounter ; -static bool bWifiConnected=false; +static bool bNetworkConnected=false; extern const uint8_t server_cert_pem_start[] asm("_binary_github_pem_start"); extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end"); @@ -71,12 +73,11 @@ extern void display_init(char *welcome); const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); } const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); } bool is_recovery_running; -/* 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){ + +void cb_connection_got_ip(nm_state_t new_state, int sub_state){ static ip4_addr_t ip; tcpip_adapter_ip_info_t ipInfo; - - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + network_get_ip_info(&ipInfo); if (ip.addr && ipInfo.ip.addr != ip.addr) { ESP_LOGW(TAG, "IP change, need to reboot"); if(!wait_for_commit()){ @@ -85,10 +86,10 @@ void cb_connection_got_ip(void *pvParameter){ esp_restart(); } ip.addr = ipInfo.ip.addr; - ESP_LOGI(TAG, "Wifi connected!"); - messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"Wifi connected"); - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - bWifiConnected=true; + ESP_LOGI(TAG, "Network connected!"); + messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"Network connected"); + xEventGroupSetBits(network_event_group, CONNECTED_BIT); + bNetworkConnected=true; led_unpush(LED_GREEN); if(is_recovery_running){ // when running in recovery, send a LMS discovery message @@ -98,24 +99,24 @@ void cb_connection_got_ip(void *pvParameter){ discover_ota_server(5); } } -void cb_connection_sta_disconnected(void *pvParameter){ +void cb_connection_sta_disconnected(nm_state_t new_state, int sub_state){ 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); + bNetworkConnected=false; + xEventGroupClearBits(network_event_group, CONNECTED_BIT); } bool wait_for_wifi(){ - bool connected=(xEventGroupGetBits(wifi_event_group) & CONNECTED_BIT)!=0; + bool connected=(xEventGroupGetBits(network_event_group) & CONNECTED_BIT)!=0; if(!connected){ - ESP_LOGD(TAG,"Waiting for WiFi..."); - connected = (xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + ESP_LOGD(TAG,"Waiting for Network..."); + connected = (xEventGroupWaitBits(network_event_group, CONNECTED_BIT, pdFALSE, pdTRUE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS)& CONNECTED_BIT)!=0; if(!connected){ - ESP_LOGW(TAG,"wifi timeout."); + ESP_LOGW(TAG,"Network timeout."); } else { - ESP_LOGI(TAG,"WiFi Connected!"); + ESP_LOGI(TAG,"Network Connected!"); } } return connected; @@ -170,9 +171,8 @@ esp_err_t update_certificates(bool force){ char *str=NULL; bool changed=false; if ( (esp_err= nvs_get_str(handle, certs_version, NULL, &len)) == ESP_OK) { - str=(char *)malloc(len+1); + str=(char *)malloc_init_external(len+1); if(str){ - memset(str,0x00,len+1); if ( (esp_err = nvs_get_str(handle, certs_version, str, &len)) == ESP_OK) { ESP_LOGI(TAG,"Certificate version: %s", str); } @@ -230,7 +230,8 @@ const char * get_certificate(){ size_t len; esp_err = nvs_get_blob(handle, certs_key, NULL, &len); if( esp_err == ESP_OK) { - blob = (char *) heap_caps_malloc(len+1, (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + //blob = (char *) heap_caps_malloc(len+1, (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + blob = (char *) malloc_init_external(len+1); if(!blob){ log_send_messaging(MESSAGING_ERROR,"Unable to retrieve HTTPS certificates. %s","Memory allocation failed"); return ""; @@ -275,7 +276,7 @@ void register_default_with_mac(const char* key, char* defval) { char* fullvalue = NULL; esp_read_mac((uint8_t*)&mac, ESP_MAC_WIFI_STA); snprintf(macStr, LOCAL_MAC_SIZE - 1, "-%x%x%x", mac[3], mac[4], mac[5]); - fullvalue = malloc(strlen(defval)+sizeof(macStr)+1); + fullvalue = malloc_init_external(strlen(defval)+sizeof(macStr)+1); if(fullvalue){ strcpy(fullvalue, defval); strcat(fullvalue, macStr); @@ -287,6 +288,13 @@ void register_default_with_mac(const char* key, char* defval) { } } +#ifndef CONFIG_DAC_KNOWN_CONFIGURATIONS +#define CONFIG_DAC_KNOWN_CONFIGURATIONS "" +#endif +#ifndef CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS +#define CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS "" +#endif + void register_default_nvs(){ @@ -341,24 +349,35 @@ void register_default_nvs(){ register_default_string_val( "stats", "n"); register_default_string_val( "rel_api", CONFIG_RELEASE_API); register_default_string_val("wifi_smode", "A"); + register_default_string_val("kndac", CONFIG_DAC_KNOWN_CONFIGURATIONS); + register_default_string_val("kngpio", CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS); wait_for_commit(); ESP_LOGD(TAG,"Done setting default values in nvs."); } uint32_t halSTORAGE_RebootCounterRead(void) { return RebootCounter ; } -uint32_t halSTORAGE_RebootCounterUpdate(int32_t xValue) { return (RebootCounter = (xValue != 0) ? (RebootCounter + xValue) : 0) ; } +uint32_t halSTORAGE_RebootCounterUpdate(int32_t xValue) { + if(RebootCounter >100) { + RebootCounter = 0; + } + return (RebootCounter = (xValue != 0) ? (RebootCounter + xValue) : 0) ; + } -void handle_ap_connect(){ +void handle_ap_connect(nm_state_t new_state, int sub_state){ start_telnet(NULL); +} +void handle_network_up(nm_state_t new_state, int sub_state){ halSTORAGE_RebootCounterUpdate(0); } +esp_reset_reason_t xReason=ESP_RST_UNKNOWN; + void app_main() { const esp_partition_t *running = esp_ota_get_running_partition(); is_recovery_running = (running->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY); - esp_reset_reason_t xReason = esp_reset_reason(); + xReason = esp_reset_reason(); ESP_LOGI(TAG,"Reset reason is: %u", xReason); - if(!is_recovery_running && xReason != ESP_RST_SW && xReason != ESP_RST_POWERON ) { + if(!is_recovery_running ) { /* unscheduled restart (HW, Watchdog or similar) thus increment dynamic * counter then log current boot statistics as a warning */ uint32_t Counter = halSTORAGE_RebootCounterUpdate(1) ; // increment counter @@ -369,30 +388,44 @@ void app_main() guided_factory(); } } + else { + uint32_t Counter = halSTORAGE_RebootCounterUpdate(1) ; // increment counter + ESP_LOGI(TAG,"Recovery Reboot counter=%u\n", Counter) ; + if (Counter == 5) { + ESP_LOGW(TAG,"System rebooted too many times. This could be an indication that configuration is corrupted. Erasing config."); + halSTORAGE_RebootCounterUpdate(0); + erase_settings_partition(); + // reboot one more time + guided_factory(); + + } + } char * fwurl = NULL; + MEMTRACE_PRINT_DELTA(); ESP_LOGI(TAG,"Starting app_main"); initialize_nvs(); + MEMTRACE_PRINT_DELTA(); ESP_LOGI(TAG,"Setting up telnet."); init_telnet(); // align on 32 bits boundaries - + MEMTRACE_PRINT_DELTA(); ESP_LOGI(TAG,"Setting up config subsystem."); config_init(); - + MEMTRACE_PRINT_DELTA(); ESP_LOGD(TAG,"Creating event group for wifi"); - wifi_event_group = xEventGroupCreate(); + network_event_group = xEventGroupCreate(); ESP_LOGD(TAG,"Clearing CONNECTED_BIT from wifi group"); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + xEventGroupClearBits(network_event_group, CONNECTED_BIT); ESP_LOGI(TAG,"Registering default values"); register_default_nvs(); - + MEMTRACE_PRINT_DELTA(); ESP_LOGI(TAG,"Configuring services"); services_init(); - + MEMTRACE_PRINT_DELTA(); ESP_LOGI(TAG,"Initializing display"); display_init("SqueezeESP32"); - + MEMTRACE_PRINT_DELTA(); if(is_recovery_running && display){ GDS_ClearExt(display, true); GDS_SetFont(display, &Font_droid_sans_fallback_15x17 ); @@ -402,7 +435,7 @@ void app_main() ESP_LOGI(TAG,"Checking if certificates need to be updated"); update_certificates(false); - + MEMTRACE_PRINT_DELTA(); ESP_LOGD(TAG,"Getting firmware OTA URL (if any)"); fwurl = process_ota_url(); @@ -412,10 +445,10 @@ void app_main() if(bypass_wm==NULL) { ESP_LOGE(TAG, "Unable to retrieve the Wifi Manager bypass flag"); - bypass_wifi_manager = false; + bypass_network_manager = false; } else { - bypass_wifi_manager=(strcmp(bypass_wm,"1")==0 ||strcasecmp(bypass_wm,"y")==0); + bypass_network_manager=(strcmp(bypass_wm,"1")==0 ||strcasecmp(bypass_wm,"y")==0); } if(!is_recovery_running){ @@ -432,25 +465,32 @@ void app_main() /* start the wifi manager */ ESP_LOGD(TAG,"Blinking led"); led_blink_pushed(LED_GREEN, 250, 250); - - if(bypass_wifi_manager){ - ESP_LOGW(TAG,"wifi manager is disabled. Use command line for wifi control."); + MEMTRACE_PRINT_DELTA(); + if(bypass_network_manager){ + ESP_LOGW(TAG,"Network manager is disabled. Use command line for wifi control."); } else { - ESP_LOGI(TAG,"Starting Wifi Manager"); - network_manager_start(); - //wifi_manager_set_callback(EVENT_STA_GOT_IP, &cb_connection_got_ip); - wifi_manager_set_callback(EVENT_ETH_GOT_IP, &cb_connection_got_ip); - wifi_manager_set_callback(EVENT_STA_DISCONNECTED, &cb_connection_sta_disconnected); + ESP_LOGI(TAG,"Starting Network Manager"); + network_start(); + MEMTRACE_PRINT_DELTA(); + network_register_state_callback(NETWORK_WIFI_ACTIVE_STATE,WIFI_CONNECTED_STATE, "cb_connection_got_ip", &cb_connection_got_ip); + network_register_state_callback(NETWORK_ETH_ACTIVE_STATE,ETH_ACTIVE_CONNECTED_STATE, "cb_connection_got_ip",&cb_connection_got_ip); + network_register_state_callback(NETWORK_WIFI_ACTIVE_STATE,WIFI_LOST_CONNECTION_STATE, "cb_connection_sta_disconnected",&cb_connection_sta_disconnected); + /* Start the telnet service after we are certain that the network stack has been properly initialized. * This can be either after we're started the AP mode, or after we've started the STA mode */ - wifi_manager_set_callback(ORDER_START_AP, &handle_ap_connect); - wifi_manager_set_callback(ORDER_CONNECT_STA, &handle_ap_connect); + network_register_state_callback(NETWORK_INITIALIZING_STATE,-1, "handle_ap_connect", &handle_ap_connect); + network_register_state_callback(NETWORK_ETH_ACTIVE_STATE,ETH_ACTIVE_LINKDOWN_STATE, "handle_network_up", &handle_network_up); + network_register_state_callback(NETWORK_WIFI_ACTIVE_STATE,WIFI_INITIALIZING_STATE, "handle_network_up", &handle_network_up); + MEMTRACE_PRINT_DELTA(); + } + MEMTRACE_PRINT_DELTA_MESSAGE("Starting Console"); console_start(); + MEMTRACE_PRINT_DELTA_MESSAGE("Console started"); if(fwurl && strlen(fwurl)>0){ if(is_recovery_running){ - while(!bWifiConnected){ + while(!bNetworkConnected){ wait_for_wifi(); taskYIELD(); } diff --git a/sdkconfig b/sdkconfig index 953ab3d5..2c21214f 100644 --- a/sdkconfig +++ b/sdkconfig @@ -202,6 +202,13 @@ CONFIG_DAC_CONTROLSET="" # Audio settings # +# +# Known Configurations +# +CONFIG_DAC_KNOWN_CONFIGURATIONS="ESP-A1S-AC101(audio kit 2.2)-Fixed GPIOs|ESP-A1S-ES8388(audio kit 2.2+)-Fixed GPIOs" +CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS="model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32|model=ES8388,bck=5,ws=25,do=26,sda=18,scl=23,i2c=16" +# end of Known Configurations + # # DAC settings # @@ -313,7 +320,7 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set # CONFIG_COMPILER_CXX_EXCEPTIONS is not set -CONFIG_COMPILER_CXX_RTTI=y +# CONFIG_COMPILER_CXX_RTTI is not set CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y # CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set # CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set @@ -930,9 +937,9 @@ CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y # CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 -CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y +# CONFIG_FREERTOS_ASSERT_FAIL_ABORT is not set # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set -# CONFIG_FREERTOS_ASSERT_DISABLE is not set +CONFIG_FREERTOS_ASSERT_DISABLE=y CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 CONFIG_FREERTOS_ISR_STACKSIZE=2096 # CONFIG_FREERTOS_LEGACY_HOOKS is not set @@ -980,12 +987,12 @@ CONFIG_HEAP_TRACING_OFF=y # Log output # # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -CONFIG_LOG_DEFAULT_LEVEL=1 +CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_COLORS=y CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set