mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 11:36:59 +03:00
Start of 5.X work
This commit is contained in:
14
.clang-format
Normal file
14
.clang-format
Normal 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
|
||||
}
|
||||
32
.cproject
32
.cproject
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||
|
||||
<cconfiguration id="org.eclipse.cdt.core.default.config.959280881">
|
||||
|
||||
<storageModule buildSystemId="org.eclipse.cdt.core.defaultConfigDataProvider" id="org.eclipse.cdt.core.default.config.959280881" moduleId="org.eclipse.cdt.core.settings" name="Configuration">
|
||||
|
||||
<externalSettings/>
|
||||
|
||||
<extensions/>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
|
||||
</cconfiguration>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.core.pathentry">
|
||||
|
||||
<pathentry excluding="**/CMakeFiles/**" kind="out" path="build"/>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
|
||||
|
||||
</cproject>
|
||||
@@ -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
7
.gitignore
vendored
@@ -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
|
||||
|
||||
20
.project
20
.project
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>squeezelite-esp32-idfv4</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.core.cBuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>com.espressif.idf.core.idfNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
182
CMakeLists.txt
182
CMakeLists.txt
@@ -10,7 +10,12 @@ endif()
|
||||
|
||||
# State machine hierarchy enabled and logging enabled
|
||||
add_definitions(-DSTATE_MACHINE_LOGGER=1)
|
||||
add_definitions(-DHIERARCHICAL_STATES=1)
|
||||
#add_definitions(-DHIERARCHICAL_STATES=1)
|
||||
|
||||
# Align the nanopb library options across the entire project
|
||||
# otherwise this results in nasty structures misalignment
|
||||
# when implementing callbacks
|
||||
add_definitions(-DPB_ENABLE_MALLOC -DPB_FIELD_32BIT)
|
||||
|
||||
# Uncomment line below to get memory usage trace details
|
||||
# add_definitions(-DENABLE_MEMTRACE=1)
|
||||
@@ -18,9 +23,11 @@ add_definitions(-DHIERARCHICAL_STATES=1)
|
||||
#add_definitions(-DNETWORK_ETHERNET_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
#uncomment line below to get network status debug logs
|
||||
#add_definitions(-DNETWORK_STATUS_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
#add_definitions(-DNETWORK_HANDLERS_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
add_definitions(-DNETWORK_HANDLERS_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
#add_definitions(-DNETWORK_WIFI_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
#add_definitions(-DNETWORK_MANAGER_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
add_definitions(-DNETWORK_MANAGER_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
#add_definitions(-DNETWORK_HTTP_SERVER_LOG_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
# utility to build sizes
|
||||
@@ -53,7 +60,6 @@ endfunction()
|
||||
set(EXTRA_COMPONENT_DIRS components/platform_console/app_recovery components/platform_console/app_squeezelite )
|
||||
|
||||
project(recovery)
|
||||
spiffs_create_partition_image(spiffs spiffs FLASH_IN_PROJECT DEPENDS generate_spiffs_bin )
|
||||
|
||||
# we need own "esp_app_desc" to take precedence
|
||||
add_custom_command(
|
||||
@@ -106,7 +112,7 @@ esptool_py_flash_target_image(flash squeezelite "${otaapp_offset}" "${BUILD_DIR}
|
||||
add_custom_target(_jtag_scripts ALL
|
||||
BYPRODUCTS "flash_dbg_project_args"
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/generate_debug_scripts.cmake"
|
||||
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_debug_scripts.cmake"
|
||||
)
|
||||
|
||||
if(CMAKE_HOST_UNIX)
|
||||
@@ -136,78 +142,95 @@ endif()
|
||||
|
||||
# Include the protocol_buffers.cmake file
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/protobuf ${CMAKE_CURRENT_BINARY_DIR}/protobuf)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/spiffs_src ${CMAKE_CURRENT_BINARY_DIR}/spiffs)
|
||||
|
||||
|
||||
|
||||
# ======================= DEBUG FLAGS ============================
|
||||
|
||||
# target_compile_definitions(__idf_wifi-manager PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_app_recovery PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
|
||||
# target_compile_definitions(__idf_esp_event PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
|
||||
# target_compile_definitions(__idf_esp_netif PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_ERROR)
|
||||
# target_compile_definitions(__idf_mdns PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_tcpip_adapter PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_tcp_transport PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
# target_compile_definitions(__idf_app_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_app_trace PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_app_update PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_asio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_audio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_bootloader_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_display PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_driver_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_platform_config PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_squeezelite-ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_telnet PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_main PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
|
||||
# target_compile_definitions(__idf_services PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
target_compile_definitions(__idf_services PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_wifi-manager PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_wifi PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_platform_console PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_wifi PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_platform_console PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
target_compile_definitions(__idf_app_recovery PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
|
||||
target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
|
||||
target_compile_definitions(__idf_esp_event PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
|
||||
target_compile_definitions(__idf_esp_netif PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
target_compile_definitions(__idf_freertos PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_ERROR)
|
||||
target_compile_definitions(__idf_mdns PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_tcpip_adapter PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_tcp_transport PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
target_compile_definitions(__idf_app_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_app_trace PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_app_update PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_asio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_audio PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_bootloader_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
target_compile_definitions(__idf_cbor PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_cmock PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_coap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_console PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_cxx PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_display PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_driver_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_efuse PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp-dsp PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp-tls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp32 PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_espcoredump PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_adc_cal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_common PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_gdbstub PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_hid PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_https_ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_http_client PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_http_server PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_hw_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_ipc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_local_ctrl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_pm PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_ringbuf PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_rom PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_serial_slave_link PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_system PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_esp_timer PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_expat PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_fatfs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_freemodbus PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_hal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_heap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_jsmn PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_json PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
|
||||
|
||||
|
||||
|
||||
# target_compile_definitions(__idf_freertos PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
|
||||
# target_compile_definitions(__idf_cbor PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_cmock PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_coap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_cxx PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_adc_cal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_https_ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_http_client PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_http_server PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_hw_support PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_ipc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_local_ctrl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_pm PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_ringbuf PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_rom PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_serial_slave_link PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_system PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_esp_timer PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_expat PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_fatfs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_freemodbus PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_hal PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_heap PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_jsmn PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_json PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_libsodium PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_log PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_lwip PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_main PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
# target_compile_definitions(__idf_mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(mbedcrypto PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(mbedx509 PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
@@ -217,24 +240,23 @@ target_compile_definitions(__idf_mbedtls PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG
|
||||
# target_compile_definitions(__idf_nvs_flash PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_openssl PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_perfmon PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_platform_config PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_protobuf-c PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_protocomm PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_pthread PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_raop PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_sdmmc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
target_compile_definitions(__idf_soc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_spiffs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_spi_flash PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_squeezelite PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_squeezelite-ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_telnet PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_tools PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_ulp PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_unity PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
# target_compile_definitions(__idf_protobuf-c PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_protocomm PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_pthread PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_raop PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_sdmmc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
# target_compile_definitions(__idf_soc PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_spiffs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_spi_flash PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
|
||||
# target_compile_definitions(__idf_tools PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_ulp PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_unity PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_vfs PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_wear_levelling PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
target_compile_definitions(__idf_wifi_provisioning PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_wear_levelling PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_wifi_provisioning PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_wpa_supplicant PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
# target_compile_definitions(__idf_xtensa PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
|
||||
222
Dockerfile
222
Dockerfile
@@ -1,188 +1,82 @@
|
||||
FROM ubuntu:20.04
|
||||
FROM espressif/idf:v4.4.6
|
||||
|
||||
# Install additional dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python3-protobuf
|
||||
|
||||
RUN DEBIAN_FRONTEND=noninteractive TZ=America/Toronto apt-get -y install tzdata
|
||||
# Add any other dependencies you need
|
||||
|
||||
# To build the image for a branch or a tag of IDF, pass --build-arg IDF_CLONE_BRANCH_OR_TAG=name.
|
||||
# To build the image with a specific commit ID of IDF, pass --build-arg IDF_CHECKOUT_REF=commit-id.
|
||||
# It is possibe to combine both, e.g.:
|
||||
# IDF_CLONE_BRANCH_OR_TAG=release/vX.Y
|
||||
# IDF_CHECKOUT_REF=<some commit on release/vX.Y branch>.
|
||||
# Docker build for release 4.3.5 as of 2023/05/18
|
||||
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv446
|
||||
# Updating the docker image in the repository
|
||||
# docker push sle118/squeezelite-esp32-idfv446
|
||||
# or to do both:
|
||||
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv446 && docker push sle118/squeezelite-esp32-idfv446
|
||||
# docker build . -t sle118/squeezelite-esp32-idfv446 && docker push sle118/squeezelite-esp32-idfv446
|
||||
#docker run --isolation=process --device="class/86E0D1E0-8089-11D0-9CE4-08003E301F73" mcr.microsoft.com/windows/servercore:1809
|
||||
# (windows) To run the image interactive :
|
||||
# docker run --rm -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv446
|
||||
# (windows powershell)
|
||||
# docker run --rm -v ${PWD}:/project -w /project -it sle118/squeezelite-esp32-idfv446
|
||||
# (linux) To run the image interactive :
|
||||
# docker run --rm -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv446
|
||||
# to build the web app inside of the interactive session
|
||||
# pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
|
||||
#
|
||||
# to run the docker with netwotrk port published on the host:
|
||||
# docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv446
|
||||
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV GCC_TOOLS_BASE=/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-
|
||||
# To build the image for a branch or a tag of IDF, pass --build-arg IDF_CLONE_BRANCH_OR_TAG=name.
|
||||
# To build the image with a specific commit ID of IDF, pass --build-arg IDF_CHECKOUT_REF=commit-id.
|
||||
# It is possibe to combine both, e.g.:
|
||||
# IDF_CLONE_BRANCH_OR_TAG=release/vX.Y
|
||||
# IDF_CHECKOUT_REF=<some commit on release/vX.Y branch>.
|
||||
# Docker build for release 4.3.5 as of 2023/05/18
|
||||
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv435
|
||||
# Updating the docker image in the repository
|
||||
# docker push sle118/squeezelite-esp32-idfv435
|
||||
# or to do both:
|
||||
# docker build . --build-arg IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d -t sle118/squeezelite-esp32-idfv435 && docker push sle118/squeezelite-esp32-idfv435
|
||||
#
|
||||
# (windows) To run the image interactive :
|
||||
# docker run --rm -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
|
||||
# (windows powershell)
|
||||
# docker run --rm -v ${PWD}:/project -w /project -it sle118/squeezelite-esp32-idfv435
|
||||
# (linux) To run the image interactive :
|
||||
# docker run --rm -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv435
|
||||
# to build the web app inside of the interactive session
|
||||
# pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
|
||||
#
|
||||
# to run the docker with netwotrk port published on the host:
|
||||
# docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
|
||||
|
||||
ARG IDF_CLONE_URL=https://github.com/espressif/esp-idf.git
|
||||
ARG IDF_CLONE_BRANCH_OR_TAG=master
|
||||
ARG IDF_CHECKOUT_REF=6d04316cbe4dc35ea7e4885e9821bd9958ac996d
|
||||
|
||||
ENV IDF_PATH=/opt/esp/idf
|
||||
ENV IDF_TOOLS_PATH=/opt/esp
|
||||
|
||||
# We need libpython2.7 due to GDB tools
|
||||
# we also need npm 8 for the webapp to work
|
||||
RUN : \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y \
|
||||
apt-utils \
|
||||
build-essential \
|
||||
bison \
|
||||
ca-certificates \
|
||||
ccache \
|
||||
check \
|
||||
curl \
|
||||
flex \
|
||||
git \
|
||||
git-lfs \
|
||||
gperf \
|
||||
lcov \
|
||||
libbsd-dev \
|
||||
libpython3.8 \
|
||||
libffi-dev \
|
||||
libncurses-dev \
|
||||
libusb-1.0-0-dev \
|
||||
make \
|
||||
ninja-build \
|
||||
python3.8 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
ruby \
|
||||
unzip \
|
||||
wget \
|
||||
xz-utils \
|
||||
zip \
|
||||
npm \
|
||||
nodejs \
|
||||
&& apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \
|
||||
&& python -m pip install --upgrade \
|
||||
pip \
|
||||
virtualenv \
|
||||
&& cd /opt \
|
||||
&& git clone https://github.com/HBehrens/puncover.git \
|
||||
&& cd puncover \
|
||||
&& python setup.py -q install \
|
||||
&& echo IDF_CHECKOUT_REF=$IDF_CHECKOUT_REF IDF_CLONE_BRANCH_OR_TAG=$IDF_CLONE_BRANCH_OR_TAG \
|
||||
&& git clone --recursive \
|
||||
${IDF_CLONE_BRANCH_OR_TAG:+-b $IDF_CLONE_BRANCH_OR_TAG} \
|
||||
$IDF_CLONE_URL $IDF_PATH \
|
||||
&& if [ -n "$IDF_CHECKOUT_REF" ]; then \
|
||||
cd $IDF_PATH \
|
||||
&& git checkout $IDF_CHECKOUT_REF \
|
||||
&& git submodule update --init --recursive; \
|
||||
fi \
|
||||
&& update-ca-certificates --fresh \
|
||||
&& $IDF_PATH/tools/idf_tools.py --non-interactive install required \
|
||||
&& $IDF_PATH/tools/idf_tools.py --non-interactive install cmake \
|
||||
&& $IDF_PATH/tools/idf_tools.py --non-interactive install-python-env \
|
||||
&& :
|
||||
RUN : \
|
||||
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 ******************************************************** \
|
||||
|
||||
22
Makefile
22
Makefile
@@ -1,22 +0,0 @@
|
||||
# build system (Jenkins) should pass the following
|
||||
#export PROJECT_BUILD_NAME="${build_version_prefix}${BUILD_NUMBER}"
|
||||
#export PROJECT_BUILD_TARGET="${config_target}"
|
||||
|
||||
#PROJECT_BUILD_NAME?=local
|
||||
#PROJECT_CONFIG_TARGET?=custom
|
||||
#PROJECT_NAME?=squeezelite.$(PROJECT_CONFIG_TARGET)
|
||||
#PROJECT_VER?="$(PROJECT_BUILD_NAME)-$(PROJECT_CONFIG_TARGET)"
|
||||
|
||||
|
||||
|
||||
#recovery: PROJECT_NAME:=recovery.$(PROJECT_CONFIG_TARGET)
|
||||
#recovery: EXTRA_CPPFLAGS+=-DRECOVERY_APPLICATION=1
|
||||
|
||||
PROJECT_NAME?=squeezelite
|
||||
EXTRA_CPPFLAGS+= -I$(PROJECT_PATH)/main
|
||||
|
||||
#/-Wno-error=maybe-uninitialized
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
# for future gcc version, this could be needed: CPPFLAGS+= -Wno-error=format-overflow -Wno-error=stringop-truncation
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
PROJECT_NAME?= squeezelite
|
||||
EXTRA_COMPONENT_DIRS := $(PROJECT_PATH)/esp-dsp
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 )
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
266
components/platform_config/Config.cpp
Normal file
266
components/platform_config/Config.cpp
Normal 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;
|
||||
}
|
||||
49
components/platform_config/Config.h
Normal file
49
components/platform_config/Config.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#pragma once
|
||||
#include "State.pb.h"
|
||||
#include "configuration.pb.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "status.pb.h"
|
||||
#include <strings.h>
|
||||
#include "accessors.h"
|
||||
#ifdef __cplusplus
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
#define PLATFORM_GET_PTR(base, sname) \
|
||||
{ \
|
||||
(base && (base)->##has_##(sname) ? &(base)->sname : NULL)
|
||||
#define PLATFORM_DEVICES PLATFORM_GET_PTR(platform)
|
||||
void configurator_load();
|
||||
bool configurator_waitcommit();
|
||||
bool configurator_has_changes();
|
||||
bool configurator_lock();
|
||||
void configurator_unlock();
|
||||
void configurator_raise_changed();
|
||||
void configurator_raise_state_changed();
|
||||
bool configurator_has_changes();
|
||||
bool configurator_waitcommit();
|
||||
void* configurator_alloc_get_config(size_t* sz);
|
||||
bool configurator_parse_config(void* buffer, size_t buffer_size);
|
||||
void configurator_reset_configuration();
|
||||
pb_type_t configurator_get_field_type(const pb_msgdesc_t* desc, uint32_t tag);
|
||||
bool configurator_set_string(
|
||||
const pb_msgdesc_t* desc, uint32_t field_tag, void* message, const char* value);
|
||||
extern sys_Config* platform;
|
||||
extern sys_State* sys_state;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace PlatformConfig {
|
||||
|
||||
class Configurator {
|
||||
private:
|
||||
static const int MaxDelay;
|
||||
static const int LockMaxWait;
|
||||
|
||||
EXT_RAM_ATTR static TimerHandle_t _timer;
|
||||
EXT_RAM_ATTR static SemaphoreHandle_t _mutex;
|
||||
EXT_RAM_ATTR static SemaphoreHandle_t _state_mutex;
|
||||
EXT_RAM_ATTR static EventGroupHandle_t _group;
|
||||
bool SetGroupBit(int bit_num, bool flag);
|
||||
void ResetModified();
|
||||
void ResetStateModified();
|
||||
sys_Config _root;
|
||||
sys_State _sys_state;
|
||||
|
||||
public:
|
||||
sys_Config* Root() { return &_root; }
|
||||
sys_State* RootState() { return &_sys_state; }
|
||||
bool WaitForCommit();
|
||||
bool Lock();
|
||||
bool LockState();
|
||||
void Unlock();
|
||||
void UnlockState();
|
||||
|
||||
void* AllocGetConfigBuffer(size_t* sz);
|
||||
static void* AllocGetConfigBuffer(size_t* sz, sys_Config* config);
|
||||
static void ResetStructure(sys_Config* config);
|
||||
bool LoadDecodeState();
|
||||
|
||||
void CommitChanges();
|
||||
bool Commit();
|
||||
static bool Commit(sys_Config* config);
|
||||
|
||||
bool CommitState();
|
||||
static bool CommitState(sys_State* state);
|
||||
sys_Config* AllocDefaultStruct();
|
||||
|
||||
static bool ApplyTargetSettings(sys_Config* conf_root);
|
||||
bool ApplyTargetSettings();
|
||||
bool HasStateChanges();
|
||||
|
||||
bool LoadDecodeBuffer(void* buffer, size_t buffer_size);
|
||||
void InitLoadConfig(const char* filename);
|
||||
void InitLoadConfig(const char* filename, sys_Config* conf_root, bool noinit = false);
|
||||
static bool LoadDecode(
|
||||
void* buffer, size_t buffer_size, sys_Config* conf_root, bool noinit = false);
|
||||
|
||||
Configurator() {
|
||||
_mutex = xSemaphoreCreateMutex();
|
||||
_state_mutex = xSemaphoreCreateMutex();
|
||||
_group = xEventGroupCreate();
|
||||
}
|
||||
void RaiseStateModified();
|
||||
void RaiseModified();
|
||||
bool HasChanges();
|
||||
~Configurator() {}
|
||||
};
|
||||
} // namespace PlatformConfig
|
||||
extern PlatformConfig::Configurator configurator;
|
||||
#endif
|
||||
45
components/platform_config/Locking.cpp
Normal file
45
components/platform_config/Locking.cpp
Normal 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);
|
||||
}
|
||||
71
components/platform_config/Locking.h
Normal file
71
components/platform_config/Locking.h
Normal 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
|
||||
272
components/platform_config/PBW.cpp
Normal file
272
components/platform_config/PBW.cpp
Normal 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;
|
||||
}
|
||||
331
components/platform_config/PBW.h
Normal file
331
components/platform_config/PBW.h
Normal 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
|
||||
845
components/platform_config/WifiList.cpp
Normal file
845
components/platform_config/WifiList.cpp
Normal 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(>s);
|
||||
}
|
||||
if (entry.has_last_seen) {
|
||||
PrintTimeStamp(&entry.last_seen);
|
||||
} else {
|
||||
PrintTimeStamp(>s);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
sys_net_wifi_entry* WifiList::GetIndex(size_t index) {
|
||||
if (index >= credentials_.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto it = credentials_.begin();
|
||||
std::advance(it, index);
|
||||
return &(it->second);
|
||||
}
|
||||
sys_net_wifi_entry& WifiList::GetStrongestSTA() {
|
||||
if (credentials_.empty()) {
|
||||
throw std::runtime_error("No credentials available");
|
||||
}
|
||||
|
||||
auto strongestIter = credentials_.begin();
|
||||
for (auto iter = credentials_.begin(); iter != credentials_.end(); ++iter) {
|
||||
if (iter->second.rssi > strongestIter->second.rssi) {
|
||||
strongestIter = iter;
|
||||
}
|
||||
}
|
||||
|
||||
return strongestIter->second;
|
||||
}
|
||||
|
||||
std::list<sys_net_radio_types> WifiList::GetRadioTypes(const wifi_ap_record_t* sta) {
|
||||
std::list<sys_net_radio_types> result;
|
||||
if (sta == nullptr) {
|
||||
result.push_back(sys_net_radio_types_UNKNOWN);
|
||||
} else {
|
||||
|
||||
// Check each bit field and return the corresponding enum value
|
||||
if (sta->phy_11b) {
|
||||
result.push_back(sys_net_radio_types_PHY_11B);
|
||||
}
|
||||
if (sta->phy_11g) {
|
||||
result.push_back(sys_net_radio_types_PHY_11G);
|
||||
}
|
||||
if (sta->phy_11n) {
|
||||
result.push_back(sys_net_radio_types_PHY_11N);
|
||||
}
|
||||
if (sta->phy_lr) {
|
||||
result.push_back(sys_net_radio_types_LR);
|
||||
}
|
||||
if (sta->wps) {
|
||||
result.push_back(sys_net_radio_types_WPS);
|
||||
}
|
||||
if (sta->ftm_responder) {
|
||||
result.push_back(sys_net_radio_types_FTM_RESPONDER);
|
||||
}
|
||||
if (sta->ftm_initiator) {
|
||||
result.push_back(sys_net_radio_types_FTM_INITIATOR);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
wifi_auth_mode_t WifiList::GetESPAuthMode(sys_net_auth_types auth_type) {
|
||||
switch (auth_type) {
|
||||
case sys_net_auth_types_OPEN:
|
||||
return WIFI_AUTH_OPEN;
|
||||
case sys_net_auth_types_WEP:
|
||||
return WIFI_AUTH_WEP;
|
||||
case sys_net_auth_types_WPA_PSK:
|
||||
return WIFI_AUTH_WPA_PSK;
|
||||
case sys_net_auth_types_WPA2_PSK:
|
||||
return WIFI_AUTH_WPA2_PSK;
|
||||
case sys_net_auth_types_WPA_WPA2_PSK:
|
||||
return WIFI_AUTH_WPA_WPA2_PSK;
|
||||
case sys_net_auth_types_WPA2_ENTERPRISE:
|
||||
return WIFI_AUTH_WPA2_ENTERPRISE;
|
||||
case sys_net_auth_types_WPA3_PSK:
|
||||
return WIFI_AUTH_WPA3_PSK;
|
||||
case sys_net_auth_types_WPA2_WPA3_PSK:
|
||||
return WIFI_AUTH_WPA2_WPA3_PSK;
|
||||
case sys_net_auth_types_WAPI_PSK:
|
||||
return WIFI_AUTH_WAPI_PSK;
|
||||
default:
|
||||
return WIFI_AUTH_OPEN; // Default case
|
||||
}
|
||||
}
|
||||
sys_net_auth_types WifiList::GetAuthType(const wifi_ap_record_t* ap) {
|
||||
return ap ? GetAuthType(ap->authmode) : sys_net_auth_types_AUTH_UNKNOWN;
|
||||
}
|
||||
sys_net_auth_types WifiList::GetAuthType(const wifi_auth_mode_t mode) {
|
||||
|
||||
switch (mode) {
|
||||
case WIFI_AUTH_OPEN:
|
||||
return sys_net_auth_types_OPEN;
|
||||
case WIFI_AUTH_WEP:
|
||||
return sys_net_auth_types_WEP;
|
||||
case WIFI_AUTH_WPA_PSK:
|
||||
return sys_net_auth_types_WPA_PSK;
|
||||
case WIFI_AUTH_WPA2_PSK:
|
||||
return sys_net_auth_types_WPA2_PSK;
|
||||
case WIFI_AUTH_WPA_WPA2_PSK:
|
||||
return sys_net_auth_types_WPA_WPA2_PSK;
|
||||
case WIFI_AUTH_WPA2_ENTERPRISE:
|
||||
return sys_net_auth_types_WPA2_ENTERPRISE;
|
||||
case WIFI_AUTH_WPA3_PSK:
|
||||
return sys_net_auth_types_WPA3_PSK;
|
||||
case WIFI_AUTH_WPA2_WPA3_PSK:
|
||||
return sys_net_auth_types_WPA2_WPA3_PSK;
|
||||
case WIFI_AUTH_WAPI_PSK:
|
||||
return sys_net_auth_types_WAPI_PSK;
|
||||
case WIFI_AUTH_MAX:
|
||||
return sys_net_auth_types_OPEN;
|
||||
}
|
||||
return sys_net_auth_types_AUTH_UNKNOWN;
|
||||
}
|
||||
185
components/platform_config/WifiList.h
Normal file
185
components/platform_config/WifiList.h
Normal 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
|
||||
20
components/platform_config/test/CMakeLists.txt
Normal file
20
components/platform_config/test/CMakeLists.txt
Normal 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})
|
||||
29
components/platform_config/test/DAC_test_extra.proto
Normal file
29
components/platform_config/test/DAC_test_extra.proto
Normal 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;
|
||||
}
|
||||
30
components/platform_config/test/DAC_test_extra2.proto
Normal file
30
components/platform_config/test/DAC_test_extra2.proto
Normal 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;
|
||||
}
|
||||
956
components/platform_config/test/test_platform_config.cpp
Normal file
956
components/platform_config/test/test_platform_config.cpp
Normal 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));
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
/****************************************************************************************
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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=⊤
|
||||
while(cur->next){
|
||||
messaging_handle_t messaging_register_subscriber(uint8_t max_count, char* name) {
|
||||
messaging_list_t* cur = ⊤
|
||||
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=⊤
|
||||
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 = ⊤
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
44
components/services/system_time.c
Normal file
44
components/services/system_time.c
Normal 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);
|
||||
}
|
||||
}
|
||||
2
components/services/system_time.h
Normal file
2
components/services/system_time.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#pragma once;
|
||||
void system_time_init(void);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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-----
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 two’s complement with the Gain0 bit set for a 0.5 dB step.
|
||||
// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
|
||||
// +12 dB in 0.5 dB steps. The gain bits are in two’s complement with the Gain0 bit set for a 0.5 dB
|
||||
// step. Register settings outside of the ±12 dB range are reserved and must not be used. See Table
|
||||
// 13 for example settings
|
||||
#define CS4265_CHB_PGA_CTL 0x7
|
||||
// Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
|
||||
// +12 dB in 0.5 dB steps. The gain bits are in two’s complement with the Gain0 bit set for a 0.5 dB step.
|
||||
// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
|
||||
// +12 dB in 0.5 dB steps. The gain bits are in two’s complement with the Gain0 bit set for a 0.5 dB
|
||||
// step. Register settings outside of the ±12 dB range are reserved and must not be used. See Table
|
||||
// 13 for example settings
|
||||
#define CS4265_CHA_PGA_CTL 0x8
|
||||
// Gain[5:0] Setting
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
261
components/squeezelite/external/dac_external.c
vendored
261
components/squeezelite/external/dac_external.c
vendored
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user