Browse Source

enhancement: new "config" module to access configuration in RAM

This is to allow moving some tasks/stack memory to SPIRAM, saving on
precious IRAM. HTTP server should also be more responsive.
Sebastien 5 years ago
parent
commit
cdbb198d8a
38 changed files with 1215 additions and 506 deletions
  1. 1 1
      build-scripts/16M-sdkconfig.defaults
  2. 1 1
      build-scripts/I2S-16MFlash-sdkconfig.defaults
  3. 1 1
      build-scripts/I2S-4MFlash-sdkconfig.defaults
  4. 1 1
      build-scripts/NonOTA-16M-sdkconfig.defaults
  5. 1 1
      build-scripts/NonOTA-I2S-16MFlash-sdkconfig.defaults
  6. 1 1
      build-scripts/NonOTA-I2S-4MFlash-sdkconfig.defaults
  7. 1 1
      build-scripts/NonOTA-SqueezeAmp-sdkconfig.defaults
  8. 1 1
      build-scripts/SqueezeAmp4MBFlash-sdkconfig.defaults
  9. 1 1
      build-scripts/SqueezeAmp8MBFlash-sdkconfig.defaults
  10. 1 1
      build-scripts/squeezelite-esp32-16M-sdkconfig.defaults
  11. 1 1
      build-scripts/squeezelite-esp32-I2S-16MFlash-sdkconfig.defaults
  12. 1 1
      build-scripts/squeezelite-esp32-I2S-4MFlash-NOAirplay-sdkconfig.defaults
  13. 1 1
      build-scripts/squeezelite-esp32-I2S-4MFlash-sdkconfig.defaults
  14. 1 1
      build-scripts/squeezelite-esp32-SqueezeAmp-sdkconfig.defaults
  15. 1 1
      components/cmd_nvs/cmd_nvs.c
  16. 2 1
      components/cmd_nvs/cmd_nvs.h
  17. 11 0
      components/cmd_system/cmd_system.c
  18. 1 0
      components/cmd_system/component.mk
  19. 2 2
      components/driver_bt/bt_app_sink.c
  20. 2 2
      components/driver_bt/bt_app_source.c
  21. 2 1
      components/driver_i2s/component.mk
  22. 6 2
      components/io/led.c
  23. 1 1
      components/raop/raop_sink.c
  24. 2 2
      components/squeezelite-ota/component.mk
  25. 11 3
      components/squeezelite-ota/squeezelite-ota.c
  26. 11 8
      components/wifi-manager/code.js
  27. 2 1
      components/wifi-manager/component.mk
  28. 113 137
      components/wifi-manager/http_server.c
  29. 200 195
      components/wifi-manager/wifi_manager.c
  30. 3 3
      components/wifi-manager/wifi_manager.h
  31. 1 1
      main/CMakeLists.txt
  32. 7 7
      main/cmd_squeezelite.c
  33. 623 0
      main/config.c
  34. 24 0
      main/config.h
  35. 6 8
      main/console.c
  36. 92 83
      main/esp_app_main.c
  37. 74 32
      main/nvs_utilities.c
  38. 4 2
      main/nvs_utilities.h

+ 1 - 1
build-scripts/16M-sdkconfig.defaults

@@ -143,4 +143,4 @@ CONFIG_DEFAULT_AP_PASSWORD="squeezelite"
 CONFIG_DEFAULT_AP_IP="192.168.4.1"
 CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"

+ 1 - 1
build-scripts/I2S-16MFlash-sdkconfig.defaults

@@ -116,7 +116,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/I2S-4MFlash-sdkconfig.defaults

@@ -116,7 +116,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/NonOTA-16M-sdkconfig.defaults

@@ -139,4 +139,4 @@ CONFIG_DEFAULT_AP_PASSWORD="squeezelite"
 CONFIG_DEFAULT_AP_IP="192.168.4.1"
 CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"

+ 1 - 1
build-scripts/NonOTA-I2S-16MFlash-sdkconfig.defaults

@@ -116,7 +116,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/NonOTA-I2S-4MFlash-sdkconfig.defaults

@@ -116,7 +116,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/NonOTA-SqueezeAmp-sdkconfig.defaults

@@ -104,7 +104,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/SqueezeAmp4MBFlash-sdkconfig.defaults

@@ -104,7 +104,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/SqueezeAmp8MBFlash-sdkconfig.defaults

@@ -71,7 +71,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
 CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y

+ 1 - 1
build-scripts/squeezelite-esp32-16M-sdkconfig.defaults

@@ -137,4 +137,4 @@ CONFIG_DEFAULT_AP_PASSWORD="squeezelite"
 CONFIG_DEFAULT_AP_IP="192.168.4.1"
 CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"

+ 1 - 1
build-scripts/squeezelite-esp32-I2S-16MFlash-sdkconfig.defaults

@@ -115,7 +115,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/squeezelite-esp32-I2S-4MFlash-NOAirplay-sdkconfig.defaults

@@ -112,7 +112,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/squeezelite-esp32-I2S-4MFlash-sdkconfig.defaults

@@ -132,7 +132,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
build-scripts/squeezelite-esp32-SqueezeAmp-sdkconfig.defaults

@@ -103,7 +103,7 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
 CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
 CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
-CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
+CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -s %s"
 
 CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
 CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y

+ 1 - 1
components/cmd_nvs/cmd_nvs.c

@@ -91,7 +91,7 @@ static nvs_type_t str_to_type(const char *type)
 
     return NVS_TYPE_ANY;
 }
-static const char *type_to_str(nvs_type_t type)
+const char *type_to_str(nvs_type_t type)
 {
     for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) {
         const type_str_pair_t *p = &type_str_pair[i];

+ 2 - 1
components/cmd_nvs/cmd_nvs.h

@@ -7,6 +7,7 @@
    CONDITIONS OF ANY KIND, either express or implied.
 */
 #pragma once
+#include "nvs_flash.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -14,7 +15,7 @@ extern "C" {
 
 // Register NVS functions
 void register_nvs();
-
+const char *type_to_str(nvs_type_t type);
 #ifdef __cplusplus
 }
 #endif

+ 11 - 0
components/cmd_system/cmd_system.c

@@ -27,6 +27,7 @@
 #include "esp_partition.h"
 #include "esp_ota_ops.h"
 #include "platform_esp32.h"
+#include "nvs_utilities.h"
 
 #ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
 #define WITH_TASKS_INFO 1
@@ -126,6 +127,9 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
 		}
 		esp_partition_iterator_release(it);
 		if(bFound) {
+			if(!wait_for_commit()){
+				ESP_LOGW(TAG,"Unable to commit configuration. ");
+			}
 			ESP_LOGI(TAG, "Restarting!.");
 			esp_restart();
 		}
