Start of 5.X work

This commit is contained in:
Sebastien L
2025-03-18 17:38:34 -04:00
parent c0ddf0a997
commit 73bd096f37
442 changed files with 227862 additions and 21075 deletions

14
.clang-format Normal file
View File

@@ -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
}

View File

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

View File

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

7
.gitignore vendored
View File

@@ -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

View File

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

View File

@@ -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)

View File

@@ -1,188 +1,82 @@
FROM ubuntu:20.04
FROM espressif/idf:v4.4.6
# Install additional dependencies
RUN apt-get update && apt-get install -y \
python3-protobuf
RUN DEBIAN_FRONTEND=noninteractive TZ=America/Toronto apt-get -y install tzdata
# Add any other dependencies you need
# To build the image for a branch or a tag of IDF, pass --build-arg IDF_CLONE_BRANCH_OR_TAG=name.
# To build the image with a specific commit ID of IDF, pass --build-arg IDF_CHECKOUT_REF=commit-id.
# It is possibe to combine both, e.g.:
# IDF_CLONE_BRANCH_OR_TAG=release/vX.Y
# IDF_CHECKOUT_REF=<some commit on release/vX.Y branch>.
# Docker build for release 4.3.5 as of 2023/05/18
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv446
# Updating the docker image in the repository
# docker push sle118/squeezelite-esp32-idfv446
# or to do both:
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv446 && docker push sle118/squeezelite-esp32-idfv446
# docker build . -t sle118/squeezelite-esp32-idfv446 && docker push sle118/squeezelite-esp32-idfv446
#docker run --isolation=process --device="class/86E0D1E0-8089-11D0-9CE4-08003E301F73" mcr.microsoft.com/windows/servercore:1809
# (windows) To run the image interactive :
# docker run --rm -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv446
# (windows powershell)
# docker run --rm -v ${PWD}:/project -w /project -it sle118/squeezelite-esp32-idfv446
# (linux) To run the image interactive :
# docker run --rm -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv446
# to build the web app inside of the interactive session
# pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
#
# to run the docker with netwotrk port published on the host:
# docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv446
ARG DEBIAN_FRONTEND=noninteractive
ENV GCC_TOOLS_BASE=/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-
# To build the image for a branch or a tag of IDF, pass --build-arg IDF_CLONE_BRANCH_OR_TAG=name.
# To build the image with a specific commit ID of IDF, pass --build-arg IDF_CHECKOUT_REF=commit-id.
# It is possibe to combine both, e.g.:
# IDF_CLONE_BRANCH_OR_TAG=release/vX.Y
# IDF_CHECKOUT_REF=<some commit on release/vX.Y branch>.
# Docker build for release 4.3.5 as of 2023/05/18
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv435
# Updating the docker image in the repository
# docker push sle118/squeezelite-esp32-idfv435
# or to do both:
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv435 && docker push sle118/squeezelite-esp32-idfv435
#
# (windows) To run the image interactive :
# docker run --rm -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
# (windows powershell)
# docker run --rm -v ${PWD}:/project -w /project -it sle118/squeezelite-esp32-idfv435
# (linux) To run the image interactive :
# docker run --rm -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv435
# to build the web app inside of the interactive session
# pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
#
# to run the docker with netwotrk port published on the host:
# docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
ARG IDF_CLONE_URL=https://github.com/espressif/esp-idf.git
ARG IDF_CLONE_BRANCH_OR_TAG=master
ARG IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d
ENV IDF_PATH=/opt/esp/idf
ENV IDF_TOOLS_PATH=/opt/esp
# We need libpython2.7 due to GDB tools
# we also need npm 8 for the webapp to work
RUN : \
&& apt-get update \
&& apt-get install -y \
apt-utils \
build-essential \
bison \
ca-certificates \
ccache \
check \
curl \
flex \
git \
git-lfs \
gperf \
lcov \
libbsd-dev \
libpython3.8 \
libffi-dev \
libncurses-dev \
libusb-1.0-0-dev \
make \
ninja-build \
python3.8 \
python3-pip \
python3-venv \
ruby \
unzip \
wget \
xz-utils \
zip \
npm \
nodejs \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \
&& python -m pip install --upgrade \
pip \
virtualenv \
&& cd /opt \
&& git clone https://github.com/HBehrens/puncover.git \
&& cd puncover \
&& python setup.py -q install \
&& echo IDF_CHECKOUT_REF=$IDF_CHECKOUT_REF IDF_CLONE_BRANCH_OR_TAG=$IDF_CLONE_BRANCH_OR_TAG \
&& git clone --recursive \
${IDF_CLONE_BRANCH_OR_TAG:+-b $IDF_CLONE_BRANCH_OR_TAG} \
$IDF_CLONE_URL $IDF_PATH \
&& if [ -n "$IDF_CHECKOUT_REF" ]; then \
cd $IDF_PATH \
&& git checkout $IDF_CHECKOUT_REF \
&& git submodule update --init --recursive; \
fi \
&& update-ca-certificates --fresh \
&& $IDF_PATH/tools/idf_tools.py --non-interactive install required \
&& $IDF_PATH/tools/idf_tools.py --non-interactive install cmake \
&& $IDF_PATH/tools/idf_tools.py --non-interactive install-python-env \
&& :
RUN : \
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 ******************************************************** \

View File

@@ -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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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 )

View File

@@ -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));

View File

@@ -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;

View File

@@ -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;

View File

@@ -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));

View File

@@ -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));

View File

@@ -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));

View File

@@ -227,24 +227,25 @@ static bool Init( struct GDS_Device* Device ) {
}
static const struct GDS_Device SSD1675 = {
.DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow,
.DrawBitmapCBR = DrawBitmapCBR,
.ClearWindow = ClearWindow,
.DrawPixelFast = _DrawPixel,
.Update = Update, .Init = Init,
.Mode = GDS_MONO, .Depth = 1,
.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;
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;
char *p;
struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
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;
if (Driver->common.ready >= 0) {
Private->ReadyPin = Driver->common.ready;
}
ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin);

View File

@@ -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));

View File

@@ -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++) {

View File

@@ -3,7 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#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 );

View File

@@ -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) {

View File

@@ -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,37 +99,26 @@ 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;
//todo: what is mode?
// PARSE_PARAM(config, "mode", '=', mode);
// todo: need to handle display offsets
mode = sys_display->dispType.spi.mode;
init = true;
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
@@ -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)

View File

