mirror of
https://github.com/alexandrebobkov/ESP-Nodes.git
synced 2025-08-08 07:16:08 +00:00
Rainmaker Table lights
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# This is a generated file and its contents are an internal implementation detail.
|
||||
# The update step will be re-executed if anything in this file changes.
|
||||
# No other meaning or use of this file is supported.
|
||||
|
||||
command=
|
||||
work_dir=
|
@@ -0,0 +1,9 @@
|
||||
# This is a generated file and its contents are an internal implementation detail.
|
||||
# The download step will be re-executed if anything in this file changes.
|
||||
# No other meaning or use of this file is supported.
|
||||
|
||||
method=source_dir
|
||||
command=
|
||||
source_dir=/home/alex/esp/v5.4.1/esp-idf/components/bootloader/subproject
|
||||
work_dir=
|
||||
|
@@ -0,0 +1,7 @@
|
||||
# This is a generated file and its contents are an internal implementation detail.
|
||||
# The patch step will be re-executed if anything in this file changes.
|
||||
# No other meaning or use of this file is supported.
|
||||
|
||||
command (connected)=
|
||||
command (disconnected)=
|
||||
work_dir=
|
@@ -0,0 +1 @@
|
||||
cmd='/home/alex/.espressif/tools/cmake/3.30.2/bin/cmake;-DSDKCONFIG=/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/sdkconfig;-DIDF_PATH=/home/alex/esp/v5.4.1/esp-idf;-DIDF_TARGET=esp32c3;-DPYTHON_DEPS_CHECKED=1;-DPYTHON=/home/alex/.espressif/python_env/idf5.4_py3.13_env/bin/python;-DEXTRA_COMPONENT_DIRS=/home/alex/esp/v5.4.1/esp-idf/components/bootloader;-DPROJECT_SOURCE_DIR=/home/alex/github/ESP-Nodes/RainMaker_Table-Lights;-DIGNORE_EXTRA_COMPONENT=;-GNinja;-S;<SOURCE_DIR><SOURCE_SUBDIR>;-B;<BINARY_DIR>'
|
@@ -0,0 +1,27 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# If CMAKE_DISABLE_SOURCE_CHANGES is set to true and the source directory is an
|
||||
# existing directory in our source tree, calling file(MAKE_DIRECTORY) on it
|
||||
# would cause a fatal error, even though it would be a no-op.
|
||||
if(NOT EXISTS "/home/alex/esp/v5.4.1/esp-idf/components/bootloader/subproject")
|
||||
file(MAKE_DIRECTORY "/home/alex/esp/v5.4.1/esp-idf/components/bootloader/subproject")
|
||||
endif()
|
||||
file(MAKE_DIRECTORY
|
||||
"/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader"
|
||||
"/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader-prefix"
|
||||
"/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader-prefix/tmp"
|
||||
"/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader-prefix/src/bootloader-stamp"
|
||||
"/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader-prefix/src"
|
||||
"/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader-prefix/src/bootloader-stamp"
|
||||
)
|
||||
|
||||
set(configSubDirs )
|
||||
foreach(subDir IN LISTS configSubDirs)
|
||||
file(MAKE_DIRECTORY "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader-prefix/src/bootloader-stamp/${subDir}")
|
||||
endforeach()
|
||||
if(cfgdir)
|
||||
file(MAKE_DIRECTORY "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/build/bootloader-prefix/src/bootloader-stamp${cfgdir}") # cfgdir has leading slash
|
||||
endif()
|
12
RainMaker_Table-Lights/build/config.env
Normal file
12
RainMaker_Table-Lights/build/config.env
Normal file
File diff suppressed because one or more lines are too long
32272
RainMaker_Table-Lights/build/config/kconfig_menus.json
Normal file
32272
RainMaker_Table-Lights/build/config/kconfig_menus.json
Normal file
File diff suppressed because it is too large
Load Diff
1790
RainMaker_Table-Lights/build/config/sdkconfig.cmake
Normal file
1790
RainMaker_Table-Lights/build/config/sdkconfig.cmake
Normal file
File diff suppressed because one or more lines are too long
1154
RainMaker_Table-Lights/build/config/sdkconfig.h
Normal file
1154
RainMaker_Table-Lights/build/config/sdkconfig.h
Normal file
File diff suppressed because it is too large
Load Diff
1564
RainMaker_Table-Lights/build/config/sdkconfig.json
Normal file
1564
RainMaker_Table-Lights/build/config/sdkconfig.json
Normal file
File diff suppressed because it is too large
Load Diff
1
RainMaker_Table-Lights/build/gdbinit/prefix_map
Normal file
1
RainMaker_Table-Lights/build/gdbinit/prefix_map
Normal file
@@ -0,0 +1 @@
|
||||
# There is no prefix map defined for the project.
|
89
RainMaker_Table-Lights/build/kconfigs.in
Normal file
89
RainMaker_Table-Lights/build/kconfigs.in
Normal file
@@ -0,0 +1,89 @@
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/app_trace/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/bt/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/console/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/driver/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/efuse/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp-tls/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_adc/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_coex/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_common/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_ana_cmpr/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_cam/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_dac/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_gpio/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_gptimer/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_i2c/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_i2s/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_isp/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_jpeg/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_ledc/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_mcpwm/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_parlio/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_pcnt/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_rmt/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_sdm/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_spi/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_touch_sens/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_tsens/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_uart/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_usb_serial_jtag/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_eth/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_event/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_gdbstub/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_hid/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_http_client/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_http_server/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_https_ota/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_https_server/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_hw_support/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_lcd/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_mm/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_netif/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_partition/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_phy/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_pm/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_psram/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_ringbuf/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_security/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_system/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_timer/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_wifi/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/espcoredump/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/fatfs/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/freertos/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/hal/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/heap/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/ieee802154/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/log/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/lwip/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/mbedtls/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/mqtt/esp-mqtt/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/newlib/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/nvs_flash/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/nvs_sec_provider/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/openthread/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/protocomm/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/pthread/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/soc/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/spi_flash/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/spiffs/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/tcp_transport/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/ulp/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/unity/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/usb/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/vfs/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/wear_levelling/Kconfig"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/wifi_provisioning/Kconfig"
|
||||
source "/home/alex/.espressif/esp-rainmaker/examples/common/app_insights/Kconfig"
|
||||
source "/home/alex/.espressif/esp-rainmaker/examples/common/gpio_button/Kconfig"
|
||||
source "/home/alex/.espressif/esp-rainmaker/examples/common/ws2812_led/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__esp-serial-flasher/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__esp_diag_data_store/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__esp_diagnostics/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__esp_insights/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__esp_rcp_update/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__esp_secure_cert_mgr/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__jsmn/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__mdns/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__network_provisioning/Kconfig"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/managed_components/espressif__rmaker_common/Kconfig"
|
8
RainMaker_Table-Lights/build/kconfigs_projbuild.in
Normal file
8
RainMaker_Table-Lights/build/kconfigs_projbuild.in
Normal file
@@ -0,0 +1,8 @@
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/bootloader/Kconfig.projbuild"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_app_format/Kconfig.projbuild"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esp_rom/Kconfig.projbuild"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/esptool_py/Kconfig.projbuild"
|
||||
source "/home/alex/esp/v5.4.1/esp-idf/components/partition_table/Kconfig.projbuild"
|
||||
source "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/main/Kconfig.projbuild"
|
||||
source "/home/alex/.espressif/esp-rainmaker/examples/common/app_network/Kconfig.projbuild"
|
||||
source "/home/alex/.espressif/esp-rainmaker/components/esp_rainmaker/Kconfig.projbuild"
|
@@ -1,225 +0,0 @@
|
||||
components:
|
||||
- name: "app_trace"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/app_trace"
|
||||
- name: "app_update"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/app_update"
|
||||
- name: "bootloader"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/bootloader"
|
||||
- name: "bootloader_support"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/bootloader_support"
|
||||
- name: "bt"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/bt"
|
||||
- name: "cmock"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/cmock"
|
||||
- name: "console"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/console"
|
||||
- name: "cxx"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/cxx"
|
||||
- name: "driver"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/driver"
|
||||
- name: "efuse"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/efuse"
|
||||
- name: "esp-tls"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp-tls"
|
||||
- name: "esp_adc"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_adc"
|
||||
- name: "esp_app_format"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_app_format"
|
||||
- name: "esp_bootloader_format"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_bootloader_format"
|
||||
- name: "esp_coex"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_coex"
|
||||
- name: "esp_common"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_common"
|
||||
- name: "esp_driver_ana_cmpr"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_ana_cmpr"
|
||||
- name: "esp_driver_cam"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_cam"
|
||||
- name: "esp_driver_dac"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_dac"
|
||||
- name: "esp_driver_gpio"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_gpio"
|
||||
- name: "esp_driver_gptimer"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_gptimer"
|
||||
- name: "esp_driver_i2c"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_i2c"
|
||||
- name: "esp_driver_i2s"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_i2s"
|
||||
- name: "esp_driver_isp"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_isp"
|
||||
- name: "esp_driver_jpeg"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_jpeg"
|
||||
- name: "esp_driver_ledc"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_ledc"
|
||||
- name: "esp_driver_mcpwm"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_mcpwm"
|
||||
- name: "esp_driver_parlio"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_parlio"
|
||||
- name: "esp_driver_pcnt"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_pcnt"
|
||||
- name: "esp_driver_ppa"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_ppa"
|
||||
- name: "esp_driver_rmt"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_rmt"
|
||||
- name: "esp_driver_sdio"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_sdio"
|
||||
- name: "esp_driver_sdm"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_sdm"
|
||||
- name: "esp_driver_sdmmc"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_sdmmc"
|
||||
- name: "esp_driver_sdspi"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_sdspi"
|
||||
- name: "esp_driver_spi"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_spi"
|
||||
- name: "esp_driver_touch_sens"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_touch_sens"
|
||||
- name: "esp_driver_tsens"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_tsens"
|
||||
- name: "esp_driver_uart"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_uart"
|
||||
- name: "esp_driver_usb_serial_jtag"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_driver_usb_serial_jtag"
|
||||
- name: "esp_eth"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_eth"
|
||||
- name: "esp_event"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_event"
|
||||
- name: "esp_gdbstub"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_gdbstub"
|
||||
- name: "esp_hid"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_hid"
|
||||
- name: "esp_http_client"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_http_client"
|
||||
- name: "esp_http_server"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_http_server"
|
||||
- name: "esp_https_ota"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_https_ota"
|
||||
- name: "esp_https_server"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_https_server"
|
||||
- name: "esp_hw_support"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_hw_support"
|
||||
- name: "esp_lcd"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_lcd"
|
||||
- name: "esp_local_ctrl"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_local_ctrl"
|
||||
- name: "esp_mm"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_mm"
|
||||
- name: "esp_netif"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_netif"
|
||||
- name: "esp_netif_stack"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_netif_stack"
|
||||
- name: "esp_partition"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_partition"
|
||||
- name: "esp_phy"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_phy"
|
||||
- name: "esp_pm"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_pm"
|
||||
- name: "esp_psram"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_psram"
|
||||
- name: "esp_ringbuf"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_ringbuf"
|
||||
- name: "esp_rom"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_rom"
|
||||
- name: "esp_security"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_security"
|
||||
- name: "esp_system"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_system"
|
||||
- name: "esp_timer"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_timer"
|
||||
- name: "esp_vfs_console"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_vfs_console"
|
||||
- name: "esp_wifi"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esp_wifi"
|
||||
- name: "espcoredump"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/espcoredump"
|
||||
- name: "esptool_py"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/esptool_py"
|
||||
- name: "fatfs"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/fatfs"
|
||||
- name: "freertos"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/freertos"
|
||||
- name: "hal"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/hal"
|
||||
- name: "heap"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/heap"
|
||||
- name: "http_parser"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/http_parser"
|
||||
- name: "idf_test"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/idf_test"
|
||||
- name: "ieee802154"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/ieee802154"
|
||||
- name: "json"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/json"
|
||||
- name: "linux"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/linux"
|
||||
- name: "log"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/log"
|
||||
- name: "lwip"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/lwip"
|
||||
- name: "mbedtls"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/mbedtls"
|
||||
- name: "mqtt"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/mqtt"
|
||||
- name: "newlib"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/newlib"
|
||||
- name: "nvs_flash"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/nvs_flash"
|
||||
- name: "nvs_sec_provider"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/nvs_sec_provider"
|
||||
- name: "openthread"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/openthread"
|
||||
- name: "partition_table"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/partition_table"
|
||||
- name: "perfmon"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/perfmon"
|
||||
- name: "protobuf-c"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/protobuf-c"
|
||||
- name: "protocomm"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/protocomm"
|
||||
- name: "pthread"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/pthread"
|
||||
- name: "riscv"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/riscv"
|
||||
- name: "rt"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/rt"
|
||||
- name: "sdmmc"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/sdmmc"
|
||||
- name: "soc"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/soc"
|
||||
- name: "spi_flash"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/spi_flash"
|
||||
- name: "spiffs"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/spiffs"
|
||||
- name: "tcp_transport"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/tcp_transport"
|
||||
- name: "touch_element"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/touch_element"
|
||||
- name: "ulp"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/ulp"
|
||||
- name: "unity"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/unity"
|
||||
- name: "usb"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/usb"
|
||||
- name: "vfs"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/vfs"
|
||||
- name: "wear_levelling"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/wear_levelling"
|
||||
- name: "wifi_provisioning"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/wifi_provisioning"
|
||||
- name: "wpa_supplicant"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/wpa_supplicant"
|
||||
- name: "xtensa"
|
||||
path: "/home/alex/esp/v5.4.1/esp-idf/components/xtensa"
|
||||
- name: "main"
|
||||
path: "/home/alex/github/ESP-Nodes/RainMaker_Table-Lights/main"
|
||||
- name: "app_insights"
|
||||
path: "/home/alex/.espressif/esp-rainmaker/examples/common/app_insights"
|
||||
- name: "app_network"
|
||||
path: "/home/alex/.espressif/esp-rainmaker/examples/common/app_network"
|
||||
- name: "app_reset"
|
||||
path: "/home/alex/.espressif/esp-rainmaker/examples/common/app_reset"
|
||||
- name: "gpio_button"
|
||||
path: "/home/alex/.espressif/esp-rainmaker/examples/common/gpio_button"
|
||||
- name: "ledc_driver"
|
||||
path: "/home/alex/.espressif/esp-rainmaker/examples/common/ledc_driver"
|
||||
- name: "ws2812_led"
|
||||
path: "/home/alex/.espressif/esp-rainmaker/examples/common/ws2812_led"
|
@@ -0,0 +1 @@
|
||||
b6ae5ae71264612d3742ef91e3f44092ae98060feecddd70e04f26bdc172cded
|
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-30T12:44:52.431051+00:00", "files": [{"path": "CMakeLists.txt", "size": 1796, "hash": "8a508fb5023a0899df0da80a887008bf5e76e5ff2424f12ba10bcb5ed574d666"}, {"path": "Kconfig", "size": 5692, "hash": "c1f95ec2df707d3b7b9bbe8ababd1ed7505ac7d58b370ad2863c60873b672f12"}, {"path": "LICENSE", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "README.md", "size": 327, "hash": "8fed44792908fbe8876d0fc31ebbb4fea047da693f656343ae4afa62e2ff8abc"}, {"path": "idf_component.yml", "size": 557, "hash": "20ec0f5b6f6041118b24f05d1d91f801fd17624640fd26c84451fcb14556daea"}, {"path": "include/esp_diagnostics.h", "size": 12531, "hash": "6863c63f8018608857bb1a374c35a24d69b8d25c7f576142f9c4c566d9087d24"}, {"path": "include/esp_diagnostics_metrics.h", "size": 9383, "hash": "63923d3f95654dc87eb325ca4b0cc38aa0af21b60f5f846cff3c8a7f44d33e38"}, {"path": "include/esp_diagnostics_network_variables.h", "size": 798, "hash": "9df14b8dee1a265932162a2ffa374cb5f342ad4b68702b031aa2dffcad19f61e"}, {"path": "include/esp_diagnostics_system_metrics.h", "size": 3153, "hash": "e807dd4c3292af2961073e31f11f245a811777696471d37a8698202f9f106c20"}, {"path": "include/esp_diagnostics_variables.h", "size": 10260, "hash": "6989111538e4fc272785661eb4474ffaa713be9f94b678a851da21d357b7434e"}, {"path": "src/esp_diagnostics_heap_metrics.c", "size": 9956, "hash": "ad56f049c25c557851e6c2c5c1b9c31e2f19d8be5749b229fa1379666069f6cd"}, {"path": "src/esp_diagnostics_internal.h", "size": 696, "hash": "6d0e13d39bd2517ec7f79ab9add4c963bc13c04a1854525fd119374a5c370e40"}, {"path": "src/esp_diagnostics_log_hook.c", "size": 17422, "hash": "d3a9d79c96e8d4019d53eb3f83e7140cb8eed8185ed1351369fdb776e223e6d7"}, {"path": "src/esp_diagnostics_metrics.c", "size": 11033, "hash": "4f4670042323c6475deeb965f38141e172f3ae395c0c4ed18c4fe8f7b54a5f97"}, {"path": "src/esp_diagnostics_network_variables.c", "size": 14862, "hash": "fac3f922554ab0ac01e43b2131e5847d55e3a493769a0de6bb0e7e7b38d3226f"}, {"path": "src/esp_diagnostics_utils.c", "size": 15990, "hash": "dab3d80d4f93c70315fadd48934c631f3b0ff0bbc3a4388cb078ba9bb9552bf0"}, {"path": "src/esp_diagnostics_variables.c", "size": 11063, "hash": "5a56a467e267490a1fc3cddbcdfdf25dc4c9b72e4502586427dc646212eba516"}, {"path": "src/esp_diagnostics_wifi_metrics.c", "size": 8173, "hash": "98c21faccdb27e7bbfc92c5783f4d430d3754d290159a6da03de69ca1f10728f"}]}
|
@@ -0,0 +1,52 @@
|
||||
set(srcs "src/esp_diagnostics_log_hook.c"
|
||||
"src/esp_diagnostics_utils.c")
|
||||
|
||||
if(CONFIG_DIAG_ENABLE_METRICS)
|
||||
list(APPEND srcs "src/esp_diagnostics_metrics.c")
|
||||
if(CONFIG_DIAG_ENABLE_HEAP_METRICS)
|
||||
list(APPEND srcs "src/esp_diagnostics_heap_metrics.c")
|
||||
endif()
|
||||
if(CONFIG_DIAG_ENABLE_WIFI_METRICS)
|
||||
list(APPEND srcs "src/esp_diagnostics_wifi_metrics.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_DIAG_ENABLE_VARIABLES)
|
||||
list(APPEND srcs "src/esp_diagnostics_variables.c")
|
||||
if(CONFIG_DIAG_ENABLE_NETWORK_VARIABLES)
|
||||
list(APPEND srcs "src/esp_diagnostics_network_variables.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(priv_req freertos app_update rmaker_common)
|
||||
|
||||
# esp_hw_support component was introduced in v4.3
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.2")
|
||||
list(APPEND priv_req esp_hw_support)
|
||||
endif()
|
||||
|
||||
# from IDF version 5.0, we need to explicitly specify requirements
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND priv_req esp_wifi esp_event)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
|
||||
# Only wrap the log functions if ESP-INSIGHTS is enabled
|
||||
if(CONFIG_DIAG_ENABLE_WRAP_LOG_FUNCTIONS)
|
||||
# If log level is set to NONE or if logging APIs are externally wrapped then skip
|
||||
# wrapping logging APIs here
|
||||
if ((NOT CONFIG_LOG_DEFAULT_LEVEL_NONE) AND (NOT CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP))
|
||||
list(APPEND WRAP_FUNCTIONS esp_log_write esp_log_writev)
|
||||
endif()
|
||||
|
||||
if(CONFIG_LIB_BUILDER_COMPILE)
|
||||
list(APPEND WRAP_FUNCTIONS log_printf)
|
||||
endif()
|
||||
|
||||
foreach(func ${WRAP_FUNCTIONS})
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=${func}")
|
||||
endforeach()
|
||||
endif()
|
@@ -0,0 +1,134 @@
|
||||
menu "Diagnostics"
|
||||
choice DIAG_LOG_MSG_ARG_FORMAT
|
||||
prompt "Diagnostics log argument format"
|
||||
default DIAG_LOG_MSG_ARG_FORMAT_TLV
|
||||
help
|
||||
For error/warning/event logs, diagnostics module collects program counter, timestamp,
|
||||
tag, RO data pointer and log arguments. Log arguments are stored in statically allocated buffer.
|
||||
This option configures how to format and store log arguments in buffer.
|
||||
Log arguments can be formatted as TLV or complete log string formatted using vsnprintf.
|
||||
|
||||
If "TLV" is selected, buffer contains arguments formatted as TLV.
|
||||
Type - 1 byte for the type of argument, please check esp_diag_arg_type_t.
|
||||
Length - 1 byte for the size of argument, size is calculated using sizeof operator.
|
||||
Value - Size bytes for the value.
|
||||
|
||||
If "STRING" is selected, buffer contains the entire string formatted using vsnprintf.
|
||||
|
||||
config DIAG_LOG_MSG_ARG_FORMAT_TLV
|
||||
bool "Format arguments as TLV"
|
||||
config DIAG_LOG_MSG_ARG_FORMAT_STRING
|
||||
bool "Format arguments as string"
|
||||
endchoice
|
||||
|
||||
config DIAG_LOG_MSG_ARG_MAX_SIZE
|
||||
int "Maximum size of diagnostics log argument buffer"
|
||||
range 32 255
|
||||
default 64
|
||||
help
|
||||
Log arguments are stored in a static allocated buffer.
|
||||
This option configures the maximum size of buffer for storing log arguments.
|
||||
|
||||
config DIAG_LOG_DROP_WIFI_LOGS
|
||||
bool "Drop Wi-Fi logs"
|
||||
default y
|
||||
help
|
||||
Every Wi-Fi log printed on the console adds three diagnostics logs.
|
||||
For some users, Wi-Fi logs may not be that useful.
|
||||
By default, diagnostics drops Wi-Fi logs. Set this config option to "n" for recording Wi-Fi logs.
|
||||
|
||||
config DIAG_ENABLE_WRAP_LOG_FUNCTIONS
|
||||
bool "Enable wrapping of log functions"
|
||||
default n
|
||||
help
|
||||
This option enables wrapping of esp_log_write and esp_log_writev APIs using `--wrap` gcc option.
|
||||
Enable this option when direct log access is required.
|
||||
|
||||
Note: This option is automatically selected when ESP Insights is enabled.
|
||||
|
||||
config DIAG_ENABLE_METRICS
|
||||
bool "Enable diagnostics metrics"
|
||||
default y
|
||||
help
|
||||
Diagnostics module supports recording and reporting metrics to cloud.
|
||||
This option enables the diagnostics metrics and related functionality.
|
||||
|
||||
config DIAG_METRICS_MAX_COUNT
|
||||
depends on DIAG_ENABLE_METRICS
|
||||
int "Maximum number of metrics"
|
||||
default 20
|
||||
help
|
||||
This option configures the maximum number of metrics that can be registered.
|
||||
|
||||
config DIAG_ENABLE_HEAP_METRICS
|
||||
depends on DIAG_ENABLE_METRICS
|
||||
bool "Enable Heap Metrics"
|
||||
default y
|
||||
help
|
||||
Enables the heap memory metrics. This collects free memory, largest free block,
|
||||
and minimum free memory for heaps in internal as well as external memory.
|
||||
|
||||
config DIAG_HEAP_POLLING_INTERVAL
|
||||
depends on DIAG_ENABLE_HEAP_METRICS
|
||||
int "Heap metrics polling interval in seconds"
|
||||
range 30 86400
|
||||
default 30
|
||||
help
|
||||
This option configures the time interval in seconds at which heap metrics are collected.
|
||||
Minimum allowed value is 30 seconds and maximum is 24 hours (86400 seconds).
|
||||
|
||||
config DIAG_ENABLE_WIFI_METRICS
|
||||
depends on DIAG_ENABLE_METRICS
|
||||
bool "Enable Wi-Fi Metrics"
|
||||
default y
|
||||
help
|
||||
Enables Wi-Fi metrics and collects Wi-Fi RSSI and minumum ever Wi-Fi RSSI.
|
||||
|
||||
config DIAG_WIFI_POLLING_INTERVAL
|
||||
depends on DIAG_ENABLE_WIFI_METRICS
|
||||
int "Wi-Fi metrics polling interval in seconds"
|
||||
range 30 86400
|
||||
default 30
|
||||
help
|
||||
This option configures the time interval in seconds at which Wi-Fi metrics are collected.
|
||||
Minimum allowed value is 30 seconds and maximum is 24 hours (86400 seconds).
|
||||
|
||||
config DIAG_ENABLE_VARIABLES
|
||||
bool "Enable diagnostics variables"
|
||||
default y
|
||||
help
|
||||
Variables are similar to metrics but they represent entities where their current value
|
||||
is much more important than over a period of time.
|
||||
This option enables the diagnostics variables and related functionality
|
||||
|
||||
config DIAG_VARIABLES_MAX_COUNT
|
||||
depends on DIAG_ENABLE_VARIABLES
|
||||
int "Maximum number of variables"
|
||||
default 20
|
||||
help
|
||||
This option configures the maximum number of variables that can be registered.
|
||||
|
||||
config DIAG_ENABLE_NETWORK_VARIABLES
|
||||
depends on DIAG_ENABLE_VARIABLES
|
||||
bool "Enable Network variables"
|
||||
default y
|
||||
help
|
||||
Enables the Wi-Fi and IP address variables. Below variables are collected.
|
||||
For Wi-Fi: SSID, BSSID, channel, auth mode, connection status, disconnection reason.
|
||||
For IP: IPv4 address, netmask, and gateway of the device.
|
||||
|
||||
config DIAG_MORE_NETWORK_VARS
|
||||
depends on DIAG_ENABLE_NETWORK_VARIABLES
|
||||
bool "Enable More Advanced Network variables"
|
||||
default n
|
||||
help
|
||||
Enable more advanced network variables
|
||||
|
||||
config DIAG_USE_EXTERNAL_LOG_WRAP
|
||||
bool "Use external log wrapper"
|
||||
default n
|
||||
help
|
||||
Diagnostics component wraps the esp_log_write and esp_log_writev APIs using `--wrap` gcc option.
|
||||
There can be scenario where another component also wants to wrap the logging functions.
|
||||
In that case, enable this option and use the data ingestion APIs esp_diag_log_write and esp_diag_log_writev.
|
||||
endmenu
|
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@@ -0,0 +1,5 @@
|
||||
# ESP Diagnostics Component
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_diagnostics)
|
||||
|
||||
This component provides the diagnostics functionality used by [ESP Insights](https://github.com/espressif/esp-insights).
|
@@ -0,0 +1,14 @@
|
||||
dependencies:
|
||||
espressif/rmaker_common:
|
||||
version: ~1.4.0
|
||||
idf:
|
||||
version: '>=4.1'
|
||||
description: Diagnostics component used in ESP Insights, which is a remote diagnostics
|
||||
solution to monitor the health of ESP devices in the field.
|
||||
issues: https://github.com/espressif/esp-insights/issues
|
||||
repository: git://github.com/espressif/esp-insights.git
|
||||
repository_info:
|
||||
commit_sha: b043f72425dadf58095847125404426bc2d4cba8
|
||||
path: components/esp_diagnostics
|
||||
url: https://github.com/espressif/esp-insights/tree/main/components/esp_diagnostics
|
||||
version: 1.2.3
|
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Callback to write log to diagnostics storage
|
||||
*/
|
||||
typedef esp_err_t (*esp_diag_log_write_cb_t)(void *data, size_t len, void *priv_data);
|
||||
|
||||
/**
|
||||
* @brief Diagnostics log configurations
|
||||
*/
|
||||
typedef struct {
|
||||
esp_diag_log_write_cb_t write_cb; /*!< Callback function to write diagnostics data */
|
||||
void *cb_arg; /*!< User data to pass in callback function */
|
||||
} esp_diag_log_config_t;
|
||||
|
||||
/**
|
||||
* @brief Supported log types in diagnostics
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_DIAG_LOG_TYPE_ERROR = 1 << 0, /*!< Diagnostics log type error */
|
||||
ESP_DIAG_LOG_TYPE_WARNING = 1 << 1, /*!< Diagnostics log type warning */
|
||||
ESP_DIAG_LOG_TYPE_EVENT = 1 << 2, /*!< Diagnostics log type event */
|
||||
} esp_diag_log_type_t;
|
||||
|
||||
/**
|
||||
* @brief Log argument data types
|
||||
*/
|
||||
typedef enum {
|
||||
ARG_TYPE_CHAR, /*!< Argument type (char) */
|
||||
ARG_TYPE_SHORT, /*!< Argument type (short) */
|
||||
ARG_TYPE_INT, /*!< Argument type (int) */
|
||||
ARG_TYPE_L, /*!< Argument type (long) */
|
||||
ARG_TYPE_LL, /*!< Argument type (long long) */
|
||||
ARG_TYPE_INTMAX, /*!< Argument type (intmax_t) */
|
||||
ARG_TYPE_PTRDIFF, /*!< Argument type (ptrdiff_t) */
|
||||
ARG_TYPE_UCHAR, /*!< Argument type (unsigned char) */
|
||||
ARG_TYPE_USHORT, /*!< Argument type (unsigned short) */
|
||||
ARG_TYPE_UINT, /*!< Argument type (unsigned int) */
|
||||
ARG_TYPE_UL, /*!< Argument type (unsigned long) */
|
||||
ARG_TYPE_ULL, /*!< Argument type (unsigned long long) */
|
||||
ARG_TYPE_UINTMAX, /*!< Argument type (uintmax_t) */
|
||||
ARG_TYPE_SIZE, /*!< Argument type (size_t) */
|
||||
ARG_TYPE_DOUBLE, /*!< Argument type (double) */
|
||||
ARG_TYPE_LDOUBLE, /*!< Argument type (long double) */
|
||||
ARG_TYPE_STR, /*!< Argument type (char *) */
|
||||
ARG_TYPE_INVALID, /*!< Argument type invalid */
|
||||
} esp_diag_arg_type_t;
|
||||
|
||||
/**
|
||||
* @brief Log argument data value
|
||||
*/
|
||||
typedef union {
|
||||
char c; /*!< Value of type signed char */
|
||||
short s; /*!< Value of type signed short */
|
||||
int i; /*!< Value of type signed integer */
|
||||
long l; /*!< Value of type signed long */
|
||||
long long ll; /*!< Value of type signed long long */
|
||||
intmax_t imx; /*!< Value of type intmax_t */
|
||||
ptrdiff_t ptrdiff; /*!< Value of type ptrdiff_t */
|
||||
unsigned char uc; /*!< Value of type unsigned char */
|
||||
unsigned short us; /*!< Value of type unsigned short */
|
||||
unsigned int u; /*!< Value of type unsigned integer */
|
||||
unsigned long ul; /*!< Value of type unsigned long */
|
||||
unsigned long long ull; /*!< Value of type unsigned long long */
|
||||
uintmax_t umx; /*!< Value of type uintmax_t */
|
||||
size_t sz; /*!< Value of type size_t */
|
||||
double d; /*!< Value of type double */
|
||||
long double ld; /*!< Value of type long double */
|
||||
char *str; /*!< value of type string */
|
||||
} esp_diag_arg_value_t;
|
||||
|
||||
/**
|
||||
* @brief Diagnostics data point type
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_DIAG_DATA_PT_METRICS, /*!< Data point of type metrics */
|
||||
ESP_DIAG_DATA_PT_VARIABLE, /*!< Data point of type variable */
|
||||
} esp_diag_data_pt_type_t;
|
||||
|
||||
/**
|
||||
* @brief Diagnostics data types
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_DIAG_DATA_TYPE_BOOL, /*!< Data type boolean */
|
||||
ESP_DIAG_DATA_TYPE_INT, /*!< Data type integer */
|
||||
ESP_DIAG_DATA_TYPE_UINT, /*!< Data type unsigned integer */
|
||||
ESP_DIAG_DATA_TYPE_FLOAT, /*!< Data type float */
|
||||
ESP_DIAG_DATA_TYPE_STR, /*!< Data type string */
|
||||
ESP_DIAG_DATA_TYPE_IPv4, /*!< Data type IPv4 address */
|
||||
ESP_DIAG_DATA_TYPE_MAC, /*!< Data type MAC address */
|
||||
ESP_DIAG_DATA_TYPE_NULL, /*!< No type */
|
||||
ESP_DIAG_DATA_TYPE_MAX, /*!< Max type */
|
||||
} esp_diag_data_type_t;
|
||||
|
||||
/**
|
||||
* @brief Diagnostics log data structure
|
||||
*/
|
||||
typedef struct {
|
||||
esp_diag_log_type_t type; /*!< Type of diagnostics log */
|
||||
uint32_t pc; /*!< Program Counter */
|
||||
uint64_t timestamp; /*!< If NTP sync enabled then POSIX time,
|
||||
otherwise relative time since bootup in microseconds */
|
||||
char tag[16]; /*!< Tag of log message */
|
||||
void *msg_ptr; /*!< Address of err/warn/event message in rodata */
|
||||
uint8_t msg_args[CONFIG_DIAG_LOG_MSG_ARG_MAX_SIZE]; /*!< Arguments of log message */
|
||||
uint8_t msg_args_len; /*!< Length of argument */
|
||||
char task_name[CONFIG_FREERTOS_MAX_TASK_NAME_LEN]; /*!< Task name */
|
||||
} esp_diag_log_data_t;
|
||||
|
||||
/**
|
||||
* @brief Device information structure
|
||||
*/
|
||||
#define DIAG_HEX_SHA_SIZE 16 /* Length of ELF SHA as HEX string*/
|
||||
#define DIAG_SHA_SIZE (DIAG_HEX_SHA_SIZE / 2) /* Length of ELF SHA as raw bytes*/
|
||||
typedef struct {
|
||||
uint32_t chip_model; /*!< Chip model */
|
||||
uint32_t chip_rev; /*!< Chip revision */
|
||||
uint32_t reset_reason; /*!< Reset reason */
|
||||
char app_version[32]; /*!< Application version */
|
||||
char project_name[32]; /*!< Project name */
|
||||
char app_elf_sha256[DIAG_HEX_SHA_SIZE + 1]; /*!< SHA256 of application elf */
|
||||
} esp_diag_device_info_t;
|
||||
|
||||
/**
|
||||
* @brief Task backtrace structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t bt[16]; /*!< Backtrace (array of PC) */
|
||||
uint32_t depth; /*!< Number of backtrace entries */
|
||||
bool corrupted; /*!< Status flag for backtrace is corrupt or not */
|
||||
} esp_diag_task_bt_t;
|
||||
|
||||
/**
|
||||
* @brief Task information structure
|
||||
*/
|
||||
typedef struct {
|
||||
char name[CONFIG_FREERTOS_MAX_TASK_NAME_LEN]; /*!< Task name */
|
||||
uint32_t state; /*!< Task state */
|
||||
uint32_t high_watermark; /*!< Task high watermark */
|
||||
#ifndef CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
esp_diag_task_bt_t bt_info; /*!< Backtrace of the task */
|
||||
#endif /* !CONFIG_IDF_TARGET_ARCH_RISCV */
|
||||
} esp_diag_task_info_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for diagnostics data point
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t type; /*!< Metrics or Variable */
|
||||
uint16_t data_type; /*!< Data type */
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
char tag[16]; /*!< TAG */
|
||||
#endif
|
||||
char key[16]; /*!< Key */
|
||||
uint64_t ts; /*!< Timestamp */
|
||||
union {
|
||||
bool b; /*!< Value for boolean data type */
|
||||
int32_t i; /*!< Value for integer data type */
|
||||
uint32_t u; /*!< Value for unsigned integer data type */
|
||||
float f; /*!< Value for float data type */
|
||||
uint32_t ipv4; /*!< Value for the IPv4 address */
|
||||
uint8_t mac[6]; /*!< Value for the MAC address */
|
||||
} value;
|
||||
} esp_diag_data_pt_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for string data type diagnostics data point
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t type; /*!< Metrics or Variable */
|
||||
uint16_t data_type; /*!< Data type */
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
char tag[16]; /*!< TAG */
|
||||
#endif
|
||||
char key[16]; /*!< Key */
|
||||
uint64_t ts; /*!< Timestamp */
|
||||
union {
|
||||
char str[32]; /*!< Value for string data type */
|
||||
} value;
|
||||
} esp_diag_str_data_pt_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize diagnostics log hook
|
||||
*
|
||||
* @param[in] config Pointer to a config structure of type \ref esp_diag_log_config_t
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_log_hook_init(esp_diag_log_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Enable the diagnostics log hook for provided log type
|
||||
*
|
||||
* @param[in] type Log type to enable, can be the bitwise OR of types from \ref esp_diag_log_type_t
|
||||
*/
|
||||
void esp_diag_log_hook_enable(uint32_t type);
|
||||
|
||||
/**
|
||||
* @brief Disable the diagnostics log hook for provided log type
|
||||
*
|
||||
* @param[in] type Log type to disable, can be the bitwise OR of types from \ref esp_diag_log_type_t
|
||||
*
|
||||
*/
|
||||
void esp_diag_log_hook_disable(uint32_t type);
|
||||
|
||||
/**
|
||||
* @brief Add diagnostics event
|
||||
*
|
||||
* @param[in] tag The tag of message
|
||||
* @param[in] format Message format
|
||||
* @param[in] ... Variable arguments
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*
|
||||
* @note This function is not intended to be used directly, Instead, use macro \ref ESP_DIAG_EVENT
|
||||
*/
|
||||
esp_err_t esp_diag_log_event(const char *tag, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
/**
|
||||
* @brief Macro to add the custom event
|
||||
*
|
||||
* @param[in] tag tag of the event
|
||||
* @param[in] format format of the event
|
||||
* @param[in] ... Variable arguments
|
||||
*/
|
||||
#define ESP_DIAG_EVENT(tag, format, ...) \
|
||||
{ \
|
||||
esp_diag_log_event(tag, "EV (%" PRIu32 ") %s: " format, esp_log_timestamp(), tag, ##__VA_ARGS__); \
|
||||
ESP_LOGI(tag, format, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the device information for diagnostics
|
||||
*
|
||||
* @param[out] device_info Pointer to device_info structure of type \ref esp_diag_device_info_t
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_ERR_INVALID_ARG if device_info is NULL
|
||||
*/
|
||||
esp_err_t esp_diag_device_info_get(esp_diag_device_info_t *device_info);
|
||||
|
||||
/**
|
||||
* @brief Get the timestamp
|
||||
*
|
||||
* This function returns POSIX time if NTP sync is enabled
|
||||
* otherwise returns time since bootup in microseconds
|
||||
*
|
||||
* @return timestamp
|
||||
*/
|
||||
uint64_t esp_diag_timestamp_get(void);
|
||||
|
||||
/**
|
||||
* @brief Get backtrace and some more details of all tasks in system
|
||||
*
|
||||
* @note On device backtrace parsing not available on RISC-V boards (ESP32C3)
|
||||
*
|
||||
* @param[out] tasks Array to store task info
|
||||
* @param[in] size Size of array, If size is less than the number of tasks in system,
|
||||
* then info of size tasks is filled in array
|
||||
*
|
||||
* @return Number of task info filled in array
|
||||
*
|
||||
* @note Allocate enough memory to store all tasks,
|
||||
* Use uxTaskGetNumberOfTasks() to get number of tasks in system
|
||||
*/
|
||||
uint32_t esp_diag_task_snapshot_get(esp_diag_task_info_t *tasks, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Dump backtrace and some more details of all tasks
|
||||
* in system to console using \ref ESP_DIAG_EVENT
|
||||
*/
|
||||
void esp_diag_task_snapshot_dump(void);
|
||||
|
||||
/**
|
||||
* @brief Get CRC of diagnostics metadata
|
||||
*
|
||||
* @return crc
|
||||
*/
|
||||
uint32_t esp_diag_meta_crc_get(void);
|
||||
|
||||
/**
|
||||
* @brief Get CRC of diagnostics data structures' size
|
||||
*
|
||||
* @return crc
|
||||
*/
|
||||
uint32_t esp_diag_data_size_get_crc(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convenience API for ingesting log data into diagnostics when esp_log_writev() is externally wrapped.
|
||||
* This API should be called from __wrap_esp_log_writev(). \see CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP.
|
||||
*
|
||||
* @param[in] level Log level
|
||||
* @param[in] tag Tag of the log
|
||||
* @param[in] format Format of the log
|
||||
* @param[in] v Variable argument list
|
||||
*
|
||||
* @note The Diagnostics component wraps the esp_log_write() and esp_log_writev() APIs using the `--wrap` GCC option
|
||||
* to collect logs. If another component intends to wrap the logging APIs, enable the configuration option
|
||||
* CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP. This will prevent the Diagnostics component from wrapping the logging APIs.
|
||||
* To enable log diagnostics in such case, call the esp_diag_log_writev() and esp_diag_log_write() APIs within
|
||||
* their respective externally wrapped APIs.
|
||||
*
|
||||
* @note Avoid calling this API explicitly unless there is an use case as the one described above.
|
||||
*/
|
||||
void esp_diag_log_writev(esp_log_level_t level, const char *tag, const char *format, va_list v);
|
||||
|
||||
/**
|
||||
* @brief Convenience API for ingesting log data into diagnostics when esp_log_write() is externally wrapped.
|
||||
* This API should be called from __wrap_esp_log_write(). \see CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP.
|
||||
*
|
||||
* @param[in] level Log level
|
||||
* @param[in] tag Tag of the log
|
||||
* @param[in] format Format of the log
|
||||
* @param[in] v variable argument list
|
||||
*
|
||||
* @note Please see notes from \see esp_diag_log_writev()
|
||||
*/
|
||||
void esp_diag_log_write(esp_log_level_t level, const char *tag, const char *format, va_list v);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_diagnostics.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_METRICS
|
||||
/**
|
||||
* @brief Callback to write metrics data
|
||||
*
|
||||
* @param[in] tag Tag for metrics
|
||||
* @param[in] data Metrics data
|
||||
* @param[in] len Length of metrics data
|
||||
* @param[in] cb_arg User data to pass in write callback
|
||||
*/
|
||||
typedef esp_err_t (*esp_diag_metrics_write_cb_t)(const char *tag, void *data, size_t len, void *cb_arg);
|
||||
|
||||
/**
|
||||
* @brief Diagnostics metrics config structure
|
||||
*/
|
||||
typedef struct {
|
||||
esp_diag_metrics_write_cb_t write_cb; /*!< Callback function to write diagnostics data */
|
||||
void *cb_arg; /*!< User data to pass in callback function */
|
||||
} esp_diag_metrics_config_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for diagnostics metrics metadata
|
||||
*/
|
||||
typedef struct {
|
||||
const char *tag; /*!< Tag of metrics */
|
||||
const char *key; /*!< Unique key for the metrics */
|
||||
const char *label; /*!< Label for the metrics */
|
||||
const char *path; /*!< Hierarchical path for the key, must be separated by '.' for more than one level,
|
||||
eg: "wifi", "heap.internal", "heap.external" */
|
||||
const char *unit; /*!< Data unit, can be NULL */
|
||||
esp_diag_data_type_t type; /*!< Data type of metrics */
|
||||
} esp_diag_metrics_meta_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the diagnostics metrics
|
||||
*
|
||||
* @param[in] config Pointer to a config structure of type \ref esp_diag_metrics_config_t
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_init(esp_diag_metrics_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the diagnostics metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Register a metrics
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Unique key for the metrics
|
||||
* @param[in] label Label for the metrics
|
||||
* @param[in] path Hierarchical path for key, must be separated by '.' for more than one level
|
||||
* @param[in] type Data type of metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_register(const char *tag,
|
||||
const char *key,
|
||||
const char *label,
|
||||
const char *path,
|
||||
esp_diag_data_type_t type);
|
||||
|
||||
/**
|
||||
* @brief Unregister all previously registered metrics
|
||||
*
|
||||
* @return ESP_OK if successful, qppropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_unregister_all(void);
|
||||
|
||||
/**
|
||||
* @brief Get metadata for all metrics
|
||||
*
|
||||
* @param[out] len Length of the metrics meta data array
|
||||
*
|
||||
* @return array Array of metrics meta data
|
||||
*/
|
||||
const esp_diag_metrics_meta_t *esp_diag_metrics_meta_get_all(uint32_t *len);
|
||||
|
||||
/**
|
||||
* @brief Print metadata for all metrics
|
||||
*/
|
||||
void esp_diag_metrics_meta_print_all(void);
|
||||
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
|
||||
/**
|
||||
* @brief Unregister a diagnostics metrics
|
||||
*
|
||||
* @param[in] tag Tag of the metrics
|
||||
* @param[in] key Key for the metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_unregister(const char *tag, const char *key);
|
||||
|
||||
/**
|
||||
* @brief Specify unit of the data for the particular key
|
||||
*
|
||||
* @param[in] tag Tag of the metrics
|
||||
* @param[in] key Key for which the unit to be specified
|
||||
* @param[in] unit Unit string of the data
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code othewise.
|
||||
*
|
||||
* @note this API if used, should be called after \ref esp_diag_metrics_register
|
||||
* API with the same `key` to take effect
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_unit(const char *tag, const char *key, const char *unit);
|
||||
|
||||
/**
|
||||
* @brief Add metrics to storage
|
||||
*
|
||||
* @param[in] data_type Data type of metrics \ref esp_diag_data_type_t
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of metrics
|
||||
* @param[in] val Value of metrics
|
||||
* @param[in] val_sz Size of val
|
||||
* @param[in] ts Timestamp in microseconds, this should be the value at the time of data gathering
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*
|
||||
* @note \ref esp_diag_timestamp_get() API can be used to get timestamp in mircoseconds.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report(esp_diag_data_type_t data_type,
|
||||
const char *tag, const char *key, const void *val,
|
||||
size_t val_sz, uint64_t ts);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type boolean
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of the metrics
|
||||
* @param[in] b Value of the metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report_bool(const char *tag, const char *key, bool b);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type integer
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of the metrics
|
||||
* @param[in] i Value of the metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report_int(const char *tag, const char *key, int32_t i);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type unsigned integer
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of the metrics
|
||||
* @param[in] u Value of the metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report_uint(const char *tag, const char *key, uint32_t u);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type float
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of the metrics
|
||||
* @param[in] f Value of the metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report_float(const char *tag, const char *key, float f);
|
||||
|
||||
/**
|
||||
* @brief Add the IPv4 address metrics
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of the metrics
|
||||
* @param[in] ip IPv4 address
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report_ipv4(const char *tag, const char *key, uint32_t ip);
|
||||
|
||||
/**
|
||||
* @brief Add the MAC address metrics
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of the metrics
|
||||
* @param[in] mac Array of length 6 i.e 6 octets of mac address
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report_mac(const char *tag, const char *key, uint8_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type string
|
||||
*
|
||||
* @param[in] tag Tag of metrics
|
||||
* @param[in] key Key of the metrics
|
||||
* @param[in] str Value of the metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_report_str(const char *tag, const char *key, const char *str);
|
||||
|
||||
#else /** APIs for older version of metadata for compatibility */
|
||||
|
||||
/**
|
||||
* @brief Unregister a diagnostics metrics
|
||||
*
|
||||
* Legacy version of metrics_unregister without `tag` parameter
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_unregister(const char *key);
|
||||
|
||||
/**
|
||||
* @brief Specify unit of the data for the particular key
|
||||
*
|
||||
* @param[in] key Key for which the unit to be specified
|
||||
* @param[in] unit Unit string of the data
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code othewise.
|
||||
*
|
||||
* @note this API if used, should be called after \ref esp_diag_metrics_register
|
||||
* API with the same `key` to take effect
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_unit(const char *key, const char *unit);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type `data_type`
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add(esp_diag_data_type_t data_type, const char *key,
|
||||
const void *val, size_t val_sz, uint64_t ts);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type bool
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report_bool but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_bool(const char *key, bool b);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type integer
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report_int but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_int(const char *key, int32_t i);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type unsigned integer
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report_uint but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_uint(const char *key, uint32_t u);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type float
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report_float but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_float(const char *key, float f);
|
||||
|
||||
/**
|
||||
* @brief Add the IPv4 address metrics
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report_ipv4 but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_ipv4(const char *key, uint32_t ip);
|
||||
|
||||
/**
|
||||
* @brief Add the MAC address metrics
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report_mac but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_mac(const char *key, uint8_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Add the metrics of data type string
|
||||
*
|
||||
* @note Same as \ref esp_diag_metrics_report_str but with legacy format
|
||||
*/
|
||||
esp_err_t esp_diag_metrics_add_str(const char *key, const char *str);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_DIAG_ENABLE_METRICS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_NETWORK_VARIABLES
|
||||
/**
|
||||
* @brief Initialize the network variables
|
||||
*
|
||||
* Below listed Wi-Fi and IP parameters are collected and reported to cloud on change.
|
||||
* Wi-Fi connection status, BSSID, SSID, channel, authentication mode,
|
||||
* Wi-Fi disconnection reason, IP address, netmask, and gateway.
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_network_variables_init(void);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the network variables
|
||||
*/
|
||||
esp_err_t esp_diag_network_variables_deinit(void);
|
||||
#endif /* CONFIG_DIAG_ENABLE_NETWORK_VARIABLES */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_HEAP_METRICS
|
||||
|
||||
/**
|
||||
* @brief Initialize the heap metrics
|
||||
*
|
||||
* Free heap, largest free block, and all time minimum free heap values are collected periodically.
|
||||
* Parameters are collected for RAM in internal memory and external memory (if device has PSRAM).
|
||||
*
|
||||
* The periodic interval is configurable through CONFIG_DIAG_HEAP_POLLING_INTERVAL Kconfig option.
|
||||
* Default is 30 seconds and can be changed with esp_diag_heap_metrics_reset_interval() at runtime.
|
||||
* Valid range is from 30 seconds to 24 hours (86400 seconds).
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_heap_metrics_init(void);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the heap metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_heap_metrics_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Reset the periodic interval
|
||||
*
|
||||
* By default, heap metrics are collected based on CONFIG_DIAG_HEAP_POLLING_INTERVAL Kconfig option.
|
||||
* This function can be used to change the interval at runtime.
|
||||
* If the interval is set to 0, heap metrics collection is disabled.
|
||||
*
|
||||
* @param[in] period Period interval in seconds
|
||||
*/
|
||||
void esp_diag_heap_metrics_reset_interval(uint32_t period);
|
||||
|
||||
/**
|
||||
* @brief Dumps the heap metrics and prints them to the console.
|
||||
*
|
||||
* This API collects and reports metrics value at any give point in time.
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_heap_metrics_dump(void);
|
||||
|
||||
#endif /* CONFIG_DIAG_ENABLE_HEAP_METRICS */
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_WIFI_METRICS
|
||||
|
||||
/**
|
||||
* @brief Initialize the wifi metrics
|
||||
*
|
||||
* Wi-Fi RSSI and minimum ever Wi-Fi RSSI values are collected periodically.
|
||||
* The periodic interval is configurable through CONFIG_DIAG_WIFI_POLLING_INTERVAL Kconfig option.
|
||||
* Default is 30 seconds and can be changed with esp_diag_wifi_metrics_reset_interval() at runtime.
|
||||
* Valid range is from 30 seconds to 24 hours (86400 seconds).
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_wifi_metrics_init(void);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the wifi metrics
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_wifi_metrics_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Dumps the wifi metrics and prints them to the console.
|
||||
*
|
||||
* This API can be used to collect wifi metrics at any given point in time.
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_wifi_metrics_dump(void);
|
||||
|
||||
/**
|
||||
* @brief Reset the periodic interval
|
||||
*
|
||||
* By default, wifi metrics are collected based on CONFIG_DIAG_WIFI_POLLING_INTERVAL Kconfig option.
|
||||
* This function can be used to change the interval at runtime.
|
||||
* If the interval is set to 0, wifi metrics collection is disabled.
|
||||
*
|
||||
* @param[in] period Period interval in seconds
|
||||
*/
|
||||
void esp_diag_wifi_metrics_reset_interval(uint32_t period);
|
||||
|
||||
#endif /* CONFIG_DIAG_ENABLE_WIFI_METRICS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_diagnostics.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_VARIABLES
|
||||
|
||||
/**
|
||||
* @brief Callback to write variable's data
|
||||
*
|
||||
* @param[in] tag Tag for variable
|
||||
* @param[in] data Data for variable
|
||||
* @param[in] len Length of variable
|
||||
* @param[in] cb_arg User data to pass in write callback
|
||||
*/
|
||||
typedef esp_err_t (*esp_diag_variable_write_cb_t)(const char *tag, void *data, size_t len, void *cb_arg);
|
||||
|
||||
/**
|
||||
* @brief Diagnostics variable config structure
|
||||
*/
|
||||
typedef struct {
|
||||
esp_diag_variable_write_cb_t write_cb; /*!< Callback function to write diagnostics data */
|
||||
void *cb_arg; /*!< User data to pass in callback function */
|
||||
} esp_diag_variable_config_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for diagnostics variable metadata
|
||||
*/
|
||||
typedef struct {
|
||||
const char *tag; /*!< Tag of variable */
|
||||
const char *key; /*!< Unique key for the variable */
|
||||
const char *label; /*!< Label for the variable */
|
||||
const char *path; /*!< Hierarchical path for the key, must be separated by '.' for more than one level,
|
||||
eg: "wifi", "heap.internal", "heap.external" */
|
||||
const char *unit; /*!< Data unit, can be NULL */
|
||||
esp_diag_data_type_t type; /*!< Data type of variables */
|
||||
} esp_diag_variable_meta_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the diagnostics variable
|
||||
*
|
||||
* @param[in] config Pointer to a config structure of type \ref esp_diag_variable_config_t
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_init(esp_diag_variable_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the diagnostics variables
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variables_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Register a diagnostics variable
|
||||
*
|
||||
* @param[in] tag Tag of variable
|
||||
* @param[in] key Unique key for the variable
|
||||
* @param[in] label Label for the variable
|
||||
* @param[in] path Hierarchical path for key, must be separated by '.' for more than one level
|
||||
* @param[in] type Data type of variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_register(const char *tag,
|
||||
const char *key,
|
||||
const char *label,
|
||||
const char *path,
|
||||
esp_diag_data_type_t type);
|
||||
|
||||
/**
|
||||
* @brief Unregister all previously registered variables
|
||||
*
|
||||
* @return ESP_OK if successful, qppropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_unregister_all(void);
|
||||
|
||||
/**
|
||||
* @brief Get metadata for all variables
|
||||
*
|
||||
* @param[out] len Length of the variables meta data array
|
||||
*
|
||||
* @return array Array of variables meta data
|
||||
*/
|
||||
const esp_diag_variable_meta_t *esp_diag_variable_meta_get_all(uint32_t *len);
|
||||
|
||||
/**
|
||||
* @brief Print metadata for all variables
|
||||
*/
|
||||
void esp_diag_variable_meta_print_all(void);
|
||||
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
|
||||
/**
|
||||
* @brief Unregister a diagnostics variable
|
||||
*
|
||||
* @param[in] tag Tag of variable
|
||||
* @param[in] key Key for the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_unregister(const char *tag, const char *key);
|
||||
|
||||
/**
|
||||
* @brief Specify unit of the data for the particular key
|
||||
*
|
||||
* @param[in] tag Tag of variable
|
||||
* @param[in] key Key for which the unit to be specified
|
||||
* @param[in] unit Unit string of the data
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code othewise.
|
||||
*
|
||||
* @note this API if used, should be called after \ref esp_diag_variable_register
|
||||
* API with the same `key` to take effect
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_unit(const char *tag, const char *key, const char *unit);
|
||||
|
||||
/**
|
||||
* @brief Add variable to storage
|
||||
*
|
||||
* @param[in] data_type Data type of variable \ref esp_diag_data_type_t
|
||||
* @param[in] key Key of variable
|
||||
* @param[in] val Value of variable
|
||||
* @param[in] val_sz Size of val
|
||||
* @param[in] ts Timestamp in microseconds, this should be the value at the time of data gathering
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*
|
||||
* @note \ref esp_diag_timestamp_get() API can be used to get timestamp in mircoseconds.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report(esp_diag_data_type_t data_type,
|
||||
const char *tag, const char *key, const void *val,
|
||||
size_t val_sz, uint64_t ts);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type boolean
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] b Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report_bool(const char *tag, const char *key, bool b);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type integer
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] i Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report_int(const char *tag, const char *key, int32_t i);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type unsigned integer
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] u Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report_uint(const char *tag, const char *key, uint32_t u);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type float
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] f Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report_float(const char *tag, const char *key, float f);
|
||||
|
||||
/**
|
||||
* @brief Add the IPv4 address variable
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] ip IPv4 address
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report_ipv4(const char *tag, const char *key, uint32_t ip);
|
||||
|
||||
/**
|
||||
* @brief Add the MAC address variable
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] mac Array of length 6 i.e 6 octets of mac address
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report_mac(const char *tag, const char *key, uint8_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type string
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] str Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_report_str(const char *tag, const char *key, const char *str);
|
||||
|
||||
#else /** APIs for older version of metadata for compatibility */
|
||||
|
||||
/**
|
||||
* @brief Unregister a diagnostics variable
|
||||
*
|
||||
* @param[in] key Key for the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_unregister(const char *key);
|
||||
|
||||
/**
|
||||
* @brief Specify unit of the data for the particular key
|
||||
*
|
||||
* @param[in] key Key for which the unit to be specified
|
||||
* @param[in] unit Unit string of the data
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code othewise.
|
||||
*
|
||||
* @note this API if used, should be called after \ref esp_diag_variable_register
|
||||
* API with the same `key` to take effect
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_unit(const char *key, const char *unit);
|
||||
|
||||
/**
|
||||
* @brief Add variable to storage
|
||||
*
|
||||
* @param[in] data_type Data type of variable \ref esp_diag_data_type_t
|
||||
* @param[in] tag Tag of variable
|
||||
* @param[in] key Key of variable
|
||||
* @param[in] val Value of variable
|
||||
* @param[in] val_sz Size of val
|
||||
* @param[in] ts Timestamp in microseconds, this should be the value at the time of data gathering
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*
|
||||
* @note \ref esp_diag_timestamp_get() API can be used to get timestamp in mircoseconds.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add(esp_diag_data_type_t data_type,
|
||||
const char *key, const void *val,
|
||||
size_t val_sz, uint64_t ts);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type boolean
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] b Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_bool(const char *key, bool b);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type integer
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] i Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_int(const char *key, int32_t i);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type unsigned integer
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] u Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_uint(const char *key, uint32_t u);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type float
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] f Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_float(const char *key, float f);
|
||||
|
||||
/**
|
||||
* @brief Add the IPv4 address variable
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] ip IPv4 address
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_ipv4(const char *key, uint32_t ip);
|
||||
|
||||
/**
|
||||
* @brief Add the MAC address variable
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] mac Array of length 6 i.e 6 octets of mac address
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_mac(const char *key, uint8_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Add the variable of data type string
|
||||
*
|
||||
* @param[in] key Key of the variable
|
||||
* @param[in] str Value of the variable
|
||||
*
|
||||
* @return ESP_OK if successful, appropriate error code otherwise.
|
||||
*/
|
||||
esp_err_t esp_diag_variable_add_str(const char *key, const char *str);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_DIAG_ENABLE_VARIABLES */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#include <esp_diagnostics.h>
|
||||
#include <esp_diagnostics_metrics.h>
|
||||
#include "esp_diagnostics_internal.h"
|
||||
|
||||
#define LOG_TAG "heap_metrics"
|
||||
#define METRICS_TAG "heap"
|
||||
#define METRICS_UNIT "bytes"
|
||||
|
||||
#define KEY_ALLOC_FAIL "alloc_fail"
|
||||
#define KEY_FREE "free"
|
||||
#define KEY_MIN_FREE "min_free_ever"
|
||||
#define KEY_LFB "lfb"
|
||||
#ifdef CONFIG_ESP32_SPIRAM_SUPPORT
|
||||
#define KEY_EXT_FREE "ext_free"
|
||||
#define KEY_EXT_LFB "ext_lfb"
|
||||
#define KEY_EXT_MIN_FREE "ext_min_free_ever"
|
||||
#endif /* CONFIG_ESP32_SPIRAM_SUPPORT */
|
||||
|
||||
#define PATH_HEAP_INTERNAL "heap.internal"
|
||||
#define PATH_HEAP_EXTERNAL "heap.external"
|
||||
|
||||
/* Use configurable polling interval with default fallback */
|
||||
#ifdef CONFIG_DIAG_HEAP_POLLING_INTERVAL
|
||||
#define DEFAULT_POLLING_INTERVAL CONFIG_DIAG_HEAP_POLLING_INTERVAL
|
||||
#else
|
||||
#define DEFAULT_POLLING_INTERVAL 30 /* 30 seconds */
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool init;
|
||||
TimerHandle_t handle;
|
||||
} heap_diag_priv_data_t;
|
||||
|
||||
static heap_diag_priv_data_t s_priv_data;
|
||||
|
||||
esp_err_t esp_diag_heap_metrics_dump(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
ESP_LOGW(LOG_TAG, "Heap metrics not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
uint32_t free = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
uint32_t lfb = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
|
||||
uint32_t min_free_ever = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_uint(METRICS_TAG, KEY_FREE, free), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_FREE);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_uint(METRICS_TAG, KEY_LFB, lfb), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_LFB);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_uint(METRICS_TAG, KEY_MIN_FREE, min_free_ever), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_MIN_FREE);
|
||||
|
||||
ESP_LOGI(LOG_TAG, KEY_FREE ":0x%" PRIx32 " " KEY_LFB ":0x%" PRIx32 " " KEY_MIN_FREE ":0x%" PRIx32, free, lfb, min_free_ever);
|
||||
#ifdef CONFIG_ESP32_SPIRAM_SUPPORT
|
||||
free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||||
lfb = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
|
||||
min_free_ever = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
|
||||
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_uint(METRICS_TAG, KEY_EXT_FREE, free), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_EXT_FREE);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_uint(METRICS_TAG, KEY_EXT_LFB, lfb), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_EXT_LFB);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_uint(METRICS_TAG, KEY_EXT_MIN_FREE, min_free_ever), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_EXT_MIN_FREE);
|
||||
|
||||
ESP_LOGI(LOG_TAG, KEY_EXT_FREE ":0x%" PRIx32 " " KEY_EXT_LFB ":0x%" PRIx32 " " KEY_EXT_MIN_FREE ":0x%" PRIx32, free, lfb, min_free_ever);
|
||||
#endif /* CONFIG_ESP32_SPIRAM_SUPPORT */
|
||||
#else
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_uint(KEY_FREE, free), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_FREE);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_uint(KEY_LFB, lfb), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_LFB);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_uint(KEY_MIN_FREE, min_free_ever), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_MIN_FREE);
|
||||
|
||||
ESP_LOGI(LOG_TAG, KEY_FREE ":0x%"PRIx32" " KEY_LFB ":0x%"PRIx32" " KEY_MIN_FREE ":0x%"PRIx32, free, lfb, min_free_ever);
|
||||
#ifdef CONFIG_ESP32_SPIRAM_SUPPORT
|
||||
free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||||
lfb = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
|
||||
min_free_ever = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
|
||||
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_uint(KEY_EXT_FREE, free), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_EXT_FREE);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_uint(KEY_EXT_LFB, lfb), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_EXT_LFB);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_uint(KEY_EXT_MIN_FREE, min_free_ever), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add heap metric key:" KEY_EXT_MIN_FREE);
|
||||
|
||||
ESP_LOGI(LOG_TAG, KEY_EXT_FREE ":0x%"PRIx32" " KEY_EXT_LFB ":0x%"PRIx32" " KEY_EXT_MIN_FREE ":0x%"PRIx32, free, lfb, min_free_ever);
|
||||
#endif /* CONFIG_ESP32_SPIRAM_SUPPORT */
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void heap_metrics_dump_cb(void *arg)
|
||||
{
|
||||
esp_diag_heap_metrics_dump();
|
||||
}
|
||||
|
||||
static void heap_timer_cb(TimerHandle_t handle)
|
||||
{
|
||||
esp_rmaker_work_queue_add_task(heap_metrics_dump_cb, NULL);
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
static void alloc_failed_hook(size_t size, uint32_t caps, const char *func)
|
||||
{
|
||||
esp_diag_heap_metrics_dump();
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_report_uint(METRICS_TAG, KEY_ALLOC_FAIL, size);
|
||||
#else
|
||||
esp_diag_metrics_add_uint(KEY_ALLOC_FAIL, size);
|
||||
#endif
|
||||
|
||||
ESP_DIAG_EVENT(METRICS_TAG, KEY_ALLOC_FAIL " size:0x%x func:%s", size, func);
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_diag_heap_metrics_init(void)
|
||||
{
|
||||
if (s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
esp_err_t err = heap_caps_register_failed_alloc_callback(alloc_failed_hook);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_ALLOC_FAIL, "Malloc fail", METRICS_TAG, ESP_DIAG_DATA_TYPE_UINT);
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_ALLOC_FAIL, METRICS_UNIT);
|
||||
#else
|
||||
esp_diag_metrics_add_unit(KEY_ALLOC_FAIL, METRICS_UNIT);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP32_SPIRAM_SUPPORT
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_EXT_FREE, "External free heap", PATH_HEAP_EXTERNAL, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_EXT_LFB, "External largest free block", PATH_HEAP_EXTERNAL, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_EXT_MIN_FREE, "External minimum free size", PATH_HEAP_EXTERNAL, ESP_DIAG_DATA_TYPE_UINT);
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_EXT_FREE, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_EXT_LFB, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_EXT_MIN_FREE, METRICS_UNIT);
|
||||
#else
|
||||
esp_diag_metrics_add_unit(KEY_EXT_FREE, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(KEY_EXT_LFB, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(KEY_EXT_MIN_FREE, METRICS_UNIT);
|
||||
#endif
|
||||
#endif /* CONFIG_ESP32_SPIRAM_SUPPORT */
|
||||
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_FREE, "Free heap", PATH_HEAP_INTERNAL, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_LFB, "Largest free block", PATH_HEAP_INTERNAL, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_MIN_FREE, "Minimum free size", PATH_HEAP_INTERNAL, ESP_DIAG_DATA_TYPE_UINT);
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_FREE, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_LFB, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_MIN_FREE, METRICS_UNIT);
|
||||
#else
|
||||
esp_diag_metrics_add_unit(KEY_FREE, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(KEY_LFB, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(KEY_MIN_FREE, METRICS_UNIT);
|
||||
#endif
|
||||
s_priv_data.handle = xTimerCreate("heap_metrics", SEC2TICKS(DEFAULT_POLLING_INTERVAL),
|
||||
pdTRUE, NULL, heap_timer_cb);
|
||||
if (s_priv_data.handle) {
|
||||
xTimerStart(s_priv_data.handle, 0);
|
||||
}
|
||||
s_priv_data.init = true;
|
||||
|
||||
// Dump metrics for the first time
|
||||
esp_diag_heap_metrics_dump();
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_heap_metrics_deinit(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
/* Try to delete timer with 10 ticks wait time */
|
||||
if (xTimerDelete(s_priv_data.handle, 10) == pdFALSE) {
|
||||
ESP_LOGW(LOG_TAG, "Failed to delete heap metric timer");
|
||||
}
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
esp_diag_metrics_unregister(KEY_ALLOC_FAIL);
|
||||
#endif
|
||||
#ifdef CONFIG_ESP32_SPIRAM_SUPPORT
|
||||
esp_diag_metrics_unregister(KEY_EXT_FREE);
|
||||
esp_diag_metrics_unregister(KEY_EXT_LFB);
|
||||
esp_diag_metrics_unregister(KEY_EXT_MIN_FREE);
|
||||
#endif
|
||||
esp_diag_metrics_unregister(KEY_FREE);
|
||||
esp_diag_metrics_unregister(KEY_LFB);
|
||||
esp_diag_metrics_unregister(KEY_MIN_FREE);
|
||||
#else
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_ALLOC_FAIL);
|
||||
#endif
|
||||
#ifdef CONFIG_ESP32_SPIRAM_SUPPORT
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_EXT_FREE);
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_EXT_LFB);
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_EXT_MIN_FREE);
|
||||
#endif
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_FREE);
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_LFB);
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_MIN_FREE);
|
||||
#endif
|
||||
memset(&s_priv_data, 0, sizeof(s_priv_data));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_diag_heap_metrics_reset_interval(uint32_t period)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return;
|
||||
}
|
||||
if (period == 0) {
|
||||
xTimerStop(s_priv_data.handle, 0);
|
||||
return;
|
||||
}
|
||||
xTimerChangePeriod(s_priv_data.handle, SEC2TICKS(period), 0);
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RET_ON_ERR_WITH_LOG(x, level, tag, format, ...) { \
|
||||
esp_err_t ret = (x); \
|
||||
if (ret != ESP_OK) { \
|
||||
ESP_LOG_LEVEL(level, tag, "%s", format, ##__VA_ARGS__); \
|
||||
return ret; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SEC2TICKS(s) ((s * 1000) / portTICK_PERIOD_MS)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_diagnostics.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
/* Onwards esp-idf v5.0 esp_cpu_process_stack_pc() is moved to
|
||||
* components/xtensa/include/esp_cpu_utils.h
|
||||
*/
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
#include "esp_cpu_utils.h"
|
||||
#else /* CONFIG_IDF_TARGET_ARCH_RISCV */
|
||||
#define esp_cpu_process_stack_pc(x) (x) // dummy definition to avoid compilation error
|
||||
#endif
|
||||
#else // For esp-idf version <= v4.4
|
||||
#include "soc/cpu.h"
|
||||
#endif
|
||||
|
||||
#define IS_LOG_TYPE_ENABLED(type) (s_priv_data.init && (type & s_priv_data.enabled_log_type))
|
||||
|
||||
typedef struct {
|
||||
uint32_t enabled_log_type;
|
||||
esp_diag_log_config_t config;
|
||||
bool init;
|
||||
} log_hook_priv_data_t;
|
||||
|
||||
static log_hook_priv_data_t s_priv_data;
|
||||
|
||||
#ifdef CONFIG_DIAG_LOG_MSG_ARG_FORMAT_TLV
|
||||
typedef enum {
|
||||
MOD_NONE, /* none */
|
||||
MOD_hh, /* char */
|
||||
MOD_h, /* short */
|
||||
MOD_l, /* long, NOTE: in case of double, this can be ignored */
|
||||
MOD_ll, /* long long */
|
||||
MOD_j, /* intmax_t or uintmax_t */
|
||||
MOD_t, /* ptrdiff_t */
|
||||
MOD_z, /* size_t */
|
||||
MOD_L, /* long double, NOTE: only for double */
|
||||
} modifiers_t;
|
||||
|
||||
static esp_err_t append_arg(uint8_t *args, uint8_t *out_size, uint8_t max_len,
|
||||
uint8_t type, uint8_t len, void *value)
|
||||
{
|
||||
if ((*out_size + 2 + len) > max_len) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
args += *out_size;
|
||||
*args++ = type;
|
||||
*args++ = len;
|
||||
memcpy(args, value, len);
|
||||
*out_size += (len + 2);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void get_tlv_from_ap(esp_diag_log_data_t *log, const char *format, va_list ap)
|
||||
{
|
||||
const char *p = NULL;
|
||||
uint8_t len, out_size = 0;
|
||||
uint8_t arg_max_len = sizeof(log->msg_args);
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_diag_arg_value_t arg_val;
|
||||
modifiers_t mf;
|
||||
|
||||
/* there can be flags, field width digits, precision digits, modifiers, specifiers
|
||||
* modifier tells the size of the field eg: hh, h, l, ll
|
||||
* specifier tells whether it is signed, unsigned, double, string, pointer, etc.
|
||||
* Following parsing is done by considering printf manual (man 3 printf)
|
||||
*/
|
||||
for (p = format; *p; p++) {
|
||||
if (*p == '%') {
|
||||
p++;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
/* skip zero or more flags */
|
||||
while (*p == '#' || *p == '0' || *p == '-' || *p == ' ' || *p == '+' || *p == '\'') {
|
||||
p++;
|
||||
}
|
||||
/* skip the field width digits */
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
p++;
|
||||
}
|
||||
/* skip precision bytes, period(.) followed by digits */
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
/* An optional length modifier, that specifies the size of the argument */
|
||||
len = 0;
|
||||
mf = MOD_NONE;
|
||||
while (*p) {
|
||||
bool _default = false;
|
||||
switch (*p) {
|
||||
case 'h':
|
||||
if (mf == MOD_h) {
|
||||
mf = MOD_hh;
|
||||
len = sizeof(char);
|
||||
} else {
|
||||
mf = MOD_h;
|
||||
len = sizeof(short);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (mf == MOD_l) {
|
||||
mf = MOD_ll;
|
||||
len = sizeof(long long);
|
||||
} else {
|
||||
mf = MOD_l;
|
||||
len = sizeof(long);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
mf = MOD_j;
|
||||
len = sizeof(intmax_t);
|
||||
break;
|
||||
case 't':
|
||||
mf = MOD_t;
|
||||
len = sizeof(ptrdiff_t);
|
||||
break;
|
||||
case 'z':
|
||||
mf = MOD_z;
|
||||
len = sizeof(size_t);
|
||||
break;
|
||||
case 'L':
|
||||
mf = MOD_L;
|
||||
len = sizeof(long double);
|
||||
break;
|
||||
default:
|
||||
_default = true;
|
||||
break;
|
||||
}
|
||||
if (_default) {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
/* specifier, character that specifies the type of conversion to be applied */
|
||||
memset(&arg_val, 0, sizeof(arg_val));
|
||||
switch (*p) {
|
||||
case 'D': /* equivalent to ld */
|
||||
arg_val.l = va_arg(ap, long);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_L, sizeof(long), &arg_val.l);
|
||||
break;
|
||||
case 'd':
|
||||
case 'i':
|
||||
switch (mf) {
|
||||
case MOD_NONE: /* none, no modifier found */
|
||||
case MOD_z: /* singed integer of size size_t */
|
||||
arg_val.i = va_arg(ap, int);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_INT, sizeof(int), &arg_val.i);
|
||||
break;
|
||||
case MOD_hh: /* char */
|
||||
arg_val.c = va_arg(ap, int); /* char is promoted to int */
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_CHAR, len, &arg_val.c);
|
||||
break;
|
||||
case MOD_h: /* short */
|
||||
arg_val.s = va_arg(ap, int); /* short is promoted to int */
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_SHORT, len, &arg_val.s);
|
||||
break;
|
||||
case MOD_l: /* long */
|
||||
arg_val.l = va_arg(ap, long);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_L, len, &arg_val.l);
|
||||
break;
|
||||
break;
|
||||
case MOD_ll: /* long long */
|
||||
arg_val.ll = va_arg(ap, long long);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_LL, len, &arg_val.ll);
|
||||
break;
|
||||
case MOD_j:
|
||||
/* intmax_t */
|
||||
arg_val.imx = va_arg(ap, intmax_t);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_INTMAX, len, &arg_val.imx);
|
||||
break;
|
||||
case MOD_t:
|
||||
/* ptrdiff_t */
|
||||
arg_val.ptrdiff = va_arg(ap, ptrdiff_t);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_PTRDIFF, len, &arg_val.ptrdiff);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'O': /* equivalent to lo */
|
||||
case 'U': /* equivalent to lu */
|
||||
arg_val.ul = va_arg(ap, unsigned long);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_ULL, sizeof(unsigned long), &arg_val.ul);
|
||||
break;
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'p':
|
||||
switch (mf) {
|
||||
case MOD_NONE: /* none, no modifier found */
|
||||
case MOD_t: /* unsigned type of size ptrdiff_t */
|
||||
len = sizeof(unsigned int);
|
||||
arg_val.u = va_arg(ap, unsigned int);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_UINT, len, &arg_val.u);
|
||||
break;
|
||||
case MOD_hh: /* unsinged char */
|
||||
arg_val.uc = va_arg(ap, unsigned int);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_UCHAR, len, &arg_val.uc);
|
||||
break;
|
||||
case MOD_h: /* unsigned short */
|
||||
arg_val.us = va_arg(ap, unsigned int);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_USHORT, len, &arg_val.us);
|
||||
break;
|
||||
case MOD_l: /* unsigned long */
|
||||
arg_val.ul = va_arg(ap, unsigned long);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_UL, len, &arg_val.ul);
|
||||
break;
|
||||
case MOD_ll: /* unsigned long long */
|
||||
arg_val.ull = va_arg(ap, unsigned long long);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_ULL, len, &arg_val.ull);
|
||||
break;
|
||||
case MOD_j: /* uintmax_t */
|
||||
arg_val.umx = va_arg(ap, uintmax_t);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_UINTMAX, len, &arg_val.umx);
|
||||
break;
|
||||
case MOD_z: /* size_t */
|
||||
arg_val.sz = va_arg(ap, size_t);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_SIZE, len, &arg_val.sz);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G':
|
||||
switch (mf) {
|
||||
case MOD_NONE: /* double */
|
||||
case MOD_l: /* double */
|
||||
len = sizeof(double);
|
||||
arg_val.d = va_arg(ap, double);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_DOUBLE, len, &arg_val.d);
|
||||
break;
|
||||
case MOD_L: /* long double */
|
||||
arg_val.ld = va_arg(ap, long double);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_LDOUBLE, len, &arg_val.ld);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'c': /* char */
|
||||
arg_val.c = va_arg(ap, int);
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_CHAR, sizeof(char), &arg_val.c);
|
||||
break;
|
||||
case 's': /* array of chars */
|
||||
arg_val.str = va_arg(ap, char *);
|
||||
if (arg_val.str) {
|
||||
len = strlen(arg_val.str);
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
err = append_arg(log->msg_args, &out_size, arg_max_len, ARG_TYPE_STR, len, arg_val.str);
|
||||
break;
|
||||
case 'n': /* %n outputs the number of bytes printed till that point, so will skip it */
|
||||
va_arg(ap, int);
|
||||
break;
|
||||
default:
|
||||
/* since we do not know the size or type of argument so, consuming the unsupported format specifier as integer. */
|
||||
va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
log->msg_args_len = out_size;
|
||||
}
|
||||
#endif /* CONFIG_DIAG_LOG_MSG_ARG_FORMAT_TLV */
|
||||
|
||||
static esp_err_t write_data(void *data, size_t len)
|
||||
{
|
||||
if (s_priv_data.config.write_cb) {
|
||||
return s_priv_data.config.write_cb(data, len, s_priv_data.config.cb_arg);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t diag_log_add(esp_diag_log_type_t type, uint32_t pc, const char *tag, const char *format, va_list args)
|
||||
{
|
||||
esp_diag_log_data_t log;
|
||||
va_list ap;
|
||||
char *task_name = NULL;
|
||||
|
||||
if (!IS_LOG_TYPE_ENABLED(type)) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
memset(&log, 0, sizeof(log));
|
||||
log.type = type;
|
||||
log.pc = pc;
|
||||
va_copy(ap, args);
|
||||
log.timestamp = esp_diag_timestamp_get();
|
||||
strlcpy(log.tag, tag, sizeof(log.tag));
|
||||
log.msg_ptr = (void *)format;
|
||||
log.msg_args_len = sizeof(log.msg_args);
|
||||
#ifdef CONFIG_DIAG_LOG_MSG_ARG_FORMAT_TLV
|
||||
get_tlv_from_ap(&log, format, ap);
|
||||
#else
|
||||
vsnprintf((char *)log.msg_args, log.msg_args_len, format, ap);
|
||||
log.msg_args_len = strlen((char *)log.msg_args);
|
||||
#endif
|
||||
va_end(ap);
|
||||
#if ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR < 3
|
||||
task_name = pcTaskGetTaskName(NULL);
|
||||
#else
|
||||
task_name = pcTaskGetName(NULL);
|
||||
#endif
|
||||
if (task_name) {
|
||||
strlcpy(log.task_name, task_name, sizeof(log.task_name));
|
||||
}
|
||||
return write_data(&log, sizeof(log));
|
||||
}
|
||||
|
||||
/**
|
||||
* If error logs are enabled via menuconfig, irrespective of if error logs are disabled
|
||||
* using `esp_log_level_set()`, error logs are still reported to Insights cloud
|
||||
*/
|
||||
static esp_err_t esp_diag_log_error(uint32_t pc, const char *tag, const char *format, va_list args)
|
||||
{
|
||||
return diag_log_add(ESP_DIAG_LOG_TYPE_ERROR, pc, tag, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* If warning logs are enabled via menuconfig, irrespective of if warning logs are disabled
|
||||
* using `esp_log_level_set()`, warning logs are still reported to Insights cloud
|
||||
*/
|
||||
static esp_err_t esp_diag_log_warning(uint32_t pc, const char *tag, const char *format, va_list args)
|
||||
{
|
||||
return diag_log_add(ESP_DIAG_LOG_TYPE_WARNING, pc, tag, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Events are reported irrespective of device logging level.
|
||||
*/
|
||||
esp_err_t esp_diag_log_event(const char *tag, const char *format, ...)
|
||||
{
|
||||
esp_err_t err;
|
||||
va_list args;
|
||||
uint32_t pc = esp_cpu_process_stack_pc((uint32_t)__builtin_return_address(0));
|
||||
va_start(args, format);
|
||||
err = diag_log_add(ESP_DIAG_LOG_TYPE_EVENT, pc, tag, format, args);
|
||||
va_end(args);
|
||||
return err;
|
||||
}
|
||||
|
||||
void esp_diag_log_hook_enable(uint32_t type)
|
||||
{
|
||||
s_priv_data.enabled_log_type |= type;
|
||||
}
|
||||
|
||||
void esp_diag_log_hook_disable(uint32_t type)
|
||||
{
|
||||
s_priv_data.enabled_log_type &= (~type);
|
||||
}
|
||||
|
||||
static void esp_diag_log(esp_log_level_t level, uint32_t pc, const char *tag, const char *format, va_list list)
|
||||
{
|
||||
if (level == ESP_LOG_ERROR) {
|
||||
esp_diag_log_error(pc, tag, format, list);
|
||||
} else if (level == ESP_LOG_WARN) {
|
||||
esp_diag_log_warning(pc, tag, format, list);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_log_hook_init(esp_diag_log_config_t *config)
|
||||
{
|
||||
if (!config && !config->write_cb) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_priv_data.init) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
memcpy(&s_priv_data.config, config, sizeof(esp_diag_log_config_t));
|
||||
s_priv_data.init = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIB_BUILDER_COMPILE
|
||||
extern int log_printfv(const char *format, va_list arg);
|
||||
|
||||
void __real_log_printf(const char *format, ...);
|
||||
|
||||
void __wrap_log_printf(const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
uint32_t pc = 0;
|
||||
pc = esp_cpu_process_stack_pc((uint32_t)__builtin_return_address(0));
|
||||
if (strlen(format) > 7 && format[6] == 'E') {
|
||||
esp_diag_log(ESP_LOG_ERROR, pc, "arduino-esp32", format, list);
|
||||
} else if (strlen(format) > 7 && format[6] == 'W') {
|
||||
esp_diag_log(ESP_LOG_WARN, pc, "arduino-esp32", format, list);
|
||||
}
|
||||
log_printfv(format, list);
|
||||
va_end(list);
|
||||
}
|
||||
#endif
|
||||
|
||||
void esp_diag_log_writev(esp_log_level_t level,
|
||||
const char *tag,
|
||||
const char *format,
|
||||
va_list args)
|
||||
{
|
||||
#ifndef CONFIG_DIAG_LOG_DROP_WIFI_LOGS
|
||||
/* Only collect logs with "wifi" tag */
|
||||
if (strcmp(tag, "wifi") == 0) {
|
||||
uint32_t pc = 0;
|
||||
pc = esp_cpu_process_stack_pc((uint32_t)__builtin_return_address(0));
|
||||
esp_diag_log(level, pc, tag, format, args);
|
||||
}
|
||||
#endif /* !CONFIG_DIAG_LOG_DROP_WIFI_LOGS */
|
||||
}
|
||||
|
||||
void esp_diag_log_write(esp_log_level_t level,
|
||||
const char *tag,
|
||||
const char *format,
|
||||
va_list list)
|
||||
{
|
||||
#ifndef BOOTLOADER_BUILD
|
||||
/* Logs with "wifi" tag, will be collected in esp_log_writev() */
|
||||
if (strcmp(tag, "wifi") != 0) {
|
||||
uint32_t pc = 0;
|
||||
pc = esp_cpu_process_stack_pc((uint32_t)__builtin_return_address(0));
|
||||
esp_diag_log(level, pc, tag, format, list);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP
|
||||
/* Wrapping esp_log_write() and esp_log_writev() reduces the
|
||||
* changes required in esp_log module to support diagnostics
|
||||
*/
|
||||
void __real_esp_log_writev(esp_log_level_t level,
|
||||
const char *tag,
|
||||
const char *format,
|
||||
va_list args);
|
||||
|
||||
void __wrap_esp_log_writev(esp_log_level_t level,
|
||||
const char *tag,
|
||||
const char *format,
|
||||
va_list args)
|
||||
{
|
||||
esp_diag_log_write(level, tag, format, args);
|
||||
__real_esp_log_writev(level, tag, format, args);
|
||||
}
|
||||
|
||||
void __wrap_esp_log_write(esp_log_level_t level,
|
||||
const char *tag,
|
||||
const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
esp_diag_log_writev(level, tag, format, list);
|
||||
esp_log_writev(level, tag, format, list);
|
||||
va_end(list);
|
||||
}
|
||||
#endif // CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP
|
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_diagnostics.h>
|
||||
#include <esp_diagnostics_metrics.h>
|
||||
|
||||
#define TAG "DIAG_METRICS"
|
||||
#define DIAG_METRICS_MAX_COUNT CONFIG_DIAG_METRICS_MAX_COUNT
|
||||
|
||||
/* Max supported string lenth */
|
||||
#define MAX_STR_LEN (sizeof(((esp_diag_str_data_pt_t *)0)->value.str) - 1)
|
||||
#define MAX_METRICS_WRITE_SZ sizeof(esp_diag_data_pt_t)
|
||||
#define MAX_STR_METRICS_WRITE_SZ sizeof(esp_diag_str_data_pt_t)
|
||||
|
||||
typedef struct {
|
||||
size_t metrics_count;
|
||||
esp_diag_metrics_meta_t metrics[DIAG_METRICS_MAX_COUNT];
|
||||
esp_diag_metrics_config_t config;
|
||||
bool init;
|
||||
} metrics_priv_data_t;
|
||||
|
||||
static metrics_priv_data_t s_priv_data;
|
||||
|
||||
static esp_diag_metrics_meta_t *esp_diag_metrics_meta_get(const char *tag, const char *key)
|
||||
{
|
||||
uint32_t i;
|
||||
if (!tag || !key) {
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < s_priv_data.metrics_count; i++) {
|
||||
if (s_priv_data.metrics[i].tag && s_priv_data.metrics[i].key &&
|
||||
(strcmp(s_priv_data.metrics[i].tag, tag) == 0) &&
|
||||
(strcmp(s_priv_data.metrics[i].key, key) == 0)) {
|
||||
return &s_priv_data.metrics[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
/* Checks only by key for registered metric. Use this for meta version < 1.1 */
|
||||
static esp_diag_metrics_meta_t *esp_diag_metrics_meta_get_by_key(const char *key)
|
||||
{
|
||||
uint32_t i;
|
||||
if (!key) {
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < s_priv_data.metrics_count; i++) {
|
||||
if (s_priv_data.metrics[i].key &&
|
||||
(strcmp(s_priv_data.metrics[i].key, key) == 0)) {
|
||||
return &s_priv_data.metrics[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool tag_key_present(const char *tag, const char *key)
|
||||
{
|
||||
return (esp_diag_metrics_meta_get(tag, key) != NULL);
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_register(const char *tag, const char *key,
|
||||
const char *label, const char *path,
|
||||
esp_diag_data_type_t type)
|
||||
{
|
||||
if (!tag || !key || !label || !path) {
|
||||
ESP_LOGE(TAG, "Failed to register metrics, tag, key, lable, or path is NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (s_priv_data.metrics_count >= DIAG_METRICS_MAX_COUNT) {
|
||||
ESP_LOGE(TAG, "No space left for more metrics");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (tag_key_present(tag, key)) {
|
||||
ESP_LOGE(TAG, "Metrics tag: %s key:%s exists", tag, key);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
s_priv_data.metrics[s_priv_data.metrics_count].tag = tag;
|
||||
s_priv_data.metrics[s_priv_data.metrics_count].key = key;
|
||||
s_priv_data.metrics[s_priv_data.metrics_count].label = label;
|
||||
s_priv_data.metrics[s_priv_data.metrics_count].unit = NULL;
|
||||
s_priv_data.metrics[s_priv_data.metrics_count].path = path;
|
||||
s_priv_data.metrics[s_priv_data.metrics_count].type = type;
|
||||
s_priv_data.metrics_count++;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_metrics_add_unit(const char *key, const char *unit)
|
||||
#else
|
||||
esp_err_t esp_diag_metrics_add_unit(const char *tag, const char *key, const char *unit)
|
||||
#endif
|
||||
{
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (!tag) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
if (!key) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_meta_t *metrics = esp_diag_metrics_meta_get_by_key(key);
|
||||
if (!metrics) {
|
||||
ESP_LOGI(TAG, "metrics with (key %s) not found", key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#else
|
||||
esp_diag_metrics_meta_t *metrics = esp_diag_metrics_meta_get(tag, key);
|
||||
if (!metrics) {
|
||||
ESP_LOGI(TAG, "metrics with (tag %s, key %s) not found", tag, key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
metrics->unit = unit;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_metrics_unregister(const char *key)
|
||||
#else
|
||||
esp_err_t esp_diag_metrics_unregister(const char *tag, const char *key)
|
||||
#endif
|
||||
{
|
||||
int i;
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (!tag) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
if (!key) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
for (i = 0; i < s_priv_data.metrics_count; i++) {
|
||||
if (s_priv_data.metrics[i].key &&
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
(strcmp(s_priv_data.metrics[i].tag, tag) == 0) &&
|
||||
#endif
|
||||
(strcmp(s_priv_data.metrics[i].key, key) == 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < s_priv_data.metrics_count) {
|
||||
s_priv_data.metrics[i] = s_priv_data.metrics[s_priv_data.metrics_count - 1];
|
||||
memset(&s_priv_data.metrics[s_priv_data.metrics_count - 1], 0, sizeof(esp_diag_metrics_meta_t));
|
||||
s_priv_data.metrics_count--;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_unregister_all(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
memset(&s_priv_data.metrics, 0, sizeof(s_priv_data.metrics));
|
||||
s_priv_data.metrics_count = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const esp_diag_metrics_meta_t *esp_diag_metrics_meta_get_all(uint32_t *len)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
*len = 0;
|
||||
return NULL;
|
||||
}
|
||||
*len = s_priv_data.metrics_count;
|
||||
return &s_priv_data.metrics[0];
|
||||
}
|
||||
|
||||
void esp_diag_metrics_meta_print_all(void)
|
||||
{
|
||||
uint32_t len;
|
||||
uint32_t i;
|
||||
const esp_diag_metrics_meta_t *meta = esp_diag_metrics_meta_get_all(&len);
|
||||
if (meta) {
|
||||
ESP_LOGI(TAG, "Tag\tKey\tLabel\tPath\tData type\n");
|
||||
for (i = 0; i < len; i++) {
|
||||
ESP_LOGI(TAG, "%s\t%s\t%s\t%s\t%d\n", meta[i].tag, meta[i].key, meta[i].label, meta[i].path, meta[i].type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_init(esp_diag_metrics_config_t *config)
|
||||
{
|
||||
if (!config || !config->write_cb) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
memcpy(&s_priv_data.config, config, sizeof(s_priv_data.config));
|
||||
s_priv_data.init = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_deinit(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
memset(&s_priv_data, 0, sizeof(s_priv_data));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_metrics_add(esp_diag_data_type_t data_type,
|
||||
#else
|
||||
esp_err_t esp_diag_metrics_report(esp_diag_data_type_t data_type, const char *tag,
|
||||
#endif
|
||||
const char *key, const void *val,
|
||||
size_t val_sz, uint64_t ts)
|
||||
{
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (!tag) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
if (!key || !val) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
const esp_diag_metrics_meta_t *metrics = esp_diag_metrics_meta_get_by_key(key);
|
||||
if (!metrics) {
|
||||
ESP_LOGI(TAG, "metrics with (key %s) not registered", key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#else
|
||||
const esp_diag_metrics_meta_t *metrics = esp_diag_metrics_meta_get(tag, key);
|
||||
if (!metrics) {
|
||||
ESP_LOGI(TAG, "metrics with (tag %s, key %s) not registered", tag, key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
if (metrics->type != data_type) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
size_t write_sz = MAX_METRICS_WRITE_SZ;
|
||||
if (metrics->type == ESP_DIAG_DATA_TYPE_STR) {
|
||||
write_sz = MAX_STR_METRICS_WRITE_SZ;
|
||||
}
|
||||
|
||||
esp_diag_str_data_pt_t data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.type = ESP_DIAG_DATA_PT_METRICS;
|
||||
data.data_type = data_type;
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
strlcpy(data.tag, tag, sizeof(data.tag));
|
||||
#endif
|
||||
strlcpy(data.key, key, sizeof(data.key));
|
||||
data.ts = ts;
|
||||
memcpy(&data.value, val, val_sz);
|
||||
|
||||
if (s_priv_data.config.write_cb) {
|
||||
return s_priv_data.config.write_cb(metrics->tag, &data, write_sz, s_priv_data.config.cb_arg);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_metrics_add_bool(const char *key, bool b)
|
||||
{
|
||||
return esp_diag_metrics_add(ESP_DIAG_DATA_TYPE_BOOL, key, &b, sizeof(b), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_add_int(const char *key, int32_t i)
|
||||
{
|
||||
return esp_diag_metrics_add(ESP_DIAG_DATA_TYPE_INT, key, &i, sizeof(i), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_add_uint(const char *key, uint32_t u)
|
||||
{
|
||||
return esp_diag_metrics_add(ESP_DIAG_DATA_TYPE_UINT, key, &u, sizeof(u), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_add_float(const char *key, float f)
|
||||
{
|
||||
return esp_diag_metrics_add(ESP_DIAG_DATA_TYPE_FLOAT, key, &f, sizeof(f), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_add_ipv4(const char *key, uint32_t ip)
|
||||
{
|
||||
return esp_diag_metrics_add(ESP_DIAG_DATA_TYPE_IPv4, key, &ip, sizeof(ip), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_add_mac(const char *key, uint8_t *mac)
|
||||
{
|
||||
return esp_diag_metrics_add(ESP_DIAG_DATA_TYPE_MAC, key, mac, 6, esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_add_str(const char *key, const char *str)
|
||||
{
|
||||
return esp_diag_metrics_add(ESP_DIAG_DATA_TYPE_STR, key, str, strlen(str), esp_diag_timestamp_get());
|
||||
}
|
||||
#else
|
||||
esp_err_t esp_diag_metrics_report_bool(const char *tag, const char *key, bool b)
|
||||
{
|
||||
return esp_diag_metrics_report(ESP_DIAG_DATA_TYPE_BOOL, tag, key, &b, sizeof(b), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_report_int(const char *tag, const char *key, int32_t i)
|
||||
{
|
||||
return esp_diag_metrics_report(ESP_DIAG_DATA_TYPE_INT, tag, key, &i, sizeof(i), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_report_uint(const char *tag, const char *key, uint32_t u)
|
||||
{
|
||||
return esp_diag_metrics_report(ESP_DIAG_DATA_TYPE_UINT, tag, key, &u, sizeof(u), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_report_float(const char *tag, const char *key, float f)
|
||||
{
|
||||
return esp_diag_metrics_report(ESP_DIAG_DATA_TYPE_FLOAT, tag, key, &f, sizeof(f), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_report_ipv4(const char *tag, const char *key, uint32_t ip)
|
||||
{
|
||||
return esp_diag_metrics_report(ESP_DIAG_DATA_TYPE_IPv4, tag, key, &ip, sizeof(ip), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_report_mac(const char *tag, const char *key, uint8_t *mac)
|
||||
{
|
||||
return esp_diag_metrics_report(ESP_DIAG_DATA_TYPE_MAC, tag, key, mac, 6, esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_metrics_report_str(const char *tag, const char *key, const char *str)
|
||||
{
|
||||
return esp_diag_metrics_report(ESP_DIAG_DATA_TYPE_STR, tag, key, str, strlen(str), esp_diag_timestamp_get());
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_netif_ip_addr.h>
|
||||
#include <esp_diagnostics_variables.h>
|
||||
|
||||
#define TAG_IP "ip"
|
||||
#define TAG_WIFI "wifi"
|
||||
|
||||
/* Wifi keys */
|
||||
#define KEY_SSID "ssid"
|
||||
#define KEY_BSSID "bssid"
|
||||
#define KEY_CHANNEL "channel"
|
||||
#define KEY_AUTHMODE "auth"
|
||||
#define KEY_REASON "reason"
|
||||
#define KEY_DISC_CNT "disconn_cnt"
|
||||
|
||||
/* Optional WiFi var keys */
|
||||
#define KEY_PROTOCOL "protocol"
|
||||
#define KEY_BANDWIDTH "bandwidth"
|
||||
#define KEY_PROTOCOL_AP "protocol_ap"
|
||||
#define KEY_BANDWIDTH_AP "bandwidth_ap"
|
||||
#define KEY_POWER_SAVE "power_save"
|
||||
#define KEY_SECOND_CH "second_ch"
|
||||
|
||||
/* IP keys */
|
||||
#define KEY_IPv4 "ipv4"
|
||||
#define KEY_NETMASK "netmask"
|
||||
#define KEY_GATEWAY "gw"
|
||||
|
||||
#define PATH_WIFI_STATION "Wi-Fi.Station"
|
||||
#define PATH_IP_STATION "IP.Station"
|
||||
#define PATH_WIFI_AP "Wi-Fi.AP"
|
||||
|
||||
typedef struct {
|
||||
wifi_event_sta_connected_t prev_sta_data;
|
||||
bool wifi_connected;
|
||||
int32_t disconn_cnt;
|
||||
bool init;
|
||||
} priv_data_t;
|
||||
|
||||
static priv_data_t s_priv_data;
|
||||
|
||||
static bool bssid_matched(uint8_t *bssid1, uint8_t *bssid2)
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (bssid1[i] != bssid2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if CONFIG_DIAG_MORE_NETWORK_VARS
|
||||
static void diag_add_more_wifi_vars()
|
||||
{
|
||||
uint8_t wifi_protocol = 0;
|
||||
wifi_bandwidth_t wifi_bw = WIFI_BW_HT20;
|
||||
wifi_ps_type_t wifi_ps_type = WIFI_PS_NONE;
|
||||
esp_wifi_get_protocol(WIFI_IF_STA, &wifi_protocol);
|
||||
esp_wifi_get_bandwidth(WIFI_IF_STA, &wifi_bw);
|
||||
esp_wifi_get_ps(&wifi_ps_type);
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_uint(KEY_PROTOCOL, wifi_protocol);
|
||||
esp_diag_variable_add_uint(KEY_BANDWIDTH, wifi_bw);
|
||||
esp_diag_variable_add_uint(KEY_POWER_SAVE, wifi_ps_type);
|
||||
if (wifi_bw == WIFI_BW_HT40) {
|
||||
// not very useful for now, but will be useful for 5GHz band
|
||||
uint8_t primary_ch = 0;
|
||||
wifi_second_chan_t secondary_ch = WIFI_SECOND_CHAN_NONE;
|
||||
esp_wifi_get_channel(&primary_ch, &secondary_ch);
|
||||
esp_diag_variable_add_uint(KEY_SECOND_CH, secondary_ch);
|
||||
}
|
||||
esp_wifi_get_protocol(WIFI_IF_AP, &wifi_protocol);
|
||||
esp_wifi_get_bandwidth(WIFI_IF_AP, &wifi_bw);
|
||||
esp_diag_variable_add_uint(KEY_PROTOCOL_AP, wifi_protocol);
|
||||
esp_diag_variable_add_uint(KEY_BANDWIDTH_AP, wifi_bw);
|
||||
#else
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_PROTOCOL, wifi_protocol);
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_BANDWIDTH, wifi_bw);
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_POWER_SAVE, wifi_ps_type);
|
||||
if (wifi_bw == WIFI_BW_HT40) {
|
||||
// not very useful for now, but will be useful for 5GHz band
|
||||
uint8_t primary_ch = 0;
|
||||
wifi_second_chan_t secondary_ch = WIFI_SECOND_CHAN_NONE;
|
||||
esp_wifi_get_channel(&primary_ch, &secondary_ch);
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_SECOND_CH, secondary_ch);
|
||||
}
|
||||
esp_wifi_get_protocol(WIFI_IF_AP, &wifi_protocol);
|
||||
esp_wifi_get_bandwidth(WIFI_IF_AP, &wifi_bw);
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_PROTOCOL_AP, wifi_protocol);
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_BANDWIDTH_AP, wifi_bw);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void diag_register_wifi_vars()
|
||||
{
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_SSID, "SSID", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_STR);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_BSSID, "BSSID", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_MAC);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_CHANNEL, "Channel", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_INT);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_AUTHMODE, "Auth Mode", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_DISC_CNT, "Disconnect count since last reboot", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_INT);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_REASON, "Last Wi-Fi disconnect reason", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_INT);
|
||||
}
|
||||
|
||||
#if CONFIG_DIAG_MORE_NETWORK_VARS
|
||||
static void diag_register_more_wifi_vars()
|
||||
{
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_PROTOCOL, "Protocol", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_BANDWIDTH, "Bandwidth", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_POWER_SAVE, "Power Save", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_SECOND_CH, "Secondary Channel", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_UINT);
|
||||
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_PROTOCOL_AP, "Protocol", PATH_WIFI_AP, ESP_DIAG_DATA_TYPE_UINT);
|
||||
esp_diag_variable_register(TAG_WIFI, KEY_BANDWIDTH_AP, "Bandwidth", PATH_WIFI_AP, ESP_DIAG_DATA_TYPE_UINT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void evt_handler(void *arg, esp_event_base_t evt_base, int32_t evt_id, void *evt_data)
|
||||
{
|
||||
if (evt_base == WIFI_EVENT) {
|
||||
switch (evt_id) {
|
||||
case WIFI_EVENT_STA_CONNECTED:
|
||||
{
|
||||
s_priv_data.wifi_connected = true;
|
||||
wifi_event_sta_connected_t *data = evt_data;
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (strncmp((char *)s_priv_data.prev_sta_data.ssid, (char *)data->ssid, data->ssid_len) != 0) {
|
||||
esp_diag_variable_add_str(KEY_SSID, (char *)data->ssid);
|
||||
}
|
||||
if (!bssid_matched(s_priv_data.prev_sta_data.bssid, data->bssid)) {
|
||||
esp_diag_variable_add_mac(KEY_BSSID, data->bssid);
|
||||
}
|
||||
if (s_priv_data.prev_sta_data.channel != data->channel) {
|
||||
esp_diag_variable_add_int(KEY_CHANNEL, data->channel);
|
||||
}
|
||||
if (s_priv_data.prev_sta_data.authmode != data->authmode) {
|
||||
esp_diag_variable_add_uint(KEY_AUTHMODE, data->authmode);
|
||||
}
|
||||
#else
|
||||
if (strncmp((char *)s_priv_data.prev_sta_data.ssid, (char *)data->ssid, data->ssid_len) != 0) {
|
||||
esp_diag_variable_report_str(TAG_WIFI, KEY_SSID, (char *)data->ssid);
|
||||
}
|
||||
if (!bssid_matched(s_priv_data.prev_sta_data.bssid, data->bssid)) {
|
||||
esp_diag_variable_report_mac(TAG_WIFI, KEY_BSSID, data->bssid);
|
||||
}
|
||||
if (s_priv_data.prev_sta_data.channel != data->channel) {
|
||||
esp_diag_variable_report_int(TAG_WIFI, KEY_CHANNEL, data->channel);
|
||||
}
|
||||
if (s_priv_data.prev_sta_data.authmode != data->authmode) {
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_AUTHMODE, data->authmode);
|
||||
}
|
||||
#endif
|
||||
memcpy(&s_priv_data.prev_sta_data, data, sizeof(s_priv_data.prev_sta_data));
|
||||
break;
|
||||
}
|
||||
case WIFI_EVENT_STA_DISCONNECTED:
|
||||
{
|
||||
if (s_priv_data.wifi_connected) {
|
||||
s_priv_data.wifi_connected = false;
|
||||
wifi_event_sta_disconnected_t *data = evt_data;
|
||||
s_priv_data.disconn_cnt++;
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_int(KEY_REASON, data->reason);
|
||||
esp_diag_variable_add_int(KEY_DISC_CNT, s_priv_data.disconn_cnt);
|
||||
#else
|
||||
esp_diag_variable_report_int(TAG_WIFI, KEY_REASON, data->reason);
|
||||
esp_diag_variable_report_int(TAG_WIFI, KEY_DISC_CNT, s_priv_data.disconn_cnt);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WIFI_EVENT_STA_AUTHMODE_CHANGE:
|
||||
{
|
||||
wifi_event_sta_authmode_change_t *data = evt_data;
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_uint(KEY_AUTHMODE, data->new_mode);
|
||||
#else
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_AUTHMODE, data->new_mode);
|
||||
#endif
|
||||
s_priv_data.prev_sta_data.authmode = data->new_mode;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (evt_base == IP_EVENT) {
|
||||
switch(evt_id) {
|
||||
case IP_EVENT_STA_GOT_IP:
|
||||
{
|
||||
ip_event_got_ip_t *data = evt_data;
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_ipv4(KEY_IPv4, data->ip_info.ip.addr);
|
||||
esp_diag_variable_add_ipv4(KEY_NETMASK, data->ip_info.netmask.addr);
|
||||
esp_diag_variable_add_ipv4(KEY_GATEWAY, data->ip_info.gw.addr);
|
||||
#else
|
||||
esp_diag_variable_report_ipv4(TAG_IP, KEY_IPv4, data->ip_info.ip.addr);
|
||||
esp_diag_variable_report_ipv4(TAG_IP, KEY_NETMASK, data->ip_info.netmask.addr);
|
||||
esp_diag_variable_report_ipv4(TAG_IP, KEY_GATEWAY, data->ip_info.gw.addr);
|
||||
#endif
|
||||
#if CONFIG_DIAG_MORE_NETWORK_VARS
|
||||
diag_add_more_wifi_vars();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case IP_EVENT_STA_LOST_IP:
|
||||
{
|
||||
uint32_t ip = 0x0;
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_ipv4(KEY_IPv4, ip);
|
||||
#else
|
||||
esp_diag_variable_report_ipv4(TAG_IP, KEY_IPv4, ip);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_network_variables_init(void)
|
||||
{
|
||||
wifi_ap_record_t ap_info;
|
||||
esp_netif_ip_info_t ip_info;
|
||||
|
||||
if (s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
/* Register the event handler for wifi events */
|
||||
esp_err_t err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, evt_handler, NULL);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
/* Register the event handler for ip events */
|
||||
err = esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, evt_handler, NULL);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* wifi variables */
|
||||
diag_register_wifi_vars();
|
||||
#if CONFIG_DIAG_MORE_NETWORK_VARS
|
||||
diag_register_more_wifi_vars();
|
||||
#endif
|
||||
|
||||
/* IP address variables */
|
||||
esp_diag_variable_register(TAG_IP, KEY_IPv4, "IPv4", PATH_IP_STATION, ESP_DIAG_DATA_TYPE_IPv4);
|
||||
esp_diag_variable_register(TAG_IP, KEY_NETMASK, "Netmask", PATH_IP_STATION, ESP_DIAG_DATA_TYPE_IPv4);
|
||||
esp_diag_variable_register(TAG_IP, KEY_GATEWAY, "Gateway", PATH_IP_STATION, ESP_DIAG_DATA_TYPE_IPv4);
|
||||
|
||||
memset(&s_priv_data.prev_sta_data, 0, sizeof(s_priv_data.prev_sta_data));
|
||||
/* If wifi is not connected then wifi details are recorded in event handler */
|
||||
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
|
||||
/* If wifi is connected then record the wifi details */
|
||||
memcpy(s_priv_data.prev_sta_data.ssid, ap_info.ssid, sizeof(s_priv_data.prev_sta_data.ssid));
|
||||
memcpy(s_priv_data.prev_sta_data.bssid, ap_info.bssid, sizeof(s_priv_data.prev_sta_data.bssid));
|
||||
s_priv_data.prev_sta_data.channel = ap_info.primary;
|
||||
s_priv_data.prev_sta_data.authmode = ap_info.authmode;
|
||||
s_priv_data.wifi_connected = true;
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_str(KEY_SSID, (char *)ap_info.ssid);
|
||||
esp_diag_variable_add_mac(KEY_BSSID, ap_info.bssid);
|
||||
esp_diag_variable_add_int(KEY_CHANNEL, ap_info.primary);
|
||||
esp_diag_variable_add_uint(KEY_AUTHMODE, ap_info.authmode);
|
||||
#else
|
||||
esp_diag_variable_report_str(TAG_WIFI, KEY_SSID, (char *)ap_info.ssid);
|
||||
esp_diag_variable_report_mac(TAG_WIFI, KEY_BSSID, ap_info.bssid);
|
||||
esp_diag_variable_report_int(TAG_WIFI, KEY_CHANNEL, ap_info.primary);
|
||||
esp_diag_variable_report_uint(TAG_WIFI, KEY_AUTHMODE, ap_info.authmode);
|
||||
#endif
|
||||
}
|
||||
|
||||
memset(&ip_info, 0, sizeof(ip_info));
|
||||
/* If wifi interface is up and running then record the details */
|
||||
if (esp_netif_is_netif_up(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"))
|
||||
&& esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info) == ESP_OK) {
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_ipv4(KEY_IPv4, ip_info.ip.addr);
|
||||
esp_diag_variable_add_ipv4(KEY_NETMASK, ip_info.netmask.addr);
|
||||
esp_diag_variable_add_ipv4(KEY_GATEWAY, ip_info.gw.addr);
|
||||
#else
|
||||
esp_diag_variable_report_ipv4(TAG_IP, KEY_IPv4, ip_info.ip.addr);
|
||||
esp_diag_variable_report_ipv4(TAG_IP, KEY_NETMASK, ip_info.netmask.addr);
|
||||
esp_diag_variable_report_ipv4(TAG_IP, KEY_GATEWAY, ip_info.gw.addr);
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_add_int(KEY_DISC_CNT, s_priv_data.disconn_cnt);
|
||||
#else
|
||||
esp_diag_variable_report_int(TAG_WIFI, KEY_DISC_CNT, s_priv_data.disconn_cnt);
|
||||
#endif
|
||||
s_priv_data.init = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_network_variables_deinit(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, evt_handler);
|
||||
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, evt_handler);
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_unregister(KEY_SSID);
|
||||
esp_diag_variable_unregister(KEY_BSSID);
|
||||
esp_diag_variable_unregister(KEY_CHANNEL);
|
||||
esp_diag_variable_unregister(KEY_AUTHMODE);
|
||||
esp_diag_variable_unregister(KEY_REASON);
|
||||
esp_diag_variable_unregister(KEY_DISC_CNT);
|
||||
esp_diag_variable_unregister(KEY_IPv4);
|
||||
esp_diag_variable_unregister(KEY_NETMASK);
|
||||
esp_diag_variable_unregister(KEY_GATEWAY);
|
||||
#if CONFIG_DIAG_MORE_NETWORK_VARS
|
||||
esp_diag_variable_unregister(KEY_PROTOCOL);
|
||||
esp_diag_variable_unregister(KEY_BANDWIDTH);
|
||||
esp_diag_variable_unregister(KEY_POWER_SAVE);
|
||||
esp_diag_variable_unregister(KEY_SECOND_CH);
|
||||
esp_diag_variable_unregister(KEY_PROTOCOL_AP);
|
||||
esp_diag_variable_unregister(KEY_BANDWIDTH_AP);
|
||||
#endif
|
||||
#else
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_SSID);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_BSSID);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_CHANNEL);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_AUTHMODE);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_REASON);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_DISC_CNT);
|
||||
esp_diag_variable_unregister(TAG_IP, KEY_IPv4);
|
||||
esp_diag_variable_unregister(TAG_IP, KEY_NETMASK);
|
||||
esp_diag_variable_unregister(TAG_IP, KEY_GATEWAY);
|
||||
#if CONFIG_DIAG_MORE_NETWORK_VARS
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_PROTOCOL);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_BANDWIDTH);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_POWER_SAVE);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_SECOND_CH);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_PROTOCOL_AP);
|
||||
esp_diag_variable_unregister(TAG_WIFI, KEY_BANDWIDTH_AP);
|
||||
#endif
|
||||
#endif
|
||||
memset(&s_priv_data, 0, sizeof(s_priv_data));
|
||||
return ESP_OK;
|
||||
}
|
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_partition.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_diagnostics.h"
|
||||
#include "esp_log.h"
|
||||
#include "sys/time.h"
|
||||
#include "esp_system.h"
|
||||
#include <esp_crc.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
#include "esp_debug_helpers.h"
|
||||
#include "esp_diagnostics_metrics.h"
|
||||
#include "esp_diagnostics_variables.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "esp_chip_info.h"
|
||||
#else
|
||||
#include "esp_ota_ops.h"
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
#include <esp_rom_crc.h>
|
||||
#define ESP_CRC32_LE(crc, buf, len) esp_rom_crc32_le(crc, buf, len)
|
||||
#define TASK_GET_NAME(handle) pcTaskGetName(handle)
|
||||
#else
|
||||
#include <esp_crc.h>
|
||||
#define ESP_CRC32_LE(crc, buf, len) esp_crc32_le(crc, buf, len)
|
||||
#define TASK_GET_NAME(handle) pcTaskGetTaskName(handle)
|
||||
#endif
|
||||
|
||||
#define TASK_SNAP_TAG "task_snap"
|
||||
#define TASK_INFO_FMT "task:%s state:%" PRIu32 " high_watermark:%" PRIu32 " "
|
||||
#define BT_FMT "bt:"
|
||||
#define BT_DEPTH_FMT_1 "0x%" PRIx32 " "
|
||||
#define BT_DEPTH_FMT_2 "0x%" PRIx32 " 0x%" PRIx32 " "
|
||||
#define BT_DEPTH_FMT_3 "0x%" PRIx32 " 0x%" PRIx32 " 0x%" PRIx32 " "
|
||||
#define BT_DEPTH_FMT_4 "0x%" PRIx32 " 0x%" PRIx32 " 0x%" PRIx32 " 0x%" PRIx32 " "
|
||||
#define BT_DEPTH_FMT_8 BT_DEPTH_FMT_4 BT_DEPTH_FMT_4
|
||||
#define BT_DEPTH_FMT_16 BT_DEPTH_FMT_8 BT_DEPTH_FMT_8
|
||||
|
||||
/* From esp-idf v4.4 onwards
|
||||
* - portENTER_CRITICAL_NESTED() and portEXIT_CRITICAL_NESTED() macros are deprecated
|
||||
* - freertos/task_snapshot.h has been removed from freertos/task.h
|
||||
*/
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
#include "freertos/task_snapshot.h"
|
||||
#define DISABLE_INTERRUPTS portSET_INTERRUPT_MASK_FROM_ISR
|
||||
#define ENABLE_INTERRUPTS portCLEAR_INTERRUPT_MASK_FROM_ISR
|
||||
#else
|
||||
#define DISABLE_INTERRUPTS portENTER_CRITICAL_NESTED
|
||||
#define ENABLE_INTERRUPTS portEXIT_CRITICAL_NESTED
|
||||
#endif
|
||||
|
||||
/* ESP-IDF v5.0 changed some existing APIs and moved some to other header files
|
||||
*/
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include <esp_app_desc.h>
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
#include <esp_cpu_utils.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
esp_err_t esp_diag_device_info_get(esp_diag_device_info_t *device_info)
|
||||
{
|
||||
esp_chip_info_t chip;
|
||||
const esp_app_desc_t *app_desc;
|
||||
|
||||
if (!device_info) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_chip_info(&chip);
|
||||
device_info->chip_model = chip.model;
|
||||
device_info->chip_rev = chip.revision;
|
||||
device_info->reset_reason = esp_reset_reason();
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
app_desc = esp_app_get_description();
|
||||
const uint8_t* src = app_desc->app_elf_sha256;
|
||||
memcpy((uint8_t *)device_info->app_elf_sha256, src, DIAG_SHA_SIZE);
|
||||
#else
|
||||
app_desc = esp_ota_get_app_description();
|
||||
esp_ota_get_app_elf_sha256(device_info->app_elf_sha256, sizeof(device_info->app_elf_sha256));
|
||||
#endif
|
||||
strlcpy(device_info->app_version, app_desc->version, sizeof(device_info->app_version));
|
||||
strlcpy(device_info->project_name, app_desc->project_name, sizeof(device_info->project_name));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint64_t esp_diag_timestamp_get(void)
|
||||
{
|
||||
struct timeval tv_now;
|
||||
if (gettimeofday(&tv_now, NULL) == 0) {
|
||||
return ((uint64_t)tv_now.tv_sec * 1000000L + (uint64_t)tv_now.tv_usec);
|
||||
} else {
|
||||
/* esp_log_timestamp returns timestamp in milliseconds */
|
||||
return ((uint64_t)esp_log_timestamp() * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
static void diag_task_get_bt(esp_diag_task_bt_t *bt_info, XtExcFrame *stack)
|
||||
{
|
||||
esp_backtrace_frame_t frame = {
|
||||
.pc = stack->pc,
|
||||
.sp = stack->a1,
|
||||
.next_pc = stack->a0,
|
||||
};
|
||||
uint32_t i = 0;
|
||||
uint32_t max_depth = sizeof(bt_info->bt) / sizeof(bt_info->bt[0]);
|
||||
bool corrupted = !(esp_stack_ptr_is_sane(frame.sp) &&
|
||||
esp_ptr_executable((void *)esp_cpu_process_stack_pc(frame.pc)));
|
||||
|
||||
bt_info->bt[i++] = esp_cpu_process_stack_pc(frame.pc);
|
||||
while (max_depth-- > 0 && frame.next_pc && !corrupted) {
|
||||
if (!esp_backtrace_get_next_frame(&frame)) {
|
||||
corrupted = true;
|
||||
}
|
||||
if (corrupted == false) {
|
||||
bt_info->bt[i++] = esp_cpu_process_stack_pc(frame.pc);
|
||||
}
|
||||
}
|
||||
bt_info->depth = i;
|
||||
bt_info->corrupted = corrupted;
|
||||
}
|
||||
#endif /* !CONFIG_IDF_TARGET_ARCH_RISCV */
|
||||
|
||||
uint32_t esp_diag_task_snapshot_get(esp_diag_task_info_t *tasks, size_t size)
|
||||
{
|
||||
if (!tasks || !size) {
|
||||
return 0;
|
||||
}
|
||||
unsigned irq_state = DISABLE_INTERRUPTS();
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
int other_cpu = xPortGetCoreID() ? 0 : 1;
|
||||
esp_cpu_stall(other_cpu);
|
||||
#endif
|
||||
|
||||
uint32_t i = 0;
|
||||
uint32_t task_count = uxTaskGetNumberOfTasks();
|
||||
TaskSnapshot_t *snapshots = calloc(task_count, sizeof(TaskSnapshot_t));
|
||||
if (!snapshots) {
|
||||
return 0;
|
||||
}
|
||||
size_t tcb_size; /* unused */
|
||||
uint32_t count = uxTaskGetSnapshotAll(snapshots, task_count, &tcb_size);
|
||||
if (count == 0) {
|
||||
free(snapshots);
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
TaskHandle_t handle = (TaskHandle_t)snapshots[i].pxTCB;
|
||||
char *name = TASK_GET_NAME(handle);
|
||||
if (name && *name) {
|
||||
strlcpy(tasks[i].name, name, sizeof(tasks[i].name));
|
||||
}
|
||||
tasks[i].state = eTaskGetState(handle);
|
||||
tasks[i].high_watermark = uxTaskGetStackHighWaterMark(handle);
|
||||
#ifndef CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
diag_task_get_bt(&tasks[i].bt_info, (XtExcFrame *)snapshots[i].pxTopOfStack);
|
||||
#endif /* !CONFIG_IDF_TARGET_ARCH_RISCV */
|
||||
}
|
||||
free(snapshots);
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
esp_cpu_unstall(other_cpu);
|
||||
#endif
|
||||
ENABLE_INTERRUPTS(irq_state);
|
||||
return i;
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
static void print_task_info(esp_diag_task_info_t *task)
|
||||
{
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT, task->name, task->state, task->high_watermark);
|
||||
}
|
||||
#else
|
||||
static void print_task_info(esp_diag_task_info_t *task)
|
||||
{
|
||||
switch(task->bt_info.depth) {
|
||||
case 1:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_1,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0]);
|
||||
break;
|
||||
case 2:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_2,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1]);
|
||||
break;
|
||||
case 3:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_3,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2]);
|
||||
break;
|
||||
case 4:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_4,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3]);
|
||||
break;
|
||||
case 5:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_4 BT_DEPTH_FMT_1,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4]);
|
||||
break;
|
||||
case 6:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_4 BT_DEPTH_FMT_2,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5]);
|
||||
break;
|
||||
case 7:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_4 BT_DEPTH_FMT_3,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6]);
|
||||
break;
|
||||
case 8:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7]);
|
||||
break;
|
||||
case 9:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8 BT_DEPTH_FMT_1,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8]);
|
||||
break;
|
||||
case 10:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8 BT_DEPTH_FMT_2,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8], task->bt_info.bt[9]);
|
||||
break;
|
||||
case 11:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8 BT_DEPTH_FMT_3,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8], task->bt_info.bt[9], task->bt_info.bt[10]);
|
||||
break;
|
||||
case 12:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8 BT_DEPTH_FMT_4,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8], task->bt_info.bt[9], task->bt_info.bt[10], task->bt_info.bt[11]);
|
||||
break;
|
||||
case 13:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8 BT_DEPTH_FMT_4 BT_DEPTH_FMT_1,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8], task->bt_info.bt[9], task->bt_info.bt[10], task->bt_info.bt[11],
|
||||
task->bt_info.bt[12]);
|
||||
break;
|
||||
case 14:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8 BT_DEPTH_FMT_4 BT_DEPTH_FMT_2,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8], task->bt_info.bt[9], task->bt_info.bt[10], task->bt_info.bt[11],
|
||||
task->bt_info.bt[12], task->bt_info.bt[13]);
|
||||
break;
|
||||
case 15:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_8 BT_DEPTH_FMT_4 BT_DEPTH_FMT_3,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8], task->bt_info.bt[9], task->bt_info.bt[10], task->bt_info.bt[11],
|
||||
task->bt_info.bt[12], task->bt_info.bt[13], task->bt_info.bt[14]);
|
||||
break;
|
||||
case 16:
|
||||
ESP_DIAG_EVENT(TASK_SNAP_TAG, TASK_INFO_FMT BT_FMT BT_DEPTH_FMT_16,
|
||||
task->name, task->state, task->high_watermark,
|
||||
task->bt_info.bt[0], task->bt_info.bt[1], task->bt_info.bt[2], task->bt_info.bt[3],
|
||||
task->bt_info.bt[4], task->bt_info.bt[5], task->bt_info.bt[6], task->bt_info.bt[7],
|
||||
task->bt_info.bt[8], task->bt_info.bt[9], task->bt_info.bt[10], task->bt_info.bt[11],
|
||||
task->bt_info.bt[12], task->bt_info.bt[13], task->bt_info.bt[14], task->bt_info.bt[15]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IDF_TARGET_ARCH_RISCV */
|
||||
|
||||
void esp_diag_task_snapshot_dump(void)
|
||||
{
|
||||
int i;
|
||||
size_t task_count = uxTaskGetNumberOfTasks();
|
||||
esp_diag_task_info_t *tasks = calloc(1, sizeof(esp_diag_task_info_t) * task_count);
|
||||
if (!tasks) {
|
||||
return;
|
||||
}
|
||||
uint32_t count = esp_diag_task_snapshot_get(tasks, task_count);
|
||||
for (i = 0; i < count; i++) {
|
||||
print_task_info(&tasks[i]);
|
||||
}
|
||||
free(tasks);
|
||||
}
|
||||
|
||||
uint32_t esp_diag_data_size_get_crc(void)
|
||||
{
|
||||
size_t diag_data_size = sizeof(esp_diag_data_pt_t) + sizeof(esp_diag_str_data_pt_t) + sizeof(esp_diag_log_data_t);
|
||||
uint32_t crc = 0;
|
||||
crc = esp_crc32_le(crc, (const unsigned char *)&diag_data_size, sizeof(diag_data_size));
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint32_t esp_diag_meta_crc_get(void)
|
||||
{
|
||||
uint32_t crc = 0;
|
||||
const esp_app_desc_t *app_desc;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
app_desc = esp_app_get_description();
|
||||
#else
|
||||
app_desc = esp_ota_get_app_description();
|
||||
#endif
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *) app_desc->app_elf_sha256, sizeof(app_desc->app_elf_sha256));
|
||||
#if CONFIG_DIAG_ENABLE_METRICS
|
||||
uint32_t metrics_len = 0;
|
||||
const esp_diag_metrics_meta_t *metrics = esp_diag_metrics_meta_get_all(&metrics_len);
|
||||
if (metrics) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < metrics_len; i++) {
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)metrics[i].tag, strlen(metrics[i].tag));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)metrics[i].key, strlen(metrics[i].key));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)metrics[i].label, strlen(metrics[i].label));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)metrics[i].path, strlen(metrics[i].path));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)&metrics[i].type, sizeof(metrics[i].type));
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_DIAG_ENABLE_METRICS */
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_VARIABLES
|
||||
uint32_t variables_len = 0;
|
||||
const esp_diag_variable_meta_t *variables = esp_diag_variable_meta_get_all(&variables_len);
|
||||
if (variables) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < variables_len; i++) {
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)variables[i].tag, strlen(variables[i].tag));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)variables[i].key, strlen(variables[i].key));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)variables[i].label, strlen(variables[i].label));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)variables[i].path, strlen(variables[i].path));
|
||||
crc = ESP_CRC32_LE(crc, (const uint8_t *)&variables[i].type, sizeof(variables[i].type));
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_DIAG_ENABLE_VARIABLES */
|
||||
return crc;
|
||||
}
|
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_diagnostics.h>
|
||||
#include <esp_diagnostics_variables.h>
|
||||
|
||||
#define TAG "DIAG_VARIABLES"
|
||||
#define DIAG_VARIABLES_MAX_COUNT CONFIG_DIAG_VARIABLES_MAX_COUNT
|
||||
|
||||
/* Max supported string lenth */
|
||||
#define MAX_STR_LEN (sizeof(((esp_diag_str_data_pt_t *)0)->value.str) - 1)
|
||||
#define MAX_VARIABLES_WRITE_SZ sizeof(esp_diag_data_pt_t)
|
||||
#define MAX_STR_VARIABLES_WRITE_SZ sizeof(esp_diag_str_data_pt_t)
|
||||
|
||||
typedef struct {
|
||||
size_t variables_count;
|
||||
esp_diag_variable_meta_t variables[DIAG_VARIABLES_MAX_COUNT];
|
||||
esp_diag_variable_config_t config;
|
||||
bool init;
|
||||
} variables_priv_data_t;
|
||||
|
||||
static variables_priv_data_t s_priv_data;
|
||||
|
||||
static esp_diag_variable_meta_t *esp_diag_variable_meta_get(const char *tag, const char *key)
|
||||
{
|
||||
uint32_t i;
|
||||
if (!tag || !key) {
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < s_priv_data.variables_count; i++) {
|
||||
if (s_priv_data.variables[i].key &&
|
||||
(strcmp(s_priv_data.variables[i].tag, tag) == 0) &&
|
||||
(strcmp(s_priv_data.variables[i].key, key) == 0)) {
|
||||
return &s_priv_data.variables[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
/* Checks only by key for registered variable. Use this for meta version < 1.1 */
|
||||
static esp_diag_variable_meta_t *esp_diag_variable_meta_get_by_key(const char *key)
|
||||
{
|
||||
uint32_t i;
|
||||
if (!key) {
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < s_priv_data.variables_count; i++) {
|
||||
if (s_priv_data.variables[i].key &&
|
||||
(strcmp(s_priv_data.variables[i].key, key) == 0)) {
|
||||
return &s_priv_data.variables[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool tag_key_present(const char *tag, const char *key)
|
||||
{
|
||||
return (esp_diag_variable_meta_get(tag, key) != NULL);
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_register(const char *tag, const char *key,
|
||||
const char *label, const char *path,
|
||||
esp_diag_data_type_t type)
|
||||
{
|
||||
if (!tag || !key || !label || !path) {
|
||||
ESP_LOGE(TAG, "Failed to register variable, tag, key, lable, or path is NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (s_priv_data.variables_count >= DIAG_VARIABLES_MAX_COUNT) {
|
||||
ESP_LOGE(TAG, "No space left for more variable");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (tag_key_present(tag, key)) {
|
||||
ESP_LOGE(TAG, "Param-val tag:%s, key:%s exists", tag, key);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
s_priv_data.variables[s_priv_data.variables_count].tag = tag;
|
||||
s_priv_data.variables[s_priv_data.variables_count].key = key;
|
||||
s_priv_data.variables[s_priv_data.variables_count].label = label;
|
||||
s_priv_data.variables[s_priv_data.variables_count].unit = NULL;
|
||||
s_priv_data.variables[s_priv_data.variables_count].path = path;
|
||||
s_priv_data.variables[s_priv_data.variables_count].type = type;
|
||||
s_priv_data.variables_count++;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_variable_add_unit(const char *key, const char *unit)
|
||||
#else
|
||||
esp_err_t esp_diag_variable_add_unit(const char *tag, const char *key, const char *unit)
|
||||
#endif
|
||||
{
|
||||
if (!key) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_variable_meta_t *variable = esp_diag_variable_meta_get_by_key(key);
|
||||
if (!variable) {
|
||||
ESP_LOGI(TAG, "var with (key %s) not found", key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#else
|
||||
esp_diag_variable_meta_t *variable = esp_diag_variable_meta_get(tag, key);
|
||||
if (!variable) {
|
||||
ESP_LOGI(TAG, "var with (tag %s, key %s) not found", tag, key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
variable->unit = unit;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_variable_unregister(const char *key)
|
||||
#else
|
||||
esp_err_t esp_diag_variable_unregister(const char *tag, const char *key)
|
||||
#endif
|
||||
{
|
||||
int i;
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (!tag) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
if (!key) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
for (i = 0; i < s_priv_data.variables_count; i++) {
|
||||
if (s_priv_data.variables[i].key &&
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
(strcmp(s_priv_data.variables[i].tag, tag) == 0) &&
|
||||
#endif
|
||||
(strcmp(s_priv_data.variables[i].key, key) == 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < s_priv_data.variables_count) {
|
||||
s_priv_data.variables[i] = s_priv_data.variables[s_priv_data.variables_count - 1];
|
||||
memset(&s_priv_data.variables[s_priv_data.variables_count - 1], 0, sizeof(esp_diag_variable_meta_t));
|
||||
s_priv_data.variables_count--;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_unregister_all(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
memset(&s_priv_data.variables, 0, sizeof(s_priv_data.variables));
|
||||
s_priv_data.variables_count = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const esp_diag_variable_meta_t *esp_diag_variable_meta_get_all(uint32_t *len)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
*len = 0;
|
||||
return NULL;
|
||||
}
|
||||
*len = s_priv_data.variables_count;
|
||||
return &s_priv_data.variables[0];
|
||||
}
|
||||
|
||||
void esp_diag_variable_meta_print_all(void)
|
||||
{
|
||||
uint32_t len;
|
||||
uint32_t i;
|
||||
const esp_diag_variable_meta_t *meta = esp_diag_variable_meta_get_all(&len);
|
||||
if (meta) {
|
||||
ESP_LOGI(TAG, "Tag\tKey\tLabel\tPath\tData type\n");
|
||||
for (i = 0; i < len; i++) {
|
||||
ESP_LOGI(TAG, "%s\t%s\t%s\t%s\t%d\n", meta[i].tag, meta[i].key, meta[i].label, meta[i].path, meta[i].type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_init(esp_diag_variable_config_t *config)
|
||||
{
|
||||
if (!config || !config->write_cb) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
memcpy(&s_priv_data.config, config, sizeof(s_priv_data.config));
|
||||
s_priv_data.init = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variables_deinit(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
memset(&s_priv_data, 0, sizeof(s_priv_data));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_variable_add(esp_diag_data_type_t data_type,
|
||||
#else
|
||||
esp_err_t esp_diag_variable_report(esp_diag_data_type_t data_type, const char *tag,
|
||||
#endif
|
||||
const char *key, const void *val,
|
||||
size_t val_sz, uint64_t ts)
|
||||
{
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (!tag) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
if (!key || !val) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
const esp_diag_variable_meta_t *variable = esp_diag_variable_meta_get_by_key(key);
|
||||
if (!variable) {
|
||||
ESP_LOGI(TAG, "var with (key %s) not registered", key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#else
|
||||
const esp_diag_variable_meta_t *variable = esp_diag_variable_meta_get(tag, key);
|
||||
if (!variable) {
|
||||
ESP_LOGI(TAG, "var with (tag %s, key %s) not registered", tag, key);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
if (variable->type != data_type) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
size_t write_sz = MAX_VARIABLES_WRITE_SZ;
|
||||
if (variable->type == ESP_DIAG_DATA_TYPE_STR) {
|
||||
write_sz = MAX_STR_VARIABLES_WRITE_SZ;
|
||||
}
|
||||
|
||||
esp_diag_str_data_pt_t data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.type = ESP_DIAG_DATA_PT_VARIABLE;
|
||||
data.data_type = data_type;
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
strlcpy(data.tag, tag, sizeof(data.tag));
|
||||
#endif
|
||||
strlcpy(data.key, key, sizeof(data.key));
|
||||
data.ts = ts;
|
||||
memcpy(&data.value, val, val_sz);
|
||||
|
||||
if (s_priv_data.config.write_cb) {
|
||||
return s_priv_data.config.write_cb(variable->tag, &data, write_sz, s_priv_data.config.cb_arg);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_err_t esp_diag_variable_add_bool(const char *key, bool b)
|
||||
{
|
||||
return esp_diag_variable_add(ESP_DIAG_DATA_TYPE_BOOL, key, &b, sizeof(b), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_add_int(const char *key, int32_t i)
|
||||
{
|
||||
return esp_diag_variable_add(ESP_DIAG_DATA_TYPE_INT, key, &i, sizeof(i), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_add_uint(const char *key, uint32_t u)
|
||||
{
|
||||
return esp_diag_variable_add(ESP_DIAG_DATA_TYPE_UINT, key, &u, sizeof(u), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_add_float(const char *key, float f)
|
||||
{
|
||||
return esp_diag_variable_add(ESP_DIAG_DATA_TYPE_FLOAT, key, &f, sizeof(f), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_add_ipv4(const char *key, uint32_t ip)
|
||||
{
|
||||
return esp_diag_variable_add(ESP_DIAG_DATA_TYPE_IPv4, key, &ip, sizeof(ip), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_add_mac(const char *key, uint8_t *mac)
|
||||
{
|
||||
return esp_diag_variable_add(ESP_DIAG_DATA_TYPE_MAC, key, mac, 6, esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_add_str(const char *key, const char *str)
|
||||
{
|
||||
return esp_diag_variable_add(ESP_DIAG_DATA_TYPE_STR, key, str, strlen(str), esp_diag_timestamp_get());
|
||||
}
|
||||
#else
|
||||
esp_err_t esp_diag_variable_report_bool(const char *tag, const char *key, bool b)
|
||||
{
|
||||
return esp_diag_variable_report(ESP_DIAG_DATA_TYPE_BOOL, tag, key, &b, sizeof(b), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_report_int(const char *tag, const char *key, int32_t i)
|
||||
{
|
||||
return esp_diag_variable_report(ESP_DIAG_DATA_TYPE_INT, tag, key, &i, sizeof(i), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_report_uint(const char *tag, const char *key, uint32_t u)
|
||||
{
|
||||
return esp_diag_variable_report(ESP_DIAG_DATA_TYPE_UINT, tag, key, &u, sizeof(u), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_report_float(const char *tag, const char *key, float f)
|
||||
{
|
||||
return esp_diag_variable_report(ESP_DIAG_DATA_TYPE_FLOAT, tag, key, &f, sizeof(f), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_report_ipv4(const char *tag, const char *key, uint32_t ip)
|
||||
{
|
||||
return esp_diag_variable_report(ESP_DIAG_DATA_TYPE_IPv4, tag, key, &ip, sizeof(ip), esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_report_mac(const char *tag, const char *key, uint8_t *mac)
|
||||
{
|
||||
return esp_diag_variable_report(ESP_DIAG_DATA_TYPE_MAC, tag, key, mac, 6, esp_diag_timestamp_get());
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_variable_report_str(const char *tag, const char *key, const char *str)
|
||||
{
|
||||
return esp_diag_variable_report(ESP_DIAG_DATA_TYPE_STR, tag, key, str, strlen(str), esp_diag_timestamp_get());
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_wifi.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#include <esp_diagnostics_metrics.h>
|
||||
#include "esp_diagnostics_internal.h"
|
||||
|
||||
#define LOG_TAG "wifi_metrics"
|
||||
#define METRICS_TAG "wifi"
|
||||
#define METRICS_UNIT "dB"
|
||||
#define KEY_RSSI "rssi"
|
||||
#define KEY_MIN_RSSI "min_rssi_ever"
|
||||
#define KEY_STATUS "conn_status"
|
||||
|
||||
#define PATH_WIFI_STATION "Wi-Fi.Station"
|
||||
|
||||
/* Use configurable polling interval with default fallback */
|
||||
#ifdef CONFIG_DIAG_WIFI_POLLING_INTERVAL
|
||||
#define DEFAULT_POLLING_INTERVAL CONFIG_DIAG_WIFI_POLLING_INTERVAL
|
||||
#else
|
||||
#define DEFAULT_POLLING_INTERVAL 30 /* 30 seconds */
|
||||
#endif
|
||||
|
||||
/* start reporting minimum ever rssi when rssi reaches -50 dbm */
|
||||
#define WIFI_RSSI_THRESHOLD -50
|
||||
|
||||
typedef struct {
|
||||
bool init;
|
||||
bool wifi_connected;
|
||||
bool status_sent;
|
||||
uint32_t period;
|
||||
TimerHandle_t handle;
|
||||
int32_t prev_rssi;
|
||||
int32_t min_rssi;
|
||||
} wifi_diag_priv_data_t;
|
||||
|
||||
static wifi_diag_priv_data_t s_priv_data;
|
||||
|
||||
static void update_min_rssi(int32_t rssi)
|
||||
{
|
||||
if (rssi < s_priv_data.min_rssi) {
|
||||
s_priv_data.min_rssi = rssi;
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_report_int(METRICS_TAG, KEY_MIN_RSSI, rssi);
|
||||
#else
|
||||
esp_diag_metrics_add_int(KEY_MIN_RSSI, rssi);
|
||||
#endif
|
||||
ESP_LOGI(LOG_TAG, "Wi-Fi RSSI crossed threshold %" PRIi32, rssi);
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
esp_wifi_set_rssi_threshold(rssi);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_evt_handler(void *arg, esp_event_base_t evt_base, int32_t evt_id, void *evt_data)
|
||||
{
|
||||
switch (evt_id) {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
case WIFI_EVENT_STA_BSS_RSSI_LOW:
|
||||
{
|
||||
wifi_event_bss_rssi_low_t *data = evt_data;
|
||||
update_min_rssi(data->rssi);
|
||||
}
|
||||
break;
|
||||
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) */
|
||||
case WIFI_EVENT_STA_CONNECTED:
|
||||
{
|
||||
s_priv_data.wifi_connected = true;
|
||||
s_priv_data.status_sent = false;
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (esp_diag_metrics_report_bool(METRICS_TAG, KEY_STATUS, 1) == ESP_OK) {
|
||||
s_priv_data.status_sent = true;
|
||||
}
|
||||
#else
|
||||
if (esp_diag_metrics_add_bool(KEY_STATUS, 1) == ESP_OK) {
|
||||
s_priv_data.status_sent = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
case WIFI_EVENT_STA_DISCONNECTED:
|
||||
{
|
||||
if (s_priv_data.wifi_connected) {
|
||||
s_priv_data.wifi_connected = false;
|
||||
s_priv_data.status_sent = false;
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (esp_diag_metrics_report_bool(METRICS_TAG, KEY_STATUS, 0) == ESP_OK) {
|
||||
s_priv_data.status_sent = true;
|
||||
}
|
||||
#else
|
||||
if (esp_diag_metrics_add_bool(KEY_STATUS, 0) == ESP_OK) {
|
||||
s_priv_data.status_sent = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function returns 1 on failure otherwise valid RSSI */
|
||||
static int32_t get_rssi(void)
|
||||
{
|
||||
wifi_ap_record_t ap_info;
|
||||
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
|
||||
return ap_info.rssi;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_wifi_metrics_dump(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
ESP_LOGW(LOG_TAG, "Wi-Fi metrics not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
int32_t rssi = get_rssi();
|
||||
if (rssi != 1) {
|
||||
update_min_rssi(rssi);
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_int(METRICS_TAG, KEY_RSSI, rssi), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add Wi-Fi metrics key:" KEY_RSSI);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_report_int(METRICS_TAG, KEY_MIN_RSSI, s_priv_data.min_rssi), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add Wi-Fi metrics key:" KEY_MIN_RSSI);
|
||||
#else
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_int(KEY_RSSI, rssi), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add Wi-Fi metrics key:" KEY_RSSI);
|
||||
RET_ON_ERR_WITH_LOG(esp_diag_metrics_add_int(KEY_MIN_RSSI, s_priv_data.min_rssi), ESP_LOG_WARN, LOG_TAG,
|
||||
"Failed to add Wi-Fi metrics key:" KEY_MIN_RSSI);
|
||||
#endif
|
||||
s_priv_data.prev_rssi = rssi;
|
||||
ESP_LOGI(LOG_TAG, "%s:%" PRIi32 " %s:%" PRIi32, KEY_RSSI, rssi, KEY_MIN_RSSI, s_priv_data.min_rssi);
|
||||
}
|
||||
if (!s_priv_data.status_sent) {
|
||||
// if for some reason we were not able to add the status, try again
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
if (esp_diag_metrics_report_bool(METRICS_TAG, KEY_STATUS, s_priv_data.wifi_connected) == ESP_OK) {
|
||||
s_priv_data.status_sent = true;
|
||||
}
|
||||
#else
|
||||
if (esp_diag_metrics_add_bool(KEY_STATUS, s_priv_data.wifi_connected) == ESP_OK) {
|
||||
s_priv_data.status_sent = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void wifi_metrics_dump_cb(void *arg)
|
||||
{
|
||||
esp_diag_wifi_metrics_dump();
|
||||
}
|
||||
|
||||
static void wifi_timer_cb(TimerHandle_t handle)
|
||||
{
|
||||
esp_rmaker_work_queue_add_task(wifi_metrics_dump_cb, NULL);
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_wifi_metrics_init(void)
|
||||
{
|
||||
if (s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
/* Register the event handler for wifi events */
|
||||
esp_err_t err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_evt_handler, NULL);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
err = esp_wifi_set_rssi_threshold(WIFI_RSSI_THRESHOLD);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(LOG_TAG, "Failed to set rssi threshold value");
|
||||
}
|
||||
#endif
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_RSSI, "Wi-Fi RSSI", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_INT);
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_MIN_RSSI, "Minimum ever Wi-Fi RSSI", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_INT);
|
||||
esp_diag_metrics_register(METRICS_TAG, KEY_STATUS, "Wi-Fi connect status", PATH_WIFI_STATION, ESP_DIAG_DATA_TYPE_BOOL);
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_RSSI, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(METRICS_TAG, KEY_MIN_RSSI, METRICS_UNIT);
|
||||
#else
|
||||
esp_diag_metrics_add_unit(KEY_RSSI, METRICS_UNIT);
|
||||
esp_diag_metrics_add_unit(KEY_MIN_RSSI, METRICS_UNIT);
|
||||
#endif
|
||||
s_priv_data.min_rssi = WIFI_RSSI_THRESHOLD;
|
||||
s_priv_data.handle = xTimerCreate("wifi_metrics", SEC2TICKS(DEFAULT_POLLING_INTERVAL),
|
||||
pdTRUE, NULL, wifi_timer_cb);
|
||||
if (s_priv_data.handle) {
|
||||
xTimerStart(s_priv_data.handle, 0);
|
||||
}
|
||||
s_priv_data.init = true;
|
||||
/* Record RSSI at start */
|
||||
esp_diag_wifi_metrics_dump();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_diag_wifi_metrics_deinit(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_evt_handler);
|
||||
/* Try to delete timer with 10 ticks wait time */
|
||||
if (xTimerDelete(s_priv_data.handle, 10) == pdFALSE) {
|
||||
ESP_LOGW(LOG_TAG, "Failed to delete heap metric timer");
|
||||
}
|
||||
#ifdef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
esp_diag_metrics_unregister(KEY_RSSI);
|
||||
esp_diag_metrics_unregister(KEY_MIN_RSSI);
|
||||
esp_diag_metrics_unregister(KEY_STATUS);
|
||||
#else
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_RSSI);
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_MIN_RSSI);
|
||||
esp_diag_metrics_unregister(METRICS_TAG, KEY_STATUS);
|
||||
#endif
|
||||
memset(&s_priv_data, 0, sizeof(s_priv_data));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_diag_wifi_metrics_reset_interval(uint32_t period)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
return;
|
||||
}
|
||||
if (period == 0) {
|
||||
xTimerStop(s_priv_data.handle, 0);
|
||||
return;
|
||||
}
|
||||
xTimerChangePeriod(s_priv_data.handle, SEC2TICKS(period), 0);
|
||||
}
|
@@ -0,0 +1 @@
|
||||
b9e0863e821a4a18d5805a969a0240e1c82c59aff13b9350568fe7892cef84b3
|
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-06-26T15:05:04.008787+00:00", "files": [{"path": "CMakeLists.txt", "size": 1851, "hash": "1ae6ac81e7c6a7f35e1feebf2d938fcfd340ea2abce645c5b7565e43cf5c4a75"}, {"path": "Kconfig", "size": 3892, "hash": "7ea93a5bb99bf5ba3ab74d04f2d14516e639d87532a2ff24e0fd707d215aff6d"}, {"path": "LICENSE", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "README.md", "size": 408, "hash": "d8860ff0dc69d4c4b6c6eff20b35af58f9b43892113fa2af674fee15724c76d3"}, {"path": "idf_component.yml", "size": 719, "hash": "9fd63245ac3e8c9de1908d897fda6453efd61900c8cc6e555495a7bdfa9a2555"}, {"path": "project_include.cmake", "size": 1404, "hash": "dfb677a3a19b8dcf26e05ad56dd8e7fed0c4e30cb95fde23d92e35f1fe8fc20f"}, {"path": "include/esp_insights.h", "size": 7444, "hash": "269463d4b55bb848fb149bce1db8136b9ba661ff9d357417ab5c3061ce27946c"}, {"path": "scripts/gen_tar_dir.cmake", "size": 3638, "hash": "e89a8b65c7f642de4249f3cd0eb01d1e95b2fe6fbbfe6f1237a8248d1f06388e"}, {"path": "scripts/get_projbuild_gitconfig.py", "size": 3560, "hash": "372daadca20c2a66062ca5f77db61f4b30674efc3de0d62c2e5c6b5accf95cc2"}, {"path": "server_certs/https_server.crt", "size": 1188, "hash": "2c43952ee9e000ff2acc4e2ed0897c0a72ad5fa72c3d934e81741cbd54f05bd1"}, {"path": "server_certs/mqtt_server.crt", "size": 1188, "hash": "2c43952ee9e000ff2acc4e2ed0897c0a72ad5fa72c3d934e81741cbd54f05bd1"}, {"path": "src/esp_insights.c", "size": 37414, "hash": "2856e052d79f0ecce4d707f57e98630cee97217e3bc8d6de3778686ea6b6aee4"}, {"path": "src/esp_insights_cbor_decoder.c", "size": 11588, "hash": "1c314643c4c7b2d622bb2ac84766317cb139db9736e9720903eea0babf5d447e"}, {"path": "src/esp_insights_cbor_decoder.h", "size": 1560, "hash": "19dde3ba304804214a41402a155e0aac8e41de2d54c0048bc37bd6d40d00a97d"}, {"path": "src/esp_insights_cbor_encoder.c", "size": 35326, "hash": "4e8c622b17a8674996d953ea19fe2cebe93b19d5dfe131274aca964ef9a5b18a"}, {"path": "src/esp_insights_cbor_encoder.h", "size": 3505, "hash": "3cda3f143edaefbc2587acf649041e8f8189a21ae2a7ef09e631bfdad4bc1dce"}, {"path": "src/esp_insights_client_data.c", "size": 3443, "hash": "6aca785110facc9aabb6feb56f04b55f761391100159251db9eeb525a7cae1bd"}, {"path": "src/esp_insights_client_data.h", "size": 554, "hash": "d95f6505138c97f6f6ac5f42ce4c02b1b19a364680900161cb8855035d59349d"}, {"path": "src/esp_insights_cmd_resp.c", "size": 25392, "hash": "e5488c0cfc617c666c9ec79b6dc8c22858fda981a6e1bcd4df730ff60e2c7c8a"}, {"path": "src/esp_insights_encoder.c", "size": 7545, "hash": "5bf287595b8b19361516070cf762c3a6c890cfbbac1a7df56bebbaaf6aa75b96"}, {"path": "src/esp_insights_encoder.h", "size": 1293, "hash": "e46706af88b8e68c3a93123a8121832aa41c2208ec2a4ead2bab263c68c04953"}, {"path": "src/esp_insights_internal.h", "size": 1792, "hash": "f945b09dac01c9d17ea130f57057fb1769f3b1f3c7acee8783c2a239bc5de461"}, {"path": "src/esp_insights_transport.c", "size": 2513, "hash": "ac07c1e809f21fca536246a726be2325b633f9ff92a047a638bb66bba88c4dbd"}, {"path": "src/transport/esp_insights_https.c", "size": 5381, "hash": "1a31930945b755ee8fa291ab5ad41c93f2846b0b1463ad0f2d61d6d861b164ee"}, {"path": "src/transport/esp_insights_mqtt.c", "size": 6564, "hash": "fc7841df7fd00cbc0dc7bcb1b658468b41d08028d814eee57709a3eb64c18616"}]}
|
@@ -0,0 +1,47 @@
|
||||
# ESP Insights
|
||||
set(srcs "src/esp_insights.c"
|
||||
"src/esp_insights_transport.c"
|
||||
"src/esp_insights_client_data.c"
|
||||
"src/esp_insights_encoder.c"
|
||||
"src/esp_insights_cmd_resp.c"
|
||||
"src/esp_insights_cbor_decoder.c"
|
||||
"src/esp_insights_cbor_encoder.c")
|
||||
|
||||
set(priv_req cbor rmaker_common esptool_py espcoredump esp_diag_data_store nvs_flash)
|
||||
|
||||
# esp_timer component was introduced in v4.2
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
|
||||
list(APPEND priv_req esp_timer)
|
||||
endif()
|
||||
|
||||
# esp_hw_support component was introduced in v4.3
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.2")
|
||||
list(APPEND priv_req esp_hw_support)
|
||||
endif()
|
||||
|
||||
# from IDF version 5.0, we need to explicitly specify requirements
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND priv_req esp_wifi)
|
||||
endif()
|
||||
|
||||
set(pub_req esp_diagnostics)
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "src"
|
||||
REQUIRES ${pub_req}
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
|
||||
if(CONFIG_ESP_INSIGHTS_TRANSPORT_MQTT)
|
||||
target_add_binary_data(${COMPONENT_TARGET} "server_certs/mqtt_server.crt" TEXT)
|
||||
target_sources(${COMPONENT_LIB} PRIVATE "src/transport/esp_insights_mqtt.c")
|
||||
else()
|
||||
target_add_binary_data(${COMPONENT_TARGET} "server_certs/https_server.crt" TEXT)
|
||||
idf_component_get_property(http_client_lib esp_http_client COMPONENT_LIB)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE ${http_client_lib})
|
||||
target_sources(${COMPONENT_LIB} PRIVATE "src/transport/esp_insights_https.c")
|
||||
endif()
|
||||
|
||||
# Added just to automatically trigger re-runs of CMake
|
||||
git_describe(ESP_INSIGHTS_VERSION ${COMPONENT_DIR})
|
||||
message("ESP Insights Project commit: " ${ESP_INSIGHTS_VERSION})
|
@@ -0,0 +1,90 @@
|
||||
menu "ESP Insights"
|
||||
|
||||
config ESP_INSIGHTS_ENABLED
|
||||
bool "Enable ESP Insights"
|
||||
default n
|
||||
select DIAG_ENABLE_WRAP_LOG_FUNCTIONS
|
||||
help
|
||||
Enable ESP Insights functionality.
|
||||
|
||||
This automatically selects log wrapping (DIAG_ENABLE_WRAP_LOG_FUNCTIONS) which is
|
||||
required for ESP Insights to capture log messages.
|
||||
|
||||
config ESP_INSIGHTS_DEBUG_ENABLED
|
||||
depends on ESP_INSIGHTS_ENABLED
|
||||
bool "Enable Insights debug prints"
|
||||
default n
|
||||
|
||||
config ESP_INSIGHTS_DEBUG_PRINT_JSON
|
||||
depends on ESP_INSIGHTS_DEBUG_ENABLED
|
||||
bool "Dump CBOR decoded data instead of hexdump"
|
||||
default y
|
||||
help
|
||||
Print pretty format json data for cbor messages
|
||||
If this option is not enabled, hexdump will be presented instead of json data
|
||||
|
||||
config ESP_INSIGHTS_COREDUMP_ENABLE
|
||||
bool "Enable core dump summary support"
|
||||
default y
|
||||
depends on (ESP_COREDUMP_ENABLE_TO_FLASH && ESP_COREDUMP_DATA_FORMAT_ELF) || (ESP32_ENABLE_COREDUMP_TO_FLASH && ESP32_COREDUMP_DATA_FORMAT_ELF)
|
||||
help
|
||||
This option enables core dump summary functionality in insights.
|
||||
In case of crash, insights sends the core dump summary to cloud on next boot.
|
||||
Once the core dump summary is sent to cloud, it is erased from flash partition of the device.
|
||||
|
||||
choice ESP_INSIGHTS_TRANSPORT
|
||||
prompt "Insights default transport"
|
||||
default ESP_INSIGHTS_TRANSPORT_HTTPS
|
||||
help
|
||||
By default insights can send data to cloud using MQTT and HTTPS transports.
|
||||
This option configures the default insights transport.
|
||||
Insights transport can be overridden using esp_insights_transport_register API.
|
||||
|
||||
config ESP_INSIGHTS_TRANSPORT_MQTT
|
||||
bool "MQTT"
|
||||
|
||||
config ESP_INSIGHTS_TRANSPORT_HTTPS
|
||||
bool "HTTPS"
|
||||
|
||||
endchoice
|
||||
|
||||
config ESP_INSIGHTS_CMD_RESP_ENABLED
|
||||
depends on (ESP_INSIGHTS_ENABLED && ESP_INSIGHTS_TRANSPORT_MQTT)
|
||||
bool "Enable command response module"
|
||||
default n
|
||||
help
|
||||
Enabling this option adds control of certain insights options remotely.
|
||||
When enabled, the available configurations, should be shown on the dashboard
|
||||
and controllable from there.
|
||||
Please note, the feature is only available with RainMaker MQTT nodes for now.
|
||||
|
||||
config ESP_INSIGHTS_TRANSPORT_HTTPS_HOST
|
||||
depends on ESP_INSIGHTS_TRANSPORT_HTTPS
|
||||
string "Insights https host"
|
||||
default "https://client.insights.espressif.com"
|
||||
|
||||
config ESP_INSIGHTS_CLOUD_POST_MIN_INTERVAL_SEC
|
||||
int "Insights cloud post min interval (sec)"
|
||||
default 60
|
||||
help
|
||||
Minimum interval between two consecutive cloud posts.
|
||||
There is a dynamic logic to decide the next timeout when the insights data will be reported.
|
||||
It depends on whether the data was sent or not during the previous timeout.
|
||||
If the data was sent, the next timeout is doubled and if not, it is halved.
|
||||
|
||||
config ESP_INSIGHTS_CLOUD_POST_MAX_INTERVAL_SEC
|
||||
int "Insights cloud post max interval (sec)"
|
||||
default 240
|
||||
help
|
||||
Maximum interval between two consecutive cloud posts.
|
||||
There is a dynamic logic to decide the next timeout when the insights data will be reported.
|
||||
It depends on whether the data was sent or not during the previous timeout.
|
||||
If the data was sent, the next timeout is doubled and if not, it is halved.
|
||||
|
||||
config ESP_INSIGHTS_META_VERSION_10
|
||||
bool "Use older metadata format (1.0)"
|
||||
default y
|
||||
help
|
||||
For users already using older metadata, this provides an option to keep using the same.
|
||||
This is important as the new metadata version (1.1), is not backwad compatible.
|
||||
endmenu
|
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@@ -0,0 +1,6 @@
|
||||
# ESP Insights Agent Component
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_insights)
|
||||
|
||||
This is the main firmware agent for ESP Insights, which will then pull in other required comoponents. Please check the [ESP Insights documentation](https://github.com/espressif/esp-insights) for details.
|
||||
|
@@ -0,0 +1,22 @@
|
||||
dependencies:
|
||||
espressif/cbor:
|
||||
rules:
|
||||
- if: idf_version >=5.0
|
||||
version: ~0.6
|
||||
espressif/esp_diag_data_store:
|
||||
version: 1.0.2
|
||||
espressif/esp_diagnostics:
|
||||
version: '>=1.2.3'
|
||||
espressif/rmaker_common:
|
||||
version: ~1.4.0
|
||||
idf:
|
||||
version: '>=4.1'
|
||||
description: Firmware agent for ESP Insights, which is a remote diagnostics solution
|
||||
to monitor the health of ESP devices in the field.
|
||||
issues: https://github.com/espressif/esp-insights/issues
|
||||
repository: git://github.com/espressif/esp-insights.git
|
||||
repository_info:
|
||||
commit_sha: de283a76dddc7bff94a774582452dcb419dd7d56
|
||||
path: components/esp_insights
|
||||
url: https://github.com/espressif/esp-insights/tree/main/components/esp_insights
|
||||
version: 1.2.7
|
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_event_base.h>
|
||||
#include <esp_diagnostics.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** @cond **/
|
||||
/**
|
||||
* @brief Insights event base
|
||||
*/
|
||||
ESP_EVENT_DECLARE_BASE(INSIGHTS_EVENT);
|
||||
/** @endcond **/
|
||||
|
||||
/**
|
||||
* @brief ESP Insights configuration
|
||||
*/
|
||||
typedef struct {
|
||||
/** Log types to enable, bitwise OR the values from \ref esp_diag_log_type_t */
|
||||
uint32_t log_type;
|
||||
/** Node id for insights. If NULL then insights agent uses MAC address as node id */
|
||||
const char *node_id;
|
||||
/** Authentication key, valid only for https transport */
|
||||
const char *auth_key;
|
||||
/** Try to allocate large buffers in External RAM */
|
||||
bool alloc_ext_ram;
|
||||
} esp_insights_config_t;
|
||||
|
||||
/**
|
||||
* @brief Insights events
|
||||
*
|
||||
* Transport layer emits events using default event loop, every transport
|
||||
* event has event data of type \ref esp_insights_transport_event_data_t;
|
||||
*/
|
||||
typedef enum {
|
||||
/** Asynchronous data send succeded. Event data contains the msg_id of the data. */
|
||||
INSIGHTS_EVENT_TRANSPORT_SEND_SUCCESS,
|
||||
/** Asynchronous data send failed. Event data contains the msg_id of the data. */
|
||||
INSIGHTS_EVENT_TRANSPORT_SEND_FAILED,
|
||||
/** Data received. Event data contains received data and data_len. */
|
||||
INSIGHTS_EVENT_TRANSPORT_RECV,
|
||||
} esp_insights_event_t;
|
||||
|
||||
/**
|
||||
* @brief Insights transport event data
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *data; /*!< Data associated with the event */
|
||||
size_t data_len; /*!< Length of the data for the event */
|
||||
int msg_id; /*!< Message id */
|
||||
} esp_insights_transport_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Insights transport init callback prototype
|
||||
*
|
||||
* @param[in] userdata User data
|
||||
*
|
||||
* @return ESP_OK on success, appropriate error code otherwise.
|
||||
*/
|
||||
typedef esp_err_t(*esp_insights_transport_init_t)(void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Insights transport deinit callback prototype
|
||||
*/
|
||||
typedef void(*esp_insights_transport_deinit_t)(void);
|
||||
|
||||
/**
|
||||
* @brief Insights transport connect callback prototype
|
||||
*
|
||||
* @return ESP_OK on success, appropriate error code otherwise.
|
||||
*/
|
||||
typedef esp_err_t(*esp_insights_transport_connect_t)(void);
|
||||
|
||||
/**
|
||||
* @brief Insights transport disconnect callback prototype
|
||||
*/
|
||||
typedef void(*esp_insights_transport_disconnect_t)(void);
|
||||
|
||||
/**
|
||||
* @brief Insights transport data send callback prototype
|
||||
*
|
||||
* @param[in] data Data to send
|
||||
* @param[in] len Length of data
|
||||
*
|
||||
* @return msg_id Message_id of the sent data.
|
||||
* On failure, -1
|
||||
* On success, 0 if data send happens synchronously.
|
||||
* On success, message-id(positive integer) if data send happened asynchronously.
|
||||
*
|
||||
* @note If data send happened asynchronously then appropriate events in \ref esp_insights_event_t must be emitted.
|
||||
*/
|
||||
typedef int(*esp_insights_transport_data_send_t)(void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Insights transport configurations
|
||||
*/
|
||||
typedef struct {
|
||||
/** Insights transport callback functions */
|
||||
struct {
|
||||
/** Transport init function */
|
||||
esp_insights_transport_init_t init;
|
||||
/** Transport deinit function */
|
||||
esp_insights_transport_deinit_t deinit;
|
||||
/** Transport connect function */
|
||||
esp_insights_transport_connect_t connect;
|
||||
/** Transport disconnect function */
|
||||
esp_insights_transport_disconnect_t disconnect;
|
||||
/** Function to send data */
|
||||
esp_insights_transport_data_send_t data_send;
|
||||
} callbacks;
|
||||
/** User data */
|
||||
void *userdata;
|
||||
} esp_insights_transport_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize ESP Insights.
|
||||
*
|
||||
* This initializes ESP Insights with the transport (HTTPS/MQTT) as per the sdkconfig.
|
||||
* To override the transport configuration, please use esp_insights_transport_register()
|
||||
* and esp_insights_enable().
|
||||
*
|
||||
* @param[in] config Configuration for ESP Insights.
|
||||
*
|
||||
* @return ESP_OK on success, appropriate error code otherwise
|
||||
*/
|
||||
esp_err_t esp_insights_init(esp_insights_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize ESP Insights.
|
||||
*
|
||||
* Disconnects the registered transport and disables ESP Insights
|
||||
*/
|
||||
void esp_insights_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Register insights transport.
|
||||
*
|
||||
* This function should be used only when default transport needs to be overridden.
|
||||
*
|
||||
* @note Call esp_insights_enable()
|
||||
* after registering your own transport to enable Insights.
|
||||
*
|
||||
* @param[in] config Configurations of type \ref esp_insights_transport_config_t
|
||||
*
|
||||
* @return ESP_OK on success, appropriate error code otherwise
|
||||
*/
|
||||
esp_err_t esp_insights_transport_register(esp_insights_transport_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Unregister insights transport.
|
||||
*
|
||||
* @note This API does not disable Insights.
|
||||
* Call esp_insights_disable() to turn off Insights.
|
||||
*/
|
||||
void esp_insights_transport_unregister(void);
|
||||
|
||||
/**
|
||||
* @brief Read insights data from buffers and send it to the cloud.
|
||||
*
|
||||
* Call to this function is asynchronous, it may take some time to send the data.
|
||||
*
|
||||
* @return ESP_OK on success, appropriate error code otherwise
|
||||
*/
|
||||
esp_err_t esp_insights_send_data(void);
|
||||
|
||||
/**
|
||||
* @brief Enable ESP Insights except transport.
|
||||
*
|
||||
* This API is used in conjunction with esp_insights_transport_register()
|
||||
* to start Insights with custom transport.
|
||||
*
|
||||
* @param[in] config Configuration for ESP Insights.
|
||||
*
|
||||
* @return ESP_OK on success, appropriate error code otherwise
|
||||
*/
|
||||
esp_err_t esp_insights_enable(esp_insights_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Disable ESP Insights.
|
||||
*
|
||||
* This API does not unregister the transport.
|
||||
*
|
||||
* @note Call esp_insights_transport_unregister() to remove the transport.
|
||||
*/
|
||||
void esp_insights_disable(void);
|
||||
|
||||
/**
|
||||
* @brief Returns pointer to the NULL terminated Node ID string.
|
||||
*
|
||||
* @return Pointer to a NULL terminated Node ID string.
|
||||
*/
|
||||
const char *esp_insights_get_node_id(void);
|
||||
|
||||
/**
|
||||
* @brief Check if insights reporting is enabled
|
||||
*
|
||||
* @return true reporting is on
|
||||
* @return false reporting is off
|
||||
*/
|
||||
bool esp_insights_is_reporting_enabled(void);
|
||||
|
||||
/**
|
||||
* @brief Turn on the Insights reporting
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success, apt error otherwise
|
||||
*/
|
||||
esp_err_t esp_insights_reporting_enable();
|
||||
|
||||
/**
|
||||
* @brief Turn off the Insights repoting
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success, apt error otherwise
|
||||
* @note meta message if changed and the boot message will still be
|
||||
* sent as this information is critical for Insights working with the
|
||||
* cloud. You may disable insight completely using esp_insights_disable
|
||||
*/
|
||||
esp_err_t esp_insights_reporting_disable();
|
||||
|
||||
/**
|
||||
* @brief Encode and parse the command directly using esp-insight's parser
|
||||
*
|
||||
* This tests only if the parser is working as expected.
|
||||
*/
|
||||
esp_err_t esp_insights_test_cmd_handler();
|
||||
|
||||
/**
|
||||
* @brief Enable esp-insights command-response module
|
||||
*
|
||||
* This API registers esp-insights command parser which when data is received,
|
||||
* parses it to filter out insights specific data, modifies configs accordingly,
|
||||
* and prepares and gives response data to the module
|
||||
*
|
||||
* The \ref esp_insights_init takes care of initializing command response and
|
||||
* enabling the same. In cases where, only esp_insights_enable is called, e.g.,
|
||||
* ESP Rainmaker's app_insights module, user needs to call this API, before or
|
||||
* after \ref esp_insights_enable
|
||||
*/
|
||||
esp_err_t esp_insights_cmd_resp_enable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,29 @@
|
||||
idf_build_get_property(python PYTHON)
|
||||
idf_build_get_property(idf_path IDF_PATH)
|
||||
idf_build_get_property(build_dir BUILD_DIR)
|
||||
|
||||
set(PROJ_BUILD_CONFIG_FILE project_build_config.json)
|
||||
set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-v${PROJECT_VER})
|
||||
|
||||
if(CONFIG_ESP_INSIGHTS_ENABLED)
|
||||
# This will create the archive tar
|
||||
add_custom_command(
|
||||
TARGET app
|
||||
POST_BUILD
|
||||
COMMAND ${python} ${CMAKE_CURRENT_LIST_DIR}/scripts/get_projbuild_gitconfig.py ${PROJECT_DIR} ${CMAKE_PROJECT_NAME} ${PROJECT_VER} ${build_dir}/${PROJ_BUILD_CONFIG_FILE} ${idf_path} ${_CMAKE_TOOLCHAIN_PREFIX}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D BUILD_DIR=${build_dir}
|
||||
-D PROJECT_DIR=${PROJECT_DIR}
|
||||
-D PROJECT_NAME=${CMAKE_PROJECT_NAME}
|
||||
-D PROJECT_VER=${PROJECT_VER}
|
||||
-D ARCHIVE_DIR=${ARCHIVE_NAME}
|
||||
-D PROJ_CONFIG_FILE=${PROJ_BUILD_CONFIG_FILE}
|
||||
-D PARTITION_CSV_FILE=${CONFIG_PARTITION_TABLE_CUSTOM_FILENAME}
|
||||
-P ${CMAKE_CURRENT_LIST_DIR}/scripts/gen_tar_dir.cmake
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "===================== Generating insights firmware package build/${ARCHIVE_NAME}.zip ======================"
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cfv ${ARCHIVE_NAME}.zip ${ARCHIVE_NAME} --format=zip
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${ARCHIVE_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${PROJ_BUILD_CONFIG_FILE}
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
@@ -0,0 +1,94 @@
|
||||
# A CMake script to create tar package for ESP Insights
|
||||
# It is created for the esp_rainmaker component
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Set file paths
|
||||
# Set app elf file path
|
||||
set(elf_file_path ${BUILD_DIR}/${PROJECT_NAME}.elf)
|
||||
# Set app binary file path
|
||||
set(bin_file_path ${BUILD_DIR}/${PROJECT_NAME}.bin)
|
||||
# Set app map file path
|
||||
set(map_file_path ${BUILD_DIR}/${PROJECT_NAME}.map)
|
||||
# Set project git config json file path
|
||||
set(proj_config_file_path ${BUILD_DIR}/${PROJ_CONFIG_FILE})
|
||||
# Set sdkconfig file path
|
||||
set(sdkconfig_file_path ${PROJECT_DIR}/sdkconfig)
|
||||
# Set partition table binary file path
|
||||
set(partition_bin_file_path ${BUILD_DIR}/partition_table/partition-table.bin)
|
||||
# Set partition table csv file path
|
||||
set(partition_csv_file_path ${PROJECT_DIR}/${PARTITION_CSV_FILE})
|
||||
# Set bootloader binary file path
|
||||
set(bootloader_bin_file_path ${BUILD_DIR}/bootloader/bootloader.bin)
|
||||
# Set ota data initial binary file path
|
||||
set(ota_data_bin_file_path ${BUILD_DIR}/ota_data_initial.bin)
|
||||
# Set flash args file path
|
||||
set(flash_args_file_path ${BUILD_DIR}/flash_args)
|
||||
# Set project build config file path
|
||||
set(proj_desc_file_path ${BUILD_DIR}/project_description.json)
|
||||
# Set custom project build config file path
|
||||
set(custom_proj_desc_file_path ${BUILD_DIR}/project_description_custom.json)
|
||||
|
||||
# Create archive directory
|
||||
file(MAKE_DIRECTORY ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
# Copy project git config file generated to archive directory
|
||||
file(COPY ${proj_config_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
|
||||
# Copy elf file to archive directory
|
||||
if(EXISTS ${elf_file_path})
|
||||
file(COPY ${elf_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
# Copy bin file to archive directory
|
||||
if(EXISTS ${bin_file_path})
|
||||
file(COPY ${bin_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
# Copy map file to archive directory
|
||||
if(EXISTS ${map_file_path})
|
||||
file(COPY ${map_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
# Copy sdkconfig file to archive directory
|
||||
if(EXISTS ${sdkconfig_file_path})
|
||||
file(COPY ${sdkconfig_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
# Copy partition csv file to archive directory
|
||||
if(EXISTS ${partition_csv_file_path})
|
||||
file(COPY ${partition_csv_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
# Copy partition bin file to archive directory
|
||||
if(EXISTS ${partition_bin_file_path})
|
||||
# Create partition_table sub-dir in archive directory
|
||||
file(MAKE_DIRECTORY ${BUILD_DIR}/${ARCHIVE_DIR}/partition_table)
|
||||
file(COPY ${partition_bin_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR}/partition_table)
|
||||
endif()
|
||||
|
||||
# Copy bootloader bin file to archive directory
|
||||
if(EXISTS ${bootloader_bin_file_path})
|
||||
# Create bootloader sub-dir in archive directory
|
||||
file(MAKE_DIRECTORY ${BUILD_DIR}/${ARCHIVE_DIR}/bootloader)
|
||||
file(COPY ${bootloader_bin_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR}/bootloader)
|
||||
endif()
|
||||
|
||||
# Copy ota_data_initial bin file to archive directory
|
||||
if(EXISTS ${ota_data_bin_file_path})
|
||||
file(COPY ${ota_data_bin_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
# Copy flash args file to archive directory
|
||||
if(EXISTS ${flash_args_file_path})
|
||||
file(COPY ${flash_args_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
# Copy project description json file to archive directory
|
||||
if (EXISTS ${proj_desc_file_path})
|
||||
file(COPY ${proj_desc_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR}/)
|
||||
endif()
|
||||
|
||||
# Copy custom project description json file to archive directory
|
||||
if (EXISTS ${custom_proj_desc_file_path})
|
||||
file(COPY ${custom_proj_desc_file_path} DESTINATION ${BUILD_DIR}/${ARCHIVE_DIR}/)
|
||||
endif()
|
@@ -0,0 +1,120 @@
|
||||
# This file is expected to be present in ${COMPONENT_DIR}
|
||||
# accessed from components/esp_insights/CMakeLists.txt
|
||||
# Used in:
|
||||
# 1. Project ESP Insights build package tar file
|
||||
|
||||
#from __future__ import unicode_literals
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
from builtins import range, str
|
||||
from io import open
|
||||
|
||||
# Input project directory from CMakeLists.txt
|
||||
PROJ_DIR=sys.argv[1]
|
||||
# Input project name
|
||||
PROJ_NAME=sys.argv[2]
|
||||
# Input project version
|
||||
PROJ_VER=sys.argv[3]
|
||||
# Input custom config filename from CMakeLists.txt
|
||||
FILENAME=sys.argv[4]
|
||||
# Input IDF_PATH from CMakeLists.txt
|
||||
IDF_PATH=sys.argv[5]
|
||||
# Toolchain Prefix
|
||||
TOOLCHAIN_PREFIX=sys.argv[6]
|
||||
|
||||
NEWLINE = "\n"
|
||||
|
||||
CONFIG = {}
|
||||
|
||||
# Set Config
|
||||
|
||||
# Set current directory i.e Set ${COMPONENT_DIR} as current directory
|
||||
CURR_DIR = os.getcwd()
|
||||
|
||||
def _change_dir(dirname):
|
||||
# Change directory
|
||||
os.chdir(dirname)
|
||||
|
||||
|
||||
def _set_submodule_cfg(submodules, repo_name):
|
||||
# Set config for submodules
|
||||
CFG_TITLE = "submodules"
|
||||
NAME_STR = "name"
|
||||
VERSION_STR = "version"
|
||||
CONFIG[repo_name][CFG_TITLE] = []
|
||||
|
||||
if submodules:
|
||||
# Get the submodule name and version
|
||||
submodules_list = submodules.strip().split(NEWLINE)
|
||||
for i in range(0, len(submodules_list), 2):
|
||||
name = submodules_list[i].split('\'')[1]
|
||||
version = submodules_list[i+1]
|
||||
submodule_json = { NAME_STR: name, VERSION_STR: version }
|
||||
CONFIG[repo_name][CFG_TITLE].append(submodule_json)
|
||||
|
||||
|
||||
def run_cmd(command, get_basename=False):
|
||||
try:
|
||||
resp = subprocess.check_output(command, shell=True).strip().decode('utf-8')
|
||||
if get_basename:
|
||||
resp = os.path.basename(resp)
|
||||
return resp
|
||||
except subprocess.CalledProcessError:
|
||||
raise Exception("ERROR: Please check command : {}".format(command))
|
||||
|
||||
def set_cfg(config_name):
|
||||
# Set config for ESP-IDF Repo
|
||||
if config_name == "esp-idf":
|
||||
# Get repo name (for IDF repo)
|
||||
REPO_CMD='git rev-parse --show-toplevel'
|
||||
repo_name = run_cmd(REPO_CMD, get_basename=True)
|
||||
CONFIG[repo_name] = {}
|
||||
|
||||
# Get commit HEAD
|
||||
GITHEAD_STR = "HEAD"
|
||||
HEAD='git describe --always --tags --dirty'
|
||||
head_ver = run_cmd(HEAD)
|
||||
CONFIG[repo_name][GITHEAD_STR] = head_ver
|
||||
|
||||
# Get submodule latest refs
|
||||
SUBMODULE = 'git submodule foreach git describe --always --tags --dirty'
|
||||
submodules = run_cmd(SUBMODULE)
|
||||
_set_submodule_cfg(submodules, repo_name)
|
||||
elif config_name == "toolchain":
|
||||
# Get toolchain version
|
||||
TOOLCHAIN_STR = "toolchain"
|
||||
TOOLCHAIN = TOOLCHAIN_PREFIX + 'gcc --version'
|
||||
toolchain = run_cmd(TOOLCHAIN)
|
||||
CONFIG[TOOLCHAIN_STR] = toolchain.strip().split(NEWLINE)[0]
|
||||
|
||||
# Set project details - name and version
|
||||
def set_project_details():
|
||||
# Set project name and version
|
||||
CONFIG['project'] = {}
|
||||
CONFIG['project']['name'] = PROJ_NAME
|
||||
CONFIG['project']['version'] = PROJ_VER
|
||||
|
||||
try:
|
||||
with open(FILENAME, "w+", encoding="utf-8") as output_file:
|
||||
# ESP-IDF REPO CONFIG
|
||||
# Change to ESP-IDF Directory
|
||||
_change_dir(IDF_PATH)
|
||||
set_cfg("esp-idf")
|
||||
|
||||
# Change back to ${COMPONENT_DIR}
|
||||
_change_dir(CURR_DIR)
|
||||
|
||||
# Set project name and version
|
||||
set_project_details()
|
||||
|
||||
# GET TOOLCHAIN VERSION
|
||||
set_cfg("toolchain")
|
||||
|
||||
output_file.write(str(json.dumps(CONFIG, indent=4, sort_keys=True)))
|
||||
|
||||
except Exception as e:
|
||||
# Remove config file created if error occurs
|
||||
os.system("rm " + FILENAME)
|
||||
sys.exit(e)
|
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
|
||||
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
|
||||
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
|
||||
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
|
||||
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
|
||||
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
|
||||
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
|
||||
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
|
||||
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
|
||||
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
|
||||
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
|
||||
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
|
||||
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
|
||||
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
|
||||
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
|
||||
rqXRfboQnoZsG4q5WTP468SQvvG5
|
||||
-----END CERTIFICATE-----
|
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
|
||||
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
|
||||
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
|
||||
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
|
||||
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
|
||||
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
|
||||
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
|
||||
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
|
||||
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
|
||||
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
|
||||
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
|
||||
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
|
||||
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
|
||||
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
|
||||
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
|
||||
rqXRfboQnoZsG4q5WTP468SQvvG5
|
||||
-----END CERTIFICATE-----
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <soc/soc_memory_layout.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#include <cbor.h>
|
||||
#include "esp_insights_cbor_decoder.h"
|
||||
|
||||
static const char *TAG = "insight_cbor_dec";
|
||||
|
||||
#define CBOR_CHECK(a, str, goto_tag, ret_value, ...) \
|
||||
do { \
|
||||
if ((a) != CborNoError) { \
|
||||
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
ret = ret_value; \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void dumpbytes(const uint8_t *buf, size_t len)
|
||||
{
|
||||
while (len--) {
|
||||
printf("%02X ", *buf++);
|
||||
}
|
||||
}
|
||||
|
||||
static void indent(int nestingLevel)
|
||||
{
|
||||
printf("%*s", 2 * nestingLevel, "");
|
||||
}
|
||||
|
||||
// extremely light implementation of stack needed for cbor dump
|
||||
typedef struct cbor_dump_value {
|
||||
CborValue *it; // current iterator
|
||||
int nesting_level;
|
||||
CborType parent_type;
|
||||
int cnt; // count of elements already processed
|
||||
} cbor_dump_value_t;
|
||||
|
||||
typedef struct cbor_dump_node cbor_dump_node_t;
|
||||
typedef struct cbor_dump_node {
|
||||
cbor_dump_value_t *data;
|
||||
cbor_dump_node_t *next;
|
||||
} cbor_dump_node_t;
|
||||
|
||||
static cbor_dump_node_t *dump_list_head = NULL;
|
||||
|
||||
static cbor_dump_value_t* cbor_dump_list_get_head()
|
||||
{
|
||||
cbor_dump_value_t *value = NULL;
|
||||
if (dump_list_head) {
|
||||
cbor_dump_node_t *head = dump_list_head;
|
||||
dump_list_head = head->next;
|
||||
value = head->data;
|
||||
free(head);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void cbor_dump_list_insert_at_head(cbor_dump_value_t* data)
|
||||
{
|
||||
cbor_dump_node_t *node = MEM_ALLOC_EXTRAM(sizeof(cbor_dump_node_t));
|
||||
node->data = data;
|
||||
node->next = dump_list_head;
|
||||
dump_list_head = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode CBOR data manually
|
||||
*/
|
||||
static CborError insights_dump_cbor_buffer(cbor_dump_value_t *value)
|
||||
{
|
||||
if (!value) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
while (1) {
|
||||
CborError ret = CborNoError;
|
||||
int cnt = value->cnt;
|
||||
CborValue *it = value->it;
|
||||
int nestingLevel = value->nesting_level;
|
||||
CborType parent_type = value->parent_type;
|
||||
|
||||
while (!cbor_value_at_end(it)) {
|
||||
CborType type = cbor_value_get_type(it);
|
||||
cnt++;
|
||||
|
||||
if ((cnt % 2 == 0) || (parent_type == CborArrayType)) {
|
||||
if (cnt) {
|
||||
puts(",");
|
||||
} else {
|
||||
puts("");
|
||||
}
|
||||
indent(nestingLevel);
|
||||
} else {
|
||||
printf(" : ");
|
||||
}
|
||||
|
||||
if (type == CborArrayType || type == CborMapType) {
|
||||
// push current node
|
||||
cbor_dump_value_t *value = MEM_ALLOC_EXTRAM(sizeof(cbor_dump_value_t));
|
||||
value->cnt = cnt;
|
||||
value->it = it;
|
||||
value->nesting_level = nestingLevel;
|
||||
value->parent_type = parent_type;
|
||||
cbor_dump_list_insert_at_head(value);
|
||||
|
||||
CborValue *recursed = MEM_ALLOC_EXTRAM(sizeof(CborValue));
|
||||
if (false == cbor_value_is_container(it)) {
|
||||
goto err;
|
||||
}
|
||||
ret = cbor_value_enter_container(it, recursed);
|
||||
CBOR_CHECK(ret, "enter container failed", err, ret);
|
||||
|
||||
cnt = -1;
|
||||
nestingLevel += 1;
|
||||
parent_type = type;
|
||||
it = recursed;
|
||||
if (type == CborArrayType) {
|
||||
printf("[");
|
||||
} else {
|
||||
printf("{");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case CborArrayType:
|
||||
case CborMapType:
|
||||
continue;
|
||||
case CborIntegerType: {
|
||||
int64_t val;
|
||||
ret = cbor_value_get_int64(it, &val);
|
||||
CBOR_CHECK(ret, "parse int64 failed", err, ret);
|
||||
printf("%lld", (long long)val);
|
||||
break;
|
||||
}
|
||||
case CborByteStringType: {
|
||||
uint8_t *buf;
|
||||
size_t n;
|
||||
ret = cbor_value_dup_byte_string(it, &buf, &n, it);
|
||||
CBOR_CHECK(ret, "parse byte string failed", err, ret);
|
||||
dumpbytes(buf, n);
|
||||
free(buf);
|
||||
continue;
|
||||
}
|
||||
case CborTextStringType: {
|
||||
char *buf;
|
||||
size_t n;
|
||||
ret = cbor_value_dup_text_string(it, &buf, &n, it);
|
||||
CBOR_CHECK(ret, "parse text string failed", err, ret);
|
||||
printf("\"%s\"", buf);
|
||||
free(buf);
|
||||
continue;
|
||||
}
|
||||
case CborTagType: {
|
||||
CborTag tag;
|
||||
ret = cbor_value_get_tag(it, &tag);
|
||||
CBOR_CHECK(ret, "parse tag failed", err, ret);
|
||||
printf("Tag(%lld)\n", (long long)tag);
|
||||
break;
|
||||
}
|
||||
case CborSimpleType: {
|
||||
uint8_t type;
|
||||
ret = cbor_value_get_simple_type(it, &type);
|
||||
CBOR_CHECK(ret, "parse simple type failed", err, ret);
|
||||
printf("simple(%u)\n", type);
|
||||
break;
|
||||
}
|
||||
case CborNullType:
|
||||
printf("null");
|
||||
break;
|
||||
case CborUndefinedType:
|
||||
printf("undefined");
|
||||
break;
|
||||
case CborBooleanType: {
|
||||
bool val;
|
||||
ret = cbor_value_get_boolean(it, &val);
|
||||
CBOR_CHECK(ret, "parse boolean type failed", err, ret);
|
||||
printf(val ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
case CborHalfFloatType: {
|
||||
uint16_t val;
|
||||
ret = cbor_value_get_half_float(it, &val);
|
||||
CBOR_CHECK(ret, "parse half float type failed", err, ret);
|
||||
printf("__f16(%04x)", val);
|
||||
break;
|
||||
}
|
||||
case CborFloatType: {
|
||||
float val;
|
||||
ret = cbor_value_get_float(it, &val);
|
||||
CBOR_CHECK(ret, "parse float type failed", err, ret);
|
||||
printf("%g", val);
|
||||
break;
|
||||
}
|
||||
case CborDoubleType: {
|
||||
double val;
|
||||
ret = cbor_value_get_double(it, &val);
|
||||
CBOR_CHECK(ret, "parse double float type failed", err, ret);
|
||||
printf("%g", val);
|
||||
break;
|
||||
}
|
||||
case CborInvalidType: {
|
||||
ret = CborErrorUnknownType;
|
||||
CBOR_CHECK(ret, "unknown cbor type", err, ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = cbor_value_advance_fixed(it);
|
||||
CBOR_CHECK(ret, "fix value failed", err, ret);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
cbor_dump_value_t *data = cbor_dump_list_get_head();
|
||||
if (data) {
|
||||
free(value); // free current
|
||||
value = data;
|
||||
CborType type = cbor_value_get_type(value->it);
|
||||
if (type == CborArrayType || type == CborMapType) {
|
||||
ret = cbor_value_leave_container(value->it, it);
|
||||
free(it);
|
||||
CBOR_CHECK(ret, "leave container failed", err, ret);
|
||||
indent(value->nesting_level);
|
||||
if (type == CborArrayType) {
|
||||
printf("]");
|
||||
} else {
|
||||
printf("}");
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ret = CborNoError;
|
||||
err:
|
||||
// done
|
||||
printf("\n");
|
||||
if (value) {
|
||||
free(value->it);
|
||||
free(value);
|
||||
value = NULL;
|
||||
}
|
||||
|
||||
// final cleanup
|
||||
value = cbor_dump_list_get_head();
|
||||
while (value) {
|
||||
free(value->it);
|
||||
free(value);
|
||||
value = cbor_dump_list_get_head();
|
||||
}
|
||||
dump_list_head = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cbor_decode_dump(const uint8_t *buffer, int len)
|
||||
{
|
||||
if (!buffer || len <= 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
CborParser root_parser;
|
||||
CborValue *it = MEM_ALLOC_EXTRAM(sizeof(CborValue));
|
||||
|
||||
// Initialize the cbor parser and the value iterator
|
||||
cbor_parser_init(buffer, len, 0, &root_parser, it);
|
||||
|
||||
cbor_dump_value_t *value = MEM_ALLOC_EXTRAM(sizeof(cbor_dump_value_t));
|
||||
value->cnt = -1;
|
||||
value->it = it;
|
||||
value->nesting_level = 0;
|
||||
value->parent_type = CborMapType;
|
||||
|
||||
if (insights_dump_cbor_buffer(value) != CborNoError) {
|
||||
ESP_LOGI(TAG, "cbor dump failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool esp_insights_cbor_decoder_at_end(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
return cbor_value_at_end(&ctx->it[ctx->curr_itr]);
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cbor_decoder_advance(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
if (CborNoError == cbor_value_advance(&ctx->it[ctx->curr_itr])) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
CborType esp_insights_cbor_decode_get_value_type(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
return cbor_value_get_type(&ctx->it[ctx->curr_itr]);
|
||||
}
|
||||
|
||||
char *esp_insights_cbor_decoder_get_string(CborValue *val)
|
||||
{
|
||||
CborError ret = CborNoError;
|
||||
char *buf = NULL;
|
||||
size_t n;
|
||||
if (cbor_value_get_type(val) != CborTextStringType) {
|
||||
return NULL;
|
||||
}
|
||||
ret = cbor_value_dup_text_string(val, &buf, &n, val);
|
||||
if (ret == CborNoError) {
|
||||
return (char *) buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cbor_decoder_enter_container(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
CborError ret = CborNoError;
|
||||
int curr_itr = ctx->curr_itr;
|
||||
if (curr_itr >= INS_CBOR_MAX_DEPTH) {
|
||||
ESP_LOGE(TAG, "Cannot parse depth more than %d", INS_CBOR_MAX_DEPTH);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ret = cbor_value_enter_container(&ctx->it[curr_itr], &ctx->it[curr_itr + 1]);
|
||||
if (ret != CborNoError) {
|
||||
ESP_LOGE(TAG, "error entering container");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (esp_insights_cbor_decoder_at_end(ctx)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ctx->curr_itr++;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cbor_decoder_exit_container(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
CborError ret = CborNoError;
|
||||
if (ctx->curr_itr <= 0) {
|
||||
ESP_LOGE(TAG, "cannot exit, already at top");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ctx->curr_itr--;
|
||||
int curr_itr = ctx->curr_itr;
|
||||
ret = cbor_value_leave_container(&ctx->it[curr_itr], &ctx->it[curr_itr + 1]);
|
||||
if (ret != CborNoError) {
|
||||
ESP_LOGE(TAG, "error leaving container");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cbor_decoder_done(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
free(ctx);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
cbor_parse_ctx_t *esp_insights_cbor_decoder_start(const uint8_t *buffer, int len)
|
||||
{
|
||||
if (!buffer || len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
cbor_parse_ctx_t *ctx = calloc(1, sizeof(cbor_parse_ctx_t));
|
||||
if (!ctx) {
|
||||
ESP_LOGE(TAG, "failed to allocate cbor ctx");
|
||||
return NULL;
|
||||
}
|
||||
CborParser root_parser = ctx->root_parser;
|
||||
CborValue *it = &ctx->it[0];
|
||||
if (cbor_parser_init(buffer, len, 0, &root_parser, it) != CborNoError) {
|
||||
ESP_LOGE(TAG, "Error initializing cbor parser");
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file esp_insights_cbor_decoder.h
|
||||
* @brief this file contains useful abstractions on cbor
|
||||
*
|
||||
* @note please keep this file as utility, avoid taking insights decisions here
|
||||
*/
|
||||
|
||||
#include <cbor.h>
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
#define INS_CBOR_MAX_DEPTH 12 // depth to which we can traverse
|
||||
|
||||
typedef struct cbor_parse_ctx {
|
||||
CborParser root_parser;
|
||||
CborValue it[INS_CBOR_MAX_DEPTH + 1];
|
||||
int curr_itr;
|
||||
} cbor_parse_ctx_t;
|
||||
|
||||
cbor_parse_ctx_t *esp_insights_cbor_decoder_start(const uint8_t *buffer, int len);
|
||||
esp_err_t esp_insights_cbor_decoder_enter_and_check_value(cbor_parse_ctx_t *ctx, const char *val);
|
||||
bool esp_insights_cbor_decoder_at_end(cbor_parse_ctx_t *ctx);
|
||||
|
||||
esp_err_t esp_insights_cbor_decoder_advance(cbor_parse_ctx_t *ctx);
|
||||
CborType esp_insights_cbor_decode_get_value_type(cbor_parse_ctx_t *ctx);
|
||||
char *esp_insights_cbor_decoder_get_string(CborValue *val);
|
||||
|
||||
esp_err_t esp_insights_cbor_decoder_enter_container(cbor_parse_ctx_t *ctx);
|
||||
esp_err_t esp_insights_cbor_decoder_exit_container(cbor_parse_ctx_t *ctx);
|
||||
|
||||
/* Do cleanups if any */
|
||||
esp_err_t esp_insights_cbor_decoder_done(cbor_parse_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief decodes a cbor message and prints into json format
|
||||
*
|
||||
* @param buffer buffer to decode and print
|
||||
* @param len length of the buffer to decode
|
||||
* @return esp_err_t ESP_OK on success, apt error otherwise
|
||||
*/
|
||||
esp_err_t esp_insights_cbor_decode_dump(const uint8_t *buffer, int len);
|
@@ -0,0 +1,947 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include <cbor.h>
|
||||
#include <esp_diagnostics.h>
|
||||
#if CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE
|
||||
#include <esp_core_dump.h>
|
||||
#endif /* CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE */
|
||||
|
||||
#include <esp_diag_data_store.h>
|
||||
#include <esp_diagnostics_metrics.h>
|
||||
#include <esp_diagnostics_variables.h>
|
||||
#include <soc/soc_memory_layout.h>
|
||||
#include "esp_insights_cbor_encoder.h"
|
||||
|
||||
#define TAG "cbor_encoder"
|
||||
|
||||
#define METRICS_PATH_VALUE "M"
|
||||
#define VARIABLES_PATH_VALUE "P"
|
||||
|
||||
static CborEncoder s_encoder, s_result_map, s_diag_map, s_diag_data_map, s_diag_conf_map;
|
||||
static CborEncoder s_meta_encoder, s_meta_result_map, s_diag_meta_map, s_diag_meta_data_map;
|
||||
|
||||
#define CBOR_ENC_MAX_CBS 10
|
||||
static struct cbor_encoder_data {
|
||||
insights_cbor_encoder_cb_t cb[CBOR_ENC_MAX_CBS];
|
||||
int cb_cnt;
|
||||
} s_priv_data;
|
||||
|
||||
static inline void _cbor_encode_meta_hdr(CborEncoder *hdr_map, const rtc_store_meta_header_t *hdr);
|
||||
|
||||
esp_err_t esp_insights_cbor_encoder_register_meta_cb(insights_cbor_encoder_cb_t cb)
|
||||
{
|
||||
if (s_priv_data.cb_cnt == CBOR_ENC_MAX_CBS) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGV(TAG, "Registering callback %p", cb);
|
||||
s_priv_data.cb[s_priv_data.cb_cnt++] = cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_diag_begin(void *data, size_t data_size, const char *version)
|
||||
{
|
||||
cbor_encoder_init(&s_encoder, data, data_size, 0);
|
||||
cbor_encoder_create_map(&s_encoder, &s_result_map, 1);
|
||||
cbor_encode_text_stringz(&s_result_map, "diag");
|
||||
cbor_encoder_create_map(&s_result_map, &s_diag_map, CborIndefiniteLength);
|
||||
|
||||
cbor_encode_text_stringz(&s_diag_map, "ver");
|
||||
cbor_encode_text_stringz(&s_diag_map, version);
|
||||
|
||||
cbor_encode_text_stringz(&s_diag_map, "ts");
|
||||
cbor_encode_uint(&s_diag_map, esp_diag_timestamp_get());
|
||||
|
||||
// cbor_encode_text_stringz(&s_diag_map, "sha256");
|
||||
// cbor_encode_text_stringz(&s_diag_map, sha256);
|
||||
|
||||
// encode meta_data
|
||||
const rtc_store_meta_header_t *hdr = rtc_store_get_meta_record_current();
|
||||
_cbor_encode_meta_hdr(&s_diag_map, hdr);
|
||||
}
|
||||
|
||||
size_t esp_insights_cbor_encode_diag_end(void *data)
|
||||
{
|
||||
cbor_encoder_close_container(&s_result_map, &s_diag_map);
|
||||
cbor_encoder_close_container(&s_encoder, &s_result_map);
|
||||
return cbor_encoder_get_buffer_size(&s_encoder, data);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_diag_data_begin(void)
|
||||
{
|
||||
cbor_encode_text_stringz(&s_diag_map, "data");
|
||||
cbor_encoder_create_map(&s_diag_map, &s_diag_data_map, CborIndefiniteLength);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_diag_conf_data_begin(void)
|
||||
{
|
||||
cbor_encode_text_stringz(&s_diag_data_map, "configs");
|
||||
cbor_encoder_create_array(&s_diag_data_map, &s_diag_conf_map, CborIndefiniteLength);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_diag_data_end(void)
|
||||
{
|
||||
cbor_encoder_close_container(&s_diag_map, &s_diag_data_map);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_diag_conf_data_end(void)
|
||||
{
|
||||
cbor_encoder_close_container(&s_diag_data_map, &s_diag_conf_map);
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE
|
||||
void esp_insights_cbor_encode_diag_crash(esp_core_dump_summary_t *summary)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
CborEncoder crash_map, val_list, bt_list;
|
||||
|
||||
cbor_encode_text_stringz(&s_diag_data_map, "crash");
|
||||
cbor_encoder_create_map(&s_diag_data_map, &crash_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&crash_map, "ver");
|
||||
cbor_encode_uint(&crash_map, summary->core_dump_version);
|
||||
cbor_encode_text_stringz(&crash_map, "sha256");
|
||||
cbor_encode_text_stringz(&crash_map, (char *)summary->app_elf_sha256);
|
||||
cbor_encode_text_stringz(&crash_map, "task");
|
||||
cbor_encode_text_stringz(&crash_map, summary->exc_task);
|
||||
|
||||
cbor_encode_text_stringz(&crash_map, "exc_val");
|
||||
cbor_encoder_create_array(&crash_map, &val_list, CborIndefiniteLength);
|
||||
cbor_encode_uint(&val_list, summary->exc_pc);
|
||||
#if CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
cbor_encode_uint(&val_list, summary->ex_info.mcause);
|
||||
cbor_encode_uint(&val_list, summary->ex_info.mtval);
|
||||
|
||||
cbor_encoder_close_container(&crash_map, &val_list);
|
||||
|
||||
cbor_encode_text_stringz(&crash_map, "stackdump");
|
||||
cbor_encoder_create_array(&crash_map, &bt_list, CborIndefiniteLength);
|
||||
|
||||
// Interpret stackdump as an array of uint32_t
|
||||
uint32_t *s_dump =(uint32_t *)&summary->exc_bt_info.stackdump[0];
|
||||
|
||||
for (i = 0; i < (summary->exc_bt_info.dump_size / 4); i++) {
|
||||
cbor_encode_uint(&bt_list, s_dump[i]);
|
||||
}
|
||||
cbor_encoder_close_container(&crash_map, &bt_list);
|
||||
|
||||
// stackdump is treated as an array of uint32_t hence converting no. of bytes to no. of words
|
||||
cbor_encode_text_stringz(&crash_map, "dump_size");
|
||||
cbor_encode_uint(&crash_map, summary->exc_bt_info.dump_size / 4);
|
||||
|
||||
CborEncoder reg_list;
|
||||
cbor_encode_text_stringz(&crash_map, "mstatus");
|
||||
cbor_encode_uint(&crash_map, summary->ex_info.mstatus);
|
||||
cbor_encode_text_stringz(&crash_map, "mtvec");
|
||||
cbor_encode_uint(&crash_map, summary->ex_info.mtvec);
|
||||
cbor_encode_text_stringz(&crash_map, "ra");
|
||||
cbor_encode_uint(&crash_map, summary->ex_info.ra);
|
||||
cbor_encode_text_stringz(&crash_map, "sp");
|
||||
cbor_encode_uint(&crash_map, summary->ex_info.sp);
|
||||
cbor_encode_text_stringz(&crash_map, "a_reg");
|
||||
cbor_encoder_create_array(&crash_map, ®_list, CborIndefiniteLength);
|
||||
for (i = 0; i < 8; i++) {
|
||||
cbor_encode_uint(®_list, summary->ex_info.exc_a[i]);
|
||||
}
|
||||
cbor_encoder_close_container(&crash_map, ®_list);
|
||||
#else /* IDF_TARGET_ARCH_XTENSA */
|
||||
cbor_encode_uint(&val_list, summary->ex_info.exc_cause);
|
||||
cbor_encode_uint(&val_list, summary->ex_info.exc_vaddr);
|
||||
|
||||
cbor_encoder_close_container(&crash_map, &val_list);
|
||||
cbor_encode_text_stringz(&crash_map, "bt");
|
||||
cbor_encoder_create_array(&crash_map, &bt_list, CborIndefiniteLength);
|
||||
for (i = 0; i < summary->exc_bt_info.depth; i++) {
|
||||
cbor_encode_uint(&bt_list, summary->exc_bt_info.bt[i]);
|
||||
}
|
||||
cbor_encoder_close_container(&crash_map, &bt_list);
|
||||
|
||||
cbor_encode_text_stringz(&crash_map, "bt_corrupt");
|
||||
cbor_encode_boolean(&crash_map, summary->exc_bt_info.corrupted);
|
||||
|
||||
CborEncoder epcx_list, reg_list;
|
||||
cbor_encode_text_stringz(&crash_map, "a_reg");
|
||||
cbor_encoder_create_array(&crash_map, ®_list, CborIndefiniteLength);
|
||||
for (i = 0; i < 16; i++) {
|
||||
cbor_encode_uint(®_list, summary->ex_info.exc_a[i]);
|
||||
}
|
||||
cbor_encoder_close_container(&crash_map, ®_list);
|
||||
|
||||
cbor_encode_text_stringz(&crash_map, "epcx");
|
||||
cbor_encoder_create_array(&crash_map, &epcx_list, CborIndefiniteLength);
|
||||
for (i = 0; i < EPCx_REGISTER_COUNT; i++) {
|
||||
if (summary->ex_info.epcx_reg_bits & (1 << i)) {
|
||||
cbor_encode_uint(&epcx_list, summary->ex_info.epcx[i]);
|
||||
}
|
||||
}
|
||||
cbor_encoder_close_container(&crash_map, &epcx_list);
|
||||
#endif /* CONFIG_IDF_TARGET_ARCH_RISCV */
|
||||
cbor_encoder_close_container(&s_diag_data_map, &crash_map);
|
||||
}
|
||||
#endif /* CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE */
|
||||
|
||||
// use a scratch_pad to memcpy data before access
|
||||
// this avoids `potential` unaligned memory accesses as
|
||||
// data pointer we receive is not guaranteed to be word aligned
|
||||
static union encode_scratch_buf {
|
||||
#if (CONFIG_DIAG_ENABLE_METRICS || CONFIG_DIAG_ENABLE_VARIABLES)
|
||||
esp_diag_str_data_pt_t str_data_pt;
|
||||
esp_diag_data_pt_t data_pt;
|
||||
#endif
|
||||
esp_diag_log_data_t log_data_pt;
|
||||
char sha_sum[DIAG_HEX_SHA_SIZE + 1];
|
||||
} enc_scratch_buf;
|
||||
|
||||
static inline uint8_t to_hex_digit(unsigned val)
|
||||
{
|
||||
return (val < 10) ? ('0' + val) : ('a' + val - 10);
|
||||
}
|
||||
|
||||
void bytes_to_hex(uint8_t *src, uint8_t *dst, int in_len)
|
||||
{
|
||||
for (int i = 0; i < in_len; i++) {
|
||||
dst[2 * i] = to_hex_digit(src[i] >> 4);
|
||||
dst[2 * i + 1] = to_hex_digit(src[i] & 0xf);
|
||||
}
|
||||
dst[2 * in_len] = 0;
|
||||
}
|
||||
|
||||
static inline void _cbor_encode_meta_hdr(CborEncoder *hdr_map, const rtc_store_meta_header_t *hdr)
|
||||
{
|
||||
cbor_encode_text_stringz(hdr_map, "sha256");
|
||||
bytes_to_hex((uint8_t *) hdr->sha_sum, (uint8_t *) enc_scratch_buf.sha_sum, DIAG_SHA_SIZE); // expand uint8 packed data to hex
|
||||
cbor_encode_text_stringz(hdr_map, enc_scratch_buf.sha_sum);
|
||||
cbor_encode_text_stringz(hdr_map, "gen_id");
|
||||
cbor_encode_uint(hdr_map, hdr->gen_id);
|
||||
cbor_encode_text_stringz(hdr_map, "boot_cnt");
|
||||
cbor_encode_uint(hdr_map, hdr->boot_cnt);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_diag_boot_info(esp_diag_device_info_t *device_info)
|
||||
{
|
||||
CborEncoder boot_map;
|
||||
cbor_encode_text_stringz(&s_diag_data_map, "boot");
|
||||
cbor_encoder_create_map(&s_diag_data_map, &boot_map, CborIndefiniteLength);
|
||||
|
||||
/* xTaskGetTickCount() API returns count of ticks since start of scheduler
|
||||
* For boot timestamp, we subtract the ticks since boot to get closest timestamp to bootup
|
||||
*/
|
||||
cbor_encode_text_stringz(&boot_map, "ts");
|
||||
cbor_encode_uint(&boot_map, esp_diag_timestamp_get() - (uint64_t)(pdTICKS_TO_MS(xTaskGetTickCount()) * 1000));
|
||||
|
||||
cbor_encode_text_stringz(&boot_map, "chip");
|
||||
cbor_encode_uint(&boot_map, device_info->chip_model);
|
||||
cbor_encode_text_stringz(&boot_map, "chip_rev");
|
||||
cbor_encode_uint(&boot_map, device_info->chip_rev);
|
||||
cbor_encode_text_stringz(&boot_map, "reason");
|
||||
cbor_encode_uint(&boot_map, device_info->reset_reason);
|
||||
cbor_encode_text_stringz(&boot_map, "proj");
|
||||
cbor_encode_text_stringz(&boot_map, device_info->project_name);
|
||||
cbor_encode_text_stringz(&boot_map, "app_ver");
|
||||
cbor_encode_text_stringz(&boot_map, device_info->app_version);
|
||||
|
||||
cbor_encoder_close_container(&s_diag_data_map, &boot_map);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_meta_c_hdr(const rtc_store_meta_header_t *hdr)
|
||||
{
|
||||
CborEncoder hdr_map;
|
||||
cbor_encode_text_stringz(&s_diag_data_map, "meta_c");
|
||||
cbor_encoder_create_map(&s_diag_data_map, &hdr_map, CborIndefiniteLength);
|
||||
|
||||
CborEncoder map_list;
|
||||
cbor_encode_text_stringz(&hdr_map, "maps_to");
|
||||
cbor_encoder_create_array(&hdr_map, &map_list, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&map_list, "traces");
|
||||
cbor_encoder_close_container(&hdr_map, &map_list);
|
||||
|
||||
_cbor_encode_meta_hdr(&hdr_map, hdr);
|
||||
cbor_encoder_close_container(&s_diag_data_map, &hdr_map);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_meta_nc_hdr(const rtc_store_meta_header_t *hdr)
|
||||
{
|
||||
CborEncoder hdr_map;
|
||||
cbor_encode_text_stringz(&s_diag_data_map, "meta_nc");
|
||||
cbor_encoder_create_map(&s_diag_data_map, &hdr_map, CborIndefiniteLength);
|
||||
|
||||
CborEncoder map_list;
|
||||
cbor_encode_text_stringz(&hdr_map, "maps_to");
|
||||
cbor_encoder_create_array(&hdr_map, &map_list, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&map_list, "metrics");
|
||||
cbor_encode_text_stringz(&map_list, "params");
|
||||
cbor_encoder_close_container(&hdr_map, &map_list);
|
||||
|
||||
_cbor_encode_meta_hdr(&hdr_map, hdr);
|
||||
cbor_encoder_close_container(&s_diag_data_map, &hdr_map);
|
||||
}
|
||||
|
||||
static void encode_msg_args(CborEncoder *element, uint8_t *args, uint8_t args_len)
|
||||
{
|
||||
#ifdef CONFIG_DIAG_LOG_MSG_ARG_FORMAT_TLV
|
||||
uint8_t type, len, i = 0;
|
||||
CborEncoder arg_list;
|
||||
esp_diag_arg_value_t arg_val;
|
||||
|
||||
cbor_encoder_create_array(element, &arg_list, CborIndefiniteLength);
|
||||
if (!args || !args_len) {
|
||||
cbor_encoder_close_container(element, &arg_list);
|
||||
return;
|
||||
}
|
||||
while (i < args_len) {
|
||||
memset(&arg_val, 0, sizeof(arg_val));
|
||||
type = args[i++];
|
||||
len = args[i++];
|
||||
switch(type) {
|
||||
case ARG_TYPE_CHAR:
|
||||
case ARG_TYPE_UCHAR:
|
||||
{
|
||||
cbor_encode_simple_value(&arg_list, args[i]);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_SHORT:
|
||||
{
|
||||
memcpy(&arg_val.s, args + i, len);
|
||||
if (arg_val.s < 0) {
|
||||
cbor_encode_negative_int(&arg_list, -arg_val.s);
|
||||
} else {
|
||||
cbor_encode_int(&arg_list, arg_val.s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_INT:
|
||||
{
|
||||
memcpy(&arg_val.i, args + i, len);
|
||||
if (arg_val.i < 0) {
|
||||
cbor_encode_negative_int(&arg_list, -arg_val.i);
|
||||
} else {
|
||||
cbor_encode_int(&arg_list, arg_val.i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_L:
|
||||
{
|
||||
memcpy(&arg_val.l, args + i, len);
|
||||
if (arg_val.l < 0) {
|
||||
cbor_encode_negative_int(&arg_list, -arg_val.l);
|
||||
} else {
|
||||
cbor_encode_int(&arg_list, arg_val.l);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_LL:
|
||||
{
|
||||
memcpy(&arg_val.ll, args + i, len);
|
||||
if (arg_val.ll < 0) {
|
||||
cbor_encode_negative_int(&arg_list, -arg_val.ll);
|
||||
} else {
|
||||
cbor_encode_int(&arg_list, arg_val.ll);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_PTRDIFF:
|
||||
{
|
||||
memcpy(&arg_val.ptrdiff, args + i, len);
|
||||
if (arg_val.ptrdiff < 0) {
|
||||
cbor_encode_negative_int(&arg_list, -arg_val.ptrdiff);
|
||||
} else {
|
||||
cbor_encode_int(&arg_list, arg_val.ptrdiff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_INTMAX:
|
||||
{
|
||||
memcpy(&arg_val.imx, args + i, len);
|
||||
if (arg_val.imx < 0) {
|
||||
cbor_encode_negative_int(&arg_list, -arg_val.imx);
|
||||
} else {
|
||||
cbor_encode_int(&arg_list, arg_val.imx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_USHORT:
|
||||
{
|
||||
memcpy(&arg_val.us, args + i, len);
|
||||
cbor_encode_uint(&arg_list, arg_val.us);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_UINT:
|
||||
{
|
||||
memcpy(&arg_val.u, args + i, len);
|
||||
cbor_encode_uint(&arg_list, arg_val.u);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_UL:
|
||||
{
|
||||
memcpy(&arg_val.ul, args + i, len);
|
||||
cbor_encode_uint(&arg_list, arg_val.ul);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_ULL:
|
||||
{
|
||||
memcpy(&arg_val.ull, args + i, len);
|
||||
cbor_encode_uint(&arg_list, arg_val.ull);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_SIZE:
|
||||
{
|
||||
memcpy(&arg_val.sz, args + i, len);
|
||||
cbor_encode_uint(&arg_list, arg_val.sz);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_UINTMAX:
|
||||
{
|
||||
memcpy(&arg_val.umx, args + i, len);
|
||||
cbor_encode_uint(&arg_list, arg_val.umx);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_DOUBLE:
|
||||
{
|
||||
memcpy(&arg_val.d, args + i, len);
|
||||
cbor_encode_double(&arg_list, arg_val.d);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_LDOUBLE:
|
||||
{
|
||||
memcpy(&arg_val.ld, args + i, len);
|
||||
cbor_encode_double(&arg_list, arg_val.ld);
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_STR:
|
||||
cbor_encode_text_string(&arg_list, (char *)(args + i), len);
|
||||
break;
|
||||
}
|
||||
i += len;
|
||||
}
|
||||
cbor_encoder_close_container(element, &arg_list);
|
||||
#else
|
||||
cbor_encode_text_stringz(element, (char *)args);
|
||||
#endif /* CONFIG_DIAG_LOG_MSG_ARG_FORMAT_TLV */
|
||||
}
|
||||
|
||||
static void encode_log_element(CborEncoder *list, esp_diag_log_data_t *data)
|
||||
{
|
||||
CborEncoder element;
|
||||
esp_diag_log_data_t *log = &enc_scratch_buf.log_data_pt;
|
||||
// copy at aligned address to avoid potential alignment issue
|
||||
memcpy(log, data, sizeof(esp_diag_log_data_t));
|
||||
|
||||
cbor_encoder_create_map(list, &element, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&element, "ts");
|
||||
cbor_encode_uint(&element, log->timestamp);
|
||||
cbor_encode_text_stringz(&element, "tag");
|
||||
cbor_encode_text_stringz(&element, log->tag);
|
||||
cbor_encode_text_stringz(&element, "pc");
|
||||
cbor_encode_uint(&element, log->pc);
|
||||
cbor_encode_text_stringz(&element, "ro");
|
||||
cbor_encode_uint(&element, (uint32_t)log->msg_ptr);
|
||||
cbor_encode_text_stringz(&element, "av");
|
||||
encode_msg_args(&element, log->msg_args, log->msg_args_len);
|
||||
if (strlen(log->task_name) > 0) {
|
||||
cbor_encode_text_stringz(&element, "task");
|
||||
cbor_encode_text_stringz(&element, log->task_name);
|
||||
}
|
||||
cbor_encoder_close_container(list, &element);
|
||||
}
|
||||
|
||||
static size_t encode_log_list(CborEncoder *map, esp_diag_log_type_t type,
|
||||
const char *key, const uint8_t *data, size_t size)
|
||||
{
|
||||
int i = 0, len = 0;
|
||||
CborEncoder list;
|
||||
esp_diag_log_data_t *log = NULL;
|
||||
cbor_encode_text_stringz(map, key);
|
||||
cbor_encoder_create_array(map, &list, CborIndefiniteLength);
|
||||
uint8_t meta_idx = data[0];
|
||||
while (size > sizeof (esp_diag_log_data_t)) {
|
||||
if (data[i] != meta_idx) {
|
||||
#if INSIGHTS_DEBUG_ENABLED
|
||||
printf("%s: skip data for next iteration meta: %d, data[i]: %d, itr: %d\n",
|
||||
"insights_cbor_enocoder", meta_idx, data[i], i);
|
||||
#endif
|
||||
break; // do not encode for next meta info
|
||||
}
|
||||
i += 1; // skip meta byte
|
||||
size -= 1;
|
||||
if (data[i] == type) {
|
||||
log = (esp_diag_log_data_t *)&data[i];
|
||||
encode_log_element(&list, log);
|
||||
}
|
||||
len = sizeof(esp_diag_log_data_t);
|
||||
i += len;
|
||||
size -= len ;
|
||||
}
|
||||
cbor_encoder_close_container(map, &list);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* The TinyCBOR library does not support DOM (Document Object Model)-like API.
|
||||
* So, we need to traverse through the entire data to encode every type of log.
|
||||
*/
|
||||
size_t esp_insights_cbor_encode_diag_logs(const uint8_t *data, size_t size)
|
||||
{
|
||||
CborEncoder log_map;
|
||||
cbor_encode_text_stringz(&s_diag_data_map, "traces");
|
||||
cbor_encoder_create_map(&s_diag_data_map, &log_map, CborIndefiniteLength);
|
||||
size_t consumed = 0, consumed_max = 0;
|
||||
consumed_max = encode_log_list(&log_map, ESP_DIAG_LOG_TYPE_ERROR, "errors", data, size);
|
||||
consumed = encode_log_list(&log_map, ESP_DIAG_LOG_TYPE_WARNING, "warnings", data, size);
|
||||
if (consumed > consumed_max) {
|
||||
consumed_max = consumed;
|
||||
}
|
||||
consumed = encode_log_list(&log_map, ESP_DIAG_LOG_TYPE_EVENT, "events", data, size);
|
||||
if (consumed > consumed_max) {
|
||||
consumed_max = consumed;
|
||||
}
|
||||
cbor_encoder_close_container(&s_diag_data_map, &log_map);
|
||||
return consumed_max;
|
||||
}
|
||||
|
||||
#if (CONFIG_DIAG_ENABLE_METRICS || CONFIG_DIAG_ENABLE_VARIABLES)
|
||||
// {"n":<key>, "v": <value>, "t": <ts> }
|
||||
static void encode_str_data_pt(CborEncoder *array, const uint8_t *data)
|
||||
{
|
||||
CborEncoder map;
|
||||
cbor_encoder_create_map(array, &map, CborIndefiniteLength);
|
||||
esp_diag_str_data_pt_t *m_data = &enc_scratch_buf.str_data_pt;
|
||||
// copy at aligned address to avoid potential alignment issue
|
||||
memcpy(m_data, data, sizeof(esp_diag_str_data_pt_t));
|
||||
cbor_encode_text_stringz(&map, "n");
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
char temp_path_key[10] = {0};
|
||||
snprintf(temp_path_key, sizeof(temp_path_key), "%s", ((m_data->type & 0xffff)==ESP_DIAG_DATA_PT_METRICS)?METRICS_PATH_VALUE:VARIABLES_PATH_VALUE);
|
||||
CborEncoder key_arr;
|
||||
cbor_encoder_create_array(&map, &key_arr, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&key_arr, temp_path_key);
|
||||
cbor_encode_text_stringz(&key_arr, m_data->tag);
|
||||
cbor_encode_text_stringz(&key_arr, m_data->key);
|
||||
cbor_encoder_close_container(&map, &key_arr);
|
||||
#else
|
||||
cbor_encode_text_stringz(&map, m_data->key);
|
||||
#endif
|
||||
cbor_encode_text_stringz(&map, "v");
|
||||
cbor_encode_text_stringz(&map, m_data->value.str);
|
||||
cbor_encode_text_stringz(&map, "t");
|
||||
cbor_encode_uint(&map, m_data->ts);
|
||||
|
||||
cbor_encoder_close_container(array, &map);
|
||||
}
|
||||
|
||||
static void encode_data_pt(CborEncoder *array, const uint8_t *data)
|
||||
{
|
||||
CborEncoder map;
|
||||
cbor_encoder_create_map(array, &map, CborIndefiniteLength);
|
||||
esp_diag_data_pt_t *m_data = &enc_scratch_buf.data_pt;
|
||||
// copy at aligned address to avoid potential alignment issue
|
||||
memcpy(m_data, data, sizeof(esp_diag_data_pt_t));
|
||||
cbor_encode_text_stringz(&map, "n");
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
char temp_path_key[10] = {0};
|
||||
snprintf(temp_path_key, sizeof(temp_path_key), "%s", ((m_data->type & 0xffff)==ESP_DIAG_DATA_PT_METRICS)?METRICS_PATH_VALUE:VARIABLES_PATH_VALUE);
|
||||
CborEncoder key_arr;
|
||||
cbor_encoder_create_array(&map, &key_arr, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&key_arr, temp_path_key);
|
||||
cbor_encode_text_stringz(&key_arr, m_data->tag);
|
||||
cbor_encode_text_stringz(&key_arr, m_data->key);
|
||||
cbor_encoder_close_container(&map, &key_arr);
|
||||
#else
|
||||
cbor_encode_text_stringz(&map, m_data->key);
|
||||
#endif
|
||||
cbor_encode_text_stringz(&map, "v");
|
||||
switch (m_data->data_type) {
|
||||
case ESP_DIAG_DATA_TYPE_BOOL:
|
||||
cbor_encode_boolean(&map, m_data->value.b);
|
||||
break;
|
||||
case ESP_DIAG_DATA_TYPE_INT:
|
||||
if (m_data->value.i < 0) {
|
||||
cbor_encode_negative_int(&map, -(m_data->value.i));
|
||||
} else {
|
||||
cbor_encode_int(&map, m_data->value.i);
|
||||
}
|
||||
break;
|
||||
case ESP_DIAG_DATA_TYPE_UINT:
|
||||
cbor_encode_uint(&map, m_data->value.u);
|
||||
break;
|
||||
case ESP_DIAG_DATA_TYPE_FLOAT:
|
||||
cbor_encode_float(&map, m_data->value.f);
|
||||
break;
|
||||
case ESP_DIAG_DATA_TYPE_IPv4:
|
||||
cbor_encode_byte_string(&map, (uint8_t *)&m_data->value.ipv4, sizeof(m_data->value.ipv4));
|
||||
break;
|
||||
case ESP_DIAG_DATA_TYPE_MAC:
|
||||
cbor_encode_byte_string(&map, &m_data->value.mac[0], sizeof(m_data->value.mac));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cbor_encode_text_stringz(&map, "t");
|
||||
cbor_encode_uint(&map, m_data->ts);
|
||||
|
||||
cbor_encoder_close_container(array, &map);
|
||||
}
|
||||
|
||||
static size_t encode_data_points(const uint8_t *data, size_t size, const char *key, uint16_t type)
|
||||
{
|
||||
assert(key);
|
||||
size_t i = 0;
|
||||
CborEncoder array;
|
||||
/* FIXME */
|
||||
rtc_store_non_critical_data_hdr_t header;
|
||||
esp_diag_data_type_t data_type;
|
||||
|
||||
if (!data || (size <= sizeof(header))) {
|
||||
printf("%s: Invalid arg! data %p, size %d. line %d\n",
|
||||
"insights_cbor_enocoder", data, size, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
cbor_encode_text_stringz(&s_diag_data_map, key);
|
||||
cbor_encoder_create_array(&s_diag_data_map, &array, CborIndefiniteLength);
|
||||
|
||||
uint8_t meta_idx = data[0];
|
||||
while (size > sizeof(header)) { // if remaining
|
||||
if (data[i] != meta_idx) {
|
||||
#if INSIGHTS_DEBUG_ENABLED
|
||||
printf("%s: skip data for next iteration meta: %d, data[i]: %d, itr: %d\n",
|
||||
"insights_cbor_enocoder", meta_idx, data[i], i);
|
||||
#endif
|
||||
break; // do not encode for next meta info
|
||||
}
|
||||
i += 1; // skip meta_idx byte
|
||||
size -= 1;
|
||||
|
||||
memcpy(&header, data + i, sizeof(header));
|
||||
if (sizeof(header) + header.len > size) {
|
||||
#if INSIGHTS_DEBUG_ENABLED
|
||||
// partial record
|
||||
printf("%s: partial record, needed %d, size %d\n",
|
||||
"insights_cbor_enocoder", sizeof(header) + header.len, size);
|
||||
#endif
|
||||
i -= 1;
|
||||
size += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!header.len) {
|
||||
#if INSIGHTS_DEBUG_ENABLED
|
||||
// invalid record
|
||||
printf("%s: invalid record, header.len %d\n", "insights_cbor_enocoder", header.len);
|
||||
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("cbor_enc", data, size, ESP_LOG_INFO);
|
||||
#endif
|
||||
i -= 1;
|
||||
size += 1;
|
||||
break;
|
||||
}
|
||||
uint32_t type_int;
|
||||
memcpy(&type_int, &data[i + sizeof(header)], 4); // copy, (b'cos alignment!)
|
||||
if ((type_int & 0xffff) == type) {
|
||||
data_type = (type_int >> 16) & 0xffff;
|
||||
if (data_type == ESP_DIAG_DATA_TYPE_STR && header.len == sizeof(esp_diag_str_data_pt_t)) {
|
||||
encode_str_data_pt(&array, data + i + sizeof(header));
|
||||
} else if (header.len == sizeof(esp_diag_data_pt_t)) {
|
||||
encode_data_pt(&array, data + i + sizeof(header));
|
||||
}
|
||||
}
|
||||
size -= (sizeof(header) + header.len);
|
||||
i += (sizeof(header) + header.len);
|
||||
}
|
||||
cbor_encoder_close_container(&s_diag_data_map, &array);
|
||||
return i;
|
||||
}
|
||||
#endif /* (CONFIG_DIAG_ENABLE_METRICS || CONFIG_DIAG_ENABLE_VARIABLES) */
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_METRICS
|
||||
size_t esp_insights_cbor_encode_diag_metrics(const uint8_t *data, size_t size)
|
||||
{
|
||||
return encode_data_points(data, size, "metrics", ESP_DIAG_DATA_PT_METRICS);
|
||||
}
|
||||
#endif /* CONFIG_DIAG_ENABLE_METRICS */
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_VARIABLES
|
||||
size_t esp_insights_cbor_encode_diag_variables(const uint8_t *data, size_t size)
|
||||
{
|
||||
return encode_data_points(data, size, "params", ESP_DIAG_DATA_PT_VARIABLE);
|
||||
}
|
||||
#endif /* CONFIG_DIAG_ENABLE_VARIABLES */
|
||||
|
||||
/* Below are the helpers to encode esp insights meta data */
|
||||
|
||||
void esp_insights_cbor_encode_meta_begin(void *data, size_t data_size, const char *version, const char *sha256)
|
||||
{
|
||||
cbor_encoder_init(&s_meta_encoder, data, data_size, 0);
|
||||
cbor_encoder_create_map(&s_meta_encoder, &s_meta_result_map, 1);
|
||||
cbor_encode_text_stringz(&s_meta_result_map, "diagmeta");
|
||||
cbor_encoder_create_map(&s_meta_result_map, &s_diag_meta_map, CborIndefiniteLength);
|
||||
|
||||
cbor_encode_text_stringz(&s_diag_meta_map, "ver");
|
||||
cbor_encode_text_stringz(&s_diag_meta_map, version);
|
||||
|
||||
cbor_encode_text_stringz(&s_diag_meta_map, "ts");
|
||||
cbor_encode_uint(&s_diag_meta_map, esp_diag_timestamp_get());
|
||||
cbor_encode_text_stringz(&s_diag_meta_map, "sha256");
|
||||
cbor_encode_text_stringz(&s_diag_meta_map, sha256);
|
||||
}
|
||||
|
||||
size_t esp_insights_cbor_encode_meta_end(void *data)
|
||||
{
|
||||
cbor_encoder_close_container(&s_meta_result_map, &s_diag_meta_map);
|
||||
cbor_encoder_close_container(&s_meta_encoder, &s_meta_result_map);
|
||||
return cbor_encoder_get_buffer_size(&s_meta_encoder, data);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_meta_data_begin(void)
|
||||
{
|
||||
cbor_encode_text_stringz(&s_diag_meta_map, "data");
|
||||
cbor_encoder_create_map(&s_diag_meta_map, &s_diag_meta_data_map, CborIndefiniteLength);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_meta_data_end(void)
|
||||
{
|
||||
cbor_encoder_close_container(&s_diag_meta_map, &s_diag_meta_data_map);
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_conf_meta_data_begin(void)
|
||||
{
|
||||
cbor_encode_text_stringz(&s_diag_meta_map, "data");
|
||||
cbor_encoder_create_map(&s_diag_meta_map, &s_diag_meta_data_map, CborIndefiniteLength);
|
||||
#ifdef NEW_META_STRUCT
|
||||
for (int i = 0; i < s_priv_data.cb_cnt; i++) {
|
||||
if (s_priv_data.cb[i]) {
|
||||
s_priv_data.cb[i] (&s_diag_meta_data_map, INSIGHTS_MSG_TYPE_META);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Vikram: remove? */
|
||||
void esp_insights_cbor_encode_conf_meta_data_end(void)
|
||||
{
|
||||
cbor_encoder_close_container(&s_diag_meta_map, &s_diag_meta_data_map);
|
||||
}
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_METRICS
|
||||
static void encode_metrics_meta_element(CborEncoder *map, const esp_diag_metrics_meta_t *metrics)
|
||||
{
|
||||
CborEncoder id_map;
|
||||
#ifdef NEW_META_STRUCT
|
||||
CborEncoder m_map;
|
||||
#endif
|
||||
cbor_encode_text_stringz(map, metrics->key);
|
||||
#ifdef NEW_META_STRUCT
|
||||
cbor_encoder_create_map(map, &m_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&m_map, "m"); // a metrics meta entry
|
||||
cbor_encoder_create_map(&m_map, &id_map, CborIndefiniteLength);
|
||||
#else
|
||||
cbor_encoder_create_map(map, &id_map, CborIndefiniteLength);
|
||||
#endif
|
||||
#ifndef TAG_IS_OUTER_KEY
|
||||
cbor_encode_text_stringz(&id_map, "tag");
|
||||
cbor_encode_text_stringz(&id_map, metrics->tag);
|
||||
#endif
|
||||
cbor_encode_text_stringz(&id_map, "label");
|
||||
cbor_encode_text_stringz(&id_map, metrics->label);
|
||||
cbor_encode_text_stringz(&id_map, "path");
|
||||
cbor_encode_text_stringz(&id_map, metrics->path);
|
||||
cbor_encode_text_stringz(&id_map, "data_type");
|
||||
cbor_encode_uint(&id_map, metrics->type);
|
||||
if (metrics->unit) {
|
||||
cbor_encode_text_stringz(&id_map, "unit");
|
||||
cbor_encode_text_stringz(&id_map, metrics->unit);
|
||||
}
|
||||
#ifdef NEW_META_STRUCT
|
||||
cbor_encoder_close_container(&m_map, &id_map); // close metrics
|
||||
cbor_encoder_close_container(map, &m_map);
|
||||
#else
|
||||
cbor_encoder_close_container(map, &id_map);
|
||||
#endif
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_meta_metrics(const esp_diag_metrics_meta_t *metrics, uint32_t metrics_len)
|
||||
{
|
||||
if (!metrics || !metrics_len) {
|
||||
return;
|
||||
}
|
||||
CborEncoder map;
|
||||
#ifdef NEW_META_STRUCT
|
||||
CborEncoder metrics_map;
|
||||
cbor_encode_text_stringz(&s_diag_meta_data_map, METRICS_PATH_VALUE);
|
||||
cbor_encoder_create_map(&s_diag_meta_data_map, &metrics_map, CborIndefiniteLength);
|
||||
// d: decendants map
|
||||
cbor_encode_text_stringz(&metrics_map, "d");
|
||||
cbor_encoder_create_map(&metrics_map, &map, CborIndefiniteLength);
|
||||
// d: descendants map
|
||||
// enabled config for data
|
||||
#else
|
||||
cbor_encode_text_stringz(&s_diag_meta_data_map, "metrics");
|
||||
cbor_encoder_create_map(&s_diag_meta_data_map, &map, CborIndefiniteLength);
|
||||
#endif
|
||||
#ifndef TAG_IS_OUTER_KEY
|
||||
for (int i = 0; i < metrics_len; i++) {
|
||||
encode_metrics_meta_element(&map, (metrics + i));
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < metrics_len; i++) {
|
||||
const esp_diag_metrics_meta_t *metrics_i = metrics + i;
|
||||
// check if this group was already encoded
|
||||
bool encoded = false;
|
||||
for (int j = 0; j < i; j++) {
|
||||
const esp_diag_metrics_meta_t *metrics_j = metrics + j;
|
||||
// ESP_LOGI(TAG, "Comparing tags %s %s", metrics_i->tag, metrics_j->tag);
|
||||
if (metrics_j->tag == metrics_i->tag) {
|
||||
encoded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (encoded == false) {
|
||||
// encode all the metrices with the group
|
||||
ESP_LOGD(TAG, "Encoding new tag %s", metrics_i->tag);
|
||||
CborEncoder tag_map;
|
||||
cbor_encode_text_stringz(&map, metrics_i->tag);
|
||||
#ifdef NEW_META_STRUCT
|
||||
CborEncoder tag_d_map;
|
||||
cbor_encoder_create_map(&map, &tag_d_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&tag_d_map, "d");
|
||||
cbor_encoder_create_map(&tag_d_map, &tag_map, CborIndefiniteLength);
|
||||
#else
|
||||
cbor_encoder_create_map(&map, &tag_map, CborIndefiniteLength);
|
||||
#endif
|
||||
for (int j = i; j < metrics_len; j++) {
|
||||
const esp_diag_metrics_meta_t *metrics_j = metrics + j;
|
||||
if (metrics_j->tag == metrics_i->tag) {
|
||||
ESP_LOGD(TAG, "Encoding key %s", metrics_j->key);
|
||||
encode_metrics_meta_element(&tag_map, metrics_j);
|
||||
}
|
||||
}
|
||||
#ifdef NEW_META_STRUCT
|
||||
cbor_encoder_close_container(&tag_d_map, &tag_map);
|
||||
cbor_encoder_close_container(&map, &tag_d_map);
|
||||
#else
|
||||
cbor_encoder_close_container(&map, &tag_map);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef NEW_META_STRUCT
|
||||
// close d: decendants
|
||||
cbor_encoder_close_container(&metrics_map, &map);
|
||||
cbor_encoder_close_container(&s_diag_meta_data_map, &metrics_map);
|
||||
#else
|
||||
cbor_encoder_close_container(&s_diag_meta_data_map, &map);
|
||||
#endif
|
||||
}
|
||||
#endif /* CONFIG_DIAG_ENABLE_METRICS */
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_VARIABLES
|
||||
static void encode_variable_meta_element(CborEncoder *map, const esp_diag_variable_meta_t *variable)
|
||||
{
|
||||
CborEncoder id_map;
|
||||
#ifdef NEW_META_STRUCT
|
||||
CborEncoder m_map;
|
||||
#endif
|
||||
cbor_encode_text_stringz(map, variable->key);
|
||||
#ifdef NEW_META_STRUCT
|
||||
cbor_encoder_create_map(map, &m_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&m_map, "p"); // a variable meta entry
|
||||
cbor_encoder_create_map(&m_map, &id_map, CborIndefiniteLength);
|
||||
#else
|
||||
cbor_encoder_create_map(map, &id_map, CborIndefiniteLength);
|
||||
#endif
|
||||
#ifndef TAG_IS_OUTER_KEY
|
||||
cbor_encode_text_stringz(&id_map, "tag");
|
||||
cbor_encode_text_stringz(&id_map, variable->tag);
|
||||
#endif
|
||||
cbor_encode_text_stringz(&id_map, "label");
|
||||
cbor_encode_text_stringz(&id_map, variable->label);
|
||||
cbor_encode_text_stringz(&id_map, "path");
|
||||
cbor_encode_text_stringz(&id_map, variable->path);
|
||||
cbor_encode_text_stringz(&id_map, "data_type");
|
||||
cbor_encode_uint(&id_map, variable->type);
|
||||
if (variable->unit) {
|
||||
cbor_encode_text_stringz(&id_map, "unit");
|
||||
cbor_encode_text_stringz(&id_map, variable->unit);
|
||||
}
|
||||
#ifdef NEW_META_STRUCT
|
||||
cbor_encoder_close_container(&m_map, &id_map); // close variable entry
|
||||
cbor_encoder_close_container(map, &m_map);
|
||||
#else
|
||||
cbor_encoder_close_container(map, &id_map);
|
||||
#endif
|
||||
}
|
||||
|
||||
void esp_insights_cbor_encode_meta_variables(const esp_diag_variable_meta_t *variables, uint32_t variables_len)
|
||||
{
|
||||
if (!variables || !variables_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
CborEncoder map;
|
||||
#ifdef NEW_META_STRUCT
|
||||
CborEncoder variables_map;
|
||||
cbor_encode_text_stringz(&s_diag_meta_data_map, VARIABLES_PATH_VALUE);
|
||||
cbor_encoder_create_map(&s_diag_meta_data_map, &variables_map, CborIndefiniteLength);
|
||||
// d: decendants map
|
||||
cbor_encode_text_stringz(&variables_map, "d");
|
||||
cbor_encoder_create_map(&variables_map, &map, CborIndefiniteLength);
|
||||
#else
|
||||
cbor_encode_text_stringz(&s_diag_meta_data_map, "params");
|
||||
cbor_encoder_create_map(&s_diag_meta_data_map, &map, CborIndefiniteLength);
|
||||
#endif
|
||||
#ifndef TAG_IS_OUTER_KEY
|
||||
for (int i = 0; i < variables_len; i++) {
|
||||
encode_variable_meta_element(&map, (variables + i));
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < variables_len; i++) {
|
||||
const esp_diag_variable_meta_t *variables_i = variables + i;
|
||||
// check if this group was already encoded
|
||||
bool encoded = false;
|
||||
for (int j = 0; j < i; j++) {
|
||||
const esp_diag_variable_meta_t *variables_j = variables + j;
|
||||
if (variables_j->tag == variables_i->tag) {
|
||||
encoded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (encoded == false) {
|
||||
CborEncoder tag_map;
|
||||
// encode all the metrices with the group
|
||||
cbor_encode_text_stringz(&map, variables_i->tag);
|
||||
#ifdef NEW_META_STRUCT
|
||||
CborEncoder tag_d_map;
|
||||
cbor_encoder_create_map(&map, &tag_d_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&tag_d_map, "d");
|
||||
cbor_encoder_create_map(&tag_d_map, &tag_map, CborIndefiniteLength);
|
||||
#else
|
||||
cbor_encoder_create_map(&map, &tag_map, CborIndefiniteLength);
|
||||
#endif
|
||||
for (int j = i; j < variables_len; j++) {
|
||||
const esp_diag_variable_meta_t *variables_j = variables + j;
|
||||
if (variables_j->tag == variables_i->tag) {
|
||||
encode_variable_meta_element(&tag_map, variables_j);
|
||||
}
|
||||
}
|
||||
#ifdef NEW_META_STRUCT
|
||||
cbor_encoder_close_container(&tag_d_map, &tag_map);
|
||||
cbor_encoder_close_container(&map, &tag_d_map);
|
||||
#else
|
||||
cbor_encoder_close_container(&map, &tag_map);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef NEW_META_STRUCT
|
||||
// close d: decendants
|
||||
cbor_encoder_close_container(&variables_map, &map);
|
||||
cbor_encoder_close_container(&s_diag_meta_data_map, &variables_map);
|
||||
#else
|
||||
cbor_encoder_close_container(&s_diag_meta_data_map, &map);
|
||||
#endif
|
||||
}
|
||||
#endif /* CONFIG_DIAG_ENABLE_VARIABLES */
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cbor.h>
|
||||
#include <esp_diagnostics_metrics.h>
|
||||
#include <esp_diagnostics_variables.h>
|
||||
#if CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE
|
||||
#include <esp_core_dump.h>
|
||||
#endif /* CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE */
|
||||
#include <rtc_store.h>
|
||||
|
||||
// make tag/group as a outer key and actual keys from it are contained within
|
||||
#ifndef CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
#define TAG_IS_OUTER_KEY 1
|
||||
#define NEW_META_STRUCT 1
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
INSIGHTS_MSG_TYPE_META,
|
||||
INSIGHTS_MSG_TYPE_DATA
|
||||
} insights_msg_type_t;
|
||||
|
||||
/**
|
||||
* @brief cbor encoder callback
|
||||
*
|
||||
* @param map parent map to which new data will be encoded
|
||||
* @param type information type to be collected
|
||||
*/
|
||||
typedef void (*insights_cbor_encoder_cb_t) (CborEncoder *map, insights_msg_type_t type);
|
||||
|
||||
/**
|
||||
* @brief register a meta collection callback
|
||||
*
|
||||
* @param cb callback of type \ref insights_cbor_encoder_cb_t
|
||||
*
|
||||
* @return ESP_OK on success, appropriate error otherwise
|
||||
*/
|
||||
esp_err_t esp_insights_cbor_encoder_register_meta_cb(insights_cbor_encoder_cb_t cb);
|
||||
|
||||
void esp_insights_cbor_encode_diag_begin(void *data, size_t data_size, const char *version);
|
||||
void esp_insights_cbor_encode_diag_data_begin(void);
|
||||
void esp_insights_cbor_encode_diag_boot_info(esp_diag_device_info_t *device_info);
|
||||
|
||||
/**
|
||||
* @brief encode master header
|
||||
*
|
||||
* @param hdr meta header which contains data regarding message it follows
|
||||
* @param type rtc_store type (viz., "critical", "non_critical")
|
||||
*/
|
||||
void esp_insights_cbor_encode_meta_c_hdr(const rtc_store_meta_header_t *hdr);
|
||||
void esp_insights_cbor_encode_meta_nc_hdr(const rtc_store_meta_header_t *hdr);
|
||||
|
||||
#if CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE
|
||||
void esp_insights_cbor_encode_diag_crash(esp_core_dump_summary_t *summary);
|
||||
#endif /* CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE */
|
||||
size_t esp_insights_cbor_encode_diag_logs(const uint8_t *data, size_t size);
|
||||
size_t esp_insights_cbor_encode_diag_metrics(const uint8_t *data, size_t size);
|
||||
size_t esp_insights_cbor_encode_diag_variables(const uint8_t *data, size_t size);
|
||||
void esp_insights_cbor_encode_diag_data_end(void);
|
||||
size_t esp_insights_cbor_encode_diag_end(void *data);
|
||||
|
||||
/* For encoding diag meta data */
|
||||
void esp_insights_cbor_encode_meta_begin(void *data, size_t data_size, const char *version, const char *sha256);
|
||||
void esp_insights_cbor_encode_meta_data_begin(void);
|
||||
#if CONFIG_DIAG_ENABLE_METRICS
|
||||
void esp_insights_cbor_encode_meta_metrics(const esp_diag_metrics_meta_t *metrics, uint32_t metrics_len);
|
||||
#endif /* CONFIG_DIAG_ENABLE_METRICS */
|
||||
#if CONFIG_DIAG_ENABLE_VARIABLES
|
||||
void esp_insights_cbor_encode_meta_variables(const esp_diag_variable_meta_t *variables, uint32_t variables_len);
|
||||
#endif /* CONFIG_DIAG_ENABLE_VARIABLES */
|
||||
void esp_insights_cbor_encode_meta_data_end(void);
|
||||
size_t esp_insights_cbor_encode_meta_end(void *data);
|
||||
|
||||
|
||||
/* For encoding conf data */
|
||||
void esp_insights_cbor_encode_conf_meta_begin(void *data, size_t data_size, const char *version, const char *sha256);
|
||||
void esp_insights_cbor_encode_conf_meta_data_begin(void);
|
||||
void esp_insights_cbor_encode_conf_meta_data_end(void);
|
||||
size_t esp_insights_cbor_encode_conf_meta_end(void *data);
|
||||
|
||||
void esp_insights_cbor_encode_diag_conf_data_begin(void);
|
||||
void esp_insights_cbor_encode_diag_conf_data_end(void);
|
||||
void esp_insights_cbor_encode_diag_conf_data(void);
|
||||
|
||||
/* For converting 8 bytes sha256 to hex form */
|
||||
void bytes_to_hex(uint8_t *src, uint8_t *dst, int in_len);
|
||||
|
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
#include <esp_rmaker_factory.h>
|
||||
|
||||
#define INSIGHTS_CLIENT_CERT_NVS_KEY "client_cert"
|
||||
#define INSIGHTS_CLIENT_KEY_NVS_KEY "client_key"
|
||||
#define INSIGHTS_MQTT_HOST_NVS_KEY "mqtt_host"
|
||||
#define INSIGHTS_NODE_ID "node_id"
|
||||
#define INSIGHTS_META_CRC_NVS_KEY "i_meta_crc"
|
||||
#define INSIGHTS_NVS_NAMESPACE "nvs"
|
||||
|
||||
extern uint8_t mqtt_server_root_ca_pem_start[] asm("_binary_mqtt_server_crt_start");
|
||||
extern uint8_t mqtt_server_root_ca_pem_end[] asm("_binary_mqtt_server_crt_end");
|
||||
|
||||
static char *esp_insights_get_mqtt_host(void)
|
||||
{
|
||||
return esp_rmaker_factory_get(INSIGHTS_MQTT_HOST_NVS_KEY);
|
||||
}
|
||||
|
||||
static char *esp_insights_get_client_cert(void)
|
||||
{
|
||||
return esp_rmaker_factory_get(INSIGHTS_CLIENT_CERT_NVS_KEY);
|
||||
}
|
||||
|
||||
static char *esp_insights_get_client_key(void)
|
||||
{
|
||||
return esp_rmaker_factory_get(INSIGHTS_CLIENT_KEY_NVS_KEY);
|
||||
}
|
||||
|
||||
esp_rmaker_mqtt_conn_params_t *esp_insights_get_mqtt_conn_params(void)
|
||||
{
|
||||
esp_rmaker_mqtt_conn_params_t *mqtt_conn_params = calloc(1, sizeof(esp_rmaker_mqtt_conn_params_t));
|
||||
if ((mqtt_conn_params->client_key = esp_insights_get_client_key()) == NULL) {
|
||||
goto init_err;
|
||||
}
|
||||
if ((mqtt_conn_params->client_cert = esp_insights_get_client_cert()) == NULL) {
|
||||
goto init_err;
|
||||
}
|
||||
if ((mqtt_conn_params->mqtt_host = esp_insights_get_mqtt_host()) == NULL) {
|
||||
goto init_err;
|
||||
}
|
||||
mqtt_conn_params->server_cert = (char *)mqtt_server_root_ca_pem_start;
|
||||
mqtt_conn_params->client_id = esp_rmaker_factory_get(INSIGHTS_NODE_ID);
|
||||
return mqtt_conn_params;
|
||||
init_err:
|
||||
if (mqtt_conn_params->mqtt_host) {
|
||||
free(mqtt_conn_params->mqtt_host);
|
||||
}
|
||||
if (mqtt_conn_params->client_cert) {
|
||||
free(mqtt_conn_params->client_cert);
|
||||
}
|
||||
if (mqtt_conn_params->client_key) {
|
||||
free(mqtt_conn_params->client_key);
|
||||
}
|
||||
free(mqtt_conn_params);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void esp_insights_clean_mqtt_conn_params(esp_rmaker_mqtt_conn_params_t *mqtt_conn_params)
|
||||
{
|
||||
if (mqtt_conn_params) {
|
||||
if (mqtt_conn_params->mqtt_host) {
|
||||
free(mqtt_conn_params->mqtt_host);
|
||||
}
|
||||
if (mqtt_conn_params->client_cert) {
|
||||
free(mqtt_conn_params->client_cert);
|
||||
}
|
||||
if (mqtt_conn_params->client_key) {
|
||||
free(mqtt_conn_params->client_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_meta_nvs_crc_get(uint32_t *crc)
|
||||
{
|
||||
if (!crc) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(INSIGHTS_NVS_NAMESPACE, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = nvs_get_u32(handle, INSIGHTS_META_CRC_NVS_KEY, crc);
|
||||
if (err != ESP_OK) {
|
||||
nvs_close(handle);
|
||||
return err;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_meta_nvs_crc_set(uint32_t crc)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(INSIGHTS_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = nvs_set_u32(handle, INSIGHTS_META_CRC_NVS_KEY, crc);
|
||||
if (err != ESP_OK) {
|
||||
nvs_close(handle);
|
||||
return err;
|
||||
}
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
return err;
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
esp_rmaker_mqtt_conn_params_t *esp_insights_get_mqtt_conn_params(void);
|
||||
void esp_insights_clean_mqtt_conn_params(esp_rmaker_mqtt_conn_params_t *mqtt_conn_params);
|
||||
esp_err_t esp_insights_meta_nvs_crc_get(uint32_t *crc);
|
||||
esp_err_t esp_insights_meta_nvs_crc_set(uint32_t crc);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,676 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file esp_insights_cmd_resp.c
|
||||
* @brief this file contains glue between received data parsing for insights config,
|
||||
* which eventually calls the commands it has registered to
|
||||
*/
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "insights_cmd_resp";
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_CMD_RESP_ENABLED
|
||||
#include <esp_rmaker_cmd_resp.h>
|
||||
|
||||
#include <esp_insights.h> /* for nodeID */
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#include "esp_insights_internal.h"
|
||||
#include "esp_insights_cbor_decoder.h"
|
||||
#include "esp_insights_cbor_encoder.h"
|
||||
|
||||
#define INS_CONF_STR "config"
|
||||
#define RMAKER_CFG_TOPIC_SUFFIX "config"
|
||||
#define TO_NODE_TOPIC_SUFFIX "to-node"
|
||||
#define FROM_NODE_TOPIC_SUFFIX "from-node"
|
||||
|
||||
/** This is a common command identifier for Insights and then Insights internally has its own commands and their handlers.
|
||||
*/
|
||||
#define INSIGHTS_CONF_CMD 0x101
|
||||
|
||||
/* depth is dictated by cmd_depth */
|
||||
#define MAX_CMD_DEPTH 10
|
||||
#define CMD_STORE_SIZE 10
|
||||
#define SCRATCH_BUF_SIZE (1 * 1024)
|
||||
|
||||
typedef esp_err_t (*esp_insights_cmd_cb_t)(const void *data, size_t data_len, const void *priv);
|
||||
|
||||
typedef struct {
|
||||
const char *cmd[MAX_CMD_DEPTH]; /* complete path of the command */
|
||||
int depth;
|
||||
esp_insights_cmd_cb_t cb; /* callback function */
|
||||
void *prv_data; /* pointer to the private data */
|
||||
} generic_cmd_t;
|
||||
|
||||
typedef struct {
|
||||
generic_cmd_t cmd_store[CMD_STORE_SIZE];
|
||||
int cmd_cnt; /* total registered commands */
|
||||
uint8_t *scratch_buf;
|
||||
bool enabled;
|
||||
bool init_done;
|
||||
} insights_cmd_resp_data_t;
|
||||
|
||||
static insights_cmd_resp_data_t s_cmd_resp_data;
|
||||
static bool reboot_report_pending = false;
|
||||
|
||||
static esp_err_t reboot_cmd_handler(const void *data, size_t data_len, const void *prv_data)
|
||||
{
|
||||
reboot_report_pending = true;
|
||||
ESP_LOGI(TAG, "rebooting in 5 seconds...");
|
||||
esp_rmaker_reboot(5);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void __collect_reboot_meta(CborEncoder *map)
|
||||
{
|
||||
cbor_encode_text_stringz(map, "reboot"); /* name of the config */
|
||||
CborEncoder conf_map, conf_data_map;
|
||||
cbor_encoder_create_map(map, &conf_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&conf_map, "c"); /* denotes this to be config */
|
||||
cbor_encoder_create_map(&conf_map, &conf_data_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&conf_data_map, "type");
|
||||
cbor_encode_uint(&conf_data_map, ESP_DIAG_DATA_TYPE_NULL); /* data_type */
|
||||
cbor_encoder_close_container(&conf_map, &conf_data_map);
|
||||
cbor_encoder_close_container(map, &conf_map);
|
||||
}
|
||||
|
||||
static void __collect_reboot_data(CborEncoder *map)
|
||||
{
|
||||
CborEncoder conf_map, key_arr;
|
||||
cbor_encoder_create_map(map, &conf_map, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&conf_map, "n");
|
||||
cbor_encoder_create_array(&conf_map, &key_arr, CborIndefiniteLength);
|
||||
cbor_encode_text_stringz(&key_arr, "reboot");
|
||||
cbor_encoder_close_container(&conf_map, &key_arr);
|
||||
cbor_encode_text_stringz(&conf_map, "t");
|
||||
cbor_encode_uint(&conf_map, esp_diag_timestamp_get());
|
||||
cbor_encoder_close_container(map, &conf_map);
|
||||
}
|
||||
|
||||
static void esp_insights_cbor_reboot_msg_cb(CborEncoder *map, insights_msg_type_t type)
|
||||
{
|
||||
if (type == INSIGHTS_MSG_TYPE_META) {
|
||||
__collect_reboot_meta(map);
|
||||
} else if (reboot_report_pending) {
|
||||
__collect_reboot_data(map);
|
||||
reboot_report_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cmd_resp_register_cmd(esp_insights_cmd_cb_t cb, void *prv_data, int cmd_depth, ...)
|
||||
{
|
||||
int idx = s_cmd_resp_data.cmd_cnt;
|
||||
|
||||
va_list valist;
|
||||
va_start(valist, cmd_depth);
|
||||
for (int i = 0; i < cmd_depth; i++) {
|
||||
s_cmd_resp_data.cmd_store[idx].cmd[i] = va_arg(valist, char *);
|
||||
}
|
||||
/* clean memory reserved for valist */
|
||||
va_end(valist);
|
||||
|
||||
s_cmd_resp_data.cmd_store[idx].depth = cmd_depth;
|
||||
s_cmd_resp_data.cmd_store[idx].prv_data = prv_data;
|
||||
s_cmd_resp_data.cmd_store[idx].cb = cb;
|
||||
s_cmd_resp_data.cmd_cnt += 1;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t insights_cmd_resp_search_execute_cmd_store(char **cmd_tree, int cmd_depth)
|
||||
{
|
||||
for(int i = 0; i< s_cmd_resp_data.cmd_cnt; i++) {
|
||||
if (cmd_depth == s_cmd_resp_data.cmd_store[i].depth) {
|
||||
bool match_found = true;
|
||||
/* the command depth matches, now go for whole path */
|
||||
for (int j = 0; j < cmd_depth; j++) {
|
||||
if (strcmp(cmd_tree[j], s_cmd_resp_data.cmd_store[i].cmd[j]) != 0) {
|
||||
match_found = false;
|
||||
break; /* break at first mismatch */
|
||||
}
|
||||
}
|
||||
if (match_found) {
|
||||
ESP_LOGI(TAG, "match found in cmd_store... Executing the callback");
|
||||
s_cmd_resp_data.cmd_store[i].cb(NULL, 0, s_cmd_resp_data.cmd_store[i].prv_data);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static void insights_cmd_parser_clear_cmd_tree(char *cmd_tree[])
|
||||
{
|
||||
for (int i = 0; i < MAX_CMD_DEPTH; i++) {
|
||||
if (cmd_tree[i]) {
|
||||
free(cmd_tree[i]);
|
||||
cmd_tree[i] = NULL;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void insights_cmd_parser_add_cmd_to_tree(char * cmd_tree[], char *cmd, int pos)
|
||||
{
|
||||
ESP_LOGV(TAG, "Adding %s to command path\n", cmd);
|
||||
/* free depth already consumed */
|
||||
for (int i = pos; i < MAX_CMD_DEPTH; i++) {
|
||||
if (cmd_tree[i]) {
|
||||
free(cmd_tree[i]);
|
||||
cmd_tree[i] = NULL;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* insert at the position */
|
||||
cmd_tree[pos] = cmd;
|
||||
}
|
||||
|
||||
static void insights_cmd_parser_print_cmd_tree(const char *cmd_tree[], int depth)
|
||||
{
|
||||
if (depth <= 0) {
|
||||
ESP_LOGI(TAG, "No command found to be printed");
|
||||
return;
|
||||
}
|
||||
printf("The command is: ");
|
||||
for (int i = 0; i < depth - 1; i++) {
|
||||
if (cmd_tree[i]) {
|
||||
printf("%s > ", cmd_tree[i]);
|
||||
} else {
|
||||
printf("\ncouldn't find complete depth\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cmd_tree[depth - 1]) {
|
||||
printf("%s\n", cmd_tree[depth - 1]);
|
||||
} else {
|
||||
printf("\ncouldn't find complete depth\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_BUFFER_SIZE 100
|
||||
|
||||
/* extract top level fields from CBOR and check for sanity */
|
||||
esp_err_t check_top_fields_from_cbor(const uint8_t *cbor_data, size_t cbor_data_len)
|
||||
{
|
||||
CborParser parser;
|
||||
CborValue map, value;
|
||||
CborError err;
|
||||
|
||||
/* Initialize the parser */
|
||||
cbor_parser_init(cbor_data, cbor_data_len, 0, &parser, &map);
|
||||
|
||||
/* Parse the top-level map */
|
||||
if (!cbor_value_is_map(&map)) {
|
||||
ESP_LOGE(TAG, "Invalid CBOR format: top-level map expected");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (CborNoError != cbor_value_enter_container(&map, &value)) {
|
||||
ESP_LOGE(TAG, "Error entering the container");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Iterate through the map and extract the desired fields */
|
||||
while (!cbor_value_at_end(&value)) {
|
||||
CborValue map_key;
|
||||
|
||||
/* Check the map key and extract the corresponding field */
|
||||
if (cbor_value_is_text_string(&value)) {
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
size_t buffer_size = sizeof(buffer);
|
||||
err = cbor_value_copy_text_string(&value, buffer, &buffer_size, &map_key);
|
||||
if (err != CborNoError) {
|
||||
ESP_LOGE(TAG, "CBOR value copy text string failed: %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
buffer_size = sizeof(buffer);
|
||||
if (strcmp(buffer, "ver") == 0) {
|
||||
if (cbor_value_is_text_string(&map_key)) {
|
||||
err = cbor_value_copy_text_string(&map_key, buffer, &buffer_size, &map_key);
|
||||
if (err != CborNoError) {
|
||||
ESP_LOGE(TAG, "CBOR value copy text string failed: %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "ver: %s", buffer);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid CBOR format: text string expected as ver key");
|
||||
}
|
||||
} else if (strcmp(buffer, "ts") == 0) {
|
||||
CborType _type = cbor_value_get_type(&map_key);
|
||||
ESP_LOGI(TAG, "ts is of type %d", _type);
|
||||
} else if (strcmp(buffer, "sha256") == 0) {
|
||||
if (cbor_value_is_text_string(&map_key)) {
|
||||
err = cbor_value_copy_text_string(&map_key, buffer, &buffer_size, &map_key);
|
||||
if (err != CborNoError) {
|
||||
ESP_LOGE(TAG, "CBOR value copy text string failed: %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "sha256: %s", buffer);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid CBOR format: text string expected as sha256 key");
|
||||
}
|
||||
} else if (strcmp(buffer, INS_CONF_STR) == 0) {
|
||||
/* Nothing to do here */
|
||||
}
|
||||
|
||||
/* skip this string now */
|
||||
err = cbor_value_advance(&value);
|
||||
if (err != CborNoError) {
|
||||
ESP_LOGE(TAG, "CBOR value advance failed: %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* advance the value */
|
||||
CborType _type = cbor_value_get_type(&value);
|
||||
ESP_LOGI(TAG, "Skipping type %d", _type);
|
||||
err = cbor_value_advance(&value);
|
||||
if (err != CborNoError) {
|
||||
ESP_LOGE(TAG, "CBOR value advance failed: %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get next config entry from data
|
||||
*
|
||||
*/
|
||||
static esp_err_t esp_insights_cmd_resp_parse_one_entry(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
char *tmp_str = NULL;
|
||||
int cmd_depth = 0;
|
||||
bool cmd_value_b;
|
||||
esp_err_t ret = ESP_OK;
|
||||
char *cmd_tree[MAX_CMD_DEPTH] = {0, };
|
||||
|
||||
/* parse till we are at the end */
|
||||
while (!esp_insights_cbor_decoder_at_end(ctx)) {
|
||||
CborType type = esp_insights_cbor_decode_get_value_type(ctx);
|
||||
CborValue *it = &ctx->it[ctx->curr_itr];
|
||||
switch (type)
|
||||
{
|
||||
case CborTextStringType:
|
||||
tmp_str = esp_insights_cbor_decoder_get_string(it);
|
||||
if (!tmp_str) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "found \"%s\"", tmp_str);
|
||||
if (strcmp(tmp_str, "n") == 0) {
|
||||
CborType _type = esp_insights_cbor_decode_get_value_type(ctx);
|
||||
if (_type == CborArrayType) {
|
||||
if (esp_insights_cbor_decoder_enter_container(ctx) == ESP_OK) {
|
||||
while(!esp_insights_cbor_decoder_at_end(ctx)) {
|
||||
char *buffer = esp_insights_cbor_decoder_get_string(&ctx->it[ctx->curr_itr]);
|
||||
if (!buffer) {
|
||||
ESP_LOGE(TAG, "Invalid entry");
|
||||
break;
|
||||
}
|
||||
insights_cmd_parser_add_cmd_to_tree(cmd_tree, buffer, cmd_depth);
|
||||
++cmd_depth;
|
||||
}
|
||||
|
||||
insights_cmd_parser_print_cmd_tree((const char **) cmd_tree, cmd_depth);
|
||||
esp_insights_cbor_decoder_exit_container(ctx);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "A config name must be of array type");
|
||||
}
|
||||
} else if (strcmp(tmp_str, "v") == 0) {
|
||||
/* decide the type of the value first and then fetch it (bool for now) */
|
||||
esp_diag_data_type_t type = ESP_DIAG_DATA_TYPE_BOOL;
|
||||
/* get the value in val */
|
||||
switch (type)
|
||||
{
|
||||
case ESP_DIAG_DATA_TYPE_BOOL:
|
||||
cbor_value_get_boolean(it, &cmd_value_b);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cbor_value_advance_fixed(it);
|
||||
} else {
|
||||
esp_insights_cbor_decoder_advance(ctx);
|
||||
}
|
||||
|
||||
if (tmp_str) {
|
||||
free(tmp_str);
|
||||
tmp_str = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
esp_insights_cbor_decoder_advance(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
insights_cmd_resp_search_execute_cmd_store(cmd_tree, cmd_depth);
|
||||
insights_cmd_parser_clear_cmd_tree(cmd_tree);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_insights_cmd_resp_parse_execute(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
esp_insights_cbor_decoder_enter_container(ctx);
|
||||
int cmd_cnt = 0;
|
||||
/* iterate till we are done with current container */
|
||||
while (!esp_insights_cbor_decoder_at_end(ctx)) {
|
||||
esp_insights_cbor_decoder_enter_container(ctx);
|
||||
esp_insights_cmd_resp_parse_one_entry(ctx);
|
||||
esp_insights_cbor_decoder_exit_container(ctx);
|
||||
cmd_cnt++;
|
||||
}
|
||||
if (cmd_cnt) {
|
||||
esp_insights_report_config_update();
|
||||
}
|
||||
esp_insights_cbor_decoder_exit_container(ctx);
|
||||
ESP_LOGI(TAG, "parsed and executed %d commands", cmd_cnt);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_insights_cmd_resp_iterate_to_cmds_array(cbor_parse_ctx_t *ctx)
|
||||
{
|
||||
/* layer one */
|
||||
if (esp_insights_cbor_decoder_at_end(ctx)) {
|
||||
ESP_LOGW(TAG, "invalid cmd_resp payload");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (esp_insights_cbor_decode_get_value_type(ctx) != CborMapType) {
|
||||
ESP_LOGE(TAG, "invalid cmd_resp payload. line (%d)", __LINE__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (esp_insights_cbor_decoder_enter_container(ctx) == ESP_OK) {
|
||||
while(!esp_insights_cbor_decoder_at_end(ctx)) {
|
||||
char *buffer = esp_insights_cbor_decoder_get_string(&ctx->it[ctx->curr_itr]);
|
||||
|
||||
if (!buffer) {
|
||||
ESP_LOGE(TAG, "Parsing problem...");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (strcmp(buffer, INS_CONF_STR) == 0) {
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
ESP_LOGI(TAG, "Found commands array:");
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "skipping token %s", buffer);
|
||||
}
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
/* skip the value and find next for INS_CONF_STR */
|
||||
esp_insights_cbor_decoder_advance(ctx);
|
||||
}
|
||||
ESP_LOGI(TAG, "failed to find a `config` array!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "invalid payload type");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static char resp_data[100]; /* FIXME: assumed that the response size is < 100 bytes */
|
||||
/** This is a common handler registered with the lower layer command response framework.
|
||||
* It parses the received CBOR payload aqnd redirects to appropriate internal insights command callback. */
|
||||
static esp_err_t esp_insights_cmd_handler(const void *in_data, size_t in_len, void **out_data,
|
||||
size_t *out_len, esp_rmaker_cmd_ctx_t *ctx, void *priv)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
if (in_data == NULL || in_len == 0) {
|
||||
ESP_LOGE(TAG, "No data received");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_INSIGHTS_DEBUG_ENABLED
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, in_data, in_len, ESP_LOG_INFO);
|
||||
ESP_LOGI(TAG, "Received command, len %d: ", in_len);
|
||||
esp_insights_cbor_decode_dump((uint8_t *) in_data, in_len);
|
||||
#endif
|
||||
|
||||
if (ESP_FAIL == check_top_fields_from_cbor(in_data, in_len)) {
|
||||
snprintf(resp_data, sizeof(resp_data), "{\"status\":\"payload error\"}");
|
||||
goto out;
|
||||
}
|
||||
cbor_parse_ctx_t *cbor_ctx = esp_insights_cbor_decoder_start(in_data, in_len);
|
||||
if (cbor_ctx) {
|
||||
ret = esp_insights_cmd_resp_iterate_to_cmds_array(cbor_ctx);
|
||||
if (ret == ESP_OK) {
|
||||
esp_insights_cmd_resp_parse_execute(cbor_ctx); /* it is okay if this is empty */
|
||||
snprintf(resp_data, sizeof(resp_data), "{\"status\":\"success\"}");
|
||||
} else {
|
||||
snprintf(resp_data, sizeof(resp_data), "{\"status\":\"payload error\"}");
|
||||
}
|
||||
esp_insights_cbor_decoder_done(cbor_ctx);
|
||||
} else {
|
||||
snprintf(resp_data, sizeof(resp_data), "{\"status\":\"internal error\"}");
|
||||
}
|
||||
out:
|
||||
*out_data = resp_data;
|
||||
*out_len = strlen(resp_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const uint8_t test_buf0[] = {
|
||||
0xA1, 0x68, 0x64, 0x69, 0x61, 0x67, 0x6D, 0x65, 0x74, 0x61, 0xA4, 0x63, 0x76, 0x65, 0x72, 0x63,
|
||||
0x31, 0x2E, 0x31, 0x62, 0x74, 0x73, 0x1B, 0x00, 0x05, 0xFB, 0x53, 0xF9, 0x42, 0x0C, 0x39, 0x66,
|
||||
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x70, 0x39, 0x61, 0x65, 0x38, 0x30, 0x36, 0x36, 0x61, 0x30,
|
||||
0x37, 0x65, 0x38, 0x37, 0x38, 0x66, 0x64, 0x64, 0x64, 0x61, 0x74, 0x61, 0xA1, 0x61, 0x64, 0xA2,
|
||||
0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79, 0x70,
|
||||
0x65, 0x00, 0x61, 0x76, 0xF5, 0x67, 0x6D, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xA1, 0x61, 0x64,
|
||||
0xA2, 0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79,
|
||||
0x70, 0x65, 0x00, 0x61, 0x76, 0xF5, 0x64, 0x68, 0x65, 0x61, 0x70, 0xA1, 0x61, 0x64, 0xA2, 0x67,
|
||||
0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79, 0x70, 0x65,
|
||||
0x00, 0x61, 0x76, 0xF5, 0x6A, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x5F, 0x66, 0x61, 0x69, 0x6C, 0xA1,
|
||||
0x61, 0x64, 0xA1, 0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64,
|
||||
0x74, 0x79, 0x70, 0x65, 0x00, 0x61, 0x76, 0xF5
|
||||
};
|
||||
|
||||
/** Test vector 2 */
|
||||
const uint8_t test_buf1[] = {
|
||||
0xA1, 0x68, 0x64, 0x69, 0x61, 0x67, 0x6D, 0x65, 0x74, 0x61, 0xA4, 0x63, 0x76, 0x65, 0x72, 0x63,
|
||||
0x31, 0x2E, 0x31, 0x62, 0x74, 0x73, 0x1B, 0x00, 0x05, 0xFB, 0x53, 0xF9, 0x42, 0x0C, 0x39, 0x66,
|
||||
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x70, 0x39, 0x61, 0x65, 0x38, 0x30, 0x36, 0x36, 0x61, 0x30,
|
||||
0x37, 0x65, 0x38, 0x37, 0x38, 0x66, 0x64, 0x64, 0x64, 0x61, 0x74, 0x61, 0xA1, 0x61, 0x64, 0xA3,
|
||||
0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79, 0x70,
|
||||
0x65, 0x00, 0x61, 0x76, 0xF5, 0x67, 0x6D, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xA1, 0x61, 0x64,
|
||||
0xA2, 0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79,
|
||||
0x70, 0x65, 0x00, 0x61, 0x76, 0xF5, 0x64, 0x68, 0x65, 0x61, 0x70, 0xA1, 0x61, 0x64, 0xA3, 0x67,
|
||||
0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79, 0x70, 0x65,
|
||||
0x00, 0x61, 0x76, 0xF5, 0x6A, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x5F, 0x66, 0x61, 0x69, 0x6C, 0xA1,
|
||||
0x61, 0x64, 0xA1, 0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64,
|
||||
0x74, 0x79, 0x70, 0x65, 0x00, 0x61, 0x76, 0xF5, 0x64, 0x66, 0x72, 0x65, 0x65, 0xA1, 0x61, 0x64,
|
||||
0xA1, 0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79,
|
||||
0x70, 0x65, 0x00, 0x61, 0x76, 0xF5, 0x66, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73, 0xA1, 0x61, 0x64,
|
||||
0xA2, 0x67, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79,
|
||||
0x70, 0x65, 0x00, 0x61, 0x76, 0xF5, 0x64, 0x77, 0x69, 0x66, 0x69, 0xA1, 0x61, 0x64, 0xA1, 0x67,
|
||||
0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0xA1, 0x61, 0x63, 0xA2, 0x64, 0x74, 0x79, 0x70, 0x65,
|
||||
0x00, 0x61, 0x76, 0xF5
|
||||
};
|
||||
|
||||
/* test vec 2 */
|
||||
const uint8_t test_buf2[] = {
|
||||
0xA4, 0x63, 0x76, 0x65, 0x72, 0x63, 0x32, 0x2E, 0x30, 0x62, 0x74, 0x73,
|
||||
0x1B, 0x00, 0x05, 0xC5, 0x85, 0x48, 0x4E, 0xCF, 0x80, 0x66, 0x73, 0x68,
|
||||
0x61, 0x32, 0x35, 0x36, 0x70, 0x37, 0x63, 0x32, 0x65, 0x64, 0x62, 0x31,
|
||||
0x39, 0x34, 0x39, 0x36, 0x33, 0x39, 0x61, 0x37, 0x33, 0x66, 0x63, 0x6F,
|
||||
0x6E, 0x66, 0x69, 0x67, 0x82, 0xA2, 0x61, 0x6E, 0x83, 0x64, 0x68, 0x65,
|
||||
0x61, 0x70, 0x6A, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x5F, 0x66, 0x61, 0x69,
|
||||
0x6C, 0x66, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x61, 0x76, 0xF5, 0xA2,
|
||||
0x61, 0x6E, 0x82, 0x64, 0x77, 0x69, 0x66, 0x69, 0x66, 0x65, 0x6E, 0x61,
|
||||
0x62, 0x6C, 0x65, 0x61, 0x76, 0xF5,
|
||||
};
|
||||
|
||||
esp_err_t esp_insights_test_cmd_handler()
|
||||
{
|
||||
int encoded_sz = sizeof (test_buf2);
|
||||
const uint8_t *test_buffer = test_buf2;
|
||||
ESP_LOGI(TAG, "Performing commands decode on test_data0");
|
||||
void *resp_data = NULL;
|
||||
size_t resp_len = 0;
|
||||
esp_err_t ret = esp_insights_cmd_handler(test_buffer, encoded_sz, &resp_data, &resp_len, NULL, NULL);
|
||||
ESP_LOGI(TAG, "response: %s", (char *) resp_data);
|
||||
|
||||
ESP_LOGI(TAG, "Performing commands decode on test_data1");
|
||||
encoded_sz = sizeof (test_buf1);
|
||||
test_buffer = test_buf1;
|
||||
resp_data = NULL;
|
||||
resp_len = 0;
|
||||
ret = esp_insights_cmd_handler(test_buffer, encoded_sz, &resp_data, &resp_len, NULL, NULL);
|
||||
ESP_LOGI(TAG, "response: %s", (char *) resp_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void esp_insights_cmd_callback(const char *topic, void *payload, size_t payload_len, void *priv_data)
|
||||
{
|
||||
void *output = NULL;
|
||||
size_t output_len = 0;
|
||||
/* Any command data received is directly sent to the command response framework and on success,
|
||||
* the response (if any) is sent back to the MQTT Broker.
|
||||
*/
|
||||
if (esp_rmaker_cmd_response_handler(payload, payload_len, &output, &output_len) == ESP_OK) {
|
||||
if (output) {
|
||||
char publish_topic[100];
|
||||
snprintf(publish_topic, sizeof(publish_topic), "node/%s/%s", esp_insights_get_node_id(), FROM_NODE_TOPIC_SUFFIX);
|
||||
if (esp_insights_mqtt_publish(publish_topic, output, output_len, RMAKER_MQTT_QOS1, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to publish reponse.");
|
||||
}
|
||||
free(output);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No output generated by command-response handler.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char notify_cmd_resp_str[] =
|
||||
"{\
|
||||
\"node_id\": \"%s\",\
|
||||
\"config_version\": \"2019-09-11\",\
|
||||
\"attributes\": [\
|
||||
{\
|
||||
\"name\": \"cmd-resp\",\
|
||||
\"value\": \"1\"\
|
||||
}\
|
||||
],\
|
||||
}";
|
||||
|
||||
esp_err_t esp_insights_cmd_resp_init(void)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
if (s_cmd_resp_data.init_done) {
|
||||
ESP_LOGI(TAG, "already initialized. Skipped");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
s_cmd_resp_data.scratch_buf = MEM_ALLOC_EXTRAM(SCRATCH_BUF_SIZE);
|
||||
if (!s_cmd_resp_data.scratch_buf) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for scratch buffer.");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto init_err;
|
||||
}
|
||||
|
||||
const int topic_size = 100;
|
||||
char *mqtt_topic = (char *) s_cmd_resp_data.scratch_buf;
|
||||
const char *node_id = esp_insights_get_node_id();
|
||||
if (!node_id) {
|
||||
ESP_LOGE(TAG, "node_id not found. Bailing out...");
|
||||
goto init_err;
|
||||
}
|
||||
snprintf(mqtt_topic, topic_size, "node/%s/%s", node_id, TO_NODE_TOPIC_SUFFIX);
|
||||
err = esp_insights_mqtt_subscribe(mqtt_topic, esp_insights_cmd_callback, RMAKER_MQTT_QOS1, NULL);
|
||||
if(err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe to %s. Error %d", mqtt_topic, err);
|
||||
goto init_err;
|
||||
}
|
||||
|
||||
/* Notify rmaker about our command response capability */
|
||||
char *publish_data = mqtt_topic + topic_size;
|
||||
sprintf(publish_data, notify_cmd_resp_str, esp_insights_get_node_id());
|
||||
snprintf(mqtt_topic, topic_size, "node/%s/%s", esp_insights_get_node_id(), RMAKER_CFG_TOPIC_SUFFIX);
|
||||
if (esp_insights_mqtt_publish(mqtt_topic, publish_data, strlen(publish_data), RMAKER_MQTT_QOS1, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to publish cmd-resp attrib");
|
||||
}
|
||||
|
||||
s_cmd_resp_data.init_done = true;
|
||||
ESP_LOGI(TAG, "Command-Response Module initialized");
|
||||
return ESP_OK;
|
||||
|
||||
init_err:
|
||||
if (s_cmd_resp_data.scratch_buf) {
|
||||
free(s_cmd_resp_data.scratch_buf);
|
||||
s_cmd_resp_data.scratch_buf = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cmd_resp_enable(void)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
|
||||
if (s_cmd_resp_data.enabled == true) {
|
||||
ESP_LOGI(TAG, "already enabled. Skipped");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (!s_cmd_resp_data.scratch_buf) {
|
||||
s_cmd_resp_data.scratch_buf = MEM_ALLOC_EXTRAM(SCRATCH_BUF_SIZE);
|
||||
}
|
||||
if (!s_cmd_resp_data.scratch_buf) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for scratch buffer.");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto enable_err;
|
||||
}
|
||||
|
||||
esp_insights_cbor_encoder_register_meta_cb(&esp_insights_cbor_reboot_msg_cb);
|
||||
/* register `reboot` command to our commands store */
|
||||
esp_insights_cmd_resp_register_cmd(reboot_cmd_handler, NULL, 1, "reboot");
|
||||
|
||||
ESP_LOGI(TAG, "Enabling Command-Response Module.");
|
||||
|
||||
/* Register our config parsing command to cmd_resp module */
|
||||
err = esp_rmaker_cmd_register(INSIGHTS_CONF_CMD, ESP_RMAKER_USER_ROLE_SUPER_ADMIN,
|
||||
esp_insights_cmd_handler, false, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register INSIGHTS_CONF_CMD");
|
||||
goto enable_err;
|
||||
}
|
||||
|
||||
s_cmd_resp_data.enabled = true;
|
||||
return ESP_OK;
|
||||
|
||||
enable_err:
|
||||
if (s_cmd_resp_data.scratch_buf) {
|
||||
free(s_cmd_resp_data.scratch_buf);
|
||||
s_cmd_resp_data.scratch_buf = NULL;
|
||||
}
|
||||
s_cmd_resp_data.init_done = false;
|
||||
return err;
|
||||
}
|
||||
|
||||
#else /* CONFIG_ESP_INSIGHTS_CMD_RESP_ENABLED */
|
||||
esp_err_t esp_insights_cmd_resp_init(void)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_cmd_resp_enable(void) {
|
||||
ESP_LOGE(TAG, "Please enable CONFIG_ESP_INSIGHTS_CMD_RESP_ENABLED=y, line %d", __LINE__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_INSIGHTS_CMD_RESP_ENABLED */
|
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_diagnostics.h>
|
||||
#include <esp_diagnostics_metrics.h>
|
||||
#include <esp_diagnostics_variables.h>
|
||||
|
||||
#include "esp_insights_cbor_encoder.h"
|
||||
|
||||
#if CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
#define INSIGHTS_VERSION_MAJOR "1"
|
||||
#else
|
||||
#define INSIGHTS_VERSION_MAJOR "2"
|
||||
#endif
|
||||
#define INSIGHTS_VERSION_MINOR "0"
|
||||
#define INSIGHTS_VERSION INSIGHTS_VERSION_MAJOR \
|
||||
"." INSIGHTS_VERSION_MINOR
|
||||
|
||||
#if CONFIG_ESP_INSIGHTS_META_VERSION_10
|
||||
#define INSIGHTS_META_VERSION_MAJOR "1"
|
||||
#else
|
||||
#define INSIGHTS_META_VERSION_MAJOR "2"
|
||||
#endif
|
||||
#define INSIGHTS_META_VERSION_MINOR "0"
|
||||
|
||||
#define INSIGHTS_META_VERSION INSIGHTS_META_VERSION_MAJOR \
|
||||
"." INSIGHTS_META_VERSION_MINOR
|
||||
|
||||
#define INSIGHTS_DATA_TYPE 0x02
|
||||
#define INSIGHTS_META_DATA_TYPE 0x03
|
||||
#define INSIGHTS_CONF_DATA_TYPE 0x12
|
||||
#define TLV_OFFSET 3
|
||||
|
||||
static void esp_insights_encode_meta_data(void)
|
||||
{
|
||||
#if CONFIG_DIAG_ENABLE_METRICS
|
||||
uint32_t metrics_len = 0;
|
||||
const esp_diag_metrics_meta_t *metrics = esp_diag_metrics_meta_get_all(&metrics_len);
|
||||
if (!metrics) {
|
||||
return;
|
||||
}
|
||||
esp_insights_cbor_encode_meta_metrics((const esp_diag_metrics_meta_t *)metrics, metrics_len);
|
||||
#endif /* CONFIG_DIAG_ENABLE_METRICS */
|
||||
|
||||
#if CONFIG_DIAG_ENABLE_VARIABLES
|
||||
uint32_t variables_len = 0;
|
||||
const esp_diag_variable_meta_t *variables = esp_diag_variable_meta_get_all(&variables_len);
|
||||
if (!variables) {
|
||||
return;
|
||||
}
|
||||
esp_insights_cbor_encode_meta_variables((const esp_diag_variable_meta_t *)variables, variables_len);
|
||||
#endif /* CONFIG_DIAG_ENABLE_VARIABLES */
|
||||
}
|
||||
|
||||
size_t esp_insights_encode_meta(uint8_t *out_data, size_t out_data_size, char *sha256)
|
||||
{
|
||||
if (!out_data || !out_data_size) {
|
||||
return 0;
|
||||
}
|
||||
char sha[DIAG_HEX_SHA_SIZE + 1];
|
||||
bytes_to_hex((uint8_t *) sha256,(uint8_t *) sha, DIAG_SHA_SIZE);
|
||||
esp_insights_cbor_encode_meta_begin(out_data + TLV_OFFSET,
|
||||
out_data_size - TLV_OFFSET,
|
||||
INSIGHTS_META_VERSION, sha);
|
||||
esp_insights_cbor_encode_meta_data_begin();
|
||||
esp_insights_encode_meta_data();
|
||||
esp_insights_cbor_encode_meta_data_end();
|
||||
uint16_t len = esp_insights_cbor_encode_meta_end(out_data + TLV_OFFSET);
|
||||
|
||||
out_data[0] = INSIGHTS_META_DATA_TYPE; /* Data type inidcation diagnostics meta - 1 byte */
|
||||
memcpy(&out_data[1], &len, sizeof(len)); /* Data length - 2 bytes */
|
||||
len += TLV_OFFSET;
|
||||
return len;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_encode_data_begin(void *out_data, size_t out_data_size)
|
||||
{
|
||||
if (!out_data || !out_data_size) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_insights_cbor_encode_diag_begin(out_data + TLV_OFFSET, out_data_size - TLV_OFFSET, INSIGHTS_VERSION);
|
||||
esp_insights_cbor_encode_diag_data_begin();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
size_t esp_insights_encode_conf_meta(uint8_t *out_data, size_t out_data_size, char *sha256)
|
||||
{
|
||||
if (!out_data || !out_data_size) {
|
||||
return 0;
|
||||
}
|
||||
char sha[DIAG_HEX_SHA_SIZE + 1];
|
||||
bytes_to_hex((uint8_t *) sha256,(uint8_t *) sha, DIAG_SHA_SIZE);
|
||||
esp_insights_cbor_encode_meta_begin(out_data + TLV_OFFSET,
|
||||
out_data_size - TLV_OFFSET,
|
||||
INSIGHTS_META_VERSION, sha);
|
||||
esp_insights_cbor_encode_conf_meta_data_begin();
|
||||
/* TODO: Implement and collect diagnostics specific conf meta */
|
||||
// esp_insights_encode_conf_meta_data();
|
||||
esp_insights_cbor_encode_conf_meta_data_end();
|
||||
|
||||
uint16_t len = esp_insights_cbor_encode_meta_end(out_data + TLV_OFFSET);
|
||||
|
||||
out_data[0] = INSIGHTS_META_DATA_TYPE; /* Data type inidcation diagnostics meta - 1 byte */
|
||||
memcpy(&out_data[1], &len, sizeof(len)); /* Data length - 2 bytes */
|
||||
len += TLV_OFFSET;
|
||||
return len;
|
||||
}
|
||||
|
||||
void esp_insights_encode_boottime_data(void)
|
||||
{
|
||||
/* encode device info */
|
||||
esp_diag_device_info_t device_info;
|
||||
memset(&device_info, 0, sizeof(device_info));
|
||||
esp_diag_device_info_get(&device_info);
|
||||
esp_insights_cbor_encode_diag_boot_info(&device_info);
|
||||
|
||||
/* encode core dump summary */
|
||||
#if CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE
|
||||
const char *TAG = "Insights";
|
||||
esp_err_t err = esp_core_dump_image_check();
|
||||
if (err == ESP_OK) {
|
||||
esp_core_dump_summary_t *summary = malloc(sizeof(esp_core_dump_summary_t));
|
||||
if (summary) {
|
||||
memset(summary, 0, sizeof(esp_core_dump_summary_t));
|
||||
if (esp_core_dump_get_summary(summary) == ESP_OK) {
|
||||
esp_insights_cbor_encode_diag_crash(summary);
|
||||
}
|
||||
free(summary);
|
||||
}
|
||||
} else if (err == ESP_ERR_INVALID_CRC) {
|
||||
ESP_LOGE(TAG, "Core dump stored in flash is corrupted");
|
||||
}
|
||||
#endif /* CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE */
|
||||
}
|
||||
|
||||
void esp_insights_encode_conf_data()
|
||||
{
|
||||
/* collect the configs */
|
||||
esp_insights_cbor_encode_diag_conf_data_begin();
|
||||
esp_insights_cbor_encode_diag_conf_data();
|
||||
esp_insights_cbor_encode_diag_conf_data_end();
|
||||
}
|
||||
|
||||
|
||||
size_t esp_insights_encode_conf_end(uint8_t *out_data)
|
||||
{
|
||||
if (!out_data) {
|
||||
return 0;
|
||||
}
|
||||
esp_insights_cbor_encode_diag_data_end();
|
||||
uint16_t len = esp_insights_cbor_encode_diag_end(out_data + TLV_OFFSET);
|
||||
|
||||
out_data[0] = INSIGHTS_CONF_DATA_TYPE; /* Data type indicating diagnostics - 1 byte */
|
||||
memcpy(&out_data[1], &len, sizeof(len)); /* Data length - 2 bytes */
|
||||
len += TLV_OFFSET;
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t esp_insights_encode_critical_data(const void *data, size_t data_size)
|
||||
{
|
||||
size_t consumed = 0;
|
||||
if (data) {
|
||||
consumed = esp_insights_cbor_encode_diag_logs(data, data_size);
|
||||
if (consumed) {
|
||||
uint8_t meta_idx = ((uint8_t *) data)[0];
|
||||
const rtc_store_meta_header_t *hdr = rtc_store_get_meta_record_by_index(meta_idx);
|
||||
if (hdr) {
|
||||
esp_insights_cbor_encode_meta_c_hdr(hdr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
size_t esp_insights_encode_non_critical_data(const void *data, size_t data_size)
|
||||
{
|
||||
size_t consumed_max = 0;
|
||||
if (data) {
|
||||
#if CONFIG_DIAG_ENABLE_METRICS
|
||||
consumed_max = esp_insights_cbor_encode_diag_metrics(data, data_size);
|
||||
#endif /* CONFIG_DIAG_ENABLE_METRICS */
|
||||
#if CONFIG_DIAG_ENABLE_VARIABLES
|
||||
size_t consumed = esp_insights_cbor_encode_diag_variables(data, data_size);
|
||||
if (consumed > consumed_max) {
|
||||
consumed_max = consumed;
|
||||
}
|
||||
#endif /* CONFIG_DIAG_ENABLE_VARIABLES */
|
||||
#if CONFIG_DIAG_ENABLE_METRICS || CONFIG_DIAG_ENABLE_VARIABLES
|
||||
if (consumed_max) {
|
||||
uint8_t meta_idx = ((uint8_t *) data)[0];
|
||||
const rtc_store_meta_header_t *hdr = rtc_store_get_meta_record_by_index(meta_idx);
|
||||
if (hdr) {
|
||||
esp_insights_cbor_encode_meta_nc_hdr(hdr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return consumed_max;
|
||||
}
|
||||
|
||||
size_t esp_insights_encode_data_end(uint8_t *out_data)
|
||||
{
|
||||
if (!out_data) {
|
||||
return 0;
|
||||
}
|
||||
esp_insights_cbor_encode_diag_data_end();
|
||||
uint16_t len = esp_insights_cbor_encode_diag_end(out_data + TLV_OFFSET);
|
||||
|
||||
out_data[0] = INSIGHTS_DATA_TYPE; /* Data type indicating diagnostics - 1 byte */
|
||||
memcpy(&out_data[1], &len, sizeof(len)); /* Data length - 2 bytes */
|
||||
len += TLV_OFFSET;
|
||||
return len;
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CONFIG_ESP_INSIGHTS_COREDUMP_ENABLE
|
||||
#include <esp_core_dump.h>
|
||||
#endif
|
||||
|
||||
size_t esp_insights_encode_meta(uint8_t *out_data, size_t out_data_size, char *sha256);
|
||||
size_t esp_insights_encode_conf_meta(uint8_t *out_data, size_t out_data_size, char *sha256);
|
||||
esp_err_t esp_insights_encode_data_begin(uint8_t *out_data, size_t out_data_size);
|
||||
void esp_insights_encode_boottime_data(void);
|
||||
|
||||
/**
|
||||
* @brief encode critical data
|
||||
*
|
||||
* @param critical_data pointer to critical data
|
||||
* @param critical_data_size size of critical data
|
||||
* @return size_t length of data consumed
|
||||
*/
|
||||
size_t esp_insights_encode_critical_data(const void *critical_data, size_t critical_data_size);
|
||||
|
||||
/**
|
||||
* @brief encode non_critical data
|
||||
*
|
||||
* @param critical_data pointer to non_critical data
|
||||
* @param critical_data_size size of non_critical data
|
||||
* @return size_t length of data consumed
|
||||
*/
|
||||
size_t esp_insights_encode_non_critical_data(const void *non_critical_data, size_t non_critical_data_size);
|
||||
|
||||
/**
|
||||
* @brief finish encoding message
|
||||
*
|
||||
* @param out_data encoded data pointer
|
||||
* @return size_t size of the data encoded
|
||||
*/
|
||||
size_t esp_insights_encode_data_end(uint8_t *out_data);
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
#ifdef CONFIG_ESP_INSIGHTS_TRANSPORT_MQTT
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
|
||||
/* Default configurations for rmaker mqtt glue lib */
|
||||
extern esp_insights_transport_config_t g_default_insights_transport_mqtt;
|
||||
|
||||
/* other functions for more granularity */
|
||||
esp_err_t esp_insights_mqtt_publish(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id);
|
||||
esp_err_t esp_insights_mqtt_subscribe(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data);
|
||||
#else
|
||||
/* Default configurations for https */
|
||||
extern esp_insights_transport_config_t g_default_insights_transport_https;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Perform transport connect
|
||||
*
|
||||
* @return ESP_OK on success, otherwise appropriate error code
|
||||
*/
|
||||
esp_err_t esp_insights_transport_connect(void);
|
||||
|
||||
/**
|
||||
* @brief Perform transport disconnect
|
||||
*/
|
||||
void esp_insights_transport_disconnect(void);
|
||||
|
||||
/**
|
||||
* @brief Send data using the transport
|
||||
*
|
||||
* @paran[in] data Data to send
|
||||
* @param[in] len Length of data
|
||||
*
|
||||
* @return msg_id Message_id of the sent data.
|
||||
* On failure, -1
|
||||
* On success, 0 if data send happens synchronously.
|
||||
* On success, message-id(positive integer) if data send happened asynchronously.
|
||||
*/
|
||||
int esp_insights_transport_data_send(void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Send update to the cloud about new state
|
||||
*/
|
||||
void esp_insights_report_config_update(void);
|
||||
|
||||
/**
|
||||
* @brief Get node id
|
||||
*
|
||||
* For MQTT transports if node id is present in factory partition then it is returned,
|
||||
* otherwise mac address string is returned.
|
||||
*
|
||||
* @return node_id
|
||||
*/
|
||||
const char *esp_insights_get_node_id(void);
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_insights.h>
|
||||
|
||||
static const char *TAG = "insights_transport";
|
||||
|
||||
typedef struct {
|
||||
bool init;
|
||||
esp_insights_transport_config_t config;
|
||||
} priv_data_t;
|
||||
|
||||
static priv_data_t s_priv_data;
|
||||
|
||||
#define CHECK_TRANSPORT_INIT(ret) do { \
|
||||
if (!s_priv_data.init) { \
|
||||
ESP_LOGE(TAG, "Transport callbacks not registered"); \
|
||||
return ret; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
esp_err_t esp_insights_transport_register(esp_insights_transport_config_t *config)
|
||||
{
|
||||
if (s_priv_data.init) {
|
||||
ESP_LOGW(TAG, "Already initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
/* We at least need data_send callback */
|
||||
if (!(config && config->callbacks.data_send)) {
|
||||
ESP_LOGE(TAG, "Failed to init transport, Please set at least data_send callback");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
memcpy(&s_priv_data.config, config, sizeof(s_priv_data.config));
|
||||
s_priv_data.init = true;
|
||||
|
||||
/* Call transport init */
|
||||
if (s_priv_data.config.callbacks.init) {
|
||||
s_priv_data.config.callbacks.init(config->userdata);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_insights_transport_unregister(void)
|
||||
{
|
||||
if (!s_priv_data.init) {
|
||||
ESP_LOGE(TAG, "Not initialized");
|
||||
return;
|
||||
}
|
||||
/* Call transport deinit */
|
||||
if (s_priv_data.config.callbacks.deinit) {
|
||||
s_priv_data.config.callbacks.deinit();
|
||||
}
|
||||
memset(&s_priv_data, 0, sizeof(s_priv_data));
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_transport_connect(void)
|
||||
{
|
||||
CHECK_TRANSPORT_INIT(ESP_ERR_INVALID_STATE);
|
||||
if (s_priv_data.config.callbacks.connect) {
|
||||
return s_priv_data.config.callbacks.connect();
|
||||
}
|
||||
ESP_LOGW(TAG, "connect callback not set");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_insights_transport_disconnect(void)
|
||||
{
|
||||
CHECK_TRANSPORT_INIT();
|
||||
if (s_priv_data.config.callbacks.disconnect) {
|
||||
return s_priv_data.config.callbacks.disconnect();
|
||||
}
|
||||
ESP_LOGW(TAG, "disconnect callback not set");
|
||||
}
|
||||
|
||||
int esp_insights_transport_data_send(void *data, size_t len)
|
||||
{
|
||||
CHECK_TRANSPORT_INIT(-1);
|
||||
if (s_priv_data.config.callbacks.data_send) {
|
||||
return s_priv_data.config.callbacks.data_send(data, len);
|
||||
}
|
||||
ESP_LOGW(TAG, "data send callback not set");
|
||||
return -1;
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_http_client.h>
|
||||
#include <esp_insights.h>
|
||||
#include <esp_insights_internal.h>
|
||||
|
||||
typedef struct {
|
||||
const char *auth_key;
|
||||
const char *node_id;
|
||||
const char *url;
|
||||
} https_data_t;
|
||||
|
||||
static https_data_t s_https_data;
|
||||
|
||||
static const char *TAG = "tport_https";
|
||||
#define HTTPS_URL_PATH "prod/node/data"
|
||||
|
||||
extern const uint8_t insights_https_server_crt_start[] asm("_binary_https_server_crt_start");
|
||||
extern const uint8_t insights_https_server_crt_end[] asm("_binary_https_server_crt_end");
|
||||
|
||||
static esp_err_t esp_insights_https_init(void *userdata)
|
||||
{
|
||||
if (sizeof(CONFIG_ESP_INSIGHTS_TRANSPORT_HTTPS_HOST) <= 1) {
|
||||
ESP_LOGE(TAG, "Insights HTTPS host is missing");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
s_https_data.url = CONFIG_ESP_INSIGHTS_TRANSPORT_HTTPS_HOST "/" HTTPS_URL_PATH;
|
||||
/* In default https transport, auth_key is sent as userdata */
|
||||
s_https_data.auth_key = (const char *)userdata;
|
||||
s_https_data.node_id = esp_insights_get_node_id();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_insights_https_deinit(void)
|
||||
{
|
||||
memset(&s_https_data, 0, sizeof(s_https_data));
|
||||
}
|
||||
|
||||
static esp_err_t http_event_handle(esp_http_client_event_t *evt)
|
||||
{
|
||||
switch(evt->event_id) {
|
||||
case HTTP_EVENT_ERROR:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_HEADER_SENT:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER %s:%s", evt->header_key, evt->header_value);
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
|
||||
if (!esp_http_client_is_chunked_response(evt->client)) {
|
||||
ESP_LOGD(TAG, "%.*s\n", evt->data_len, (char*)evt->data);
|
||||
}
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int esp_insights_https_data_send(void *data, size_t len)
|
||||
{
|
||||
if (!data) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_https_data.auth_key) {
|
||||
ESP_LOGE(TAG, "Transport not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
int msg_id = -1;
|
||||
char url[256];
|
||||
memset(url, 0, sizeof(url));
|
||||
snprintf(url, sizeof(url), "%s?node_id=%s", s_https_data.url, s_https_data.node_id);
|
||||
|
||||
esp_http_client_config_t client_config = {
|
||||
.url = url,
|
||||
.method = HTTP_METHOD_POST,
|
||||
.transport_type = HTTP_TRANSPORT_OVER_SSL,
|
||||
.buffer_size_tx = 1024,
|
||||
.event_handler = http_event_handle,
|
||||
.cert_pem = (const char *)insights_https_server_crt_start,
|
||||
};
|
||||
esp_http_client_handle_t client = esp_http_client_init(&client_config);
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Failed to initialize esp_http_client");
|
||||
return msg_id;
|
||||
}
|
||||
esp_err_t err = esp_http_client_set_header(client, "Authorization", s_https_data.auth_key);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set Authorization header err:0x%x", err);
|
||||
goto cleanup;
|
||||
}
|
||||
err = esp_http_client_set_header(client, "Content-type", "application/octet-stream");
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set content type err:0x%x", err);
|
||||
goto cleanup;
|
||||
}
|
||||
err = esp_http_client_set_post_field(client, data, len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_http_client_set_post_field failed err:0x%x", err);
|
||||
goto cleanup;
|
||||
}
|
||||
err = esp_http_client_perform(client);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_http_client_perform failed err:0x%x", err);
|
||||
} else {
|
||||
int status = esp_http_client_get_status_code(client);
|
||||
if (status == HttpStatus_Ok) {
|
||||
msg_id = 0;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "API response status = %d", status);
|
||||
}
|
||||
}
|
||||
if (msg_id == 0) {
|
||||
esp_event_post(INSIGHTS_EVENT, INSIGHTS_EVENT_TRANSPORT_SEND_SUCCESS, NULL, 0, portMAX_DELAY);
|
||||
} else {
|
||||
esp_event_post(INSIGHTS_EVENT, INSIGHTS_EVENT_TRANSPORT_SEND_FAILED, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
cleanup:
|
||||
esp_http_client_cleanup(client);
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
esp_insights_transport_config_t g_default_insights_transport_https = {
|
||||
.callbacks = {
|
||||
.init = esp_insights_https_init,
|
||||
.deinit = esp_insights_https_deinit,
|
||||
.data_send = esp_insights_https_data_send,
|
||||
}
|
||||
};
|
@@ -0,0 +1,201 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
#include <esp_rmaker_factory.h>
|
||||
#include <esp_insights_client_data.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include <esp_insights.h>
|
||||
#include <esp_insights_internal.h>
|
||||
|
||||
static const char *TAG = "tport_mqtt";
|
||||
|
||||
#define INSIGHTS_TOPIC_SUFFIX "diagnostics/from-node"
|
||||
|
||||
typedef struct {
|
||||
const char *node_id;
|
||||
esp_rmaker_mqtt_config_t mqtt_config;
|
||||
esp_rmaker_mqtt_conn_params_t *conn_params;
|
||||
bool connected;
|
||||
} mqtt_data_t;
|
||||
|
||||
static mqtt_data_t s_mqtt_data;
|
||||
|
||||
static esp_rmaker_mqtt_conn_params_t *get_mqtt_conn_params(void)
|
||||
{
|
||||
if (s_mqtt_data.mqtt_config.get_conn_params) {
|
||||
return s_mqtt_data.mqtt_config.get_conn_params();
|
||||
} else {
|
||||
return esp_insights_get_mqtt_conn_params();
|
||||
}
|
||||
}
|
||||
|
||||
static void clean_mqtt_conn_params(void)
|
||||
{
|
||||
if (s_mqtt_data.conn_params) {
|
||||
esp_insights_clean_mqtt_conn_params(s_mqtt_data.conn_params);
|
||||
free(s_mqtt_data.conn_params);
|
||||
s_mqtt_data.conn_params = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void rmaker_common_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base != RMAKER_COMMON_EVENT) {
|
||||
return;
|
||||
}
|
||||
esp_insights_transport_event_data_t data;
|
||||
switch(event_id) {
|
||||
case RMAKER_MQTT_EVENT_DISCONNECTED:
|
||||
s_mqtt_data.connected = false;
|
||||
break;
|
||||
case RMAKER_MQTT_EVENT_CONNECTED:
|
||||
s_mqtt_data.connected = true;
|
||||
break;
|
||||
case RMAKER_MQTT_EVENT_PUBLISHED:
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.msg_id = *(int *)event_data;
|
||||
esp_event_post(INSIGHTS_EVENT, INSIGHTS_EVENT_TRANSPORT_SEND_SUCCESS, &data, sizeof(data), portMAX_DELAY);
|
||||
break;
|
||||
#ifdef CONFIG_MQTT_REPORT_DELETED_MESSAGES
|
||||
case RMAKER_MQTT_EVENT_MSG_DELETED:
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.msg_id = *(int *)event_data;
|
||||
esp_event_post(INSIGHTS_EVENT, INSIGHTS_EVENT_TRANSPORT_SEND_FAILED, &data, sizeof(data), portMAX_DELAY);
|
||||
break;
|
||||
#endif /* CONFIG_MQTT_REPORT_DELETED_MESSAGES */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_insights_mqtt_init(void *userdata)
|
||||
{
|
||||
if (!s_mqtt_data.mqtt_config.setup_done) {
|
||||
esp_rmaker_mqtt_glue_setup(&s_mqtt_data.mqtt_config);
|
||||
}
|
||||
esp_err_t err = esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, rmaker_common_event_handler, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register event handler for RMAKER_COMMON_EVENT");
|
||||
return err;
|
||||
}
|
||||
err = esp_rmaker_factory_init();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize factory partition");
|
||||
return err;
|
||||
}
|
||||
s_mqtt_data.conn_params = get_mqtt_conn_params();
|
||||
if (!s_mqtt_data.conn_params) {
|
||||
ESP_LOGE(TAG, "Failed to get MQTT connection parameters.");
|
||||
err = ESP_FAIL;
|
||||
goto init_err;
|
||||
}
|
||||
s_mqtt_data.node_id = esp_insights_get_node_id();
|
||||
if (!s_mqtt_data.node_id) {
|
||||
ESP_LOGE(TAG, "Failed to get node id");
|
||||
err = ESP_FAIL;
|
||||
goto init_err;
|
||||
}
|
||||
if (!s_mqtt_data.mqtt_config.init) {
|
||||
ESP_LOGW(TAG, "esp_insights_mqtt_init not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
err = s_mqtt_data.mqtt_config.init(s_mqtt_data.conn_params);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize MQTT connection");
|
||||
goto init_err;
|
||||
}
|
||||
return ESP_OK;
|
||||
init_err:
|
||||
clean_mqtt_conn_params();
|
||||
esp_event_handler_unregister(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, rmaker_common_event_handler);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void esp_insights_mqtt_deinit(void)
|
||||
{
|
||||
if (s_mqtt_data.mqtt_config.deinit) {
|
||||
s_mqtt_data.mqtt_config.deinit();
|
||||
}
|
||||
if (s_mqtt_data.node_id) {
|
||||
s_mqtt_data.node_id = NULL;
|
||||
}
|
||||
clean_mqtt_conn_params();
|
||||
esp_event_handler_unregister(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, rmaker_common_event_handler);
|
||||
}
|
||||
|
||||
static esp_err_t esp_insights_mqtt_connect(void)
|
||||
{
|
||||
if (s_mqtt_data.mqtt_config.connect) {
|
||||
return s_mqtt_data.mqtt_config.connect();
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_insights_mqtt_connect not registered");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static void esp_insights_mqtt_disconnect(void)
|
||||
{
|
||||
if (s_mqtt_data.mqtt_config.disconnect) {
|
||||
s_mqtt_data.mqtt_config.disconnect();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "esp_insights_mqtt_disconnect not registered");
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_mqtt_publish(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id)
|
||||
{
|
||||
if (s_mqtt_data.mqtt_config.publish) {
|
||||
return s_mqtt_data.mqtt_config.publish(topic, data, data_len, qos, msg_id);
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_insights_mqtt_publish not registered");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t esp_insights_mqtt_subscribe(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data)
|
||||
{
|
||||
if (s_mqtt_data.mqtt_config.subscribe) {
|
||||
ESP_LOGI(TAG, "subscribing to %s", topic);
|
||||
return s_mqtt_data.mqtt_config.subscribe(topic, cb, qos, priv_data);
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_insights_mqtt_subscribe not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int esp_insights_mqtt_data_send(void *data, size_t len)
|
||||
{
|
||||
char topic[128];
|
||||
int msg_id = -1;
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
if (!s_mqtt_data.node_id) {
|
||||
return -1;
|
||||
}
|
||||
snprintf(topic, sizeof(topic), "node/%s/%s", s_mqtt_data.node_id, INSIGHTS_TOPIC_SUFFIX);
|
||||
esp_insights_mqtt_publish(topic, data, len, 1, &msg_id);
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
esp_insights_transport_config_t g_default_insights_transport_mqtt = {
|
||||
.callbacks = {
|
||||
.init = esp_insights_mqtt_init,
|
||||
.deinit = esp_insights_mqtt_deinit,
|
||||
.connect = esp_insights_mqtt_connect,
|
||||
.disconnect = esp_insights_mqtt_disconnect,
|
||||
.data_send = esp_insights_mqtt_data_send,
|
||||
}
|
||||
};
|
@@ -0,0 +1 @@
|
||||
0629efd75df671b132cc3b3d66e37267d12da63ea734f820280431e4d805ab0b
|
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T16:59:20.876878+00:00", "files": [{"path": "CMakeLists.txt", "size": 1173, "hash": "4c0898e4897412abc6833081d94158c57c980882f03845427548e59e0d545c5f"}, {"path": "LICENSE", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "idf_component.yml", "size": 443, "hash": "992e11cd00c0046d8b9d94eed38a2a8d84fc128d71ece31a30923c9e679d197c"}, {"path": "Kconfig", "size": 1066, "hash": "0952686c100f7c8ba7d7b4f7854fc1d9a98b924c86178694bb33415d53e0f714"}, {"path": "create_ota_image.py", "size": 3591, "hash": "4e9eb7bbd13fe10daad30f33ff009665e0a83dd8b1c94813d5be0a77437ab691"}, {"path": "include/esp_rcp_update.h", "size": 4118, "hash": "6754270d95281b64fb089e076249baaa3e6ee40b66bb1aa9275d3ef60c5507b5"}, {"path": "include/esp_rcp_ota.h", "size": 2904, "hash": "5197cf63da01d833b6884b6b2b097f9f33907d40db16c71f1c7fe8ff8c8b63b5"}, {"path": "include/esp_rcp_firmware.h", "size": 735, "hash": "976bea2dfccb706f8c425b57ff490fbe900610d914eccd8b67ef23517e224a75"}, {"path": "src/esp_rcp_update.c", "size": 11289, "hash": "b0acc1f10cad3c743deea316fe3982c21cbc79e7ddec99a553722c1aab073ee3"}, {"path": "src/esp_rcp_ota.c", "size": 11292, "hash": "17da7bdcb4d793d2d5eea3cc22c860dec47d0c9a83d0fbe14757927bba2fd292"}]}
|
@@ -0,0 +1,28 @@
|
||||
idf_component_register(SRC_DIRS src
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES driver esp-serial-flasher nvs_flash)
|
||||
|
||||
idf_build_get_property(python PYTHON)
|
||||
if(CONFIG_AUTO_UPDATE_RCP)
|
||||
add_custom_target(rcp_image_generation ALL
|
||||
COMMAND ${python} ${CMAKE_CURRENT_SOURCE_DIR}/create_ota_image.py
|
||||
--rcp-build-dir ${CONFIG_RCP_SRC_DIR}
|
||||
--target-file ${CMAKE_CURRENT_BINARY_DIR}/spiffs_image/ot_rcp_0/rcp_image
|
||||
)
|
||||
|
||||
spiffs_create_partition_image(${CONFIG_RCP_PARTITION_NAME} ${CMAKE_CURRENT_BINARY_DIR}/spiffs_image FLASH_IN_PROJECT
|
||||
DEPENDS rcp_image_generation)
|
||||
endif()
|
||||
|
||||
if(CONFIG_CREATE_OTA_IMAGE_WITH_RCP_FW)
|
||||
add_custom_command(OUTPUT ${build_dir}/ota_with_rcp_image
|
||||
COMMAND ${python} ${CMAKE_CURRENT_SOURCE_DIR}/create_ota_image.py
|
||||
--rcp-build-dir ${CONFIG_RCP_SRC_DIR}
|
||||
--target-file ${build_dir}/ota_with_rcp_image
|
||||
--br-firmware "${build_dir}/${elf_name}.bin"
|
||||
DEPENDS "${build_dir}/.bin_timestamp"
|
||||
)
|
||||
|
||||
add_custom_target(gen_ota_image ALL DEPENDS ${build_dir}/ota_with_rcp_image)
|
||||
add_dependencies(gen_ota_image gen_project_binary)
|
||||
endif()
|
@@ -0,0 +1,31 @@
|
||||
menu "OpenThread RCP Update"
|
||||
|
||||
config AUTO_UPDATE_RCP
|
||||
bool 'Update RCP automatically'
|
||||
default n
|
||||
help
|
||||
If enabled, the device will store the RCP image in its firmware and
|
||||
compare the stored image version with the running RCP image upon boot. The RCP
|
||||
will be automatically updated upon version mismatch.
|
||||
|
||||
config CREATE_OTA_IMAGE_WITH_RCP_FW
|
||||
bool 'Create the OTA image with rcp for border router'
|
||||
default n
|
||||
help
|
||||
If enabled, an ota image will be generated during building.
|
||||
|
||||
config RCP_SRC_DIR
|
||||
depends on AUTO_UPDATE_RCP || CREATE_OTA_IMAGE_WITH_RCP_FW
|
||||
string "Source folder containing the RCP firmware"
|
||||
default "$ENV{IDF_PATH}/examples/openthread/ot_rcp/build"
|
||||
help
|
||||
The source folder containing the RCP firmware.
|
||||
|
||||
config RCP_PARTITION_NAME
|
||||
depends on AUTO_UPDATE_RCP
|
||||
string "Name of RCP storage partition"
|
||||
default "rcp_fw"
|
||||
help
|
||||
The name of RCP storage partition.
|
||||
|
||||
endmenu
|
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import pathlib
|
||||
import shutil
|
||||
import struct
|
||||
|
||||
FILETAG_RCP_VERSION = 0
|
||||
FILETAG_RCP_FLASH_ARGS = 1
|
||||
FILETAG_RCP_BOOTLOADER = 2
|
||||
FILETAG_RCP_PARTITION_TABLE = 3
|
||||
FILETAG_RCP_FIRMWARE = 4
|
||||
FILETAG_BR_OTA_IMAGE = 5
|
||||
FILETAG_IMAGE_HEADER = 0xff
|
||||
|
||||
HEADER_ENTRY_SIZE = 3 * 4
|
||||
RCP_IMAGE_HEADER_SIZE = HEADER_ENTRY_SIZE * 6
|
||||
RCP_FLASH_ARGS_SIZE = 2 * 4 * 3
|
||||
|
||||
|
||||
def append_subfile_header(fout, tag, size, offset):
|
||||
fout.write(struct.pack('<LLL', tag, size, offset))
|
||||
return offset + size
|
||||
|
||||
|
||||
def append_subfile(fout, target_file):
|
||||
buf_size = 4096
|
||||
with open(target_file, 'rb') as fin:
|
||||
data = fin.read(buf_size)
|
||||
while data and len(data) > 0:
|
||||
fout.write(data)
|
||||
data = fin.read(buf_size)
|
||||
|
||||
|
||||
def append_flash_args(fout, flash_args_path):
|
||||
with open(flash_args_path, 'r') as f:
|
||||
# skip first line
|
||||
next(f)
|
||||
partition_info_list = [l.split() for l in f]
|
||||
for offset, partition_file in partition_info_list:
|
||||
offset = int(offset, 0)
|
||||
if partition_file.find('bootloader') >= 0:
|
||||
fout.write(struct.pack('<LL', FILETAG_RCP_BOOTLOADER, offset))
|
||||
elif partition_file.find('partition_table') >= 0:
|
||||
fout.write(struct.pack('<LL', FILETAG_RCP_PARTITION_TABLE, offset))
|
||||
else:
|
||||
fout.write(struct.pack('<LL', FILETAG_RCP_FIRMWARE, offset))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--rcp-build-dir', type=str, required=True)
|
||||
parser.add_argument('--br-firmware', type=str, required=False)
|
||||
parser.add_argument('--target-file', type=str, required=True)
|
||||
args = parser.parse_args()
|
||||
base_dir = args.rcp_build_dir
|
||||
pathlib.Path(os.path.dirname(args.target_file)).mkdir(parents=True, exist_ok=True)
|
||||
rcp_version_path = os.path.join(base_dir, 'rcp_version')
|
||||
flash_args_path = os.path.join(base_dir, 'flash_args')
|
||||
bootloader_path = os.path.join(base_dir, 'bootloader', 'bootloader.bin')
|
||||
partition_table_path = os.path.join(
|
||||
base_dir, 'partition_table', 'partition-table.bin')
|
||||
rcp_firmware_path = os.path.join(base_dir, 'esp_ot_rcp.bin')
|
||||
with open(args.target_file, 'wb') as fout:
|
||||
image_header_size = RCP_IMAGE_HEADER_SIZE
|
||||
if args.br_firmware:
|
||||
image_header_size += HEADER_ENTRY_SIZE
|
||||
offset = append_subfile_header(
|
||||
fout, FILETAG_IMAGE_HEADER, image_header_size, 0)
|
||||
offset = append_subfile_header(
|
||||
fout, FILETAG_RCP_VERSION, os.path.getsize(rcp_version_path), offset)
|
||||
offset = append_subfile_header(
|
||||
fout, FILETAG_RCP_FLASH_ARGS, RCP_FLASH_ARGS_SIZE, offset)
|
||||
offset = append_subfile_header(
|
||||
fout, FILETAG_RCP_BOOTLOADER, os.path.getsize(bootloader_path), offset)
|
||||
offset = append_subfile_header(
|
||||
fout, FILETAG_RCP_PARTITION_TABLE, os.path.getsize(partition_table_path), offset)
|
||||
offset = append_subfile_header(
|
||||
fout, FILETAG_RCP_FIRMWARE, os.path.getsize(rcp_firmware_path), offset)
|
||||
if args.br_firmware:
|
||||
offset = append_subfile_header(fout, FILETAG_BR_OTA_IMAGE, os.path.getsize(args.br_firmware), offset)
|
||||
append_subfile(fout, rcp_version_path)
|
||||
append_flash_args(fout, flash_args_path)
|
||||
append_subfile(fout, bootloader_path)
|
||||
append_subfile(fout, partition_table_path)
|
||||
append_subfile(fout, rcp_firmware_path)
|
||||
if args.br_firmware:
|
||||
append_subfile(fout, args.br_firmware)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@@ -0,0 +1,12 @@
|
||||
dependencies:
|
||||
espressif/esp-serial-flasher:
|
||||
version: 1.8.0
|
||||
idf:
|
||||
version: '>=5.0'
|
||||
description: Espressif RCP Update Component for Thread Border Router and Zigbee Gateway
|
||||
repository: git://github.com/espressif/esp-thread-br.git
|
||||
repository_info:
|
||||
commit_sha: a5ed23e1a1abc8973c5f5c629de4cde44aef5b7c
|
||||
path: components/esp_rcp_update
|
||||
url: https://github.com/espressif/esp-thread-br/tree/main/components/esp_rcp_update
|
||||
version: 1.4.0
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_SUBFILE_INFO 7
|
||||
|
||||
typedef enum {
|
||||
FILETAG_RCP_VERSION = 0,
|
||||
FILETAG_RCP_FLASH_ARGS = 1,
|
||||
FILETAG_RCP_BOOTLOADER = 2,
|
||||
FILETAG_RCP_PARTITION_TABLE = 3,
|
||||
FILETAG_RCP_FIRMWARE = 4,
|
||||
FILETAG_HOST_FIRMWARE = 5,
|
||||
FILETAG_IMAGE_HEADER = 0xff,
|
||||
} esp_rcp_filetag_t;
|
||||
|
||||
struct esp_rcp_subfile_info {
|
||||
uint32_t tag;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef struct esp_rcp_subfile_info esp_rcp_subfile_info_t;
|
||||
|
||||
#define ESP_RCP_IMAGE_FILENAME "rcp_image"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_rcp_firmware.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ESP_RCP_OTA_STATE_READ_HEADER = 0, /* State of reading image header */
|
||||
ESP_RCP_OTA_STATE_DOWNLOAD_RCP_FW, /* State of writing RCP firmware */
|
||||
ESP_RCP_OTA_STATE_FINISHED, /* State of finishing RCP firmwares writing */
|
||||
ESP_RCP_OTA_STATE_INVALID, /* Invalid state */
|
||||
} esp_rcp_ota_state_t;
|
||||
|
||||
/* RCP OTA handle */
|
||||
typedef uint32_t esp_rcp_ota_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a handle of RCP OTA
|
||||
*
|
||||
* @param[out] out_handle On success, returns a handle which should be used for subsequent esp_rcp_ota_receive()
|
||||
* and esp_rcp_ota_end() calls.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rcp_ota_begin(esp_rcp_ota_handle_t *out_handle);
|
||||
|
||||
/**
|
||||
* @brief Get state of RCP OTA
|
||||
*
|
||||
* @param[in] handle Handle of RCP OTA
|
||||
*
|
||||
* @return state of RCP OTA
|
||||
*/
|
||||
esp_rcp_ota_state_t esp_rcp_ota_get_state(esp_rcp_ota_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Get subfile size from the parsed RCP image header
|
||||
*
|
||||
* This function must be called after the RCP OTA handle finishes the state of ESP_RCP_OTA_STATE_READ_HEADER
|
||||
*
|
||||
* @param[in] handle Handle of RCP OTA
|
||||
* @param[in] filetag Tag of the subfile
|
||||
*
|
||||
* @return size of the subfile in the RCP image
|
||||
*/
|
||||
uint32_t esp_rcp_ota_get_subfile_size(esp_rcp_ota_handle_t handle, esp_rcp_filetag_t filetag);
|
||||
|
||||
/**
|
||||
* @brief Receive RCP OTA data
|
||||
*
|
||||
* This function should be called multiple times as data is received during the OTA operation.
|
||||
* Data should be read sequentially from the image file generated by esp_rcp_update/create_ota_image.py.
|
||||
*
|
||||
* @param[in] handle Handle of RCP OTA
|
||||
* @param[in] data Data buffer received
|
||||
* @param[in] size Size of data buffer in bytes.
|
||||
* @param[out] received_size Received size from the data buffer.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rcp_ota_receive(esp_rcp_ota_handle_t handle, const void *data, size_t size, size_t *received_size);
|
||||
|
||||
/**
|
||||
* @brief Finish RCP OTA update, validate and apply newly updated image.
|
||||
*
|
||||
* @note After calling esp_rcp_ota_end(), the handle is no longer valid and any memory associated with
|
||||
* it is freed (regardless of result).
|
||||
*
|
||||
* @param[in] handle Handle of RCP OTA
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rcp_ota_end(esp_rcp_ota_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Abort RCP OTA update, free the handle and memory associated with it.
|
||||
*
|
||||
* @param[in] handle Handle of RCP OTA
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rcp_ota_abort(esp_rcp_ota_handle_t handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_loader.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RCP_FIRMWARE_DIR_SIZE 20
|
||||
#define RCP_FILENAME_MAX_SIZE 100
|
||||
#define RCP_URL_MAX_SIZE 100
|
||||
|
||||
typedef enum {
|
||||
RCP_TYPE_INVALID = 0, /* Invalid type */
|
||||
RCP_TYPE_UART = 1, /* Connected via UART */
|
||||
RCP_TYPE_MAX = 2, /* Max type */
|
||||
} esp_rcp_type_t;
|
||||
|
||||
/* For compatibility, will be removed in the next major update. */
|
||||
#define RCP_TYPE_ESP32H2_UART \
|
||||
_Pragma("GCC warning \"'RCP_TYPE_ESP32H2_UART' enum is deprecated, use 'RCP_TYPE_UART' instead\"") RCP_TYPE_UART
|
||||
|
||||
/**
|
||||
* @brief The RCP update config for OpenThread.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
esp_rcp_type_t rcp_type; /*!< RCP type */
|
||||
int uart_rx_pin; /*!< UART rx pin */
|
||||
int uart_tx_pin; /*!< UART tx pin */
|
||||
int uart_port; /*!< UART port */
|
||||
int uart_baudrate; /*!< UART baudrate */
|
||||
int reset_pin; /*!< RESET pin */
|
||||
int boot_pin; /*!< Boot mode select pin */
|
||||
uint32_t update_baudrate; /*!< Baudrate when flashing the firmware */
|
||||
char firmware_dir[RCP_FIRMWARE_DIR_SIZE]; /*!< The directory storing the RCP firmware */
|
||||
target_chip_t target_chip; /*!< The target chip type */
|
||||
} esp_rcp_update_config_t;
|
||||
|
||||
/**
|
||||
* @brief This function initializes the RCP update process
|
||||
*
|
||||
* @param[in] update_config The RCP update specific config
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
* - ESP_ERR_INVALID_ARG If the RCP type is not supported.
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_rcp_update_init(const esp_rcp_update_config_t *update_config);
|
||||
|
||||
/**
|
||||
* @brief This function triggers an RCP firmware update.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
* - ESP_ERR_INVALID_STASTE If the RCP update is not initialized.
|
||||
* - ESP_ERR_NOT_FOUND RCP firmware not found in storage.
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_rcp_update(void);
|
||||
|
||||
/**
|
||||
* @brief This function acquires the RCP image base directory.
|
||||
*
|
||||
* @note The real RCP image directory should be suffixed the update sequence.
|
||||
*
|
||||
*/
|
||||
const char *esp_rcp_get_firmware_dir(void);
|
||||
|
||||
/**
|
||||
* @brief This function retrieves the update image sequence.
|
||||
*
|
||||
* The current update image sequence will be used to update the RCP.
|
||||
*
|
||||
*/
|
||||
int8_t esp_rcp_get_update_seq(void);
|
||||
|
||||
/**
|
||||
* @brief This function retrieves the next update image sequence.
|
||||
*
|
||||
* The next update image sequence will be used for the downloaded image.
|
||||
*
|
||||
*/
|
||||
int8_t esp_rcp_get_next_update_seq(void);
|
||||
|
||||
/**
|
||||
* @brief This function resets the RCP.
|
||||
*
|
||||
*/
|
||||
void esp_rcp_reset(void);
|
||||
|
||||
/**
|
||||
* @brief This function marks the downloaded image as valid.
|
||||
*
|
||||
* The image in the next update image sequence will then be used for RCP update.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_STASTE If the RCP update is not initialized.
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_rcp_submit_new_image(void);
|
||||
|
||||
/**
|
||||
* @brief This function marks previously downloaded image as valid.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_STASTE If the RCP update is not initialized.
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_rcp_mark_image_verified(bool verified);
|
||||
|
||||
/**
|
||||
* @brief This function marks previously downloaded image as unusable.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_STASTE If the RCP update is not initialized.
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_rcp_mark_image_unusable(void);
|
||||
|
||||
/**
|
||||
* @brief This function loads the RCP version in the current update image.
|
||||
*
|
||||
* @param[out] version_str The RCP version string output.
|
||||
* @param[in] size Size of version_str.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_STASTE If the RCP update is not initialized.
|
||||
* - ESP_ERR_NOT_FOUND RCP version not found in update image.
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_rcp_load_version_in_storage(char *version_str, size_t size);
|
||||
|
||||
/**
|
||||
* @brief This function deinitializes the RCP update process.
|
||||
*
|
||||
*/
|
||||
void esp_rcp_update_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <esp_check.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_rcp_firmware.h>
|
||||
#include <esp_rcp_ota.h>
|
||||
#include <esp_rcp_update.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#define IMAGE_HEADER_MAX_LEN sizeof(esp_rcp_subfile_info_t) * MAX_SUBFILE_INFO
|
||||
#define OTA_MAX_WRITE_SIZE 16
|
||||
|
||||
typedef struct rcp_ota_entry_ {
|
||||
esp_rcp_ota_handle_t handle;
|
||||
esp_rcp_ota_state_t state;
|
||||
uint32_t header_size;
|
||||
uint32_t header_read;
|
||||
uint8_t image_header_buffer[IMAGE_HEADER_MAX_LEN];
|
||||
uint32_t rcp_firmware_size;
|
||||
uint32_t rcp_firmware_downloaded;
|
||||
FILE *rcp_fp;
|
||||
LIST_ENTRY(rcp_ota_entry_) entries;
|
||||
} rcp_ota_entry_t;
|
||||
|
||||
static LIST_HEAD(rcp_ota_entries_head,
|
||||
rcp_ota_entry_) s_rcp_ota_entries_head = LIST_HEAD_INITIALIZER(s_rcp_ota_entries_head);
|
||||
|
||||
static esp_rcp_ota_handle_t s_ota_last_handle = 0;
|
||||
|
||||
const static char *TAG = "esp_rcp_ota";
|
||||
|
||||
esp_err_t esp_rcp_ota_begin(esp_rcp_ota_handle_t *out_handle)
|
||||
{
|
||||
rcp_ota_entry_t *new_entry = NULL;
|
||||
ESP_RETURN_ON_FALSE(out_handle, ESP_ERR_INVALID_ARG, TAG, "out_handle cannot be NULL");
|
||||
|
||||
new_entry = (rcp_ota_entry_t *)calloc(1, sizeof(rcp_ota_entry_t));
|
||||
ESP_RETURN_ON_FALSE(new_entry, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory for RCP OTA handle");
|
||||
LIST_INSERT_HEAD(&s_rcp_ota_entries_head, new_entry, entries);
|
||||
|
||||
new_entry->state = ESP_RCP_OTA_STATE_READ_HEADER;
|
||||
new_entry->header_size = 0;
|
||||
new_entry->header_read = 0;
|
||||
new_entry->rcp_firmware_size = 0;
|
||||
new_entry->rcp_firmware_downloaded = 0;
|
||||
new_entry->rcp_fp = NULL;
|
||||
memset(new_entry->image_header_buffer, 0, sizeof(new_entry->image_header_buffer));
|
||||
new_entry->handle = ++s_ota_last_handle;
|
||||
*out_handle = new_entry->handle;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static rcp_ota_entry_t *find_esp_rcp_ota_entry(esp_rcp_ota_handle_t handle)
|
||||
{
|
||||
rcp_ota_entry_t *ret = NULL;
|
||||
for (ret = LIST_FIRST(&s_rcp_ota_entries_head); ret != NULL; ret = LIST_NEXT(ret, entries)) {
|
||||
if (ret->handle == handle) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_rcp_ota_state_t esp_rcp_ota_get_state(esp_rcp_ota_handle_t handle)
|
||||
{
|
||||
rcp_ota_entry_t *entry = find_esp_rcp_ota_entry(handle);
|
||||
return entry ? entry->state : ESP_RCP_OTA_STATE_INVALID;
|
||||
}
|
||||
|
||||
uint32_t esp_rcp_ota_get_subfile_size(esp_rcp_ota_handle_t handle, esp_rcp_filetag_t tag)
|
||||
{
|
||||
rcp_ota_entry_t *entry = find_esp_rcp_ota_entry(handle);
|
||||
if (!entry || entry->header_size % sizeof(esp_rcp_subfile_info_t) != 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t subfile_info_num = entry->header_size / sizeof(esp_rcp_subfile_info_t);
|
||||
for (size_t i = 0; i < subfile_info_num; ++i) {
|
||||
esp_rcp_subfile_info_t *subfile_info =
|
||||
(esp_rcp_subfile_info_t *)(&entry->image_header_buffer[i * sizeof(esp_rcp_subfile_info_t)]);
|
||||
if (subfile_info->tag == tag) {
|
||||
return subfile_info->size;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_image_header(rcp_ota_entry_t *entry)
|
||||
{
|
||||
size_t subfile_info_num = entry->header_size / sizeof(esp_rcp_subfile_info_t);
|
||||
for (size_t i = 0; i < subfile_info_num; ++i) {
|
||||
esp_rcp_subfile_info_t *subfile_info =
|
||||
(esp_rcp_subfile_info_t *)(&entry->image_header_buffer[i * sizeof(esp_rcp_subfile_info_t)]);
|
||||
if (subfile_info->tag == FILETAG_IMAGE_HEADER || subfile_info->tag == FILETAG_RCP_VERSION ||
|
||||
subfile_info->tag == FILETAG_RCP_BOOTLOADER || subfile_info->tag == FILETAG_RCP_FLASH_ARGS ||
|
||||
subfile_info->tag == FILETAG_RCP_PARTITION_TABLE || subfile_info->tag == FILETAG_RCP_FIRMWARE) {
|
||||
entry->rcp_firmware_size += subfile_info->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int write_file_for_length(FILE *fp, const void *buf, size_t size)
|
||||
{
|
||||
static const int k_max_retry = 5;
|
||||
int retry_count = 0;
|
||||
int offset = 0;
|
||||
const uint8_t *data = (const uint8_t *)buf;
|
||||
while (offset < size) {
|
||||
int ret =
|
||||
fwrite(data + offset, 1, ((size - offset) < OTA_MAX_WRITE_SIZE ? (size - offset) : OTA_MAX_WRITE_SIZE), fp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
retry_count++;
|
||||
} else {
|
||||
offset += ret;
|
||||
retry_count = 0;
|
||||
}
|
||||
if (retry_count > k_max_retry) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static esp_err_t receive_header(const uint8_t *data, size_t size, rcp_ota_entry_t *entry, size_t *consumed_size)
|
||||
{
|
||||
if (entry->header_size == 0) {
|
||||
if (entry->header_read < sizeof(esp_rcp_subfile_info_t)) {
|
||||
size_t copy_size = size > sizeof(esp_rcp_subfile_info_t) - entry->header_read
|
||||
? sizeof(esp_rcp_subfile_info_t) - entry->header_read
|
||||
: size;
|
||||
memcpy(entry->image_header_buffer + entry->header_read, data, copy_size);
|
||||
data = data + copy_size;
|
||||
size -= copy_size;
|
||||
entry->header_read += copy_size;
|
||||
*consumed_size += copy_size;
|
||||
}
|
||||
if (entry->header_read >= sizeof(esp_rcp_subfile_info_t)) {
|
||||
esp_rcp_subfile_info_t *subfile_info = (esp_rcp_subfile_info_t *)(entry->image_header_buffer);
|
||||
if (subfile_info->tag != FILETAG_IMAGE_HEADER || subfile_info->offset != 0 ||
|
||||
subfile_info->size % sizeof(esp_rcp_subfile_info_t) != 0) {
|
||||
ESP_LOGE(TAG, "Invalid image header");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
} else {
|
||||
entry->header_size = subfile_info->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->header_size > 0 && entry->header_read < entry->header_size) {
|
||||
size_t copy_size =
|
||||
size > entry->header_size - entry->header_read ? entry->header_size - entry->header_read : size;
|
||||
memcpy(entry->image_header_buffer + entry->header_read, data, copy_size);
|
||||
data += copy_size;
|
||||
size -= copy_size;
|
||||
entry->header_read += copy_size;
|
||||
*consumed_size += copy_size;
|
||||
}
|
||||
if (entry->header_size > 0 && entry->header_read >= entry->header_size) {
|
||||
parse_image_header(entry);
|
||||
if (entry->rcp_firmware_size > 0) {
|
||||
entry->state = ESP_RCP_OTA_STATE_DOWNLOAD_RCP_FW;
|
||||
} else {
|
||||
entry->state = ESP_RCP_OTA_STATE_FINISHED;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t receive_rcp_fw(const uint8_t *data, size_t size, rcp_ota_entry_t *entry, size_t *consumed_size)
|
||||
{
|
||||
if (entry->rcp_firmware_size == 0 || entry->rcp_firmware_size <= entry->rcp_firmware_downloaded) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!entry->rcp_fp) {
|
||||
const char *rcp_firmware_dir = esp_rcp_get_firmware_dir();
|
||||
int8_t rcp_update_seq = esp_rcp_get_next_update_seq();
|
||||
char rcp_target_path[RCP_FILENAME_MAX_SIZE];
|
||||
sprintf(rcp_target_path, "%s_%d/" ESP_RCP_IMAGE_FILENAME, rcp_firmware_dir, rcp_update_seq);
|
||||
entry->rcp_fp = fopen(rcp_target_path, "w");
|
||||
if (!entry->rcp_fp) {
|
||||
ESP_LOGE(TAG, "Fail to open %s, will delete it and create a new one", rcp_target_path);
|
||||
remove(rcp_target_path);
|
||||
entry->rcp_fp = fopen(rcp_target_path, "w");
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(entry->rcp_fp, ESP_FAIL, TAG, "Fail to open %s", rcp_target_path);
|
||||
ESP_LOGI(TAG, "Start downloading the rcp firmware");
|
||||
}
|
||||
if (entry->rcp_firmware_downloaded == 0) {
|
||||
ESP_RETURN_ON_FALSE(write_file_for_length(entry->rcp_fp, entry->image_header_buffer, entry->header_size) ==
|
||||
entry->header_size,
|
||||
ESP_FAIL, TAG, "Failed to write data");
|
||||
entry->rcp_firmware_downloaded += entry->header_size;
|
||||
ESP_LOGD(TAG, "RCP firmware download %ld/%ld", entry->rcp_firmware_downloaded, entry->rcp_firmware_size);
|
||||
}
|
||||
if (entry->rcp_firmware_downloaded < entry->rcp_firmware_size &&
|
||||
entry->rcp_firmware_downloaded >= entry->header_size) {
|
||||
size_t copy_size = size > entry->rcp_firmware_size - entry->rcp_firmware_downloaded
|
||||
? entry->rcp_firmware_size - entry->rcp_firmware_downloaded
|
||||
: size;
|
||||
ESP_RETURN_ON_FALSE(write_file_for_length(entry->rcp_fp, data, copy_size) == copy_size, ESP_FAIL, TAG,
|
||||
"Failed to write data");
|
||||
entry->rcp_firmware_downloaded += copy_size;
|
||||
*consumed_size += copy_size;
|
||||
ESP_LOGD(TAG, "RCP firmware download %ld/%ld", entry->rcp_firmware_downloaded, entry->rcp_firmware_size);
|
||||
}
|
||||
if (entry->rcp_firmware_downloaded >= entry->rcp_firmware_size) {
|
||||
if (entry->rcp_fp != NULL) {
|
||||
fclose(entry->rcp_fp);
|
||||
entry->rcp_fp = NULL;
|
||||
}
|
||||
entry->state = ESP_RCP_OTA_STATE_FINISHED;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_ota_receive(esp_rcp_ota_handle_t handle, const void *data, size_t size, size_t *received_size)
|
||||
{
|
||||
rcp_ota_entry_t *entry = find_esp_rcp_ota_entry(handle);
|
||||
ESP_RETURN_ON_FALSE(entry, ESP_ERR_NOT_FOUND, TAG, "Invalid rcp_ota handle");
|
||||
ESP_RETURN_ON_FALSE(data && size != 0, ESP_ERR_INVALID_ARG, TAG, "Invalid data received");
|
||||
const uint8_t *cur_data = data;
|
||||
*received_size = 0;
|
||||
|
||||
while (size > 0) {
|
||||
size_t consumed_size = 0;
|
||||
switch (entry->state) {
|
||||
case ESP_RCP_OTA_STATE_READ_HEADER: {
|
||||
ESP_RETURN_ON_ERROR(receive_header(cur_data, size, entry, &consumed_size), TAG, "Failed to receive header");
|
||||
break;
|
||||
}
|
||||
case ESP_RCP_OTA_STATE_DOWNLOAD_RCP_FW: {
|
||||
ESP_RETURN_ON_ERROR(receive_rcp_fw(cur_data, size, entry, &consumed_size), TAG, "Failed to receive rcp_fw");
|
||||
break;
|
||||
}
|
||||
case ESP_RCP_OTA_STATE_FINISHED:
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid state of Border Router OTA");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
cur_data += consumed_size;
|
||||
size -= consumed_size;
|
||||
*received_size = cur_data - (const uint8_t *)data;
|
||||
if (entry->state == ESP_RCP_OTA_STATE_FINISHED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_ota_end(esp_rcp_ota_handle_t handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
rcp_ota_entry_t *entry = find_esp_rcp_ota_entry(handle);
|
||||
ESP_RETURN_ON_FALSE(entry, ESP_ERR_NOT_FOUND, TAG, "Invalid rcp_ota handle");
|
||||
ESP_GOTO_ON_FALSE(entry->state == ESP_RCP_OTA_STATE_FINISHED, ESP_ERR_INVALID_STATE, cleanup, TAG, "Invalid State");
|
||||
// TODO: esp_rcp_submit_new_image() is not a thread-safe function, we need to make it thread-safe.
|
||||
ESP_GOTO_ON_ERROR(esp_rcp_submit_new_image(), cleanup, TAG, "Failed to submit RCP image");
|
||||
cleanup:
|
||||
if (entry->rcp_fp != NULL) {
|
||||
fclose(entry->rcp_fp);
|
||||
}
|
||||
LIST_REMOVE(entry, entries);
|
||||
free(entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_ota_abort(esp_rcp_ota_handle_t handle)
|
||||
{
|
||||
rcp_ota_entry_t *entry = find_esp_rcp_ota_entry(handle);
|
||||
ESP_RETURN_ON_FALSE(entry, ESP_ERR_NOT_FOUND, TAG, "Invalid rcp_ota handle");
|
||||
|
||||
if (entry->rcp_fp != NULL) {
|
||||
fclose(entry->rcp_fp);
|
||||
}
|
||||
LIST_REMOVE(entry, entries);
|
||||
free(entry);
|
||||
return ESP_OK;
|
||||
}
|
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_rcp_update.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp32_port.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_loader.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_rcp_firmware.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#define RCP_UPDATE_MAX_RETRY 3
|
||||
#define RCP_VERIFIED_FLAG (1 << 5)
|
||||
#define RCP_SEQ_KEY "rcp_seq"
|
||||
#define TAG "RCP_UPDATE"
|
||||
|
||||
typedef struct esp_rcp_update_handle {
|
||||
nvs_handle_t nvs_handle;
|
||||
int8_t update_seq;
|
||||
bool verified;
|
||||
esp_rcp_update_config_t update_config;
|
||||
} esp_rcp_update_handle;
|
||||
|
||||
struct rcp_flash_arg_t {
|
||||
uint32_t tag;
|
||||
uint32_t offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef struct rcp_flash_arg_t rcp_flash_arg_t;
|
||||
|
||||
static esp_rcp_update_handle s_handle;
|
||||
|
||||
static esp_loader_error_t connect_to_target(target_chip_t target_chip, uint32_t higher_baudrate)
|
||||
{
|
||||
esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT();
|
||||
|
||||
esp_loader_error_t err = esp_loader_connect(&connect_config);
|
||||
if (err != ESP_LOADER_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
if (esp_loader_get_target() != target_chip) {
|
||||
return ESP_LOADER_ERROR_UNSUPPORTED_CHIP;
|
||||
}
|
||||
|
||||
if (higher_baudrate) {
|
||||
ESP_RETURN_ON_ERROR(esp_loader_change_transmission_rate(higher_baudrate), TAG,
|
||||
"Failed to change bootloader baudrate");
|
||||
ESP_RETURN_ON_ERROR(loader_port_change_transmission_rate(higher_baudrate), TAG,
|
||||
"Failed to change local port baudrate");
|
||||
}
|
||||
return ESP_LOADER_SUCCESS;
|
||||
}
|
||||
|
||||
static esp_err_t seek_to_subfile(FILE *fp, esp_rcp_filetag_t tag, esp_rcp_subfile_info_t *found_info)
|
||||
{
|
||||
if (fseek(fp, 0, SEEK_SET) != 0) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rcp_subfile_info_t subfile_info;
|
||||
if (fread(&subfile_info, 1, sizeof(subfile_info), fp) != sizeof(subfile_info)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (subfile_info.tag != FILETAG_IMAGE_HEADER || subfile_info.size % sizeof(subfile_info) != 0) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int num_subfiles = subfile_info.size / sizeof(subfile_info);
|
||||
for (int i = 1; i < num_subfiles; i++) {
|
||||
if (fread(&subfile_info, 1, sizeof(subfile_info), fp) != sizeof(subfile_info)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (subfile_info.tag == tag) {
|
||||
*found_info = subfile_info;
|
||||
return fseek(fp, subfile_info.offset, SEEK_SET) == 0 ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_load_version_in_storage(char *version_str, size_t size)
|
||||
{
|
||||
char fullpath[RCP_FILENAME_MAX_SIZE];
|
||||
int8_t update_seq = esp_rcp_get_update_seq();
|
||||
|
||||
sprintf(fullpath, "%s_%d/" ESP_RCP_IMAGE_FILENAME, s_handle.update_config.firmware_dir, update_seq);
|
||||
FILE *fp = fopen(fullpath, "r");
|
||||
if (fp == NULL) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
esp_rcp_subfile_info_t version_info;
|
||||
ESP_RETURN_ON_ERROR(seek_to_subfile(fp, FILETAG_RCP_VERSION, &version_info), TAG, "Failed to find version subfile");
|
||||
memset(version_str, 0, size);
|
||||
int read_size = size < version_info.size ? size : version_info.size;
|
||||
fread(version_str, 1, read_size, fp);
|
||||
fclose(fp);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_loader_error_t flash_binary(FILE *firmware, size_t size, size_t address)
|
||||
{
|
||||
esp_loader_error_t err;
|
||||
static uint8_t payload[1024];
|
||||
|
||||
ESP_LOGI(TAG, "Erasing flash (this may take a while)...");
|
||||
err = esp_loader_flash_start(address, size, sizeof(payload));
|
||||
if (err != ESP_LOADER_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Erasing flash failed with error %d.", err);
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Start programming");
|
||||
|
||||
size_t binary_size = size;
|
||||
size_t written = 0;
|
||||
|
||||
ESP_LOGI(TAG, "binary_size %u", binary_size);
|
||||
while (size > 0) {
|
||||
size_t to_read = size < sizeof(payload) ? size : sizeof(payload);
|
||||
fread(payload, 1, to_read, firmware);
|
||||
|
||||
err = esp_loader_flash_write(payload, to_read);
|
||||
if (err != ESP_LOADER_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Packet could not be written! Error %d.", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
size -= to_read;
|
||||
written += to_read;
|
||||
ESP_LOGI(TAG, "left size %u, written %u", size, written);
|
||||
|
||||
int progress = (int)(((float)written / binary_size) * 100);
|
||||
ESP_LOGI(TAG, "Progress: %d %%", progress);
|
||||
fflush(stdout);
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Finished programming");
|
||||
|
||||
err = esp_loader_flash_verify();
|
||||
if (err != ESP_LOADER_SUCCESS) {
|
||||
ESP_LOGE(TAG, "MD5 does not match. err: %d", err);
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Flash verified");
|
||||
|
||||
return ESP_LOADER_SUCCESS;
|
||||
}
|
||||
|
||||
static void load_rcp_update_seq(esp_rcp_update_handle *handle)
|
||||
{
|
||||
int8_t seq = 0;
|
||||
bool verified;
|
||||
esp_err_t err = nvs_get_i8(handle->nvs_handle, RCP_SEQ_KEY, &seq);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
seq = 0;
|
||||
verified = 1;
|
||||
} else {
|
||||
verified = (seq & RCP_VERIFIED_FLAG);
|
||||
seq = (seq & ~RCP_VERIFIED_FLAG);
|
||||
}
|
||||
handle->update_seq = seq;
|
||||
handle->verified = verified;
|
||||
}
|
||||
|
||||
const char *esp_rcp_get_firmware_dir(void)
|
||||
{
|
||||
return s_handle.update_config.firmware_dir;
|
||||
}
|
||||
|
||||
int8_t esp_rcp_get_update_seq(void)
|
||||
{
|
||||
return s_handle.verified ? (s_handle.update_seq) : (1 - s_handle.update_seq);
|
||||
}
|
||||
|
||||
int8_t esp_rcp_get_next_update_seq(void)
|
||||
{
|
||||
return 1 - esp_rcp_get_update_seq();
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_submit_new_image()
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(s_handle.update_config.rcp_type != RCP_TYPE_INVALID, ESP_ERR_INVALID_STATE, TAG,
|
||||
"RCP update not initialized");
|
||||
s_handle.update_seq = esp_rcp_get_next_update_seq();
|
||||
s_handle.verified = true;
|
||||
|
||||
int8_t new_seq = s_handle.update_seq | RCP_VERIFIED_FLAG;
|
||||
esp_err_t error = nvs_set_i8(s_handle.nvs_handle, RCP_SEQ_KEY, new_seq);
|
||||
if (error == ESP_OK) {
|
||||
return nvs_commit(s_handle.nvs_handle);
|
||||
} else {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_update_init(const esp_rcp_update_config_t *update_config)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(nvs_open("storage", NVS_READWRITE, &s_handle.nvs_handle), "TAG", "Failed to open nvs");
|
||||
ESP_RETURN_ON_FALSE(update_config->rcp_type > RCP_TYPE_INVALID && update_config->rcp_type < RCP_TYPE_MAX,
|
||||
ESP_ERR_INVALID_ARG, TAG, "Unsupported RCP type");
|
||||
|
||||
s_handle.update_config = *update_config;
|
||||
load_rcp_update_seq(&s_handle);
|
||||
ESP_LOGI(TAG, "RCP: using update sequence %d", s_handle.update_seq);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
|
||||
static esp_err_t esp_rcp_boot_pin_mux(void)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
memset(&io_conf, 0, sizeof(io_conf));
|
||||
io_conf.intr_type = GPIO_INTR_NEGEDGE;
|
||||
io_conf.pin_bit_mask = (1ULL << s_handle.update_config.boot_pin);
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&io_conf), TAG, "Failed to config GPIO[%d]", s_handle.update_config.boot_pin);
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_rcp_update(void)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(s_handle.update_config.rcp_type != RCP_TYPE_INVALID, ESP_ERR_INVALID_STATE, TAG,
|
||||
"RCP update not initialized");
|
||||
|
||||
loader_esp32_config_t loader_config = {
|
||||
.baud_rate = s_handle.update_config.uart_baudrate,
|
||||
.uart_port = s_handle.update_config.uart_port,
|
||||
.uart_rx_pin = s_handle.update_config.uart_rx_pin,
|
||||
.uart_tx_pin = s_handle.update_config.uart_tx_pin,
|
||||
.reset_trigger_pin = s_handle.update_config.reset_pin,
|
||||
.gpio0_trigger_pin = s_handle.update_config.boot_pin,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(loader_port_esp32_init(&loader_config), TAG, "Failed to initialize UART port");
|
||||
ESP_RETURN_ON_ERROR(connect_to_target(s_handle.update_config.target_chip, s_handle.update_config.update_baudrate),
|
||||
TAG, "Failed to connect to RCP");
|
||||
|
||||
char fullpath[RCP_FILENAME_MAX_SIZE];
|
||||
int update_seq = esp_rcp_get_update_seq();
|
||||
sprintf(fullpath, "%s_%d/" ESP_RCP_IMAGE_FILENAME, s_handle.update_config.firmware_dir, update_seq);
|
||||
FILE *fp = fopen(fullpath, "r");
|
||||
ESP_RETURN_ON_FALSE(fp != NULL, ESP_ERR_NOT_FOUND, TAG, "Cannot find rcp image");
|
||||
esp_rcp_subfile_info_t subfile;
|
||||
seek_to_subfile(fp, FILETAG_RCP_FLASH_ARGS, &subfile);
|
||||
int num_flash_binaries = subfile.size / sizeof(rcp_flash_arg_t);
|
||||
|
||||
for (int i = 0; i < num_flash_binaries; i++) {
|
||||
rcp_flash_arg_t flash_args;
|
||||
fread(&flash_args, 1, sizeof(flash_args), fp);
|
||||
int num_retry = 0;
|
||||
long current = ftell(fp);
|
||||
if (seek_to_subfile(fp, flash_args.tag, &subfile) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to seek to subfile with tag %lu", flash_args.tag);
|
||||
abort();
|
||||
}
|
||||
while (flash_binary(fp, subfile.size, flash_args.offset) != ESP_LOADER_SUCCESS) {
|
||||
ESP_LOGW(TAG, "Failed to flash %s, retrying...", fullpath);
|
||||
num_retry++;
|
||||
if (num_retry > RCP_UPDATE_MAX_RETRY) {
|
||||
ESP_LOGE(TAG, "Failed to update RCP, abort and reboot");
|
||||
abort();
|
||||
}
|
||||
seek_to_subfile(fp, flash_args.tag, &subfile);
|
||||
}
|
||||
fseek(fp, current, SEEK_SET);
|
||||
}
|
||||
fclose(fp);
|
||||
esp_loader_reset_target();
|
||||
loader_port_esp32_deinit();
|
||||
|
||||
#if CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
|
||||
ESP_RETURN_ON_ERROR(esp_rcp_boot_pin_mux(), TAG, "Failed to multiplex boot pin");
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_rcp_update_deinit(void)
|
||||
{
|
||||
nvs_close(s_handle.nvs_handle);
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_mark_image_verified(bool verified)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(s_handle.update_config.rcp_type != RCP_TYPE_INVALID, ESP_ERR_INVALID_STATE, TAG,
|
||||
"RCP update not initialized");
|
||||
int8_t val;
|
||||
if (!verified) {
|
||||
val = esp_rcp_get_update_seq();
|
||||
} else {
|
||||
val = esp_rcp_get_update_seq() | RCP_VERIFIED_FLAG;
|
||||
}
|
||||
s_handle.verified = verified;
|
||||
s_handle.update_seq = (val & ~RCP_VERIFIED_FLAG);
|
||||
esp_err_t error = nvs_set_i8(s_handle.nvs_handle, RCP_SEQ_KEY, val);
|
||||
if (error == ESP_OK) {
|
||||
return nvs_commit(s_handle.nvs_handle);
|
||||
} else {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_rcp_mark_image_unusable(void)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(s_handle.update_config.rcp_type != RCP_TYPE_INVALID, ESP_ERR_INVALID_STATE, TAG,
|
||||
"RCP update not initialized");
|
||||
|
||||
s_handle.verified = 0;
|
||||
int8_t val = s_handle.update_seq & ~RCP_VERIFIED_FLAG;
|
||||
esp_err_t error = nvs_set_i8(s_handle.nvs_handle, RCP_SEQ_KEY, val);
|
||||
if (error == ESP_OK) {
|
||||
return nvs_commit(s_handle.nvs_handle);
|
||||
} else {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_rcp_reset(void)
|
||||
{
|
||||
gpio_config_t io_conf = {};
|
||||
uint8_t reset_pin = s_handle.update_config.reset_pin;
|
||||
io_conf.pin_bit_mask = ((1ULL << reset_pin));
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(reset_pin, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(150));
|
||||
gpio_set_level(reset_pin, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(850));
|
||||
}
|
@@ -0,0 +1 @@
|
||||
e202a9c688f7f1ab601efb91d682e4bcfaebc508dcceee1a1e0a0d2d1ca75a26
|
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T17:01:57.285779+00:00", "files": [{"path": "CMakeLists.txt", "size": 385, "hash": "b17de1af3a50ce7620daf4e6bffbc68fc7d3c59d865226e2f6583008e3c2c524"}, {"path": "LICENSE", "size": 11357, "hash": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4"}, {"path": "idf_component.yml", "size": 425, "hash": "a668bdef108230d23c87160ad253336784d0b2d1100cf9998b7db675b89286b6"}, {"path": "component.mk", "size": 95, "hash": "c20dd1fca41afe18163119c0f27ae2dcdd1276cc75b69d1440f4c76c1dc1ef09"}, {"path": "README.md", "size": 1707, "hash": "5b0d31601429dc46cde78716affe25c40ef05fdf0a50afa347051445871f61f6"}, {"path": "include/esp_schedule.h", "size": 8387, "hash": "4328488dea993ec634725f12eab95a3d0b32fb4798de34415a2228bc7260df37"}, {"path": "src/esp_schedule_nvs.c", "size": 10106, "hash": "5dca7ad66b84fc2b3a7bf434586ba1832029e987b899f02c91c191929eacbb57"}, {"path": "src/esp_schedule_internal.h", "size": 844, "hash": "55af8d787a7d4fca912a2f815c55bdca92f9afa1b14b41c3b032c0cd9db3f58f"}, {"path": "src/esp_schedule.c", "size": 24094, "hash": "81afd23f2e74a1e6e7329eac63dab44b7868cc119b2e0729fe1829af9e665c0e"}]}
|
@@ -0,0 +1,10 @@
|
||||
set(component_srcs "src/esp_schedule.c"
|
||||
"src/esp_schedule_nvs.c")
|
||||
|
||||
idf_component_register(SRCS "${component_srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "src"
|
||||
PRIV_REQUIRES "rmaker_common"
|
||||
REQUIRES "nvs_flash")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function")
|
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@@ -0,0 +1,50 @@
|
||||
# ESP Scheduling
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_schedule)
|
||||
|
||||
This component is used internally by ESP RainMaker to implement schedules.
|
||||
|
||||
> Note: By default, the time is w.r.t. UTC. If the timezone has been set, then the time is w.r.t. the specified timezone.
|
||||
|
||||
## Test code:
|
||||
|
||||
```
|
||||
#include <esp_schedule.h>
|
||||
|
||||
void app_schedule_trigger_cb(esp_schedule_handle_t handle, void *priv_data)
|
||||
{
|
||||
printf("priv_data: %.*s\n", (char *)priv_data);
|
||||
}
|
||||
|
||||
static char *priv_data_global = "from app";
|
||||
|
||||
void app_schedule_set()
|
||||
{
|
||||
esp_schedule_config_t schedule_config = {
|
||||
.name = "test",
|
||||
.trigger.type = ESP_SCHEDULE_TYPE_DAYS_OF_WEEK,
|
||||
.trigger.hours = 13,
|
||||
.trigger.minutes = 30,
|
||||
.trigger.day.repeat_days = ESP_SCHEDULE_DAY_MONDAY | ESP_SCHEDULE_DAY_THURSDAY,
|
||||
.trigger_cb = app_schedule_trigger_cb,
|
||||
.priv_data = priv_data_global,
|
||||
};
|
||||
esp_schedule_create(&schedule_config);
|
||||
}
|
||||
|
||||
void app_schedule_init()
|
||||
{
|
||||
uint8_t schedule_count;
|
||||
esp_schedule_handle_t *schedule_list = esp_schedule_init(true, NULL, &schedule_count);
|
||||
if (schedule_count > 0 && schedule_list != NULL) {
|
||||
ESP_LOGI(TAG, "Found %d schedule(s) in NVS.", schedule_count);
|
||||
for (size_t i = 0; i < schedule_count; i++) {
|
||||
esp_schedule_config_t schedule_config;
|
||||
esp_schedule_get(schedule_list[i], &schedule_config);
|
||||
schedule_config.trigger_cb = app_schedule_trigger_cb;
|
||||
schedule_config.priv_data = priv_data_global;
|
||||
esp_schedule_edit(schedule_list[i], &schedule_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,5 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
|
||||
CFLAGS += -Wno-unused-function
|
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
espressif/rmaker_common:
|
||||
version: ~1.4.2
|
||||
description: ESP Schedules, used in RainMaker
|
||||
issues: https://github.com/espressif/esp-rainmaker/issues
|
||||
repository: git://github.com/espressif/esp-rainmaker.git
|
||||
repository_info:
|
||||
commit_sha: 2f7f487bc0fa835fb9ac9ce108c76a667e9d2f42
|
||||
path: components/esp_schedule
|
||||
url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_schedule
|
||||
version: 1.2.0
|
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/** Schedule Handle */
|
||||
typedef void *esp_schedule_handle_t;
|
||||
|
||||
/** Maximum length of the schedule name allowed. This value cannot be more than 16 as it is used for NVS key. */
|
||||
#define MAX_SCHEDULE_NAME_LEN 16
|
||||
|
||||
/** Callback for schedule trigger
|
||||
*
|
||||
* This callback is called when the schedule is triggered.
|
||||
*
|
||||
* @param[in] handle Schedule handle.
|
||||
* @param[in] priv_data Pointer to the private data passed while creating/editing the schedule.
|
||||
*/
|
||||
typedef void (*esp_schedule_trigger_cb_t)(esp_schedule_handle_t handle, void *priv_data);
|
||||
|
||||
/** Callback for schedule timestamp
|
||||
*
|
||||
* This callback is called when the next trigger timestamp of the schedule is changed. This might be useful to check if
|
||||
* one time schedules have already passed while the device was powered off.
|
||||
*
|
||||
* @param[in] handle Schedule handle.
|
||||
* @param[in] next_timestamp timestamp at which the schedule will trigger next.
|
||||
* @param[in] priv_data Pointer to the user data passed while creating/editing the schedule.
|
||||
*/
|
||||
typedef void (*esp_schedule_timestamp_cb_t)(esp_schedule_handle_t handle, uint32_t next_timestamp, void *priv_data);
|
||||
|
||||
/** Schedule type */
|
||||
typedef enum esp_schedule_type {
|
||||
ESP_SCHEDULE_TYPE_INVALID = 0,
|
||||
ESP_SCHEDULE_TYPE_DAYS_OF_WEEK,
|
||||
ESP_SCHEDULE_TYPE_DATE,
|
||||
ESP_SCHEDULE_TYPE_RELATIVE,
|
||||
} esp_schedule_type_t;
|
||||
|
||||
/** Schedule days. Used for ESP_SCHEDULE_TYPE_DAYS_OF_WEEK. */
|
||||
typedef enum esp_schedule_days {
|
||||
ESP_SCHEDULE_DAY_ONCE = 0,
|
||||
ESP_SCHEDULE_DAY_EVERYDAY = 0b1111111,
|
||||
ESP_SCHEDULE_DAY_MONDAY = 1 << 0,
|
||||
ESP_SCHEDULE_DAY_TUESDAY = 1 << 1,
|
||||
ESP_SCHEDULE_DAY_WEDNESDAY = 1 << 2,
|
||||
ESP_SCHEDULE_DAY_THURSDAY = 1 << 3,
|
||||
ESP_SCHEDULE_DAY_FRIDAY = 1 << 4,
|
||||
ESP_SCHEDULE_DAY_SATURDAY = 1 << 5,
|
||||
ESP_SCHEDULE_DAY_SUNDAY = 1 << 6,
|
||||
} esp_schedule_days_t;
|
||||
|
||||
/** Schedule months. Used for ESP_SCHEDULE_TYPE_DATE. */
|
||||
typedef enum esp_schedule_months {
|
||||
ESP_SCHEDULE_MONTH_ONCE = 0,
|
||||
ESP_SCHEDULE_MONTH_ALL = 0b1111111,
|
||||
ESP_SCHEDULE_MONTH_JANUARY = 1 << 0,
|
||||
ESP_SCHEDULE_MONTH_FEBRUARY = 1 << 1,
|
||||
ESP_SCHEDULE_MONTH_MARCH = 1 << 2,
|
||||
ESP_SCHEDULE_MONTH_APRIL = 1 << 3,
|
||||
ESP_SCHEDULE_MONTH_MAY = 1 << 4,
|
||||
ESP_SCHEDULE_MONTH_JUNE = 1 << 5,
|
||||
ESP_SCHEDULE_MONTH_JULY = 1 << 6,
|
||||
ESP_SCHEDULE_MONTH_AUGUST = 1 << 7,
|
||||
ESP_SCHEDULE_MONTH_SEPTEMBER = 1 << 8,
|
||||
ESP_SCHEDULE_MONTH_OCTOBER = 1 << 9,
|
||||
ESP_SCHEDULE_MONTH_NOVEMBER = 1 << 10,
|
||||
ESP_SCHEDULE_MONTH_DECEMBER = 1 << 11,
|
||||
} esp_schedule_months_t;
|
||||
|
||||
/** Trigger details of the schedule */
|
||||
typedef struct esp_schedule_trigger {
|
||||
/** Type of schedule */
|
||||
esp_schedule_type_t type;
|
||||
/** Hours in 24 hour format. Accepted values: 0-23 */
|
||||
uint8_t hours;
|
||||
/** Minutes in the given hour. Accepted values: 0-59. */
|
||||
uint8_t minutes;
|
||||
/** For type ESP_SCHEDULE_TYPE_DAYS_OF_WEEK */
|
||||
struct {
|
||||
/** 'OR' list of esp_schedule_days_t */
|
||||
uint8_t repeat_days;
|
||||
} day;
|
||||
/** For type ESP_SCHEDULE_TYPE_DATE */
|
||||
struct {
|
||||
/** Day of the month. Accepted values: 1-31. */
|
||||
uint8_t day;
|
||||
/* 'OR' list of esp_schedule_months_t */
|
||||
uint16_t repeat_months;
|
||||
/** Year */
|
||||
uint16_t year;
|
||||
/** If the schedule is to be repeated every year. */
|
||||
bool repeat_every_year;
|
||||
} date;
|
||||
/** For type ESP_SCHEDULE_TYPE_SECONDS */
|
||||
int relative_seconds;
|
||||
/** Used for passing the next schedule timestamp for
|
||||
* ESP_SCHEDULE_TYPE_RELATIVE */
|
||||
time_t next_scheduled_time_utc;
|
||||
} esp_schedule_trigger_t;
|
||||
|
||||
/** Schedule Validity
|
||||
* Start and end time within which the schedule will be applicable.
|
||||
*/
|
||||
typedef struct esp_schedule_validity {
|
||||
/* Start time as UTC timestamp */
|
||||
time_t start_time;
|
||||
/* End time as UTC timestamp */
|
||||
time_t end_time;
|
||||
} esp_schedule_validity_t;
|
||||
|
||||
/** Schedule config */
|
||||
typedef struct esp_schedule_config {
|
||||
/** Name of the schedule. This is like a primary key for the schedule. This is required. +1 for NULL termination. */
|
||||
char name[MAX_SCHEDULE_NAME_LEN + 1];
|
||||
/** Trigger details */
|
||||
esp_schedule_trigger_t trigger;
|
||||
/** Trigger callback */
|
||||
esp_schedule_trigger_cb_t trigger_cb;
|
||||
/** Timestamp callback */
|
||||
esp_schedule_timestamp_cb_t timestamp_cb;
|
||||
/** Private data associated with the schedule. This will be passed to callbacks. */
|
||||
void *priv_data;
|
||||
/** Validity of schedules. */
|
||||
esp_schedule_validity_t validity;
|
||||
} esp_schedule_config_t;
|
||||
|
||||
/** Initialize ESP Schedule
|
||||
*
|
||||
* This initializes ESP Schedule. This must be called first before calling any of the other APIs.
|
||||
* This API also gets all the schedules from NVS (if it has been enabled).
|
||||
*
|
||||
* Note: After calling this API, the pointers to the callbacks should be updated for all the schedules by calling
|
||||
* esp_schedule_get() followed by esp_schedule_edit() with the correct callbacks.
|
||||
*
|
||||
* @param[in] enable_nvs If NVS is to be enabled or not.
|
||||
* @param[in] nvs_partition (Optional) The NVS partition to be used. If NULL is passed, the default partition is used.
|
||||
* @param[out] schedule_count Number of active schedules found in NVS.
|
||||
*
|
||||
* @return Array of schedule handles if any schedules have been found.
|
||||
* @return NULL if no schedule is found in NVS (or if NVS is not enabled).
|
||||
*/
|
||||
esp_schedule_handle_t *esp_schedule_init(bool enable_nvs, char *nvs_partition, uint8_t *schedule_count);
|
||||
|
||||
/** Create Schedule
|
||||
*
|
||||
* This API can be used to create a new schedule. The schedule still needs to be enabled using
|
||||
* esp_schedule_enable().
|
||||
*
|
||||
* @param[in] schedule_config Configuration of the schedule to be created.
|
||||
*
|
||||
* @return Schedule handle if successfully created.
|
||||
* @return NULL in case of error.
|
||||
*/
|
||||
esp_schedule_handle_t esp_schedule_create(esp_schedule_config_t *schedule_config);
|
||||
|
||||
/** Remove Schedule
|
||||
*
|
||||
* This API can be used to remove an existing schedule.
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be removed.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_delete(esp_schedule_handle_t handle);
|
||||
|
||||
/** Edit Schedule
|
||||
*
|
||||
* This API can be used to edit an existing schedule.
|
||||
* The schedule name should be same as when the schedule was created. The complete config must be provided
|
||||
* or the previously stored config might be over-written.
|
||||
*
|
||||
* Note: If a schedule is edited when it is on-going, the new changes will not be reflected.
|
||||
* You will need to disable the schedule, edit it, and then enable it again.
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be edited.
|
||||
* @param[in] schedule_config Configuration of the schedule to be edited.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_edit(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config);
|
||||
|
||||
/** Enable Schedule
|
||||
*
|
||||
* This API can be used to enable an existing schedule.
|
||||
* It can be used to enable a schedule after it has been created using esp_schedule_create()
|
||||
* or if the schedule has been disabled using esp_schedule_disable().
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be enabled.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_enable(esp_schedule_handle_t handle);
|
||||
|
||||
/** Disable Schedule
|
||||
*
|
||||
* This API can be used to disable an on-going schedule.
|
||||
* It does not remove the schedule, just stops it. The schedule can be enabled again using
|
||||
* esp_schedule_enable().
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be disabled.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_disable(esp_schedule_handle_t handle);
|
||||
|
||||
/** Get Schedule
|
||||
*
|
||||
* This API can be used to get details of an existing schedule.
|
||||
* The schedule_config is populated with the schedule details.
|
||||
*
|
||||
* @param[in] handle Schedule handle.
|
||||
* @param[out] schedule_config Details of the schedule whose handle is passed.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_get(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,600 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_sntp.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include "esp_schedule_internal.h"
|
||||
|
||||
static const char *TAG = "esp_schedule";
|
||||
|
||||
#define SECONDS_TILL_2020 ((2020 - 1970) * 365 * 24 * 3600)
|
||||
#define SECONDS_IN_DAY (60 * 60 * 24)
|
||||
|
||||
static bool init_done = false;
|
||||
|
||||
static int esp_schedule_get_no_of_days(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||
{
|
||||
/* for day, monday = 0, sunday = 6. */
|
||||
int next_day = 0;
|
||||
/* struct tm has tm_wday with sunday as 0. Whereas we have monday as 0. Converting struct tm to our format */
|
||||
int today = ((current_time->tm_wday + 7 - 1) % 7);
|
||||
|
||||
esp_schedule_days_t today_bit = 1 << today;
|
||||
uint8_t repeat_days = trigger->day.repeat_days;
|
||||
int current_seconds = (current_time->tm_hour * 60 + current_time->tm_min) * 60 + current_time->tm_sec;
|
||||
int schedule_seconds = (schedule_time->tm_hour * 60 + schedule_time->tm_min) * 60;
|
||||
|
||||
/* Handling for one time schedule */
|
||||
if (repeat_days == ESP_SCHEDULE_DAY_ONCE) {
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off */
|
||||
return 0;
|
||||
} else {
|
||||
/* The schedule is tomorrow */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handling for repeating schedules */
|
||||
/* Check if it is today */
|
||||
if ((repeat_days & today_bit)) {
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Check if it is this week or next week */
|
||||
if ((repeat_days & (today_bit ^ 0xFF)) > today_bit) {
|
||||
/* Next schedule is yet to come in this week */
|
||||
next_day = ffs(repeat_days & (0xFF << (today + 1))) - 1;
|
||||
return (next_day - today);
|
||||
} else {
|
||||
/* First scheduled day of the next week */
|
||||
next_day = ffs(repeat_days) - 1;
|
||||
if (next_day == today) {
|
||||
/* Same day, next week */
|
||||
return 7;
|
||||
}
|
||||
return (7 - today + next_day);
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "No of days could not be found. This should not happen.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t esp_schedule_get_next_month(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||
{
|
||||
int current_seconds = (current_time->tm_hour * 60 + current_time->tm_min) * 60 + current_time->tm_sec;
|
||||
int schedule_seconds = (schedule_time->tm_hour * 60 + schedule_time->tm_min) * 60;
|
||||
/* +1 is because struct tm has months starting from 0, whereas we have them starting from 1 */
|
||||
uint8_t current_month = current_time->tm_mon + 1;
|
||||
/* -1 because month_bit starts from 0b1. So for January, it should be 1 << 0. And current_month starts from 1. */
|
||||
uint16_t current_month_bit = 1 << (current_month - 1);
|
||||
uint8_t next_schedule_month = 0;
|
||||
uint16_t repeat_months = trigger->date.repeat_months;
|
||||
|
||||
/* Check if month is not specified */
|
||||
if (repeat_months == ESP_SCHEDULE_MONTH_ONCE) {
|
||||
if (trigger->date.day == current_time->tm_mday) {
|
||||
/* The schedule day is same. Check if time has already passed */
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off */
|
||||
return current_month;
|
||||
} else {
|
||||
/* Today's time has passed */
|
||||
return (current_month + 1);
|
||||
}
|
||||
} else if (trigger->date.day > current_time->tm_mday) {
|
||||
/* The day is yet to come in this month */
|
||||
return current_month;
|
||||
} else {
|
||||
/* The day has passed in the current month */
|
||||
return (current_month + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if schedule is not this year itself, it is in future. */
|
||||
if (trigger->date.year > (current_time->tm_year + 1900)) {
|
||||
/* Find first schedule month of next year */
|
||||
next_schedule_month = ffs(repeat_months);
|
||||
/* Year will be handled by the caller. So no need to add any additional months */
|
||||
return next_schedule_month;
|
||||
}
|
||||
|
||||
/* Check if schedule is this month and is yet to come */
|
||||
if (current_month_bit & repeat_months) {
|
||||
if (trigger->date.day == current_time->tm_mday) {
|
||||
/* The schedule day is same. Check if time has already passed */
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off */
|
||||
return current_month;
|
||||
}
|
||||
}
|
||||
if (trigger->date.day > current_time->tm_mday) {
|
||||
/* The day is yet to come in this month */
|
||||
return current_month;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if schedule is this year */
|
||||
if ((repeat_months & (current_month_bit ^ 0xFFFF)) > current_month_bit) {
|
||||
/* Next schedule month is yet to come in this year */
|
||||
next_schedule_month = ffs(repeat_months & (0xFFFF << (current_month)));
|
||||
return next_schedule_month;
|
||||
}
|
||||
|
||||
/* Check if schedule is for this year and does not repeat */
|
||||
if (!trigger->date.repeat_every_year) {
|
||||
if (trigger->date.year <= (current_time->tm_year + 1900)) {
|
||||
ESP_LOGE(TAG, "Schedule does not repeat next year, but get_next_month has been called.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule is not this year */
|
||||
/* Find first schedule month of next year */
|
||||
next_schedule_month = ffs(repeat_months);
|
||||
/* +12 because the schedule is next year */
|
||||
return (next_schedule_month + 12);
|
||||
}
|
||||
|
||||
static uint16_t esp_schedule_get_next_year(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||
{
|
||||
uint16_t current_year = current_time->tm_year + 1900;
|
||||
uint16_t schedule_year = trigger->date.year;
|
||||
if (schedule_year > current_year) {
|
||||
return schedule_year;
|
||||
}
|
||||
/* If the schedule is set to repeat_every_year, we return the current year */
|
||||
/* If the schedule has already passed in this year, we still return current year, as the additional months will be handled in get_next_month */
|
||||
return current_year;
|
||||
}
|
||||
|
||||
static uint32_t esp_schedule_get_next_schedule_time_diff(const char *schedule_name, esp_schedule_trigger_t *trigger)
|
||||
{
|
||||
struct tm current_time, schedule_time;
|
||||
time_t now;
|
||||
char time_str[64];
|
||||
int32_t time_diff;
|
||||
|
||||
/* Get current time */
|
||||
time(&now);
|
||||
/* Handling ESP_SCHEDULE_TYPE_RELATIVE first since it doesn't require any
|
||||
* computation based on days, hours, minutes, etc.
|
||||
*/
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
/* If next scheduled time is already set, just compute the difference
|
||||
* between current time and next scheduled time and return that diff.
|
||||
*/
|
||||
time_t target;
|
||||
if (trigger->next_scheduled_time_utc > 0) {
|
||||
target = (time_t)trigger->next_scheduled_time_utc;
|
||||
time_diff = difftime(target, now);
|
||||
} else {
|
||||
target = now + (time_t)trigger->relative_seconds;
|
||||
time_diff = trigger->relative_seconds;
|
||||
}
|
||||
localtime_r(&target, &schedule_time);
|
||||
trigger->next_scheduled_time_utc = mktime(&schedule_time);
|
||||
/* Print schedule time */
|
||||
memset(time_str, 0, sizeof(time_str));
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &schedule_time);
|
||||
ESP_LOGI(TAG, "Schedule %s will be active on: %s. DST: %s", schedule_name, time_str, schedule_time.tm_isdst ? "Yes" : "No");
|
||||
return time_diff;
|
||||
}
|
||||
localtime_r(&now, ¤t_time);
|
||||
|
||||
/* Get schedule time */
|
||||
localtime_r(&now, &schedule_time);
|
||||
schedule_time.tm_sec = 0;
|
||||
schedule_time.tm_min = trigger->minutes;
|
||||
schedule_time.tm_hour = trigger->hours;
|
||||
mktime(&schedule_time);
|
||||
|
||||
/* Adjust schedule day */
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
int no_of_days = 0;
|
||||
no_of_days = esp_schedule_get_no_of_days(trigger, ¤t_time, &schedule_time);
|
||||
schedule_time.tm_sec += no_of_days * SECONDS_IN_DAY;
|
||||
}
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
schedule_time.tm_mday = trigger->date.day;
|
||||
schedule_time.tm_mon = esp_schedule_get_next_month(trigger, ¤t_time, &schedule_time) - 1;
|
||||
schedule_time.tm_year = esp_schedule_get_next_year(trigger, ¤t_time, &schedule_time) - 1900;
|
||||
if (schedule_time.tm_mon < 0) {
|
||||
ESP_LOGE(TAG, "Invalid month found: %d. Setting it to next month.", schedule_time.tm_mon);
|
||||
schedule_time.tm_mon = current_time.tm_mon + 1;
|
||||
}
|
||||
if (schedule_time.tm_mon >= 12) {
|
||||
schedule_time.tm_year += schedule_time.tm_mon / 12;
|
||||
schedule_time.tm_mon = schedule_time.tm_mon % 12;
|
||||
}
|
||||
}
|
||||
mktime(&schedule_time);
|
||||
|
||||
/* Adjust time according to DST */
|
||||
time_t dst_adjust = 0;
|
||||
if (!current_time.tm_isdst && schedule_time.tm_isdst) {
|
||||
dst_adjust = -3600;
|
||||
} else if (current_time.tm_isdst && !schedule_time.tm_isdst ) {
|
||||
dst_adjust = 3600;
|
||||
}
|
||||
ESP_LOGD(TAG, "DST adjust seconds: %lld", (long long) dst_adjust);
|
||||
schedule_time.tm_sec += dst_adjust;
|
||||
mktime(&schedule_time);
|
||||
|
||||
/* Print schedule time */
|
||||
memset(time_str, 0, sizeof(time_str));
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &schedule_time);
|
||||
ESP_LOGI(TAG, "Schedule %s will be active on: %s. DST: %s", schedule_name, time_str, schedule_time.tm_isdst ? "Yes" : "No");
|
||||
|
||||
/* Calculate difference */
|
||||
time_diff = difftime((mktime(&schedule_time)), mktime(¤t_time));
|
||||
|
||||
/* For one time schedules to check for expiry after a reboot. If NVS is enabled, this should be stored in NVS. */
|
||||
trigger->next_scheduled_time_utc = mktime(&schedule_time);
|
||||
|
||||
return time_diff;
|
||||
}
|
||||
|
||||
static bool esp_schedule_is_expired(esp_schedule_trigger_t *trigger)
|
||||
{
|
||||
time_t current_timestamp = 0;
|
||||
struct tm current_time = {0};
|
||||
time(¤t_timestamp);
|
||||
localtime_r(¤t_timestamp, ¤t_time);
|
||||
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||
/* Relative seconds based schedule has expired */
|
||||
return true;
|
||||
} else if (trigger->next_scheduled_time_utc == 0) {
|
||||
/* Schedule has been disabled , so it is as good as expired. */
|
||||
return true;
|
||||
}
|
||||
} else if (trigger->type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
if (trigger->day.repeat_days == ESP_SCHEDULE_DAY_ONCE) {
|
||||
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||
/* One time schedule has expired */
|
||||
return true;
|
||||
} else if (trigger->next_scheduled_time_utc == 0) {
|
||||
/* Schedule has been disabled , so it is as good as expired. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (trigger->type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
if (trigger->date.repeat_months == 0) {
|
||||
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||
/* One time schedule has expired */
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (trigger->date.repeat_every_year == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct tm schedule_time = {0};
|
||||
localtime_r(¤t_timestamp, &schedule_time);
|
||||
schedule_time.tm_sec = 0;
|
||||
schedule_time.tm_min = trigger->minutes;
|
||||
schedule_time.tm_hour = trigger->hours;
|
||||
schedule_time.tm_mday = trigger->date.day;
|
||||
/* For expiry, just check the last month of the repeat_months. */
|
||||
/* '-1' because struct tm has months starting from 0 and we have months starting from 1. */
|
||||
schedule_time.tm_mon = fls(trigger->date.repeat_months) - 1;
|
||||
/* '-1900' because struct tm has number of years after 1900 */
|
||||
schedule_time.tm_year = trigger->date.year - 1900;
|
||||
time_t schedule_timestamp = mktime(&schedule_time);
|
||||
|
||||
if (schedule_timestamp < current_timestamp) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* Invalid type. Mark as expired */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void esp_schedule_stop_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
xTimerStop(schedule->timer, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void esp_schedule_start_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
time_t current_time = 0;
|
||||
time(¤t_time);
|
||||
if (current_time < SECONDS_TILL_2020) {
|
||||
ESP_LOGE(TAG, "Time is not updated");
|
||||
return;
|
||||
}
|
||||
|
||||
schedule->next_scheduled_time_diff = esp_schedule_get_next_schedule_time_diff(schedule->name, &schedule->trigger);
|
||||
ESP_LOGI(TAG, "Starting a timer for %"PRIu32" seconds for schedule %s", schedule->next_scheduled_time_diff, schedule->name);
|
||||
|
||||
if (schedule->timestamp_cb) {
|
||||
schedule->timestamp_cb((esp_schedule_handle_t)schedule, schedule->trigger.next_scheduled_time_utc, schedule->priv_data);
|
||||
}
|
||||
|
||||
xTimerStop(schedule->timer, portMAX_DELAY);
|
||||
xTimerChangePeriod(schedule->timer, (schedule->next_scheduled_time_diff * 1000) / portTICK_PERIOD_MS, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void esp_schedule_common_timer_cb(TimerHandle_t timer)
|
||||
{
|
||||
void *priv_data = pvTimerGetTimerID(timer);
|
||||
if (priv_data == NULL) {
|
||||
return;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)priv_data;
|
||||
time_t now;
|
||||
time(&now);
|
||||
struct tm validity_time;
|
||||
char time_str[64] = {0};
|
||||
if (schedule->validity.start_time != 0) {
|
||||
if (now < schedule->validity.start_time) {
|
||||
memset(time_str, 0, sizeof(time_str));
|
||||
localtime_r(&schedule->validity.start_time, &validity_time);
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &validity_time);
|
||||
ESP_LOGW(TAG, "Schedule %s skipped. It will be active only after: %s. DST: %s.", schedule->name, time_str, validity_time.tm_isdst ? "Yes" : "No");
|
||||
/* TODO: Start the timer such that the next time it triggeres, it will be within the valid window.
|
||||
* Currently, it will just keep triggering and then get skipped if not in valid range.
|
||||
*/
|
||||
goto restart_schedule;
|
||||
}
|
||||
}
|
||||
if (schedule->validity.end_time != 0) {
|
||||
if (now > schedule->validity.end_time) {
|
||||
localtime_r(&schedule->validity.end_time, &validity_time);
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &validity_time);
|
||||
ESP_LOGW(TAG, "Schedule %s skipped. It can't be active after: %s. DST: %s.", schedule->name, time_str, validity_time.tm_isdst ? "Yes" : "No");
|
||||
/* Return from here will ensure that the timer does not start again for this schedule */
|
||||
return;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Schedule %s triggered", schedule->name);
|
||||
if (schedule->trigger_cb) {
|
||||
schedule->trigger_cb((esp_schedule_handle_t)schedule, schedule->priv_data);
|
||||
}
|
||||
|
||||
restart_schedule:
|
||||
|
||||
if (esp_schedule_is_expired(&schedule->trigger)) {
|
||||
/* Not deleting the schedule here. Just not starting it again. */
|
||||
return;
|
||||
}
|
||||
esp_schedule_start_timer(schedule);
|
||||
}
|
||||
|
||||
static void esp_schedule_delete_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
xTimerDelete(schedule->timer, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void esp_schedule_create_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
if (esp_schedule_nvs_is_enabled()) {
|
||||
/* This is just used for calculating next_scheduled_time_utc for ESP_SCHEDULE_DAY_ONCE (in case of ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) or for ESP_SCHEDULE_MONTH_ONCE (in case of ESP_SCHEDULE_TYPE_DATE), and only used when NVS is enabled. And if NVS is enabled, time will already be synced and the time will be correctly calculated. */
|
||||
schedule->next_scheduled_time_diff = esp_schedule_get_next_schedule_time_diff(schedule->name, &schedule->trigger);
|
||||
}
|
||||
|
||||
/* Temporarily setting the timer for 1 (anything greater than 0) tick. This will get changed when xTimerChangePeriod() is called. */
|
||||
schedule->timer = xTimerCreate("schedule", 1, pdFALSE, (void *)schedule, esp_schedule_common_timer_cb);
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_get(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
if (schedule_config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
|
||||
strcpy(schedule_config->name, schedule->name);
|
||||
schedule_config->trigger.type = schedule->trigger.type;
|
||||
schedule_config->trigger.hours = schedule->trigger.hours;
|
||||
schedule_config->trigger.minutes = schedule->trigger.minutes;
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
schedule_config->trigger.day.repeat_days = schedule->trigger.day.repeat_days;
|
||||
} else if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
schedule_config->trigger.date.day = schedule->trigger.date.day;
|
||||
schedule_config->trigger.date.repeat_months = schedule->trigger.date.repeat_months;
|
||||
schedule_config->trigger.date.year = schedule->trigger.date.year;
|
||||
schedule_config->trigger.date.repeat_every_year = schedule->trigger.date.repeat_every_year;
|
||||
}
|
||||
|
||||
schedule_config->trigger_cb = schedule->trigger_cb;
|
||||
schedule_config->timestamp_cb = schedule->timestamp_cb;
|
||||
schedule_config->priv_data = schedule->priv_data;
|
||||
schedule_config->validity = schedule->validity;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_enable(esp_schedule_handle_t handle)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
esp_schedule_start_timer(schedule);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_disable(esp_schedule_handle_t handle)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
esp_schedule_stop_timer(schedule);
|
||||
/* Disabling a schedule should also reset the next_scheduled_time.
|
||||
* It would be re-computed after enabling.
|
||||
*/
|
||||
schedule->trigger.next_scheduled_time_utc = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_schedule_set(esp_schedule_t *schedule, esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
/* Setting everything apart from name. */
|
||||
schedule->trigger.type = schedule_config->trigger.type;
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
schedule->trigger.relative_seconds = schedule_config->trigger.relative_seconds;
|
||||
schedule->trigger.next_scheduled_time_utc = schedule_config->trigger.next_scheduled_time_utc;
|
||||
} else {
|
||||
schedule->trigger.hours = schedule_config->trigger.hours;
|
||||
schedule->trigger.minutes = schedule_config->trigger.minutes;
|
||||
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
schedule->trigger.day.repeat_days = schedule_config->trigger.day.repeat_days;
|
||||
} else if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
schedule->trigger.date.day = schedule_config->trigger.date.day;
|
||||
schedule->trigger.date.repeat_months = schedule_config->trigger.date.repeat_months;
|
||||
schedule->trigger.date.year = schedule_config->trigger.date.year;
|
||||
schedule->trigger.date.repeat_every_year = schedule_config->trigger.date.repeat_every_year;
|
||||
}
|
||||
}
|
||||
|
||||
schedule->trigger_cb = schedule_config->trigger_cb;
|
||||
schedule->timestamp_cb = schedule_config->timestamp_cb;
|
||||
schedule->priv_data = schedule_config->priv_data;
|
||||
schedule->validity = schedule_config->validity;
|
||||
esp_schedule_nvs_add(schedule);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_edit(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
if (handle == NULL || schedule_config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
if (strncmp(schedule->name, schedule_config->name, sizeof(schedule->name)) != 0) {
|
||||
ESP_LOGE(TAG, "Schedule name mismatch. Expected: %s, Passed: %s", schedule->name, schedule_config->name);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Editing a schedule with relative time should also reset it. */
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
schedule->trigger.next_scheduled_time_utc = 0;
|
||||
}
|
||||
esp_schedule_set(schedule, schedule_config);
|
||||
ESP_LOGD(TAG, "Schedule %s edited", schedule->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_delete(esp_schedule_handle_t handle)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
ESP_LOGI(TAG, "Deleting schedule %s", schedule->name);
|
||||
if (schedule->timer) {
|
||||
esp_schedule_stop_timer(schedule);
|
||||
esp_schedule_delete_timer(schedule);
|
||||
}
|
||||
esp_schedule_nvs_remove(schedule);
|
||||
free(schedule);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_schedule_handle_t esp_schedule_create(esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
if (schedule_config == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(schedule_config->name) <= 0) {
|
||||
ESP_LOGE(TAG, "Set schedule failed. Please enter a unique valid name for the schedule.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (schedule_config->trigger.type == ESP_SCHEDULE_TYPE_INVALID) {
|
||||
ESP_LOGE(TAG, "Schedule type is invalid.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_schedule_t));
|
||||
if (schedule == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate handle");
|
||||
return NULL;
|
||||
}
|
||||
strlcpy(schedule->name, schedule_config->name, sizeof(schedule->name));
|
||||
|
||||
esp_schedule_set(schedule, schedule_config);
|
||||
|
||||
esp_schedule_create_timer(schedule);
|
||||
ESP_LOGD(TAG, "Schedule %s created", schedule->name);
|
||||
return (esp_schedule_handle_t)schedule;
|
||||
}
|
||||
|
||||
esp_schedule_handle_t *esp_schedule_init(bool enable_nvs, char *nvs_partition, uint8_t *schedule_count)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
if (!esp_sntp_enabled()) {
|
||||
ESP_LOGI(TAG, "Initializing SNTP");
|
||||
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
esp_sntp_setservername(0, "pool.ntp.org");
|
||||
esp_sntp_init();
|
||||
}
|
||||
#else
|
||||
if (!sntp_enabled()) {
|
||||
ESP_LOGI(TAG, "Initializing SNTP");
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, "pool.ntp.org");
|
||||
sntp_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!enable_nvs) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Wait for time to be updated here */
|
||||
|
||||
|
||||
/* Below this is initialising schedules from NVS */
|
||||
esp_schedule_nvs_init(nvs_partition);
|
||||
|
||||
/* Get handle list from NVS */
|
||||
esp_schedule_handle_t *handle_list = NULL;
|
||||
*schedule_count = 0;
|
||||
handle_list = esp_schedule_nvs_get_all(schedule_count);
|
||||
if (handle_list == NULL) {
|
||||
ESP_LOGI(TAG, "No schedules found in NVS");
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Schedules found in NVS: %"PRIu8, *schedule_count);
|
||||
/* Start/Delete the schedules */
|
||||
esp_schedule_t *schedule = NULL;
|
||||
for (size_t handle_count = 0; handle_count < *schedule_count; handle_count++) {
|
||||
schedule = (esp_schedule_t *)handle_list[handle_count];
|
||||
schedule->trigger_cb = NULL;
|
||||
schedule->timer = NULL;
|
||||
/* Check for ONCE and expired schedules and delete them. */
|
||||
if (esp_schedule_is_expired(&schedule->trigger)) {
|
||||
/* This schedule has already expired. */
|
||||
ESP_LOGI(TAG, "Schedule %s does not repeat and has already expired. Deleting it.", schedule->name);
|
||||
esp_schedule_delete((esp_schedule_handle_t)schedule);
|
||||
/* Removing the schedule from the list */
|
||||
handle_list[handle_count] = handle_list[*schedule_count - 1];
|
||||
(*schedule_count)--;
|
||||
handle_count--;
|
||||
continue;
|
||||
}
|
||||
esp_schedule_create_timer(schedule);
|
||||
esp_schedule_start_timer(schedule);
|
||||
}
|
||||
init_done = true;
|
||||
return handle_list;
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_schedule.h>
|
||||
|
||||
typedef struct esp_schedule {
|
||||
char name[MAX_SCHEDULE_NAME_LEN + 1];
|
||||
esp_schedule_trigger_t trigger;
|
||||
uint32_t next_scheduled_time_diff;
|
||||
TimerHandle_t timer;
|
||||
esp_schedule_trigger_cb_t trigger_cb;
|
||||
esp_schedule_timestamp_cb_t timestamp_cb;
|
||||
void *priv_data;
|
||||
esp_schedule_validity_t validity;
|
||||
} esp_schedule_t;
|
||||
|
||||
esp_err_t esp_schedule_nvs_add(esp_schedule_t *schedule);
|
||||
esp_err_t esp_schedule_nvs_remove(esp_schedule_t *schedule);
|
||||
esp_schedule_handle_t *esp_schedule_nvs_get_all(uint8_t *schedule_count);
|
||||
bool esp_schedule_nvs_is_enabled(void);
|
||||
esp_err_t esp_schedule_nvs_init(char *nvs_partition);
|
@@ -0,0 +1,296 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs.h>
|
||||
#include "esp_schedule_internal.h"
|
||||
|
||||
static const char *TAG = "esp_schedule_nvs";
|
||||
|
||||
#define ESP_SCHEDULE_NVS_NAMESPACE "schd"
|
||||
#define ESP_SCHEDULE_COUNT_KEY "schd_count"
|
||||
|
||||
static char *esp_schedule_nvs_partition = NULL;
|
||||
static bool nvs_enabled = false;
|
||||
|
||||
esp_err_t esp_schedule_nvs_add(esp_schedule_t *schedule)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not adding to NVS.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check if this is new schedule or editing an existing schedule */
|
||||
size_t buf_size;
|
||||
bool editing_schedule = true;
|
||||
err = nvs_get_blob(nvs_handle, schedule->name, NULL, &buf_size);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
editing_schedule = false;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Updating the existing schedule %s", schedule->name);
|
||||
}
|
||||
|
||||
err = nvs_set_blob(nvs_handle, schedule->name, schedule, sizeof(esp_schedule_t));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS set failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
if (editing_schedule == false) {
|
||||
uint8_t schedule_count;
|
||||
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
schedule_count = 0;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
schedule_count++;
|
||||
err = nvs_set_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS set failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedule %s added in NVS", schedule->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_nvs_remove_all(void)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not removing from NVS.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return err;
|
||||
}
|
||||
err = nvs_erase_all(nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS erase all keys failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "All schedules removed from NVS");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_nvs_remove(esp_schedule_t *schedule)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not removing from NVS.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return err;
|
||||
}
|
||||
err = nvs_erase_key(nvs_handle, schedule->name);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS erase key failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
uint8_t schedule_count;
|
||||
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
schedule_count--;
|
||||
err = nvs_set_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS set failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedule %s removed from NVS", schedule->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint8_t esp_schedule_nvs_get_count(void)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not getting count from NVS.");
|
||||
return 0;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return 0;
|
||||
}
|
||||
uint8_t schedule_count;
|
||||
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return 0;
|
||||
}
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedules in NVS: %d", schedule_count);
|
||||
return schedule_count;
|
||||
}
|
||||
|
||||
static esp_schedule_handle_t esp_schedule_nvs_get(char *nvs_key)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not getting from NVS.");
|
||||
return NULL;
|
||||
}
|
||||
size_t buf_size;
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return NULL;
|
||||
}
|
||||
err = nvs_get_blob(nvs_handle, nvs_key, NULL, &buf_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return NULL;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)malloc(buf_size);
|
||||
if (schedule == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate handle");
|
||||
nvs_close(nvs_handle);
|
||||
return NULL;
|
||||
}
|
||||
err = nvs_get_blob(nvs_handle, nvs_key, schedule, &buf_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
free(schedule);
|
||||
return NULL;
|
||||
}
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedule %s found in NVS", schedule->name);
|
||||
return (esp_schedule_handle_t) schedule;
|
||||
}
|
||||
|
||||
esp_schedule_handle_t *esp_schedule_nvs_get_all(uint8_t *schedule_count)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not Initialising NVS.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*schedule_count = esp_schedule_nvs_get_count();
|
||||
if (*schedule_count == 0) {
|
||||
ESP_LOGI(TAG, "No Entries found in NVS");
|
||||
return NULL;
|
||||
}
|
||||
esp_schedule_handle_t *handle_list = (esp_schedule_handle_t *)malloc(sizeof(esp_schedule_handle_t) * (*schedule_count));
|
||||
if (handle_list == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate schedule list");
|
||||
*schedule_count = 0;
|
||||
return NULL;
|
||||
}
|
||||
int handle_count = 0;
|
||||
|
||||
nvs_entry_info_t nvs_entry;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
nvs_iterator_t nvs_iterator = NULL;
|
||||
esp_err_t err = nvs_entry_find(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_TYPE_BLOB, &nvs_iterator);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "No entry found in NVS");
|
||||
return NULL;;
|
||||
}
|
||||
while (err == ESP_OK) {
|
||||
nvs_entry_info(nvs_iterator, &nvs_entry);
|
||||
ESP_LOGI(TAG, "Found schedule in NVS with key: %s", nvs_entry.key);
|
||||
handle_list[handle_count] = esp_schedule_nvs_get(nvs_entry.key);
|
||||
if (handle_list[handle_count] != NULL) {
|
||||
/* Increase count only if nvs_get was successful */
|
||||
handle_count++;
|
||||
}
|
||||
err = nvs_entry_next(&nvs_iterator);
|
||||
}
|
||||
nvs_release_iterator(nvs_iterator);
|
||||
#else
|
||||
nvs_iterator_t nvs_iterator = nvs_entry_find(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_TYPE_BLOB);
|
||||
if (nvs_iterator == NULL) {
|
||||
ESP_LOGE(TAG, "No entry found in NVS");
|
||||
return NULL;;
|
||||
}
|
||||
while (nvs_iterator != NULL) {
|
||||
nvs_entry_info(nvs_iterator, &nvs_entry);
|
||||
ESP_LOGI(TAG, "Found schedule in NVS with key: %s", nvs_entry.key);
|
||||
handle_list[handle_count] = esp_schedule_nvs_get(nvs_entry.key);
|
||||
if (handle_list[handle_count] != NULL) {
|
||||
/* Increase count only if nvs_get was successful */
|
||||
handle_count++;
|
||||
}
|
||||
nvs_iterator = nvs_entry_next(nvs_iterator);
|
||||
}
|
||||
#endif
|
||||
*schedule_count = handle_count;
|
||||
ESP_LOGI(TAG, "Found %d schedules in NVS", *schedule_count);
|
||||
return handle_list;
|
||||
}
|
||||
|
||||
bool esp_schedule_nvs_is_enabled(void)
|
||||
{
|
||||
return nvs_enabled;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_nvs_init(char *nvs_partition)
|
||||
{
|
||||
if (nvs_enabled) {
|
||||
ESP_LOGI(TAG, "NVS already enabled");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (nvs_partition) {
|
||||
esp_schedule_nvs_partition = strndup(nvs_partition, strlen(nvs_partition));
|
||||
} else {
|
||||
esp_schedule_nvs_partition = strndup("nvs", strlen("nvs"));
|
||||
}
|
||||
if (esp_schedule_nvs_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate nvs_partition");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
nvs_enabled = true;
|
||||
return ESP_OK;
|
||||
}
|
@@ -0,0 +1 @@
|
||||
5d9175b416f751ba6a7cb35bdf092f0af85658ce06c4a592c7c541d8017ebeb9
|
2
RainMaker_Table-Lights/managed_components/espressif__esp_secure_cert_mgr/.gitignore
vendored
Normal file
2
RainMaker_Table-Lights/managed_components/espressif__esp_secure_cert_mgr/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
tools/esp_secure_cert_data/*
|
@@ -0,0 +1,16 @@
|
||||
# Contains the pre-commit hooks for the repository
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.3.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude: '.+\.(md|rst)'
|
||||
- id: end-of-file-fixer
|
||||
- id: check-executables-have-shebangs
|
||||
- id: mixed-line-ending
|
||||
args: ['-f=lf']
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.8.4
|
||||
hooks:
|
||||
- id: flake8
|
||||
args: ['--config=.flake8', '--tee', '--benchmark']
|
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
|
||||
set(srcs "srcs/esp_secure_cert_tlv_read.c" "srcs/esp_secure_cert_crypto.c")
|
||||
|
||||
if(CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS)
|
||||
list(APPEND srcs "srcs/esp_secure_cert_read.c")
|
||||
endif()
|
||||
|
||||
set(priv reqs "")
|
||||
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||
if(esp_partition IN_LIST build_components)
|
||||
list(APPEND priv_reqs esp_partition)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "private_include"
|
||||
REQUIRES spi_flash mbedtls nvs_flash efuse
|
||||
PRIV_REQUIRES ${priv_reqs})
|
@@ -0,0 +1,88 @@
|
||||
# This file contains the list of changes across different versions
|
||||
|
||||
## v2.5.0
|
||||
* Assigned 2 project specific entries for Matter project in the list of TLV entries
|
||||
|
||||
## v2.4.1
|
||||
* Added a new API `esp_secure_cert_get_tlv_info` for obtaining TLV information
|
||||
* Added `esp_secure_cert_free_tlv_info` API for freeing TLV information.
|
||||
* Added `esp_secure_cert_iterate_to_next_tlv` API for iterating the TLV entries
|
||||
* Updated the API documentation for available `esp_secure_cert_get_*` APIs
|
||||
|
||||
## v2.4.0 (yanked)
|
||||
* Added support for multiple entries of the same type by adding a new field called subtype.
|
||||
* Fixed API for obtaining CA cert for the legacy flash formats (9b091ee)
|
||||
|
||||
### Yank explanation
|
||||
This version was later yanked due to following reason.
|
||||
|
||||
* The API `esp_secure_cert_tlv_get_addr` which was made public in this version has incorrect documentation and the respective free API was not present.
|
||||
* The changes in this version also modifiy the behaviour of existing APIs to obtain the TLV entry of latest subtype. While no current users shall be affected, this may cause inconsistency in the available API usage going forward.
|
||||
|
||||
Please note that the yanked version does not affect any of existing users. The yanking is done due to future API usage considerations and to avoid any possible inconsistencies.
|
||||
|
||||
## v2.3.1
|
||||
* Make esp_secure_cert_get_key_type API available for DS peripheral case as well.
|
||||
|
||||
## v2.3.0
|
||||
* Added support to obtain the priv key type
|
||||
* Added support for getting the efuse key id for priv key
|
||||
|
||||
## v2.2.0
|
||||
* tools: Support DER encoded private keys when creating secure cert partition on host
|
||||
### Breaking changes in v2.2.0
|
||||
* Updated the order of arguments for esp_pbkdf2_hmac_sha256 API to match it with corresponding mbedTLS API
|
||||
|
||||
## v2.1.0
|
||||
* Added support for HMAC based ECDSA key derivation with PBKDF2-HMAC-SHA256
|
||||
* Fixed build failure when example is setup through component manager
|
||||
|
||||
## v2.0.8
|
||||
* Fix for supporting IDF v4.3
|
||||
|
||||
## v2.0.7
|
||||
* Updated documentation regarding TLV format
|
||||
* Fixed priv_key free API when HMAC based encryption scheme is enabled.
|
||||
|
||||
## v2.0.6
|
||||
* Added HMAC based encryption scheme to protect private key data
|
||||
* Added support for private key validation in the esp_secure_cert_app
|
||||
* Added support of configurable esp_secure_cert partition offset in for configure_esp_secure_cert.py utility
|
||||
|
||||
## v2.0.5
|
||||
* Fixed targets in Kconfig to reflect DS Peripheral compatibility
|
||||
|
||||
## v2.0.4
|
||||
* Add implementation of `esp_secure_cert_free_*` APIs for TLV configuration.
|
||||
|
||||
## v2.0.3
|
||||
* Added C linkage so that C++ code can find the definitions for secure cert APIs.
|
||||
* Minor documentation fixes.
|
||||
|
||||
## v2.0.2
|
||||
* Updated reference to the new esp_partition component (IDFv5.0)
|
||||
|
||||
## v2.0.1
|
||||
* Added fixes for build failures with `-Wstrict-prototypes` CFLAG.
|
||||
* Added fix for build failure with toolchain change in IDFv4.x and IDFv5.x
|
||||
|
||||
## v2.0.0
|
||||
* Added esp-secure-cert-tool to PyPi.
|
||||
* Restructure esp-secure-cert-tool
|
||||
### Breaking changes in v2.0.0
|
||||
* Added the support for TLV format for storing data in esp_secure_cert partition.
|
||||
* Make the TLV `cust_flash_tlv` as the default flash format.
|
||||
* Marked all the supported flash formats before TLV as legacy: `cust_flash`, `nvs`.
|
||||
* esp_secure_cert_app: Updated the partition table for the example
|
||||
|
||||
## v1.0.3
|
||||
* esp_secure_cert API now Dynamically identify the type of partitionand access the data accordingly
|
||||
* esp_secure_cert_app: Enable support for target esp32
|
||||
* Added tests based on qemu
|
||||
* Added priv_key functionality to the configure_esp_secure_cert.py script.
|
||||
### Breaking changes in v1.0.3
|
||||
* Removed all the configuration options related to selecting the type of `esp_secure_cert` partition
|
||||
* Remove `esp_secure_cert_get_*_addr` API, the contents can now be obtained through `esp_secure_cert_get_*` API.
|
||||
* Remove APIs to obain the contents of the DS contexts e.g. efuse key id, ciphertext, iv etc. The contents can be accesed from inside the DS context which can be obtained through respective API.
|
||||
* Breaking change in the `esp_secure_cert_get_*` API:
|
||||
The API now accepts `char **buffer` instead of `char *buffer`. It will allocate the required memory dynamically and directly if necessary and provide the respective pointer.
|
@@ -0,0 +1,22 @@
|
||||
menu "ESP Secure Cert Manager"
|
||||
|
||||
config ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
bool "Enable DS peripheral support"
|
||||
default y
|
||||
depends on !IDF_TARGET_ESP32 && !IDF_TARGET_ESP32C2
|
||||
help
|
||||
Enable the DS peripheral support. Not supported on esp32 and esp32c2.
|
||||
|
||||
config ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS
|
||||
bool "Enable support for legacy formats"
|
||||
default n
|
||||
help
|
||||
This option enables support for the legacy formats along with
|
||||
the current format in the esp_secure_cert component.
|
||||
The current format is
|
||||
cust_flash_tlv
|
||||
The legacy formats are as follows:
|
||||
cust_flash
|
||||
nvs
|
||||
|
||||
endmenu # ESP Secure Cert Manager
|
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@@ -0,0 +1,51 @@
|
||||
# ESP Secure Certificate Manager
|
||||
|
||||
The *esp_secure_cert_mgr* provides a simplified interface to access the PKI credentials of a device pre-provisioned with the
|
||||
Espressif Provisioning Service. It provides the set of APIs that are required to access the contents of
|
||||
the `esp_secure_cert` partition.
|
||||
A demo example has also been provided with the `esp_secure_cert_mgr`, more details can be found out
|
||||
in the [example README](https://github.com/espressif/esp_secure_cert_mgr/blob/main/examples/esp_secure_cert_app/README.md)
|
||||
|
||||
## Usage Guidelines
|
||||
|
||||
### 1) Include `esp_secure_cert_mgr` in your project
|
||||
There are two ways to include `esp_secure_cert_mgr` in your project:
|
||||
|
||||
i) Add `esp_secure_cert_mgr` to your project with help of IDF component manager:
|
||||
* The component is hosted at https://components.espressif.com/component/espressif/esp_secure_cert_mgr. Please use the same link to obtain the latest available version of the component along with the instructions on how to add it to your project.
|
||||
* Additional details about using a component through IDF component manager can be found [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html#using-with-a-project)
|
||||
|
||||
ii) Add `esp_secure_cert_mgr` as an extra component in your project.
|
||||
|
||||
* Download `esp_secure_cert_mgr` with:
|
||||
```
|
||||
git clone https://github.com/espressif/esp_secure_cert_mgr.git
|
||||
```
|
||||
* Include `esp_secure_cert_mgr` in `ESP-IDF` with setting `EXTRA_COMPONENT_DIRS` in CMakeLists.txt/Makefile of your project.For reference see [Optional Project Variables](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#optional-project-variables)
|
||||
|
||||
### 2) Use the public API provided by `esp_secure_cert_mgr` in your project
|
||||
* The file [esp_secure_cert_read.h](https://github.com/espressif/esp_secure_cert_mgr/blob/main/include/esp_secure_cert_read.h) contains the public APIs provided by the `esp_secure_cert_mgr`. Please include the file in your project to make use of the available APIs. The file also contains more details about the available APIs.
|
||||
|
||||
## What is Pre-Provisioning?
|
||||
|
||||
With the Espressif Pre-Provisioning Service, the ESP modules are pre-provisioned with an encrypted RSA private key and respective X509 public certificate before they are shipped out to you. The PKI credentials can then be registered with the cloud service to establish a secure TLS channel for communication. With the pre-provisioning taking place in the factory, it provides a hassle-free PKI infrastructure to the Makers. You may use this repository to set up your test modules to validate that your firmware works with the pre-provisioned modules that you ordered through Espressif's pre-provisioning service.
|
||||
|
||||
## ESP Secure Cert Partition
|
||||
|
||||
When a device is pre-provisioned that means the PKI credentials are generated for the device. The PKI credentials are then stored in a partition named
|
||||
*esp_secure_cert*.
|
||||
|
||||
The `esp_secure_cert` partition can be generated on host with help of [configure_esp_secure_cert.py](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/configure_esp_secure_cert.py) utility, more details about the utility can be found in the [tools/README](https://github.com/espressif/esp_secure_cert_mgr/tree/main/tools#readme).
|
||||
|
||||
For esp devices that support DS peripheral, the pre-provisioning is done by leveraging the security benefit of the DS peripheral. In that case, all of the data which is present in the *esp_secure_cert* partition is completely secure.
|
||||
|
||||
When the device is pre-provisioned with help of the DS peripheral then by default the partition primarily contains the following data:
|
||||
1) Device certificate: It is the public key/ certificate for the device's private key. It is used in TLS authentication.
|
||||
2) CA certificate: This is the certificate of the CA which is used to sign the device cert.
|
||||
3) Ciphertext: This is the encrypted private key of the device. The ciphertext is encrypted using the DS peripheral, thus it is completely safe to store on the flash.
|
||||
|
||||
As listed above, the data only contains the public certificates and the encrypted private key and hence it is completely secure in itself. There is no need to further encrypt this data with any additional security algorithm.
|
||||
|
||||
### Partition Format
|
||||
|
||||
The *esp_secure_cert* partition uses TLV format by default. Please take a look at the [format document](https://github.com/espressif/esp_secure_cert_mgr/tree/main/docs/format.md) for more details.
|
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
ESP_SECURE_CERT_APP=$PWD/../examples/esp_secure_cert_app
|
||||
|
||||
# This option causes the script to fail if any command fails in the main body of the script
|
||||
# Note: this does not apply to the internal functions
|
||||
# We need to check the exit status of each command individually
|
||||
set -e
|
||||
|
||||
clean() {
|
||||
rm -r sdkconfig 2>&1
|
||||
rm -r build 2>&1
|
||||
}
|
||||
|
||||
idf_path_verify() {
|
||||
if [[ -z "$IDF_PATH" ]]; then
|
||||
echo "IDF_PATH not set. Please set IDF_PATH "
|
||||
exit 1
|
||||
else
|
||||
echo '--------####--------'
|
||||
echo 'IDF_PATH & Branch'
|
||||
echo $IDF_PATH;(cd $IDF_PATH;git branch --show-current)
|
||||
echo '--------####--------'
|
||||
fi
|
||||
}
|
||||
|
||||
# Build the default config with ds support enabled
|
||||
# $1 = IDF_TARGET
|
||||
build_ds_config() {
|
||||
idf_target=$1
|
||||
cd $ESP_SECURE_CERT_APP
|
||||
clean
|
||||
idf.py set-target $idf_target || exit $?
|
||||
idf.py build || exit $?
|
||||
clean
|
||||
}
|
||||
|
||||
# Build the firmware with the config option for legacy flash formats enabled
|
||||
# $1 = IDF_TARGET
|
||||
build_ds_config_legacy_flash_format() {
|
||||
idf_target=$1
|
||||
cd $ESP_SECURE_CERT_APP
|
||||
clean
|
||||
echo "CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS=y" >> sdkconfig.defaults
|
||||
idf.py set-target $idf_target || exit $?
|
||||
idf.py build || exit $?
|
||||
clean
|
||||
}
|
||||
|
||||
# Build the firmware with ds support disabled
|
||||
build_no_ds_config() {
|
||||
idf_target=$1
|
||||
if [[ $idf_target == "esp32" ]]; then
|
||||
echo "No ds config is same as default config for esp32"
|
||||
echo "Exiting"
|
||||
exit 0
|
||||
fi
|
||||
cd $ESP_SECURE_CERT_APP
|
||||
clean
|
||||
echo "CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n" >> sdkconfig.defaults
|
||||
idf.py set-target $idf_target || exit $?
|
||||
idf.py build || exit $?
|
||||
clean
|
||||
}
|
||||
|
||||
# $1 = IDF_TARGET
|
||||
# $2 = CI_PARALLEL_COUNT
|
||||
# This function assigns tests based on the parallel count value
|
||||
assign_test() {
|
||||
idf_target=$1
|
||||
ci_parallel_count=$2
|
||||
if [[ $ci_parallel_count == "1" ]]; then
|
||||
echo "Building the default config"
|
||||
build_ds_config $idf_target || exit 1
|
||||
fi
|
||||
if [[ $ci_parallel_count == "2" ]]; then
|
||||
echo "Building the legacy flash format config"
|
||||
build_ds_config_legacy_flash_format $idf_target || exit 1
|
||||
fi
|
||||
if [[ $ci_parallel_count == "3" ]]; then
|
||||
echo "Building with ds support disabled"
|
||||
build_no_ds_config $idf_target || exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
IDF_TARGET=$1
|
||||
CI_PARALLEL_COUNT=$2
|
||||
echo "Building for target "$IDF_TARGET
|
||||
echo "CI Parallel count: "$CI_PARALLEL_COUNT
|
||||
idf_path_verify || exit 1
|
||||
assign_test $IDF_TARGET $CI_PARALLEL_COUNT
|
BIN
RainMaker_Table-Lights/managed_components/espressif__esp_secure_cert_mgr/docs/_static/tlv_format.png
vendored
Normal file
BIN
RainMaker_Table-Lights/managed_components/espressif__esp_secure_cert_mgr/docs/_static/tlv_format.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,75 @@
|
||||
# `esp_secure_cert` partition format
|
||||
|
||||
## TLV format
|
||||
|
||||
The `esp_secure_cert` partition uses TLV format by default. TLV format has been decided based on its simplicity and efficiency. We use customized headers specifically designed for the `esp_secure_cert` partition. The `esp_secure_cert` partition is supposed to be a READ-only partition which can contain the data which is bound to the device and is not usually updated e.g. Device private key, Device certificate, CA certificate. Currently `esp_secure_cert_mgr` component does not support modification of the `esp_secure_cert` partition.
|
||||
|
||||
The TLV format used in the esp_secure_cert partition is as follows:
|
||||
|
||||

|
||||
|
||||
* TLV header: It contains the information regarding the data such as the type of the data and the length of the data. For more details about the TLV, please take a look at [tlv_config.h](https://github.com/espressif/esp_secure_cert_mgr/tree/main/private_include/esp_secure_cert_tlv_config.h).
|
||||
|
||||
* i) TLV flags field - flags byte that identifies different characteristics for the TLV
|
||||
* ii) TLV type: Currently the TLV format supports only a list of pre-defined types. The latest list for pre-defined types can be found at [tlv_config.h](https://github.com/espressif/esp_secure_cert_mgr/tree/main/private_include/esp_secure_cert_tlv_config.h). Additional custom types are also provided in order to allow storing custom data in TLV format. The custom data types can be typecasted to appropriate types after reading the TLV.
|
||||
* iii) TLV length - the length of the data field in the TLV.
|
||||
* TLV footer: It contains the crc32 of the data and header field.
|
||||
* TLV format Padding - In TLV format a padding is added automatically between the end offset of data and TLV footer. The padding is added in order to make the data field a multiple of 16 bytes which is the minimum alignment required for flash encrypted writes.
|
||||
|
||||
|
||||
### Partition table entry
|
||||
|
||||
* For TLV format the `partitions.csv` file for the project should contain the following line which enables it to identify the `esp_secure_cert` partition:
|
||||
|
||||
```
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
esp_secure_cert, 0x3F, , 0xD000, 0x2000, encrypted
|
||||
```
|
||||
|
||||
Please note that, TLV format uses compact data representation and hence partition size is kept as 8KiB.
|
||||
|
||||
> Note: The TLV read API expects that a padding of appropriate size is added to data to make it size as a multiple of 16 bytes, the partition generation utility i.e. [configure_esp_secure_cert.py](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/configure_esp_secure_cert.py) takes care of this internally while generating the partition.
|
||||
|
||||
When flash encryption is enabled for the device it is imporatant to encrypt the `esp_secure_cert` partition as well. Adding the encrypted flag in the partition table as done above can ensure that this is done. When flash encryption is not enabled this flag shall be ignored.
|
||||
|
||||
## TLV storage algorithms
|
||||
|
||||
The `esp_secure_cert_mgr` component also supports following algorithms for TLV. In this case the TLV data is not stored directly but is stored by using one of the following algorithms. The information about the algorithms is a part of the TLV itself.
|
||||
|
||||
- HMAC based AES-GCM encryption:
|
||||
In this case the first key stored on the device with purpose `HMAC_UP` is used to derive an AES key and the salt.
|
||||
This key is used to encrypt the TLV contents.
|
||||
This algorithm is suitable where the platform encryption is not enabled.
|
||||
|
||||
- HMAC based ECDSA key derivation:
|
||||
In this case the ECDSA key is derived using following two values
|
||||
- Salt: This is the salt stored in a different tlv with type `ESP_SECURE_CERT_HMAC_ECDSA_KEY_SALT`
|
||||
- HMAC key: This is the first key stored on the device with purpose `HMAC_UP`.
|
||||
Both of these entities are used as input for the pbkdf2 algorithm to generate the ECDSA key.
|
||||
|
||||
Please ensure that appropriate TLV flags are enabled to take care of the underlying security configuration for the given TLV entry. On top of these security configuration, if platform flash encryption is enabled then the secure cert partition contents shall be stored in an encrypted manner on the external flash storage
|
||||
|
||||
## Legacy formats for `esp_secure_cert` partition
|
||||
|
||||
`esp_secure_cert` partition also supports two legacy flash formats.
|
||||
The support for these can be enabled through following menuconfig option:
|
||||
* `Component config > ESP Secure Cert Manager -> Enable support for legacy formats`
|
||||
|
||||
1) *cust_flash*: In this case, the partition is a custom flash partition. The data is directly stored over the flash.
|
||||
* In this case the `partitions.csv` file for the project should contain the following line which enables it to identify the `esp_secure_cert` partition.
|
||||
|
||||
```
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
esp_secure_cert, 0x3F, , 0xD000, 0x6000, encrypted
|
||||
```
|
||||
When flash encryption is enabled the behaviour is same as mentioned above for the TLV format.
|
||||
|
||||
2) *nvs partition*: In this case, the partition is of the `nvs` type. The `nvs_flash` abstraction layer from the ESP-IDF is used to store and then retreive the contents of the `esp_secure_cert` partition.
|
||||
|
||||
* In this case the `partitions.csv` file for the project should contain the following line which enables it to identify the `esp_secure_cert` partition.
|
||||
|
||||
```
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
esp_secure_cert, data, nvs, 0xD000, 0x6000,
|
||||
```
|
||||
Currently the nvs encryption option is not supported for the `esp_secure_cert` partition.
|
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp_secure_cert_app)
|
@@ -0,0 +1,61 @@
|
||||
# ESP Secure Certificate Application
|
||||
|
||||
The sample app demonstrates the use of APIs from *esp_secure_cert_mgr* to retrieve the contents of the *esp_secure_cert* partition. The example can also be used to verify the validity of the contents from the *esp_secure_cert* partition.
|
||||
|
||||
## Requirements
|
||||
* The device must be pre-provisioned and have an *esp_secure_cert* partition.
|
||||
|
||||
## How to use the example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
### Configure the project
|
||||
|
||||
* The *esp_secure_cert* partition needs to be generated and flashed first with help of [configure_esp_secure_cert.py](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/configure_esp_secure_cert.py) script. See [tools/README.md](https://github.com/espressif/esp_secure_cert_mgr/blob/main/tools/README.md) for more details.
|
||||
|
||||
* Please ensure that appropriate type of esp_secure_cert partition has been set in your projects `partitions.csv` file. Please refer the "esp_secure_cert partition" section in the [component README](https://github.com/espressif/esp_secure_cert_mgr#readme) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run the monitor tool to view the serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
### Example Output
|
||||
```
|
||||
I (331) sample_app: Device Cert:
|
||||
Length: 1233
|
||||
-----BEGIN CERTIFICATE-----
|
||||
.
|
||||
.
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
I (441) sample_app: CA Cert:
|
||||
Length: 1285
|
||||
-----BEGIN CERTIFICATE-----
|
||||
.
|
||||
.
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
I (561) sample_app: Successfuly obtained ciphertext, ciphertext length is 1200
|
||||
I (571) sample_app: Successfuly obtained initialization vector, iv length is 16
|
||||
I (571) sample_app: RSA length is 2048
|
||||
I (581) sample_app: Efuse key id 1
|
||||
I (581) sample_app: Successfully obtained the ds context
|
||||
I (831) sample_app: Ciphertext validated succcessfully
|
||||
```
|
||||
|
||||
## Additional configurations for `pre_prov` partition
|
||||
Few of the modules which were pre-provisioned initially had the name of the pre-provisioning partition as `pre_prov`. For the modules which have pre-provisioning partition of name `esp_secure_cert` this part can be ignored.
|
||||
|
||||
* For modules with `pre_prov` partition of type *cust_flash*, please update the line refering to `esp_secure_cert` partition in the partitions.csv with following:
|
||||
```
|
||||
pre_prov, 0x3F, , 0xD000, 0x6000,
|
||||
```
|
||||
* No change is necessary for `pre_prov` partition of type *nvs*.
|
@@ -0,0 +1,4 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
)
|
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* ESP Secure Cert App
|
||||
|
||||
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 <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_secure_cert_read.h"
|
||||
#include "esp_secure_cert_tlv_read.h"
|
||||
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/pk.h"
|
||||
#include "mbedtls/x509.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/error.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#define TAG "esp_secure_cert_app"
|
||||
|
||||
|
||||
#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
static esp_err_t test_ciphertext_validity(esp_ds_data_ctx_t *ds_data, unsigned char *dev_cert, size_t dev_cert_len)
|
||||
{
|
||||
mbedtls_x509_crt crt;
|
||||
mbedtls_x509_crt_init(&crt);
|
||||
unsigned char *sig = NULL;
|
||||
|
||||
if (ds_data == NULL || dev_cert == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int ret = mbedtls_x509_crt_parse(&crt, dev_cert, dev_cert_len);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
esp_err_t esp_ret = esp_ds_init_data_ctx(ds_data);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialze the DS context");
|
||||
return esp_ret;
|
||||
}
|
||||
|
||||
const size_t sig_len = 256;
|
||||
uint32_t hash[8] = {[0 ... 7] = 0xAABBCCDD};
|
||||
|
||||
sig = (unsigned char *) calloc(1, 1000 * sizeof(char));
|
||||
if (sig == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for signature");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ret = esp_ds_rsa_sign(NULL, NULL, NULL, 0, MBEDTLS_MD_SHA256, 0, (const unsigned char *) hash, sig);
|
||||
#else
|
||||
ret = esp_ds_rsa_sign(NULL, NULL, NULL, MBEDTLS_MD_SHA256, 0, (const unsigned char *) hash, sig);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to sign the data with rsa key, returned %02X", ret);
|
||||
goto exit;
|
||||
}
|
||||
esp_ds_release_ds_lock();
|
||||
|
||||
ret = mbedtls_pk_verify(&crt.pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, sig_len);
|
||||
if (ret != 0) {
|
||||
printf("\nFailed to verify the data\n");
|
||||
goto exit;
|
||||
}
|
||||
free(sig);
|
||||
return ESP_OK;
|
||||
exit:
|
||||
free(sig);
|
||||
mbedtls_x509_crt_free(&crt);
|
||||
printf("\nFailed to verify the ciphertext\n");
|
||||
esp_ds_release_ds_lock();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#else
|
||||
static esp_err_t test_priv_key_validity(unsigned char* priv_key, size_t priv_key_len, unsigned char *dev_cert, size_t dev_cert_len)
|
||||
{
|
||||
static const char *pers = "Hello";
|
||||
mbedtls_x509_crt crt;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_pk_context pk;
|
||||
unsigned char *sig = NULL;
|
||||
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
mbedtls_x509_crt_init(&crt);
|
||||
mbedtls_entropy_init(&entropy);
|
||||
mbedtls_pk_init(&pk);
|
||||
esp_err_t esp_ret = ESP_FAIL;
|
||||
if (priv_key == NULL || dev_cert == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int ret = mbedtls_x509_crt_parse(&crt, dev_cert, dev_cert_len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret);
|
||||
esp_ret = ESP_FAIL;
|
||||
goto exit;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Successfully parsed the certificate");
|
||||
}
|
||||
ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, strlen(pers));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned -0x%04x", -ret );
|
||||
esp_ret = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if (MBEDTLS_VERSION_NUMBER < 0x03000000)
|
||||
ret = mbedtls_pk_parse_key(&pk, (const uint8_t *)priv_key, priv_key_len, NULL, 0);
|
||||
#else
|
||||
ret = mbedtls_pk_parse_key(&pk, (const uint8_t *)priv_key, priv_key_len, NULL, 0, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to parse the key");
|
||||
esp_ret = ESP_FAIL;
|
||||
goto exit;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Successfully parsed the key");
|
||||
}
|
||||
|
||||
static uint32_t hash[8] = {[0 ... 7] = 0xAABBCCDD};
|
||||
#define SIG_SIZE 1024
|
||||
sig = (unsigned char*)calloc(1, SIG_SIZE * sizeof(char));
|
||||
if (sig == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory");
|
||||
esp_ret = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
size_t sig_len = 0;
|
||||
#if (MBEDTLS_VERSION_NUMBER < 0x03000000)
|
||||
ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
#else
|
||||
ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, SIG_SIZE, &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to sign the data");
|
||||
esp_ret = ESP_FAIL;
|
||||
goto exit;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Successfully signed the data");
|
||||
}
|
||||
|
||||
ret = mbedtls_pk_verify(&crt.pk, MBEDTLS_MD_SHA256, (const unsigned char *) hash, 0, sig, sig_len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to verify the signed data");
|
||||
esp_ret = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
esp_ret = ESP_OK;
|
||||
exit:
|
||||
free(sig);
|
||||
mbedtls_pk_free(&pk);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_x509_crt_free(&crt);
|
||||
return esp_ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void app_main()
|
||||
{
|
||||
uint32_t len = 0;
|
||||
char *addr = NULL;
|
||||
esp_err_t esp_ret = ESP_FAIL;
|
||||
|
||||
esp_ret = esp_secure_cert_get_device_cert(&addr, &len);
|
||||
if (esp_ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Device Cert: \nLength: %"PRIu32"\n%s", len, (char *)addr);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain flash address of device cert");
|
||||
}
|
||||
|
||||
esp_ret = esp_secure_cert_get_ca_cert(&addr, &len);
|
||||
if (esp_ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "CA Cert: \nLength: %"PRIu32"\n%s", len, (char *)addr);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, addr, len, ESP_LOG_DEBUG);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain flash address of ca_cert");
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
esp_ret = esp_secure_cert_get_priv_key(&addr, &len);
|
||||
if (esp_ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "PEM KEY: \nLength: %"PRIu32"\n%s", len, (char *)addr);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain flash address of private_key");
|
||||
}
|
||||
uint32_t dev_cert_len = 0;
|
||||
char *dev_cert_addr = NULL;
|
||||
esp_ret = esp_secure_cert_get_device_cert(&dev_cert_addr, &dev_cert_len);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to obtain the dev cert flash address");
|
||||
}
|
||||
|
||||
esp_ret = test_priv_key_validity((unsigned char *)addr, len, (unsigned char *)dev_cert_addr, dev_cert_len);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to validate the private key and device certificate");
|
||||
}
|
||||
#else
|
||||
esp_ds_data_ctx_t *ds_data = NULL;
|
||||
ds_data = esp_secure_cert_get_ds_ctx();
|
||||
if (ds_data != NULL) {
|
||||
ESP_LOGI(TAG, "Successfully obtained the ds context");
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->c, ESP_DS_C_LEN, ESP_LOG_DEBUG);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->iv, ESP_DS_IV_LEN, ESP_LOG_DEBUG);
|
||||
ESP_LOGI(TAG, "The value of rsa length is %d", ds_data->rsa_length_bits);
|
||||
ESP_LOGI(TAG, "The value of efuse key id is %d", ds_data->efuse_key_id);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain the ds context");
|
||||
}
|
||||
|
||||
/* Read the dev_cert addr again */
|
||||
esp_ret = esp_secure_cert_get_device_cert(&addr, &len);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to obtain the dev cert flash address");
|
||||
}
|
||||
|
||||
esp_ret = test_ciphertext_validity(ds_data, (unsigned char *)addr, len);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to validate ciphertext");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Ciphertext validated succcessfully");
|
||||
}
|
||||
#endif
|
||||
if (esp_ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Successfully obtained and verified the contents of esp_secure_cert partition");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain and verify the contents of the esp_secure_cert partition");
|
||||
}
|
||||
#ifndef CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS
|
||||
esp_secure_cert_tlv_config_t tlv_config = {};
|
||||
tlv_config.type = ESP_SECURE_CERT_DEV_CERT_TLV;
|
||||
tlv_config.subtype = ESP_SECURE_CERT_SUBTYPE_0;
|
||||
esp_secure_cert_tlv_info_t tlv_info = {};
|
||||
esp_ret = esp_secure_cert_get_tlv_info(&tlv_config, &tlv_info);
|
||||
if (esp_ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Device Cert: \nLength: %"PRIu32"\n%s", tlv_info.length, tlv_info.data);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Printing a list of TLV entries");
|
||||
esp_secure_cert_list_tlv_entries();
|
||||
#endif
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user