diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..0fdb014e
--- /dev/null
+++ b/.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
+}
\ No newline at end of file
diff --git a/.cproject b/.cproject
deleted file mode 100644
index 317be43f..00000000
--- a/.cproject
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
deleted file mode 100644
index aa2ccfb0..00000000
--- a/.devcontainer/devcontainer.json
+++ /dev/null
@@ -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
- ]
-}
diff --git a/.gitignore b/.gitignore
index caa347ce..b4ae7dfb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,10 @@ components/wifi-manager/UML-State-Machine-in-C
envfile.txt
artifacts
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
diff --git a/.project b/.project
deleted file mode 100644
index 20702302..00000000
--- a/.project
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
- squeezelite-esp32-idfv4
-
-
-
-
-
- org.eclipse.cdt.core.cBuilder
- clean,full,incremental,
-
-
-
-
-
- org.eclipse.cdt.core.cnature
- org.eclipse.cdt.core.ccnature
- com.espressif.idf.core.idfNature
-
-
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 644b7260..4c56f023 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,12 @@ endif()
# State machine hierarchy enabled and logging enabled
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
# add_definitions(-DENABLE_MEMTRACE=1)
@@ -18,9 +23,11 @@ add_definitions(-DHIERARCHICAL_STATES=1)
#add_definitions(-DNETWORK_ETHERNET_LOG_LEVEL=ESP_LOG_DEBUG)
#uncomment line below to get network status debug logs
#add_definitions(-DNETWORK_STATUS_LOG_LEVEL=ESP_LOG_DEBUG)
-#add_definitions(-DNETWORK_HANDLERS_LOG_LEVEL=ESP_LOG_DEBUG)
+add_definitions(-DNETWORK_HANDLERS_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)
# utility to build sizes
@@ -53,7 +60,6 @@ endfunction()
set(EXTRA_COMPONENT_DIRS components/platform_console/app_recovery components/platform_console/app_squeezelite )
project(recovery)
-spiffs_create_partition_image(spiffs spiffs FLASH_IN_PROJECT DEPENDS generate_spiffs_bin )
# we need own "esp_app_desc" to take precedence
add_custom_command(
@@ -106,7 +112,7 @@ esptool_py_flash_target_image(flash squeezelite "${otaapp_offset}" "${BUILD_DIR}
add_custom_target(_jtag_scripts ALL
BYPRODUCTS "flash_dbg_project_args"
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)
@@ -136,78 +142,95 @@ endif()
# Include the protocol_buffers.cmake file
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 ============================
+# 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_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_log 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(mbedtls 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_openssl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
# target_compile_definitions(__idf_perfmon PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_platform_config PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
-target_compile_definitions(__idf_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_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_xtensa PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index fb8815c6..d9c0e2c7 100644
--- a/Dockerfile
+++ b/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=.
+ # 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=.
-# 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 : \
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 show pygit2 \
&& python --version \
&& pip --version \
&& pip install protobuf grpcio-tools \
&& 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
-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 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"
-# 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 components/wifi-manager/webapp/package.json /opt
-
-ENV NODE_VERSION 8
-
-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
-
-
+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"]
+
+# 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 \
+ && :
+
COPY ./docker/build_tools.py /usr/sbin/build_tools.py
RUN : \
&& echo Changing permissions ******************************************************** \
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 51ac175c..00000000
--- a/Makefile
+++ /dev/null
@@ -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
-
diff --git a/Makefile_std.mk b/Makefile_std.mk
deleted file mode 100644
index b1a67a7d..00000000
--- a/Makefile_std.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-PROJECT_NAME?= squeezelite
-EXTRA_COMPONENT_DIRS := $(PROJECT_PATH)/esp-dsp
-include $(IDF_PATH)/make/project.mk
\ No newline at end of file
diff --git a/buildFirmware.sh b/buildFirmware.sh
deleted file mode 100755
index 2dc9298d..00000000
--- a/buildFirmware.sh
+++ /dev/null
@@ -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
diff --git a/build_flash_cmd.sh b/build_flash_cmd.sh
deleted file mode 100644
index 7124e1f3..00000000
--- a/build_flash_cmd.sh
+++ /dev/null
@@ -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
-
diff --git a/components/display/CMakeLists.txt b/components/display/CMakeLists.txt
index 31e1c10e..b036e9ca 100644
--- a/components/display/CMakeLists.txt
+++ b/components/display/CMakeLists.txt
@@ -3,7 +3,7 @@ set(TJPGD tjpgd)
idf_component_register(SRC_DIRS . core core/ifaces
INCLUDE_DIRS . core
- REQUIRES platform_config tools esp_common spiffs
+ REQUIRES tools platform_config esp_common spiffs
PRIV_REQUIRES services freertos driver ${TJPGD}
EMBED_FILES note.jpg )
diff --git a/components/display/ILI9341.c b/components/display/ILI9341.c
index af0ce1d4..f6a2dc30 100644
--- a/components/display/ILI9341.c
+++ b/components/display/ILI9341.c
@@ -319,12 +319,12 @@ static const struct GDS_Device ILI9341_X = {
.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;
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;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
diff --git a/components/display/SH1106.c b/components/display/SH1106.c
index 78a44f03..7a2f0300 100644
--- a/components/display/SH1106.c
+++ b/components/display/SH1106.c
@@ -152,9 +152,9 @@ static const struct GDS_Device SH1106 = {
#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(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));
*Device = SH1106;
diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c
index 44d07cdc..4f1bb73f 100644
--- a/components/display/SSD1306.c
+++ b/components/display/SSD1306.c
@@ -162,8 +162,8 @@ static const struct GDS_Device SSD1306 = {
#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));
*Device = SSD1306;
diff --git a/components/display/SSD1322.c b/components/display/SSD1322.c
index 0f368a60..e58f03bf 100644
--- a/components/display/SSD1322.c
+++ b/components/display/SSD1322.c
@@ -191,8 +191,8 @@ static const struct GDS_Device SSD1322 = {
.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));
diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c
index 5020b629..c0b32afc 100644
--- a/components/display/SSD132x.c
+++ b/components/display/SSD132x.c
@@ -319,12 +319,12 @@ static const struct GDS_Device SSD132x = {
.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;
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;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
diff --git a/components/display/SSD1351.c b/components/display/SSD1351.c
index d1c7b3a8..d692c02a 100644
--- a/components/display/SSD1351.c
+++ b/components/display/SSD1351.c
@@ -268,10 +268,10 @@ static const struct GDS_Device SSD1351 = {
.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;
- 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));
diff --git a/components/display/SSD1675.c b/components/display/SSD1675.c
index d298f228..a1b01781 100644
--- a/components/display/SSD1675.c
+++ b/components/display/SSD1675.c
@@ -224,30 +224,31 @@ static bool Init( struct GDS_Device* Device ) {
Update( Device );
return true;
-}
+}
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,
-};
+ .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;
+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;
}
\ No newline at end of file
diff --git a/components/display/ST77xx.c b/components/display/ST77xx.c
index f874067c..42260375 100644
--- a/components/display/ST77xx.c
+++ b/components/display/ST77xx.c
@@ -273,11 +273,11 @@ static const struct GDS_Device ST77xx = {
.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;
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;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
diff --git a/components/display/core/gds.c b/components/display/core/gds.c
index 44581774..4ebe6477 100644
--- a/components/display/core/gds.c
+++ b/components/display/core/gds.c
@@ -15,7 +15,7 @@
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "esp_log.h"
-#include "Configurator.h"
+#include "Config.h"
#include "gds.h"
#include "gds_private.h"
@@ -30,8 +30,8 @@ static struct GDS_BacklightPWM PWMConfig;
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;
for (int i = 0; DetectFunc[i]; i++) {
diff --git a/components/display/core/gds.h b/components/display/core/gds.h
index 0b96c7a4..fdac295a 100644
--- a/components/display/core/gds.h
+++ b/components/display/core/gds.h
@@ -3,7 +3,7 @@
#include
#include
-#include "Configurator.h"
+#include "Config.h"
/* NOTE for drivers:
The build-in DrawPixel(Fast), DrawCBR and ClearWindow have optimized for 1 bit
@@ -34,9 +34,9 @@ struct GDS_Layout {
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_DisplayOn( struct GDS_Device* Device );
diff --git a/components/display/core/gds_font.c b/components/display/core/gds_font.c
index 0cb9ca49..59fd1858 100644
--- a/components/display/core/gds_font.c
+++ b/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");
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
- 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
if (loadedFont == NULL) {
diff --git a/components/display/display.c b/components/display/display.c
index 8d5c1e10..10aaecaa 100644
--- a/components/display/display.c
+++ b/components/display/display.c
@@ -22,7 +22,7 @@
#include "gds_text.h"
#include "gds_font.h"
#include "gds_image.h"
-#include "Configurator.h"
+#include "Config.h"
static const char *TAG = "display";
#define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -44,7 +44,7 @@ static EXT_RAM_ATTR struct {
char header[HEADER_SIZE + 1];
char string[SCROLLABLE_SIZE + 1];
int offset, boundary;
- sys_Metadata *metadata_config;
+ sys_metadata_config *metadata_config;
bool timer, refresh;
uint32_t elapsed;
struct {
@@ -76,10 +76,11 @@ GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD
void display_init(char *welcome) {
bool init = false;
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;
}
// // so far so good
@@ -87,11 +88,10 @@ void display_init(char *welcome) {
ESP_LOGE(TAG,"Misconfigured display missing data");
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 };
display = GDS_AutoDetect(sys_display, drivers, &PWMConfig);
} else {
@@ -99,38 +99,27 @@ void display_init(char *welcome) {
}
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;
height = common->height;
// 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;
address = sys_display->dispType.i2c.address;
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 );
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;
- 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;
+ mode = sys_display->dispType.spi.mode;
- //todo: what is mode?
-
- // PARSE_PARAM(config, "mode", '=', mode);
-
- // todo: need to handle display offsets
-
init = true;
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
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
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.fit = true;
if (height <= 64 && width > height * 2)
diff --git a/components/driver_bt/bt_app_core.c b/components/driver_bt/bt_app_core.c
index fd7c1bdf..281ad606 100644
--- a/components/driver_bt/bt_app_core.c
+++ b/components/driver_bt/bt_app_core.c
@@ -6,31 +6,31 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
-#include
-#include
-#include
+#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 "freertos/FreeRTOS.h"
#include "freertos/queue.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
+#include
+#include
-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 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;
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;
}
-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) {
return false;
}
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 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) {
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;
- 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();
- 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) {
ESP_LOGE(TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err));
goto exit;
@@ -104,17 +101,16 @@ static void bt_app_task_handler(void *arg)
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)
/* Set default parameters for Secure Simple Pairing */
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_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
#endif
-
+
/*
* Set default parameters for Legacy 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_code_t 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)) {
- 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) {
case BT_APP_SIG_WORK_DISPATCH:
bt_app_work_dispatched(&msg);
break;
default:
- ESP_LOGW(TAG,"%s, unhandled sig: %d", __func__, msg.sig);
+ ESP_LOGW(TAG, "%s, unhandled sig: %d", __func__, msg.sig);
break;
}
@@ -142,40 +138,37 @@ static void bt_app_task_handler(void *arg)
free(msg.param);
}
} 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");
if (esp_bluedroid_deinit() != ESP_OK) goto exit;
-
+
ESP_LOGD(TAG, "esp_bluedroid_deinit called successfully");
if (esp_bt_controller_disable() != ESP_OK) goto exit;
-
+
ESP_LOGD(TAG, "esp_bt_controller_disable called successfully");
if (esp_bt_controller_deinit() != ESP_OK) goto exit;
-
- ESP_LOGD(TAG, "bt stopped successfully");
+
+ ESP_LOGD(TAG, "bt stopped successfully");
exit:
- vQueueDelete(s_bt_app_task_queue);
- running = false;
+ vQueueDelete(s_bt_app_task_queue);
+ running = false;
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);
}
-void bt_app_task_shut_down(void)
-{
- running = false;
-}
+void bt_app_task_shut_down(void) { running = false; }
diff --git a/components/driver_bt/bt_app_core.h b/components/driver_bt/bt_app_core.h
index 7ccf9651..7ef70c90 100644
--- a/components/driver_bt/bt_app_core.h
+++ b/components/driver_bt/bt_app_core.h
@@ -8,53 +8,58 @@
#ifndef __BT_APP_CORE_H__
#define __BT_APP_CORE_H__
+#include "Status.pb.h"
#include "esp_log.h"
#include "time.h"
-#include
#include
+#include
#include
-#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 {
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
*/
-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 */
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;
/**
* @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
*/
-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
*/
-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_shut_down(void);
#endif /* __BT_APP_CORE_H__ */
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/components/driver_bt/bt_app_sink.c b/components/driver_bt/bt_app_sink.c
index c5e9db08..16fa5ccf 100644
--- a/components/driver_bt/bt_app_sink.c
+++ b/components/driver_bt/bt_app_sink.c
@@ -21,7 +21,7 @@
#include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
-#include "Configurator.h"
+#include "Config.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tools.h"
@@ -45,7 +45,6 @@ static const char BT_RC_CT_TAG[] = "RCCT";
#define CONFIG_BT_NAME "ESP32-BT"
#endif
-static char * bt_name = NULL;
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);
@@ -511,7 +510,7 @@ static void volume_set_by_local_host(int value, bool is_step)
_lock_release(&s_volume_lock);
if(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) {
@@ -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;
cmd_handler_chain = cmd_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;
}
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;
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);
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);
esp_bt_gap_set_pin(pin_type, strlen(pin_code), esp_pin_code);
- free(pin_code);
}
void bt_sink_deinit(void)
diff --git a/components/driver_bt/bt_app_source.c b/components/driver_bt/bt_app_source.c
index 724ca50c..047b9a1f 100644
--- a/components/driver_bt/bt_app_source.c
+++ b/components/driver_bt/bt_app_source.c
@@ -20,8 +20,10 @@
#include "cJSON.h"
#include "tools.h"
#include "accessors.h"
-#include "Configurator.h"
+#include "Config.h"
#include "Status.pb.h"
+#include "bootstate.h"
+
static const char * TAG = "bt_app_source";
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 char* output_state_str(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 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)
@@ -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 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 TimerHandle_t s_tmr=NULL;
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 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;
}
-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;
}
void set_app_source_state(int 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;
}
}
@@ -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;
}
- 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;
}
- 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);
- /*
- * 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;
- 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;
- }
- else {
- pin_code = (uint8_t *) out_bt->pin;
+ } else {
+ pin_code = (uint8_t*)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) {
@@ -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) {
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_a2d_source_connect(s_peer_bda);
} 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)
{
- 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){
@@ -444,20 +445,20 @@ static void handle_connect_state_connecting(uint16_t event, esp_a2d_cb_param_t *
else {
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;
case ESP_A2D_CONNECTION_STATE_CONNECTING:
break;
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_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.");
break;
case ESP_A2D_CONNECTION_STATE_DISCONNECTING:
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;
default:
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:
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);
break;
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);
break;
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;
default:
@@ -493,7 +494,7 @@ static void handle_connect_state_disconnecting(uint16_t event, esp_a2d_cb_param_
{
case ESP_A2D_CONNECTION_STATE_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);
break;
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)
{
- 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) {
- 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;
- 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;
- case sys_AV_STATE_A_UNCONNECTED:
+ case sys_status_av_states_A_UNCONNECTED:
bt_app_av_state_unconnected(event, param);
break;
- case sys_AV_STATE_A_CONNECTING:
+ case sys_status_av_states_A_CONNECTING:
bt_app_av_state_connecting(event, param);
break;
- case sys_AV_STATE_A_CONNECTED:
+ case sys_status_av_states_A_CONNECTED:
bt_app_av_state_connected(event, param);
break;
- case sys_AV_STATE_A_DISCONNECTING:
+ case sys_status_av_states_A_DISCONNECTING:
bt_app_av_state_disconnecting(event, param);
break;
default:
@@ -591,7 +592,7 @@ static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param)
uint8_t nameLen = 0;
esp_bt_gap_dev_prop_t *p;
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
// 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) {
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);
- set_app_source_state(sys_AV_STATE_A_DISCOVERED);
+ set_app_source_state(sys_status_av_states_A_DISCOVERED);
esp_bt_gap_cancel_discovery();
} else {
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 */
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);
/* 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_GET_RN_CAPABILITIES_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);
break;
}
@@ -735,7 +736,7 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
{
esp_a2d_cb_param_t *a2d = NULL;
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(!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
) {
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);
}
}
break;
}
- case sys_MEDIA_STATE_M_STARTING: {
+ case sys_status_media_states_M_STARTING: {
if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) {
a2d = (esp_a2d_cb_param_t *)(param);
if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START &&
a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
ESP_LOGI(TAG,"a2dp media started successfully.");
output_bt_start();
- set_a2dp_media_state(sys_MEDIA_STATE_M_STARTED);
+ set_a2dp_media_state(sys_status_media_states_M_STARTED);
} else {
// not started succesfully, transfer to idle state
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;
}
- case sys_MEDIA_STATE_M_STARTED: {
+ case sys_status_media_states_M_STARTED: {
if (event == BT_APP_HEART_BEAT_EVT) {
if(output_stopped()) {
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);
} else {
output_bt_tick();
@@ -784,15 +785,15 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
}
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) {
a2d = (esp_a2d_cb_param_t *)(param);
if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP &&
a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
ESP_LOGI(TAG,"a2dp media stopped successfully...");
output_bt_stop();
- set_a2dp_media_state(sys_MEDIA_STATE_M_IDLE);
+ set_a2dp_media_state(sys_status_media_states_M_IDLE);
} else {
ESP_LOGI(TAG,"a2dp media stopping...");
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;
}
- case sys_MEDIA_STATE_M_WAIT_DISCONNECT:{
+ case sys_status_media_states_M_WAIT_DISCONNECT:{
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...");
}
}
@@ -843,11 +844,11 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param)
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]);
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;
}
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
ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
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;
case BT_APP_HEART_BEAT_EVT:
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. ");
s_connecting_intv = 0;
}
diff --git a/components/led_strip/CMakeLists.txt b/components/led_strip/CMakeLists.txt
index c7e9f3f0..a62e1f2a 100644
--- a/components/led_strip/CMakeLists.txt
+++ b/components/led_strip/CMakeLists.txt
@@ -1,7 +1,7 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
- REQUIRES platform_config tools esp_common
+ REQUIRES tools platform_config esp_common
PRIV_REQUIRES services freertos driver
)
diff --git a/components/led_strip/led_vu.c b/components/led_strip/led_vu.c
index 6367208c..f62b596b 100644
--- a/components/led_strip/led_vu.c
+++ b/components/led_strip/led_vu.c
@@ -23,7 +23,7 @@
#include "globdefs.h"
#include "monitor.h"
#include "led_strip.h"
-#include "Configurator.h"
+#include "Config.h"
#include "accessors.h"
#include "led_vu.h"
@@ -76,22 +76,23 @@ static void battery_svc(float value, int cells) {
*
*/
void led_vu_init()
-{
- sys_LEDStrip * config = NULL;
+{
+ sys_dev_led_strip*config;
if(!SYS_DEV_LEDSTRIP(config)){
+ ESP_LOGI(TAG,"No LED Strip configuration");
return;
}
- if(!!config->has_gpio ){
+ if(config->gpio<0){
+ ESP_LOGI(TAG,"LED Strip has no GPIO configured");
return;
}
- // char* config = config_alloc_get_str("led_vu_config", NULL, "N/A");
-
+ ESP_LOGI(TAG, "Initializing led_vu");
// Initialize led VU strip
strip.length = config->length;
- strip.gpio = config->gpio.pin;
+ strip.gpio = config->gpio;
// 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");
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);
// 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.led_strip_length = strip.length;
led_strip_config.led_strip_working = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT);
diff --git a/components/metrics/CMakeLists.txt b/components/metrics/CMakeLists.txt
index 289ff534..ec5fb2bf 100644
--- a/components/metrics/CMakeLists.txt
+++ b/components/metrics/CMakeLists.txt
@@ -1,5 +1,5 @@
idf_component_register(SRC_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
)
diff --git a/components/metrics/Metrics.cpp b/components/metrics/Metrics.cpp
index d1656ee2..fe1b0d15 100644
--- a/components/metrics/Metrics.cpp
+++ b/components/metrics/Metrics.cpp
@@ -20,13 +20,12 @@
#include "cJSON.h"
#include "freertos/timers.h"
#include "network_manager.h"
-// #include "Configurator.h"
+// #include "Config.h"
#pragma message("fixme: search for TODO below")
static const char* TAG = "metrics";
#if CONFIG_WITH_METRICS
-extern bool is_network_connected();
#define METRICS_CLIENT_ID_LEN 50
#define MAX_HTTP_RECV_BUFFER 512
diff --git a/components/platform_config/CMakeLists.txt b/components/platform_config/CMakeLists.txt
index d13b2517..658c41a1 100644
--- a/components/platform_config/CMakeLists.txt
+++ b/components/platform_config/CMakeLists.txt
@@ -1,5 +1,7 @@
+set(CMAKE_CXX_STANDARD 20)
idf_component_register(SRC_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)
\ No newline at end of file
diff --git a/components/platform_config/Config.cpp b/components/platform_config/Config.cpp
new file mode 100644
index 00000000..ccedda64
--- /dev/null
+++ b/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
+#include
+#include
+#include
+#include
+
+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 configWrapper("config", &sys_config_msg, sizeof(sys_config_msg));
+__attribute__((section(".ext_ram.bss"))) System::PB stateWrapper("state", &sys_state_data_msg, sizeof(sys_state_data_msg));
+__attribute__((section(".ext_ram.bss"))) System::PB 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(*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(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;
+}
diff --git a/components/platform_config/Config.h b/components/platform_config/Config.h
new file mode 100644
index 00000000..40b819ae
--- /dev/null
+++ b/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 stateWrapper;
+extern System::PB 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
+
diff --git a/components/platform_config/Configurator.cpp b/components/platform_config/Configurator.cpp
deleted file mode 100644
index 382c7442..00000000
--- a/components/platform_config/Configurator.cpp
+++ /dev/null
@@ -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
-#include
-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(*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;
-}
\ No newline at end of file
diff --git a/components/platform_config/Configurator.h b/components/platform_config/Configurator.h
deleted file mode 100644
index 3ed9b34e..00000000
--- a/components/platform_config/Configurator.h
+++ /dev/null
@@ -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
-#include "accessors.h"
-#ifdef __cplusplus
-#include
-#include
-#include
-
-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
diff --git a/components/platform_config/Locking.cpp b/components/platform_config/Locking.cpp
new file mode 100644
index 00000000..078574d9
--- /dev/null
+++ b/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(Locking::Create(std::string(name)));
+}
+
+void Locking_Destroy(LockingHandle* lock) { Locking::Destroy(reinterpret_cast(lock)); }
+
+bool Locking_Lock(LockingHandle* lock, TickType_t maxWait_ms) {
+ return reinterpret_cast(lock)->Lock(maxWait_ms);
+}
+
+void Locking_Unlock(LockingHandle* lock) { reinterpret_cast(lock)->Unlock(); }
+
+bool Locking_IsLocked(LockingHandle* lock) { return reinterpret_cast(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);
+}
diff --git a/components/platform_config/Locking.h b/components/platform_config/Locking.h
new file mode 100644
index 00000000..5be78646
--- /dev/null
+++ b/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
+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
\ No newline at end of file
diff --git a/components/platform_config/PBW.cpp b/components/platform_config/PBW.cpp
new file mode 100644
index 00000000..f31e45dd
--- /dev/null
+++ b/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
+#include
+#include
+
+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(flags);
+ int curFlags = xEventGroupGetBits(_group);
+ if ((curFlags & static_cast(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(protoWrapper);
+ if (protoWrapperBase) {
+ protoWrapperBase->CommitChanges();
+ }
+}
+std::vector 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 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 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(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(Flags::COMMITTED), pdFALSE, pdTRUE, (MaxDelay * 2) / portTICK_PERIOD_MS);
+ commit_pending = !(bits & static_cast(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(static_cast(this)));
+ }
+}
+const std::string& PBHelper::GetFileName() { return filename; }
+bool PBHelper::HasChanges() { return !(xEventGroupGetBits(_group) & static_cast(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;
+}
diff --git a/components/platform_config/PBW.h b/components/platform_config/PBW.h
new file mode 100644
index 00000000..7de4f268
--- /dev/null
+++ b/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
+#include
+#include
+
+namespace System {
+template 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(&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(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 EncodeData(const pb_msgdesc_t* fields, const void* src_struct);
+ static void DecodeData(std::vector 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 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{}); }
+ 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{});
+ 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(_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(_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(_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 Encode() {
+ auto data = std::vector();
+ 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 data, bool noinit = false) { PBHelper::DecodeData(data, _fields, (void*)_root, noinit); }
+
+ ~PB() { vEventGroupDelete(_group); };
+};
+template <> struct has_target_implementation : std::true_type {};
+
+template <> struct has_target_implementation : 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
\ No newline at end of file
diff --git a/components/platform_config/WifiList.cpp b/components/platform_config/WifiList.cpp
new file mode 100644
index 00000000..fd9edfdb
--- /dev/null
+++ b/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
+
+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(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 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& 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& 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& 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(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(>s);
+ }
+ if (entry.has_last_seen) {
+ PrintTimeStamp(&entry.last_seen);
+ } else {
+ PrintTimeStamp(>s);
+ }
+ 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 WifiList::GetRadioTypes(const wifi_ap_record_t* sta) {
+ std::list 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;
+}
diff --git a/components/platform_config/WifiList.h b/components/platform_config/WifiList.h
new file mode 100644
index 00000000..ea8ffa89
--- /dev/null
+++ b/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
+#include
+#include