diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..437f255 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Remove: [-f*, -m*] diff --git a/.gitignore b/.gitignore index ec8c6ab..2e2a816 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,14 @@ -sdkconfig* -!sdkconfig.defaults - # Build directory build/ -# IDE directory -.pio -.vscode +# SDK configuration +sdkconfig* +!sdkconfig.defaults +!sdkconfig.defaults.* + +# IDE directories +.pio/ +.vscode/ -# CLang -.clangd \ No newline at end of file +# Clang +.clangd diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..57f6fec --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,532 @@ +// +// !!! WARNING !!! AUTO-GENERATED FILE! +// PLEASE DO NOT MODIFY IT AND USE "platformio.ini": +// https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags +// +{ + "configurations": [ + { + "name": "PlatformIO", + "includePath": [ + "G:/projects/prj_aether/GitHub/temperature-sensor/main", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/riscv/include", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/config", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_libc/platform_include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/config/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/config/include/freertos", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/config/riscv/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/portable/riscv/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/esp_additions/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/include/soc", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/ldo/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/debug_probe/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/etm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/mspi_timing_tuning/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/mspi_timing_tuning/tuning_scheme_impl/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/power_supply/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/modem/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/port/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/port/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/port/esp32c6/private_include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/heap/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/heap/tlsf", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/log/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/esp32c6/register", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/hal/platform_port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/hal/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/hal/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/esp32c6/include/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_common/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/port/soc", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/port/include/riscv", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/port/include/private", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_stdio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpio/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_usb/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_usb/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pmu/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pmu/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ana_conv/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ana_conv/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_dma/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_dma/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/include/apps", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/lwip/src/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/freertos/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/esp32xx/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/esp32xx/include/arch", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/esp32xx/include/sys", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpspi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpspi/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_clock/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_clock/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mspi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mspi/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_blockdev/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_security/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_security/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_app_format/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_bootloader_format/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/app_update/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/bootloader_support/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/bootloader_support/bootloader_flash/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_partition/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/efuse/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/efuse/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_timg/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_timg/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_timer/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_security/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_gpio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_uart/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_uart/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_pm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_mm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_dma/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/library", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/core", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/builtin/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/esp_crt_bundle/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/port/psa_driver/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/builtin/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/everest/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/p256-m", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_wdt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_wdt/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rtc_timer/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rtc_timer/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/spi_flash/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_usb_cdc_rom_console/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_touch_sens/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2s/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2s/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/pthread/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_ringbuf/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_psram/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_uart/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/vfs/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_gptimer/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/app_trace/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_event/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/nvs_sec_provider/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/nvs_flash/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_phy/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_phy/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_usb_serial_jtag/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_netif/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wpa_supplicant/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wpa_supplicant/port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wpa_supplicant/esp_supplicant/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_coex/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/include/local", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/wifi_apps/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/wifi_apps/nan_app/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_spi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_gdbstub/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/unity/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/unity/unity/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/cmock/CMock/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/console", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2c/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_twai/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_twai/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/driver/i2c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/driver/touch_sensor/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/driver/twai/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/http_parser", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp-tls", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp-tls/esp-tls-crypto", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_adc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_adc/interface", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_adc/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_blockdev_util/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ana_cmpr/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_ana_cmpr/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_bitscrambler/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_cam/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_isp/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_cam/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_cam/interface", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_dac/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_i2c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_i2s/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_i3c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_jpeg/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_jpeg/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ledc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ledc/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_ledc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mcpwm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mcpwm/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_mcpwm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_parlio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_parlio/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_parlio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pcnt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pcnt/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_pcnt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ppa/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_ppa/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rmt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rmt/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_rmt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_sd/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_sd/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/sdmmc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sd_intf/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdmmc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdmmc/legacy/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdspi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_tsens/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_twai/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_eth/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ieee802154/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ieee802154/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_lcd/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hid/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/tcp_transport/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_http_client/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_http_server/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_https_ota/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_https_server/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_lcd/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_lcd/interface", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protobuf-c/protobuf-c", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/common", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/security", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/transports", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/crypto/srp6a", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/proto-c", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_local_ctrl/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_tee/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_trace/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wear_levelling/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/fatfs/diskio", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/fatfs/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/fatfs/vfs", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/idf_test/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/idf_test/include/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ieee802154/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/rt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/spiffs/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ulp/ulp_common/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ulp/lp_core/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ulp/lp_core/shared/include", + "G:/projects/prj_aether/GitHub/temperature-sensor", + "" + ], + "browse": { + "limitSymbolsToIncludedHeaders": true, + "path": [ + "G:/projects/prj_aether/GitHub/temperature-sensor/main", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/riscv/include", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/config", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_libc/platform_include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/config/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/config/include/freertos", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/config/riscv/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/portable/riscv/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/freertos/esp_additions/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/include/soc", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/ldo/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/debug_probe/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/etm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/mspi_timing_tuning/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/mspi_timing_tuning/tuning_scheme_impl/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/power_supply/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/modem/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/port/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/port/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hw_support/port/esp32c6/private_include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/heap/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/heap/tlsf", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/log/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/soc/esp32c6/register", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/hal/platform_port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/hal/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/hal/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/esp32c6/include/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_rom/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_common/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/port/soc", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/port/include/riscv", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_system/port/include/private", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_stdio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpio/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_usb/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_usb/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pmu/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pmu/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ana_conv/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ana_conv/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_dma/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_dma/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/include/apps", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/lwip/src/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/freertos/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/esp32xx/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/esp32xx/include/arch", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/lwip/port/esp32xx/include/sys", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpspi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_gpspi/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_clock/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_clock/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mspi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mspi/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_blockdev/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_security/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_security/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_app_format/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_bootloader_format/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/app_update/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/bootloader_support/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/bootloader_support/bootloader_flash/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_partition/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/efuse/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/efuse/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_timg/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_timg/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_timer/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_security/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_gpio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_uart/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_uart/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_pm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_mm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_dma/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/library", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/core", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/builtin/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/esp_crt_bundle/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/port/psa_driver/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/builtin/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/everest/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/mbedtls/mbedtls/tf-psa-crypto/drivers/p256-m", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_wdt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_wdt/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rtc_timer/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rtc_timer/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/spi_flash/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_usb_cdc_rom_console/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_touch_sens/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2s/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2s/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/pthread/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_ringbuf/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_psram/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_uart/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/vfs/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_gptimer/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/app_trace/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_event/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/nvs_sec_provider/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/nvs_flash/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_phy/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_phy/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_usb_serial_jtag/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_netif/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wpa_supplicant/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wpa_supplicant/port/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wpa_supplicant/esp_supplicant/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_coex/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/include/local", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/wifi_apps/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_wifi/wifi_apps/nan_app/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_spi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_gdbstub/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/unity/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/unity/unity/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/cmock/CMock/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/console", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2c/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_i2c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_twai/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_twai/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/driver/i2c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/driver/touch_sensor/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/driver/twai/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/http_parser", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp-tls", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp-tls/esp-tls-crypto", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_adc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_adc/interface", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_adc/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_blockdev_util/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ana_cmpr/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_ana_cmpr/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_bitscrambler/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_cam/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_isp/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_cam/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_cam/interface", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_dac/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_i2c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_i2s/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_i3c/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_jpeg/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_jpeg/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ledc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ledc/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_ledc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mcpwm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_mcpwm/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_mcpwm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_parlio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_parlio/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_parlio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pcnt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_pcnt/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_pcnt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ppa/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_ppa/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rmt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_rmt/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_rmt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_sd/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_sd/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/sdmmc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sd_intf/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdio/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdm/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdmmc/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdmmc/legacy/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_sdspi/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_tsens/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_driver_twai/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_eth/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ieee802154/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_ieee802154/esp32c6/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hal_lcd/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_hid/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/tcp_transport/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_http_client/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_http_server/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_https_ota/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_https_server/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_lcd/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_lcd/interface", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protobuf-c/protobuf-c", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/common", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/security", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/transports", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/include/crypto/srp6a", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/protocomm/proto-c", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_local_ctrl/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_tee/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/esp_trace/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/wear_levelling/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/fatfs/diskio", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/fatfs/src", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/fatfs/vfs", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/idf_test/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/idf_test/include/esp32c6", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ieee802154/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/rt/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/spiffs/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ulp/ulp_common/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ulp/lp_core/include", + "C:/Users/Vise/.platformio/packages/framework-espidf/components/ulp/lp_core/shared/include", + "G:/projects/prj_aether/GitHub/temperature-sensor", + "" + ] + }, + "defines": [ + "NDEBUG", + "_POSIX_READER_WRITER_LOCKS", + "_GNU_SOURCE", + "_GLIBCXX_USE_POSIX_SEMAPHORE", + "_GLIBCXX_HAVE_POSIX_SEMAPHORE", + "UTM_ID=0", + "USER_CONFIG=\"../main/user_config.h\"", + "SODIUM_STATIC", + "SOC_XTAL_FREQ_MHZ=CONFIG_XTAL_FREQ", + "SOC_MMU_PAGE_SIZE=CONFIG_MMU_PAGE_SIZE", + "NDEBUG", + "IDF_VER=\"6.0.1\"", + "ESP_PLATFORM", + "CM_ESP32", + "AE_PROJECT_VERSION=\"0.1.0\"", + "AE_GIT_VERSION=\"157aadbec8e7b852d0f89274307ff7cb8103e5f7\"", + "AE_FILTRATION=1", + "AE_DISTILLATION=1", + "AETHER_PREPARED_WIFI_PS=0", + "AETHER_PREPARED_POST_SEND_HOLD_MS=300", + "AETHER_PREPARED_NONCE_RESERVE=10", + "AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS=15000", + "AETHER_PREPARED_HOT_WIFI_MAX_RETRY=10", + "AETHER_PREPARED_HOT_SLEEP_SECONDS=10", + "PLATFORMIO=60200", + "WIFI_SSID=\"test\"", + "WIFI_PASSWORD=\"test\"", + "AE_FILTRATION=1", + "" + ], + "cStandard": "gnu23", + "cppStandard": "gnu++26", + "compilerPath": "C:/Users/Vise/.platformio/packages/toolchain-riscv32-esp/bin/riscv32-esp-elf-gcc.exe", + "compilerArgs": [ + "@G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/toolchain/cflags", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-src", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/aether-tele/8517/src", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/aether-miscpp/c27d/src", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/ae-numeric/b9a3", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/gcem/87fb/include", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/libbcrypt/a95a", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/libsodium/526a/src/libsodium/include", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/libhydrogen/92de", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/etl/8a4e/include", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/aether-client-cpp-build/cpm.cache/stdexec/ff1b/include", + "-isystem", + "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/_deps/stdexec-build/include", + "" + ] + } + ], + "version": 4 +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..7062365 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "Jason2866.esp-decoder", + "pioarduino.pioarduino-ide", + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack", + "pioarduino.pioarduino-ide" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e52d87b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,44 @@ +// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY +// +// PlatformIO Debugging Solution +// +// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html +// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html + +{ + "version": "0.2.0", + "configurations": [ + { + "type": "platformio-debug", + "request": "launch", + "name": "PIO Debug", + "executable": "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/firmware.elf", + "projectEnvName": "esp32-c6-devkitc-1", + "toolchainBinDir": "C:/Users/Vise/.platformio/packages/toolchain-riscv32-esp/bin", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": { + "type": "PlatformIO", + "task": "Pre-Debug" + } + }, + { + "type": "platformio-debug", + "request": "launch", + "name": "PIO Debug (skip Pre-Debug)", + "executable": "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/firmware.elf", + "projectEnvName": "esp32-c6-devkitc-1", + "toolchainBinDir": "C:/Users/Vise/.platformio/packages/toolchain-riscv32-esp/bin", + "internalConsoleOptions": "openOnSessionStart" + }, + { + "type": "platformio-debug", + "request": "launch", + "name": "PIO Debug (without uploading)", + "executable": "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/firmware.elf", + "projectEnvName": "esp32-c6-devkitc-1", + "toolchainBinDir": "C:/Users/Vise/.platformio/packages/toolchain-riscv32-esp/bin", + "internalConsoleOptions": "openOnSessionStart", + "loadMode": "manual" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..461fb7b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,27 @@ +{ + "idf.cmakeCompilerArgs": [ + "-G", + "Ninja", + "-DCMAKE_BUILD_TYPE=Debug", + "-DIDF_TARGET=esp32c6", + "-DBOARD=BOARD_AETHER_ESP32_C6", + "-DWIFI_SSID=Test1234", + "-DWIFI_PASSWORD=Test2345", + "-DUSER_CONFIG=../main/user_config.h" + ], + "idf.currentSetup": "G:\\dev\\esp32\\v6.1\\esp-idf", + "idf.openOcdConfigs": [ + "board/esp32c6-lpcore-builtin.cfg" + ], + "idf.customExtraVars": { + "IDF_TARGET": "esp32c6" + }, + "clangd.path": "G:\\dev\\esp32\\.espressif\\v6.0.2\\tools\\esp-clang\\esp-20.1.1_20250829\\esp-clang\\bin\\clangd.exe", + "clangd.arguments": [ + "--background-index", + "--query-driver=**", + "--compile-commands-dir=g:\\projects\\prj_aether\\GitHub\\temperature-sensor\\build" + ], + "idf.flashType": "UART", + "idf.portWin": "COM9" +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 507e06c..a9a8f0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,9 +30,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(AE_FILTRATION On CACHE BOOL "Set filtration mode by default" FORCE) -# Add a user-provided config file, which will be included as a regular .h file. -set(USER_CONFIG "${CMAKE_CURRENT_LIST_DIR}/main/user_config.h" CACHE FILEPATH - "Path to the Aether user configuration header") +# Add a user-provided config file, which will be included as a regular .h file +set(USER_CONFIG "../main/user_config.h" CACHE PATH "" FORCE) +# ${USER_CONFIG} must be an absolute path or a path to something listed in include directories + include_directories(${CMAKE_CURRENT_LIST_DIR}/main) if (CM_PLATFORM STREQUAL "ESP32") diff --git a/README.md b/README.md index 1f0e51b..cb61e6f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ ## Build Instructions ### Init dependencies -It uses git submodules to manage dependencies. -``` -git submodule update --init --remote ./aether-client-cpp -``` +`aether-client-cpp` is fetched automatically by [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) during the CMake configure step (branch `prepared-packet-v0`). ### For desktop For desktop a regular cmake project is used. diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ab499eb..b6c7511 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -17,6 +17,7 @@ cmake_minimum_required(VERSION 3.16.0) list(APPEND src_list "main.cpp" "controller.cpp" + "prepared_send/prepared_send.cpp" ) list(APPEND bme68x_srcs @@ -47,7 +48,8 @@ if(NOT CM_PLATFORM) include(../cmake/CPM.cmake) - CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git#main") + # Pin exact SHA. Override locally with -DCPM_aether-client-cpp_SOURCE=. + CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git") add_executable(${PROJECT_NAME} ${src_list} ${sleeping_src} ${sensors_src}) set(TARGET_NAME ${PROJECT_NAME}) @@ -61,14 +63,17 @@ else() set(BOARD "" CACHE STRING "Selected board - see user_config.h") #ESP32 CMake - idf_component_register(SRCS ${src_list} ${bme68x_srcs} ${sleeping_src} ${sensors_src} + idf_component_register(SRCS "peripherals/power.cpp" "peripherals/adc.cpp" ${src_list} ${bme68x_srcs} ${sleeping_src} ${sensors_src} INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/.." REQUIRES soc ulp PRIV_REQUIRES + idf::esp_adc + idf::esp_wifi idf::driver idf::esp_driver_i2c idf::esp_driver_tsens idf::esp_driver_gpio + idf::esp_driver_usb_serial_jtag idf::esp_http_server idf::esp_event idf::esp_timer @@ -77,7 +82,8 @@ else() set(TARGET_NAME "${COMPONENT_LIB}") include(../cmake/CPM.cmake) - CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git#main") + # Pin exact SHA. Override locally with -DCPM_aether-client-cpp_SOURCE=. + CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git#157aadbec8e7b852d0f89274307ff7cb8103e5f7") target_link_libraries(${TARGET_NAME} PRIVATE aether) @@ -91,17 +97,13 @@ else() target_compile_definitions(${TARGET_NAME} PRIVATE "BOARD=${BOARD}") endif() - # Define the path to the board configuration file in the build folder - set(ULP_BOARD_FILE "${CMAKE_BINARY_DIR}/ulp_board_config.cmake") - - # Write the value of the BOARD variable to this file + # ulp_add_project() configures a separate ExternalProject, so it does not + # inherit ordinary CMake variables from this component. Forward BOARD via + # ESP-IDF's arguments for CMake subprojects. if(NOT "${BOARD}" STREQUAL "") - file(WRITE "${ULP_BOARD_FILE}" "set(BOARD \"${BOARD}\")\n") - else() - file(WRITE "${ULP_BOARD_FILE}" "set(BOARD \"\")\n") + idf_build_set_property(EXTRA_CMAKE_ARGS "-DBOARD=${BOARD}" APPEND) endif() - # We call the project addition as usual ulp_add_project("ulp_main" "${CMAKE_CURRENT_LIST_DIR}/../ulp") else() @@ -113,3 +115,26 @@ endif() if (NOT "${SERVICE_UID}" STREQUAL "") target_compile_definitions(${TARGET_NAME} PRIVATE "SERVICE_UID=\"${SERVICE_UID}\"") endif() + +# Thermometer prepared-send settings (override via -D...). +if(NOT DEFINED AETHER_PREPARED_NONCE_RESERVE) + set(AETHER_PREPARED_NONCE_RESERVE 10 CACHE STRING "Prepared nonce reserve count") +endif() +if(NOT DEFINED AETHER_PREPARED_HOT_SLEEP_MS) + set(AETHER_PREPARED_HOT_SLEEP_MS 100000 CACHE STRING "Deep sleep milliseconds after send") +endif() +if(NOT DEFINED AETHER_PREPARED_POST_SEND_HOLD_MS) + set(AETHER_PREPARED_POST_SEND_HOLD_MS 300 CACHE STRING "Post-send TX hold ms on prepared path") +endif() +if(NOT DEFINED AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS) + set(AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS 15000 CACHE STRING "Prepared WiFi connect timeout ms") +endif() +if(NOT DEFINED AETHER_PREPARED_HOT_WIFI_MAX_RETRY) + set(AETHER_PREPARED_HOT_WIFI_MAX_RETRY 10 CACHE STRING "Prepared WiFi max association retries") +endif() +target_compile_definitions(${TARGET_NAME} PRIVATE + "AETHER_PREPARED_NONCE_RESERVE=${AETHER_PREPARED_NONCE_RESERVE}" + "AETHER_PREPARED_HOT_SLEEP_MS=${AETHER_PREPARED_HOT_SLEEP_MS}" + "AETHER_PREPARED_POST_SEND_HOLD_MS=${AETHER_PREPARED_POST_SEND_HOLD_MS}" + "AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS=${AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS}" + "AETHER_PREPARED_HOT_WIFI_MAX_RETRY=${AETHER_PREPARED_HOT_WIFI_MAX_RETRY}") diff --git a/main/boards/aether_esp32_c6.h b/main/boards/aether_esp32_c6.h index bc92e41..7d2934b 100644 --- a/main/boards/aether_esp32_c6.h +++ b/main/boards/aether_esp32_c6.h @@ -20,8 +20,6 @@ # error "Illegal CPU! It must be an ESP32C6." #endif -#include "soc/gpio_num.h" - #ifndef BOARD_HAS_ULP # define BOARD_HAS_ULP 0 #endif diff --git a/main/controller.cpp b/main/controller.cpp index 9063ff5..6f17980 100644 --- a/main/controller.cpp +++ b/main/controller.cpp @@ -15,17 +15,16 @@ */ #include -#include #include #include - +#include #include "aether/all.h" #include "aether/env.h" +#include "prepared_send/prepared_send.h" #include "sensors/sensors.h" #include "sleeping/sleeping.h" - using namespace std::chrono_literals; /** @@ -34,7 +33,7 @@ using namespace std::chrono_literals; * For real applications you should register your own uid \see aethernet.io */ static constexpr auto kParentUid = - ae::Uid::FromString("3ac93165-3d37-4970-87a6-fa4ee27744e4"); + ae::Uid::FromString("b1ac52c8-8d94-bd39-4c01-a631ac594165"); /** * \brief Uid of aether service for store the temperature values. * TODO: add actual uid @@ -43,7 +42,7 @@ static constexpr auto kServiceUid = #ifdef SERVICE_UID ae::Uid::FromString(SERVICE_UID); #else - ae::Uid::FromString("752fa297-ec98-47d9-8def-a3ef80ecca42"); + ae::Uid::FromString("6b24b68f-063f-4fe3-8d5a-dc35386983fb"); #endif #ifdef ESP_PLATFORM @@ -95,9 +94,52 @@ static std::unique_ptr aether_app; static ae::Client::ptr client; static std::unique_ptr message_stream; +#ifndef AETHER_PREPARED_HOT_SLEEP_MS +# define AETHER_PREPARED_HOT_SLEEP_MS 100000 +#endif + +static constexpr auto kPreparedHotSleep = + std::chrono::milliseconds{AETHER_PREPARED_HOT_SLEEP_MS}; + +static constexpr std::size_t kPreparedNonceReserve = +#ifdef AETHER_PREPARED_NONCE_RESERVE + AETHER_PREPARED_NONCE_RESERVE; +#else + 32; +#endif + void setup() { std::cout << ae::Format("Setup {:%Y-%m-%d %H:%M:%S}\n") << ae::Now(); +#if defined(ESP_PLATFORM) + // Prepared-send hot path: try to send current temperature without creating + // full AetherApp. Any error falls back to the normal full boot below. + { + std::int16_t hot_temperature = {}; + ReadSensors(&hot_temperature, nullptr, nullptr, nullptr, nullptr); + +#if BOARD_HAS_STCC4 == 1 && BOARD_HAS_ULP == 0 + if (stcc4_error != 0) { + std::cerr << " !!! STCC4 read failed; skipping prepared sensor send\n"; + } else +#endif + { + auto hot_status = + temp_sensor::prepared_send::TryHotWakePreparedSend(hot_temperature); + + std::cout << ae::Format(" >>> Prepared hot path status: {}\n", + temp_sensor::prepared_send::ToString(hot_status)); + + if (hot_status == temp_sensor::prepared_send::HotSendStatus::kSent) { + auto sleep_until = + std::chrono::system_clock::now() + kPreparedHotSleep; + DeepSleep(sleep_until, sleep_until, 3000); + return; + } + } + } +#endif + aether_app = ae::AetherApp::Construct( ae::AetherAppContext{} #if AE_DISTILLATION @@ -198,6 +240,14 @@ void UpdateSensors() { std::uint32_t co2 = {}; ReadSensors(&temperature, &humidity, nullptr, &co2, nullptr); +#if BOARD_HAS_STCC4 == 1 && BOARD_HAS_ULP == 0 + if (stcc4_error != 0) { + std::cerr << " !!! STCC4 read failed; no valid sample to send\n"; + next_tx_time = ae::Now() + kTxInterval; + SleepReady(); + return; + } +#endif std::cout << ae::Format(" >>> Temperature: [{}], Humidity: [{}], CO2: [{}]\n", temperature, humidity, co2); // TODO: add check if wakeup cause is ulp then send value @@ -210,16 +260,29 @@ void MessageReceived(ae::DataBuffer const& buffer) { } void SendValue(std::int16_t temperature) { - temperature = (temperature/100 + 30) * 3; - auto payload = { 0x03, 0x03, 0x0A, (temperature) & 0xFF, (temperature >> 8) & 0xFF }; - auto message = ae::DataBuffer{std::begin(payload), std::end(payload)}; + // The stream is not initialized yet + if (!message_stream) { + return; + } + + auto message = + temp_sensor::prepared_send::MakeTemperaturePayload(temperature); + std::cout << ae::Format(" [CALL-CHAIN] SendValue payload_size={}\n", + message.size()); + next_tx_time = ae::Now() + kTxInterval; message_stream->Write(std::move(message)).status_event().Subscribe([](auto) { + // Export/refresh prepared block after the full send path has a valid + // stream. If export fails, keep normal behavior and just sleep. + if (client.is_valid() && message_stream) { + temp_sensor::prepared_send::ExportPreparedSendBlock( + client, message_stream->destination(), kPreparedNonceReserve); + } + // with any result ready to sleep SleepReady(); }); - next_tx_time = ae::Now() + kTxInterval; } void SleepReady() { @@ -236,7 +299,7 @@ void SleepReady() { go_to_sleep(status.next_service_time); } else { std::cout << ">>> Wait for can suspend\n"; - client->connectivity_policy()->suspend_allowed_event().Subscribe([&]() { + client->connectivity_policy()->suspend_allowed_event().Subscribe([go_to_sleep]() { go_to_sleep(client->connectivity_policy()->GetStatus().next_service_time); }); } diff --git a/main/main.cpp b/main/main.cpp index ef245cd..7a831eb 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -20,11 +20,17 @@ # include #endif +#include "peripherals/adc.h" +#include "peripherals/power.h" + extern void setup(); extern void loop(); #if defined ESP_PLATFORM extern "C" void app_main(void) { + peripherals_release_hold(); + init_adc(); + esp_task_wdt_config_t config_wdt = { /*.timeout_ms = */ 60000, /*.idle_core_mask = */ 0, // i.e. do not watch any idle task diff --git a/main/peripherals/adc.cpp b/main/peripherals/adc.cpp new file mode 100644 index 0000000..896518c --- /dev/null +++ b/main/peripherals/adc.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * 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 "peripherals/adc.h" +#include "peripherals/power.h" +#include "user_config.h" +#include "esp_log.h" +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + +namespace { +constexpr char TAG[] = "BATTERY"; +adc_oneshot_unit_handle_t adc_handle = nullptr; +adc_cali_handle_t cali_handle = nullptr; +void battery_task(void*) { + for (;;) { + const auto voltage = read_battery_voltage_x100(); + if (voltage >= 0 && voltage < BATTERY_LOW_VOLTAGE_X100) { + std::puts("Battery low"); + std::fflush(stdout); + battery_low_deep_sleep(); + } + vTaskDelay(pdMS_TO_TICKS(BATTERY_CHECK_INTERVAL_MS)); + } +} +} + +void init_adc() { +#if BOARD == BOARD_AETHER_ESP32_C6 + if (adc_handle) return; + static_assert(BATTERY_CHECK_INTERVAL_MS >= portTICK_PERIOD_MS); + static_assert(BATTERY_LOW_VOLTAGE_X100 > 0); + adc_oneshot_unit_init_cfg_t unit = {}; + unit.unit_id = ADC_UNIT_1; + unit.ulp_mode = ADC_ULP_MODE_DISABLE; + ESP_ERROR_CHECK(adc_oneshot_new_unit(&unit, &adc_handle)); + adc_oneshot_chan_cfg_t channel = {}; + channel.atten = ADC_ATTEN_DB_12; + channel.bitwidth = ADC_BITWIDTH_12; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_3, &channel)); + adc_cali_curve_fitting_config_t calibration = {}; + calibration.unit_id = ADC_UNIT_1; + calibration.chan = ADC_CHANNEL_3; + calibration.atten = ADC_ATTEN_DB_12; + calibration.bitwidth = ADC_BITWIDTH_12; + ESP_ERROR_CHECK(adc_cali_create_scheme_curve_fitting(&calibration, &cali_handle)); + ESP_ERROR_CHECK(xTaskCreate(battery_task, "battery", 3072, nullptr, 5, nullptr) + == pdPASS ? ESP_OK : ESP_ERR_NO_MEM); +#endif +} + +std::int16_t read_battery_voltage_x100() { + if (!adc_handle || !cali_handle) return -1; + int sum = 0; + for (int i = 0; i < 10; ++i) { + int raw = 0; + const auto error = adc_oneshot_read(adc_handle, ADC_CHANNEL_3, &raw); + if (error != ESP_OK) { + ESP_LOGW(TAG, "ADC read failed: %s", esp_err_to_name(error)); + return -1; + } + sum += raw; + } + int millivolts = 0; + if (adc_cali_raw_to_voltage(cali_handle, sum / 10, &millivolts) != ESP_OK) { + ESP_LOGW(TAG, "ADC calibration conversion failed"); + return -1; + } + return static_cast(millivolts / 10); +} diff --git a/main/peripherals/adc.h b/main/peripherals/adc.h new file mode 100644 index 0000000..37a304b --- /dev/null +++ b/main/peripherals/adc.h @@ -0,0 +1,34 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * 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. + */ + +#ifndef PERIPHERALS_ADC_H_ +#define PERIPHERALS_ADC_H_ + +#include + +#ifndef BATTERY_LOW_VOLTAGE_X100 +#define BATTERY_LOW_VOLTAGE_X100 250 +#endif +#ifndef BATTERY_CHECK_INTERVAL_MS +#define BATTERY_CHECK_INTERVAL_MS 10000 +#endif + +// Call once from app_main; also starts the monitor task on the Aether board. +void init_adc(); +// Calibrated volts * 100, or -1 on failure. No artificial upper clipping. +std::int16_t read_battery_voltage_x100(); + +#endif // PERIPHERALS_ADC_H_ diff --git a/main/peripherals/power.cpp b/main/peripherals/power.cpp new file mode 100644 index 0000000..f76aa2d --- /dev/null +++ b/main/peripherals/power.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * 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 "peripherals/power.h" +#include "user_config.h" +#include "esp_rom_gpio.h" +#include "esp_rom_serial_output.h" +#include "soc/gpio_sig_map.h" +#include +#include "driver/gpio.h" +#include "esp_sleep.h" +#include "esp_wifi.h" +#include "esp_log.h" +#include +#if BOARD_HAS_ULP == 1 +#include "ulp_lp_core.h" +#include "driver/rtc_io.h" +#endif + +namespace { +void power_off_wifi() { + // Stop directly: a separate disconnect event can trigger auto-reconnect. + // The prepared-send path may already have stopped/deinitialized Wi-Fi. + auto error = esp_wifi_stop(); + if (error != ESP_OK && error != ESP_ERR_WIFI_NOT_INIT && + error != ESP_ERR_WIFI_NOT_STARTED) { + ESP_LOGW("POWER", "Wi-Fi stop failed: %s", esp_err_to_name(error)); + } + error = esp_wifi_deinit(); + if (error != ESP_OK && error != ESP_ERR_WIFI_NOT_INIT) { + ESP_LOGW("POWER", "Wi-Fi deinit failed: %s", esp_err_to_name(error)); + } +} + +void hold_low(gpio_num_t pin) { + ESP_ERROR_CHECK(gpio_set_level(pin, 0)); + ESP_ERROR_CHECK(gpio_set_direction(pin, GPIO_MODE_OUTPUT)); + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_FLOATING)); + ESP_ERROR_CHECK(gpio_hold_dis(pin)); + ESP_ERROR_CHECK(gpio_set_level(pin, 0)); + ESP_ERROR_CHECK(gpio_hold_en(pin)); +} +} + +void peripherals_release_hold() { +#if defined(STATUS_LED_ON_PIN) + ESP_ERROR_CHECK(gpio_hold_dis(STATUS_LED_ON_PIN)); + ESP_ERROR_CHECK(gpio_hold_dis(STATUS_LED_PIN)); +#endif +#if BOARD_HAS_PWR_ON == 1 + for (auto pin : {SENSOR_SDA_PIN, SENSOR_SCL_PIN}) { + ESP_ERROR_CHECK(gpio_set_direction(pin, GPIO_MODE_INPUT)); + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_FLOATING)); + ESP_ERROR_CHECK(gpio_hold_dis(pin)); + } +#endif +} + +void peripherals_off(bool keep_sensors) { + power_off_wifi(); + std::fflush(stdout); + std::fflush(stderr); +#if CONFIG_ESP_CONSOLE_UART + esp_rom_output_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM); +#endif + // Use the same LOW / no pulls / hold sequence for every switched pad. +#if BOARD_HAS_PWR_ON == 1 + if (!keep_sensors) { +#if BOARD_HAS_ULP == 1 + ESP_ERROR_CHECK(rtc_gpio_deinit(static_cast(PWR_ON_GPIO))); + ESP_ERROR_CHECK(rtc_gpio_deinit(static_cast(SENSOR_SDA_PIN))); + ESP_ERROR_CHECK(rtc_gpio_deinit(static_cast(SENSOR_SCL_PIN))); +#endif + hold_low(static_cast(PWR_ON_GPIO)); + } +#endif +#if defined(STATUS_LED_ON_PIN) + hold_low(STATUS_LED_ON_PIN); + hold_low(STATUS_LED_PIN); +#endif +#if BOARD_HAS_PWR_ON == 1 + if (!keep_sensors) { + hold_low(SENSOR_SDA_PIN); + hold_low(SENSOR_SCL_PIN); + } +#endif +#if BOARD == BOARD_AETHER_ESP32_C6 + // Battery through R12: disable the digital input/output and both pulls. + gpio_config_t battery = {}; + battery.pin_bit_mask = 1ULL << GPIO_NUM_3; + battery.mode = GPIO_MODE_DISABLE; + battery.pull_up_en = GPIO_PULLUP_DISABLE; + battery.pull_down_en = GPIO_PULLDOWN_DISABLE; + battery.intr_type = GPIO_INTR_DISABLE; + ESP_ERROR_CHECK(gpio_config(&battery)); +#endif +} +[[noreturn]] void battery_low_deep_sleep() { +#if BOARD_HAS_ULP == 1 + ulp_lp_core_stop(); +#endif + ESP_ERROR_CHECK(esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL)); + peripherals_off(); + esp_deep_sleep_start(); +} diff --git a/main/peripherals/power.h b/main/peripherals/power.h new file mode 100644 index 0000000..b1b07c8 --- /dev/null +++ b/main/peripherals/power.h @@ -0,0 +1,28 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * 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. + */ + +#ifndef PERIPHERALS_POWER_H_ +#define PERIPHERALS_POWER_H_ + +// Release retained LED and I2C pads before initialization after wake-up. +void peripherals_release_hold(); + +// Preserve the sensor rail only when ULP needs it during normal sleep. +void peripherals_off(bool keep_sensors = false); +// No automatic wake-up: restart with reset or a power cycle. +[[noreturn]] void battery_low_deep_sleep(); + +#endif // PERIPHERALS_POWER_H_ diff --git a/main/prepared_send/prepared_send.cpp b/main/prepared_send/prepared_send.cpp new file mode 100644 index 0000000..bf7bebb --- /dev/null +++ b/main/prepared_send/prepared_send.cpp @@ -0,0 +1,724 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * ESP32 prepared-send integration for the battery thermometer. + * + * This file intentionally keeps the hot path isolated from controller.cpp. + * If hot path fails for any reason, controller.cpp falls back to normal + * full-Aether boot. + */ + +#include "prepared_send/prepared_send.h" + +#include +#include +#include +#include + +#include "aether-miscpp/misc/defer.h" + +#include "aether/all.h" +#include "aether/prepared_packet/packet_encoder.h" +#include "aether/prepared_packet/prepared_send_message.h" + +#if !defined(ESP_PLATFORM) +# include +#endif + +#if defined(ESP_PLATFORM) +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +namespace temp_sensor::prepared_send { + +void ClearPreparedSendBlock(); + +#if defined(ESP_PLATFORM) +static constexpr char const* kTag = "prepared-send"; +# define PS_LOGI(...) ESP_LOGI(kTag, __VA_ARGS__) +# define PS_LOGW(...) ESP_LOGW(kTag, __VA_ARGS__) +# define PS_LOGE(...) ESP_LOGE(kTag, __VA_ARGS__) +# define PS_LOGD(...) ESP_LOGD(kTag, __VA_ARGS__) +#else +# define PS_LOGI(...) \ + do { \ + } while (0) +# define PS_LOGW(...) \ + do { \ + } while (0) +# define PS_LOGE(...) \ + do { \ + } while (0) +# define PS_LOGD(...) \ + do { \ + } while (0) +#endif + +#ifndef AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS +# define AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS 15000 +#endif + +#ifndef AETHER_PREPARED_HOT_WIFI_MAX_RETRY +# define AETHER_PREPARED_HOT_WIFI_MAX_RETRY 10 +#endif + +#ifndef AETHER_PREPARED_ARP_TIMEOUT_MS +# define AETHER_PREPARED_ARP_TIMEOUT_MS 500 +#endif + +#if defined(ESP_PLATFORM) +static RTC_NOINIT_ATTR ae::prepared_packet::PreparedSendMessageBlock + g_prepared_send_message_block; + +static RTC_DATA_ATTR esp_netif_ip_info_t rtc_ip_info = {}; +static RTC_DATA_ATTR WiFiBaseStation base_station{}; +static RTC_DATA_ATTR std::uint8_t gateway_mac[6] = {}; +static RTC_NOINIT_ATTR bool address_is_valid; +static RTC_NOINIT_ATTR bool bs_is_valid; +static RTC_NOINIT_ATTR bool gateway_mac_valid; + +static_assert(sizeof(ae::prepared_packet::PreparedSendMessageBlock) <= 8 * 1024, + "PreparedSendMessageBlock must fit in ESP32 RTC slow memory"); +#else +static ae::prepared_packet::PreparedSendMessageBlock + g_prepared_send_message_block; +#endif + +#if defined(ESP_PLATFORM) +void InvalidatePreparedWifiCache(); // defined below +#endif + +namespace { + +#if defined(ESP_PLATFORM) + +bool FillUdpDestination(ae::prepared_packet::PreparedEndpoint const& endpoint, + sockaddr* dest_addr, socklen_t* dest_len) { + if (endpoint.address.Index() == ae::AddrVersion::kIpV4) { + auto* dest = reinterpret_cast(dest_addr); + std::memset(dest, 0, sizeof(*dest)); + dest->sin_family = AF_INET; + dest->sin_port = htons(endpoint.port); + std::memcpy(&dest->sin_addr.s_addr, + &endpoint.address.Get().ipv4_value, 4); + *dest_len = sizeof(*dest); + return true; + } + + if (endpoint.address.Index() == ae::AddrVersion::kIpV6) { + auto* dest = reinterpret_cast(dest_addr); + std::memset(dest, 0, sizeof(*dest)); + dest->sin6_family = AF_INET6; + dest->sin6_port = htons(endpoint.port); + std::memcpy(&dest->sin6_addr.s6_addr, + &endpoint.address.Get().ipv6_value, 16); + *dest_len = sizeof(*dest); + return true; + } + + return false; +} + +static EventGroupHandle_t g_wifi_event_group = nullptr; +static esp_netif_t* g_wifi_netif = nullptr; +static esp_event_handler_instance_t g_wifi_any_id_handler = nullptr; +static esp_event_handler_instance_t g_wifi_got_ip_handler = nullptr; +static bool g_wifi_initialized = false; +static bool g_wifi_started = false; +static bool g_default_event_loop_created = false; +static int g_wifi_retry_count = 0; +static bool g_wait_got_ip = true; +static bool g_using_bssid_cache = false; +static int g_max_wifi_retry = AETHER_PREPARED_HOT_WIFI_MAX_RETRY; +static constexpr EventBits_t kWifiReadyBit = BIT0; +static constexpr EventBits_t kWifiFailBit = BIT1; + +void CaptureApIntoCache() { + wifi_ap_record_t ap_info{}; + if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK) { + return; + } + base_station.target_channel = ap_info.primary; + std::memcpy(base_station.target_bssid, ap_info.bssid, + sizeof(base_station.target_bssid)); + bs_is_valid = true; +} + +struct GatewayMacLookupCtx { + struct netif* lwip_netif{nullptr}; + ip4_addr_t gw{}; + std::uint8_t mac[6]{}; + bool found{false}; +}; + +esp_err_t LookupGatewayMacTcpip(void* arg) { + auto* ctx = static_cast(arg); + struct eth_addr* eth_ret = nullptr; + ip4_addr_t const* ip_ret = nullptr; + auto const idx = + etharp_find_addr(ctx->lwip_netif, &ctx->gw, ð_ret, &ip_ret); + if (idx >= 0 && eth_ret != nullptr) { + std::memcpy(ctx->mac, eth_ret->addr, sizeof(ctx->mac)); + ctx->found = true; + } + return ESP_OK; +} + +esp_err_t RequestGatewayArpTcpip(void* arg) { + auto* ctx = static_cast(arg); + (void)etharp_request(ctx->lwip_netif, &ctx->gw); + return ESP_OK; +} + +struct StaticArpInstallCtx { + ip4_addr_t gw{}; + struct eth_addr eth{}; + err_t err{ERR_VAL}; +}; + +esp_err_t InstallStaticGatewayArpTcpip(void* arg) { + auto* ctx = static_cast(arg); + ctx->err = etharp_add_static_entry(&ctx->gw, &ctx->eth); + return ESP_OK; +} + +bool LookupGatewayMac(esp_netif_t* esp_netif, std::uint8_t out_mac[6]) { + if (esp_netif == nullptr || rtc_ip_info.gw.addr == 0) { + return false; + } + auto* lwip_netif = + static_cast(esp_netif_get_netif_impl(esp_netif)); + if (lwip_netif == nullptr) { + return false; + } + + GatewayMacLookupCtx ctx{}; + ctx.lwip_netif = lwip_netif; + ctx.gw.addr = rtc_ip_info.gw.addr; + if (esp_netif_tcpip_exec(&LookupGatewayMacTcpip, &ctx) != ESP_OK || + !ctx.found) { + return false; + } + std::memcpy(out_mac, ctx.mac, 6); + return true; +} + +bool ResolveAndCacheGatewayMac(esp_netif_t* esp_netif) { + std::uint8_t mac[6]{}; + if (LookupGatewayMac(esp_netif, mac)) { + std::memcpy(gateway_mac, mac, sizeof(gateway_mac)); + gateway_mac_valid = true; + return true; + } + + auto* lwip_netif = + static_cast(esp_netif_get_netif_impl(esp_netif)); + if (lwip_netif == nullptr || rtc_ip_info.gw.addr == 0) { + return false; + } + + GatewayMacLookupCtx req{}; + req.lwip_netif = lwip_netif; + req.gw.addr = rtc_ip_info.gw.addr; + (void)esp_netif_tcpip_exec(&RequestGatewayArpTcpip, &req); + + auto const deadline = + std::chrono::steady_clock::now() + + std::chrono::milliseconds(AETHER_PREPARED_ARP_TIMEOUT_MS); + while (std::chrono::steady_clock::now() < deadline) { + vTaskDelay(pdMS_TO_TICKS(20)); + if (LookupGatewayMac(esp_netif, mac)) { + std::memcpy(gateway_mac, mac, sizeof(gateway_mac)); + gateway_mac_valid = true; + return true; + } + } + return false; +} + +bool InstallStaticGatewayArp() { + if (!gateway_mac_valid || !address_is_valid || rtc_ip_info.gw.addr == 0) { + return false; + } + StaticArpInstallCtx ctx{}; + ctx.gw.addr = rtc_ip_info.gw.addr; + std::memcpy(ctx.eth.addr, gateway_mac, sizeof(gateway_mac)); + if (esp_netif_tcpip_exec(&InstallStaticGatewayArpTcpip, &ctx) != ESP_OK) { + return false; + } + return ctx.err == ERR_OK; +} + +// Ensure gateway L2 destination is known before UDP sendto. +// Prefer cached MAC + static ARP; otherwise resolve via ARP (do not drop). +bool EnsureGatewayArpReady(esp_netif_t* esp_netif) { + if (gateway_mac_valid && InstallStaticGatewayArp()) { + return true; + } + + if (ResolveAndCacheGatewayMac(esp_netif)) { + (void)InstallStaticGatewayArp(); + } + // Do not discard the message if ARP is still unresolved; sendto may queue. + return true; +} + +void WifiEventHandler(void*, esp_event_base_t event_base, std::int32_t event_id, + void* event_data) { + if (g_wifi_event_group == nullptr) { + return; + } + + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + g_wifi_retry_count = 0; + auto err = esp_wifi_connect(); + if (err != ESP_OK) { + PS_LOGE("Wi-Fi hot path connect start failed: %s", esp_err_to_name(err)); + xEventGroupSetBits(g_wifi_event_group, kWifiFailBit); + } + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { + CaptureApIntoCache(); + if (!g_wait_got_ip) { + xEventGroupSetBits(g_wifi_event_group, kWifiReadyBit); + } + } else if (event_base == WIFI_EVENT && + event_id == WIFI_EVENT_STA_DISCONNECTED) { + auto const* event = + static_cast(event_data); + auto const reason = event != nullptr ? static_cast(event->reason) : -1; + if (g_wifi_retry_count < g_max_wifi_retry) { + ++g_wifi_retry_count; + PS_LOGW("Wi-Fi hot path disconnected reason=%d; retry %d/%d", reason, + g_wifi_retry_count, g_max_wifi_retry); + auto err = esp_wifi_connect(); + if (err != ESP_OK) { + PS_LOGE("Wi-Fi hot path reconnect failed: %s", esp_err_to_name(err)); + xEventGroupSetBits(g_wifi_event_group, kWifiFailBit); + } + } else { + PS_LOGE("Wi-Fi hot path retry limit reached; reason=%d", reason); + xEventGroupSetBits(g_wifi_event_group, kWifiFailBit); + } + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + auto* event = static_cast(event_data); + rtc_ip_info.ip = event->ip_info.ip; + rtc_ip_info.netmask = event->ip_info.netmask; + rtc_ip_info.gw = event->ip_info.gw; + address_is_valid = true; + CaptureApIntoCache(); + PS_LOGI("Wi-Fi hot path GOT_IP after %d retries", g_wifi_retry_count); + xEventGroupSetBits(g_wifi_event_group, kWifiReadyBit); + } +} + +void CleanupHotPathWifiRuntime() { + if (g_wifi_any_id_handler != nullptr) { + esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, + g_wifi_any_id_handler); + g_wifi_any_id_handler = nullptr; + } + + if (g_wifi_got_ip_handler != nullptr) { + esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, + g_wifi_got_ip_handler); + g_wifi_got_ip_handler = nullptr; + } + + if (g_wifi_started) { + auto err = esp_wifi_stop(); + (void)err; + g_wifi_started = false; + } + + if (g_wifi_initialized) { + auto err = esp_wifi_deinit(); + (void)err; + g_wifi_initialized = false; + } + + if (g_wifi_netif != nullptr) { + esp_netif_destroy_default_wifi(g_wifi_netif); + g_wifi_netif = nullptr; + } + + if (g_wifi_event_group != nullptr) { + vEventGroupDelete(g_wifi_event_group); + g_wifi_event_group = nullptr; + } + + g_wifi_retry_count = 0; + + if (g_default_event_loop_created) { + esp_event_loop_delete_default(); + g_default_event_loop_created = false; + } +} + +bool StartWifiAttempt(bool use_bssid_cache, bool use_static_ip) { +# ifndef WIFI_SSID + return false; +# endif +# ifndef WIFI_PASSWORD + return false; +# endif + + CleanupHotPathWifiRuntime(); + + g_wait_got_ip = !use_static_ip; + g_using_bssid_cache = use_bssid_cache; + g_max_wifi_retry = use_bssid_cache ? 1 : AETHER_PREPARED_HOT_WIFI_MAX_RETRY; + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + wifi_config_t wifi_config{}; + + auto err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || + err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + if (err != ESP_OK && err != ESP_ERR_NVS_NO_FREE_PAGES) { + return false; + } + + (void)esp_netif_init(); + err = esp_event_loop_create_default(); + if (err == ESP_OK) { + g_default_event_loop_created = true; + } else if (err != ESP_ERR_INVALID_STATE) { + return false; + } + + g_wifi_event_group = xEventGroupCreate(); + if (g_wifi_event_group == nullptr) { + CleanupHotPathWifiRuntime(); + return false; + } + + g_wifi_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + if (g_wifi_netif == nullptr) { + g_wifi_netif = esp_netif_create_default_wifi_sta(); + } + if (g_wifi_netif == nullptr) { + CleanupHotPathWifiRuntime(); + return false; + } + + if (use_static_ip && address_is_valid) { + esp_netif_dhcpc_stop(g_wifi_netif); + esp_netif_ip_info_t ip_info = { + .ip = {.addr = rtc_ip_info.ip.addr}, + .netmask = {.addr = rtc_ip_info.netmask.addr}, + .gw = {.addr = rtc_ip_info.gw.addr}}; + esp_netif_set_ip_info(g_wifi_netif, &ip_info); + } + + cfg.ampdu_rx_enable = 0; + cfg.ampdu_tx_enable = 0; + + err = esp_wifi_init(&cfg); + if (err == ESP_ERR_WIFI_INIT_STATE) { + g_wifi_initialized = true; + } else if (err != ESP_OK) { + CleanupHotPathWifiRuntime(); + return false; + } else { + g_wifi_initialized = true; + } + + err = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, + &WifiEventHandler, nullptr, + &g_wifi_any_id_handler); + if (err != ESP_OK) { + CleanupHotPathWifiRuntime(); + return false; + } + + err = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, + &WifiEventHandler, nullptr, + &g_wifi_got_ip_handler); + if (err != ESP_OK) { + CleanupHotPathWifiRuntime(); + return false; + } + + std::strncpy(reinterpret_cast(wifi_config.sta.ssid), WIFI_SSID, + sizeof(wifi_config.sta.ssid)); + std::strncpy(reinterpret_cast(wifi_config.sta.password), WIFI_PASSWORD, + sizeof(wifi_config.sta.password)); + + if (use_bssid_cache && bs_is_valid) { + wifi_config.sta.scan_method = WIFI_FAST_SCAN; + wifi_config.sta.bssid_set = true; + wifi_config.sta.channel = base_station.target_channel; + std::memcpy(wifi_config.sta.bssid, base_station.target_bssid, + sizeof(base_station.target_bssid)); + } + + err = esp_wifi_set_mode(WIFI_MODE_STA); + if (err != ESP_OK) { + CleanupHotPathWifiRuntime(); + return false; + } + + err = esp_wifi_set_config(WIFI_IF_STA, &wifi_config); + if (err != ESP_OK) { + CleanupHotPathWifiRuntime(); + return false; + } + + esp_wifi_set_protocol( + WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N); + + err = esp_wifi_start(); + if (err != ESP_OK) { + CleanupHotPathWifiRuntime(); + return false; + } + g_wifi_started = true; + + (void)esp_wifi_set_max_tx_power(80); + (void)esp_wifi_set_ps(WIFI_PS_MAX_MODEM); + + EventBits_t bits = xEventGroupWaitBits( + g_wifi_event_group, kWifiReadyBit | kWifiFailBit, pdFALSE, pdFALSE, + pdMS_TO_TICKS(AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS)); + + esp_wifi_internal_set_fix_rate(WIFI_IF_STA, true, (wifi_phy_rate_t)0x0); + + if ((bits & kWifiReadyBit) == 0) { + return false; + } + + // After DHCP (cold) or whenever IP is known, refresh gateway MAC cache. + if (address_is_valid && g_wifi_netif != nullptr) { + if (!gateway_mac_valid || !use_static_ip) { + (void)ResolveAndCacheGatewayMac(g_wifi_netif); + } + } + return true; +} + +bool EnsureWifiConnectedForHotPath() { + bool const have_bssid = bs_is_valid; + bool const have_ip = address_is_valid; + + if (have_bssid || have_ip) { + if (StartWifiAttempt(have_bssid, have_ip)) { + (void)EnsureGatewayArpReady(g_wifi_netif); + return true; + } + + // Cached BSSID/channel (and/or static IP) failed — invalidate and fall + // back. + CleanupHotPathWifiRuntime(); + InvalidatePreparedWifiCache(); + } + + if (!StartWifiAttempt(/*use_bssid_cache=*/false, /*use_static_ip=*/false)) { + CleanupHotPathWifiRuntime(); + return false; + } + (void)EnsureGatewayArpReady(g_wifi_netif); + return true; +} + +// Encode + socket/sendto/close only. Does not touch Wi-Fi lifetime. +HotSendStatus EncodeAndUdpSend(ae::DataBuffer const& payload) { + if (!g_prepared_send_message_block.is_valid()) { + return HotSendStatus::kNoPreparedBlock; + } + + if (g_prepared_send_message_block.Resolve()->message_left == 0) { + return HotSendStatus::kNonceExhausted; + } + + ae::DataBuffer packet; + auto encode_result = ae::prepared_packet::EncodePacket( + g_prepared_send_message_block, payload, packet); + + if (!encode_result) { + ClearPreparedSendBlock(); + return HotSendStatus::kEncodeFailed; + } + + auto const resolved_block = g_prepared_send_message_block.Resolve(); + auto endpoint = resolved_block->endpoint; + + sockaddr_storage dest_storage{}; + socklen_t dest_len = 0; + if (!FillUdpDestination(endpoint, reinterpret_cast(&dest_storage), + &dest_len)) { + return HotSendStatus::kSendFailed; + } + + int sock = socket( + endpoint.address.Index() == ae::AddrVersion::kIpV6 ? AF_INET6 : AF_INET, + SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) { + return HotSendStatus::kSendFailed; + } + + auto sent = sendto(sock, packet.data(), packet.size(), 0, + reinterpret_cast(&dest_storage), dest_len); + close(sock); + + if (sent != static_cast(packet.size())) { + return HotSendStatus::kSendFailed; + } + return HotSendStatus::kSent; +} + +#else + +bool EnsureWifiConnectedForHotPath() { return true; } + +void CleanupHotPathWifiRuntime() {} + +HotSendStatus EncodeAndUdpSend(ae::DataBuffer const&) { + return HotSendStatus::kUnsupported; +} + +#endif + +} // namespace + +std::string_view ToString(HotSendStatus status) { + switch (status) { + case HotSendStatus::kSent: + return "sent"; + case HotSendStatus::kNoPreparedBlock: + return "no-prepared-block"; + case HotSendStatus::kNonceExhausted: + return "nonce-exhausted"; + case HotSendStatus::kEncodeFailed: + return "encode-failed"; + case HotSendStatus::kPersistFailed: + return "persist-failed"; + case HotSendStatus::kWifiFailed: + return "wifi-failed"; + case HotSendStatus::kSendFailed: + return "send-failed"; + case HotSendStatus::kUnsupported: + return "unsupported"; + } + return "unknown"; +} + +ae::DataBuffer MakeTemperaturePayload(std::int16_t temperature) { + auto const encoded_temperature = + static_cast((temperature / 100 + 30) * 3); + auto const payload = std::array{ + 0x03, 0x03, 0x0A, static_cast(encoded_temperature & 0xFF), + static_cast((encoded_temperature >> 8) & 0xFF)}; + return ae::DataBuffer{payload.begin(), payload.end()}; +} + +bool HasPreparedSendBlock() { return g_prepared_send_message_block.is_valid(); } + +std::uint32_t PreparedMessageLeft() { + if (!g_prepared_send_message_block.is_valid()) { + return 0; + } + return g_prepared_send_message_block.Resolve()->message_left; +} + +void ClearPreparedSendBlock() { g_prepared_send_message_block.raw.magic = {}; } + +bool ExportPreparedSendBlock(ae::Client::ptr const& client, ae::Uid destination, + std::size_t reserve_message_count) { + auto prep_res = ae::prepared_packet::PrepareSendMessageBlock( + client, destination, reserve_message_count); + + if (!prep_res) { + PS_LOGE("PrepareSendMessage failed ec=%d", prep_res.error().ec); + return false; + } + + g_prepared_send_message_block = std::move(prep_res).value(); + return true; +} + +#if defined(ESP_PLATFORM) +void InvalidatePreparedWifiCache() { + address_is_valid = false; + bs_is_valid = false; + gateway_mac_valid = false; + std::memset(&rtc_ip_info, 0, sizeof(rtc_ip_info)); + std::memset(&base_station, 0, sizeof(base_station)); + std::memset(gateway_mac, 0, sizeof(gateway_mac)); +} + +#endif + +HotSendStatus SendPreparedOnce(ae::DataBuffer const& payload) { +#if defined(ESP_PLATFORM) + if (!g_prepared_send_message_block.is_valid()) { + return HotSendStatus::kNoPreparedBlock; + } + + if (g_prepared_send_message_block.Resolve()->message_left == 0) { + return HotSendStatus::kNonceExhausted; + } + + // Connect BEFORE EncodePacket so a failed Wi-Fi attempt does not burn a + // nonce. + if (!EnsureWifiConnectedForHotPath()) { + return HotSendStatus::kWifiFailed; + } + + auto fail_after_wifi = ae_defer_at[] { CleanupHotPathWifiRuntime(); }; + + auto const status = EncodeAndUdpSend(payload); + if (status != HotSendStatus::kSent) { + return status; + } + +# ifndef AETHER_PREPARED_POST_SEND_HOLD_MS +# define AETHER_PREPARED_POST_SEND_HOLD_MS 300 +# endif + std::this_thread::sleep_for( + std::chrono::milliseconds(AETHER_PREPARED_POST_SEND_HOLD_MS)); + + return HotSendStatus::kSent; +#else + (void)payload; + return HotSendStatus::kUnsupported; +#endif +} + +HotSendStatus TryHotWakePreparedSend( + [[maybe_unused]] std::int16_t temperature) { +#if defined(ESP_PLATFORM) + esp_reset_reason_t reset = esp_reset_reason(); + + // Production deep-sleep semantics: only preserve local Wi-Fi RTC cache across + // deep-sleep wakes. Cold / other resets invalidate the local prepared cache. + if (reset != ESP_RST_DEEPSLEEP) { + InvalidatePreparedWifiCache(); + } + + return SendPreparedOnce(MakeTemperaturePayload(temperature)); +#else + return HotSendStatus::kUnsupported; +#endif +} + +} // namespace temp_sensor::prepared_send diff --git a/main/prepared_send/prepared_send.h b/main/prepared_send/prepared_send.h new file mode 100644 index 0000000..d8e33c4 --- /dev/null +++ b/main/prepared_send/prepared_send.h @@ -0,0 +1,55 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Prepared-send hot path for battery thermometer. + * + * Scope: + * - full boot prepares/exports PreparedSendMessageBlock for the service stream; + * - following ESP32 wakeups try to send one UDP prepared packet without + * constructing full AetherApp; + * - any error falls back to normal full Aether boot; + */ + +#ifndef TEMP_SENSOR_PREPARED_SEND_H_ +#define TEMP_SENSOR_PREPARED_SEND_H_ + +#include +#include +#include + +#include "aether/all.h" + +namespace temp_sensor::prepared_send { + +enum class HotSendStatus { + kSent, + kNoPreparedBlock, + kNonceExhausted, + kEncodeFailed, + kPersistFailed, + kWifiFailed, + kSendFailed, + kUnsupported, +}; + +std::string_view ToString(HotSendStatus status); + +// Build the same binary temperature payload as SendValue(). +// Temperature is in hundredths of a degree Celsius. +ae::DataBuffer MakeTemperaturePayload(std::int16_t temperature); + +HotSendStatus TryHotWakePreparedSend(std::int16_t temperature); + +bool ExportPreparedSendBlock(ae::Client::ptr const& client, ae::Uid destination, + std::size_t reserve_message_count); + +bool HasPreparedSendBlock(); +std::uint32_t PreparedMessageLeft(); + +struct WiFiBaseStation { + uint8_t target_bssid[6]; + uint8_t target_channel; +}; +} // namespace temp_sensor::prepared_send + +#endif // TEMP_SENSOR_PREPARED_SEND_H_ diff --git a/main/sensors/esp_main_utils.c b/main/sensors/esp_main_utils.c index 750808c..21fb7c9 100644 --- a/main/sensors/esp_main_utils.c +++ b/main/sensors/esp_main_utils.c @@ -30,7 +30,6 @@ esp_err_t i2c_init(i2c_master_bus_handle_t *bus_handle, i2c_port_t i2c_handle_po i2c_master_bus_config_t bus_cfg = {}; - ESP_LOGI(TAG_I2C, "Init ESP i2c"); bus_cfg.i2c_port = i2c_handle_port; bus_cfg.sda_io_num = sda_pin; bus_cfg.scl_io_num = scl_pin; @@ -43,7 +42,7 @@ esp_err_t i2c_init(i2c_master_bus_handle_t *bus_handle, i2c_port_t i2c_handle_po err = i2c_new_master_bus(&bus_cfg, bus_handle); if(err != ESP_OK){ ESP_LOGE(TAG_I2C, "Failed to install the i2c driver! Error: %s", esp_err_to_name(err)); - return ESP_ERR_INVALID_STATE; + return err; } return ESP_OK; diff --git a/main/sensors/sensors.h b/main/sensors/sensors.h index 844980b..5a5a86a 100644 --- a/main/sensors/sensors.h +++ b/main/sensors/sensors.h @@ -18,11 +18,26 @@ #define SENSORS_SENSORS_H_ #include +#include "user_config.h" #ifdef __cplusplus extern "C" { #endif +#if BOARD_HAS_STCC4 == 1 && BOARD_HAS_ULP == 0 +// Status of the most recent STCC4 read; outputs stay unchanged on error. +extern volatile uint32_t stcc4_error; +#if BOARD_HAS_PWR_ON == 1 +// Release the sensor bus and switch off the rail before main-CPU deep sleep. +void PowerOffSensors(void); +#endif +#endif + +#if BOARD_HAS_ULP == 1 && ULP_COMP == 0 +// Start ULP on cold boot and wait for a sample before reading shared memory. +void PrepareUlpSensors(void); +#endif + /** * \brief Read the sensor values. * The values are optional, you may specify only those you are interested in. diff --git a/main/sensors/stcc4.c b/main/sensors/stcc4.c index 15e9d18..7b80c6d 100644 --- a/main/sensors/stcc4.c +++ b/main/sensors/stcc4.c @@ -17,8 +17,8 @@ #include "user_config.h" #if BOARD_HAS_STCC4 == 1 -#if ((ULP_COMP == 1) && (BOARD_HAS_ULP == 1)) || ((ULP_COMP == 0) && (BOARD_HAS_ULP == 0)) -#pragma message("STTC4 is enabled") +# if ((ULP_COMP == 1) && (BOARD_HAS_ULP == 1)) || ((ULP_COMP == 0) && (BOARD_HAS_ULP == 0)) +# pragma message("STTC4 is enabled") # include # include @@ -27,6 +27,8 @@ # include "ulp_lp_core_gpio.h" # elif ULP_COMP == 0 # include "driver/gpio.h" +# include "esp_log.h" +# include "esp_rom_sys.h" # endif # include "sensors/sensors.h" @@ -43,17 +45,90 @@ # define STCC4_CMD_SLEEP 0x3650 // Command to enter sleep // Constants -# define LP_I2C_TRANS_TIMEOUT_CYCLES 5000 -# define LP_I2C_TRANS_WAIT_FOREVER -1 +# if ULP_COMP == 1 +// LP driver takes CPU cycles (100 ms at 16 MHz); HP takes milliseconds. +# define STCC4_I2C_TIMEOUT 1600000 +# else +# define STCC4_I2C_TIMEOUT 100 +# endif # define I2C_BUS_SPEED 100000 // 100 KHz // I2C Buffers static uint8_t data_wr[2]; static uint8_t data_rd[12]; static bool initialized = false; +// Exported through the ULP linker script for main-CPU diagnostics. +volatile uint32_t stcc4_error; +volatile uint32_t stcc4_stage; # if ULP_COMP == 0 +static const char* TAG_STCC4 = "STCC4"; static i2c_master_bus_handle_t bus_handle; static i2c_master_dev_handle_t dev_handle_stcc4; + +// Preserve the working startup sequence before handing the pins to hardware I2C. +// Never drive a high level: both lines are open drain with external pull-ups. +static bool startup_scl_high(void) { + gpio_set_level(SENSOR_SCL_PIN, 1); + for (unsigned i = 0; i < 1000; ++i) { + if (gpio_get_level(SENSOR_SCL_PIN)) { + esp_rom_delay_us(10); + return true; + } + esp_rom_delay_us(1); + } + return false; +} + +static int startup_write_byte(uint8_t value) { + for (unsigned i = 0; i < 8; ++i) { + gpio_set_level(SENSOR_SCL_PIN, 0); + gpio_set_level(SENSOR_SDA_PIN, (value & 0x80) != 0); + esp_rom_delay_us(10); + if (!startup_scl_high()) return -1; + value <<= 1; + } + gpio_set_level(SENSOR_SCL_PIN, 0); + gpio_set_level(SENSOR_SDA_PIN, 1); + esp_rom_delay_us(10); + if (!startup_scl_high()) return -1; + int ack = gpio_get_level(SENSOR_SDA_PIN) == 0; + gpio_set_level(SENSOR_SCL_PIN, 0); + return ack; +} + +static void prepare_gpio_bus(void) { + gpio_config_t cfg = { + .pin_bit_mask = (1ULL << SENSOR_SDA_PIN) | (1ULL << SENSOR_SCL_PIN), + .mode = GPIO_MODE_INPUT_OUTPUT_OD, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_set_level(SENSOR_SDA_PIN, 1); + gpio_set_level(SENSOR_SCL_PIN, 1); + if (gpio_config(&cfg) != ESP_OK) return; + // First transaction also supplies the documented wake-up byte; its NACK + // is allowed. The second transaction tests just the address after waking. + for (unsigned attempt = 0; attempt < 2; ++attempt) { + int ack = -1; + if (startup_scl_high() && gpio_get_level(SENSOR_SDA_PIN)) { + gpio_set_level(SENSOR_SDA_PIN, 0); // START + esp_rom_delay_us(10); + ack = startup_write_byte(STCC4_SLAVE_ADDR << 1); + if (attempt == 0 && ack == 1) (void)startup_write_byte(0x00); + } + gpio_set_level(SENSOR_SCL_PIN, 0); + gpio_set_level(SENSOR_SDA_PIN, 0); + esp_rom_delay_us(10); + (void)startup_scl_high(); + gpio_set_level(SENSOR_SDA_PIN, 1); // STOP + esp_rom_delay_us(10); + wait_for(5000); + } + gpio_reset_pin(SENSOR_SDA_PIN); + gpio_reset_pin(SENSOR_SCL_PIN); +} + # endif @@ -75,7 +150,7 @@ static esp_err_t send_command_16bit(uint16_t cmd, uint8_t slave_addr) { data_wr[1] = cmd & 0xFF; // Low byte esp_err_t err = i2c_write(I2C_HANDLE_PORT_S, slave_addr, data_wr, - sizeof(data_wr), LP_I2C_TRANS_WAIT_FOREVER); + sizeof(data_wr), STCC4_I2C_TIMEOUT); if (err != ESP_OK) { // Bail and try again @@ -85,6 +160,15 @@ static esp_err_t send_command_16bit(uint16_t cmd, uint8_t slave_addr) { return ESP_OK; } +static void exit_sleep_mode(void) { + // STCC4 wakes on the single byte 0x00. The sensor may NACK this byte while + // waking up, so the return code must intentionally be ignored. + data_wr[0] = 0x00; + (void)i2c_write(I2C_HANDLE_PORT_S, STCC4_SLAVE_ADDR, data_wr, 1, + STCC4_I2C_TIMEOUT); + wait_for(5000); // 5 ms +} + bool Init() { // 1. power ON #if BOARD_HAS_PWR_ON == 1 @@ -93,11 +177,18 @@ bool Init() { ulp_lp_core_gpio_output_enable(LP_PWR_ON_GPIO); ulp_lp_core_gpio_set_level(LP_PWR_ON_GPIO, 1); # elif ULP_COMP == 0 + gpio_hold_dis((gpio_num_t)PWR_ON_GPIO); gpio_set_direction((gpio_num_t)PWR_ON_GPIO, GPIO_MODE_OUTPUT); gpio_set_level((gpio_num_t)PWR_ON_GPIO, 1); # endif #endif + // Let the switched sensor rail settle before preparing the GPIO bus. +#if ULP_COMP == 0 + wait_for(125000); + prepare_gpio_bus(); +#endif + // 2. Install I2C driver if (i2c_init(I2C_BUS_HANDLE_S, STCC4_I2C_NUM_0, SENSOR_SDA_PIN, SENSOR_SCL_PIN, I2C_BUS_SPEED) != ESP_OK) { return false; @@ -109,26 +200,54 @@ bool Init() { dev_cfg_stcc4.dev_addr_length = I2C_ADDR_BIT_LEN_7; dev_cfg_stcc4.device_address = STCC4_SLAVE_ADDR; dev_cfg_stcc4.scl_speed_hz = I2C_BUS_SPEED; - - if(i2c_master_bus_add_device(bus_handle, &dev_cfg_stcc4, &dev_handle_stcc4) != ESP_OK){ + esp_err_t err = i2c_master_bus_add_device(bus_handle, &dev_cfg_stcc4, &dev_handle_stcc4); + if (err != ESP_OK) { + ESP_LOGE(TAG_STCC4, "Failed to add I2C device: %s", esp_err_to_name(err)); + i2c_del_master_bus(bus_handle); + bus_handle = NULL; return false; } #endif + // After power-up STCC4 is already in idle mode. Do not put it to sleep here: + // waking it requires a separate one-byte 0x00 command. Send that command as + // well so a warm MCU restart can recover a sensor left asleep by old firmware. wait_for(125000); // 125 ms - - esp_err_t err = send_command_16bit(STCC4_CMD_SLEEP, STCC4_SLAVE_ADDR); - if (err != ESP_OK) { - return false; - } + exit_sleep_mode(); return true; } +#if ULP_COMP == 0 && BOARD_HAS_PWR_ON == 1 +void PowerOffSensors(void) { + // No more measurements are scheduled in this boot after sleep preparation. + if (dev_handle_stcc4 != NULL) { + ESP_ERROR_CHECK(i2c_master_bus_rm_device(dev_handle_stcc4)); + dev_handle_stcc4 = NULL; + } + if (bus_handle != NULL) { + ESP_ERROR_CHECK(i2c_del_master_bus(bus_handle)); + bus_handle = NULL; + } + // The external pull-ups are on the switched rail. Release both lines so + // they cannot back-power an unpowered sensor through its I2C pins. + ESP_ERROR_CHECK(gpio_set_direction(SENSOR_SDA_PIN, GPIO_MODE_INPUT)); + ESP_ERROR_CHECK(gpio_set_pull_mode(SENSOR_SDA_PIN, GPIO_FLOATING)); + ESP_ERROR_CHECK(gpio_set_direction(SENSOR_SCL_PIN, GPIO_MODE_INPUT)); + ESP_ERROR_CHECK(gpio_set_pull_mode(SENSOR_SCL_PIN, GPIO_FLOATING)); + ESP_ERROR_CHECK(gpio_set_direction((gpio_num_t)PWR_ON_GPIO, GPIO_MODE_OUTPUT)); + ESP_ERROR_CHECK(gpio_set_level((gpio_num_t)PWR_ON_GPIO, 0)); + ESP_ERROR_CHECK(gpio_hold_en((gpio_num_t)PWR_ON_GPIO)); + initialized = false; +} +#endif + void ReadSensors(int16_t* temperature, uint32_t* humidity, uint32_t* pressure, uint32_t* co2, uint32_t* gas_resistance) { esp_err_t ret; uint16_t tmp; + stcc4_stage = 1; // Initialization + stcc4_error = ESP_FAIL; if (!initialized) { initialized = Init(); @@ -137,27 +256,81 @@ void ReadSensors(int16_t* temperature, uint32_t* humidity, uint32_t* pressure, } } + stcc4_stage = 2; // Start single shot ret = send_command_16bit(STCC4_CMD_MEASURE_SINGLE_SHOT, STCC4_SLAVE_ADDR); +#if ULP_COMP == 0 + if (ret == ESP_ERR_INVALID_RESPONSE) { + // A warm MCU reset does not reset a separately powered sensor. Wake it + // and distinguish an absent address from a rejected command first. + exit_sleep_mode(); + esp_err_t reset = i2c_master_bus_reset(bus_handle); + if (reset != ESP_OK) { + stcc4_error = reset; + return; + } + esp_err_t probe = i2c_master_probe(bus_handle, STCC4_SLAVE_ADDR, + STCC4_I2C_TIMEOUT); + if (probe == ESP_OK) { + ret = send_command_16bit(STCC4_CMD_STOP_CONTINUOUS, STCC4_SLAVE_ADDR); + if (ret == ESP_OK) { + wait_for(1200000); // Datasheet: stop completes within 1200 ms. + ret = send_command_16bit(STCC4_CMD_MEASURE_SINGLE_SHOT, STCC4_SLAVE_ADDR); + } + } else { + ret = probe; // Report the observed timeout instead of the earlier error. + } + } +#endif + stcc4_error = ret; if (ret != ESP_OK) { +#if ULP_COMP == 0 + ESP_LOGE(TAG_STCC4, "Failed to start measurement: %s", esp_err_to_name(ret)); +#endif return; } - // Wait for measurement completion (according to STCC4 datasheet ~500-720ms) - wait_for(750000); // 750 ms + wait_for(500000); // 500 ms, specified single-shot execution time - // Read 3 bytes: CO2 with CRC + // Measurement data is not returned by a bare I2C read. The STCC4 first + // requires the read_measurement command, followed by a short wait. + stcc4_stage = 3; // Request result + ret = send_command_16bit(STCC4_CMD_READ_DATA, STCC4_SLAVE_ADDR); + stcc4_error = ret; + if (ret != ESP_OK) { +#if ULP_COMP == 0 + ESP_LOGE(TAG_STCC4, "Failed to request measurement: %s", esp_err_to_name(ret)); +#endif + return; + } + wait_for(1000); // 1 ms + + // Four 16-bit values (CO2, temperature, humidity and status), each followed + // by its CRC byte, are transferred as 12 bytes on the wire. + stcc4_stage = 4; // Read result ret = i2c_read(I2C_HANDLE_PORT_S, STCC4_SLAVE_ADDR, data_rd, sizeof(data_rd), - LP_I2C_TRANS_WAIT_FOREVER); + STCC4_I2C_TIMEOUT); + stcc4_error = ret; if (ret != ESP_OK) { +#if ULP_COMP == 0 + ESP_LOGE(TAG_STCC4, "Failed to read measurement: %s", esp_err_to_name(ret)); +#endif return; } - // Read CO2 - // Checking CRC - if (!check_crc(data_rd, data_rd[2])) { - return; + // Validate the whole frame on both CPUs before publishing any values. + stcc4_stage = 5; // CRC validation + for (unsigned i = 0; i < sizeof(data_rd); i += 3) { + if (!check_crc(&data_rd[i], data_rd[i + 2])) { + stcc4_error = ESP_ERR_INVALID_CRC; +#if ULP_COMP == 0 + ESP_LOGE(TAG_STCC4, "Measurement CRC mismatch in word %u", i / 3); +#endif + return; + } } + + // Read CO2 // Convert to CO2 value (format depends on sensor, typically 16 bits) uint16_t co2_ppm = (data_rd[0] << 8) | data_rd[1]; @@ -166,31 +339,19 @@ void ReadSensors(int16_t* temperature, uint32_t* humidity, uint32_t* pressure, } // Read Temperature - // Checking CRC -#if ULP_COMP == 0 - if (!check_crc(&data_rd[3], data_rd[5])) { - return; - } -#endif // Temperature: T = -45 + 175 * raw_temp / 65535 tmp = (data_rd[3] << 8) | data_rd[4]; int32_t temp_x100 = (17500ULL * tmp) / 65535 - 4500; if (temperature) { - *temperature = (uint32_t)temp_x100; + *temperature = (int16_t)temp_x100; } // Read Humidity - // Checking CRC -#if ULP_COMP == 0 - if (!check_crc(&data_rd[6], data_rd[8])) { - return; - } -#endif - // Humidity: RH = 100 * raw_hum / 65535 + // Humidity: RH = -6 + 125 * raw_hum / 65535 tmp = (data_rd[6] << 8) | data_rd[7]; - int32_t hum_x100 = (10000ULL * tmp) / 65535; + int32_t hum_x100 = (12500ULL * tmp) / 65535 - 600; // Humidity limitation within 0..100% if (hum_x100 > 10000) hum_x100 = 10000; @@ -199,6 +360,9 @@ void ReadSensors(int16_t* temperature, uint32_t* humidity, uint32_t* pressure, if (humidity) { *humidity = (uint32_t)hum_x100; } + + stcc4_error = ESP_OK; + stcc4_stage = 0; // Complete } bool check_crc(const uint8_t *data, uint8_t crc) { diff --git a/main/sensors/ulp_saved_val.c b/main/sensors/ulp_saved_val.c index c42af12..25664b5 100644 --- a/main/sensors/ulp_saved_val.c +++ b/main/sensors/ulp_saved_val.c @@ -20,16 +20,18 @@ #if ((BOARD_HAS_ULP == 1) && (ULP_COMP == 0)) # include +# include # include "ulp_main.h" void ReadSensors(int16_t* temperature, uint32_t* humidity, uint32_t* pressure, uint32_t* co2, uint32_t* gas_resistance) { + PrepareUlpSensors(); printf(" >>> ULP Temperature: [%d]\n", (int16_t)ulp_temperature); - printf(" >>> ULP Humidity: [%d]\n", (int16_t)ulp_humidity); - printf(" >>> ULP Pressure: [%d]\n", (int16_t)ulp_pressure); - printf(" >>> ULP Co2: [%d]\n", (int16_t)ulp_co2); - printf(" >>> ULP Gas_resistance: [%d]\n", (int16_t)ulp_gas_resistance); + printf(" >>> ULP Humidity: [%" PRIu32 "]\n", ulp_humidity); + printf(" >>> ULP Pressure: [%" PRIu32 "]\n", ulp_pressure); + printf(" >>> ULP Co2: [%" PRIu32 "]\n", ulp_co2); + printf(" >>> ULP Gas_resistance: [%" PRIu32 "]\n", ulp_gas_resistance); if (temperature) { *temperature = (int16_t)ulp_temperature; diff --git a/main/sleeping/esp_main_sleep.cpp b/main/sleeping/esp_main_sleep.cpp index f6655aa..2e40c22 100644 --- a/main/sleeping/esp_main_sleep.cpp +++ b/main/sleeping/esp_main_sleep.cpp @@ -15,16 +15,20 @@ */ #include "sleeping/sleeping.h" +#include "sensors/sensors.h" #include +#include #include "aether/all.h" +#include "peripherals/power.h" #if ESP_MAIN_SLEEP == 1 # include # include # include +# include # include # include @@ -48,11 +52,18 @@ static const char* TAG = "ESP_MAIN_SLEEP"; int DeepSleep(time_point soft_sleep_tp, time_point, std::int16_t) { - auto time_us = std::chrono::duration_cast( - soft_sleep_tp - std::chrono::system_clock::now()) - .count(); + // A deadline can expire while sending or waiting for permission to suspend. + // Wake shortly to process overdue work, rather than requesting a zero timer. + const auto remaining_us = std::chrono::duration_cast( + soft_sleep_tp - std::chrono::system_clock::now()).count(); + const auto time_us = static_cast( + std::max(remaining_us, 1000000)); - esp_sleep_enable_timer_wakeup(time_us); + esp_err_t ret = esp_sleep_enable_timer_wakeup(time_us); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Cannot configure sleep timer: %s; restarting", esp_err_to_name(ret)); + esp_restart(); + } ESP_LOGI(TAG, "Timer wakeup enabled: %llu us", time_us); ESP_LOGI(TAG, "Entering deep sleep..."); @@ -64,10 +75,20 @@ int DeepSleep(time_point soft_sleep_tp, time_point, std::int16_t) { esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON); # endif + // Only the main-CPU sensor path powers down here. ULP owns the rail when + // it is responsible for measurements during main-CPU sleep. +#if BOARD_HAS_STCC4 == 1 && BOARD_HAS_PWR_ON == 1 && BOARD_HAS_ULP == 0 + PowerOffSensors(); +#endif + // Enter deep sleep - esp_err_t ret = esp_deep_sleep_try_to_start(); + peripherals_off(BOARD_HAS_ULP == 1); + ret = esp_deep_sleep_try_to_start(); if (ret != ESP_OK) { - ESP_LOGE(TAG, "Deep sleep failed: %s", esp_err_to_name(ret)); + // Callers have finished their send/suspend flow and do not schedule a + // retry after this function returns. Restart to resume normal startup. + ESP_LOGE(TAG, "Deep sleep failed: %s; restarting", esp_err_to_name(ret)); + esp_restart(); } return ret; // Should be never reached diff --git a/main/sleeping/sleeping.h b/main/sleeping/sleeping.h index 10441b4..d5a0b5d 100644 --- a/main/sleeping/sleeping.h +++ b/main/sleeping/sleeping.h @@ -20,6 +20,11 @@ #include #include +#ifndef ULP_WAKEUP_TIMER_MS +// ULP sampling interval during deep sleep, in milliseconds. +#define ULP_WAKEUP_TIMER_MS 25000 +#endif + using time_point = std::chrono::time_point; int DeepSleep(time_point soft_sleep_tp, time_point hard_sleep_tp, diff --git a/main/sleeping/ulp_sleep.cpp b/main/sleeping/ulp_sleep.cpp index 873bfc2..f20af18 100644 --- a/main/sleeping/ulp_sleep.cpp +++ b/main/sleeping/ulp_sleep.cpp @@ -17,11 +17,13 @@ #include "sleeping/sleeping.h" #include "user_config.h" +#include "peripherals/power.h" #include "aether/all.h" #if ULP_SLEEP == 1 # include +# include # include # include @@ -29,6 +31,8 @@ # include # include # include +# include +# include "sensors/sensors.h" # include # include @@ -39,11 +43,11 @@ extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); -static void lp_core_init(void) { +static void lp_core_init(uint32_t wakeup_timer_us) { esp_err_t ret = ESP_OK; ulp_lp_core_cfg_t cfg = {.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, - .lp_timer_sleep_duration_us = 1000000}; + .lp_timer_sleep_duration_us = wakeup_timer_us}; ret = ulp_lp_core_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start)); @@ -52,6 +56,13 @@ static void lp_core_init(void) { abort(); } + // Configure shared memory before running the LP core. No wakeups are + // requested while the main CPU is acquiring its initial sample. + ulp_wakeup_temp_threshold = INT16_MAX; + ulp_wakeup_co2_threshold = UINT32_MAX; + ulp_wakeup_gas_threshold = UINT32_MAX; + ulp_can_start = 1; + ret = ulp_lp_core_run(&cfg); if (ret != ESP_OK) { std::cout << ae::Format("LP Core run failed!\n"); @@ -94,17 +105,52 @@ static void lp_i2c_init(void) { std::cout << ae::Format("LP I2C initialized successfully!\n"); } +extern "C" void PrepareUlpSensors(void) { + static bool prepared = false; + if (prepared) { + return; + } + + // Freeze retained results before accessing them or reloading the binary. + ulp_lp_core_stop(); + if (esp_reset_reason() != ESP_RST_DEEPSLEEP || ulp_sample_count == 0 || + ulp_sample_in_progress != 0) { + lp_i2c_init(); + // The first timer wakeup must fit within the initial sample timeout, + // independently of the sampling interval used during deep sleep. + lp_core_init(1000000); + const auto deadline = esp_timer_get_time() + 5000000; + volatile uint32_t& completed_samples = ulp_sample_count; + while (completed_samples == 0 && esp_timer_get_time() < deadline) { + vTaskDelay(1); + } + ulp_lp_core_stop(); + if (ulp_sample_count == 0) { + ESP_LOGE("ULP", "No sensor sample within 5 seconds"); + abort(); + } + } +#if BOARD_HAS_STCC4 == 1 + if (ulp_stcc4_error != ESP_OK) { + ESP_LOGE("ULP", "STCC4 measurement failed: stage=%" PRIu32 ", error=%" PRIu32, + ulp_stcc4_stage, ulp_stcc4_error); + abort(); + } +#endif + prepared = true; +} + int DeepSleep(time_point, time_point hard_sleep_tp, std::int16_t temperature_threshold) { /* Initialize LP_I2C from the main processor */ lp_i2c_init(); /* Load LP Core binary and start the coprocessor */ - lp_core_init(); - - vTaskDelay(pdMS_TO_TICKS(1)); + static_assert(ULP_WAKEUP_TIMER_MS > 0 && + ULP_WAKEUP_TIMER_MS <= UINT32_MAX / 1000, + "ULP interval must fit the ESP-IDF microsecond timer"); + lp_core_init(static_cast(ULP_WAKEUP_TIMER_MS) * 1000U); ulp_wakeup_temp_threshold = static_cast(temperature_threshold); - ulp_can_start = 1; // sleep either until hard sleep or ulp wakeup auto time_us = std::chrono::duration_cast( @@ -112,6 +158,7 @@ int DeepSleep(time_point, time_point hard_sleep_tp, .count(); esp_sleep_enable_timer_wakeup(time_us); esp_sleep_enable_ulp_wakeup(); + ulp_wakeup_enabled = 1; std::cout << ae::Format("Timer wakeup enabled: {} us\n", time_us); @@ -124,6 +171,7 @@ int DeepSleep(time_point, time_point hard_sleep_tp, esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON); # endif + peripherals_off(BOARD_HAS_ULP == 1); esp_deep_sleep_start(); return 0; diff --git a/main/user_config.h b/main/user_config.h index 123c80d..75ac1cd 100644 --- a/main/user_config.h +++ b/main/user_config.h @@ -24,6 +24,11 @@ #if !defined IS_ULP_COCPU # include "aether/config_consts.h" +// EventHandler may wrap SmallFunction instances whose default alignment is +// max_align_t. Keep the outer storage equally aligned (not just pointer-aligned) +// so GCC 15 does not reject the nested callback in ActionsQueue. +# define AE_EVENT_HANDLER_ALIGN alignof(std::max_align_t) + # define AE_CRYPTO_ASYNC AE_HYDRO_CRYPTO_PK # define AE_CRYPTO_SYNC AE_HYDRO_CRYPTO_SK # define AE_SIGNATURE AE_HYDRO_SIGNATURE diff --git a/platformio.ini b/platformio.ini index e1a231a..0d34bde 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,11 @@ build_type = release board_build.partitions =./system/partitions.csv monitor_filters = esp32_exception_decoder board_build.esp-idf.preserve_source_file_extension = yes +# PlatformIO 7.0.1 leaks this C-only ESP-IDF warning flag into C++ commands. +# With the project's -Werror policy GCC 15 treats that diagnostic as an error. +build_unflags = -Wno-old-style-declaration build_flags = + -Wno-error=old-style-declaration -DWIFI_SSID="\"test\"" -DWIFI_PASSWORD="\"test\"" -DAE_FILTRATION=1 diff --git a/sdkconfig.defaults b/sdkconfig.defaults index ff4a7d8..a8799b2 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -32,6 +32,7 @@ CONFIG_PARTITION_TABLE_MD5=y CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096 CONFIG_HTTPD_MAX_REQ_HDR_LEN=2048 CONFIG_HTTPD_MAX_URI_LEN=2048 @@ -48,14 +49,92 @@ CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_LP_CORE=y CONFIG_ULP_COPROC_RESERVE_MEM=13312 -# Set log level to Warning to produce clean output +# Bootloader warnings and application info logs, matching the working configuration CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y CONFIG_BOOTLOADER_LOG_LEVEL=2 CONFIG_LOG_DEFAULT_LEVEL_INFO=y CONFIG_LOG_DEFAULT_LEVEL=3 -# Enable USB console +# Keep UART0 as the primary console and USB Serial/JTAG as the secondary console +CONFIG_ESP_CONSOLE_UART_DEFAULT=y CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=n CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y -CONFIG_ESP_CONSOLE_UART=n + +#==============================Optimization========================================= + +# 1. Compiler optimization (Critical for execution speed) +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y +# PlatformIO 7 currently mixes language-specific flags when using ESP-IDF 6 +# with preserved source extensions. Do not promote those GCC diagnostics to +# hard errors; project-owned targets still keep their explicit warning policy. +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y + +# 2. Bootloader acceleration +# Skip image validation when exiting Deep Sleep (saves ~100-150ms) +CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y +CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON=y +CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y + +# 3. Disable logging (UART is the main bottleneck at startup) +CONFIG_LOG_DEFAULT_LEVEL_NONE=y +CONFIG_LOG_MAXIMUM_LEVEL_0=y + +# 4. Radio and Wi-Fi (No-NVS mode) +# Move PHY calibration to RTC RAM (eliminates Flash accesses) +# CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE_IN_RTC=y +# Completely disable Wi-Fi stack dependency on NVS +# CONFIG_ESP_WIFI_NVS_ENABLED=n + +# 5. ESP32-C6 specific settings for fast startup +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +CONFIG_ESP32C6_REV_MIN_0=y + +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=n +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=n +# Use partial calibration (takes data from NVS, doesn't re-measure) +CONFIG_ESP_PHY_RF_CAL_PARTIAL=y +# Disable verbose PHY logs +CONFIG_ESP_PHY_LOG_VERBOSE=n +CONFIG_RTC_CLK_CAL_THRESHOLD=0 +CONFIG_ESP_PHY_ENABLE_USB_CONTROLLER=n + +# Force power-down of Flash in deep sleep +CONFIG_ESP_SLEEP_POWER_DOWN_FLASH=y + +# Power down VDDSDIO (memory controller) during sleep +CONFIG_ESP_SLEEP_PD_DOMAIN_VDDSDIO=y + +# Use internal RC oscillator (most power-efficient sleep clocking option) +CONFIG_RTC_CLK_SRC_INT_RC=y + +# Disable clocking of unused RTC-domain peripherals +CONFIG_RTC_CLK_CAL_THRESHOLD=0 + +# Redirect console to UART0 or disable entirely (None) +# This frees built-in USB-JTAG resources +#CONFIG_ESP_CONSOLE_UART_DEFAULT=y +#CONFIG_ESP_CONSOLE_SECONDARY_NONE=y + +# Disable USB-Serial-JTAG clocking in sleep mode +CONFIG_ESP_PHY_ENABLE_USB_CONTROLLER=n + +# Minimize power stabilization time (Brownout) +CONFIG_ESP_BROWNOUT_DET=n +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y + +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_40=y + +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +## CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +CONFIG_PM_ENABLE=y +# CONFIG_PM_DFS_INIT_FREQ_MAX_40=y + +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_FREERTOS_IDLE_TASK_REPLICATION=n +CONFIG_ESP_CONSOLE_UART=n \ No newline at end of file diff --git a/tests/readme.md b/tests/readme.md new file mode 100644 index 0000000..33c7e1d --- /dev/null +++ b/tests/readme.md @@ -0,0 +1,129 @@ +# Running the STCC4 Test + +`test_stcc4_ulp.py` tests the ULP branch of the STCC4 driver on your computer. +No board, sensor, or ESP-IDF installation is required: GPIO, I2C, and delays are stubbed out. + +## Requirements + +- Python 3. No additional Python packages are required. +- A C compiler for your operating system with C11 support: GCC or Clang. + The ESP32 compiler (`riscv32-esp-elf-gcc`) is not suitable: the test executable must run on your computer. +- A complete copy of the project: the test uses `main/sensors/stcc4.c` and headers from `main`. + +The `--cc` option accepts a compiler name available in `PATH` or a path to the compiler. +It defaults to `gcc`. Specify only the executable, without additional flags. + +## Windows (PowerShell) + +You need Python and Clang with a linker and C runtime installed +(for example, Visual Studio Build Tools with the C++ components and Windows SDK), +or GCC from MinGW-w64. `cl.exe` and `clang-cl.exe` do not support the arguments used by this script. + +If Python and Clang are available in `PATH`, run from the project root: + +```powershell +python .\tests\test_stcc4_ulp.py --cc clang +``` + +If you are already in the `tests` directory: + +```powershell +python .\test_stcc4_ulp.py --cc clang +``` + +If Python Launcher is installed, you can use `py -3` instead of `python`. +For MinGW-w64, replace `--cc clang` with `--cc gcc`. + +Example using absolute paths from the current project environment, **from the `tests` directory**: + +```powershell +& 'G:\dev\esp32\tools\python\v6.1\venv\Scripts\python.exe' .\test_stcc4_ulp.py --cc 'H:\Program Files\LLVM\bin\clang.exe' +``` + +On another computer, replace the Python and compiler paths with your own. +The ESP-IDF virtual environment in this example is used only to provide a Python interpreter. + +## Linux + +You need Python 3 and GCC with the system C headers and libraries. +For example, on Debian/Ubuntu: + +```bash +sudo apt update +sudo apt install python3 build-essential +``` + +From the project root: + +```bash +python3 tests/test_stcc4_ulp.py --cc gcc +``` + +From the `tests` directory: + +```bash +python3 ./test_stcc4_ulp.py --cc gcc +``` + +If Clang is installed, you can use `--cc clang` instead. + +## macOS + +You need Python 3 and Apple's developer tools with Clang. +To install Command Line Tools if they are not already installed: + +```bash +xcode-select --install +``` + +Check that the tools are available: + +```bash +python3 --version +clang --version +``` + +From the project root: + +```bash +python3 tests/test_stcc4_ulp.py --cc clang +``` + +From the `tests` directory: + +```bash +python3 ./test_stcc4_ulp.py --cc clang +``` + +## Expected Result and Test Coverage + +On success, the script exits with code `0` and prints: + +```text +PASS: ULP STCC4 conversion, all CRC words, I2C errors, recovery, optional outputs +``` + +The test checks temperature, humidity, and CO2 conversion, CRC validation for all four words, +unchanged output values on errors, successful reads after an error, +optional output parameters, and positive transaction timeouts. + +This test checks driver logic using stubs. It does not test physical I2C signals, +power, the main processor's GPIO startup sequence, actual timeout durations, +or operation after deep sleep. + +The test creates and runs a program in a temporary directory, then automatically removes it. +The filename `test.exe` is used on every operating system; on Linux/macOS it is a native executable and does not require Wine. + +## Troubleshooting + +- **`can't open file .../tests/tests/test_stcc4_ulp.py`**: you are already in the `tests` directory. + Use `./test_stcc4_ulp.py` without the extra `tests/` prefix. +- **`python` / `python3` not found**: install Python or specify the full path to the interpreter. +- **Compiler not found (`FileNotFoundError`)**: check `PATH` or specify the full path using `--cc`. + Enclose paths containing spaces in quotes. +- **Linker error or missing `assert.h` / `string.h`**: install the headers, + C runtime, and linker for your compiler. On Windows, `clang.exe` alone may not be sufficient. +- **`Assertion failed` / `CalledProcessError`**: the test failed. Check the first compiler error + or failed `assert` above the traceback; no successful `PASS` message will be printed. +- **Execution blocked in the temporary directory**: select a temporary directory that permits execution + using `TMPDIR` (Linux/macOS) or `TEMP`/`TMP` (Windows) before starting Python. diff --git a/tests/test_esp_main_sleep.py b/tests/test_esp_main_sleep.py new file mode 100644 index 0000000..c4cdbae --- /dev/null +++ b/tests/test_esp_main_sleep.py @@ -0,0 +1,90 @@ +"""Host test for sleep deadlines and rejected sleep; requires a C++ compiler.""" +import argparse +from pathlib import Path +import subprocess +import tempfile + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--cc", default="g++") + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + with tempfile.TemporaryDirectory() as tmp: + folder = Path(tmp) + for name in ("aether/all.h", "user_config.h", "esp_timer.h", "hal/uart_types.h", "soc/gpio_num.h"): + path = folder / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("") + (folder / "esp_log.h").write_text( + '#define ESP_LOGI(...) ((void)0)\n#define ESP_LOGE(...) ((void)0)\n' + ) + (folder / "esp_sleep.h").write_text( + '#include \nusing esp_err_t = int;\n#define ESP_OK 0\n' + 'int esp_sleep_enable_timer_wakeup(std::uint64_t);\n' + 'int esp_deep_sleep_try_to_start();\n' + ) + (folder / "esp_system.h").write_text('[[noreturn]] void esp_restart();\n') + (folder / "sensors").mkdir() + (folder / "sensors/sensors.h").write_text( + '#define BOARD_HAS_STCC4 1\n#define BOARD_HAS_PWR_ON 1\n' + '#define BOARD_HAS_ULP 0\nvoid PowerOffSensors();\n' + ) + source = folder / "test.cpp" + source.write_text('#include "' + (root / 'main/sleeping/esp_main_sleep.cpp').as_posix() + '"\n' + r''' +#include +struct Restart {}; +struct Sleeping {}; +static std::uint64_t timer; +static int timer_error, sleep_error, sleep_calls; +static bool powered_off; +static bool peripherals_disabled; +void peripherals_off(bool keep_sensors) { + assert(!keep_sensors); + peripherals_disabled = true; +} +void PowerOffSensors() { powered_off = true; } +int esp_sleep_enable_timer_wakeup(std::uint64_t us) { timer = us; return timer_error; } +int esp_deep_sleep_try_to_start() { + assert(powered_off); + assert(peripherals_disabled); + ++sleep_calls; + if (sleep_error) return sleep_error; + throw Sleeping{}; // Successful deep sleep never returns. +} +[[noreturn]] void esp_restart() { throw Restart{}; } +int main() { + using namespace std::chrono; + for (auto offset : {seconds{-300}, seconds{0}, seconds{30}}) { + powered_off = false; + peripherals_disabled = false; + auto deadline = system_clock::now() + offset; + try { DeepSleep(deadline, deadline, 0); assert(false); } catch (Sleeping&) {} + if (offset <= seconds{0}) assert(timer == 1000000); + else assert(timer > 1000000 && timer <= 30000000); + } + sleep_error = 0x103; + try { DeepSleep(system_clock::now(), {}, 0); assert(false); } catch (Restart&) {} + assert(timer == 1000000); + timer_error = 0x102; + int before = sleep_calls; + try { DeepSleep(system_clock::now(), {}, 0); assert(false); } catch (Restart&) {} + assert(sleep_calls == before); +} +''') + exe = folder / "test.exe" + if Path(args.cc).name.lower() in ("cl", "cl.exe"): + command = [args.cc, "/nologo", "/EHsc", "/std:c++17", "/DESP_MAIN_SLEEP=1", + "/I" + str(folder), "/I" + str(root / "main"), + str(source), "/Fe:" + str(exe)] + else: + command = [args.cc, "-std=c++17", "-DESP_MAIN_SLEEP=1", + "-I" + str(folder), "-I" + str(root / "main"), + str(source), "-o", str(exe)] + subprocess.run(command, cwd=folder, check=True) + subprocess.run([str(exe)], check=True) + print("PASS: overdue/future sleep deadlines, sleep rejection, timer failure") + + +if __name__ == "__main__": + main() diff --git a/tests/test_stcc4_ulp.py b/tests/test_stcc4_ulp.py new file mode 100644 index 0000000..df9dcca --- /dev/null +++ b/tests/test_stcc4_ulp.py @@ -0,0 +1,105 @@ +"""Host regression test for the ULP STCC4 driver; run with --cc .""" +import argparse +from pathlib import Path +import subprocess +import tempfile + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--cc", default="gcc") + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + with tempfile.TemporaryDirectory() as tmp: + folder = Path(tmp) + (folder / "esp_err.h").write_text( + "#pragma once\ntypedef int esp_err_t;\n" + "#define ESP_OK 0\n#define ESP_FAIL -1\n" + "#define ESP_ERR_INVALID_CRC 0x109\n" + ) + (folder / "esp_log.h").write_text("") + (folder / "ulp_lp_core_i2c.h").write_text( + "#pragma once\ntypedef int i2c_port_t;\n#define LP_I2C_NUM_0 0\n" + ) + (folder / "ulp_lp_core_gpio.h").write_text("") + harness = '#include "' + (root / "main/sensors/stcc4.c").as_posix() + '"\n' + harness += r''' +#include +#include +static uint8_t frame[12]; +static int fail_stage; +esp_err_t i2c_init(int bus, int port, int sda, int scl, int speed) { return 0; } +esp_err_t i2c_write(int port, uint8_t addr, const uint8_t *data, + uint8_t len, int32_t timeout) { + assert(addr == 0x64); + assert(timeout > 0); // A missing sensor must never block indefinitely. + if (len == 1) { assert(data[0] == 0); return -1; } // Wake NACK is normal. + assert(len == 2); + int cmd = (data[0] << 8) | data[1]; + assert(cmd == 0x219d || cmd == 0xec05); + return stcc4_stage == (unsigned)fail_stage ? 0x107 : 0; +} +esp_err_t i2c_read(int port, uint8_t addr, uint8_t *data, + uint8_t len, int32_t timeout) { + assert(len == sizeof(frame)); + assert(timeout > 0); + memcpy(data, frame, len); + return fail_stage == 4 ? 0x107 : 0; +} +void wait_for(int32_t us) {} +static void make_frame(void) { + const uint16_t values[] = {800, 26214, 29360, 0}; + for (int i = 0; i < 4; ++i) { + frame[i * 3] = values[i] >> 8; + frame[i * 3 + 1] = values[i]; + unsigned crc = 0xff; + for (int j = 0; j < 2; ++j) { + crc ^= frame[i * 3 + j]; + for (int b = 0; b < 8; ++b) + crc = ((crc << 1) ^ ((crc & 0x80) ? 0x31 : 0)) & 0xff; + } + frame[i * 3 + 2] = crc; + } +} +int main(void) { + int16_t t = -123; + uint32_t h = 123, co2 = 123; + make_frame(); + ReadSensors(&t, &h, 0, &co2, 0); + assert(stcc4_error == 0 && stcc4_stage == 0); + assert(t == 2500 && h >= 4999 && h <= 5000 && co2 == 800); + // A bad CRC in any word must leave ALL outputs untouched, including CO2. + for (int i = 0; i < 4; ++i) { + make_frame(); frame[i * 3 + 2] ^= 1; + t = -123; h = co2 = 123; + ReadSensors(&t, &h, 0, &co2, 0); + assert(stcc4_error == ESP_ERR_INVALID_CRC && stcc4_stage == 5); + assert(t == -123 && h == 123 && co2 == 123); + } + make_frame(); + for (fail_stage = 2; fail_stage <= 4; ++fail_stage) { + ReadSensors(&t, &h, 0, &co2, 0); + assert(stcc4_error == 0x107 && stcc4_stage == (unsigned)fail_stage); + assert(t == -123 && h == 123 && co2 == 123); + } + fail_stage = 0; + ReadSensors(&t, 0, 0, 0, 0); + assert(t == 2500 && stcc4_error == 0); +} +''' + source = folder / "test.c" + source.write_text(harness) + exe = folder / "test.exe" + defines = ["USER_CONFIG_H_", "ESP_PLATFORM=1", "ULP_COMP=1", + "BOARD_HAS_ULP=1", "BOARD_HAS_STCC4=1", "BOARD_HAS_PWR_ON=0", + "SENSOR_SDA_PIN=6", "SENSOR_SCL_PIN=7"] + subprocess.run([args.cc, "-std=c11", "-I" + str(folder), + "-I" + str(root / "main"), + *["-D" + d for d in defines], str(source), "-o", str(exe)], + check=True) + subprocess.run([str(exe)], check=True) + print("PASS: ULP STCC4 conversion, all CRC words, I2C errors, recovery, optional outputs") + + +if __name__ == "__main__": + main() diff --git a/ulp/CMakeLists.txt b/ulp/CMakeLists.txt index f716ed9..46c3a14 100644 --- a/ulp/CMakeLists.txt +++ b/ulp/CMakeLists.txt @@ -16,6 +16,17 @@ cmake_minimum_required(VERSION 3.16) set(ULP_APP_NAME ulp_main) +# ESP-IDF 6.x no longer makes all of its CMake helpers available to custom +# ULP projects implicitly. This defines check_expected_tool_version() and the +# other helpers used by IDFULPProject.cmake. IDF_PATH is explicitly passed to +# this ExternalProject, so do not let a stale host environment select another +# ESP-IDF installation while configuring it. +set(_ulp_idf_path "${IDF_PATH}") +list(GET _ulp_idf_path 0 _ulp_idf_path) +set(IDF_PATH "${_ulp_idf_path}") +set(ENV{IDF_PATH} "${_ulp_idf_path}") +include(${_ulp_idf_path}/tools/cmake/idf.cmake) + project(${ULP_APP_NAME}) list(APPEND main_src @@ -51,24 +62,14 @@ ulp_add_build_binary_targets(${ULP_APP_NAME}) target_include_directories(${ULP_APP_NAME} PUBLIC "${CMAKE_CURRENT_LIST_DIR}/../main") -# Calculate where the build folder of the main project is located -# ExternalProject is usually built inside build/ulp_main-prefix/src/ulp_main-build -# We need to go up to the root of the build folder -set(MAIN_BUILD_DIR "${CMAKE_BINARY_DIR}/../../..") -set(ULP_BOARD_FILE "${MAIN_BUILD_DIR}/ulp_board_config.cmake") - -# If the configuration file exists, connect it -if(EXISTS "${ULP_BOARD_FILE}") - include("${ULP_BOARD_FILE}") -endif() +include(../cmake/CPM.cmake) +CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git#157aadbec8e7b852d0f89274307ff7cb8103e5f7") -# Checking if the BOARD variable value appears after including if(NOT "${BOARD}" STREQUAL "") message(STATUS "--------------------------------------------------") - message(STATUS "ULP Project SUCCESS! Board loaded from file: ${BOARD}") + message(STATUS "ULP Project SUCCESS! Board: ${BOARD}") message(STATUS "--------------------------------------------------") - - # Checking if the BOARD variable has been included + add_compile_definitions(BOARD=${BOARD}) else() message(WARNING "ULP Project: BOARD is not defined!") diff --git a/ulp/main.c b/ulp/main.c index fc82684..7da2c60 100644 --- a/ulp/main.c +++ b/ulp/main.c @@ -26,13 +26,16 @@ # include "ulp_lp_core_utils.h" # include "sensors/sensors.h" +# if BOARD_HAS_STCC4 == 1 +extern volatile uint32_t stcc4_error; +# endif # define nullptr ((void*)0) // Variables in RTC memory (accessible from main.c) -volatile int16_t wakeup_temp_threshold; // Threshold: XX.XX°C +volatile int32_t wakeup_temp_threshold; // Threshold: XX.XX°C volatile uint32_t wakeup_co2_threshold; // Threshold: XXX ppm -volatile uint16_t wakeup_gas_threshold; // Threshold: Gas +volatile uint32_t wakeup_gas_threshold; // Threshold: Gas // Variables to store latest values int16_t temperature; @@ -42,15 +45,26 @@ uint32_t co2; uint32_t gas_resistance; volatile uint32_t can_start = 0; -// Local variables -static bool should_wakeup = false; +volatile uint32_t sample_count = 0; +volatile uint32_t sample_in_progress = 0; +volatile uint32_t wakeup_enabled = 0; int main(void) { while (can_start == 0) { asm("nop"); } // Waiting main CPU + sample_in_progress = 1; ReadSensors(&temperature, &humidity, &pressure, &co2, &gas_resistance); + asm volatile("fence rw, rw" ::: "memory"); + ++sample_count; + sample_in_progress = 0; +# if BOARD_HAS_STCC4 == 1 + if (stcc4_error != 0) { + return 0; + } +# endif + bool should_wakeup = false; if (temperature > wakeup_temp_threshold) { should_wakeup = true; } @@ -61,7 +75,7 @@ int main(void) { should_wakeup = true; } - if (should_wakeup) { + if (wakeup_enabled && should_wakeup) { ulp_lp_core_wakeup_main_processor(); }