@@ -136,6 +140,9 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
 
 static int restart(int argc, char **argv)
 {
+	if(!wait_for_commit()){
+		ESP_LOGW(TAG,"Unable to commit configuration. ");
+	}
     ESP_LOGW(TAG, "Restarting");
     esp_restart();
     return 0;
@@ -143,6 +150,10 @@ static int restart(int argc, char **argv)
 
 void simple_restart()
 {
+	if(!wait_for_commit()){
+		ESP_LOGW(TAG,"Unable to commit configuration. ");
+	}
+
 	ESP_LOGW(TAG, "Restarting");
     esp_restart();
 }

+ 1 - 0
components/cmd_system/component.mk

@@ -9,3 +9,4 @@
 
 COMPONENT_ADD_INCLUDEDIRS := .
 COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/
+COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools/

+ 2 - 2
components/driver_bt/bt_app_sink.c

@@ -402,7 +402,7 @@ void bt_sink_init(bt_cmd_cb_t cmd_cb, bt_data_cb_t data_cb)
      */
     esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
 
-    char * pin_code = get_nvs_value_alloc_default(NVS_TYPE_STR, "bt_sink_pin", STR(CONFIG_BT_SINK_PIN), 0);
+    char * pin_code = config_alloc_get_default(NVS_TYPE_STR, "bt_sink_pin", STR(CONFIG_BT_SINK_PIN), 0);
     if(strlen(pin_code)>ESP_BT_PIN_CODE_LEN){
 
     	ESP_LOGW(BT_AV_TAG, "BT Sink pin code [%s] too long. ", pin_code);
@@ -487,7 +487,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
     switch (event) {
     case BT_APP_EVT_STACK_UP: {
         /* set up device name */
-		bt_name = (char * )get_nvs_value_alloc_default(NVS_TYPE_STR, "bt_name", CONFIG_BT_NAME, 0);
+		bt_name = (char * )config_alloc_get_default(NVS_TYPE_STR, "bt_name", CONFIG_BT_NAME, 0);
 		esp_bt_dev_set_device_name(bt_name);
 		free(bt_name);
         esp_bt_gap_register_callback(bt_app_gap_cb);

+ 2 - 2
components/driver_bt/bt_app_source.c

@@ -166,7 +166,7 @@ void hal_bluetooth_init(const char * options)
 	}
 	if(squeezelite_args.sink_name->count == 0)
 	{
-		squeezelite_conf.sink_name = get_nvs_value_alloc_default(NVS_TYPE_STR, "a2dp_sink_name", CONFIG_A2DP_SINK_NAME, 0);
+		squeezelite_conf.sink_name = config_alloc_get_default(NVS_TYPE_STR, "a2dp_sink_name", CONFIG_A2DP_SINK_NAME, 0);
     	if(squeezelite_conf.sink_name  == NULL){
     		ESP_LOGW(TAG,"Unable to retrieve the a2dp sink name from nvs");
     		squeezelite_conf.sink_name = strdup(CONFIG_A2DP_SINK_NAME);
@@ -511,7 +511,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
         /* set up device name */
 
 
-        char * a2dp_dev_name = 	get_nvs_value_alloc_default(NVS_TYPE_STR, "a2dp_dev_name", CONFIG_A2DP_DEV_NAME, 0);
+        char * a2dp_dev_name = 	config_alloc_get_default(NVS_TYPE_STR, "a2dp_dev_name", CONFIG_A2DP_DEV_NAME, 0);
     	if(a2dp_dev_name  == NULL){
     		ESP_LOGW(TAG,"Unable to retrieve the a2dp device name from nvs");
     		esp_bt_dev_set_device_name(CONFIG_A2DP_DEV_NAME);

+ 2 - 1
components/driver_i2s/component.mk

@@ -9,4 +9,5 @@
 
 COMPONENT_ADD_INCLUDEDIRS := .
 CFLAGS += -Os -DPOSIX -DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 	
-CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
+#CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
+CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO

+ 6 - 2
components/io/led.c

@@ -50,7 +50,7 @@ static void vCallbackFunction( TimerHandle_t xTimer ) {
 }
 
 bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
-	if (!leds[idx].gpio) return false;
+	if (!leds[idx].gpio || leds[idx].gpio<0 ) return false;
 	
 	ESP_LOGD(TAG,"led_blink_core");
 	if (leds[idx].timer) {
@@ -96,7 +96,7 @@ bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
 } 
 
 bool led_unpush(int idx) {
-	if (!leds[idx].gpio) return false;
+	if (!leds[idx].gpio || leds[idx].gpio<0) return false;
 	
 	led_blink_core(idx, leds[idx].pushedon, leds[idx].pushedoff, true);
 	leds[idx].pushed = false;
@@ -105,6 +105,10 @@ bool led_unpush(int idx) {
 }	
 
 bool led_config(int idx, gpio_num_t gpio, int onstate) {
+	if(gpio<0){
+		ESP_LOGW(TAG,"LED GPIO not configured");
+		return false;
+	}
 	ESP_LOGD(TAG,"Index %d, GPIO %d, on state %s", idx, gpio, onstate>0?"On":"Off");
 	if (idx >= MAX_LED) return false;
 	leds[idx].gpio = gpio;

+ 1 - 1
components/raop/raop_sink.c

@@ -56,7 +56,7 @@ void raop_sink_init(raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb) {
     ESP_ERROR_CHECK( mdns_init() );
     ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
         
-    char * sink_name_buffer= (char *)get_nvs_value_alloc(NVS_TYPE_STR, "airplay_name");
+    char * sink_name_buffer= (char *)config_alloc_get(NVS_TYPE_STR, "airplay_name");
     if(sink_name_buffer != NULL){
     	memset(sink_name, 0x00, sizeof(sink_name));
     	strncpy(sink_name,sink_name_buffer,sizeof(sink_name)-1 );

+ 2 - 2
components/squeezelite-ota/component.mk

@@ -8,6 +8,6 @@ COMPONENT_ADD_INCLUDEDIRS := .
 COMPONENT_ADD_INCLUDEDIRS += include
 COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/
 COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools
-
-CFLAGS += -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCONFIG_OTA_ALLOW_HTTP=1
+CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO -DCONFIG_OTA_ALLOW_HTTP=1
+#CFLAGS += -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCONFIG_OTA_ALLOW_HTTP=1
 COMPONENT_EMBED_TXTFILES :=  ${PROJECT_PATH}/server_certs/github.pem

+ 11 - 3
components/squeezelite-ota/squeezelite-ota.c

@@ -329,7 +329,7 @@ esp_err_t process_recovery_ota(const char * bin_url){
 #define OTA_CORE 0
 #warning "OTA will run on core 0"
 #else
-#warning "OTA will run on core 1"
+#pragma message "OTA will run on core 1"
 #define OTA_CORE 1
 #endif
     ESP_LOGI(TAG, "Starting ota on core %u for : %s", OTA_CORE,urlPtr);
@@ -343,12 +343,20 @@ esp_err_t process_recovery_ota(const char * bin_url){
 
 esp_err_t start_ota(const char * bin_url, bool bFromAppMain)
 {
-//	uint8_t * get_nvs_value_alloc_default(NVS_TYPE_BLOB, "certs", server_cert_pem_start , server_cert_pem_end-server_cert_pem_start);
+//	uint8_t * config_alloc_get_default(NVS_TYPE_BLOB, "certs", server_cert_pem_start , server_cert_pem_end-server_cert_pem_start);
 #if RECOVERY_APPLICATION
 	return process_recovery_ota(bin_url);
 #else
 		ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
-		store_nvs_value(NVS_TYPE_STR, "fwurl", bin_url);
+		if(config_set_value(NVS_TYPE_STR, "fwurl", bin_url) != ESP_OK){
+			ESP_LOGE(TAG,"Failed to save the OTA url into nvs cache");
+			return ESP_FAIL;
+		}
+
+		if(!wait_for_commit()){
+			ESP_LOGW(TAG,"Unable to commit configuration. ");
+		}
+
 		ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
 	return guided_factory();
 	return ESP_OK;

+ 11 - 8
components/wifi-manager/code.js

@@ -289,14 +289,17 @@ $(document).ready(function(){
             var val = $(this).val();
             if (key != '') {
                 headers["X-Custom-"+key] = val;
-                data[key] = val;
+                data[key] = {};
+                data[key].value = val;
+                data[key].type = 33;
             }
         });
         var key = $("#nvs-new-key").val();
         var val = $("#nvs-new-value").val();
         if (key != '') {
             headers["X-Custom-"+key] = val;
-            data[key] = val;
+            data[key] = {};
+            data[key].value = val;
         }
         $.ajax({
             url: '/config.json',
@@ -454,7 +457,7 @@ $(document).ready(function(){
 
     //start timers
     startCheckStatusInterval();
-    startRefreshAPInterval();
+    //startRefreshAPInterval();
 
     $('[data-toggle="tooltip"]').tooltip({
         html: true,
@@ -718,16 +721,16 @@ function getConfig() {
         Object.keys(data).sort().forEach(function(key, i) {
             if (data.hasOwnProperty(key)) {
                 if (key == 'autoexec') {
-                    if (data["autoexec"] === "1") {
+                    if (data["autoexec"].value === "1") {
                         $("#autoexec-cb")[0].checked=true;
                     } else {
                         $("#autoexec-cb")[0].checked=false;
                     }
                 } else if (key == 'autoexec1') {
-                    $("textarea#autoexec1").val(data[key]);
+                    $("textarea#autoexec1").val(data[key].value);
                 } else if (key == 'host_name') {
-                    $("dhcp-name1").val(data[key]);
-                    $("dhcp-name2").val(data[key]);
+                    $("dhcp-name1").val(data[key].value);
+                    $("dhcp-name2").val(data[key].value);
                 }
 
                 $("tbody#nvsTable").append(
@@ -738,7 +741,7 @@ function getConfig() {
                         "</td>"+
                     "</tr>"
                 );
-                $("input#"+key).val(data[key]);
+                $("input#"+key).val(data[key].value);
             }
         });
         $("tbody#nvsTable").append(

+ 2 - 1
components/wifi-manager/component.mk

@@ -7,7 +7,8 @@
 # please read the SDK documents if you need to do this.
 #
 COMPONENT_EMBED_FILES := style.css code.js index.html bootstrap.min.css.gz jquery.min.js.gz popper.min.js.gz bootstrap.min.js.gz
-CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG \
+#CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG 
+CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO \
 	-I$(COMPONENT_PATH)/../tools				
 COMPONENT_ADD_INCLUDEDIRS := .
 COMPONENT_ADD_INCLUDEDIRS += $(COMPONENT_PATH)/../tools

+ 113 - 137
components/wifi-manager/http_server.c

@@ -42,9 +42,8 @@ function to process requests, decode URLs, serve files, etc. etc.
 #include "esp_system.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
+#include "config.h"
 
-#define NVS_PARTITION_NAME "nvs"
-#define NUM_BUFFER_LEN 101
 
 /* @brief tag used for ESP serial console messages */
 static const char TAG[] = "http_server";
@@ -87,7 +86,7 @@ const static char http_redirect_hdr_end[] = "/\n\n";
 
 
 void http_server_start() {
-	ESP_LOGD(TAG,"http_server_start ");
+	ESP_LOGD(TAG,  "http_server_start ");
 	if(task_http_server == NULL) {
 		xTaskCreate(&http_server, "http_server", 1024*5, NULL, WIFI_MANAGER_TASK_PRIORITY, &task_http_server);
 	}
@@ -99,7 +98,7 @@ void http_server(void *pvParameters) {
 	conn = netconn_new(NETCONN_TCP);
 	netconn_bind(conn, IP_ADDR_ANY, 80);
 	netconn_listen(conn);
-	ESP_LOGI(TAG, "HTTP Server listening on 80/tcp");
+	ESP_LOGI(TAG,   "HTTP Server listening on 80/tcp");
 	do {
 		err = netconn_accept(conn, &newconn);
 		if(err == ERR_OK) {
@@ -108,7 +107,7 @@ void http_server(void *pvParameters) {
 		}
 		else
 		{
-			ESP_LOGE(TAG,"Error accepting new connection. Terminating HTTP server");
+			ESP_LOGE(TAG,  "Error accepting new connection. Terminating HTTP server");
 		}
 		taskYIELD();  /* allows the freeRTOS scheduler to take over if needed. */
 	} while(err == ERR_OK);
@@ -144,7 +143,7 @@ char* http_server_search_header(char *request, char *header_name, int *len, char
 	char *ptr = NULL;
 	int currentLength=0;
 
-	ESP_LOGV(TAG, "searching for header name: [%s]", header_name);
+	ESP_LOGV(TAG,   "searching for header name: [%s]", header_name);
 	ptr = strstr(request, header_name);
 
 
@@ -152,23 +151,23 @@ char* http_server_search_header(char *request, char *header_name, int *len, char
 		ret = ptr + strlen(header_name);
 		ptr = ret;
 		currentLength=(int)(ptr-request);
-		ESP_LOGV(TAG, "found string at %d", currentLength);
+		ESP_LOGV(TAG,   "found string at %d", currentLength);
 
 		while (*ptr != '\0' && *ptr != '\n' && *ptr != '\r' && *ptr != ':' && ptr<bufEnd) {
 			ptr++;
 		}
 		if(*ptr==':') {
 			currentLength=(int)(ptr-ret);
-			ESP_LOGV(TAG, "Found parameter name end, length : %d", currentLength);
+			ESP_LOGV(TAG,   "Found parameter name end, length : %d", currentLength);
 			// save the parameter name: the string between header name and ":"
 			*parm_name=malloc(currentLength+1);
 			if(*parm_name==NULL) {
-				ESP_LOGE(TAG, "Unable to allocate memory for new header name");
+				ESP_LOGE(TAG,   "Unable to allocate memory for new header name");
 				return NULL;
 			}
 			memset(*parm_name, 0x00,currentLength+1);
 			strncpy(*parm_name,ret,currentLength);
-			ESP_LOGV(TAG, "Found parameter name : %s ", *parm_name);
+			ESP_LOGV(TAG,   "Found parameter name : %s ", *parm_name);
 			ptr++;
 			while (*ptr == ' ' && ptr<bufEnd) {
 				ptr++;
@@ -183,12 +182,12 @@ char* http_server_search_header(char *request, char *header_name, int *len, char
 		// Terminate value inside its actual buffer so we can treat it as individual string
 		*ptr='\0';
 		currentLength=(int)(ptr-ret);
-		ESP_LOGV(TAG, "Found parameter value end, length : %d, 	value: %s", currentLength,ret );
+		ESP_LOGV(TAG,   "Found parameter value end, length : %d, 	value: %s", currentLength,ret );
 
 		*next_position=++ptr;
 		return ret;
 	}
-	ESP_LOGD(TAG, "No more match for : %s", header_name);
+	ESP_LOGD(TAG,   "No more match for : %s", header_name);
 	return NULL;
 }
 void http_server_send_resource_file(struct netconn *conn,const uint8_t * start, const uint8_t * end, char * content_type,char * encoding) {
@@ -196,7 +195,7 @@ void http_server_send_resource_file(struct netconn *conn,const uint8_t * start,
 	size_t  buff_length= sizeof(http_hdr_template)+strlen(content_type)+strlen(encoding);
 	char * http_hdr=malloc(buff_length);
 	if( http_hdr == NULL) {
-		ESP_LOGE(TAG,"Cound not allocate %d bytes for headers.",buff_length);
+		ESP_LOGE(TAG,  "Cound not allocate %d bytes for headers.",buff_length);
 		netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
 	}
 	else
@@ -204,73 +203,25 @@ void http_server_send_resource_file(struct netconn *conn,const uint8_t * start,
 		memset(http_hdr,0x00,buff_length);
 		snprintf(http_hdr, buff_length-1,http_hdr_template,content_type,len,encoding);
 		netconn_write(conn, http_hdr, strlen(http_hdr), NETCONN_NOCOPY);
-		ESP_LOGD(TAG,"sending response : %s",http_hdr);
+		ESP_LOGD(TAG,  "sending response : %s",http_hdr);
 		netconn_write(conn, start, end - start, NETCONN_NOCOPY);
 		free(http_hdr);
 	}
 }
 
-err_t http_server_nvs_dump(struct netconn *conn, nvs_type_t nvs_type) {
-	nvs_entry_info_t info;
-	char * num_buffer = NULL;
-	cJSON * nvs_json = cJSON_CreateObject();
-	num_buffer = malloc(NUM_BUFFER_LEN);
-	nvs_iterator_t it = nvs_entry_find(settings_partition, NULL, nvs_type);
-	if(it == NULL) {
-		ESP_LOGW(TAG, "No nvs entry found in %s",NVS_PARTITION_NAME );
+err_t http_server_send_config_json(struct netconn *conn) {
+	char * json = config_alloc_get_json(false);
+	if(json!=NULL){
+		ESP_LOGD(TAG,  "config json : %s",json );
+		netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
+		netconn_write(conn, json, strlen(json), NETCONN_NOCOPY);
+		free(json);
 	}
-	while (it != NULL) {
-		nvs_entry_info(it, &info);
-		memset(num_buffer,0x00,NUM_BUFFER_LEN);
-		if(strstr(info.namespace_name, current_namespace)) {
-			void * value = get_nvs_value_alloc(nvs_type,info.key);
-			if(value==NULL)
-			{
-				ESP_LOGE(TAG,"nvs read failed.");
-				netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY); //200ok
-				free(num_buffer);
-				cJSON_Delete(nvs_json);
-				return ESP_FAIL;
-			}
-			switch (nvs_type) {
-				case NVS_TYPE_I8:
-					snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int8_t*)value);
-					break;
-				case NVS_TYPE_I16:
-					snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int16_t*)value);
-					break;
-				case NVS_TYPE_I32:
-					snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int32_t*)value);
-					break;
-				case NVS_TYPE_U8:
-					snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint8_t*)value);
-					break;
-				case NVS_TYPE_U16:
-					snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint16_t*)value);
-					break;
-				case NVS_TYPE_U32:
-					snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint32_t*)value);
-					break;
-				case NVS_TYPE_STR:
-					// string will be processed directly below
-					break;
-				case NVS_TYPE_I64:
-				case NVS_TYPE_U64:
-				default:
-					ESP_LOGE(TAG, "nvs type %u not supported", nvs_type);
-					break;
-			}
-			cJSON_AddItemToObject(nvs_json, info.key, cJSON_CreateString((nvs_type==NVS_TYPE_STR)?(char *)value:num_buffer));
-			free(value );
-		}
-		it = nvs_entry_next(it);
+	else{
+		ESP_LOGD(TAG,  "Error retrieving config json string. ");
+		netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
 	}
-	ESP_LOGD(TAG,"config json : %s\n", cJSON_Print(nvs_json));
 
-	netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
-	netconn_write(conn, cJSON_Print(nvs_json), strlen(cJSON_Print(nvs_json)), NETCONN_NOCOPY);
-	cJSON_Delete(nvs_json);
-	free(num_buffer);
 	return ESP_OK;
 }
 
@@ -285,8 +236,8 @@ void http_server_process_config(struct netconn *conn, 	char *inbuf) {
 //		/* extract the first line of the request */
 //		char *save_ptr = buf;
 //		char *line = strtok_r(save_ptr, new_line, &save_ptr);
-//		ESP_LOGD(TAG,"Processing line %s",line);
-	ESP_LOGD(TAG,"Processing request buffer: \n%s",inbuf);
+//		ESP_LOGD(TAG,  "Processing line %s",line);
+	ESP_LOGD(TAG,  "Processing request buffer: \n%s",inbuf);
 	char *last = NULL;
 	char *ptr = NULL;
 	last = ptr = inbuf;
@@ -299,7 +250,7 @@ void http_server_process_config(struct netconn *conn, 	char *inbuf) {
 			}
 			// terminate the header string
 			if( *(ptr) == '\0' ) {
-				ESP_LOGD(TAG, "End of buffer found");
+				ESP_LOGD(TAG,   "End of buffer found");
 				return;
 			}
 			*ptr = '\0';
@@ -308,21 +259,21 @@ void http_server_process_config(struct netconn *conn, 	char *inbuf) {
 				ptr+=2;
 			}
 			if(ptr==last) {
-				ESP_LOGD(TAG,"Processing body. ");
+				ESP_LOGD(TAG,  "Processing body. ");
 				break;
 			}
 			if(strlen(last)>0) {
-				ESP_LOGD(TAG,"Found Header Line %s ", last);
+				ESP_LOGD(TAG,  "Found Header Line %s ", last);
 				//Content-Type: application/json
 			}
 			else {
-				ESP_LOGD(TAG,"Found end of headers");
+				ESP_LOGD(TAG,  "Found end of headers");
 				bHeaders = false;
 			}
 			last=ptr;
 		}
 		else {
-			//ESP_LOGD(TAG,"Body content: %s", last);
+			//ESP_LOGD(TAG,  "Body content: %s", last);
 			//cJSON * json = cJSON_Parse(last);
 			//cJSON_Delete(json);
 			//todo:  implement body json parsing
@@ -336,13 +287,13 @@ void http_server_process_config(struct netconn *conn, 	char *inbuf) {
 
 void dump_net_buffer(void * buf, u16_t buflen) {
 	char * curbuf = malloc(buflen+1);
-	ESP_LOGD(TAG,"netconn buffer, length=%u",buflen);
+	ESP_LOGV(TAG,  "netconn buffer, length=%u",buflen);
 	if(curbuf==NULL) {
-		ESP_LOGE(TAG,"Unable to show netconn buffer.  Malloc failed");
+		ESP_LOGE(TAG,  "Unable to show netconn buffer.  Malloc failed");
 	}
 	memset(curbuf,0x0, buflen+1);
 	memcpy(curbuf,buf,buflen);
-	ESP_LOGV(TAG,"netconn buffer content:\n%s",curbuf);
+	ESP_LOGV(TAG,  "netconn buffer content:\n%s",curbuf);
 	free(curbuf);
 }
 
@@ -354,22 +305,22 @@ void http_server_netconn_serve(struct netconn *conn) {
 	err_t err;
 	ip_addr_t remote_add;
 	u16_t port;
-	ESP_LOGD(TAG,"Serving page.  Getting device AP address.");
+	ESP_LOGV(TAG,  "Serving page.  Getting device AP address.");
 	const char new_line[2] = "\n";
-	char * ap_ip_address= get_nvs_value_alloc_default(NVS_TYPE_STR, "ap_ip_address", DEFAULT_AP_IP, 0);
+	char * ap_ip_address= config_alloc_get_default(NVS_TYPE_STR, "ap_ip_address", DEFAULT_AP_IP, 0);
 	if(ap_ip_address==NULL){
-		ESP_LOGE(TAG,"Unable to retrieve default AP IP Address");
+		ESP_LOGE(TAG,  "Unable to retrieve default AP IP Address");
 		netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
 		netconn_close(conn);
 		return;
 	}
-	ESP_LOGD(TAG,"Getting remote device IP address.");
+	ESP_LOGV(TAG,  "Getting remote device IP address.");
 	netconn_getaddr(conn,	&remote_add,	&port,	0);
 	char * remote_address = strdup(ip4addr_ntoa(ip_2_ip4(&remote_add)));
-	ESP_LOGD(TAG,"Local Access Point IP address is: %s. Remote device IP address is %s. Receiving request buffer", ap_ip_address, remote_address);
+	ESP_LOGD(TAG,  "Local Access Point IP address is: %s. Remote device IP address is %s. Receiving request buffer", ap_ip_address, remote_address);
 	err = netconn_recv(conn, &inbuf);
 	if(err == ERR_OK) {
-		ESP_LOGD(TAG,"Getting data buffer.");
+		ESP_LOGV(TAG,  "Getting data buffer.");
 		netbuf_data(inbuf, (void**)&buf, &buflen);
 		dump_net_buffer(buf, buflen);
 		int lenH = 0;
@@ -382,14 +333,14 @@ void http_server_netconn_serve(struct netconn *conn) {
 		if(lenH>0){
 			strlcpy(host,temphost,lenH+1);
 		}
-		ESP_LOGD(TAG,"http_server_netconn_serve Host: [%s], host: [%s], Processing line [%s]",remote_address,host,line);
+		ESP_LOGD(TAG,  "http_server_netconn_serve Host: [%s], host: [%s], Processing line [%s]",remote_address,host,line);
 
 		if(line) {
 
 			/* captive portal functionality: redirect to access point IP for HOST that are not the access point IP OR the STA IP */
 			const char * host_name=NULL;
 			if((err=tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &host_name )) !=ESP_OK) {
-				ESP_LOGE(TAG,"Unable to get host name. Error: %s",esp_err_to_name(err));
+				ESP_LOGE(TAG,  "Unable to get host name. Error: %s",esp_err_to_name(err));
 			}
 
 			/* determine if Host is from the STA IP address */
@@ -399,7 +350,7 @@ void http_server_netconn_serve(struct netconn *conn) {
 			bool access_from_host_name = (host_name!=NULL) && strstr(host, host_name);
 
 			if(lenH > 0 && !strstr(host, ap_ip_address) && !(access_from_sta_ip || access_from_host_name)) {
-				ESP_LOGI(TAG,"Redirecting host [%s] to AP IP Address : %s",remote_address, ap_ip_address);
+				ESP_LOGI(TAG,  "Redirecting host [%s] to AP IP Address : %s",remote_address, ap_ip_address);
 				netconn_write(conn, http_redirect_hdr_start, sizeof(http_redirect_hdr_start) - 1, NETCONN_NOCOPY);
 				netconn_write(conn, ap_ip_address, strlen(ap_ip_address), NETCONN_NOCOPY);
 				netconn_write(conn, http_redirect_hdr_end, sizeof(http_redirect_hdr_end) - 1, NETCONN_NOCOPY);
@@ -433,32 +384,43 @@ void http_server_netconn_serve(struct netconn *conn) {
 				}
 
                 //dynamic stuff
+				else if(strstr(line, "GET /scan.json ")) {
+					ESP_LOGI(TAG,  "Starting wifi scan");
+					wifi_manager_scan_async();
+				}
 				else if(strstr(line, "GET /ap.json ")) {
 					/* if we can get the mutex, write the last version of the AP list */
-					ESP_LOGI(TAG,"Processing ap.json request");
+					ESP_LOGI(TAG,  "Processing ap.json request");
 					if(wifi_manager_lock_json_buffer(( TickType_t ) 10)) {
 						netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
-						char *buff = wifi_manager_get_ap_list_json();
-						netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
+						char *buff = wifi_manager_alloc_get_ap_list_json();
 						wifi_manager_unlock_json_buffer();
+						if(buff!=NULL){
+							netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
+							free(buff);
+						}
+						else {
+							ESP_LOGD(TAG,  "Error retrieving ap list json string. ");
+							netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
+						}
 					}
 					else {
 						netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
-						ESP_LOGE(TAG, "http_server_netconn_serve: GET /ap.json failed to obtain mutex");
+						ESP_LOGE(TAG,   "http_server_netconn_serve: GET /ap.json failed to obtain mutex");
 					}
 					/* request a wifi scan */
-					ESP_LOGI(TAG,"Starting wifi scan");
+					ESP_LOGI(TAG,  "Starting wifi scan");
 					wifi_manager_scan_async();
-					ESP_LOGI(TAG,"Done serving ap.json");
+					ESP_LOGI(TAG,  "Done serving ap.json");
 				}
 				else if(strstr(line, "GET /config.json ")) {
-					ESP_LOGI(TAG,"Serving config.json");
-					ESP_LOGI(TAG, "About to get config from flash");
-					http_server_nvs_dump(conn,NVS_TYPE_STR);
-					ESP_LOGD(TAG,"Done serving config.json");
+					ESP_LOGI(TAG,  "Serving config.json");
+					ESP_LOGI(TAG,   "About to get config from flash");
+					http_server_send_config_json(conn);
+					ESP_LOGD(TAG,  "Done serving config.json");
 				}
 				else if(strstr(line, "POST /config.json ")) {
-					ESP_LOGI(TAG,"Serving POST config.json");
+					ESP_LOGI(TAG,  "Serving POST config.json");
 					int lenA=0;
 					char * last_parm=save_ptr;
 					char * next_parm=save_ptr;
@@ -471,20 +433,21 @@ void http_server_netconn_serve(struct netconn *conn) {
 
 					while(last_parm!=NULL) {
 						// Search will return
-						ESP_LOGD(TAG, "Getting parameters from X-Custom headers");
+						ESP_LOGD(TAG,   "Getting parameters from X-Custom headers");
 						last_parm = http_server_search_header(next_parm, "X-Custom-", &lenA, &last_parm_name,&next_parm,buf+buflen);
 						if(last_parm!=NULL && last_parm_name!=NULL) {
-							ESP_LOGI(TAG, "http_server_netconn_serve: POST config.json, config %s=%s", last_parm_name, last_parm);
+							ESP_LOGI(TAG,   "http_server_netconn_serve: POST config.json, config %s=%s", last_parm_name, last_parm);
 							if(strcmp(last_parm_name, "fwurl")==0) {
 								// we're getting a request to do an OTA from that URL
-								ESP_LOGW(TAG, "Found OTA request!");
+								ESP_LOGW(TAG,   "Found OTA request!");
 								otaURL=strdup(last_parm);
 								bOTA=true;
 							}
 							else {
-									ESP_LOGV(TAG, "http_server_netconn_serve: POST config.json Storing parameter");
-									err= store_nvs_value(NVS_TYPE_STR, last_parm_name , last_parm);
-									if(err!=ESP_OK) ESP_LOGE(TAG,"Unable to save nvs value. Error: %s",esp_err_to_name(err));
+								ESP_LOGV(TAG,   "http_server_netconn_serve: POST config.json Storing parameter");
+								if(config_set_value(NVS_TYPE_STR, last_parm_name , last_parm) != ESP_OK){
+									ESP_LOGE(TAG,  "Unable to save nvs value.");
+								}
 							}
 						}
 						if(last_parm_name!=NULL) {
@@ -498,10 +461,11 @@ void http_server_netconn_serve(struct netconn *conn) {
 					else {
 						netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
 						if(bOTA) {
+
 #if RECOVERY_APPLICATION
-							ESP_LOGW(TAG, "Starting process OTA for url %s",otaURL);
+							ESP_LOGW(TAG,   "Starting process OTA for url %s",otaURL);
 #else
-							ESP_LOGW(TAG, "Restarting system to process OTA for url %s",otaURL);
+							ESP_LOGW(TAG,   "Restarting system to process OTA for url %s",otaURL);
 							// close the connection cleanly
 							netconn_close(conn);
 							netconn_delete(conn);
@@ -510,97 +474,109 @@ void http_server_netconn_serve(struct netconn *conn) {
 							free(otaURL);
 						}
 					}
-					ESP_LOGI(TAG,"Done Serving POST config.json");
+					ESP_LOGI(TAG,  "Done Serving POST config.json");
 				} 
 				else if(strstr(line, "POST /connect.json ")) {
-					ESP_LOGI(TAG, "http_server_netconn_serve: POST /connect.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: POST /connect.json");
 					bool found = false;
-					int lenS = 0, lenP = 0;
+					int lenS = 0, lenP = 0, lenN = 0;
 					char *ssid = NULL, *password = NULL;
 					ssid = http_server_get_header(save_ptr, "X-Custom-ssid: ", &lenS);
 					password = http_server_get_header(save_ptr, "X-Custom-pwd: ", &lenP);
+					char * new_host_name_b = http_server_get_header(save_ptr, "X-Custom-host_name: ", &lenN);
+					if(lenN > 0){
+						lenN++;
+						char * new_host_name = malloc(lenN);
+						strlcpy(new_host_name, new_host_name_b, lenN);
+						if(config_set_value(NVS_TYPE_STR, "host_name", new_host_name) != ESP_OK){
+							ESP_LOGE(TAG,  "Unable to save host name configuration");
+						}
+						free(new_host_name);
+					}
 
 					if(ssid && lenS <= MAX_SSID_SIZE && password && lenP <= MAX_PASSWORD_SIZE) {
 						wifi_config_t* config = wifi_manager_get_wifi_sta_config();
 						memset(config, 0x00, sizeof(wifi_config_t));
 						memcpy(config->sta.ssid, ssid, lenS);
 						memcpy(config->sta.password, password, lenP);
-						ESP_LOGD(TAG, "http_server_netconn_serve: wifi_manager_connect_async() call, with ssid: %s, password: %s", config->sta.ssid, config->sta.password);
+						ESP_LOGD(TAG,   "http_server_netconn_serve: wifi_manager_connect_async() call, with ssid: %s, password: %s", config->sta.ssid, config->sta.password);
 						wifi_manager_connect_async();
 						netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
 						found = true;
 					}
 					else{
-						ESP_LOGE(TAG,"SSID or Password invalid");
+						ESP_LOGE(TAG,  "SSID or Password invalid");
 					}
 
 
 					if(!found) {
 						/* bad request the authentification header is not complete/not the correct format */
 						netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
-						ESP_LOGE(TAG, "bad request the authentification header is not complete/not the correct format");
+						ESP_LOGE(TAG,   "bad request the authentification header is not complete/not the correct format");
 					}
 
-					ESP_LOGI(TAG, "http_server_netconn_serve: done serving connect.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: done serving connect.json");
 				}
 				else if(strstr(line, "DELETE /connect.json ")) {
-					ESP_LOGI(TAG, "http_server_netconn_serve: DELETE /connect.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: DELETE /connect.json");
 					/* request a disconnection from wifi and forget about it */
 					wifi_manager_disconnect_async();
 					netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
-					ESP_LOGI(TAG, "http_server_netconn_serve: done serving DELETE /connect.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: done serving DELETE /connect.json");
 				}
 				else if(strstr(line, "POST /reboot_ota.json ")) {
-					ESP_LOGI(TAG, "http_server_netconn_serve: POST reboot.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: POST reboot.json");
 					netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
 					netconn_close(conn);
 					netconn_delete(conn);
 					guided_restart_ota();
-					ESP_LOGI(TAG, "http_server_netconn_serve: done serving POST reboot.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: done serving POST reboot.json");
 				}
 				else if(strstr(line, "POST /reboot.json ")) {
-					ESP_LOGI(TAG, "http_server_netconn_serve: POST restart.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: POST restart.json");
 					netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
 					netconn_close(conn);
 					netconn_delete(conn);
 					simple_restart();
-					ESP_LOGI(TAG, "http_server_netconn_serve: done serving POST restart.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: done serving POST restart.json");
 				}
 				else if(strstr(line, "POST /recovery.json ")) {
-					ESP_LOGI(TAG, "http_server_netconn_serve: POST recovery.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: POST recovery.json");
 					netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
 					netconn_close(conn);
 					netconn_delete(conn);
 					guided_factory();
-					ESP_LOGI(TAG, "http_server_netconn_serve: done serving POST recovery.json");
+					ESP_LOGI(TAG,   "http_server_netconn_serve: done serving POST recovery.json");
 				}
 				else if(strstr(line, "GET /status.json ")) {
-					ESP_LOGI(TAG,"Serving status.json");
+					ESP_LOGI(TAG,  "Serving status.json");
 					if(wifi_manager_lock_json_buffer(( TickType_t ) 10)) {
-						char *buff = wifi_manager_get_ip_info_json();
+						char *buff = wifi_manager_alloc_get_ip_info_json();
+						wifi_manager_unlock_json_buffer();
 						if(buff) {
 							netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
 							netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
+							free(buff);
 						}
 						else {
 							netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
 						}
-						wifi_manager_unlock_json_buffer();
+
 					}
 					else {
 						netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
-						ESP_LOGE(TAG, "http_server_netconn_serve: GET /status failed to obtain mutex");
+						ESP_LOGE(TAG,   "http_server_netconn_serve: GET /status failed to obtain mutex");
 					}
-					ESP_LOGI(TAG,"Done Serving status.json");
+					ESP_LOGI(TAG,  "Done Serving status.json");
 				}
 				else {
 					netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
-					ESP_LOGE(TAG, "bad request from host: %s, request %s",remote_address, line);
+					ESP_LOGE(TAG,   "bad request from host: %s, request %s",remote_address, line);
 				}
 			}
 		}
 		else {
-			ESP_LOGE(TAG, "URL not found processing for remote host : %s",remote_address);
+			ESP_LOGE(TAG,   "URL not found processing for remote host : %s",remote_address);
 			netconn_write(conn, http_404_hdr, sizeof(http_404_hdr) - 1, NETCONN_NOCOPY);
 		}
 		free(host);
@@ -616,26 +592,26 @@ void http_server_netconn_serve(struct netconn *conn) {
 }
 
 bool http_server_lock_json_object(TickType_t xTicksToWait) {
-	ESP_LOGD(TAG,"Locking config json object");
+	ESP_LOGD(TAG,  "Locking config json object");
 	if(http_server_config_mutex) {
 		if( xSemaphoreTake( http_server_config_mutex, xTicksToWait ) == pdTRUE ) {
-			ESP_LOGV(TAG,"config Json object locked!");
+			ESP_LOGV(TAG,  "config Json object locked!");
 			return true;
 		}
 		else {
-			ESP_LOGW(TAG,"Semaphore take failed. Unable to lock config Json object mutex");
+			ESP_LOGW(TAG,  "Semaphore take failed. Unable to lock config Json object mutex");
 			return false;
 		}
 	}
 	else {
-		ESP_LOGW(TAG,"Unable to lock config Json object mutex");
+		ESP_LOGW(TAG,  "Unable to lock config Json object mutex");
 		return false;
 	}
 
 }
 
 void http_server_unlock_json_object() {
-	ESP_LOGD(TAG,"Unlocking json buffer!");
+	ESP_LOGD(TAG,  "Unlocking json buffer!");
 	xSemaphoreGive( http_server_config_mutex );
 }
 

File diff suppressed because it is too large
+ 200 - 195
components/wifi-manager/wifi_manager.c


+ 3 - 3
components/wifi-manager/wifi_manager.h

@@ -48,7 +48,7 @@ extern "C" {
 
 #if RECOVERY_APPLICATION==1
 #elif RECOVERY_APPLICATION==0
-#warning "compiling for squeezelite."
+#pragma message "compiling for squeezelite."
 #else
 #error "unknown configuration"
 #endif
@@ -268,8 +268,8 @@ void  filter_unique( wifi_ap_record_t * aplist, uint16_t * ap_num);
 void wifi_manager( void * pvParameters );
 
 
-char* wifi_manager_get_ap_list_json();
-char* wifi_manager_get_ip_info_json();
+char* wifi_manager_alloc_get_ap_list_json();
+char* wifi_manager_alloc_get_ip_info_json();
 cJSON * wifi_manager_clear_ap_list_json(cJSON **old);
 
 /**

+ 1 - 1
main/CMakeLists.txt

@@ -1,6 +1,6 @@
 set(COMPONENT_ADD_INCLUDEDIRS . )
 
-set(COMPONENT_SRCS "esp_app_main.c" "platform_esp32.c" "cmd_wifi.c" "console.c" "nvs_utilities.c" "cmd_squeezelite.c")
+set(COMPONENT_SRCS "esp_app_main.c" "platform_esp32.c" "cmd_wifi.c" "console.c" "nvs_utilities.c" "cmd_squeezelite.c" "config.c")
 set(REQUIRES esp_common)
 set(REQUIRES_COMPONENTS freertos squeezelite nvs_flash esp32 spi_flash newlib log console ota tools )
 

+ 7 - 7
main/cmd_squeezelite.c

@@ -48,13 +48,13 @@ static void * squeezelite_thread(){
 //  Let's not wait on WiFi to allow squeezelite to run in bluetooth mode
 //	ESP_LOGI(TAG,"Waiting for WiFi.");
 //	while(!wait_for_wifi()){usleep(100000);};
-	ESP_LOGD(TAG ,"Number of args received: %u",thread_parms.argc );
-	ESP_LOGD(TAG ,"Values:");
+	ESP_LOGV(TAG ,"Number of args received: %u",thread_parms.argc );
+	ESP_LOGV(TAG ,"Values:");
     for(int i = 0;i<thread_parms.argc; i++){
-    	ESP_LOGD(TAG ,"     %s",thread_parms.argv[i]);
+    	ESP_LOGV(TAG ,"     %s",thread_parms.argv[i]);
     }
 
-    ESP_LOGD(TAG,"Starting Squeezelite runner Thread");
+    ESP_LOGV(TAG,"Starting Squeezelite runner Thread");
     esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
     cfg.thread_name= "squeezelite-run";
     cfg.inherit_cfg = true;
@@ -80,9 +80,9 @@ static int launchsqueezelite(int argc, char **argv)
 {
 	ESP_LOGV(TAG ,"Begin");
 
-    ESP_LOGD(TAG, "Parameters:");
+	ESP_LOGV(TAG, "Parameters:");
     for(int i = 0;i<argc; i++){
-    	ESP_LOGD(TAG, "     %s",argv[i]);
+    	ESP_LOGV(TAG, "     %s",argv[i]);
     }
     ESP_LOGV(TAG,"Saving args in thread structure");
 
@@ -102,7 +102,7 @@ static int launchsqueezelite(int argc, char **argv)
 		thread_parms.argv[thread_parms.argc++]=strdup("-?");
 	}
 
-    ESP_LOGD(TAG,"Starting Squeezelite Thread");
+	ESP_LOGD(TAG,"Starting Squeezelite Thread");
     esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
     cfg.thread_name= "squeezelite";
     cfg.inherit_cfg = true;

+ 623 - 0
main/config.c

@@ -0,0 +1,623 @@
+/*
+ *  Squeezelite for esp32
+ *
+ *  (c) Sebastien 2019
+ *      Philippe G. 2019, philippe_44@outlook.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
+#include "config.h"
+#include "nvs_utilities.h"
+
+#include <stdio.h>
+#include <string.h>
+#include "esp_system.h"
+#include "esp_log.h"
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+#include "driver/uart.h"
+#include "linenoise/linenoise.h"
+#include "argtable3/argtable3.h"
+#include "cmd_decl.h"
+#include "esp_vfs_fat.h"
+#include "nvs.h"
+#include "nvs_flash.h"
+#include "nvs_utilities.h"
+#include "cJSON.h"
+#include "freertos/timers.h"
+#include "freertos/event_groups.h"
+
+
+#define CONFIG_COMMIT_DELAY 1000
+#define LOCK_MAX_WAIT 20*CONFIG_COMMIT_DELAY
+static const char * TAG = "config";
+cJSON * nvs_json=NULL;
+TimerHandle_t timer;
+SemaphoreHandle_t config_mutex = NULL;
+EventGroupHandle_t config_group;
+/* @brief indicate that the ESP32 is currently connected. */
+const int CONFIG_PENDING_CHANGE_BIT = BIT0;
+const int CONFIG_LOAD_BIT = BIT1;
+
+bool config_lock(TickType_t xTicksToWait);
+void config_unlock();
+extern esp_err_t nvs_load_config();
+void config_raise_change(bool flag);
+cJSON_bool config_is_entry_changed(cJSON * entry);
+bool config_set_group_bit(int bit_num,bool flag);
+cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, void * value);
+static void vCallbackFunction( TimerHandle_t xTimer );
+void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag);
+
+void config_init(){
+	ESP_LOGD(TAG, "Creating mutex for Config");
+	config_mutex = xSemaphoreCreateMutex();
+	ESP_LOGD(TAG, "Creating event group");
+	config_group = xEventGroupCreate();
+	ESP_LOGD(TAG, "Loading config from nvs");
+	if(nvs_json !=NULL){
+		cJSON_Delete(nvs_json);
+	}
+	nvs_json = cJSON_CreateObject();
+
+	config_set_group_bit(CONFIG_LOAD_BIT,true);
+	nvs_load_config();
+	config_set_group_bit(CONFIG_LOAD_BIT,false);
+	config_start_timer();
+}
+
+void config_start_timer(){
+	ESP_LOGD(TAG, "Starting config timer");
+	timer = xTimerCreate("configTimer", CONFIG_COMMIT_DELAY / portTICK_RATE_MS, pdFALSE, NULL, vCallbackFunction);
+    if( xTimerStart( timer , CONFIG_COMMIT_DELAY/ portTICK_RATE_MS ) != pdPASS )    {
+        ESP_LOGE(TAG, "config commitment timer failed to start.");
+    }
+
+}
+cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, void * value){
+	char * num_buffer = NULL;
+	num_buffer = malloc(NUM_BUFFER_LEN);
+	memset(num_buffer,0x00,NUM_BUFFER_LEN);
+	cJSON * entry = cJSON_CreateObject();
+
+	if(entry == NULL) {
+		ESP_LOGE(TAG, "Unable to allocate memory for entry %s",key);
+		return NULL;
+	}
+	cJSON_AddNumberToObject(entry,"type", nvs_type	);
+	switch (nvs_type) {
+		case NVS_TYPE_I8:
+			snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int8_t*)value);
+			cJSON_AddNumberToObject(entry,"value", *(int8_t*)value	);
+			break;
+		case NVS_TYPE_I16:
+			snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int16_t*)value);
+			cJSON_AddNumberToObject(entry,"value", *(int16_t*)value	);
+			break;
+		case NVS_TYPE_I32:
+			snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int32_t*)value);
+			cJSON_AddNumberToObject(entry,"value", *(int32_t*)value	);
+			break;
+		case NVS_TYPE_U8:
+			snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint8_t*)value);
+			cJSON_AddNumberToObject(entry,"value", *(uint8_t*)value	);
+			break;
+		case NVS_TYPE_U16:
+			snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint16_t*)value);
+			cJSON_AddNumberToObject(entry,"value", *(uint16_t*)value	);
+			break;
+		case NVS_TYPE_U32:
+			snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint32_t*)value);
+			cJSON_AddNumberToObject(entry,"value", *(uint32_t*)value	);
+			break;
+		case NVS_TYPE_STR:
+			cJSON_AddStringToObject(entry, "value", (char *)value);
+			break;
+		case NVS_TYPE_I64:
+		case NVS_TYPE_U64:
+		default:
+			ESP_LOGE(TAG, "nvs type %u not supported", nvs_type);
+			break;
+	}
+	cJSON * existing = cJSON_GetObjectItemCaseSensitive(nvs_json, key);
+	if(existing!=NULL ) {
+		ESP_LOGV(TAG, "Changing existing entry [%s].", key);
+		char * exist_str = cJSON_PrintUnformatted(existing);
+		if(exist_str!=NULL){
+			ESP_LOGV(TAG,"Existing entry: %s", exist_str);
+			free(exist_str);
+		}
+		else {
+			ESP_LOGV(TAG,"Failed to print existing entry");
+		}
+		// set commit flag as equal so we can compare
+		cJSON * chg_flag =cJSON_AddBoolToObject(entry,"chg",config_is_entry_changed(existing));
+		if(!cJSON_Compare(entry,existing,false)){
+			char * entry_str = cJSON_PrintUnformatted(entry);
+			if(entry_str!=NULL){
+				ESP_LOGD(TAG,"New config object: \n%s", entry_str );
+				free(entry_str);
+			}
+			else {
+				ESP_LOGD(TAG,"Failed to print entry");
+			}
+			cJSON_Delete(chg_flag);
+			ESP_LOGI(TAG, "Setting changed flag config [%s]", key);
+			config_set_entry_changed_flag(entry,true);
+			ESP_LOGI(TAG, "Updating config [%s]", key);
+			cJSON_ReplaceItemInObject(nvs_json,key, entry);
+			entry_str = cJSON_PrintUnformatted(entry);
+			if(entry_str!=NULL){
+				ESP_LOGD(TAG,"New config: %s", entry_str );
+				free(entry_str);
+			}
+			else {
+				ESP_LOGD(TAG,"Failed to print entry");
+			}
+		}
+		else {
+			ESP_LOGD(TAG, "Config not changed. ");
+		}
+	}
+	else {
+		// This is a new entry.
+		config_set_entry_changed_flag(entry,true);
+		cJSON_AddItemToObject(nvs_json, key, entry);
+	}
+	free(num_buffer);
+
+	return entry;
+}
+
+nvs_type_t config_get_entry_type(cJSON * entry){
+	if(entry==NULL){
+		ESP_LOGE(TAG,"null pointer received!");
+		return 0;
+	}
+	cJSON * entry_type = cJSON_GetObjectItemCaseSensitive(entry, "type");
+	if(entry_type ==NULL ) {
+		ESP_LOGE(TAG, "Entry type not found in nvs cache for existing setting.");
+		return 0;
+	}
+	return entry_type->valuedouble;
+}
+void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag){
+	if(entry==NULL){
+		ESP_LOGE(TAG,"null pointer received!");
+		return;
+	}
+	bool bIsConfigLoading=((xEventGroupGetBits(config_group) & CONFIG_LOAD_BIT)!=0);
+	bool changedFlag=bIsConfigLoading?false:flag;
+	cJSON * changed = cJSON_GetObjectItemCaseSensitive(entry, "chg");
+	if(changed ==NULL ) {
+		ESP_LOGV(TAG, "Adding change flag. ");
+		cJSON_AddBoolToObject(entry,"chg",changedFlag);
+	}
+	else {
+		if(cJSON_IsTrue(changed) && changedFlag){
+			ESP_LOGW(TAG, "Commit flag not changed!");
+		}
+		else{
+			ESP_LOGV(TAG, "Updating change flag to %s",changedFlag?"TRUE":"FALSE");
+			cJSON_Delete(changed);
+			cJSON_AddBoolToObject(entry,"chg",changedFlag);
+		}
+	}
+	if(changedFlag) config_raise_change(true);
+}
+cJSON_bool config_is_entry_changed(cJSON * entry){
+	if(entry==NULL){
+		ESP_LOGE(TAG,"null pointer received!");
+		return true;
+	}
+	cJSON * changed = cJSON_GetObjectItemCaseSensitive(entry, "chg");
+	if(changed ==NULL ) {
+		ESP_LOGE(TAG, "Change flag not found! ");
+		return true;
+	}
+	return cJSON_IsTrue(changed);
+}
+void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry){
+	void * value=NULL;
+	if(entry==NULL){
+		ESP_LOGE(TAG,"null pointer received!");
+	}
+	ESP_LOGV(TAG, "getting config value type %s", type_to_str(nvs_type));
+	cJSON * entry_value = cJSON_GetObjectItemCaseSensitive(entry, "value");
+	if(entry_value==NULL ) {
+		char * entry_str = cJSON_PrintUnformatted(entry);
+		if(entry_str!=NULL){
+			ESP_LOGE(TAG, "Missing config value!. Object: \n%s", entry_str);
+			free(entry_str);
+		}
+		else{
+			ESP_LOGE(TAG, "Missing config value");
+		}
+		return NULL;
+	}
+
+	nvs_type_t type = config_get_entry_type(entry);
+	if(nvs_type != type){
+		// requested value type different than the stored type
+		char * entry_str = cJSON_PrintUnformatted(entry);
+		if(entry_str!=NULL){
+			ESP_LOGE(TAG, "Requested value type %s, found value type %s instead, Object: \n%s", type_to_str(nvs_type), type_to_str(type),entry_str);
+			free(entry_str);
+		}
+		else{
+			ESP_LOGE(TAG, "Requested value type %s, found value type %s instead", type_to_str(nvs_type), type_to_str(type));
+		}
+
+		return NULL;
+	}
+
+	if (nvs_type == NVS_TYPE_I8) {
+		value=malloc(sizeof(int8_t));
+		*(int8_t *)value = (int8_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_U8) {
+		value=malloc(sizeof(uint8_t));
+		*(uint8_t *)value = (uint8_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_I16) {
+		value=malloc(sizeof(int16_t));
+		*(int16_t *)value = (int16_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_U16) {
+		value=malloc(sizeof(uint16_t));
+		*(uint16_t *)value = (uint16_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_I32) {
+		value=malloc(sizeof(int32_t));
+		*(int32_t *)value = (int32_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_U32) {
+		value=malloc(sizeof(uint32_t));
+		*(uint32_t *)value = (uint32_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_I64) {
+		value=malloc(sizeof(int64_t));
+		*(int64_t *)value = (int64_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_U64) {
+		value=malloc(sizeof(uint64_t));
+		*(uint64_t *)value = (uint64_t)entry_value->valuedouble;
+	} else if (nvs_type == NVS_TYPE_STR) {
+		if(!cJSON_IsString(entry_value)){
+			char * entry_str = cJSON_PrintUnformatted(entry);
+			if(entry_str!=NULL){
+				ESP_LOGE(TAG, "requested value type string, config type is different. key: %s, value: %s, type %d, Object: \n%s",
+					entry_value->string,
+					entry_value->valuestring,
+					entry_value->type,
+					entry_str);
+				free(entry_str);
+			}
+			else {
+				ESP_LOGE(TAG, "requested value type string, config type is different. key: %s, value: %s, type %d",
+					entry_value->string,
+					entry_value->valuestring,
+					entry_value->type);
+			}
+		}
+		else {
+			value=(void *)strdup(cJSON_GetStringValue(entry_value));
+			if(value==NULL){
+				char * entry_str = cJSON_PrintUnformatted(entry);
+				if(entry_str!=NULL){
+					ESP_LOGE(TAG, "strdup failed on value for object \n%s",entry_str);
+					free(entry_str);
+				}
+				else {
+					ESP_LOGE(TAG, "strdup failed on value");
+				}
+			}
+		}
+	} else if (nvs_type == NVS_TYPE_BLOB) {
+		ESP_LOGE(TAG, "Unsupported type NVS_TYPE_BLOB");
+	}
+	return value;
+}
+
+void config_commit_to_nvs(){
+	ESP_LOGI(TAG,"Committing configuration to nvs. Locking config object.");
+	ESP_LOGV(TAG,"config_commit_to_nvs. Locking config object.");
+	if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
+		ESP_LOGE(TAG, "config_commit_to_nvs: Unable to lock config for commit ");
+		return ;
+	}
+	if(nvs_json==NULL){
+		ESP_LOGE(TAG, ": cJSON nvs cache object not set.");
+		return;
+	}
+	ESP_LOGV(TAG,"config_commit_to_nvs. Config Locked!");
+	cJSON * entry=nvs_json->child;
+	while(entry!= NULL){
+		char * entry_str = cJSON_PrintUnformatted(entry);
+		if(entry_str!=NULL){
+			ESP_LOGV(TAG,"config_commit_to_nvs processing item %s",entry_str);
+			free(entry_str);
+		}
+
+		if(config_is_entry_changed(entry)){
+			ESP_LOGD(TAG, "Committing entry %s value to nvs.",(entry->string==NULL)?"UNKNOWN":entry->string);
+			nvs_type_t type = config_get_entry_type(entry);
+			void * value = config_safe_alloc_get_entry_value(type, entry);
+			if(value!=NULL){
+				esp_err_t err = store_nvs_value(type,entry->string,value);
+				free(value);
+				if(err!=ESP_OK){
+					char * entry_str = cJSON_PrintUnformatted(entry);
+					if(entry_str!=NULL){
+						ESP_LOGE(TAG, "Error comitting value to nvs for key %s, Object: \n%s",entry->string,entry_str);
+						free(entry_str);
+					}
+					else {
+						ESP_LOGE(TAG, "Error comitting value to nvs for key %s",entry->string);
+					}
+				}
+				else {
+					config_set_entry_changed_flag(entry, false);
+				}
+			}
+			else {
+				char * entry_str = cJSON_PrintUnformatted(entry);
+				if(entry_str!=NULL){
+					ESP_LOGE(TAG, "Unable to retrieve value. Error comitting value to nvs for key %s, Object: \n%s",entry->string,entry_str);
+					free(entry_str);
+				}
+				else {
+					ESP_LOGE(TAG, "Unable to retrieve value. Error comitting value to nvs for key %s",entry->string);
+				}
+			}
+		}
+		else {
+			ESP_LOGV(TAG,"config_commit_to_nvs. Item already committed.  Ignoring.");
+		}
+		taskYIELD();  /* allows the freeRTOS scheduler to take over if needed. */
+		entry = entry->next;
+	}
+	ESP_LOGV(TAG,"config_commit_to_nvs. Resetting the global commit flag.");
+	config_raise_change(false);
+	ESP_LOGV(TAG,"config_commit_to_nvs. Releasing the lock object.");
+	config_unlock();
+}
+bool config_has_changes(){
+	return  (xEventGroupGetBits(config_group) & CONFIG_PENDING_CHANGE_BIT)!=0;
+}
+
+
+bool wait_for_commit(){
+	bool needs_commit=(xEventGroupGetBits(config_group) & CONFIG_PENDING_CHANGE_BIT)!=0;
+	if(needs_commit){
+		ESP_LOGD(TAG,"Waiting for config commit ...");
+		needs_commit = (xEventGroupWaitBits(config_group, CONFIG_PENDING_CHANGE_BIT,pdFALSE, pdTRUE, (CONFIG_COMMIT_DELAY*5) / portTICK_PERIOD_MS) & CONFIG_PENDING_CHANGE_BIT)!=0;
+		if(needs_commit){
+			ESP_LOGE(TAG,"Timeout waiting for config commit.");
+	    }
+	    else
+	    {
+	    	ESP_LOGI(TAG,"Config committed!");
+	    }
+	}
+	return needs_commit;
+}
+
+bool config_lock(TickType_t xTicksToWait) {
+	ESP_LOGV(TAG, "Locking config json object");
+	if( xSemaphoreTake( config_mutex, xTicksToWait ) == pdTRUE ) {
+		ESP_LOGV(TAG, "config Json object locked!");
+		return true;
+	}
+	else {
+		ESP_LOGE(TAG, "Semaphore take failed. Unable to lock config Json object mutex");
+		return false;
+	}
+}
+
+void config_unlock() {
+	ESP_LOGV(TAG, "Unlocking json buffer!");
+	xSemaphoreGive( config_mutex );
+}
+
+static void vCallbackFunction( TimerHandle_t xTimer ) {
+	if(config_has_changes()){
+		ESP_LOGI(TAG, "configuration has some uncommitted entries");
+		config_commit_to_nvs();
+	}
+	else{
+		ESP_LOGV(TAG,"commit timer: commit flag not set");
+	}
+	xTimerReset( xTimer, 10 );
+}
+void config_raise_change(bool flag){
+	if(config_set_group_bit(CONFIG_PENDING_CHANGE_BIT,flag))
+	{
+		ESP_LOGD(TAG,"Config change indicator was %s",flag?"Set":"Cleared");
+	}
+}
+bool config_set_group_bit(int bit_num,bool flag){
+	bool result = true;
+	int curFlags=xEventGroupGetBits(config_group);
+	if((curFlags & CONFIG_LOAD_BIT) && bit_num == CONFIG_PENDING_CHANGE_BIT ){
+		ESP_LOGD(TAG,"Loading config, ignoring changes");
+		result = false;
+	}
+	if(result){
+		bool curBit=(xEventGroupGetBits(config_group) & bit_num);
+		if(curBit == flag){
+			ESP_LOGV(TAG,"Flag %d already %s", bit_num, flag?"Set":"Cleared");
+			result = false;
+		}
+	}
+	if(result){
+		ESP_LOGV(TAG,"%s Flag %d ", flag?"Setting":"Clearing",bit_num);
+		if(!flag){
+			xEventGroupClearBits(config_group, bit_num);
+		}
+		else {
+			xEventGroupSetBits(config_group, bit_num);
+		}
+	}
+	return result;
+}
+void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size) {
+	if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
+		ESP_LOGE(TAG, "Unable to lock config");
+		return;
+	}
+
+	ESP_LOGV(TAG, "Checking if key %s exists in nvs cache for type %s.", key,type_to_str(type));
+	cJSON * entry = cJSON_GetObjectItemCaseSensitive(nvs_json, key);
+
+	if(entry !=NULL){
+		ESP_LOGV(TAG, "Entry found.");
+	}
+	else {
+		// Value was not found
+		ESP_LOGW(TAG, "Adding default value for [%s].", key);
+		entry=config_set_value_safe(type, key, default_value);
+		if(entry == NULL){
+			ESP_LOGE(TAG, "Failed to add value to cache!");
+		}
+		char * entry_str = cJSON_PrintUnformatted(entry);
+		if(entry_str!=NULL){
+			ESP_LOGD(TAG, "Value added to default for object: \n%s",entry_str);
+			free(entry_str);
+		}
+	}
+
+	config_unlock();
+
+}
+
+void config_delete_key(const char *key){
+	nvs_handle nvs;
+	ESP_LOGD(TAG, "Deleting nvs entry for [%s]", key);
+	if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
+		ESP_LOGE(TAG, "Unable to lock config for delete");
+		return false;
+	}
+	esp_err_t err = nvs_open_from_partition(settings_partition, current_namespace, NVS_READWRITE, &nvs);
+	if (err == ESP_OK) {
+		err = nvs_erase_key(nvs, key);
+		if (err == ESP_OK) {
+			ESP_LOGD(TAG, "key [%s] erased from nvs.",key);
+			err = nvs_commit(nvs);
+			if (err == ESP_OK) {
+				ESP_LOGD(TAG, "nvs erase committed.");
+			}
+			else {
+				ESP_LOGE(TAG, "Unable to commit nvs erase operation for key [%s]. %s.",key,esp_err_to_name(err));
+			}
+		}
+		else {
+			ESP_LOGE(TAG, "Unable to delete nvs key [%s]. %s. ",key, esp_err_to_name(err));
+		}
+		nvs_close(nvs);
+	}
+	else {
+		ESP_LOGE(TAG, "Error opening nvs: %s. Unable to delete nvs key [%s].",esp_err_to_name(err),key);
+	}
+	cJSON * entry = cJSON_GetObjectItemCaseSensitive(nvs_json, key);
+	if(entry !=NULL){
+		ESP_LOGI(TAG, "Removing config key [%s]", entry->string);
+		cJSON_Delete(entry);
+	}
+	else {
+		ESP_LOGW(TAG, "Unable to remove config key [%s]: not found.", key);
+	}
+	config_unlock();
+}
+void * config_alloc_get(nvs_type_t nvs_type, const char *key) {
+	return config_alloc_get_default(nvs_type, key, NULL, 0);
+}
+void * config_alloc_get_default(nvs_type_t nvs_type, const char *key, void * default_value, size_t blob_size) {
+
+	void * value = NULL;
+	ESP_LOGV(TAG, "Retrieving key %s from nvs cache for type %s.", key,type_to_str(nvs_type));
+	if(nvs_json==NULL){
+		ESP_LOGE(TAG,"configuration not loaded!");
+		return value;
+	}
+	if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
+		ESP_LOGE(TAG, "Unable to lock config");
+		return value;
+	}
+	ESP_LOGD(TAG,"Getting config entry for key %s",key);
+	cJSON * entry = cJSON_GetObjectItemCaseSensitive(nvs_json, key);
+	if(entry !=NULL){
+		ESP_LOGV(TAG, "Entry found, getting value.");
+		value = config_safe_alloc_get_entry_value(nvs_type, entry);
+	}
+	else if(default_value!=NULL){
+		// Value was not found
+		ESP_LOGW(TAG, "Adding new config value for key [%s]",key);
+		entry=config_set_value_safe(nvs_type, key, default_value);
+		if(entry == NULL){
+			ESP_LOGE(TAG, "Failed to add value to cache");
+		}
+		else {
+			char * entry_str = cJSON_PrintUnformatted(entry);
+			if(entry_str!=NULL){
+				ESP_LOGV(TAG, "Value added configuration object for key [%s]: \n%s", entry->string,entry_str);
+				free(entry_str);
+			}
+			else {
+				ESP_LOGV(TAG, "Value added configuration object for key [%s]", entry->string);
+			}
+			value = config_safe_alloc_get_entry_value(nvs_type, entry);
+		}
+	}
+	else{
+		ESP_LOGW(TAG,"Value not found for key %s",key);
+	}
+	config_unlock();
+	return value;
+}
+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.\"}");
+	}
+	if(bFormatted){
+		json_buffer= cJSON_Print(nvs_json);
+	}
+	else {
+		json_buffer= cJSON_PrintUnformatted(nvs_json);
+	}
+	config_unlock();
+	return json_buffer;
+}
+esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value){
+	esp_err_t result = ESP_OK;
+	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;
+	}
+	cJSON * entry = config_set_value_safe(nvs_type, key, value);
+	if(entry == NULL){
+		result = ESP_FAIL;
+	}
+	else{
+		char * entry_str = cJSON_PrintUnformatted(entry);
+		if(entry_str!=NULL){
+			ESP_LOGV(TAG,"config_set_value result: \n%s",entry_str);
+			free(entry_str);
+		}
+		else {
+			ESP_LOGV(TAG,"config_set_value completed");
+		}
+
+	}
+	config_unlock();
+	return result;
+}
+

+ 24 - 0
main/config.h

@@ -0,0 +1,24 @@
+#pragma once
+#include <stdio.h>
+#include <string.h>
+#include "esp_system.h"
+#include "nvs_utilities.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+}
+#endif
+bool config_has_uncommitted();
+void config_commit_to_nvs();
+void config_start_timer();
+void config_init();
+void * config_alloc_get_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
+void config_delete_key(const char *key);
+void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
+void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
+bool wait_for_commit();
+char * config_alloc_get_json(bool bFormatted);
+esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value);
+

+ 6 - 8
main/console.c

@@ -55,7 +55,7 @@ void process_autoexec(){
 	char * autoexec_value=NULL;
 	uint8_t autoexec_flag=0;
 
-	char * str_flag = get_nvs_value_alloc(NVS_TYPE_STR, "autoexec");
+	char * str_flag = config_alloc_get(NVS_TYPE_STR, "autoexec");
 	if(!bypass_wifi_manager){
 		ESP_LOGW(TAG, "Procesing autoexec commands while wifi_manager active.  Wifi related commands will be ignored.");
 	}
@@ -65,12 +65,12 @@ void process_autoexec(){
 
 	if(str_flag !=NULL ){
 		autoexec_flag=atoi(str_flag);
-		ESP_LOGI(TAG,"autoexec flag value found with value %u, from string value: %s", autoexec_flag, str_flag);
+		ESP_LOGI(TAG,"autoexec is set to %s auto-process", autoexec_flag>0?"perform":"skip");
 		if(autoexec_flag == 1) {
 			do {
 				snprintf(autoexec_name,sizeof(autoexec_name)-1,"autoexec%u",i++);
 				ESP_LOGD(TAG,"Getting command name %s", autoexec_name);
-				autoexec_value= get_nvs_value_alloc(NVS_TYPE_STR, 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 ){
 						ESP_LOGW(TAG,"Ignoring wifi join command.");
@@ -97,11 +97,7 @@ void process_autoexec(){
 	}
 	else
 	{
-		ESP_LOGD(TAG,"No matching command found for name autoexec. Adding default entries");
-		char autoexec_dft[]="0";
-		char autoexec1_dft[256]="squeezelite -o I2S -b 500:2000 -d all=info -M esp32";
-		store_nvs_value(NVS_TYPE_STR,"autoexec",autoexec_dft);
-		store_nvs_value(NVS_TYPE_STR,"autoexec1",autoexec1_dft);
+		ESP_LOGD(TAG,"No matching command found for name autoexec.");
 	}
 }
 
@@ -262,6 +258,8 @@ static void * console_thread() {
 		run_command(line);
 		/* linenoise allocates line buffer on the heap, so need to free it */
 		linenoiseFree(line);
+		config_commit_to_nvs();
+		taskYIELD();
 	}
 	return NULL;
 }

+ 92 - 83
main/esp_app_main.c

@@ -44,6 +44,8 @@
 #include "wifi_manager.h"
 #include "squeezelite-ota.h"
 #include <math.h>
+#include "config.h"
+
 
 EventGroupHandle_t wifi_event_group;
 bool enable_bt_sink=false;
@@ -52,16 +54,17 @@ bool jack_mutes_amp=false;
 bool bypass_wifi_manager=false;
 const int CONNECTED_BIT = BIT0;
 #define JOIN_TIMEOUT_MS (10000)
-
+#define LOCAL_MAC_SIZE 20
 static const char TAG[] = "esp_app_main";
+#define DEFAULT_HOST_NAME "squeezelite"
 char * fwurl = NULL;
 
 #ifdef CONFIG_SQUEEZEAMP
 #define LED_GREEN_GPIO 	12
 #define LED_RED_GPIO	13
 #else
-#define LED_GREEN_GPIO 	0
-#define LED_RED_GPIO	0
+#define LED_GREEN_GPIO 	-1
+#define LED_RED_GPIO	-1
 #endif
 static bool bWifiConnected=false;
 
@@ -96,61 +99,14 @@ bool wait_for_wifi(){
 	}
     return connected;
 }
-static void initialize_nvs() {
-	ESP_LOGI(TAG,"Initializing nvs from flash");
-	esp_err_t err = nvs_flash_init();
-	if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
-		ESP_LOGW(TAG,"Error %s. Erasing nvs flash", esp_err_to_name(err));
-		ESP_ERROR_CHECK(nvs_flash_erase());
-		err = nvs_flash_init();
-	}
-	ESP_ERROR_CHECK(err);
-	ESP_LOGI(TAG,"Initializing nvs from partition %s",settings_partition);
-	err = nvs_flash_init_partition(settings_partition);
-	if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
-		ESP_LOGW(TAG,"Error %s. Erasing nvs on partition %s",esp_err_to_name(err),settings_partition);
-		ESP_ERROR_CHECK(nvs_flash_erase_partition(settings_partition));
-		err = nvs_flash_init_partition(settings_partition);
-	}
-	if(err!=ESP_OK){
-		ESP_LOGE(TAG,"nvs init completed with error %s",esp_err_to_name(err));
-	}
-	ESP_ERROR_CHECK(err);
-	ESP_LOGD(TAG,"nvs init completed");
 
-}
 char * process_ota_url(){
-    nvs_handle nvs;
     ESP_LOGI(TAG,"Checking for update url");
-    char * fwurl=get_nvs_value_alloc(NVS_TYPE_STR, "fwurl");
+    char * fwurl=config_alloc_get(NVS_TYPE_STR, "fwurl");
 	if(fwurl!=NULL)
 	{
 		ESP_LOGD(TAG,"Deleting nvs entry for Firmware URL %s", fwurl);
-		esp_err_t err = nvs_open_from_partition(settings_partition, current_namespace, NVS_READWRITE, &nvs);
-		if (err == ESP_OK) {
-			err = nvs_erase_key(nvs, "fwurl");
-			if (err == ESP_OK) {
-				ESP_LOGD(TAG,"Firmware url erased from nvs.");
-				err = nvs_commit(nvs);
-				if (err == ESP_OK) {
-					ESP_LOGI(TAG, "Value with key '%s' erased", "fwurl");
-					ESP_LOGD(TAG,"nvs erase committed.");
-				}
-				else
-				{
-					ESP_LOGE(TAG,"Unable to commit nvs erase operation. Error : %s.",esp_err_to_name(err));
-				}
-			}
-			else
-			{
-				ESP_LOGE(TAG,"Error : %s. Unable to delete firmware url key.",esp_err_to_name(err));
-			}
-			nvs_close(nvs);
-		}
-		else
-		{
-			ESP_LOGE(TAG,"Error opening nvs: %s. Unable to delete firmware url key.",esp_err_to_name(err));
-		}
+		config_delete_key("fwurl");
 	}
 	return fwurl;
 }
@@ -164,7 +120,7 @@ char * process_ota_url(){
 //CONFIG_WIFI_MANAGER_MAX_RETRY=2
 u16_t get_adjusted_volume(u16_t volume){
 
-	char * str_factor = get_nvs_value_alloc_default(NVS_TYPE_STR, "volumefactor", "3", 0);
+	char * str_factor = config_alloc_get_default(NVS_TYPE_STR, "volumefactor", "3", 0);
 	if(str_factor != NULL ){
 
 		float factor = atof(str_factor);
@@ -175,54 +131,103 @@ u16_t get_adjusted_volume(u16_t volume){
 		ESP_LOGW(TAG,"Error retrieving volume factor.  Returning unmodified volume level. ");
 		return volume;
 	}
-
 }
 
 void register_default_nvs(){
+	uint8_t mac[6];
+	char macStr[LOCAL_MAC_SIZE+1];
+	char default_ap_name[strlen(CONFIG_DEFAULT_AP_SSID)+sizeof(macStr)];
+	char default_host_name[strlen(DEFAULT_HOST_NAME)+sizeof(macStr)];
+	char default_command_line[strlen(CONFIG_DEFAULT_COMMAND_LINE)+sizeof(macStr)];
+
+
+	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]);
+
+	strcpy(default_ap_name,CONFIG_DEFAULT_AP_SSID);
+	strcat(default_ap_name,macStr);
+
+	strcpy(default_host_name,DEFAULT_HOST_NAME);
+	strcat(default_host_name,macStr);
+
+	if(!strstr(CONFIG_DEFAULT_COMMAND_LINE, "-n %s")){
+		snprintf(default_command_line, sizeof(default_command_line)-1,CONFIG_DEFAULT_COMMAND_LINE,default_host_name);
+	}
+	else{
+		strncpy(default_command_line, CONFIG_DEFAULT_COMMAND_LINE,sizeof(default_command_line)-1);
+		strncat(default_command_line, "-n ",sizeof(default_command_line)-1);
+		strncat(default_command_line, default_host_name,sizeof(default_command_line)-1);
+	}
 
 
-	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_sink_name", CONFIG_BT_NAME);
-	nvs_value_set_default(NVS_TYPE_STR, "bt_sink_name", CONFIG_BT_NAME, 0);
+	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "autoexec", "1");
+	config_set_default(NVS_TYPE_STR,"autoexec","1", 0);
+	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "autoexec1",default_command_line);
+	config_set_default(NVS_TYPE_STR,"autoexec1",default_command_line,0);
+
+	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "volumefactor", "3");
+	config_set_default(NVS_TYPE_STR, "volumefactor", "3", 0);
+	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_name", CONFIG_BT_NAME);
+	config_set_default(NVS_TYPE_STR, "bt_name", CONFIG_BT_NAME, 0);
+
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_sink_pin", STR(CONFIG_BT_SINK_PIN));
-	nvs_value_set_default(NVS_TYPE_STR, "bt_sink_pin", STR(CONFIG_BT_SINK_PIN), 0);
-	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "host_name", "squeezelite-esp32");
-	nvs_value_set_default(NVS_TYPE_STR, "host_name", "squeezelite-esp32", 0);
+	config_set_default(NVS_TYPE_STR, "bt_sink_pin", STR(CONFIG_BT_SINK_PIN), 0);
+	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "host_name", default_host_name);
+	config_set_default(NVS_TYPE_STR, "host_name", default_host_name, 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "release_url", SQUEEZELITE_ESP32_RELEASE_URL);
-	nvs_value_set_default(NVS_TYPE_STR, "release_url", SQUEEZELITE_ESP32_RELEASE_URL, 0);
+	config_set_default(NVS_TYPE_STR, "release_url", SQUEEZELITE_ESP32_RELEASE_URL, 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s","ap_ip_address",CONFIG_DEFAULT_AP_IP );
-	nvs_value_set_default(NVS_TYPE_STR, "ap_ip_address",CONFIG_DEFAULT_AP_IP , 0);
+	config_set_default(NVS_TYPE_STR, "ap_ip_address",CONFIG_DEFAULT_AP_IP , 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY );
-	nvs_value_set_default(NVS_TYPE_STR, "ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY , 0);
+	config_set_default(NVS_TYPE_STR, "ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY , 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s","ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK );
-	nvs_value_set_default(NVS_TYPE_STR, "ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK , 0);
+	config_set_default(NVS_TYPE_STR, "ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK , 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL));
-	nvs_value_set_default(NVS_TYPE_STR, "ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL) , 0);
-	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_ssid",CONFIG_DEFAULT_AP_SSID );
-	nvs_value_set_default(NVS_TYPE_STR, "ap_ssid",CONFIG_DEFAULT_AP_SSID , 0);
-	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_password", CONFIG_DEFAULT_AP_PASSWORD);
-	nvs_value_set_default(NVS_TYPE_STR, "ap_password", CONFIG_DEFAULT_AP_PASSWORD, 0);
+	config_set_default(NVS_TYPE_STR, "ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL) , 0);
+	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_ssid", default_ap_name);
+	config_set_default(NVS_TYPE_STR, "ap_ssid",default_ap_name , 0);
+	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_pwd", CONFIG_DEFAULT_AP_PASSWORD);
+	config_set_default(NVS_TYPE_STR, "ap_pwd", CONFIG_DEFAULT_AP_PASSWORD, 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "airplay_name",CONFIG_AIRPLAY_NAME);
-	nvs_value_set_default(NVS_TYPE_STR, "airplay_name",CONFIG_AIRPLAY_NAME , 0);
+	config_set_default(NVS_TYPE_STR, "airplay_name",CONFIG_AIRPLAY_NAME , 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "airplay_port", CONFIG_AIRPLAY_PORT);
-	nvs_value_set_default(NVS_TYPE_STR, "airplay_port", CONFIG_AIRPLAY_PORT, 0);
+	config_set_default(NVS_TYPE_STR, "airplay_port", CONFIG_AIRPLAY_PORT, 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_sink_name", CONFIG_A2DP_SINK_NAME);
-	nvs_value_set_default(NVS_TYPE_STR, "a2dp_sink_name", CONFIG_A2DP_SINK_NAME, 0);
+	config_set_default(NVS_TYPE_STR, "a2dp_sink_name", CONFIG_A2DP_SINK_NAME, 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_dev_name", CONFIG_A2DP_DEV_NAME);
-	nvs_value_set_default(NVS_TYPE_STR, "a2dp_dev_name", CONFIG_A2DP_DEV_NAME, 0);
+	config_set_default(NVS_TYPE_STR, "a2dp_dev_name", CONFIG_A2DP_DEV_NAME, 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bypass_wm", "0");
-	nvs_value_set_default(NVS_TYPE_STR, "bypass_wm", "0", 0);
+	config_set_default(NVS_TYPE_STR, "bypass_wm", "0", 0);
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "enable_bt_sink", STR(CONFIG_BT_SINK));
-	char * flag = get_nvs_value_alloc_default(NVS_TYPE_STR, "enable_bt_sink", STR(CONFIG_BT_SINK), 0);
-	enable_bt_sink= (strcmp(flag,"1")==0 ||strcasecmp(flag,"y")==0);
-	free(flag);
+	char * flag = config_alloc_get_default(NVS_TYPE_STR, "enable_bt_sink", STR(CONFIG_BT_SINK), 0);
+	if(flag !=NULL){
+		enable_bt_sink= (strcmp(flag,"1")==0 ||strcasecmp(flag,"y")==0);
+		free(flag);
+	}
+	else {
+		ESP_LOGE(TAG,"Unable to get flag 'enable_bt_sink'");
+	}
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "enable_airplay", STR(CONFIG_AIRPLAY_SINK));
-	flag = get_nvs_value_alloc_default(NVS_TYPE_STR, "enable_airplay", STR(CONFIG_AIRPLAY_SINK), 0);
-	enable_airplay= (strcmp(flag,"1")==0 ||strcasecmp(flag,"y")==0);
-	free(flag);
+	flag = config_alloc_get_default(NVS_TYPE_STR, "enable_airplay", STR(CONFIG_AIRPLAY_SINK), 0);
+	if(flag !=NULL){
+		enable_airplay= (strcmp(flag,"1")==0 ||strcasecmp(flag,"y")==0);
+		free(flag);
+	}
+	else {
+		ESP_LOGE(TAG,"Unable to get flag 'enable_airplay'");
+	}
+
+
 	ESP_LOGD(TAG,"Registering default value for key %s, value %s", "jack_mutes_amp", "n");
-	flag = get_nvs_value_alloc_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
-	jack_mutes_amp= (strcmp(flag,"1")==0 ||strcasecmp(flag,"y")==0);
-	free(flag);
+	flag = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
+
+	if(flag !=NULL){
+		jack_mutes_amp= (strcmp(flag,"1")==0 ||strcasecmp(flag,"y")==0);
+		free(flag);
+	}
+	else {
+		ESP_LOGE(TAG,"Unable to get flag 'jack_mutes_amp'");
+	}
 	ESP_LOGD(TAG,"Done setting default values in nvs.");
 }
 
@@ -238,6 +243,9 @@ void app_main()
 
 	ESP_LOGI(TAG,"Starting app_main");
 	initialize_nvs();
+	ESP_LOGI(TAG,"Setting up config subsystem.");
+	config_init();
+
 	ESP_LOGD(TAG,"Registering default values");
 	register_default_nvs();
 
@@ -246,7 +254,7 @@ void app_main()
 
 
 	ESP_LOGD(TAG,"Getting value for WM bypass, nvs 'bypass_wm'");
-	char * bypass_wm = get_nvs_value_alloc_default(NVS_TYPE_STR, "bypass_wm", "0", 0);
+	char * bypass_wm = config_alloc_get_default(NVS_TYPE_STR, "bypass_wm", "0", 0);
 	if(bypass_wm==NULL)
 	{
 		ESP_LOGE(TAG, "Unable to retrieve the Wifi Manager bypass flag");
@@ -257,6 +265,7 @@ void app_main()
 	}
 
 	ESP_LOGD(TAG,"Configuring Green led");
+
 	led_config(LED_GREEN, LED_GREEN_GPIO, 0);
 	ESP_LOGD(TAG,"Configuring Red led");
 	led_config(LED_RED, LED_RED_GPIO, 0);

+ 74 - 32
main/nvs_utilities.c

@@ -1,3 +1,4 @@
+//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
 #include "nvs_utilities.h"
 
 #include <stdio.h>
@@ -13,10 +14,78 @@
 #include "esp_vfs_fat.h"
 #include "nvs.h"
 #include "nvs_flash.h"
+#include "nvs_utilities.h"
 
 const char current_namespace[] = "config";
 const char settings_partition[] = "settings";
-static const char * TAG = "platform_esp32";
+static const char * TAG = "nvs_utilities";
+
+void initialize_nvs() {
+	ESP_LOGI(TAG,  "Initializing flash nvs ");
+	esp_err_t err = nvs_flash_init();
+	if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+		ESP_LOGW(TAG,  "%s. Erasing nvs flash", esp_err_to_name(err));
+		ESP_ERROR_CHECK(nvs_flash_erase());
+		err = nvs_flash_init();
+	}
+	if(err != ESP_OK){
+		ESP_LOGE(TAG,  "nvs_flash_init failed. %s.", esp_err_to_name(err));
+	}
+	ESP_ERROR_CHECK(err);
+	ESP_LOGI(TAG,  "Initializing nvs partition %s",settings_partition);
+	err = nvs_flash_init_partition(settings_partition);
+	if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+		ESP_LOGW(TAG,  "%s. Erasing nvs on partition %s",esp_err_to_name(err),settings_partition);
+		ESP_ERROR_CHECK(nvs_flash_erase_partition(settings_partition));
+		err = nvs_flash_init_partition(settings_partition);
+	}
+	if(err!=ESP_OK){
+		ESP_LOGE(TAG,  "nvs_flash_init_partition failed. %s",esp_err_to_name(err));
+	}
+	ESP_ERROR_CHECK(err);
+	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);
+
+	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;
+			}
+			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;
+}
+
 
 esp_err_t store_nvs_value(nvs_type_t type, const char *key, void * data) {
 	if (type == NVS_TYPE_BLOB)
@@ -61,39 +130,12 @@ esp_err_t store_nvs_value_len(nvs_type_t type, const char *key, void * data,
 	if (err == ESP_OK) {
 		err = nvs_commit(nvs);
 		if (err == ESP_OK) {
-			ESP_LOGI(TAG, "Value stored under key '%s'", key);
+			ESP_LOGI(TAG,   "Value stored under key '%s'", key);
 		}
 	}
 	nvs_close(nvs);
 	return err;
 }
-void nvs_value_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size) {
-	free(get_nvs_value_alloc_default(type, key, default_value, blob_size));
-}
-void * get_nvs_value_alloc_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size) {
-	void * current_value = get_nvs_value_alloc(type, key);
-	if(current_value == NULL && default_value != NULL){
-		if(type == NVS_TYPE_BLOB && blob_size == 0){
-			ESP_LOGE(TAG,"Unable to store default value for BLOB object'  blob size was not specified");
-			return NULL;
-		}
-		else {
-			esp_err_t err = store_nvs_value_len(type, key, default_value, blob_size);
-			if(err!=ESP_OK){
-				ESP_LOGE(TAG,"Unable to store default nvs value for key %s. Error: %s", key,esp_err_to_name(err));
-				return NULL;
-			}
-			else{
-				ESP_LOGI(TAG,"Stored new default value for key %s", key);
-			}
-		}
-	}
-	if(current_value == NULL){
-		current_value = get_nvs_value_alloc(type, key);
-	}
-	return current_value;
-}
-
 void * get_nvs_value_alloc(nvs_type_t type, const char *key) {
 	nvs_handle nvs;
 	esp_err_t err;
@@ -101,7 +143,7 @@ void * get_nvs_value_alloc(nvs_type_t type, const char *key) {
 
 	err = nvs_open_from_partition(settings_partition, current_namespace, NVS_READONLY, &nvs);
 	if (err != ESP_OK) {
-		ESP_LOGE(TAG,"Could not open the nvs storage.");
+		ESP_LOGE(TAG,  "Could not open the nvs storage.");
 		return NULL;
 	}
 
@@ -145,7 +187,7 @@ void * get_nvs_value_alloc(nvs_type_t type, const char *key) {
 		}
 	}
 	if(err!=ESP_OK){
-		ESP_LOGD(TAG,"Value not found for key %s",key);
+		ESP_LOGD(TAG,  "Value not found for key %s",key);
 		if(value!=NULL)
 			free(value);
 		value=NULL;
@@ -215,7 +257,7 @@ esp_err_t erase_nvs(const char *key)
         if (err == ESP_OK) {
             err = nvs_commit(nvs);
             if (err == ESP_OK) {
-                ESP_LOGI(TAG, "Value with key '%s' erased", key);
+                ESP_LOGI(TAG,   "Value with key '%s' erased", key);
             }
         }
         nvs_close(nvs);

+ 4 - 2
main/nvs_utilities.h

@@ -2,18 +2,20 @@
 #include "esp_err.h"
 #include "nvs.h"
 #include "trace.h"
+#include "config.h"
 #ifdef __cplusplus
 extern "C" {
 #endif
 extern const char current_namespace[];
 extern const char settings_partition[];
+
+#define NUM_BUFFER_LEN 101
+void initialize_nvs();
 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(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);
 esp_err_t erase_nvs(const char *key);
-void * get_nvs_value_alloc_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
-void nvs_value_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
 #ifdef __cplusplus
 }
 #endif

Some files were not shown because too many files changed in this diff