Sebastien L пре 2 недеља
родитељ
комит
73bd096f37
100 измењених фајлова са 7606 додато и 5169 уклоњено
  1. 14 0
      .clang-format
  2. 0 32
      .cproject
  3. 0 21
      .devcontainer/devcontainer.json
  4. 7 0
      .gitignore
  5. 0 20
      .project
  6. 106 84
      CMakeLists.txt
  7. 57 163
      Dockerfile
  8. 0 22
      Makefile
  9. 0 3
      Makefile_std.mk
  10. 0 47
      buildFirmware.sh
  11. 0 113
      build_flash_cmd.sh
  12. 1 1
      components/display/CMakeLists.txt
  13. 3 3
      components/display/ILI9341.c
  14. 2 2
      components/display/SH1106.c
  15. 2 2
      components/display/SSD1306.c
  16. 2 2
      components/display/SSD1322.c
  17. 3 3
      components/display/SSD132x.c
  18. 2 2
      components/display/SSD1351.c
  19. 25 24
      components/display/SSD1675.c
  20. 3 3
      components/display/ST77xx.c
  21. 3 3
      components/display/core/gds.c
  22. 3 3
      components/display/core/gds.h
  23. 3 2
      components/display/core/gds_font.c
  24. 18 29
      components/display/display.c
  25. 53 60
      components/driver_bt/bt_app_core.c
  26. 21 16
      components/driver_bt/bt_app_core.h
  27. 5 7
      components/driver_bt/bt_app_sink.c
  28. 71 70
      components/driver_bt/bt_app_source.c
  29. 1 1
      components/led_strip/CMakeLists.txt
  30. 10 9
      components/led_strip/led_vu.c
  31. 1 1
      components/metrics/CMakeLists.txt
  32. 1 2
      components/metrics/Metrics.cpp
  33. 4 2
      components/platform_config/CMakeLists.txt
  34. 266 0
      components/platform_config/Config.cpp
  35. 49 0
      components/platform_config/Config.h
  36. 0 480
      components/platform_config/Configurator.cpp
  37. 0 107
      components/platform_config/Configurator.h
  38. 45 0
      components/platform_config/Locking.cpp
  39. 71 0
      components/platform_config/Locking.h
  40. 272 0
      components/platform_config/PBW.cpp
  41. 331 0
      components/platform_config/PBW.h
  42. 845 0
      components/platform_config/WifiList.cpp
  43. 185 0
      components/platform_config/WifiList.h
  44. 20 0
      components/platform_config/test/CMakeLists.txt
  45. 29 0
      components/platform_config/test/DAC_test_extra.proto
  46. 30 0
      components/platform_config/test/DAC_test_extra2.proto
  47. 956 0
      components/platform_config/test/test_platform_config.cpp
  48. 1 1
      components/platform_console/CMakeLists.txt
  49. 1 1
      components/platform_console/app_squeezelite/CMakeLists.txt
  50. 98 115
      components/platform_console/app_squeezelite/cmd_squeezelite.c
  51. 463 415
      components/platform_console/cmd_config.c
  52. 48 49
      components/platform_console/cmd_i2ctools.c
  53. 460 347
      components/platform_console/cmd_system.c
  54. 1 3
      components/platform_console/cmd_system.h
  55. 1 1
      components/platform_console/cmd_wifi.c
  56. 319 381
      components/platform_console/platform_console.c
  57. 1 0
      components/platform_console/platform_console.h
  58. 1 1
      components/platform_console/test/test_system.c
  59. 1 1
      components/raop/CMakeLists.txt
  60. 1 1
      components/raop/raop_sink.c
  61. 2 1
      components/services/CMakeLists.txt
  62. 27 29
      components/services/accessors.c
  63. 14 11
      components/services/accessors.h
  64. 419 487
      components/services/audio_controls.c
  65. 16 15
      components/services/audio_controls.h
  66. 4 4
      components/services/battery.c
  67. 348 338
      components/services/buttons.c
  68. 0 3
      components/services/globdefs.h
  69. 15 3
      components/services/gpio_exp.c
  70. 4 0
      components/services/gpio_exp.h
  71. 33 30
      components/services/led.c
  72. 2 2
      components/services/led.h
  73. 263 260
      components/services/messaging.c
  74. 7 9
      components/services/monitor.c
  75. 8 2
      components/services/monitor.h
  76. 281 158
      components/services/services.c
  77. 2 2
      components/services/services.h
  78. 44 0
      components/services/system_time.c
  79. 2 0
      components/services/system_time.h
  80. 8 7
      components/spotify/Shim.cpp
  81. 3 3
      components/spotify/cspot/bell/external/nanopb/generator/nanopb_generator.py
  82. 32 20
      components/spotify/cspot/bell/external/nanopb/pb_decode.c
  83. 2 1
      components/spotify/cspot_sink.c
  84. 0 10
      components/squeezelite-ota/component.mk
  85. 0 41
      components/squeezelite-ota/github.pem
  86. 20 3
      components/squeezelite-ota/squeezelite-ota.c
  87. 4 1
      components/squeezelite-ota/squeezelite-ota.h
  88. 2 2
      components/squeezelite/CMakeLists.txt
  89. 6 6
      components/squeezelite/ac101/ac101.c
  90. 5 4
      components/squeezelite/adac.h
  91. 10 11
      components/squeezelite/adac_core.c
  92. 8 7
      components/squeezelite/controls.c
  93. 430 432
      components/squeezelite/cs4265/cs4265.c
  94. 396 383
      components/squeezelite/decode_external.c
  95. 6 3
      components/squeezelite/embedded.c
  96. 10 11
      components/squeezelite/equalizer.c
  97. 87 64
      components/squeezelite/esp32_main.c
  98. 145 136
      components/squeezelite/external/dac_external.c
  99. 6 4
      components/squeezelite/output_bt.c
  100. 19 11
      components/squeezelite/output_embedded.c

+ 14 - 0
.clang-format

@@ -0,0 +1,14 @@
+{
+    "BasedOnStyle": "LLVM",
+    "UseTab": "Never",
+    "IndentWidth": 4,
+    "TabWidth": 4,
+    "ColumnLimit": 150,
+    "PointerAlignment": "Left",
+    "AlignAfterOpenBracket": "DontAlign",
+    "BreakBeforeBraces": "Attach",
+    "AllowShortIfStatementsOnASingleLine": true,
+    "AllowShortFunctionsOnASingleLine": "All",
+    "IndentCaseLabels": false,
+    "SpacesBeforeTrailingComments": 1
+}

+ 0 - 32
.cproject

@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
-    	
-    <storageModule moduleId="org.eclipse.cdt.core.settings">
-        		
-        <cconfiguration id="org.eclipse.cdt.core.default.config.959280881">
-            			
-            <storageModule buildSystemId="org.eclipse.cdt.core.defaultConfigDataProvider" id="org.eclipse.cdt.core.default.config.959280881" moduleId="org.eclipse.cdt.core.settings" name="Configuration">
-                				
-                <externalSettings/>
-                				
-                <extensions/>
-                			
-            </storageModule>
-            			
-            <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-            		
-        </cconfiguration>
-        	
-    </storageModule>
-    	
-    <storageModule moduleId="org.eclipse.cdt.core.pathentry">
-        		
-        <pathentry excluding="**/CMakeFiles/**" kind="out" path="build"/>
-        	
-    </storageModule>
-    	
-    <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
-    	
-    <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
-    
-</cproject>

+ 0 - 21
.devcontainer/devcontainer.json

@@ -1,21 +0,0 @@
-{
-    "name": "ESP-IDF Development",
-    "image": "espressif/idf:v4.4.6",
-    "workspaceFolder": "/project",
-    "workspaceMount": "source=${localWorkspaceFolder},target=/project,type=bind",
-    "customizations": {
-        "vscode": {
-            "settings": {
-                "terminal.integrated.shell.linux": "/bin/bash"
-            },
-            "extensions": [
-                "ms-vscode.cpptools",
-                "usernamehw.errorlens",
-                "espressif.esp-idf-extension"  // ESP-IDF extension
-            ]
-        }
-    },
-    "runArgs": [
-        "--privileged" // If needed for accessing certain hardware resources
-    ]
-}

+ 7 - 0
.gitignore

@@ -18,3 +18,10 @@ components/wifi-manager/UML-State-Machine-in-C
 envfile.txt
 envfile.txt
 artifacts
 artifacts
 web-installer
 web-installer
+esp-idf-vscode-generated.gdb
+.vscode/.espidf.peripherals.state.json
+.vscode/squeezelite-esp32.code-workspace
+debug.log
+components/driver_bt/bt_app_source - Copy.c.old
+components/driver_bt/bt_app_source - Copy.c.old
+components/driver_bt/bt_app_source - Copy.c.old

+ 0 - 20
.project

@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>squeezelite-esp32-idfv4</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.cdt.core.cBuilder</name>
-			<triggers>clean,full,incremental,</triggers>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.cdt.core.cnature</nature>
-		<nature>org.eclipse.cdt.core.ccnature</nature>
-		<nature>com.espressif.idf.core.idfNature</nature>
-	</natures>
-</projectDescription>

+ 106 - 84
CMakeLists.txt

@@ -10,7 +10,12 @@ endif()
 
 
 # State machine hierarchy enabled and logging enabled
 # State machine hierarchy enabled and logging enabled
 add_definitions(-DSTATE_MACHINE_LOGGER=1)
 add_definitions(-DSTATE_MACHINE_LOGGER=1)
-add_definitions(-DHIERARCHICAL_STATES=1)
+#add_definitions(-DHIERARCHICAL_STATES=1)
+
+# Align the nanopb library options across the entire project
+# otherwise this results in nasty structures misalignment 
+# when implementing callbacks
+add_definitions(-DPB_ENABLE_MALLOC -DPB_FIELD_32BIT)
 
 
 # Uncomment line below to get memory usage trace details
 # Uncomment line below to get memory usage trace details
 # add_definitions(-DENABLE_MEMTRACE=1)
 # add_definitions(-DENABLE_MEMTRACE=1)
@@ -18,9 +23,11 @@ add_definitions(-DHIERARCHICAL_STATES=1)
 #add_definitions(-DNETWORK_ETHERNET_LOG_LEVEL=ESP_LOG_DEBUG)
 #add_definitions(-DNETWORK_ETHERNET_LOG_LEVEL=ESP_LOG_DEBUG)
 #uncomment line below to get network status debug logs
 #uncomment line below to get network status debug logs
 #add_definitions(-DNETWORK_STATUS_LOG_LEVEL=ESP_LOG_DEBUG)
 #add_definitions(-DNETWORK_STATUS_LOG_LEVEL=ESP_LOG_DEBUG)
-#add_definitions(-DNETWORK_HANDLERS_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_WIFI_LOG_LEVEL=ESP_LOG_DEBUG)
-#add_definitions(-DNETWORK_MANAGER_LOG_LEVEL=ESP_LOG_DEBUG)
+add_definitions(-DNETWORK_MANAGER_LOG_LEVEL=ESP_LOG_DEBUG)
+
 #add_definitions(-DNETWORK_HTTP_SERVER_LOG_LEVEL=ESP_LOG_DEBUG)
 #add_definitions(-DNETWORK_HTTP_SERVER_LOG_LEVEL=ESP_LOG_DEBUG)
 
 
 # utility to build sizes
 # utility to build sizes
@@ -53,7 +60,6 @@ endfunction()
 set(EXTRA_COMPONENT_DIRS components/platform_console/app_recovery components/platform_console/app_squeezelite  )
 set(EXTRA_COMPONENT_DIRS components/platform_console/app_recovery components/platform_console/app_squeezelite  )
 
 
 project(recovery)
 project(recovery)
-spiffs_create_partition_image(spiffs  spiffs  FLASH_IN_PROJECT DEPENDS generate_spiffs_bin )
 
 
 # we need own "esp_app_desc" to take precedence
 # we need own "esp_app_desc" to take precedence
 add_custom_command(
 add_custom_command(
@@ -106,7 +112,7 @@ esptool_py_flash_target_image(flash squeezelite "${otaapp_offset}" "${BUILD_DIR}
 add_custom_target(_jtag_scripts  ALL
 add_custom_target(_jtag_scripts  ALL
 					BYPRODUCTS "flash_dbg_project_args"  
 					BYPRODUCTS "flash_dbg_project_args"  
 					POST_BUILD
 					POST_BUILD
-					COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/generate_debug_scripts.cmake"
+					COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_debug_scripts.cmake"
 )
 )
 
 
 if(CMAKE_HOST_UNIX)
 if(CMAKE_HOST_UNIX)
@@ -136,78 +142,95 @@ endif()
 
 
 # Include the protocol_buffers.cmake file
 # Include the protocol_buffers.cmake file
 add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/protobuf ${CMAKE_CURRENT_BINARY_DIR}/protobuf)
 add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/protobuf ${CMAKE_CURRENT_BINARY_DIR}/protobuf)
+add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/spiffs_src ${CMAKE_CURRENT_BINARY_DIR}/spiffs)
+
 
 
 
 
 # ======================= DEBUG FLAGS ============================
 # ======================= DEBUG FLAGS ============================
 
 
+# target_compile_definitions(__idf_wifi-manager 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_event PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
+# target_compile_definitions(__idf_esp_netif PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_ERROR)
+# target_compile_definitions(__idf_mdns PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_tcpip_adapter PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_tcp_transport PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+
+# target_compile_definitions(__idf_app_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_app_trace PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_app_update PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_asio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_audio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_bootloader_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_display PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_driver_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_platform_config PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_squeezelite-ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_telnet PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_main PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+
+
+
+
+
+
+
+
+
+
+
+
+
 #target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
 #target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
+# target_compile_definitions(__idf_services PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 
 
-target_compile_definitions(__idf_services 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_DEBUG)
-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_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)
- 
-target_compile_definitions(__idf_freertos PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_ERROR)
-target_compile_definitions(__idf_mdns PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_tcpip_adapter PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_tcp_transport PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-
-target_compile_definitions(__idf_app_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_app_trace PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_app_update PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_asio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_audio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_bootloader_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-
-target_compile_definitions(__idf_cbor PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_cmock PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_coap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_console PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_cxx PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_display PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_driver_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_efuse PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp-dsp PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp-tls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp32 PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_espcoredump PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_adc_cal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_common PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_gdbstub PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_hid PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_https_ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_http_client PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_http_server PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_hw_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_ipc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_local_ctrl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_pm PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_ringbuf PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_rom PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_serial_slave_link PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_system PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_esp_timer PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_expat PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_fatfs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_freemodbus PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_hal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_heap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_jsmn PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_json PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+
+
+
+# target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
+
+
+
+# target_compile_definitions(__idf_freertos PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+
+
+# target_compile_definitions(__idf_cbor PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_cmock PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_coap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_cxx PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_adc_cal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_https_ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_http_client PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_http_server PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_hw_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_ipc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_local_ctrl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_pm PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_ringbuf PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_rom PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_serial_slave_link PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_system PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_esp_timer PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_expat PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_fatfs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_freemodbus PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_hal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_heap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_jsmn PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_json PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_libsodium PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_libsodium PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_log PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_log PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_lwip PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_lwip PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_main PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+
+# target_compile_definitions(__idf_mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(mbedcrypto PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(mbedcrypto PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(mbedx509 PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(mbedx509 PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
@@ -217,24 +240,23 @@ target_compile_definitions(__idf_mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG
 # target_compile_definitions(__idf_nvs_flash PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_nvs_flash PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_openssl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # 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_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_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_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)
-target_compile_definitions(__idf_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_squeezelite-ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_telnet PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_tools PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_ulp PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_unity 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_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)
+
+# target_compile_definitions(__idf_tools PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_ulp PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_unity PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_vfs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_vfs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_wear_levelling PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_wifi_provisioning PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_wear_levelling PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
+# target_compile_definitions(__idf_wifi_provisioning PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_wpa_supplicant PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_wpa_supplicant PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_xtensa PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
 # target_compile_definitions(__idf_xtensa PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)

+ 57 - 163
Dockerfile

@@ -1,188 +1,82 @@
-FROM ubuntu:20.04
+FROM espressif/idf:v4.4.6
+
+# Install additional dependencies
+RUN apt-get update && apt-get install -y \
+  python3-protobuf 
+
+RUN DEBIAN_FRONTEND=noninteractive TZ=America/Toronto apt-get -y install tzdata
+  # Add any other dependencies you need
+
+  # To build the image for a branch or a tag of IDF, pass --build-arg IDF_CLONE_BRANCH_OR_TAG=name.
+  # To build the image with a specific commit ID of IDF, pass --build-arg IDF_CHECKOUT_REF=commit-id.
+  # It is possibe to combine both, e.g.:
+  #   IDF_CLONE_BRANCH_OR_TAG=release/vX.Y
+  #   IDF_CHECKOUT_REF=<some commit on release/vX.Y branch>.
+  # Docker build for release 4.3.5 as of 2023/05/18
+  # docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv446 
+  # Updating the docker image in the repository
+  # docker push sle118/squeezelite-esp32-idfv446
+  # or to do both:
+  # docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv446 && docker push sle118/squeezelite-esp32-idfv446
+  # docker build . -t sle118/squeezelite-esp32-idfv446 && docker push sle118/squeezelite-esp32-idfv446
+  #docker run --isolation=process --device="class/86E0D1E0-8089-11D0-9CE4-08003E301F73" mcr.microsoft.com/windows/servercore:1809
+  # (windows) To run the image interactive : 
+  # docker run --rm -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv446
+  # (windows powershell)
+  # docker run --rm -v ${PWD}:/project -w /project -it sle118/squeezelite-esp32-idfv446
+  # (linux) To run the image interactive :
+  # docker run --rm -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv446
+  # to build the web app inside of the interactive session
+  # pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
+  #
+  # to run the docker with netwotrk port published on the host:
+  # docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv446
 
 
 
 
-ARG DEBIAN_FRONTEND=noninteractive
-ENV GCC_TOOLS_BASE=/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-
-# To build the image for a branch or a tag of IDF, pass --build-arg IDF_CLONE_BRANCH_OR_TAG=name.
-# To build the image with a specific commit ID of IDF, pass --build-arg IDF_CHECKOUT_REF=commit-id.
-# It is possibe to combine both, e.g.:
-#   IDF_CLONE_BRANCH_OR_TAG=release/vX.Y
-#   IDF_CHECKOUT_REF=<some commit on release/vX.Y branch>.
-# Docker build for release 4.3.5 as of 2023/05/18
-# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv435 
-# Updating the docker image in the repository
-# docker push sle118/squeezelite-esp32-idfv435
-# or to do both:
-# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv435 && docker push sle118/squeezelite-esp32-idfv435
-#
-# (windows) To run the image interactive : 
-# docker run --rm -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
-# (windows powershell)
-# docker run --rm -v ${PWD}:/project -w /project -it sle118/squeezelite-esp32-idfv435
-# (linux) To run the image interactive :
-# docker run --rm -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv435
-# to build the web app inside of the interactive session
-# pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
-#
-# to run the docker with netwotrk port published on the host:
-# docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
-
-ARG IDF_CLONE_URL=https://github.com/espressif/esp-idf.git
-ARG IDF_CLONE_BRANCH_OR_TAG=master
-ARG IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d
-
-ENV IDF_PATH=/opt/esp/idf
-ENV IDF_TOOLS_PATH=/opt/esp
-
-# We need libpython2.7 due to GDB tools
-# we also need npm 8 for the webapp to work
-RUN : \
-  && apt-get update \
-  && apt-get install -y \
-    apt-utils \
-    build-essential \
-    bison \
-    ca-certificates \
-    ccache \
-    check \
-    curl \
-    flex \
-    git \
-    git-lfs \    
-    gperf \
-    lcov \
-    libbsd-dev \    
-    libpython3.8 \
-    libffi-dev \
-    libncurses-dev \
-    libusb-1.0-0-dev \
-    make \
-    ninja-build \
-    python3.8 \
-    python3-pip \
-    python3-venv \
-    ruby \
-    unzip \
-    wget \
-    xz-utils \
-    zip \
-   	npm \
-  	nodejs \
-  && apt-get autoremove -y \
-  && rm -rf /var/lib/apt/lists/* \
-  && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \
-  && python -m pip install --upgrade \
-    pip \
-    virtualenv \
-  && cd /opt \  
-  && git clone https://github.com/HBehrens/puncover.git \
-  && cd puncover \
-  && python setup.py -q install \
-  && echo IDF_CHECKOUT_REF=$IDF_CHECKOUT_REF IDF_CLONE_BRANCH_OR_TAG=$IDF_CLONE_BRANCH_OR_TAG \
-  && git clone --recursive \
-      ${IDF_CLONE_BRANCH_OR_TAG:+-b $IDF_CLONE_BRANCH_OR_TAG} \
-      $IDF_CLONE_URL $IDF_PATH \
-	&& if [ -n "$IDF_CHECKOUT_REF" ]; then \
-      cd $IDF_PATH \
-  &&  git checkout $IDF_CHECKOUT_REF \
-  &&  git submodule update --init --recursive; \
-    fi \
-  && update-ca-certificates --fresh \
-  && $IDF_PATH/tools/idf_tools.py --non-interactive install required \
-  && $IDF_PATH/tools/idf_tools.py --non-interactive install cmake \
-  && $IDF_PATH/tools/idf_tools.py --non-interactive install-python-env \
-  && :
 RUN : \
 RUN : \
   echo Installing pygit2  ******************************************************** \
   echo Installing pygit2  ******************************************************** \
-  && . /opt/esp/python_env/idf4.3_py3.8_env/bin/activate \
-  && ln -sf /opt/esp/python_env/idf4.3_py3.8_env/bin/python  /usr/local/bin/python \
+  && . /opt/esp/python_env/idf4.4_py3.8_env/bin/activate \
+  && ln -sf /opt/esp/python_env/idf4.4_py3.8_env/bin/python  /usr/local/bin/python \
   && pip install pygit2 requests \
   && pip install pygit2 requests \
   && pip show pygit2 \ 
   && pip show pygit2 \ 
   && python --version \  
   && python --version \  
   && pip --version \
   && pip --version \
   && pip install protobuf  grpcio-tools \
   && pip install protobuf  grpcio-tools \
   && rm -rf $IDF_TOOLS_PATH/dist \
   && rm -rf $IDF_TOOLS_PATH/dist \
+  && apt-get install -y  nodejs \
   && :
   && :
 
 
-COPY docker/patches $IDF_PATH
+# COPY docker/patches $IDF_PATH
 
 
 #set idf environment variabies
 #set idf environment variabies
-ENV PATH /opt/esp/idf/components/esptool_py/esptool:/opt/esp/idf/components/espcoredump:/opt/esp/idf/components/partition_table:/opt/esp/idf/components/app_update:/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin:/opt/esp/tools/xtensa-esp32s2-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32s2-elf/bin:/opt/esp/tools/xtensa-esp32s3-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32s3-elf/bin:/opt/esp/tools/riscv32-esp-elf/esp-2021r2-patch3-8.4.0/riscv32-esp-elf/bin:/opt/esp/tools/esp32ulp-elf/2.28.51-esp-20191205/esp32ulp-elf-binutils/bin:/opt/esp/tools/esp32s2ulp-elf/2.28.51-esp-20191205/esp32s2ulp-elf-binutils/bin:/opt/esp/tools/cmake/3.16.4/bin:/opt/esp/tools/openocd-esp32/v0.11.0-esp32-20220706/openocd-esp32/bin:/opt/esp/python_env/idf4.3_py3.8_env/bin:/opt/esp/idf/tools:$PATH
+ENV PATH /opt/esp/idf/components/esptool_py/esptool:/opt/esp/idf/components/espcoredump:/opt/esp/idf/components/partition_table:/opt/esp/idf/components/app_update:/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin:/opt/esp/tools/xtensa-esp32s2-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32s2-elf/bin:/opt/esp/tools/xtensa-esp32s3-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32s3-elf/bin:/opt/esp/tools/riscv32-esp-elf/esp-2021r2-patch3-8.4.0/riscv32-esp-elf/bin:/opt/esp/tools/esp32ulp-elf/2.28.51-esp-20191205/esp32ulp-elf-binutils/bin:/opt/esp/tools/esp32s2ulp-elf/2.28.51-esp-20191205/esp32s2ulp-elf-binutils/bin:/opt/esp/tools/cmake/3.16.4/bin:/opt/esp/tools/openocd-esp32/v0.11.0-esp32-20220706/openocd-esp32/bin:/opt/esp/python_env/idf4.4_py3.8_env/bin:/opt/esp/idf/tools:$PATH
 ENV GCC_TOOLS_BASE="/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-"
 ENV GCC_TOOLS_BASE="/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-"
-ENV IDF_PATH="/opt/esp/idf"
-ENV IDF_PYTHON_ENV_PATH="/opt/esp/python_env/idf4.3_py3.8_env"
-ENV IDF_TOOLS_EXPORT_CMD="/opt/esp/idf/export.sh"
-ENV IDF_TOOLS_INSTALL_CMD="/opt/esp/idf/install.sh"
-ENV IDF_TOOLS_PATH="/opt/esp"
-ENV NODE_PATH="/v8/lib/node_modules"
-ENV NODE_VERSION="8"
+ENV IDF_PYTHON_ENV_PATH="/opt/esp/python_env/idf4.4_py3.8_env"
 ENV OPENOCD_SCRIPTS="/opt/esp/tools/openocd-esp32/v0.10.0-esp32-20211111/openocd-esp32/share/openocd/scripts"
 ENV OPENOCD_SCRIPTS="/opt/esp/tools/openocd-esp32/v0.10.0-esp32-20211111/openocd-esp32/share/openocd/scripts"
-# Ccache is installed, enable it by default
-
-# The constraint file has been downloaded and the right Python package versions installed. No need to check and
-# download this at every invocation of the container.
-ENV IDF_PYTHON_CHECK_CONSTRAINTS=no
-
-# Ccache is installed, enable it by default
-ENV IDF_CCACHE_ENABLE=1
-
-# Install QEMU runtime dependencies
-RUN : \
-  && apt-get update && apt-get install -y -q \
-    libglib2.0-0 \
-    libpixman-1-0 \
-  && rm -rf /var/lib/apt/lists/* \
-  && :
-
-# Install QEMU
-ARG QEMU_VER=esp-develop-20220919
-ARG QEMU_DIST=qemu-${QEMU_VER}.tar.bz2
-ARG QEMU_SHA256=f6565d3f0d1e463a63a7f81aec94cce62df662bd42fc7606de4b4418ed55f870
-RUN : \
-  && wget --no-verbose https://github.com/espressif/qemu/releases/download/${QEMU_VER}/${QEMU_DIST} \
-  && echo "${QEMU_SHA256} *${QEMU_DIST}" | sha256sum --check --strict - \
-  && tar -xf ${QEMU_DIST} -C /opt \
-  && rm ${QEMU_DIST} \
-  && :
 
 
 COPY docker/entrypoint.sh /opt/esp/entrypoint.sh
 COPY docker/entrypoint.sh /opt/esp/entrypoint.sh
 COPY components/wifi-manager/webapp/package.json /opt
 COPY components/wifi-manager/webapp/package.json /opt
-
-ENV NODE_VERSION 8
-
+ENV NODE_PATH="/v8/lib/node_modules"
+ENV NODE_VERSION="8"
+ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
+ENV PATH $IDF_PYTHON_ENV_PATH:$NVM_DIR/v$NODE_VERSION/bin:$PATH
 SHELL ["/bin/bash", "--login", "-c"]
 SHELL ["/bin/bash", "--login", "-c"]
-# Install nvm with node and npm
-# RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash \
-#     && export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" \
-#     && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" \
-#     && nvm install $NODE_VERSION \
-#     && nvm alias default $NODE_VERSION \
-#     && nvm use default \
-#     && echo installing nodejs version 16  \
-#     && curl -sL https://deb.nodesource.com/setup_16.x | bash - \
-#     && echo installing node modules  \
-#     && cd /opt \
-#     && nvm use default \
-#     && npm install -g \  
-#     && :    
-
-RUN : \
-  && curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \
-  && apt-get install -y nodejs jq \
-  && echo installing dev node modules globally \
-  && cd /opt \
-  && cat ./package.json | jq '.devDependencies | keys[] as $k | "\($k)@\(.[$k])"' | xargs -t npm install --global \
-  && echo installing npm global packages \
-  && npm i -g npm \
-  && node --version \
-  && npm install -g \  
-  && :      
-RUN : \
-  && npm install -g html-webpack-plugin 
 
 
+# Install NVM, Node.js, and global NPM packages
+RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash \
+    && export NVM_DIR="$HOME/.nvm" \
+    && . "$NVM_DIR/nvm.sh" \
+    && nvm install 16 \
+    && nvm alias default 16 \
+    && nvm use default \
+    && echo "Node.js version:" && node --version \
+    && echo "NPM version:" && npm --version \
+    && echo "Installing global NPM packages..." \
+    && cd /opt \
+    && cat ./package.json | jq '.devDependencies | keys[] as $k | "\($k)@\(.[$k])"' | xargs -t npm install --global \
+    && npm install -g html-webpack-plugin \
+    && :
 
 
-ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
-ENV PATH $IDF_PYTHON_ENV_PATH:$NVM_DIR/v$NODE_VERSION/bin:$PATH
 COPY ./docker/build_tools.py /usr/sbin/build_tools.py
 COPY ./docker/build_tools.py /usr/sbin/build_tools.py
 RUN : \
 RUN : \
   && echo Changing permissions ********************************************************  \
   && echo Changing permissions ********************************************************  \

+ 0 - 22
Makefile

@@ -1,22 +0,0 @@
-# build system (Jenkins) should pass the following
-#export PROJECT_BUILD_NAME="${build_version_prefix}${BUILD_NUMBER}"
-#export PROJECT_BUILD_TARGET="${config_target}"
-
-#PROJECT_BUILD_NAME?=local
-#PROJECT_CONFIG_TARGET?=custom
-#PROJECT_NAME?=squeezelite.$(PROJECT_CONFIG_TARGET)
-#PROJECT_VER?="$(PROJECT_BUILD_NAME)-$(PROJECT_CONFIG_TARGET)"
-
-
-
-#recovery: PROJECT_NAME:=recovery.$(PROJECT_CONFIG_TARGET)
-#recovery: EXTRA_CPPFLAGS+=-DRECOVERY_APPLICATION=1
-
-PROJECT_NAME?=squeezelite
-EXTRA_CPPFLAGS+=  -I$(PROJECT_PATH)/main 
-
-#/-Wno-error=maybe-uninitialized 
-include $(IDF_PATH)/make/project.mk 
-
-# for future gcc version, this could be needed: CPPFLAGS+= -Wno-error=format-overflow -Wno-error=stringop-truncation
-

+ 0 - 3
Makefile_std.mk

@@ -1,3 +0,0 @@
-PROJECT_NAME?= squeezelite
-EXTRA_COMPONENT_DIRS := $(PROJECT_PATH)/esp-dsp
-include $(IDF_PATH)/make/project.mk

+ 0 - 47
buildFirmware.sh

@@ -1,47 +0,0 @@
-#!/bin/bash
-
-
-
-echo "Build process started"
-echo "Setting up build name and build number"
-if [ -z "${TARGET_BUILD_NAME}" ]
-then
-    export TARGET_BUILD_NAME="I2S-4MFlash"
-    echo "TARGET_BUILD_NAME is not set. Defaulting to ${TARGET_BUILD_NAME}"
-fi
-if [ -z "${BUILD_NUMBER}" ]
-then
-    export BUILD_NUMBER="500"
-    echo "BUILD_NUMBER is not set. Defaulting to ${BUILD_NUMBER}"
-fi
-if [ -z "$DEPTH" ]
-then
-    export DEPTH="16"
-    echo "DEPTH is not set. Defaulting to ${DEPTH}"
-fi
-if [ -z "$tag" ]
-then
-    branch_name="$(git rev-parse --abbrev-ref HEAD)"
-    branch_name="${branch_name//[^a-zA-Z0-9\-~!@_\.]/}"
-    app_name="${TARGET_BUILD_NAME}.${DEPTH}.dev-$(git log --pretty=format:'%h' --max-count=1).${branch_name}" 
-    echo "${app_name}">version.txt
-    echo "app_name is not set. Defaulting to ${app_name}"
-else
-    echo "${tag}" >version.txt
-fi
-
-echo "Copying target sdkconfig"
-cp build-scripts/${TARGET_BUILD_NAME}-sdkconfig.defaults sdkconfig
-echo "Building project"
-idf.py build -DDEPTH=${DEPTH} -DBUILD_NUMBER=${BUILD_NUMBER}-${DEPTH} 
-echo "Generating size report"
-idf.py size-components >build/size_components.txt
-idf.py size-components-squeezelite build/size_components_squeezelite.txt
-if [ -z "${artifact_file_name}" ]
-then
-    echo "No artifact file name set.  Will not generate zip file."
-else
-    echo "Generating build artifact zip file"
-    zip -r build_output.zip build
-    zip build/${artifact_file_name} partitions*.csv components/ build/*.bin build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flash_project_args build/size_*.txt
-fi

+ 0 - 113
build_flash_cmd.sh

@@ -1,113 +0,0 @@
-#!/bin/bash
-echo 
-echo =================================================================
-echo Build flash command
-echo =================================================================
-# Location of partitions.csv relative to this script
-partitionsCsv="../partitions.csv"
-
-# File to output readme instructions to
-outputReadme="./flash_cmd.txt"
-
-# File to output bash script to
-outputBashScript="./writeSequeezeEsp.sh"
-
-# File to output bat script to
-outputBatScript="./writeSequeezeEsp.bat"
-
-# The name of partitions to ignore from partitions.csv
-paritionsToIgnore=(
-  "nvs"
-  "phy_init"
-  "storage"
-  "coredump"
-  "settings"
-)
-
-# Function that maps partition name to actual bin file
-# defaults to "[PARTION_NAME_FROM_CSV].bin"
-function partitionNameToBinFile {
-  if [[ "$1" == "otadata" ]]; then
-    echo "ota_data_initial.bin"
-  elif [[ "$1" == "ota_0" || "$1" == "factory" ]]; then
-    echo "squeezelite.bin"
-  else
-    echo $1.bin
-  fi
-}
-
-# write parameters for esptool.py
-writeParameters="$writeParameters write_flash"
-writeParameters="$writeParameters --flash_mode dio --flash_freq 80m --flash_size detect"
-
-# bootloader.bin and partitions.bin not in partitions.csv so manually add here
-partitionsParameters=" 0x1000 bootloader/bootloader.bin"
-partitionsParameters="$partitionsParameters 0x8000 partitions.bin"
-
-# ==============================================================================
-
-# Loop over partitions.csv and add partition bins and offsets to partitionsParameters
-
-for line in $($IDF_PATH/components/partition_table/gen_esp32part.py --quiet build/partitions.bin | grep '^[^#]')
-do
-  partitionName=$(echo $line | awk -F',' '{printf "%s", $1}' )
-  partitionOffset=$(echo $line |awk -F',' '{printf "%s", $4}' )
-  partitionFile=$(partitionNameToBinFile $partitionName)
-	
-  if [[ " ${paritionsToIgnore[@]} " =~ " ${partitionName} " ]]; then
-	continue
-  fi
-
-  partitionsParameters="$partitionsParameters $partitionOffset $partitionFile"
-  echo "$partitionsParameters"
-  
-done
-
-# Write README Instructions
-if [ ! -f "$outputReadme" ]; then
-  touch $outputReadme
-fi
-
-echo "" >> $outputReadme
-echo "====LINUX====" >> $outputReadme
-echo "To flash sequeezelite run the following script:" >> $outputReadme
-echo "$outputBashScript [PORT_HERE] [BAUD_RATE]" >> $outputReadme
-echo "e.g. $outputBashScript /dev/ttyUSB0 115200" >> $outputReadme
-echo "" >> $outputReadme
-echo "====WINDOWS====" >> $outputReadme
-echo "To flash sequeezelite run the following script:" >> $outputReadme
-echo "$outputBatScript [PORT_HERE] [BAUD_RATE]" >> $outputReadme
-echo "e.g. $outputBatScript COM11 115200" >> $outputReadme
-echo "" >> $outputReadme
-echo "If you don't know how to run the BAT file with arguments then you can" >> $outputReadme
-echo "edit the bat file in Notepad. Open the file up and edit the following:" >> $outputReadme
-echo "Change 'set port=%1' to 'set port=[PORT_HERE]'. E.g. 'set port=COM11'" >> $outputReadme
-echo "Change 'set baud=%2' to 'set baud=[BAUD_RATE]'. E.g. 'set baud=115200'" >> $outputReadme
-echo "" >> $outputReadme
-echo "====MANUAL====" >> $outputReadme
-echo "Python esptool.py --port [PORT_HERE] --baud [BAUD_RATE] $writeParameters $partitionsParameters" >> $outputReadme
-
-# Write Linux BASH File
-if [ ! -f "$outputBashScript" ]; then
-  touch $outputBashScript
-fi
-
-echo "#!/bin/bash" >> $outputBashScript
-echo >> $outputBashScript
-echo "port=\$1" >> $outputBashScript
-echo "baud=\$2" >> $outputBashScript
-linuxFlashCommand="Python esptool.py --port \$port --baud \$baud"
-echo "$linuxFlashCommand $writeParameters $partitionsParameters" >> $outputBashScript
-
-# Write Windows BAT File
-if [ ! -f "$outputBatScript" ]; then
-  touch $outputBatScript
-fi
-
-echo "echo off" >> $outputBatScript
-echo "" >> $outputBatScript
-echo "set port=%1" >> $outputBatScript
-echo "set baud=%2" >> $outputBatScript
-windowsFlashCommand="Python esptool.py --port %port% --baud %baud%"
-echo "$windowsFlashCommand $writeParameters $partitionsParameters" >> $outputBatScript
-

+ 1 - 1
components/display/CMakeLists.txt

@@ -3,7 +3,7 @@ set(TJPGD tjpgd)
 
 
 idf_component_register(SRC_DIRS . core core/ifaces 
 idf_component_register(SRC_DIRS . core core/ifaces 
 						INCLUDE_DIRS . core
 						INCLUDE_DIRS . core
-						REQUIRES platform_config tools esp_common spiffs
+						REQUIRES tools platform_config  esp_common spiffs
 						PRIV_REQUIRES services freertos driver ${TJPGD}
 						PRIV_REQUIRES services freertos driver ${TJPGD}
 						EMBED_FILES note.jpg )		
 						EMBED_FILES note.jpg )		
 
 

+ 3 - 3
components/display/ILI9341.c

@@ -319,12 +319,12 @@ static const struct GDS_Device ILI9341_X = {
 	.Mode = GDS_RGB565, .Depth = 16,
 	.Mode = GDS_RGB565, .Depth = 16,
 };		
 };		
 
 
-struct GDS_Device* ILI9341_Detect(sys_Display * Driver, struct GDS_Device* Device) {
+struct GDS_Device* ILI9341_Detect(sys_display_config * Driver, struct GDS_Device* Device) {
 	uint8_t Model;
 	uint8_t Model;
 	int Depth=16;		// 16bit colordepth
 	int Depth=16;		// 16bit colordepth
 	
 	
-	if(Driver->common.driver == sys_DisplayDriverEnum_ILI9341) Model = ILI9341;
-	else if(Driver->common.driver == sys_DisplayDriverEnum_ILI9341_24) Model = ILI9341_24;
+	if(Driver->common.driver == sys_display_drivers_ILI9341) Model = ILI9341;
+	else if(Driver->common.driver == sys_display_drivers_ILI9341_24) Model = ILI9341_24;
 	else return NULL;
 	else return NULL;
 	
 	
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));

+ 2 - 2
components/display/SH1106.c

@@ -152,9 +152,9 @@ static const struct GDS_Device SH1106 = {
 #endif		
 #endif		
 };	
 };	
 
 
-struct GDS_Device* SH1106_Detect(sys_Display * Driver, struct GDS_Device* Device) {
+struct GDS_Device* SH1106_Detect(sys_display_config * Driver, struct GDS_Device* Device) {
 	// if (!strcasestr(Driver, "SH1106")) return NULL;
 	// if (!strcasestr(Driver, "SH1106")) return NULL;
-	if(Driver->common.driver != sys_DisplayDriverEnum_SH1106) return NULL;
+	if(Driver->common.driver != sys_display_drivers_SH1106) return NULL;
 	
 	
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	*Device = SH1106;
 	*Device = SH1106;

+ 2 - 2
components/display/SSD1306.c

@@ -162,8 +162,8 @@ static const struct GDS_Device SSD1306 = {
 #endif		
 #endif		
 };	
 };	
 
 
-struct GDS_Device* SSD1306_Detect(sys_Display * Driver, struct GDS_Device* Device) {
-	if(Driver->common.driver != sys_DisplayDriverEnum_SSD1306) return NULL;
+struct GDS_Device* SSD1306_Detect(sys_display_config * Driver, struct GDS_Device* Device) {
+	if(Driver->common.driver != sys_display_drivers_SSD1306) return NULL;
 	
 	
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	*Device = SSD1306;	
 	*Device = SSD1306;	

+ 2 - 2
components/display/SSD1322.c

@@ -191,8 +191,8 @@ static const struct GDS_Device SSD1322 = {
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 };	
 };	
 
 
-struct GDS_Device* SSD1322_Detect(sys_Display * Driver, struct GDS_Device* Device) {
-	if(Driver->common.driver != sys_DisplayDriverEnum_SSD1322) return NULL;
+struct GDS_Device* SSD1322_Detect(sys_display_config * Driver, struct GDS_Device* Device) {
+	if(Driver->common.driver != sys_display_drivers_SSD1322) return NULL;
 		
 		
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	
 	

+ 3 - 3
components/display/SSD132x.c

@@ -319,12 +319,12 @@ static const struct GDS_Device SSD132x = {
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 };	
 };	
 
 
-struct GDS_Device* SSD132x_Detect(sys_Display * Driver, struct GDS_Device* Device) {
+struct GDS_Device* SSD132x_Detect(sys_display_config * Driver, struct GDS_Device* Device) {
 	uint8_t Model;
 	uint8_t Model;
 	int Depth;
 	int Depth;
 		
 		
-	if(Driver->common.driver == sys_DisplayDriverEnum_SSD1326) Model = SSD1326;
-	else if(Driver->common.driver == sys_DisplayDriverEnum_SSD1327) Model = SSD1327;
+	if(Driver->common.driver == sys_display_drivers_SSD1326) Model = SSD1326;
+	else if(Driver->common.driver == sys_display_drivers_SSD1327) Model = SSD1327;
 	return NULL;
 	return NULL;
 	
 	
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));

+ 2 - 2
components/display/SSD1351.c

@@ -268,10 +268,10 @@ static const struct GDS_Device SSD1351 = {
 	.Mode = GDS_RGB565, .Depth = 16,
 	.Mode = GDS_RGB565, .Depth = 16,
 };	
 };	
 
 
-struct GDS_Device* SSD1351_Detect(sys_Display * Driver, struct GDS_Device* Device) {
+struct GDS_Device* SSD1351_Detect(sys_display_config * Driver, struct GDS_Device* Device) {
 	int Depth;
 	int Depth;
 	
 	
-	if(Driver->common.driver != sys_DisplayDriverEnum_SSD1351) return NULL;
+	if(Driver->common.driver != sys_display_drivers_SSD1351) return NULL;
 	
 	
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	
 	

+ 25 - 24
components/display/SSD1675.c

@@ -224,30 +224,31 @@ static bool Init( struct GDS_Device* Device ) {
 	Update( Device );
 	Update( Device );
 	
 	
 	return true;
 	return true;
-}	
+}
 
 
 static const struct GDS_Device SSD1675 = {
 static const struct GDS_Device SSD1675 = {
-	.DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow,
-	.DrawPixelFast = _DrawPixel,
-	.Update = Update, .Init = Init,
-	.Mode = GDS_MONO, .Depth = 1,
-	.Alloc = GDS_ALLOC_NONE,
-};	
-
-struct GDS_Device* SSD1675_Detect(sys_Display * Driver, struct GDS_Device* Device) {
-	if(Driver->common.driver != sys_DisplayDriverEnum_SSD1675) return NULL;
-	
-	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
-	*Device = SSD1675;	
-	
-	char *p;
-	struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
-	Private->ReadyPin = -1;
-	if(Driver->common.has_ready && Driver->common.ready.pin >=0){
-		Private->ReadyPin = Driver->common.ready.pin;
-	}
-	
-	ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin);
-	
-	return Device;
+    .DrawBitmapCBR = DrawBitmapCBR,
+    .ClearWindow = ClearWindow,
+    .DrawPixelFast = _DrawPixel,
+    .Update = Update,
+    .Init = Init,
+    .Mode = GDS_MONO,
+    .Depth = 1,
+    .Alloc = GDS_ALLOC_NONE,
+};
+
+struct GDS_Device* SSD1675_Detect(sys_display_config* Driver, struct GDS_Device* Device) {
+    if (Driver->common.driver != sys_display_drivers_SSD1675) return NULL;
+
+    if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
+    *Device = SSD1675;
+    struct PrivateSpace* Private = (struct PrivateSpace*)Device->Private;
+    Private->ReadyPin = -1;
+    if (Driver->common.ready >= 0) {
+        Private->ReadyPin = Driver->common.ready;
+    }
+
+    ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin);
+
+    return Device;
 }
 }

+ 3 - 3
components/display/ST77xx.c

@@ -273,11 +273,11 @@ static const struct GDS_Device ST77xx = {
 	.Mode = GDS_RGB565, .Depth = 16,
 	.Mode = GDS_RGB565, .Depth = 16,
 };		
 };		
 
 
-struct GDS_Device* ST77xx_Detect(sys_Display * Driver, struct GDS_Device* Device) {
+struct GDS_Device* ST77xx_Detect(sys_display_config * Driver, struct GDS_Device* Device) {
 	uint8_t Model;
 	uint8_t Model;
 	int Depth;
 	int Depth;
-	if(Driver->common.driver == sys_DisplayDriverEnum_ST7735)  Model = ST7735;
-	else if(Driver->common.driver == sys_DisplayDriverEnum_ST7789)  Model = ST7789;
+	if(Driver->common.driver == sys_display_drivers_ST7735)  Model = ST7735;
+	else if(Driver->common.driver == sys_display_drivers_ST7789)  Model = ST7789;
 	else return NULL;
 	else return NULL;
 		
 		
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));

+ 3 - 3
components/display/core/gds.c

@@ -15,7 +15,7 @@
 #include "driver/gpio.h"
 #include "driver/gpio.h"
 #include "driver/ledc.h"
 #include "driver/ledc.h"
 #include "esp_log.h"
 #include "esp_log.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "gds.h"
 #include "gds.h"
 #include "gds_private.h"
 #include "gds_private.h"
 
 
@@ -30,8 +30,8 @@ static struct GDS_BacklightPWM PWMConfig;
 
 
 static char TAG[] = "gds";
 static char TAG[] = "gds";
 
 
-struct GDS_Device* GDS_AutoDetect( sys_Display * Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM* PWM ) {
-	if (!Driver->has_common || Driver->common.driver == sys_DisplayDriverEnum_UNSPECIFIED_DRIVER) return NULL;
+struct GDS_Device* GDS_AutoDetect( sys_display_config * Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM* PWM ) {
+	if (!Driver->has_common || Driver->common.driver == sys_display_drivers_UNSPECIFIED) return NULL;
 	if (PWM) PWMConfig = *PWM;
 	if (PWM) PWMConfig = *PWM;
 	
 	
 	for (int i = 0; DetectFunc[i]; i++) {
 	for (int i = 0; DetectFunc[i]; i++) {

+ 3 - 3
components/display/core/gds.h

@@ -3,7 +3,7 @@
 
 
 #include <stdint.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <stdbool.h>
-#include "Configurator.h"
+#include "Config.h"
 
 
 /* NOTE for drivers:
 /* NOTE for drivers:
  The build-in DrawPixel(Fast), DrawCBR and ClearWindow have optimized for 1 bit 
  The build-in DrawPixel(Fast), DrawCBR and ClearWindow have optimized for 1 bit 
@@ -34,9 +34,9 @@ struct GDS_Layout {
 	bool ColorSwap;
 	bool ColorSwap;
 };
 };
 
 
-typedef struct GDS_Device* GDS_DetectFunc(sys_Display * Driver, struct GDS_Device *Device);
+typedef struct GDS_Device* GDS_DetectFunc(sys_display_config * Driver, struct GDS_Device *Device);
 
 
-struct GDS_Device*	GDS_AutoDetect( sys_Display * Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM *PWM );
+struct GDS_Device*	GDS_AutoDetect( sys_display_config * Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM *PWM );
 
 
 void 	GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
 void 	GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
 void 	GDS_DisplayOn( struct GDS_Device* Device );
 void 	GDS_DisplayOn( struct GDS_Device* Device );

+ 3 - 2
components/display/core/gds_font.c

@@ -40,9 +40,10 @@ static bool LoadFont(struct GDS_FontDef ** fontPtr, const char * fileName){
         ESP_LOGE(TAG, "Invalid pointer for LoadFont");
         ESP_LOGE(TAG, "Invalid pointer for LoadFont");
         return false;
         return false;
     }
     }
-
+    char font_file_name[CONFIG_SPIFFS_OBJ_NAME_LEN+1]={0};
+    snprintf(font_file_name,sizeof(font_file_name),"/spiffs/fonts/%s",fileName);
     // Allocate DMA-capable memory for the font
     // Allocate DMA-capable memory for the font
-    struct GDS_FontDef* loadedFont = load_file_dma(NULL,"fonts",fileName);
+    struct GDS_FontDef* loadedFont = load_file_dma(NULL,font_file_name);
 
 
     // Check if allocation succeeded
     // Check if allocation succeeded
     if (loadedFont == NULL) {
     if (loadedFont == NULL) {

+ 18 - 29
components/display/display.c

@@ -22,7 +22,7 @@
 #include "gds_text.h"
 #include "gds_text.h"
 #include "gds_font.h"
 #include "gds_font.h"
 #include "gds_image.h"
 #include "gds_image.h"
-#include "Configurator.h"
+#include "Config.h"
 static const char *TAG = "display";
 static const char *TAG = "display";
 
 
 #define min(a,b) (((a) < (b)) ? (a) : (b))
 #define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -44,7 +44,7 @@ static EXT_RAM_ATTR struct {
 	char header[HEADER_SIZE + 1];
 	char header[HEADER_SIZE + 1];
 	char string[SCROLLABLE_SIZE + 1];
 	char string[SCROLLABLE_SIZE + 1];
 	int offset, boundary;
 	int offset, boundary;
-	sys_Metadata *metadata_config;
+	sys_metadata_config *metadata_config;
 	bool timer, refresh;
 	bool timer, refresh;
 	uint32_t elapsed;
 	uint32_t elapsed;
 	struct {
 	struct {
@@ -76,10 +76,11 @@ GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD
 void display_init(char *welcome) {
 void display_init(char *welcome) {
 	bool init = false;
 	bool init = false;
 	int width = -1, height = -1, backlight_pin = -1, RST_pin = -1;
 	int width = -1, height = -1, backlight_pin = -1, RST_pin = -1;
-	sys_Display * sys_display;
-	sys_DispCommon * common;
+	sys_display_config * sys_display;
+	sys_display_common * common;
 
 
-	if(!SYS_DISPLAY(sys_display) || !SYS_DISPLAY_COMMON(common)){
+	if(!SYS_DISPLAY(sys_display) || !SYS_DISPLAY_COMMON(common) || common->driver == sys_display_drivers_UNSPECIFIED){
+		ESP_LOGI(TAG,"No display configuration");
 		return;
 		return;
 	}
 	}
 	// // so far so good
 	// // so far so good
@@ -87,11 +88,10 @@ void display_init(char *welcome) {
 		ESP_LOGE(TAG,"Misconfigured display missing data");
 		ESP_LOGE(TAG,"Misconfigured display missing data");
 		return;
 		return;
 	}	
 	}	
-
-	ESP_LOGI(TAG, "Trying to configure display type %s, driver: %s", 
-				sys_DeviceTypeEnum_name(sys_display->type),
-				sys_DisplayDriverEnum_name(common->driver));
-	if (common->has_back && common->back.pin >= 0) {
+	ESP_LOGI(TAG, "Initializing display type %s, driver: %s", 
+				sys_dev_common_types_name(sys_display->type),
+				sys_display_drivers_name(common->driver));
+	if (common->back >= 0) {
 		struct GDS_BacklightPWM PWMConfig = { .Channel = pwm_system.base_channel++, .Timer = pwm_system.timer, .Max = pwm_system.max, .Init = false	};
 		struct GDS_BacklightPWM PWMConfig = { .Channel = pwm_system.base_channel++, .Timer = pwm_system.timer, .Max = pwm_system.max, .Init = false	};
 		display = GDS_AutoDetect(sys_display, drivers, &PWMConfig);
 		display = GDS_AutoDetect(sys_display, drivers, &PWMConfig);
 	} else {
 	} else {
@@ -99,38 +99,27 @@ void display_init(char *welcome) {
 	}
 	}
 
 
 	if (display) {
 	if (display) {
-		if(common->has_reset){
-			RST_pin = common->reset.pin;
-		}
-		if(common->has_back){
-			backlight_pin = common->back.pin;
-		}		
+		RST_pin = common->reset;
+		backlight_pin = common->back;
 		width = common->width;
 		width = common->width;
 		height = common->height;
 		height = common->height;
 
 
 		// Detect driver interface
 		// Detect driver interface
-		if (sys_display->which_dispType == sys_Display_i2c_tag && i2c_system_port != -1){
+		if (sys_display->which_dispType == sys_display_config_i2c_tag && platform->dev.i2c.port != sys_i2c_port_UNSPECIFIED){
 			int address = 0x3C;
 			int address = 0x3C;
 			
 			
 			address = sys_display->dispType.i2c.address;
 			address = sys_display->dispType.i2c.address;
 			init = true;
 			init = true;
-			GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ;
+			GDS_I2CInit( platform->dev.i2c.port-sys_i2c_port_PORT0, -1, -1, platform->dev.i2c.speed ) ;
 			GDS_I2CAttachDevice( display, width, height, address, RST_pin, backlight_pin );
 			GDS_I2CAttachDevice( display, width, height, address, RST_pin, backlight_pin );
 		
 		
 			ESP_LOGI(TAG, "Display is I2C on port %u", address);
 			ESP_LOGI(TAG, "Display is I2C on port %u", address);
-		} else if (sys_display->which_dispType == sys_Display_spi_tag && spi_system_host != -1) {
+		} else if (sys_display->which_dispType == sys_display_config_spi_tag && spi_system_host != -1) {
 			int CS_pin = -1, speed = 0, mode = 0;
 			int CS_pin = -1, speed = 0, mode = 0;
-			if(sys_display->dispType.spi.has_cs){
-				CS_pin = sys_display->dispType.spi.cs.pin;
-			}		
+			CS_pin = sys_display->dispType.spi.cs;
 			speed = sys_display->dispType.spi.speed;
 			speed = sys_display->dispType.spi.speed;
+			mode = sys_display->dispType.spi.mode;
 			
 			
-			//todo: what is mode? 
-
-			// PARSE_PARAM(config, "mode", '=', mode);
-
-			// todo: need to handle display offsets 
-		
 			init = true;
 			init = true;
 			GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
 			GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
 			GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, backlight_pin, speed, mode );
 			GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, backlight_pin, speed, mode );
@@ -174,7 +163,7 @@ void display_init(char *welcome) {
 		
 		
 			// leave room for artwork is display is horizontal-style
 			// leave room for artwork is display is horizontal-style
 			if (displayer.metadata_config->has_artwork && displayer.metadata_config->artwork.enabled) {
 			if (displayer.metadata_config->has_artwork && displayer.metadata_config->artwork.enabled) {
-				// todo : check for resize flag
+				#pragma message("todo: check for resize flag and possibly offsets?")
 				displayer.artwork.enable = true;
 				displayer.artwork.enable = true;
 				displayer.artwork.fit = true;
 				displayer.artwork.fit = true;
 				if (height <= 64 && width > height * 2)
 				if (height <= 64 && width > height * 2)

+ 53 - 60
components/driver_bt/bt_app_core.c

@@ -6,31 +6,31 @@
    CONDITIONS OF ANY KIND, either express or implied.
    CONDITIONS OF ANY KIND, either express or implied.
 */
 */
 
 
-#include <stdint.h>
-#include <string.h>
-#include <stdbool.h>
+#include "bt_app_core.h"
+#include "esp_bt.h"
+#include "esp_bt_main.h"
+#include "esp_gap_bt_api.h"
 #include "esp_log.h"
 #include "esp_log.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/queue.h"
 #include "freertos/queue.h"
 #include "freertos/task.h"
 #include "freertos/task.h"
-#include "esp_bt.h"
-#include "esp_bt_main.h"
-#include "esp_gap_bt_api.h"
-#include "bt_app_core.h"
 #include "tools.h"
 #include "tools.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
 
 
-static const char *TAG = "btappcore";
+static const char* TAG = "btappcore";
 
 
-static void bt_app_task_handler(void *arg);
-static bool bt_app_send_msg(bt_app_msg_t *msg);
-static void bt_app_work_dispatched(bt_app_msg_t *msg);
+static void bt_app_task_handler(void* arg);
+static bool bt_app_send_msg(bt_app_msg_t* msg);
+static void bt_app_work_dispatched(bt_app_msg_t* msg);
 
 
 static xQueueHandle s_bt_app_task_queue;
 static xQueueHandle s_bt_app_task_queue;
 static bool running;
 static bool running;
 
 
-bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback)
-{
-	ESP_LOGV(TAG,"%s event 0x%x, param len %d", __func__, event, param_len);
+bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void* p_params, int param_len,
+    bt_app_copy_cb_t p_copy_cback) {
+    ESP_LOGV(TAG, "%s event 0x%x, param len %d", __func__, event, param_len);
 
 
     bt_app_msg_t msg;
     bt_app_msg_t msg;
     memset(&msg, 0, sizeof(bt_app_msg_t));
     memset(&msg, 0, sizeof(bt_app_msg_t));
@@ -54,36 +54,33 @@ bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, i
     return false;
     return false;
 }
 }
 
 
-static bool bt_app_send_msg(bt_app_msg_t *msg)
-{
+static bool bt_app_send_msg(bt_app_msg_t* msg) {
     if (msg == NULL) {
     if (msg == NULL) {
         return false;
         return false;
     }
     }
 
 
     if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
     if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
-    	ESP_LOGE(TAG,"%s xQueue send failed", __func__);
+        ESP_LOGE(TAG, "%s xQueue send failed", __func__);
         return false;
         return false;
     }
     }
     return true;
     return true;
 }
 }
 
 
-static void bt_app_work_dispatched(bt_app_msg_t *msg)
-{
+static void bt_app_work_dispatched(bt_app_msg_t* msg) {
     if (msg->cb) {
     if (msg->cb) {
         msg->cb(msg->event, msg->param);
         msg->cb(msg->event, msg->param);
     }
     }
 }
 }
 
 
-static void bt_app_task_handler(void *arg)
-{
+static void bt_app_task_handler(void* arg) {
     bt_app_msg_t msg;
     bt_app_msg_t msg;
-	esp_err_t err;
-	
-	s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
-	
-	esp_bt_controller_mem_release(ESP_BT_MODE_BLE);
+    esp_err_t err;
+
+    s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
+
+    esp_bt_controller_mem_release(ESP_BT_MODE_BLE);
     esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
     esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
-	if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE ) {
+    if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
         if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
         if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
             ESP_LOGE(TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err));
             ESP_LOGE(TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err));
             goto exit;
             goto exit;
@@ -104,17 +101,16 @@ static void bt_app_task_handler(void *arg)
             goto exit;
             goto exit;
         }
         }
     }
     }
-	
-	/* Bluetooth device name, connection mode and profile set up */
-	bt_app_work_dispatch((bt_av_hdl_stack_evt_t*) arg, BT_APP_EVT_STACK_UP, NULL, 0, NULL);
-	
+
+    /* Bluetooth device name, connection mode and profile set up */
+    bt_app_work_dispatch((bt_av_hdl_stack_evt_t*)arg, BT_APP_EVT_STACK_UP, NULL, 0, NULL);
 #if (CONFIG_BT_SSP_ENABLED)
 #if (CONFIG_BT_SSP_ENABLED)
     /* Set default parameters for Secure Simple Pairing */
     /* Set default parameters for Secure Simple Pairing */
     esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
     esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
     esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
     esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
     esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
     esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
 #endif
 #endif
-	
+
     /*
     /*
      * Set default parameters for Legacy Pairing
      * Set default parameters for Legacy Pairing
      * Use variable pin, input pin code when pairing
      * Use variable pin, input pin code when pairing
@@ -122,19 +118,19 @@ static void bt_app_task_handler(void *arg)
     esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
     esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
     esp_bt_pin_code_t pin_code;
     esp_bt_pin_code_t pin_code;
     esp_bt_gap_set_pin(pin_type, 0, pin_code);
     esp_bt_gap_set_pin(pin_type, 0, pin_code);
-	
-	running = true;
-	
-	while (running) {
+
+    running = true;
+
+    while (running) {
         if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
         if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
-        	ESP_LOGV(TAG,"%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
-			
+            ESP_LOGV(TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
+
             switch (msg.sig) {
             switch (msg.sig) {
             case BT_APP_SIG_WORK_DISPATCH:
             case BT_APP_SIG_WORK_DISPATCH:
                 bt_app_work_dispatched(&msg);
                 bt_app_work_dispatched(&msg);
                 break;
                 break;
             default:
             default:
-                ESP_LOGW(TAG,"%s, unhandled sig: %d", __func__, msg.sig);
+                ESP_LOGW(TAG, "%s, unhandled sig: %d", __func__, msg.sig);
                 break;
                 break;
             }
             }
 
 
@@ -142,40 +138,37 @@ static void bt_app_task_handler(void *arg)
                 free(msg.param);
                 free(msg.param);
             }
             }
         } else {
         } else {
-        	ESP_LOGW(TAG,"No messaged received from queue.");
+            ESP_LOGW(TAG, "No messaged received from queue.");
         }
         }
     }
     }
-	
-	ESP_LOGD(TAG, "bt_app_task shutting down");
-	
-	if (esp_bluedroid_disable() != ESP_OK) goto exit;
-	// this disable has a sleep timer BTA_DISABLE_DELAY in bt_target.h and 
-	// if we don't wait for it then disable crashes... don't know why
-	vTaskDelay(2*200 / portTICK_PERIOD_MS);	
-	
+
+    ESP_LOGD(TAG, "bt_app_task shutting down");
+
+    if (esp_bluedroid_disable() != ESP_OK) goto exit;
+    // this disable has a sleep timer BTA_DISABLE_DELAY in bt_target.h and
+    // if we don't wait for it then disable crashes... don't know why
+    vTaskDelay(2 * 200 / portTICK_PERIOD_MS);
+
     ESP_LOGD(TAG, "esp_bluedroid_disable called successfully");
     ESP_LOGD(TAG, "esp_bluedroid_disable called successfully");
     if (esp_bluedroid_deinit() != ESP_OK) goto exit;
     if (esp_bluedroid_deinit() != ESP_OK) goto exit;
-	
+
     ESP_LOGD(TAG, "esp_bluedroid_deinit called successfully");
     ESP_LOGD(TAG, "esp_bluedroid_deinit called successfully");
     if (esp_bt_controller_disable() != ESP_OK) goto exit;
     if (esp_bt_controller_disable() != ESP_OK) goto exit;
-	
+
     ESP_LOGD(TAG, "esp_bt_controller_disable called successfully");
     ESP_LOGD(TAG, "esp_bt_controller_disable called successfully");
     if (esp_bt_controller_deinit() != ESP_OK) goto exit;
     if (esp_bt_controller_deinit() != ESP_OK) goto exit;
-	
-	ESP_LOGD(TAG, "bt stopped successfully");	
+
+    ESP_LOGD(TAG, "bt stopped successfully");
 
 
 exit:
 exit:
-	vQueueDelete(s_bt_app_task_queue);
-	running = false;		
+    vQueueDelete(s_bt_app_task_queue);
+    running = false;
     vTaskDelete(NULL);
     vTaskDelete(NULL);
 }
 }
 
 
-void bt_app_task_start_up(bt_av_hdl_stack_evt_t* handler)
-{
+void bt_app_task_start_up(bt_av_hdl_stack_evt_t* handler) {
+
     xTaskCreate(bt_app_task_handler, "BtAppT", 4096, handler, configMAX_PRIORITIES - 3, NULL);
     xTaskCreate(bt_app_task_handler, "BtAppT", 4096, handler, configMAX_PRIORITIES - 3, NULL);
 }
 }
 
 
-void bt_app_task_shut_down(void)
-{
-	running = false;
-}
+void bt_app_task_shut_down(void) { running = false; }

+ 21 - 16
components/driver_bt/bt_app_core.h

@@ -8,53 +8,58 @@
 
 
 #ifndef __BT_APP_CORE_H__
 #ifndef __BT_APP_CORE_H__
 #define __BT_APP_CORE_H__
 #define __BT_APP_CORE_H__
+#include "Status.pb.h"
 #include "esp_log.h"
 #include "esp_log.h"
 #include "time.h"
 #include "time.h"
-#include <stdint.h>
 #include <stdbool.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdio.h>
-#include "Status.pb.h"
-
-#define BT_APP_CORE_TAG                   "BT_APP_CORE"
-#define BT_APP_SIG_WORK_DISPATCH          (0x01)
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define BT_APP_CORE_TAG "BT_APP_CORE"
+#define BT_APP_SIG_WORK_DISPATCH (0x01)
 
 
 enum {
 enum {
     BT_APP_EVT_STACK_UP = 0,
     BT_APP_EVT_STACK_UP = 0,
 };
 };
 
 
-
-extern sys_AV_STATE bt_app_source_get_a2d_state();
-extern sys_MEDIA_STATE bt_app_source_get_media_state();
+extern sys_status_av_states bt_app_source_get_a2d_state();
+extern sys_status_media_states bt_app_source_get_media_state();
 /**
 /**
  * @brief     handler for the dispatched work
  * @brief     handler for the dispatched work
  */
  */
-typedef void (* bt_app_cb_t) (uint16_t event, void *param);
+typedef void (*bt_app_cb_t)(uint16_t event, void* param);
 
 
 /* message to be sent */
 /* message to be sent */
 typedef struct {
 typedef struct {
-    uint16_t             sig;      /*!< signal to bt_app_task */
-    uint16_t             event;    /*!< message event id */
-    bt_app_cb_t          cb;       /*!< context switch callback */
-    void                 *param;   /*!< parameter area needs to be last */
+    uint16_t sig;   /*!< signal to bt_app_task */
+    uint16_t event; /*!< message event id */
+    bt_app_cb_t cb; /*!< context switch callback */
+    void* param;    /*!< parameter area needs to be last */
 } bt_app_msg_t;
 } bt_app_msg_t;
 
 
 /**
 /**
  * @brief     parameter deep-copy function to be customized
  * @brief     parameter deep-copy function to be customized
  */
  */
-typedef void (* bt_app_copy_cb_t) (bt_app_msg_t *msg, void *p_dest, void *p_src);
+typedef void (*bt_app_copy_cb_t)(bt_app_msg_t* msg, void* p_dest, void* p_src);
 
 
 /**
 /**
  * @brief     callback for startup event
  * @brief     callback for startup event
  */
  */
-typedef void bt_av_hdl_stack_evt_t(uint16_t event, void *p_param);
+typedef void bt_av_hdl_stack_evt_t(uint16_t event, void* p_param);
 
 
 /**
 /**
  * @brief     work dispatcher for the application task
  * @brief     work dispatcher for the application task
  */
  */
-bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback);
+bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void* p_params, int param_len,
+    bt_app_copy_cb_t p_copy_cback);
 
 
 void bt_app_task_start_up(bt_av_hdl_stack_evt_t* handler);
 void bt_app_task_start_up(bt_av_hdl_stack_evt_t* handler);
 
 
 void bt_app_task_shut_down(void);
 void bt_app_task_shut_down(void);
 
 
 #endif /* __BT_APP_CORE_H__ */
 #endif /* __BT_APP_CORE_H__ */
+#ifdef __cplusplus
+}
+#endif

+ 5 - 7
components/driver_bt/bt_app_sink.c

@@ -21,7 +21,7 @@
 #include "esp_gap_bt_api.h"
 #include "esp_gap_bt_api.h"
 #include "esp_a2dp_api.h"
 #include "esp_a2dp_api.h"
 #include "esp_avrc_api.h"
 #include "esp_avrc_api.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/task.h"
 #include "tools.h"
 #include "tools.h"
@@ -45,7 +45,6 @@ static const char BT_RC_CT_TAG[] = "RCCT";
 #define CONFIG_BT_NAME	"ESP32-BT"
 #define CONFIG_BT_NAME	"ESP32-BT"
 #endif
 #endif
 
 
-static char * bt_name = NULL;
 
 
 static bool (*bt_app_a2d_cmd_cb)(bt_sink_cmd_t cmd, ...);
 static bool (*bt_app_a2d_cmd_cb)(bt_sink_cmd_t cmd, ...);
 static void (*bt_app_a2d_data_cb)(const uint8_t *data, uint32_t len);
 static void (*bt_app_a2d_data_cb)(const uint8_t *data, uint32_t len);
@@ -511,7 +510,7 @@ static void volume_set_by_local_host(int value, bool is_step)
 	_lock_release(&s_volume_lock);
 	_lock_release(&s_volume_lock);
     if(sys_state->bt_sink_volume != s_volume){
     if(sys_state->bt_sink_volume != s_volume){
         sys_state->bt_sink_volume = s_volume;
         sys_state->bt_sink_volume = s_volume;
-        configurator_raise_state_changed();
+        config_raise_state_changed();
     }
     }
 	
 	
     if (s_volume_notify) {
     if (s_volume_notify) {
@@ -567,8 +566,8 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
 	bt_app_a2d_cmd_cb = cmd_handler;
 	bt_app_a2d_cmd_cb = cmd_handler;
 	cmd_handler_chain = cmd_cb;
 	cmd_handler_chain = cmd_cb;
   	bt_app_a2d_data_cb = data_cb;
   	bt_app_a2d_data_cb = data_cb;
-    sys_BluetoothSink * bt_sink;
-    if(!SYS_SERVICES_BTSINK(bt_sink)){
+    sys_services_bt_sink * bt_sink;
+    if(!sys_services_config_BTSINK(bt_sink)){
         return;
         return;
     }
     }
     char pin_code[ESP_BT_PIN_CODE_LEN+1] = "1234\0";
     char pin_code[ESP_BT_PIN_CODE_LEN+1] = "1234\0";
@@ -583,7 +582,7 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
      */
      */
     esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
     esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
     strncpy(pin_code,bt_sink->pin,sizeof(pin_code));
     strncpy(pin_code,bt_sink->pin,sizeof(pin_code));
-    if(SYS_SERVICES_BTSINK(bt_sink) && strlen(bt_sink->pin)>ESP_BT_PIN_CODE_LEN){
+    if( strlen(bt_sink->pin)>ESP_BT_PIN_CODE_LEN){
 
 
     	ESP_LOGW(BT_AV_TAG, "BT Sink pin code [%s] too long. ", bt_sink->pin);
     	ESP_LOGW(BT_AV_TAG, "BT Sink pin code [%s] too long. ", bt_sink->pin);
     	pin_code[ESP_BT_PIN_CODE_LEN] = '\0';
     	pin_code[ESP_BT_PIN_CODE_LEN] = '\0';
@@ -606,7 +605,6 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
     if (bError) memcpy(esp_pin_code, "1234", 4);
     if (bError) memcpy(esp_pin_code, "1234", 4);
 	esp_bt_gap_set_pin(pin_type, strlen(pin_code), esp_pin_code);
 	esp_bt_gap_set_pin(pin_type, strlen(pin_code), esp_pin_code);
 	
 	
-	free(pin_code);
 }
 }
 
 
 void bt_sink_deinit(void)
 void bt_sink_deinit(void)

+ 71 - 70
components/driver_bt/bt_app_source.c

@@ -20,8 +20,10 @@
 #include "cJSON.h"
 #include "cJSON.h"
 #include "tools.h"
 #include "tools.h"
 #include "accessors.h"
 #include "accessors.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "Status.pb.h"
 #include "Status.pb.h"
+#include "bootstate.h"
+
 
 
 static const char * TAG = "bt_app_source";
 static const char * TAG = "bt_app_source";
 static const char * BT_RC_CT_TAG="RCCT";
 static const char * BT_RC_CT_TAG="RCCT";
@@ -31,11 +33,12 @@ extern void 	output_bt_stop(void);
 extern void 	output_bt_start(void);
 extern void 	output_bt_start(void);
 extern char*	output_state_str(void);
 extern char*	output_state_str(void);
 extern bool		output_stopped(void);
 extern bool		output_stopped(void);
-extern bool is_recovery_running;
+extern sys_squeezelite_config* get_profile(const char* name);
+
 
 
 static void bt_app_av_state_connecting(uint16_t event, void *param);
 static void bt_app_av_state_connecting(uint16_t event, void *param);
 static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param);
 static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param);
-sys_OutputBT * out_bt = NULL;
+sys_squeezelite_bt * out_bt = NULL;
 
 
 
 
 #define BT_APP_HEART_BEAT_EVT                (0xff00)
 #define BT_APP_HEART_BEAT_EVT                (0xff00)
@@ -76,8 +79,8 @@ static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *even
 
 
 static esp_bd_addr_t s_peer_bda = {0};
 static esp_bd_addr_t s_peer_bda = {0};
 static uint8_t s_peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
 static uint8_t s_peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
-sys_AV_STATE bt_app_source_a2d_state = sys_AV_STATE_A_IDLE;
-sys_MEDIA_STATE bt_app_source_media_state = sys_MEDIA_STATE_M_IDLE;
+sys_status_av_states bt_app_source_a2d_state = sys_status_av_states_A_IDLE;
+sys_status_media_states bt_app_source_media_state = sys_status_media_states_M_IDLE;
 static uint32_t s_pkt_cnt = 0;
 static uint32_t s_pkt_cnt = 0;
 static TimerHandle_t s_tmr=NULL;
 static TimerHandle_t s_tmr=NULL;
 static int prev_duration=10000;
 static int prev_duration=10000;
@@ -175,23 +178,23 @@ static void peers_list_maintain(const char * s_peer_bdname, int32_t rssi){
     }    
     }    
 }
 }
 
 
-sys_AV_STATE bt_app_source_get_a2d_state(){
+sys_status_av_states bt_app_source_get_a2d_state(){
     if(!is_recovery_running){
     if(!is_recovery_running){
         // if we are in recovery mode, don't log BT status
         // if we are in recovery mode, don't log BT status
-        ESP_LOGD(TAG,"a2dp status: %u = %s", bt_app_source_a2d_state, sys_AV_STATE_name(bt_app_source_a2d_state));
+        ESP_LOGD(TAG,"a2dp status: %u = %s", bt_app_source_a2d_state, sys_status_av_states_name(bt_app_source_a2d_state));
     }
     }
     return bt_app_source_a2d_state;
     return bt_app_source_a2d_state;
 }
 }
 
 
 
 
-sys_MEDIA_STATE bt_app_source_get_media_state(){
-    ESP_LOGD(TAG,"media state : %s ", sys_MEDIA_STATE_name(bt_app_source_media_state));
+sys_status_media_states bt_app_source_get_media_state(){
+    ESP_LOGD(TAG,"media state : %s ", sys_status_media_states_name(bt_app_source_media_state));
     return bt_app_source_media_state;
     return bt_app_source_media_state;
 }
 }
 
 
 void set_app_source_state(int new_state){
 void set_app_source_state(int new_state){
     if(bt_app_source_a2d_state!=new_state){
     if(bt_app_source_a2d_state!=new_state){
-        ESP_LOGD(TAG, "Updating state from %s to %s", sys_AV_STATE_name(bt_app_source_a2d_state), sys_AV_STATE_name(new_state));
+        ESP_LOGD(TAG, "Updating state from %s to %s", sys_status_av_states_name(bt_app_source_a2d_state), sys_status_av_states_name(new_state));
         bt_app_source_a2d_state=new_state;
         bt_app_source_a2d_state=new_state;
     }
     }
 }
 }
@@ -202,39 +205,37 @@ void set_a2dp_media_state(int new_state){
     }
     }
 }
 }
 
 
-void hal_bluetooth_init()
-{
-   
-    if(!ASSIGN_COND_VAL_3LVL(services,squeezelite,output_bt,out_bt) ){
-        ESP_LOGD(TAG,"Bluetooth not configured");
+void hal_bluetooth_init() {
+    sys_squeezelite_config* config = get_profile(NULL); // get the active profile
+    sys_squeezelite_bt* out_bt = (config && config->has_output_bt) ? &config->output_bt : NULL;
+    if (!out_bt) {
+        ESP_LOGD(TAG, "Bluetooth not configured");
         return;
         return;
     }
     }
-    if(strlen(out_bt->sink_name) == 0){
-        ESP_LOGE(TAG,"Sink name not configured!");
+    if (strlen(out_bt->sink_name) == 0) {
+        ESP_LOGE(TAG, "Sink name not configured!");
         return;
         return;
     }
     }
-	ESP_LOGD(TAG,"Initializing bluetooth sink");
-
+    ESP_LOGD(TAG, "Initializing bluetooth sink");
 
 
-	// create task and run event loop
+    // create task and run event loop
     bt_app_task_start_up(bt_av_hdl_stack_evt);
     bt_app_task_start_up(bt_av_hdl_stack_evt);
 
 
-	/*
-	 * Set default parameters for Legacy Pairing
-	 * Use variable pin, input pin code when pairing
-	*/
-	esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
+    /*
+     * Set default parameters for Legacy Pairing
+     * Use variable pin, input pin code when pairing
+     */
+    esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
     uint8_t pin_code_len;
     uint8_t pin_code_len;
-    uint8_t *pin_code;
-    if(strlen(out_bt->pin) == 0){
-        pin_code = (uint8_t *)"0000";
+    uint8_t* pin_code;
+    if (strlen(out_bt->pin) == 0) {
+        pin_code = (uint8_t*)"0000";
         pin_code_len = 4;
         pin_code_len = 4;
-    }
-    else {
-        pin_code = (uint8_t *) out_bt->pin;
+    } else {
+        pin_code = (uint8_t*)out_bt->pin;
         pin_code_len = strlen(out_bt->pin);
         pin_code_len = strlen(out_bt->pin);
     }
     }
-	esp_bt_gap_set_pin(pin_type, pin_code_len,pin_code);
+    esp_bt_gap_set_pin(pin_type, pin_code_len, pin_code);
 }
 }
 
 
 void hal_bluetooth_stop(void) {
 void hal_bluetooth_stop(void) {
@@ -301,8 +302,8 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
 		
 		
 		if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
 		if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
             peers_list_maintain(NULL, PEERS_LIST_MAINTAIN_PURGE);
             peers_list_maintain(NULL, PEERS_LIST_MAINTAIN_PURGE);
-            if (bt_app_source_a2d_state == sys_AV_STATE_A_DISCOVERED) {
-				set_app_source_state(sys_AV_STATE_A_CONNECTING);
+            if (bt_app_source_a2d_state == sys_status_av_states_A_DISCOVERED) {
+				set_app_source_state(sys_status_av_states_A_CONNECTING);
 				ESP_LOGI(TAG,"Discovery completed.  Ready to start connecting to %s. ", s_peer_bdname);
 				ESP_LOGI(TAG,"Discovery completed.  Ready to start connecting to %s. ", s_peer_bdname);
                 esp_a2d_source_connect(s_peer_bda);
                 esp_a2d_source_connect(s_peer_bda);
             } else {
             } else {
@@ -403,7 +404,7 @@ static const char * conn_state_str(esp_a2d_connection_state_t state){
 
 
 static void unexpected_connection_state(int from, esp_a2d_connection_state_t to)
 static void unexpected_connection_state(int from, esp_a2d_connection_state_t to)
 {
 {
-    ESP_LOGW(TAG,"Unexpected connection state change. App State: %s (%u) Connection State %s (%u)", sys_AV_STATE_name(from), from,conn_state_str(to), to);
+    ESP_LOGW(TAG,"Unexpected connection state change. App State: %s (%u) Connection State %s (%u)", sys_status_av_states_name(from), from,conn_state_str(to), to);
 }
 }
 
 
 static void handle_connect_state_unconnected(uint16_t event, esp_a2d_cb_param_t *param){
 static void handle_connect_state_unconnected(uint16_t event, esp_a2d_cb_param_t *param){
@@ -444,20 +445,20 @@ static void handle_connect_state_connecting(uint16_t event, esp_a2d_cb_param_t *
             else {
             else {
                 ESP_LOGW(TAG,"A2DP connect unsuccessful");
                 ESP_LOGW(TAG,"A2DP connect unsuccessful");
             }
             }
-            set_app_source_state(sys_AV_STATE_A_UNCONNECTED);
+            set_app_source_state(sys_status_av_states_A_UNCONNECTED);
             break;
             break;
         case ESP_A2D_CONNECTION_STATE_CONNECTING:
         case ESP_A2D_CONNECTION_STATE_CONNECTING:
             break;
             break;
         case ESP_A2D_CONNECTION_STATE_CONNECTED:
         case ESP_A2D_CONNECTION_STATE_CONNECTED:
-            set_app_source_state(sys_AV_STATE_A_CONNECTED);
-            set_a2dp_media_state(sys_MEDIA_STATE_M_IDLE);
+            set_app_source_state(sys_status_av_states_A_CONNECTED);
+            set_a2dp_media_state(sys_status_media_states_M_IDLE);
             ESP_LOGD(TAG,"Setting scan mode to ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE");
             ESP_LOGD(TAG,"Setting scan mode to ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE");
             esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
             esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
             ESP_LOGD(TAG,"Done setting scan mode. App state is now CONNECTED and media state IDLE.");
             ESP_LOGD(TAG,"Done setting scan mode. App state is now CONNECTED and media state IDLE.");
             break;
             break;
         case ESP_A2D_CONNECTION_STATE_DISCONNECTING:
         case ESP_A2D_CONNECTION_STATE_DISCONNECTING:
             unexpected_connection_state(bt_app_source_a2d_state, param->conn_stat.state); 
             unexpected_connection_state(bt_app_source_a2d_state, param->conn_stat.state); 
-            set_app_source_state(sys_AV_STATE_A_CONNECTING);       
+            set_app_source_state(sys_status_av_states_A_CONNECTING);       
             break;
             break;
         default:
         default:
             break;
             break;
@@ -469,7 +470,7 @@ static void handle_connect_state_connected(uint16_t event, esp_a2d_cb_param_t *p
     {
     {
         case ESP_A2D_CONNECTION_STATE_DISCONNECTED:
         case ESP_A2D_CONNECTION_STATE_DISCONNECTED:
             ESP_LOGW(TAG,"a2dp disconnected");
             ESP_LOGW(TAG,"a2dp disconnected");
-            set_app_source_state(sys_AV_STATE_A_UNCONNECTED);
+            set_app_source_state(sys_status_av_states_A_UNCONNECTED);
             esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
             esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
             break;
             break;
         case ESP_A2D_CONNECTION_STATE_CONNECTING:
         case ESP_A2D_CONNECTION_STATE_CONNECTING:
@@ -479,7 +480,7 @@ static void handle_connect_state_connected(uint16_t event, esp_a2d_cb_param_t *p
             unexpected_connection_state(bt_app_source_a2d_state, param->conn_stat.state);
             unexpected_connection_state(bt_app_source_a2d_state, param->conn_stat.state);
             break;
             break;
         case ESP_A2D_CONNECTION_STATE_DISCONNECTING:
         case ESP_A2D_CONNECTION_STATE_DISCONNECTING:
-            set_app_source_state(sys_AV_STATE_A_DISCONNECTING);
+            set_app_source_state(sys_status_av_states_A_DISCONNECTING);
 
 
             break;
             break;
         default:
         default:
@@ -493,7 +494,7 @@ static void handle_connect_state_disconnecting(uint16_t event, esp_a2d_cb_param_
     {
     {
         case ESP_A2D_CONNECTION_STATE_DISCONNECTED:
         case ESP_A2D_CONNECTION_STATE_DISCONNECTED:
             ESP_LOGI(TAG,"a2dp disconnected");
             ESP_LOGI(TAG,"a2dp disconnected");
-            set_app_source_state(sys_AV_STATE_A_UNCONNECTED);
+            set_app_source_state(sys_status_av_states_A_UNCONNECTED);
             esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);        
             esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);        
             break;
             break;
         case ESP_A2D_CONNECTION_STATE_CONNECTING:
         case ESP_A2D_CONNECTION_STATE_CONNECTING:
@@ -513,24 +514,24 @@ static void handle_connect_state_disconnecting(uint16_t event, esp_a2d_cb_param_
 
 
 static void bt_app_av_sm_hdlr(uint16_t event, void *param)
 static void bt_app_av_sm_hdlr(uint16_t event, void *param)
 {
 {
-    ESP_LOGV(TAG,"bt_app_av_sm_hdlr.%s a2d state: %s", event==BT_APP_HEART_BEAT_EVT?"Heart Beat.":"",sys_AV_STATE_name(bt_app_source_a2d_state));
+    ESP_LOGV(TAG,"bt_app_av_sm_hdlr.%s a2d state: %s", event==BT_APP_HEART_BEAT_EVT?"Heart Beat.":"",sys_status_av_states_name(bt_app_source_a2d_state));
     switch (bt_app_source_a2d_state) {
     switch (bt_app_source_a2d_state) {
-    case sys_AV_STATE_A_DISCOVERING:
-    	ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", sys_AV_STATE_name(bt_app_source_a2d_state), event, output_state_str());
+    case sys_status_av_states_A_DISCOVERING:
+    	ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", sys_status_av_states_name(bt_app_source_a2d_state), event, output_state_str());
     	break;
     	break;
-    case sys_AV_STATE_A_DISCOVERED:
-    	ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", sys_AV_STATE_name(bt_app_source_a2d_state), event, output_state_str());
+    case sys_status_av_states_A_DISCOVERED:
+    	ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", sys_status_av_states_name(bt_app_source_a2d_state), event, output_state_str());
         break;
         break;
-    case sys_AV_STATE_A_UNCONNECTED:
+    case sys_status_av_states_A_UNCONNECTED:
         bt_app_av_state_unconnected(event, param);
         bt_app_av_state_unconnected(event, param);
         break;
         break;
-    case sys_AV_STATE_A_CONNECTING:
+    case sys_status_av_states_A_CONNECTING:
         bt_app_av_state_connecting(event, param);
         bt_app_av_state_connecting(event, param);
         break;
         break;
-    case sys_AV_STATE_A_CONNECTED:
+    case sys_status_av_states_A_CONNECTED:
         bt_app_av_state_connected(event, param);
         bt_app_av_state_connected(event, param);
         break;
         break;
-    case sys_AV_STATE_A_DISCONNECTING:
+    case sys_status_av_states_A_DISCONNECTING:
         bt_app_av_state_disconnecting(event, param);
         bt_app_av_state_disconnecting(event, param);
         break;
         break;
     default:
     default:
@@ -591,7 +592,7 @@ static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param)
     uint8_t nameLen = 0;
     uint8_t nameLen = 0;
     esp_bt_gap_dev_prop_t *p;
     esp_bt_gap_dev_prop_t *p;
     memset(bda_str, 0x00, sizeof(bda_str));
     memset(bda_str, 0x00, sizeof(bda_str));
-    if(bt_app_source_a2d_state != sys_AV_STATE_A_DISCOVERING)
+    if(bt_app_source_a2d_state != sys_status_av_states_A_DISCOVERING)
     {
     {
     	// Ignore messages that might have been queued already
     	// Ignore messages that might have been queued already
     	// when we've discovered the target device.
     	// when we've discovered the target device.
@@ -650,7 +651,7 @@ static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param)
     if (squeezelite_conf.sink_name && strlen(squeezelite_conf.sink_name) >0 && strcmp((char *)s_peer_bdname, squeezelite_conf.sink_name) == 0) {
     if (squeezelite_conf.sink_name && strlen(squeezelite_conf.sink_name) >0 && strcmp((char *)s_peer_bdname, squeezelite_conf.sink_name) == 0) {
         ESP_LOGI(TAG,"Found our target device. address %s, name %s", bda_str, s_peer_bdname);
         ESP_LOGI(TAG,"Found our target device. address %s, name %s", bda_str, s_peer_bdname);
 		memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN);
 		memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN);
-        set_app_source_state(sys_AV_STATE_A_DISCOVERED);
+        set_app_source_state(sys_status_av_states_A_DISCOVERED);
         esp_bt_gap_cancel_discovery();
         esp_bt_gap_cancel_discovery();
     } else {
     } else {
     	ESP_LOGV(TAG,"Not the device we are looking for (%s). Continuing scan", squeezelite_conf.sink_name?squeezelite_conf.sink_name:"N/A");
     	ESP_LOGV(TAG,"Not the device we are looking for (%s). Continuing scan", squeezelite_conf.sink_name?squeezelite_conf.sink_name:"N/A");
@@ -698,7 +699,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
 
 
         /* start device discovery */
         /* start device discovery */
         ESP_LOGI(TAG,"Starting device discovery...");
         ESP_LOGI(TAG,"Starting device discovery...");
-        set_app_source_state(sys_AV_STATE_A_DISCOVERING);
+        set_app_source_state(sys_status_av_states_A_DISCOVERING);
         esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
         esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
 
 
         /* create and start heart beat timer */
         /* create and start heart beat timer */
@@ -722,7 +723,7 @@ static void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t
     case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
     case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
     case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT:
     case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT:
     case ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT: {
     case ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT: {
-        ESP_LOGD(TAG,"Received %s message", sys_ESP_AVRC_CT_name(event));
+        ESP_LOGD(TAG,"Received %s message", sys_status_avrc_ct_name(event));
         bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
         bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
         break;
         break;
     }
     }
@@ -735,7 +736,7 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
 {
 {
     esp_a2d_cb_param_t *a2d = NULL;
     esp_a2d_cb_param_t *a2d = NULL;
     switch (bt_app_source_media_state) {
     switch (bt_app_source_media_state) {
-    case sys_MEDIA_STATE_M_IDLE: {
+    case sys_status_media_states_M_IDLE: {
     	if (event == BT_APP_HEART_BEAT_EVT) {
     	if (event == BT_APP_HEART_BEAT_EVT) {
             if(!output_stopped())
             if(!output_stopped())
             {
             {
@@ -749,34 +750,34 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
 					a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS
 					a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS
 					) {
 					) {
 				ESP_LOGI(TAG,"a2dp media ready, starting playback!");
 				ESP_LOGI(TAG,"a2dp media ready, starting playback!");
-				set_a2dp_media_state(sys_MEDIA_STATE_M_STARTING);
+				set_a2dp_media_state(sys_status_media_states_M_STARTING);
 				esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START);
 				esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START);
 			}
 			}
         }
         }
         break;
         break;
     }
     }
 
 
-    case sys_MEDIA_STATE_M_STARTING: {
+    case sys_status_media_states_M_STARTING: {
     	if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) {
     	if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) {
             a2d = (esp_a2d_cb_param_t *)(param);
             a2d = (esp_a2d_cb_param_t *)(param);
             if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START &&
             if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START &&
                     a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
                     a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
             	ESP_LOGI(TAG,"a2dp media started successfully.");
             	ESP_LOGI(TAG,"a2dp media started successfully.");
                 output_bt_start();
                 output_bt_start();
-                set_a2dp_media_state(sys_MEDIA_STATE_M_STARTED);
+                set_a2dp_media_state(sys_status_media_states_M_STARTED);
             } else {
             } else {
                 // not started succesfully, transfer to idle state
                 // not started succesfully, transfer to idle state
             	ESP_LOGI(TAG,"a2dp media start failed.");
             	ESP_LOGI(TAG,"a2dp media start failed.");
-                set_a2dp_media_state(sys_MEDIA_STATE_M_IDLE);
+                set_a2dp_media_state(sys_status_media_states_M_IDLE);
             }
             }
         }
         }
         break;
         break;
     }
     }
-    case sys_MEDIA_STATE_M_STARTED: {
+    case sys_status_media_states_M_STARTED: {
         if (event == BT_APP_HEART_BEAT_EVT) {
         if (event == BT_APP_HEART_BEAT_EVT) {
         	if(output_stopped()) {
         	if(output_stopped()) {
         		ESP_LOGI(TAG,"Output state is %s. Stopping a2dp media ...", output_state_str());
         		ESP_LOGI(TAG,"Output state is %s. Stopping a2dp media ...", output_state_str());
-                set_a2dp_media_state(sys_MEDIA_STATE_M_STOPPING);
+                set_a2dp_media_state(sys_status_media_states_M_STOPPING);
                 esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
                 esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
             } else {
             } else {
 				output_bt_tick();	
 				output_bt_tick();	
@@ -784,15 +785,15 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
         }
         }
         break;
         break;
     }
     }
-    case sys_MEDIA_STATE_M_STOPPING: {
-    	ESP_LOG_DEBUG_EVENT(TAG,QUOTE(sys_MEDIA_STATE_M_STOPPING));
+    case sys_status_media_states_M_STOPPING: {
+    	ESP_LOG_DEBUG_EVENT(TAG,QUOTE(sys_status_media_states_M_STOPPING));
         if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) {
         if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) {
             a2d = (esp_a2d_cb_param_t *)(param);
             a2d = (esp_a2d_cb_param_t *)(param);
             if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP &&
             if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP &&
                     a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
                     a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
                 ESP_LOGI(TAG,"a2dp media stopped successfully...");
                 ESP_LOGI(TAG,"a2dp media stopped successfully...");
                 output_bt_stop();
                 output_bt_stop();
-               	set_a2dp_media_state(sys_MEDIA_STATE_M_IDLE);
+               	set_a2dp_media_state(sys_status_media_states_M_IDLE);
             } else {
             } else {
                 ESP_LOGI(TAG,"a2dp media stopping...");
                 ESP_LOGI(TAG,"a2dp media stopping...");
                 esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
                 esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
@@ -801,9 +802,9 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
         break;
         break;
     }
     }
 
 
-    case sys_MEDIA_STATE_M_WAIT_DISCONNECT:{
+    case sys_status_media_states_M_WAIT_DISCONNECT:{
     	esp_a2d_source_disconnect(s_peer_bda);
     	esp_a2d_source_disconnect(s_peer_bda);
-		set_app_source_state(sys_AV_STATE_A_DISCONNECTING);
+		set_app_source_state(sys_status_av_states_A_DISCONNECTING);
 		ESP_LOGI(TAG,"a2dp disconnecting...");
 		ESP_LOGI(TAG,"a2dp disconnecting...");
     }
     }
     }
     }
@@ -843,11 +844,11 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param)
         uint8_t *p = s_peer_bda;
         uint8_t *p = s_peer_bda;
         ESP_LOGI(TAG, "a2dp connecting to %s, BT peer: %02x:%02x:%02x:%02x:%02x:%02x",s_peer_bdname,p[0], p[1], p[2], p[3], p[4], p[5]);
         ESP_LOGI(TAG, "a2dp connecting to %s, BT peer: %02x:%02x:%02x:%02x:%02x:%02x",s_peer_bdname,p[0], p[1], p[2], p[3], p[4], p[5]);
         if(esp_a2d_source_connect(s_peer_bda)==ESP_OK) {  
         if(esp_a2d_source_connect(s_peer_bda)==ESP_OK) {  
-            set_app_source_state(sys_AV_STATE_A_CONNECTING);
+            set_app_source_state(sys_status_av_states_A_CONNECTING);
             s_connecting_intv = 0;
             s_connecting_intv = 0;
 		}
 		}
 		else {
 		else {
-            set_app_source_state(sys_AV_STATE_A_UNCONNECTED);
+            set_app_source_state(sys_status_av_states_A_UNCONNECTED);
 			// there was an issue connecting... continue to discover
 			// there was an issue connecting... continue to discover
 			ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
 			ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
 			esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
 			esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
@@ -877,7 +878,7 @@ static void bt_app_av_state_connecting(uint16_t event, void *param)
     	break;
     	break;
     case BT_APP_HEART_BEAT_EVT:
     case BT_APP_HEART_BEAT_EVT:
         if (++s_connecting_intv >= 2) {
         if (++s_connecting_intv >= 2) {
-            set_app_source_state(sys_AV_STATE_A_UNCONNECTED);
+            set_app_source_state(sys_status_av_states_A_UNCONNECTED);
             ESP_LOGW(TAG,"A2DP Connect time out!  Setting state to Unconnected. ");
             ESP_LOGW(TAG,"A2DP Connect time out!  Setting state to Unconnected. ");
             s_connecting_intv = 0;
             s_connecting_intv = 0;
         }
         }

+ 1 - 1
components/led_strip/CMakeLists.txt

@@ -1,7 +1,7 @@
 
 
 idf_component_register(SRC_DIRS .
 idf_component_register(SRC_DIRS .
 						INCLUDE_DIRS .
 						INCLUDE_DIRS .
-						REQUIRES platform_config tools esp_common
+						REQUIRES tools platform_config  esp_common
 						PRIV_REQUIRES services freertos driver           
 						PRIV_REQUIRES services freertos driver           
 )
 )
 
 

+ 10 - 9
components/led_strip/led_vu.c

@@ -23,7 +23,7 @@
 #include "globdefs.h"
 #include "globdefs.h"
 #include "monitor.h"
 #include "monitor.h"
 #include "led_strip.h"
 #include "led_strip.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "accessors.h"
 #include "accessors.h"
 #include "led_vu.h"
 #include "led_vu.h"
 
 
@@ -76,22 +76,23 @@ static void battery_svc(float value, int cells) {
  * 
  * 
  */
  */
 void led_vu_init()
 void led_vu_init()
-{
-    sys_LEDStrip * config = NULL;
+{   
+    sys_dev_led_strip*config;
     if(!SYS_DEV_LEDSTRIP(config)){
     if(!SYS_DEV_LEDSTRIP(config)){
+        ESP_LOGI(TAG,"No LED Strip configuration");
         return;
         return;
     }
     }
-    if(!!config->has_gpio ){
+    if(config->gpio<0){
+        ESP_LOGI(TAG,"LED Strip has no GPIO configured");
         return;
         return;
     }
     }
-    // char* config = config_alloc_get_str("led_vu_config", NULL, "N/A");
-
+    ESP_LOGI(TAG, "Initializing led_vu");    
     // Initialize led VU strip 
     // Initialize led VU strip 
     strip.length = config->length;
     strip.length = config->length;
-    strip.gpio = config->gpio.pin;
+    strip.gpio = config->gpio;
     
     
     // check for valid configuration
     // check for valid configuration
-    if (config->strip_type == sys_LEDStripType_LS_UNKNOWN || !config->has_gpio || config->gpio.pin <0) {
+    if (config->strip_type == sys_dev_strip_types_LS_UNKNOWN || config->gpio <0) {
         ESP_LOGI(TAG, "led_vu configuration invalid");
         ESP_LOGI(TAG, "led_vu configuration invalid");
         return;
         return;
     }
     }
@@ -117,7 +118,7 @@ void led_vu_init()
     ESP_LOGI(TAG, "vu meter using length:%d left:%d right:%d status:%d", strip.vu_length, strip.vu_start_l, strip.vu_start_r, strip.vu_status);
     ESP_LOGI(TAG, "vu meter using length:%d left:%d right:%d status:%d", strip.vu_length, strip.vu_start_l, strip.vu_start_r, strip.vu_status);
 
 
     // create driver configuration
     // create driver configuration
-    led_strip_config.rgb_led_type = config->strip_type == sys_LEDStripType_LS_WS2812?RGB_LED_TYPE_WS2812:RGB_LED_TYPE_MAX;
+    led_strip_config.rgb_led_type = config->strip_type == sys_dev_strip_types_LS_WS2812?RGB_LED_TYPE_WS2812:RGB_LED_TYPE_MAX;
     led_strip_config.access_semaphore = xSemaphoreCreateBinary();
     led_strip_config.access_semaphore = xSemaphoreCreateBinary();
     led_strip_config.led_strip_length = strip.length;
     led_strip_config.led_strip_length = strip.length;
     led_strip_config.led_strip_working = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT);
     led_strip_config.led_strip_working = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT);

+ 1 - 1
components/metrics/CMakeLists.txt

@@ -1,5 +1,5 @@
 idf_component_register(SRC_DIRS .
 idf_component_register(SRC_DIRS .
 						INCLUDE_DIRS .
 						INCLUDE_DIRS .
-						REQUIRES json tools platform_config wifi-manager esp-tls platform_config
+						REQUIRES json tools platform_config wifi-manager esp-tls 
 						PRIV_REQUIRES esp32 freertos
 						PRIV_REQUIRES esp32 freertos
 )
 )

+ 1 - 2
components/metrics/Metrics.cpp

@@ -20,13 +20,12 @@
 #include "cJSON.h"
 #include "cJSON.h"
 #include "freertos/timers.h"
 #include "freertos/timers.h"
 #include "network_manager.h"
 #include "network_manager.h"
-// #include "Configurator.h"
+// #include "Config.h"
 #pragma message("fixme: search for TODO below")
 #pragma message("fixme: search for TODO below")
 
 
 static const char* TAG = "metrics";
 static const char* TAG = "metrics";
 
 
 #if CONFIG_WITH_METRICS
 #if CONFIG_WITH_METRICS
-extern bool is_network_connected();
 #define METRICS_CLIENT_ID_LEN 50
 #define METRICS_CLIENT_ID_LEN 50
 #define MAX_HTTP_RECV_BUFFER 512
 #define MAX_HTTP_RECV_BUFFER 512
 
 

+ 4 - 2
components/platform_config/CMakeLists.txt

@@ -1,5 +1,7 @@
+set(CMAKE_CXX_STANDARD 20)
 idf_component_register(SRC_DIRS .
 idf_component_register(SRC_DIRS .
 						INCLUDE_DIRS . 
 						INCLUDE_DIRS . 
-						PRIV_REQUIRES   tools newlib  console esp_common freertos tools services
-						REQUIRES spiffs
+						PRIV_REQUIRES    newlib  console esp_common freertos  
+						REQUIRES spiffs tools services esp_http_server
 )
 )
+add_dependencies(${COMPONENT_LIB} generate_plugins_files)

+ 266 - 0
components/platform_config/Config.cpp

@@ -0,0 +1,266 @@
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
+#include "Config.h"
+#include "PBW.h"
+#include "WifiList.h"
+#include "bootstate.h"
+#include "DAC.pb.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "pb_common.h" // Nanopb header for encoding (serialization)
+#include "pb_decode.h" // Nanopb header for decoding (deserialization)
+#include "pb_encode.h" // Nanopb header for encoding (serialization)
+#include "tools.h"
+#include <algorithm>
+#include <cctype>
+#include <sstream>
+#include <stdexcept>
+#include <string.h>
+
+static const char* TAG = "Configurator";
+
+using namespace System;
+__attribute__((section(".ext_ram.bss"))) sys_config* platform;
+__attribute__((section(".ext_ram.bss"))) sys_state_data* sys_state;
+__attribute__((section(".ext_ram.bss"))) sys_dac_default_sets* default_dac_sets;
+
+__attribute__((section(".ext_ram.bss"))) System::PB<sys_config> configWrapper("config", &sys_config_msg, sizeof(sys_config_msg));
+__attribute__((section(".ext_ram.bss"))) System::PB<sys_state_data> stateWrapper("state", &sys_state_data_msg, sizeof(sys_state_data_msg));
+__attribute__((section(".ext_ram.bss"))) System::PB<sys_dac_default_sets> defaultSets("default_sets", &sys_dac_default_sets_msg, sizeof(sys_dac_default_sets_msg));
+
+const int MaxDelay = 1000;
+bool config_update_mac_string(const pb_msgdesc_t* desc, uint32_t field_tag, void* message) {
+    pb_field_iter_t iter;
+    if (pb_field_iter_begin(&iter, desc, message) && pb_field_iter_find(&iter, field_tag)) {
+        if (!iter.pData) {
+            ESP_LOGW(TAG, "Unable to check mac string member. Data not initialized");
+            return false;
+        }
+        if (iter.pData) {
+            auto curvalue = std::string((char*)iter.pData);
+            if (curvalue.find(get_mac_str()) != std::string::npos) {
+                ESP_LOGD(TAG, "Entry already has mac string: %s", curvalue.c_str());
+                return true;
+            }
+            if (curvalue.find("@@init_from_mac@@") == std::string::npos) {
+                ESP_LOGW(TAG, "Member not configured for mac address or was overwritten: %s", curvalue.c_str());
+                return false;
+            }
+            auto newval = std::string("squeezelite-") + get_mac_str();
+            if (PB_ATYPE(iter.type) == PB_ATYPE_POINTER) {
+                ESP_LOGD(TAG, "Field is a pointer. Freeing previous value if any: %s", STR_OR_BLANK((char*)iter.pField));
+                FREE_AND_NULL(*(char**)iter.pField);
+                ESP_LOGD(TAG, "Field is a pointer. Setting new value as %s", newval.c_str());
+                *(char**)iter.pField = strdup_psram(newval.c_str());
+            } else if (PB_ATYPE(iter.type) == PB_ATYPE_STATIC) {
+                ESP_LOGD(TAG, "Static string. Setting new value as %s from %s", newval.c_str(), STR_OR_BLANK((char*)iter.pData));
+                memset((char*)iter.pData, 0x00, iter.data_size);
+                strncpy((char*)iter.pData, newval.c_str(), iter.data_size);
+            }
+        } else {
+            ESP_LOGE(TAG, "Set mac string failed: member should be initialized with default");
+            return false;
+        }
+    }
+    return true;
+}
+bool set_pb_string_from_mac(pb_ostream_t* stream, const pb_field_t* field, void* const* arg) {
+    if (!stream) {
+        // This is a size calculation pass, return true to indicate field presence
+        return true;
+    }
+
+    // Generate the string based on MAC and prefix
+    const char* prefix = reinterpret_cast<const char*>(*arg);
+    char* value = alloc_get_string_with_mac(prefix && strlen(prefix) > 0 ? prefix : "squeezelite-");
+
+    // Write the string to the stream
+    if (!pb_encode_string(stream, (uint8_t*)value, strlen(value))) {
+        free(value); // Free memory if encoding fails
+        return false;
+    }
+
+    free(value); // Free memory after encoding
+    return true;
+}
+
+bool config_erase_config() {
+    // make sure the config object doesn't have
+    // any pending changes to commit
+    ESP_LOGW(TAG, "Erasing configuration object and rebooting");
+    configWrapper.ResetModified();
+    // Erase the file and reboot
+    erase_path(configWrapper.GetFileName().c_str(), false);
+    guided_factory();
+    return true;
+}
+void set_mac_string() {
+    auto expected = std::string("squeezelite-") + get_mac_str();
+    bool changed = false;
+    auto config = configWrapper.get();
+    changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_device_tag, &config->names) || changed;
+    changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_airplay_tag, &config->names) || changed;
+    changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_spotify_tag, &config->names) || changed;
+    changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_bluetooth_tag, &config->names) || changed;
+    changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_squeezelite_tag, &config->names) || changed;
+    changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_wifi_ap_name_tag, &config->names) || changed;
+
+    if (changed) {
+        ESP_LOGI(TAG, "One or more name was changed. Committing");
+        configWrapper.RaiseChangedAsync();
+    }
+}
+
+void config_load() {
+    ESP_LOGI(TAG, "Loading configuration.");
+    bool restart = false;
+    sys_state = stateWrapper.get();
+    platform = configWrapper.get();
+    default_dac_sets = defaultSets.get();
+
+    assert(platform != nullptr);
+    assert(sys_state != nullptr);
+    assert(default_dac_sets != nullptr);
+    
+    configWrapper.get()->net.credentials = reinterpret_cast<sys_net_wifi_entry*>(new WifiList("wifi"));
+    assert(configWrapper.get()->net.credentials != nullptr);
+
+    if (!stateWrapper.FileExists()) {
+        ESP_LOGI(TAG, "State file not found or is empty. ");
+        stateWrapper.Reinitialize(true);
+        stateWrapper.SetTarget(CONFIG_FW_PLATFORM_NAME);
+    } else {
+        stateWrapper.LoadFile();
+    }
+    if (!configWrapper.FileExists()) {
+        ESP_LOGI(TAG, "Configuration file not found or is empty. ");
+        configWrapper.Reinitialize(true);
+        ESP_LOGI(TAG, "Current device name after load: %s", platform->names.device);
+        
+        configWrapper.SetTarget(CONFIG_FW_PLATFORM_NAME,true);
+        set_mac_string();
+        ESP_LOGW(TAG, "Restart required after initializing configuration");
+        restart = true;
+
+    } else {
+        configWrapper.LoadFile();
+        if (configWrapper.GetTargetName().empty() && !std::string(CONFIG_FW_PLATFORM_NAME).empty()) {
+            ESP_LOGW(TAG, "Config target is empty. Updating to %s", CONFIG_FW_PLATFORM_NAME);
+            configWrapper.Reinitialize(false,true,std::string(CONFIG_FW_PLATFORM_NAME));
+            ESP_LOGW(TAG, "Restart required due to target change");
+            restart = true;
+        }
+    }
+    if (!defaultSets.FileExists()) {
+        ESP_LOGE(TAG, "Default Sets file not found or is empty. (%s)", defaultSets.GetFileName().c_str());
+    } else {
+        defaultSets.LoadFile();
+    }        
+    if (restart) {
+        network_async_reboot(OTA);
+    }
+}
+void config_dump_config() {
+    auto serialized = configWrapper.Encode();
+    dump_data(serialized.data(), serialized.size());
+}
+
+void config_set_target(const char* target_name, bool reset) {
+    std::string new_target = std::string(target_name);
+    bool restart = false;
+    if (configWrapper.GetTargetName() != new_target) {
+        ESP_LOGI(TAG, "Setting configuration target name to %s, %s", target_name, reset ? "full reset" : "reapply only");
+        if (reset) {
+            ESP_LOGD(TAG, "Reinitializing Config structure");
+            configWrapper.Reinitialize(false,true,new_target);
+        } else {
+            ESP_LOGD(TAG, "Loading Config target values for %s", target_name);
+            configWrapper.SetTarget(target_name,true);
+            configWrapper.LoadTargetValues();
+            configWrapper.RaiseChangedAsync();
+        }
+        restart = true;
+    } else {
+        ESP_LOGW(TAG, "Target name has no change");
+    }
+    if (stateWrapper.GetTargetName() != new_target) {
+        ESP_LOGI(TAG, "Setting state target name to %s, %s", target_name, reset ? "full reset" : "reapply only");
+        restart=true;
+        if (reset) {
+            ESP_LOGD(TAG, "Reinitializing State structure");
+            stateWrapper.Reinitialize(false,true,new_target);
+        } else {
+            ESP_LOGD(TAG, "Loading State target values for %s", target_name);
+            stateWrapper.SetTarget(target_name,true);
+            stateWrapper.LoadTargetValues();
+            stateWrapper.RaiseChangedAsync();
+        }
+    }
+    ESP_LOGD(TAG, "Done updating target to %s", target_name);
+    if(restart){
+        network_async_reboot(RESTART);
+    }
+}
+void config_set_target_no_reset(const char* target_name) { config_set_target(target_name, false); }
+void config_set_target_reset(const char* target_name) { config_set_target(target_name, true); }
+void config_raise_changed(bool sync) {
+    if (sync) {
+        configWrapper.CommitChanges();
+    } else {
+        configWrapper.RaiseChangedAsync();
+    }
+}
+void config_commit_protowrapper(void* protoWrapper) {
+    ESP_LOGD(TAG, "Committing synchronously");
+    PBHelper::SyncCommit(protoWrapper);
+}
+
+void config_commit_state() { stateWrapper.CommitChanges(); }
+void config_raise_state_changed() { stateWrapper.RaiseChangedAsync(); }
+
+bool config_has_changes() { return configWrapper.HasChanges() || stateWrapper.HasChanges(); }
+
+bool config_waitcommit() { return configWrapper.WaitForCommit(2) && stateWrapper.WaitForCommit(2); }
+
+bool config_http_send_config(httpd_req_t* req) {
+    try {
+        auto data = configWrapper.Encode();
+        httpd_resp_send(req, (const char*)data.data(), data.size());
+        return true;
+    } catch (const std::runtime_error& e) {
+        std::string errdesc = (std::string("Unable to get configuration: ") + e.what());
+        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, errdesc.c_str());
+    }
+    return false;
+}
+
+bool system_set_string(const pb_msgdesc_t* desc, uint32_t field_tag, void* message, const char* value) {
+    pb_field_iter_t iter;
+    ESP_LOGD(TAG,"system_set_string. Getting new value");
+    std::string newval = std::string(STR_OR_BLANK(value));
+    ESP_LOGD(TAG,"system_set_string. Done getting new value");
+    ESP_LOGD(TAG, "Setting value [%s] in message field tag %d", newval.c_str(), field_tag);
+    if (pb_field_iter_begin(&iter, desc, message) && pb_field_iter_find(&iter, field_tag)) {
+        std::string old= std::string((char*)iter.pData);
+        if (iter.pData && old == newval) {
+            ESP_LOGW(TAG, "No change, from and to values are the same: [%s]", newval.c_str());
+            return false;
+        }
+        if (PB_ATYPE(iter.type) == PB_ATYPE_POINTER) {
+            ESP_LOGD(TAG, "Field is a pointer. Freeing previous value if any: %s", STR_OR_BLANK((char*)iter.pField));
+            FREE_AND_NULL(*(char**)iter.pField);
+            ESP_LOGD(TAG, "Field is a pointer. Setting new value ");
+            if (!newval.empty()) {
+                *(char**)iter.pField = strdup_psram(newval.c_str());
+            }
+
+        } else if (PB_ATYPE(iter.type) == PB_ATYPE_STATIC) {
+            ESP_LOGD(TAG, "Static string. Setting new value. Existing value: %s", (char*)iter.pData);
+            memset((char*)iter.pData, 0x00, iter.data_size);
+            if (!newval.empty()) {
+                strncpy((char*)iter.pData, newval.c_str(), iter.data_size);
+            }
+        }
+        ESP_LOGD(TAG, "Done setting value ");
+    }
+    return true;
+}

+ 49 - 0
components/platform_config/Config.h

@@ -0,0 +1,49 @@
+#pragma once
+#include "State.pb.h"
+#include "Status.pb.h"
+#include "configuration.pb.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/event_groups.h"
+#include "freertos/semphr.h"
+#include "freertos/timers.h"
+#include "status.pb.h"
+#include "accessors.h"
+#include "esp_http_server.h"
+#include "PBW.h"
+
+#ifdef __cplusplus
+extern System::PB<sys_state_data> stateWrapper;
+extern System::PB<sys_config> configWrapper;
+extern "C" {
+#endif
+#define PLATFORM_GET_PTR(base, sname)                                                              \
+    {                                                                                              \
+        (base && (base)->##has_##(sname) ? &(base)->sname : NULL)
+#define PLATFORM_DEVICES PLATFORM_GET_PTR(platform)
+void config_load();
+bool config_waitcommit();
+bool config_has_changes();
+void config_raise_changed(bool sync);
+void config_raise_state_changed();
+bool config_has_changes();
+bool config_waitcommit();
+void config_set_target(const char* target_name);
+void config_set_target_no_reset(const char* target_name);
+void config_set_target_reset(const char* target_name);
+
+void config_commit_protowrapper(void * protoWrapper);
+bool config_http_send_config(httpd_req_t* req);
+bool config_erase_config();
+void set_mac_string();
+void config_dump_config();
+bool system_set_string(
+    const pb_msgdesc_t* desc, uint32_t field_tag, void* message, const char* value);
+extern sys_config* platform;
+extern sys_state_data* sys_state;
+extern sys_config* presets;
+#ifdef __cplusplus
+}
+
+
+#endif
+

+ 0 - 480
components/platform_config/Configurator.cpp

@@ -1,480 +0,0 @@
-#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
-#include "Configurator.h"
-#include "esp_log.h"
-#include "esp_system.h"
-#include "pb_common.h" // Nanopb header for encoding (serialization)
-#include "pb_decode.h" // Nanopb header for decoding (deserialization)
-#include "pb_encode.h" // Nanopb header for encoding (serialization)
-// #include "sys_options.h"
-#include "tools.h"
-#include <string.h>
-#include <algorithm>
-static const char* TAG = "Configurator";
-static const char* targets_folder = "targets";
-static const char* config_file_name = "settings.bin";
-static const char* state_file_name = "state.bin";
-__attribute__((section(".ext_ram.bss"))) PlatformConfig::Configurator configurator;
-sys_Config* platform = NULL;
-sys_State* sys_state = NULL;
-
-bool set_pb_string_from_mac(pb_ostream_t* stream, const pb_field_t* field, void* const* arg) {
-    if (!stream) {
-        // This is a size calculation pass, return true to indicate field presence
-        return true;
-    }
-
-    // Generate the string based on MAC and prefix
-    const char* prefix = reinterpret_cast<const char*>(*arg);
-    char* value = alloc_get_string_with_mac(prefix && strlen(prefix) > 0 ? prefix : "squeezelite-");
-
-    // Write the string to the stream
-    if (!pb_encode_string(stream, (uint8_t*)value, strlen(value))) {
-        free(value); // Free memory if encoding fails
-        return false;
-    }
-
-    free(value); // Free memory after encoding
-    return true;
-}
-
-namespace PlatformConfig {
-
-EXT_RAM_ATTR static const int NO_COMMIT_PENDING = BIT0;
-EXT_RAM_ATTR static const int LOAD_BIT = BIT1;
-EXT_RAM_ATTR static const int NO_STATE_COMMIT_PENDING = BIT2;
-
-const int Configurator::MaxDelay = 1000;
-const int Configurator::LockMaxWait = 20 * Configurator::MaxDelay;
-
-EXT_RAM_ATTR TimerHandle_t Configurator::_timer;
-EXT_RAM_ATTR SemaphoreHandle_t Configurator::_mutex;
-EXT_RAM_ATTR SemaphoreHandle_t Configurator::_state_mutex;
-EXT_RAM_ATTR EventGroupHandle_t Configurator::_group;
-
-static void ConfiguratorCallback(TimerHandle_t xTimer) {
-    static int cnt = 0, scnt = 0;
-    if (configurator.HasChanges()) {
-        ESP_LOGI(TAG, "Configuration has some uncommitted entries");
-        configurator.CommitChanges();
-    } else {
-        if (++cnt >= 15) {
-            ESP_LOGV(TAG, "commit timer: commit flag not set");
-            cnt = 0;
-        }
-    }
-    if (configurator.HasStateChanges()) {
-        ESP_LOGI(TAG, "State has some uncommitted changes");
-        configurator.CommitState();
-    } else {
-        if (++scnt >= 15) {
-            ESP_LOGV(TAG, "commit timer: commit flag not set");
-            cnt = 0;
-        }
-    }    
-    xTimerReset(xTimer, 10);
-}
-void Configurator::RaiseStateModified() { SetGroupBit(NO_STATE_COMMIT_PENDING, false); }
-void Configurator::RaiseModified() { SetGroupBit(NO_COMMIT_PENDING, false); }
-void Configurator::ResetModified() {
-    ESP_LOGV(TAG, "Resetting the global commit flag.");
-    SetGroupBit(NO_COMMIT_PENDING, false);
-}
-void Configurator::ResetStateModified() {
-    ESP_LOGV(TAG, "Resetting the state commit flag.");
-    SetGroupBit(NO_STATE_COMMIT_PENDING, false);
-}
-bool Configurator::SetGroupBit(int bit_num, bool flag) {
-    bool result = true;
-    int curFlags = xEventGroupGetBits(_group);
-    if ((curFlags & LOAD_BIT) && bit_num == NO_COMMIT_PENDING) {
-        ESP_LOGD(TAG, "Loading config, ignoring changes");
-        result = false;
-    }
-    if (result) {
-        bool curBit = (xEventGroupGetBits(_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(_group, bit_num);
-        } else {
-            xEventGroupSetBits(_group, bit_num);
-        }
-    }
-    return result;
-}
-bool Configurator::Lock() {
-    ESP_LOGV(TAG, "Locking Configurator");
-    if (xSemaphoreTake(_mutex, LockMaxWait) == pdTRUE) {
-        ESP_LOGV(TAG, "Configurator locked!");
-        return true;
-    } else {
-        ESP_LOGE(TAG, "Semaphore take failed. Unable to lock Configurator");
-        return false;
-    }
-}
-bool Configurator::LockState() {
-    ESP_LOGV(TAG, "Locking State");
-    if (xSemaphoreTake(_state_mutex, LockMaxWait) == pdTRUE) {
-        ESP_LOGV(TAG, "State locked!");
-        return true;
-    } else {
-        ESP_LOGE(TAG, "Semaphore take failed. Unable to lock State");
-        return false;
-    }
-}
-void* Configurator::AllocGetConfigBuffer(size_t* sz, sys_Config* config) {
-    size_t datasz;
-    pb_byte_t* data = NULL;
-
-    if (!pb_get_encoded_size(&datasz, sys_Config_fields, (const void*)platform) || datasz <= 0) {
-        return data;
-    }
-    data = (pb_byte_t*)malloc_init_external(datasz * sizeof(pb_byte_t));
-    pb_ostream_t stream = pb_ostream_from_buffer(data, datasz);
-    pb_encode(&stream, sys_Config_fields, (const void*)platform);
-    if (sz) {
-        *sz = datasz * sizeof(pb_byte_t);
-    }
-    return data;
-}
-void* Configurator::AllocGetConfigBuffer(size_t* sz) {
-    return AllocGetConfigBuffer(sz, &this->_root);
-}
-bool Configurator::WaitForCommit() {
-    bool commit_pending = (xEventGroupGetBits(_group) & NO_COMMIT_PENDING) == 0;
-    while (commit_pending) {
-        ESP_LOGW(TAG, "Waiting for config commit ...");
-        commit_pending = (xEventGroupWaitBits(_group, NO_COMMIT_PENDING | NO_STATE_COMMIT_PENDING, pdFALSE, pdTRUE,
-                              (MaxDelay * 2) / portTICK_PERIOD_MS) &
-                             ( NO_COMMIT_PENDING | NO_STATE_COMMIT_PENDING)) == 0;
-        if (commit_pending) {
-            ESP_LOGW(TAG, "Timeout waiting for config commit.");
-        } else {
-            ESP_LOGI(TAG, "Config committed!");
-        }
-    }
-    return !commit_pending;
-}
-void Configurator::CommitChanges() {
-    esp_err_t err = ESP_OK;
-    ESP_LOGI(TAG, "Committing configuration to flash. Locking config object.");
-    if (!Lock()) {
-        ESP_LOGE(TAG, "Unable to lock config for commit ");
-        return;
-    }
-    ESP_LOGV(TAG, "Config Locked. Committing");
-    Commit(&_root);
-    ResetModified();
-    Unlock();
-    ESP_LOGI(TAG, "Done Committing configuration to flash.");
-}
-
-bool Configurator::CommitState() {
-    esp_err_t err = ESP_OK;
-    ESP_LOGI(TAG, "Committing configuration to flash. Locking config object.");
-    if (!LockState()) {
-        ESP_LOGE(TAG, "Unable to lock config for commit ");
-        return false;
-    }
-    ESP_LOGV(TAG, "Config Locked. Committing");
-    CommitState(&_sys_state);   
-    ResetStateModified();
-    Unlock();
-    ESP_LOGI(TAG, "Done Committing configuration to flash.");
-    return true;
-}
-bool Configurator::HasChanges() { return (xEventGroupGetBits(_group) & NO_COMMIT_PENDING); }
-bool Configurator::HasStateChanges() { return (xEventGroupGetBits(_group) & NO_STATE_COMMIT_PENDING); }
-void Configurator::Unlock() {
-    ESP_LOGV(TAG, "Unlocking Configurator!");
-    xSemaphoreGive(_mutex);
-}
-void Configurator::UnlockState() {
-    ESP_LOGV(TAG, "Unlocking State!");
-    xSemaphoreGive(_state_mutex);
-}
-
-void Configurator::ResetStructure(sys_Config* config) {
-    if (!config) {
-        return;
-    }
-    sys_Config blankconfig = sys_Config_init_default;
-    memcpy(config, &blankconfig, sizeof(blankconfig));
-}
-bool Configurator::LoadDecodeBuffer(void* buffer, size_t buffer_size) {
-    size_t msgsize = 0;
-    size_t newsize = 0;
-    sys_Config config = sys_Config_init_default;
-    bool result = Configurator::LoadDecode(buffer, buffer_size, &config);
-    if (result) {
-        Configurator::ApplyTargetSettings(&config);
-    }
-    if (result) {
-        void* currentbuffer = AllocGetConfigBuffer(&msgsize);
-        void* newbuffer = AllocGetConfigBuffer(&newsize, &config);
-        if (msgsize != newsize || !memcmp(currentbuffer, newbuffer, msgsize)) {
-            ESP_LOGI(TAG, "Config change detected.");
-            // todo: here we are assuming that all strings and repeated elements have fixed size
-            //  and therefore size should always be the same.
-            result = Configurator::LoadDecode(buffer, buffer_size, &this->_root);
-            RaiseModified();
-        }
-        free(currentbuffer);
-        free(newbuffer);
-    }
-    return result;
-}
-bool Configurator::LoadDecodeState() {
-    bool result = true;
-    sys_State blank_state = sys_State_init_default;
-    FILE* file = open_file("rb", state_file_name);
-    if (file == nullptr) {
-        ESP_LOGD(TAG,"No state file found. Initializing ");
-        pb_release(&sys_State_msg,(void *)&_sys_state);
-        memcpy(&_sys_state, &blank_state, sizeof(sys_State));
-        ESP_LOGD(TAG,"Done Initializing state");
-        return true;
-    }
-    ESP_LOGD(TAG, "Creating binding");
-    pb_istream_t filestream = {&in_file_binding,NULL,0};
-    ESP_LOGD(TAG, "Starting encode");
-    if (!pb_decode(&filestream, &sys_State_msg, (void*)&_sys_state)) {
-        ESP_LOGE(TAG, "Decoding failed: %s\n", PB_GET_ERROR(&filestream));
-        result = false;
-    }
-
-    fclose(file);
-    configurator_raise_state_changed();
-    ESP_LOGD(TAG, "State loaded");
-    return true;
-}
-bool Configurator::LoadDecode(
-    void* buffer, size_t buffer_size, sys_Config* conf_root, bool noinit) {
-    if (!conf_root || !buffer) {
-        ESP_LOGE(TAG, "Invalid arguments passed to Load");
-    }
-    bool result = true;
-    // Prepare to read the data into the 'config' structure
-    pb_istream_t stream = pb_istream_from_buffer((uint8_t*)buffer, buffer_size);
-
-    // Decode the Protocol Buffers message
-    if (noinit) {
-        ESP_LOGD(TAG, "Decoding WITHOUT initialization");
-        result = pb_decode_noinit(&stream, &sys_Config_msg, conf_root);
-    } else {
-        ESP_LOGD(TAG, "Decoding WITH initialization");
-        result = pb_decode(&stream, &sys_Config_msg, conf_root);
-    }
-    if (!result) {
-        ESP_LOGE(TAG, "Failed to decode settings: %s", PB_GET_ERROR(&stream));
-        return false;
-    }
-    ESP_LOGD(TAG, "Settings decoded");
-    return true;
-}
-
-bool Configurator::Commit(sys_Config* config) {
-    if (!config) {
-        ESP_LOGE(TAG, "Invalid configuration structure!");
-        return false;
-    }
-    FILE* file = open_file("wb", config_file_name);
-    bool result = true;
-    if (file == nullptr) {
-        return false;
-    }
-    ESP_LOGD(TAG, "Creating binding");
-    pb_ostream_t filestream = {&out_file_binding, file, SIZE_MAX, 0};
-    ESP_LOGD(TAG, "Starting encode");
-    if (!pb_encode(&filestream, sys_Config_fields, (void*)config)) {
-        ESP_LOGE(TAG, "Encoding failed: %s\n", PB_GET_ERROR(&filestream));
-        result = false;
-    }
-    ESP_LOGD(TAG, "Encoded size: %d", filestream.bytes_written);
-    if (filestream.bytes_written == 0) {
-        ESP_LOGE(TAG, "Empty configuration!");
-        ESP_LOGD(TAG, "Device name: %s", config->names.device);
-    }
-
-    fclose(file);
-    return result;
-}
-
-bool Configurator::CommitState(sys_State* state) {
-    if (!state) {
-        ESP_LOGE(TAG, "Invalid state structure!");
-        return false;
-    }
-    FILE* file = open_file("wb", state_file_name);
-    bool result = true;
-    if (file == nullptr) {
-        return false;
-    }
-    ESP_LOGD(TAG, "Creating binding for state commit");
-    pb_ostream_t filestream = {&out_file_binding, file, SIZE_MAX, 0};
-    ESP_LOGD(TAG, "Starting state encode");
-    if (!pb_encode(&filestream, sys_Config_fields, (void*)state)) {
-        ESP_LOGE(TAG, "Encoding failed: %s\n", PB_GET_ERROR(&filestream));
-        result = false;
-    }
-    ESP_LOGD(TAG, "Encoded size: %d", filestream.bytes_written);
-    if (filestream.bytes_written == 0) {
-        ESP_LOGE(TAG, "Empty state!");
-    }
-
-    fclose(file);
-    return result;
-}
-
-
-void Configurator::InitLoadConfig(const char* filename) {
-    return Configurator::InitLoadConfig(filename, &this->_root);
-}
-void Configurator::InitLoadConfig(const char* filename, sys_Config* conf_root, bool noinit) {
-    esp_err_t err = ESP_OK;
-    size_t data_length = 0;
-    bool result = false;
-    ESP_LOGI(TAG, "Loading settings from %s", filename);
-    void* data = load_file(&data_length, filename);
-    if (!data) {
-        ESP_LOGW(TAG, "Config file %s was empty. ", filename);
-        return;
-    } else {
-        result = LoadDecode(data, data_length, conf_root, noinit);
-        free(data);
-    }
-    if (ApplyTargetSettings(conf_root)) {
-        result = true;
-    }
-    if (result) {
-        _timer = xTimerCreate(
-            "configTimer", MaxDelay / portTICK_RATE_MS, pdFALSE, NULL, ConfiguratorCallback);
-        if (xTimerStart(_timer, MaxDelay / portTICK_RATE_MS) != pdPASS) {
-            ESP_LOGE(TAG, "config commitment timer failed to start.");
-        }
-    }
-
-    return;
-}
-bool Configurator::ApplyTargetSettings() { return ApplyTargetSettings(&this->_root); }
-bool Configurator::ApplyTargetSettings(sys_Config* conf_root) {
-    size_t data_length = 0;
-    bool result = false;
-    std::string target_name = conf_root->target; 
-    std::string target_file;
-
-#ifdef CONFIG_FW_PLATFORM_NAME
-    if( target_name.empty()){
-        target_name = CONFIG_FW_PLATFORM_NAME;
-    }
-#endif
-    target_file = target_name+ std::string(".bin");
-
-    std::transform(target_file.begin(), target_file.end(), target_file.begin(),
-        [](unsigned char c){ return std::tolower(c); });
-    if (target_file.empty() || !get_file_info(NULL, targets_folder, target_file.c_str())) {
-        ESP_LOGD(TAG, "Platform settings file not found: %s", target_file.c_str());
-        return result;
-    }
-
-    ESP_LOGI(TAG, "Applying target %s settings", target_name.c_str());
-    void* data = load_file(&data_length, targets_folder, target_file.c_str());
-    if (!data) {
-        ESP_LOGE(TAG, "File read fail");
-        return false;
-    } else {
-
-        result = LoadDecode(data, data_length, conf_root, true);
-        if (result) {
-            ESP_LOGI(TAG, "Target %s settings loaded", target_name.c_str());
-        }
-        free(data);
-    }
-
-    return result;
-}
-
-}; // namespace PlatformConfig
-
-void configurator_reset_configuration() {
-    ESP_LOGI(TAG, "Creating default configuration file. ");
-    sys_Config config = sys_Config_init_default;
-    ESP_LOGD(TAG, "Device name before target settings: %s", config.names.device);
-    PlatformConfig::Configurator::ApplyTargetSettings(&config);
-    ESP_LOGD(TAG, "Device name after target settings: %s", config.names.device);
-    ESP_LOGD(TAG, "Committing new structure");
-    PlatformConfig::Configurator::Commit(&config);
-}
-
-void configurator_load() {
-    struct stat fileInformation;
-    ESP_LOGI(TAG, "Loading system settings file");
-    ESP_LOGD(TAG, "Checking if file %s exists", config_file_name);
-    bool found = get_file_info(&fileInformation, config_file_name);
-    if (!found || fileInformation.st_size == 0) {
-        ESP_LOGI(TAG, "Configuration file not found or is empty. ");
-        configurator_reset_configuration();
-    }
-    configurator.InitLoadConfig(config_file_name);
-    ESP_LOGD(TAG, "Assigning global config pointer");
-    platform = configurator.Root();
-    configurator.LoadDecodeState();
-    sys_state = configurator.RootState();
-}
-bool configurator_lock() { return configurator.Lock(); }
-
-void configurator_unlock() { configurator.Unlock(); }
-void configurator_raise_changed() { configurator.RaiseModified(); }
-void configurator_raise_state_changed() { configurator.RaiseStateModified(); }
-
-bool configurator_has_changes() { return configurator.HasChanges(); }
-
-bool configurator_waitcommit() { return configurator.WaitForCommit(); }
-
-void* configurator_alloc_get_config(size_t* sz) { return configurator.AllocGetConfigBuffer(sz); }
-bool configurator_parse_config(void* buffer, size_t buffer_size) {
-    // Load and decode buffer. The method also applies any overlay if needed.
-    return configurator.LoadDecodeBuffer(buffer, buffer_size);
-}
-pb_type_t configurator_get_field_type(const pb_msgdesc_t* desc, uint32_t tag) {
-    pb_field_iter_t iter;
-    if (pb_field_iter_begin(&iter, desc, NULL) && pb_field_iter_find(&iter, tag)) {
-        /* Found our field. */
-        return iter.type;
-    }
-    return 0;
-}
-bool configurator_set_string(
-    const pb_msgdesc_t* desc, uint32_t field_tag, void* message, const char* value) {
-    pb_field_iter_t iter;
-    const char * newval = STR_OR_BLANK(value);
-    ESP_LOGD(TAG, "Setting value [%s] in message field tag %d",newval , field_tag);
-    if (pb_field_iter_begin(&iter, desc, message) && pb_field_iter_find(&iter, field_tag)) {
-        if (iter.pData && !strcmp((char*)iter.pData, newval)) {
-            ESP_LOGW(TAG, "No change, from and to values are the same: [%s]",  STR_OR_BLANK(newval));
-            return false;
-        }
-        if (PB_ATYPE(iter.type) == PB_ATYPE_POINTER) {
-            ESP_LOGD(TAG, "Field is a pointer. Freeing previous value if any");
-            FREE_AND_NULL(iter.pData);
-            ESP_LOGD(TAG, "Field is a pointer. Setting new value ");
-            if(newval && strlen(newval)>0){
-                iter.pData = strdup_psram(newval);
-            }
-            
-        } else if (PB_ATYPE(iter.type) == PB_ATYPE_STATIC) {
-            ESP_LOGD(TAG, "Static string. Setting new value");
-            memset(iter.pData,0x00,iter.data_size);
-            if(newval && strlen(newval)>0){
-                strncpy((char*)iter.pData, newval, iter.data_size);
-            }
-        }
-        ESP_LOGD(TAG, "Done setting value ");
-    }
-    return true;
-}

+ 0 - 107
components/platform_config/Configurator.h

@@ -1,107 +0,0 @@
-#pragma once
-#include "State.pb.h"
-#include "configuration.pb.h"
-#include "esp_log.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/event_groups.h"
-#include "freertos/semphr.h"
-#include "freertos/timers.h"
-#include "status.pb.h"
-#include <strings.h>
-#include "accessors.h"
-#ifdef __cplusplus
-#include <cstdlib>
-#include <string>
-#include <vector>
-
-extern "C" {
-#endif
-#define PLATFORM_GET_PTR(base, sname)                                                              \
-    {                                                                                              \
-        (base && (base)->##has_##(sname) ? &(base)->sname : NULL)
-#define PLATFORM_DEVICES PLATFORM_GET_PTR(platform)
-void configurator_load();
-bool configurator_waitcommit();
-bool configurator_has_changes();
-bool configurator_lock();
-void configurator_unlock();
-void configurator_raise_changed();
-void configurator_raise_state_changed();
-bool configurator_has_changes();
-bool configurator_waitcommit();
-void* configurator_alloc_get_config(size_t* sz);
-bool configurator_parse_config(void* buffer, size_t buffer_size);
-void configurator_reset_configuration();
-pb_type_t configurator_get_field_type(const pb_msgdesc_t* desc, uint32_t tag);
-bool configurator_set_string(
-    const pb_msgdesc_t* desc, uint32_t field_tag, void* message, const char* value);
-extern sys_Config* platform;
-extern sys_State* sys_state;
-#ifdef __cplusplus
-}
-#endif
-
-#ifdef __cplusplus
-
-namespace PlatformConfig {
-
-class Configurator {
-  private:
-    static const int MaxDelay;
-    static const int LockMaxWait;
-
-    EXT_RAM_ATTR static TimerHandle_t _timer;
-    EXT_RAM_ATTR static SemaphoreHandle_t _mutex;
-    EXT_RAM_ATTR static SemaphoreHandle_t _state_mutex;
-    EXT_RAM_ATTR static EventGroupHandle_t _group;
-    bool SetGroupBit(int bit_num, bool flag);
-    void ResetModified();
-    void ResetStateModified();
-    sys_Config _root;
-    sys_State _sys_state;
-
-  public:
-    sys_Config* Root() { return &_root; }
-    sys_State* RootState() { return &_sys_state; }
-    bool WaitForCommit();
-    bool Lock();
-    bool LockState();
-    void Unlock();
-    void UnlockState();
-
-    void* AllocGetConfigBuffer(size_t* sz);
-    static void* AllocGetConfigBuffer(size_t* sz, sys_Config* config);
-    static void ResetStructure(sys_Config* config);
-    bool LoadDecodeState();
-
-    void CommitChanges();
-    bool Commit();
-    static bool Commit(sys_Config* config);
-
-    bool CommitState();
-    static bool CommitState(sys_State* state);
-    sys_Config* AllocDefaultStruct();
-
-    static bool ApplyTargetSettings(sys_Config* conf_root);
-    bool ApplyTargetSettings();
-    bool HasStateChanges();
-
-    bool LoadDecodeBuffer(void* buffer, size_t buffer_size);
-    void InitLoadConfig(const char* filename);
-    void InitLoadConfig(const char* filename, sys_Config* conf_root, bool noinit = false);
-    static bool LoadDecode(
-        void* buffer, size_t buffer_size, sys_Config* conf_root, bool noinit = false);
-
-    Configurator() {
-        _mutex = xSemaphoreCreateMutex();
-        _state_mutex = xSemaphoreCreateMutex();
-        _group = xEventGroupCreate();
-    }
-    void RaiseStateModified();
-    void RaiseModified();
-    bool HasChanges();
-    ~Configurator() {}
-};
-} // namespace PlatformConfig
-extern PlatformConfig::Configurator configurator;
-#endif

+ 45 - 0
components/platform_config/Locking.cpp

@@ -0,0 +1,45 @@
+
+#include "Locking.h"
+#include "esp_log.h"
+#include "tools.h"
+static const char* TAG = "Locking";
+
+using namespace System;
+
+const int Locking::MaxDelay = 1000;
+const int Locking::LockMaxWait = 20 * Locking::MaxDelay;
+
+// C++ methods
+Locking* Locking::Create(std::string name) { return new Locking(name); }
+
+void Locking::Destroy(Locking* lock) { delete lock; }
+
+LockingHandle* Locking_Create(const char* name) {
+    return reinterpret_cast<LockingHandle*>(Locking::Create(std::string(name)));
+}
+
+void Locking_Destroy(LockingHandle* lock) { Locking::Destroy(reinterpret_cast<Locking*>(lock)); }
+
+bool Locking_Lock(LockingHandle* lock, TickType_t maxWait_ms) {
+    return reinterpret_cast<Locking*>(lock)->Lock(maxWait_ms);
+}
+
+void Locking_Unlock(LockingHandle* lock) { reinterpret_cast<Locking*>(lock)->Unlock(); }
+
+bool Locking_IsLocked(LockingHandle* lock) { return reinterpret_cast<Locking*>(lock)->IsLocked(); }
+
+bool Locking::Lock(TickType_t maxWait_ms) {
+    assert(_mutex != nullptr);
+    ESP_LOGV(TAG, "Locking %s", _name.c_str());
+    if (xSemaphoreTakeRecursive(_mutex, pdMS_TO_TICKS(maxWait_ms)) == pdTRUE) {
+        ESP_LOGV(TAG, "locked %s", _name.c_str());
+        return true;
+    } else {
+        ESP_LOGE(TAG, "Unable to lock %s", _name.c_str());
+        return false;
+    }
+}
+void Locking::Unlock() {
+    ESP_LOGV(TAG, "Unlocking %s", _name.c_str());
+    xSemaphoreGiveRecursive(_mutex);
+}

+ 71 - 0
components/platform_config/Locking.h

@@ -0,0 +1,71 @@
+/*
+ *
+ *      Sebastien L. 2023, sle118@hotmail.com
+ *      Philippe G. 2023, philippe_44@outlook.com
+ *
+ *  This software is released under the MIT License.
+ *  https://opensource.org/licenses/MIT
+ *
+ *  License Overview:
+ *  ----------------
+ *  The MIT License is a permissive open source license. As a user of this software, you are free to:
+ *  - Use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of this software.
+ *  - Use the software for private, commercial, or any other purposes.
+ *
+ *  Conditions:
+ *  - You must include the above copyright notice and this permission notice in all
+ *    copies or substantial portions of the Software.
+ *
+ *  The MIT License offers a high degree of freedom and is well-suited for both open source and
+ *  commercial applications. It places minimal restrictions on how the software can be used,
+ *  modified, and redistributed. For more details on the MIT License, please refer to the link above.
+ */
+
+#pragma once
+#include "esp_attr.h"
+#include "esp_system.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct LockingHandle LockingHandle;
+
+#ifdef __cplusplus
+} // extern "C"
+
+#include <string>
+namespace System {
+class Locking {
+  private:
+    SemaphoreHandle_t _mutex;
+    static const int MaxDelay;
+    static const int LockMaxWait;
+    std::string _name;
+
+  public:
+    Locking(std::string name) : _mutex(xSemaphoreCreateRecursiveMutex()), _name(name) {}
+    bool Lock(TickType_t maxWait_ms = LockMaxWait);
+    void Unlock();
+    bool IsLocked() { return xSemaphoreGetMutexHolder(_mutex) != nullptr; }
+    ~Locking() { vSemaphoreDelete(_mutex); }
+    static Locking* Create(std::string name);
+    static void Destroy(Locking* lock);
+};
+
+} // namespace PlatformConfig
+
+extern "C" {
+#endif
+
+LockingHandle* Locking_Create(const char* name);
+void Locking_Destroy(LockingHandle* lock);
+bool Locking_Lock(LockingHandle* lock, TickType_t maxWait_ms);
+void Locking_Unlock(LockingHandle* lock);
+bool Locking_IsLocked(LockingHandle* lock);
+
+#ifdef __cplusplus
+}
+#endif

+ 272 - 0
components/platform_config/PBW.cpp

@@ -0,0 +1,272 @@
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
+#include "Locking.h"
+#include "cpp_tools.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/event_groups.h"
+
+#include "PBW.h"
+#include "accessors.h"
+#include "esp_log.h"
+#include "network_manager.h"
+#include "pb.h"
+#include "pb_decode.h" // Nanopb header for decoding (deserialization)
+#include "pb_encode.h" // Nanopb header for encoding (serialization)
+#include "tools.h"
+#include <functional>
+#include <sstream>
+#include <vector>
+
+namespace System {
+const char* PBHelper::PROTOTAG = "PB";
+
+void PBHelper::ResetModified() {
+    ESP_LOGD(PROTOTAG, "Resetting the global commit flag for %s.", name.c_str());
+    SetGroupBit(Flags::COMMITTED, true);
+}
+
+bool PBHelper::SetGroupBit(Flags flags, bool flag) {
+    bool result = true;
+    int bit_num = static_cast<int>(flags);
+    int curFlags = xEventGroupGetBits(_group);
+    if ((curFlags & static_cast<int>(Flags::LOAD)) && flags == Flags::COMMITTED) {
+        ESP_LOGD(PROTOTAG, "Loading %s, ignoring changes", name.c_str());
+        result = false;
+    }
+    if (result) {
+        if ((curFlags & bit_num) == flag) {
+            ESP_LOGV(PROTOTAG, "Flag %d already %s", bit_num, flag ? "Set" : "Cleared");
+            result = false;
+        }
+    }
+    if (result) {
+        ESP_LOGV(PROTOTAG, "%s Flag %d ", flag ? "Setting" : "Clearing", bit_num);
+        if (!flag) {
+            xEventGroupClearBits(_group, bit_num);
+        } else {
+            xEventGroupSetBits(_group, bit_num);
+        }
+    }
+    return result;
+}
+
+void PBHelper::SyncCommit(void* protoWrapper) {
+    IPBBase* protoWrapperBase = reinterpret_cast<IPBBase*>(protoWrapper);
+    if (protoWrapperBase) {
+        protoWrapperBase->CommitChanges();
+    }
+}
+std::vector<pb_byte_t> PBHelper::EncodeData(
+    const pb_msgdesc_t* fields, const void* src_struct) {
+    size_t datasz;
+    ESP_LOGV(PROTOTAG, "EncodeData: getting size");
+    if (!pb_get_encoded_size(&datasz, fields, src_struct)) {
+        throw std::runtime_error("Failed to get encoded size.");
+    }
+    ESP_LOGV(PROTOTAG, "EncodeData: size: %d. Encoding", datasz);
+    std::vector<pb_byte_t> data(datasz);
+    pb_ostream_t stream = pb_ostream_from_buffer(data.data(), datasz);
+    if (!pb_encode(&stream, fields, src_struct)) {
+        throw std::runtime_error("Failed to encode data.");
+    }
+    ESP_LOGV(PROTOTAG, "EncodeData: Done");
+    return data;
+}
+
+void PBHelper::DecodeData(
+    std::vector<pb_byte_t> data, const pb_msgdesc_t* fields, void* target, bool noinit) {
+    pb_istream_t stream = pb_istream_from_buffer((uint8_t*)data.data(), data.size());
+    bool result = false;
+
+    // Decode the Protocol Buffers message
+    if (noinit) {
+        ESP_LOGD(PROTOTAG, "Decoding WITHOUT initialization");
+        result = pb_decode_noinit(&stream, fields, data.data());
+    } else {
+        ESP_LOGD(PROTOTAG, "Decoding WITH initialization");
+        result = pb_decode(&stream, fields, target);
+    }
+    if (!result) {
+        throw std::runtime_error(
+            std::string("Failed to decode settings: %s", PB_GET_ERROR(&stream)));
+    }
+    ESP_LOGD(PROTOTAG, "Data decoded");
+}
+void PBHelper::CommitFile(
+    const std::string& filename, const pb_msgdesc_t* fields, const void* src_struct) {
+    size_t datasz = 0;
+    ESP_LOGD(PROTOTAG, "Committing data to file File %s", filename.c_str());
+
+
+    if (!pb_get_encoded_size(&datasz, fields, src_struct)) {
+        throw std::runtime_error("Failed to get encoded size.");
+    }
+
+    if (datasz == 0) {
+        ESP_LOGW(PROTOTAG, "File %s not written. Data size is zero", filename.c_str());
+        return;
+    }
+
+    ESP_LOGD(PROTOTAG, "Committing to file %s", filename.c_str());
+    if (!src_struct) {
+        throw std::runtime_error("Null pointer received.");
+    }
+    FILE* file = fopen(filename.c_str(), "wb");
+    if (file == nullptr) {
+        throw std::runtime_error(std::string("Error opening file ") + filename.c_str());
+    }
+    pb_ostream_t filestream = PB_OSTREAM_SIZING;
+    filestream.callback = &out_file_binding;
+    filestream.state = file;
+    filestream.max_size = SIZE_MAX;
+    ESP_LOGD(PROTOTAG, "Starting file encode for %s", filename.c_str());
+    if (!pb_encode(&filestream, fields, (void*)src_struct)) {
+        fclose(file);
+        throw std::runtime_error("Encoding file failed");
+    }
+    ESP_LOGD(PROTOTAG, "Encoded size: %d", filestream.bytes_written);
+    if (filestream.bytes_written == 0) {
+        ESP_LOGW(PROTOTAG, "Empty structure for file %s", filename.c_str());
+    }
+    fclose(file);
+}
+
+bool PBHelper::IsDataDifferent(
+    const pb_msgdesc_t* fields, const void* src_struct, const void* other_struct) {
+    bool changed = false;
+    try {
+        ESP_LOGV(PROTOTAG, "Encoding Source data");
+        auto src_data = EncodeData(fields, src_struct);
+        ESP_LOGV(PROTOTAG, "Encoding Compared data");
+        auto other_data = EncodeData(fields, other_struct);
+        if (src_data.size() != other_data.size()) {
+            ESP_LOGD(PROTOTAG, "IsDataDifferent: source and target size different: [%d!=%d]",
+                src_data.size(), other_data.size());
+            changed = true;
+        } else if (src_data != other_data) {
+            ESP_LOGD(PROTOTAG, "IsDataDifferent: source and target not the same");
+            changed = true;
+        }
+        if (changed && esp_log_level_get(PROTOTAG) >= ESP_LOG_DEBUG) {
+            ESP_LOGD(PROTOTAG, "Source data: ");
+            dump_data((const uint8_t*)src_data.data(), src_data.size());
+            ESP_LOGD(PROTOTAG, "Compared data: ");
+            dump_data((const uint8_t*)other_data.data(), src_data.size());
+        }
+    } catch (const std::runtime_error& e) {
+        throw std::runtime_error(std::string("Comparison failed: ") + e.what());
+    }
+    ESP_LOGD(PROTOTAG, "IsDataDifferent: %s", changed ? "TRUE" : "FALSE");
+    return changed;
+}
+
+void PBHelper::CopyStructure(
+    const void* src_data, const pb_msgdesc_t* fields, void* target_data) {
+    try {
+        auto src = EncodeData(fields, src_data);
+        ESP_LOGD(PROTOTAG, "Encoded structure to copy has %d bytes", src.size());
+        DecodeData(src, fields, target_data, false);
+    } catch (const std::runtime_error& e) {
+        throw std::runtime_error(std::string("Copy failed: ") + e.what());
+    }
+}
+void PBHelper::LoadFile(
+    const std::string& filename, const pb_msgdesc_t* fields, void* target_data, bool noinit) {
+    struct stat fileInformation;
+    if (!get_file_info(&fileInformation, filename.c_str()) || fileInformation.st_size == 0) {
+        throw FileNotFoundException("filename");
+    }
+
+    FILE* file = fopen(filename.c_str(), "rb");
+    ESP_LOGI(PROTOTAG, "Loading file %s", filename.c_str());
+    if (file == nullptr) {
+        int errNum = errno;
+        ESP_LOGE(
+            PROTOTAG, "Unable to open file: %s. Error: %s", filename.c_str(), strerror(errNum));
+        throw std::runtime_error(
+            "Unable to open file: " + filename + ". Error: " + strerror(errNum));
+    }
+    pb_istream_t filestream = PB_ISTREAM_EMPTY;
+    filestream.callback = &in_file_binding;
+    filestream.state = file;
+    filestream.bytes_left = fileInformation.st_size;
+
+    ESP_LOGV(PROTOTAG, "Starting decode.");
+    bool result = false;
+    if (noinit) {
+        ESP_LOGV(PROTOTAG, "Decoding WITHOUT initialization");
+        result = pb_decode_noinit(&filestream, fields, target_data);
+    } else {
+        ESP_LOGV(PROTOTAG, "Decoding WITH initialization");
+        result = pb_decode(&filestream, fields, target_data);
+    }
+    fclose(file);
+    if (!result) {
+        throw System::DecodeError(PB_GET_ERROR(&filestream));
+    }
+    ESP_LOGV(PROTOTAG, "Decode done.");
+}
+
+bool PBHelper::FileExists(std::string filename) {
+    struct stat fileInformation;
+    ESP_LOGD(PROTOTAG, "Checking if file %s exists", filename.c_str());
+    return get_file_info(&fileInformation, filename.c_str()) && fileInformation.st_size > 0;
+}
+bool PBHelper::FileExists() { return FileExists(filename); }
+bool PBHelper::IsLoading() { return xEventGroupGetBits(_group) & static_cast<int>(Flags::LOAD); }
+void PBHelper::SetLoading(bool active) { SetGroupBit(Flags::LOAD, active); }
+
+bool PBHelper::WaitForCommit(uint8_t retries=2) {
+    auto remain= retries;
+    auto bits = xEventGroupGetBits(_group);
+    bool commit_pending = HasChanges();
+    ESP_LOGD(PROTOTAG, "Entering WaitForCommit bits: %d, changes? %s", bits,
+        commit_pending ? "YES" : "NO");
+    while (commit_pending && remain-->0) {
+        ESP_LOGD(PROTOTAG, "Waiting for config commit ...");
+        auto bits = xEventGroupWaitBits(
+            _group, static_cast<int>(Flags::COMMITTED), pdFALSE, pdTRUE, (MaxDelay * 2) / portTICK_PERIOD_MS);
+        commit_pending = !(bits & static_cast<int>(Flags::COMMITTED));
+        ESP_LOGD(
+            PROTOTAG, "WaitForCommit bits: %d, changes? %s", bits, commit_pending ? "YES" : "NO");
+        if (commit_pending) {
+            ESP_LOGW(PROTOTAG, "Timeout waiting for config commit for [%s]", name.c_str());
+        } else {
+            ESP_LOGI(PROTOTAG, "Changes to %s committed", name.c_str());
+        }
+    }
+    return !commit_pending;
+}
+
+void PBHelper::RaiseChangedAsync() {
+    if(_no_save){
+        ESP_LOGD(PROTOTAG,"Ignoring changes for %s, as it is marked not to be saved", name.c_str());
+    }
+    ESP_LOGI(PROTOTAG, "Changes made to %s", name.c_str());
+    if (IsLoading()) {
+        ESP_LOGD(PROTOTAG, "Ignoring raise modified during load of %s", name.c_str());
+    } else {
+        SetGroupBit(Flags::COMMITTED, false);
+        network_async_commit_protowrapper(
+            static_cast<void*>(static_cast<IPBBase*>(this)));
+    }
+}
+const std::string& PBHelper::GetFileName() { return filename; }
+bool PBHelper::HasChanges() { return !(xEventGroupGetBits(_group) & static_cast<int>(Flags::COMMITTED)); }
+} // namespace PlatformConfig
+
+bool proto_load_file(
+    const char* filename, const pb_msgdesc_t* fields, void* target_data, bool noinit = false) {
+    try {
+        ESP_LOGI(System::PBHelper::PROTOTAG,"Loading file %s",filename);
+        System::PBHelper::LoadFile(std::string(filename), fields, target_data, noinit);
+    } catch (const std::exception& e) {
+        if(!noinit){
+            // initialize the structure 
+            pb_istream_t stream = pb_istream_from_buffer(nullptr, 0);
+            pb_decode(&stream, fields, target_data);
+        }
+        ESP_LOGE(System::PBHelper::PROTOTAG, "Error loading file %s: %s", filename, e.what());
+        return false;
+    }
+    return true;
+}

+ 331 - 0
components/platform_config/PBW.h

@@ -0,0 +1,331 @@
+/*
+ *
+ *      Sebastien L. 2023, sle118@hotmail.com
+ *      Philippe G. 2023, philippe_44@outlook.com
+ *
+ *  This software is released under the MIT License.
+ *  https://opensource.org/licenses/MIT
+ *
+ *  License Overview:
+ *  ----------------
+ *  The MIT License is a permissive open source license. As a user of this software, you are free to:
+ *  - Use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of this software.
+ *  - Use the software for private, commercial, or any other purposes.
+ *
+ *  Conditions:
+ *  - You must include the above copyright notice and this permission notice in all
+ *    copies or substantial portions of the Software.
+ *
+ *  The MIT License offers a high degree of freedom and is well-suited for both open source and
+ *  commercial applications. It places minimal restrictions on how the software can be used,
+ *  modified, and redistributed. For more details on the MIT License, please refer to the link above.
+ */
+#pragma once
+#ifndef LOG_LOCAL_LEVEL
+#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
+#endif
+
+#include "Locking.h"
+#include "MessageDefinition.pb.h"
+#include "accessors.h"
+#include "configuration.pb.h"
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/event_groups.h"
+#include "network_manager.h"
+#include "pb.h"
+#include "pb_decode.h" // Nanopb header for decoding (deserialization)
+#include "pb_encode.h" // Nanopb header for encoding (serialization)
+#include "tools.h"
+#include "tools_spiffs_utils.h"
+#include "bootstate.h"
+#include "State.pb.h"
+
+#ifdef __cplusplus
+// #include <functional>
+#include <sstream>
+#include <vector>
+
+namespace System {
+template <typename T> struct has_target_implementation : std::false_type {};
+
+class FileNotFoundException : public std::runtime_error {
+  public:
+    explicit FileNotFoundException(const std::string& message) : std::runtime_error(message) {}
+};
+class DecodeError : public std::runtime_error {
+  public:
+    explicit DecodeError(const std::string& message) : std::runtime_error(message) {}
+};
+class IPBBase {
+  public:
+    virtual void CommitChanges() = 0;
+    virtual ~IPBBase() {}
+};
+class PBHelper : public IPBBase {
+
+  protected:
+    const pb_msgdesc_t* _fields;
+    EventGroupHandle_t _group;
+    std::string name;
+    std::string filename;
+    static const int MaxDelay = 1000;
+    Locking _lock;
+    size_t _datasize;    
+    bool _no_save;
+
+  public:
+    enum class Flags { COMMITTED = BIT0, LOAD = BIT1 };
+    bool SetGroupBit(Flags flags, bool flag);
+    static const char* PROTOTAG;
+    std::string& GetName() { return name; }
+    const char* GetCName() { return name.c_str(); }
+    static std::string GetDefFileName(std::string name){
+        return std::string(spiffs_base_path) + "/data/def_" + name + ".bin";
+    }
+    std::string GetDefFileName(){
+        return GetDefFileName(this->name);
+    }
+    size_t GetDataSize(){
+        return  _datasize;
+    }
+    PBHelper(std::string name, const pb_msgdesc_t* fields, size_t defn_size, size_t datasize, bool no_save = false)
+        : _fields(fields), _group(xEventGroupCreate()), name(std::move(name)), _lock(this->name),_datasize(datasize), _no_save(no_save) {
+        sys_message_def definition = sys_message_def_init_default;
+        bool savedef = false;
+        ESP_LOGD(PROTOTAG,"Getting definition file name");
+        auto deffile = GetDefFileName();
+        ESP_LOGD(PROTOTAG,"Instantiating with definition size %d and data size %d", defn_size,datasize);
+        
+        try {
+            PBHelper::LoadFile(deffile, &sys_message_def_msg, static_cast<void*>(&definition));
+            if (definition.data->size != defn_size || definition.datasize != _datasize) {
+                ESP_LOGW(PROTOTAG, "Structure definition %s has changed", this->name.c_str());
+                if (!is_recovery_running) {
+                    savedef = true;
+                    pb_release(&sys_message_def_msg, &definition);
+                } else {
+                    ESP_LOGW(PROTOTAG, "Using existing definition for recovery");
+                    _fields = reinterpret_cast<const pb_msgdesc_t*>(definition.data->bytes);
+                    _datasize = definition.datasize;
+                }
+            }
+        } catch (const FileNotFoundException& e) {
+            savedef = true;
+        }
+
+        if (savedef) {
+            ESP_LOGW(PROTOTAG, "Saving definition for structure %s", this->name.c_str());
+            auto data = (pb_bytes_array_t*)malloc_init_external(sizeof(pb_bytes_array_t)+defn_size);
+            memcpy(&data->bytes, fields, defn_size);
+            data->size = defn_size;
+            definition.data = data;
+            definition.datasize = _datasize;
+            ESP_LOGD(PROTOTAG,"Committing structure with %d bytes ",data->size);
+            PBHelper::CommitFile(deffile, &sys_message_def_msg, &definition);
+            ESP_LOGD(PROTOTAG,"Releasing memory");
+            free(data);
+        }
+    }
+    void ResetModified();
+    static void SyncCommit(void* protoWrapper);
+    static void CommitFile(const std::string& filename, const pb_msgdesc_t* fields, const void* src_struct);
+    static bool IsDataDifferent(const pb_msgdesc_t* fields, const void* src_struct, const void* other_struct);
+
+    static void CopyStructure(const void* src_data, const pb_msgdesc_t* fields, void* target_data);
+    static void LoadFile(const std::string& filename, const pb_msgdesc_t* fields, void* target_data, bool noinit = false);
+    static std::vector<pb_byte_t> EncodeData(const pb_msgdesc_t* fields, const void* src_struct);
+    static void DecodeData(std::vector<pb_byte_t> data, const pb_msgdesc_t* fields, void* target, bool noinit = false);
+    bool FileExists(std::string filename);
+    bool FileExists();
+    bool IsLoading();
+    void SetLoading(bool active);
+    bool WaitForCommit(uint8_t retries );
+    void RaiseChangedAsync();
+    const std::string& GetFileName();
+    bool HasChanges();
+};
+template <typename T> class PB : public PBHelper {
+  private:
+    T *_root;
+
+    // Generic _setTarget implementation
+    void _setTarget(std::string target, std::false_type) { ESP_LOGE(PROTOTAG, "Setting target not implemented for %s", name.c_str()); }
+
+    // Special handling for sys_config
+    void _setTarget(std::string target, std::true_type) {
+        if (_root->target) {
+            free(_root->target);
+        }
+        _root->target = strdup_psram(target.c_str());
+    }
+    std::string _getTargetName(std::false_type) { return ""; }
+    std::string _getTargetName(std::true_type) { return STR_OR_BLANK(_root->target); }
+
+  public:
+    // Accessor for the underlying structure
+    T& Root() { return *_root; }
+    // Const accessor for the underlying structure
+
+    const T& Root() const { return *_root; }
+    T* get()  { return _root; }
+    const T* get() const { return (const T*)_root; }
+
+    // Constructor
+    explicit PB(std::string name, const pb_msgdesc_t* fields, size_t defn_size, bool no_save = false) : 
+        PBHelper(std::move(name), fields,defn_size, sizeof(T), no_save) {
+        ESP_LOGD(PROTOTAG, "Instantiating PB class for %s with data size %d", this->name.c_str(), sizeof(T));
+        ResetModified();
+        filename = std::string(spiffs_base_path) + "/data/" + this->name + ".bin";
+        _root = (T*)(malloc_init_external(_datasize));
+        memset(_root, 0x00, sizeof(_datasize));
+    }
+
+    std::string GetTargetName() { return _getTargetName(has_target_implementation<T>{}); }
+    void SetTarget(std::string targetname, bool skip_commit = false) {
+        std::string newtarget = trim(targetname);
+        std::string currenttarget = trim(GetTargetName());
+        ESP_LOGD(PROTOTAG, "SetTarget called with %s", newtarget.c_str());
+        if (newtarget == currenttarget && !newtarget.empty()) {
+            ESP_LOGD(PROTOTAG, "Target name %s not changed for %s", currenttarget.c_str(), name.c_str());
+        } else if (newtarget.empty() && !currenttarget.empty()) {
+            ESP_LOGW(PROTOTAG, "Target name %s was removed for %s ", currenttarget.c_str(), name.c_str());
+        }
+        ESP_LOGI(PROTOTAG, "Setting target %s for %s", newtarget.c_str(), name.c_str());
+        _setTarget(newtarget, has_target_implementation<T>{});
+        if (!skip_commit) {
+            ESP_LOGD(PROTOTAG, "Raising changed flag to commit new target name.");
+            RaiseChangedAsync();
+        } else {
+            SetGroupBit(Flags::COMMITTED, false);
+        }
+    }
+    std::string GetTargetFileName() {
+        if (GetTargetName().empty()) {
+            return "";
+        }
+        auto target_name = GetTargetName();
+        return std::string(spiffs_base_path) + "/targets/" + toLowerStr(target_name) + "/" + name + ".bin";
+    }
+    void Reinitialize(bool skip_target = false, bool commit = false, std::string target_name = "") {
+        ESP_LOGW(PROTOTAG, "Initializing %s", name.c_str());
+        pb_istream_t stream = PB_ISTREAM_EMPTY;
+        // initialize blank structure by
+        // decoding a dummy stream
+        pb_decode(&stream, _fields, _root);
+        SetLoading(true);
+        try {
+            std::string fullpath = std::string(spiffs_base_path) + "/defaults/" + this->name + ".bin";
+            ESP_LOGD(PROTOTAG, "Attempting to load defaults file for %s", fullpath.c_str());
+            PBHelper::LoadFile(fullpath.c_str(), _fields, static_cast<void*>(_root), true);
+        } catch (FileNotFoundException&) {
+            ESP_LOGW(PROTOTAG, "No defaults found for %s", name.c_str());
+        } catch (std::runtime_error& e) {
+            ESP_LOGE(PROTOTAG, "Error loading Target %s overrides file: %s", GetTargetName().c_str(), e.what());
+        }
+        SetLoading(false);
+        if (!skip_target) {
+            if (!target_name.empty()) {
+                SetTarget(target_name, true);
+            }
+            LoadTargetValues();
+        }
+        if (commit) {
+            CommitChanges();
+        }
+    }
+    void LoadFile(bool skip_target = false, bool noinit = false) {
+        SetLoading(true);
+        PBHelper::LoadFile(filename, _fields, static_cast<void*>(_root), noinit);
+        SetLoading(false);
+        if (!skip_target) {
+            LoadTargetValues();
+        }
+    }
+    void LoadTargetValues() {
+        ESP_LOGD(PROTOTAG, "Loading target %s values for %s", GetTargetName().c_str(), name.c_str());
+        if (GetTargetFileName().empty()) {
+            ESP_LOGD(PROTOTAG, "No target file to load for %s", name.c_str());
+            return;
+        }
+        try {
+            // T old;
+            // CopyTo(old);
+            ESP_LOGI(PROTOTAG, "Loading target %s values for %s", GetTargetName().c_str(), name.c_str());
+            PBHelper::LoadFile(GetTargetFileName(), _fields, static_cast<void*>(_root), true);
+            // don't commit the values here, as it doesn't work well with
+            // repeated values
+            // if (*this != old) {
+            //     ESP_LOGI(PROTOTAG, "Changes detected from target values.");
+            //     RaiseChangedAsync();
+            // }
+            SetGroupBit(Flags::COMMITTED, false);
+
+        } catch (FileNotFoundException&) {
+            ESP_LOGD(PROTOTAG, "Target %s overrides file not found for %s", GetTargetName().c_str(), name.c_str());
+        } catch (std::runtime_error& e) {
+            ESP_LOGE(PROTOTAG, "Error loading Target %s overrides file: %s", GetTargetName().c_str(), e.what());
+        }
+    }
+    void CommitChanges() override {
+        ESP_LOGI(PROTOTAG, "Committing %s to flash.", name.c_str());
+        if (!_lock.Lock()) {
+            ESP_LOGE(PROTOTAG, "Unable to lock config for commit ");
+            return;
+        }
+        ESP_LOGV(PROTOTAG, "Config Locked. Committing");
+        try {
+            CommitFile(filename, _fields, _root);
+        } catch (...) {
+        }
+        ResetModified();
+        _lock.Unlock();
+        ESP_LOGI(PROTOTAG, "Done committing %s to flash.", name.c_str());
+    }
+    bool Lock() { return _lock.Lock(); }
+    void Unlock() { return _lock.Unlock(); }
+    std::vector<pb_byte_t> Encode() {
+        auto data = std::vector<pb_byte_t>();
+        if (!_lock.Lock()) {
+            throw std::runtime_error("Unable to lock object");
+        }
+        data = EncodeData(_fields, this->_root);
+        _lock.Unlock();
+        return data;
+    }
+
+    void CopyTo(T& target_data) {
+        if (!_lock.Lock()) {
+            ESP_LOGE(PROTOTAG, "Lock failed for %s", name.c_str());
+            throw std::runtime_error("Lock failed ");
+        }
+        CopyStructure(_root, _fields, &target_data);
+        _lock.Unlock();
+    }
+    void CopyFrom(const T& source_data) {
+        if (!_lock.Lock()) {
+            ESP_LOGE(PROTOTAG, "Lock failed for %s", name.c_str());
+            throw std::runtime_error("Lock failed ");
+        }
+        CopyStructure(&source_data, _fields, _root);
+        _lock.Unlock();
+    }
+
+    bool operator!=(const T& other) const { return IsDataDifferent(_fields, _root, &other); }
+    bool operator==(const T& other) const { return !IsDataDifferent(_fields, _root, &other); }
+    void DecodeData(const std::vector<pb_byte_t> data, bool noinit = false) { PBHelper::DecodeData(data, _fields, (void*)_root, noinit); }
+
+    ~PB() { vEventGroupDelete(_group); };
+};
+template <> struct has_target_implementation<sys_config> : std::true_type {};
+
+template <> struct has_target_implementation<sys_state_data> : std::true_type {};
+
+} // namespace PlatformConfig
+extern "C" {
+#endif
+bool proto_load_file(const char* filename, const pb_msgdesc_t* fields, void* target_data, bool noinit);
+
+#ifdef __cplusplus
+}
+#endif

+ 845 - 0
components/platform_config/WifiList.cpp

@@ -0,0 +1,845 @@
+#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
+#include "WifiList.h"
+#include "Config.h"
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include <memory>
+
+static const char* TAG_CRED_MANAGER = "credentials_manager";
+bool sys_status_wifi_callback(pb_istream_t* istream, pb_ostream_t* ostream, const pb_field_iter_t* field) {
+    return sys_net_config_callback(istream, ostream, field);
+}
+bool sys_net_config_callback(pb_istream_t* istream, pb_ostream_t* ostream, const pb_field_iter_t* field) {
+    WifiList** managerPtr = static_cast<WifiList**>(field->pData);
+    WifiList* manager = *managerPtr;
+
+    if (istream != NULL && (field->tag == sys_net_config_credentials_tag || field->tag == sys_status_wifi_scan_result_tag)) {
+        if (manager == nullptr) {
+            ESP_LOGE(TAG_CRED_MANAGER, "Invalid pointer to wifi list manager");
+            return false;
+        }
+        ESP_LOGV(TAG_CRED_MANAGER, "Decoding credentials");
+        sys_net_wifi_entry entry = sys_net_wifi_entry_init_default;
+        if (!pb_decode(istream, &sys_net_wifi_entry_msg, &entry)) return false;
+        printf("\nFound ssid %s, password %s\n", entry.ssid, entry.password);
+        try {
+            manager->AddUpdate(entry); // Add to the manager
+        } catch (const std::exception& e) {
+            ESP_LOGE(TAG_CRED_MANAGER, "decode exception: %s", e.what());
+            return false;
+        }
+        ESP_LOGV(TAG_CRED_MANAGER, "Credentials decoding completed");
+    } else if (ostream != NULL && (field->tag == sys_net_config_credentials_tag || field->tag == sys_status_wifi_scan_result_tag)) {
+        if (manager == nullptr) {
+            ESP_LOGV(TAG_CRED_MANAGER, "No wifi entries manager instance. nothing to encode");
+            return true;
+        }
+        ESP_LOGV(TAG_CRED_MANAGER, "Encoding %d access points", manager->GetCount());
+
+        for (int i = 0; i < manager->GetCount(); i++) {
+            ESP_LOGV(TAG_CRED_MANAGER, "Encoding credential #%d: SSID: %s, PASS: %s", i, manager->GetIndex(i)->ssid, manager->GetIndex(i)->password);
+            if (!pb_encode_tag_for_field(ostream, field)) {
+                return false;
+            }
+            if (!pb_encode_submessage(ostream, &sys_net_wifi_entry_msg, manager->GetIndex(i))) {
+                return false;
+            }
+        }
+        ESP_LOGV(TAG_CRED_MANAGER, "Credentials encoding completed");
+    }
+
+    return true;
+}
+std::string WifiList::GetBSSID(const wifi_event_sta_connected_t* evt) {
+    char buffer[18]={};
+    FormatBSSID(buffer, sizeof(buffer), evt->bssid);
+    ESP_LOGD(TAG_CRED_MANAGER, "Formatted BSSID: %s", buffer);
+    return std::string(buffer);
+}
+bool WifiList::OffsetTimeStamp(google_protobuf_Timestamp* ts) {
+    timeval tts;
+    google_protobuf_Timestamp gts;
+    gettimeofday((struct timeval*)&tts, NULL);
+    gts.nanos = tts.tv_usec * 1000;
+    gts.seconds = tts.tv_sec;
+    if (tts.tv_sec < 1704143717) {
+        ESP_LOGE(TAG_CRED_MANAGER, "Error updating time stamp. Clock doesn't seem right");
+        return false;
+    }
+    if (ts && ts->seconds < 1704143717) {
+        ESP_LOGV(TAG_CRED_MANAGER, "Updating time stamp based on new clock value");
+        ts->seconds = gts.seconds - ts->seconds;
+        ts->nanos = gts.nanos - ts->nanos;
+        return true;
+    }
+    ESP_LOGD(TAG_CRED_MANAGER, "Time stamp already updated. Skipping");
+    return false;
+}
+bool WifiList::UpdateTimeStamp(google_protobuf_Timestamp* ts, bool& has_flag_val) {
+    ESP_RETURN_ON_FALSE(ts != nullptr, false, TAG_CRED_MANAGER, "Null pointer!");
+    bool changed = false;
+    timeval tts;
+    google_protobuf_Timestamp gts;
+    gettimeofday((struct timeval*)&tts, NULL);
+    gts.nanos = tts.tv_usec * 1000;
+    gts.seconds = tts.tv_sec;
+    if (!has_flag_val || gts.nanos != ts->nanos || gts.seconds != ts->seconds) {
+        ts->seconds = gts.seconds;
+        ts->nanos = gts.nanos;
+        has_flag_val = true;
+        changed = true;
+    }
+    return changed;
+}
+
+bool WifiList::isEmpty(const char* str, size_t len) {
+    for (size_t i = 0; i < len; ++i) {
+        if (str[i] != '\0') {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool WifiList::Update(const wifi_ap_record_t* ap, bool connected) {
+    if (!Lock()) {
+        throw std::runtime_error("Lock failed");
+    }
+    auto existing = Get(ap);
+    if (!existing) {
+        return false;
+    }
+    auto updated = ToSTAEntry(ap);
+    updated.connected = connected;
+    bool changed = Update(*existing, updated);
+    Release(&updated);
+    Unlock();
+    return changed;
+}
+bool WifiList::Update(sys_net_wifi_entry& existingEntry, sys_net_wifi_entry& updated) {
+
+    // Check if any relevant fields have changed
+    bool hasChanged = false;
+    if (!isEmpty(updated.ssid, sizeof(updated.ssid)) && memcmp(existingEntry.ssid, updated.ssid, sizeof(existingEntry.ssid)) != 0) {
+        memcpy(existingEntry.ssid, updated.ssid, sizeof(existingEntry.ssid));
+        hasChanged = true;
+    }
+
+    // Check and copy BSSID if the compared BSSID is not empty
+    if (!isEmpty(updated.bssid, sizeof(updated.bssid)) && strcmp(updated.bssid, "00:00:00:00:00:00") != 0 &&
+        memcmp(existingEntry.bssid, updated.bssid, sizeof(existingEntry.bssid)) != 0) {
+        memcpy(existingEntry.bssid, updated.bssid, sizeof(existingEntry.bssid));
+        hasChanged = true;
+    }
+
+    // Check and copy password if the compared password is not empty
+    if (!isEmpty(updated.password, sizeof(updated.password)) &&
+        memcmp(existingEntry.password, updated.password, sizeof(existingEntry.password)) != 0) {
+        memcpy(existingEntry.password, updated.password, sizeof(existingEntry.password));
+        hasChanged = true;
+    }
+
+    if (existingEntry.channel != updated.channel && updated.channel > 0) {
+        existingEntry.channel = updated.channel;
+        hasChanged = true;
+    }
+
+    if (existingEntry.auth_type != updated.auth_type && updated.auth_type != sys_net_auth_types_AUTH_UNKNOWN) {
+        existingEntry.auth_type = updated.auth_type;
+        hasChanged = true;
+    }
+
+    if (areRadioTypesDifferent(existingEntry.radio_type, existingEntry.radio_type_count, updated.radio_type, updated.radio_type_count) &&
+        updated.radio_type_count > 0 && updated.radio_type[0] != sys_net_radio_types_UNKNOWN) {
+
+        if (existingEntry.radio_type != nullptr) {
+            // Free the old radio_type array if it exists
+            delete[] existingEntry.radio_type;
+        }
+        // Allocate new memory and copy the updated radio types
+        existingEntry.radio_type_count = updated.radio_type_count;
+        existingEntry.radio_type = new sys_net_radio_types[updated.radio_type_count];
+        std::copy(updated.radio_type, updated.radio_type + updated.radio_type_count, existingEntry.radio_type);
+        hasChanged = true;
+    }
+
+    if (updated.has_last_try) {
+        if (memcmp(&existingEntry.last_try, &updated.last_try, sizeof(existingEntry.last_try)) != 0) {
+            memcpy(&existingEntry.last_try, &updated.last_try, sizeof(existingEntry.last_try));
+            hasChanged = true;
+        }
+    }
+    if (updated.has_last_seen) {
+        if (memcmp(&existingEntry.last_seen, &updated.last_seen, sizeof(existingEntry.last_seen)) != 0) {
+            memcpy(&existingEntry.last_seen, &updated.last_seen, sizeof(existingEntry.last_seen));
+            hasChanged = true;
+        }
+    }
+    if (existingEntry.has_last_seen != updated.has_last_seen && updated.has_last_seen) {
+        existingEntry.has_last_seen = updated.has_last_seen;
+        hasChanged = true;
+    }
+    if (existingEntry.has_last_try != updated.has_last_try && updated.has_last_try) {
+        existingEntry.has_last_try = updated.has_last_try;
+        hasChanged = true;
+    }
+
+    if (existingEntry.connected != updated.connected && updated.connected) {
+        existingEntry.connected = updated.connected;
+        hasChanged = true;
+    }
+
+    if (existingEntry.rssi != updated.rssi && updated.rssi != 0) {
+        existingEntry.rssi = updated.rssi;
+        hasChanged = true;
+    }
+
+    return hasChanged;
+}
+
+std::string WifiList::formatRadioTypes(const sys_net_radio_types* radioTypes, pb_size_t count) {
+    std::string result;
+
+    for (pb_size_t i = 0; i < count; ++i) {
+        switch (radioTypes[i]) {
+        case sys_net_radio_types_PHY_11B:
+            result += "B";
+            break;
+        case sys_net_radio_types_PHY_11G:
+            result += "G";
+            break;
+        case sys_net_radio_types_PHY_11N:
+            result += "N";
+            break;
+        case sys_net_radio_types_LR:
+            result += "L";
+            break;
+        case sys_net_radio_types_WPS:
+            result += "W";
+            break;
+        case sys_net_radio_types_FTM_RESPONDER:
+            result += "FR";
+            break;
+        case sys_net_radio_types_FTM_INITIATOR:
+            result += "FI";
+            break;
+        case sys_net_radio_types_UNKNOWN:
+        default:
+            result += "U";
+            break;
+        }
+        if (i < count - 1) {
+            result += ",";
+        }
+    }
+
+    return result;
+}
+
+bool WifiList::Update(const wifi_sta_config_t* sta, bool connected) {
+    if (!sta) {
+        return false; // Invalid input
+    }
+    if (!Lock()) {
+        throw std::runtime_error("Lock failed");
+    }
+    sys_net_wifi_entry* existingEntry = Get(sta);
+
+    // If the entry does not exist, nothing to update
+    if (!existingEntry) {
+        Unlock();
+        return false;
+    }
+    auto updated = ToSTAEntry(sta);
+    // Check if any relevant fields have changed
+    bool hasChanged = false;
+
+    if (strlen(updated.ssid) > 0 && memcmp(existingEntry->ssid, updated.ssid, sizeof(existingEntry->ssid)) != 0) {
+        memcpy(existingEntry->ssid, updated.ssid, sizeof(existingEntry->ssid));
+        hasChanged = true;
+    }
+
+    if (strlen(updated.bssid) > 0 && strcmp(updated.bssid, "00:00:00:00:00:00") != 0 &&
+        memcmp(existingEntry->bssid, updated.bssid, sizeof(existingEntry->bssid)) != 0) {
+        memcpy(existingEntry->bssid, updated.bssid, sizeof(existingEntry->bssid));
+        hasChanged = true;
+    }
+
+    if (existingEntry->channel != updated.channel) {
+        existingEntry->channel = updated.channel;
+        hasChanged = true;
+    }
+
+    if (existingEntry->auth_type != updated.auth_type && updated.auth_type != sys_net_auth_types_AUTH_UNKNOWN) {
+        existingEntry->auth_type = updated.auth_type;
+        hasChanged = true;
+    }
+
+    if (areRadioTypesDifferent(existingEntry->radio_type, existingEntry->radio_type_count, updated.radio_type, updated.radio_type_count) &&
+        updated.radio_type_count > 0 && updated.radio_type[0] != sys_net_radio_types_UNKNOWN) {
+        // Free the old radio_type array if it exists
+        delete[] existingEntry->radio_type;
+
+        // Allocate new memory and copy the updated radio types
+        existingEntry->radio_type_count = updated.radio_type_count;
+        existingEntry->radio_type = new sys_net_radio_types[updated.radio_type_count];
+        std::copy(updated.radio_type, updated.radio_type + updated.radio_type_count, existingEntry->radio_type);
+
+        hasChanged = true;
+    }
+
+    if (updated.has_last_try) {
+        if (memcmp(&existingEntry->last_try, &updated.last_try, sizeof(existingEntry->last_try)) != 0) {
+            memcpy(&existingEntry->last_try, &updated.last_try, sizeof(existingEntry->last_try));
+            hasChanged = true;
+        }
+    }
+
+    if (updated.has_last_seen) {
+        if (memcmp(&existingEntry->last_seen, &updated.last_seen, sizeof(existingEntry->last_seen)) != 0) {
+            memcpy(&existingEntry->last_seen, &updated.last_seen, sizeof(existingEntry->last_seen));
+            hasChanged = true;
+        }
+    }
+    if (existingEntry->has_last_try != updated.has_last_try) {
+        existingEntry->has_last_try = updated.has_last_try;
+        hasChanged = true;
+    }
+    if (existingEntry->has_last_seen != updated.has_last_seen) {
+        existingEntry->has_last_seen = updated.has_last_seen;
+        hasChanged = true;
+    }
+    if (existingEntry->connected != (connected | updated.connected)) {
+        existingEntry->connected = connected | updated.connected;
+        hasChanged = true;
+    }
+
+    if (strlen(updated.password) == 0 && strlen(existingEntry->password) > 0) {
+        ESP_LOGW(TAG_CRED_MANAGER, "Updated password is empty, while existing password is %s for %s. Ignoring.", existingEntry->password,
+            existingEntry->ssid);
+    } else {
+        if (memcmp(existingEntry->password, updated.password, sizeof(existingEntry->password)) != 0) {
+            memcpy(existingEntry->password, updated.password, sizeof(existingEntry->password));
+            hasChanged = true;
+        }
+    }
+
+    if (existingEntry->rssi != updated.rssi && updated.rssi != 0) {
+        existingEntry->rssi = updated.rssi;
+        hasChanged = true;
+    }
+    Release(&updated);
+    Unlock();
+    return hasChanged;
+}
+sys_net_wifi_entry WifiList::ToSTAEntry(const sys_net_wifi_entry* sta) {
+  if (!sta) {
+        throw std::runtime_error("Null STA entry provided");
+    }
+    sys_net_wifi_entry result = *sta;
+    if (result.radio_type_count > 0) {
+        std::unique_ptr<sys_net_radio_types[]> newRadioTypes(new sys_net_radio_types[result.radio_type_count]);
+        if (!newRadioTypes) {
+            throw std::runtime_error("Failed to allocate memory for radio types");
+        }
+        memcpy(newRadioTypes.get(), sta->radio_type, sizeof(sys_net_radio_types) * result.radio_type_count);
+        result.radio_type = newRadioTypes.release();
+    } else {
+        result.radio_type = nullptr;
+    }
+
+    ESP_LOGD(TAG_CRED_MANAGER, "ToSTAEntry: SSID: %s, PASS: %s", result.ssid, result.password);
+    return result;
+}
+sys_net_wifi_entry WifiList::ToSTAEntry(const wifi_sta_config_t* sta, sys_net_auth_types auth_type) {
+    return ToSTAEntry(sta, GetRadioTypes(nullptr), auth_type);
+}
+sys_net_wifi_entry WifiList::ToSTAEntry(
+    const wifi_sta_config_t* sta, const std::list<sys_net_radio_types>& radio_types, sys_net_auth_types auth_type) {
+    sys_net_wifi_entry item = sys_net_wifi_entry_init_default;
+    ESP_LOGD(TAG_CRED_MANAGER,"%s (sta_config)","toSTAEntry");
+    auto result = ToSTAEntry(sta, item, radio_types);
+    ESP_LOGV(TAG_CRED_MANAGER, "ToSTAEntry: SSID: %s, PASS: %s", result.ssid, result.password);
+    return result;
+}
+sys_net_wifi_entry& WifiList::ToSTAEntry(const wifi_ap_record_t* ap, sys_net_wifi_entry& item) {
+    if (ap) {
+        auto radioTypes = GetRadioTypes(ap);
+        item.radio_type_count=radioTypes.size();
+        item.radio_type = new sys_net_radio_types[item.radio_type_count];
+        int i = 0;
+        for (const auto& type : radioTypes) {
+            item.radio_type[i++] = type;
+        }
+        item.auth_type = GetAuthType(ap);
+        FormatBSSID(ap, item);
+        item.channel = ap->primary;
+        item.rssi = ap->rssi;
+        strncpy(item.ssid, GetSSID(ap).c_str(), sizeof(item.ssid));
+    }
+    return item;
+}
+sys_net_wifi_entry WifiList::ToSTAEntry(const wifi_ap_record_t* ap) {
+    sys_net_wifi_entry item = sys_net_wifi_entry_init_default;
+    return ToSTAEntry(ap, item);
+}
+sys_net_wifi_entry& WifiList::ToSTAEntry(
+    const wifi_sta_config_t* sta, sys_net_wifi_entry& item, const std::list<sys_net_radio_types>& radio_types, sys_net_auth_types auth_type) {
+    if (!sta) {
+        ESP_LOGE(TAG_CRED_MANAGER, "Invalid access point entry");
+        return item;
+    }
+    
+    std::string ssid = GetSSID(sta);         // Convert SSID to std::string
+    std::string password = GetPassword(sta); // Convert password to std::string
+
+    if (ssid.empty()) {
+        ESP_LOGE(TAG_CRED_MANAGER, "Invalid access point ssid");
+        return item;
+    }
+    memset(item.ssid, 0x00, sizeof(item.ssid));
+    memset(item.password, 0x00, sizeof(item.password));
+    strncpy(item.ssid, ssid.c_str(), sizeof(item.ssid));             // Copy SSID
+    strncpy(item.password, password.c_str(), sizeof(item.password)); // Copy password
+    if (LOG_LOCAL_LEVEL > ESP_LOG_DEBUG) {
+        WifiList::FormatBSSID(item.bssid, sizeof(item.bssid), sta->bssid); // Format BSSID
+    }
+    item.channel = sta->channel;
+
+    item.auth_type = auth_type;
+
+    // Handle the radio_type array
+    if (item.radio_type != nullptr) {
+        delete[] item.radio_type; // Free existing array if any
+        item.radio_type_count = 0;
+    }
+    item.radio_type_count = radio_types.size();
+    item.radio_type = new sys_net_radio_types[item.radio_type_count];
+    int i = 0;
+    for (const auto& type : radio_types) {
+        item.radio_type[i++] = type;
+    }
+
+    ESP_LOGV(TAG_CRED_MANAGER, "ToSTAEntry wifi : %s, password: %s", item.ssid, item.password);
+    return item;
+}
+bool WifiList::RemoveCredential(const wifi_sta_config_t* sta) { return RemoveCredential(GetSSID(sta)); }
+bool WifiList::RemoveCredential(const std::string& ssid) {
+    auto it = credentials_.find(ssid);
+    if (it != credentials_.end()) {
+        // Release any dynamically allocated fields in the structure
+        Release(&it->second);
+        // Erase the entry from the map
+        credentials_.erase(it);
+        return true;
+    }
+    return false;
+}
+
+void WifiList::Clear() {
+    if (Lock()) {
+        for (auto& e : credentials_) {
+            Release( &e.second);
+        }
+        credentials_.clear();
+        Unlock();
+    }
+}
+bool WifiList::ResetRSSI() {
+    if (!Lock()) {
+        ESP_LOGE(TAG_CRED_MANAGER, "Unable to lock structure %s", name_.c_str());
+        return false;
+    }
+    for (auto& e : credentials_) {
+        e.second.rssi = 0;
+    }
+    Unlock();
+    return true;
+}
+bool WifiList::ResetConnected() {
+    if (!Lock()) {
+        throw std::runtime_error("Lock failed");
+    }
+    for (auto& e : credentials_) {
+        e.second.connected = false;
+    }
+    Unlock();
+    return true;
+}
+sys_net_wifi_entry* WifiList::Get(const std::string& ssid) {
+    auto it = credentials_.find(ssid);
+    if (it != credentials_.end()) {
+        return &(it->second);
+    }
+    return nullptr;
+}
+const sys_net_wifi_entry* WifiList::GetConnected() {
+    if (!Lock()) {
+        ESP_LOGE(TAG_CRED_MANAGER, "Unable to lock structure %s", name_.c_str());
+        return nullptr;
+    }
+    for (auto& e : credentials_) {
+        if (e.second.connected) {
+            return &e.second;
+        }
+    }
+    Unlock();
+    return nullptr;
+}
+bool WifiList::SetConnected(const wifi_event_sta_connected_t* evt, bool connected) {
+    auto ssid = GetSSID(evt);
+    auto bssid = GetBSSID(evt);
+    auto existing = Get(ssid);
+    if (existing) {
+        if (bssid != existing->bssid || existing->connected != connected || existing->auth_type != GetAuthType(evt->authmode) ||
+            existing->channel != evt->channel) {
+            ResetConnected();
+            if (!Lock()) {
+                throw std::runtime_error("Lock failed");
+            }            
+            strncpy(existing->bssid, bssid.c_str(), sizeof(existing->bssid));
+            existing->connected = connected;
+            existing->auth_type = GetAuthType(evt->authmode);
+            existing->channel = evt->channel;
+            config_raise_changed(false);
+            Unlock();
+            return true;
+        }
+    } else {
+        ESP_LOGE(TAG_CRED_MANAGER, "Cannot set unknown ssid %s as connected", ssid.c_str());
+    }
+    return false;
+}
+
+
+
+sys_net_wifi_entry& WifiList::AddUpdate(const wifi_sta_config_t* sta, sys_net_auth_types auth_type) {
+    return AddUpdate(sta, GetRadioTypes(nullptr), auth_type);
+}
+sys_net_wifi_entry& WifiList::AddUpdate(
+    const wifi_sta_config_t* sta, const std::list<sys_net_radio_types>& radio_types, sys_net_auth_types auth_type) {
+    auto ssid = GetSSID(sta);
+    
+    if (!Exists(sta)) {
+        auto entry = ToSTAEntry(sta, radio_types, auth_type);
+        ESP_LOGD(TAG_CRED_MANAGER, "Added new entry %s to list %s", ssid.c_str(), name_.c_str());
+        if (!Lock()) {
+            throw std::runtime_error("Lock failed");
+        }
+        credentials_[ssid] = entry;
+        Unlock();
+    } else {
+        Update(sta);
+    }
+    ESP_LOGV(TAG_CRED_MANAGER, "AddUpdate: SSID: %s, PASS: %s", ssid.c_str(), credentials_[ssid].password);
+    return credentials_[ssid];
+}
+bool WifiList::UpdateFromClock() {
+    bool changed = false;
+    if (Lock()) {
+
+        for (auto iter = credentials_.begin(); iter != credentials_.end(); ++iter) {
+            bool entrychanged = false;
+            if (iter->second.has_last_seen) {
+                entrychanged |= OffsetTimeStamp(&iter->second.last_seen);
+            }
+            if (iter->second.has_last_try) {
+                entrychanged |= OffsetTimeStamp(&iter->second.last_try);
+            }
+            if (entrychanged) {
+                ESP_LOGD(TAG_CRED_MANAGER, "Updated from clock");
+                PrintWifiSTAEntry(iter->second);
+            }
+            changed |= entrychanged;
+        }
+
+        Unlock();
+    }
+    return changed;
+}
+void WifiList::PrintTimeStamp(const google_protobuf_Timestamp* timestamp) {
+    if (timestamp == NULL) {
+        printf("Timestamp is NULL\n");
+        return;
+    }
+
+    char buffer[80];
+
+    // Check for special case of time == 0
+    if (timestamp->seconds == 0) {
+        if (timestamp->nanos != 0) {
+            printf("nanos not empty!");
+        }
+        snprintf(buffer, sizeof(buffer), "%-26s", "N/A");
+    }
+    // Check for timestamps less than 1704143717 (2024-01-01)
+    else if (timestamp->seconds > 0 && timestamp->seconds < 1704143717) {
+        // Convert seconds to time_t for use with localtime
+        time_t rawtime = (time_t)timestamp->seconds;
+        struct tm* timeinfo = localtime(&rawtime);
+
+        strftime(buffer, sizeof(buffer), "%H:%M:%S", timeinfo);
+    } else {
+        // Convert seconds to time_t for use with localtime
+        time_t rawtime = (time_t)timestamp->seconds;
+        struct tm* timeinfo = localtime(&rawtime);
+
+        strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", timeinfo);
+    }
+
+    printf("%-26s", buffer);
+}
+bool WifiList::UpdateLastTry(const std::string ssid) {
+    if (!Lock()) {
+        throw std::runtime_error("Lock failed");
+    }
+    auto entry = Get(ssid);
+    ESP_RETURN_ON_FALSE(entry != nullptr, false, TAG_CRED_MANAGER, "Unknown ssid %s", ssid.c_str());
+    ESP_RETURN_ON_FALSE(entry->ssid != nullptr, false, TAG_CRED_MANAGER, "Invalid pointer!");
+    bool changed = UpdateLastTry(entry);
+    Unlock();
+    return changed;
+}
+bool WifiList::UpdateLastTry(sys_net_wifi_entry* entry) {
+    ESP_RETURN_ON_FALSE(entry != nullptr, false, TAG_CRED_MANAGER, "Invalid pointer!");
+    ESP_RETURN_ON_FALSE(entry->ssid != nullptr, false, TAG_CRED_MANAGER, "Invalid pointer!");
+    ESP_LOGV(TAG_CRED_MANAGER, "Updating last try for %s", entry->ssid);
+    return UpdateTimeStamp(&entry->last_try, entry->has_last_try);
+}
+bool WifiList::UpdateLastTry(const wifi_sta_config_t* sta) { return UpdateLastTry(GetSSID(sta)); }
+bool WifiList::UpdateLastTry(const wifi_ap_record_t* ap) { return UpdateLastTry(GetSSID(ap)); }
+bool WifiList::UpdateLastSeen(const wifi_sta_config_t* sta) { return UpdateLastSeen(GetSSID(sta)); }
+bool WifiList::UpdateLastSeen(const wifi_ap_record_t* ap) { return UpdateLastSeen(GetSSID(ap)); }
+bool WifiList::UpdateLastSeen(const std::string ssid) {
+    if (!Lock()) {
+        throw std::runtime_error("Lock failed");
+    }
+    auto entry = Get(ssid);
+    bool changed = false;
+    if (entry != nullptr) {
+        changed = UpdateLastSeen(entry);
+    }
+    Unlock();
+    return changed;
+}
+bool WifiList::UpdateLastSeen(sys_net_wifi_entry* entry) {
+    ESP_RETURN_ON_FALSE(entry != nullptr, false, TAG_CRED_MANAGER, "Invalid pointer!");
+    ESP_LOGV(TAG_CRED_MANAGER, "Updating last seen for %s", entry->ssid);
+    return UpdateTimeStamp(&entry->last_seen, entry->has_last_seen);
+}
+sys_net_wifi_entry& WifiList::AddUpdate(const wifi_ap_record_t* scan_rec) {
+    auto ssid = GetSSID(scan_rec);
+    if (!Exists(scan_rec)) {
+        ESP_LOGV(TAG_CRED_MANAGER, "Added new entry %s to list %s", ssid.c_str(), name_.c_str());
+        if (!Lock()) {
+            throw std::runtime_error("Lock failed");
+        }
+        credentials_[ssid] = ToSTAEntry(scan_rec);
+        Unlock();
+    } else {
+        Update(scan_rec);
+    }
+    return credentials_[ssid];
+}
+sys_net_wifi_entry& WifiList::AddUpdate(const char* ssid, const char* password) {
+    if (ssid == nullptr || password == nullptr) {
+        throw std::invalid_argument("SSID and password cannot be null");
+    }
+    // Ensure that the SSID and password are not too long
+    if (std::strlen(ssid) >= sizeof(sys_net_wifi_entry::ssid) || std::strlen(password) >= sizeof(sys_net_wifi_entry::password)) {
+        throw std::length_error("SSID or password is too long");
+    }
+    if (!Exists(ssid)) {
+        if (!Lock()) {
+            throw std::runtime_error("Lock failed");
+        }
+        sys_net_wifi_entry newEntry = sys_net_wifi_entry_init_default;
+        // Copy the SSID and password into the new entry, ensuring null termination
+        std::strncpy(newEntry.ssid, ssid, sizeof(newEntry.ssid) - 1);
+        newEntry.ssid[sizeof(newEntry.ssid) - 1] = '\0';
+        std::strncpy(newEntry.password, password, sizeof(newEntry.password) - 1);
+        newEntry.password[sizeof(newEntry.password) - 1] = '\0';
+        ESP_LOGV(TAG_CRED_MANAGER, "Added new entry %s to list %s", ssid, name_.c_str());
+        credentials_[ssid] = newEntry;
+        Unlock();
+    } else {
+        auto existing = Get(ssid);
+        if (strncmp(existing->password, password, sizeof(existing->password)) != 0) {
+            strncpy(existing->password, password, sizeof(existing->password));
+            existing->password[sizeof(existing->password) - 1] = '\0';
+        }
+    }
+    return credentials_[ssid];
+}
+sys_net_wifi_entry& WifiList::AddUpdate(const sys_net_wifi_entry* sta, const char* password) {
+    if (sta == nullptr) {
+        throw std::invalid_argument("Entry pointer cannot be null");
+    }
+    auto converted = ToSTAEntry(sta);
+    strncpy(converted.password, password, sizeof(converted.password));        
+    if (!Exists(sta->ssid)) {
+        ESP_LOGD(TAG_CRED_MANAGER, "Added new entry %s to list %s", sta->ssid, name_.c_str());
+        if (!Lock()) {
+            throw std::runtime_error("Lock failed");
+        }
+        credentials_[sta->ssid] = converted;
+        Unlock();
+    } else {
+        auto existing = Get(sta->ssid);
+        Update(*existing, converted);
+        // release the converted structure now
+        Release(&converted);
+    }
+    return credentials_[sta->ssid];
+}
+void WifiList::PrintString(const char* pData, size_t length, const char* format) {
+    std::string buffer;
+    for (size_t i = 0; i < length && pData[i]; i++) {
+        if (isprint((char)pData[i])) {
+            buffer += (char)pData[i]; // Print as a character
+        } else {
+            buffer += '?';
+        }
+    }
+    printf(format, buffer.c_str());
+}
+void WifiList::PrintWifiSTAEntryTitle() {
+    printf("-----------------------------------------------------------------------------------------------------------------------------------------"
+           "--------------------\n");
+    printf("CONN SSID                PASSWORD                 BSSID               RSSI  CHAN AUTH          RADIO    LAST TRY                   LAST "
+           "SEEN\n");
+    printf("-----------------------------------------------------------------------------------------------------------------------------------------"
+           "--------------------\n");
+}
+
+void WifiList::PrintWifiSTAEntry(const sys_net_wifi_entry& entry) {
+    google_protobuf_Timestamp gts = google_protobuf_Timestamp_init_default;
+    printf("%-5c", entry.connected ? 'X' : ' ');
+    printf("%-20s", entry.ssid);
+    PrintString(entry.password, sizeof(entry.password), "%-25s");
+    PrintString(entry.bssid, sizeof(entry.bssid), "%-20s");
+    printf("%-4ddB", entry.rssi);
+    printf("%3u  ", static_cast<unsigned>(entry.channel));
+    printf("%-14s", sys_net_auth_types_name(entry.auth_type));
+    printf("%-9s", formatRadioTypes(entry.radio_type, entry.radio_type_count).c_str());
+    if (entry.has_last_try) {
+        PrintTimeStamp(&entry.last_try);
+    } else {
+        PrintTimeStamp(&gts);
+    }
+    if (entry.has_last_seen) {
+        PrintTimeStamp(&entry.last_seen);
+    } else {
+        PrintTimeStamp(&gts);
+    }
+    printf("\n");
+}
+sys_net_wifi_entry* WifiList::GetIndex(size_t index) {
+    if (index >= credentials_.size()) {
+        return nullptr;
+    }
+    auto it = credentials_.begin();
+    std::advance(it, index);
+    return &(it->second);
+}
+sys_net_wifi_entry& WifiList::GetStrongestSTA() {
+    if (credentials_.empty()) {
+        throw std::runtime_error("No credentials available");
+    }
+
+    auto strongestIter = credentials_.begin();
+    for (auto iter = credentials_.begin(); iter != credentials_.end(); ++iter) {
+        if (iter->second.rssi > strongestIter->second.rssi) {
+            strongestIter = iter;
+        }
+    }
+
+    return strongestIter->second;
+}
+
+std::list<sys_net_radio_types> WifiList::GetRadioTypes(const wifi_ap_record_t* sta) {
+    std::list<sys_net_radio_types> result;
+    if (sta == nullptr) {
+        result.push_back(sys_net_radio_types_UNKNOWN);
+    } else {
+
+        // Check each bit field and return the corresponding enum value
+        if (sta->phy_11b) {
+            result.push_back(sys_net_radio_types_PHY_11B);
+        }
+        if (sta->phy_11g) {
+            result.push_back(sys_net_radio_types_PHY_11G);
+        }
+        if (sta->phy_11n) {
+            result.push_back(sys_net_radio_types_PHY_11N);
+        }
+        if (sta->phy_lr) {
+            result.push_back(sys_net_radio_types_LR);
+        }
+        if (sta->wps) {
+            result.push_back(sys_net_radio_types_WPS);
+        }
+        if (sta->ftm_responder) {
+            result.push_back(sys_net_radio_types_FTM_RESPONDER);
+        }
+        if (sta->ftm_initiator) {
+            result.push_back(sys_net_radio_types_FTM_INITIATOR);
+        }
+    }
+    return result;
+}
+
+wifi_auth_mode_t WifiList::GetESPAuthMode(sys_net_auth_types auth_type) {
+    switch (auth_type) {
+    case sys_net_auth_types_OPEN:
+        return WIFI_AUTH_OPEN;
+    case sys_net_auth_types_WEP:
+        return WIFI_AUTH_WEP;
+    case sys_net_auth_types_WPA_PSK:
+        return WIFI_AUTH_WPA_PSK;
+    case sys_net_auth_types_WPA2_PSK:
+        return WIFI_AUTH_WPA2_PSK;
+    case sys_net_auth_types_WPA_WPA2_PSK:
+        return WIFI_AUTH_WPA_WPA2_PSK;
+    case sys_net_auth_types_WPA2_ENTERPRISE:
+        return WIFI_AUTH_WPA2_ENTERPRISE;
+    case sys_net_auth_types_WPA3_PSK:
+        return WIFI_AUTH_WPA3_PSK;
+    case sys_net_auth_types_WPA2_WPA3_PSK:
+        return WIFI_AUTH_WPA2_WPA3_PSK;
+    case sys_net_auth_types_WAPI_PSK:
+        return WIFI_AUTH_WAPI_PSK;
+    default:
+        return WIFI_AUTH_OPEN; // Default case
+    }
+}
+sys_net_auth_types WifiList::GetAuthType(const wifi_ap_record_t* ap) {
+    return ap ? GetAuthType(ap->authmode) : sys_net_auth_types_AUTH_UNKNOWN;
+}
+sys_net_auth_types WifiList::GetAuthType(const wifi_auth_mode_t mode) {
+
+    switch (mode) {
+    case WIFI_AUTH_OPEN:
+        return sys_net_auth_types_OPEN;
+    case WIFI_AUTH_WEP:
+        return sys_net_auth_types_WEP;
+    case WIFI_AUTH_WPA_PSK:
+        return sys_net_auth_types_WPA_PSK;
+    case WIFI_AUTH_WPA2_PSK:
+        return sys_net_auth_types_WPA2_PSK;
+    case WIFI_AUTH_WPA_WPA2_PSK:
+        return sys_net_auth_types_WPA_WPA2_PSK;
+    case WIFI_AUTH_WPA2_ENTERPRISE:
+        return sys_net_auth_types_WPA2_ENTERPRISE;
+    case WIFI_AUTH_WPA3_PSK:
+        return sys_net_auth_types_WPA3_PSK;
+    case WIFI_AUTH_WPA2_WPA3_PSK:
+        return sys_net_auth_types_WPA2_WPA3_PSK;
+    case WIFI_AUTH_WAPI_PSK:
+        return sys_net_auth_types_WAPI_PSK;
+    case WIFI_AUTH_MAX:
+        return sys_net_auth_types_OPEN;
+    }
+    return sys_net_auth_types_AUTH_UNKNOWN;
+}

+ 185 - 0
components/platform_config/WifiList.h

@@ -0,0 +1,185 @@
+/*
+ *
+ *      Sebastien L. 2023, sle118@hotmail.com
+ *      Philippe G. 2023, philippe_44@outlook.com
+ *
+ *  This software is released under the MIT License.
+ *  https://opensource.org/licenses/MIT
+ *
+ *  License Overview:
+ *  ----------------
+ *  The MIT License is a permissive open source license. As a user of this software, you are free to:
+ *  - Use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of this software.
+ *  - Use the software for private, commercial, or any other purposes.
+ *
+ *  Conditions:
+ *  - You must include the above copyright notice and this permission notice in all
+ *    copies or substantial portions of the Software.
+ *
+ *  The MIT License offers a high degree of freedom and is well-suited for both open source and
+ *  commercial applications. It places minimal restrictions on how the software can be used,
+ *  modified, and redistributed. For more details on the MIT License, please refer to the link above.
+ */
+#pragma once
+#include "Network.pb.h"
+#include "Status.pb.h"
+#include "esp_log.h"
+#include "esp_wifi.h"
+#include "PBW.h"
+#include "pb_decode.h"
+#include "pb_encode.h"
+#ifdef __cplusplus
+#include "Locking.h"
+#include <cstring>
+#include <list>
+#include <map>
+#include <string>
+
+class WifiList : public System::Locking {
+  public:
+    WifiList(std::string name) : System::Locking(name), name_(name) {}
+    ~WifiList(){
+        Clear();
+    }
+    static std::string toString(const uint8_t* data, size_t max_length) {
+        // Find the actual length of the string up to max_length
+        size_t length = strnlen(reinterpret_cast<const char*>(data), max_length);
+        // Construct a std::string using the data and its length
+        auto p = std::string(reinterpret_cast<const char*>(data), length);
+        return p;
+    }
+    static void Release(sys_net_wifi_entry& entry){
+        Release(&entry);
+    }
+    static void Release(sys_net_wifi_entry* entry){
+        pb_release(&sys_net_wifi_entry_msg,entry);
+    }
+    static std::list<sys_net_radio_types> GetRadioTypes(const wifi_ap_record_t* sta);
+    static wifi_auth_mode_t GetESPAuthMode(sys_net_auth_types auth_type);
+    static sys_net_auth_types GetAuthType(const wifi_ap_record_t* ap);
+    static sys_net_auth_types GetAuthType(const wifi_auth_mode_t mode);
+    static bool areRadioTypesDifferent(const sys_net_radio_types* types1, pb_size_t count1, const sys_net_radio_types* types2, pb_size_t count2) {
+        if (count1 != count2) {
+            return true;
+        }
+
+        for (pb_size_t i = 0; i < count1; ++i) {
+            if (types1[i] != types2[i]) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+    static std::string GetPassword(const wifi_sta_config_t* config) {
+        auto p = toString(config->password, sizeof(config->password));
+        return p;
+    }
+
+    static std::string GetSSID(const wifi_event_sta_connected_t* evt) { return toString(evt->ssid, sizeof(evt->ssid)); }
+    static std::string GetSSID(const wifi_sta_config_t* config) { return toString(config->ssid, sizeof(config->ssid)); }
+    static std::string GetSSID(const wifi_ap_record_t* ap) { return toString(ap->ssid, sizeof(ap->ssid)); }
+    static void FormatBSSID(char* buffer, size_t len, const uint8_t* bssid) {
+        memset(buffer,0x00,len);
+        snprintf(buffer, len, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+    }
+    static void FormatBSSID(const wifi_ap_record_t* ap, sys_net_wifi_entry& entry) {
+        memset(entry.bssid,0x00,sizeof(entry.bssid));
+        if (!ap) return;
+        FormatBSSID(entry.bssid, sizeof(entry.bssid), ap->bssid);
+    }
+    static std::string GetBSSID(const wifi_event_sta_connected_t* evt);
+    static bool isEmpty(const char* str, size_t len);
+    static void PrintString(const char* pData, size_t length, const char* format);
+    static void PrintWifiSTAEntryTitle();
+
+    sys_net_wifi_entry& GetStrongestSTA();
+    bool RemoveCredential(const std::string& ssid);
+    bool RemoveCredential(const wifi_sta_config_t* sta);
+    void Clear();
+
+    static sys_net_wifi_entry ToSTAEntry(const sys_net_wifi_entry* sta);
+    static sys_net_wifi_entry ToSTAEntry(const wifi_sta_config_t* sta, const std::list<sys_net_radio_types>& radio_types,
+        sys_net_auth_types auth_type = sys_net_auth_types_AUTH_UNKNOWN);
+    static sys_net_wifi_entry ToSTAEntry(const wifi_sta_config_t* sta, sys_net_auth_types auth_type = sys_net_auth_types_AUTH_UNKNOWN);
+    static sys_net_wifi_entry& ToSTAEntry(const wifi_ap_record_t* ap, sys_net_wifi_entry& item);
+    static sys_net_wifi_entry ToSTAEntry(const wifi_ap_record_t* ap);
+    static sys_net_wifi_entry& ToSTAEntry(const wifi_sta_config_t* sta, sys_net_wifi_entry& item, const std::list<sys_net_radio_types>& radio_types,
+        sys_net_auth_types auth_type = sys_net_auth_types_AUTH_UNKNOWN);
+
+    static void PrintTimeStamp(const google_protobuf_Timestamp* timestamp);
+    static void PrintWifiSTAEntry(const sys_net_wifi_entry& entry);
+    static std::string formatRadioTypes(const sys_net_radio_types* radioTypes, pb_size_t count);
+
+    static bool OffsetTimeStamp(google_protobuf_Timestamp * ts);
+    static bool UpdateTimeStamp(google_protobuf_Timestamp* ts, bool& has_flag_val);
+
+    bool ResetRSSI();
+    bool ResetConnected();
+    const sys_net_wifi_entry* GetConnected();
+
+    bool SetConnected(const wifi_event_sta_connected_t* evt, bool connected = true);
+    size_t GetCount() const { return credentials_.size(); }
+    sys_net_wifi_entry* GetIndex(size_t index);
+
+    bool Exists(const std::string& ssid) { return Get(ssid) != nullptr; }
+    bool Exists(const char* ssid) { return Get(ssid) != nullptr; }
+    bool Exists(const wifi_ap_record_t* ap) { return ap != nullptr && Get(GetSSID(ap)) != nullptr; }
+    bool Exists(const wifi_sta_config_t* sta) { return Exists(GetSSID(sta)); }
+
+    bool Update(const wifi_sta_config_t* sta, bool connected = false);
+    bool Update(const wifi_ap_record_t* ap, bool connected = false);
+    static bool Update(sys_net_wifi_entry& existingEntry, sys_net_wifi_entry& compared);
+
+    sys_net_wifi_entry* Get(const wifi_sta_config_t* sta) { return Get(GetSSID(sta)); }
+    sys_net_wifi_entry* Get(const wifi_ap_record_t* ap) { return Get(GetSSID(ap)); }
+    sys_net_wifi_entry* Get(const char* ssid) { return Get(std::string(ssid)); }
+    sys_net_wifi_entry* Get(const std::string& ssid);
+
+    bool UpdateFromClock();
+    bool UpdateLastTry(const wifi_sta_config_t* sta);
+    bool UpdateLastTry(const wifi_ap_record_t* ap);
+    bool UpdateLastTry(const std::string ssid);
+    bool UpdateLastTry(sys_net_wifi_entry* entry);
+    bool UpdateLastSeen(const wifi_sta_config_t* sta);
+    bool UpdateLastSeen(const wifi_ap_record_t* ap);
+    bool UpdateLastSeen(const std::string ssid);
+    bool UpdateLastSeen(sys_net_wifi_entry* entry);
+    sys_net_wifi_entry& AddUpdate(const wifi_sta_config_t* sta, sys_net_auth_types auth_type = sys_net_auth_types_AUTH_UNKNOWN);
+    sys_net_wifi_entry& AddUpdate(const wifi_sta_config_t* sta, const std::list<sys_net_radio_types>& radio_types,
+        sys_net_auth_types auth_type = sys_net_auth_types_AUTH_UNKNOWN);
+    sys_net_wifi_entry& AddUpdate(const wifi_ap_record_t* scan_rec);
+    sys_net_wifi_entry& AddUpdate(const char* ssid = "", const char* password = "");
+    sys_net_wifi_entry& AddUpdate(const sys_net_wifi_entry* existing, const char* password = "");
+    // this one below is used by pb_decode
+    void AddUpdate(const sys_net_wifi_entry& entry) {
+        if (!Lock()) {
+            throw std::runtime_error("Lock failed");
+        }
+        credentials_[entry.ssid] = entry;
+        Unlock();
+    }
+    using Iterator = std::map<std::string, sys_net_wifi_entry>::iterator;
+    using ConstIterator = std::map<std::string, sys_net_wifi_entry>::const_iterator;
+    Iterator begin() { return credentials_.begin(); }
+    ConstIterator begin() const { return credentials_.begin(); }
+    Iterator end() { return credentials_.end(); }
+    ConstIterator end() const { return credentials_.end(); }
+
+  private:
+    static std::string FormatTimestamp(const google_protobuf_Timestamp& timestamp) {
+        // Format the timestamp as needed
+        // This is a placeholder implementation.
+        return std::to_string(timestamp.seconds) + "s";
+    }
+    std::map<std::string, sys_net_wifi_entry> credentials_;
+    std::string name_; // Name of the WifiCredentialsManager
+};
+
+extern "C" {
+#endif
+typedef struct WifiList WifiList;
+
+#ifdef __cplusplus
+}
+#endif

+ 20 - 0
components/platform_config/test/CMakeLists.txt

@@ -0,0 +1,20 @@
+idf_component_register(SRC_DIRS .
+                      INCLUDE_DIRS "."
+                      REQUIRES  tools platform_config unity )
+# target_compile_options(__idf_platform_config PRIVATE --coverage)
+target_include_directories(${COMPONENT_LIB}  PUBLIC SYSTEM ${CMAKE_SOURCE_DIR}/test_main)
+
+message(STATUS "** PLATFORM PROTOBUF")
+include(../../../tools/protoc_utils/protobuf_utils.cmake)
+configure_env()
+file(GLOB  PROTO_FILES *.proto)
+set(NANOPB_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}" "-I${PROJECT_ROOT_DIR}/protobuf/proto")
+nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS RELPATH ${CMAKE_CURRENT_SOURCE_DIR} ${PROTO_FILES})
+
+
+# Create a custom target to generate the proto files
+set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS} PROPERTIES GENERATED TRUE)
+add_custom_target(generate_test_proto ALL DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
+target_sources(${COMPONENT_LIB} PRIVATE ${PROTO_SRCS})
+add_dependencies(${COMPONENT_LIB} generate_test_proto) 
+target_include_directories(${COMPONENT_LIB} PUBLIC "include" ${CMAKE_CURRENT_BINARY_DIR} ${NANOPB_INCLUDE_DIRS} ${EXTRA_INCLUDES})

+ 29 - 0
components/platform_config/test/DAC_test_extra.proto

@@ -0,0 +1,29 @@
+syntax = "proto3";
+
+package sys.dac.extra;
+import "DAC.proto";
+import "GPIO.proto";
+import "I2CBus.proto";
+import "DacControlSet.proto";
+import "customoptions.proto";
+import "nanopb.proto";
+option (nanopb_fileopt).enum_to_string = true;
+
+message config {
+  option (nanopb_msgopt).packed_struct  = true;
+  option (nanopb_msgopt).msgid  = 80008;
+  int32 bck = 1 [(cust_field).v_int32=-1];
+  int32 ws = 2 [(cust_field).v_int32=-1];
+  int32 dout = 3 [(cust_field).v_int32=-1];
+  MCK mck = 4;
+  gpio.pinmute = 5 [(cust_field).v_msg='{"pin":-1,"level":"LOW"}'];
+  Models model = 6;
+  I2CBus i2c = 7;
+  dac.control.Set daccontrolset = 8;
+  bool jack_mutes_amp = 9;
+  uint32 addr = 10;
+  int32 din = 11 [(cust_field).v_int32=-1];
+  int32 dummy1 = 20;
+  int64 dummy2 = 21;  
+  gpio.pindummy3 = 22;
+}

+ 30 - 0
components/platform_config/test/DAC_test_extra2.proto

@@ -0,0 +1,30 @@
+syntax = "proto3";
+
+package sys.dac.extra2;
+import "DAC.proto";
+import "GPIO.proto";
+import "I2CBus.proto";
+import "DacControlSet.proto";
+import "customoptions.proto";
+import "nanopb.proto";
+option (nanopb_fileopt).enum_to_string = true;
+
+message config {
+  option (nanopb_msgopt).packed_struct  = true;
+  option (nanopb_msgopt).msgid  = 90008;
+  int32 bck = 1 [(cust_field).v_int32=-1];
+  int32 ws = 2 [(cust_field).v_int32=-1];
+  int32 dout = 3 [(cust_field).v_int32=-1];
+  MCK mck = 4;
+  gpio.config mute = 5 [(cust_field).v_msg='{"pin":-1,"level":"LOW"}'];
+  Models model = 6;
+  I2CBus i2c = 7;
+  dac.control.Set daccontrolset = 8;
+  bool jack_mutes_amp = 9;
+  uint32 addr = 10;
+  int32 din = 11 [(cust_field).v_int32=-1];
+  int32 dummy1 = 20;
+  int64 dummy2 = 21;  
+  gpio.config dummy3 = 22;
+  gpio.config dummy4 = 23;
+}

+ 956 - 0
components/platform_config/test/test_platform_config.cpp

@@ -0,0 +1,956 @@
+
+#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
+#include "Config.h"
+#include "DAC.pb.h"
+#include "DAC_test_extra.pb.h"
+#include "DAC_test_extra2.pb.h"
+#include "Locking.h"
+#include "PBW.h"
+#include "State.pb.h"
+#include "WifiList.h"
+#include "configuration.pb.h"
+#include "esp_log.h"
+#include "test_common_init.h"
+#include "tools.h"
+#include "unity.h"
+
+using namespace System;
+
+// Helper macro to stringify the expanded value of a macro
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+// Use the helper macro to stringify LOG_LOCAL_LEVEL
+#pragma message("The current log local level value is " TOSTRING(LOG_LOCAL_LEVEL))
+#define AUTH_MODE_INDEX(i) ((start_auth_mode + i) % WIFI_AUTH_MAX == 0 ? (wifi_auth_mode_t)1 : (wifi_auth_mode_t)(start_auth_mode + i))
+static const char* config_file_name = "settings.bin";
+
+static const uint8_t bssid[6] = {0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56};
+uint8_t fill_last = 0x56;
+uint8_t start_rssi = 70;
+uint8_t start_channel = 2;
+auto start_auth_mode = WIFI_AUTH_WEP;
+uint8_t fill_bssid[6] = {0xAB, 0xCD, 0xEF, 0x12, 0x34, fill_last};
+static const char* char_bssid = "AB:CD:EF:12:34:56";
+static const char* TAG = "test_platform_config";
+const char* password = "TestPassword";
+bool HasMemoryUsageIncreased(int round) {
+    static const size_t thresholdInternal = 500; // Example threshold for internal memory
+    static const size_t thresholdSPIRAM = 1000;  // Example threshold for SPI RAM
+    size_t postTestFreeInternal = 0;
+    size_t postTestFreeSPIRAM = 0;
+    static size_t initialFreeInternal = 0;
+    static size_t initialFreeSPIRAM = 0;
+    auto minFreeInternal = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
+    auto minFreeSPIRAM = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
+    if (round > 0) {
+        postTestFreeInternal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
+        postTestFreeSPIRAM = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
+        minFreeInternal = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
+        minFreeSPIRAM = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
+
+        printf("Memory usage summary (after round %d): "
+               "Internal: %zu->%zu bytes. Min free: %zu. Increase: %d bytes. "
+               "SPIRAM: %zu->%zu bytes. Min free: %zu. Increase: %d bytes\n",
+            round, initialFreeInternal, postTestFreeInternal, minFreeInternal, initialFreeInternal - postTestFreeInternal, initialFreeSPIRAM,
+            postTestFreeSPIRAM, minFreeSPIRAM, initialFreeSPIRAM - postTestFreeSPIRAM);
+        int32_t diffInternal = initialFreeInternal > postTestFreeInternal ? initialFreeInternal - postTestFreeInternal : 0;
+        int32_t diffSPIRAM = initialFreeSPIRAM > postTestFreeSPIRAM ? initialFreeSPIRAM - postTestFreeSPIRAM : 0;
+        if (diffSPIRAM > 0 || diffInternal > 0) {
+            ESP_LOGW(TAG, "Internal increase: %d, SPIRAM: %d", diffInternal, diffSPIRAM);
+            return true;
+        }
+    } else {
+        initialFreeInternal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
+        initialFreeSPIRAM = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
+        printf("Memory usage at start: "
+               "Internal: %zu bytes. Min free: %zu. "
+               "SPIRAM: %zu bytes. Min free: %zu.\n",
+            initialFreeInternal, minFreeInternal, initialFreeSPIRAM, minFreeSPIRAM);
+    }
+    return false;
+}
+void advanceTime(int seconds) {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    tv.tv_sec += seconds;
+    tv.tv_usec += seconds * 100;
+    settimeofday((const timeval*)&tv, 0);
+}
+wifi_event_sta_connected_t getMockConnectedEvent(int i = 0) {
+    wifi_event_sta_connected_t mock = {};
+    mock.authmode = WIFI_AUTH_WPA3_PSK;
+    memset(mock.ssid, 0x00, sizeof(mock.ssid));
+    memset(mock.bssid, 0x00, sizeof(mock.bssid));
+    snprintf((char*)mock.ssid, sizeof(mock.ssid), "SSID_%d", i);
+    fill_bssid[5] = fill_last + i;
+    memcpy(mock.bssid, fill_bssid, sizeof(mock.bssid));
+    mock.channel = 6 + i;
+    mock.ssid_len = strlen((char*)mock.ssid);
+    return mock;
+}
+wifi_ap_record_t getMockAPRec(int i = 0) {
+    wifi_ap_record_t mock = {};
+    mock.primary = start_channel + i; // Set some channel
+    mock.rssi = start_rssi + i;       // Set some RSSI value
+    memset(mock.ssid, 0x00, sizeof(mock.ssid));
+    memset(mock.bssid, 0x00, sizeof(mock.bssid));
+    snprintf((char*)mock.ssid, sizeof(mock.ssid), "SSID_%d", i);
+    fill_bssid[5] = fill_last + i;
+    memcpy(mock.bssid, fill_bssid, sizeof(mock.bssid));
+    mock.second = WIFI_SECOND_CHAN_ABOVE;
+    mock.authmode = AUTH_MODE_INDEX(i);                  /**< authmode of AP */
+    mock.pairwise_cipher = WIFI_CIPHER_TYPE_AES_GMAC128; /**< pairwise cipher of AP */
+    mock.group_cipher = WIFI_CIPHER_TYPE_TKIP_CCMP;      /**< group cipher of AP */
+    mock.ant = WIFI_ANT_ANT1;                            /**< antenna used to receive beacon from AP */
+    mock.phy_11b = 1;                                    /**< bit: 0 flag to identify if 11b mode is enabled or not */
+    mock.phy_11g = 0;                                    /**< bit: 1 flag to identify if 11g mode is enabled or not */
+    mock.phy_11n = 1;                                    /**< bit: 2 flag to identify if 11n mode is enabled or not */
+    mock.phy_lr = 0;                                     /**< bit: 3 flag to identify if low rate is enabled or not */
+    mock.wps = 1;                                        /**< bit: 4 flag to identify if WPS is supported or not */
+    mock.ftm_responder = 0;                              /**< bit: 5 flag to identify if FTM is supported in responder mode */
+    mock.ftm_initiator = 1;                              /**< bit: 6 flag to identify if FTM is supported in initiator mode */
+    return mock;
+}
+wifi_sta_config_t getMockSTA(int i = 0) {
+    wifi_sta_config_t mock = {};
+    memset(mock.ssid, 0x00, sizeof(mock.ssid));
+    memset(mock.bssid, 0x00, sizeof(mock.bssid));
+    snprintf((char*)mock.password, sizeof(mock.password), "Password_%d", i);
+    snprintf((char*)mock.ssid, sizeof(mock.ssid), "SSID_%d", i);
+    fill_bssid[5] = fill_last + i;
+    memcpy(mock.bssid, fill_bssid, sizeof(mock.bssid));
+    mock.channel = start_channel + i;
+    mock.failure_retry_cnt = 2 + i;
+    mock.listen_interval = 4 + i;
+    mock.mbo_enabled = 1;
+    mock.scan_method = WIFI_ALL_CHANNEL_SCAN;
+    return mock;
+}
+sys_net_wifi_entry getMockEntry(int i = 0) {
+    sys_net_wifi_entry mock = sys_net_wifi_entry_init_default;
+    mock.auth_type = WifiList::GetAuthType(AUTH_MODE_INDEX(i));
+    snprintf((char*)mock.password, sizeof(mock.password), "Password_%d", i);
+    snprintf((char*)mock.ssid, sizeof(mock.ssid), "SSID_%d", i);
+    fill_bssid[5] = fill_last + i;
+    WifiList::FormatBSSID(mock.bssid, sizeof(mock.bssid), fill_bssid);
+    mock.channel = start_channel + i;
+    mock.connected = false;
+    mock.has_last_seen = false;
+    mock.has_last_try = false;
+    mock.radio_type_count = 3;
+    mock.radio_type = new sys_net_radio_types[mock.radio_type_count];
+    mock.radio_type[0] = sys_net_radio_types_PHY_11B;
+    mock.radio_type[1] = sys_net_radio_types_PHY_11G;
+    mock.radio_type[2] = sys_net_radio_types_PHY_11N;
+    mock.rssi = start_rssi + i;
+    return mock;
+}
+
+void FillSSIDs(WifiList& manager, int count, int start=1) {
+    for (int i = start; i <= count; i++) {
+        auto mock = getMockSTA(i);
+        auto& entry = manager.AddUpdate(&mock);
+        entry.rssi = 70 + i;
+        entry.connected = true;
+    }
+}
+void FillSSIDFromAPRec(WifiList& manager, int count, int start=1) {
+    for (int i = start; i <= count; i++) {
+        auto mock = getMockAPRec(i);
+        auto& entry = manager.AddUpdate(&mock);
+        entry.rssi = 70 + i;
+        entry.connected = true;
+    }
+}
+void FillSSIDFromMockEntry(WifiList& manager, int count, int start=1) {
+    for (int i = start; i <= count; i++) {
+        auto mock = getMockEntry(i);
+        auto& entry = manager.AddUpdate(&mock);
+        entry.rssi = 70 + i;
+        entry.connected = true;
+        WifiList::Release(mock);
+    }
+}
+void eraseConfigFile() {
+    PB<sys_config> wrapper(std::string("config"), &sys_config_msg, sizeof(sys_config_msg), false);
+    erase_path(wrapper.GetFileName().c_str(), false);
+}
+
+TEST_CASE("Test empty target settings empty", "[platform_config]") {
+    PB<sys_config> wrapper(std::string("Config"), &sys_config_msg, sizeof(sys_config_msg), false);
+    sys_config* conf = wrapper.get();
+    assert(conf != nullptr);
+    conf->target = strdup_psram("");
+#ifdef CONFIG_FW_PLATFORM_NAME
+    system_set_string(&sys_config_msg, sys_config_target_tag, conf, CONFIG_FW_PLATFORM_NAME);
+    TEST_ASSERT_TRUE(strcmp(conf->target, CONFIG_FW_PLATFORM_NAME) == 0);
+#endif
+}
+
+TEST_CASE("Test init from default config", "[platform_config]") {
+    struct stat fileInformation;
+    PB<sys_config> wrapper(std::string("config"), &sys_config_msg, sizeof(sys_config_msg), false);
+    sys_config* confprt = wrapper.get();
+    auto filename = wrapper.GetFileName();
+    erase_path(filename.c_str(), false);
+    TEST_ASSERT_FALSE(get_file_info(&fileInformation, config_file_name));
+    TEST_ASSERT_TRUE(strlen(STR_OR_BLANK(confprt->target)) == 0);
+    TEST_ASSERT_FALSE(confprt->has_dev);
+}
+const sys_config* GetTestConfig() {
+    static sys_config test_config;
+    memset(&test_config, 0x00, sizeof(test_config));
+
+    // Assuming test_config is an instance of sys_config or a similar structure
+    test_config.has_dev = true;
+    test_config.dev.has_spi = true;
+    test_config.dev.spi.mosi = 4;
+    test_config.dev.spi.clk = 5;
+    test_config.dev.spi.dc = 18;
+    test_config.dev.spi.host = sys_dev_common_hosts_Host1;
+
+    test_config.dev.has_dac = true;
+    test_config.dev.dac.bck = 25;
+    test_config.dev.dac.ws = 26;
+    test_config.dev.dac.dout = 33;
+    test_config.dev.dac.model = sys_dac_models_WM8978;
+
+    test_config.dev.has_display = true;
+    test_config.dev.display.has_common = true;
+    test_config.dev.display.common.width = 256;
+    test_config.dev.display.common.height = 64;
+    test_config.dev.display.common.HFlip = false;
+    test_config.dev.display.common.VFlip = false;
+    test_config.dev.display.common.rotate = false;
+    test_config.dev.display.common.driver = sys_display_drivers_SSD1322;
+    test_config.dev.display.common.reset = 21;
+    test_config.dev.display.which_dispType = sys_display_config_spi_tag;
+    test_config.dev.display.dispType.spi.cs = 19;
+    test_config.dev.display.dispType.spi.speed = 8000000;
+    test_config.dev.has_rotary = true;
+
+    test_config.dev.rotary.A = 23;
+    test_config.dev.rotary.B = 22;
+    test_config.dev.rotary.SW = 34;
+
+    test_config.dev.rotary.volume = true;
+    test_config.dev.rotary.longpress = true;
+    test_config.has_names = true;
+    strcpy(test_config.names.device, "test_name");
+    if (!test_config.target) {
+        test_config.target = strdup_psram("test_target");
+    }
+    return &test_config;
+}
+void check_sys_config_structure(sys_config* config) {
+    auto check = GetTestConfig();
+    // Test SPI configuration
+    TEST_ASSERT_EQUAL(check->dev.has_spi, config->dev.has_spi);
+    TEST_ASSERT_EQUAL(check->dev.spi.mosi, config->dev.spi.mosi);
+    TEST_ASSERT_EQUAL(check->dev.spi.clk, config->dev.spi.clk);
+    TEST_ASSERT_EQUAL(check->dev.spi.dc, config->dev.spi.dc);
+    TEST_ASSERT_EQUAL(check->dev.spi.host, config->dev.spi.host);
+    TEST_ASSERT_EQUAL(check->dev.has_dac, config->dev.has_dac);
+    TEST_ASSERT_EQUAL(check->dev.dac.bck, config->dev.dac.bck);
+    TEST_ASSERT_EQUAL(check->dev.dac.ws, config->dev.dac.ws);
+    TEST_ASSERT_EQUAL(check->dev.dac.dout, config->dev.dac.dout);
+    TEST_ASSERT_EQUAL(check->dev.dac.model, config->dev.dac.model);
+    TEST_ASSERT_EQUAL(check->dev.has_display, config->dev.has_display);
+    TEST_ASSERT_EQUAL(check->dev.display.common.width, config->dev.display.common.width);
+    TEST_ASSERT_EQUAL(check->dev.display.common.height, config->dev.display.common.height);
+    TEST_ASSERT_EQUAL(check->dev.display.common.HFlip, config->dev.display.common.HFlip);
+    TEST_ASSERT_EQUAL(check->dev.display.common.VFlip, config->dev.display.common.VFlip);
+    TEST_ASSERT_EQUAL(check->dev.display.common.rotate, config->dev.display.common.rotate);
+    TEST_ASSERT_EQUAL(check->dev.display.common.driver, config->dev.display.common.driver);
+    TEST_ASSERT_EQUAL(check->dev.display.common.reset, config->dev.display.common.reset);
+    TEST_ASSERT_EQUAL(check->dev.display.which_dispType, config->dev.display.which_dispType);
+    TEST_ASSERT_EQUAL(check->dev.display.dispType.spi.cs, config->dev.display.dispType.spi.cs);
+    TEST_ASSERT_EQUAL(check->dev.display.dispType.spi.speed, config->dev.display.dispType.spi.speed);
+    TEST_ASSERT_EQUAL(check->dev.has_rotary, config->dev.has_rotary);
+    TEST_ASSERT_EQUAL(check->dev.rotary.A, config->dev.rotary.A);
+    TEST_ASSERT_EQUAL(check->dev.rotary.B, config->dev.rotary.B);
+    TEST_ASSERT_EQUAL(check->dev.rotary.SW, config->dev.rotary.SW);
+    TEST_ASSERT_EQUAL(check->dev.rotary.volume, config->dev.rotary.volume);
+    TEST_ASSERT_EQUAL(check->dev.rotary.longpress, config->dev.rotary.longpress);
+    TEST_ASSERT_EQUAL_STRING(check->names.device, config->names.device);
+    TEST_ASSERT_EQUAL_STRING(check->target, config->target);
+}
+TEST_CASE("Test change platform", "[platform_config]") {
+    struct stat fileInformation;
+    eraseConfigFile();
+    std::stringstream test_target_file;
+    test_target_file << spiffs_base_path << "/targets/" << GetTestConfig()->target << "/config.bin";
+    PlatformConfig::ProtoWrapperHelper::CommitFile(test_target_file.str().c_str(), &sys_config_msg, GetTestConfig());
+
+    // first ensure that the target state file exists.
+    TEST_ASSERT_TRUE(get_file_info(&fileInformation, test_target_file.str().c_str()));
+    TEST_ASSERT_TRUE(fileInformation.st_size > 0);
+    platform = nullptr;
+
+    // here we must use the configurator object
+    // since we're testing some config_ functions
+    std::string expectedTarget = "ESP32";
+    TEST_ASSERT_NULL(platform);
+    config_load();
+    TEST_ASSERT_NOT_NULL(platform);
+    TEST_ASSERT_NOT_NULL(platform->target)
+    TEST_ASSERT_EQUAL_STRING(expectedTarget.c_str(), platform->target);
+    config_set_target_reset(GetTestConfig()->target);
+    check_sys_config_structure(platform);
+    TEST_ASSERT_EQUAL_STRING(platform->target, GetTestConfig()->target);
+    TEST_ASSERT_TRUE(erase_path(test_target_file.str().c_str(), false));
+    TEST_ASSERT_FALSE(get_file_info(&fileInformation, test_target_file.str().c_str()));
+}
+
+// TEST_CASE("Test load state", "[platform_config]") {
+//     eraseConfigFile();
+//     const char* urlvalue = "http://somerandomurl";
+//     config_load();
+//     system_set_string(&sys_state_data_msg, sys_state_data_ota_url_tag, sys_state, urlvalue);
+//     TEST_ASSERT_EQUAL_STRING(urlvalue, sys_state->ota_url);
+//     ESP_LOGI(TAG, "Raising state change");
+//     config_raise_state_changed();
+
+// // create an async timer lambda to trigger the commit after 1 second so
+// //we can test waitcommit
+// config_commit_config
+
+// TEST_CASE("Test Raise State Change", "[platform_config]") {
+//     // config_load();
+//     ESP_LOGI(TAG, "Raising state change");
+//     TEST_ASSERT_FALSE(configurator.HasStateChanges());
+//     TEST_ASSERT_FALSE(configurator.HasChanges());
+//     config_raise_state_changed();
+//     TEST_ASSERT_TRUE(configurator.HasStateChanges());
+//     TEST_ASSERT_FALSE(configurator.HasChanges());
+//     configurator.ResetStateModified();
+//     TEST_ASSERT_FALSE(configurator.HasStateChanges());
+//     TEST_ASSERT_FALSE(configurator.HasChanges());
+// }
+// TEST_CASE("Test Raise Change", "[platform_config]") {
+//     // config_load();
+//     ESP_LOGI(TAG, "Raising change");
+//     PlatformConfig::PB wrapper =
+//         PlatformConfig::PB("config", "", &sys_config_msg, Init_sys_config);
+//     TEST_ASSERT_FALSE(configurator.HasStateChanges());
+//     TEST_ASSERT_FALSE(configurator.HasChanges());
+//     config_raise_changed();
+//     TEST_ASSERT_TRUE(configurator.HasChanges());
+//     TEST_ASSERT_FALSE(configurator.HasStateChanges());
+//     configurator.ResetModified();
+//     TEST_ASSERT_FALSE(configurator.HasChanges());
+//     TEST_ASSERT_FALSE(configurator.HasStateChanges());
+// }
+
+TEST_CASE("Test Lock Unlock", "[platform_config]") {
+    auto lock = PlatformConfig::Locking("test");
+    TEST_ASSERT_FALSE(lock.IsLocked());
+    TEST_ASSERT_TRUE(lock.Lock());
+    TEST_ASSERT_TRUE(lock.Lock());
+    TEST_ASSERT_TRUE(lock.IsLocked());
+    lock.Unlock();
+    TEST_ASSERT_TRUE(lock.IsLocked());
+    lock.Unlock();
+    TEST_ASSERT_FALSE(lock.IsLocked());
+}
+
+TEST_CASE("Recovery not updating message definition", "[platform_config]") {
+    is_recovery_running = false;
+    auto name = std::string("extra");
+    struct stat struct_info_extra;
+    struct stat struct_info_reco;
+    // create instance with fresh definition
+    erase_path(PBHelper::GetDefFileName(name).c_str(), false);
+    PB<sys_dac_extra_config> wrapper_extra(name.c_str(), &sys_dac_extra_config_msg, sizeof(sys_dac_extra_config_msg));
+    TEST_ASSERT_TRUE(get_file_info(&struct_info_extra, wrapper_extra.GetDefFileName().c_str()));
+    TEST_ASSERT_TRUE(struct_info_extra.st_size > 0);
+    auto& extra = wrapper_extra.Root();
+    extra.dummy1 = 20;
+    extra.dummy2 = 30;
+    extra.has_dummy3 = true;
+    extra.dummy3.level = sys_gpio_lvl_HIGH;
+    extra.dummy3.pin = 22;
+    wrapper_extra.CommitChanges();
+
+    is_recovery_running = true;
+    PB<sys_dac_config> wrapper(name, &sys_dac_config_msg, sizeof(sys_dac_config_msg));
+    TEST_ASSERT_TRUE(get_file_info(&struct_info_reco, wrapper.GetDefFileName().c_str()));
+    TEST_ASSERT_TRUE(struct_info_reco.st_size == struct_info_extra.st_size);
+    TEST_ASSERT_EQUAL(wrapper_extra.GetDataSize(), wrapper.GetDataSize());
+    wrapper.LoadFile(true);
+
+    sys_dac_extra_config* config = reinterpret_cast<sys_dac_extra_config*>(wrapper.get());
+    TEST_ASSERT_EQUAL(config->dummy1, extra.dummy1);
+    TEST_ASSERT_EQUAL(config->dummy2, extra.dummy2);
+    TEST_ASSERT_EQUAL(config->has_dummy3, extra.has_dummy3);
+    TEST_ASSERT_EQUAL(config->dummy3.level, extra.dummy3.level);
+    TEST_ASSERT_EQUAL(config->dummy3.pin, extra.dummy3.pin);
+
+    config->bck = 55;
+    wrapper.CommitChanges();
+    PB<sys_dac_extra_config> check_structure(name.c_str(), &sys_dac_extra_config_msg, sizeof(sys_dac_extra_config_msg));
+    check_structure.LoadFile(true);
+    auto config_check = check_structure.get();
+    TEST_ASSERT_EQUAL(config->bck, check_structure.get()->bck);
+    TEST_ASSERT_EQUAL(config_check->dummy1, extra.dummy1);
+    TEST_ASSERT_EQUAL(config_check->dummy2, extra.dummy2);
+    TEST_ASSERT_EQUAL(config_check->has_dummy3, extra.has_dummy3);
+    TEST_ASSERT_EQUAL(config_check->dummy3.level, extra.dummy3.level);
+    TEST_ASSERT_EQUAL(config_check->dummy3.pin, extra.dummy3.pin);
+
+    // not simulate an update
+    is_recovery_running = false;
+    PB<sys_dac_extra2_config> extra2(name.c_str(), &sys_dac_extra2_config_msg, sizeof(sys_dac_extra2_config_msg));
+    extra2.LoadFile(true);
+    auto config_extra2 = extra2.get();
+    config_extra2->has_dummy4 = true;
+    config_extra2->dummy4.pin = 99;
+    extra2.CommitChanges();
+    is_recovery_running = true;
+    PB<sys_dac_config> wrapper2(name, &sys_dac_config_msg, sizeof(sys_dac_config_msg));
+
+    wrapper2.LoadFile(true);
+    wrapper2.Root().bck = 88;
+    wrapper2.CommitChanges();
+
+    is_recovery_running = false;
+    extra2.LoadFile(true);
+    TEST_ASSERT_EQUAL(config_extra2->bck, 88);
+    TEST_ASSERT_TRUE(config_extra2->has_dummy4);
+    TEST_ASSERT_EQUAL(config_extra2->dummy4.pin, 99);
+}
+TEST_CASE("String conversion from uint8_t array", "[WifiCredentialsManager]") {
+    // Prepare test data
+    const uint8_t testData[] = {'T', 'e', 's', 't', '\0', 'D', 'a', 't', 'a'};
+    const size_t testDataLength = 9; // Including '\0'
+
+    // Call the method
+    std::string result = WifiList::toString(testData, testDataLength);
+
+    // Assert expectations
+    TEST_ASSERT_EQUAL_STRING_LEN("Test", result.c_str(), result.length());
+}
+TEST_CASE("Get SSID from wifi_sta_config_t", "[WifiCredentialsManager]") {
+    // Prepare test data
+    wifi_sta_config_t testConfig = getMockSTA(1);
+    // Call the method
+    std::string result = WifiList::GetSSID(&testConfig);
+    TEST_ASSERT_EQUAL_STRING("SSID_1", result.c_str());
+}
+TEST_CASE("Get SSID from wifi_event_sta_connected_t", "[WifiCredentialsManager]") {
+    wifi_event_sta_connected_t testEvent = {};
+    const char* ssid = "EventSSID";
+    memcpy(testEvent.ssid, ssid, strlen(ssid) + 1); // Including '\0'
+
+    std::string result = WifiList::GetSSID(&testEvent);
+    TEST_ASSERT_EQUAL_STRING(ssid, result.c_str());
+}
+TEST_CASE("Get SSID from wifi_ap_record_t", "[WifiCredentialsManager]") {
+    wifi_ap_record_t testRecord = {};
+    const char* ssid = "RecordSSID";
+    memcpy(testRecord.ssid, ssid, strlen(ssid) + 1); // Including '\0'
+
+    std::string result = WifiList::GetSSID(&testRecord);
+    TEST_ASSERT_EQUAL_STRING(ssid, result.c_str());
+}
+TEST_CASE("Get Password from wifi_sta_config_t", "[WifiCredentialsManager]") {
+    wifi_sta_config_t testConfig = {};
+    memcpy(testConfig.password, password, strlen(password) + 1); // Including '\0'
+
+    std::string result = WifiList::GetPassword(&testConfig);
+    TEST_ASSERT_EQUAL_STRING(password, result.c_str());
+}
+TEST_CASE("Get BSSID from wifi_event_sta_connected_t", "[WifiCredentialsManager]") {
+    wifi_event_sta_connected_t testEvent = {};
+    memcpy(testEvent.bssid, bssid, sizeof(testEvent.bssid));
+    std::string result = WifiCredentialsManager::GetBSSID(&testEvent);
+    TEST_ASSERT_EQUAL_STRING(char_bssid, result.c_str());
+}
+TEST_CASE("Format BSSID from uint8_t array", "[WifiCredentialsManager]") {
+    char buffer[18] = {0};
+    WifiCredentialsManager::FormatBSSID(buffer, sizeof(buffer), bssid);
+    TEST_ASSERT_EQUAL_STRING(char_bssid, buffer);
+}
+
+TEST_CASE("Update timestamp", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+
+    // Test with a non-null timestamp and an uninitialized flag
+    google_protobuf_Timestamp ts = {0, 0};
+    bool has_flag = false;
+
+    bool result = manager.UpdateTimeStamp(&ts, has_flag);
+
+    TEST_ASSERT_TRUE(result);             // Check if the method returns true for change
+    TEST_ASSERT_TRUE(has_flag);           // Check if the flag is set to true
+    TEST_ASSERT_NOT_EQUAL(0, ts.seconds); // Assuming gettimeofday() gives a non-zero time
+    TEST_ASSERT_NOT_EQUAL(0, ts.nanos);
+
+    // Store the updated values for comparison
+    long prev_seconds = ts.seconds;
+    long prev_nanos = ts.nanos;
+    advanceTime(2);
+
+    // Call the method again with the same timestamp
+    result = manager.UpdateTimeStamp(&ts, has_flag);
+
+    // Since the timestamp should be updated, check if the new values are different
+    TEST_ASSERT_TRUE(result);
+    TEST_ASSERT_TRUE(has_flag);
+    TEST_ASSERT_NOT_EQUAL(prev_seconds, ts.seconds);
+    TEST_ASSERT_NOT_EQUAL(prev_nanos, ts.nanos);
+
+    // Test with a null timestamp
+    result = manager.UpdateTimeStamp(nullptr, has_flag);
+
+    // The method should return false, and the flag should remain unchanged
+    TEST_ASSERT_FALSE(result);
+    TEST_ASSERT_TRUE(has_flag); // Flag remains unchanged
+    advanceTime(-2);
+}
+
+TEST_CASE("Update wifi entry", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+
+    sys_net_wifi_entry existingEntry = getMockEntry();
+    sys_net_wifi_entry updatedEntry = getMockEntry();
+
+    // Modify updatedEntry with different values
+    strcpy(updatedEntry.password, "NewPassword");
+    updatedEntry.channel = 5;
+    updatedEntry.auth_type = sys_net_auth_types_WPA2_PSK;
+    delete[] updatedEntry.radio_type;
+    updatedEntry.radio_type_count = 1;
+    updatedEntry.radio_type = new sys_net_radio_types[updatedEntry.radio_type_count]; // Dynamic allocation for radio_type
+    updatedEntry.radio_type[0] = sys_net_radio_types_PHY_11N;
+
+    updatedEntry.rssi = 42;
+    updatedEntry.connected = true;
+
+    // Call the Update method
+    bool result = manager.Update(existingEntry, updatedEntry);
+
+    // Assert that the Update method returns true
+    TEST_ASSERT_TRUE(result);
+
+    // Assert that the existingEntry is updated correctly
+    TEST_ASSERT_EQUAL_STRING("NewPassword", existingEntry.password);
+    TEST_ASSERT_EQUAL(updatedEntry.channel, existingEntry.channel);
+    TEST_ASSERT_EQUAL(sys_net_auth_types_WPA2_PSK, existingEntry.auth_type);
+    TEST_ASSERT_EQUAL(sys_net_radio_types_PHY_11N, existingEntry.radio_type[0]);
+    TEST_ASSERT_EQUAL(updatedEntry.radio_type_count, existingEntry.radio_type_count);
+    TEST_ASSERT_EQUAL(updatedEntry.rssi, existingEntry.rssi);
+    TEST_ASSERT_TRUE(existingEntry.connected);
+
+    // Clean up dynamically allocated memory
+    delete[] updatedEntry.radio_type;
+    delete[] existingEntry.radio_type;
+}
+
+TEST_CASE("Update wifi entry from AP record", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+
+    // Create a mock wifi_ap_record_t
+    wifi_ap_record_t mockAP = {};
+    // Initialize mockAP with test data
+    // ...
+
+    // Add the initial entry
+    sys_net_wifi_entry initialEntry = manager.ToSTAEntry(&mockAP);
+    initialEntry.connected = false; // Initially not connected
+    manager.AddUpdate(initialEntry);
+
+    // Now call the update with the same AP but with 'connected' status
+    bool result = manager.Update(&mockAP, true);
+
+    // Assert that the update was successful
+    TEST_ASSERT_TRUE(result);
+
+    // Retrieve the entry and check if it's updated
+    auto updatedEntry = manager.Get(&mockAP);
+    TEST_ASSERT_NOT_NULL(updatedEntry);
+    TEST_ASSERT_TRUE(updatedEntry->connected); // Check if connected is now true
+    manager.Clear();
+}
+
+TEST_CASE("Format radio types", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+
+    // Create an array of radio types for testing
+    sys_net_radio_types radioTypes[] = {sys_net_radio_types_PHY_11B, sys_net_radio_types_PHY_11G, sys_net_radio_types_PHY_11N, sys_net_radio_types_LR,
+        sys_net_radio_types_WPS, sys_net_radio_types_FTM_RESPONDER, sys_net_radio_types_FTM_INITIATOR, sys_net_radio_types_UNKNOWN};
+    pb_size_t count = sizeof(radioTypes) / sizeof(radioTypes[0]);
+
+    // Call the formatRadioTypes method
+    std::string formattedString = manager.formatRadioTypes(radioTypes, count);
+
+    // Define the expected result
+    std::string expectedResult = "B,G,N,L,W,FR,FI,U";
+
+    // Assert that the formatted string is as expected
+    TEST_ASSERT_EQUAL_STRING(expectedResult.c_str(), formattedString.c_str());
+    manager.Clear();
+}
+
+TEST_CASE("Update wifi entry from STA config", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+
+    // Create a mock wifi_sta_config_t
+    wifi_sta_config_t mockSTA = getMockSTA();
+
+    // Create and add an initial entry
+    sys_net_wifi_entry initialEntry = manager.ToSTAEntry(&mockSTA);
+    initialEntry.connected = false; // Initially not connected
+    manager.AddUpdate(initialEntry);
+
+    // Modify the mockSTA for the update
+    mockSTA.channel = 6; // Change the channel
+
+    // Now call the update with the modified mockSTA and 'connected' status
+    bool result = manager.Update(&mockSTA, true);
+
+    // Assert that the update was successful
+    TEST_ASSERT_TRUE(result);
+
+    // Retrieve the updated entry and check if it's correctly updated
+    auto updatedEntry = manager.Get(&mockSTA);
+    TEST_ASSERT_NOT_NULL(updatedEntry);
+    TEST_ASSERT_EQUAL(mockSTA.channel, updatedEntry->channel);
+    TEST_ASSERT_TRUE(updatedEntry->connected); // Check if connected is now true
+    manager.Clear();
+}
+
+TEST_CASE("Convert AP record to STA entry", "[WifiCredentialsManager]") {
+    int testindex = 1;
+    WifiCredentialsManager manager("test_manager");
+
+    // Create a mock wifi_ap_record_t
+    wifi_ap_record_t mockAP = getMockAPRec(testindex);
+
+    sys_net_wifi_entry entry; // Create an entry to be populated
+
+    // Call ToSTAEntry
+    sys_net_wifi_entry& resultEntry = manager.ToSTAEntry(&mockAP, entry);
+
+    // Assert that the entry is populated correctly
+    TEST_ASSERT_EQUAL_STRING("SSID_1", resultEntry.ssid);
+    TEST_ASSERT_EQUAL(start_channel + testindex, resultEntry.channel);
+    TEST_ASSERT_EQUAL(start_rssi + testindex, resultEntry.rssi);
+    TEST_ASSERT_EQUAL(WifiList::GetAuthType(AUTH_MODE_INDEX(testindex)), resultEntry.auth_type);
+    TEST_ASSERT_EQUAL(4, resultEntry.radio_type_count);
+    std::string formattedString = manager.formatRadioTypes(resultEntry.radio_type, resultEntry.radio_type_count);
+    // Define the expected result
+    std::string expectedResult = "B,N,W,FI";
+    TEST_ASSERT_EQUAL_STRING(expectedResult.c_str(), formattedString.c_str());
+    char bssid_buffer[20] = {};
+    WifiList::FormatBSSID(bssid_buffer, sizeof(bssid_buffer), mockAP.bssid);
+    TEST_ASSERT_EQUAL_STRING((char*)bssid_buffer, resultEntry.bssid);
+
+    manager.Clear();
+}
+
+TEST_CASE("Remove entries from manager instance", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+    FillSSIDs(manager, 4);
+    TEST_ASSERT_EQUAL(4, manager.GetCount());
+    TEST_ASSERT_TRUE(manager.RemoveCredential("SSID_1"));
+    TEST_ASSERT_EQUAL(3, manager.GetCount());
+    TEST_ASSERT_FALSE(manager.RemoveCredential("SSID_1"));
+    TEST_ASSERT_EQUAL(3, manager.GetCount());
+    manager.Clear();
+    TEST_ASSERT_EQUAL(0, manager.GetCount());
+
+    FillSSIDs(manager, 4);
+    TEST_ASSERT_EQUAL(4, manager.GetCount());
+    TEST_ASSERT_TRUE(manager.RemoveCredential(std::string("SSID_1")));
+    TEST_ASSERT_FALSE(manager.RemoveCredential(std::string("SSID_1")));
+    TEST_ASSERT_TRUE(manager.RemoveCredential("SSID_2"));
+    TEST_ASSERT_FALSE(manager.RemoveCredential(std::string("SSID_2")));
+    TEST_ASSERT_EQUAL(2, manager.GetCount());
+    manager.Clear();
+    TEST_ASSERT_EQUAL(0, manager.GetCount());
+}
+TEST_CASE("Reset all entries", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+    FillSSIDs(manager, 4);
+    for (auto& e : manager) {
+        TEST_ASSERT_TRUE(e.second.rssi > 0);
+        TEST_ASSERT_TRUE(e.second.connected);
+    }
+    manager.ResetRSSI();
+    for (auto& e : manager) {
+        TEST_ASSERT_TRUE(e.second.rssi == 0);
+        TEST_ASSERT_TRUE(e.second.connected);
+    }
+    manager.Clear();
+    FillSSIDs(manager, 4);
+    manager.ResetConnected();
+    for (auto& e : manager) {
+        TEST_ASSERT_TRUE(e.second.rssi > 0);
+        TEST_ASSERT_FALSE(e.second.connected);
+    }
+}
+
+TEST_CASE("Getting/setting Connected", "[WifiCredentialsManager]") {
+    WifiCredentialsManager manager("test_manager");
+    auto conn_mock_4 = getMockConnectedEvent(4);
+    auto conn_mock_5 = getMockConnectedEvent(5);
+    auto entry_5 = getMockEntry(5);
+    char bssid_buffer[20] = {};
+    FillSSIDs(manager, 4);
+    for (auto& e : manager) {
+        if (strcmp(e.second.ssid, "SSID_3") != 0) {
+            e.second.connected = false;
+        }
+    }
+    auto entry = manager.GetConnected();
+    TEST_ASSERT_EQUAL_STRING("SSID_3", entry->ssid);
+    TEST_ASSERT_FALSE(manager.SetConnected(&conn_mock_5, true));
+    manager.AddUpdate(&entry_5);
+    WifiCredentialsManager::Release(entry_5);
+    TEST_ASSERT_TRUE(manager.SetConnected(&conn_mock_5, true));
+    entry = manager.GetConnected();
+    TEST_ASSERT_EQUAL_STRING_LEN((char*)conn_mock_5.ssid, entry->ssid, conn_mock_5.ssid_len);
+    WifiCredentialsManager::FormatBSSID(bssid_buffer, sizeof(bssid_buffer), conn_mock_5.bssid);
+    TEST_ASSERT_EQUAL_STRING((char*)bssid_buffer, entry->bssid);
+    TEST_ASSERT_EQUAL(WifiList::GetAuthType(conn_mock_5.authmode), entry->auth_type);
+    TEST_ASSERT_EQUAL(conn_mock_5.channel, entry->channel);
+    TEST_ASSERT_TRUE(manager.SetConnected(&conn_mock_4, true));
+    entry = manager.GetConnected();
+    TEST_ASSERT_EQUAL_STRING_LEN((char*)conn_mock_4.ssid, entry->ssid, conn_mock_5.ssid_len);
+    WifiList::FormatBSSID(bssid_buffer, sizeof(bssid_buffer), conn_mock_4.bssid);
+    TEST_ASSERT_EQUAL_STRING((char*)bssid_buffer, entry->bssid);
+    TEST_ASSERT_EQUAL(WifiList::GetAuthType(conn_mock_4.authmode), entry->auth_type);
+    TEST_ASSERT_EQUAL(conn_mock_4.channel, entry->channel);
+    manager.ResetConnected();
+    TEST_ASSERT_NULL(manager.GetConnected());
+    manager.Clear();
+}
+
+TEST_CASE("Getting by index/by name", "[WifiCredentialsManager]") {
+    char buffer[25] = {};
+    WifiCredentialsManager manager("test_manager");
+    FillSSIDs(manager, 4);
+    for (int i = 0; i < manager.GetCount(); i++) {
+        sprintf(buffer, "SSID_%d", i + 1);
+        auto mockap = getMockAPRec(i + 1);
+        auto mockSTA = getMockSTA(i + 1);
+        auto entry = manager.GetIndex(i);
+        TEST_ASSERT_EQUAL_STRING(buffer, entry->ssid);
+        auto pEntry = manager.Get(std::string(buffer));
+        TEST_ASSERT_EQUAL_STRING(buffer, pEntry->ssid);
+        pEntry = manager.Get(buffer);
+        TEST_ASSERT_EQUAL_STRING(buffer, pEntry->ssid);
+        pEntry = manager.Get(&mockap);
+        TEST_ASSERT_EQUAL_STRING(buffer, pEntry->ssid);
+        pEntry = manager.Get(&mockSTA);
+        TEST_ASSERT_EQUAL_STRING(buffer, pEntry->ssid);
+    }
+    auto entry = manager.GetStrongestSTA();
+    TEST_ASSERT_EQUAL_STRING("SSID_4", entry.ssid);
+    manager.Clear();
+}
+
+TEST_CASE("Update last try", "[WifiCredentialsManager]") {
+    char buffer[25] = {};
+    google_protobuf_Timestamp ts;
+    bool flag;
+    WifiCredentialsManager::UpdateTimeStamp(&ts, flag);
+
+    // now advance the time to ensure tests are a success
+    advanceTime(2);
+
+    WifiList manager("test_manager");
+    FillSSIDs(manager, 4);
+    for (int i = 0; i < manager.GetCount(); i++) {
+        sprintf(buffer, "SSID_%d", i + 1);
+        auto mockap = getMockAPRec(i + 1);
+        auto mockSTA = getMockSTA(i + 1);
+        auto entry = manager.GetIndex(i);
+        entry->has_last_try = false;
+        memset(&entry->last_try, 0x00, sizeof(entry->last_try));
+        manager.UpdateLastTry(entry);
+        TEST_ASSERT_TRUE(entry->has_last_try);
+        TEST_ASSERT_TRUE(entry->last_try.seconds >= ts.seconds);
+        entry->has_last_try = false;
+        memset(&entry->last_try, 0x00, sizeof(entry->last_try));
+        manager.UpdateLastTry(std::string(buffer));
+        TEST_ASSERT_TRUE(entry->has_last_try);
+        TEST_ASSERT_TRUE(entry->last_try.seconds >= ts.seconds);
+        entry->has_last_try = false;
+        memset(&entry->last_try, 0x00, sizeof(entry->last_try));
+        manager.UpdateLastTry(&mockap);
+        TEST_ASSERT_TRUE(entry->has_last_try);
+        TEST_ASSERT_TRUE(entry->last_try.seconds >= ts.seconds);
+        entry->has_last_try = false;
+        memset(&entry->last_try, 0x00, sizeof(entry->last_try));
+        manager.UpdateLastTry(&mockSTA);
+        TEST_ASSERT_TRUE(entry->has_last_try);
+        TEST_ASSERT_TRUE(entry->last_try.seconds >= ts.seconds);
+    }
+    auto entry = manager.GetStrongestSTA();
+    TEST_ASSERT_EQUAL_STRING("SSID_4", entry.ssid);
+    manager.Clear();
+    advanceTime(-2);
+}
+
+TEST_CASE("Update last seen", "[WifiCredentialsManager]") {
+    char buffer[25] = {};
+    google_protobuf_Timestamp ts;
+    bool flag;
+    WifiCredentialsManager::UpdateTimeStamp(&ts, flag);
+
+    // now advance the time to ensure tests are a success
+    advanceTime(2);
+
+    WifiList manager("test_manager");
+    FillSSIDs(manager, 4);
+    for (int i = 0; i < manager.GetCount(); i++) {
+        sprintf(buffer, "SSID_%d", i + 1);
+        auto mockap = getMockAPRec(i + 1);
+        auto mockSTA = getMockSTA(i + 1);
+        auto entry = manager.GetIndex(i);
+        entry->has_last_seen = false;
+        memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+        manager.UpdateLastSeen(entry);
+        TEST_ASSERT_TRUE(entry->has_last_seen);
+        TEST_ASSERT_TRUE(entry->last_seen.seconds >= ts.seconds);
+        entry->has_last_seen = false;
+        memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+        manager.UpdateLastSeen(std::string(buffer));
+        TEST_ASSERT_TRUE(entry->has_last_seen);
+        TEST_ASSERT_TRUE(entry->last_seen.seconds >= ts.seconds);
+        entry->has_last_seen = false;
+        memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+        manager.UpdateLastSeen(&mockap);
+        TEST_ASSERT_TRUE(entry->has_last_seen);
+        TEST_ASSERT_TRUE(entry->last_seen.seconds >= ts.seconds);
+        entry->has_last_seen = false;
+        memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+        manager.UpdateLastSeen(&mockSTA);
+        TEST_ASSERT_TRUE(entry->has_last_seen);
+        TEST_ASSERT_TRUE(entry->last_seen.seconds >= ts.seconds);
+    }
+
+    manager.Clear();
+    advanceTime(-2);
+}
+
+TEST_CASE("Memory leak test", "[WifiCredentialsManager]") {
+    char buffer[25] = {};
+    char err_buffer[55] = {};
+    int fillqty=20;
+    google_protobuf_Timestamp ts;
+    bool flag;
+
+    WifiCredentialsManager::UpdateTimeStamp(&ts, flag);
+
+    // now advance the time to ensure tests are a success
+    advanceTime(2);
+    HasMemoryUsageIncreased(0);
+
+    for (int runs = 0; runs < 100; runs++) {
+        WifiCredentialsManager manager("test_manager");
+        FillSSIDs(manager, fillqty);
+        for (int i = 1; i <= manager.GetCount(); i++) {
+            sprintf(buffer, "SSID_%d", i);
+            sprintf(err_buffer,"Round %d, SSID_%d",runs,i);
+            auto mockap = getMockAPRec(i);
+            auto mockSTA = getMockSTA(i);
+            auto entry = manager.Get(buffer);
+            TEST_ASSERT_EQUAL_STRING(buffer,entry->ssid);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(entry);
+            TEST_ASSERT_TRUE(entry->has_last_seen);
+            TEST_ASSERT_TRUE(entry->last_seen.seconds >= ts.seconds);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(std::string(buffer));
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(&mockap);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(&mockSTA);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+        }
+        manager.Clear();
+    }
+    TEST_ASSERT_FALSE(HasMemoryUsageIncreased(1));
+
+    for (int runs = 0; runs < 100; runs++) {
+        WifiCredentialsManager manager("test_manager");
+        FillSSIDFromAPRec(manager, fillqty);
+        for (int i = 1; i <= manager.GetCount(); i++) {
+            sprintf(buffer, "SSID_%d", i);
+            sprintf(err_buffer,"Round %d, SSID_%d",runs,i);
+            auto mockap = getMockAPRec(i);
+            auto mockSTA = getMockSTA(i);
+            auto entry = manager.GetIndex(i-1);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(entry);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(std::string(buffer));
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(&mockap);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(&mockSTA);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+        }
+        manager.Clear();
+    }
+    TEST_ASSERT_FALSE(HasMemoryUsageIncreased(2));
+
+    for (int runs = 0; runs < 100; runs++) {
+        WifiCredentialsManager manager("test_manager");
+        FillSSIDFromMockEntry(manager, fillqty);
+        for (int i = 1; i <= manager.GetCount(); i++) {
+            sprintf(buffer, "SSID_%d", i);
+            sprintf(err_buffer,"Round %d, SSID_%d",runs,i);
+            auto mockap = getMockAPRec(i);
+            auto mockSTA = getMockSTA(i);
+            auto entry = manager.GetIndex(i-1);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(entry);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(std::string(buffer));
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(&mockap);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+            entry->has_last_seen = false;
+            memset(&entry->last_seen, 0x00, sizeof(entry->last_seen));
+            manager.UpdateLastSeen(&mockSTA);
+            TEST_ASSERT_TRUE_MESSAGE(entry->has_last_seen,err_buffer);
+            TEST_ASSERT_TRUE_MESSAGE(entry->last_seen.seconds >= ts.seconds,err_buffer);
+        }
+        manager.Clear();
+    }
+    advanceTime(-2);
+    TEST_ASSERT_FALSE(HasMemoryUsageIncreased(3));
+}

+ 1 - 1
components/platform_console/CMakeLists.txt

@@ -7,7 +7,7 @@ idf_component_register( SRCS
 							cmd_config.c
 							cmd_config.c
 						INCLUDE_DIRS .   
 						INCLUDE_DIRS .   
 						REQUIRES nvs_flash 
 						REQUIRES nvs_flash 
-						PRIV_REQUIRES console app_update tools services spi_flash  platform_config vfs pthread wifi-manager platform_config newlib  telnet display squeezelite tools metrics)
+						PRIV_REQUIRES console app_update tools services spi_flash tools platform_config vfs pthread wifi-manager  newlib  telnet display squeezelite  metrics)
 
 
 set_source_files_properties(cmd_config.c
 set_source_files_properties(cmd_config.c
     PROPERTIES COMPILE_FLAGS
     PROPERTIES COMPILE_FLAGS

+ 1 - 1
components/platform_console/app_squeezelite/CMakeLists.txt

@@ -1,7 +1,7 @@
 idf_build_get_property(idf_path IDF_PATH)
 idf_build_get_property(idf_path IDF_PATH)
 idf_component_register( SRCS cmd_squeezelite.c 
 idf_component_register( SRCS cmd_squeezelite.c 
 						INCLUDE_DIRS . 
 						INCLUDE_DIRS . 
-						PRIV_REQUIRES spi_flash bootloader_support  partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display tools services)
+						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")
 target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=feof")

+ 98 - 115
components/platform_console/app_squeezelite/cmd_squeezelite.c

@@ -1,23 +1,27 @@
-#include <stdio.h>
-#include <string.h>
-#include "application_name.h"
-#include "esp_log.h"
-#include "esp_console.h"
 #include "../cmd_system.h"
 #include "../cmd_system.h"
+#include "Config.h"
+#include "application_name.h"
 #include "argtable3/argtable3.h"
 #include "argtable3/argtable3.h"
+#include "esp_app_format.h"
+#include "esp_console.h"
+#include "esp_log.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/event_groups.h"
 #include "freertos/event_groups.h"
-#include "pthread.h"
+#include "messaging.h"
 #include "platform_esp32.h"
 #include "platform_esp32.h"
-#include "Configurator.h"
-#include "esp_app_format.h"
+#include "pthread.h"
 #include "tools.h"
 #include "tools.h"
-#include "messaging.h"
-extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
+#include <stdio.h>
+#include <string.h>
+#include "bootstate.h"
+
+extern esp_err_t process_recovery_ota(const char* bin_url, char* bin_buffer, uint32_t length);
 extern int squeezelite_main_start();
 extern int squeezelite_main_start();
+extern sys_dac_default_sets* default_dac_sets;
 
 
-static const char * TAG = "squeezelite_cmd";
-#define SQUEEZELITE_THREAD_STACK_SIZE (8*1024)
+
+static const char* TAG = "squeezelite_cmd";
+#define SQUEEZELITE_THREAD_STACK_SIZE (8 * 1024)
 #define ADDITIONAL_SQUEEZELITE_ARGS 5
 #define ADDITIONAL_SQUEEZELITE_ARGS 5
 
 
 const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
 const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
@@ -45,112 +49,91 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
 extern void register_audio_config(void);
 extern void register_audio_config(void);
 // extern void register_rotary_config(void);
 // extern void register_rotary_config(void);
 extern void register_nvs();
 extern void register_nvs();
-extern cJSON * get_gpio_list_handler(bool refresh);
-cJSON * get_gpio_list(bool refresh){
-#if CONFIG_WITH_CONFIG_UI		
-	return get_gpio_list_handler(refresh);
+extern cJSON* get_gpio_list_handler(bool refresh);
+cJSON* get_gpio_list(bool refresh) {
+#if CONFIG_WITH_CONFIG_UI
+    return get_gpio_list_handler(refresh);
 #else
 #else
-	return cJSON_CreateArray();
+    return cJSON_CreateArray();
 #endif
 #endif
 }
 }
 void register_optional_cmd(void) {
 void register_optional_cmd(void) {
-// #if CONFIG_WITH_CONFIG_UI	
-//     register_rotary_config();
-// #endif
+    // #if CONFIG_WITH_CONFIG_UI
+    //     register_rotary_config();
+    // #endif
     register_audio_config();
     register_audio_config();
-	// register_ledvu_config();
-
+    // register_ledvu_config();
 }
 }
 
 
-/** Arguments used by 'squeezelite' function */
 static struct {
 static struct {
-    struct arg_str *parameters;
-    struct arg_end *end;
-} squeezelite_args;
-static struct {
-	int argc;
-	char ** argv;
-} thread_parms ;
-
-static void squeezelite_thread(void *arg){  
-	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_LOGV(TAG ,"     %s",thread_parms.argv[i]);
-    }
-    ESP_LOGI(TAG ,"Calling squeezelite");
-    int ret = squeezelite_main_start(thread_parms.argc, thread_parms.argv);
-        
-    cmd_send_messaging("cfg-audio-tmpl",ret > 1 ?  MESSAGING_ERROR : MESSAGING_WARNING,"squeezelite exited with error code %d\n", ret);
-
-    if (ret <= 1) {
-        int wait = 60;
-        // wait_for_commit();
-		// TODO: Add support for the commented code
-        cmd_send_messaging("cfg-audio-tmpl",MESSAGING_ERROR,"Rebooting in %d sec\n", wait);
-        vTaskDelay( pdMS_TO_TICKS(wait * 1000));
-        esp_restart();
+    int argc;
+    char** argv;
+} thread_parms;
+
+static void squeezelite_thread(void* arg) {
+    ESP_LOGI(TAG, "Calling squeezelite");
+    int wait = 60;
+    int ret = squeezelite_main_start();
+
+    if (ret == -2) {
+        cmd_send_messaging("cfg-audio-tmpl", MESSAGING_ERROR,
+            "Squeezelite not configured. Rebooting to factory in %d sec\n", wait);
+        vTaskDelay(pdMS_TO_TICKS(wait * 1000));
+        guided_factory();
     } else {
     } else {
-		cmd_send_messaging("cfg-audio-tmpl",MESSAGING_ERROR,"Correct command line and reboot\n");
-        vTaskSuspend(NULL);
+        cmd_send_messaging("cfg-audio-tmpl", ret > 1 ? MESSAGING_ERROR : MESSAGING_WARNING,
+            "squeezelite exited with error code %d\n", ret);
+        if (ret <= 1) {
+
+            cmd_send_messaging(
+                "cfg-audio-tmpl", MESSAGING_ERROR, "Rebooting to factory in %d sec\n", wait);
+            vTaskDelay(pdMS_TO_TICKS(wait * 1000));
+            guided_factory();
+        } else {
+
+            cmd_send_messaging(
+                "cfg-audio-tmpl", MESSAGING_ERROR, "Correct command line and reboot\n");
+            vTaskSuspend(NULL);
+        }
     }
     }
 
 
-	ESP_LOGV(TAG, "Exited from squeezelite's main(). Freeing argv structure.");
+    ESP_LOGV(TAG, "Exited from squeezelite's main(). Freeing argv structure.");
 
 
-	for(int i=0;i<thread_parms.argc;i++) free(thread_parms.argv[i]);
-	free(thread_parms.argv);
+    for (int i = 0; i < thread_parms.argc; i++)
+        free(thread_parms.argv[i]);
+    free(thread_parms.argv);
 }
 }
 
 
-static int launchsqueezelite(int argc, char **argv) {
-	static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
-	static EXT_RAM_ATTR StackType_t xStack[SQUEEZELITE_THREAD_STACK_SIZE] __attribute__ ((aligned (4)));
-	static bool isRunning = false;
-
-	if (isRunning) {
-		ESP_LOGE(TAG,"Squeezelite already running. Exiting!");
-		return -1;
-	}
-	
-	ESP_LOGV(TAG ,"Begin");
-	isRunning = true;
-
-	// ESP_LOGV(TAG, "Parameters:");
-    // for(int i = 0;i<argc; i++){
-    // 	ESP_LOGV(TAG, "     %s",argv[i]);
-    // }
-    // ESP_LOGV(TAG,"Saving args in thread structure");
-
-    // thread_parms.argc=0;
-    // thread_parms.argv = malloc_init_external(sizeof(char**)*(argc+ADDITIONAL_SQUEEZELITE_ARGS));
-
-	// for(int i=0;i<argc;i++){
-	// 	ESP_LOGD(TAG ,"assigning parm %u : %s",i,argv[i]);
-	// 	thread_parms.argv[thread_parms.argc++]=strdup(argv[i]);
-	// }
-
-	// if(argc==1){
-	// 	// There isn't a default configuration that would actually work
-	// 	// if no parameter is passed.
-	// 	ESP_LOGV(TAG ,"Adding argv value at %u. Prev value: %s",thread_parms.argc,thread_parms.argv[thread_parms.argc-1]);
-	// 	thread_parms.argv[thread_parms.argc++]=strdup("-?");
-	// }
-
-	ESP_LOGD(TAG,"Starting Squeezelite Thread");
-	xTaskCreateStaticPinnedToCore(squeezelite_thread, "squeezelite", SQUEEZELITE_THREAD_STACK_SIZE, 
-					  NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, xStack, &xTaskBuffer, CONFIG_PTHREAD_TASK_CORE_DEFAULT);
-	ESP_LOGD(TAG ,"Back to console thread!");
+static int launchsqueezelite(int argc, char** argv) {
+    static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__((aligned(4)));
+    static EXT_RAM_ATTR StackType_t xStack[SQUEEZELITE_THREAD_STACK_SIZE]
+        __attribute__((aligned(4)));
+    static bool isRunning = false;
+
+    if (isRunning) {
+        ESP_LOGE(TAG, "Squeezelite already running. Exiting!");
+        return -1;
+    }
+
+    ESP_LOGV(TAG, "Begin");
+    isRunning = true;
+
+    // load the presets here, as the squeezelite cannot access the 
+    // spiffs storage
+    // proto_load_file("/spiffs/presets/default_sets.bin", &sys_dac_default_sets_msg, default_dac_sets, false);
+
+    ESP_LOGD(TAG, "Starting Squeezelite Thread");
+    xTaskCreateStaticPinnedToCore(squeezelite_thread, "squeezelite", SQUEEZELITE_THREAD_STACK_SIZE,
+        NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, xStack, &xTaskBuffer,
+        CONFIG_PTHREAD_TASK_CORE_DEFAULT);
 
 
     return 0;
     return 0;
 }
 }
-void start_squeezelite(){
-	launchsqueezelite(0,NULL);
-}
+void start_squeezelite() { launchsqueezelite(0, NULL); }
 // void register_squeezelite() {
 // void register_squeezelite() {
-// 	squeezelite_args.parameters = arg_str0(NULL, NULL, "<parms>", "command line for squeezelite. -h for help, --defaults to launch with default values.");
-// 	squeezelite_args.end = arg_end(1);
-// 	const esp_console_cmd_t launch_squeezelite = {
-// 		.command = "squeezelite",
-// 		.help = "Starts squeezelite",
+// 	squeezelite_args.parameters = arg_str0(NULL, NULL, "<parms>", "command line for squeezelite. -h
+// for help, --defaults to launch with default values."); 	squeezelite_args.end = arg_end(1); 	const
+// esp_console_cmd_t launch_squeezelite = { 		.command = "squeezelite", 		.help = "Starts squeezelite",
 // 		.hint = NULL,
 // 		.hint = NULL,
 // 		.func = &launchsqueezelite,
 // 		.func = &launchsqueezelite,
 // 		.argtable = &squeezelite_args
 // 		.argtable = &squeezelite_args
@@ -158,19 +141,19 @@ void start_squeezelite(){
 // 	ESP_ERROR_CHECK( esp_console_cmd_register(&launch_squeezelite) );
 // 	ESP_ERROR_CHECK( esp_console_cmd_register(&launch_squeezelite) );
 // }
 // }
 
 
-esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length) {
-	if(!bin_url || strlen(bin_url)==0){
-		ESP_LOGE(TAG,"missing URL parameter. Unable to start OTA");
-		return ESP_ERR_INVALID_ARG;
-	}
-	ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
-	configurator_set_string(&sys_State_msg,sys_State_ota_url_tag,sys_state,bin_url);
-	configurator_raise_state_changed();
-
-	if(!configurator_waitcommit()){
-		ESP_LOGW(TAG,"Unable to commit configuration. ");
-	}
-	ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
-	return guided_factory();
-	return ESP_OK;
+esp_err_t start_ota(const char* bin_url, char* bin_buffer, uint32_t length) {
+    if (!bin_url || strlen(bin_url) == 0) {
+        ESP_LOGE(TAG, "missing URL parameter. Unable to start OTA");
+        return ESP_ERR_INVALID_ARG;
+    }
+    ESP_LOGW(TAG, "Called to update the firmware from url: %s", bin_url);
+    system_set_string(&sys_state_data_msg, sys_state_data_ota_url_tag, sys_state, bin_url);
+    config_raise_state_changed();
+
+    if (!config_waitcommit()) {
+        ESP_LOGW(TAG, "Unable to commit configuration. ");
+    }
+    ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
+    return guided_factory();
+    return ESP_OK;
 }
 }

Разлика између датотеке није приказан због своје велике величине
+ 463 - 415
components/platform_console/cmd_config.c


+ 48 - 49
components/platform_console/cmd_i2ctools.c

@@ -16,7 +16,7 @@
 #include "driver/i2c.h"
 #include "driver/i2c.h"
 #include "esp_log.h"
 #include "esp_log.h"
 #include "messaging.h"
 #include "messaging.h"
-// #include "Configurator.h"
+// #include "Config.h"
 #pragma message("fixme: look for TODO below")
 #pragma message("fixme: look for TODO below")
 #include "platform_console.h"
 #include "platform_console.h"
 #include "stdio.h"
 #include "stdio.h"
@@ -41,19 +41,18 @@ const char* desc_spiconfig = "SPI Bus Parameters";
 const char* desc_i2c = "I2C Bus Parameters";
 const char* desc_i2c = "I2C Bus Parameters";
 const char* desc_display = "Display";
 const char* desc_display = "Display";
 
 
-#ifdef CONFIG_I2C_LOCKED
-static i2c_port_t i2c_port = I2C_NUM_1;
-#else
-static i2c_port_t i2c_port = I2C_NUM_0;
-#endif
 
 
 static struct {
 static struct {
+    struct arg_int* port;
     struct arg_int* chip_address;
     struct arg_int* chip_address;
     struct arg_int* register_address;
     struct arg_int* register_address;
     struct arg_int* data_length;
     struct arg_int* data_length;
     struct arg_end* end;
     struct arg_end* end;
 } i2cget_args;
 } i2cget_args;
-
+static struct {
+    struct arg_int* port;
+    struct arg_end* end;
+} i2cdetect_args;
 static struct {
 static struct {
     struct arg_int* chip_address;
     struct arg_int* chip_address;
     struct arg_int* port;
     struct arg_int* port;
@@ -288,31 +287,31 @@ static esp_err_t i2c_get_port(int port, i2c_port_t* i2c_port) {
     }
     }
     return ESP_OK;
     return ESP_OK;
 }
 }
-static esp_err_t i2c_master_driver_install(const char* cmdname) {
-    esp_err_t err = ESP_OK;
-    cmd_send_messaging(cmdname, MESSAGING_INFO, "Installing i2c driver on port %u\n", i2c_port);
-    if ((err = i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE,
-             I2C_MASTER_TX_BUF_DISABLE, 0)) != ESP_OK) {
-        cmd_send_messaging(
-            cmdname, MESSAGING_ERROR, "Driver install failed! %s\n", esp_err_to_name(err));
-    }
-    return err;
-}
-
-static esp_err_t i2c_master_driver_initialize(const char* cmdname, i2c_config_t* conf) {
-    esp_err_t err = ESP_OK;
-    cmd_send_messaging(cmdname, MESSAGING_INFO,
-        "Initializing i2c driver configuration.\n   mode = I2C_MODE_MASTER, \n   scl_pullup_en = "
-        "GPIO_PULLUP_ENABLE, \n   i2c port = %u, \n   sda_io_num = %u, \n   sda_pullup_en = "
-        "GPIO_PULLUP_ENABLE, \n   scl_io_num = %u, \n   scl_pullup_en = GPIO_PULLUP_ENABLE, \n   "
-        "master.clk_speed = %u\n",
-        i2c_port, conf->sda_io_num, conf->scl_io_num, conf->master.clk_speed);
-    if ((err = i2c_param_config(i2c_port, conf)) != ESP_OK) {
-        cmd_send_messaging(
-            cmdname, MESSAGING_ERROR, "i2c driver config load failed. %s\n", esp_err_to_name(err));
-    }
-    return err;
-}
+// static esp_err_t i2c_master_driver_install(const char* cmdname) {
+//     esp_err_t err = ESP_OK;
+//     cmd_send_messaging(cmdname, MESSAGING_INFO, "Installing i2c driver on port %u\n", i2c_port);
+//     if ((err = i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE,
+//              I2C_MASTER_TX_BUF_DISABLE, 0)) != ESP_OK) {
+//         cmd_send_messaging(
+//             cmdname, MESSAGING_ERROR, "Driver install failed! %s\n", esp_err_to_name(err));
+//     }
+//     return err;
+// }
+
+// static esp_err_t i2c_master_driver_initialize(const char* cmdname, i2c_config_t* conf) {
+//     esp_err_t err = ESP_OK;
+//     cmd_send_messaging(cmdname, MESSAGING_INFO,
+//         "Initializing i2c driver configuration.\n   mode = I2C_MODE_MASTER, \n   scl_pullup_en = "
+//         "GPIO_PULLUP_ENABLE, \n   i2c port = %u, \n   sda_io_num = %u, \n   sda_pullup_en = "
+//         "GPIO_PULLUP_ENABLE, \n   scl_io_num = %u, \n   scl_pullup_en = GPIO_PULLUP_ENABLE, \n   "
+//         "master.clk_speed = %u\n",
+//         i2c_port, conf->sda_io_num, conf->scl_io_num, conf->master.clk_speed);
+//     if ((err = i2c_param_config(i2c_port, conf)) != ESP_OK) {
+//         cmd_send_messaging(
+//             cmdname, MESSAGING_ERROR, "i2c driver config load failed. %s\n", esp_err_to_name(err));
+//     }
+//     return err;
+// }
 
 
 #if CONFIG_WITH_CONFIG_UI
 #if CONFIG_WITH_CONFIG_UI
 static int do_i2c_set_display(int argc, char** argv) {
 static int do_i2c_set_display(int argc, char** argv) {
@@ -763,12 +762,9 @@ static int do_i2cget_cmd(int argc, char** argv) {
     if (i2cget_args.data_length->count) {
     if (i2cget_args.data_length->count) {
         len = i2cget_args.data_length->ival[0];
         len = i2cget_args.data_length->ival[0];
     }
     }
-    i2c_port_t loc_i2c_port = i2c_port;
-    if (i2cset_args.port->count &&
-        i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
-        return 1;
-    }
-
+    i2c_port_t loc_i2c_port = i2cget_args.port->ival[0];
+    const sys_i2c_bus*i2c = get_i2c_bus(loc_i2c_port);
+    #pragma message("TODO: Fix this!")
     char* buf = NULL;
     char* buf = NULL;
     size_t buf_size = 0;
     size_t buf_size = 0;
     FILE* f = open_memstream(&buf, &buf_size);
     FILE* f = open_memstream(&buf, &buf_size);
@@ -897,18 +893,21 @@ esp_err_t cmd_i2ctools_scan_bus(FILE* f, int sda, int scl) {
 
 
     return 0;
     return 0;
 }
 }
+
 static int do_i2cdetect_cmd(int argc, char** argv) {
 static int do_i2cdetect_cmd(int argc, char** argv) {
 #ifdef CONFIG_WITH_CONFIG_UI
 #ifdef CONFIG_WITH_CONFIG_UI
     uint8_t matches[128] = {};
     uint8_t matches[128] = {};
     int last_match = 0;
     int last_match = 0;
 #endif
 #endif
     esp_err_t ret = ESP_OK;
     esp_err_t ret = ESP_OK;
-    i2c_port_t loc_i2c_port = i2c_port;
+    i2c_port_t loc_i2c_port = i2cdetect_args.port->ival[0];
+    
     // if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) !=
     // if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) !=
     // ESP_OK) { 	return 1;
     // ESP_OK) { 	return 1;
     // }
     // }
-    int i2c_port;
-    const i2c_config_t* i2c = config_i2c_get(&i2c_port);
+
+    const sys_i2c_bus*i2c = get_i2c_bus(loc_i2c_port);
+
 
 
     uint8_t address;
     uint8_t address;
     char* buf = NULL;
     char* buf = NULL;
@@ -919,8 +918,7 @@ static int do_i2cdetect_cmd(int argc, char** argv) {
         return 1;
         return 1;
     }
     }
 
 
-    if (!GPIO_IS_VALID_OUTPUT_GPIO(i2c->scl_io_num) || !GPIO_IS_VALID_GPIO(i2c->scl_io_num) ||
-        !GPIO_IS_VALID_OUTPUT_GPIO(i2c->sda_io_num) || !GPIO_IS_VALID_GPIO(i2c->sda_io_num)) {
+    if (!i2c) {
         fprintf(f, "I2C bus configuration invalid.\r\n");
         fprintf(f, "I2C bus configuration invalid.\r\n");
     } else {
     } else {
 
 
@@ -1007,12 +1005,13 @@ static void register_i2c_set_display() {
 }
 }
 #endif
 #endif
 static void register_i2cdectect(void) {
 static void register_i2cdectect(void) {
+    i2cdetect_args.port = arg_int1(NULL,NULL, "<0|1>", "Specify the i2c port number");
+    i2cdetect_args.end = arg_end(1);
     const esp_console_cmd_t i2cdetect_cmd = {.command = "i2cdetect",
     const esp_console_cmd_t i2cdetect_cmd = {.command = "i2cdetect",
         .help = "Scan I2C bus for devices",
         .help = "Scan I2C bus for devices",
         .hint = NULL,
         .hint = NULL,
         .func = &do_i2cdetect_cmd,
         .func = &do_i2cdetect_cmd,
-        .argtable = NULL};
-    cmd_to_json(&i2cdetect_cmd);
+        .argtable = &i2cdetect_args};
     ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdetect_cmd));
     ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdetect_cmd));
 }
 }
 
 
@@ -1023,6 +1022,7 @@ static void register_i2cget(void) {
         "r", "register", "<register_addr>", "Specify the address on that chip to read from");
         "r", "register", "<register_addr>", "Specify the address on that chip to read from");
     i2cget_args.data_length =
     i2cget_args.data_length =
         arg_int0("l", "length", "<length>", "Specify the length to read from that data address");
         arg_int0("l", "length", "<length>", "Specify the length to read from that data address");
+    i2cget_args.port = arg_int1(NULL,NULL, "<0|1>", "Specify the i2c port number");
     i2cget_args.end = arg_end(1);
     i2cget_args.end = arg_end(1);
     const esp_console_cmd_t i2cget_cmd = {.command = "i2cget",
     const esp_console_cmd_t i2cget_cmd = {.command = "i2cget",
         .help = "Read registers visible through the I2C bus",
         .help = "Read registers visible through the I2C bus",
@@ -1070,8 +1070,7 @@ static void register_i2cdump(void) {
 
 
 cJSON* i2config_cb() {
 cJSON* i2config_cb() {
     cJSON* values = cJSON_CreateObject();
     cJSON* values = cJSON_CreateObject();
-    int i2c_port;
-    const i2c_config_t* i2c = config_i2c_get(&i2c_port);
+    const i2c_config_t* i2c = config_i2c_get(&platform->dev.i2c);
     if (i2c->scl_io_num > 0) {
     if (i2c->scl_io_num > 0) {
         cJSON_AddNumberToObject(values, "scl", i2c->scl_io_num);
         cJSON_AddNumberToObject(values, "scl", i2c->scl_io_num);
     }
     }
@@ -1081,8 +1080,8 @@ cJSON* i2config_cb() {
     if (i2c->master.clk_speed > 0) {
     if (i2c->master.clk_speed > 0) {
         cJSON_AddNumberToObject(values, "speed", i2c->master.clk_speed);
         cJSON_AddNumberToObject(values, "speed", i2c->master.clk_speed);
     }
     }
-    if (i2c_port >= 0) {
-        cJSON_AddNumberToObject(values, "port", i2c_port);
+    if (platform->dev.i2c.port-sys_i2c_port_PORT0 >= 0) {
+        cJSON_AddNumberToObject(values, "port", platform->dev.i2c.port-sys_i2c_port_PORT0);
     }
     }
     return values;
     return values;
 }
 }

Разлика између датотеке није приказан због своје велике величине
+ 460 - 347
components/platform_console/cmd_system.c


+ 1 - 3
components/platform_console/cmd_system.h

@@ -14,9 +14,7 @@ extern "C" {
 
 
 // Register system functions
 // Register system functions
 void register_system();
 void register_system();
-esp_err_t guided_factory();
-esp_err_t guided_restart_ota();
-void simple_restart();
+
 FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size);
 FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size);
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 1 - 1
components/platform_console/cmd_wifi.c

@@ -54,7 +54,7 @@ static struct {
 
 
 
 
 
 
-// todo: implement access point config - cmd_to_json(&i2cdetect_cmd);
+
 
 
 static void event_handler(void* arg, esp_event_base_t event_base,
 static void event_handler(void* arg, esp_event_base_t event_base,
                                 int32_t event_id, void* event_data)
                                 int32_t event_id, void* event_data)

+ 319 - 381
components/platform_console/platform_console.c

@@ -9,24 +9,24 @@
 
 
 #include "platform_console.h"
 #include "platform_console.h"
 
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "esp_log.h"
+#include "argtable3/argtable3.h"
+#include "bootstate.h"
+#include "cmd_decl.h"
+#include "driver/uart.h"
 #include "esp_console.h"
 #include "esp_console.h"
+#include "esp_log.h"
 #include "esp_vfs_dev.h"
 #include "esp_vfs_dev.h"
-#include "driver/uart.h"
 #include "linenoise/linenoise.h"
 #include "linenoise/linenoise.h"
-#include "argtable3/argtable3.h"
-#include "nvs.h" 
+#include "nvs.h"
 #include "nvs_flash.h"
 #include "nvs_flash.h"
-#include "pthread.h"
 #include "platform_esp32.h"
 #include "platform_esp32.h"
-#include "cmd_decl.h"
+#include "pthread.h"
 #include "trace.h"
 #include "trace.h"
-// #include "Configurator.h"
-#pragma message("fixme: search for TODO below")
-#include "telnet.h" 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "telnet.h"
 #include "tools.h"
 #include "tools.h"
 #if defined(CONFIG_WITH_METRICS)
 #if defined(CONFIG_WITH_METRICS)
 #include "metrics.h"
 #include "metrics.h"
@@ -35,19 +35,19 @@
 
 
 #include "config.h"
 #include "config.h"
 static pthread_t thread_console;
 static pthread_t thread_console;
-static void * console_thread();
+static void* console_thread();
 void console_start();
 void console_start();
-static const char * TAG = "console";
+static const char* TAG = "console";
 extern bool bypass_network_manager;
 extern bool bypass_network_manager;
 extern void launchsqueezelite();
 extern void launchsqueezelite();
 
 
 static EXT_RAM_ATTR QueueHandle_t uart_queue;
 static EXT_RAM_ATTR QueueHandle_t uart_queue;
 static EXT_RAM_ATTR struct {
 static EXT_RAM_ATTR struct {
-		uint8_t _buf[512];
-		StaticRingbuffer_t _ringbuf;
-		RingbufHandle_t handle;
-		QueueSetHandle_t queue_set;
-} stdin_redir;	
+    uint8_t _buf[512];
+    StaticRingbuffer_t _ringbuf;
+    RingbufHandle_t handle;
+    QueueSetHandle_t queue_set;
+} stdin_redir;
 
 
 /* Prompt to be printed before each line.
 /* Prompt to be printed before each line.
  * This can be customized, made dynamic, etc.
  * This can be customized, made dynamic, etc.
@@ -62,94 +62,99 @@ const char* recovery_prompt = LOG_COLOR_E "recovery-squeezelite-esp32> " LOG_RES
 
 
 #define MOUNT_PATH "/data"
 #define MOUNT_PATH "/data"
 #define HISTORY_PATH MOUNT_PATH "/history.txt"
 #define HISTORY_PATH MOUNT_PATH "/history.txt"
-static esp_err_t run_command(char * line);
-#define ADD_TO_JSON(o,t,n) if (t->n) cJSON_AddStringToObject(o,QUOTE(n),t->n);
-#define ADD_PARMS_TO_CMD(o,t,n) { cJSON * parms = ParmsToJSON(&t.n->hdr); if(parms) cJSON_AddItemToObject(o,QUOTE(n),parms); }
-cJSON * cmdList;
-cJSON * values_fn_list;
-cJSON * get_cmd_list(){
-	cJSON * element;
-	cJSON * values=cJSON_CreateObject();
-	cJSON * list = cJSON_CreateObject();
-	cJSON_AddItemReferenceToObject(list,"commands",cmdList);
-	cJSON_AddItemToObject(list,"values",values);
-	cJSON_ArrayForEach(element,cmdList){
-		cJSON * name = cJSON_GetObjectItem(element,"name");
-		cJSON * vals_fn = cJSON_GetObjectItem(values_fn_list,cJSON_GetStringValue(name));
-		if(vals_fn!=NULL ){
-			parm_values_fn_t *parm_values_fn = (parm_values_fn_t *)strtoul(cJSON_GetStringValue(vals_fn), NULL, 16);;
-
-			if(parm_values_fn){
-				cJSON_AddItemToObject(values,cJSON_GetStringValue(name),parm_values_fn());
-			}
-		}
-	}
-	return list;
+static esp_err_t run_command(char* line);
+#define ADD_TO_JSON(o, t, n)                                                                                                                         \
+    if (t->n) cJSON_AddStringToObject(o, QUOTE(n), t->n);
+#define ADD_PARMS_TO_CMD(o, t, n)                                                                                                                    \
+    {                                                                                                                                                \
+        cJSON* parms = ParmsToJSON(&t.n->hdr);                                                                                                       \
+        if (parms) cJSON_AddItemToObject(o, QUOTE(n), parms);                                                                                        \
+    }
+cJSON* cmdList;
+cJSON* values_fn_list;
+cJSON* get_cmd_list() {
+    cJSON* element;
+    cJSON* values = cJSON_CreateObject();
+    cJSON* list = cJSON_CreateObject();
+    cJSON_AddItemReferenceToObject(list, "commands", cmdList);
+    cJSON_AddItemToObject(list, "values", values);
+    cJSON_ArrayForEach(element, cmdList) {
+        cJSON* name = cJSON_GetObjectItem(element, "name");
+        cJSON* vals_fn = cJSON_GetObjectItem(values_fn_list, cJSON_GetStringValue(name));
+        if (vals_fn != NULL) {
+            parm_values_fn_t* parm_values_fn = (parm_values_fn_t*)strtoul(cJSON_GetStringValue(vals_fn), NULL, 16);
+            ;
+
+            if (parm_values_fn) {
+                cJSON_AddItemToObject(values, cJSON_GetStringValue(name), parm_values_fn());
+            }
+        }
+    }
+    return list;
 }
 }
-void console_set_bool_parameter(cJSON * root,char * nvs_name, struct arg_lit *arg){
-    char * p=NULL;
-	bool enabled = false;
-	if(!root) {
-        ESP_LOGE(TAG,"Invalid json parameter. Cannot set %s from %s",arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary,nvs_name);
+void console_set_bool_parameter(cJSON* root, char* nvs_name, struct arg_lit* arg) {
+    char* p = NULL;
+    bool enabled = false;
+    if (!root) {
+        ESP_LOGE(TAG, "Invalid json parameter. Cannot set %s from %s", arg->hdr.longopts ? arg->hdr.longopts : arg->hdr.glossary, nvs_name);
         return;
         return;
     }
     }
-//     if ((p = config_alloc_get(NVS_TYPE_STR, nvs_name)) != NULL) {
-// 		enabled = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
-// 		cJSON_AddBoolToObject(root,arg->hdr.longopts,enabled);
-//         FREE_AND_NULL(p);
-//     }
-// #if defined(CONFIG_WITH_METRICS)	
-// 	if(enabled){
-// 		metrics_add_feature(nvs_name,"enabled");
-// 	}
-// #endif
-// TODO: Add support for the commented code
-	
+    //     if ((p = config_alloc_get(NVS_TYPE_STR, nvs_name)) != NULL) {
+    // 		enabled = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
+    // 		cJSON_AddBoolToObject(root,arg->hdr.longopts,enabled);
+    //         FREE_AND_NULL(p);
+    //     }
+    // #if defined(CONFIG_WITH_METRICS)
+    // 	if(enabled){
+    // 		metrics_add_feature(nvs_name,"enabled");
+    // 	}
+    // #endif
+    // TODO: Add support for the commented code
 }
 }
-struct arg_end *getParmsEnd(struct arg_hdr * * argtable){
-	if(!argtable) return NULL;
-	struct arg_hdr * *table = (struct arg_hdr * *)argtable;
-	int tabindex = 0;
-	while (!(table[tabindex]->flag & ARG_TERMINATOR))
-	{
-		tabindex++;
-	}
-	return (struct arg_end *)table[tabindex];
+struct arg_end* getParmsEnd(struct arg_hdr** argtable) {
+    if (!argtable) return NULL;
+    struct arg_hdr** table = (struct arg_hdr**)argtable;
+    int tabindex = 0;
+    while (!(table[tabindex]->flag & ARG_TERMINATOR)) {
+        tabindex++;
+    }
+    return (struct arg_end*)table[tabindex];
 }
 }
-cJSON * ParmsToJSON(struct arg_hdr * * argtable){
-	if(!argtable) return NULL;
-	cJSON * arg_list = cJSON_CreateArray();
-	struct arg_hdr * *table = (struct arg_hdr * *)argtable;
-	int tabindex = 0;
-	while (!(table[tabindex]->flag & ARG_TERMINATOR))
-	{
-		cJSON * entry = cJSON_CreateObject();
-		ADD_TO_JSON(entry,table[tabindex],datatype);
-		ADD_TO_JSON(entry,table[tabindex],glossary);
-		ADD_TO_JSON(entry,table[tabindex],longopts);
-		ADD_TO_JSON(entry,table[tabindex],shortopts);
-		cJSON_AddBoolToObject(entry, "checkbox", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (table[tabindex]->longopts || table[tabindex]->shortopts) );
-		cJSON_AddBoolToObject(entry, "remark", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (!table[tabindex]->longopts && !table[tabindex]->shortopts));
-		cJSON_AddBoolToObject(entry, "hasvalue", table[tabindex]->flag & ARG_HASVALUE);
-		cJSON_AddNumberToObject(entry,"mincount",table[tabindex]->mincount);
-		cJSON_AddNumberToObject(entry,"maxcount",table[tabindex]->maxcount);
-		cJSON_AddItemToArray(arg_list, entry);
-		tabindex++;
-	}
-	return arg_list;
+cJSON* ParmsToJSON(struct arg_hdr** argtable) {
+    if (!argtable) return NULL;
+    cJSON* arg_list = cJSON_CreateArray();
+    struct arg_hdr** table = (struct arg_hdr**)argtable;
+    int tabindex = 0;
+    while (!(table[tabindex]->flag & ARG_TERMINATOR)) {
+        cJSON* entry = cJSON_CreateObject();
+        ADD_TO_JSON(entry, table[tabindex], datatype);
+        ADD_TO_JSON(entry, table[tabindex], glossary);
+        ADD_TO_JSON(entry, table[tabindex], longopts);
+        ADD_TO_JSON(entry, table[tabindex], shortopts);
+        cJSON_AddBoolToObject(entry, "checkbox",
+            (table[tabindex]->flag & ARG_HASOPTVALUE) == 0 && (table[tabindex]->flag & ARG_HASVALUE) == 0 &&
+                (table[tabindex]->longopts || table[tabindex]->shortopts));
+        cJSON_AddBoolToObject(entry, "remark",
+            (table[tabindex]->flag & ARG_HASOPTVALUE) == 0 && (table[tabindex]->flag & ARG_HASVALUE) == 0 &&
+                (!table[tabindex]->longopts && !table[tabindex]->shortopts));
+        cJSON_AddBoolToObject(entry, "hasvalue", table[tabindex]->flag & ARG_HASVALUE);
+        cJSON_AddNumberToObject(entry, "mincount", table[tabindex]->mincount);
+        cJSON_AddNumberToObject(entry, "maxcount", table[tabindex]->maxcount);
+        cJSON_AddItemToArray(arg_list, entry);
+        tabindex++;
+    }
+    return arg_list;
 }
 }
 
 
-esp_err_t cmd_to_json(const esp_console_cmd_t *cmd){
-	return cmd_to_json_with_cb(cmd, NULL);
-}
+esp_err_t cmd_to_json(const esp_console_cmd_t* cmd) { return cmd_to_json_with_cb(cmd, NULL); }
 
 
-esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn){
-	if(!cmdList){
-		cmdList=cJSON_CreateArray();
-	}
-	if(!values_fn_list){
-		values_fn_list=cJSON_CreateObject();
-	}
+esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t* cmd, parm_values_fn_t parm_values_fn) {
+    if (!cmdList) {
+        cmdList = cJSON_CreateArray();
+    }
+    if (!values_fn_list) {
+        values_fn_list = cJSON_CreateObject();
+    }
 
 
     if (cmd->command == NULL) {
     if (cmd->command == NULL) {
         return ESP_ERR_INVALID_ARG;
         return ESP_ERR_INVALID_ARG;
@@ -157,27 +162,26 @@ esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t par
     if (strchr(cmd->command, ' ') != NULL) {
     if (strchr(cmd->command, ' ') != NULL) {
         return ESP_ERR_INVALID_ARG;
         return ESP_ERR_INVALID_ARG;
     }
     }
-    cJSON * jsoncmd = cJSON_CreateObject();
-    ADD_TO_JSON(jsoncmd,cmd,help);
-    ADD_TO_JSON(jsoncmd,cmd,hint);
-	if(parm_values_fn){
-		char addr[11]={0};
-		snprintf(addr,sizeof(addr),"%lx",(unsigned long)parm_values_fn);
-		cJSON_AddStringToObject(values_fn_list,cmd->command,addr);
-	}
-	cJSON_AddBoolToObject(jsoncmd,"hascb",parm_values_fn!=NULL);
-
-    if(cmd->argtable){
-    	cJSON_AddItemToObject(jsoncmd,"argtable",ParmsToJSON(cmd->argtable));
+    cJSON* jsoncmd = cJSON_CreateObject();
+    ADD_TO_JSON(jsoncmd, cmd, help);
+    ADD_TO_JSON(jsoncmd, cmd, hint);
+    if (parm_values_fn) {
+        char addr[11] = {0};
+        snprintf(addr, sizeof(addr), "%lx", (unsigned long)parm_values_fn);
+        cJSON_AddStringToObject(values_fn_list, cmd->command, addr);
     }
     }
-    if (cmd->hint) {
-    	cJSON_AddStringToObject(jsoncmd, "hint", cmd->hint);
+    cJSON_AddBoolToObject(jsoncmd, "hascb", parm_values_fn != NULL);
+
+    if (cmd->argtable) {
+        cJSON_AddItemToObject(jsoncmd, "argtable", ParmsToJSON(cmd->argtable));
     }
     }
-    else if (cmd->argtable) {
+    if (cmd->hint) {
+        cJSON_AddStringToObject(jsoncmd, "hint", cmd->hint);
+    } else if (cmd->argtable) {
         /* Generate hint based on cmd->argtable */
         /* Generate hint based on cmd->argtable */
-        char *buf = NULL;
+        char* buf = NULL;
         size_t buf_size = 0;
         size_t buf_size = 0;
-        FILE *f = open_memstream(&buf, &buf_size);
+        FILE* f = open_memstream(&buf, &buf_size);
         if (f != NULL) {
         if (f != NULL) {
             arg_print_syntax(f, cmd->argtable, NULL);
             arg_print_syntax(f, cmd->argtable, NULL);
             fflush(f);
             fflush(f);
@@ -187,303 +191,237 @@ esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t par
         FREE_AND_NULL(buf);
         FREE_AND_NULL(buf);
     }
     }
     cJSON_AddStringToObject(jsoncmd, "name", cmd->command);
     cJSON_AddStringToObject(jsoncmd, "name", cmd->command);
-    char * b=cJSON_Print(jsoncmd);
-    if(b){
-    	ESP_LOGD(TAG,"Adding command table %s",b);
-    	free(b);
+    char* b = cJSON_Print(jsoncmd);
+    if (b) {
+        ESP_LOGD(TAG, "Adding command table %s", b);
+        free(b);
     }
     }
     cJSON_AddItemToArray(cmdList, jsoncmd);
     cJSON_AddItemToArray(cmdList, jsoncmd);
     return ESP_OK;
     return ESP_OK;
 }
 }
-int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args){
-    int nerrors = arg_parse(argc, argv, (void **)args);
+int arg_parse_msg(int argc, char** argv, struct arg_hdr** args) {
+    int nerrors = arg_parse(argc, argv, (void**)args);
 
 
     if (nerrors != 0) {
     if (nerrors != 0) {
-    	char *buf = NULL;
-		size_t buf_size = 0;
-		FILE *f = open_memstream(&buf, &buf_size);
-		if (f != NULL) {
-			arg_print_errors(f, getParmsEnd(args), argv[0]);
-			fflush (f);
-			cmd_send_messaging(argv[0],MESSAGING_ERROR,"%s", buf);
-		}
+        char* buf = NULL;
+        size_t buf_size = 0;
+        FILE* f = open_memstream(&buf, &buf_size);
+        if (f != NULL) {
+            arg_print_errors(f, getParmsEnd(args), argv[0]);
+            fflush(f);
+            cmd_send_messaging(argv[0], MESSAGING_ERROR, "%s", buf);
+        }
         fclose(f);
         fclose(f);
         FREE_AND_NULL(buf);
         FREE_AND_NULL(buf);
     }
     }
     return nerrors;
     return nerrors;
 }
 }
-void process_autoexec(){
-	int i=1;
-	char autoexec_name[21]={0};
-	char * autoexec_value=NULL;
-	uint8_t autoexec_flag=0;
-	// TODO: Add support for the commented code
-	void * cmd = run_command; 
-
-	// char * str_flag = config_alloc_get(NVS_TYPE_STR, "autoexec");
-	// 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.");
-	// }
-	// if(str_flag !=NULL ){
-	// 	autoexec_flag=atoi(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= config_alloc_get(NVS_TYPE_STR, autoexec_name);
-	// 			if(autoexec_value!=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 " ) ){
-	// 					ESP_LOGW(TAG,"Ignoring command. ");
-	// 				}
-	// 				else {
-	// 					ESP_LOGI(TAG,"Running command %s = %s", autoexec_name, autoexec_value);
-	// 					run_command(autoexec_value);
-	// 				}
-	// 				ESP_LOGD(TAG,"Freeing memory for command %s name", autoexec_name);
-	// 				free(autoexec_value);
-	// 			}
-	// 			else {
-	// 				ESP_LOGD(TAG,"No matching command found for name %s", autoexec_name);
-	// 				break;
-	// 			}
-	// 		} while(1);
-	// 	}
-	// 	free(str_flag);
-	
-	// }
-	// else
-	// {
-	// 	ESP_LOGD(TAG,"No matching command found for name autoexec.");
-	// }
-	// TODO: Add support for the commented code
-}
-
 static ssize_t stdin_read(int fd, void* data, size_t size) {
 static ssize_t stdin_read(int fd, void* data, size_t size) {
-	size_t bytes = -1;
-	
-	while (1) {
-		QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, portMAX_DELAY);
-	
-		if (activated == uart_queue) {
-			uart_event_t event;
-			
-			xQueueReceive(uart_queue, &event, 0);
-	
-			if (event.type == UART_DATA) {
-				bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < event.size ? size : event.size, 0);
-				// we have to do our own line ending translation here 
-				for (int i = 0; i < bytes; i++) if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';
-				break;
-			}	
-		} else if (xRingbufferCanRead(stdin_redir.handle, activated)) {
-			char *p = xRingbufferReceiveUpTo(stdin_redir.handle, &bytes, 0, size);
-			// we might receive strings, replace null by \n
-			for (int i = 0; i < bytes; i++) if (p[i] == '\0' || p[i] == '\r') p[i] = '\n';						
-			memcpy(data, p, bytes);
-			vRingbufferReturnItem(stdin_redir.handle, p);
-			break;
-		}
-	}	
-	
-	return bytes;
+    size_t bytes = -1;
+    static size_t remain = 0;
+    if (remain > 0) {
+        bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < remain ? size : remain, 0);
+        remain -= bytes;
+		for (int i = 0; i < bytes; i++)
+			if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';		
+		return bytes;
+    }
+    while (1) {
+        QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, portMAX_DELAY);
+
+        if (activated == uart_queue) {
+            uart_event_t event;
+            xQueueReceive(uart_queue, &event, 0);
+            if (event.type == UART_DATA) {
+                bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < event.size ? size : event.size, 0);
+                // we have to do our own line ending translation here
+                for (int i = 0; i < bytes; i++)
+                    if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';
+                if (event.size > bytes) {
+                    remain = event.size - bytes;
+                }
+                break;
+            }
+        } else if (xRingbufferCanRead(stdin_redir.handle, activated)) {
+            char* p = xRingbufferReceiveUpTo(stdin_redir.handle, &bytes, 0, size);
+            // we might receive strings, replace null by \n
+            for (int i = 0; i < bytes; i++)
+                if (p[i] == '\0' || p[i] == '\r') p[i] = '\n';
+            memcpy(data, p, bytes);
+            vRingbufferReturnItem(stdin_redir.handle, p);
+            xRingbufferPrintInfo(stdin_redir.handle);
+            break;
+        }
+    }
+
+    return bytes;
 }
 }
 
 
-static int stdin_dummy(const char * path, int flags, int mode) {	return 0; }
+static int stdin_dummy(const char* path, int flags, int mode) { return 0; }
 
 
 void initialize_console() {
 void initialize_console() {
-	/* Minicom, screen, idf_monitor send CR when ENTER key is pressed (unused if we redirect stdin) */
-	esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
-	/* Move the caret to the beginning of the next line on '\n' */
-	esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
-
-	/* Configure UART. Note that REF_TICK is used so that the baud rate remains
-	 * correct while APB frequency is changing in light sleep mode.
-	 */
-	const uart_config_t uart_config = { .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, 
-			.data_bits = UART_DATA_8_BITS,
-			.parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1,
-	};
-	ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
-
-	/* Install UART driver for interrupt-driven reads and writes */
-	ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 3, &uart_queue, 0));
-	
-	/* Tell VFS to use UART driver */
-	esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
-		
-	/* re-direct stdin to our own driver so we can gather data from various sources */
-	stdin_redir.queue_set = xQueueCreateSet(2);
-	stdin_redir.handle = xRingbufferCreateStatic(sizeof(stdin_redir._buf), RINGBUF_TYPE_BYTEBUF, stdin_redir._buf, &stdin_redir._ringbuf);
-	xRingbufferAddToQueueSetRead(stdin_redir.handle, stdin_redir.queue_set);
-	xQueueAddToSet(uart_queue, stdin_redir.queue_set);
-	
-	esp_vfs_t vfs = { };
-	vfs.flags = ESP_VFS_FLAG_DEFAULT;
-	vfs.open = stdin_dummy;
-	vfs.read = stdin_read;
-
-	ESP_ERROR_CHECK(esp_vfs_register("/dev/redirect", &vfs, NULL));
+    /* Minicom, screen, idf_monitor send CR when ENTER key is pressed (unused if we redirect stdin) */
+    esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
+    /* Move the caret to the beginning of the next line on '\n' */
+    esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
+
+    /* Configure UART. Note that REF_TICK is used so that the baud rate remains
+     * correct while APB frequency is changing in light sleep mode.
+     */
+    const uart_config_t uart_config = {
+        .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
+        .data_bits = UART_DATA_8_BITS,
+        .parity = UART_PARITY_DISABLE,
+        .stop_bits = UART_STOP_BITS_1,
+    };
+    ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
+
+    /* Install UART driver for interrupt-driven reads and writes */
+    ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 3, &uart_queue, 0));
+
+    /* Tell VFS to use UART driver */
+    esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
+
+    /* re-direct stdin to our own driver so we can gather data from various sources */
+    stdin_redir.queue_set = xQueueCreateSet(2);
+    stdin_redir.handle = xRingbufferCreateStatic(sizeof(stdin_redir._buf), RINGBUF_TYPE_BYTEBUF, stdin_redir._buf, &stdin_redir._ringbuf);
+    xRingbufferAddToQueueSetRead(stdin_redir.handle, stdin_redir.queue_set);
+    xQueueAddToSet(uart_queue, stdin_redir.queue_set);
+
+    esp_vfs_t vfs = {};
+    vfs.flags = ESP_VFS_FLAG_DEFAULT;
+    vfs.open = stdin_dummy;
+    vfs.read = stdin_read;
+
+    ESP_ERROR_CHECK(esp_vfs_register("/dev/redirect", &vfs, NULL));
     freopen("/dev/redirect", "r", stdin);
     freopen("/dev/redirect", "r", stdin);
 
 
-	/* Disable buffering on stdin */
-	setvbuf(stdin, NULL, _IONBF, 0);
+    /* Disable buffering on stdin */
+    setvbuf(stdin, NULL, _IONBF, 0);
 
 
-	/* Initialize the console */
-	esp_console_config_t console_config = { .max_cmdline_args = 28,
-			.max_cmdline_length = 600,
+    /* Initialize the console */
+    esp_console_config_t console_config = {
+        .max_cmdline_args = 28,
+        .max_cmdline_length = 600,
 #if CONFIG_LOG_COLORS
 #if CONFIG_LOG_COLORS
-			.hint_color = atoi(LOG_COLOR_CYAN)
+        .hint_color = atoi(LOG_COLOR_CYAN)
 #endif
 #endif
-			};
-	ESP_ERROR_CHECK(esp_console_init(&console_config));
+    };
+    ESP_ERROR_CHECK(esp_console_init(&console_config));
 
 
-	/* Configure linenoise line completion library */
-	/* Enable multiline editing. If not set, long commands will scroll within
-	 * single line.
-	 */
-	linenoiseSetMultiLine(1);
+    /* Configure linenoise line completion library */
+    /* Enable multiline editing. If not set, long commands will scroll within
+     * single line.
+     */
+    linenoiseSetMultiLine(1);
 
 
-	/* Tell linenoise where to get command completions and hints */
-	linenoiseSetCompletionCallback(&esp_console_get_completion);
-	linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
+    /* Tell linenoise where to get command completions and hints */
+    linenoiseSetCompletionCallback(&esp_console_get_completion);
+    linenoiseSetHintsCallback((linenoiseHintsCallback*)&esp_console_get_hint);
 
 
-	/* Set command history size */
-	linenoiseHistorySetMaxLen(100);
+    /* Set command history size */
+    linenoiseHistorySetMaxLen(100);
 
 
-	/* Load command history from filesystem */
-	//linenoiseHistoryLoad(HISTORY_PATH);
 }
 }
 
 
-bool console_push(const char *data, size_t size) {
-	return xRingbufferSend(stdin_redir.handle, data, size, pdMS_TO_TICKS(100)) == pdPASS;
-}	
+bool console_push(const char* data, size_t size) { return xRingbufferSend(stdin_redir.handle, data, size, pdMS_TO_TICKS(100)) == pdPASS; }
 
 
 void console_start() {
 void console_start() {
-	/* we always run console b/c telnet sends commands to stdin */
-	initialize_console();
-
-	/* 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 wifi commands");
-	register_wifi();
-
-	if(is_recovery_running){
-		MEMTRACE_PRINT_DELTA_MESSAGE("Registering recovery commands");
-		register_ota_cmd();
-	}
-	MEMTRACE_PRINT_DELTA_MESSAGE("Registering i2c commands");
-	register_i2ctools();
-	
-	printf("\n");
-	if(is_recovery_running){
-		printf("****************************************************************\n"
-		"RECOVERY APPLICATION\n"
-		"This mode is used to flash Squeezelite into the OTA partition\n"
-		"****\n\n");
-	}
-	printf("Type 'help' to get the list of commands.\n"
-	"Use UP/DOWN arrows to navigate through command history.\n"
-	"Press TAB when typing command name to auto-complete.\n"
-	"\n");
-	if(!is_recovery_running){
-		printf("To automatically execute lines at startup:\n"
-				"\tSet NVS variable autoexec (U8) = 1 to enable, 0 to disable automatic execution.\n"
-				"\tSet NVS variable autoexec[1~9] (string)to a command that should be executed automatically\n");
-	}
-	printf("\n\n");
-
-	/* Figure out if the terminal supports escape sequences */
-	int probe_status = linenoiseProbe();
-	if (probe_status) { /* zero indicates success */
-		printf("\n****************************\n"
-				"Your terminal application does not support escape sequences.\n"
-				"Line editing and history features are disabled.\n"
-				"On Windows, try using Putty instead.\n"
-				"****************************\n");
-		linenoiseSetDumbMode(1);
-#if CONFIG_LOG_COLORS
-		/* Since the terminal doesn't support escape sequences,
-		 * don't use color codes in the prompt.
-		 */
-		if(is_recovery_running){
-			recovery_prompt=  "recovery-squeezelite-esp32>";
-		}
-		prompt = "squeezelite-esp32> ";
-#endif //CONFIG_LOG_COLORS
-	}
-	esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
-	cfg.thread_name= "console";
-	cfg.inherit_cfg = true;
-	cfg.stack_size = 4*1024;
-	if(is_recovery_running){
-		prompt = recovery_prompt;
-	}
-		MEMTRACE_PRINT_DELTA_MESSAGE("Creating console thread with stack size of 4096 bytes");
-	esp_pthread_set_cfg(&cfg);
-	pthread_create(&thread_console, NULL, console_thread, NULL);
-	MEMTRACE_PRINT_DELTA_MESSAGE("Console thread created");
 
 
-}
+    /* Register commands */
+    esp_console_register_help_command();
+    register_system();
+    register_config_cmd();
+    register_wifi();
 
 
-static esp_err_t run_command(char * line){
-	/* Try to run the command */
-	int ret;
-	esp_err_t err = esp_console_run(line, &ret);
-
-	if (err == ESP_ERR_NOT_FOUND) {
-		ESP_LOGE(TAG,"Unrecognized command: %s", line);
-	} else if (err == ESP_ERR_INVALID_ARG) {
-		// command was empty
-	} else if (err != ESP_OK && ret != ESP_OK) {
-		ESP_LOGW(TAG,"Command returned non-zero error code: 0x%x (%s)", ret,
-		esp_err_to_name(err));
-	} else if (err == ESP_OK && ret != ESP_OK) {
-		ESP_LOGW(TAG,"Command returned in error");
-		err = ESP_FAIL;
-	} else if (err != ESP_OK) {
-		ESP_LOGE(TAG,"Internal error: %s", esp_err_to_name(err));
-	}
-	return err;
+    if (is_recovery_running) {
+        register_ota_cmd();
+    }
+    register_i2ctools();
+
+    printf("\n");
+    if (is_recovery_running) {
+        printf("****************************************************************\n"
+               "RECOVERY APPLICATION\n"
+               "This mode is used to flash Squeezelite into the OTA partition\n"
+               "****\n\n");
+    }
+    printf("Type 'help' to get the list of commands.\n"
+           "Use UP/DOWN arrows to navigate through command history.\n"
+           "Press TAB when typing command name to auto-complete.\n"
+           "\n");
+    printf("\n\n");
+
+    /* Figure out if the terminal supports escape sequences */
+    int probe_status = linenoiseProbe();
+    if (probe_status) { /* zero indicates success */
+        printf("\n****************************\n"
+               "Your terminal application does not support escape sequences.\n"
+               "Line editing and history features are disabled.\n"
+               "On Windows, try using Putty instead.\n"
+               "****************************\n");
+        linenoiseSetDumbMode(1);
+#if CONFIG_LOG_COLORS
+        /* Since the terminal doesn't support escape sequences,
+         * don't use color codes in the prompt.
+         */
+        if (is_recovery_running) {
+            recovery_prompt = "recovery-squeezelite-esp32>";
+        }
+        prompt = "squeezelite-esp32> ";
+#endif // CONFIG_LOG_COLORS
+    }
+    esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
+    cfg.thread_name = "console";
+    cfg.inherit_cfg = true;
+    cfg.stack_size = 4 * 1024;
+    if (is_recovery_running) {
+        prompt = recovery_prompt;
+    }
+    esp_pthread_set_cfg(&cfg);
+    pthread_create(&thread_console, NULL, console_thread, NULL);
 }
 }
 
 
-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) {
-		/* Get a line using linenoise.
-		 * The line is returned when ENTER is pressed.
-		 */
-		char* line = linenoise(prompt);
-		if (line == NULL) { /* Ignore empty lines */
-			continue;
-		}
-		/* Add the command to the history */
-		linenoiseHistoryAdd(line);
-
-		/* Save command history to filesystem */
-		linenoiseHistorySave(HISTORY_PATH);
-		printf("\n");
-		run_command(line);
-		/* linenoise allocates line buffer on the heap, so need to free it */
-		linenoiseFree(line);
-		taskYIELD();
-	}
-	return NULL;
+static esp_err_t run_command(char* line) {
+    /* Try to run the command */
+    int ret;
+    esp_err_t err = esp_console_run(line, &ret);
+
+    if (err == ESP_ERR_NOT_FOUND) {
+        ESP_LOGE(TAG, "Unrecognized command: %s", line);
+    } else if (err == ESP_ERR_INVALID_ARG) {
+        // command was empty
+    } else if (err != ESP_OK && ret != ESP_OK) {
+        ESP_LOGW(TAG, "Command returned non-zero error code: 0x%x (%s)", ret, esp_err_to_name(err));
+    } else if (err == ESP_OK && ret != ESP_OK) {
+        ESP_LOGW(TAG, "Command returned in error");
+        err = ESP_FAIL;
+    } else if (err != ESP_OK) {
+        ESP_LOGE(TAG, "Internal error: %s", esp_err_to_name(err));
+    }
+    return err;
 }
 }
 
 
+static void* console_thread() {
+    /* Main loop */
+    while (1) {
+        /* Get a line using linenoise.
+         * The line is returned when ENTER is pressed.
+         */
+        char* line = linenoise(prompt);
+        if (line == NULL) { /* Ignore empty lines */
+            continue;
+        }
+        /* Add the command to the history */
+        linenoiseHistoryAdd(line);
+
+        /* Save command history to filesystem */
+        linenoiseHistorySave(HISTORY_PATH);
+        printf("\n");
+        run_command(line);
+        /* linenoise allocates line buffer on the heap, so need to free it */
+        linenoiseFree(line);
+        taskYIELD();
+    }
+    return NULL;
+}

+ 1 - 0
components/platform_console/platform_console.h

@@ -22,6 +22,7 @@ typedef cJSON * parm_values_fn_t(void);
 esp_err_t cmd_to_json(const esp_console_cmd_t *cmd);
 esp_err_t cmd_to_json(const esp_console_cmd_t *cmd);
 esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn);
 esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn);
 int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args);
 int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args);
+void initialize_console();
 cJSON * get_cmd_list();
 cJSON * get_cmd_list();
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 1 - 1
components/platform_console/test/test_system.c

@@ -11,7 +11,7 @@
 #include "unity.h"
 #include "unity.h"
 #include "platform_console.h"
 #include "platform_console.h"
 #include "platform_esp32.h"
 #include "platform_esp32.h"
-// #include "Configurator.h"
+// #include "Config.h"
 #pragma message("fixme: search for TODO below")
 #pragma message("fixme: search for TODO below")
 #include "string.h"
 #include "string.h"
 struct arg_lit *arglit;
 struct arg_lit *arglit;

+ 1 - 1
components/raop/CMakeLists.txt

@@ -1,7 +1,7 @@
 
 
 idf_component_register(SRC_DIRS .    
 idf_component_register(SRC_DIRS .    
 						INCLUDE_DIRS .   
 						INCLUDE_DIRS .   
-						PRIV_REQUIRES newlib freertos pthread platform_config mdns services codecs tools display wifi-manager
+						PRIV_REQUIRES newlib freertos pthread tools platform_config mdns services codecs  display wifi-manager
 						  
 						  
 )
 )
 set_source_files_properties(raop.c
 set_source_files_properties(raop.c

+ 1 - 1
components/raop/raop_sink.c

@@ -9,7 +9,7 @@
 #include "esp_pthread.h"
 #include "esp_pthread.h"
 #include "esp_system.h"
 #include "esp_system.h"
 #include "freertos/timers.h"
 #include "freertos/timers.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "raop.h"
 #include "raop.h"
 #include "audio_controls.h"
 #include "audio_controls.h"
 #include "display.h"
 #include "display.h"

+ 2 - 1
components/services/CMakeLists.txt

@@ -1,5 +1,6 @@
 idf_component_register(SRC_DIRS .
 idf_component_register(SRC_DIRS .
 						INCLUDE_DIRS .
 						INCLUDE_DIRS .
-						REQUIRES json tools platform_config display wifi-manager esp-tls platform_config
+						REQUIRES json tools platform_config display wifi-manager esp-tls 
 						PRIV_REQUIRES soc esp32
 						PRIV_REQUIRES soc esp32
 )
 )
+

+ 27 - 29
components/services/accessors.c

@@ -5,15 +5,13 @@
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    CONDITIONS OF ANY KIND, either express or implied.
    CONDITIONS OF ANY KIND, either express or implied.
 */
 */
-#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
-
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
 #include <stdio.h>
 #include <stdio.h>
 #include "esp_log.h"
 #include "esp_log.h"
 #include "driver/gpio.h"
 #include "driver/gpio.h"
 #include "driver/i2c.h"
 #include "driver/i2c.h"
 #include "driver/spi_master.h"
 #include "driver/spi_master.h"
-// #include "Configurator.h"
-#pragma message("fixme: look for TODO below")
+#include "Config.h"
 #include "accessors.h"
 #include "accessors.h"
 #include "globdefs.h"
 #include "globdefs.h"
 #include "display.h"
 #include "display.h"
@@ -94,12 +92,20 @@ bool is_dac_config_locked(){
 }
 }
 
 
 
 
-
+const sys_i2c_bus* get_i2c_bus(i2c_port_t port){
+    if(platform->dev.has_i2c && port == platform->dev.i2c.port-sys_i2c_port_PORT0 && platform->dev.i2c.scl>=0){
+        return &platform->dev.i2c;
+    }
+    if(platform->dev.has_dac && platform->dev.dac.has_i2c && platform->dev.dac.i2c.port == port && platform->dev.dac.i2c.scl>=0){
+        return &platform->dev.dac.i2c;
+    }
+    return NULL;
+}
 /****************************************************************************************
 /****************************************************************************************
  * 
  * 
  */
  */
-const i2c_config_t * config_i2c_get(int * i2c_port) {
-	sys_I2CBus * bus = NULL;
+const i2c_config_t * config_i2c_get(sys_i2c_bus * bus) {
+	 
 	static i2c_config_t i2c = {
 	static i2c_config_t i2c = {
 		.mode = I2C_MODE_MASTER,
 		.mode = I2C_MODE_MASTER,
 		.sda_io_num = -1,
 		.sda_io_num = -1,
@@ -109,38 +115,30 @@ const i2c_config_t * config_i2c_get(int * i2c_port) {
 		.master.clk_speed = 0,
 		.master.clk_speed = 0,
 	};
 	};
 
 
-	if(SYS_I2CBUS(bus)){
-		if(bus->has_scl){
-			i2c.scl_io_num = bus->scl.pin;
+	if(bus && bus->scl >=0  && bus->sda >=0 ){
+		if(bus->scl>=0){
+			i2c.scl_io_num = bus->scl;
 		}
 		}
 		else {
 		else {
 			ESP_LOGE(TAG,"%s missing for i2c","SCL");
 			ESP_LOGE(TAG,"%s missing for i2c","SCL");
 		}
 		}
 		
 		
-		if(bus->has_sda){
-			i2c.sda_io_num= bus->sda.pin;
+		if(bus->sda>=0){
+			i2c.sda_io_num= bus->sda;
 		}
 		}
 		else {
 		else {
 			ESP_LOGE(TAG,"%s missing for i2c","SDA");
 			ESP_LOGE(TAG,"%s missing for i2c","SDA");
 		}
 		}
 		if(bus->speed>0){
 		if(bus->speed>0){
 			i2c.master.clk_speed = bus->speed;
 			i2c.master.clk_speed = bus->speed;
-		}
-		if(bus->port != sys_I2CPortEnum_UNSPECIFIED_PORT){
-			i2c_system_port = bus->port - sys_I2CPortEnum_I2CPort0;
-		}
-		// TODO: untangle i2c system port handling
-		if(i2c_port) {
-			*i2c_port=i2c_system_port;
-		}
-		
+		}		
 	}
 	}
 
 
 	return &i2c;
 	return &i2c;
 }
 }
 
 
 
 
-void config_set_gpio(int * pin, sys_GPIO * gpio,bool has_value, const char * name, bool mandatory){
+void config_set_gpio(int * pin, sys_gpio_config * gpio,bool has_value, const char * name, bool mandatory){
 	if(has_value){
 	if(has_value){
 		ESP_LOGD(TAG, "Setting pin %d as %s", gpio->pin, name);
 		ESP_LOGD(TAG, "Setting pin %d as %s", gpio->pin, name);
 		if(pin) *pin= gpio->pin;
 		if(pin) *pin= gpio->pin;
@@ -166,14 +164,14 @@ const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) {
         .quadhd_io_num = -1
         .quadhd_io_num = -1
     };
     };
 	if(platform->has_dev && platform->dev.has_spi){
 	if(platform->has_dev && platform->dev.has_spi){
-		ESP_LOGI(TAG,"SPI Configuration found");
-		ASSIGN_GPIO(spi.mosi_io_num,platform->dev.spi,mosi,true);
-		ASSIGN_GPIO(spi.miso_io_num,platform->dev.spi,miso,false);
-		ASSIGN_GPIO(spi.sclk_io_num,platform->dev.spi,clk,true);
-		ASSIGN_GPIO(spi_system_dc_gpio,platform->dev.spi,dc,true);
+		ESP_LOGD(TAG,"SPI Configuration found");
+		spi.mosi_io_num = platform->dev.spi.mosi;
+		spi.miso_io_num = platform->dev.spi.miso;
+		spi.sclk_io_num = platform->dev.spi.clk;
+		spi_system_dc_gpio = platform->dev.spi.dc;
 		// only VSPI (1) can be used as Flash and PSRAM run at 80MHz
 		// only VSPI (1) can be used as Flash and PSRAM run at 80MHz
-		if(platform->dev.spi.host!=sys_HostEnum_UNSPECIFIED_HOST){
-			spi_system_host = platform->dev.spi.host;
+		if(platform->dev.spi.host!=sys_dev_common_hosts_NONE){
+			spi_system_host = platform->dev.spi.host-sys_dev_common_hosts_Host0;
 		}
 		}
 	}
 	}
 	else {
 	else {

+ 14 - 11
components/services/accessors.h

@@ -13,6 +13,7 @@
 #include "driver/i2s.h"
 #include "driver/i2s.h"
 #include "driver/spi_master.h"
 #include "driver/spi_master.h"
 #include "gpio_exp.h"
 #include "gpio_exp.h"
+#include "I2CBus.pb.h"
 #define ASSIGN_GPIO(pin, root, name, mandatory) config_set_gpio(&pin, &(root).name, (root).has_##name, #name, mandatory)
 #define ASSIGN_GPIO(pin, root, name, mandatory) config_set_gpio(&pin, &(root).name, (root).has_##name, #name, mandatory)
 
 
 #define ASSIGN_COND_VAL_1LVL(name, targetval) \
 #define ASSIGN_COND_VAL_1LVL(name, targetval) \
@@ -31,7 +32,9 @@
 #define SYS_NET(target) ASSIGN_COND_VAL_1LVL(net,target)
 #define SYS_NET(target) ASSIGN_COND_VAL_1LVL(net,target)
 #define SYS_DISPLAY(target) ASSIGN_COND_VAL_2LVL(dev,display,target)
 #define SYS_DISPLAY(target) ASSIGN_COND_VAL_2LVL(dev,display,target)
 #define SYS_DEV_LEDSTRIP(target) ASSIGN_COND_VAL_2LVL(dev,led_strip,target)
 #define SYS_DEV_LEDSTRIP(target) ASSIGN_COND_VAL_2LVL(dev,led_strip,target)
+#define SYS_DEV(target) ASSIGN_COND_VAL_1LVL(dev,target)
 #define SYS_DEV_ROTARY(target) ASSIGN_COND_VAL_2LVL(dev,rotary,target)
 #define SYS_DEV_ROTARY(target) ASSIGN_COND_VAL_2LVL(dev,rotary,target)
+#define SYS_DEV_IR(target) ASSIGN_COND_VAL_2LVL(dev,ir,target)
 #define SYS_DISPLAY_COMMON(target) ASSIGN_COND_VAL_3LVL(dev,display,common,target)
 #define SYS_DISPLAY_COMMON(target) ASSIGN_COND_VAL_3LVL(dev,display,common,target)
 #define SYS_DISPLAY_SPI(target) ASSIGN_COND_VAL_3LVL(dev,display,spi,target)
 #define SYS_DISPLAY_SPI(target) ASSIGN_COND_VAL_3LVL(dev,display,spi,target)
 #define SYS_DISPLAY_I2C(target) ASSIGN_COND_VAL_3LVL(dev,display,i2c,target)
 #define SYS_DISPLAY_I2C(target) ASSIGN_COND_VAL_3LVL(dev,display,i2c,target)
@@ -40,25 +43,25 @@
 #define SYS_ETH_COMMON(target) ASSIGN_COND_VAL_3LVL(dev,eth,common,target)
 #define SYS_ETH_COMMON(target) ASSIGN_COND_VAL_3LVL(dev,eth,common,target)
 
 
 
 
-#define SYS_I2CBUS(target) ASSIGN_COND_VAL_2LVL(dev,i2c,target)
+#define sys_i2c_bus(target) ASSIGN_COND_VAL_2LVL(dev,i2c,target)
 #define SYS_GPIOS_NAME(name,target) ASSIGN_COND_VAL_2LVL(gpios,name,target)
 #define SYS_GPIOS_NAME(name,target) ASSIGN_COND_VAL_2LVL(gpios,name,target)
-#define SYS_SERVICES(target) ASSIGN_COND_VAL_1LVL(services,target)
-#define SYS_SERVICES_SPOTIFY(target) ASSIGN_COND_VAL_2LVL(services,cspot,target)
-#define SYS_SERVICES_METADATA(target) ASSIGN_COND_VAL_2LVL(services,metadata,target)
-#define SYS_SERVICES_AIRPLAY(target) ASSIGN_COND_VAL_2LVL(services,airplay,target)
-#define SYS_SERVICES_SLEEP(target) ASSIGN_COND_VAL_2LVL(services,sleep,target)
-#define SYS_SERVICES_EQUALIZER(target) ASSIGN_COND_VAL_2LVL(services,equalizer,target)
+#define sys_services_config(target) ASSIGN_COND_VAL_1LVL(services,target)
+#define sys_services_config_SPOTIFY(target) ASSIGN_COND_VAL_2LVL(services,cspot,target)
+#define sys_services_config_METADATA(target) ASSIGN_COND_VAL_2LVL(services,metadata,target)
+#define sys_services_config_AIRPLAY(target) ASSIGN_COND_VAL_2LVL(services,airplay,target)
+#define sys_services_config_SLEEP(target) ASSIGN_COND_VAL_2LVL(services,sleep,target)
+#define sys_services_config_EQUALIZER(target) ASSIGN_COND_VAL_2LVL(services,equalizer,target)
 
 
-#define SYS_SERVICES_TELNET(target) ASSIGN_COND_VAL_2LVL(services,telnet,target)
-#define SYS_SERVICES_BTSINK(target) ASSIGN_COND_VAL_2LVL(services,bt_sink,target)
-#define SYS_SERVICES_SQUEEZELITE(target) ASSIGN_COND_VAL_2LVL(services,squeezelite,target)
+#define sys_services_config_TELNET(target) ASSIGN_COND_VAL_2LVL(services,telnet,target)
+#define sys_services_config_BTSINK(target) ASSIGN_COND_VAL_2LVL(services,bt_sink,target)
 
 
 
 
 #define SYS_DEV_BATTERY(target) ASSIGN_COND_VAL_2LVL(dev,battery,target)
 #define SYS_DEV_BATTERY(target) ASSIGN_COND_VAL_2LVL(dev,battery,target)
 
 
-const i2c_config_t * 		config_i2c_get(int * i2c_port);
+const i2c_config_t * 		config_i2c_get(sys_i2c_bus * bus);
 const spi_bus_config_t * 	config_spi_get(spi_host_device_t * spi_host);
 const spi_bus_config_t * 	config_spi_get(spi_host_device_t * spi_host);
 const gpio_exp_config_t *   config_gpio_exp_get(int index);
 const gpio_exp_config_t *   config_gpio_exp_get(int index);
+const sys_i2c_bus*           get_i2c_bus(i2c_port_t port);
 
 
 bool 						is_dac_config_locked();
 bool 						is_dac_config_locked();
 bool 						are_statistics_enabled();
 bool 						are_statistics_enabled();

+ 419 - 487
components/services/audio_controls.c

@@ -1,4 +1,4 @@
-/* 
+/*
  *  audio control callbacks
  *  audio control callbacks
  *
  *
  *  (c) Sebastien 2019
  *  (c) Sebastien 2019
@@ -8,113 +8,93 @@
  *  https://opensource.org/licenses/MIT
  *  https://opensource.org/licenses/MIT
  *
  *
  */
  */
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
+#include "audio_controls.h"
+#include "Config.h"
+#include "accessors.h"
+#include "buttons.h"
+#include "cJSON.h"
+#include "esp_log.h"
+#include "esp_system.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/timers.h"
 #include "freertos/timers.h"
-#include "esp_system.h"
-#include "esp_log.h"
-#include "cJSON.h"
-#include "buttons.h"
-#include "Configurator.h"
-#include "accessors.h"
 #include "services.h"
 #include "services.h"
-#include "audio_controls.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 
-typedef esp_err_t (actrls_config_map_handler) (const cJSON * member, actrls_config_t *cur_config,uint32_t offset);
+typedef esp_err_t(actrls_config_map_handler)(const cJSON* member, actrls_config_t* cur_config, uint32_t offset);
 typedef struct {
 typedef struct {
-	char * member;
-	uint32_t offset;
-	actrls_config_map_handler * handler;
+    char* member;
+    uint32_t offset;
+    actrls_config_map_handler* handler;
 } actrls_config_map_t;
 } actrls_config_map_t;
 
 
-static esp_err_t actrls_process_member(const cJSON * member, actrls_config_t *cur_config);
-static esp_err_t actrls_process_button(const cJSON * button, actrls_config_t *cur_config);
-static esp_err_t actrls_process_int (const cJSON * member, actrls_config_t *cur_config, uint32_t offset);
-static esp_err_t actrls_process_type (const cJSON * member, actrls_config_t *cur_config, uint32_t offset);
-static esp_err_t actrls_process_bool (const cJSON * member, actrls_config_t *cur_config, uint32_t offset);
-static esp_err_t actrls_process_action (const cJSON * member, actrls_config_t *cur_config, uint32_t offset);
-
-static esp_err_t actrls_init_json(const char *profile_name, bool create);
-static void control_rotary_handler(void *client, rotary_event_e event, bool long_press);
-static void rotary_timer( TimerHandle_t xTimer );
-
-static const actrls_config_map_t actrls_config_map[] =
-		{
-			{"gpio", offsetof(actrls_config_t,gpio), actrls_process_int},
-			{"debounce", offsetof(actrls_config_t,debounce), actrls_process_int},
-			{"type", offsetof(actrls_config_t,type), actrls_process_type},
-			{"pull", offsetof(actrls_config_t,pull), actrls_process_bool},
-			{"long_press", offsetof(actrls_config_t,long_press),actrls_process_int},
-			{"shifter_gpio", offsetof(actrls_config_t,shifter_gpio), actrls_process_int},
-			{"normal", offsetof(actrls_config_t,normal), actrls_process_action},
-			{"shifted", offsetof(actrls_config_t,shifted), actrls_process_action},
-			{"longpress", offsetof(actrls_config_t,longpress), actrls_process_action},
-			{"longshifted", offsetof(actrls_config_t,longshifted), actrls_process_action},
-			{"", 0, NULL}
-		};
-
-// BEWARE: the actions below need to stay aligned with the corresponding enum to properly support json parsing
-//   along with the actrls_t controls in LMS_controls, bt_sink and raop_sink
-#define EP(x) [x] = #x  /* ENUM PRINT */
-static const char * actrls_action_s[ ] = { EP(ACTRLS_POWER),EP(ACTRLS_VOLUP),EP(ACTRLS_VOLDOWN),EP(ACTRLS_TOGGLE),EP(ACTRLS_PLAY),
-									EP(ACTRLS_PAUSE),EP(ACTRLS_STOP),EP(ACTRLS_REW),EP(ACTRLS_FWD),EP(ACTRLS_PREV),EP(ACTRLS_NEXT),
-									EP(BCTRLS_UP),EP(BCTRLS_DOWN),EP(BCTRLS_LEFT),EP(BCTRLS_RIGHT), 
-									EP(BCTRLS_PS0),EP(BCTRLS_PS1),EP(BCTRLS_PS2),EP(BCTRLS_PS3),EP(BCTRLS_PS4),EP(BCTRLS_PS5),EP(BCTRLS_PS6),EP(BCTRLS_PS7),EP(BCTRLS_PS8),EP(BCTRLS_PS9),
-									EP(KNOB_LEFT),EP(KNOB_RIGHT),EP(KNOB_PUSH), EP(ACTRLS_SLEEP),
-									""} ;
-									
-static const char * TAG = "audio controls";
-static actrls_config_t *json_config;
-cJSON * control_profiles = NULL;
+static void actrls_parse_action(const char* name);
+static esp_err_t actrls_init_profile(const char* profile_name, bool create);
+
+static esp_err_t actrls_process_press(const sys_btns_press* action, sys_btns_press* cur_config_press);
+static esp_err_t actrls_process_button(const sys_btns_btn* button, actrls_config_t* cur_config);
+static void control_rotary_handler(void* client, rotary_event_e event, bool long_press);
+static void rotary_timer(TimerHandle_t xTimer);
+
+static const char* TAG = "audio controls";
+static actrls_config_t* json_config;
+cJSON* control_profiles = NULL;
 static EXT_RAM_ATTR actrls_t default_controls, current_controls;
 static EXT_RAM_ATTR actrls_t default_controls, current_controls;
 static actrls_hook_t *default_hook, *current_hook;
 static actrls_hook_t *default_hook, *current_hook;
 static bool default_raw_controls, current_raw_controls;
 static bool default_raw_controls, current_raw_controls;
 static actrls_ir_handler_t *default_ir_handler, *current_ir_handler;
 static actrls_ir_handler_t *default_ir_handler, *current_ir_handler;
 
 
 static EXT_RAM_ATTR struct {
 static EXT_RAM_ATTR struct {
-	bool long_state;
-	bool volume_lock;
-	TimerHandle_t timer;
-	bool click_pending;
-	int left_count;
+    bool long_state;
+    bool volume_lock;
+    TimerHandle_t timer;
+    bool click_pending;
+    int left_count;
 } rotary;
 } rotary;
 
 
-static const struct ir_action_map_s{
-		uint32_t code;
-		actrls_action_e action;
-} ir_action_map[] = {	
-	{0x7689b04f, BCTRLS_DOWN}, {0x7689906f, BCTRLS_LEFT}, {0x7689d02f, BCTRLS_RIGHT}, {0x7689e01f, BCTRLS_UP},
-	{0x768900ff, ACTRLS_VOLDOWN}, {0x7689807f, ACTRLS_VOLUP}, 
-	{0x7689c03f, ACTRLS_PREV}, {0x7689a05f, ACTRLS_NEXT},
-	{0x768920df, ACTRLS_PAUSE}, {0x768910ef, ACTRLS_PLAY},
-	{0x00, 0x00},
+static const struct ir_action_map_s {
+    uint32_t code;
+    sys_btns_actions action;
+} ir_action_map[] = {
+    {0x7689b04f, sys_btns_actions_B_DOWN},
+    {0x7689906f, sys_btns_actions_B_LEFT},
+    {0x7689d02f, sys_btns_actions_B_RIGHT},
+    {0x7689e01f, sys_btns_actions_B_UP},
+    {0x768900ff, sys_btns_actions_A_VOLDOWN},
+    {0x7689807f, sys_btns_actions_A_VOLUP},
+    {0x7689c03f, sys_btns_actions_A_PREV},
+    {0x7689a05f, sys_btns_actions_A_NEXT},
+    {0x768920df, sys_btns_actions_A_PAUSE},
+    {0x768910ef, sys_btns_actions_A_PLAY},
+    {0x00, 0x00},
 };
 };
 
 
 /****************************************************************************************
 /****************************************************************************************
  * This function can be called to map IR codes to default actions
  * This function can be called to map IR codes to default actions
  */
  */
 bool actrls_ir_action(uint16_t addr, uint16_t cmd) {
 bool actrls_ir_action(uint16_t addr, uint16_t cmd) {
-	uint32_t code = (addr << 16) | cmd;
-	struct ir_action_map_s const *map = ir_action_map;
-	
-	while (map->code && map->code != code) map++;
-	
-	if (map->code && current_controls[map->action]) {
-		current_controls[map->action](true);
-		return true;
-	} else {
-		return false;	
-	}	
+    uint32_t code = (addr << 16) | cmd;
+    struct ir_action_map_s const* map = ir_action_map;
+
+    while (map->code && map->code != code)
+        map++;
+
+    if (map->code && current_controls[map->action]) {
+        current_controls[map->action](true);
+        return true;
+    } else {
+        return false;
+    }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
 static void ir_handler(uint16_t addr, uint16_t cmd) {
 static void ir_handler(uint16_t addr, uint16_t cmd) {
-	ESP_LOGD(TAG, "recaived IR %04hx:%04hx", addr, cmd);
-	if (current_ir_handler) current_ir_handler(addr, cmd);
+    ESP_LOGD(TAG, "recaived IR %04hx:%04hx", addr, cmd);
+    if (current_ir_handler) current_ir_handler(addr, cmd);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
@@ -122,484 +102,436 @@ static void ir_handler(uint16_t addr, uint16_t cmd) {
  */
  */
 esp_err_t actrls_init(const char* profile_name) {
 esp_err_t actrls_init(const char* profile_name) {
     esp_err_t err = ESP_OK;
     esp_err_t err = ESP_OK;
-    sys_Rotary* dev_config = NULL;
-    if (!SYS_DEV_ROTARY(dev_config) ) {
+    sys_dev_ir* ir = NULL;
+    sys_btns_rotary* dev_config = NULL;
+    if (!SYS_DEV_ROTARY(dev_config)) {
         ESP_LOGD(TAG, "Rotary not configured");
         ESP_LOGD(TAG, "Rotary not configured");
         return ESP_OK;
         return ESP_OK;
     }
     }
 
 
     char* p;
     char* p;
     int A = -1, B = -1, SW = -1, longpress = 0;
     int A = -1, B = -1, SW = -1, longpress = 0;
-    A = dev_config->A.pin;
-    B = dev_config->B.pin;
-    SW = dev_config->SW.pin;
-
-    if (dev_config->has_knobonly && dev_config->knobonly.enable) {
-        p = strchr(p, '=');
-
-        int double_press =
-            dev_config->knobonly.delay_ms > 0 ? dev_config->knobonly.delay_ms : 350;
-        rotary.timer =
-            xTimerCreate("knobTimer", double_press / portTICK_RATE_MS, pdFALSE, NULL, rotary_timer);
-        longpress = 500;
-        ESP_LOGI(TAG, "single knob navigation %d", double_press);
-    } else {
-
-        if (dev_config->volume) rotary.volume_lock = true;
-        if (dev_config->longpress) longpress = 1000;
-    }
+    A = dev_config->A;
+    B = dev_config->B;
+    SW = dev_config->SW;
+    if (A >= 0 && B >= 0) {
+        if (dev_config->has_knobonly && dev_config->knobonly.enable) {
+            p = strchr(p, '=');
+
+            int double_press = dev_config->knobonly.delay_ms > 0 ? dev_config->knobonly.delay_ms : 350;
+            rotary.timer = xTimerCreate("knobTimer", double_press / portTICK_RATE_MS, pdFALSE, NULL, rotary_timer);
+            longpress = 500;
+            ESP_LOGI(TAG, "single knob navigation %d", double_press);
+        } else {
 
 
-    // create rotary (no handling of long press)
-    err = create_rotary(NULL, A, B, SW, longpress, control_rotary_handler) ? ESP_OK : ESP_FAIL;
+            if (dev_config->volume) rotary.volume_lock = true;
+            if (dev_config->longpress) longpress = 1000;
+        }
 
 
-    if (platform->dev.has_ir && platform->dev.ir.gpio.pin >= 0) {
+        // create rotary (no handling of long press)
+        err = create_rotary(NULL, A, B, SW, longpress, control_rotary_handler) ? ESP_OK : ESP_FAIL;
+    } else {
+        ESP_LOGI(TAG, "Rotary control not configured.");
+    }
 
 
-        if (platform->dev.ir.type == sys_InfraredType_IR_NEC) {
-            create_infrared(platform->dev.ir.gpio.pin, ir_handler, IR_RC5);
+    if (SYS_DEV_IR(ir) && ir->gpio >= 0 && ir->type != sys_dev_ir_types_IR_UNKNOWN) {
+        ESP_LOGD(TAG, "Infrared config found on pin %d, protocol: %s", ir->gpio, sys_dev_ir_types_name(ir->type));
+        if (ir->type == sys_dev_ir_types_IR_NEC) {
+            create_infrared(ir->gpio, ir_handler, IR_RC5);
         } else {
         } else {
-            create_infrared(platform->dev.ir.gpio.pin, ir_handler, IR_NEC);
+            create_infrared(ir->gpio, ir_handler, IR_NEC);
         }
         }
     }
     }
     if (!err)
     if (!err)
-        return actrls_init_json(profile_name, true);
+        return actrls_init_profile(profile_name, true);
     else
     else
         return err;
         return err;
     return err;
     return err;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static void control_handler(void *client, button_event_e event, button_press_e press, bool long_press) {
-	actrls_config_t *key = (actrls_config_t*) client;
-	actrls_action_detail_t  action_detail;
-
-	switch(press) {
-	case BUTTON_NORMAL:
-		if (long_press) action_detail = key->longpress[event == BUTTON_PRESSED ? 0 : 1];
-		else action_detail = key->normal[event == BUTTON_PRESSED ? 0 : 1];
-		break;
-	case BUTTON_SHIFTED:
-		if (long_press) action_detail = key->longshifted[event == BUTTON_PRESSED ? 0 : 1];
-		else action_detail = key->shifted[event == BUTTON_PRESSED ? 0 : 1];
-		break;
-	default:
-		action_detail.action = ACTRLS_NONE;
-		break;
-	}
-	
-	ESP_LOGD(TAG, "control gpio:%u press:%u long:%u event:%u action:%u", key->gpio, press, long_press, event, action_detail.action);
-
-	// stop here if control hook served the request
-	if (current_hook && (*current_hook)(key->gpio, action_detail.action, event, press, long_press)) return;
-    
-   	// in raw mode, we just do normal action press *and* release, there is no longpress nor shift
-	if (current_raw_controls && action_detail.action != ACTRLS_SLEEP) {
-        actrls_action_e action = key->normal[0].action != ACTRLS_NONE ? key->normal[0].action : key->normal[1].action;
-		ESP_LOGD(TAG, "calling action %u in raw mode", action);
-		if (action != ACTRLS_NONE && current_controls[action]) current_controls[action](event == BUTTON_PRESSED);
-		return;
-	}
-
-	// otherwise process using configuration
-	if (action_detail.action == ACTRLS_REMAP) {
-		// remap requested
-		ESP_LOGD(TAG, "remapping buttons to profile %s",action_detail.name);
-		cJSON * profile_obj = cJSON_GetObjectItem(control_profiles,action_detail.name);
-		if (profile_obj) {
-			actrls_config_t *cur_config  = (actrls_config_t *) cJSON_GetStringValue(profile_obj);
-			if (cur_config) {
-				ESP_LOGD(TAG,"Remapping all the buttons that are found in the new profile");
-				while (cur_config->gpio != -1) {
-					ESP_LOGD(TAG,"Remapping button with gpio %u", cur_config->gpio);
-					button_remap((void*) cur_config, cur_config->gpio, control_handler, cur_config->long_press, cur_config->shifter_gpio);
-					cur_config++;
-				}
-			} else {
-				ESP_LOGE(TAG,"Profile %s exists, but is empty. Cannot remap buttons",action_detail.name);
-			}
-		} else {
-			ESP_LOGE(TAG,"Invalid profile name %s. Cannot remap buttons",action_detail.name);
-		}	
-	} else if (action_detail.action == ACTRLS_SLEEP) {
-        ESP_LOGI(TAG, "special sleep button pressed");
-        services_sleep_activate(SLEEP_ONKEY);
-    } else if (action_detail.action != ACTRLS_NONE) {
-		ESP_LOGD(TAG, "calling action %u", action_detail.action);
-		if (current_controls[action_detail.action]) (*current_controls[action_detail.action])(event == BUTTON_PRESSED);
-	}	
+static const char* get_action_desc(sys_btns_action* action_detail) {
+    if (action_detail->type != sys_btns_actions_REMAP) {
+        return sys_btns_actions_name(action_detail->type);
+    }
+    return STR_OR_ALT(action_detail->profile_name, "");
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static void control_rotary_handler(void *client, rotary_event_e event, bool long_press) {
-	actrls_action_e action = ACTRLS_NONE;
-	bool pressed = true;
-	
-	// in raw mode, we just pass rotary events
-	if (current_raw_controls) {
-		if (event == ROTARY_LEFT) (*current_controls[KNOB_LEFT])(true);
-		else if (event == ROTARY_RIGHT) (*current_controls[KNOB_RIGHT])(true);
-		else (*current_controls[KNOB_PUSH])(event == ROTARY_PRESSED);
-		return;
-	}
-	
-	switch(event) {
-	case ROTARY_LEFT:
-		if (rotary.timer) {
-			if (rotary.left_count) {
-				action = KNOB_LEFT;
-				// need to add a left button the first time
-				if (rotary.left_count == 1) (*current_controls[KNOB_LEFT])(true);
-			}
-			xTimerStart(rotary.timer, 20 / portTICK_RATE_MS);
-			rotary.left_count++;
-		}
-		else if (rotary.long_state) action = ACTRLS_PREV;
-		else if (rotary.volume_lock) action = ACTRLS_VOLDOWN;
-		else action = KNOB_LEFT;
-		break;
-	case ROTARY_RIGHT:
-		if (rotary.timer) {
-			if (rotary.left_count == 1) {
-				action = ACTRLS_PAUSE;
-				rotary.left_count = 0;
-				xTimerStop(rotary.timer, 0);
-			} else action = KNOB_RIGHT;
-		}	
-		else if (rotary.long_state) action = ACTRLS_NEXT;
-		else if (rotary.volume_lock) action = ACTRLS_VOLUP;
-		else action = KNOB_RIGHT;
-		break;
-	case ROTARY_PRESSED:
-		if (rotary.timer) {
-			if (long_press) action = ACTRLS_PLAY;
-			else if (rotary.click_pending) {
-				action = BCTRLS_LEFT;
-				xTimerStop(rotary.timer, 0);
-			} 
-			else xTimerStart(rotary.timer, 20 / portTICK_RATE_MS);
-			rotary.click_pending = !rotary.click_pending;
-		} 
-		else if (long_press) rotary.long_state = !rotary.long_state;
-		else if (rotary.volume_lock) action = ACTRLS_TOGGLE;
-		else action = KNOB_PUSH;
-		break;
-	default:
-		break;
-	}
-	
-	if (action != ACTRLS_NONE) (*current_controls[action])(pressed);
-}
+static void control_handler(void* client, button_event_e event, button_press_e press, bool long_press) {
+    actrls_config_t* key = (actrls_config_t*)client;
+    sys_btns_action* action_detail;
+    static sys_btns_action actionNone = sys_btns_action_init_default;
+
+    switch (press) {
+    case BUTTON_NORMAL:
+        if (long_press)
+            action_detail = (event == BUTTON_PRESSED ? &key->longpress.pressed : &key->longpress.released);
+        else
+            action_detail = (event == BUTTON_PRESSED ? &key->normal.pressed : &key->normal.released);
+        break;
+    case BUTTON_SHIFTED:
+        if (long_press)
+            action_detail = (event == BUTTON_PRESSED ? &key->longshifted.pressed : &key->longshifted.released);
+        else
+            action_detail = (event == BUTTON_PRESSED ? &key->shifted.pressed : &key->shifted.released);
+        break;
+    default:
+        action_detail = &actionNone;
+        break;
+    }
 
 
-/****************************************************************************************
- * 
- */
-static void rotary_timer( TimerHandle_t xTimer ) {
-	if (rotary.click_pending) {
-		(*current_controls[KNOB_PUSH])(true);
-		rotary.click_pending = false;
-	} else if (rotary.left_count) {
-		if (rotary.left_count == 1) (*current_controls[KNOB_LEFT])(true);
-		rotary.left_count = 0;
-	}
-}
+    ESP_LOGD(TAG, "control gpio:%u press:%u long:%u event:%u action:%s", key->gpio, press, long_press, event, get_action_desc(action_detail));
 
 
-/****************************************************************************************
- * 
- */
-static actrls_action_e actrls_parse_action_json(const char * name){
-	actrls_action_e action = ACTRLS_NONE;
-	
-	if(!strcasecmp("ACTRLS_NONE",name)) return ACTRLS_NONE;
-	for(int i=0;i<ACTRLS_MAX && actrls_action_s[i][0]!='\0' ;i++){
-		if(!strcmp(actrls_action_s[i], name)){
-			return (actrls_action_e) i;
-		}
-	}
-	// Action name wasn't recognized.
-	// Check if this is a profile name that has a match in nvs
-	ESP_LOGD(TAG,"unknown action %s, trying to find matching profile ", name);
-	cJSON * existing = cJSON_GetObjectItem(control_profiles, name);
-
-	if (!existing) {
-		ESP_LOGD(TAG,"Loading new audio control profile with name: %s", name);
-		if (actrls_init_json(name, false) == ESP_OK) {
-			action = ACTRLS_REMAP;
-		}
-	} else {
-		ESP_LOGD(TAG,"Existing profile %s was referenced", name);
-		action = ACTRLS_REMAP;
-	}
-
-	return action;
+    // stop here if control hook served the request
+    if (current_hook && (*current_hook)(key->gpio, action_detail, event, press, long_press)) return;
+
+    // in raw mode, we just do normal action press *and* release, there is no longpress nor shift
+    if (current_raw_controls && action_detail->type != sys_btns_actions_A_SLEEP) {
+        sys_btns_actions action =
+            key->normal.has_pressed && key->normal.pressed.type != sys_btns_actions_A_NONE ? key->normal.pressed.type : key->normal.released.type;
+        ESP_LOGD(TAG, "calling action %s in raw mode", sys_btns_actions_name(action));
+        if (action != sys_btns_actions_A_NONE && current_controls[action]) current_controls[action](event == BUTTON_PRESSED);
+        return;
+    }
+
+    // otherwise process using configuration
+    if (action_detail->type == sys_btns_actions_REMAP) {
+        // remap requested
+        ESP_LOGD(TAG, "remapping buttons to profile %s", action_detail->profile_name);
+        cJSON* profile_obj = cJSON_GetObjectItem(control_profiles, action_detail->profile_name);
+        if (profile_obj) {
+            actrls_config_t* cur_config = (actrls_config_t*)cJSON_GetStringValue(profile_obj);
+            if (cur_config) {
+                ESP_LOGD(TAG, "Remapping all the buttons that are found in the new profile");
+                while (cur_config->gpio != -1) {
+                    ESP_LOGD(TAG, "Remapping button with gpio %u", cur_config->gpio);
+                    button_remap((void*)cur_config, cur_config->gpio, control_handler, cur_config->long_press, cur_config->shifter_gpio);
+                    cur_config++;
+                }
+            } else {
+                ESP_LOGE(TAG, "Profile %s exists, but is empty. Cannot remap buttons", action_detail->profile_name);
+            }
+        } else {
+            ESP_LOGE(TAG, "Invalid profile name %s. Cannot remap buttons", action_detail->profile_name);
+        }
+    } else if (action_detail->type == sys_btns_actions_A_SLEEP) {
+        ESP_LOGI(TAG, "special sleep button pressed");
+        services_sleep_activate(SLEEP_ONKEY);
+    } else if (action_detail->type != sys_btns_actions_A_NONE) {
+        ESP_LOGD(TAG, "calling action %s", sys_btns_actions_name(action_detail->type));
+        if (current_controls[action_detail->type]) (*current_controls[action_detail->type])(event == BUTTON_PRESSED);
+    }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static esp_err_t actrls_process_int (const cJSON * member, actrls_config_t *cur_config,uint32_t offset){
-	esp_err_t err = ESP_OK;
-	ESP_LOGD(TAG,"Processing int member");
-	int *value = (int*)((char*) cur_config + offset);
-	*value = member->valueint;
-	return err;
+static void control_rotary_handler(void* client, rotary_event_e event, bool long_press) {
+    sys_btns_actions action = sys_btns_actions_A_NONE;
+    bool pressed = true;
+
+    // in raw mode, we just pass rotary events
+    if (current_raw_controls) {
+        if (event == ROTARY_LEFT)
+            (*current_controls[sys_btns_actions_KNOB_LEFT])(true);
+        else if (event == ROTARY_RIGHT)
+            (*current_controls[sys_btns_actions_KNOB_RIGHT])(true);
+        else
+            (*current_controls[sys_btns_actions_KNOB_PUSH])(event == ROTARY_PRESSED);
+        return;
+    }
+
+    switch (event) {
+    case ROTARY_LEFT:
+        if (rotary.timer) {
+            if (rotary.left_count) {
+                action = sys_btns_actions_KNOB_LEFT;
+                // need to add a left button the first time
+                if (rotary.left_count == 1) (*current_controls[sys_btns_actions_KNOB_LEFT])(true);
+            }
+            xTimerStart(rotary.timer, 20 / portTICK_RATE_MS);
+            rotary.left_count++;
+        } else if (rotary.long_state)
+            action = sys_btns_actions_A_PREV;
+        else if (rotary.volume_lock)
+            action = sys_btns_actions_A_VOLDOWN;
+        else
+            action = sys_btns_actions_KNOB_LEFT;
+        break;
+    case ROTARY_RIGHT:
+        if (rotary.timer) {
+            if (rotary.left_count == 1) {
+                action = sys_btns_actions_A_PAUSE;
+                rotary.left_count = 0;
+                xTimerStop(rotary.timer, 0);
+            } else
+                action = sys_btns_actions_KNOB_RIGHT;
+        } else if (rotary.long_state)
+            action = sys_btns_actions_A_NEXT;
+        else if (rotary.volume_lock)
+            action = sys_btns_actions_A_VOLUP;
+        else
+            action = sys_btns_actions_KNOB_RIGHT;
+        break;
+    case ROTARY_PRESSED:
+        if (rotary.timer) {
+            if (long_press)
+                action = sys_btns_actions_A_PLAY;
+            else if (rotary.click_pending) {
+                action = sys_btns_actions_B_LEFT;
+                xTimerStop(rotary.timer, 0);
+            } else
+                xTimerStart(rotary.timer, 20 / portTICK_RATE_MS);
+            rotary.click_pending = !rotary.click_pending;
+        } else if (long_press)
+            rotary.long_state = !rotary.long_state;
+        else if (rotary.volume_lock)
+            action = sys_btns_actions_A_TOGGLE;
+        else
+            action = sys_btns_actions_KNOB_PUSH;
+        break;
+    default:
+        break;
+    }
+
+    if (action != sys_btns_actions_A_NONE) (*current_controls[action])(pressed);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static esp_err_t actrls_process_type (const cJSON * member, actrls_config_t *cur_config, uint32_t offset){
-	esp_err_t err = ESP_OK;
-	ESP_LOGD(TAG,"Processing type member");
-	int *value = (int *)((char*) cur_config + offset);
-	if (member->type == cJSON_String) {
-		*value =
-				!strcmp(member->valuestring,
-						"BUTTON_LOW") ?
-						BUTTON_LOW : BUTTON_HIGH;
-	} else {
-		ESP_LOGE(TAG,
-				"Button type value expected string value of BUTTON_LOW or BUTTON_HIGH, none found");
-		err=ESP_FAIL;
-	}
-	return err;
+static void rotary_timer(TimerHandle_t xTimer) {
+    if (rotary.click_pending) {
+        (*current_controls[sys_btns_actions_KNOB_PUSH])(true);
+        rotary.click_pending = false;
+    } else if (rotary.left_count) {
+        if (rotary.left_count == 1) (*current_controls[sys_btns_actions_KNOB_LEFT])(true);
+        rotary.left_count = 0;
+    }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static esp_err_t actrls_process_bool (const cJSON * member, actrls_config_t *cur_config, uint32_t offset){
-	esp_err_t err = ESP_OK;
-	if (!member) {
-		ESP_LOGE(TAG,"Null json member pointer!");
-		err = ESP_FAIL;
-	} else {
-		ESP_LOGD(TAG,"Processing bool member ");
-		if (cJSON_IsBool(member)) {
-			bool*value = (bool*)((char*) cur_config + offset);
-			*value = cJSON_IsTrue(member);
-		} else {
-			ESP_LOGE(TAG,"Member %s is not a boolean", member->string?member->string:"unknown");
-			err = ESP_FAIL;
-		}
-	}
-
-	return err;
+static void actrls_parse_action(const char* name) {
+    // Check if there is a profile name that has a match
+    ESP_LOGD(TAG, "unknown action %s, trying to find matching profile ", name);
+    cJSON* existing = cJSON_GetObjectItem(control_profiles, name);
+
+    if (!existing) {
+        ESP_LOGD(TAG, "Loading new audio control profile with name: %s", name);
+        actrls_init_profile(name, false);
+    } else {
+        ESP_LOGD(TAG, "Existing profile %s was referenced", name);
+    }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static esp_err_t actrls_process_action (const cJSON * member, actrls_config_t *cur_config, uint32_t offset){
-	esp_err_t err = ESP_OK;
-	cJSON * button_action= cJSON_GetObjectItemCaseSensitive(member, "pressed");
-	actrls_action_detail_t*value = (actrls_action_detail_t*)((char *)cur_config + offset);
-	if (button_action != NULL) {
-		value[0].action = actrls_parse_action_json( button_action->valuestring);
-		if(value[0].action == ACTRLS_REMAP){
-			value[0].name = strdup(button_action->valuestring);
-		}
-	} 
-	button_action = cJSON_GetObjectItemCaseSensitive(member, "released");
-	if (button_action != NULL) {
-		value[1].action = actrls_parse_action_json( button_action->valuestring);
-		if (value[1].action == ACTRLS_REMAP){
-			value[1].name = strdup(button_action->valuestring);
-		}
-	}
-
-	return err;
-}
+static esp_err_t actrls_process_action(const sys_btns_action* action, sys_btns_action* cur_config_act) {
+    bool valid_name = action->profile_name && strlen(action->profile_name) > 0;
+    cur_config_act->type = action->type;
+    if (valid_name) {
+        cur_config_act->profile_name = strdup_psram(action->profile_name);
+        if (!cur_config_act->profile_name) {
+            ESP_LOGE(TAG, "Error allocating memory for action profile name %s", action->profile_name);
+            return ESP_ERR_NO_MEM;
+        }
+    }
 
 
+    if (action->type == sys_btns_actions_REMAP) {
+        if (!valid_name) {
+            ESP_LOGE(TAG, "Missing name for action %s", sys_btns_actions_name(action->type));
+            return ESP_ERR_INVALID_ARG;
+        }
+        actrls_parse_action(action->profile_name);
+    }
+    return ESP_OK;
+}
+static esp_err_t actrls_process_press(const sys_btns_press* press, sys_btns_press* cur_config_press) {
+    esp_err_t err = ESP_OK;
+    if (press->has_pressed) {
+        cur_config_press->has_pressed = true;
+        err = actrls_process_action(&press->pressed, &cur_config_press->pressed);
+    }
+    if (err == ESP_OK && press->has_released) {
+        cur_config_press->has_released = true;
+        err = actrls_process_action(&press->released, &cur_config_press->released);
+    }
+    return err;
+}
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static esp_err_t actrls_process_member(const cJSON * member, actrls_config_t *cur_config) {
-	esp_err_t err = ESP_OK;
-	const actrls_config_map_t * h=actrls_config_map;
-
-	char * str = cJSON_Print(member);
-	while (h->handler && strcmp(member->string, h->member)) { h++; }
-
-	if (h->handler) {
-		ESP_LOGD(TAG,"found handler for member %s, json value %s", h->member,str?str:"");
-		err = h->handler(member, cur_config, h->offset);
-	} else {
-		err = ESP_FAIL;
-		ESP_LOGE(TAG, "Unknown json structure member : %s", str?str:"");
-	}
-
-	if (str) free(str);
-	return err;
+static esp_err_t actrls_process_button(const sys_btns_btn* button, actrls_config_t* cur_config) {
+    esp_err_t err = ESP_OK;
+    if (button->has_gpio && button->gpio.pin >= 0) {
+        cur_config->type = button->gpio.level == sys_gpio_lvl_LOW ? BUTTON_LOW : BUTTON_HIGH;
+        cur_config->gpio = button->gpio.pin;
+    }
+    cur_config->pull = button->pull;
+    cur_config->debounce = button->debounce;
+    cur_config->long_press = button->longduration;
+    err = actrls_process_press(&button->normal, &cur_config->normal);
+    if (err == ESP_OK) err = actrls_process_press(&button->longpress, &cur_config->longpress);
+    if (err == ESP_OK) err = actrls_process_press(&button->shifted, &cur_config->shifted);
+    if (err == ESP_OK) err = actrls_process_press(&button->longshifted, &cur_config->longshifted);
+    cur_config->shifter_gpio = button->shifter;
+    return err;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static esp_err_t actrls_process_button(const cJSON * button, actrls_config_t *cur_config) {
-	esp_err_t err= ESP_OK;
-	const cJSON *member;
-
-	cJSON_ArrayForEach(member, button)
-	{
-		ESP_LOGD(TAG,"Processing member %s. ", member->string);
-		esp_err_t loc_err = actrls_process_member(member, cur_config);
-		err = (err == ESP_OK) ? loc_err : err;
-	}
-	return err;
+static actrls_config_t* actrls_init_alloc_structure(const sys_btns_profile* buttons, const char* name) {
+    actrls_config_t* json_config = NULL;
 
 
+    // Check if the main profiles array was created
+    if (!control_profiles) {
+        control_profiles = cJSON_CreateObject();
+    }
+    ESP_LOGD(TAG, "config contains %u button definitions", buttons->buttons_count);
+    if (buttons->buttons_count != 0) {
+        json_config = calloc(sizeof(actrls_config_t) * (buttons->buttons_count + 1), 1);
+        if (json_config) {
+            json_config[buttons->buttons_count].gpio = -1;
+        } else {
+            ESP_LOGE(TAG, "Unable to allocate memory to hold configuration for %u buttons ", buttons->buttons_count);
+        }
+    } else {
+        ESP_LOGE(TAG, "No button found in configuration structure");
+    }
+
+    // we're cheating here; we're going to store the control profile
+    // pointer as a string reference;  this will prevent cJSON
+    // from trying to free the structure if we ever want to free the object
+    cJSON* new_profile = cJSON_CreateStringReference((const char*)json_config);
+    cJSON_AddItemToObject(control_profiles, name, new_profile);
+
+    return json_config;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static actrls_config_t * actrls_init_alloc_structure(const cJSON *buttons, const char * name){
-	int member_count = 0;
-	const cJSON *button;
-	actrls_config_t * json_config=NULL;
-
-	// Check if the main profiles array was created
-	if(!control_profiles){
-		control_profiles = cJSON_CreateObject();
-	}
-
-	ESP_LOGD(TAG,"Counting the number of buttons definition");
-	cJSON_ArrayForEach(button, buttons)	{
-		member_count++;
-	}
-
-	ESP_LOGD(TAG, "config contains %u button definitions",	member_count);
-	if (member_count != 0) {
-		json_config = calloc(sizeof(actrls_config_t) * (member_count + 1), 1);
-		if (json_config){
-			json_config[member_count].gpio = -1;
-		} else {	
-			ESP_LOGE(TAG,"Unable to allocate memory to hold configuration for %u buttons ",member_count);
-		}
-	} else {
-		ESP_LOGE(TAG,"No button found in configuration structure");
-	}
-
-	// we're cheating here; we're going to store the control profile
-	// pointer as a string reference;  this will prevent cJSON
-	// from trying to free the structure if we ever want to free the object
-	cJSON * new_profile = cJSON_CreateStringReference((const char *)json_config);
-	cJSON_AddItemToObject(control_profiles, name, new_profile);
-
-	return json_config;
+static void actrls_defaults(actrls_config_t* config) {
+    sys_btns_press PressDefault = sys_btns_press_init_default;
+    config->type = BUTTON_LOW;
+    config->pull = false;
+    config->debounce = 0;
+    config->long_press = 0;
+    config->shifter_gpio = -1;
+    config->normal = PressDefault;
+    config->longpress = PressDefault;
+    config->shifted = PressDefault;
+    config->longshifted = PressDefault;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static void actrls_defaults(actrls_config_t *config) {
-	config->type = BUTTON_LOW;
-	config->pull = false;
-	config->debounce = 0;
-	config->long_press = 0;
-	config->shifter_gpio = -1;
-	config->normal[0].action = config->normal[1].action = ACTRLS_NONE;
-	config->longpress[0].action = config->longpress[1].action = ACTRLS_NONE;
-	config->shifted[0].action = config->shifted[1].action = ACTRLS_NONE;
-	config->longshifted[0].action = config->longshifted[1].action = ACTRLS_NONE;
+static sys_btns_profile* get_profile(const char* profile_name) {
+    ESP_LOGD(TAG, "Looking for profile name %s in %d profile(s)", profile_name, platform->dev.buttons_profiles_count);
+    for (int i = 0; i < platform->dev.buttons_profiles_count; i++) {
+        if (strcasecmp(platform->dev.buttons_profiles[i].profile_name, profile_name) == 0) {
+            ESP_LOGD(TAG, "Found profile name %s", platform->dev.buttons_profiles[i].profile_name);
+            return &platform->dev.buttons_profiles[i];
+        } else {
+            ESP_LOGD(TAG, "Profile name %s doesn't match %s", platform->dev.buttons_profiles[i].profile_name, profile_name);
+        }
+    }
+    ESP_LOGW(TAG, "Button control profile %s not found", profile_name);
+    return NULL;
 }
 }
 
 
-
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-static esp_err_t actrls_init_json(const char *profile_name, bool create) {
-	esp_err_t err = ESP_OK;
-	#pragma message("Add support to button profile names")
-// 	actrls_config_t *cur_config = NULL;
-// 	actrls_config_t *config_root = NULL;
-// 	char *config=NULL;
-// 	const cJSON *button;
-	
-// 	if (!profile_name) return ESP_OK;
-// 	//if ((config = config_alloc_get_str(profile_name, NULL, CONFIG_AUDIO_CONTROLS)) == NULL) return ESP_FAIL;
-// 	// TODO: Add support for the commented code
-// 	if (!*config) goto exit;
-		
-// 	ESP_LOGD(TAG,"Parsing JSON structure %s", config);
-// 	cJSON *buttons = cJSON_Parse(config);
-// 	if (buttons == NULL) {
-// 		ESP_LOGE(TAG,"JSON Parsing failed for %s", config);
-// 		err = ESP_FAIL;
-// 	} else {
-// 		ESP_LOGD(TAG,"Json parsing completed");
-// 		if (cJSON_IsArray(buttons)) {
-// 			ESP_LOGD(TAG,"configuration is an array as expected");
-// 			cur_config =config_root= actrls_init_alloc_structure(buttons, profile_name);
-// 			if(!cur_config) {
-// 				ESP_LOGE(TAG,"Config buffer was empty. ");
-// 				cJSON_Delete(buttons);
-// 				err = ESP_FAIL;
-// 				goto exit;
-// 			}
-// 			ESP_LOGD(TAG,"Processing button definitions. ");
-// 			cJSON_ArrayForEach(button, buttons){
-// 				char * str = cJSON_Print(button);
-// 				ESP_LOGD(TAG,"Processing %s. ", str?str:"");
-// 				if(str){
-// 					free(str);
-// 				}
-// 				actrls_defaults(cur_config);
-// 				esp_err_t loc_err = actrls_process_button(button, cur_config);
-// 				err = (err == ESP_OK) ? loc_err : err;
-// 				if (loc_err == ESP_OK) {
-// 					if (create) button_create((void*) cur_config, cur_config->gpio,cur_config->type, 
-// 												cur_config->pull,cur_config->debounce, control_handler, 
-// 												cur_config->long_press, cur_config->shifter_gpio);
-// 				} else {
-// 					ESP_LOGE(TAG,"Error parsing button structure.  Button will not be registered.");
-// 				}
-
-// 				cur_config++;
-// 			}
-// 		} else {
-// 			ESP_LOGE(TAG,"Invalid configuration; array is expected and none received in %s ", config);
-// 		}
-// 		cJSON_Delete(buttons);
-// 	}
-// 	// Now update the global json_config object.  If we are recursively processing menu structures,
-// 	// the last init that completes will assigh the first json config object found, which will match
-// 	// the default config from nvs.
-// 	json_config = config_root;
-// exit:	
-// 	free(config);
-	return err;
-}
+static esp_err_t actrls_init_profile(const char* profile_name, bool create) {
+    esp_err_t err = ESP_OK;
+    actrls_config_t* cur_config = NULL;
+    actrls_config_t* config_root = NULL;
+    sys_btns_profile* config;
 
 
+    if (!profile_name || strlen(profile_name) == 0) {
+        ESP_LOGI(TAG, "No control button configured");
+        return ESP_OK;
+    }
+    ESP_LOGI(TAG, "Initializing button control profile %s", profile_name);
+    config = get_profile(profile_name);
+    if (!config) {
+        ESP_LOGE(TAG, "Invalid button control profile %s", profile_name);
+        goto exit;
+    }
+
+    if (config->buttons_count == 0) {
+        ESP_LOGE(TAG, "No button found %s", profile_name);
+        err = ESP_FAIL;
+    } else {
+        ESP_LOGD(TAG, "Number of buttons: %d", config->buttons_count);
+        cur_config = config_root = actrls_init_alloc_structure(config, profile_name);
+        if (!cur_config) {
+            ESP_LOGE(TAG, "Config buffer was empty. ");
+            err = ESP_FAIL;
+            goto exit;
+        }
+        ESP_LOGD(TAG, "Processing button definitions. ");
+        for (int i = 0; i < config->buttons_count; i++) {
+            ESP_LOGD(TAG, "Processing button %d of %d for profile %s. ", i + 1, config->buttons_count, profile_name);
+            actrls_defaults(cur_config);
+            esp_err_t loc_err = actrls_process_button(&config->buttons[i], cur_config);
+            err = (err == ESP_OK) ? loc_err : err;
+            if (loc_err == ESP_OK) {
+                if (create)
+                    button_create((void*)cur_config, cur_config->gpio, cur_config->type, cur_config->pull, cur_config->debounce, control_handler,
+                        cur_config->long_press, cur_config->shifter_gpio);
+            } else {
+                ESP_LOGE(TAG, "Error parsing button structure.  Button will not be registered.");
+            }
+
+            cur_config++;
+        }
+    }
+    // Now update the global json_config object.  If we are recursively processing menu structures,
+    // the last init that completes will assigh the first json config object found, which will match
+    // the default config from nvs.
+    json_config = config_root;
+exit:
+    return err;
+}
 /****************************************************************************************
 /****************************************************************************************
  *
  *
  */
  */
-void actrls_set_default(const actrls_t controls, bool raw_controls, actrls_hook_t *hook, actrls_ir_handler_t *ir_handler) {
-	memcpy(default_controls, controls, sizeof(actrls_t));
-	memcpy(current_controls, default_controls, sizeof(actrls_t));
-	default_hook = current_hook = hook;
-	default_raw_controls = current_raw_controls = raw_controls;
-	default_ir_handler = current_ir_handler = ir_handler;
+void actrls_set_default(const actrls_t controls, bool raw_controls, actrls_hook_t* hook, actrls_ir_handler_t* ir_handler) {
+    memcpy(default_controls, controls, sizeof(actrls_t));
+    memcpy(current_controls, default_controls, sizeof(actrls_t));
+    default_hook = current_hook = hook;
+    default_raw_controls = current_raw_controls = raw_controls;
+    default_ir_handler = current_ir_handler = ir_handler;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-void actrls_set(const actrls_t controls, bool raw_controls, actrls_hook_t *hook, actrls_ir_handler_t *ir_handler) {
-	memcpy(current_controls, controls, sizeof(actrls_t));
-	current_hook = hook;
-	current_raw_controls = raw_controls;
-	current_ir_handler = ir_handler;
+void actrls_set(const actrls_t controls, bool raw_controls, actrls_hook_t* hook, actrls_ir_handler_t* ir_handler) {
+    memcpy(current_controls, controls, sizeof(actrls_t));
+    current_hook = hook;
+    current_raw_controls = raw_controls;
+    current_ir_handler = ir_handler;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
 void actrls_unset(void) {
 void actrls_unset(void) {
-	memcpy(current_controls, default_controls, sizeof(actrls_t));
-	current_hook = default_hook;
-	current_raw_controls = default_raw_controls;
-	current_ir_handler = default_ir_handler;
+    memcpy(current_controls, default_controls, sizeof(actrls_t));
+    current_hook = default_hook;
+    current_raw_controls = default_raw_controls;
+    current_ir_handler = default_ir_handler;
 }
 }

+ 16 - 15
components/services/audio_controls.h

@@ -9,27 +9,28 @@
 #pragma once
 #pragma once
 
 
 #include "buttons.h"
 #include "buttons.h"
+#include "Buttons.pb.h"
 
 
 // BEWARE: this is the index of the array of action below (change actrls_action_s as well!)
 // BEWARE: this is the index of the array of action below (change actrls_action_s as well!)
-typedef enum { 	ACTRLS_NONE = -1, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY, 
-				ACTRLS_PAUSE, ACTRLS_STOP, ACTRLS_REW, ACTRLS_FWD, ACTRLS_PREV, ACTRLS_NEXT, 
-				BCTRLS_UP, BCTRLS_DOWN, BCTRLS_LEFT, BCTRLS_RIGHT, 
-				BCTRLS_PS0,BCTRLS_PS1,BCTRLS_PS2,BCTRLS_PS3,BCTRLS_PS4,BCTRLS_PS5,BCTRLS_PS6,BCTRLS_PS7,BCTRLS_PS8,BCTRLS_PS9,
-				KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH,
-                ACTRLS_SLEEP,
-				ACTRLS_REMAP, ACTRLS_MAX 
-		} actrls_action_e;
+// typedef enum { 	ACTRLS_NONE = -1, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY, 
+// 				ACTRLS_PAUSE, ACTRLS_STOP, ACTRLS_REW, ACTRLS_FWD, ACTRLS_PREV, ACTRLS_NEXT, 
+// 				BCTRLS_UP, BCTRLS_DOWN, BCTRLS_LEFT, BCTRLS_RIGHT, 
+// 				BCTRLS_PS0,BCTRLS_PS1,BCTRLS_PS2,BCTRLS_PS3,BCTRLS_PS4,BCTRLS_PS5,BCTRLS_PS6,BCTRLS_PS7,BCTRLS_PS8,BCTRLS_PS9,
+// 				KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH,
+//                 ACTRLS_SLEEP,
+// 				ACTRLS_REMAP, ACTRLS_MAX 
+// 		} actrls_action_e;
 
 
 typedef void (*actrls_handler)(bool pressed);
 typedef void (*actrls_handler)(bool pressed);
-typedef actrls_handler actrls_t[ACTRLS_MAX - ACTRLS_NONE - 1];
-typedef bool actrls_hook_t(int gpio, actrls_action_e action, button_event_e event, button_press_e press, bool long_press);
+typedef actrls_handler actrls_t[sys_btns_actions_MAX- sys_btns_actions_A_NONE - 1];
+typedef bool actrls_hook_t(int gpio, sys_btns_action *action, button_event_e event, button_press_e press, bool long_press);
 typedef bool actrls_ir_handler_t(uint16_t addr, uint16_t cmd);
 typedef bool actrls_ir_handler_t(uint16_t addr, uint16_t cmd);
 
 
 // BEWARE any change to struct below must be mapped to actrls_config_map
 // BEWARE any change to struct below must be mapped to actrls_config_map
-typedef struct {
-	actrls_action_e action;
-	const char * name;
-} actrls_action_detail_t;
+// typedef struct {
+// 	actrls_action_e action;
+// 	const char * name;
+// } actrls_action_detail_t;
 typedef struct actrl_config_s {
 typedef struct actrl_config_s {
 	int gpio;
 	int gpio;
 	int type;
 	int type;
@@ -37,7 +38,7 @@ typedef struct actrl_config_s {
 	int	debounce;
 	int	debounce;
 	int long_press;
 	int long_press;
 	int shifter_gpio;
 	int shifter_gpio;
-	actrls_action_detail_t normal[2], longpress[2], shifted[2], longshifted[2];	// [0] keypressed, [1] keyreleased
+	sys_btns_press normal, longpress, shifted, longshifted;	// [0] keypressed, [1] keyreleased
 } actrls_config_t;
 } actrls_config_t;
 
 
 esp_err_t actrls_init(const char *profile_name);
 esp_err_t actrls_init(const char *profile_name);

+ 4 - 4
components/services/battery.c

@@ -16,7 +16,7 @@
 #include "esp_log.h"
 #include "esp_log.h"
 #include "driver/adc.h"
 #include "driver/adc.h"
 #include "battery.h"
 #include "battery.h"
-#include "Configurator.h"
+#include "Config.h"
 
 
 /* 
 /* 
  There is a bug in esp32 which causes a spurious interrupt on gpio 36/39 when
  There is a bug in esp32 which causes a spurious interrupt on gpio 36/39 when
@@ -32,11 +32,11 @@ static const char *TAG = "battery";
 static struct {
 static struct {
 	float sum, avg, scale;
 	float sum, avg, scale;
 	int count;
 	int count;
-	sys_Battery * battery_config;
+	sys_battery_config * battery_config;
 	TimerHandle_t timer;
 	TimerHandle_t timer;
 } battery;
 } battery;
-#define BATTERY_CHANNEL(b) (b.battery_config?b.battery_config->channel - sys_BatteryChannelEnum_CH0:-1)
-#define ATTENUATION(b) (b.battery_config?b.battery_config->atten - sys_BatteryAttenEnum_ATT_0:-1)
+#define BATTERY_CHANNEL(b) (b.battery_config?b.battery_config->channel - sys_battery_channels_CH0:-1)
+#define ATTENUATION(b) (b.battery_config?b.battery_config->atten - sys_battery_atten_ATT_0:-1)
 void (*battery_handler_svc)(float value, int cells);
 void (*battery_handler_svc)(float value, int cells);
 
 
 /****************************************************************************************
 /****************************************************************************************

+ 348 - 338
components/services/buttons.c

@@ -1,4 +1,4 @@
-/* 
+/*
  *  a crude button press/long-press/shift management based on GPIO
  *  a crude button press/long-press/shift management based on GPIO
  *
  *
  *  (c) Philippe G. 2019, philippe_44@outlook.com
  *  (c) Philippe G. 2019, philippe_44@outlook.com
@@ -7,448 +7,458 @@
  *  https://opensource.org/licenses/MIT
  *  https://opensource.org/licenses/MIT
  *
  *
  */
  */
- 
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
+#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
+#include "buttons.h"
+#include "driver/gpio.h"
+#include "driver/rmt.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "esp_task.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
 #include "freertos/task.h"
 #include "freertos/task.h"
 #include "freertos/timers.h"
 #include "freertos/timers.h"
-#include "freertos/queue.h"
-#include "esp_system.h"
-#include "esp_log.h"
-#include "esp_task.h"
-#include "driver/gpio.h"
-#include "driver/rmt.h"
+#include "globdefs.h"
 #include "gpio_exp.h"
 #include "gpio_exp.h"
-#include "buttons.h"
-#include "services.h"
 #include "rotary_encoder.h"
 #include "rotary_encoder.h"
-#include "globdefs.h"
+#include "services.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 
-static const char * TAG = "buttons";
+static const char* TAG = "buttons";
 
 
 static EXT_RAM_ATTR int n_buttons;
 static EXT_RAM_ATTR int n_buttons;
 static EXT_RAM_ATTR uint32_t buttons_idle_since;
 static EXT_RAM_ATTR uint32_t buttons_idle_since;
 
 
-#define BUTTON_STACK_SIZE	4096
-#define MAX_BUTTONS			32
-#define DEBOUNCE			50
-#define BUTTON_QUEUE_LEN	10
+#define BUTTON_STACK_SIZE 4096
+#define MAX_BUTTONS 32
+#define DEBOUNCE 50
+#define BUTTON_QUEUE_LEN 10
 
 
 static EXT_RAM_ATTR struct button_s {
 static EXT_RAM_ATTR struct button_s {
-	void *client;
-	int gpio;
-	int debounce;
-	button_handler handler;
-	struct button_s *self, *shifter;
-	int shifter_gpio;	// this one is just for post-creation						
-	int	long_press;
-	bool long_timer, shifted, shifting;
-	int type, level;	
-	TimerHandle_t timer;
+    void* client;
+    int gpio;
+    int debounce;
+    button_handler handler;
+    struct button_s *self, *shifter;
+    int shifter_gpio; // this one is just for post-creation
+    int long_press;
+    bool long_timer, shifted, shifting;
+    int type, level;
+    TimerHandle_t timer;
 } buttons[MAX_BUTTONS];
 } buttons[MAX_BUTTONS];
 
 
 // can't use EXT_RAM_ATTR for initialized structure
 // can't use EXT_RAM_ATTR for initialized structure
 static struct {
 static struct {
-	int gpio, level;
-	struct button_s *button;
-} polled_gpio[] = { {36, -1, NULL}, {39, -1, NULL}, {-1, -1, NULL} };
+    int gpio, level;
+    struct button_s* button;
+} polled_gpio[] = {{36, -1, NULL}, {39, -1, NULL}, {-1, -1, NULL}};
 
 
 static TimerHandle_t polled_timer;
 static TimerHandle_t polled_timer;
 
 
 static EXT_RAM_ATTR struct {
 static EXT_RAM_ATTR struct {
-	QueueHandle_t queue;
-	void *client;
-	rotary_encoder_info_t info;
-	int A, B, SW;
-	rotary_handler handler;
+    QueueHandle_t queue;
+    void* client;
+    rotary_encoder_info_t info;
+    int A, B, SW;
+    rotary_handler handler;
 } rotary;
 } rotary;
 
 
 static EXT_RAM_ATTR struct {
 static EXT_RAM_ATTR struct {
-	RingbufHandle_t rb;
-	infrared_handler handler;
+    RingbufHandle_t rb;
+    infrared_handler handler;
 } infrared;
 } infrared;
 
 
 static EXT_RAM_ATTR QueueHandle_t button_queue;
 static EXT_RAM_ATTR QueueHandle_t button_queue;
 static EXT_RAM_ATTR QueueSetHandle_t common_queue_set;
 static EXT_RAM_ATTR QueueSetHandle_t common_queue_set;
 
 
 static void buttons_task(void* arg);
 static void buttons_task(void* arg);
-static void buttons_handler(struct button_s *button, int level);
+static void buttons_handler(struct button_s* button, int level);
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Start task needed by button,s rotaty and infrared
  * Start task needed by button,s rotaty and infrared
  */
  */
 static void common_task_init(void) {
 static void common_task_init(void) {
-	static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
-	static EXT_RAM_ATTR StackType_t xStack[BUTTON_STACK_SIZE] __attribute__ ((aligned (4)));
-	
-	if (!common_queue_set) {
-		common_queue_set = xQueueCreateSet(BUTTON_QUEUE_LEN + 1);
-		xTaskCreateStatic( (TaskFunction_t) buttons_task, "buttons", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 2, xStack, &xTaskBuffer);
-	}
- }	
+    static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__((aligned(4)));
+    static EXT_RAM_ATTR StackType_t xStack[BUTTON_STACK_SIZE] __attribute__((aligned(4)));
+
+    if (!common_queue_set) {
+		ESP_LOGD(TAG,"Creating buttons task with a queue set length of %d",BUTTON_QUEUE_LEN+1);
+        common_queue_set = xQueueCreateSet(BUTTON_QUEUE_LEN + 1);
+        xTaskCreateStatic((TaskFunction_t)buttons_task, "buttons", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 2, xStack, &xTaskBuffer);
+    }
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * GPIO low-level ISR handler
  * GPIO low-level ISR handler
  */
  */
-static void IRAM_ATTR gpio_isr_handler(void* arg)
-{
-	struct button_s *button = (struct button_s*) arg;
-	BaseType_t woken = pdFALSE;
-
-	if (xTimerGetPeriod(button->timer) > pdMS_TO_TICKS(button->debounce)) {
-		if (button->gpio < GPIO_NUM_MAX) xTimerChangePeriodFromISR(button->timer, pdMS_TO_TICKS(button->debounce), &woken); 
-		else xTimerChangePeriod(button->timer, pdMS_TO_TICKS(button->debounce), pdMS_TO_TICKS(10)); 
-	} else {
-		if (button->gpio < GPIO_NUM_MAX) xTimerResetFromISR(button->timer, &woken);
-		else xTimerReset(button->timer, portMAX_DELAY);
-	}
-
-	if (woken) portYIELD_FROM_ISR();
-
-	ESP_EARLY_LOGD(TAG, "INT gpio %u level %u", button->gpio, button->level);
+static void IRAM_ATTR gpio_isr_handler(void* arg) {
+    struct button_s* button = (struct button_s*)arg;
+    BaseType_t woken = pdFALSE;
+
+    if (xTimerGetPeriod(button->timer) > pdMS_TO_TICKS(button->debounce)) {
+        if (button->gpio < GPIO_NUM_MAX)
+            xTimerChangePeriodFromISR(button->timer, pdMS_TO_TICKS(button->debounce), &woken);
+        else
+            xTimerChangePeriod(button->timer, pdMS_TO_TICKS(button->debounce), pdMS_TO_TICKS(10));
+    } else {
+        if (button->gpio < GPIO_NUM_MAX)
+            xTimerResetFromISR(button->timer, &woken);
+        else
+            xTimerReset(button->timer, portMAX_DELAY);
+    }
+
+    if (woken) portYIELD_FROM_ISR();
+
+    ESP_EARLY_LOGD(TAG, "INT gpio %u level %u", button->gpio, button->level);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Buttons debounce/longpress timer
  * Buttons debounce/longpress timer
  */
  */
-static void buttons_timer_handler( TimerHandle_t xTimer ) {
-	struct button_s *button = (struct button_s*) pvTimerGetTimerID (xTimer);
-	// if this is an expanded GPIO, must give cache a chance
-	buttons_handler(button, gpio_exp_get_level(button->gpio, (button->debounce * 3) / 2, NULL));
+static void buttons_timer_handler(TimerHandle_t xTimer) {
+    struct button_s* button = (struct button_s*)pvTimerGetTimerID(xTimer);
+    // if this is an expanded GPIO, must give cache a chance
+    buttons_handler(button, gpio_exp_get_level(button->gpio, (button->debounce * 3) / 2, NULL));
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Buttons polling timer
  * Buttons polling timer
  */
  */
-static void buttons_polling( TimerHandle_t xTimer ) {
-	for (int i = 0; polled_gpio[i].gpio != -1; i++) {
-		if (!polled_gpio[i].button) continue;
-		
-		int level = gpio_get_level(polled_gpio[i].gpio);
-	
-		if (level != polled_gpio[i].level) {
-			polled_gpio[i].level = level;
-			buttons_handler(polled_gpio[i].button, level);
-		}	
-	}	
+static void buttons_polling(TimerHandle_t xTimer) {
+    for (int i = 0; polled_gpio[i].gpio != -1; i++) {
+        if (!polled_gpio[i].button) continue;
+
+        int level = gpio_get_level(polled_gpio[i].gpio);
+
+        if (level != polled_gpio[i].level) {
+            polled_gpio[i].level = level;
+            buttons_handler(polled_gpio[i].button, level);
+        }
+    }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Buttons timer handler for press/longpress
  * Buttons timer handler for press/longpress
  */
  */
-static void buttons_handler(struct button_s *button, int level) {
-	button->level = level;
-
-	if (button->shifter && button->shifter->type == button->shifter->level) button->shifter->shifting = true;
-
-	if (button->long_press && !button->long_timer && button->level == button->type) {
-		// detect a long press, so hold event generation
-		ESP_LOGD(TAG, "setting long timer gpio:%u level:%u", button->gpio, button->level);
-		xTimerChangePeriod(button->timer, button->long_press / portTICK_RATE_MS, 0);
-		button->long_timer = true;
-	} else {
-		// send a button pressed/released event (content is copied in queue)
-		ESP_LOGD(TAG, "sending event for gpio:%u level:%u", button->gpio, button->level);
-		// queue will have a copy of button's context
-		xQueueSend(button_queue, button, 0);
-		button->long_timer = false;
-	}
+static void buttons_handler(struct button_s* button, int level) {
+    button->level = level;
+
+    if (button->shifter && button->shifter->type == button->shifter->level) button->shifter->shifting = true;
+
+    if (button->long_press && !button->long_timer && button->level == button->type) {
+        // detect a long press, so hold event generation
+        ESP_LOGD(TAG, "setting long timer gpio:%u level:%u", button->gpio, button->level);
+        xTimerChangePeriod(button->timer, button->long_press / portTICK_RATE_MS, 0);
+        button->long_timer = true;
+    } else {
+        // send a button pressed/released event (content is copied in queue)
+        ESP_LOGD(TAG, "sending event for gpio:%u level:%u", button->gpio, button->level);
+        // queue will have a copy of button's context
+        xQueueSend(button_queue, button, 0);
+        button->long_timer = false;
+    }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Get inactivity callback
  * Get inactivity callback
  */
  */
-static uint32_t buttons_idle_callback(void) {
-    return pdTICKS_TO_MS(xTaskGetTickCount()) - buttons_idle_since;
-}    
+static uint32_t buttons_idle_callback(void) { return pdTICKS_TO_MS(xTaskGetTickCount()) - buttons_idle_since; }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Tasks that calls the appropriate functions when buttons are pressed
  * Tasks that calls the appropriate functions when buttons are pressed
  */
  */
 static void buttons_task(void* arg) {
 static void buttons_task(void* arg) {
-	ESP_LOGI(TAG, "starting button tasks");   
-    
-    buttons_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());    
+    ESP_LOGI(TAG, "starting button tasks");
+
+    buttons_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
     services_sleep_setsleeper(buttons_idle_callback);
     services_sleep_setsleeper(buttons_idle_callback);
-	
+
     while (1) {
     while (1) {
-		QueueSetMemberHandle_t xActivatedMember;
+        QueueSetMemberHandle_t xActivatedMember;
         bool active = true;
         bool active = true;
 
 
-		// wait on button, rotary and infrared queues 
-		if ((xActivatedMember = xQueueSelectFromSet( common_queue_set, portMAX_DELAY )) == NULL) continue;
-        	
-		if (xActivatedMember == button_queue) {
-			struct button_s button;
-			button_event_e event;
-			button_press_e press;
-			
-			// received a button event
-			xQueueReceive(button_queue, &button, 0);
-
-			event = (button.level == button.type) ? BUTTON_PRESSED : BUTTON_RELEASED;		
-
-			ESP_LOGD(TAG, "received event:%u from gpio:%u level:%u (timer %u shifting %u)", event, button.gpio, button.level, button.long_timer, button.shifting);
-
-			// find if shifting is activated
-			if (button.shifter && button.shifter->type == button.shifter->level) press = BUTTON_SHIFTED;
-			else press = BUTTON_NORMAL;
-	
-			/* 
-			long_timer will be set either because we truly have a long press 
-			or we have a release before the long press timer elapsed, so two 
-			events shall be sent
-			*/
-			if (button.long_timer) {
-				if (event == BUTTON_RELEASED) {
-					// early release of a long-press button, send press/release
-					if (!button.shifting) {
-						button.handler(button.client, BUTTON_PRESSED, press, false);		
-						button.handler(button.client, BUTTON_RELEASED, press, false);		
-					}
-					// button is a copy, so need to go to real context
-					button.self->shifting = false;
-				} else if (!button.shifting) {
-					// normal long press and not shifting so don't discard
-					button.handler(button.client, BUTTON_PRESSED, press, true);
-				}  
-			} else {
-				// normal press/release of a button or release of a long-press button
-				if (!button.shifting) button.handler(button.client, event, press, button.long_press);
-				// button is a copy, so need to go to real context
-				button.self->shifting = false;
-			}
-		} else if (xActivatedMember == rotary.queue) {
-			rotary_encoder_event_t event = { 0 };
-			
-			// received a rotary event
-		    xQueueReceive(rotary.queue, &event, 0);
-
-			ESP_LOGD(TAG, "Event: position %d, direction %s", event.state.position,
-					event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
-			
-			rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? 
-											ROTARY_RIGHT : ROTARY_LEFT, false);   
-		} else {
-			// this is IR
-			active = infrared_receive(infrared.rb, infrared.handler);
-		}	
-        
+        // wait on button, rotary and infrared queues
+        if ((xActivatedMember = xQueueSelectFromSet(common_queue_set, portMAX_DELAY)) == NULL) continue;
+
+        if (xActivatedMember == button_queue) {
+            struct button_s button;
+            button_event_e event;
+            button_press_e press;
+
+            // received a button event
+            xQueueReceive(button_queue, &button, 0);
+
+            event = (button.level == button.type) ? BUTTON_PRESSED : BUTTON_RELEASED;
+
+            ESP_LOGD(TAG, "received event:%u from gpio:%u level:%u (timer %u shifting %u)", event, button.gpio, button.level, button.long_timer,
+                button.shifting);
+
+            // find if shifting is activated
+            if (button.shifter && button.shifter->type == button.shifter->level)
+                press = BUTTON_SHIFTED;
+            else
+                press = BUTTON_NORMAL;
+
+            /*
+            long_timer will be set either because we truly have a long press
+            or we have a release before the long press timer elapsed, so two
+            events shall be sent
+            */
+            if (button.long_timer) {
+                if (event == BUTTON_RELEASED) {
+                    // early release of a long-press button, send press/release
+                    if (!button.shifting) {
+                        button.handler(button.client, BUTTON_PRESSED, press, false);
+                        button.handler(button.client, BUTTON_RELEASED, press, false);
+                    }
+                    // button is a copy, so need to go to real context
+                    button.self->shifting = false;
+                } else if (!button.shifting) {
+                    // normal long press and not shifting so don't discard
+                    button.handler(button.client, BUTTON_PRESSED, press, true);
+                }
+            } else {
+                // normal press/release of a button or release of a long-press button
+                if (!button.shifting) button.handler(button.client, event, press, button.long_press);
+                // button is a copy, so need to go to real context
+                button.self->shifting = false;
+            }
+        } else if (xActivatedMember == rotary.queue) {
+            rotary_encoder_event_t event = {0};
+
+            // received a rotary event
+            xQueueReceive(rotary.queue, &event, 0);
+
+            ESP_LOGD(TAG, "Event: position %d, direction %s", event.state.position,
+                event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
+
+            rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? ROTARY_RIGHT : ROTARY_LEFT, false);
+        } else {
+            // this is IR
+            active = infrared_receive(infrared.rb, infrared.handler);
+        }
+
         // mark the last activity
         // mark the last activity
         if (active) buttons_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
         if (active) buttons_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
     }
     }
-}	
-	
+}
+
 /****************************************************************************************
 /****************************************************************************************
  * dummy button handler
  * dummy button handler
- */	
-void dummy_handler(void *id, button_event_e event, button_press_e press) {
-	ESP_LOGW(TAG, "should not be here");
-}
+ */
+void dummy_handler(void* id, button_event_e event, button_press_e press) { ESP_LOGW(TAG, "should not be here"); }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * Create buttons 
+ * Create buttons
  */
  */
-void button_create(void *client, int gpio, int type, bool pull, int debounce, button_handler handler, int long_press, int shifter_gpio) { 
-	if (n_buttons >= MAX_BUTTONS) return;
-
-	ESP_LOGI(TAG, "Creating button using GPIO %u, type %u, pull-up/down %u, long press %u shifter %d", gpio, type, pull, long_press, shifter_gpio);
-
-	if (!n_buttons) {
-		button_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s));
-		common_task_init();
-		xQueueAddToSet( button_queue, common_queue_set );
-	}
-	
-	// just in case this structure is allocated in a future release
-	memset(buttons + n_buttons, 0, sizeof(struct button_s));
-
-	// set mandatory parameters
-	buttons[n_buttons].client = client;
- 	buttons[n_buttons].gpio = gpio;
- 	buttons[n_buttons].debounce = debounce ? debounce: DEBOUNCE;
-	buttons[n_buttons].handler = handler;
-	buttons[n_buttons].long_press = long_press;
-	buttons[n_buttons].shifter_gpio = shifter_gpio;
-	buttons[n_buttons].type = type;
-	buttons[n_buttons].timer = xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void *) &buttons[n_buttons], buttons_timer_handler);
-	buttons[n_buttons].self = buttons + n_buttons;
-
-	for (int i = 0; i < n_buttons; i++) {
-		// first try to find our shifter
-		if (buttons[i].gpio == shifter_gpio) {
-			buttons[n_buttons].shifter = buttons + i;
-			// a shifter must have a long-press handler
-			if (!buttons[i].long_press) buttons[i].long_press = -1;
-		}
-		// then try to see if we are a non-assigned shifter
-		if (buttons[i].shifter_gpio == gpio) {
-			buttons[i].shifter = buttons + n_buttons;
-			ESP_LOGI(TAG, "post-assigned shifter gpio %u", buttons[i].gpio);			
-		}	
-	}
-
-	gpio_pad_select_gpio_x(gpio);
-	gpio_set_direction_x(gpio, GPIO_MODE_INPUT);
-
-	// do we need pullup or pulldown
-	if (pull) {
-		if (GPIO_IS_VALID_OUTPUT_GPIO(gpio) || gpio >= GPIO_NUM_MAX) {
-			if (type == BUTTON_LOW) gpio_set_pull_mode_x(gpio, GPIO_PULLUP_ONLY);
-			else gpio_set_pull_mode_x(gpio, GPIO_PULLDOWN_ONLY);
-		} else {	
-			ESP_LOGW(TAG, "cannot set pull up/down for gpio %u", gpio);
-		}
-	}
-	
-	// and initialize level ...
-	buttons[n_buttons].level = gpio_get_level_x(gpio);
-	
-	// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
-	for (int i = 0; polled_gpio[i].gpio != -1; i++) if (polled_gpio[i].gpio == gpio) {
-		if (!polled_timer) {
-			polled_timer = xTimerCreate("buttonsPolling", 100 / portTICK_RATE_MS, pdTRUE, polled_gpio, buttons_polling);		
-			xTimerStart(polled_timer, portMAX_DELAY);
-		}	
-	
-		polled_gpio[i].button = buttons + n_buttons;					
-		polled_gpio[i].level = gpio_get_level(gpio);
-		ESP_LOGW(TAG, "creating polled gpio %u, level %u", gpio, polled_gpio[i].level);		
-	
-		gpio = -1;
-		break;
-	}
-	
-	// only create ISR if this is not a polled gpio
-	if (gpio != -1) {
-		// we need any edge detection
-		gpio_set_intr_type_x(gpio, GPIO_INTR_ANYEDGE);
-		gpio_isr_handler_add_x(gpio, gpio_isr_handler, buttons + n_buttons);
-		gpio_intr_enable_x(gpio);
-	}	
-
-	n_buttons++;
-}	
+void button_create(void* client, int gpio, int type, bool pull, int debounce, button_handler handler, int long_press, int shifter_gpio) {
+    if (n_buttons >= MAX_BUTTONS) return;
+
+    ESP_LOGI(TAG, "Creating button using GPIO %u, type %s, %s pull-up/down, long press %u shifter %d", gpio,
+        sys_gpio_lvl_name(type == 0 ? sys_gpio_lvl_LOW : sys_gpio_lvl_HIGH), pull?"with":"without", long_press, shifter_gpio);
+
+    if (!n_buttons) {
+		ESP_LOGD(TAG,"Creating new buttton message queue with a length of %d entries",BUTTON_QUEUE_LEN);
+        button_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s));
+        common_task_init();
+        xQueueAddToSet(button_queue, common_queue_set);
+    }
+
+    // just in case this structure is allocated in a future release
+    memset(buttons + n_buttons, 0, sizeof(struct button_s));
+
+    // set mandatory parameters
+    buttons[n_buttons].client = client;
+    buttons[n_buttons].gpio = gpio;
+    buttons[n_buttons].debounce = debounce ? debounce : DEBOUNCE;
+    buttons[n_buttons].handler = handler;
+    buttons[n_buttons].long_press = long_press;
+    buttons[n_buttons].shifter_gpio = shifter_gpio;
+    buttons[n_buttons].type = type;
+    buttons[n_buttons].timer =
+        xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void*)&buttons[n_buttons], buttons_timer_handler);
+    buttons[n_buttons].self = buttons + n_buttons;
+
+    for (int i = 0; i < n_buttons; i++) {
+        // first try to find our shifter
+        if (buttons[i].gpio == shifter_gpio) {
+            buttons[n_buttons].shifter = buttons + i;
+            // a shifter must have a long-press handler
+            if (!buttons[i].long_press) buttons[i].long_press = -1;
+        }
+        // then try to see if we are a non-assigned shifter
+        if (buttons[i].shifter_gpio == gpio) {
+            buttons[i].shifter = buttons + n_buttons;
+            ESP_LOGI(TAG, "post-assigned shifter gpio %u", buttons[i].gpio);
+        }
+    }
+
+    gpio_pad_select_gpio_x(gpio);
+    gpio_set_direction_x(gpio, GPIO_MODE_INPUT);
+
+    // do we need pullup or pulldown
+    if (pull) {
+        if (GPIO_IS_VALID_OUTPUT_GPIO(gpio) || gpio >= GPIO_NUM_MAX) {
+            if (type == BUTTON_LOW)
+                gpio_set_pull_mode_x(gpio, GPIO_PULLUP_ONLY);
+            else
+                gpio_set_pull_mode_x(gpio, GPIO_PULLDOWN_ONLY);
+        } else {
+            ESP_LOGW(TAG, "cannot set pull up/down for gpio %u", gpio);
+        }
+    }
+
+    // and initialize level ...
+    buttons[n_buttons].level = gpio_get_level_x(gpio);
+
+    // nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
+    for (int i = 0; polled_gpio[i].gpio != -1; i++)
+        if (polled_gpio[i].gpio == gpio) {
+            if (!polled_timer) {
+                polled_timer = xTimerCreate("buttonsPolling", 100 / portTICK_RATE_MS, pdTRUE, polled_gpio, buttons_polling);
+                xTimerStart(polled_timer, portMAX_DELAY);
+            }
+
+            polled_gpio[i].button = buttons + n_buttons;
+            polled_gpio[i].level = gpio_get_level(gpio);
+            ESP_LOGW(TAG, "creating polled gpio %u, level %u", gpio, polled_gpio[i].level);
+
+            gpio = -1;
+            break;
+        }
+
+    // only create ISR if this is not a polled gpio
+    if (gpio != -1) {
+        // we need any edge detection
+        gpio_set_intr_type_x(gpio, GPIO_INTR_ANYEDGE);
+        gpio_isr_handler_add_x(gpio, gpio_isr_handler, buttons + n_buttons);
+        gpio_intr_enable_x(gpio);
+    }
+
+    n_buttons++;
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Get stored id
  * Get stored id
  */
  */
-void *button_get_client(int gpio) {
-	 for (int i = 0; i < n_buttons; i++) {
-		 if (buttons[i].gpio == gpio) return buttons[i].client;
-	 }
-	 return NULL;
+void* button_get_client(int gpio) {
+    for (int i = 0; i < n_buttons; i++) {
+        if (buttons[i].gpio == gpio) return buttons[i].client;
+    }
+    return NULL;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Get stored id
  * Get stored id
  */
  */
-bool button_is_pressed(int gpio, void *client) {
-	for (int i = 0; i < n_buttons; i++) {
-		if (gpio != -1 && buttons[i].gpio == gpio) return buttons[i].level == buttons[i].type;
-		else if (client && buttons[i].client == client) return buttons[i].level == buttons[i].type;
-	}
-	return false; 
+bool button_is_pressed(int gpio, void* client) {
+    for (int i = 0; i < n_buttons; i++) {
+        if (gpio != -1 && buttons[i].gpio == gpio)
+            return buttons[i].level == buttons[i].type;
+        else if (client && buttons[i].client == client)
+            return buttons[i].level == buttons[i].type;
+    }
+    return false;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * Update buttons 
+ * Update buttons
  */
  */
-void *button_remap(void *client, int gpio, button_handler handler, int long_press, int shifter_gpio) { 
-	int i;
-	struct button_s *button = NULL;
-	void *prev_client;
-	
-	ESP_LOGI(TAG, "remapping GPIO %u, long press %u shifter %u", gpio, long_press, shifter_gpio);
-
-	// find button
-	for (i = 0; i < n_buttons; i++) {
-		if (buttons[i].gpio == gpio) {
-			button = buttons + i;
-			break;
-		}	
-	}	
-	
-	// don't know what we are doing here
-	if (!button) return NULL;	
-	
-	prev_client = button->client;
-	button->client = client;
- 	button->handler = handler;
-	button->long_press = long_press;
-	button->shifter_gpio = shifter_gpio;
-
-	// find our shifter	(if any)	
-	for (i = 0; shifter_gpio != -1 && i < n_buttons; i++) {
-		if (buttons[i].gpio == shifter_gpio) {
-			button->shifter = buttons + i;
-			// a shifter must have a long-press handler
-			if (!buttons[i].long_press) buttons[i].long_press = -1;
-			break;
-		}
-	}
-	
-	return prev_client;
+void* button_remap(void* client, int gpio, button_handler handler, int long_press, int shifter_gpio) {
+    int i;
+    struct button_s* button = NULL;
+    void* prev_client;
+
+    ESP_LOGI(TAG, "remapping GPIO %u, long press %u shifter %u", gpio, long_press, shifter_gpio);
+
+    // find button
+    for (i = 0; i < n_buttons; i++) {
+        if (buttons[i].gpio == gpio) {
+            button = buttons + i;
+            break;
+        }
+    }
+
+    // don't know what we are doing here
+    if (!button) return NULL;
+
+    prev_client = button->client;
+    button->client = client;
+    button->handler = handler;
+    button->long_press = long_press;
+    button->shifter_gpio = shifter_gpio;
+
+    // find our shifter	(if any)
+    for (i = 0; shifter_gpio != -1 && i < n_buttons; i++) {
+        if (buttons[i].gpio == shifter_gpio) {
+            button->shifter = buttons + i;
+            // a shifter must have a long-press handler
+            if (!buttons[i].long_press) buttons[i].long_press = -1;
+            break;
+        }
+    }
+
+    return prev_client;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Rotary encoder handler
  * Rotary encoder handler
  */
  */
-static void rotary_button_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
-	ESP_LOGI(TAG, "Rotary push-button %d", event);
-	rotary.handler(id, event == BUTTON_PRESSED ? ROTARY_PRESSED : ROTARY_RELEASED, long_press);
+static void rotary_button_handler(void* id, button_event_e event, button_press_e mode, bool long_press) {
+    ESP_LOGI(TAG, "Rotary push-button %d", event);
+    rotary.handler(id, event == BUTTON_PRESSED ? ROTARY_PRESSED : ROTARY_RELEASED, long_press);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Create rotary encoder
  * Create rotary encoder
  */
  */
-bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler) {
-	// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
-	if (A == -1 || B == -1 || A == 36 || A == 39 || B == 36 || B == 39) {
-		ESP_LOGI(TAG, "Cannot create rotary %d %d", A, B);
-		return false;
-	}
-
-	rotary.A = A;
-	rotary.B = B;
-	rotary.SW = SW;
-	rotary.client = id;
-	rotary.handler = handler;
-	
+bool create_rotary(void* id, int A, int B, int SW, int long_press, rotary_handler handler) {
+    // nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
+    if (A == -1 || B == -1 || A == 36 || A == 39 || B == 36 || B == 39) {
+        ESP_LOGI(TAG, "Cannot create rotary %d %d", A, B);
+        return false;
+    }
+
+    rotary.A = A;
+    rotary.B = B;
+    rotary.SW = SW;
+    rotary.client = id;
+    rotary.handler = handler;
+
     // Initialise the rotary encoder device with the GPIOs for A and B signals
     // Initialise the rotary encoder device with the GPIOs for A and B signals
     rotary_encoder_init(&rotary.info, A, B);
     rotary_encoder_init(&rotary.info, A, B);
-		
+
     // Create a queue for events from the rotary encoder driver.
     // Create a queue for events from the rotary encoder driver.
     rotary.queue = rotary_encoder_create_queue();
     rotary.queue = rotary_encoder_create_queue();
     rotary_encoder_set_queue(&rotary.info, rotary.queue);
     rotary_encoder_set_queue(&rotary.info, rotary.queue);
-	
-	common_task_init();
-	xQueueAddToSet( rotary.queue, common_queue_set );
-
-	// create companion button if rotary has a switch
-	if (SW != -1) button_create(id, SW, BUTTON_LOW, true, 0, rotary_button_handler, long_press, -1);
-	
-	ESP_LOGI(TAG, "Created rotary encoder A:%d B:%d, SW:%d", A, B, SW);
-	
-	return true;
-}	
+
+    common_task_init();
+    xQueueAddToSet(rotary.queue, common_queue_set);
+
+    // create companion button if rotary has a switch
+    if (SW != -1) button_create(id, SW, BUTTON_LOW, true, 0, rotary_button_handler, long_press, -1);
+
+    ESP_LOGI(TAG, "Created rotary encoder A:%d B:%d, SW:%d", A, B, SW);
+
+    return true;
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Create Infrared
  * Create Infrared
  */
  */
 bool create_infrared(int gpio, infrared_handler handler, infrared_mode_t mode) {
 bool create_infrared(int gpio, infrared_handler handler, infrared_mode_t mode) {
-	// initialize IR infrastructure
-	infrared_init(&infrared.rb, gpio, mode);
-	infrared.handler = handler;
-	
-	// join the queue set
-	common_task_init();
-	xRingbufferAddToQueueSetRead(infrared.rb, common_queue_set);
-	
-	ESP_LOGI(TAG, "Created infrared receiver using GPIO %u", gpio);	
-	
-	return (infrared.rb != NULL);
-}	
+    // initialize IR infrastructure
+    infrared_init(&infrared.rb, gpio, mode);
+    infrared.handler = handler;
+
+    // join the queue set
+    common_task_init();
+    xRingbufferAddToQueueSetRead(infrared.rb, common_queue_set);
+
+    ESP_LOGI(TAG, "Created infrared receiver using GPIO %u", gpio);
+
+    return (infrared.rb != NULL);
+}

+ 0 - 3
components/services/globdefs.h

@@ -10,14 +10,11 @@
  
  
 #pragma once
 #pragma once
 
 
-#define I2C_SYSTEM_PORT		1
 #define SPI_SYSTEM_HOST		SPI2_HOST
 #define SPI_SYSTEM_HOST		SPI2_HOST
 
 
 #define RMT_NEXT_TX_CHANNEL() rmt_system_base_tx_channel++;
 #define RMT_NEXT_TX_CHANNEL() rmt_system_base_tx_channel++;
 #define RMT_NEXT_RX_CHANNEL() rmt_system_base_rx_channel--;
 #define RMT_NEXT_RX_CHANNEL() rmt_system_base_rx_channel--;
 
 
-extern int i2c_system_port;
-extern int i2c_system_speed;
 extern int spi_system_host;
 extern int spi_system_host;
 extern int spi_system_dc_gpio;
 extern int spi_system_dc_gpio;
 extern int rmt_system_base_tx_channel;
 extern int rmt_system_base_tx_channel;

+ 15 - 3
components/services/gpio_exp.c

@@ -18,7 +18,7 @@
 #include "driver/i2c.h"
 #include "driver/i2c.h"
 #include "driver/spi_master.h"
 #include "driver/spi_master.h"
 #include "gpio_exp.h"
 #include "gpio_exp.h"
-
+#include "GPIO.pb.h"
 #define GPIO_EXP_INTR	0x100
 #define GPIO_EXP_INTR	0x100
 #define	GPIO_EXP_WRITE	0x200
 #define	GPIO_EXP_WRITE	0x200
 
 
@@ -181,7 +181,6 @@ gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) {
 		ESP_LOGE(TAG, "Cannot create GPIO expander %s, check i2c/spi configuration", config->model);
 		ESP_LOGE(TAG, "Cannot create GPIO expander %s, check i2c/spi configuration", config->model);
 		return NULL;
 		return NULL;
 	}
 	}
-
 	n_expanders++;
 	n_expanders++;
 	expander->first = config->base;
 	expander->first = config->base;
 	expander->last = config->base + config->count - 1;
 	expander->last = config->base + config->count - 1;
@@ -197,6 +196,12 @@ gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) {
 		message_queue = xQueueCreate(4, sizeof(queue_request_t));
 		message_queue = xQueueCreate(4, sizeof(queue_request_t));
 		service_task = xTaskCreateStatic(service_handler, "gpio_expander", sizeof(xStack), NULL, ESP_TASK_PRIO_MIN + 1, xStack, xTaskBuffer);
 		service_task = xTaskCreateStatic(service_handler, "gpio_expander", sizeof(xStack), NULL, ESP_TASK_PRIO_MIN + 1, xStack, xTaskBuffer);
 	}
 	}
+	if(config->phy.ena_pin>=0){
+		ESP_LOGD(TAG,"Enabling expander with pin %d level %d",config->phy.ena_pin,config->phy.ena_lvl);
+		gpio_pad_select_gpio(config->phy.ena_pin);
+		gpio_set_direction(config->phy.ena_pin, GPIO_MODE_DEF_OUTPUT);
+		gpio_set_level(config->phy.ena_pin, config->phy.ena_lvl);
+	}
 
 
 	// set interrupt if possible
 	// set interrupt if possible
 	if (config->intr >= 0) {
 	if (config->intr >= 0) {
@@ -222,7 +227,7 @@ gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) {
 		gpio_intr_enable(config->intr);						
 		gpio_intr_enable(config->intr);						
 	}
 	}
 	
 	
-	ESP_LOGI(TAG, "Create GPIO expander %s at base %u with intr %d at @%x on port/host %d/%d", config->model, config->base, config->intr, config->phy.addr, config->phy.port, config->phy.host);
+	ESP_LOGI(TAG, "Create GPIO expander %s at base %u with intr %d at @%x on port/host %d/%d, enable pin: %d:%d", config->model, config->base, config->intr, config->phy.addr, config->phy.port, config->phy.host,config->phy.ena_pin,config->phy.ena_lvl);
 	return expander;
 	return expander;
 }
 }
 
 
@@ -377,6 +382,13 @@ esp_err_t gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, gpio_exp_t *ex
 /******************************************************************************
 /******************************************************************************
  * Wrapper function
  * Wrapper function
  */
  */
+
+void esp_rom_gpio_pad_select_gpio_x(uint32_t gpio){
+	if (gpio < GPIO_NUM_MAX) {
+		esp_rom_gpio_pad_select_gpio(gpio);
+	} 
+}
+
 esp_err_t gpio_set_pull_mode_x(int gpio, gpio_pull_mode_t mode) {
 esp_err_t gpio_set_pull_mode_x(int gpio, gpio_pull_mode_t mode) {
 	if (gpio < GPIO_NUM_MAX) return gpio_set_pull_mode(gpio, mode);
 	if (gpio < GPIO_NUM_MAX) return gpio_set_pull_mode(gpio, mode);
 	return gpio_exp_set_pull_mode(gpio, mode, NULL);
 	return gpio_exp_set_pull_mode(gpio, mode, NULL);

+ 4 - 0
components/services/gpio_exp.h

@@ -11,6 +11,7 @@
 #include <stdint.h>
 #include <stdint.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "driver/gpio.h"
 #include "driver/gpio.h"
+#include "esp_rom_gpio.h"
 
 
 struct gpio_exp_s;
 struct gpio_exp_s;
 
 
@@ -29,6 +30,8 @@ typedef struct {
 			uint8_t host;	
 			uint8_t host;	
 			uint8_t cs_pin; 		
 			uint8_t cs_pin; 		
 		};
 		};
+		int8_t ena_pin; // enable pin
+		int8_t ena_lvl; // enable level
 	} phy;	
 	} phy;	
 } gpio_exp_config_t;
 } gpio_exp_config_t;
 
 
@@ -42,6 +45,7 @@ struct gpio_exp_s*  gpio_exp_get_expander(int gpio);
  For all functions below when <expander> is provided, GPIO's can be numbered from 0. If <expander>
  For all functions below when <expander> is provided, GPIO's can be numbered from 0. If <expander>
  is NULL, then GPIO must start from base OR be on-chip
  is NULL, then GPIO must start from base OR be on-chip
 */
 */
+void esp_rom_gpio_pad_select_gpio_x(uint32_t iopad_num);
 esp_err_t	gpio_exp_set_direction(int gpio, gpio_mode_t mode, struct gpio_exp_s *expander);
 esp_err_t	gpio_exp_set_direction(int gpio, gpio_mode_t mode, struct gpio_exp_s *expander);
 esp_err_t   gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, struct gpio_exp_s *expander);
 esp_err_t   gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, struct gpio_exp_s *expander);
 int         gpio_exp_get_level(int gpio, int age, struct gpio_exp_s *expander);
 int         gpio_exp_get_level(int gpio, int age, struct gpio_exp_s *expander);

+ 33 - 30
components/services/led.c

@@ -5,7 +5,10 @@
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    CONDITIONS OF ANY KIND, either express or implied.
    CONDITIONS OF ANY KIND, either express or implied.
 */
 */
-
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
+#include "led.h"
+#include "Config.h"
+#include "accessors.h"
 #include "driver/gpio.h"
 #include "driver/gpio.h"
 #include "driver/ledc.h"
 #include "driver/ledc.h"
 #include "driver/rmt.h"
 #include "driver/rmt.h"
@@ -14,17 +17,14 @@
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/task.h"
 #include "freertos/timers.h"
 #include "freertos/timers.h"
+#include "globdefs.h"
+#include "gpio_exp.h"
+#include "services.h"
 #include <math.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
 #include <unistd.h>
 #include <unistd.h>
-#include "Configurator.h"
-#include "accessors.h"
-#include "globdefs.h"
-#include "gpio_exp.h"
-#include "led.h"
-#include "services.h"
 
 
 #define MAX_LED 8
 #define MAX_LED 8
 #define BLOCKTIME 10 // up to portMAX_DELAY
 #define BLOCKTIME 10 // up to portMAX_DELAY
@@ -43,7 +43,7 @@ static int8_t led_rmt_channel = -1;
 static uint32_t scale24(uint32_t bright, uint8_t);
 static uint32_t scale24(uint32_t bright, uint8_t);
 
 
 static const struct rmt_led_param_s {
 static const struct rmt_led_param_s {
-    sys_LedTypesEnum type;
+    sys_led_types type;
     uint8_t bits;
     uint8_t bits;
     // number of ticks in nanoseconds converted in RMT_CLK ticks
     // number of ticks in nanoseconds converted in RMT_CLK ticks
     rmt_item32_t bit_0;
     rmt_item32_t bit_0;
@@ -51,7 +51,7 @@ static const struct rmt_led_param_s {
     uint32_t green, red;
     uint32_t green, red;
     uint32_t (*scale)(uint32_t, uint8_t);
     uint32_t (*scale)(uint32_t, uint8_t);
 } rmt_led_param[] = {
 } rmt_led_param[] = {
-    {sys_LedTypesEnum_LED_TYPE_WS2812, 24, {{{350 / RMT_CLK, 1, 1000 / RMT_CLK, 0}}},
+    {sys_led_types_WS2812, 24, {{{350 / RMT_CLK, 1, 1000 / RMT_CLK, 0}}},
         {{{1000 / RMT_CLK, 1, 350 / RMT_CLK, 0}}}, 0xff0000, 0x00ff00, scale24},
         {{{1000 / RMT_CLK, 1, 350 / RMT_CLK, 0}}}, 0xff0000, 0x00ff00, scale24},
     {.type = -1}};
     {.type = -1}};
 
 
@@ -73,15 +73,9 @@ static struct led_config_s {
     int gpio;
     int gpio;
     int color;
     int color;
     int bright;
     int bright;
-    sys_LedTypesEnum type;
-} green = {.gpio = CONFIG_LED_GREEN_GPIO,
-    .color = 0,
-    .bright = -1,
-    .type = sys_LedTypesEnum_LED_TYPE_GPIO},
-  red = {.gpio = CONFIG_LED_RED_GPIO,
-      .color = 0,
-      .bright = -1,
-      .type = sys_LedTypesEnum_LED_TYPE_GPIO};
+    sys_led_types type;
+} green = {.gpio = -1, .color = 0, .bright = -1, .type = sys_led_types_GPIO},
+  red = {.gpio = -1, .color = 0, .bright = -1, .type = sys_led_types_GPIO};
 
 
 static int led_max = 2;
 static int led_max = 2;
 
 
@@ -127,7 +121,7 @@ static void vCallbackFunction(TimerHandle_t xTimer) {
     if (!led->timer) return;
     if (!led->timer) return;
 
 
     led->on = !led->on;
     led->on = !led->on;
-    ESP_EARLY_LOGD(TAG, "led vCallbackFunction setting gpio %d level %d (bright:%d)", led->gpio,
+    ESP_EARLY_LOGV(TAG, "led vCallbackFunction setting gpio %d level %d (bright:%d)", led->gpio,
         led->on, led->bright);
         led->on, led->bright);
     set_level(led, led->on);
     set_level(led, led->on);
 
 
@@ -231,12 +225,13 @@ int led_allocate(void) {
 /****************************************************************************************
 /****************************************************************************************
  *
  *
  */
  */
-bool led_config(int idx, sys_LED* led_config) {
+bool led_config(int idx, sys_led_config* led_config) {
     if (!led_config->has_gpio) {
     if (!led_config->has_gpio) {
+        ESP_LOGD(TAG,"No GPIO configured for %s LED",idx == LED_GREEN ? "GREEN" : "RED");
         return false;
         return false;
     }
     }
     if (led_config->gpio.pin < 0) {
     if (led_config->gpio.pin < 0) {
-        ESP_LOGW(TAG, "LED GPIO -1 ignored");
+        ESP_LOGD(TAG,"GPIO -1 ignored for %s LED",idx == LED_GREEN ? "GREEN" : "RED");
         return false;
         return false;
     }
     }
     if (idx >= MAX_LED) return false;
     if (idx >= MAX_LED) return false;
@@ -247,7 +242,7 @@ bool led_config(int idx, sys_LED* led_config) {
     leds[idx].rmt = NULL;
     leds[idx].rmt = NULL;
     leds[idx].bright = -1;
     leds[idx].bright = -1;
 
 
-    if (led_config->led_type != sys_LedTypesEnum_LED_TYPE_GPIO) {
+    if (led_config->led_type != sys_led_types_GPIO) {
         // first make sure we have a known addressable led
         // first make sure we have a known addressable led
         for (const struct rmt_led_param_s* p = rmt_led_param; !leds[idx].rmt && p->type >= 0; p++)
         for (const struct rmt_led_param_s* p = rmt_led_param; !leds[idx].rmt && p->type >= 0; p++)
             if (p->type == led_config->led_type) leds[idx].rmt = p;
             if (p->type == led_config->led_type) leds[idx].rmt = p;
@@ -285,7 +280,7 @@ bool led_config(int idx, sys_LED* led_config) {
 
 
     set_level(leds + idx, false);
     set_level(leds + idx, false);
     ESP_LOGI(TAG, "Configuring LED %s %d (on:%d rmt:%s %d%% )", idx == LED_GREEN ? "GREEN" : "RED",
     ESP_LOGI(TAG, "Configuring LED %s %d (on:%d rmt:%s %d%% )", idx == LED_GREEN ? "GREEN" : "RED",
-        led_config->gpio.pin, led_config->gpio.level, sys_LedTypesEnum_name(led_config->led_type),
+        led_config->gpio.pin, led_config->gpio.level, sys_led_types_name(led_config->led_type),
         led_config->brightness);
         led_config->brightness);
     return true;
     return true;
 }
 }
@@ -294,6 +289,7 @@ bool led_config(int idx, sys_LED* led_config) {
  *
  *
  */
  */
 static void led_suspend(void) {
 static void led_suspend(void) {
+    ESP_LOGD(TAG,"led_suspend: turning off leds");
     led_off(LED_GREEN);
     led_off(LED_GREEN);
     led_off(LED_RED);
     led_off(LED_RED);
 }
 }
@@ -316,12 +312,12 @@ void set_led_gpio(int gpio, char* value) {
     while ((p = strchr(p, ':')) != NULL) {
     while ((p = strchr(p, ':')) != NULL) {
         p++;
         p++;
         if ((strcasestr(p, "ws2812")) != NULL)
         if ((strcasestr(p, "ws2812")) != NULL)
-            config->type = sys_LedTypesEnum_LED_TYPE_WS2812;
+            config->type = sys_led_types_WS2812;
         else
         else
             config->color = atoi(p);
             config->color = atoi(p);
     }
     }
 
 
-    if (config->type != sys_LedTypesEnum_LED_TYPE_GPIO) {
+    if (config->type != sys_led_types_GPIO) {
         for (const struct rmt_led_param_s* p = rmt_led_param; p->type >= 0; p++) {
         for (const struct rmt_led_param_s* p = rmt_led_param; p->type >= 0; p++) {
             if (p->type == config->type) {
             if (p->type == config->type) {
                 if (config == &green)
                 if (config == &green)
@@ -335,18 +331,25 @@ void set_led_gpio(int gpio, char* value) {
 }
 }
 
 
 void led_svc_init(void) {
 void led_svc_init(void) {
-    sys_Gpios* gpios = NULL;
+    sys_gpios_config* gpios = NULL;
+    bool found = false;
+    
     if (!platform->has_gpios) {
     if (!platform->has_gpios) {
+        ESP_LOGI(TAG, "No LED configured");
         return;
         return;
     }
     }
+    ESP_LOGI(TAG,"Setting up leds");
     gpios = &platform->gpios;
     gpios = &platform->gpios;
     if (gpios->has_greenLED) {
     if (gpios->has_greenLED) {
-        led_config(LED_GREEN, &gpios->greenLED);
+        found = found | led_config(LED_GREEN, &gpios->greenLED);
     }
     }
     if (gpios->has_redLED) {
     if (gpios->has_redLED) {
-        led_config(LED_RED, &gpios->redLED);
+        found = found | led_config(LED_RED, &gpios->redLED);
     }
     }
-
+    ESP_LOGD(TAG,"Done setting up leds");
     // make sure we switch off all leds (useful for gpio expanders)
     // make sure we switch off all leds (useful for gpio expanders)
-    services_sleep_setsuspend(led_suspend);
+    if (found) {
+        ESP_LOGD(TAG,"Switching leds off");
+        services_sleep_setsuspend(led_suspend);
+    }
 }
 }

+ 2 - 2
components/services/led.h

@@ -12,7 +12,7 @@
 #ifndef LED_H
 #ifndef LED_H
 #define LED_H
 #define LED_H
 #include "driver/gpio.h"
 #include "driver/gpio.h"
-#include "Configurator.h"
+#include "Config.h"
 
 
 enum { LED_GREEN = 0, LED_RED };
 enum { LED_GREEN = 0, LED_RED };
 #define led_on(idx)						led_blink_core(idx, 1, 0, false)
 #define led_on(idx)						led_blink_core(idx, 1, 0, false)
@@ -21,7 +21,7 @@ enum { LED_GREEN = 0, LED_RED };
 #define led_blink_pushed(idx, on, off)	led_blink_core(idx, on, off, true)
 #define led_blink_pushed(idx, on, off)	led_blink_core(idx, on, off, true)
 
 
 // if type is LED_GPIO then color set the GPIO logic value for "on"
 // if type is LED_GPIO then color set the GPIO logic value for "on"
-bool led_config(int idx, sys_LED * led_config);
+bool led_config(int idx, sys_led_config * led_config);
 bool led_brightness(int idx, int percent);
 bool led_brightness(int idx, int percent);
 bool led_blink_core(int idx, int ontime, int offtime, bool push);
 bool led_blink_core(int idx, int ontime, int offtime, bool push);
 bool led_unpush(int idx);
 bool led_unpush(int idx);

+ 263 - 260
components/services/messaging.c

@@ -1,20 +1,20 @@
-	/**
+/**
  *
  *
  */
  */
-#include <stdlib.h> // Required for libtelnet.h
-#include <esp_log.h>
+#include "config.h"
+#include "esp_app_trace.h"
+#include "esp_attr.h"
 #include "stdbool.h"
 #include "stdbool.h"
+#include <errno.h>
+#include <esp_log.h>
 #include <lwip/def.h>
 #include <lwip/def.h>
 #include <lwip/sockets.h>
 #include <lwip/sockets.h>
-#include <errno.h>
+#include <stdlib.h> // Required for libtelnet.h
 #include <string.h>
 #include <string.h>
-#include "esp_app_trace.h"
-#include "esp_attr.h"
-#include "config.h"
 // #include "nvs_utilities.h"
 // #include "nvs_utilities.h"
 
 
-#include "platform_esp32.h"
 #include "messaging.h"
 #include "messaging.h"
+#include "platform_esp32.h"
 #include "tools.h"
 #include "tools.h"
 /************************************
 /************************************
  * Globals
  * Globals
@@ -22,290 +22,293 @@
 
 
 const static char tag[] = "messaging";
 const static char tag[] = "messaging";
 typedef struct {
 typedef struct {
-	struct messaging_list_t * next;
-	char * subscriber_name;
-	size_t max_count;
-	RingbufHandle_t buf_handle;
+    struct messaging_list_t* next;
+    char* subscriber_name;
+    size_t max_count;
+    RingbufHandle_t buf_handle;
 } messaging_list_t;
 } messaging_list_t;
 static messaging_list_t top;
 static messaging_list_t top;
 #define MSG_LENGTH_AVG 1024
 #define MSG_LENGTH_AVG 1024
 
 
-messaging_list_t * get_struct_ptr(messaging_handle_t handle){
-	return (messaging_list_t *)handle;
-}
-messaging_handle_t  get_handle_ptr(messaging_list_t * handle){
-	return (messaging_handle_t )handle;
-}
+messaging_list_t* get_struct_ptr(messaging_handle_t handle) { return (messaging_list_t*)handle; }
+messaging_handle_t get_handle_ptr(messaging_list_t* handle) { return (messaging_handle_t)handle; }
 
 
-RingbufHandle_t messaging_create_ring_buffer(uint8_t max_count){
-	RingbufHandle_t buf_handle = NULL;
-	StaticRingbuffer_t *buffer_struct = malloc_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);
-		uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_32BIT);
-		if (buffer_storage== NULL) {
-			ESP_LOGE(tag,"buff alloc failed");
-		}
-		else {
-			buf_handle = xRingbufferCreateStatic(buf_size, RINGBUF_TYPE_NOSPLIT, buffer_storage, buffer_struct);
-		}
-	}
-	else {
-		ESP_LOGE(tag,"ringbuf alloc failed");
-	}
-	return buf_handle;
+RingbufHandle_t messaging_create_ring_buffer(uint8_t max_count) {
+    RingbufHandle_t buf_handle = NULL;
+    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);
+        uint8_t* buffer_storage =
+            (uint8_t*)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_32BIT);
+        if (buffer_storage == NULL) {
+            ESP_LOGE(tag, "buff alloc failed");
+        } else {
+            buf_handle = xRingbufferCreateStatic(
+                buf_size, RINGBUF_TYPE_NOSPLIT, buffer_storage, buffer_struct);
+        }
+    } else {
+        ESP_LOGE(tag, "ringbuf alloc failed");
+    }
+    return buf_handle;
 }
 }
-void messaging_fill_messages(messaging_list_t * target_subscriber){
-	single_message_t * message=NULL;
+void messaging_fill_messages(messaging_list_t* target_subscriber) {
+    single_message_t* message = NULL;
     UBaseType_t uxItemsWaiting;
     UBaseType_t uxItemsWaiting;
-
+    if (!top.buf_handle) {
+        ESP_LOGE(tag, "Queue initialization error!");
+        return;
+    }
     vRingbufferGetInfo(top.buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
     vRingbufferGetInfo(top.buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
-    for(size_t i=0;i<uxItemsWaiting;i++){
-    	message= messaging_retrieve_message(top.buf_handle);
-    	if(message){
-			//re-post to original queue so it is available to future subscribers
-			messaging_post_to_queue(get_handle_ptr(&top), message, message->msg_size);
-			// post to new subscriber
-			messaging_post_to_queue(get_handle_ptr(target_subscriber) , message, message->msg_size);
-			FREE_AND_NULL(message);
-    	}
+    for (size_t i = 0; i < uxItemsWaiting; i++) {
+        message = messaging_retrieve_message(top.buf_handle);
+        if (message) {
+            // re-post to original queue so it is available to future subscribers
+            messaging_post_to_queue(get_handle_ptr(&top), message, message->msg_size);
+            // post to new subscriber
+            messaging_post_to_queue(get_handle_ptr(target_subscriber), message, message->msg_size);
+            FREE_AND_NULL(message);
+        }
     }
     }
 }
 }
-messaging_handle_t messaging_register_subscriber(uint8_t max_count, char * name){
-	messaging_list_t * cur=&top;
-	while(cur->next){
-		cur = get_struct_ptr(cur->next);
-	}
-	cur->next=malloc_init_external(sizeof(messaging_list_t));
-	if(!cur->next){
-		ESP_LOGE(tag,"subscriber alloc failed");
-		return NULL;
-	}
-	memset(cur->next,0x00,sizeof(messaging_list_t));
-	cur = get_struct_ptr(cur->next);
-	cur->max_count=max_count;
-	cur->subscriber_name=strdup_psram(name);
-	cur->buf_handle = messaging_create_ring_buffer(max_count);
-	if(cur->buf_handle){
-		messaging_fill_messages(cur);
-	}
-	return cur->buf_handle;
+messaging_handle_t messaging_register_subscriber(uint8_t max_count, char* name) {
+    messaging_list_t* cur = &top;
+    while (cur->next) {
+        cur = get_struct_ptr(cur->next);
+    }
+    cur->next = malloc_init_external(sizeof(messaging_list_t));
+    if (!cur->next) {
+        ESP_LOGE(tag, "subscriber alloc failed");
+        return NULL;
+    }
+    memset(cur->next, 0x00, sizeof(messaging_list_t));
+    cur = get_struct_ptr(cur->next);
+    cur->max_count = max_count;
+    cur->subscriber_name = strdup_psram(name);
+    cur->buf_handle = messaging_create_ring_buffer(max_count);
+    if (cur->buf_handle) {
+        messaging_fill_messages(cur);
+    }
+    return cur->buf_handle;
 }
 }
-void messaging_service_init(){
-	size_t max_count=15;
-	top.buf_handle = messaging_create_ring_buffer(max_count);
-	if(!top.buf_handle){
-		ESP_LOGE(tag, "messaging service init failed.");
-	}
-	else {
-		top.max_count = max_count;
-		top.subscriber_name = strdup_psram("messaging");
-	}
-	return;
+void messaging_service_init() {
+    size_t max_count = 15;
+    ESP_LOGI(tag, "Setting up messaging");
+    top.buf_handle = messaging_create_ring_buffer(max_count);
+    if (!top.buf_handle) {
+        ESP_LOGE(tag, "messaging service init failed.");
+    } else {
+        top.max_count = max_count;
+        top.subscriber_name = strdup_psram("messaging");
+    }
+    return;
 }
 }
 
 
-const char * messaging_get_type_desc(messaging_types msg_type){
-	switch (msg_type) {
-	CASE_TO_STR(MESSAGING_INFO);
-	CASE_TO_STR(MESSAGING_WARNING);
-	CASE_TO_STR(MESSAGING_ERROR);
-		default:
-			return "Unknown";
-			break;
-	}
+const char* messaging_get_type_desc(messaging_types msg_type) {
+    switch (msg_type) {
+        CASE_TO_STR(MESSAGING_INFO);
+        CASE_TO_STR(MESSAGING_WARNING);
+        CASE_TO_STR(MESSAGING_ERROR);
+    default:
+        return "Unknown";
+        break;
+    }
 }
 }
-const char * messaging_get_class_desc(messaging_classes msg_class){
-	switch (msg_class) {
-	CASE_TO_STR(MESSAGING_CLASS_OTA);
-	CASE_TO_STR(MESSAGING_CLASS_SYSTEM);
-	CASE_TO_STR(MESSAGING_CLASS_STATS);
-	CASE_TO_STR(MESSAGING_CLASS_CFGCMD);
-	CASE_TO_STR(MESSAGING_CLASS_BT);
-		default:
-			return "Unknown";
-			break;
-	}
+const char* messaging_get_class_desc(messaging_classes msg_class) {
+    switch (msg_class) {
+        CASE_TO_STR(MESSAGING_CLASS_OTA);
+        CASE_TO_STR(MESSAGING_CLASS_SYSTEM);
+        CASE_TO_STR(MESSAGING_CLASS_STATS);
+        CASE_TO_STR(MESSAGING_CLASS_CFGCMD);
+        CASE_TO_STR(MESSAGING_CLASS_BT);
+    default:
+        return "Unknown";
+        break;
+    }
 }
 }
 
 
-cJSON *  messaging_retrieve_messages(RingbufHandle_t buf_handle){
-	single_message_t * message=NULL;
-	cJSON * json_messages=cJSON_CreateArray();
-	cJSON * json_message=NULL;
-	size_t item_size;
+cJSON* messaging_retrieve_messages(RingbufHandle_t buf_handle) {
+    single_message_t* message = NULL;
+    cJSON* json_messages = cJSON_CreateArray();
+    cJSON* json_message = NULL;
+    size_t item_size;
     UBaseType_t uxItemsWaiting;
     UBaseType_t uxItemsWaiting;
     vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
     vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
-	for(int i = 0;i<uxItemsWaiting;i++){
-		message = (single_message_t *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(50));
-		//Check received data
-		if (message== NULL) {
-			ESP_LOGE(tag,"received null ptr");
-		}
-		else {
-			json_message = cJSON_CreateObject();
-			cJSON_AddStringToObject(json_message, "message", message->message);
-			cJSON_AddStringToObject(json_message, "type", messaging_get_type_desc(message->type));
-			cJSON_AddStringToObject(json_message, "class", messaging_get_class_desc(message->msg_class));
-			cJSON_AddNumberToObject(json_message,"sent_time",message->sent_time);
-			cJSON_AddNumberToObject(json_message,"current_time",esp_timer_get_time() / 1000);
-			cJSON_AddItemToArray(json_messages,json_message);
-			vRingbufferReturnItem(buf_handle, (void *)message);
-		}
-	}
-	return json_messages;
+    for (int i = 0; i < uxItemsWaiting; i++) {
+        message = (single_message_t*)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(50));
+        // Check received data
+        if (message == NULL) {
+            ESP_LOGE(tag, "received null ptr");
+        } else {
+            json_message = cJSON_CreateObject();
+            cJSON_AddStringToObject(json_message, "message", message->message);
+            cJSON_AddStringToObject(json_message, "type", messaging_get_type_desc(message->type));
+            cJSON_AddStringToObject(
+                json_message, "class", messaging_get_class_desc(message->msg_class));
+            cJSON_AddNumberToObject(json_message, "sent_time", message->sent_time);
+            cJSON_AddNumberToObject(json_message, "current_time", esp_timer_get_time() / 1000);
+            cJSON_AddItemToArray(json_messages, json_message);
+            vRingbufferReturnItem(buf_handle, (void*)message);
+        }
+    }
+    return json_messages;
 }
 }
-single_message_t *  messaging_retrieve_message(RingbufHandle_t buf_handle){
-	single_message_t * message=NULL;
-	single_message_t * message_copy=NULL;
-	size_t item_size;
+single_message_t* messaging_retrieve_message(RingbufHandle_t buf_handle) {
+    single_message_t* message = NULL;
+    single_message_t* message_copy = NULL;
+    size_t item_size;
     UBaseType_t uxItemsWaiting;
     UBaseType_t uxItemsWaiting;
     vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
     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  = clone_obj_psram(message,item_size);
-		vRingbufferReturnItem(buf_handle, (void *)message);
-	}
-	return message_copy;
+    if (uxItemsWaiting > 0) {
+        message = (single_message_t*)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(50));
+        message_copy = clone_obj_psram(message, item_size);
+        vRingbufferReturnItem(buf_handle, (void*)message);
+    }
+    return message_copy;
 }
 }
 
 
-esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_message_t * message, size_t message_size){
-	size_t item_size=0;
-	messaging_list_t * subscriber=get_struct_ptr(subscriber_handle);
-	if(!subscriber->buf_handle){
-		ESP_LOGE(tag,"post failed: null buffer for %s", str_or_unknown(subscriber->subscriber_name));
-		return ESP_FAIL;
-	}
-	void * pItem=NULL;
-	UBaseType_t res=pdFALSE;
-	while(1){
-		ESP_LOGD(tag,"Attempting to reserve %d bytes for %s",message_size, str_or_unknown(subscriber->subscriber_name));
-		res =  xRingbufferSendAcquire(subscriber->buf_handle, &pItem, message_size, pdMS_TO_TICKS(50));
-		if(res == pdTRUE && pItem){
-			ESP_LOGD(tag,"Reserving complete for %s", str_or_unknown(subscriber->subscriber_name));
-			memcpy(pItem,message,message_size);
-			xRingbufferSendComplete(subscriber->buf_handle, pItem);
-			break;
-		}
-		ESP_LOGD(tag,"Dropping for %s",str_or_unknown(subscriber->subscriber_name));
-		single_message_t * dummy = (single_message_t *)xRingbufferReceive(subscriber->buf_handle, &item_size, pdMS_TO_TICKS(50));
-		if (dummy== NULL) {
-			ESP_LOGE(tag,"Dropping message failed");
-			break;
-		}
-		else {
-			ESP_LOGD(tag,"Dropping message of %d bytes for %s",item_size, str_or_unknown(subscriber->subscriber_name));
-			vRingbufferReturnItem(subscriber->buf_handle, (void *)dummy);
-		}
-	}
-	if (res != pdTRUE) {
-		ESP_LOGE(tag,"post to %s failed",str_or_unknown(subscriber->subscriber_name));
-		return ESP_FAIL;
-	}
-	return ESP_OK;
+esp_err_t messaging_post_to_queue(
+    messaging_handle_t subscriber_handle, single_message_t* message, size_t message_size) {
+    size_t item_size = 0;
+    messaging_list_t* subscriber = get_struct_ptr(subscriber_handle);
+    if (!subscriber->buf_handle) {
+        ESP_LOGE(
+            tag, "post failed: null buffer for %s", str_or_unknown(subscriber->subscriber_name));
+        return ESP_FAIL;
+    }
+    void* pItem = NULL;
+    UBaseType_t res = pdFALSE;
+    while (1) {
+        ESP_LOGD(tag, "Attempting to reserve %d bytes for %s", message_size,
+            str_or_unknown(subscriber->subscriber_name));
+        res =
+            xRingbufferSendAcquire(subscriber->buf_handle, &pItem, message_size, pdMS_TO_TICKS(50));
+        if (res == pdTRUE && pItem) {
+            ESP_LOGD(tag, "Reserving complete for %s", str_or_unknown(subscriber->subscriber_name));
+            memcpy(pItem, message, message_size);
+            xRingbufferSendComplete(subscriber->buf_handle, pItem);
+            break;
+        }
+        ESP_LOGD(tag, "Dropping for %s", str_or_unknown(subscriber->subscriber_name));
+        single_message_t* dummy = (single_message_t*)xRingbufferReceive(
+            subscriber->buf_handle, &item_size, pdMS_TO_TICKS(50));
+        if (dummy == NULL) {
+            ESP_LOGE(tag, "Dropping message failed");
+            break;
+        } else {
+            ESP_LOGD(tag, "Dropping message of %d bytes for %s", item_size,
+                str_or_unknown(subscriber->subscriber_name));
+            vRingbufferReturnItem(subscriber->buf_handle, (void*)dummy);
+        }
+    }
+    if (res != pdTRUE) {
+        ESP_LOGE(tag, "post to %s failed", str_or_unknown(subscriber->subscriber_name));
+        return ESP_FAIL;
+    }
+    return ESP_OK;
+}
+esp_err_t messaging_type_to_err_type(messaging_types type) {
+    switch (type) {
+    case MESSAGING_INFO:
+        return ESP_LOG_INFO;
+        break;
+    case MESSAGING_ERROR:
+        return ESP_LOG_ERROR;
+        break;
+    case MESSAGING_WARNING:
+        return ESP_LOG_WARN;
+        break;
+    default:
+        return ESP_LOG_DEBUG;
+        break;
+    }
+    return ESP_LOG_DEBUG;
 }
 }
-	esp_err_t messaging_type_to_err_type(messaging_types type){
-		switch (type) {
-		case MESSAGING_INFO:
-			return ESP_LOG_INFO;
-			break;
-		case MESSAGING_ERROR:
-			return ESP_LOG_ERROR;
-			break;
-		case MESSAGING_WARNING:
-			return ESP_LOG_WARN;
-			break;
-		default:
-			return ESP_LOG_DEBUG;
-			break;
-		}
-		return ESP_LOG_DEBUG;
-	}
-    
-void messaging_post_message(messaging_types type,messaging_classes msg_class, const char *fmt, ...){
+
+void messaging_post_message(
+    messaging_types type, messaging_classes msg_class, const char* fmt, ...) {
     va_list va;
     va_list va;
-	va_start(va, fmt);
+    va_start(va, fmt);
     vmessaging_post_message(type, msg_class, fmt, va);
     vmessaging_post_message(type, msg_class, fmt, va);
     va_end(va);
     va_end(va);
 }
 }
-    
-void vmessaging_post_message(messaging_types type,messaging_classes msg_class, const char *fmt, va_list va){    
-	single_message_t * message=NULL;
-	size_t msg_size=0;
-	size_t ln =0;
-	messaging_list_t * cur=&top;
-	ln = vsnprintf(NULL, 0, fmt, va)+1;
-	msg_size = sizeof(single_message_t)+ln;
-	message = (single_message_t *)malloc_init_external(msg_size);
-	vsprintf(message->message, fmt, va);
-	message->msg_size = msg_size;
-	message->type = type;
-	message->msg_class = msg_class;
-	message->sent_time = esp_timer_get_time() / 1000;
-	if(type==MESSAGING_WARNING) {
-		ESP_LOGW(tag,"%s",message->message);	
-	}
-	else if(type==MESSAGING_ERROR) {
-		ESP_LOGE(tag,"%s",message->message);	
-	}
-	else {
-		ESP_LOGD(tag,"Post: %s",message->message);
-	}
 
 
-	while(cur){
-		messaging_post_to_queue(get_handle_ptr(cur),  message, msg_size);
-		cur = get_struct_ptr(cur->next);
-	}
-	FREE_AND_NULL(message);
-	return;
+void vmessaging_post_message(
+    messaging_types type, messaging_classes msg_class, const char* fmt, va_list va) {
+    single_message_t* message = NULL;
+    size_t msg_size = 0;
+    size_t ln = 0;
+    messaging_list_t* cur = &top;
+    ln = vsnprintf(NULL, 0, fmt, va) + 1;
+    msg_size = sizeof(single_message_t) + ln;
+    message = (single_message_t*)malloc_init_external(msg_size);
+    vsprintf(message->message, fmt, va);
+    message->msg_size = msg_size;
+    message->type = type;
+    message->msg_class = msg_class;
+    message->sent_time = esp_timer_get_time() / 1000;
+    if (type == MESSAGING_WARNING) {
+        ESP_LOGW(tag, "%s", message->message);
+    } else if (type == MESSAGING_ERROR) {
+        ESP_LOGE(tag, "%s", message->message);
+    } else {
+        ESP_LOGD(tag, "Post: %s", message->message);
+    }
 
 
+    while (cur) {
+        messaging_post_to_queue(get_handle_ptr(cur), message, msg_size);
+        cur = get_struct_ptr(cur->next);
+    }
+    FREE_AND_NULL(message);
+    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;
+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_init_external(ln);
-	if(message_txt){
-		vsprintf(message_txt, fmt, va);
-		va_end(va);
-		ESP_LOG_LEVEL_LOCAL(messaging_type_to_err_type(msgtype),tag, "%s",message_txt);
-		messaging_post_message(msgtype, MESSAGING_CLASS_SYSTEM, message_txt );
-		free(message_txt);
-	}
-	else{
-		ESP_LOGE(tag, "Memory allocation failed while sending message");
-	}
+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_init_external(ln);
+    if (message_txt) {
+        vsprintf(message_txt, fmt, va);
+        va_end(va);
+        ESP_LOG_LEVEL_LOCAL(messaging_type_to_err_type(msgtype), tag, "%s", message_txt);
+        messaging_post_message(msgtype, MESSAGING_CLASS_SYSTEM, message_txt);
+        free(message_txt);
+    } else {
+        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_init_external(ln+cmd_len);
-	if(message_txt){
-		strcpy(message_txt,cmdname);
-		strcat(message_txt,"\n");
-		vsprintf((message_txt+cmd_len), fmt, va);
-		va_end(va);
-		ESP_LOG_LEVEL_LOCAL(messaging_type_to_err_type(msgtype),tag, "%s",message_txt);
-		messaging_post_message(msgtype, MESSAGING_CLASS_CFGCMD, message_txt );
-		free(message_txt);
-	}
-	else{
-		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_init_external(ln + cmd_len);
+    if (message_txt) {
+        strcpy(message_txt, cmdname);
+        strcat(message_txt, "\n");
+        vsprintf((message_txt + cmd_len), fmt, va);
+        va_end(va);
+        ESP_LOG_LEVEL_LOCAL(messaging_type_to_err_type(msgtype), tag, "%s", message_txt);
+        messaging_post_message(msgtype, MESSAGING_CLASS_CFGCMD, message_txt);
+        free(message_txt);
+    } else {
+        ESP_LOGE(tag, "Memory allocation failed while sending message");
+    }
 }
 }

+ 7 - 9
components/services/monitor.c

@@ -5,7 +5,7 @@
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    CONDITIONS OF ANY KIND, either express or implied.
    CONDITIONS OF ANY KIND, either express or implied.
 */
 */
-
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <unistd.h>
@@ -20,8 +20,6 @@
 #include "buttons.h"
 #include "buttons.h"
 #include "led.h"
 #include "led.h"
 #include "globdefs.h"
 #include "globdefs.h"
-// #include "Configurator.h"
-// TODO: Add support for the commented code: search for TODO in the code below")
 #include "accessors.h"
 #include "accessors.h"
 #include "messaging.h"
 #include "messaging.h"
 #include "cJSON.h"
 #include "cJSON.h"
@@ -163,7 +161,7 @@ static void jack_handler_default(void *id, button_event_e event, button_press_e
  *
  *
  */
  */
 bool jack_inserted_svc (void) {
 bool jack_inserted_svc (void) {
-	sys_GPIO * jack=NULL;
+	sys_gpio_config * jack=NULL;
 	if(SYS_GPIOS_NAME(jack,jack)){
 	if(SYS_GPIOS_NAME(jack,jack)){
 		return button_is_pressed(jack->pin, NULL);
 		return button_is_pressed(jack->pin, NULL);
 	}
 	}
@@ -184,7 +182,7 @@ static void spkfault_handler_default(void *id, button_event_e event, button_pres
  *
  *
  */
  */
 bool spkfault_svc (void) {
 bool spkfault_svc (void) {
-	sys_GPIO * spkfault=NULL;
+	sys_gpio_config * spkfault=NULL;
 	if(SYS_GPIOS_NAME(spkfault,spkfault)){
 	if(SYS_GPIOS_NAME(spkfault,spkfault)){
 		return button_is_pressed(spkfault->pin, NULL);
 		return button_is_pressed(spkfault->pin, NULL);
 	}
 	}
@@ -209,10 +207,10 @@ static void pseudo_idle(void *arg) {
  */
  */
 void monitor_svc_init(void) {
 void monitor_svc_init(void) {
  	ESP_LOGI(TAG, "Initializing monitoring");
  	ESP_LOGI(TAG, "Initializing monitoring");
-	sys_Services * services = NULL;
-	sys_GPIO * gpio=NULL;
+	sys_services_config * services = NULL;
+	sys_gpio_config * gpio=NULL;
 	if(SYS_GPIOS_NAME(jack,gpio) && gpio->pin>=0){
 	if(SYS_GPIOS_NAME(jack,gpio) && gpio->pin>=0){
-		ESP_LOGI(TAG,"Adding jack (%s) detection GPIO %d", gpio->level ? "high" : "low", gpio->pin);
+		ESP_LOGI(TAG,"Adding jack (%s) detection GPIO %d",  sys_gpio_lvl_name(gpio->level), gpio->pin);
 		button_create(NULL, gpio->pin, gpio->level ? BUTTON_HIGH : BUTTON_LOW, false, 250, jack_handler_default, 0, -1);
 		button_create(NULL, gpio->pin, gpio->level ? BUTTON_HIGH : BUTTON_LOW, false, 250, jack_handler_default, 0, -1);
 	}
 	}
 	if(SYS_GPIOS_NAME(spkfault,gpio) && gpio->pin>=0){
 	if(SYS_GPIOS_NAME(spkfault,gpio) && gpio->pin>=0){
@@ -220,7 +218,7 @@ void monitor_svc_init(void) {
 		button_create(NULL, gpio->pin, gpio->level ? BUTTON_HIGH : BUTTON_LOW, false, 0, spkfault_handler_default, 0, -1);		
 		button_create(NULL, gpio->pin, gpio->level ? BUTTON_HIGH : BUTTON_LOW, false, 0, spkfault_handler_default, 0, -1);		
 	}
 	}
 	// do we want stats
 	// do we want stats
-	monitor_stats = SYS_SERVICES(services) && services->statistics;
+	monitor_stats = sys_services_config(services) && services->statistics;
 
 
 	ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%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_free_size(MALLOC_CAP_INTERNAL),

+ 8 - 2
components/services/monitor.h

@@ -1,4 +1,4 @@
-/* 
+/*
  *  Squeezelite for esp32
  *  Squeezelite for esp32
  *
  *
  *  (c) Philippe G. 2019, philippe_44@outlook.com
  *  (c) Philippe G. 2019, philippe_44@outlook.com
@@ -7,8 +7,11 @@
  *  https://opensource.org/licenses/MIT
  *  https://opensource.org/licenses/MIT
  *
  *
  */
  */
- 
+
 #pragma once
 #pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
 extern void (*pseudo_idle_svc)(uint32_t now);
 extern void (*pseudo_idle_svc)(uint32_t now);
 
 
 extern void (*jack_handler_svc)(bool inserted);
 extern void (*jack_handler_svc)(bool inserted);
@@ -20,3 +23,6 @@ extern bool spkfault_svc(void);
 extern void (*battery_handler_svc)(float value, int cells);
 extern void (*battery_handler_svc)(float value, int cells);
 extern float battery_value_svc(void);
 extern float battery_value_svc(void);
 extern uint16_t battery_level_svc(void);
 extern uint16_t battery_level_svc(void);
+#ifdef __cplusplus
+}
+#endif

+ 281 - 158
components/services/services.c

@@ -5,44 +5,46 @@
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    CONDITIONS OF ANY KIND, either express or implied.
    CONDITIONS OF ANY KIND, either express or implied.
 */
 */
-
-#include <stdio.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/timers.h"
 #include "freertos/timers.h"
-#include "esp_log.h"
-#include "esp_sleep.h"
-#include "driver/rtc_io.h"
-#include "driver/ledc.h"
+#include <stdio.h>
+#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
+#include "Config.h"
+#include "accessors.h"
+#include "battery.h"
+#include "buttons.h"
 #include "driver/i2c.h"
 #include "driver/i2c.h"
+#include "driver/ledc.h"
 #include "driver/rmt.h"
 #include "driver/rmt.h"
-#include "Configurator.h"
+#include "driver/rtc_io.h"
+#include "esp_log.h"
+#include "esp_sleep.h"
+#include "globdefs.h"
 #include "gpio_exp.h"
 #include "gpio_exp.h"
-#include "battery.h"
 #include "led.h"
 #include "led.h"
-#include "monitor.h"
-#include "globdefs.h"
-#include "accessors.h"
 #include "messaging.h"
 #include "messaging.h"
-#include "buttons.h"
+#include "monitor.h"
 #include "services.h"
 #include "services.h"
+#include "tools.h"
 
 
 extern void battery_svc_init(void);
 extern void battery_svc_init(void);
 extern void monitor_svc_init(void);
 extern void monitor_svc_init(void);
 extern void led_svc_init(void);
 extern void led_svc_init(void);
 
 
-int i2c_system_port = I2C_SYSTEM_PORT;
-int i2c_system_speed = 400000;
 int spi_system_host = SPI_SYSTEM_HOST;
 int spi_system_host = SPI_SYSTEM_HOST;
 int spi_system_dc_gpio = -1;
 int spi_system_dc_gpio = -1;
 int rmt_system_base_tx_channel = RMT_CHANNEL_0;
 int rmt_system_base_tx_channel = RMT_CHANNEL_0;
-int rmt_system_base_rx_channel = RMT_CHANNEL_MAX-1;
+int rmt_system_base_rx_channel = RMT_CHANNEL_MAX - 1;
 
 
 pwm_system_t pwm_system = {
 pwm_system_t pwm_system = {
-		.timer = LEDC_TIMER_0,
-		.base_channel = LEDC_CHANNEL_0,
-		.max = (1 << LEDC_TIMER_13_BIT),
+    .timer = LEDC_TIMER_0,
+    .base_channel = LEDC_CHANNEL_0,
+    .max = (1 << LEDC_TIMER_13_BIT),
 };
 };
-static sys_SleepService * sleep_config;
+static sys_sleep_config* sleep_config;
+static EXT_RAM_ATTR uint8_t gpio_exp_count = 0;
+static EXT_RAM_ATTR bool spi_configured = false;
+static EXT_RAM_ATTR bool i2c_configured = false;
 static EXT_RAM_ATTR struct {
 static EXT_RAM_ATTR struct {
     uint64_t wake_gpio, wake_level;
     uint64_t wake_gpio, wake_level;
     uint64_t rtc_gpio, rtc_level;
     uint64_t rtc_gpio, rtc_level;
@@ -55,31 +57,93 @@ static EXT_RAM_ATTR struct {
     uint32_t (*sleeper[10])(void);
     uint32_t (*sleeper[10])(void);
 } sleep_context;
 } sleep_context;
 
 
-static const char *TAG = "services";
+static const char* TAG = "services";
 
 
-void set_gpio_level(sys_GPIO*gpio,const char * name, gpio_mode_t mode){
-    gpio_pad_select_gpio(gpio->pin);
-    gpio_set_direction(gpio->pin, mode);
-    gpio_set_level(gpio->pin, gpio->level);    
-    ESP_LOGI(TAG, "set GPIO %u to %s, level %d", gpio->pin,name,  gpio->level);
-}
-void set_chip_power_gpio(sys_Gpios*gpios) {
-    
-    if(gpios->has_power){
-        gpios->power.level = 1;
-        set_gpio_level(&gpios->power,"vcc", GPIO_MODE_OUTPUT);
+bool are_GPIOExp_equal(const sys_exp_config* exp1, const sys_exp_config* exp2) {
+    if (exp1 == NULL || exp2 == NULL) {
+        return false; // Safeguard against NULL pointers
     }
     }
-    if(gpios->has_GND){
-        gpios->GND.level = 0;
-        set_gpio_level(&gpios->GND,"gnd", GPIO_MODE_OUTPUT);
+
+    // Check if model, address, and base are the same
+    if (exp1->model != exp2->model || exp1->addr != exp2->addr || exp1->base != exp2->base) {
+        return false;
+    }
+
+    // Check if intr structure (pin and level) are the same
+    if (exp1->intr != exp2->intr) {
+        return false;
     }
     }
 
 
+    return true;
+}
+
+bool sys_dev_config_callback(pb_istream_t* istream, pb_ostream_t* ostream, const pb_field_iter_t* field) {
+    ESP_LOGV(TAG, "Decoding/Encoding Devices, tag: %d", field->tag);
+    sys_exp_config** pExp = (sys_exp_config**)field->pData;
+    sys_exp_config* exp = NULL;
+
+    if (istream != NULL && field->tag == sys_dev_config_gpio_exp_tag) {
+        ESP_LOGD(TAG, "Decoding GPIO Expander #%d", gpio_exp_count + 1);
+        sys_exp_config entry = sys_exp_config_init_default;
+        if (!pb_decode(istream, &sys_exp_config_msg, &entry)) {
+            return false;
+        }
+        if (entry.model == sys_exp_models_UNSPECIFIED_EXP) {
+            ESP_LOGD(TAG, "Skipping GPIO Expander model %s", sys_exp_models_name(entry.model));
+            return true;
+        }
+        // Don't add the expander if it was already decoded. This could
+        // happen if both the configuration and the platform configuration
+        // contain the definition.
+        for (int i = 0; i < gpio_exp_count; i++) {
+            if (are_GPIOExp_equal(&(*pExp)[i], &entry)) {
+                ESP_LOGW(TAG, "GPIO Expander entry already exists, skipping addition.");
+                return true; // Skip adding as it already exists
+            }
+        }
+
+        gpio_exp_count++;
+
+        *pExp = heap_caps_realloc(*pExp, sizeof(sys_exp_config) * gpio_exp_count, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+
+        // Assert after realloc to ensure memory allocation was successful
+        assert(*pExp != NULL);
+        exp = (*pExp) + gpio_exp_count - 1; // Simplified pointer arithmetic
+        memcpy(exp, &entry, sizeof(entry));
+        ESP_LOGD(TAG, "GPIO Expander #%d model %s", gpio_exp_count, sys_exp_models_name(entry.model));
+
+    } else if (ostream != NULL && field->tag == sys_dev_config_gpio_exp_tag) {
+        ESP_LOGV(TAG, "Encoding %d GPIO Expanders", gpio_exp_count);
+
+        for (int i = 0; i < gpio_exp_count; i++) {
+            if (!pb_encode_tag_for_field(ostream, field)) {
+                return false;
+            }
+            if (!pb_encode_submessage(ostream, &sys_exp_config_msg, &(*pExp)[i])) {
+                return false;
+            }
+        }
+        ESP_LOGV(TAG, "GPIO Expander encoding completed");
+    }
+
+    return true;
+}
+
+void set_gpio_level(sys_gpio_config* gpio, const char* name, gpio_mode_t mode) {
+    ESP_LOGI(TAG, "set GPIO %u to %s, level %d", gpio->pin, name, gpio->level);
+    if (gpio->pin < 0) {
+        ESP_LOGW(TAG, "Invalid gpio %d for %s", gpio->pin, name);
+        return;
+    }
+    gpio_pad_select_gpio(gpio->pin);
+    gpio_set_direction(gpio->pin, mode);
+    gpio_set_level(gpio->pin, gpio->level);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  *
  *
  */
  */
-static void sleep_gpio_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
+static void sleep_gpio_handler(void* id, button_event_e event, button_press_e mode, bool long_press) {
     if (event == BUTTON_PRESSED) services_sleep_activate(SLEEP_ONGPIO);
     if (event == BUTTON_PRESSED) services_sleep_activate(SLEEP_ONGPIO);
 }
 }
 
 
@@ -96,7 +160,7 @@ static void sleep_timer(uint32_t now) {
     if (!first) first = now;
     if (!first) first = now;
 
 
     // only query callbacks every 30s if we have at least one sleeper
     // only query callbacks every 30s if we have at least one sleeper
-    if (!*sleep_context.sleeper || now < last + 30*1000) return;
+    if (!*sleep_context.sleeper || now < last + 30 * 1000) return;
     last = now;
     last = now;
 
 
     // time to evaluate if we had spurious wake-up
     // time to evaluate if we had spurious wake-up
@@ -151,12 +215,13 @@ static void sleep_battery(float level, int cells) {
  *
  *
  */
  */
 void services_sleep_init(void) {
 void services_sleep_init(void) {
-    ESP_LOGD(TAG,"Initializing sleep services");
-    if(!SYS_SERVICES_SLEEP(sleep_config)){
-     ESP_LOGD(TAG,"No sleep service configured")   ;
+    ESP_LOGD(TAG, "Initializing sleep services");
+    if (!sys_services_config_SLEEP(sleep_config)) {
+        ESP_LOGD(TAG, "No sleep service configured");
+        return;
     }
     }
     // get the wake criteria
     // get the wake criteria
-    for(int i=0;i<sleep_config->wake_count;i++){
+    for (int i = 0; i < sleep_config->wake_count; i++) {
         if (!rtc_gpio_is_valid_gpio(sleep_config->wake[i].pin)) {
         if (!rtc_gpio_is_valid_gpio(sleep_config->wake[i].pin)) {
             ESP_LOGE(TAG, "invalid wake GPIO %d (not in RTC domain)", sleep_config->wake[i].pin);
             ESP_LOGE(TAG, "invalid wake GPIO %d (not in RTC domain)", sleep_config->wake[i].pin);
         } else {
         } else {
@@ -165,7 +230,8 @@ void services_sleep_init(void) {
             sleep_context.wake_level |= sleep_config->wake[i].level << sleep_config->wake[i].pin;
             sleep_context.wake_level |= sleep_config->wake[i].level << sleep_config->wake[i].pin;
         }
         }
     }
     }
-    // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can be done
+    // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can
+    // be done
     if (sleep_context.wake_gpio) {
     if (sleep_context.wake_gpio) {
         ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_context.wake_gpio, sleep_context.wake_level);
         ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_context.wake_gpio, sleep_context.wake_level);
     }
     }
@@ -177,7 +243,7 @@ void services_sleep_init(void) {
         battery_handler_svc = sleep_battery;
         battery_handler_svc = sleep_battery;
         ESP_LOGI(TAG, "Sleep on battery level of %.2f", sleep_context.battery_level);
         ESP_LOGI(TAG, "Sleep on battery level of %.2f", sleep_context.battery_level);
     }
     }
-    for(int i = 0;i<sleep_config->rtc_count;i++){
+    for (int i = 0; i < sleep_config->rtc_count; i++) {
         if (!rtc_gpio_is_valid_gpio(sleep_config->rtc[i].pin)) {
         if (!rtc_gpio_is_valid_gpio(sleep_config->rtc[i].pin)) {
             ESP_LOGE(TAG, "invalid rtc GPIO %d", sleep_config->rtc[i].pin);
             ESP_LOGE(TAG, "invalid rtc GPIO %d", sleep_config->rtc[i].pin);
         } else {
         } else {
@@ -185,20 +251,21 @@ void services_sleep_init(void) {
             sleep_context.rtc_level |= sleep_config->rtc[i].level << sleep_config->rtc[i].pin;
             sleep_context.rtc_level |= sleep_config->rtc[i].level << sleep_config->rtc[i].pin;
         }
         }
     }
     }
-    // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can be done
+    // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can
+    // be done
     if (sleep_context.rtc_gpio) {
     if (sleep_context.rtc_gpio) {
         ESP_LOGI(TAG, "RTC forced gpio bitmap 0x%llx (active 0x%llx)", sleep_context.rtc_gpio, sleep_context.rtc_level);
         ESP_LOGI(TAG, "RTC forced gpio bitmap 0x%llx (active 0x%llx)", sleep_context.rtc_gpio, sleep_context.rtc_level);
     }
     }
 
 
     // get the GPIOs that activate sleep (we could check that we have a valid wake)
     // get the GPIOs that activate sleep (we could check that we have a valid wake)
-    if(sleep_config->has_sleep && sleep_config->sleep.pin >=0 ){
-		ESP_LOGI(TAG, "Sleep activation gpio %d (active %d)", sleep_config->sleep.pin, sleep_config->sleep.level);
+    if (sleep_config->has_sleep && sleep_config->sleep.pin >= 0) {
+        ESP_LOGI(TAG, "Sleep activation gpio %d (active %d)", sleep_config->sleep.pin, sleep_config->sleep.level);
         button_create(NULL, sleep_config->sleep.pin, sleep_config->sleep.level ? BUTTON_HIGH : BUTTON_LOW, true, 0, sleep_gpio_handler, 0, -1);
         button_create(NULL, sleep_config->sleep.pin, sleep_config->sleep.level ? BUTTON_HIGH : BUTTON_LOW, true, 0, sleep_gpio_handler, 0, -1);
     }
     }
 
 
     // do we want delay sleep
     // do we want delay sleep
-    
-    sleep_context.delay = sleep_config->delay*60*1000;
+
+    sleep_context.delay = sleep_config->delay * 60 * 1000;
 
 
     // now check why we woke-up
     // now check why we woke-up
     esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
     esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
@@ -207,17 +274,19 @@ void services_sleep_init(void) {
 
 
         // find the type of wake-up
         // find the type of wake-up
         uint64_t wake_gpio;
         uint64_t wake_gpio;
-        if (cause == ESP_SLEEP_WAKEUP_EXT0) wake_gpio = sleep_context.wake_gpio;
-        else wake_gpio = esp_sleep_get_ext1_wakeup_status();
+        if (cause == ESP_SLEEP_WAKEUP_EXT0)
+            wake_gpio = sleep_context.wake_gpio;
+        else
+            wake_gpio = esp_sleep_get_ext1_wakeup_status();
 
 
         // we might be woken up by infrared in which case we want a short sleep
         // we might be woken up by infrared in which case we want a short sleep
         if (infrared_gpio() >= 0 && ((1LL << infrared_gpio()) & wake_gpio)) {
         if (infrared_gpio() >= 0 && ((1LL << infrared_gpio()) & wake_gpio)) {
             sleep_context.spurious = 1;
             sleep_context.spurious = 1;
-            if(sleep_config->spurious>0){
+            if (sleep_config->spurious > 0) {
                 sleep_context.spurious = sleep_config->spurious;
                 sleep_context.spurious = sleep_config->spurious;
             }
             }
-            sleep_context.spurious *= 60*1000;
-            
+            sleep_context.spurious *= 60 * 1000;
+
             ESP_LOGI(TAG, "spurious wake-up detection during %d sec", sleep_context.spurious / 1000);
             ESP_LOGI(TAG, "spurious wake-up detection during %d sec", sleep_context.spurious / 1000);
         }
         }
     }
     }
@@ -226,7 +295,7 @@ void services_sleep_init(void) {
     if (sleep_context.delay || sleep_context.spurious) {
     if (sleep_context.delay || sleep_context.spurious) {
         sleep_context.idle_chain = pseudo_idle_svc;
         sleep_context.idle_chain = pseudo_idle_svc;
         pseudo_idle_svc = sleep_timer;
         pseudo_idle_svc = sleep_timer;
-        if (sleep_context.delay) ESP_LOGI(TAG, "inactivity timer of %d minute(s)", sleep_context.delay / (60*1000));
+        if (sleep_context.delay) ESP_LOGI(TAG, "inactivity timer of %d minute(s)", sleep_context.delay / (60 * 1000));
     }
     }
 }
 }
 
 
@@ -235,7 +304,8 @@ void services_sleep_init(void) {
  */
  */
 void services_sleep_activate(sleep_cause_e cause) {
 void services_sleep_activate(sleep_cause_e cause) {
     // call all sleep hooks that might want to do something
     // call all sleep hooks that might want to do something
-    for (void (**suspend)(void) = sleep_context.suspend; *suspend; suspend++) (*suspend)();
+    for (void (**suspend)(void) = sleep_context.suspend; *suspend; suspend++)
+        (*suspend)();
 
 
     // isolate all possible GPIOs, except the wake-up and RTC-maintaines ones
     // isolate all possible GPIOs, except the wake-up and RTC-maintaines ones
     esp_sleep_config_gpio_isolate();
     esp_sleep_config_gpio_isolate();
@@ -249,9 +319,11 @@ void services_sleep_activate(sleep_cause_e cause) {
 
 
         // do we need to maintain a pull-up or down of that GPIO
         // do we need to maintain a pull-up or down of that GPIO
         if ((1LL << i) & sleep_context.rtc_gpio) {
         if ((1LL << i) & sleep_context.rtc_gpio) {
-            if ((sleep_context.rtc_level >> i) & 0x01) rtc_gpio_pullup_en(i);
-            else rtc_gpio_pulldown_en(i);
-        // or is this not wake-up GPIO, just isolate it
+            if ((sleep_context.rtc_level >> i) & 0x01)
+                rtc_gpio_pullup_en(i);
+            else
+                rtc_gpio_pulldown_en(i);
+            // or is this not wake-up GPIO, just isolate it
         } else if (!((1LL << i) & sleep_context.wake_gpio)) {
         } else if (!((1LL << i) & sleep_context.wake_gpio)) {
             rtc_gpio_isolate(i);
             rtc_gpio_isolate(i);
         }
         }
@@ -261,10 +333,11 @@ void services_sleep_activate(sleep_cause_e cause) {
     if (sleep_context.wake_gpio & (sleep_context.wake_gpio - 1)) {
     if (sleep_context.wake_gpio & (sleep_context.wake_gpio - 1)) {
         ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_context.wake_gpio);
         ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_context.wake_gpio);
 #if defined(CONFIG_IDF_TARGET_ESP32S3) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
 #if defined(CONFIG_IDF_TARGET_ESP32S3) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
-        if (!sleep_context.wake_level) esp_sleep_enable_ext1_wakeup(sleep_context.wake_gpio, ESP_EXT1_WAKEUP_ANY_LOW);
+        if (!sleep_context.wake_level)
+            esp_sleep_enable_ext1_wakeup(sleep_context.wake_gpio, ESP_EXT1_WAKEUP_ANY_LOW);
         else
         else
 #endif
 #endif
-        esp_sleep_enable_ext1_wakeup(sleep_context.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH);
+            esp_sleep_enable_ext1_wakeup(sleep_context.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH);
     } else if (sleep_context.wake_gpio) {
     } else if (sleep_context.wake_gpio) {
         int gpio = __builtin_ctzll(sleep_context.wake_gpio);
         int gpio = __builtin_ctzll(sleep_context.wake_gpio);
         int level = (sleep_context.wake_level >> gpio) & 0x01;
         int level = (sleep_context.wake_level >> gpio) & 0x01;
@@ -274,136 +347,186 @@ void services_sleep_activate(sleep_cause_e cause) {
         ESP_LOGW(TAG, "going to sleep cause %d, no wake-up option", cause);
         ESP_LOGW(TAG, "going to sleep cause %d, no wake-up option", cause);
     }
     }
 
 
-    // we need to use a timer in case the same button is used for sleep and wake-up and it's "pressed" vs "released" selected
-    if (cause == SLEEP_ONKEY) xTimerStart(xTimerCreate("sleepTimer", pdMS_TO_TICKS(1000), pdFALSE, NULL, (void (*)(void*)) esp_deep_sleep_start), 0);
-    else esp_deep_sleep_start();
+    // we need to use a timer in case the same button is used for sleep and wake-up and it's
+    // "pressed" vs "released" selected
+    if (cause == SLEEP_ONKEY)
+        xTimerStart(xTimerCreate("sleepTimer", pdMS_TO_TICKS(1000), pdFALSE, NULL, (void (*)(void*))esp_deep_sleep_start), 0);
+    else
+        esp_deep_sleep_start();
 }
 }
 
 
-
 /****************************************************************************************
 /****************************************************************************************
  *
  *
  */
  */
-static void register_method(void **store, size_t size, void *method) {
-    for (int i = 0; i < size; i++, *store++) if (!*store) {
-        *store = method;
-        return;
-    }
+static void register_method(void** store, size_t size, void* method) {
+    for (int i = 0; i < size; i++, *store++)
+        if (!*store) {
+            *store = method;
+            return;
+        }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  *
  *
  */
  */
 void services_sleep_setsuspend(void (*hook)(void)) {
 void services_sleep_setsuspend(void (*hook)(void)) {
-    register_method((void**) sleep_context.suspend, sizeof(sleep_context.suspend)/sizeof(*sleep_context.suspend), (void*) hook);
+    register_method((void**)sleep_context.suspend, sizeof(sleep_context.suspend) / sizeof(*sleep_context.suspend), (void*)hook);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  *
  *
  */
  */
 void services_sleep_setsleeper(uint32_t (*sleeper)(void)) {
 void services_sleep_setsleeper(uint32_t (*sleeper)(void)) {
-    register_method((void**) sleep_context.sleeper, sizeof(sleep_context.sleeper)/sizeof(*sleep_context.sleeper), (void*) sleeper);
+    register_method((void**)sleep_context.sleeper, sizeof(sleep_context.sleeper) / sizeof(*sleep_context.sleeper), (void*)sleeper);
 }
 }
+void services_ports_init(void) {
+    esp_err_t err = ESP_OK;
+
+    ESP_LOGI(TAG, "Initializing ports");
+    gpio_install_isr_service(0);
+    ESP_LOGD(TAG, "Checking i2c port usage");
+    if (platform->dev.dac.has_i2c && platform->dev.has_i2c && platform->dev.dac.i2c.port !=  sys_i2c_port_UNSPECIFIED &&
+        platform->dev.dac.i2c.port == platform->dev.i2c.port) {
+        ESP_LOGE(TAG, "Port %s is used for internal DAC use. Switching to ", sys_i2c_port_name(platform->dev.dac.i2c.port));
+        platform->dev.i2c.port = platform->dev.i2c.port == sys_i2c_port_PORT0 ? sys_i2c_port_PORT1 : sys_i2c_port_PORT0;
+        config_raise_changed(false);
+    }
 
 
-/****************************************************************************************
- *
- */
-void services_init(void) {
-	messaging_service_init();
-	gpio_install_isr_service(0);
-    // todo: untangle i2c stuff
-#ifdef CONFIG_I2C_LOCKED
-	if (i2c_system_port == 0) {
-		i2c_system_port = 1;
-		ESP_LOGE(TAG, "Port 0 is reserved for internal DAC use");
-	}
-#endif
+    // shared I2C bus
+    ESP_LOGD(TAG, "Configuring I2C");
+    const i2c_config_t* i2c_config = config_i2c_get(&platform->dev.i2c);
+    ESP_LOGD(TAG, "Stored I2C configuration [sda:%d scl:%d port:%s speed:%u]", i2c_config->sda_io_num, i2c_config->scl_io_num,
+        sys_i2c_port_name(platform->dev.i2c.port), i2c_config->master.clk_speed);
+    if (i2c_config->sda_io_num != -1 && i2c_config->scl_io_num != -1) {
+        ESP_LOGI(TAG, "Configuring I2C sda:%d scl:%d port:%s speed:%u", i2c_config->sda_io_num, i2c_config->scl_io_num,
+            sys_i2c_port_name(platform->dev.i2c.port), i2c_config->master.clk_speed);
+        i2c_param_config(platform->dev.i2c.port - sys_i2c_port_PORT0, i2c_config);
+        if ((err = i2c_driver_install(platform->dev.i2c.port - sys_i2c_port_PORT0, i2c_config->mode, 0, 0, 0)) != ESP_OK) {
+            ESP_LOGE(TAG, "Error setting up i2c: %s", esp_err_to_name(err));
+        } else {
+            i2c_configured = true;
+        }
+    } else {
+        if (platform->dev.has_display && platform->dev.display.has_common && platform->dev.display.which_dispType == sys_display_config_i2c_tag) {
+            ESP_LOGE(TAG, "I2C configuration missing for display %s", sys_display_drivers_name(platform->dev.display.common.driver));
+        } else {
+            ESP_LOGI(TAG, "Shared I2C not configured");
+        }
+    }
 
 
-	// set potential power GPIO on chip first in case expanders are power using these
-	set_chip_power_gpio(&platform->gpios);
-
-	// shared I2C bus
-	const i2c_config_t * i2c_config = config_i2c_get(&i2c_system_port);
-	ESP_LOGI(TAG,"Configuring I2C sda:%d scl:%d port:%u speed:%u", i2c_config->sda_io_num, i2c_config->scl_io_num, i2c_system_port, i2c_config->master.clk_speed);
-
-	if (i2c_config->sda_io_num != -1 && i2c_config->scl_io_num != -1) {
-		i2c_param_config(i2c_system_port, i2c_config);
-		i2c_driver_install(i2c_system_port, i2c_config->mode, 0, 0, 0 );
-	} else {
-		i2c_system_port = -1;
-		ESP_LOGW(TAG, "no I2C configured");
-	}
-
-	const spi_bus_config_t * spi_config = config_spi_get((spi_host_device_t*) &spi_system_host);
-	ESP_LOGI(TAG,"Configuring SPI mosi:%d miso:%d clk:%d host:%u dc:%d", spi_config->mosi_io_num, spi_config->miso_io_num, spi_config->sclk_io_num, spi_system_host, spi_system_dc_gpio);
-
-	if (spi_config->mosi_io_num != -1 && spi_config->sclk_io_num != -1) {
-		spi_bus_initialize( spi_system_host, spi_config, SPI_DMA_CH_AUTO );
-		if (spi_system_dc_gpio != -1) {
-			gpio_reset_pin(spi_system_dc_gpio);
-			gpio_set_direction( spi_system_dc_gpio, GPIO_MODE_OUTPUT );
-			gpio_set_level( spi_system_dc_gpio, 0 );
-		} else {
-			ESP_LOGW(TAG, "No DC GPIO set, SPI display will not work");
-		}
-	} else {
-		spi_system_host = -1;
-		ESP_LOGW(TAG, "no SPI configured");
-	}
-
-	// create GPIO expanders
-	gpio_exp_config_t gpio_exp_config;
-    if(platform->has_dev && platform->dev.gpio_exp_count>0){
-        for(int count = 0;count<platform->dev.gpio_exp_count;count++){
-            sys_GPIOExp * exp = &platform->dev.gpio_exp[count];
-            gpio_exp_config.base =  exp->base;
+    const spi_bus_config_t* spi_config = config_spi_get((spi_host_device_t*)&spi_system_host);
+    ESP_LOGD(TAG, "Stored SPI configuration[mosi:%d miso:%d clk:%d host:%u dc:%d]", spi_config->mosi_io_num, spi_config->miso_io_num,
+        spi_config->sclk_io_num, spi_system_host, spi_system_dc_gpio);
+    if (spi_config->mosi_io_num != -1 && spi_config->sclk_io_num != -1) {
+        ESP_LOGI(TAG, "Configuring SPI mosi:%d miso:%d clk:%d host:%u dc:%d", spi_config->mosi_io_num, spi_config->miso_io_num,
+            spi_config->sclk_io_num, spi_system_host, spi_system_dc_gpio);
+        if ((err = spi_bus_initialize(spi_system_host, spi_config, SPI_DMA_CH_AUTO)) != ESP_OK) {
+            ESP_LOGE(TAG, "Error setting up SPI bus: %s", esp_err_to_name(err));
+        } else {
+            spi_configured = true;
+        }
+        if (spi_system_dc_gpio != -1) {
+            gpio_reset_pin(spi_system_dc_gpio);
+            gpio_set_direction(spi_system_dc_gpio, GPIO_MODE_OUTPUT);
+            gpio_set_level(spi_system_dc_gpio, 0);
+        } else {
+            ESP_LOGW(TAG, "No DC GPIO set, SPI display will not work");
+        }
+    } else {
+        spi_system_host = -1;
+        if (platform->dev.has_display && platform->dev.display.has_common &&
+            platform->dev.display.common.driver != sys_display_drivers_UNSPECIFIED &&
+            platform->dev.display.which_dispType == sys_display_config_spi_tag) {
+            ESP_LOGE(TAG, "SPI bus configuration missing for display %s", sys_display_drivers_name(platform->dev.display.common.driver));
+        } else {
+            ESP_LOGI(TAG, "SPI bus not configured");
+        }
+    }
+}
+void services_gpio_init(void) {
+    ESP_LOGI(TAG, "Initializing GPIOs");
+    // set potential power GPIO on chip first in case expanders are power using these
+    sys_gpio_config* gpio = NULL;
+    if (SYS_GPIOS_NAME(power, gpio) && gpio->pin >= 0) {
+        ESP_LOGD(TAG, "Handling power gpio");
+        gpio->level = sys_gpio_lvl_HIGH;
+        set_gpio_level(gpio, "power", GPIO_MODE_OUTPUT);
+    } else {
+        ESP_LOGD(TAG, "No power GPIO defined");
+    }
+
+    if (SYS_GPIOS_NAME(GND, gpio) && gpio->pin >= 0) {
+        ESP_LOGD(TAG, "Handling GND gpio");
+        gpio->level = sys_gpio_lvl_LOW;
+        set_gpio_level(gpio, "GND", GPIO_MODE_OUTPUT);
+    } else {
+        ESP_LOGD(TAG, "No GND gpio defined");
+    }
+
+    // create GPIO expanders
+    gpio_exp_config_t gpio_exp_config;
+    if (platform->has_dev && gpio_exp_count > 0 && platform->dev.gpio_exp[0].model != sys_exp_models_UNSPECIFIED_EXP) {
+        ESP_LOGI(TAG, "Initializing %d GPIO Expander(s)", gpio_exp_count);
+        for (int count = 0; count < gpio_exp_count; count++) {
+            sys_exp_config* exp = &platform->dev.gpio_exp[count];
+            if (exp->model == sys_exp_models_UNSPECIFIED_EXP) {
+                ESP_LOGD(TAG, "Skipping unknown model");
+                continue;
+            }
+            gpio_exp_config.phy.ena_pin = -1;
+            gpio_exp_config.base = exp->base;
             gpio_exp_config.count = exp->count;
             gpio_exp_config.count = exp->count;
             gpio_exp_config.phy.addr = exp->addr;
             gpio_exp_config.phy.addr = exp->addr;
-            if(exp->has_intr){
-                gpio_exp_config.intr = exp->intr.pin;
+            gpio_exp_config.intr = exp->intr;
+            if (exp->has_ena && exp->ena.pin >= 0) {
+                gpio_exp_config.phy.ena_pin = exp->ena.pin;
+                gpio_exp_config.phy.ena_lvl = exp->ena.level;
             }
             }
-            else {
-                ESP_LOGW(TAG,"Expander doesn't have intr pin");
+            if (exp->which_ExpType == sys_exp_config_spi_tag) {
+                if (!spi_configured) {
+                    ESP_LOGE(TAG, "SPI bus not configured for GPIO Expander index %d (%s)", count, sys_exp_models_name(exp->model));
+                    continue;
+                }
+                gpio_exp_config.phy.cs_pin = exp->ExpType.spi.cs;
+                gpio_exp_config.phy.host =
+                    (!platform->dev.has_spi || (platform->dev.has_spi  &&  platform->dev.spi.host == sys_dev_common_hosts_NONE) ? sys_dev_common_hosts_Host0 : platform->dev.spi.host) - sys_dev_common_hosts_Host0;
+                gpio_exp_config.phy.speed = exp->ExpType.spi.speed > 0 ? exp->ExpType.spi.speed : 0;
+            } else {
+                if (!i2c_configured) {
+                    ESP_LOGE(TAG, "I2C bus not configured for GPIO Expander index %d (%s)", count, sys_exp_models_name(exp->model));
+                    continue;
+                }
+                gpio_exp_config.phy.port =
+                   ((!platform->dev.has_i2c || (platform->dev.has_i2c && platform->dev.i2c.port == sys_i2c_port_UNSPECIFIED) )? sys_dev_common_ports_SYSTEM : platform->dev.i2c.port) - sys_dev_common_ports_SYSTEM ;
             }
             }
-            if(exp->which_ExpType == sys_GPIOExp_spi_tag){
-                gpio_exp_config.phy.cs_pin= exp->ExpType.spi.cs.pin;
-                gpio_exp_config.phy.host = exp->ExpType.spi.host == sys_HostEnum_UNSPECIFIED_HOST ?sys_HostEnum_Host0:exp->ExpType.spi.host -1;
-                gpio_exp_config.phy.speed = exp->ExpType.spi.speed>0?exp->ExpType.spi.speed:0;
-            }
-            else {
-                gpio_exp_config.phy.port = exp->ExpType.i2c.port == sys_PortEnum_UNSPECIFIED_SYSTPORT?sys_PortEnum_SYSTEM:exp->ExpType.i2c.port -1;
-            }
-            strncpy(gpio_exp_config.model,sys_GPIOExpModelEnum_name(exp->model),sizeof(gpio_exp_config.model)-1);
+            strncpy(gpio_exp_config.model, sys_exp_models_name(exp->model), sizeof(gpio_exp_config.model) - 1);
             gpio_exp_create(&gpio_exp_config);
             gpio_exp_create(&gpio_exp_config);
         }
         }
-	}
-    if(platform->has_gpios ){
-        // if(platform->gpios.has_GND){
-        //     platform->gpios.GND.level = 0;
-        //     set_gpio_level(&platform->gpios.GND,"GND", GPIO_MODE_OUTPUT);
-        // }
-        // if(platform->gpios.has_Vcc){
-        //     platform->gpios.Vcc.level = 1;
-        //     set_gpio_level(&platform->gpios.Vcc,"VCC", GPIO_MODE_OUTPUT);
-        // }
-        set_chip_power_gpio(&platform->gpios);
+    } else if (gpio_exp_count > 0) {
+        ESP_LOGW(TAG, "GPIO Expander count %d but none is valid", gpio_exp_count);
     }
     }
-    
+}
+/****************************************************************************************
+ *
+ */
+void services_init(void) {
+    ESP_LOGI(TAG, "Initializing services");
+    esp_err_t err = ESP_OK;
 
 
-	// system-wide PWM timer configuration
-	ledc_timer_config_t pwm_timer = {
-		.duty_resolution = LEDC_TIMER_13_BIT,
-		.freq_hz = 5000,
+    // system-wide PWM timer configuration
+    ledc_timer_config_t pwm_timer = {
+        .duty_resolution = LEDC_TIMER_13_BIT,
+        .freq_hz = 5000,
 #ifdef CONFIG_IDF_TARGET_ESP32S3
 #ifdef CONFIG_IDF_TARGET_ESP32S3
         .speed_mode = LEDC_LOW_SPEED_MODE,
         .speed_mode = LEDC_LOW_SPEED_MODE,
 #else
 #else
-		.speed_mode = LEDC_HIGH_SPEED_MODE,
+        .speed_mode = LEDC_HIGH_SPEED_MODE,
 #endif
 #endif
-		.timer_num = pwm_system.timer,
-	};
-
-	ledc_timer_config(&pwm_timer);
+        .timer_num = pwm_system.timer,
+    };
 
 
-	led_svc_init();
-	battery_svc_init();
-	monitor_svc_init();
+    ledc_timer_config(&pwm_timer);
+    led_svc_init();
+    battery_svc_init();
+    monitor_svc_init();
 }
 }

+ 2 - 2
components/services/services.h

@@ -7,7 +7,7 @@
  *  https://opensource.org/licenses/MIT
  *  https://opensource.org/licenses/MIT
  *
  *
  */
  */
-#include "Configurator.h" 
+#include "Config.h" 
 #include "driver/gpio.h"
 #include "driver/gpio.h"
 
 
 #pragma once
 #pragma once
@@ -17,4 +17,4 @@ void services_sleep_activate(sleep_cause_e cause);
 void services_sleep_setsuspend(void (*hook)(void));
 void services_sleep_setsuspend(void (*hook)(void));
 void services_sleep_setsleeper(uint32_t (*sleeper)(void));
 void services_sleep_setsleeper(uint32_t (*sleeper)(void));
 void services_sleep_init(void);
 void services_sleep_init(void);
-void set_gpio_level(sys_GPIO*gpio,const char * name, gpio_mode_t mode);
+void set_gpio_level(sys_gpio_config*gpio,const char * name, gpio_mode_t mode);

+ 44 - 0
components/services/system_time.c

@@ -0,0 +1,44 @@
+/* LwIP SNTP example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include "Config.h"
+#include "esp_attr.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#include "esp_sleep.h"
+#include "esp_sntp.h"
+#include "esp_system.h"
+#include "tools.h"
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+static const char* TAG = "system_time";
+
+void system_time_init(void) {
+    char strftime_buf[64];
+    time_t now;
+    struct tm timeinfo;
+    const char* timezone =
+        platform->has_services && platform->services.timezone && strlen(platform->services.timezone) > 0 ? platform->services.timezone : "EST5EDT,M3.2.0/2,M11.1.0";
+    setenv("TZ", timezone, 1);
+    tzset();
+    time(&now);
+    localtime_r(&now, &timeinfo);
+    // Is time set? If not, tm_year will be (1970 - 1900).
+    if (timeinfo.tm_year < (2016 - 1900)) {
+        ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
+        sntp_servermode_dhcp(2);
+        esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
+        esp_sntp_setservername(0, "pool.ntp.org");
+        esp_sntp_init();
+
+    } else {
+        strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
+        ESP_LOGI(TAG, "Current date/time is: %s for time zone %s", strftime_buf, timezone);
+    }
+}

+ 2 - 0
components/services/system_time.h

@@ -0,0 +1,2 @@
+#pragma once;
+void system_time_init(void);

+ 8 - 7
components/spotify/Shim.cpp

@@ -10,7 +10,6 @@
 #include <PlainConnection.h>
 #include <PlainConnection.h>
 #include <memory>
 #include <memory>
 #include <vector>
 #include <vector>
-#include <iostream>
 #include <inttypes.h>
 #include <inttypes.h>
 #include <fstream>
 #include <fstream>
 #include <stdarg.h>
 #include <stdarg.h>
@@ -29,9 +28,11 @@
 #include "esp_http_server.h"
 #include "esp_http_server.h"
 #include "cspot_private.h"
 #include "cspot_private.h"
 #include "cspot_sink.h"
 #include "cspot_sink.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "tools.h"
 #include "tools.h"
 #include "accessors.h"
 #include "accessors.h"
+#include "tools_http_utils.h"
+
 static class cspotPlayer *player;
 static class cspotPlayer *player;
 
 
 static const struct {
 static const struct {
@@ -69,7 +70,7 @@ private:
     void enableZeroConf(void);
     void enableZeroConf(void);
 
 
     void runTask();
     void runTask();
-    sys_Spotify * cspot_config = NULL;
+    sys_spotify_config * cspot_config = NULL;
 
 
 public:
 public:
     typedef enum {TRACK_INIT, TRACK_NOTIFY, TRACK_STREAM, TRACK_END} TrackStatus;
     typedef enum {TRACK_INIT, TRACK_NOTIFY, TRACK_STREAM, TRACK_END} TrackStatus;
@@ -86,7 +87,7 @@ cspotPlayer::cspotPlayer(const char* name, httpd_handle_t server, int port, cspo
                         serverHandle(server), serverPort(port),
                         serverHandle(server), serverPort(port),
                         cmdHandler(cmdHandler), dataHandler(dataHandler) {
                         cmdHandler(cmdHandler), dataHandler(dataHandler) {
 
 
-    if(!SYS_SERVICES_SPOTIFY(cspot_config)){
+    if(!sys_services_config_SPOTIFY(cspot_config)){
         return;        
         return;        
     }
     }
     volume = cspot_config->volume;
     volume = cspot_config->volume;
@@ -360,8 +361,8 @@ void cspotPlayer::runTask() {
             // we might have been forced to use zeroConf, so store credentials and reset zeroConf usage
             // we might have been forced to use zeroConf, so store credentials and reset zeroConf usage
             if (!zeroConf) {
             if (!zeroConf) {
                 useZeroConf = false;
                 useZeroConf = false;
-                if(configurator_set_string(&sys_State_msg,sys_State_cspot_credentials_tag,sys_state,credentials.c_str())){
-                    configurator.RaiseStateModified();
+                if(system_set_string(&sys_state_data_msg,sys_state_data_cspot_credentials_tag,sys_state,credentials.c_str())){
+                    config_raise_state_changed();
                 }
                 }
             }
             }
 
 
@@ -416,7 +417,7 @@ void cspotPlayer::runTask() {
                 if (state == DISCO) {
                 if (state == DISCO) {
                     // update volume then
                     // update volume then
                     cspot_config->volume = volume;
                     cspot_config->volume = volume;
-                    configurator_raise_changed();
+                    config_raise_changed(false);
                     // in ZeroConf mod, stay connected (in this loop)
                     // in ZeroConf mod, stay connected (in this loop)
                     if (!zeroConf) state = LINKED;
                     if (!zeroConf) state = LINKED;
                 }
                 }

+ 3 - 3
components/spotify/cspot/bell/external/nanopb/generator/nanopb_generator.py

@@ -1754,6 +1754,9 @@ class ProtoFile:
             for extension in self.extensions:
             for extension in self.extensions:
                 yield extension.extension_decl()
                 yield extension.extension_decl()
             yield '\n'
             yield '\n'
+        yield '#ifdef __cplusplus\n'
+        yield 'extern "C" {\n'
+        yield '#endif\n\n'
 
 
         if self.enums:
         if self.enums:
                 yield '/* Helper constants for enums */\n'
                 yield '/* Helper constants for enums */\n'
@@ -1761,9 +1764,6 @@ class ProtoFile:
                     yield enum.auxiliary_defines() + '\n'
                     yield enum.auxiliary_defines() + '\n'
                 yield '\n'
                 yield '\n'
 
 
-        yield '#ifdef __cplusplus\n'
-        yield 'extern "C" {\n'
-        yield '#endif\n\n'
 
 
         if self.messages:
         if self.messages:
             yield '/* Initializer values for message structs */\n'
             yield '/* Initializer values for message structs */\n'

+ 32 - 20
components/spotify/cspot/bell/external/nanopb/pb_decode.c

@@ -17,6 +17,24 @@
 #include "pb_decode.h"
 #include "pb_decode.h"
 #include "pb_common.h"
 #include "pb_common.h"
 #include <stdio.h>
 #include <stdio.h>
+#include <inttypes.h> // Include this header for PRIu64
+
+
+// Macros for debugging encode/decode
+// #define DUMP_PB_ENABLE 1
+#ifdef DUMP_PB_ENABLE
+#define DUMP_PB_FIELD(iter,msg) dump_pb_field(iter,msg, __FUNCTION__, __LINE__)
+#define DUMP_PB_HEAD(iter,msg) dump_pb_head(iter,msg, __FUNCTION__, __LINE__)
+#define DUMP_PB_MARK(msg) printf("MRK\t%-30s\t%-20s\t%-5d\n",msg, __FUNCTION__, __LINE__)
+#define WRITE_DEBUG_HEADER() write_debug_header()
+#else
+#define DUMP_PB_FIELD(iter,msg)
+#define DUMP_PB_HEAD(iter,msg) 
+#define DUMP_PB_MARK(msg) 
+#define WRITE_DEBUG_HEADER()
+#endif
+
+
 /**************************************
 /**************************************
  * Declarations internal to this file *
  * Declarations internal to this file *
  **************************************/
  **************************************/
@@ -122,13 +140,13 @@ void dump_pb_field(const pb_field_iter_t* iter, const char* msg, const char* fun
            "| %-20s"
            "| %-20s"
            "| %-20s"
            "| %-20s"
            "| %-20s"
            "| %-20s"
-           "| %-10zu|\n",
+           "| %-10zu| %-20zu|\n",
         "FLD", msg, func, line, 
         "FLD", msg, func, line, 
         "","","",
         "","","",
         iter->submessage_index,iter->index, iter->field_info_index, iter->required_field_index, 
         iter->submessage_index,iter->index, iter->field_info_index, iter->required_field_index, 
         iter->tag, iter->data_size, iter->array_size, 
         iter->tag, iter->data_size, iter->array_size, 
         pb_ltype_description(iter->type), pb_htype_description(iter->type), pb_atype_description(iter->type), 
         pb_ltype_description(iter->type), pb_htype_description(iter->type), pb_atype_description(iter->type), 
-        iter->type);
+        iter->type,PB_LTYPE(iter->type)==PB_LTYPE_STRING && iter->pData && strlen(iter->pData)>0?iter->pData:"");
 }
 }
 void dump_pb_head(const pb_msgdesc_t* desc, const char* msg, const char* func, int line) {
 void dump_pb_head(const pb_msgdesc_t* desc, const char* msg, const char* func, int line) {
     if (!desc) {
     if (!desc) {
@@ -160,18 +178,7 @@ void dump_pb_head(const pb_msgdesc_t* desc, const char* msg, const char* func, i
         "");
         "");
 }
 }
 
 
-// Macros for convenience
-#ifdef DUMP_PB_ENABLE
-#define DUMP_PB_FIELD(iter,msg) dump_pb_field(iter,msg, __FUNCTION__, __LINE__)
-#define DUMP_PB_HEAD(iter,msg) dump_pb_head(iter,msg, __FUNCTION__, __LINE__)
-#define DUMP_PB_MARK(msg) printf("MRK\t%-30s\t%-20s\t%-5d\n",msg, __FUNCTION__, __LINE__)
-#define WRITE_DEBUG_HEADER write_debug_header()
-#else
-#define DUMP_PB_FIELD(iter,msg)
-#define DUMP_PB_HEAD(iter,msg) 
-#define DUMP_PB_MARK(msg) 
-#define WRITE_DEBUG_HEADER()
-#endif
+
 
 
 static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
 static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
 static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof);
 static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof);
@@ -1282,7 +1289,7 @@ static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t
         if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED
         if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED
             && iter.required_field_index < PB_MAX_REQUIRED_FIELDS)
             && iter.required_field_index < PB_MAX_REQUIRED_FIELDS)
         {
         {
-            DUMP_PB_FIELD("Mark field as seen",&iter);
+            DUMP_PB_FIELD(&iter,"Mark field as seen");
             uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31));
             uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31));
             fields_seen.bitfield[iter.required_field_index >> 5] |= tmp;
             fields_seen.bitfield[iter.required_field_index >> 5] |= tmp;
         }
         }
@@ -1595,6 +1602,7 @@ static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t
     return pb_decode_bool(stream, (bool*)field->pData);
     return pb_decode_bool(stream, (bool*)field->pData);
 }
 }
 
 
+
 static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field)
 static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field)
 {
 {
     if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT)
     if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT)
@@ -1615,8 +1623,11 @@ static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_
         else
         else
             PB_RETURN_ERROR(stream, "invalid data_size");
             PB_RETURN_ERROR(stream, "invalid data_size");
 
 
-        if (clamped != value)
-            PB_RETURN_ERROR(stream, "integer too large");
+if (clamped != value) {
+    printf("Clamped value: %" PRIu64 ", Original value: %" PRIu64 "\n", clamped, value);
+    PB_RETURN_ERROR(stream, "integer too large");
+}
+
 
 
         return true;
         return true;
     }
     }
@@ -1660,8 +1671,10 @@ static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_
         else
         else
             PB_RETURN_ERROR(stream, "invalid data_size");
             PB_RETURN_ERROR(stream, "invalid data_size");
 
 
-        if (clamped != svalue)
+        if (clamped != svalue){
+            printf("Clamped value: %" PRIi64 ", Original value: %" PRIi64 "\n", clamped, svalue);
             PB_RETURN_ERROR(stream, "integer too large");
             PB_RETURN_ERROR(stream, "integer too large");
+        }
 
 
         return true;
         return true;
     }
     }
@@ -1749,12 +1762,11 @@ static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_
 
 
     if (!pb_read(stream, dest, (size_t)size))
     if (!pb_read(stream, dest, (size_t)size))
         return false;
         return false;
-
 #ifdef PB_VALIDATE_UTF8
 #ifdef PB_VALIDATE_UTF8
     if (!pb_validate_utf8((const char*)dest))
     if (!pb_validate_utf8((const char*)dest))
         PB_RETURN_ERROR(stream, "invalid utf8");
         PB_RETURN_ERROR(stream, "invalid utf8");
 #endif
 #endif
-
+    DUMP_PB_FIELD(field,"String");
     return true;
     return true;
 }
 }
 
 

+ 2 - 1
components/spotify/cspot_sink.c

@@ -6,7 +6,7 @@
 #include "esp_console.h"
 #include "esp_console.h"
 #include "esp_pthread.h"
 #include "esp_pthread.h"
 #include "esp_system.h"
 #include "esp_system.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "audio_controls.h"
 #include "audio_controls.h"
 #include "display.h"
 #include "display.h"
 #include "accessors.h"
 #include "accessors.h"
@@ -15,6 +15,7 @@
 #include "tools.h"
 #include "tools.h"
 #include "cspot_private.h"
 #include "cspot_private.h"
 #include "cspot_sink.h"
 #include "cspot_sink.h"
+#include "tools_http_utils.h"
 
 
 char EXT_RAM_ATTR deviceId[16];
 char EXT_RAM_ATTR deviceId[16];
 
 

+ 0 - 10
components/squeezelite-ota/component.mk

@@ -1,10 +0,0 @@
-#
-# "main" pseudo-component makefile.
-#
-# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
-
-# todo: add support for https
-COMPONENT_ADD_INCLUDEDIRS := .
-CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO -DCONFIG_OTA_ALLOW_HTTP=1
-
-

+ 0 - 41
components/squeezelite-ota/github.pem

@@ -1,41 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIHQjCCBiqgAwIBAgIQCgYwQn9bvO1pVzllk7ZFHzANBgkqhkiG9w0BAQsFADB1
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMTQwMgYDVQQDEytEaWdpQ2VydCBTSEEyIEV4dGVuZGVk
-IFZhbGlkYXRpb24gU2VydmVyIENBMB4XDTE4MDUwODAwMDAwMFoXDTIwMDYwMzEy
-MDAwMFowgccxHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB
-BAGCNzwCAQMTAlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQF
-Ewc1MTU3NTUwMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG
-A1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRMwEQYD
-VQQDEwpnaXRodWIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-xjyq8jyXDDrBTyitcnB90865tWBzpHSbindG/XqYQkzFMBlXmqkzC+FdTRBYyneZ
-w5Pz+XWQvL+74JW6LsWNc2EF0xCEqLOJuC9zjPAqbr7uroNLghGxYf13YdqbG5oj
-/4x+ogEG3dF/U5YIwVr658DKyESMV6eoYV9mDVfTuJastkqcwero+5ZAKfYVMLUE
-sMwFtoTDJFmVf6JlkOWwsxp1WcQ/MRQK1cyqOoUFUgYylgdh3yeCDPeF22Ax8AlQ
-xbcaI+GwfQL1FB7Jy+h+KjME9lE/UpgV6Qt2R1xNSmvFCBWu+NFX6epwFP/JRbkM
-fLz0beYFUvmMgLtwVpEPSwIDAQABo4IDeTCCA3UwHwYDVR0jBBgwFoAUPdNQpdag
-re7zSmAKZdMh1Pj41g8wHQYDVR0OBBYEFMnCU2FmnV+rJfQmzQ84mqhJ6kipMCUG
-A1UdEQQeMByCCmdpdGh1Yi5jb22CDnd3dy5naXRodWIuY29tMA4GA1UdDwEB/wQE
-AwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdQYDVR0fBG4wbDA0
-oDKgMIYuaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItZXYtc2VydmVyLWcy
-LmNybDA0oDKgMIYuaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItZXYtc2Vy
-dmVyLWcyLmNybDBLBgNVHSAERDBCMDcGCWCGSAGG/WwCATAqMCgGCCsGAQUFBwIB
-FhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAcGBWeBDAEBMIGIBggrBgEF
-BQcBAQR8MHowJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBS
-BggrBgEFBQcwAoZGaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
-U0hBMkV4dGVuZGVkVmFsaWRhdGlvblNlcnZlckNBLmNydDAMBgNVHRMBAf8EAjAA
-MIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgCkuQmQtBhYFIe7E6LMZ3AKPDWY
-BPkb37jjd80OyA3cEAAAAWNBYm0KAAAEAwBHMEUCIQDRZp38cTWsWH2GdBpe/uPT
-Wnsu/m4BEC2+dIcvSykZYgIgCP5gGv6yzaazxBK2NwGdmmyuEFNSg2pARbMJlUFg
-U5UAdgBWFAaaL9fC7NP14b1Esj7HRna5vJkRXMDvlJhV1onQ3QAAAWNBYm0tAAAE
-AwBHMEUCIQCi7omUvYLm0b2LobtEeRAYnlIo7n6JxbYdrtYdmPUWJQIgVgw1AZ51
-vK9ENinBg22FPxb82TvNDO05T17hxXRC2IYAdgC72d+8H4pxtZOUI5eqkntHOFeV
-CqtS6BqQlmQ2jh7RhQAAAWNBYm3fAAAEAwBHMEUCIQChzdTKUU2N+XcqcK0OJYrN
-8EYynloVxho4yPk6Dq3EPgIgdNH5u8rC3UcslQV4B9o0a0w204omDREGKTVuEpxG
-eOQwDQYJKoZIhvcNAQELBQADggEBAHAPWpanWOW/ip2oJ5grAH8mqQfaunuCVE+v
-ac+88lkDK/LVdFgl2B6kIHZiYClzKtfczG93hWvKbST4NRNHP9LiaQqdNC17e5vN
-HnXVUGw+yxyjMLGqkgepOnZ2Rb14kcTOGp4i5AuJuuaMwXmCo7jUwPwfLe1NUlVB
-Kqg6LK0Hcq4K0sZnxE8HFxiZ92WpV2AVWjRMEc/2z2shNoDvxvFUYyY1Oe67xINk
-myQKc+ygSBZzyLnXSFVWmHr3u5dcaaQGGAR42v6Ydr4iL38Hd4dOiBma+FXsXBIq
-WUjbST4VXmdaol7uzFMojA4zkxQDZAvF5XgJlAFadfySna/teik=
------END CERTIFICATE-----

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

@@ -21,7 +21,7 @@
 #include "esp_err.h"
 #include "esp_err.h"
 #include "squeezelite-ota.h"
 #include "squeezelite-ota.h"
 #include "esp_netif.h"
 #include "esp_netif.h"
-#include "Configurator.h"
+#include "Config.h"
 #include <time.h>
 #include <time.h>
 #include <sys/time.h>
 #include <sys/time.h>
 #include <stdarg.h>
 #include <stdarg.h>
@@ -40,6 +40,7 @@
 #include "lwip/sockets.h"
 #include "lwip/sockets.h"
 #include "globdefs.h"
 #include "globdefs.h"
 #include "tools.h"
 #include "tools.h"
+#include "bootstate.h"
 
 
 #define IF_DISPLAY(x) if(display) { x; }
 #define IF_DISPLAY(x) if(display) { x; }
 
 
@@ -685,8 +686,8 @@ esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t
 	if(bin_url){
 	if(bin_url){
 		ESP_LOGI(TAG,"Processing recovery OTA for url %s",STR_OR_ALT(bin_url,"N/A"));
 		ESP_LOGI(TAG,"Processing recovery OTA for url %s",STR_OR_ALT(bin_url,"N/A"));
 		ota_thread_parms.url =strdup_psram(bin_url);
 		ota_thread_parms.url =strdup_psram(bin_url);
-		configurator_set_string(&sys_State_msg,sys_State_ota_url_tag,sys_state,NULL);
-		configurator_raise_state_changed();
+		system_set_string(&sys_state_data_msg,sys_state_data_ota_url_tag,sys_state,NULL);
+		config_raise_state_changed();
 		ESP_LOGD(TAG, "Starting ota on core %u for : %s", OTA_CORE,ota_thread_parms.url);
 		ESP_LOGD(TAG, "Starting ota on core %u for : %s", OTA_CORE,ota_thread_parms.url);
 	}
 	}
 	else {
 	else {
@@ -769,3 +770,19 @@ in_addr_t discover_ota_server(int max) {
 	return s.sin_addr.s_addr;
 	return s.sin_addr.s_addr;
 }
 }
 
 
+
+// Callback to handle ota when an IP address is obtained
+void cb_handle_ota(nm_state_t new_state, int sub_state) {
+    if (sys_state && sys_state->ota_url && strlen(sys_state->ota_url)) {
+        ESP_LOGD(TAG, "Found OTA URL %s", sys_state->ota_url);
+        if (is_recovery_running) {
+            ESP_LOGI(TAG, "Updating firmware from link: %s", sys_state->ota_url);
+#if defined(CONFIG_WITH_METRICS)
+            metrics_event("fw_update");
+#endif
+            start_ota(sys_state->ota_url, NULL, 0);
+        } else {
+            ESP_LOGE(TAG, "Restarted to application partition. We're not going to perform OTA!");
+        }
+    }
+}

+ 4 - 1
components/squeezelite-ota/squeezelite-ota.h

@@ -9,7 +9,7 @@
 #include "esp_attr.h"
 #include "esp_attr.h"
 #include "esp_image_format.h"
 #include "esp_image_format.h"
 #include "esp_ota_ops.h"
 #include "esp_ota_ops.h"
-//
+#include "network_services.h"
 
 
 // ERASE BLOCK needs to be a multiple of sector size. If a different multiple is passed
 // ERASE BLOCK needs to be a multiple of sector size. If a different multiple is passed
 // the OTA process will adjust. Here, we need to strike the balance between speed and
 // the OTA process will adjust. Here, we need to strike the balance between speed and
@@ -32,3 +32,6 @@ uint8_t ota_get_pct_complete();
 
 
 esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length);
 esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length);
 in_addr_t discover_ota_server(int max);
 in_addr_t discover_ota_server(int max);
+
+// Callback to handle ota when an IP address is obtained
+void cb_handle_ota(nm_state_t new_state, int sub_state);

+ 2 - 2
components/squeezelite/CMakeLists.txt

@@ -3,19 +3,19 @@ if(IDF_TARGET STREQUAL "esp32")
 	set(target_requires "driver_bt" "platform_config")
 	set(target_requires "driver_bt" "platform_config")
 endif()
 endif()
 
 
-idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978
+idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978 cs4265
 						INCLUDE_DIRS .  ac101 
 						INCLUDE_DIRS .  ac101 
 						PRIV_REQUIRES 	
 						PRIV_REQUIRES 	
 									codecs
 									codecs
 									newlib 
 									newlib 
 									esp_common 
 									esp_common 
 									esp-dsp
 									esp-dsp
+									tools
 						  			platform_config 
 						  			platform_config 
 						 			services 
 						 			services 
 									spotify
 									spotify
 						 			raop   
 						 			raop   
 						 			display
 						 			display
-						 			tools
 						 			audio
 						 			audio
 									led_strip
 									led_strip
 									_override
 									_override

+ 6 - 6
components/squeezelite/ac101/ac101.c

@@ -32,7 +32,7 @@
 #include <driver/i2s.h>
 #include <driver/i2s.h>
 #include "adac.h"
 #include "adac.h"
 #include "ac101.h"
 #include "ac101.h"
-#include "Configurator.h"
+#include "Config.h"
 static const char TAG[] = "AC101";
 static const char TAG[] = "AC101";
 
 
 #define SPKOUT_EN ((1 << 9) | (1 << 11) | (1 << 7) | (1 << 5))
 #define SPKOUT_EN ((1 << 9) | (1 << 11) | (1 << 7) | (1 << 5))
@@ -48,13 +48,13 @@ static const char TAG[] = "AC101";
         return b;\
         return b;\
     }
     }
 	
 	
-static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck);
+static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck);
 static void speaker(bool active);
 static void speaker(bool active);
 static void headset(bool active);
 static void headset(bool active);
 static bool volume(unsigned left, unsigned right);
 static bool volume(unsigned left, unsigned right);
 static void power(adac_power_e mode);
 static void power(adac_power_e mode);
 
 
-const struct adac_s dac_ac101 = { sys_DACModelEnum_AC101, init, adac_deinit, power, speaker, headset, volume };
+const struct adac_s dac_ac101 = { sys_dac_models_AC101, init, adac_deinit, power, speaker, headset, volume };
 
 
 static void ac101_start(ac_module_t mode);
 static void ac101_start(ac_module_t mode);
 static void ac101_stop(void);
 static void ac101_stop(void);
@@ -64,11 +64,11 @@ static void ac101_set_spk_volume(uint8_t volume);
 /****************************************************************************************
 /****************************************************************************************
  * init
  * init
  */
  */
-static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck) {	 
-	adac_init(config, i2c_port);
+static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck) {	 
+	adac_init(config);
 	if (adac_read_word(AC101_ADDR, CHIP_AUDIO_RS) == 0xffff) {
 	if (adac_read_word(AC101_ADDR, CHIP_AUDIO_RS) == 0xffff) {
 		ESP_LOGW(TAG, "No AC101 detected");
 		ESP_LOGW(TAG, "No AC101 detected");
-		i2c_driver_delete(i2c_port);
+		i2c_driver_delete(config->i2c.port-sys_i2c_port_PORT0);
 		return false;		
 		return false;		
 	}
 	}
 	
 	

+ 5 - 4
components/squeezelite/adac.h

@@ -12,12 +12,12 @@
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "driver/i2s.h"
 #include "driver/i2s.h"
 #include "driver/i2c.h"
 #include "driver/i2c.h"
-#include "Configurator.h"
+#include "Config.h"
 typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
 typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
 
 
 struct adac_s {
 struct adac_s {
-	sys_DACModelEnum model;
-	bool (*init)(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool *mck);
+	sys_dac_models model;
+	bool (*init)(sys_dac_config *config,i2s_config_t *i2s_config, bool *mck);
 	void (*deinit)(void);
 	void (*deinit)(void);
 	void (*power)(adac_power_e mode);
 	void (*power)(adac_power_e mode);
 	void (*speaker)(bool active);
 	void (*speaker)(bool active);
@@ -29,9 +29,10 @@ extern const struct adac_s dac_tas57xx;
 extern const struct adac_s dac_tas5713;
 extern const struct adac_s dac_tas5713;
 extern const struct adac_s dac_ac101;
 extern const struct adac_s dac_ac101;
 extern const struct adac_s dac_wm8978;
 extern const struct adac_s dac_wm8978;
+extern const struct adac_s dac_cs4265;
 extern const struct adac_s dac_external;
 extern const struct adac_s dac_external;
 
 
-int 		adac_init(char *config, int i2c_port);
+int 		adac_init(sys_dac_config *config);
 void		adac_deinit(void);
 void		adac_deinit(void);
 esp_err_t 	adac_write(int i2c_addr, uint8_t reg, uint8_t *data, size_t count);
 esp_err_t 	adac_write(int i2c_addr, uint8_t reg, uint8_t *data, size_t count);
 esp_err_t 	adac_write_byte(int i2c_addr, uint8_t reg, uint8_t val);
 esp_err_t 	adac_write_byte(int i2c_addr, uint8_t reg, uint8_t val);

+ 10 - 11
components/squeezelite/adac_core.c

@@ -23,16 +23,13 @@
 } while (0)
 } while (0)
 
 
 static const char TAG[] = "DAC core";
 static const char TAG[] = "DAC core";
-static int i2c_port = -1;
-
+i2c_port_t i2c_port = -1;
 /****************************************************************************************
 /****************************************************************************************
  * init
  * init
  */
  */
-int adac_init(char *config, int i2c_port_num) {	 
+int adac_init(sys_dac_config *config) {	 
 	int i2c_addr = 0;
 	int i2c_addr = 0;
 
 
-	i2c_port = i2c_port_num;
-
 	// configure i2c
 	// configure i2c
 	i2c_config_t i2c_config = {
 	i2c_config_t i2c_config = {
 			.mode = I2C_MODE_MASTER,
 			.mode = I2C_MODE_MASTER,
@@ -42,17 +39,19 @@ int adac_init(char *config, int i2c_port_num) {
 			.scl_pullup_en = GPIO_PULLUP_ENABLE,
 			.scl_pullup_en = GPIO_PULLUP_ENABLE,
 			.master.clk_speed = 250000,
 			.master.clk_speed = 250000,
 		};
 		};
-
-	PARSE_PARAM(config, "i2c", '=', i2c_addr);
-	PARSE_PARAM(config, "sda", '=', i2c_config.sda_io_num);
-	PARSE_PARAM(config, "scl", '=', i2c_config.scl_io_num);
+	i2c_addr = config->addr;
+	if(config->has_i2c){
+		i2c_config.sda_io_num = config->i2c.sda;
+		i2c_config.scl_io_num = config->i2c.scl;
+		i2c_config.master.clk_speed =  config->i2c.speed>0?config->i2c.speed:i2c_config.master.clk_speed;
+	}
 
 
 	if (i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) {
 	if (i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) {
 		ESP_LOGW(TAG, "DAC does not use i2c");
 		ESP_LOGW(TAG, "DAC does not use i2c");
 		return i2c_addr;
 		return i2c_addr;
 	}	
 	}	
-	
-	ESP_LOGI(TAG, "DAC uses I2C port:%d, sda:%d, scl:%d", i2c_port, i2c_config.sda_io_num, i2c_config.scl_io_num);
+	i2c_port = config->i2c.port-sys_i2c_port_PORT0;
+	ESP_LOGI(TAG, "DAC uses I2C port:%s, sda:%d, scl:%d", sys_i2c_port_name(config->i2c.port), i2c_config.sda_io_num, i2c_config.scl_io_num);
 	
 	
 	// we have an I2C configured	
 	// we have an I2C configured	
 	i2c_param_config(i2c_port, &i2c_config);
 	i2c_param_config(i2c_port, &i2c_config);

+ 8 - 7
components/squeezelite/controls.c

@@ -7,7 +7,7 @@
  */
  */
 
 
 #include "squeezelite.h"
 #include "squeezelite.h"
-// #include "Configurator.h"
+// #include "Config.h"
 #pragma message("fixme: look for TODO below")
 #pragma message("fixme: look for TODO below")
 #include "audio_controls.h"
 #include "audio_controls.h"
 
 
@@ -54,7 +54,7 @@ static u16_t server_cport;
 static int cli_sock = -1;
 static int cli_sock = -1;
 static u8_t mac[6];
 static u8_t mac[6];
 static void	(*chained_notify)(in_addr_t, u16_t, u16_t);
 static void	(*chained_notify)(in_addr_t, u16_t, u16_t);
-static bool raw_mode;
+static bool raw_mode=false;
 
 
 static void cli_send_cmd(char *cmd);
 static void cli_send_cmd(char *cmd);
 
 
@@ -244,15 +244,16 @@ static bool ir_handler(u16_t addr, u16_t cmd) {
  */
  */
 void sb_controls_init(void) {
 void sb_controls_init(void) {
 	// TODO: Add support for the commented code
 	// TODO: Add support for the commented code
+	#pragma message("sb_controls_init needs to be implemented")
 	// char *p = config_alloc_get_default(NVS_TYPE_STR, "lms_ctrls_raw", "n", 0);
 	// char *p = config_alloc_get_default(NVS_TYPE_STR, "lms_ctrls_raw", "n", 0);
 	// raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y');
 	// raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y');
 	// free(p);
 	// free(p);
 	
 	
-	// LOG_INFO("initializing audio (buttons/rotary/ir) controls (raw:%u)", raw_mode);
+	LOG_INFO("initializing audio (buttons/rotary/ir) controls (raw:%u)", raw_mode);
 	
 	
-	// get_mac(mac);
-	// actrls_set_default(LMS_controls, raw_mode, NULL, ir_handler);
+	get_mac(mac);
+	actrls_set_default(LMS_controls, raw_mode, NULL, ir_handler);
 	
 	
-	// chained_notify = server_notify;
-	// server_notify = notify;
+	chained_notify = server_notify;
+	server_notify = notify;
 }
 }

+ 430 - 432
components/squeezelite/cs4265/cs4265.c

@@ -1,4 +1,4 @@
-/* 
+/*
  *  Squeezelite for esp32
  *  Squeezelite for esp32
  *
  *
  *  (c) Sebastien 2019
  *  (c) Sebastien 2019
@@ -8,21 +8,21 @@
  *  https://opensource.org/licenses/MIT
  *  https://opensource.org/licenses/MIT
  *
  *
  */
  */
- 
+
 #include <string.h>
 #include <string.h>
-//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "driver/i2s.h"
-#include "driver/i2c.h"
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
+#include "Config.h"
+#include "adac.h"
 #include "driver/gpio.h"
 #include "driver/gpio.h"
+#include "driver/i2c.h"
+#include "driver/i2s.h"
 #include "esp_log.h"
 #include "esp_log.h"
-#include "adac.h"
-#include "stdio.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
 #include "math.h"
 #include "math.h"
-#include "Configurator.h"
-#define CS4265_PULL_UP (0x4F )
-#define CS4265_PULL_DOWN (0x4E )
+#include "stdio.h"
+#define CS4265_PULL_UP (0x4F)
+#define CS4265_PULL_DOWN (0x4E)
 
 
 #ifndef ARRAY_SIZE
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -30,188 +30,181 @@
 
 
 static const char TAG[] = "CS4265";
 static const char TAG[] = "CS4265";
 
 
-static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
+static bool init(sys_dac_config * config, i2s_config_t* i2s_config, bool* mck);
 static void speaker(bool active);
 static void speaker(bool active);
 static void headset(bool active);
 static void headset(bool active);
 static bool volume(unsigned left, unsigned right);
 static bool volume(unsigned left, unsigned right);
 static void power(adac_power_e mode);
 static void power(adac_power_e mode);
-static esp_err_t cs4265_update_bit(uint8_t reg_no,uint8_t mask,uint8_t val );
+static esp_err_t cs4265_update_bit(uint8_t reg_no, uint8_t mask, uint8_t val);
 static esp_err_t set_clock();
 static esp_err_t set_clock();
-const struct adac_s dac_cs4265 = { sys_DACModelEnum_CS4265, init, adac_deinit, power, speaker, headset, volume };
+const struct adac_s dac_cs4265 = {
+    sys_dac_models_CS4265, init, adac_deinit, power, speaker, headset, volume};
 
 
 struct cs4265_cmd_s {
 struct cs4265_cmd_s {
-	uint8_t reg;
-	uint8_t value;
+    uint8_t reg;
+    uint8_t value;
 };
 };
 struct cs4265_private {
 struct cs4265_private {
-	uint8_t format;
-	uint32_t sysclk;
-	i2s_config_t *i2s_config;
-	int i2c_port;
-};
+    uint8_t format;
+    uint32_t sysclk;
+    i2s_config_t* i2s_config;
+	i2c_port_t port;
+	};
 struct cs4265_private cs4265;
 struct cs4265_private cs4265;
 
 
-#define CS4265_CHIP_ID				0x1
-#define CS4265_CHIP_ID_VAL			0xD0
-#define CS4265_CHIP_ID_MASK			0xF0
-#define CS4265_REV_ID_MASK			0x0F
-
-#define CS4265_PWRCTL				0x02
-#define CS4265_PWRCTL_PDN			(1 << 0)
-#define CS4265_PWRCTL_PDN_DAC       (1 << 1)
-#define CS4265_PWRCTL_PDN_ADC       (1 << 2)
-#define CS4265_PWRCTL_PDN_MIC       (1 << 3)
-#define CS4265_PWRCTL_FREEZE        (1 << 7)
-#define CS4265_PWRCTL_PDN_ALL   	CS4265_PWRCTL_PDN | CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC
-
-
-
-#define CS4265_DAC_CTL				0x3
+#define CS4265_CHIP_ID 0x1
+#define CS4265_CHIP_ID_VAL 0xD0
+#define CS4265_CHIP_ID_MASK 0xF0
+#define CS4265_REV_ID_MASK 0x0F
+
+#define CS4265_PWRCTL 0x02
+#define CS4265_PWRCTL_PDN (1 << 0)
+#define CS4265_PWRCTL_PDN_DAC (1 << 1)
+#define CS4265_PWRCTL_PDN_ADC (1 << 2)
+#define CS4265_PWRCTL_PDN_MIC (1 << 3)
+#define CS4265_PWRCTL_FREEZE (1 << 7)
+#define CS4265_PWRCTL_PDN_ALL                                                                      \
+    CS4265_PWRCTL_PDN | CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC
+
+#define CS4265_DAC_CTL 0x3
 // De-Emphasis Control (Bit 1)
 // De-Emphasis Control (Bit 1)
 // The standard 50/15 i2s digital de-emphasis filter response may be implemented for a sample
 // The standard 50/15 i2s digital de-emphasis filter response may be implemented for a sample
-// rate of 44.1 kHz when the DeEmph bit is set.  NOTE: De-emphasis is available only in Single-Speed Mode.
-#define CS4265_DAC_CTL_DEEMPH		(1 << 1)
+// rate of 44.1 kHz when the DeEmph bit is set.  NOTE: De-emphasis is available only in Single-Speed
+// Mode.
+#define CS4265_DAC_CTL_DEEMPH (1 << 1)
 // MUTE DAC
 // MUTE DAC
-// The DAC outputs will mute and the MUTEC pin will become active when this bit is set. Though this bit is
-// active high, it should be noted that the MUTEC pin is active low. The common mode voltage on the outputs
-// will be retained when this bit is set. The muting function is effected, similar to attenuation changes, by the
-// DACSoft and DACZero bits in the DAC Control 2 register.
-#define CS4265_DAC_CTL_MUTE			(1 << 2)
-// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital Interface
-// DAC_DIF1 DAC_DIF0 Description                                    Format Figure
-// 0        0        Left Justified, up to 24-bit data (default)    0       5
-// 0        1        I²S, up to 24-bit data                         1       6
+// The DAC outputs will mute and the MUTEC pin will become active when this bit is set. Though this
+// bit is active high, it should be noted that the MUTEC pin is active low. The common mode voltage
+// on the outputs will be retained when this bit is set. The muting function is effected, similar to
+// attenuation changes, by the DACSoft and DACZero bits in the DAC Control 2 register.
+#define CS4265_DAC_CTL_MUTE (1 << 2)
+// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital
+// Interface DAC_DIF1 DAC_DIF0 Description                                    Format Figure 0 0 Left
+// Justified, up to 24-bit data (default)    0       5 0        1        I²S, up to 24-bit data 1 6
 // 1        0        Right-Justified, 16-bit Data                   2       7
 // 1        0        Right-Justified, 16-bit Data                   2       7
 // 1        1        Right-Justified, 24-bit Data                   3       7
 // 1        1        Right-Justified, 24-bit Data                   3       7
-#define CS4265_DAC_CTL_DIF0			(1 << 4)
-// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital Interface
-// DAC_DIF1 DAC_DIF0 Description                                    Format Figure
-// 0        0        Left Justified, up to 24-bit data (default)    0       5
-// 0        1        I²S, up to 24-bit data                         1       6
+#define CS4265_DAC_CTL_DIF0 (1 << 4)
+// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital
+// Interface DAC_DIF1 DAC_DIF0 Description                                    Format Figure 0 0 Left
+// Justified, up to 24-bit data (default)    0       5 0        1        I²S, up to 24-bit data 1 6
 // 1        0        Right-Justified, 16-bit Data                   2       7
 // 1        0        Right-Justified, 16-bit Data                   2       7
 // 1        1        Right-Justified, 24-bit Data                   3       7
 // 1        1        Right-Justified, 24-bit Data                   3       7
-#define CS4265_DAC_CTL_DIF1			(1 << 5)
+#define CS4265_DAC_CTL_DIF1 (1 << 5)
 
 
+#define CS4265_ADC_CTL 0x4
+#define CS4265_ADC_MASTER 1
 
 
+#define CS4265_ADC_CTL_MUTE (1 << 2)
+#define CS4265_ADC_DIF (1 << 4)
+#define CS4265_ADC_FM (3 << 6)
 
 
-#define CS4265_ADC_CTL				0x4
-#define CS4265_ADC_MASTER			1
-
-#define CS4265_ADC_CTL_MUTE   		(1 << 2)
-#define CS4265_ADC_DIF				(1 << 4)
-#define CS4265_ADC_FM				(3 << 6)
-
-//Master Clock Dividers (Bits 6:4)
-//Sets the frequency of the supplied MCLK signal. 
+// Master Clock Dividers (Bits 6:4)
+// Sets the frequency of the supplied MCLK signal.
 //
 //
-//MCLK Divider MCLK Freq2 MCLK Freq1 MCLK Freq0
-// ÷   1 	   0          0           0
-// ÷   1.5 	   0          0           1
-// ÷   2 	   0          1           0
-// ÷   3 	   0          1           1
-// ÷   4 	   1          0           0
-// NA          1          0           1
-// NA 		   1          1           x 
-#define CS4265_MCLK_FREQ			0x5
-#define CS4265_MCLK_FREQ_1_0X	(0b000<<4 )
-#define CS4265_MCLK_FREQ_1_5X	(0b001<<4 )
-#define CS4265_MCLK_FREQ_2_0X	(0b010<<4 )
-#define CS4265_MCLK_FREQ_3_0X	(0b011<<4 )
-#define CS4265_MCLK_FREQ_4_0X	(0b100<<4 )
-
-
-#define CS4265_MCLK_FREQ_MASK			(7 << 4)
-
-#define CS4265_SIG_SEL				0x6
-#define CS4265_SIG_SEL_LOOP			(1 << 1)
-#define CS4265_SIG_SEL_SDIN2		(1 << 7)
-#define CS4265_SIG_SEL_SDIN1		(0 << 7)
+// MCLK Divider MCLK Freq2 MCLK Freq1 MCLK Freq0
+//  ÷   1 	   0          0           0
+//  ÷   1.5 	   0          0           1
+//  ÷   2 	   0          1           0
+//  ÷   3 	   0          1           1
+//  ÷   4 	   1          0           0
+//  NA          1          0           1
+//  NA 		   1          1           x
+#define CS4265_MCLK_FREQ 0x5
+#define CS4265_MCLK_FREQ_1_0X (0b000 << 4)
+#define CS4265_MCLK_FREQ_1_5X (0b001 << 4)
+#define CS4265_MCLK_FREQ_2_0X (0b010 << 4)
+#define CS4265_MCLK_FREQ_3_0X (0b011 << 4)
+#define CS4265_MCLK_FREQ_4_0X (0b100 << 4)
+
+#define CS4265_MCLK_FREQ_MASK (7 << 4)
+
+#define CS4265_SIG_SEL 0x6
+#define CS4265_SIG_SEL_LOOP (1 << 1)
+#define CS4265_SIG_SEL_SDIN2 (1 << 7)
+#define CS4265_SIG_SEL_SDIN1 (0 << 7)
 
 
 // Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
 // Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
-// +12 dB in 0.5 dB steps. The gain bits are in two’s complement with the Gain0 bit set for a 0.5 dB step.
-// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
-#define CS4265_CHB_PGA_CTL			0x7
+// +12 dB in 0.5 dB steps. The gain bits are in two’s complement with the Gain0 bit set for a 0.5 dB
+// step. Register settings outside of the ±12 dB range are reserved and must not be used. See Table
+// 13 for example settings
+#define CS4265_CHB_PGA_CTL 0x7
 // Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
 // Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
-// +12 dB in 0.5 dB steps. The gain bits are in two’s complement with the Gain0 bit set for a 0.5 dB step.
-// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
-#define CS4265_CHA_PGA_CTL			0x8
+// +12 dB in 0.5 dB steps. The gain bits are in two’s complement with the Gain0 bit set for a 0.5 dB
+// step. Register settings outside of the ±12 dB range are reserved and must not be used. See Table
+// 13 for example settings
+#define CS4265_CHA_PGA_CTL 0x8
 // Gain[5:0]    Setting
 // Gain[5:0]    Setting
 // 101000       -12 dB
 // 101000       -12 dB
 // 000000       0 dB
 // 000000       0 dB
 // 011000       +12 dB
 // 011000       +12 dB
 
 
-
-#define CS4265_ADC_CTL2				0x9
-
-// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to -127 dB.
-// The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared. The Vol[7:1] bits
-// activate attenuation equal to their decimal equivalent (in dB). 
-//Binary Code 	Volume Setting
-//00000000 		0 dB
-//00000001 		-0.5 dB
-//00101000 		-20 dB
-//00101001 		-20.5 dB
-//11111110 		-127 dB
-//11111111 		-127.5 dB
-#define CS4265_DAC_CHA_VOL			0xA
-// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to -127 dB.
-// The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared. The Vol[7:1] bits
-// activate attenuation equal to their decimal equivalent (in dB). 
-//Binary Code 	Volume Setting
-//00000000 		0 dB
-//00000001 		-0.5 dB
-//00101000 		-20 dB
-//00101001 		-20.5 dB
-//11111110 		-127 dB
-//11111111 		-127.5 dB
-#define CS4265_DAC_CHB_VOL			0xB
-#define CS4265_DAC_VOL_ATT_000_0		0b00000000
-#define CS4265_DAC_VOL_ATT_000_5		0b00000001
-#define CS4265_DAC_VOL_ATT_020_0		0b00101000
-#define CS4265_DAC_VOL_ATT_020_5		0b00101001
-#define CS4265_DAC_VOL_ATT_127_0		0b11111110
-#define CS4265_DAC_VOL_ATT_127_5		0b11111111
+#define CS4265_ADC_CTL2 0x9
+
+// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to
+// -127 dB. The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared.
+// The Vol[7:1] bits activate attenuation equal to their decimal equivalent (in dB).
+// Binary Code 	Volume Setting
+// 00000000 		0 dB
+// 00000001 		-0.5 dB
+// 00101000 		-20 dB
+// 00101001 		-20.5 dB
+// 11111110 		-127 dB
+// 11111111 		-127.5 dB
+#define CS4265_DAC_CHA_VOL 0xA
+// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to
+// -127 dB. The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared.
+// The Vol[7:1] bits activate attenuation equal to their decimal equivalent (in dB).
+// Binary Code 	Volume Setting
+// 00000000 		0 dB
+// 00000001 		-0.5 dB
+// 00101000 		-20 dB
+// 00101001 		-20.5 dB
+// 11111110 		-127 dB
+// 11111111 		-127.5 dB
+#define CS4265_DAC_CHB_VOL 0xB
+#define CS4265_DAC_VOL_ATT_000_0 0b00000000
+#define CS4265_DAC_VOL_ATT_000_5 0b00000001
+#define CS4265_DAC_VOL_ATT_020_0 0b00101000
+#define CS4265_DAC_VOL_ATT_020_5 0b00101001
+#define CS4265_DAC_VOL_ATT_127_0 0b11111110
+#define CS4265_DAC_VOL_ATT_127_5 0b11111111
 
 
 // DAC Soft Ramp or Zero Cross Enable (Bits 7:6)
 // DAC Soft Ramp or Zero Cross Enable (Bits 7:6)
 //
 //
 // Soft Ramp Enable
 // Soft Ramp Enable
-// Soft Ramp allows level changes, both muting and attenuation, to be implemented by incrementally ramping, in 1/8 dB steps, from the current level to the new level at a rate of 1 dB per 8 left/right clock periods.
-// See Table 17.
-// Zero Cross Enable
-// Zero Cross Enable dictates that signal-level changes, either by attenuation changes or muting, will occur
-// on a signal zero crossing to minimize audible artifacts. The requested level change will occur after a timeout period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the signal
-// does not encounter a zero crossing. The zero cross function is independently monitored and implemented
-// for each channel. See Table 17.
-// Soft Ramp and Zero Cross Enable
-// Soft Ramp and Zero Cross Enable dictate that signal-level changes, either by attenuation changes or muting, will occur in 1/8 dB steps and be implemented on a signal zero crossing. The 1/8 dB level change will
-// occur after a time-out period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the signal does not encounter a zero crossing. The zero cross function is independently monitored and implemented for each channel
-// DACSoft DACZeroCross Mode
-// 0 0 Changes to affect immediately
-// 0 1 Zero Cross enabled
-// 1 0 Soft Ramp enabled
-// 1 1 Soft Ramp and Zero Cross enabled (default)
-#define CS4265_DAC_CTL2								0xC
-#define CS4265_DAC_CTL2_ZERO_CROSS_EN  				(uint8_t)(0b01 <<7)
-#define CS4265_DAC_CTL2_SOFT_RAMP_EN  				(uint8_t)(0b10 <<7)
-#define CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN  	(uint8_t)(0b11 <<7)
-
-
-#define CS4265_INT_STATUS			0xD
-#define CS4265_INT_STATUS_ADC_UNDF  (1<<0)
-#define CS4265_INT_STATUS_ADC_OVF   (1<<1)
-#define CS4265_INT_STATUS_CLKERR    (1<<3)
-
-
-#define CS4265_INT_MASK				0xE
-#define CS4265_STATUS_MODE_MSB			0xF
-#define CS4265_STATUS_MODE_LSB			0x10
-
-//Transmitter Control 1 - Address 11h
-#define CS4265_SPDIF_CTL1			0x11
-
-
-
-#define CS4265_SPDIF_CTL2			0x12
+// Soft Ramp allows level changes, both muting and attenuation, to be implemented by incrementally
+// ramping, in 1/8 dB steps, from the current level to the new level at a rate of 1 dB per 8
+// left/right clock periods. See Table 17. Zero Cross Enable Zero Cross Enable dictates that
+// signal-level changes, either by attenuation changes or muting, will occur on a signal zero
+// crossing to minimize audible artifacts. The requested level change will occur after a timeout
+// period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the
+// signal does not encounter a zero crossing. The zero cross function is independently monitored and
+// implemented for each channel. See Table 17. Soft Ramp and Zero Cross Enable Soft Ramp and Zero
+// Cross Enable dictate that signal-level changes, either by attenuation changes or muting, will
+// occur in 1/8 dB steps and be implemented on a signal zero crossing. The 1/8 dB level change will
+// occur after a time-out period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz
+// sample rate) if the signal does not encounter a zero crossing. The zero cross function is
+// independently monitored and implemented for each channel DACSoft DACZeroCross Mode 0 0 Changes to
+// affect immediately 0 1 Zero Cross enabled 1 0 Soft Ramp enabled 1 1 Soft Ramp and Zero Cross
+// enabled (default)
+#define CS4265_DAC_CTL2 0xC
+#define CS4265_DAC_CTL2_ZERO_CROSS_EN (uint8_t)(0b01 << 7)
+#define CS4265_DAC_CTL2_SOFT_RAMP_EN (uint8_t)(0b10 << 7)
+#define CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN (uint8_t)(0b11 << 7)
+
+#define CS4265_INT_STATUS 0xD
+#define CS4265_INT_STATUS_ADC_UNDF (1 << 0)
+#define CS4265_INT_STATUS_ADC_OVF (1 << 1)
+#define CS4265_INT_STATUS_CLKERR (1 << 3)
+
+#define CS4265_INT_MASK 0xE
+#define CS4265_STATUS_MODE_MSB 0xF
+#define CS4265_STATUS_MODE_LSB 0x10
+
+// Transmitter Control 1 - Address 11h
+#define CS4265_SPDIF_CTL1 0x11
+
+#define CS4265_SPDIF_CTL2 0x12
 // Transmitter Digital Interface Format (Bits 7:6)
 // Transmitter Digital Interface Format (Bits 7:6)
 // Function:
 // Function:
 // The required relationship between LRCK, SCLK and SDIN for the transmitter is defined
 // The required relationship between LRCK, SCLK and SDIN for the transmitter is defined
@@ -220,343 +213,348 @@ struct cs4265_private cs4265;
 // 0 1 I²S, up to 24-bit data 1 6
 // 0 1 I²S, up to 24-bit data 1 6
 // 1 0 Right-Justified, 16-bit Data 2 7
 // 1 0 Right-Justified, 16-bit Data 2 7
 // 1 1 Right-Justified, 24-bit Data 3 7
 // 1 1 Right-Justified, 24-bit Data 3 7
-#define CS4265_SPDIF_CTL2_MMTLR         (1<<0)
-#define CS4265_SPDIF_CTL2_MMTCS         (1<<1)
-#define CS4265_SPDIF_CTL2_MMT           (1<<2)
-#define CS4265_SPDIF_CTL2_V             (1<<3)
-#define CS4265_SPDIF_CTL2_TXMUTE        (1<<4)
-#define CS4265_SPDIF_CTL2_TXOFF         (1<<5)
-#define CS4265_SPDIF_CTL2_MUTE			(1 << 4)
-#define CS4265_SPDIF_CTL2_DIF			(3 << 6)
-#define CS4265_SPDIF_CTL2_DIF0			(1 << 6)
-#define CS4265_SPDIF_CTL2_DIF1			(1 << 7)
-
-
-
-
-
-
-#define CS4265_C_DATA_BUFF			0x13
-#define CS4265_MAX_REGISTER			0x2A
+#define CS4265_SPDIF_CTL2_MMTLR (1 << 0)
+#define CS4265_SPDIF_CTL2_MMTCS (1 << 1)
+#define CS4265_SPDIF_CTL2_MMT (1 << 2)
+#define CS4265_SPDIF_CTL2_V (1 << 3)
+#define CS4265_SPDIF_CTL2_TXMUTE (1 << 4)
+#define CS4265_SPDIF_CTL2_TXOFF (1 << 5)
+#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
+#define CS4265_SPDIF_CTL2_DIF (3 << 6)
+#define CS4265_SPDIF_CTL2_DIF0 (1 << 6)
+#define CS4265_SPDIF_CTL2_DIF1 (1 << 7)
+
+#define CS4265_C_DATA_BUFF 0x13
+#define CS4265_MAX_REGISTER 0x2A
 struct cs4265_clk_para {
 struct cs4265_clk_para {
-	uint32_t mclk;
-	uint32_t rate;
-	uint8_t fm_mode; /* values 1, 2, or 4 */
-	uint8_t mclkdiv;
+    uint32_t mclk;
+    uint32_t rate;
+    uint8_t fm_mode; /* values 1, 2, or 4 */
+    uint8_t mclkdiv;
 };
 };
 static const struct cs4265_clk_para clk_map_table[] = {
 static const struct cs4265_clk_para clk_map_table[] = {
-	/*32k*/
-	{8192000, 32000, 0, 0},
-	{12288000, 32000, 0, 1},
-	{16384000, 32000, 0, 2},
-	{24576000, 32000, 0, 3},
-	{32768000, 32000, 0, 4},
-
-	/*44.1k*/
-	{11289600, 44100, 0, 0},
-	{16934400, 44100, 0, 1},
-	{22579200, 44100, 0, 2},
-	{33868000, 44100, 0, 3},
-	{45158400, 44100, 0, 4},
-
-	/*48k*/
-	{12288000, 48000, 0, 0},
-	{18432000, 48000, 0, 1},
-	{24576000, 48000, 0, 2},
-	{36864000, 48000, 0, 3},
-	{49152000, 48000, 0, 4},
-
-	/*64k*/
-	{8192000, 64000, 1, 0},
-	{12288000, 64000, 1, 1},
-	{16934400, 64000, 1, 2},
-	{24576000, 64000, 1, 3},
-	{32768000, 64000, 1, 4},
-
-	/* 88.2k */
-	{11289600, 88200, 1, 0},
-	{16934400, 88200, 1, 1},
-	{22579200, 88200, 1, 2},
-	{33868000, 88200, 1, 3},
-	{45158400, 88200, 1, 4},
-
-	/* 96k */
-	{12288000, 96000, 1, 0},
-	{18432000, 96000, 1, 1},
-	{24576000, 96000, 1, 2},
-	{36864000, 96000, 1, 3},
-	{49152000, 96000, 1, 4},
-
-	/* 128k */
-	{8192000, 128000, 2, 0},
-	{12288000, 128000, 2, 1},
-	{16934400, 128000, 2, 2},
-	{24576000, 128000, 2, 3},
-	{32768000, 128000, 2, 4},
-
-	/* 176.4k */
-	{11289600, 176400, 2, 0},
-	{16934400, 176400, 2, 1},
-	{22579200, 176400, 2, 2},
-	{33868000, 176400, 2, 3},
-	{49152000, 176400, 2, 4},
-
-	/* 192k */
-	{12288000, 192000, 2, 0},
-	{18432000, 192000, 2, 1},
-	{24576000, 192000, 2, 2},
-	{36864000, 192000, 2, 3},
-	{49152000, 192000, 2, 4},
+    /*32k*/
+    {8192000, 32000, 0, 0},
+    {12288000, 32000, 0, 1},
+    {16384000, 32000, 0, 2},
+    {24576000, 32000, 0, 3},
+    {32768000, 32000, 0, 4},
+
+    /*44.1k*/
+    {11289600, 44100, 0, 0},
+    {16934400, 44100, 0, 1},
+    {22579200, 44100, 0, 2},
+    {33868000, 44100, 0, 3},
+    {45158400, 44100, 0, 4},
+
+    /*48k*/
+    {12288000, 48000, 0, 0},
+    {18432000, 48000, 0, 1},
+    {24576000, 48000, 0, 2},
+    {36864000, 48000, 0, 3},
+    {49152000, 48000, 0, 4},
+
+    /*64k*/
+    {8192000, 64000, 1, 0},
+    {12288000, 64000, 1, 1},
+    {16934400, 64000, 1, 2},
+    {24576000, 64000, 1, 3},
+    {32768000, 64000, 1, 4},
+
+    /* 88.2k */
+    {11289600, 88200, 1, 0},
+    {16934400, 88200, 1, 1},
+    {22579200, 88200, 1, 2},
+    {33868000, 88200, 1, 3},
+    {45158400, 88200, 1, 4},
+
+    /* 96k */
+    {12288000, 96000, 1, 0},
+    {18432000, 96000, 1, 1},
+    {24576000, 96000, 1, 2},
+    {36864000, 96000, 1, 3},
+    {49152000, 96000, 1, 4},
+
+    /* 128k */
+    {8192000, 128000, 2, 0},
+    {12288000, 128000, 2, 1},
+    {16934400, 128000, 2, 2},
+    {24576000, 128000, 2, 3},
+    {32768000, 128000, 2, 4},
+
+    /* 176.4k */
+    {11289600, 176400, 2, 0},
+    {16934400, 176400, 2, 1},
+    {22579200, 176400, 2, 2},
+    {33868000, 176400, 2, 3},
+    {49152000, 176400, 2, 4},
+
+    /* 192k */
+    {12288000, 192000, 2, 0},
+    {18432000, 192000, 2, 1},
+    {24576000, 192000, 2, 2},
+    {36864000, 192000, 2, 3},
+    {49152000, 192000, 2, 4},
 };
 };
 static const struct cs4265_cmd_s cs4265_init_sequence[] = {
 static const struct cs4265_cmd_s cs4265_init_sequence[] = {
-	{CS4265_PWRCTL, CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_FREEZE | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC},
- 	{CS4265_DAC_CTL, CS4265_DAC_CTL_DIF0 | CS4265_DAC_CTL_MUTE}, 
- 	{CS4265_SIG_SEL, CS4265_SIG_SEL_SDIN1},/// SDIN1
- 	{CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_DIF0 },//
- 	{CS4265_ADC_CTL, 0x00 },// // Set the serial audio port in slave mode
- 	{CS4265_MCLK_FREQ, CS4265_MCLK_FREQ_1_0X },// // no divider 
- 	{CS4265_CHB_PGA_CTL, 0x00 },// // sets the gain to 0db on channel B
- 	{CS4265_CHA_PGA_CTL, 0x00 },// // sets the gain to 0db on channel A
- 	{CS4265_ADC_CTL2, 0x19 },//
- 	{CS4265_DAC_CHA_VOL,CS4265_DAC_VOL_ATT_000_0   },// Full volume out 
- 	{CS4265_DAC_CHB_VOL, CS4265_DAC_VOL_ATT_000_0 },// // Full volume out 
- 	{CS4265_DAC_CTL2, CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN },//
- 	{CS4265_SPDIF_CTL1, 0x00 },//
- 	{CS4265_INT_MASK, 0x00 },//
- 	{CS4265_STATUS_MODE_MSB, 0x00 },//
- 	{CS4265_STATUS_MODE_LSB, 0x00 },//
-	{0xff,0xff}
-};
-
+    {CS4265_PWRCTL, CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_FREEZE | CS4265_PWRCTL_PDN_DAC |
+                        CS4265_PWRCTL_PDN_MIC},
+    {CS4265_DAC_CTL, CS4265_DAC_CTL_DIF0 | CS4265_DAC_CTL_MUTE},
+    {CS4265_SIG_SEL, CS4265_SIG_SEL_SDIN1},         /// SDIN1
+    {CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_DIF0},    //
+    {CS4265_ADC_CTL, 0x00},                         // // Set the serial audio port in slave mode
+    {CS4265_MCLK_FREQ, CS4265_MCLK_FREQ_1_0X},      // // no divider
+    {CS4265_CHB_PGA_CTL, 0x00},                     // // sets the gain to 0db on channel B
+    {CS4265_CHA_PGA_CTL, 0x00},                     // // sets the gain to 0db on channel A
+    {CS4265_ADC_CTL2, 0x19},                        //
+    {CS4265_DAC_CHA_VOL, CS4265_DAC_VOL_ATT_000_0}, // Full volume out
+    {CS4265_DAC_CHB_VOL, CS4265_DAC_VOL_ATT_000_0}, // // Full volume out
+    {CS4265_DAC_CTL2, CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN}, //
+    {CS4265_SPDIF_CTL1, 0x00},                                  //
+    {CS4265_INT_MASK, 0x00},                                    //
+    {CS4265_STATUS_MODE_MSB, 0x00},                             //
+    {CS4265_STATUS_MODE_LSB, 0x00},                             //
+    {0xff, 0xff}};
 
 
 // matching orders
 // matching orders
-typedef enum { cs4265_ACTIVE = 0, cs4265_STANDBY, cs4265_DOWN, cs4265_ANALOGUE_OFF, cs4265_ANALOGUE_ON, cs4265_VOLUME } dac_cmd_e;
-
-
+typedef enum {
+    cs4265_ACTIVE = 0,
+    cs4265_STANDBY,
+    cs4265_DOWN,
+    cs4265_ANALOGUE_OFF,
+    cs4265_ANALOGUE_ON,
+    cs4265_VOLUME
+} dac_cmd_e;
 
 
 static int cs4265_addr;
 static int cs4265_addr;
 
 
 static void dac_cmd(dac_cmd_e cmd, ...);
 static void dac_cmd(dac_cmd_e cmd, ...);
 static int cs4265_detect(void);
 static int cs4265_detect(void);
-static uint32_t calc_rnd_mclk_freq(){
-	float m_scale = (cs4265.i2s_config->sample_rate > 96000 && cs4265.i2s_config->bits_per_sample > 16) ? 4 : 8;
-	float num_channels = cs4265.i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1;
-     return (uint32_t) round(cs4265.i2s_config->bits_per_sample*i2s_get_clk(cs4265.i2c_port)* m_scale*num_channels/100)*100;
+static uint32_t calc_rnd_mclk_freq() {
+    float m_scale =
+        (cs4265.i2s_config->sample_rate > 96000 && cs4265.i2s_config->bits_per_sample > 16) ? 4 : 8;
+    float num_channels = cs4265.i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1;
+    return (uint32_t)round(cs4265.i2s_config->bits_per_sample * i2s_get_clk(cs4265.port) *
+                           m_scale * num_channels / 100) *
+           100;
 }
 }
-static int cs4265_get_clk_index(int mclk, int rate)
-{
-	for (int i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
-		if (clk_map_table[i].rate == rate &&
-				clk_map_table[i].mclk == mclk)
-			return i;
-	}
-	return -1;
+static int cs4265_get_clk_index(int mclk, int rate) {
+    for (int i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+        if (clk_map_table[i].rate == rate && clk_map_table[i].mclk == mclk) return i;
+    }
+    return -1;
 }
 }
 
 
-static esp_err_t set_clock(){
+static esp_err_t set_clock() {
     esp_err_t err = ESP_OK;
     esp_err_t err = ESP_OK;
-	uint32_t mclk = calc_rnd_mclk_freq();
-    int index = cs4265_get_clk_index(mclk,cs4265.i2s_config->sample_rate );
-	if (index >= 0) {
-        ESP_LOGD(TAG, "Setting clock for mclk %u, rate %u (fm mode:%u, clk div:%u))", mclk,cs4265.i2s_config->sample_rate,clk_map_table[index].fm_mode,clk_map_table[index].mclkdiv);
-		err=cs4265_update_bit(CS4265_ADC_CTL,CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
-		err|=cs4265_update_bit( CS4265_MCLK_FREQ,CS4265_MCLK_FREQ_MASK,clk_map_table[index].mclkdiv << 4);
-	} else {
-		ESP_LOGE(TAG,"can't get correct mclk for ");
-		return -1;
-	}
+    uint32_t mclk = calc_rnd_mclk_freq();
+    int index = cs4265_get_clk_index(mclk, cs4265.i2s_config->sample_rate);
+    if (index >= 0) {
+        ESP_LOGD(TAG, "Setting clock for mclk %u, rate %u (fm mode:%u, clk div:%u))", mclk,
+            cs4265.i2s_config->sample_rate, clk_map_table[index].fm_mode,
+            clk_map_table[index].mclkdiv);
+        err = cs4265_update_bit(CS4265_ADC_CTL, CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
+        err |= cs4265_update_bit(
+            CS4265_MCLK_FREQ, CS4265_MCLK_FREQ_MASK, clk_map_table[index].mclkdiv << 4);
+    } else {
+        ESP_LOGE(TAG, "can't get correct mclk for ");
+        return -1;
+    }
     return err;
     return err;
 }
 }
 
 
-
-static void get_status(){
-    uint8_t sts1= adac_read_byte(cs4265_addr, CS4265_INT_STATUS);	
-    ESP_LOGD(TAG,"Status: %s",sts1&CS4265_INT_STATUS_CLKERR?"CLK Error":"CLK OK");
+static void get_status() {
+    uint8_t sts1 = adac_read_byte(cs4265_addr, CS4265_INT_STATUS);
+    ESP_LOGD(TAG, "Status: %s", sts1 & CS4265_INT_STATUS_CLKERR ? "CLK Error" : "CLK OK");
 }
 }
 
 
-
 /****************************************************************************************
 /****************************************************************************************
  * init
  * init
  */
  */
-static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {	 
-	// find which TAS we are using (if any)
-	cs4265_addr = adac_init(config, i2c_port);
-	cs4265.i2s_config = i2s_config;
-	cs4265.i2c_port=i2c_port;
-	if (!cs4265_addr) cs4265_addr = cs4265_detect();
-	if (!cs4265_addr) {
-		ESP_LOGE(TAG, "No cs4265 detected");
-		adac_deinit();
-		return false;
-	}
-	#if BYTES_PER_FRAME == 8
-		ESP_LOGE(TAG,"The CS4265 does not support 32 bits mode. ");
-		adac_deinit();
-		return false;
-	#endif	
-	// configure MLK
-    ESP_LOGD(TAG, "Configuring MCLK on GPIO0");
-	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
-   	REG_WRITE(PIN_CTRL, 0xFFFFFFF0);
-	i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
-	for (int i = 0; cs4265_init_sequence[i].reg != 0xff; i++) {
-		i2c_master_start(i2c_cmd);
-		i2c_master_write_byte(i2c_cmd, (cs4265_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
-		i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].reg, I2C_MASTER_NACK);
-		i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].value, I2C_MASTER_NACK);
-		ESP_LOGD(TAG, "i2c write %x at %u", cs4265_init_sequence[i].reg, cs4265_init_sequence[i].value);
-	}
-
-	i2c_master_stop(i2c_cmd);	
-	esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
-    i2c_cmd_link_delete(i2c_cmd);
-
-	if (res != ESP_OK) {
-		ESP_LOGE(TAG, "could not intialize cs4265 %d", res);
-		return false;
-	}	
+static bool init(sys_dac_config * config, i2s_config_t* i2s_config, bool* mck) {
+
+    // find which CS4265 we are using (if any)
+    cs4265_addr = adac_init(config);
+    cs4265.i2s_config = i2s_config;
+    if (!cs4265_addr) cs4265_addr = cs4265_detect();
+    if (!cs4265_addr) {
+        ESP_LOGE(TAG, "No cs4265 detected");
+        adac_deinit();
+        return false;
+    }
+	cs4265.port = config->i2c.port-sys_i2c_port_PORT0;
+#if BYTES_PER_FRAME == 8
+    ESP_LOGE(TAG, "The CS4265 does not support 32 bits mode. ");
+    adac_deinit();
+    return false;
+#endif
+    // we need mclk for this DAC
+    *mck = true;
+
+	// Initialize the chip
+    i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
+    for (int i = 0; cs4265_init_sequence[i].reg != 0xff; i++) {
+        i2c_master_start(i2c_cmd);
+        i2c_master_write_byte(i2c_cmd, (cs4265_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
+        i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].reg, I2C_MASTER_NACK);
+        i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].value, I2C_MASTER_NACK);
+        ESP_LOGD(
+            TAG, "i2c write %x at %u", cs4265_init_sequence[i].reg, cs4265_init_sequence[i].value);
+    }
 	
 	
-	return true;
+    i2c_master_stop(i2c_cmd);
+    esp_err_t res = i2c_master_cmd_begin(cs4265.port, i2c_cmd, 500 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(i2c_cmd);
+    if (res != ESP_OK) {
+        ESP_LOGE(TAG, "could not intialize cs4265 %d", res);
+        return false;
+    }
 
 
-}	
+    return true;
+}
 
 
-static esp_err_t cs4265_update_bit(uint8_t reg_no,uint8_t mask,uint8_t val ){
-    esp_err_t ret=ESP_OK;
-    uint8_t old= adac_read_byte(cs4265_addr, reg_no);
+static esp_err_t cs4265_update_bit(uint8_t reg_no, uint8_t mask, uint8_t val) {
+    esp_err_t ret = ESP_OK;
+    uint8_t old = adac_read_byte(cs4265_addr, reg_no);
     uint8_t newval = (old & ~mask) | (val & mask);
     uint8_t newval = (old & ~mask) | (val & mask);
-	bool change = old != newval;
-	if (change){
-		ret = adac_write_byte(cs4265_addr, reg_no, newval);
-		if(ret != ESP_OK){
-        	ESP_LOGE(TAG,"Unable to change dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x  ",reg_no,old,newval,val,mask);
-    	}
-    	else {
-	        ESP_LOGD(TAG,"Changed dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x ",reg_no,old,newval,val,mask);
-	    }
-	}
-	
+    bool change = old != newval;
+    if (change) {
+        ret = adac_write_byte(cs4265_addr, reg_no, newval);
+        if (ret != ESP_OK) {
+            ESP_LOGE(TAG,
+                "Unable to change dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask "
+                "0x%02x  ",
+                reg_no, old, newval, val, mask);
+        } else {
+            ESP_LOGD(TAG,
+                "Changed dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x ",
+                reg_no, old, newval, val, mask);
+        }
+    }
+
     return ret;
     return ret;
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * change volume
  * change volume
  */
  */
-static bool volume(unsigned left, unsigned right) { 
-	return false; 
-}
+static bool volume(unsigned left, unsigned right) { return false; }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * power
  * power
  */
  */
 static void power(adac_power_e mode) {
 static void power(adac_power_e mode) {
-	switch(mode) {
-	case ADAC_STANDBY:
-		dac_cmd(cs4265_STANDBY);
-		break;
-	case ADAC_ON:
-		dac_cmd(cs4265_ACTIVE);
-		break;		
-	case ADAC_OFF:
-		dac_cmd(cs4265_DOWN);
-		break;				
-	default:
-		ESP_LOGW(TAG, "unknown DAC command");
-		break;
-	}
+    switch (mode) {
+    case ADAC_STANDBY:
+        dac_cmd(cs4265_STANDBY);
+        break;
+    case ADAC_ON:
+        dac_cmd(cs4265_ACTIVE);
+        break;
+    case ADAC_OFF:
+        dac_cmd(cs4265_DOWN);
+        break;
+    default:
+        ESP_LOGW(TAG, "unknown DAC command");
+        break;
+    }
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * speaker
  * speaker
  */
  */
 static void speaker(bool active) {
 static void speaker(bool active) {
-	if (active) dac_cmd(cs4265_ANALOGUE_ON);
-	else dac_cmd(cs4265_ANALOGUE_OFF);
-} 
+    if (active)
+        dac_cmd(cs4265_ANALOGUE_ON);
+    else
+        dac_cmd(cs4265_ANALOGUE_OFF);
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * headset
  * headset
  */
  */
-static void headset(bool active) { } 
- 
+static void headset(bool active) {}
+
 /****************************************************************************************
 /****************************************************************************************
  * DAC specific commands
  * DAC specific commands
  */
  */
 void dac_cmd(dac_cmd_e cmd, ...) {
 void dac_cmd(dac_cmd_e cmd, ...) {
-	va_list args;
-	esp_err_t ret = ESP_OK;
-	
-	va_start(args, cmd);
+    va_list args;
+    esp_err_t ret = ESP_OK;
+
+    va_start(args, cmd);
 
 
-	switch(cmd) {
-	case cs4265_VOLUME:
-		ESP_LOGE(TAG, "DAC volume not handled yet");
-		break;
+    switch (cmd) {
+    case cs4265_VOLUME:
+        ESP_LOGE(TAG, "DAC volume not handled yet");
+        break;
     case cs4265_ACTIVE:
     case cs4265_ACTIVE:
-		ESP_LOGD(TAG, "Activating DAC");
-        adac_write_byte(cs4265_addr, CS4265_PWRCTL,0);
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,0);
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,0);
-		cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,0);				
-		break;
+        ESP_LOGD(TAG, "Activating DAC");
+        adac_write_byte(cs4265_addr, CS4265_PWRCTL, 0);
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, 0);
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, 0);
+        cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, 0);
+        break;
     case cs4265_STANDBY:
     case cs4265_STANDBY:
-		ESP_LOGD(TAG, "DAC Stand-by");
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,CS4265_SPDIF_CTL2_TXOFF);
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,CS4265_SPDIF_CTL2_TXMUTE);
-		cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,CS4265_DAC_CTL_MUTE);
-		break;
+        ESP_LOGD(TAG, "DAC Stand-by");
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, CS4265_SPDIF_CTL2_TXOFF);
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, CS4265_SPDIF_CTL2_TXMUTE);
+        cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, CS4265_DAC_CTL_MUTE);
+        break;
     case cs4265_DOWN:
     case cs4265_DOWN:
-		ESP_LOGD(TAG, "DAC Power Down");
-        adac_write_byte(cs4265_addr, CS4265_PWRCTL,CS4265_PWRCTL_PDN_ALL);
-		break;
+        ESP_LOGD(TAG, "DAC Power Down");
+        adac_write_byte(cs4265_addr, CS4265_PWRCTL, CS4265_PWRCTL_PDN_ALL);
+        break;
     case cs4265_ANALOGUE_OFF:
     case cs4265_ANALOGUE_OFF:
-		ESP_LOGD(TAG, "DAC Analog off");
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,CS4265_SPDIF_CTL2_TXOFF);
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,CS4265_SPDIF_CTL2_TXMUTE);
-		cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,CS4265_DAC_CTL_MUTE);
-		break;
+        ESP_LOGD(TAG, "DAC Analog off");
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, CS4265_SPDIF_CTL2_TXOFF);
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, CS4265_SPDIF_CTL2_TXMUTE);
+        cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, CS4265_DAC_CTL_MUTE);
+        break;
     case cs4265_ANALOGUE_ON:
     case cs4265_ANALOGUE_ON:
-		ESP_LOGD(TAG, "DAC Analog on");
-		adac_write_byte(cs4265_addr, CS4265_PWRCTL,0);
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,0);
-        cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,0);
-		cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,0);		
-		break;
-	}
-	
-  	if (ret != ESP_OK) {
-		ESP_LOGE(TAG, "could not use cs4265 %d", ret);
-	}
+        ESP_LOGD(TAG, "DAC Analog on");
+        adac_write_byte(cs4265_addr, CS4265_PWRCTL, 0);
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, 0);
+        cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, 0);
+        cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, 0);
+        break;
+    }
+
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "could not use cs4265 %d", ret);
+    }
     get_status();
     get_status();
-	// now set the clock
-	ret=set_clock(cs4265.i2s_config,cs4265.i2c_port);
-	if (ret != ESP_OK) {
-		ESP_LOGE(TAG, "could not set the cs4265's clock %d", ret);
-	}	
+    // now set the clock
+    ret = set_clock();
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "could not set the cs4265's clock %d", ret);
+    }
 
 
-	va_end(args);
+    va_end(args);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * TAS57 detection
  * TAS57 detection
  */
  */
 static int cs4265_detect(void) {
 static int cs4265_detect(void) {
-	uint8_t addr[] = {CS4265_PULL_DOWN,CS4265_PULL_UP};
-	
-	for (int i = 0; i < sizeof(addr); i++) {
-		ESP_LOGI(TAG,"Looking for CS4265 @0x%x",addr[i]);
-		uint8_t reg=adac_read_byte(addr[i], CS4265_CHIP_ID);
-		if(reg==255){
-			continue;
-		}
-			// found a device at that address
-		uint8_t devid = reg & CS4265_CHIP_ID_MASK;
-		if (devid != CS4265_CHIP_ID_VAL) {
-			ESP_LOGE(TAG,"CS4265 Device ID (%X). Expected %X",devid, CS4265_CHIP_ID);
-			return 0;
-		}
-		ESP_LOGI(TAG,"Found DAC @0x%x, Version %x",addr[i], reg & CS4265_REV_ID_MASK);
-		return addr[i];	
-	}
-	return 0;
+    uint8_t addr[] = {CS4265_PULL_DOWN, CS4265_PULL_UP};
+    ESP_LOGD(TAG, "Detecting chip connection type/address");
+    for (int i = 0; i < sizeof(addr); i++) {
+        ESP_LOGI(TAG, "Looking for CS4265 @0x%x", addr[i]);
+        uint8_t reg = adac_read_byte(addr[i], CS4265_CHIP_ID);
+        if (reg == 255) {
+            ESP_LOGD(TAG, "Nothing there");
+            continue;
+        }
+        // found a device at that address
+        ESP_LOGD(TAG, "Found a device. Check signature");
+        uint8_t devid = reg & CS4265_CHIP_ID_MASK;
+        if (devid != CS4265_CHIP_ID_VAL) {
+            ESP_LOGE(TAG, "CS4265 Device ID (%X). Expected %X", devid, CS4265_CHIP_ID);
+            return 0;
+        }
+        ESP_LOGI(TAG, "Found DAC @0x%x, Version %x", addr[i], reg & CS4265_REV_ID_MASK);
+        return addr[i];
+    }
+    return 0;
 }
 }
-

+ 396 - 383
components/squeezelite/decode_external.c

@@ -1,4 +1,4 @@
-/* 
+/*
  *  Squeezelite for esp32
  *  Squeezelite for esp32
  *
  *
  *  (c) Sebastien 2019
  *  (c) Sebastien 2019
@@ -11,14 +11,16 @@
 
 
 #include <math.h>
 #include <math.h>
 #ifdef ESP_PLATFORM
 #ifdef ESP_PLATFORM
+#include "Services.pb.h"
+#include "Squeezelite.pb.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/timers.h"
 #include "freertos/timers.h"
+extern sys_squeezelite_config* get_profile(const char* name);
 #endif
 #endif
-#include "Configurator.h"
+#include "Config.h"
 #include "accessors.h"
 #include "accessors.h"
 #include "squeezelite.h"
 #include "squeezelite.h"
 
 
-
 #if CONFIG_BT_SINK
 #if CONFIG_BT_SINK
 #include "bt_app_sink.h"
 #include "bt_app_sink.h"
 static bool enable_bt_sink;
 static bool enable_bt_sink;
@@ -34,91 +36,93 @@ static bool enable_cspot;
 static bool enable_airplay;
 static bool enable_airplay;
 
 
 #define RAOP_OUTPUT_SIZE (((RAOP_SAMPLE_RATE * BYTES_PER_FRAME * 2 * 120) / 100) & ~BYTES_PER_FRAME)
 #define RAOP_OUTPUT_SIZE (((RAOP_SAMPLE_RATE * BYTES_PER_FRAME * 2 * 120) / 100) & ~BYTES_PER_FRAME)
-#define SYNC_WIN_SLOW	32
-#define SYNC_WIN_CHECK	8
-#define SYNC_WIN_FAST	2
+#define SYNC_WIN_SLOW 32
+#define SYNC_WIN_CHECK 8
+#define SYNC_WIN_FAST 2
 
 
-static raop_event_t	raop_state;
-static sys_Squeezelite * squeezelite;
+static raop_event_t raop_state;
+static sys_squeezelite_config* squeezelite;
 
 
 static EXT_RAM_ATTR struct {
 static EXT_RAM_ATTR struct {
-	bool enabled;
-	int sum, count, win, errors[SYNC_WIN_SLOW];
-	s32_t len;
-	u32_t start_time, playtime;
+    bool enabled;
+    int sum, count, win, errors[SYNC_WIN_SLOW];
+    s32_t len;
+    u32_t start_time, playtime;
 } raop_sync;
 } raop_sync;
 #endif
 #endif
 
 
 static enum { SINK_RUNNING, SINK_ABORT, SINK_DISCARD } sink_state;
 static enum { SINK_RUNNING, SINK_ABORT, SINK_DISCARD } sink_state;
 
 
-#define LOCK_O   mutex_lock(outputbuf->mutex)
+#define LOCK_O mutex_lock(outputbuf->mutex)
 #define UNLOCK_O mutex_unlock(outputbuf->mutex)
 #define UNLOCK_O mutex_unlock(outputbuf->mutex)
-#define LOCK_D   mutex_lock(decode.mutex);
+#define LOCK_D mutex_lock(decode.mutex);
 #define UNLOCK_D mutex_unlock(decode.mutex);
 #define UNLOCK_D mutex_unlock(decode.mutex);
 
 
 enum { DECODE_BT = 1, DECODE_RAOP, DECODE_CSPOT };
 enum { DECODE_BT = 1, DECODE_RAOP, DECODE_CSPOT };
 
 
 extern struct outputstate output;
 extern struct outputstate output;
 extern struct decodestate decode;
 extern struct decodestate decode;
-extern struct buffer *outputbuf;
+extern struct buffer* outputbuf;
 // this is the only system-wide loglevel variable
 // this is the only system-wide loglevel variable
 extern log_level loglevel;
 extern log_level loglevel;
 
 
 /****************************************************************************************
 /****************************************************************************************
  * Common sink data handler
  * Common sink data handler
  */
  */
-static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries)
-{
+static uint32_t sink_data_handler(const uint8_t* data, uint32_t len, int retries) {
     size_t bytes, space;
     size_t bytes, space;
-    uint32_t written = 0;    
-	int wait = retries + 1;
-		
-	// would be better to lock output, but really, it does not matter
-	if (!output.external) {
-		LOG_SDEBUG("Cannot use external sink while LMS is controlling player");
-		return 0;
-	} 
-
-	LOCK_O;
-	if (sink_state == SINK_ABORT) sink_state = SINK_RUNNING;
-
-	// there will always be room at some point
-	while (len && wait && sink_state == SINK_RUNNING) {
-		bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4);
-		bytes = min(len, bytes);
+    uint32_t written = 0;
+    int wait = retries + 1;
+
+    // would be better to lock output, but really, it does not matter
+    if (!output.external) {
+        LOG_SDEBUG("Cannot use external sink while LMS is controlling player");
+        return 0;
+    }
+
+    LOCK_O;
+    if (sink_state == SINK_ABORT) sink_state = SINK_RUNNING;
+
+    // there will always be room at some point
+    while (len && wait && sink_state == SINK_RUNNING) {
+        bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4);
+        bytes = min(len, bytes);
 #if BYTES_PER_FRAME == 4
 #if BYTES_PER_FRAME == 4
-		memcpy(outputbuf->writep, data, bytes);
+        memcpy(outputbuf->writep, data, bytes);
 #else
 #else
-		{
-			s16_t *iptr = (s16_t*) data;
-			ISAMPLE_T *optr = (ISAMPLE_T *) outputbuf->writep;
-			size_t n = bytes / 2;
-			while (n--) *optr++ = *iptr++ << 16;
-		}
-#endif	
-		_buf_inc_writep(outputbuf, bytes * BYTES_PER_FRAME / 4);
-		space = _buf_space(outputbuf);
-
-		len -= bytes;
-		data += bytes;
+        {
+            s16_t* iptr = (s16_t*)data;
+            ISAMPLE_T* optr = (ISAMPLE_T*)outputbuf->writep;
+            size_t n = bytes / 2;
+            while (n--)
+                *optr++ = *iptr++ << 16;
+        }
+#endif
+        _buf_inc_writep(outputbuf, bytes * BYTES_PER_FRAME / 4);
+        space = _buf_space(outputbuf);
+
+        len -= bytes;
+        data += bytes;
         written += bytes;
         written += bytes;
-				
-		// allow i2s to empty the buffer if needed
-		if (len && !space) {
+
+        // allow i2s to empty the buffer if needed
+        if (len && !space) {
             if (!retries) break;
             if (!retries) break;
-			wait--;
-			UNLOCK_O; usleep(50000); LOCK_O;
-		}
-	}	
+            wait--;
+            UNLOCK_O;
+            usleep(50000);
+            LOCK_O;
+        }
+    }
 
 
-	if (!wait) {
+    if (!wait) {
         // re-align the buffer according to what we threw away
         // re-align the buffer according to what we threw away
         _buf_inc_writep(outputbuf, outputbuf->size - (BYTES_PER_FRAME - (len % BYTES_PER_FRAME)));
         _buf_inc_writep(outputbuf, outputbuf->size - (BYTES_PER_FRAME - (len % BYTES_PER_FRAME)));
-		LOG_WARN("Waited too long, dropping frames %d", len);
-	}
-    
+        LOG_WARN("Waited too long, dropping frames %d", len);
+    }
+
     UNLOCK_O;
     UNLOCK_O;
-    
+
     return written;
     return written;
 }
 }
 
 
@@ -126,77 +130,77 @@ static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries
  * BT sink data handler
  * BT sink data handler
  */
  */
 #if CONFIG_BT_SINK
 #if CONFIG_BT_SINK
-static void bt_sink_data_handler(const uint8_t *data, uint32_t len) {
+static void bt_sink_data_handler(const uint8_t* data, uint32_t len) {
     sink_data_handler(data, len, 10);
     sink_data_handler(data, len, 10);
-}    
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * BT sink command handler
  * BT sink command handler
  */
  */
-static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) 
-{
-	// don't LOCK_O as there is always a chance that LMS takes control later anyway
-	if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
-		LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player %d", output.external);
-		return false;
-	} 	
-
-	LOCK_D;
-
-	if (cmd != BT_SINK_VOLUME) LOCK_O;
-		
-	switch(cmd) {
-	case BT_SINK_AUDIO_STARTED:
-		_buf_flush(outputbuf);
-		_buf_limit(outputbuf, 0);
-		output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
-		output.external = DECODE_BT;
-		output.state = OUTPUT_STOPPED;
-		output.frames_played = 0;
-		if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
-		LOG_INFO("BT sink started");
-		break;
-	case BT_SINK_AUDIO_STOPPED:	
-		if (output.external == DECODE_BT) {
-			if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
-			output.external = 0;
-			output.stop_time = gettime_ms();
-			LOG_INFO("BT sink stopped");
-		}	
-		break;
-	case BT_SINK_PLAY:
-		output.state = OUTPUT_RUNNING;
-		LOG_INFO("BT play");
-		break;
-	case BT_SINK_STOP:		
-		_buf_flush(outputbuf);
-		output.state = OUTPUT_STOPPED;
-		output.stop_time = gettime_ms();
-		sink_state = SINK_ABORT;
-		LOG_INFO("BT stop");
-		break;
-	case BT_SINK_PAUSE:		
-		output.stop_time = gettime_ms();
-		LOG_INFO("BT pause, just silence");
-		break;
-	case BT_SINK_RATE:
-		output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
-		LOG_INFO("Setting BT sample rate %u", output.next_sample_rate);
-		break;
-	case BT_SINK_VOLUME: {
-		u32_t volume = va_arg(args, u32_t);
-		volume = 65536 * powf(volume / 128.0f, 3);
-		set_volume(volume, volume);
-		break;
-	default:
-		break;
-	}
-	}
-	
-	if (cmd != BT_SINK_VOLUME) UNLOCK_O;
-	UNLOCK_D;
-
-	return true;
+static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) {
+    // don't LOCK_O as there is always a chance that LMS takes control later anyway
+    if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
+        LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player %d",
+            output.external);
+        return false;
+    }
+
+    LOCK_D;
+
+    if (cmd != BT_SINK_VOLUME) LOCK_O;
+
+    switch (cmd) {
+    case BT_SINK_AUDIO_STARTED:
+        _buf_flush(outputbuf);
+        _buf_limit(outputbuf, 0);
+        output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
+        output.external = DECODE_BT;
+        output.state = OUTPUT_STOPPED;
+        output.frames_played = 0;
+        if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
+        LOG_INFO("BT sink started");
+        break;
+    case BT_SINK_AUDIO_STOPPED:
+        if (output.external == DECODE_BT) {
+            if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
+            output.external = 0;
+            output.stop_time = gettime_ms();
+            LOG_INFO("BT sink stopped");
+        }
+        break;
+    case BT_SINK_PLAY:
+        output.state = OUTPUT_RUNNING;
+        LOG_INFO("BT play");
+        break;
+    case BT_SINK_STOP:
+        _buf_flush(outputbuf);
+        output.state = OUTPUT_STOPPED;
+        output.stop_time = gettime_ms();
+        sink_state = SINK_ABORT;
+        LOG_INFO("BT stop");
+        break;
+    case BT_SINK_PAUSE:
+        output.stop_time = gettime_ms();
+        LOG_INFO("BT pause, just silence");
+        break;
+    case BT_SINK_RATE:
+        output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
+        LOG_INFO("Setting BT sample rate %u", output.next_sample_rate);
+        break;
+    case BT_SINK_VOLUME: {
+        u32_t volume = va_arg(args, u32_t);
+        volume = 65536 * powf(volume / 128.0f, 3);
+        set_volume(volume, volume);
+        break;
+    default:
+        break;
+    }
+    }
+
+    if (cmd != BT_SINK_VOLUME) UNLOCK_O;
+    UNLOCK_D;
+
+    return true;
 }
 }
 #endif
 #endif
 
 
@@ -204,144 +208,154 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
  * raop sink data handler
  * raop sink data handler
  */
  */
 #if CONFIG_AIRPLAY_SINK
 #if CONFIG_AIRPLAY_SINK
-static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t playtime) {
-	
-	raop_sync.playtime = playtime;
-	raop_sync.len = len;
+static void raop_sink_data_handler(const uint8_t* data, uint32_t len, u32_t playtime) {
 
 
-	sink_data_handler(data, len, 10);
-}	
+    raop_sync.playtime = playtime;
+    raop_sync.len = len;
+
+    sink_data_handler(data, len, 10);
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * AirPlay sink command handler
  * AirPlay sink command handler
  */
  */
-static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
-{
-	// don't LOCK_O as there is always a chance that LMS takes control later anyway
-	if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
-		LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player %d", output.external);
-		return false;
-	} 	
-
-	LOCK_D;
-	
-	if (event != RAOP_VOLUME) LOCK_O;
-	
-	// this is async, so player might have been deleted
-	switch (event) {
-		case RAOP_TIMING: {
-			if (!raop_sync.enabled || output.state != OUTPUT_RUNNING || output.frames_played_dmp < output.device_frames) break;
-
-			u32_t ms, now = gettime_ms();
-			u32_t level = _buf_used(outputbuf);
-			int error;
-				
-			// in how many ms will the most recent block play 
-			ms = (((s32_t)(level - raop_sync.len) / BYTES_PER_FRAME + output.device_frames + output.frames_in_process) * 10) / (RAOP_SAMPLE_RATE / 100) - (s32_t) (now - output.updated);
-				
-			// when outputbuf is empty, it means we have a network black-out or something
-			error = level ? (raop_sync.playtime - now) - ms : 0;
-				
-			if (loglevel == lDEBUG || !level) {
-				LOG_INFO("head local:%d, remote:%d (delta:%d)", ms, raop_sync.playtime - now, error);
-				LOG_INFO("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf), raop_sync.len, output.device_frames, output.frames_in_process);
-			}	
-			
-			// calculate sum, error and update sliding window
-			raop_sync.errors[raop_sync.count++ % raop_sync.win] = error;
-			raop_sync.sum += error;
-			error = raop_sync.sum / min(raop_sync.count, raop_sync.win);
-
-			// wait till we have enough data or there is a strong deviation
-			if ((raop_sync.count >= raop_sync.win && abs(error) > 10) || (raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) {
-				if (error < 0) {
-					output.skip_frames = -(error * RAOP_SAMPLE_RATE) / 1000;
-					output.state = OUTPUT_SKIP_FRAMES;					
-					LOG_INFO("skipping %u frames (count:%d)", output.skip_frames, raop_sync.count);
-				} else {
-					output.pause_frames = (error * RAOP_SAMPLE_RATE) / 1000;
-					output.state = OUTPUT_PAUSE_FRAMES;
-					LOG_INFO("pausing for %u frames (count: %d)", output.pause_frames, raop_sync.count);
-				}
-				
-				raop_sync.sum = raop_sync.count = 0;
-				memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
-			}	
-			
-			// move to normal mode if possible			
-			if (raop_sync.win == 1) {
-				raop_sync.win = SYNC_WIN_FAST;
-				LOG_INFO("backend played %u, desired %u, (delta:%d)", ms, raop_sync.playtime - now, error);
-			} else if (raop_sync.win == SYNC_WIN_FAST && raop_sync.count >= SYNC_WIN_FAST && abs(error) < 10) {
-				raop_sync.win = SYNC_WIN_SLOW;
-				LOG_INFO("switching to slow sync mode %u", raop_sync.win);
-			}	
-
-			break;
-		}
-		case RAOP_SETUP: {
-			uint8_t **buffer = va_arg(args, uint8_t**);
-			size_t *size = va_arg(args, size_t*);
-
-			// steal buffer tail from outputbuf but do not reallocate
-			*size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE);
-			*buffer = outputbuf->writep + RAOP_OUTPUT_SIZE;
-
-			output.frames_played = 0;
-			output.external = DECODE_RAOP;
-			output.state = OUTPUT_STOPPED;
-			if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
-			LOG_INFO("resizing buffer %u", outputbuf->size);
-			break;
-		}
-		case RAOP_STREAM:
-			LOG_INFO("Stream", NULL);
-			raop_state = event;
-			raop_sync.win = 1;
-			raop_sync.sum = raop_sync.count = 0;
-			memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
-			raop_sync.enabled = !strcasestr(output.device, "BT");
-			output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
-			break;
-        case RAOP_STALLED:
-		case RAOP_STOP:
-			output.external = 0;
-			__attribute__ ((fallthrough));
-		case RAOP_FLUSH:
-			LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop");
-			_buf_flush(outputbuf);
-			raop_state = event;
-			if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
-			sink_state = SINK_ABORT;
-			output.frames_played = 0;
-			output.stop_time = gettime_ms();
-			break;
-		case RAOP_PLAY: {
-			LOG_INFO("Play", NULL);
-			if (raop_state != RAOP_PLAY) {
-				output.state = OUTPUT_START_AT;
-				output.start_at = va_arg(args, u32_t);
-				raop_sync.start_time = output.start_at;
-				LOG_INFO("Starting at %u (in %d ms)", output.start_at, output.start_at - gettime_ms());
-			}
-			raop_state = event;
-			break;
-		}
-		case RAOP_VOLUME: {
-			float volume = va_arg(args, double);
-			LOG_INFO("Volume[0..1] %0.4f", volume);
-			volume = 65536 * powf(volume, 3);
-			set_volume(volume, volume);
-			break;
-		}
-		default:
-			break;
-	}
-	
-	if (event != RAOP_VOLUME) UNLOCK_O;
-	
-	UNLOCK_D;
-	return true;
+static bool raop_sink_cmd_handler(raop_event_t event, va_list args) {
+    // don't LOCK_O as there is always a chance that LMS takes control later anyway
+    if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
+        LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player %d",
+            output.external);
+        return false;
+    }
+
+    LOCK_D;
+
+    if (event != RAOP_VOLUME) LOCK_O;
+
+    // this is async, so player might have been deleted
+    switch (event) {
+    case RAOP_TIMING: {
+        if (!raop_sync.enabled || output.state != OUTPUT_RUNNING ||
+            output.frames_played_dmp < output.device_frames)
+            break;
+
+        u32_t ms, now = gettime_ms();
+        u32_t level = _buf_used(outputbuf);
+        int error;
+
+        // in how many ms will the most recent block play
+        ms = (((s32_t)(level - raop_sync.len) / BYTES_PER_FRAME + output.device_frames +
+                  output.frames_in_process) *
+                 10) /
+                 (RAOP_SAMPLE_RATE / 100) -
+             (s32_t)(now - output.updated);
+
+        // when outputbuf is empty, it means we have a network black-out or something
+        error = level ? (raop_sync.playtime - now) - ms : 0;
+
+        if (loglevel == lDEBUG || !level) {
+            LOG_INFO("head local:%d, remote:%d (delta:%d)", ms, raop_sync.playtime - now, error);
+            LOG_INFO("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf),
+                raop_sync.len, output.device_frames, output.frames_in_process);
+        }
+
+        // calculate sum, error and update sliding window
+        raop_sync.errors[raop_sync.count++ % raop_sync.win] = error;
+        raop_sync.sum += error;
+        error = raop_sync.sum / min(raop_sync.count, raop_sync.win);
+
+        // wait till we have enough data or there is a strong deviation
+        if ((raop_sync.count >= raop_sync.win && abs(error) > 10) ||
+            (raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) {
+            if (error < 0) {
+                output.skip_frames = -(error * RAOP_SAMPLE_RATE) / 1000;
+                output.state = OUTPUT_SKIP_FRAMES;
+                LOG_INFO("skipping %u frames (count:%d)", output.skip_frames, raop_sync.count);
+            } else {
+                output.pause_frames = (error * RAOP_SAMPLE_RATE) / 1000;
+                output.state = OUTPUT_PAUSE_FRAMES;
+                LOG_INFO("pausing for %u frames (count: %d)", output.pause_frames, raop_sync.count);
+            }
+
+            raop_sync.sum = raop_sync.count = 0;
+            memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
+        }
+
+        // move to normal mode if possible
+        if (raop_sync.win == 1) {
+            raop_sync.win = SYNC_WIN_FAST;
+            LOG_INFO(
+                "backend played %u, desired %u, (delta:%d)", ms, raop_sync.playtime - now, error);
+        } else if (raop_sync.win == SYNC_WIN_FAST && raop_sync.count >= SYNC_WIN_FAST &&
+                   abs(error) < 10) {
+            raop_sync.win = SYNC_WIN_SLOW;
+            LOG_INFO("switching to slow sync mode %u", raop_sync.win);
+        }
+
+        break;
+    }
+    case RAOP_SETUP: {
+        uint8_t** buffer = va_arg(args, uint8_t**);
+        size_t* size = va_arg(args, size_t*);
+
+        // steal buffer tail from outputbuf but do not reallocate
+        *size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE);
+        *buffer = outputbuf->writep + RAOP_OUTPUT_SIZE;
+
+        output.frames_played = 0;
+        output.external = DECODE_RAOP;
+        output.state = OUTPUT_STOPPED;
+        if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
+        LOG_INFO("resizing buffer %u", outputbuf->size);
+        break;
+    }
+    case RAOP_STREAM:
+        LOG_INFO("Stream", NULL);
+        raop_state = event;
+        raop_sync.win = 1;
+        raop_sync.sum = raop_sync.count = 0;
+        memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
+        raop_sync.enabled = !strcasestr(output.device, "BT");
+        output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
+        break;
+    case RAOP_STALLED:
+    case RAOP_STOP:
+        output.external = 0;
+        __attribute__((fallthrough));
+    case RAOP_FLUSH:
+        LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop");
+        _buf_flush(outputbuf);
+        raop_state = event;
+        if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
+        sink_state = SINK_ABORT;
+        output.frames_played = 0;
+        output.stop_time = gettime_ms();
+        break;
+    case RAOP_PLAY: {
+        LOG_INFO("Play", NULL);
+        if (raop_state != RAOP_PLAY) {
+            output.state = OUTPUT_START_AT;
+            output.start_at = va_arg(args, u32_t);
+            raop_sync.start_time = output.start_at;
+            LOG_INFO("Starting at %u (in %d ms)", output.start_at, output.start_at - gettime_ms());
+        }
+        raop_state = event;
+        break;
+    }
+    case RAOP_VOLUME: {
+        float volume = va_arg(args, double);
+        LOG_INFO("Volume[0..1] %0.4f", volume);
+        volume = 65536 * powf(volume, 3);
+        set_volume(volume, volume);
+        break;
+    }
+    default:
+        break;
+    }
+
+    if (event != RAOP_VOLUME) UNLOCK_O;
+
+    UNLOCK_D;
+    return true;
 }
 }
 #endif
 #endif
 
 
@@ -349,98 +363,98 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
  * cspot sink data handler
  * cspot sink data handler
  */
  */
 #if CONFIG_CSPOT_SINK
 #if CONFIG_CSPOT_SINK
-static uint32_t cspot_sink_data_handler(const uint8_t *data, uint32_t len) {
+static uint32_t cspot_sink_data_handler(const uint8_t* data, uint32_t len) {
     return sink_data_handler(data, len, 0);
     return sink_data_handler(data, len, 0);
-}    
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * cspot sink command handler
  * cspot sink command handler
  */
  */
 
 
-static bool cspot_cmd_handler(cspot_event_t cmd, va_list args) 
-{
-	// don't LOCK_O as there is always a chance that LMS takes control later anyway
-	if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) {
-		LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player %d", output.external);
-		return false;
-	} 	
-
-	LOCK_D;
-	
-	if (cmd != CSPOT_VOLUME) LOCK_O;
-
-	switch(cmd) {
-	case CSPOT_START:
-		output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t);
-		output.external = DECODE_CSPOT;
-		output.frames_played = 0;
+static bool cspot_cmd_handler(cspot_event_t cmd, va_list args) {
+    // don't LOCK_O as there is always a chance that LMS takes control later anyway
+    if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) {
+        LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player %d",
+            output.external);
+        return false;
+    }
+
+    LOCK_D;
+
+    if (cmd != CSPOT_VOLUME) LOCK_O;
+
+    switch (cmd) {
+    case CSPOT_START:
+        output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t);
+        output.external = DECODE_CSPOT;
+        output.frames_played = 0;
         // in 1/10 of seconds
         // in 1/10 of seconds
         output.threshold = 25;
         output.threshold = 25;
-		output.state = OUTPUT_STOPPED;
+        output.state = OUTPUT_STOPPED;
         sink_state = SINK_ABORT;
         sink_state = SINK_ABORT;
-		_buf_flush(outputbuf);
-		if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
-		LOG_INFO("CSpot start track");
-		break;
-	case CSPOT_DISC:
-		_buf_flush(outputbuf);
-		sink_state = SINK_ABORT;
-		output.external = 0;
-		output.state = OUTPUT_STOPPED;
-		output.stop_time = gettime_ms();
-		LOG_INFO("CSpot disconnected");
-		break;
-	case CSPOT_PLAY:
-		sink_state = SINK_RUNNING;			
-		output.state = OUTPUT_RUNNING;
-		LOG_INFO("CSpot play");
-		break;
-	case CSPOT_SEEK:
-		_buf_flush(outputbuf);		
-		sink_state = SINK_ABORT;
-		LOG_INFO("CSpot seek by %d", va_arg(args, uint32_t));
-		break;
-	case CSPOT_FLUSH:
-		_buf_flush(outputbuf);
-		sink_state = SINK_DISCARD;
-		output.state = OUTPUT_STOPPED;
-		LOG_INFO("CSpot flush");	
-		break;		
-	case CSPOT_PAUSE:		
-		output.state = OUTPUT_STOPPED;
-		output.stop_time = gettime_ms();
-		LOG_INFO("CSpot pause");
-		break;
+        _buf_flush(outputbuf);
+        if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
+        LOG_INFO("CSpot start track");
+        break;
+    case CSPOT_DISC:
+        _buf_flush(outputbuf);
+        sink_state = SINK_ABORT;
+        output.external = 0;
+        output.state = OUTPUT_STOPPED;
+        output.stop_time = gettime_ms();
+        LOG_INFO("CSpot disconnected");
+        break;
+    case CSPOT_PLAY:
+        sink_state = SINK_RUNNING;
+        output.state = OUTPUT_RUNNING;
+        LOG_INFO("CSpot play");
+        break;
+    case CSPOT_SEEK:
+        _buf_flush(outputbuf);
+        sink_state = SINK_ABORT;
+        LOG_INFO("CSpot seek by %d", va_arg(args, uint32_t));
+        break;
+    case CSPOT_FLUSH:
+        _buf_flush(outputbuf);
+        sink_state = SINK_DISCARD;
+        output.state = OUTPUT_STOPPED;
+        LOG_INFO("CSpot flush");
+        break;
+    case CSPOT_PAUSE:
+        output.state = OUTPUT_STOPPED;
+        output.stop_time = gettime_ms();
+        LOG_INFO("CSpot pause");
+        break;
     case CSPOT_TRACK_MARK:
     case CSPOT_TRACK_MARK:
         output.track_start = outputbuf->writep;
         output.track_start = outputbuf->writep;
         break;
         break;
     case CSPOT_QUERY_REMAINING: {
     case CSPOT_QUERY_REMAINING: {
-        uint32_t *remaining = va_arg(args, uint32_t*);
+        uint32_t* remaining = va_arg(args, uint32_t*);
         *remaining = (_buf_used(outputbuf) * 1000) / (output.current_sample_rate * BYTES_PER_FRAME);
         *remaining = (_buf_used(outputbuf) * 1000) / (output.current_sample_rate * BYTES_PER_FRAME);
-        break;      
+        break;
     }
     }
     case CSPOT_QUERY_STARTED: {
     case CSPOT_QUERY_STARTED: {
-        uint32_t *started = va_arg(args, uint32_t*);
+        uint32_t* started = va_arg(args, uint32_t*);
         *started = output.track_started;
         *started = output.track_started;
         // this is a read_and_clear event
         // this is a read_and_clear event
         output.track_started = false;
         output.track_started = false;
-        break;      
+        break;
+    }
+    case CSPOT_VOLUME: {
+        u32_t volume = va_arg(args, u32_t);
+        LOG_INFO("CSpot volume %u", volume);
+        volume = 65536 * powf(volume / 65536.0f, 4);
+        set_volume(volume, volume);
+        break;
+    default:
+        break;
+    }
     }
     }
-	case CSPOT_VOLUME: {
-		u32_t volume = va_arg(args, u32_t);
-		LOG_INFO("CSpot volume %u", volume);
-		volume = 65536 * powf(volume / 65536.0f, 4);
-		set_volume(volume, volume);
-		break;
-	default:
-		break;
-	}
-	}
-	
-	if (cmd != CSPOT_VOLUME) UNLOCK_O;
-	UNLOCK_D;
-	
-	return true;
+
+    if (cmd != CSPOT_VOLUME) UNLOCK_O;
+    UNLOCK_D;
+
+    return true;
 }
 }
 #endif
 #endif
 
 
@@ -448,80 +462,79 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
  * We provide the generic codec register option
  * We provide the generic codec register option
  */
  */
 void register_external(void) {
 void register_external(void) {
-	sys_BluetoothSink * bt_sink;
-	sys_AirPlay * airplay;
-	sys_Spotify * spotify;
+    squeezelite = get_profile(NULL); // get the active profile
 #if CONFIG_BT_SINK
 #if CONFIG_BT_SINK
-	enable_bt_sink= (SYS_SERVICES_BTSINK(bt_sink) && bt_sink->enabled);
-	if ( enable_bt_sink) {
-		#pragma message ("Is the BT sink logic correct?")
-		if(SYS_SERVICES_SQUEEZELITE(squeezelite) && squeezelite->output_type == sys_OutputTypeEnum_OUTPUT_Bluetooth ){
-			LOG_ERROR("BT Sink cannot be enabled with Bluetooth output");
-		}
-		else {
-			bt_sink_init(bt_sink_cmd_handler,  bt_sink_data_handler);
-		}
-	}		
-#endif	
+    sys_services_bt_sink* bt_sink;
+    enable_bt_sink = (sys_services_config_BTSINK(bt_sink) && bt_sink->enabled);
+    if (enable_bt_sink && squeezelite) {
+#pragma message("Is the BT sink logic correct?")
+        if (squeezelite->output_type == sys_squeezelite_outputs_BT) {
+            LOG_ERROR("BT Sink cannot be enabled with Bluetooth output");
+        } else {
+            bt_sink_init(bt_sink_cmd_handler, bt_sink_data_handler);
+        }
+    }
+#endif
 
 
 #if CONFIG_AIRPLAY_SINK
 #if CONFIG_AIRPLAY_SINK
-	enable_airplay = SYS_SERVICES_AIRPLAY(airplay) && airplay->enabled;
-	if (enable_airplay){
-		raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler);
-		LOG_INFO("Initializing AirPlay sink");
-	}
-	
-#endif	
-	
-#if CONFIG_CSPOT_SINK	
-	enable_cspot = SYS_SERVICES_SPOTIFY(spotify) && spotify->enabled;
-	if (enable_cspot){
-		cspot_sink_init(cspot_cmd_handler, cspot_sink_data_handler);
-		LOG_INFO("Initializing CSpot sink");
-	}	
-#endif	
+    sys_airplay_config* airplay;
+    enable_airplay = sys_services_config_AIRPLAY(airplay) && airplay->enabled;
+    if (enable_airplay) {
+        raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler);
+        LOG_INFO("Initializing AirPlay sink");
+    }
 
 
+#endif
+
+#if CONFIG_CSPOT_SINK
+    sys_spotify_config* spotify;
+    enable_cspot = sys_services_config_SPOTIFY(spotify) && spotify->enabled;
+    if (enable_cspot) {
+        cspot_sink_init(cspot_cmd_handler, cspot_sink_data_handler);
+        LOG_INFO("Initializing CSpot sink");
+    }
+#endif
 }
 }
 
 
 void deregister_external(void) {
 void deregister_external(void) {
 #if CONFIG_BT_SINK
 #if CONFIG_BT_SINK
-	sys_Squeezelite * squeezelite;
-	if(SYS_SERVICES_SQUEEZELITE(squeezelite) && squeezelite->output_type != sys_OutputTypeEnum_OUTPUT_Bluetooth && enable_bt_sink ){
-		bt_sink_deinit();
-	}
+    squeezelite = get_profile(NULL); // get the active profile
+    if (squeezelite && squeezelite->output_type == sys_squeezelite_outputs_BT && enable_bt_sink) {
+        bt_sink_deinit();
+    }
 #endif
 #endif
 
 
 #if CONFIG_AIRPLAY_SINK
 #if CONFIG_AIRPLAY_SINK
-	if (enable_airplay){
-		LOG_INFO("Stopping AirPlay sink");		
-		raop_sink_deinit();
-	}
+    if (enable_airplay) {
+        LOG_INFO("Stopping AirPlay sink");
+        raop_sink_deinit();
+    }
 #endif
 #endif
 
 
 #if CONFIG_CSPOT_SINK
 #if CONFIG_CSPOT_SINK
-	if (enable_cspot){
-		LOG_INFO("Stopping CSpot sink");		
-		cspot_sink_deinit();
-	}
+    if (enable_cspot) {
+        LOG_INFO("Stopping CSpot sink");
+        cspot_sink_deinit();
+    }
 #endif
 #endif
 }
 }
 
 
 void decode_restore(int external) {
 void decode_restore(int external) {
-	switch (external) {
-#if CONFIG_BT_SINK		
-	case DECODE_BT:
-		bt_disconnect();
-		break;
+    switch (external) {
+#if CONFIG_BT_SINK
+    case DECODE_BT:
+        bt_disconnect();
+        break;
 #endif
 #endif
 #if CONFIG_AIRPLAY_SINK
 #if CONFIG_AIRPLAY_SINK
-	case DECODE_RAOP:
-		raop_disconnect();
-		break;
+    case DECODE_RAOP:
+        raop_disconnect();
+        break;
 #endif
 #endif
 #if CONFIG_CSPOT_SINK
 #if CONFIG_CSPOT_SINK
-	case DECODE_CSPOT:
-		cspot_disconnect();
-		break;
-#endif			
-	}
+    case DECODE_CSPOT:
+        cspot_disconnect();
+        break;
+#endif
+    }
 }
 }

+ 6 - 3
components/squeezelite/embedded.c

@@ -17,13 +17,13 @@
 #include "esp_wifi.h"
 #include "esp_wifi.h"
 #include "esp_log.h"
 #include "esp_log.h"
 #include "monitor.h"
 #include "monitor.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "messaging.h"
 #include "messaging.h"
 #include "gpio_exp.h"
 #include "gpio_exp.h"
 #include "accessors.h"
 #include "accessors.h"
 
 
 static const char TAG[] = "embedded";
 static const char TAG[] = "embedded";
-static sys_GPIO * power=NULL;
+static sys_gpio_config * power=NULL;
 
 
 extern void sb_controls_init(void);
 extern void sb_controls_init(void);
 extern bool sb_displayer_init(void);
 extern bool sb_displayer_init(void);
@@ -69,7 +69,9 @@ uint32_t _gettime_ms_(void) {
 
 
 int embedded_init(void) {
 int embedded_init(void) {
 	mutex_create(slimp_mutex);
 	mutex_create(slimp_mutex);
+	ESP_LOGI(TAG,"Initializing controls");
 	sb_controls_init();
 	sb_controls_init();
+	ESP_LOGI(TAG,"Initializing displayer");
 	custom_player_id = sb_displayer_init() ? 100 : 101;
 	custom_player_id = sb_displayer_init() ? 100 : 101;
 
 
 	
 	
@@ -88,10 +90,11 @@ void embedded_exit(int code) {
 }    
 }    
 
 
 void powering(bool on) {
 void powering(bool on) {
-    if (power->pin != -1) {
+    if (SYS_GPIOS_NAME(power,power) && power->pin != -1) {
         ESP_LOGI(TAG, "powering player %s", on ? "ON" : "OFF");	
         ESP_LOGI(TAG, "powering player %s", on ? "ON" : "OFF");	
         gpio_set_level_x(power->pin, on ? power->level : !power->level);
         gpio_set_level_x(power->pin, on ? power->level : !power->level);
     }
     }
+
 }
 }
 
 
 u16_t get_RSSI(void) {
 u16_t get_RSSI(void) {

+ 10 - 11
components/squeezelite/equalizer.c

@@ -9,7 +9,7 @@
  */
  */
 
 
 #include "math.h"
 #include "math.h"
-#include "Configurator.h"
+#include "Config.h"
 #include "squeezelite.h"
 #include "squeezelite.h"
 #include "equalizer.h"
 #include "equalizer.h"
 #include "esp_equalizer.h"
 #include "esp_equalizer.h"
@@ -24,7 +24,7 @@ static EXT_RAM_ATTR struct {
     float volume;
     float volume;
 	float loudness_gain[EQ_BANDS];
 	float loudness_gain[EQ_BANDS];
 	bool update;
 	bool update;
-    sys_Equalizer *state; 
+    sys_equalizer_config *state; 
 } equalizer;
 } equalizer;
 
 
 
 
@@ -83,19 +83,18 @@ static void calculate_loudness(void) {
  * initialize equalizer
  * initialize equalizer
  */
  */
 void equalizer_init(void) {
 void equalizer_init(void) {
-    sys_Services * services;
-    sys_Equalizer blank_eq = sys_Equalizer_init_default;
-
+    sys_services_config * services;
+    sys_equalizer_config blank_eq = sys_equalizer_config_init_default;
     equalizer.state = &sys_state->equalizer;
     equalizer.state = &sys_state->equalizer;
     if(!sys_state->has_equalizer ){
     if(!sys_state->has_equalizer ){
         sys_state->has_equalizer = true;
         sys_state->has_equalizer = true;
-        if(SYS_SERVICES(services) && services->has_equalizer){
-            memcpy(equalizer.state,&services->equalizer,sizeof(sys_Equalizer));
+        if(sys_services_config(services) && services->has_equalizer){
+            memcpy(equalizer.state,&services->equalizer,sizeof(sys_equalizer_config));
         }
         }
         else {
         else {
-            memcpy(equalizer.state,&blank_eq,sizeof(sys_Equalizer)); 
+            memcpy(equalizer.state,&blank_eq,sizeof(sys_equalizer_config)); 
         }
         }
-        configurator_raise_state_changed();
+        config_raise_state_changed();
     }
     }
 }
 }
 
 
@@ -160,7 +159,7 @@ void equalizer_set_gain(int8_t *gain) {
     // update only if something changed
     // update only if something changed
     if (!memcmp(&equalizer.state->gains, gain, EQ_BANDS)) {
     if (!memcmp(&equalizer.state->gains, gain, EQ_BANDS)) {
         equalizer.update = true;
         equalizer.update = true;
-        configurator_raise_state_changed();
+        config_raise_state_changed();
     }
     }
 
 
     LOG_INFO("equalizer gain %s", config);
     LOG_INFO("equalizer gain %s", config);
@@ -178,7 +177,7 @@ void equalizer_set_loudness(uint8_t loudness) {
    // update loudness gains as a factor of loudness and volume
    // update loudness gains as a factor of loudness and volume
     if (equalizer.state->loudness != loudness / 10.0) {
     if (equalizer.state->loudness != loudness / 10.0) {
         equalizer.state->loudness = loudness / 10.0;
         equalizer.state->loudness = loudness / 10.0;
-        configurator_raise_state_changed();
+        config_raise_state_changed();
         calculate_loudness();
         calculate_loudness();
         equalizer.update = true;
         equalizer.update = true;
     }
     }

+ 87 - 64
components/squeezelite/esp32_main.c

@@ -1,34 +1,20 @@
 /*
 /*
- *  Squeezelite - lightweight headless squeezebox emulator
+ *  Squeezelite for esp32
  *
  *
- *  (c) Adrian Smith 2012-2015, triode1@btinternet.com
- *      Ralph Irving 2015-2017, ralph_irving@hotmail.com
+ *  (c) Sebastien 2024
+ *      Philippe G. 2024, 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 software is released under the MIT License.
+ *  https://opensource.org/licenses/MIT
  *
  *
- * 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/>.
- *
- * Additions (c) Paul Hermann, 2015-2017 under the same license terms
- *   -Control of Raspberry pi GPIO for amplifier power
- *   -Launch script on power status change from LMS
  */
  */
-
+#include "Config.h"
 #include "squeezelite.h"
 #include "squeezelite.h"
 #include <signal.h>
 #include <signal.h>
-#include "Configurator.h"
 
 
 extern bool user_rates;
 extern bool user_rates;
 static unsigned int rates[MAX_SUPPORTED_SAMPLERATES] = {0};
 static unsigned int rates[MAX_SUPPORTED_SAMPLERATES] = {0};
-sys_Squeezelite* config;
+sys_squeezelite_config* config;
 log_level loglevel = lDEBUG;
 log_level loglevel = lDEBUG;
 static void sighandler(int signum) {
 static void sighandler(int signum) {
     slimproto_stop();
     slimproto_stop();
@@ -38,8 +24,8 @@ static void sighandler(int signum) {
 }
 }
 
 
 unsigned int* get_rates() {
 unsigned int* get_rates() {
-    unsigned int ref[]  TEST_RATES;
-    sys_RatesOption* ratescfg = &config->rates;
+    unsigned int ref[] TEST_RATES;
+    sys_squeezelite_rates_opt* ratescfg = &config->rates;
     if (!config->has_rates || ((ratescfg->list_count == 0 || ratescfg->list[0] == 0) &&
     if (!config->has_rates || ((ratescfg->list_count == 0 || ratescfg->list[0] == 0) &&
                                   ratescfg->min == 0 && ratescfg->max == 0)) {
                                   ratescfg->min == 0 && ratescfg->max == 0)) {
         user_rates = false;
         user_rates = false;
@@ -71,73 +57,105 @@ unsigned int* get_rates() {
     user_rates = true;
     user_rates = true;
     return rates;
     return rates;
 }
 }
-log_level log_level_from_sys_level(sys_DebugLevelEnum level) {
+log_level log_level_from_sys_level(sys_squeezelite_debug_levels level) {
     switch (level) {
     switch (level) {
-    case sys_DebugLevelEnum_DEFAULT:
+    case sys_squeezelite_debug_levels_DEFAULT:
         return lWARN;
         return lWARN;
         break;
         break;
-    case sys_DebugLevelEnum_INFO:
+    case sys_squeezelite_debug_levels_INFO:
         return lINFO;
         return lINFO;
         break;
         break;
-    case sys_DebugLevelEnum_ERROR:
+    case sys_squeezelite_debug_levels_ERROR:
         return lERROR;
         return lERROR;
         break;
         break;
-    case sys_DebugLevelEnum_WARN:
+    case sys_squeezelite_debug_levels_WARN:
         return lWARN;
         return lWARN;
         break;
         break;
-    case sys_DebugLevelEnum_DEBUG:
+    case sys_squeezelite_debug_levels_DEBUG:
         return lDEBUG;
         return lDEBUG;
         break;
         break;
-    case sys_DebugLevelEnum_SDEBUG:
+    case sys_squeezelite_debug_levels_SDEBUG:
         return lSDEBUG;
         return lSDEBUG;
         break;
         break;
     default:
     default:
         return lWARN;
         return lWARN;
     }
     }
 }
 }
-void build_codec_string(sys_CodexEnum* list, size_t count, char* buffer, size_t buf_size) {
-    const char* prefix = STR(sys_CodexEnum) "_c_";
+void build_codec_string(sys_squeezelite_codecs* list, size_t count, char* buffer, size_t buf_size) {
+    const char* prefix = STR(sys_squeezelite_codecs) "_c_";
     const char* name = NULL;
     const char* name = NULL;
     for (int i = 0; i < count; i++) {
     for (int i = 0; i < count; i++) {
         if (i > 0) {
         if (i > 0) {
             strncat(buffer, ", ", buf_size);
             strncat(buffer, ", ", buf_size);
         }
         }
-        name = sys_CodexEnum_name(list[i]) + strlen(prefix);
+        name = sys_squeezelite_codecs_name(list[i]) + strlen(prefix);
         LOG_INFO("Found codec: %s ", name);
         LOG_INFO("Found codec: %s ", name);
         strncat(buffer, name, buf_size);
         strncat(buffer, name, buf_size);
     }
     }
     LOG_INFO("Codec list: %s ", buffer);
     LOG_INFO("Codec list: %s ", buffer);
 }
 }
+
+sys_squeezelite_config* get_profile(const char* name) {
+    const char* resolved_name = name && strlen(name) > 0 ? name : platform->services.current_profile;
+    LOG_DEBUG("get_profile called with name: %s, resolved to: %s", 
+              name ? name : "NULL", 
+              resolved_name ? resolved_name : "NULL or EMPTY");
+
+    if (!platform->has_services || platform->services.squeezelite_profiles_count <= 0) {
+        LOG_ERROR("No squeezelite profiles available");
+        return NULL;
+    }
+
+    if (!resolved_name || strlen(resolved_name) == 0) {
+        LOG_WARN("No specific profile requested, using the first available profile");
+        return &platform->services.squeezelite_profiles[0].profile;
+    }
+
+    for (int i = 0; i < platform->services.squeezelite_profiles_count; i++) {
+        LOG_DEBUG("Checking profile: %s", platform->services.squeezelite_profiles[i].name);
+        if (strcmp(platform->services.squeezelite_profiles[i].name, resolved_name) == 0) {
+            LOG_DEBUG("Profile matched: %s", platform->services.squeezelite_profiles[i].name);
+            return &platform->services.squeezelite_profiles[i].profile;
+        }
+    }
+    if(platform->services.squeezelite_profiles[0].name && strlen(platform->services.squeezelite_profiles[0].name)>0){
+        LOG_WARN("Could not find profile %s. Falling back to %s", resolved_name,platform->services.squeezelite_profiles[0].name);
+        return &platform->services.squeezelite_profiles[0].profile;
+    }
+
+    LOG_ERROR("Profile '%s' not found", resolved_name);
+    return NULL;
+}
+
 int squeezelite_main_start() {
 int squeezelite_main_start() {
     u8_t mac[6];
     u8_t mac[6];
     unsigned output_buf_size = 0;
     unsigned output_buf_size = 0;
+    LOG_INFO("Starting squeezelite");
     char include_codecs[101] = {0};
     char include_codecs[101] = {0};
     char exclude_codecs[101] = {0};
     char exclude_codecs[101] = {0};
-    config = platform->has_services && platform->services.has_squeezelite
-                 ? &platform->services.squeezelite
-                 : NULL;
+    config = get_profile(NULL); // get the active profile
     if (!config) {
     if (!config) {
         LOG_ERROR("Squeezelite not configured");
         LOG_ERROR("Squeezelite not configured");
-        return -1;
+        return -2;
     }
     }
-
+    LOG_INFO("Running embedded init");
     int err = embedded_init();
     int err = embedded_init();
     if (err) return err;
     if (err) return err;
     get_mac(mac);
     get_mac(mac);
-    unsigned int * rates = get_rates();
-
-	signal(SIGINT, sighandler);
-	signal(SIGTERM, sighandler);
-    #if defined(SIGQUIT)
-    	signal(SIGQUIT, sighandler);
-    #endif
-    #if defined(SIGHUP)
-    	signal(SIGHUP, sighandler);
-    #endif
-    output_buf_size = config->buffers.output;
-
-    // set the output buffer size if not specified on the command line, take account of resampling
-    if (!output_buf_size) {
+    unsigned int* rates = get_rates();
+
+    signal(SIGINT, sighandler);
+    signal(SIGTERM, sighandler);
+#if defined(SIGQUIT)
+    signal(SIGQUIT, sighandler);
+#endif
+#if defined(SIGHUP)
+    signal(SIGHUP, sighandler);
+#endif
+    output_buf_size = config->buffers.output*1024;
+
+    // set the output buffer size if not specified in the parameters, take account of resampling
+    if (output_buf_size == 0) {
         output_buf_size = OUTPUTBUF_SIZE;
         output_buf_size = OUTPUTBUF_SIZE;
         if (strlen(config->resample) > 0) {
         if (strlen(config->resample) > 0) {
             unsigned scale = 8;
             unsigned scale = 8;
@@ -151,32 +169,37 @@ int squeezelite_main_start() {
     }
     }
     build_codec_string(config->excluded_codex, config->excluded_codex_count, exclude_codecs,
     build_codec_string(config->excluded_codex, config->excluded_codex_count, exclude_codecs,
         sizeof(exclude_codecs));
         sizeof(exclude_codecs));
-    build_codec_string(
-        config->included_codex, config->included_codex, include_codecs, sizeof(include_codecs));
+    build_codec_string(config->included_codex, config->included_codex_count, include_codecs,
+        sizeof(include_codecs));
 
 
-    unsigned int stream_buf_size =
-        config->buffers.stream > 0 ? config->buffers.stream : STREAMBUF_SIZE;
-    stream_init(
-        log_level_from_sys_level(platform->services.squeezelite.log.stream), stream_buf_size);
+    unsigned int stream_buf_size =STREAMBUF_SIZE;
+    if(config->buffers.stream > 0){
+        stream_buf_size = config->buffers.stream *1024;
+        LOG_DEBUG("Found stream buffer configuration: %d (%d)",config->buffers.stream,stream_buf_size);
+    }
+    else {
+        LOG_DEBUG("Using default stream buffer: %d",stream_buf_size);
+    }
+    stream_init(log_level_from_sys_level(config->log.stream), stream_buf_size);
     output_init_embedded();
     output_init_embedded();
-    decode_init(log_level_from_sys_level(platform->services.squeezelite.log.decode), include_codecs,
-        exclude_codecs);
+    decode_init(log_level_from_sys_level(config->log.decode),
+        strlen(include_codecs) > 0 ? include_codecs : NULL, exclude_codecs);
 
 
 #if RESAMPLE || RESAMPLE16
 #if RESAMPLE || RESAMPLE16
-    if (strlen(config->resample) > 0) {
+    if (config->resample && strlen(config->resample) > 0) {
         process_init(config->resample);
         process_init(config->resample);
     }
     }
 #endif
 #endif
 
 
     if (!config->enabled) {
     if (!config->enabled) {
-        LOG_ERROR("LMS is disabled");
+        LOG_WARN("LMS is disabled");
         while (1)
         while (1)
             sleep(3600);
             sleep(3600);
     }
     }
     char* name = strlen(platform->names.squeezelite) > 0 ? platform->names.squeezelite
     char* name = strlen(platform->names.squeezelite) > 0 ? platform->names.squeezelite
                                                          : platform->names.device;
                                                          : platform->names.device;
-    slimproto(log_level_from_sys_level(platform->services.squeezelite.log.slimproto),
-        config->server_name_ip, mac, name, NULL, NULL, config->max_rate);
+    slimproto(log_level_from_sys_level(config->log.slimproto), config->server_name_ip, mac, name,
+        NULL, NULL, config->max_rate);
 
 
     decode_close();
     decode_close();
     stream_close();
     stream_close();

+ 145 - 136
components/squeezelite/external/dac_external.c

@@ -1,4 +1,4 @@
-/* 
+/*
  *  Squeezelite for esp32
  *  Squeezelite for esp32
  *
  *
  *  (c) Sebastien 2019
  *  (c) Sebastien 2019
@@ -8,18 +8,19 @@
  *  https://opensource.org/licenses/MIT
  *  https://opensource.org/licenses/MIT
  *
  *
  */
  */
- 
-#include <freertos/FreeRTOS.h>
-#include <freertos/task.h>
-#include <driver/i2s.h>
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
+#include "Config.h"
+#include "adac.h"
+#include "cJSON.h"
 #include "driver/i2c.h"
 #include "driver/i2c.h"
 #include "esp_log.h"
 #include "esp_log.h"
+#include "esp_check.h"
 #include "gpio_exp.h"
 #include "gpio_exp.h"
-#include "cJSON.h"
+#include "inttypes.h"
 #include "string.h"
 #include "string.h"
-// #include "Configurator.h"
-#pragma message("fixme: look for TODO below")
-#include "adac.h"
+#include <driver/i2s.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
 
 
 static const char TAG[] = "DAC external";
 static const char TAG[] = "DAC external";
 
 
@@ -27,159 +28,167 @@ static void speaker(bool active);
 static void headset(bool active);
 static void headset(bool active);
 static bool volume(unsigned left, unsigned right) { return false; }
 static bool volume(unsigned left, unsigned right) { return false; }
 static void power(adac_power_e mode);
 static void power(adac_power_e mode);
-static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool *mck);
+static bool init(sys_dac_config* config, i2s_config_t* i2s_config, bool* mck);
 
 
-static bool i2c_json_execute(char *set);
+// static bool i2c_json_execute(char *set);
+static bool i2c_execute_cmd(sys_dac_control_type cmd_type);
 
 
-const struct adac_s dac_external = { sys_DACModelEnum_I2S, init, adac_deinit, power, speaker, headset, volume };
-static cJSON *i2c_json;
+const struct adac_s dac_external = {sys_dac_models_I2S, init, adac_deinit, power, speaker, headset, volume};
 static int i2c_addr;
 static int i2c_addr;
-
-static const struct {
-	char *model;
-	bool mclk;
-	char *controlset;
-} codecs[] = {
-	{ "es8388", true,
-		"{\"init\":[ 																						\
-			{\"reg\":8,\"val\":0}, {\"reg\":2,\"val\":243}, {\"reg\":43,\"val\":128}, {\"reg\":0,\"val\":5}, 		\
-			{\"reg\":1,\"val\":64}, {\"reg\":4,\"val\":60},"
-#if BYTES_PER_FRAME == 8
-		   "{\"reg\":23,\"val\":32},"
-#else			
-		   "{\"reg\":23,\"val\":24},"
-#endif
-		   "{\"reg\":24,\"val\":2},		\
-			{\"reg\":26,\"val\":0}, {\"reg\":27,\"val\":0}, {\"reg\":25,\"val\":50}, {\"reg\":38,\"val\":0},		\
-			{\"reg\":39,\"val\":184}, {\"reg\":42,\"val\":184}, {\"reg\":46,\"val\":30}, {\"reg\":47,\"val\":30},	\
-			{\"reg\":48,\"val\":30}, {\"reg\":49,\"val\":30}, {\"reg\":2,\"val\":170}]}" },
-	{ NULL, false, NULL }		
-};
+extern sys_dac_default_sets* default_dac_sets;
+static EXT_RAM_ATTR sys_dac_control_set* i2c_default_controlset = NULL;
+static EXT_RAM_ATTR sys_dac_control_set* i2c_controlset = NULL;
 
 
 /****************************************************************************************
 /****************************************************************************************
  * init
  * init
  */
  */
-static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool *mck) {	 
-	char *p=NULL;	
-	void * dummy = &codecs;
-	// i2c_addr = adac_init(config, i2c_port_num);
-	// if (!i2c_addr) return true;
-	
-	// ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
-	
-	// p = config_alloc_get_str("dac_controlset", CONFIG_DAC_CONTROLSET, NULL);
-
-	// if ((!p || !*p) && (p = strcasestr(config, "model")) != NULL) {
-	// 	char model[32] = "";
-	// 	int i;
-	// 	sscanf(p, "%*[^=]=%31[^,]", model);
-	// 	for (i = 0; *model && ((p = codecs[i].controlset) != NULL) && strcasecmp(codecs[i].model, model); i++);
-	//  	if (p) *mck = codecs[i].mclk;
-	//  }	 
-
-	// i2c_json = cJSON_Parse(p);
-	
-	// if (!i2c_json) {
-	// 	ESP_LOGW(TAG, "no i2c controlset found");
-	// 	return true;
-	// }	
-		
-	// if (!i2c_json_execute("init")) {	
-	// 	ESP_LOGE(TAG, "could not intialize DAC");
-	// 	return false;
-	// }	
-	// TODO: Add support for the commented code
-
-	return true;
-}	
+static bool init(sys_dac_config* config, i2s_config_t* i2s_config, bool* mck) {
+
+#ifdef BYTES_PER_FRAME
+    uint32_t bytes_per_frame = BYTES_PER_FRAME;
+#else
+    uint32_t bytes_per_frame = 4;
+#endif
+    i2c_addr = adac_init(config);
+    if (!i2c_addr) return true;
+    ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
+    ESP_LOGD(TAG, "Checkinf if there's a default control set for DAC %s in the list of %d presets", sys_dac_models_name(config->model),
+        default_dac_sets->sets_count);
+
+    for (int i = 0; i < default_dac_sets->sets_count; i++) {
+        ESP_LOGD(TAG, "Checkinf if preset #%d (%s[%d]) matches %s[%d]", i + 1, sys_dac_models_name(default_dac_sets->sets[i].model),
+            default_dac_sets->sets[i].bytes_per_frame, sys_dac_models_name(config->model), bytes_per_frame);
+        if (default_dac_sets->sets[i].bytes_per_frame == bytes_per_frame && default_dac_sets->sets[i].model == config->model) {
+            if (!default_dac_sets->sets[i].valid) {
+                ESP_LOGE(TAG, "DAC %s is unsupported with %d bytes per frame", sys_dac_models_name(config->model), bytes_per_frame);
+                return false;
+            }
+            i2c_default_controlset = &default_dac_sets->sets[i].set;
+        }
+    }
+
+    if (config->has_daccontrolset && config->daccontrolset.commands_count > 0) {
+        i2c_controlset = &config->daccontrolset;
+    }
+    if (!i2c_controlset && !i2c_default_controlset) {
+        ESP_LOGE(TAG, "DAC configuration invalid for %s", sys_dac_models_name(config->model));
+        return false;
+    }
+    if (mck) {
+        *mck = i2c_controlset != NULL ? i2c_controlset->mclk_needed : i2c_default_controlset->mclk_needed;
+        ESP_LOGD(TAG, "Master clock is %s", (*mck) ? "NEEDED" : "NOT NEEDED");
+    }
+    if (!i2c_execute_cmd(sys_dac_control_type_INIT)) {
+        ESP_LOGE(TAG, "could not intialize DAC");
+        return false;
+    }
+
+    return true;
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * power
  * power
  */
  */
 static void power(adac_power_e mode) {
 static void power(adac_power_e mode) {
-	if (mode == ADAC_STANDBY || mode == ADAC_OFF) i2c_json_execute("poweroff");
-	else i2c_json_execute("poweron");
+    if (mode == ADAC_STANDBY || mode == ADAC_OFF)
+        i2c_execute_cmd(sys_dac_control_type_POWER_OFF);
+    else
+        i2c_execute_cmd(sys_dac_control_type_POWER_ON);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * speaker
  * speaker
  */
  */
 static void speaker(bool active) {
 static void speaker(bool active) {
-	if (active) i2c_json_execute("speakeron");
-	else i2c_json_execute("speakeroff");
-} 
+    if (active)
+        i2c_execute_cmd(sys_dac_control_type_SPEAKER_ON);
+    else
+        i2c_execute_cmd(sys_dac_control_type_SPEAKER_OFF);
+}
 
 
 /****************************************************************************************
 /****************************************************************************************
  * headset
  * headset
  */
  */
 static void headset(bool active) {
 static void headset(bool active) {
-	if (active) i2c_json_execute("headseton");
-	else i2c_json_execute("headsetoff");
+    if (active)
+        i2c_execute_cmd(sys_dac_control_type_HEADSET_ON);
+    else
+        i2c_execute_cmd(sys_dac_control_type_HEADSET_OFF);
 }
 }
 
 
 /****************************************************************************************
 /****************************************************************************************
- * 
+ *
  */
  */
-bool i2c_json_execute(char *set) {
-	cJSON *json_set = cJSON_GetObjectItemCaseSensitive(i2c_json, set);
-	cJSON *item;
-
-	if (!json_set) return true;
-	
-	cJSON_ArrayForEach(item, json_set) {
-        cJSON *action;
-        
-        // is this a delay
-        if ((action = cJSON_GetObjectItemCaseSensitive(item, "delay")) != NULL) {
-            vTaskDelay(pdMS_TO_TICKS(action->valueint));
-            ESP_LOGI(TAG, "DAC waiting %d ms", action->valueint);
-            continue;
+static const sys_dac_control_itm* i2c_get_controlset_cmd(sys_dac_control_set* set, sys_dac_control_type cmd_type, pb_size_t* items_count) {
+    ESP_RETURN_ON_FALSE(set!=NULL,NULL,TAG,"Invalid set");
+    ESP_RETURN_ON_FALSE(items_count!=NULL,NULL,TAG,"Invalid items count");
+    ESP_LOGD(TAG,"Looking for command %s in control from a list of %d commands",sys_dac_control_type_name(cmd_type),set->commands_count);
+    *items_count = 0;
+    for (int i = 0; i < set->commands_count; i++) {
+        if (set->commands[i].type == cmd_type) {
+            *items_count = set->commands[i].items_count;
+            return set->commands[i].items;
         }
         }
-        
-        // is this a gpio toggle
-        if ((action = cJSON_GetObjectItemCaseSensitive(item, "gpio")) != NULL) {
-            cJSON *level = cJSON_GetObjectItemCaseSensitive(item, "level");
-            ESP_LOGI(TAG, "set GPIO %d at %d", action->valueint, level->valueint);
-            gpio_set_direction_x(action->valueint, GPIO_MODE_OUTPUT);
-            gpio_set_level_x(action->valueint, level->valueint);
-            continue;
+    }
+    ESP_LOGD(TAG,"No control set found for command %s",sys_dac_control_type_name(cmd_type));
+    return NULL;
+}
+
+/****************************************************************************************
+ *
+ */
+bool i2c_execute_cmd(sys_dac_control_type cmd_type) {
+    pb_size_t items_count;
+    const sys_dac_control_itm* cmd = NULL;
+    esp_err_t err = ESP_OK;
+    ESP_LOGD(TAG, "Getting control set for command %s", sys_dac_control_type_name(cmd_type));
+    if(i2c_controlset){
+        cmd = i2c_get_controlset_cmd(i2c_controlset, cmd_type, &items_count);
+    }
+    if (!cmd || items_count == 0) {
+        ESP_LOGD(TAG, "Not found. Checking if defaults are known %s", sys_dac_control_type_name(cmd_type));
+        cmd = i2c_get_controlset_cmd(i2c_default_controlset, cmd_type, &items_count);
+    }
+    if (!cmd || items_count == 0) {
+        ESP_LOGD(TAG, "No commands for %s", sys_dac_control_type_name(cmd_type));
+        return true;
+    }
+    for (pb_size_t i = 0; i < items_count && err==ESP_OK; i++) {
+        switch (cmd->which_item_type) {
+        case sys_dac_control_itm_reg_action_tag:
+            ESP_LOGD(TAG, "Setting reg %d mode %s value %d ", cmd->item_type.reg_action.reg,
+                sys_dac_control_mode_name(cmd->item_type.reg_action.mode), cmd->item_type.reg_action.val);
+            if (cmd->item_type.reg_action.mode == sys_dac_control_mode_NOTHING) {
+                err = adac_write_byte(i2c_addr, cmd->item_type.reg_action.reg, cmd->item_type.reg_action.val);
+            } else if (cmd->item_type.reg_action.mode == sys_dac_control_mode_OR) {
+                uint8_t data = adac_read_byte(i2c_addr, cmd->item_type.reg_action.reg);
+                data |= (uint8_t)cmd->item_type.reg_action.val;
+                err = adac_write_byte(i2c_addr, cmd->item_type.reg_action.reg, data);
+            } else if (cmd->item_type.reg_action.mode == sys_dac_control_mode_AND) {
+                uint8_t data = adac_read_byte(i2c_addr, cmd->item_type.reg_action.reg);
+                data &= (uint8_t)cmd->item_type.reg_action.val;
+                err = adac_write_byte(i2c_addr, cmd->item_type.reg_action.reg, data);
+            }
+
+            break;
+        case sys_dac_control_itm_regs_action_tag:
+            ESP_LOGD(TAG, "Setting reg %d with %d byte(s)", cmd->item_type.regs_action.reg, cmd->item_type.regs_action.vals_count);
+            err = adac_write(i2c_addr, cmd->item_type.regs_action.reg, cmd->item_type.regs_action.vals, cmd->item_type.regs_action.vals_count);
+            break;
+        case sys_dac_control_itm_gpio_action_tag:
+            ESP_LOGI(TAG, "set GPIO %d at %s", cmd->item_type.gpio_action.gpio, sys_dac_control_lvl_name(cmd->item_type.gpio_action.level));
+            err = gpio_set_direction_x(cmd->item_type.gpio_action.gpio, GPIO_MODE_OUTPUT);
+            if(err == ESP_OK) err = gpio_set_level_x(cmd->item_type.gpio_action.gpio, cmd->item_type.gpio_action.level - sys_dac_control_lvl_LV_0);
+            break;
+        case sys_dac_control_itm_delay_action_tag:
+            vTaskDelay(pdMS_TO_TICKS(cmd->item_type.delay_action.delay));
+            ESP_LOGI(TAG, "DAC waiting %" PRIu64 " ms", cmd->item_type.delay_action.delay);
+            break;
+
+        default:
+            break;
         }
         }
-                    
-		action= cJSON_GetObjectItemCaseSensitive(item, "reg");
-		cJSON *val = cJSON_GetObjectItemCaseSensitive(item, "val");
-		
-        // this is gpio register setting or crap
-		if (cJSON_IsArray(val)) {
-			cJSON *value;			
-			uint8_t *data = malloc(cJSON_GetArraySize(val));
-			int count = 0;
-			
-			if (!data) continue;
-			
-			cJSON_ArrayForEach(value, val) {
-				data[count++] = value->valueint;		
-			}
-			
-			adac_write(i2c_addr, action->valueint, data, count);
-			free(data);			
-		} else {
-			cJSON *mode = cJSON_GetObjectItemCaseSensitive(item, "mode");
-
-			if (!action || !val) continue;
-
-			if (!mode) {
-				adac_write_byte(i2c_addr, action->valueint, val->valueint);
-			} else if (!strcasecmp(mode->valuestring, "or")) {
-				uint8_t data = adac_read_byte(i2c_addr, action->valueint);
-				data |= (uint8_t) val->valueint;
-				adac_write_byte(i2c_addr, action->valueint, data);
-			} else if (!strcasecmp(mode->valuestring, "and")) {
-				uint8_t data = adac_read_byte(i2c_addr, action->valueint);
-				data &= (uint8_t) val->valueint;
-				adac_write_byte(i2c_addr, action->valueint, data);
-			}
-		}
-	}
-	
-	return true;
-}	
+    }
+
+    return (err==ESP_OK);
+}

+ 6 - 4
components/squeezelite/output_bt.c

@@ -16,9 +16,10 @@
 #include "perf_trace.h"
 #include "perf_trace.h"
 #include "services.h"
 #include "services.h"
 #include "led.h"
 #include "led.h"
-#include "Configurator.h"
-extern log_level log_level_from_sys_level(sys_DebugLevelEnum level);
-static sys_Squeezelite * config = NULL;
+#include "Config.h"
+extern log_level log_level_from_sys_level(sys_squeezelite_debug_levels level);
+extern sys_squeezelite_config* get_profile(const char* name);
+static sys_squeezelite_config * config = NULL;
 extern struct outputstate output;
 extern struct outputstate output;
 extern struct buffer *outputbuf;
 extern struct buffer *outputbuf;
 extern struct buffer *streambuf;
 extern struct buffer *streambuf;
@@ -76,7 +77,8 @@ static uint32_t bt_idle_callback(void) {
  * Init BT sink
  * Init BT sink
  */    
  */    
 void output_init_bt() {
 void output_init_bt() {
-	config = &platform->services.squeezelite;
+	config = get_profile(NULL); // get the active profile
+	if(!config) return;
 	loglevel = log_level_from_sys_level(config->log.output);
 	loglevel = log_level_from_sys_level(config->log.output);
 	bt_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
 	bt_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
     services_sleep_setsleeper(bt_idle_callback);
     services_sleep_setsleeper(bt_idle_callback);

+ 19 - 11
components/squeezelite/output_embedded.c

@@ -10,9 +10,10 @@
  */
  */
 #include "squeezelite.h"
 #include "squeezelite.h"
 #include "equalizer.h"
 #include "equalizer.h"
-#include "Configurator.h"
-extern log_level log_level_from_sys_level(sys_DebugLevelEnum level);
-static sys_Squeezelite* config = NULL;
+#include "Config.h"
+extern log_level log_level_from_sys_level(sys_squeezelite_debug_levels level);
+extern sys_squeezelite_config* get_profile(const char* name);
+static sys_squeezelite_config* config = NULL;
 
 
 
 
 extern unsigned int* get_rates() ;
 extern unsigned int* get_rates() ;
@@ -76,9 +77,9 @@ static bool handler(u8_t* data, int len) {
 }
 }
 
 
 void output_init_embedded() {
 void output_init_embedded() {
-    config = &platform->services.squeezelite;
+    config = get_profile(NULL); // get the active profile
     loglevel = log_level_from_sys_level(config->log.output);
     loglevel = log_level_from_sys_level(config->log.output);
-    LOG_INFO("init device: %s", sys_OutputTypeEnum_name(config->output_type));
+    LOG_INFO("init device: %s", sys_squeezelite_outputs_name(config->output_type));
 
 
     // chain handlers
     // chain handlers
     slimp_handler_chain = slimp_handler;
     slimp_handler_chain = slimp_handler;
@@ -87,15 +88,22 @@ void output_init_embedded() {
     // init equalizer before backends
     // init equalizer before backends
     equalizer_init();
     equalizer_init();
     memset(&output, 0, sizeof(output));
     memset(&output, 0, sizeof(output));
-
-    output_init_common(loglevel, sys_OutputTypeEnum_name(config->output_type),
-        config->buffers.output, get_rates(), config->amp_gpio_timeout);
+    unsigned int output_buffer = OUTPUTBUF_SIZE;
+    if(config->buffers.output > 0){
+        output_buffer = config->buffers.output *1024;
+        LOG_DEBUG("Found output buffer configuration: %d (%d)",config->buffers.output,output_buffer);
+    }
+    else {
+        LOG_DEBUG("Using default output buffer: %d",output_buffer);
+    }    
+    output_init_common(loglevel, sys_squeezelite_outputs_name(config->output_type),
+        output_buffer, get_rates(), config->amp_gpio_timeout);
     output.start_frames = FRAME_BLOCK;
     output.start_frames = FRAME_BLOCK;
     #pragma message("Rate delay logic incomplete")
     #pragma message("Rate delay logic incomplete")
 	output.rate_delay = 0;
 	output.rate_delay = 0;
 
 
 #if CONFIG_BT_SINK
 #if CONFIG_BT_SINK
-    if (config->output_type == sys_OutputTypeEnum_OUTPUT_Bluetooth) {
+    if (config->output_type == sys_squeezelite_outputs_BT) {
         LOG_INFO("init Bluetooth");
         LOG_INFO("init Bluetooth");
         close_cb = &output_close_bt;
         close_cb = &output_close_bt;
         output_init_bt(get_rates());
         output_init_bt(get_rates());
@@ -132,7 +140,7 @@ void set_volume(unsigned left, unsigned right) {
 
 
 bool test_open(const char* device, unsigned rates[], bool userdef_rates) {
 bool test_open(const char* device, unsigned rates[], bool userdef_rates) {
     memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned));
     memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned));
-    if (config->output_type == sys_OutputTypeEnum_OUTPUT_I2S) {
+    if (config->output_type == sys_squeezelite_outputs_I2S) {
         unsigned _rates[] = {
         unsigned _rates[] = {
 #if BYTES_PER_FRAME == 4
 #if BYTES_PER_FRAME == 4
             192000,
             192000,
@@ -152,7 +160,7 @@ bool test_open(const char* device, unsigned rates[], bool userdef_rates) {
             0
             0
         };
         };
         memcpy(rates, _rates, sizeof(_rates));
         memcpy(rates, _rates, sizeof(_rates));
-    } else if (config->output_type == sys_OutputTypeEnum_OUTPUT_SPDIF) {
+    } else if (config->output_type == sys_squeezelite_outputs_SPDIF) {
         unsigned _rates[] = {
         unsigned _rates[] = {
             96000, 88200, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 0};
             96000, 88200, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 0};
         memcpy(rates, _rates, sizeof(_rates));
         memcpy(rates, _rates, sizeof(_rates));

Неке датотеке нису приказане због велике количине промена