@@ -6,31 +6,31 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "bt_app_core.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_log.h"
#include "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 <stdbool.h>
#include <stdint.h>
#include <string.h>
static const char *TAG = "btappcore";
static const char* TAG = "btappcore";
static void bt_app_task_handler(void *arg);
static bool bt_app_send_msg(bt_app_msg_t *msg);
static void bt_app_work_dispatched(bt_app_msg_t *msg);
static void bt_app_task_handler(void* arg);
static bool bt_app_send_msg(bt_app_msg_t* msg);
static void bt_app_work_dispatched(bt_app_msg_t* msg);
static xQueueHandle s_bt_app_task_queue;
static 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,28 +54,25 @@ 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;
@@ -83,7 +80,7 @@ static void bt_app_task_handler(void *arg)
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;
@@ -106,8 +103,7 @@ static void bt_app_task_handler(void *arg)
}
/* 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);
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;
@@ -127,14 +123,14 @@ static void bt_app_task_handler(void *arg)
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,7 +138,7 @@ 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.");
}
}
@@ -151,7 +147,7 @@ static void bt_app_task_handler(void *arg)
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);
vTaskDelay(2 * 200 / portTICK_PERIOD_MS);
ESP_LOGD(TAG, "esp_bluedroid_disable called successfully");
if (esp_bluedroid_deinit() != ESP_OK) goto exit;
@@ -170,12 +166,9 @@ exit:
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; }

View File

@@ -8,13 +8,15 @@
#ifndef __BT_APP_CORE_H__
#define __BT_APP_CORE_H__
#include "Status.pb.h"
#include "esp_log.h"
#include "time.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "Status.pb.h"
#ifdef __cplusplus
extern "C" {
#endif
#define BT_APP_CORE_TAG "BT_APP_CORE"
#define BT_APP_SIG_WORK_DISPATCH (0x01)
@@ -22,39 +24,42 @@ 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 */
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

View File

@@ -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)

View File

@@ -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,19 +205,18 @@ 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
bt_app_task_start_up(bt_av_hdl_stack_evt);
@@ -225,16 +227,15 @@ void hal_bluetooth_init()
*/
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;
}

View File

@@ -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
)

View File

@@ -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"
@@ -77,21 +77,22 @@ 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);

View File

@@ -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
)

View File

@@ -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

View File

@@ -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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -9,23 +9,23 @@
#include "platform_console.h"
#include "argtable3/argtable3.h"
#include "bootstate.h"
#include "cmd_decl.h"
#include "driver/uart.h"
#include "esp_console.h"
#include "esp_log.h"
#include "esp_vfs_dev.h"
#include "linenoise/linenoise.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "platform_esp32.h"
#include "pthread.h"
#include "trace.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "pthread.h"
#include "platform_esp32.h"
#include "cmd_decl.h"
#include "trace.h"
// #include "Configurator.h"
#pragma message("fixme: search for TODO below")
#include "telnet.h"
#include "tools.h"
#if defined(CONFIG_WITH_METRICS)
@@ -35,9 +35,9 @@
#include "config.h"
static pthread_t thread_console;
static void * console_thread();
static void* console_thread();
void console_start();
static const char * TAG = "console";
static const char* TAG = "console";
extern bool bypass_network_manager;
extern void launchsqueezelite();
@@ -62,93 +62,98 @@ const char* recovery_prompt = LOG_COLOR_E "recovery-squeezelite-esp32> " LOG_RES
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
static esp_err_t run_command(char * line);
#define ADD_TO_JSON(o,t,n) if (t->n) cJSON_AddStringToObject(o,QUOTE(n),t->n);
#define ADD_PARMS_TO_CMD(o,t,n) { cJSON * parms = ParmsToJSON(&t.n->hdr); if(parms) cJSON_AddItemToObject(o,QUOTE(n),parms); }
cJSON * cmdList;
cJSON * values_fn_list;
cJSON * get_cmd_list(){
cJSON * element;
cJSON * values=cJSON_CreateObject();
cJSON * list = cJSON_CreateObject();
cJSON_AddItemReferenceToObject(list,"commands",cmdList);
cJSON_AddItemToObject(list,"values",values);
cJSON_ArrayForEach(element,cmdList){
cJSON * name = cJSON_GetObjectItem(element,"name");
cJSON * vals_fn = cJSON_GetObjectItem(values_fn_list,cJSON_GetStringValue(name));
if(vals_fn!=NULL ){
parm_values_fn_t *parm_values_fn = (parm_values_fn_t *)strtoul(cJSON_GetStringValue(vals_fn), NULL, 16);;
static esp_err_t run_command(char* line);
#define ADD_TO_JSON(o, t, n) \
if (t->n) cJSON_AddStringToObject(o, QUOTE(n), t->n);
#define ADD_PARMS_TO_CMD(o, t, n) \
{ \
cJSON* parms = ParmsToJSON(&t.n->hdr); \
if (parms) cJSON_AddItemToObject(o, QUOTE(n), parms); \
}
cJSON* cmdList;
cJSON* values_fn_list;
cJSON* get_cmd_list() {
cJSON* element;
cJSON* values = cJSON_CreateObject();
cJSON* list = cJSON_CreateObject();
cJSON_AddItemReferenceToObject(list, "commands", cmdList);
cJSON_AddItemToObject(list, "values", values);
cJSON_ArrayForEach(element, cmdList) {
cJSON* name = cJSON_GetObjectItem(element, "name");
cJSON* vals_fn = cJSON_GetObjectItem(values_fn_list, cJSON_GetStringValue(name));
if (vals_fn != NULL) {
parm_values_fn_t* parm_values_fn = (parm_values_fn_t*)strtoul(cJSON_GetStringValue(vals_fn), NULL, 16);
;
if(parm_values_fn){
cJSON_AddItemToObject(values,cJSON_GetStringValue(name),parm_values_fn());
if (parm_values_fn) {
cJSON_AddItemToObject(values, cJSON_GetStringValue(name), parm_values_fn());
}
}
}
return list;
}
void console_set_bool_parameter(cJSON * root,char * nvs_name, struct arg_lit *arg){
char * p=NULL;
void console_set_bool_parameter(cJSON* root, char* nvs_name, struct arg_lit* arg) {
char* p = NULL;
bool enabled = false;
if(!root) {
ESP_LOGE(TAG,"Invalid json parameter. Cannot set %s from %s",arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary,nvs_name);
if (!root) {
ESP_LOGE(TAG, "Invalid json parameter. Cannot set %s from %s", arg->hdr.longopts ? arg->hdr.longopts : arg->hdr.glossary, nvs_name);
return;
}
// if ((p = config_alloc_get(NVS_TYPE_STR, nvs_name)) != NULL) {
// enabled = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
// cJSON_AddBoolToObject(root,arg->hdr.longopts,enabled);
// FREE_AND_NULL(p);
// }
// #if defined(CONFIG_WITH_METRICS)
// if(enabled){
// metrics_add_feature(nvs_name,"enabled");
// }
// #endif
// TODO: Add support for the commented code
// if ((p = config_alloc_get(NVS_TYPE_STR, nvs_name)) != NULL) {
// enabled = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
// cJSON_AddBoolToObject(root,arg->hdr.longopts,enabled);
// FREE_AND_NULL(p);
// }
// #if defined(CONFIG_WITH_METRICS)
// if(enabled){
// metrics_add_feature(nvs_name,"enabled");
// }
// #endif
// TODO: Add support for the commented code
}
struct arg_end *getParmsEnd(struct arg_hdr * * argtable){
if(!argtable) return NULL;
struct arg_hdr * *table = (struct arg_hdr * *)argtable;
struct arg_end* getParmsEnd(struct arg_hdr** argtable) {
if (!argtable) return NULL;
struct arg_hdr** table = (struct arg_hdr**)argtable;
int tabindex = 0;
while (!(table[tabindex]->flag & ARG_TERMINATOR))
{
while (!(table[tabindex]->flag & ARG_TERMINATOR)) {
tabindex++;
}
return (struct arg_end *)table[tabindex];
return (struct arg_end*)table[tabindex];
}
cJSON * ParmsToJSON(struct arg_hdr * * argtable){
if(!argtable) return NULL;
cJSON * arg_list = cJSON_CreateArray();
struct arg_hdr * *table = (struct arg_hdr * *)argtable;
cJSON* ParmsToJSON(struct arg_hdr** argtable) {
if (!argtable) return NULL;
cJSON* arg_list = cJSON_CreateArray();
struct arg_hdr** table = (struct arg_hdr**)argtable;
int tabindex = 0;
while (!(table[tabindex]->flag & ARG_TERMINATOR))
{
cJSON * entry = cJSON_CreateObject();
ADD_TO_JSON(entry,table[tabindex],datatype);
ADD_TO_JSON(entry,table[tabindex],glossary);
ADD_TO_JSON(entry,table[tabindex],longopts);
ADD_TO_JSON(entry,table[tabindex],shortopts);
cJSON_AddBoolToObject(entry, "checkbox", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (table[tabindex]->longopts || table[tabindex]->shortopts) );
cJSON_AddBoolToObject(entry, "remark", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (!table[tabindex]->longopts && !table[tabindex]->shortopts));
while (!(table[tabindex]->flag & ARG_TERMINATOR)) {
cJSON* entry = cJSON_CreateObject();
ADD_TO_JSON(entry, table[tabindex], datatype);
ADD_TO_JSON(entry, table[tabindex], glossary);
ADD_TO_JSON(entry, table[tabindex], longopts);
ADD_TO_JSON(entry, table[tabindex], shortopts);
cJSON_AddBoolToObject(entry, "checkbox",
(table[tabindex]->flag & ARG_HASOPTVALUE) == 0 && (table[tabindex]->flag & ARG_HASVALUE) == 0 &&
(table[tabindex]->longopts || table[tabindex]->shortopts));
cJSON_AddBoolToObject(entry, "remark",
(table[tabindex]->flag & ARG_HASOPTVALUE) == 0 && (table[tabindex]->flag & ARG_HASVALUE) == 0 &&
(!table[tabindex]->longopts && !table[tabindex]->shortopts));
cJSON_AddBoolToObject(entry, "hasvalue", table[tabindex]->flag & ARG_HASVALUE);
cJSON_AddNumberToObject(entry,"mincount",table[tabindex]->mincount);
cJSON_AddNumberToObject(entry,"maxcount",table[tabindex]->maxcount);
cJSON_AddNumberToObject(entry, "mincount", table[tabindex]->mincount);
cJSON_AddNumberToObject(entry, "maxcount", table[tabindex]->maxcount);
cJSON_AddItemToArray(arg_list, entry);
tabindex++;
}
return arg_list;
}
esp_err_t cmd_to_json(const esp_console_cmd_t *cmd){
return cmd_to_json_with_cb(cmd, NULL);
}
esp_err_t cmd_to_json(const esp_console_cmd_t* cmd) { return cmd_to_json_with_cb(cmd, NULL); }
esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn){
if(!cmdList){
cmdList=cJSON_CreateArray();
esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t* cmd, parm_values_fn_t parm_values_fn) {
if (!cmdList) {
cmdList = cJSON_CreateArray();
}
if(!values_fn_list){
values_fn_list=cJSON_CreateObject();
if (!values_fn_list) {
values_fn_list = cJSON_CreateObject();
}
if (cmd->command == NULL) {
@@ -157,27 +162,26 @@ esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t par
if (strchr(cmd->command, ' ') != NULL) {
return ESP_ERR_INVALID_ARG;
}
cJSON * jsoncmd = cJSON_CreateObject();
ADD_TO_JSON(jsoncmd,cmd,help);
ADD_TO_JSON(jsoncmd,cmd,hint);
if(parm_values_fn){
char addr[11]={0};
snprintf(addr,sizeof(addr),"%lx",(unsigned long)parm_values_fn);
cJSON_AddStringToObject(values_fn_list,cmd->command,addr);
cJSON* jsoncmd = cJSON_CreateObject();
ADD_TO_JSON(jsoncmd, cmd, help);
ADD_TO_JSON(jsoncmd, cmd, hint);
if (parm_values_fn) {
char addr[11] = {0};
snprintf(addr, sizeof(addr), "%lx", (unsigned long)parm_values_fn);
cJSON_AddStringToObject(values_fn_list, cmd->command, addr);
}
cJSON_AddBoolToObject(jsoncmd,"hascb",parm_values_fn!=NULL);
cJSON_AddBoolToObject(jsoncmd, "hascb", parm_values_fn != NULL);
if(cmd->argtable){
cJSON_AddItemToObject(jsoncmd,"argtable",ParmsToJSON(cmd->argtable));
if (cmd->argtable) {
cJSON_AddItemToObject(jsoncmd, "argtable", ParmsToJSON(cmd->argtable));
}
if (cmd->hint) {
cJSON_AddStringToObject(jsoncmd, "hint", cmd->hint);
}
else if (cmd->argtable) {
} else if (cmd->argtable) {
/* Generate hint based on cmd->argtable */
char *buf = NULL;
char* buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE* f = open_memstream(&buf, &buf_size);
if (f != NULL) {
arg_print_syntax(f, cmd->argtable, NULL);
fflush(f);
@@ -187,107 +191,65 @@ esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t par
FREE_AND_NULL(buf);
}
cJSON_AddStringToObject(jsoncmd, "name", cmd->command);
char * b=cJSON_Print(jsoncmd);
if(b){
ESP_LOGD(TAG,"Adding command table %s",b);
char* b = cJSON_Print(jsoncmd);
if (b) {
ESP_LOGD(TAG, "Adding command table %s", b);
free(b);
}
cJSON_AddItemToArray(cmdList, jsoncmd);
return ESP_OK;
}
int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args){
int nerrors = arg_parse(argc, argv, (void **)args);
int arg_parse_msg(int argc, char** argv, struct arg_hdr** args) {
int nerrors = arg_parse(argc, argv, (void**)args);
if (nerrors != 0) {
char *buf = NULL;
char* buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE* f = open_memstream(&buf, &buf_size);
if (f != NULL) {
arg_print_errors(f, getParmsEnd(args), argv[0]);
fflush (f);
cmd_send_messaging(argv[0],MESSAGING_ERROR,"%s", buf);
fflush(f);
cmd_send_messaging(argv[0], MESSAGING_ERROR, "%s", buf);
}
fclose(f);
FREE_AND_NULL(buf);
}
return nerrors;
}
void process_autoexec(){
int i=1;
char autoexec_name[21]={0};
char * autoexec_value=NULL;
uint8_t autoexec_flag=0;
// TODO: Add support for the commented code
void * cmd = run_command;
// char * str_flag = config_alloc_get(NVS_TYPE_STR, "autoexec");
// if(!bypass_network_manager){
// ESP_LOGW(TAG, "Processing autoexec commands while network manager active. Wifi related commands will be ignored.");
// }
// if(is_recovery_running){
// ESP_LOGD(TAG, "Processing autoexec commands in recovery mode. Squeezelite commands will be ignored.");
// }
// if(str_flag !=NULL ){
// autoexec_flag=atoi(str_flag);
// ESP_LOGI(TAG,"autoexec is set to %s auto-process", autoexec_flag>0?"perform":"skip");
// if(autoexec_flag == 1) {
// do {
// snprintf(autoexec_name,sizeof(autoexec_name)-1,"autoexec%u",i++);
// ESP_LOGD(TAG,"Getting command name %s", autoexec_name);
// autoexec_value= config_alloc_get(NVS_TYPE_STR, autoexec_name);
// if(autoexec_value!=NULL ){
// if(!bypass_network_manager && strstr(autoexec_value, "join ")!=NULL ){
// ESP_LOGW(TAG,"Ignoring wifi join command.");
// }
// else if(is_recovery_running && !strstr(autoexec_value, "squeezelite " ) ){
// ESP_LOGW(TAG,"Ignoring command. ");
// }
// else {
// ESP_LOGI(TAG,"Running command %s = %s", autoexec_name, autoexec_value);
// run_command(autoexec_value);
// }
// ESP_LOGD(TAG,"Freeing memory for command %s name", autoexec_name);
// free(autoexec_value);
// }
// else {
// ESP_LOGD(TAG,"No matching command found for name %s", autoexec_name);
// break;
// }
// } while(1);
// }
// free(str_flag);
// }
// else
// {
// ESP_LOGD(TAG,"No matching command found for name autoexec.");
// }
// TODO: Add support for the commented code
}
static ssize_t stdin_read(int fd, void* data, size_t size) {
size_t bytes = -1;
static size_t remain = 0;
if (remain > 0) {
bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < remain ? size : remain, 0);
remain -= bytes;
for (int i = 0; i < bytes; i++)
if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';
return bytes;
}
while (1) {
QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, portMAX_DELAY);
if (activated == uart_queue) {
uart_event_t event;
xQueueReceive(uart_queue, &event, 0);
if (event.type == UART_DATA) {
bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < event.size ? size : event.size, 0);
// we have to do our own line ending translation here
for (int i = 0; i < bytes; i++) if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';
for (int i = 0; i < bytes; i++)
if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';
if (event.size > bytes) {
remain = event.size - bytes;
}
break;
}
} else if (xRingbufferCanRead(stdin_redir.handle, activated)) {
char *p = xRingbufferReceiveUpTo(stdin_redir.handle, &bytes, 0, size);
char* p = xRingbufferReceiveUpTo(stdin_redir.handle, &bytes, 0, size);
// we might receive strings, replace null by \n
for (int i = 0; i < bytes; i++) if (p[i] == '\0' || p[i] == '\r') p[i] = '\n';
for (int i = 0; i < bytes; i++)
if (p[i] == '\0' || p[i] == '\r') p[i] = '\n';
memcpy(data, p, bytes);
vRingbufferReturnItem(stdin_redir.handle, p);
xRingbufferPrintInfo(stdin_redir.handle);
break;
}
}
@@ -295,7 +257,7 @@ static ssize_t stdin_read(int fd, void* data, size_t size) {
return bytes;
}
static int stdin_dummy(const char * path, int flags, int mode) { return 0; }
static int stdin_dummy(const char* path, int flags, int mode) { return 0; }
void initialize_console() {
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed (unused if we redirect stdin) */
@@ -306,14 +268,16 @@ void initialize_console() {
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = { .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
const uart_config_t uart_config = {
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
};
ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 3, &uart_queue, 0));
ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 3, &uart_queue, 0));
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
@@ -324,7 +288,7 @@ void initialize_console() {
xRingbufferAddToQueueSetRead(stdin_redir.handle, stdin_redir.queue_set);
xQueueAddToSet(uart_queue, stdin_redir.queue_set);
esp_vfs_t vfs = { };
esp_vfs_t vfs = {};
vfs.flags = ESP_VFS_FLAG_DEFAULT;
vfs.open = stdin_dummy;
vfs.read = stdin_read;
@@ -336,7 +300,8 @@ void initialize_console() {
setvbuf(stdin, NULL, _IONBF, 0);
/* Initialize the console */
esp_console_config_t console_config = { .max_cmdline_args = 28,
esp_console_config_t console_config = {
.max_cmdline_args = 28,
.max_cmdline_length = 600,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
@@ -352,42 +317,30 @@ void initialize_console() {
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
linenoiseSetHintsCallback((linenoiseHintsCallback*)&esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
/* Load command history from filesystem */
//linenoiseHistoryLoad(HISTORY_PATH);
}
bool console_push(const char *data, size_t size) {
return xRingbufferSend(stdin_redir.handle, data, size, pdMS_TO_TICKS(100)) == pdPASS;
}
bool console_push(const char* data, size_t size) { return xRingbufferSend(stdin_redir.handle, data, size, pdMS_TO_TICKS(100)) == pdPASS; }
void console_start() {
/* we always run console b/c telnet sends commands to stdin */
initialize_console();
/* Register commands */
MEMTRACE_PRINT_DELTA_MESSAGE("Registering help command");
esp_console_register_help_command();
MEMTRACE_PRINT_DELTA_MESSAGE("Registering system commands");
register_system();
MEMTRACE_PRINT_DELTA_MESSAGE("Registering config commands");
register_config_cmd();
MEMTRACE_PRINT_DELTA_MESSAGE("Registering wifi commands");
register_wifi();
if(is_recovery_running){
MEMTRACE_PRINT_DELTA_MESSAGE("Registering recovery commands");
if (is_recovery_running) {
register_ota_cmd();
}
MEMTRACE_PRINT_DELTA_MESSAGE("Registering i2c commands");
register_i2ctools();
printf("\n");
if(is_recovery_running){
if (is_recovery_running) {
printf("****************************************************************\n"
"RECOVERY APPLICATION\n"
"This mode is used to flash Squeezelite into the OTA partition\n"
@@ -397,11 +350,6 @@ void console_start() {
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n"
"\n");
if(!is_recovery_running){
printf("To automatically execute lines at startup:\n"
"\tSet NVS variable autoexec (U8) = 1 to enable, 0 to disable automatic execution.\n"
"\tSet NVS variable autoexec[1~9] (string)to a command that should be executed automatically\n");
}
printf("\n\n");
/* Figure out if the terminal supports escape sequences */
@@ -417,53 +365,44 @@ void console_start() {
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
if(is_recovery_running){
recovery_prompt= "recovery-squeezelite-esp32>";
if (is_recovery_running) {
recovery_prompt = "recovery-squeezelite-esp32>";
}
prompt = "squeezelite-esp32> ";
#endif //CONFIG_LOG_COLORS
#endif // CONFIG_LOG_COLORS
}
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
cfg.thread_name= "console";
cfg.thread_name = "console";
cfg.inherit_cfg = true;
cfg.stack_size = 4*1024;
if(is_recovery_running){
cfg.stack_size = 4 * 1024;
if (is_recovery_running) {
prompt = recovery_prompt;
}
MEMTRACE_PRINT_DELTA_MESSAGE("Creating console thread with stack size of 4096 bytes");
esp_pthread_set_cfg(&cfg);
pthread_create(&thread_console, NULL, console_thread, NULL);
MEMTRACE_PRINT_DELTA_MESSAGE("Console thread created");
}
static esp_err_t run_command(char * line){
static esp_err_t run_command(char* line) {
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG,"Unrecognized command: %s", line);
ESP_LOGE(TAG, "Unrecognized command: %s", line);
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err != ESP_OK && ret != ESP_OK) {
ESP_LOGW(TAG,"Command returned non-zero error code: 0x%x (%s)", ret,
esp_err_to_name(err));
ESP_LOGW(TAG, "Command returned non-zero error code: 0x%x (%s)", ret, esp_err_to_name(err));
} else if (err == ESP_OK && ret != ESP_OK) {
ESP_LOGW(TAG,"Command returned in error");
ESP_LOGW(TAG, "Command returned in error");
err = ESP_FAIL;
} else if (err != ESP_OK) {
ESP_LOGE(TAG,"Internal error: %s", esp_err_to_name(err));
ESP_LOGE(TAG, "Internal error: %s", esp_err_to_name(err));
}
return err;
}
static void * console_thread() {
if(!is_recovery_running){
MEMTRACE_PRINT_DELTA_MESSAGE("Running autoexec");
process_autoexec();
MEMTRACE_PRINT_DELTA_MESSAGE("Autoexec done");
}
static void* console_thread() {
/* Main loop */
while (1) {
/* Get a line using linenoise.
@@ -486,4 +425,3 @@ static void * console_thread() {
}
return NULL;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,27 +7,27 @@
* https://opensource.org/licenses/MIT
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_task.h"
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include "buttons.h"
#include "driver/gpio.h"
#include "driver/rmt.h"
#include "gpio_exp.h"
#include "buttons.h"
#include "services.h"
#include "rotary_encoder.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "globdefs.h"
#include "gpio_exp.h"
#include "rotary_encoder.h"
#include "services.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static const char * TAG = "buttons";
static const char* TAG = "buttons";
static EXT_RAM_ATTR int n_buttons;
static EXT_RAM_ATTR uint32_t buttons_idle_since;
@@ -38,7 +38,7 @@ static EXT_RAM_ATTR uint32_t buttons_idle_since;
#define BUTTON_QUEUE_LEN 10
static EXT_RAM_ATTR struct button_s {
void *client;
void* client;
int gpio;
int debounce;
button_handler handler;
@@ -53,14 +53,14 @@ static EXT_RAM_ATTR struct button_s {
// can't use EXT_RAM_ATTR for initialized structure
static struct {
int gpio, level;
struct button_s *button;
} polled_gpio[] = { {36, -1, NULL}, {39, -1, NULL}, {-1, -1, NULL} };
struct button_s* button;
} polled_gpio[] = {{36, -1, NULL}, {39, -1, NULL}, {-1, -1, NULL}};
static TimerHandle_t polled_timer;
static EXT_RAM_ATTR struct {
QueueHandle_t queue;
void *client;
void* client;
rotary_encoder_info_t info;
int A, B, SW;
rotary_handler handler;
@@ -75,35 +75,39 @@ static EXT_RAM_ATTR QueueHandle_t button_queue;
static EXT_RAM_ATTR QueueSetHandle_t common_queue_set;
static void buttons_task(void* arg);
static void buttons_handler(struct button_s *button, int level);
static void buttons_handler(struct button_s* button, int level);
/****************************************************************************************
* Start task needed by button,s rotaty and infrared
*/
static void common_task_init(void) {
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static EXT_RAM_ATTR StackType_t xStack[BUTTON_STACK_SIZE] __attribute__ ((aligned (4)));
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__((aligned(4)));
static EXT_RAM_ATTR StackType_t xStack[BUTTON_STACK_SIZE] __attribute__((aligned(4)));
if (!common_queue_set) {
ESP_LOGD(TAG,"Creating buttons task with a queue set length of %d",BUTTON_QUEUE_LEN+1);
common_queue_set = xQueueCreateSet(BUTTON_QUEUE_LEN + 1);
xTaskCreateStatic( (TaskFunction_t) buttons_task, "buttons", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 2, xStack, &xTaskBuffer);
}
xTaskCreateStatic((TaskFunction_t)buttons_task, "buttons", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 2, xStack, &xTaskBuffer);
}
}
/****************************************************************************************
* GPIO low-level ISR handler
*/
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
struct button_s *button = (struct button_s*) arg;
static void IRAM_ATTR gpio_isr_handler(void* arg) {
struct button_s* button = (struct button_s*)arg;
BaseType_t woken = pdFALSE;
if (xTimerGetPeriod(button->timer) > pdMS_TO_TICKS(button->debounce)) {
if (button->gpio < GPIO_NUM_MAX) xTimerChangePeriodFromISR(button->timer, pdMS_TO_TICKS(button->debounce), &woken);
else xTimerChangePeriod(button->timer, pdMS_TO_TICKS(button->debounce), pdMS_TO_TICKS(10));
if (button->gpio < GPIO_NUM_MAX)
xTimerChangePeriodFromISR(button->timer, pdMS_TO_TICKS(button->debounce), &woken);
else
xTimerChangePeriod(button->timer, pdMS_TO_TICKS(button->debounce), pdMS_TO_TICKS(10));
} else {
if (button->gpio < GPIO_NUM_MAX) xTimerResetFromISR(button->timer, &woken);
else xTimerReset(button->timer, portMAX_DELAY);
if (button->gpio < GPIO_NUM_MAX)
xTimerResetFromISR(button->timer, &woken);
else
xTimerReset(button->timer, portMAX_DELAY);
}
if (woken) portYIELD_FROM_ISR();
@@ -114,8 +118,8 @@ static void IRAM_ATTR gpio_isr_handler(void* arg)
/****************************************************************************************
* Buttons debounce/longpress timer
*/
static void buttons_timer_handler( TimerHandle_t xTimer ) {
struct button_s *button = (struct button_s*) pvTimerGetTimerID (xTimer);
static void buttons_timer_handler(TimerHandle_t xTimer) {
struct button_s* button = (struct button_s*)pvTimerGetTimerID(xTimer);
// if this is an expanded GPIO, must give cache a chance
buttons_handler(button, gpio_exp_get_level(button->gpio, (button->debounce * 3) / 2, NULL));
}
@@ -123,7 +127,7 @@ static void buttons_timer_handler( TimerHandle_t xTimer ) {
/****************************************************************************************
* Buttons polling timer
*/
static void buttons_polling( TimerHandle_t xTimer ) {
static void buttons_polling(TimerHandle_t xTimer) {
for (int i = 0; polled_gpio[i].gpio != -1; i++) {
if (!polled_gpio[i].button) continue;
@@ -139,7 +143,7 @@ static void buttons_polling( TimerHandle_t xTimer ) {
/****************************************************************************************
* Buttons timer handler for press/longpress
*/
static void buttons_handler(struct button_s *button, int level) {
static void buttons_handler(struct button_s* button, int level) {
button->level = level;
if (button->shifter && button->shifter->type == button->shifter->level) button->shifter->shifting = true;
@@ -161,9 +165,7 @@ static void buttons_handler(struct button_s *button, int level) {
/****************************************************************************************
* Get inactivity callback
*/
static uint32_t buttons_idle_callback(void) {
return pdTICKS_TO_MS(xTaskGetTickCount()) - buttons_idle_since;
}
static uint32_t buttons_idle_callback(void) { return pdTICKS_TO_MS(xTaskGetTickCount()) - buttons_idle_since; }
/****************************************************************************************
* Tasks that calls the appropriate functions when buttons are pressed
@@ -179,7 +181,7 @@ static void buttons_task(void* arg) {
bool active = true;
// wait on button, rotary and infrared queues
if ((xActivatedMember = xQueueSelectFromSet( common_queue_set, portMAX_DELAY )) == NULL) continue;
if ((xActivatedMember = xQueueSelectFromSet(common_queue_set, portMAX_DELAY)) == NULL) continue;
if (xActivatedMember == button_queue) {
struct button_s button;
@@ -191,11 +193,14 @@ static void buttons_task(void* arg) {
event = (button.level == button.type) ? BUTTON_PRESSED : BUTTON_RELEASED;
ESP_LOGD(TAG, "received event:%u from gpio:%u level:%u (timer %u shifting %u)", event, button.gpio, button.level, button.long_timer, button.shifting);
ESP_LOGD(TAG, "received event:%u from gpio:%u level:%u (timer %u shifting %u)", event, button.gpio, button.level, button.long_timer,
button.shifting);
// find if shifting is activated
if (button.shifter && button.shifter->type == button.shifter->level) press = BUTTON_SHIFTED;
else press = BUTTON_NORMAL;
if (button.shifter && button.shifter->type == button.shifter->level)
press = BUTTON_SHIFTED;
else
press = BUTTON_NORMAL;
/*
long_timer will be set either because we truly have a long press
@@ -222,7 +227,7 @@ static void buttons_task(void* arg) {
button.self->shifting = false;
}
} else if (xActivatedMember == rotary.queue) {
rotary_encoder_event_t event = { 0 };
rotary_encoder_event_t event = {0};
// received a rotary event
xQueueReceive(rotary.queue, &event, 0);
@@ -230,8 +235,7 @@ static void buttons_task(void* arg) {
ESP_LOGD(TAG, "Event: position %d, direction %s", event.state.position,
event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ?
ROTARY_RIGHT : ROTARY_LEFT, false);
rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? ROTARY_RIGHT : ROTARY_LEFT, false);
} else {
// this is IR
active = infrared_receive(infrared.rb, infrared.handler);
@@ -245,22 +249,22 @@ static void buttons_task(void* arg) {
/****************************************************************************************
* dummy button handler
*/
void dummy_handler(void *id, button_event_e event, button_press_e press) {
ESP_LOGW(TAG, "should not be here");
}
void dummy_handler(void* id, button_event_e event, button_press_e press) { ESP_LOGW(TAG, "should not be here"); }
/****************************************************************************************
* Create buttons
*/
void button_create(void *client, int gpio, int type, bool pull, int debounce, button_handler handler, int long_press, int shifter_gpio) {
void button_create(void* client, int gpio, int type, bool pull, int debounce, button_handler handler, int long_press, int shifter_gpio) {
if (n_buttons >= MAX_BUTTONS) return;
ESP_LOGI(TAG, "Creating button using GPIO %u, type %u, pull-up/down %u, long press %u shifter %d", gpio, type, pull, long_press, shifter_gpio);
ESP_LOGI(TAG, "Creating button using GPIO %u, type %s, %s pull-up/down, long press %u shifter %d", gpio,
sys_gpio_lvl_name(type == 0 ? sys_gpio_lvl_LOW : sys_gpio_lvl_HIGH), pull?"with":"without", long_press, shifter_gpio);
if (!n_buttons) {
ESP_LOGD(TAG,"Creating new buttton message queue with a length of %d entries",BUTTON_QUEUE_LEN);
button_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s));
common_task_init();
xQueueAddToSet( button_queue, common_queue_set );
xQueueAddToSet(button_queue, common_queue_set);
}
// just in case this structure is allocated in a future release
@@ -269,12 +273,13 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
// set mandatory parameters
buttons[n_buttons].client = client;
buttons[n_buttons].gpio = gpio;
buttons[n_buttons].debounce = debounce ? debounce: DEBOUNCE;
buttons[n_buttons].debounce = debounce ? debounce : DEBOUNCE;
buttons[n_buttons].handler = handler;
buttons[n_buttons].long_press = long_press;
buttons[n_buttons].shifter_gpio = shifter_gpio;
buttons[n_buttons].type = type;
buttons[n_buttons].timer = xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void *) &buttons[n_buttons], buttons_timer_handler);
buttons[n_buttons].timer =
xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void*)&buttons[n_buttons], buttons_timer_handler);
buttons[n_buttons].self = buttons + n_buttons;
for (int i = 0; i < n_buttons; i++) {
@@ -297,8 +302,10 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
// do we need pullup or pulldown
if (pull) {
if (GPIO_IS_VALID_OUTPUT_GPIO(gpio) || gpio >= GPIO_NUM_MAX) {
if (type == BUTTON_LOW) gpio_set_pull_mode_x(gpio, GPIO_PULLUP_ONLY);
else gpio_set_pull_mode_x(gpio, GPIO_PULLDOWN_ONLY);
if (type == BUTTON_LOW)
gpio_set_pull_mode_x(gpio, GPIO_PULLUP_ONLY);
else
gpio_set_pull_mode_x(gpio, GPIO_PULLDOWN_ONLY);
} else {
ESP_LOGW(TAG, "cannot set pull up/down for gpio %u", gpio);
}
@@ -308,7 +315,8 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
buttons[n_buttons].level = gpio_get_level_x(gpio);
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
for (int i = 0; polled_gpio[i].gpio != -1; i++) if (polled_gpio[i].gpio == gpio) {
for (int i = 0; polled_gpio[i].gpio != -1; i++)
if (polled_gpio[i].gpio == gpio) {
if (!polled_timer) {
polled_timer = xTimerCreate("buttonsPolling", 100 / portTICK_RATE_MS, pdTRUE, polled_gpio, buttons_polling);
xTimerStart(polled_timer, portMAX_DELAY);
@@ -336,7 +344,7 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
/****************************************************************************************
* Get stored id
*/
void *button_get_client(int gpio) {
void* button_get_client(int gpio) {
for (int i = 0; i < n_buttons; i++) {
if (buttons[i].gpio == gpio) return buttons[i].client;
}
@@ -346,10 +354,12 @@ void *button_get_client(int gpio) {
/****************************************************************************************
* Get stored id
*/
bool button_is_pressed(int gpio, void *client) {
bool button_is_pressed(int gpio, void* client) {
for (int i = 0; i < n_buttons; i++) {
if (gpio != -1 && buttons[i].gpio == gpio) return buttons[i].level == buttons[i].type;
else if (client && buttons[i].client == client) return buttons[i].level == buttons[i].type;
if (gpio != -1 && buttons[i].gpio == gpio)
return buttons[i].level == buttons[i].type;
else if (client && buttons[i].client == client)
return buttons[i].level == buttons[i].type;
}
return false;
}
@@ -357,10 +367,10 @@ bool button_is_pressed(int gpio, void *client) {
/****************************************************************************************
* Update buttons
*/
void *button_remap(void *client, int gpio, button_handler handler, int long_press, int shifter_gpio) {
void* button_remap(void* client, int gpio, button_handler handler, int long_press, int shifter_gpio) {
int i;
struct button_s *button = NULL;
void *prev_client;
struct button_s* button = NULL;
void* prev_client;
ESP_LOGI(TAG, "remapping GPIO %u, long press %u shifter %u", gpio, long_press, shifter_gpio);
@@ -397,7 +407,7 @@ void *button_remap(void *client, int gpio, button_handler handler, int long_pres
/****************************************************************************************
* Rotary encoder handler
*/
static void rotary_button_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
static void rotary_button_handler(void* id, button_event_e event, button_press_e mode, bool long_press) {
ESP_LOGI(TAG, "Rotary push-button %d", event);
rotary.handler(id, event == BUTTON_PRESSED ? ROTARY_PRESSED : ROTARY_RELEASED, long_press);
}
@@ -405,7 +415,7 @@ static void rotary_button_handler(void *id, button_event_e event, button_press_e
/****************************************************************************************
* Create rotary encoder
*/
bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler) {
bool create_rotary(void* id, int A, int B, int SW, int long_press, rotary_handler handler) {
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
if (A == -1 || B == -1 || A == 36 || A == 39 || B == 36 || B == 39) {
ESP_LOGI(TAG, "Cannot create rotary %d %d", A, B);
@@ -426,7 +436,7 @@ bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handle
rotary_encoder_set_queue(&rotary.info, rotary.queue);
common_task_init();
xQueueAddToSet( rotary.queue, common_queue_set );
xQueueAddToSet(rotary.queue, common_queue_set);
// create companion button if rotary has a switch
if (SW != -1) button_create(id, SW, BUTTON_LOW, true, 0, rotary_button_handler, long_press, -1);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,9 @@
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern void (*pseudo_idle_svc)(uint32_t now);
extern void (*jack_handler_svc)(bool inserted);
@@ -20,3 +23,6 @@ extern bool spkfault_svc(void);
extern void (*battery_handler_svc)(float value, int cells);
extern float battery_value_svc(void);
extern uint16_t battery_level_svc(void);
#ifdef __cplusplus
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,14 +11,16 @@
#include <math.h>
#ifdef ESP_PLATFORM
#include "Services.pb.h"
#include "Squeezelite.pb.h"
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
extern sys_squeezelite_config* get_profile(const char* name);
#endif
#include "Configurator.h"
#include "Config.h"
#include "accessors.h"
#include "squeezelite.h"
#if CONFIG_BT_SINK
#include "bt_app_sink.h"
static bool enable_bt_sink;
@@ -39,7 +41,7 @@ static bool enable_airplay;
#define SYNC_WIN_FAST 2
static raop_event_t raop_state;
static sys_Squeezelite * squeezelite;
static sys_squeezelite_config* squeezelite;
static EXT_RAM_ATTR struct {
bool enabled;
@@ -60,15 +62,14 @@ enum { DECODE_BT = 1, DECODE_RAOP, DECODE_CSPOT };
extern struct outputstate output;
extern struct decodestate decode;
extern struct buffer *outputbuf;
extern struct buffer* outputbuf;
// this is the only system-wide loglevel variable
extern log_level loglevel;
/****************************************************************************************
* Common sink data handler
*/
static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries)
{
static uint32_t sink_data_handler(const uint8_t* data, uint32_t len, int retries) {
size_t bytes, space;
uint32_t written = 0;
int wait = retries + 1;
@@ -90,10 +91,11 @@ static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries
memcpy(outputbuf->writep, data, bytes);
#else
{
s16_t *iptr = (s16_t*) data;
ISAMPLE_T *optr = (ISAMPLE_T *) outputbuf->writep;
s16_t* iptr = (s16_t*)data;
ISAMPLE_T* optr = (ISAMPLE_T*)outputbuf->writep;
size_t n = bytes / 2;
while (n--) *optr++ = *iptr++ << 16;
while (n--)
*optr++ = *iptr++ << 16;
}
#endif
_buf_inc_writep(outputbuf, bytes * BYTES_PER_FRAME / 4);
@@ -107,7 +109,9 @@ static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries
if (len && !space) {
if (!retries) break;
wait--;
UNLOCK_O; usleep(50000); LOCK_O;
UNLOCK_O;
usleep(50000);
LOCK_O;
}
}
@@ -126,18 +130,18 @@ static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries
* BT sink data handler
*/
#if CONFIG_BT_SINK
static void bt_sink_data_handler(const uint8_t *data, uint32_t len) {
static void bt_sink_data_handler(const uint8_t* data, uint32_t len) {
sink_data_handler(data, len, 10);
}
/****************************************************************************************
* BT sink command handler
*/
static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
{
static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) {
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player %d", output.external);
LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player %d",
output.external);
return false;
}
@@ -145,7 +149,7 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
if (cmd != BT_SINK_VOLUME) LOCK_O;
switch(cmd) {
switch (cmd) {
case BT_SINK_AUDIO_STARTED:
_buf_flush(outputbuf);
_buf_limit(outputbuf, 0);
@@ -204,7 +208,7 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
* raop sink data handler
*/
#if CONFIG_AIRPLAY_SINK
static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t playtime) {
static void raop_sink_data_handler(const uint8_t* data, uint32_t len, u32_t playtime) {
raop_sync.playtime = playtime;
raop_sync.len = len;
@@ -215,11 +219,11 @@ static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t play
/****************************************************************************************
* AirPlay sink command handler
*/
static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
{
static bool raop_sink_cmd_handler(raop_event_t event, va_list args) {
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player %d", output.external);
LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player %d",
output.external);
return false;
}
@@ -230,21 +234,28 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
// this is async, so player might have been deleted
switch (event) {
case RAOP_TIMING: {
if (!raop_sync.enabled || output.state != OUTPUT_RUNNING || output.frames_played_dmp < output.device_frames) break;
if (!raop_sync.enabled || output.state != OUTPUT_RUNNING ||
output.frames_played_dmp < output.device_frames)
break;
u32_t ms, now = gettime_ms();
u32_t level = _buf_used(outputbuf);
int error;
// in how many ms will the most recent block play
ms = (((s32_t)(level - raop_sync.len) / BYTES_PER_FRAME + output.device_frames + output.frames_in_process) * 10) / (RAOP_SAMPLE_RATE / 100) - (s32_t) (now - output.updated);
ms = (((s32_t)(level - raop_sync.len) / BYTES_PER_FRAME + output.device_frames +
output.frames_in_process) *
10) /
(RAOP_SAMPLE_RATE / 100) -
(s32_t)(now - output.updated);
// when outputbuf is empty, it means we have a network black-out or something
error = level ? (raop_sync.playtime - now) - ms : 0;
if (loglevel == lDEBUG || !level) {
LOG_INFO("head local:%d, remote:%d (delta:%d)", ms, raop_sync.playtime - now, error);
LOG_INFO("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf), raop_sync.len, output.device_frames, output.frames_in_process);
LOG_INFO("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf),
raop_sync.len, output.device_frames, output.frames_in_process);
}
// calculate sum, error and update sliding window
@@ -253,7 +264,8 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
error = raop_sync.sum / min(raop_sync.count, raop_sync.win);
// wait till we have enough data or there is a strong deviation
if ((raop_sync.count >= raop_sync.win && abs(error) > 10) || (raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) {
if ((raop_sync.count >= raop_sync.win && abs(error) > 10) ||
(raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) {
if (error < 0) {
output.skip_frames = -(error * RAOP_SAMPLE_RATE) / 1000;
output.state = OUTPUT_SKIP_FRAMES;
@@ -271,8 +283,10 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
// move to normal mode if possible
if (raop_sync.win == 1) {
raop_sync.win = SYNC_WIN_FAST;
LOG_INFO("backend played %u, desired %u, (delta:%d)", ms, raop_sync.playtime - now, error);
} else if (raop_sync.win == SYNC_WIN_FAST && raop_sync.count >= SYNC_WIN_FAST && abs(error) < 10) {
LOG_INFO(
"backend played %u, desired %u, (delta:%d)", ms, raop_sync.playtime - now, error);
} else if (raop_sync.win == SYNC_WIN_FAST && raop_sync.count >= SYNC_WIN_FAST &&
abs(error) < 10) {
raop_sync.win = SYNC_WIN_SLOW;
LOG_INFO("switching to slow sync mode %u", raop_sync.win);
}
@@ -280,8 +294,8 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
break;
}
case RAOP_SETUP: {
uint8_t **buffer = va_arg(args, uint8_t**);
size_t *size = va_arg(args, size_t*);
uint8_t** buffer = va_arg(args, uint8_t**);
size_t* size = va_arg(args, size_t*);
// steal buffer tail from outputbuf but do not reallocate
*size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE);
@@ -306,7 +320,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
case RAOP_STALLED:
case RAOP_STOP:
output.external = 0;
__attribute__ ((fallthrough));
__attribute__((fallthrough));
case RAOP_FLUSH:
LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop");
_buf_flush(outputbuf);
@@ -349,7 +363,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
* cspot sink data handler
*/
#if CONFIG_CSPOT_SINK
static uint32_t cspot_sink_data_handler(const uint8_t *data, uint32_t len) {
static uint32_t cspot_sink_data_handler(const uint8_t* data, uint32_t len) {
return sink_data_handler(data, len, 0);
}
@@ -357,11 +371,11 @@ static uint32_t cspot_sink_data_handler(const uint8_t *data, uint32_t len) {
* cspot sink command handler
*/
static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
{
static bool cspot_cmd_handler(cspot_event_t cmd, va_list args) {
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player %d", output.external);
LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player %d",
output.external);
return false;
}
@@ -369,7 +383,7 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
if (cmd != CSPOT_VOLUME) LOCK_O;
switch(cmd) {
switch (cmd) {
case CSPOT_START:
output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t);
output.external = DECODE_CSPOT;
@@ -415,12 +429,12 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
output.track_start = outputbuf->writep;
break;
case CSPOT_QUERY_REMAINING: {
uint32_t *remaining = va_arg(args, uint32_t*);
uint32_t* remaining = va_arg(args, uint32_t*);
*remaining = (_buf_used(outputbuf) * 1000) / (output.current_sample_rate * BYTES_PER_FRAME);
break;
}
case CSPOT_QUERY_STARTED: {
uint32_t *started = va_arg(args, uint32_t*);
uint32_t* started = va_arg(args, uint32_t*);
*started = output.track_started;
// this is a read_and_clear event
output.track_started = false;
@@ -448,25 +462,24 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
* We provide the generic codec register option
*/
void register_external(void) {
sys_BluetoothSink * bt_sink;
sys_AirPlay * airplay;
sys_Spotify * spotify;
squeezelite = get_profile(NULL); // get the active profile
#if CONFIG_BT_SINK
enable_bt_sink= (SYS_SERVICES_BTSINK(bt_sink) && bt_sink->enabled);
if ( enable_bt_sink) {
#pragma message ("Is the BT sink logic correct?")
if(SYS_SERVICES_SQUEEZELITE(squeezelite) && squeezelite->output_type == sys_OutputTypeEnum_OUTPUT_Bluetooth ){
sys_services_bt_sink* bt_sink;
enable_bt_sink = (sys_services_config_BTSINK(bt_sink) && bt_sink->enabled);
if (enable_bt_sink && squeezelite) {
#pragma message("Is the BT sink logic correct?")
if (squeezelite->output_type == sys_squeezelite_outputs_BT) {
LOG_ERROR("BT Sink cannot be enabled with Bluetooth output");
}
else {
} else {
bt_sink_init(bt_sink_cmd_handler, bt_sink_data_handler);
}
}
#endif
#if CONFIG_AIRPLAY_SINK
enable_airplay = SYS_SERVICES_AIRPLAY(airplay) && airplay->enabled;
if (enable_airplay){
sys_airplay_config* airplay;
enable_airplay = sys_services_config_AIRPLAY(airplay) && airplay->enabled;
if (enable_airplay) {
raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler);
LOG_INFO("Initializing AirPlay sink");
}
@@ -474,32 +487,32 @@ void register_external(void) {
#endif
#if CONFIG_CSPOT_SINK
enable_cspot = SYS_SERVICES_SPOTIFY(spotify) && spotify->enabled;
if (enable_cspot){
sys_spotify_config* spotify;
enable_cspot = sys_services_config_SPOTIFY(spotify) && spotify->enabled;
if (enable_cspot) {
cspot_sink_init(cspot_cmd_handler, cspot_sink_data_handler);
LOG_INFO("Initializing CSpot sink");
}
#endif
}
void deregister_external(void) {
#if CONFIG_BT_SINK
sys_Squeezelite * squeezelite;
if(SYS_SERVICES_SQUEEZELITE(squeezelite) && squeezelite->output_type != sys_OutputTypeEnum_OUTPUT_Bluetooth && enable_bt_sink ){
squeezelite = get_profile(NULL); // get the active profile
if (squeezelite && squeezelite->output_type == sys_squeezelite_outputs_BT && enable_bt_sink) {
bt_sink_deinit();
}
#endif
#if CONFIG_AIRPLAY_SINK
if (enable_airplay){
if (enable_airplay) {
LOG_INFO("Stopping AirPlay sink");
raop_sink_deinit();
}
#endif
#if CONFIG_CSPOT_SINK
if (enable_cspot){
if (enable_cspot) {
LOG_INFO("Stopping CSpot sink");
cspot_sink_deinit();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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