From b6e0b416712f5be48b521fcc0304f6ad82df588f Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 14:56:27 -0600 Subject: [PATCH 01/22] Add OTA BLE service and device settings support Introduces OTA (Over-the-Air) firmware update BLE service with new UUIDs and implementation files. Adds device settings management (header and source), updates build system for ESP-IDF/Arduino hybrid, and includes new partition and configuration files for dual OTA support. Also updates BMS CAN initialization and various build flags for improved compatibility. --- CMakeLists.txt | 3 + inc/sp140/ble/ble_ids.h | 7 + inc/sp140/ble/ota_service.h | 18 + inc/sp140/bms.h | 1 + inc/sp140/device_settings.h | 17 + partitions.csv | 6 + platformio.ini | 7 +- sdkconfig.OpenPPG-CESP32S3-CAN-SP140 | 2018 +++++++++++++++++ sdkconfig.defaults | 6 + src/CMakeLists.txt | 5 + src/sp140/CMakeLists.txt | 6 + src/sp140/ble/ble_core.cpp | 3 + src/sp140/ble/ota_service.cpp | 150 ++ src/sp140/buzzer.cpp | 4 +- .../{extra-data.ino => device_settings.cpp} | 5 + src/sp140/esc.cpp | 4 +- src/sp140/lvgl/lvgl_updates.cpp | 6 +- src/sp140/{sp140.ino => main.cpp} | 19 +- 18 files changed, 2275 insertions(+), 10 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 inc/sp140/ble/ota_service.h create mode 100644 inc/sp140/device_settings.h create mode 100644 partitions.csv create mode 100644 sdkconfig.OpenPPG-CESP32S3-CAN-SP140 create mode 100644 sdkconfig.defaults create mode 100644 src/CMakeLists.txt create mode 100644 src/sp140/CMakeLists.txt create mode 100644 src/sp140/ble/ota_service.cpp rename src/sp140/{extra-data.ino => device_settings.cpp} (98%) rename src/sp140/{sp140.ino => main.cpp} (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..4cfeb783 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16.0) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(openppg_controller) diff --git a/inc/sp140/ble/ble_ids.h b/inc/sp140/ble/ble_ids.h index 6d22eab8..bdb33577 100644 --- a/inc/sp140/ble/ble_ids.h +++ b/inc/sp140/ble/ble_ids.h @@ -67,4 +67,11 @@ #define CONTROLLER_SERVICE_UUID "01C63B60-0891-4655-BBCA-8E745C48A175" #define CONTROLLER_TELEMETRY_UUID "01C63B61-0891-4655-BBCA-8E745C48A176" +// ============================================================================ +// OTA Service (Firmware Update) +// ============================================================================ +#define OTA_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" +#define OTA_CONTROL_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" +#define OTA_DATA_UUID "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" + #endif // INC_SP140_BLE_BLE_IDS_H_ diff --git a/inc/sp140/ble/ota_service.h b/inc/sp140/ble/ota_service.h new file mode 100644 index 00000000..d538f694 --- /dev/null +++ b/inc/sp140/ble/ota_service.h @@ -0,0 +1,18 @@ +#ifndef INC_SP140_BLE_OTA_SERVICE_H_ +#define INC_SP140_BLE_OTA_SERVICE_H_ + +#include + +/** + * Initialize the OTA BLE service. + * @param pServer Pointer to the NimBLEServer instance. + */ +void initOtaBleService(NimBLEServer* pServer); + +/** + * Check if an OTA update is currently in progress. + * @return true if OTA is active (should pause other tasks) + */ +bool isOtaInProgress(); + +#endif // INC_SP140_BLE_OTA_SERVICE_H_ diff --git a/inc/sp140/bms.h b/inc/sp140/bms.h index fcde6e9a..aa0fea57 100644 --- a/inc/sp140/bms.h +++ b/inc/sp140/bms.h @@ -15,5 +15,6 @@ extern STR_BMS_TELEMETRY_140 bmsTelemetryData; extern BMS_CAN* bms_can; // BMS functions +bool initBMSCAN(SPIClass* spi); void updateBMSData(); void printBMSData(); diff --git a/inc/sp140/device_settings.h b/inc/sp140/device_settings.h new file mode 100644 index 00000000..4fc0acc6 --- /dev/null +++ b/inc/sp140/device_settings.h @@ -0,0 +1,17 @@ +#ifndef INC_SP140_DEVICE_SETTINGS_H_ +#define INC_SP140_DEVICE_SETTINGS_H_ + +#include "structs.h" +#include "esp32s3-config.h" + +// Constants +extern const unsigned int DEFAULT_SCREEN_ROTATION; +void refreshDeviceData(); +void writeDeviceData(); +void resetDeviceData(); +void parse_serial_commands(); +void send_device_data(); +bool sanitizeDeviceData(); +void debugHardwareConfig(const HardwareConfig& config); + +#endif // INC_SP140_DEVICE_SETTINGS_H_ diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 00000000..778a346c --- /dev/null +++ b/partitions.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, , 0x4000, +otadata, data, ota, , 0x2000, +app0, app, ota_0, , 3M, +app1, app, ota_1, , 3M, +spiffs, data, spiffs, , 1500K, diff --git a/platformio.ini b/platformio.ini index 7ed8fb27..2abb15d0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,8 +23,9 @@ lib_ignore = [env:OpenPPG-CESP32S3-CAN-SP140] platform = espressif32@6.12.0 board = m5stack-stamps3 -framework = arduino -src_folder = sp140 +framework = arduino, espidf +board_build.partitions = partitions.csv +src_folder = sp140 ; Commented out as we are using standard src structure ; If esptool.py complains about missing intelhex, install into the tool package: ; ~/.platformio/penv/bin/python -m pip install intelhex -t ~/.platformio/packages/tool-esptoolpy extra_scripts = @@ -38,6 +39,8 @@ build_flags = -D LV_LVGL_H_INCLUDE_SIMPLE ; Disable legacy individual BLE characteristics (use packed binary only) -D DISABLE_LEGACY_BLE_TELEMETRY + -D CONFIG_ARDUINO_LOOP_STACK_SIZE=8192 + -Wno-error=format build_type = debug debug_speed = 12000 diff --git a/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 new file mode 100644 index 00000000..16e31989 --- /dev/null +++ b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 @@ -0,0 +1,2018 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +# +# SDK tool configuration +# +CONFIG_SDK_TOOLPREFIX="xtensa-esp32s3-elf-" +# CONFIG_SDK_TOOLCHAIN_SUPPORTS_TIME_WIDE_64_BITS is not set +# end of SDK tool configuration + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# end of Build type + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 +# end of Application manager + +# +# Bootloader config +# +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Serial Flash Configurations +# +# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_SUPPORTS_RSA=y +CONFIG_SECURE_TARGET_HAS_SECURE_ROM_DL_MODE=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +# end of Security features + +# +# Boot ROM Behavior +# +CONFIG_BOOT_ROM_LOG_ALWAYS_ON=y +# CONFIG_BOOT_ROM_LOG_ALWAYS_OFF is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW is not set +# end of Boot ROM Behavior + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_OCT_FLASH is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_S3_STR=y +# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +# CONFIG_ESPTOOLPY_MONITOR_BAUD_CONSOLE is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B is not set +CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y +# CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER is not set +CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Arduino Configuration +# +CONFIG_ARDUINO_VARIANT="esp32s3" +CONFIG_ENABLE_ARDUINO_DEPENDS=y +CONFIG_AUTOSTART_ARDUINO=y +# CONFIG_ARDUINO_RUN_CORE0 is not set +CONFIG_ARDUINO_RUN_CORE1=y +# CONFIG_ARDUINO_RUN_NO_AFFINITY is not set +CONFIG_ARDUINO_RUNNING_CORE=1 +CONFIG_ARDUINO_LOOP_STACK_SIZE=8192 +# CONFIG_ARDUINO_EVENT_RUN_CORE0 is not set +CONFIG_ARDUINO_EVENT_RUN_CORE1=y +# CONFIG_ARDUINO_EVENT_RUN_NO_AFFINITY is not set +CONFIG_ARDUINO_EVENT_RUNNING_CORE=1 +# CONFIG_ARDUINO_SERIAL_EVENT_RUN_CORE0 is not set +# CONFIG_ARDUINO_SERIAL_EVENT_RUN_CORE1 is not set +CONFIG_ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY=y +CONFIG_ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE=-1 +CONFIG_ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE=2048 +CONFIG_ARDUINO_SERIAL_EVENT_TASK_PRIORITY=24 +CONFIG_ARDUINO_UDP_RUN_CORE0=y +# CONFIG_ARDUINO_UDP_RUN_CORE1 is not set +# CONFIG_ARDUINO_UDP_RUN_NO_AFFINITY is not set +CONFIG_ARDUINO_UDP_RUNNING_CORE=0 +CONFIG_ARDUINO_UDP_TASK_PRIORITY=3 +# CONFIG_ARDUINO_ISR_IRAM is not set +# CONFIG_DISABLE_HAL_LOCKS is not set + +# +# Debug Log Configuration +# +# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_NONE is not set +CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_ERROR=y +# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_WARN is not set +# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO is not set +# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL=1 +# CONFIG_ARDUHAL_LOG_COLORS is not set +# CONFIG_ARDUHAL_ESP_LOG is not set +# end of Debug Log Configuration + +CONFIG_ARDUHAL_PARTITION_SCHEME_DEFAULT=y +# CONFIG_ARDUHAL_PARTITION_SCHEME_MINIMAL is not set +# CONFIG_ARDUHAL_PARTITION_SCHEME_NO_OTA is not set +# CONFIG_ARDUHAL_PARTITION_SCHEME_HUGE_APP is not set +# CONFIG_ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS is not set +CONFIG_ARDUHAL_PARTITION_SCHEME="default" +# CONFIG_AUTOCONNECT_WIFI is not set +# CONFIG_ARDUINO_SELECTIVE_COMPILATION is not set +# end of Arduino Configuration + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +# CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +# end of Compiler options + +# +# Component config +# + +# +# Application Level Tracing +# +# CONFIG_APPTRACE_DEST_JTAG is not set +CONFIG_APPTRACE_DEST_NONE=y +CONFIG_APPTRACE_LOCK_ENABLE=y +# end of Application Level Tracing + +# +# ESP-ASIO +# +# CONFIG_ASIO_SSL_SUPPORT is not set +# end of ESP-ASIO + +# +# Bluetooth +# +CONFIG_BT_ENABLED=y +CONFIG_BT_SOC_SUPPORT_5_0=y + +# +# Bluetooth controller +# +CONFIG_BT_CTRL_MODE_EFF=1 +CONFIG_BT_CTRL_BLE_MAX_ACT=6 +CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6 +CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0 +CONFIG_BT_CTRL_PINNED_TO_CORE_0=y +# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set +CONFIG_BT_CTRL_PINNED_TO_CORE=0 +CONFIG_BT_CTRL_HCI_MODE_VHCI=y +# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set +CONFIG_BT_CTRL_HCI_TL=1 +CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30 +CONFIG_BT_BLE_CCA_MODE_NONE=y +# CONFIG_BT_BLE_CCA_MODE_HW is not set +# CONFIG_BT_BLE_CCA_MODE_SW is not set +CONFIG_BT_BLE_CCA_MODE=0 +CONFIG_BT_CTRL_HW_CCA_VAL=20 +CONFIG_BT_CTRL_HW_CCA_EFF=0 +CONFIG_BT_CTRL_CE_LENGTH_TYPE_ORIG=y +# CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE is not set +# CONFIG_BT_CTRL_CE_LENGTH_TYPE_SD is not set +CONFIG_BT_CTRL_CE_LENGTH_TYPE_EFF=0 +CONFIG_BT_CTRL_TX_ANTENNA_INDEX_0=y +# CONFIG_BT_CTRL_TX_ANTENNA_INDEX_1 is not set +CONFIG_BT_CTRL_TX_ANTENNA_INDEX_EFF=0 +CONFIG_BT_CTRL_RX_ANTENNA_INDEX_0=y +# CONFIG_BT_CTRL_RX_ANTENNA_INDEX_1 is not set +CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0 +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N24 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N21 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N18 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N15 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N12 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N9 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N6 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N3 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set +CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P21 is not set +CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=11 +CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y +CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100 +CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 +CONFIG_BT_CTRL_BLE_SCAN_DUPL=y +CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE=y +# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA is not set +# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE is not set +CONFIG_BT_CTRL_SCAN_DUPL_TYPE=0 +CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100 +CONFIG_BT_CTRL_DUPL_SCAN_CACHE_REFRESH_PERIOD=0 +# CONFIG_BT_CTRL_BLE_MESH_SCAN_DUPL_EN is not set +# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set +CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y +CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0 + +# +# MODEM SLEEP Options +# +# CONFIG_BT_CTRL_MODEM_SLEEP is not set +# end of MODEM SLEEP Options + +CONFIG_BT_CTRL_SLEEP_MODE_EFF=0 +CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0 +CONFIG_BT_CTRL_HCI_TL_EFF=1 +# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set +# CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set +# CONFIG_BT_BLE_ADV_DATA_LENGTH_ZERO_AUX is not set +# end of Bluetooth controller + +CONFIG_BT_BLUEDROID_ENABLED=y +# CONFIG_BT_NIMBLE_ENABLED is not set +# CONFIG_BT_CONTROLLER_ONLY is not set + +# +# Bluedroid Options +# +CONFIG_BT_BTC_TASK_STACK_SIZE=3072 +CONFIG_BT_BLUEDROID_PINNED_TO_CORE_0=y +# CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1 is not set +CONFIG_BT_BLUEDROID_PINNED_TO_CORE=0 +CONFIG_BT_BTU_TASK_STACK_SIZE=4352 +# CONFIG_BT_BLUEDROID_MEM_DEBUG is not set +CONFIG_BT_BLE_ENABLED=y +CONFIG_BT_GATTS_ENABLE=y +# CONFIG_BT_GATTS_PPCP_CHAR_GAP is not set +# CONFIG_BT_BLE_BLUFI_ENABLE is not set +CONFIG_BT_GATT_MAX_SR_PROFILES=8 +CONFIG_BT_GATT_MAX_SR_ATTRIBUTES=100 +# CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set +CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_AUTO=y +CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE=0 +# CONFIG_BT_GATTS_ROBUST_CACHING_ENABLED is not set +# CONFIG_BT_GATTS_DEVICE_NAME_WRITABLE is not set +# CONFIG_BT_GATTS_APPEARANCE_WRITABLE is not set +CONFIG_BT_GATTC_ENABLE=y +CONFIG_BT_GATTC_MAX_CACHE_CHAR=40 +CONFIG_BT_GATTC_NOTIF_REG_MAX=5 +# CONFIG_BT_GATTC_CACHE_NVS_FLASH is not set +CONFIG_BT_GATTC_CONNECT_RETRY_COUNT=3 +CONFIG_BT_BLE_SMP_ENABLE=y +# CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set +# CONFIG_BT_STACK_NO_LOG is not set + +# +# BT DEBUG LOG LEVEL +# +# CONFIG_BT_LOG_HCI_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_HCI_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_HCI_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_HCI_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_HCI_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_HCI_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_HCI_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_HCI_TRACE_LEVEL=2 +# CONFIG_BT_LOG_BTM_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_BTM_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_BTM_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_BTM_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_BTM_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_BTM_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_BTM_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_BTM_TRACE_LEVEL=2 +# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_L2CAP_TRACE_LEVEL=2 +# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL=2 +# CONFIG_BT_LOG_SDP_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_SDP_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_SDP_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_SDP_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_SDP_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_SDP_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_SDP_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_SDP_TRACE_LEVEL=2 +# CONFIG_BT_LOG_GAP_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_GAP_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_GAP_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_GAP_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_GAP_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_GAP_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_GAP_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_GAP_TRACE_LEVEL=2 +# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_BNEP_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_BNEP_TRACE_LEVEL=2 +# CONFIG_BT_LOG_PAN_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_PAN_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_PAN_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_PAN_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_PAN_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_PAN_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_PAN_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_PAN_TRACE_LEVEL=2 +# CONFIG_BT_LOG_A2D_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_A2D_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_A2D_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_A2D_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_A2D_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_A2D_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_A2D_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_A2D_TRACE_LEVEL=2 +# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_AVDT_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_AVDT_TRACE_LEVEL=2 +# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_AVCT_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_AVCT_TRACE_LEVEL=2 +# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_AVRC_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_AVRC_TRACE_LEVEL=2 +# CONFIG_BT_LOG_MCA_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_MCA_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_MCA_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_MCA_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_MCA_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_MCA_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_MCA_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_MCA_TRACE_LEVEL=2 +# CONFIG_BT_LOG_HID_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_HID_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_HID_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_HID_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_HID_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_HID_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_HID_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_HID_TRACE_LEVEL=2 +# CONFIG_BT_LOG_APPL_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_APPL_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_APPL_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_APPL_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_APPL_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_APPL_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_APPL_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_APPL_TRACE_LEVEL=2 +# CONFIG_BT_LOG_GATT_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_GATT_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_GATT_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_GATT_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_GATT_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_GATT_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_GATT_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_GATT_TRACE_LEVEL=2 +# CONFIG_BT_LOG_SMP_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_SMP_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_SMP_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_SMP_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_SMP_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_SMP_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_SMP_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_SMP_TRACE_LEVEL=2 +# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_BTIF_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_BTIF_TRACE_LEVEL=2 +# CONFIG_BT_LOG_BTC_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_BTC_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_BTC_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_BTC_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_BTC_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_BTC_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_BTC_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_BTC_TRACE_LEVEL=2 +# CONFIG_BT_LOG_OSI_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_OSI_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_OSI_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_OSI_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_OSI_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_OSI_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_OSI_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_OSI_TRACE_LEVEL=2 +# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_NONE is not set +# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_ERROR is not set +CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_WARNING=y +# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_API is not set +# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_EVENT is not set +# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_DEBUG is not set +# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_VERBOSE is not set +CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2 +# end of BT DEBUG LOG LEVEL + +CONFIG_BT_ACL_CONNECTIONS=4 +CONFIG_BT_MULTI_CONNECTION_ENBALE=y +# CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST is not set +# CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY is not set +# CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK is not set +CONFIG_BT_SMP_ENABLE=y +CONFIG_BT_SMP_MAX_BONDS=15 +# CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN is not set +CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT=30 +CONFIG_BT_MAX_DEVICE_NAME_LEN=32 +CONFIG_BT_BLE_RPA_TIMEOUT=900 +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y +# CONFIG_BT_BLE_42_FEATURES_SUPPORTED is not set +# CONFIG_BT_BLE_HIGH_DUTY_ADV_INTERVAL is not set +# end of Bluedroid Options +# end of Bluetooth + +# CONFIG_BLE_MESH is not set + +# +# CoAP Configuration +# +CONFIG_COAP_MBEDTLS_PSK=y +# CONFIG_COAP_MBEDTLS_PKI is not set +# CONFIG_COAP_MBEDTLS_DEBUG is not set +CONFIG_COAP_LOG_DEFAULT_LEVEL=0 +# end of CoAP Configuration + +# +# Driver configurations +# + +# +# ADC configuration +# +# CONFIG_ADC_FORCE_XPD_FSM is not set +CONFIG_ADC_DISABLE_DAC=y +# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set +# end of ADC configuration + +# +# MCPWM configuration +# +# CONFIG_MCPWM_ISR_IN_IRAM is not set +# end of MCPWM configuration + +# +# SPI configuration +# +# CONFIG_SPI_MASTER_IN_IRAM is not set +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of SPI configuration + +# +# TWAI configuration +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM is not set +# end of TWAI configuration + +# +# UART configuration +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of UART configuration + +# +# GDMA Configuration +# +# CONFIG_GDMA_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GDMA_ISR_IRAM_SAFE is not set +# end of GDMA Configuration +# end of Driver configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +CONFIG_EFUSE_MAX_BLK_LEN=256 +# end of eFuse Bit Manager + +# +# ESP-TLS +# +CONFIG_ESP_TLS_USING_MBEDTLS=y +CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +# CONFIG_ESP_TLS_INSECURE is not set +# end of ESP-TLS + +# +# ESP32S3-Specific +# +CONFIG_ESP32S3_REV_MIN_0=y +# CONFIG_ESP32S3_REV_MIN_1 is not set +# CONFIG_ESP32S3_REV_MIN_2 is not set +CONFIG_ESP32S3_REV_MIN_FULL=0 +CONFIG_ESP_REV_MIN_FULL=0 +CONFIG_ESP32S3_REV_MAX_FULL_STR_OPT=y +CONFIG_ESP32S3_REV_MAX_FULL=99 +CONFIG_ESP_REV_MAX_FULL=99 +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160=y +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=160 + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB=y +# CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x4000 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_4WAYS is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_16B is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_WRAP is not set +# CONFIG_ESP32S3_DATA_CACHE_16KB is not set +CONFIG_ESP32S3_DATA_CACHE_32KB=y +# CONFIG_ESP32S3_DATA_CACHE_64KB is not set +CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 +# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_DATA_CACHE_LINE_16B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y +# CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32 +# CONFIG_ESP32S3_DATA_CACHE_WRAP is not set +# end of Cache config + +# CONFIG_ESP32S3_SPIRAM_SUPPORT is not set +# CONFIG_ESP32S3_TRAX is not set +CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0 +# CONFIG_ESP32S3_ULP_COPROC_ENABLED is not set +CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM=0 +CONFIG_ESP32S3_DEBUG_OCDAWARE=y +CONFIG_ESP32S3_BROWNOUT_DET=y +CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set +CONFIG_ESP32S3_BROWNOUT_DET_LVL=7 +CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024 +CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000 +# CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM is not set +# CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE is not set +# end of ESP32S3-Specific + +# +# ADC-Calibration +# +# end of ADC-Calibration + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# Ethernet +# +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_SPI_ETHERNET=y +# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set +# CONFIG_ETH_SPI_ETHERNET_W5500 is not set +# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set +# CONFIG_ETH_USE_OPENETH is not set +# end of Ethernet + +# +# Event Loop Library +# +# CONFIG_ESP_EVENT_LOOP_PROFILING is not set +CONFIG_ESP_EVENT_POST_FROM_ISR=y +CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y +# end of Event Loop Library + +# +# GDB Stub +# +# end of GDB Stub + +# +# ESP HTTP client +# +CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y +# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set +CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y +# end of ESP HTTP client + +# +# HTTP Server +# +CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_URI_LEN=512 +CONFIG_HTTPD_ERR_RESP_NO_DELAY=y +CONFIG_HTTPD_PURGE_BUF_LEN=32 +# CONFIG_HTTPD_LOG_PURGE_DATA is not set +# CONFIG_HTTPD_WS_SUPPORT is not set +# end of HTTP Server + +# +# ESP HTTPS OTA +# +# CONFIG_OTA_ALLOW_HTTP is not set +# end of ESP HTTPS OTA + +# +# ESP HTTPS server +# +# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set +# end of ESP HTTPS server + +# +# Hardware Settings +# + +# +# MAC Config +# +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y +# CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_TWO is not set +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES=4 +# end of MAC Config + +# +# Sleep Config +# +# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set +CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y +CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND=y +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU=y +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLOCK_BBPLL_POWER_ON_WITH_USB=y +# end of RTC Clock Config +# end of Hardware Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_TASK_STACK_SIZE=1536 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_IPC_ISR_ENABLE=y +# end of IPC (Inter-Processor Call) + +# +# LCD and Touch Panel +# + +# +# LCD Touch Drivers are maintained in the IDF Component Registry +# + +# +# LCD Peripheral Configuration +# +CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE=32 +# CONFIG_LCD_RGB_ISR_IRAM_SAFE is not set +# end of LCD Peripheral Configuration +# end of LCD and Touch Panel + +# +# ESP NETIF Adapter +# +CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 +CONFIG_ESP_NETIF_TCPIP_LWIP=y +# CONFIG_ESP_NETIF_LOOPBACK is not set +CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=y +# end of ESP NETIF Adapter + +# +# PHY +# +CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y +# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set +CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP_PHY_MAX_TX_POWER=20 +# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set +CONFIG_ESP_PHY_ENABLE_USB=y +CONFIG_ESP_PHY_RF_CAL_PARTIAL=y +# CONFIG_ESP_PHY_RF_CAL_NONE is not set +# CONFIG_ESP_PHY_RF_CAL_FULL is not set +CONFIG_ESP_PHY_CALIBRATION_MODE=0 +# end of PHY + +# +# Power Management +# +# CONFIG_PM_ENABLE is not set +CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y +CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP=y +# end of Power Management + +# +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP System Settings +# +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set +# CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME is not set +CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y + +# +# Memory protection +# +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +# CONFIG_ESP_CONSOLE_USB_CDC is not set +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is not set +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_NONE is not set +# CONFIG_ESP_CONSOLE_SECONDARY_NONE is not set +CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_MULTIPLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y +CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y +# end of ESP System Settings + +# +# High resolution timer (esp_timer) +# +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_SYSTIMER=y +# end of High resolution timer (esp_timer) + +# +# Wi-Fi +# +CONFIG_ESP32_WIFI_ENABLED=y +CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +# CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y +# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set +CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0 +CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5 +# CONFIG_ESP32_WIFI_CSI_ENABLED is not set +CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP32_WIFI_TX_BA_WIN=6 +CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP32_WIFI_RX_BA_WIN=6 +CONFIG_ESP32_WIFI_NVS_ENABLED=y +CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y +# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set +CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 +CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 +CONFIG_ESP32_WIFI_IRAM_OPT=y +CONFIG_ESP32_WIFI_RX_IRAM_OPT=y +CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y +# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set +# CONFIG_ESP_WIFI_FTM_ENABLE is not set +# CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE is not set +# CONFIG_ESP_WIFI_GCMP_SUPPORT is not set +# CONFIG_ESP_WIFI_GMAC_SUPPORT is not set +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y +# CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set +CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 +# end of Wi-Fi + +# +# Core dump +# +# CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH is not set +# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set +CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y +# end of Core dump + +# +# FAT Filesystem support +# +# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set +CONFIG_FATFS_CODEPAGE_437=y +# CONFIG_FATFS_CODEPAGE_720 is not set +# CONFIG_FATFS_CODEPAGE_737 is not set +# CONFIG_FATFS_CODEPAGE_771 is not set +# CONFIG_FATFS_CODEPAGE_775 is not set +# CONFIG_FATFS_CODEPAGE_850 is not set +# CONFIG_FATFS_CODEPAGE_852 is not set +# CONFIG_FATFS_CODEPAGE_855 is not set +# CONFIG_FATFS_CODEPAGE_857 is not set +# CONFIG_FATFS_CODEPAGE_860 is not set +# CONFIG_FATFS_CODEPAGE_861 is not set +# CONFIG_FATFS_CODEPAGE_862 is not set +# CONFIG_FATFS_CODEPAGE_863 is not set +# CONFIG_FATFS_CODEPAGE_864 is not set +# CONFIG_FATFS_CODEPAGE_865 is not set +# CONFIG_FATFS_CODEPAGE_866 is not set +# CONFIG_FATFS_CODEPAGE_869 is not set +# CONFIG_FATFS_CODEPAGE_932 is not set +# CONFIG_FATFS_CODEPAGE_936 is not set +# CONFIG_FATFS_CODEPAGE_949 is not set +# CONFIG_FATFS_CODEPAGE_950 is not set +CONFIG_FATFS_CODEPAGE=437 +CONFIG_FATFS_LFN_NONE=y +# CONFIG_FATFS_LFN_HEAP is not set +# CONFIG_FATFS_LFN_STACK is not set +CONFIG_FATFS_FS_LOCK=0 +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE=y +# CONFIG_FATFS_USE_FASTSEEK is not set +# end of FAT Filesystem support + +# +# Modbus configuration +# +CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_PORT_MAX_CONN=5 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +CONFIG_FMB_COMM_MODE_RTU_EN=y +CONFIG_FMB_COMM_MODE_ASCII_EN=y +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_FMB_QUEUE_LENGTH=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_SERIAL_BUF_SIZE=256 +CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 +CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 +CONFIG_FMB_PORT_TASK_PRIO=10 +# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1 is not set +CONFIG_FMB_PORT_TASK_AFFINITY=0x0 +CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 +CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 +# CONFIG_FMB_TIMER_PORT_ENABLED is not set +# CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD is not set +# end of Modbus configuration + +# +# FreeRTOS +# +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y +CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y +# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set +CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y +# CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set +# CONFIG_FREERTOS_ASSERT_DISABLE is not set +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +# CONFIG_FREERTOS_LEGACY_HOOKS is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +# CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH is not set +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# end of Heap memory debugging + +# +# jsmn +# +# CONFIG_JSMN_PARENT_LINKS is not set +# CONFIG_JSMN_STRICT is not set +# end of jsmn + +# +# libsodium +# +# end of libsodium + +# +# Log output +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 +CONFIG_LOG_COLORS=y +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Log output + +# +# LWIP +# +CONFIG_LWIP_LOCAL_HOSTNAME="espressif" +# CONFIG_LWIP_NETIF_API is not set +CONFIG_LWIP_TCPIP_TASK_PRIO=18 +# CONFIG_LWIP_TCPIP_CORE_LOCKING is not set +# CONFIG_LWIP_CHECK_THREAD_SAFETY is not set +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +# CONFIG_LWIP_L2_TO_L3_COPY is not set +# CONFIG_LWIP_IRAM_OPTIMIZATION is not set +CONFIG_LWIP_TIMERS_ONDEMAND=y +CONFIG_LWIP_MAX_SOCKETS=10 +# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set +# CONFIG_LWIP_SO_LINGER is not set +CONFIG_LWIP_SO_REUSE=y +CONFIG_LWIP_SO_REUSE_RXTOALL=y +CONFIG_LWIP_SO_RCVBUF=y +# CONFIG_LWIP_NETBUF_RECVINFO is not set +CONFIG_LWIP_IP_DEFAULT_TTL=64 +CONFIG_LWIP_IP4_FRAG=y +CONFIG_LWIP_IP6_FRAG=y +# CONFIG_LWIP_IP4_REASSEMBLY is not set +# CONFIG_LWIP_IP6_REASSEMBLY is not set +# CONFIG_LWIP_IP_FORWARD is not set +# CONFIG_LWIP_STATS is not set +# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set +CONFIG_LWIP_ESP_GRATUITOUS_ARP=y +CONFIG_LWIP_GARP_TMR_INTERVAL=60 +CONFIG_LWIP_ESP_MLDV6_REPORT=y +CONFIG_LWIP_MLDV6_TMR_INTERVAL=40 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 +CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y +# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set +CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y +# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set +CONFIG_LWIP_DHCP_OPTIONS_LEN=68 +CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1 + +# +# DHCP server +# +CONFIG_LWIP_DHCPS=y +CONFIG_LWIP_DHCPS_LEASE_UNIT=60 +CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 +# end of DHCP server + +# CONFIG_LWIP_AUTOIP is not set +CONFIG_LWIP_IPV6=y +# CONFIG_LWIP_IPV6_AUTOCONFIG is not set +CONFIG_LWIP_IPV6_NUM_ADDRESSES=3 +# CONFIG_LWIP_IPV6_FORWARD is not set +# CONFIG_LWIP_NETIF_STATUS_CALLBACK is not set +CONFIG_LWIP_NETIF_LOOPBACK=y +CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# +CONFIG_LWIP_MAX_ACTIVE_TCP=16 +CONFIG_LWIP_MAX_LISTENING_TCP=16 +CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y +CONFIG_LWIP_TCP_MAXRTX=12 +CONFIG_LWIP_TCP_SYNMAXRTX=12 +CONFIG_LWIP_TCP_MSS=1440 +CONFIG_LWIP_TCP_TMR_INTERVAL=250 +CONFIG_LWIP_TCP_MSL=60000 +CONFIG_LWIP_TCP_FIN_WAIT_TIMEOUT=20000 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760 +CONFIG_LWIP_TCP_WND_DEFAULT=5760 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCP_QUEUE_OOSEQ=y +CONFIG_LWIP_TCP_OOSEQ_TIMEOUT=6 +CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4 +# CONFIG_LWIP_TCP_SACK_OUT is not set +# CONFIG_LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set +CONFIG_LWIP_TCP_OVERSIZE_MSS=y +# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set +CONFIG_LWIP_TCP_RTO_TIME=1500 +# end of TCP + +# +# UDP +# +CONFIG_LWIP_MAX_UDP_PCBS=16 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +# end of UDP + +# +# Checksums +# +# CONFIG_LWIP_CHECKSUM_CHECK_IP is not set +# CONFIG_LWIP_CHECKSUM_CHECK_UDP is not set +CONFIG_LWIP_CHECKSUM_CHECK_ICMP=y +# end of Checksums + +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set +# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set +CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF +# CONFIG_LWIP_PPP_SUPPORT is not set +CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 +CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 +# CONFIG_LWIP_SLIP_SUPPORT is not set + +# +# ICMP +# +CONFIG_LWIP_ICMP=y +# CONFIG_LWIP_MULTICAST_PING is not set +# CONFIG_LWIP_BROADCAST_PING is not set +# end of ICMP + +# +# LWIP RAW API +# +CONFIG_LWIP_MAX_RAW_PCBS=16 +# end of LWIP RAW API + +# +# SNTP +# +CONFIG_LWIP_SNTP_MAX_SERVERS=1 +# CONFIG_LWIP_DHCP_GET_NTP_SRV is not set +CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 +# end of SNTP + +# +# DNS +# +CONFIG_LWIP_DNS_MAX_SERVERS=3 +# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set +# end of DNS + +CONFIG_LWIP_ESP_LWIP_ASSERT=y + +# +# Hooks +# +# CONFIG_LWIP_HOOK_TCP_ISN_NONE is not set +CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT=y +# CONFIG_LWIP_HOOK_TCP_ISN_CUSTOM is not set +CONFIG_LWIP_HOOK_IP6_ROUTE_NONE=y +# CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT is not set +# CONFIG_LWIP_HOOK_IP6_ROUTE_CUSTOM is not set +CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y +# CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT is not set +# CONFIG_LWIP_HOOK_ND6_GET_GW_CUSTOM is not set +CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set +# end of Hooks + +# CONFIG_LWIP_DEBUG is not set +# end of LWIP + +# +# mbedTLS +# +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v2.28.x related +# +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# end of mbedTLS v2.28.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +# CONFIG_MBEDTLS_CMAC_C is not set +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_AES_USE_INTERRUPT=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA512_C=y +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +# CONFIG_MBEDTLS_SSL_PROTO_SSL3 is not set +CONFIG_MBEDTLS_SSL_PROTO_TLS1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_X509_CHECK_KEY_USAGE=y +CONFIG_MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +CONFIG_MBEDTLS_RC4_DISABLED=y +# CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT is not set +# CONFIG_MBEDTLS_RC4_ENABLED is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +# CONFIG_MBEDTLS_SECURITY_RISKS is not set +# end of mbedTLS + +# +# mDNS +# +CONFIG_MDNS_MAX_SERVICES=10 +CONFIG_MDNS_TASK_PRIORITY=1 +CONFIG_MDNS_TASK_STACK_SIZE=4096 +# CONFIG_MDNS_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_MDNS_TASK_AFFINITY_CPU0=y +# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set +CONFIG_MDNS_TASK_AFFINITY=0x0 +CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000 +# CONFIG_MDNS_STRICT_MODE is not set +CONFIG_MDNS_TIMER_PERIOD_MS=100 +# CONFIG_MDNS_NETWORKING_SOCKET is not set +CONFIG_MDNS_MULTIPLE_INSTANCE=y +# end of mDNS + +# +# ESP-MQTT Configurations +# +CONFIG_MQTT_PROTOCOL_311=y +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +# CONFIG_MQTT_MSG_ID_INCREMENTAL is not set +# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set +# CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set +# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set +# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set +# CONFIG_MQTT_CUSTOM_OUTBOX is not set +# end of ESP-MQTT Configurations + +# +# Newlib +# +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +# CONFIG_NEWLIB_NANO_FORMAT is not set +# end of Newlib + +# +# NVS +# +# CONFIG_NVS_ASSERT_ERROR_CHECK is not set +# end of NVS + +# +# OpenSSL +# +# CONFIG_OPENSSL_DEBUG is not set +CONFIG_OPENSSL_ERROR_STACK=y +# CONFIG_OPENSSL_ASSERT_DO_NOTHING is not set +CONFIG_OPENSSL_ASSERT_EXIT=y +# end of OpenSSL + +# +# OpenThread +# +# CONFIG_OPENTHREAD_ENABLED is not set +# end of OpenThread + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set +# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# Main Flash configuration +# + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +# CONFIG_SPI_FLASH_HPM_ENA is not set +CONFIG_SPI_FLASH_HPM_AUTO=y +# CONFIG_SPI_FLASH_HPM_DIS is not set +CONFIG_SPI_FLASH_HPM_ON=y +CONFIG_SPI_FLASH_HPM_DC_AUTO=y +# CONFIG_SPI_FLASH_HPM_DC_DISABLE is not set +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +# CONFIG_SPI_FLASH_ROM_IMPL is not set +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_USE_LEGACY_IMPL is not set +# CONFIG_SPI_FLASH_SHARE_SPI1_BUS is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_TH_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP=y +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# SPIFFS Configuration +# +CONFIG_SPIFFS_MAX_PARTITIONS=3 + +# +# SPIFFS Cache Configuration +# +CONFIG_SPIFFS_CACHE=y +CONFIG_SPIFFS_CACHE_WR=y +# CONFIG_SPIFFS_CACHE_STATS is not set +# end of SPIFFS Cache Configuration + +CONFIG_SPIFFS_PAGE_CHECK=y +CONFIG_SPIFFS_GC_MAX_RUNS=10 +# CONFIG_SPIFFS_GC_STATS is not set +CONFIG_SPIFFS_PAGE_SIZE=256 +CONFIG_SPIFFS_OBJ_NAME_LEN=32 +# CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set +CONFIG_SPIFFS_USE_MAGIC=y +CONFIG_SPIFFS_USE_MAGIC_LENGTH=y +CONFIG_SPIFFS_META_LENGTH=4 +CONFIG_SPIFFS_USE_MTIME=y + +# +# Debug Configuration +# +# CONFIG_SPIFFS_DBG is not set +# CONFIG_SPIFFS_API_DBG is not set +# CONFIG_SPIFFS_GC_DBG is not set +# CONFIG_SPIFFS_CACHE_DBG is not set +# CONFIG_SPIFFS_CHECK_DBG is not set +# CONFIG_SPIFFS_TEST_VISUALISATION is not set +# end of Debug Configuration +# end of SPIFFS Configuration + +# +# TCP Transport +# + +# +# Websocket +# +CONFIG_WS_TRANSPORT=y +CONFIG_WS_BUFFER_SIZE=1024 +# end of Websocket +# end of TCP Transport + +# +# TinyUSB Stack +# +# CONFIG_TINYUSB is not set +# end of TinyUSB Stack + +# +# Unity unit testing library +# +CONFIG_UNITY_ENABLE_FLOAT=y +CONFIG_UNITY_ENABLE_DOUBLE=y +# CONFIG_UNITY_ENABLE_64BIT is not set +# CONFIG_UNITY_ENABLE_COLOR is not set +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y +# CONFIG_UNITY_ENABLE_FIXTURE is not set +# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set +# end of Unity unit testing library + +# +# USB-OTG +# +CONFIG_USB_OTG_SUPPORTED=y +CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=256 +CONFIG_USB_HOST_HW_BUFFER_BIAS_BALANCED=y +# CONFIG_USB_HOST_HW_BUFFER_BIAS_IN is not set +# CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT is not set + +# +# Root Hub configuration +# +CONFIG_USB_HOST_DEBOUNCE_DELAY_MS=250 +CONFIG_USB_HOST_RESET_HOLD_MS=30 +CONFIG_USB_HOST_RESET_RECOVERY_MS=30 +CONFIG_USB_HOST_SET_ADDR_RECOVERY_MS=10 +# end of Root Hub configuration +# end of USB-OTG + +# +# Virtual file system +# +CONFIG_VFS_SUPPORT_IO=y +CONFIG_VFS_SUPPORT_DIR=y +CONFIG_VFS_SUPPORT_SELECT=y +CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y +CONFIG_VFS_SUPPORT_TERMIOS=y + +# +# Host File System I/O (Semihosting) +# +CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +# end of Host File System I/O (Semihosting) +# end of Virtual file system + +# +# Wear Levelling +# +# CONFIG_WL_SECTOR_SIZE_512 is not set +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 +# end of Wear Levelling + +# +# Wi-Fi Provisioning Manager +# +CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 +CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 +# CONFIG_WIFI_PROV_BLE_BONDING is not set +# CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION is not set +# CONFIG_WIFI_PROV_KEEP_BLE_ON_AFTER_PROV is not set +# end of Wi-Fi Provisioning Manager + +# +# Supplicant +# +CONFIG_WPA_MBEDTLS_CRYPTO=y +# CONFIG_WPA_WAPI_PSK is not set +# CONFIG_WPA_SUITE_B_192 is not set +# CONFIG_WPA_DEBUG_PRINT is not set +# CONFIG_WPA_TESTING_OPTIONS is not set +# CONFIG_WPA_WPS_STRICT is not set +# CONFIG_WPA_11KV_SUPPORT is not set +# CONFIG_WPA_MBO_SUPPORT is not set +# CONFIG_WPA_DPP_SUPPORT is not set +# end of Supplicant +# end of Component config + +# +# Compatibility options +# +# CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set +# end of Compatibility options + +# Deprecated options for backward compatibility +CONFIG_TOOLPREFIX="xtensa-esp32s3-elf-" +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +# CONFIG_MONITOR_BAUD_9600B is not set +# CONFIG_MONITOR_BAUD_57600B is not set +CONFIG_MONITOR_BAUD_115200B=y +# CONFIG_MONITOR_BAUD_230400B is not set +# CONFIG_MONITOR_BAUD_921600B is not set +# CONFIG_MONITOR_BAUD_2MB is not set +# CONFIG_MONITOR_BAUD_OTHER is not set +CONFIG_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_MONITOR_BAUD=115200 +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +CONFIG_CXX_EXCEPTIONS=y +CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_DISABLE_GCC8_WARNINGS is not set +# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y +CONFIG_BLUEDROID_ENABLED=y +# CONFIG_NIMBLE_ENABLED is not set +CONFIG_BTC_TASK_STACK_SIZE=3072 +CONFIG_BLUEDROID_PINNED_TO_CORE_0=y +# CONFIG_BLUEDROID_PINNED_TO_CORE_1 is not set +CONFIG_BLUEDROID_PINNED_TO_CORE=0 +CONFIG_BTU_TASK_STACK_SIZE=4352 +# CONFIG_BLUEDROID_MEM_DEBUG is not set +CONFIG_GATTS_ENABLE=y +# CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set +CONFIG_GATTS_SEND_SERVICE_CHANGE_AUTO=y +CONFIG_GATTS_SEND_SERVICE_CHANGE_MODE=0 +CONFIG_GATTC_ENABLE=y +# CONFIG_GATTC_CACHE_NVS_FLASH is not set +CONFIG_BLE_SMP_ENABLE=y +# CONFIG_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set +# CONFIG_HCI_TRACE_LEVEL_NONE is not set +# CONFIG_HCI_TRACE_LEVEL_ERROR is not set +CONFIG_HCI_TRACE_LEVEL_WARNING=y +# CONFIG_HCI_TRACE_LEVEL_API is not set +# CONFIG_HCI_TRACE_LEVEL_EVENT is not set +# CONFIG_HCI_TRACE_LEVEL_DEBUG is not set +# CONFIG_HCI_TRACE_LEVEL_VERBOSE is not set +CONFIG_HCI_INITIAL_TRACE_LEVEL=2 +# CONFIG_BTM_TRACE_LEVEL_NONE is not set +# CONFIG_BTM_TRACE_LEVEL_ERROR is not set +CONFIG_BTM_TRACE_LEVEL_WARNING=y +# CONFIG_BTM_TRACE_LEVEL_API is not set +# CONFIG_BTM_TRACE_LEVEL_EVENT is not set +# CONFIG_BTM_TRACE_LEVEL_DEBUG is not set +# CONFIG_BTM_TRACE_LEVEL_VERBOSE is not set +CONFIG_BTM_INITIAL_TRACE_LEVEL=2 +# CONFIG_L2CAP_TRACE_LEVEL_NONE is not set +# CONFIG_L2CAP_TRACE_LEVEL_ERROR is not set +CONFIG_L2CAP_TRACE_LEVEL_WARNING=y +# CONFIG_L2CAP_TRACE_LEVEL_API is not set +# CONFIG_L2CAP_TRACE_LEVEL_EVENT is not set +# CONFIG_L2CAP_TRACE_LEVEL_DEBUG is not set +# CONFIG_L2CAP_TRACE_LEVEL_VERBOSE is not set +CONFIG_L2CAP_INITIAL_TRACE_LEVEL=2 +# CONFIG_RFCOMM_TRACE_LEVEL_NONE is not set +# CONFIG_RFCOMM_TRACE_LEVEL_ERROR is not set +CONFIG_RFCOMM_TRACE_LEVEL_WARNING=y +# CONFIG_RFCOMM_TRACE_LEVEL_API is not set +# CONFIG_RFCOMM_TRACE_LEVEL_EVENT is not set +# CONFIG_RFCOMM_TRACE_LEVEL_DEBUG is not set +# CONFIG_RFCOMM_TRACE_LEVEL_VERBOSE is not set +CONFIG_RFCOMM_INITIAL_TRACE_LEVEL=2 +# CONFIG_SDP_TRACE_LEVEL_NONE is not set +# CONFIG_SDP_TRACE_LEVEL_ERROR is not set +CONFIG_SDP_TRACE_LEVEL_WARNING=y +# CONFIG_SDP_TRACE_LEVEL_API is not set +# CONFIG_SDP_TRACE_LEVEL_EVENT is not set +# CONFIG_SDP_TRACE_LEVEL_DEBUG is not set +# CONFIG_SDP_TRACE_LEVEL_VERBOSE is not set +CONFIG_BTH_LOG_SDP_INITIAL_TRACE_LEVEL=2 +# CONFIG_GAP_TRACE_LEVEL_NONE is not set +# CONFIG_GAP_TRACE_LEVEL_ERROR is not set +CONFIG_GAP_TRACE_LEVEL_WARNING=y +# CONFIG_GAP_TRACE_LEVEL_API is not set +# CONFIG_GAP_TRACE_LEVEL_EVENT is not set +# CONFIG_GAP_TRACE_LEVEL_DEBUG is not set +# CONFIG_GAP_TRACE_LEVEL_VERBOSE is not set +CONFIG_GAP_INITIAL_TRACE_LEVEL=2 +CONFIG_BNEP_INITIAL_TRACE_LEVEL=2 +# CONFIG_PAN_TRACE_LEVEL_NONE is not set +# CONFIG_PAN_TRACE_LEVEL_ERROR is not set +CONFIG_PAN_TRACE_LEVEL_WARNING=y +# CONFIG_PAN_TRACE_LEVEL_API is not set +# CONFIG_PAN_TRACE_LEVEL_EVENT is not set +# CONFIG_PAN_TRACE_LEVEL_DEBUG is not set +# CONFIG_PAN_TRACE_LEVEL_VERBOSE is not set +CONFIG_PAN_INITIAL_TRACE_LEVEL=2 +# CONFIG_A2D_TRACE_LEVEL_NONE is not set +# CONFIG_A2D_TRACE_LEVEL_ERROR is not set +CONFIG_A2D_TRACE_LEVEL_WARNING=y +# CONFIG_A2D_TRACE_LEVEL_API is not set +# CONFIG_A2D_TRACE_LEVEL_EVENT is not set +# CONFIG_A2D_TRACE_LEVEL_DEBUG is not set +# CONFIG_A2D_TRACE_LEVEL_VERBOSE is not set +CONFIG_A2D_INITIAL_TRACE_LEVEL=2 +# CONFIG_AVDT_TRACE_LEVEL_NONE is not set +# CONFIG_AVDT_TRACE_LEVEL_ERROR is not set +CONFIG_AVDT_TRACE_LEVEL_WARNING=y +# CONFIG_AVDT_TRACE_LEVEL_API is not set +# CONFIG_AVDT_TRACE_LEVEL_EVENT is not set +# CONFIG_AVDT_TRACE_LEVEL_DEBUG is not set +# CONFIG_AVDT_TRACE_LEVEL_VERBOSE is not set +CONFIG_AVDT_INITIAL_TRACE_LEVEL=2 +# CONFIG_AVCT_TRACE_LEVEL_NONE is not set +# CONFIG_AVCT_TRACE_LEVEL_ERROR is not set +CONFIG_AVCT_TRACE_LEVEL_WARNING=y +# CONFIG_AVCT_TRACE_LEVEL_API is not set +# CONFIG_AVCT_TRACE_LEVEL_EVENT is not set +# CONFIG_AVCT_TRACE_LEVEL_DEBUG is not set +# CONFIG_AVCT_TRACE_LEVEL_VERBOSE is not set +CONFIG_AVCT_INITIAL_TRACE_LEVEL=2 +# CONFIG_AVRC_TRACE_LEVEL_NONE is not set +# CONFIG_AVRC_TRACE_LEVEL_ERROR is not set +CONFIG_AVRC_TRACE_LEVEL_WARNING=y +# CONFIG_AVRC_TRACE_LEVEL_API is not set +# CONFIG_AVRC_TRACE_LEVEL_EVENT is not set +# CONFIG_AVRC_TRACE_LEVEL_DEBUG is not set +# CONFIG_AVRC_TRACE_LEVEL_VERBOSE is not set +CONFIG_AVRC_INITIAL_TRACE_LEVEL=2 +# CONFIG_MCA_TRACE_LEVEL_NONE is not set +# CONFIG_MCA_TRACE_LEVEL_ERROR is not set +CONFIG_MCA_TRACE_LEVEL_WARNING=y +# CONFIG_MCA_TRACE_LEVEL_API is not set +# CONFIG_MCA_TRACE_LEVEL_EVENT is not set +# CONFIG_MCA_TRACE_LEVEL_DEBUG is not set +# CONFIG_MCA_TRACE_LEVEL_VERBOSE is not set +CONFIG_MCA_INITIAL_TRACE_LEVEL=2 +# CONFIG_HID_TRACE_LEVEL_NONE is not set +# CONFIG_HID_TRACE_LEVEL_ERROR is not set +CONFIG_HID_TRACE_LEVEL_WARNING=y +# CONFIG_HID_TRACE_LEVEL_API is not set +# CONFIG_HID_TRACE_LEVEL_EVENT is not set +# CONFIG_HID_TRACE_LEVEL_DEBUG is not set +# CONFIG_HID_TRACE_LEVEL_VERBOSE is not set +CONFIG_HID_INITIAL_TRACE_LEVEL=2 +# CONFIG_APPL_TRACE_LEVEL_NONE is not set +# CONFIG_APPL_TRACE_LEVEL_ERROR is not set +CONFIG_APPL_TRACE_LEVEL_WARNING=y +# CONFIG_APPL_TRACE_LEVEL_API is not set +# CONFIG_APPL_TRACE_LEVEL_EVENT is not set +# CONFIG_APPL_TRACE_LEVEL_DEBUG is not set +# CONFIG_APPL_TRACE_LEVEL_VERBOSE is not set +CONFIG_APPL_INITIAL_TRACE_LEVEL=2 +# CONFIG_GATT_TRACE_LEVEL_NONE is not set +# CONFIG_GATT_TRACE_LEVEL_ERROR is not set +CONFIG_GATT_TRACE_LEVEL_WARNING=y +# CONFIG_GATT_TRACE_LEVEL_API is not set +# CONFIG_GATT_TRACE_LEVEL_EVENT is not set +# CONFIG_GATT_TRACE_LEVEL_DEBUG is not set +# CONFIG_GATT_TRACE_LEVEL_VERBOSE is not set +CONFIG_GATT_INITIAL_TRACE_LEVEL=2 +# CONFIG_SMP_TRACE_LEVEL_NONE is not set +# CONFIG_SMP_TRACE_LEVEL_ERROR is not set +CONFIG_SMP_TRACE_LEVEL_WARNING=y +# CONFIG_SMP_TRACE_LEVEL_API is not set +# CONFIG_SMP_TRACE_LEVEL_EVENT is not set +# CONFIG_SMP_TRACE_LEVEL_DEBUG is not set +# CONFIG_SMP_TRACE_LEVEL_VERBOSE is not set +CONFIG_SMP_INITIAL_TRACE_LEVEL=2 +# CONFIG_BTIF_TRACE_LEVEL_NONE is not set +# CONFIG_BTIF_TRACE_LEVEL_ERROR is not set +CONFIG_BTIF_TRACE_LEVEL_WARNING=y +# CONFIG_BTIF_TRACE_LEVEL_API is not set +# CONFIG_BTIF_TRACE_LEVEL_EVENT is not set +# CONFIG_BTIF_TRACE_LEVEL_DEBUG is not set +# CONFIG_BTIF_TRACE_LEVEL_VERBOSE is not set +CONFIG_BTIF_INITIAL_TRACE_LEVEL=2 +# CONFIG_BTC_TRACE_LEVEL_NONE is not set +# CONFIG_BTC_TRACE_LEVEL_ERROR is not set +CONFIG_BTC_TRACE_LEVEL_WARNING=y +# CONFIG_BTC_TRACE_LEVEL_API is not set +# CONFIG_BTC_TRACE_LEVEL_EVENT is not set +# CONFIG_BTC_TRACE_LEVEL_DEBUG is not set +# CONFIG_BTC_TRACE_LEVEL_VERBOSE is not set +CONFIG_BTC_INITIAL_TRACE_LEVEL=2 +# CONFIG_OSI_TRACE_LEVEL_NONE is not set +# CONFIG_OSI_TRACE_LEVEL_ERROR is not set +CONFIG_OSI_TRACE_LEVEL_WARNING=y +# CONFIG_OSI_TRACE_LEVEL_API is not set +# CONFIG_OSI_TRACE_LEVEL_EVENT is not set +# CONFIG_OSI_TRACE_LEVEL_DEBUG is not set +# CONFIG_OSI_TRACE_LEVEL_VERBOSE is not set +CONFIG_OSI_INITIAL_TRACE_LEVEL=2 +# CONFIG_BLUFI_TRACE_LEVEL_NONE is not set +# CONFIG_BLUFI_TRACE_LEVEL_ERROR is not set +CONFIG_BLUFI_TRACE_LEVEL_WARNING=y +# CONFIG_BLUFI_TRACE_LEVEL_API is not set +# CONFIG_BLUFI_TRACE_LEVEL_EVENT is not set +# CONFIG_BLUFI_TRACE_LEVEL_DEBUG is not set +# CONFIG_BLUFI_TRACE_LEVEL_VERBOSE is not set +CONFIG_BLUFI_INITIAL_TRACE_LEVEL=2 +# CONFIG_BLE_HOST_QUEUE_CONGESTION_CHECK is not set +CONFIG_SMP_ENABLE=y +# CONFIG_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY is not set +CONFIG_BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT=30 +CONFIG_ADC2_DISABLE_DAC=y +# CONFIG_EVENT_LOOP_PROFILING is not set +CONFIG_POST_EVENTS_FROM_ISR=y +CONFIG_POST_EVENTS_FROM_IRAM_ISR=y +# CONFIG_ESP_SYSTEM_PD_FLASH is not set +CONFIG_ESP32C3_LIGHTSLEEP_GPIO_RESET_WORKAROUND=y +CONFIG_IPC_TASK_STACK_SIZE=1536 +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 +# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set +CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y +# CONFIG_ESP32S2_PANIC_PRINT_HALT is not set +CONFIG_ESP32S2_PANIC_PRINT_REBOOT=y +# CONFIG_ESP32S2_PANIC_SILENT_REBOOT is not set +# CONFIG_ESP32S2_PANIC_GDBSTUB is not set +CONFIG_ESP32S2_ALLOW_RTC_FAST_MEM_AS_HEAP=y +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_SW_COEXIST_ENABLE=y +# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150 +CONFIG_MB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_MB_QUEUE_LENGTH=20 +CONFIG_MB_SERIAL_TASK_STACK_SIZE=4096 +CONFIG_MB_SERIAL_BUF_SIZE=256 +CONFIG_MB_SERIAL_TASK_PRIO=10 +CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_MB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_MB_CONTROLLER_STACK_SIZE=4096 +CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 +# CONFIG_MB_TIMER_PORT_ENABLED is not set +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_L2_TO_L3_COPY is not set +# CONFIG_USE_ONLY_LWIP_SELECT is not set +CONFIG_ESP_GRATUITOUS_ARP=y +CONFIG_GARP_TMR_INTERVAL=60 +CONFIG_TCPIP_RECVMBOX_SIZE=32 +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=12 +CONFIG_TCP_MSS=1440 +CONFIG_TCP_MSL=60000 +CONFIG_TCP_SND_BUF_DEFAULT=5760 +CONFIG_TCP_WND_DEFAULT=5760 +CONFIG_TCP_RECVMBOX_SIZE=6 +CONFIG_TCP_QUEUE_OOSEQ=y +# CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set +CONFIG_TCP_OVERSIZE_MSS=y +# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_TCP_OVERSIZE_DISABLE is not set +CONFIG_UDP_RECVMBOX_SIZE=6 +CONFIG_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set +# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set +CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF +# CONFIG_PPP_SUPPORT is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +# CONFIG_USB_ENABLED is not set +CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y +CONFIG_SUPPORT_TERMIOS=y +CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +# End of deprecated options diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 00000000..b7682f13 --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,6 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_AUTOSTART_ARDUINO=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_BT_ENABLED=y +CONFIG_BT_CONTROLLER_ENABLED=y +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..b7fa655a --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE SOURCES "sp140/*.cpp") + +idf_component_register(SRCS ${SOURCES} + INCLUDE_DIRS "../inc" "../inc/sp140" "../inc/sp140/lvgl" + REQUIRES arduino) diff --git a/src/sp140/CMakeLists.txt b/src/sp140/CMakeLists.txt new file mode 100644 index 00000000..d8278036 --- /dev/null +++ b/src/sp140/CMakeLists.txt @@ -0,0 +1,6 @@ +# This file was automatically generated for projects +# without default 'CMakeLists.txt' file. + +FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/sp140/*.*) + +idf_component_register(SRCS ${app_sources}) diff --git a/src/sp140/ble/ble_core.cpp b/src/sp140/ble/ble_core.cpp index e97b54ec..4c108e89 100644 --- a/src/sp140/ble/ble_core.cpp +++ b/src/sp140/ble/ble_core.cpp @@ -8,6 +8,7 @@ #include "sp140/ble/config_service.h" #include "sp140/ble/controller_service.h" #include "sp140/ble/esc_service.h" +#include "sp140/ble/ota_service.h" namespace { @@ -53,6 +54,7 @@ void setupBLE() { initBmsBleService(pServer); initEscBleService(pServer); initControllerBleService(pServer); + initOtaBleService(pServer); NimBLEAdvertising* advertising = pServer->getAdvertising(); advertising->setName("OpenPPG Controller"); @@ -62,6 +64,7 @@ void setupBLE() { advertising->addServiceUUID(NimBLEUUID(BMS_TELEMETRY_SERVICE_UUID)); advertising->addServiceUUID(NimBLEUUID(ESC_TELEMETRY_SERVICE_UUID)); advertising->addServiceUUID(NimBLEUUID(CONTROLLER_SERVICE_UUID)); + advertising->addServiceUUID(NimBLEUUID(OTA_SERVICE_UUID)); advertising->enableScanResponse(true); advertising->start(); diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp new file mode 100644 index 00000000..fa4c3cdb --- /dev/null +++ b/src/sp140/ble/ota_service.cpp @@ -0,0 +1,150 @@ +#include "sp140/ble/ota_service.h" + +#include +#include "esp_ota_ops.h" +#include "esp_partition.h" +#include "sp140/ble/ble_ids.h" + +namespace { + +// OTA Commands +const uint8_t CMD_START = 0x01; +const uint8_t CMD_END = 0x02; +const uint8_t CMD_ABORT = 0x03; + +// OTA Responses +const uint8_t RESP_SUCCESS = 0x00; +const uint8_t RESP_ERROR_BEGIN = 0xE1; +const uint8_t RESP_ERROR_WRITE = 0xE2; +const uint8_t RESP_ERROR_END = 0xE3; +const uint8_t RESP_ERROR_UNKNOWN = 0xFF; + +volatile bool otaInProgress = false; +esp_ota_handle_t updateHandle = 0; +const esp_partition_t* updatePartition = nullptr; +size_t receivedBytes = 0; + +void sendResponse(NimBLECharacteristic* pChar, uint8_t responseCode) { + pChar->setValue(&responseCode, 1); + pChar->notify(); + USBSerial.printf("OTA: Sent response 0x%02X\n", responseCode); +} + +class OtaControlCallback : public NimBLECharacteristicCallbacks { + void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { + (void)connInfo; + std::string value = pChar->getValue(); + if (value.length() == 0) return; + + uint8_t command = value[0]; + + if (command == CMD_START) { + USBSerial.println("OTA: Start command received"); + + updatePartition = esp_ota_get_next_update_partition(nullptr); + if (!updatePartition) { + USBSerial.println("OTA Error: No update partition found"); + sendResponse(pChar, RESP_ERROR_BEGIN); + return; + } + + USBSerial.printf("OTA: Writing to partition subtype %d at offset 0x%x\n", + updatePartition->subtype, updatePartition->address); + + // OTA_SIZE_UNKNOWN allows flashing any size, but requires enough space + esp_err_t err = esp_ota_begin(updatePartition, OTA_SIZE_UNKNOWN, &updateHandle); + if (err != ESP_OK) { + USBSerial.printf("OTA Error: esp_ota_begin failed (0x%x)\n", err); + sendResponse(pChar, RESP_ERROR_BEGIN); + return; + } + + otaInProgress = true; + receivedBytes = 0; + sendResponse(pChar, RESP_SUCCESS); + + } else if (command == CMD_END) { + USBSerial.println("OTA: End command received"); + + if (!otaInProgress) { + sendResponse(pChar, RESP_ERROR_UNKNOWN); + return; + } + + esp_err_t err = esp_ota_end(updateHandle); + if (err != ESP_OK) { + USBSerial.printf("OTA Error: esp_ota_end failed (0x%x)\n", err); + sendResponse(pChar, RESP_ERROR_END); + otaInProgress = false; + return; + } + + err = esp_ota_set_boot_partition(updatePartition); + if (err != ESP_OK) { + USBSerial.printf("OTA Error: set_boot_partition failed (0x%x)\n", err); + sendResponse(pChar, RESP_ERROR_END); + otaInProgress = false; + return; + } + + USBSerial.println("OTA: Success! Restarting..."); + sendResponse(pChar, RESP_SUCCESS); + delay(1000); + ESP.restart(); + + } else if (command == CMD_ABORT) { + USBSerial.println("OTA: Abort command received"); + if (otaInProgress) { + esp_ota_end(updateHandle); + otaInProgress = false; + } + sendResponse(pChar, RESP_SUCCESS); + } + } +}; + +class OtaDataCallback : public NimBLECharacteristicCallbacks { + void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { + (void)connInfo; + if (!otaInProgress) return; + + std::string value = pChar->getValue(); + if (value.length() > 0) { + esp_err_t err = esp_ota_write(updateHandle, value.data(), value.length()); + if (err != ESP_OK) { + USBSerial.printf("OTA Error: Write failed (0x%x)\n", err); + // Optionally notify control char of error, but might be slow + } else { + receivedBytes += value.length(); + // Verbose logging only every 10KB to avoid spam + if (receivedBytes % 10240 < value.length()) { + USBSerial.printf("OTA: Received %d bytes\n", receivedBytes); + } + } + } + } +}; + +} // namespace + +void initOtaBleService(NimBLEServer* pServer) { + NimBLEService* pService = pServer->createService(OTA_SERVICE_UUID); + + NimBLECharacteristic* pControlChar = pService->createCharacteristic( + OTA_CONTROL_UUID, + NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY + ); + pControlChar->setCallbacks(new OtaControlCallback()); + + NimBLECharacteristic* pDataChar = pService->createCharacteristic( + OTA_DATA_UUID, + NIMBLE_PROPERTY::WRITE_NR // Write No Response for speed + ); + pDataChar->setCallbacks(new OtaDataCallback()); + + pService->start(); +} + +bool isOtaInProgress() { + return otaInProgress; +} diff --git a/src/sp140/buzzer.cpp b/src/sp140/buzzer.cpp index de653431..4e4bde65 100644 --- a/src/sp140/buzzer.cpp +++ b/src/sp140/buzzer.cpp @@ -4,8 +4,8 @@ #include "sp140/shared-config.h" #include "sp140/esp32s3-config.h" -#include -#include +#include +#include #define DEBUG_SERIAL USBSerial diff --git a/src/sp140/extra-data.ino b/src/sp140/device_settings.cpp similarity index 98% rename from src/sp140/extra-data.ino rename to src/sp140/device_settings.cpp index 8eedce62..33355e46 100644 --- a/src/sp140/extra-data.ino +++ b/src/sp140/device_settings.cpp @@ -1,8 +1,13 @@ // Copyright 2020 // OpenPPG +#include "Arduino.h" +#include +#include "sp140/device_settings.h" +#include "../../inc/sp140/esp32s3-config.h" #include // Add ESP32 Preferences library #include "../../inc/sp140/throttle.h" +#include "../../inc/sp140/globals.h" /** * WebSerial Protocol Documentation diff --git a/src/sp140/esc.cpp b/src/sp140/esc.cpp index 1a435c8b..00e9046b 100644 --- a/src/sp140/esc.cpp +++ b/src/sp140/esc.cpp @@ -129,14 +129,14 @@ void readESCTelemetry() { if (lastSuccessfulCommTimeMs == 0 || (currentTimeMs - lastSuccessfulCommTimeMs) > TELEMETRY_TIMEOUT_MS) { if (escTelemetryData.escState != TelemetryState::NOT_CONNECTED) { // Log state change only if it actually changed - USBSerial.printf("ESC State: %d -> NOT_CONNECTED (Timeout)\n", escTelemetryData.escState); + USBSerial.printf("ESC State: %d -> NOT_CONNECTED (Timeout)\n", static_cast(escTelemetryData.escState)); escTelemetryData.escState = TelemetryState::NOT_CONNECTED; // Optional: Consider resetting telemetry values here if needed when disconnected } } else { if (escTelemetryData.escState != TelemetryState::CONNECTED) { // Log state change only if it actually changed - USBSerial.printf("ESC State: %d -> CONNECTED\n", escTelemetryData.escState); + USBSerial.printf("ESC State: %d -> CONNECTED\n", static_cast(escTelemetryData.escState)); escTelemetryData.escState = TelemetryState::CONNECTED; } } diff --git a/src/sp140/lvgl/lvgl_updates.cpp b/src/sp140/lvgl/lvgl_updates.cpp index 96cf7d20..6680b64d 100644 --- a/src/sp140/lvgl/lvgl_updates.cpp +++ b/src/sp140/lvgl/lvgl_updates.cpp @@ -454,7 +454,7 @@ void updateLvglMainScreen( int ones = whole_part % 10; // Populate character positions using static buffers - static char power_digit_buffers[4][2]; + static char power_digit_buffers[4][12]; // Tens digit (only show if >= 10kW) if (tens > 0) { @@ -533,7 +533,7 @@ void updateLvglMainScreen( int ones = whole_part % 10; // Populate each character position using static character buffers - static char digit_buffers[7][2]; // Static buffers for single digits + static char digit_buffers[7][12]; // Static buffers for single digits // Clear all positions first for (int i = 0; i < 7; i++) { @@ -598,7 +598,7 @@ void updateLvglMainScreen( int tens = (feet / 10) % 10; int ones = feet % 10; - static char digit_buffers_ft[7][2]; // Static buffers for feet + static char digit_buffers_ft[7][12]; // Static buffers for feet // Adjust width of position 4 for feet (should be normal width, not narrow) lv_obj_set_size(altitude_char_labels[4], 17, 24); // char_width=19, char_height=24 diff --git a/src/sp140/sp140.ino b/src/sp140/main.cpp similarity index 98% rename from src/sp140/sp140.ino rename to src/sp140/main.cpp index 1a2b3e20..ab1813f5 100644 --- a/src/sp140/sp140.ino +++ b/src/sp140/main.cpp @@ -1,6 +1,7 @@ // Copyright 2020 // OpenPPG #include "Arduino.h" +#include "sp140/device_settings.h" #include "../../inc/sp140/esp32s3-config.h" @@ -45,6 +46,22 @@ #include #include +// Forward declarations +void disarmSystem(); +bool armSystem(); +void afterCruiseEnd(); +void afterCruiseStart(); +void pushTelemetrySnapshot(); +void initButtons(); +void setup140(); +void setupTasks(); +void audioTask(void* parameter); +void updateESCBLETask(void* parameter); +void webSerialTask(void* parameter); +void toggleArm(); +void toggleCruise(); +void syncESCTelemetry(); + // Global variable for shared SPI SPIClass* hardwareSPI = nullptr; @@ -1087,7 +1104,7 @@ void handleThrottle() { } } - int finalPwm; + int finalPwm = ESC_DISARMED_PWM; // Handle throttle based on current device state switch (currentState) { From 2bf9acf58c9e5c13031555b0beb0d6d38644f702 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 15:06:16 -0600 Subject: [PATCH 02/22] Update BLE OTA service to Espressif standard Refactored OTA BLE service to use Espressif standard UUIDs and characteristics, separating command, data, and status. Added checks to suppress BLE notifications during OTA updates across all telemetry and config services. Improves compatibility and prevents notification conflicts during firmware updates. --- inc/sp140/ble/ble_ids.h | 9 +++-- src/sp140/ble/bms_service.cpp | 7 ++-- src/sp140/ble/config_service.cpp | 5 ++- src/sp140/ble/controller_service.cpp | 3 +- src/sp140/ble/esc_service.cpp | 5 ++- src/sp140/ble/ota_service.cpp | 57 +++++++++++++++++----------- src/sp140/main.cpp | 3 +- 7 files changed, 55 insertions(+), 34 deletions(-) diff --git a/inc/sp140/ble/ble_ids.h b/inc/sp140/ble/ble_ids.h index bdb33577..dd5f944b 100644 --- a/inc/sp140/ble/ble_ids.h +++ b/inc/sp140/ble/ble_ids.h @@ -68,10 +68,11 @@ #define CONTROLLER_TELEMETRY_UUID "01C63B61-0891-4655-BBCA-8E745C48A176" // ============================================================================ -// OTA Service (Firmware Update) +// OTA Service (Firmware Update) - Espressif Standard // ============================================================================ -#define OTA_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" -#define OTA_CONTROL_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" -#define OTA_DATA_UUID "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" +#define OTA_SERVICE_UUID "00008018-0000-1000-8000-00805F9B34FB" +#define OTA_DATA_UUID "00008019-0000-1000-8000-00805F9B34FB" // RX: Firmware data +#define OTA_STATUS_UUID "0000801A-0000-1000-8000-00805F9B34FB" // TX: Notifications/Status +#define OTA_COMMAND_UUID "0000801B-0000-1000-8000-00805F9B34FB" // RX: Commands (Start/End) #endif // INC_SP140_BLE_BLE_IDS_H_ diff --git a/src/sp140/ble/bms_service.cpp b/src/sp140/ble/bms_service.cpp index 4478473f..217d809a 100644 --- a/src/sp140/ble/bms_service.cpp +++ b/src/sp140/ble/bms_service.cpp @@ -5,6 +5,7 @@ #include "sp140/ble.h" #include "sp140/ble/ble_ids.h" #include "sp140/ble/ble_utils.h" +#include "sp140/ble/ota_service.h" namespace { @@ -152,7 +153,7 @@ void updateBMSPackedTelemetry(const STR_BMS_TELEMETRY_140& telemetry, uint8_t bm reinterpret_cast(&packet), sizeof(BLE_BMS_Telemetry_V1)); - if (deviceConnected) { + if (deviceConnected && !isOtaInProgress()) { pBMSPackedTelemetry->notify(); } } @@ -223,8 +224,8 @@ void updateBMSTelemetry(const STR_BMS_TELEMETRY_140& telemetry) { setAndNotifyOnChange(pBMSChargeMos, static_cast(telemetry.is_charge_mos ? 1 : 0), lastChargeMos); setAndNotifyOnChange(pBMSDischargeMos, static_cast(telemetry.is_discharge_mos ? 1 : 0), lastDischargeMos); - if (!deviceConnected) { - return; // No notifications needed without a subscriber. + if (!deviceConnected || isOtaInProgress()) { + return; // No notifications needed without a subscriber or if OTA is running. } if (pBMSSOC) pBMSSOC->notify(); diff --git a/src/sp140/ble/config_service.cpp b/src/sp140/ble/config_service.cpp index 803766bb..6c7d2659 100644 --- a/src/sp140/ble/config_service.cpp +++ b/src/sp140/ble/config_service.cpp @@ -17,6 +17,7 @@ #include "sp140/globals.h" #include "sp140/throttle.h" #include "version.h" +#include "sp140/ble/ota_service.h" extern void writeDeviceData(); extern QueueHandle_t throttleUpdateQueue; @@ -363,7 +364,9 @@ void updateThrottleBLE(int value) { try { pThrottleCharacteristic->setValue(reinterpret_cast(&value), sizeof(value)); - pThrottleCharacteristic->notify(); + if (!isOtaInProgress()) { + pThrottleCharacteristic->notify(); + } vTaskDelay(pdMS_TO_TICKS(5)); } catch (...) { USBSerial.println("Error sending BLE notification"); diff --git a/src/sp140/ble/controller_service.cpp b/src/sp140/ble/controller_service.cpp index efc09edf..7fb8bec0 100644 --- a/src/sp140/ble/controller_service.cpp +++ b/src/sp140/ble/controller_service.cpp @@ -5,6 +5,7 @@ #include "sp140/ble.h" #include "sp140/ble/ble_ids.h" #include "sp140/throttle.h" +#include "sp140/ble/ota_service.h" namespace { @@ -62,7 +63,7 @@ void updateControllerPackedTelemetry(float altitude, float baro_temp, reinterpret_cast(&packet), sizeof(BLE_Controller_Telemetry_V1)); - if (deviceConnected) { + if (deviceConnected && !isOtaInProgress()) { pControllerPackedTelemetry->notify(); } } diff --git a/src/sp140/ble/esc_service.cpp b/src/sp140/ble/esc_service.cpp index 74a666e8..cbef110d 100644 --- a/src/sp140/ble/esc_service.cpp +++ b/src/sp140/ble/esc_service.cpp @@ -4,6 +4,7 @@ #include "sp140/ble.h" #include "sp140/ble/ble_ids.h" +#include "sp140/ble/ota_service.h" namespace { @@ -103,7 +104,7 @@ void updateESCPackedTelemetry(const STR_ESC_TELEMETRY_140& telemetry) { reinterpret_cast(&packet), sizeof(BLE_ESC_Telemetry_V1)); - if (deviceConnected) { + if (deviceConnected && !isOtaInProgress()) { pESCPackedTelemetry->notify(); } } @@ -130,7 +131,7 @@ void updateESCTelemetryBLE(const STR_ESC_TELEMETRY_140& telemetry) { if (pESCRPM) pESCRPM->setValue(rpm); if (pESCTemps) pESCTemps->setValue(reinterpret_cast(&temps), sizeof(temps)); - if (!deviceConnected) { + if (!deviceConnected || isOtaInProgress()) { return; // No notifications needed without a subscriber. } diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index fa4c3cdb..6eaf6c82 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -7,7 +7,8 @@ namespace { -// OTA Commands +// OTA Commands (matching Espressif example constants where applicable) +// Example: 0x01 = Start, 0x02 = End, 0x03 = Abort const uint8_t CMD_START = 0x01; const uint8_t CMD_END = 0x02; const uint8_t CMD_ABORT = 0x03; @@ -24,13 +25,18 @@ esp_ota_handle_t updateHandle = 0; const esp_partition_t* updatePartition = nullptr; size_t receivedBytes = 0; -void sendResponse(NimBLECharacteristic* pChar, uint8_t responseCode) { - pChar->setValue(&responseCode, 1); - pChar->notify(); - USBSerial.printf("OTA: Sent response 0x%02X\n", responseCode); +// Status characteristic for notifications +NimBLECharacteristic* pStatusChar = nullptr; + +void sendResponse(uint8_t responseCode) { + if (pStatusChar) { + pStatusChar->setValue(&responseCode, 1); + pStatusChar->notify(); + USBSerial.printf("OTA: Sent response 0x%02X\n", responseCode); + } } -class OtaControlCallback : public NimBLECharacteristicCallbacks { +class OtaCommandCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { (void)connInfo; std::string value = pChar->getValue(); @@ -44,37 +50,36 @@ class OtaControlCallback : public NimBLECharacteristicCallbacks { updatePartition = esp_ota_get_next_update_partition(nullptr); if (!updatePartition) { USBSerial.println("OTA Error: No update partition found"); - sendResponse(pChar, RESP_ERROR_BEGIN); + sendResponse(RESP_ERROR_BEGIN); return; } USBSerial.printf("OTA: Writing to partition subtype %d at offset 0x%x\n", updatePartition->subtype, updatePartition->address); - // OTA_SIZE_UNKNOWN allows flashing any size, but requires enough space esp_err_t err = esp_ota_begin(updatePartition, OTA_SIZE_UNKNOWN, &updateHandle); if (err != ESP_OK) { USBSerial.printf("OTA Error: esp_ota_begin failed (0x%x)\n", err); - sendResponse(pChar, RESP_ERROR_BEGIN); + sendResponse(RESP_ERROR_BEGIN); return; } otaInProgress = true; receivedBytes = 0; - sendResponse(pChar, RESP_SUCCESS); + sendResponse(RESP_SUCCESS); } else if (command == CMD_END) { USBSerial.println("OTA: End command received"); if (!otaInProgress) { - sendResponse(pChar, RESP_ERROR_UNKNOWN); + sendResponse(RESP_ERROR_UNKNOWN); return; } esp_err_t err = esp_ota_end(updateHandle); if (err != ESP_OK) { USBSerial.printf("OTA Error: esp_ota_end failed (0x%x)\n", err); - sendResponse(pChar, RESP_ERROR_END); + sendResponse(RESP_ERROR_END); otaInProgress = false; return; } @@ -82,13 +87,13 @@ class OtaControlCallback : public NimBLECharacteristicCallbacks { err = esp_ota_set_boot_partition(updatePartition); if (err != ESP_OK) { USBSerial.printf("OTA Error: set_boot_partition failed (0x%x)\n", err); - sendResponse(pChar, RESP_ERROR_END); + sendResponse(RESP_ERROR_END); otaInProgress = false; return; } USBSerial.println("OTA: Success! Restarting..."); - sendResponse(pChar, RESP_SUCCESS); + sendResponse(RESP_SUCCESS); delay(1000); ESP.restart(); @@ -98,7 +103,7 @@ class OtaControlCallback : public NimBLECharacteristicCallbacks { esp_ota_end(updateHandle); otaInProgress = false; } - sendResponse(pChar, RESP_SUCCESS); + sendResponse(RESP_SUCCESS); } } }; @@ -113,10 +118,10 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { esp_err_t err = esp_ota_write(updateHandle, value.data(), value.length()); if (err != ESP_OK) { USBSerial.printf("OTA Error: Write failed (0x%x)\n", err); - // Optionally notify control char of error, but might be slow + sendResponse(RESP_ERROR_WRITE); + // Abort? } else { receivedBytes += value.length(); - // Verbose logging only every 10KB to avoid spam if (receivedBytes % 10240 < value.length()) { USBSerial.printf("OTA: Received %d bytes\n", receivedBytes); } @@ -130,12 +135,20 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { void initOtaBleService(NimBLEServer* pServer) { NimBLEService* pService = pServer->createService(OTA_SERVICE_UUID); - NimBLECharacteristic* pControlChar = pService->createCharacteristic( - OTA_CONTROL_UUID, - NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY + // Command Characteristic (Write) + NimBLECharacteristic* pCommandChar = pService->createCharacteristic( + OTA_COMMAND_UUID, + NIMBLE_PROPERTY::WRITE ); - pControlChar->setCallbacks(new OtaControlCallback()); + pCommandChar->setCallbacks(new OtaCommandCallback()); + // Status Characteristic (Notify) + pStatusChar = pService->createCharacteristic( + OTA_STATUS_UUID, + NIMBLE_PROPERTY::NOTIFY + ); + + // Data Characteristic (Write No Response) NimBLECharacteristic* pDataChar = pService->createCharacteristic( OTA_DATA_UUID, NIMBLE_PROPERTY::WRITE_NR // Write No Response for speed @@ -147,4 +160,4 @@ void initOtaBleService(NimBLEServer* pServer) { bool isOtaInProgress() { return otaInProgress; -} +} \ No newline at end of file diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index ab1813f5..b6ef27e4 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -2,6 +2,7 @@ // OpenPPG #include "Arduino.h" #include "sp140/device_settings.h" +#include "sp140/ble/ota_service.h" #include "../../inc/sp140/esp32s3-config.h" @@ -225,7 +226,7 @@ void bleStateUpdateTask(void* parameter) { pDeviceStateCharacteristic->setValue(&update.state, sizeof(update.state)); // Only notify if requested and connected - if (update.needsNotify && deviceConnected) { + if (update.needsNotify && deviceConnected && !isOtaInProgress()) { vTaskDelay(pdMS_TO_TICKS(10)); // Additional delay before notify pDeviceStateCharacteristic->notify(); } From dd8cc21da96886447ecb1d07aab40155f367b789 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 15:13:50 -0600 Subject: [PATCH 03/22] Enable OTA rollback and confirm firmware validity Added CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE to sdkconfig.defaults to support OTA rollback. In main.cpp, added logic to mark the firmware as valid and cancel rollback after successful boot, ensuring OTA updates are confirmed. --- sdkconfig.defaults | 1 + src/sp140/main.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/sdkconfig.defaults b/sdkconfig.defaults index b7682f13..de6dfec4 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -4,3 +4,4 @@ CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y CONFIG_BT_ENABLED=y CONFIG_BT_CONTROLLER_ENABLED=y CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y \ No newline at end of file diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index b6ef27e4..b1f8eeb4 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -3,6 +3,7 @@ #include "Arduino.h" #include "sp140/device_settings.h" #include "sp140/ble/ota_service.h" +#include "esp_ota_ops.h" #include "../../inc/sp140/esp32s3-config.h" @@ -761,6 +762,11 @@ void setup() { // Simple instrumentation to detect UI/BMS latency USBSerial.println("Init complete. UI + BMS loop running"); + // Confirm successful boot to prevent OTA rollback + if (esp_ota_mark_app_valid_cancel_rollback() == ESP_OK) { + USBSerial.println("OTA: Firmware marked valid - Rollback cancelled"); + } + // Enable sensor monitoring after splash screen and UI setup // This gives BMS/ESC time to initialize and start providing valid data enableMonitoring(); From 4833b13fc6c41235493a27bbc5b38d3d4a07bc33 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 15:42:51 -0600 Subject: [PATCH 04/22] Refactor BLE OTA service to Espressif sector/CRC protocol Reworks OTA BLE service to use Espressif's sector-based protocol with CRC16 validation, sector buffering, and explicit ACK/ERR responses. OTA UUIDs are now NimBLEUUID constants for compatibility with official Espressif OTA apps. Improves reliability and error handling, and merges command/status characteristics as per Espressif's protocol. --- inc/sp140/ble/ble_ids.h | 13 +- src/sp140/ble/ota_service.cpp | 267 ++++++++++++++++++++++------------ 2 files changed, 176 insertions(+), 104 deletions(-) diff --git a/inc/sp140/ble/ble_ids.h b/inc/sp140/ble/ble_ids.h index dd5f944b..17bb05fb 100644 --- a/inc/sp140/ble/ble_ids.h +++ b/inc/sp140/ble/ble_ids.h @@ -67,12 +67,11 @@ #define CONTROLLER_SERVICE_UUID "01C63B60-0891-4655-BBCA-8E745C48A175" #define CONTROLLER_TELEMETRY_UUID "01C63B61-0891-4655-BBCA-8E745C48A176" -// ============================================================================ -// OTA Service (Firmware Update) - Espressif Standard -// ============================================================================ -#define OTA_SERVICE_UUID "00008018-0000-1000-8000-00805F9B34FB" -#define OTA_DATA_UUID "00008019-0000-1000-8000-00805F9B34FB" // RX: Firmware data -#define OTA_STATUS_UUID "0000801A-0000-1000-8000-00805F9B34FB" // TX: Notifications/Status -#define OTA_COMMAND_UUID "0000801B-0000-1000-8000-00805F9B34FB" // RX: Commands (Start/End) +// Espressif Standard OTA Service UUIDs +// Compatible with official Espressif BLE OTA apps (requires Sector/CRC protocol) +static const NimBLEUUID OTA_SERVICE_UUID("00008018-0000-1000-8000-00805f9b34fb"); +static const NimBLEUUID OTA_COMMAND_UUID("0000801a-0000-1000-8000-00805f9b34fb"); // Command (Write/Notify) +static const NimBLEUUID OTA_STATUS_UUID("0000801a-0000-1000-8000-00805f9b34fb"); // Status (Notify) - effectively same as Command in this protocol +static const NimBLEUUID OTA_DATA_UUID("00008019-0000-1000-8000-00805f9b34fb"); // Data (Write) #endif // INC_SP140_BLE_BLE_IDS_H_ diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index 6eaf6c82..c502a998 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -4,156 +4,229 @@ #include "esp_ota_ops.h" #include "esp_partition.h" #include "sp140/ble/ble_ids.h" +#include namespace { -// OTA Commands (matching Espressif example constants where applicable) -// Example: 0x01 = Start, 0x02 = End, 0x03 = Abort -const uint8_t CMD_START = 0x01; -const uint8_t CMD_END = 0x02; -const uint8_t CMD_ABORT = 0x03; +// Protocol Constants +const uint16_t CMD_START = 0x0001; +const uint16_t CMD_END = 0x0002; +const uint16_t CMD_ACK = 0x0003; -// OTA Responses -const uint8_t RESP_SUCCESS = 0x00; -const uint8_t RESP_ERROR_BEGIN = 0xE1; -const uint8_t RESP_ERROR_WRITE = 0xE2; -const uint8_t RESP_ERROR_END = 0xE3; -const uint8_t RESP_ERROR_UNKNOWN = 0xFF; +const uint16_t ACK_SUCCESS = 0x0000; +const uint16_t ACK_ERR_CRC = 0x0001; +const uint16_t ACK_ERR_SECTOR = 0x0002; +const uint16_t ACK_ERR_LEN = 0x0003; +// State volatile bool otaInProgress = false; esp_ota_handle_t updateHandle = 0; const esp_partition_t* updatePartition = nullptr; -size_t receivedBytes = 0; +volatile unsigned long lastOtaActivity = 0; + +// Buffering +uint8_t sectorBuffer[4096]; +uint16_t sectorBufferLen = 0; +uint16_t currentSectorIndex = 0; + +// Notify Characteristic +NimBLECharacteristic* pCommandChar = nullptr; + +// CRC16 Implementation (Polynomial 0x8005, reversed for LE) +// Matches Espressif's expected CRC16-ARC/Modbus +uint16_t crc16_le(uint16_t crc, const uint8_t *buffer, size_t len) { + while (len--) { + crc ^= *buffer++; + for (int i = 0; i < 8; i++) { + if (crc & 1) crc = (crc >> 1) ^ 0xA001; + else crc = (crc >> 1); + } + } + return crc; +} -// Status characteristic for notifications -NimBLECharacteristic* pStatusChar = nullptr; +void abortOta() { + if (otaInProgress) { + if (updateHandle) esp_ota_end(updateHandle); + otaInProgress = false; + USBSerial.println("OTA: Aborted."); + } + sectorBufferLen = 0; + currentSectorIndex = 0; +} -void sendResponse(uint8_t responseCode) { - if (pStatusChar) { - pStatusChar->setValue(&responseCode, 1); - pStatusChar->notify(); - USBSerial.printf("OTA: Sent response 0x%02X\n", responseCode); +void sendAck(uint16_t sector, uint16_t status) { + if (pCommandChar) { + uint8_t packet[6]; + // Sector (2B LE) + packet[0] = sector & 0xFF; + packet[1] = (sector >> 8) & 0xFF; + // Status (2B LE) + packet[2] = status & 0xFF; + packet[3] = (status >> 8) & 0xFF; + // CRC (2B LE) - CRC of the first 4 bytes + uint16_t crc = crc16_le(0, packet, 4); + packet[4] = crc & 0xFF; + packet[5] = (crc >> 8) & 0xFF; + + pCommandChar->setValue(packet, 6); + pCommandChar->notify(); + USBSerial.printf("OTA: Sent ACK Sector=%d Status=%d\n", sector, status); } } class OtaCommandCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { - (void)connInfo; std::string value = pChar->getValue(); - if (value.length() == 0) return; + if (value.length() < 4) return; // Min length check - uint8_t command = value[0]; + lastOtaActivity = millis(); + const uint8_t* data = (const uint8_t*)value.data(); + + uint16_t cmdId = data[0] | (data[1] << 8); - if (command == CMD_START) { - USBSerial.println("OTA: Start command received"); - + if (cmdId == CMD_START) { + USBSerial.println("OTA: CMD_START"); updatePartition = esp_ota_get_next_update_partition(nullptr); if (!updatePartition) { - USBSerial.println("OTA Error: No update partition found"); - sendResponse(RESP_ERROR_BEGIN); + USBSerial.println("OTA Error: No partition"); return; } - - USBSerial.printf("OTA: Writing to partition subtype %d at offset 0x%x\n", - updatePartition->subtype, updatePartition->address); - + esp_err_t err = esp_ota_begin(updatePartition, OTA_SIZE_UNKNOWN, &updateHandle); if (err != ESP_OK) { - USBSerial.printf("OTA Error: esp_ota_begin failed (0x%x)\n", err); - sendResponse(RESP_ERROR_BEGIN); - return; + USBSerial.printf("OTA Error: Begin failed 0x%x\n", err); + return; } otaInProgress = true; - receivedBytes = 0; - sendResponse(RESP_SUCCESS); - - } else if (command == CMD_END) { - USBSerial.println("OTA: End command received"); - - if (!otaInProgress) { - sendResponse(RESP_ERROR_UNKNOWN); - return; - } - - esp_err_t err = esp_ota_end(updateHandle); - if (err != ESP_OK) { - USBSerial.printf("OTA Error: esp_ota_end failed (0x%x)\n", err); - sendResponse(RESP_ERROR_END); - otaInProgress = false; - return; - } - - err = esp_ota_set_boot_partition(updatePartition); - if (err != ESP_OK) { - USBSerial.printf("OTA Error: set_boot_partition failed (0x%x)\n", err); - sendResponse(RESP_ERROR_END); - otaInProgress = false; - return; + sectorBufferLen = 0; + currentSectorIndex = 0; + + // Send Command Response (ID=3, Payload=Success) + uint8_t response[6] = { + 0x03, 0x00, // ID = 3 (Response) + 0x00, 0x00, // Payload = 0 (Success) + 0x00, 0x00 // CRC placeholder (TODO: calculate if strictly needed) + }; + pChar->setValue(response, 6); + pChar->notify(); + + } else if (cmdId == CMD_END) { + USBSerial.println("OTA: CMD_END"); + if (otaInProgress) { + if (esp_ota_end(updateHandle) == ESP_OK) { + if (esp_ota_set_boot_partition(updatePartition) == ESP_OK) { + USBSerial.println("OTA Success. Restarting..."); + + uint8_t response[6] = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }; + pChar->setValue(response, 6); + pChar->notify(); + + delay(500); + ESP.restart(); + } + } + abortOta(); } - - USBSerial.println("OTA: Success! Restarting..."); - sendResponse(RESP_SUCCESS); - delay(1000); - ESP.restart(); - - } else if (command == CMD_ABORT) { - USBSerial.println("OTA: Abort command received"); - if (otaInProgress) { - esp_ota_end(updateHandle); - otaInProgress = false; - } - sendResponse(RESP_SUCCESS); } } }; class OtaDataCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { - (void)connInfo; if (!otaInProgress) return; + + std::string valStr = pChar->getValue(); + size_t len = valStr.length(); + const uint8_t* data = (const uint8_t*)valStr.data(); + + if (len < 3) return; // Header: Sector(2) + Seq(1) + + lastOtaActivity = millis(); + + uint16_t sector = data[0] | (data[1] << 8); + uint8_t seq = data[2]; + const uint8_t* payload = data + 3; + size_t payloadLen = len - 3; + + if (sector != currentSectorIndex) { + // Silently ignore or warn? Official app might retry if we don't ACK. + // But if we send ACK_ERR_SECTOR, it forces resync. + USBSerial.printf("OTA Warn: Sector mismatch exp %d got %d\n", currentSectorIndex, sector); + sendAck(sector, ACK_ERR_SECTOR); + return; + } - std::string value = pChar->getValue(); - if (value.length() > 0) { - esp_err_t err = esp_ota_write(updateHandle, value.data(), value.length()); - if (err != ESP_OK) { - USBSerial.printf("OTA Error: Write failed (0x%x)\n", err); - sendResponse(RESP_ERROR_WRITE); - // Abort? - } else { - receivedBytes += value.length(); - if (receivedBytes % 10240 < value.length()) { - USBSerial.printf("OTA: Received %d bytes\n", receivedBytes); + if (seq == 0xFF) { // Last packet of sector: Payload contains Data + CRC16(2) + if (payloadLen < 2) return; + + size_t actualDataLen = payloadLen - 2; + + if (sectorBufferLen + actualDataLen > sizeof(sectorBuffer)) { + sendAck(sector, ACK_ERR_LEN); + sectorBufferLen = 0; // Reset buffer + return; + } + + memcpy(sectorBuffer + sectorBufferLen, payload, actualDataLen); + sectorBufferLen += actualDataLen; + + // Verify CRC + // CRC is at the end of the packet payload + uint16_t rxCrc = payload[actualDataLen] | (payload[actualDataLen + 1] << 8); + uint16_t calcCrc = crc16_le(0, sectorBuffer, sectorBufferLen); + + if (rxCrc == calcCrc) { + // Write to flash + esp_err_t err = esp_ota_write(updateHandle, sectorBuffer, sectorBufferLen); + if (err == ESP_OK) { + sendAck(sector, ACK_SUCCESS); + currentSectorIndex++; + sectorBufferLen = 0; + } else { + USBSerial.printf("OTA Error: Write failed 0x%x\n", err); + abortOta(); } + } else { + USBSerial.printf("OTA Error: CRC fail exp 0x%04X got 0x%04X\n", rxCrc, calcCrc); + sendAck(sector, ACK_ERR_CRC); + sectorBufferLen = 0; // Reset for retry } + + } else { // Normal packet + if (sectorBufferLen + payloadLen > sizeof(sectorBuffer)) { + USBSerial.println("OTA Error: Buffer overflow"); + // Don't ACK, let client retry or timeout + return; + } + memcpy(sectorBuffer + sectorBufferLen, payload, payloadLen); + sectorBufferLen += payloadLen; } } }; +static OtaCommandCallback cmdCallback; +static OtaDataCallback dataCallback; + } // namespace void initOtaBleService(NimBLEServer* pServer) { NimBLEService* pService = pServer->createService(OTA_SERVICE_UUID); - // Command Characteristic (Write) - NimBLECharacteristic* pCommandChar = pService->createCharacteristic( + // Command (Write + Notify for ACKs) + pCommandChar = pService->createCharacteristic( OTA_COMMAND_UUID, - NIMBLE_PROPERTY::WRITE - ); - pCommandChar->setCallbacks(new OtaCommandCallback()); - - // Status Characteristic (Notify) - pStatusChar = pService->createCharacteristic( - OTA_STATUS_UUID, - NIMBLE_PROPERTY::NOTIFY + NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY ); + pCommandChar->setCallbacks(&cmdCallback); - // Data Characteristic (Write No Response) + // Data (Write No Response) NimBLECharacteristic* pDataChar = pService->createCharacteristic( OTA_DATA_UUID, - NIMBLE_PROPERTY::WRITE_NR // Write No Response for speed + NIMBLE_PROPERTY::WRITE_NR ); - pDataChar->setCallbacks(new OtaDataCallback()); + pDataChar->setCallbacks(&dataCallback); pService->start(); } From a9a2b10b525b2d46541f3e510165aa0ee3977cc0 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 16:39:07 -0600 Subject: [PATCH 05/22] Update ota_service.cpp --- src/sp140/ble/ota_service.cpp | 136 +++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 43 deletions(-) diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index c502a998..e827dd4e 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -4,8 +4,12 @@ #include "esp_ota_ops.h" #include "esp_partition.h" #include "sp140/ble/ble_ids.h" +#include "sp140/device_state.h" #include +// Access global state to block OTA when ARMED +extern volatile DeviceState currentState; + namespace { // Protocol Constants @@ -22,7 +26,6 @@ const uint16_t ACK_ERR_LEN = 0x0003; volatile bool otaInProgress = false; esp_ota_handle_t updateHandle = 0; const esp_partition_t* updatePartition = nullptr; -volatile unsigned long lastOtaActivity = 0; // Buffering uint8_t sectorBuffer[4096]; @@ -33,7 +36,6 @@ uint16_t currentSectorIndex = 0; NimBLECharacteristic* pCommandChar = nullptr; // CRC16 Implementation (Polynomial 0x8005, reversed for LE) -// Matches Espressif's expected CRC16-ARC/Modbus uint16_t crc16_le(uint16_t crc, const uint8_t *buffer, size_t len) { while (len--) { crc ^= *buffer++; @@ -45,26 +47,14 @@ uint16_t crc16_le(uint16_t crc, const uint8_t *buffer, size_t len) { return crc; } -void abortOta() { - if (otaInProgress) { - if (updateHandle) esp_ota_end(updateHandle); - otaInProgress = false; - USBSerial.println("OTA: Aborted."); - } - sectorBufferLen = 0; - currentSectorIndex = 0; -} - void sendAck(uint16_t sector, uint16_t status) { if (pCommandChar) { uint8_t packet[6]; - // Sector (2B LE) packet[0] = sector & 0xFF; packet[1] = (sector >> 8) & 0xFF; - // Status (2B LE) packet[2] = status & 0xFF; packet[3] = (status >> 8) & 0xFF; - // CRC (2B LE) - CRC of the first 4 bytes + uint16_t crc = crc16_le(0, packet, 4); packet[4] = crc & 0xFF; packet[5] = (crc >> 8) & 0xFF; @@ -75,27 +65,89 @@ void sendAck(uint16_t sector, uint16_t status) { } } +// Send Command Response (ID=3, Payload, CRC) +void sendCommandResponse(uint16_t status) { + if (pCommandChar) { + uint8_t packet[20]; // 2 ID + 16 Payload + 2 CRC + memset(packet, 0, 20); // Fill payload with 0 + + packet[0] = CMD_ACK & 0xFF; // 0x03 + packet[1] = (CMD_ACK >> 8) & 0xFF; // 0x00 + + // Payload (Bytes 2-17): Byte 2-3 = Status + packet[2] = status & 0xFF; + packet[3] = (status >> 8) & 0xFF; + + uint16_t crc = crc16_le(0, packet, 18); + packet[18] = crc & 0xFF; + packet[19] = (crc >> 8) & 0xFF; + + pCommandChar->setValue(packet, 20); + pCommandChar->notify(); + USBSerial.printf("OTA: Sent CMD Response Status=%d\n", status); + } +} + +void abortOta() { + if (otaInProgress) { + if (updateHandle) esp_ota_abort(updateHandle); + otaInProgress = false; + USBSerial.println("OTA: Aborted."); + } + sectorBufferLen = 0; + currentSectorIndex = 0; +} + class OtaCommandCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { std::string value = pChar->getValue(); - if (value.length() < 4) return; // Min length check + // Command Packet: ID(2) + Payload(16) + CRC(2) = 20 bytes + if (value.length() < 20) { + USBSerial.println("OTA Error: Invalid CMD length"); + return; + } - lastOtaActivity = millis(); const uint8_t* data = (const uint8_t*)value.data(); + // Verify CRC + uint16_t rxCrc = data[18] | (data[19] << 8); + uint16_t calcCrc = crc16_le(0, data, 18); + if (rxCrc != calcCrc) { + USBSerial.printf("OTA Error: CMD CRC Fail Exp %04X Got %04X\n", rxCrc, calcCrc); + return; + } + uint16_t cmdId = data[0] | (data[1] << 8); if (cmdId == CMD_START) { USBSerial.println("OTA: CMD_START"); + + // Safety Check: Block if Armed + if (currentState == ARMED || currentState == ARMED_CRUISING) { + USBSerial.println("OTA Blocked: Device ARMED"); + sendCommandResponse(0x0001); // Reject (Status 1) + return; + } + updatePartition = esp_ota_get_next_update_partition(nullptr); if (!updatePartition) { USBSerial.println("OTA Error: No partition"); + sendCommandResponse(0x0001); // Reject + return; + } + + // Validate image length from payload (Bytes 2-5) + uint32_t imageLen = data[2] | (data[3] << 8) | (data[4] << 16) | (data[5] << 24); + if (imageLen > updatePartition->size) { + USBSerial.printf("OTA Error: Image size %u > partition %u\n", imageLen, updatePartition->size); + sendCommandResponse(0x0001); return; } esp_err_t err = esp_ota_begin(updatePartition, OTA_SIZE_UNKNOWN, &updateHandle); if (err != ESP_OK) { USBSerial.printf("OTA Error: Begin failed 0x%x\n", err); + sendCommandResponse(0x0001); // Reject return; } @@ -103,14 +155,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { sectorBufferLen = 0; currentSectorIndex = 0; - // Send Command Response (ID=3, Payload=Success) - uint8_t response[6] = { - 0x03, 0x00, // ID = 3 (Response) - 0x00, 0x00, // Payload = 0 (Success) - 0x00, 0x00 // CRC placeholder (TODO: calculate if strictly needed) - }; - pChar->setValue(response, 6); - pChar->notify(); + sendCommandResponse(0x0000); // Accept (Status 0) } else if (cmdId == CMD_END) { USBSerial.println("OTA: CMD_END"); @@ -118,16 +163,21 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { if (esp_ota_end(updateHandle) == ESP_OK) { if (esp_ota_set_boot_partition(updatePartition) == ESP_OK) { USBSerial.println("OTA Success. Restarting..."); - - uint8_t response[6] = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }; - pChar->setValue(response, 6); - pChar->notify(); - + sendCommandResponse(0x0000); // Success delay(500); ESP.restart(); + return; + } else { + USBSerial.println("OTA Error: Set Boot Partition Failed"); } + } else { + USBSerial.println("OTA Error: OTA End Failed"); } + // If we get here, something failed + sendCommandResponse(0x0001); // Reject/Fail abortOta(); + } else { + sendCommandResponse(0x0001); // Not in progress } } } @@ -135,7 +185,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { class OtaDataCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { - if (!otaInProgress) return; + if (!otaInProgress) return; std::string valStr = pChar->getValue(); size_t len = valStr.length(); @@ -143,37 +193,34 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (len < 3) return; // Header: Sector(2) + Seq(1) - lastOtaActivity = millis(); - uint16_t sector = data[0] | (data[1] << 8); uint8_t seq = data[2]; const uint8_t* payload = data + 3; size_t payloadLen = len - 3; if (sector != currentSectorIndex) { - // Silently ignore or warn? Official app might retry if we don't ACK. - // But if we send ACK_ERR_SECTOR, it forces resync. USBSerial.printf("OTA Warn: Sector mismatch exp %d got %d\n", currentSectorIndex, sector); + sectorBufferLen = 0; // Reset buffer on mismatch sendAck(sector, ACK_ERR_SECTOR); return; } - if (seq == 0xFF) { // Last packet of sector: Payload contains Data + CRC16(2) + if (seq == 0xFF) { + // Last packet of sector: Payload contains Data + CRC16(2) if (payloadLen < 2) return; size_t actualDataLen = payloadLen - 2; if (sectorBufferLen + actualDataLen > sizeof(sectorBuffer)) { sendAck(sector, ACK_ERR_LEN); - sectorBufferLen = 0; // Reset buffer + sectorBufferLen = 0; return; } memcpy(sectorBuffer + sectorBufferLen, payload, actualDataLen); sectorBufferLen += actualDataLen; - // Verify CRC - // CRC is at the end of the packet payload + // Verify CRC (Last 2 bytes of payload) uint16_t rxCrc = payload[actualDataLen] | (payload[actualDataLen + 1] << 8); uint16_t calcCrc = crc16_le(0, sectorBuffer, sectorBufferLen); @@ -186,6 +233,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { sectorBufferLen = 0; } else { USBSerial.printf("OTA Error: Write failed 0x%x\n", err); + sendAck(sector, ACK_ERR_CRC); // Force retry abortOta(); } } else { @@ -194,10 +242,12 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { sectorBufferLen = 0; // Reset for retry } - } else { // Normal packet + } else { + // Normal packet if (sectorBufferLen + payloadLen > sizeof(sectorBuffer)) { USBSerial.println("OTA Error: Buffer overflow"); - // Don't ACK, let client retry or timeout + sectorBufferLen = 0; + sendAck(sector, ACK_ERR_LEN); return; } memcpy(sectorBuffer + sectorBufferLen, payload, payloadLen); @@ -214,7 +264,7 @@ static OtaDataCallback dataCallback; void initOtaBleService(NimBLEServer* pServer) { NimBLEService* pService = pServer->createService(OTA_SERVICE_UUID); - // Command (Write + Notify for ACKs) + // Command (Write + Notify) pCommandChar = pService->createCharacteristic( OTA_COMMAND_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY @@ -233,4 +283,4 @@ void initOtaBleService(NimBLEServer* pServer) { bool isOtaInProgress() { return otaInProgress; -} \ No newline at end of file +} From cd1304fb92e9de5507b13e062e1f2dfae066b244 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 16:42:45 -0600 Subject: [PATCH 06/22] Enhance OTA service with image size tracking and validation Added tracking of total image length and received bytes during OTA updates. The service now validates that the received image size matches the expected size before completing the update, improving reliability and error handling. Also refined CRC and sector error handling to allow client-side retries instead of aborting the OTA process immediately. --- src/sp140/ble/ota_service.cpp | 53 +++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index e827dd4e..43e352c6 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -24,6 +24,7 @@ const uint16_t ACK_ERR_LEN = 0x0003; // State volatile bool otaInProgress = false; +volatile size_t imageTotalLen = 0; esp_ota_handle_t updateHandle = 0; const esp_partition_t* updatePartition = nullptr; @@ -31,6 +32,7 @@ const esp_partition_t* updatePartition = nullptr; uint8_t sectorBuffer[4096]; uint16_t sectorBufferLen = 0; uint16_t currentSectorIndex = 0; +size_t receivedBytes = 0; // Notify Characteristic NimBLECharacteristic* pCommandChar = nullptr; @@ -66,10 +68,11 @@ void sendAck(uint16_t sector, uint16_t status) { } // Send Command Response (ID=3, Payload, CRC) +// Matches esp_ble_ota_cmd_t structure: 20 bytes total void sendCommandResponse(uint16_t status) { if (pCommandChar) { uint8_t packet[20]; // 2 ID + 16 Payload + 2 CRC - memset(packet, 0, 20); // Fill payload with 0 + memset(packet, 0, 20); packet[0] = CMD_ACK & 0xFF; // 0x03 packet[1] = (CMD_ACK >> 8) & 0xFF; // 0x00 @@ -96,6 +99,7 @@ void abortOta() { } sectorBufferLen = 0; currentSectorIndex = 0; + receivedBytes = 0; } class OtaCommandCallback : public NimBLECharacteristicCallbacks { @@ -104,6 +108,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { // Command Packet: ID(2) + Payload(16) + CRC(2) = 20 bytes if (value.length() < 20) { USBSerial.println("OTA Error: Invalid CMD length"); + // Can't reliably reply if packet structure is broken return; } @@ -114,6 +119,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { uint16_t calcCrc = crc16_le(0, data, 18); if (rxCrc != calcCrc) { USBSerial.printf("OTA Error: CMD CRC Fail Exp %04X Got %04X\n", rxCrc, calcCrc); + sendCommandResponse(0x0001); // Reject (Status 1) return; } @@ -122,21 +128,20 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { if (cmdId == CMD_START) { USBSerial.println("OTA: CMD_START"); - // Safety Check: Block if Armed if (currentState == ARMED || currentState == ARMED_CRUISING) { USBSerial.println("OTA Blocked: Device ARMED"); - sendCommandResponse(0x0001); // Reject (Status 1) + sendCommandResponse(0x0001); // Reject return; } updatePartition = esp_ota_get_next_update_partition(nullptr); if (!updatePartition) { USBSerial.println("OTA Error: No partition"); - sendCommandResponse(0x0001); // Reject + sendCommandResponse(0x0001); return; } - // Validate image length from payload (Bytes 2-5) + // Validate image length (Bytes 2-5 of payload -> data[2]..data[5]) uint32_t imageLen = data[2] | (data[3] << 8) | (data[4] << 16) | (data[5] << 24); if (imageLen > updatePartition->size) { USBSerial.printf("OTA Error: Image size %u > partition %u\n", imageLen, updatePartition->size); @@ -147,24 +152,34 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { esp_err_t err = esp_ota_begin(updatePartition, OTA_SIZE_UNKNOWN, &updateHandle); if (err != ESP_OK) { USBSerial.printf("OTA Error: Begin failed 0x%x\n", err); - sendCommandResponse(0x0001); // Reject + sendCommandResponse(0x0001); return; } otaInProgress = true; + imageTotalLen = imageLen; + receivedBytes = 0; sectorBufferLen = 0; currentSectorIndex = 0; - sendCommandResponse(0x0000); // Accept (Status 0) + sendCommandResponse(0x0000); // Accept } else if (cmdId == CMD_END) { USBSerial.println("OTA: CMD_END"); if (otaInProgress) { + // Defensive: Verify total bytes received match manifest + if (receivedBytes != imageTotalLen) { + USBSerial.printf("OTA Error: Size Mismatch Rx:%u Exp:%u\n", receivedBytes, imageTotalLen); + sendCommandResponse(0x0001); // Fail + abortOta(); + return; + } + if (esp_ota_end(updateHandle) == ESP_OK) { if (esp_ota_set_boot_partition(updatePartition) == ESP_OK) { USBSerial.println("OTA Success. Restarting..."); sendCommandResponse(0x0000); // Success - delay(500); + delay(1000); // Allow BLE flush ESP.restart(); return; } else { @@ -173,8 +188,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { } else { USBSerial.println("OTA Error: OTA End Failed"); } - // If we get here, something failed - sendCommandResponse(0x0001); // Reject/Fail + sendCommandResponse(0x0001); // Fail abortOta(); } else { sendCommandResponse(0x0001); // Not in progress @@ -185,7 +199,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { class OtaDataCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { - if (!otaInProgress) return; + if (!otaInProgress) return; std::string valStr = pChar->getValue(); size_t len = valStr.length(); @@ -200,13 +214,13 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (sector != currentSectorIndex) { USBSerial.printf("OTA Warn: Sector mismatch exp %d got %d\n", currentSectorIndex, sector); - sectorBufferLen = 0; // Reset buffer on mismatch + sectorBufferLen = 0; sendAck(sector, ACK_ERR_SECTOR); return; } if (seq == 0xFF) { - // Last packet of sector: Payload contains Data + CRC16(2) + // Last packet: Payload is Data + CRC16(2) if (payloadLen < 2) return; size_t actualDataLen = payloadLen - 2; @@ -220,26 +234,29 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { memcpy(sectorBuffer + sectorBufferLen, payload, actualDataLen); sectorBufferLen += actualDataLen; - // Verify CRC (Last 2 bytes of payload) + // Verify CRC uint16_t rxCrc = payload[actualDataLen] | (payload[actualDataLen + 1] << 8); uint16_t calcCrc = crc16_le(0, sectorBuffer, sectorBufferLen); if (rxCrc == calcCrc) { - // Write to flash esp_err_t err = esp_ota_write(updateHandle, sectorBuffer, sectorBufferLen); if (err == ESP_OK) { + receivedBytes += sectorBufferLen; sendAck(sector, ACK_SUCCESS); currentSectorIndex++; sectorBufferLen = 0; } else { USBSerial.printf("OTA Error: Write failed 0x%x\n", err); - sendAck(sector, ACK_ERR_CRC); // Force retry - abortOta(); + sendAck(sector, ACK_ERR_SECTOR); // Force retry + sectorBufferLen = 0; + // Don't hard abort yet, allow retry? + // Espressif tools usually retry logic is client side. + // If we return ERR_SECTOR, client re-sends sector. } } else { USBSerial.printf("OTA Error: CRC fail exp 0x%04X got 0x%04X\n", rxCrc, calcCrc); sendAck(sector, ACK_ERR_CRC); - sectorBufferLen = 0; // Reset for retry + sectorBufferLen = 0; } } else { From faeb2ca3cdf0d7a0181fc36011963d61650f2399 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 17:12:03 -0600 Subject: [PATCH 07/22] Update BLE OTA service UUIDs and protocol Revised BLE OTA characteristic UUIDs to match Android esp-ble-ota-android app requirements and updated protocol handling in ota_service.cpp. ACK and command response logic now use new characteristics and payload formats. Also added 'rbl' as an alias for the 'reboot' serial command. --- inc/sp140/ble/ble_ids.h | 10 ++-- src/sp140/ble/ota_service.cpp | 93 +++++++++++++++++++++-------------- src/sp140/device_settings.cpp | 2 +- 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/inc/sp140/ble/ble_ids.h b/inc/sp140/ble/ble_ids.h index 17bb05fb..5582af05 100644 --- a/inc/sp140/ble/ble_ids.h +++ b/inc/sp140/ble/ble_ids.h @@ -67,11 +67,11 @@ #define CONTROLLER_SERVICE_UUID "01C63B60-0891-4655-BBCA-8E745C48A175" #define CONTROLLER_TELEMETRY_UUID "01C63B61-0891-4655-BBCA-8E745C48A176" -// Espressif Standard OTA Service UUIDs -// Compatible with official Espressif BLE OTA apps (requires Sector/CRC protocol) +// Espressif Standard OTA Service UUIDs (Android esp-ble-ota-android app) static const NimBLEUUID OTA_SERVICE_UUID("00008018-0000-1000-8000-00805f9b34fb"); -static const NimBLEUUID OTA_COMMAND_UUID("0000801a-0000-1000-8000-00805f9b34fb"); // Command (Write/Notify) -static const NimBLEUUID OTA_STATUS_UUID("0000801a-0000-1000-8000-00805f9b34fb"); // Status (Notify) - effectively same as Command in this protocol -static const NimBLEUUID OTA_DATA_UUID("00008019-0000-1000-8000-00805f9b34fb"); // Data (Write) +static const NimBLEUUID OTA_RECV_FW_UUID("00008020-0000-1000-8000-00805f9b34fb"); // Firmware data (Write/Indicate) +static const NimBLEUUID OTA_PROGRESS_UUID("00008021-0000-1000-8000-00805f9b34fb"); // Progress (Indicate) +static const NimBLEUUID OTA_COMMAND_UUID("00008022-0000-1000-8000-00805f9b34fb"); // Command (Write/Indicate) +static const NimBLEUUID OTA_CUSTOMER_UUID("00008023-0000-1000-8000-00805f9b34fb"); // Customer (Indicate) #endif // INC_SP140_BLE_BLE_IDS_H_ diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index 43e352c6..85189922 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -34,8 +34,11 @@ uint16_t sectorBufferLen = 0; uint16_t currentSectorIndex = 0; size_t receivedBytes = 0; -// Notify Characteristic -NimBLECharacteristic* pCommandChar = nullptr; +// OTA characteristics +NimBLECharacteristic* pRecvFwChar = nullptr; +NimBLECharacteristic* pProgressChar = nullptr; +NimBLECharacteristic* pCommandChar = nullptr; +NimBLECharacteristic* pCustomerChar = nullptr; // CRC16 Implementation (Polynomial 0x8005, reversed for LE) uint16_t crc16_le(uint16_t crc, const uint8_t *buffer, size_t len) { @@ -49,27 +52,27 @@ uint16_t crc16_le(uint16_t crc, const uint8_t *buffer, size_t len) { return crc; } -void sendAck(uint16_t sector, uint16_t status) { - if (pCommandChar) { +void sendAck(uint16_t sector, uint16_t status, uint16_t expectedSector = 0) { + if (pRecvFwChar) { uint8_t packet[6]; packet[0] = sector & 0xFF; packet[1] = (sector >> 8) & 0xFF; packet[2] = status & 0xFF; packet[3] = (status >> 8) & 0xFF; - - uint16_t crc = crc16_le(0, packet, 4); - packet[4] = crc & 0xFF; - packet[5] = (crc >> 8) & 0xFF; - - pCommandChar->setValue(packet, 6); - pCommandChar->notify(); + + // Android app expects expected sector index in bytes 4-5 on sector error. + packet[4] = expectedSector & 0xFF; + packet[5] = (expectedSector >> 8) & 0xFF; + + pRecvFwChar->setValue(packet, 6); + pRecvFwChar->indicate(); USBSerial.printf("OTA: Sent ACK Sector=%d Status=%d\n", sector, status); } } // Send Command Response (ID=3, Payload, CRC) // Matches esp_ble_ota_cmd_t structure: 20 bytes total -void sendCommandResponse(uint16_t status) { +void sendCommandResponse(uint16_t ackId, uint16_t status) { if (pCommandChar) { uint8_t packet[20]; // 2 ID + 16 Payload + 2 CRC memset(packet, 0, 20); @@ -77,17 +80,19 @@ void sendCommandResponse(uint16_t status) { packet[0] = CMD_ACK & 0xFF; // 0x03 packet[1] = (CMD_ACK >> 8) & 0xFF; // 0x00 - // Payload (Bytes 2-17): Byte 2-3 = Status - packet[2] = status & 0xFF; - packet[3] = (status >> 8) & 0xFF; + // Payload (Bytes 2-17): Byte 2-3 = Ack ID, Byte 4-5 = Status + packet[2] = ackId & 0xFF; + packet[3] = (ackId >> 8) & 0xFF; + packet[4] = status & 0xFF; + packet[5] = (status >> 8) & 0xFF; uint16_t crc = crc16_le(0, packet, 18); packet[18] = crc & 0xFF; packet[19] = (crc >> 8) & 0xFF; pCommandChar->setValue(packet, 20); - pCommandChar->notify(); - USBSerial.printf("OTA: Sent CMD Response Status=%d\n", status); + pCommandChar->indicate(); + USBSerial.printf("OTA: Sent CMD Response AckId=%d Status=%d\n", ackId, status); } } @@ -114,30 +119,30 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { const uint8_t* data = (const uint8_t*)value.data(); + uint16_t cmdId = data[0] | (data[1] << 8); + // Verify CRC uint16_t rxCrc = data[18] | (data[19] << 8); uint16_t calcCrc = crc16_le(0, data, 18); if (rxCrc != calcCrc) { USBSerial.printf("OTA Error: CMD CRC Fail Exp %04X Got %04X\n", rxCrc, calcCrc); - sendCommandResponse(0x0001); // Reject (Status 1) + sendCommandResponse(cmdId, 0x0001); // Reject (Status 1) return; } - - uint16_t cmdId = data[0] | (data[1] << 8); if (cmdId == CMD_START) { USBSerial.println("OTA: CMD_START"); if (currentState == ARMED || currentState == ARMED_CRUISING) { USBSerial.println("OTA Blocked: Device ARMED"); - sendCommandResponse(0x0001); // Reject + sendCommandResponse(CMD_START, 0x0001); // Reject return; } updatePartition = esp_ota_get_next_update_partition(nullptr); if (!updatePartition) { USBSerial.println("OTA Error: No partition"); - sendCommandResponse(0x0001); + sendCommandResponse(CMD_START, 0x0001); return; } @@ -145,14 +150,14 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { uint32_t imageLen = data[2] | (data[3] << 8) | (data[4] << 16) | (data[5] << 24); if (imageLen > updatePartition->size) { USBSerial.printf("OTA Error: Image size %u > partition %u\n", imageLen, updatePartition->size); - sendCommandResponse(0x0001); + sendCommandResponse(CMD_START, 0x0001); return; } esp_err_t err = esp_ota_begin(updatePartition, OTA_SIZE_UNKNOWN, &updateHandle); if (err != ESP_OK) { USBSerial.printf("OTA Error: Begin failed 0x%x\n", err); - sendCommandResponse(0x0001); + sendCommandResponse(CMD_START, 0x0001); return; } @@ -162,7 +167,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { sectorBufferLen = 0; currentSectorIndex = 0; - sendCommandResponse(0x0000); // Accept + sendCommandResponse(CMD_START, 0x0000); // Accept } else if (cmdId == CMD_END) { USBSerial.println("OTA: CMD_END"); @@ -170,7 +175,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { // Defensive: Verify total bytes received match manifest if (receivedBytes != imageTotalLen) { USBSerial.printf("OTA Error: Size Mismatch Rx:%u Exp:%u\n", receivedBytes, imageTotalLen); - sendCommandResponse(0x0001); // Fail + sendCommandResponse(CMD_END, 0x0001); // Fail abortOta(); return; } @@ -178,7 +183,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { if (esp_ota_end(updateHandle) == ESP_OK) { if (esp_ota_set_boot_partition(updatePartition) == ESP_OK) { USBSerial.println("OTA Success. Restarting..."); - sendCommandResponse(0x0000); // Success + sendCommandResponse(CMD_END, 0x0000); // Success delay(1000); // Allow BLE flush ESP.restart(); return; @@ -188,10 +193,10 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { } else { USBSerial.println("OTA Error: OTA End Failed"); } - sendCommandResponse(0x0001); // Fail + sendCommandResponse(CMD_END, 0x0001); // Fail abortOta(); } else { - sendCommandResponse(0x0001); // Not in progress + sendCommandResponse(CMD_END, 0x0001); // Not in progress } } } @@ -215,7 +220,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (sector != currentSectorIndex) { USBSerial.printf("OTA Warn: Sector mismatch exp %d got %d\n", currentSectorIndex, sector); sectorBufferLen = 0; - sendAck(sector, ACK_ERR_SECTOR); + sendAck(sector, ACK_ERR_SECTOR, currentSectorIndex); return; } @@ -247,7 +252,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { sectorBufferLen = 0; } else { USBSerial.printf("OTA Error: Write failed 0x%x\n", err); - sendAck(sector, ACK_ERR_SECTOR); // Force retry + sendAck(sector, ACK_ERR_SECTOR, currentSectorIndex); // Force retry sectorBufferLen = 0; // Don't hard abort yet, allow retry? // Espressif tools usually retry logic is client side. @@ -281,19 +286,31 @@ static OtaDataCallback dataCallback; void initOtaBleService(NimBLEServer* pServer) { NimBLEService* pService = pServer->createService(OTA_SERVICE_UUID); - // Command (Write + Notify) + // Firmware data (Write No Response + Indicate for ACKs) + pRecvFwChar = pService->createCharacteristic( + OTA_RECV_FW_UUID, + NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::INDICATE + ); + pRecvFwChar->setCallbacks(&dataCallback); + + // Progress (Indicate only; app subscribes) + pProgressChar = pService->createCharacteristic( + OTA_PROGRESS_UUID, + NIMBLE_PROPERTY::INDICATE + ); + + // Command (Write + Indicate) pCommandChar = pService->createCharacteristic( OTA_COMMAND_UUID, - NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY + NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::INDICATE ); pCommandChar->setCallbacks(&cmdCallback); - // Data (Write No Response) - NimBLECharacteristic* pDataChar = pService->createCharacteristic( - OTA_DATA_UUID, - NIMBLE_PROPERTY::WRITE_NR + // Customer (Indicate only; app subscribes) + pCustomerChar = pService->createCharacteristic( + OTA_CUSTOMER_UUID, + NIMBLE_PROPERTY::INDICATE ); - pDataChar->setCallbacks(&dataCallback); pService->start(); } diff --git a/src/sp140/device_settings.cpp b/src/sp140/device_settings.cpp index 33355e46..139b6647 100644 --- a/src/sp140/device_settings.cpp +++ b/src/sp140/device_settings.cpp @@ -214,7 +214,7 @@ void parse_serial_commands() { if (!doc["command"].isNull()) { String command = doc["command"].as(); - if (command == "reboot") { + if (command == "reboot" || command == "rbl") { USBSerial.println("Rebooting"); ESP.restart(); return; From 756d6fb262a9dd421f9ad4c8aba423696790b493 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 26 Jan 2026 17:23:12 -0600 Subject: [PATCH 08/22] linting --- inc/sp140/device_settings.h | 2 +- src/sp140/ble/ota_service.cpp | 80 +++++++++++++++++------------------ src/sp140/main.cpp | 21 +++++---- 3 files changed, 49 insertions(+), 54 deletions(-) diff --git a/inc/sp140/device_settings.h b/inc/sp140/device_settings.h index 4fc0acc6..988ca955 100644 --- a/inc/sp140/device_settings.h +++ b/inc/sp140/device_settings.h @@ -14,4 +14,4 @@ void send_device_data(); bool sanitizeDeviceData(); void debugHardwareConfig(const HardwareConfig& config); -#endif // INC_SP140_DEVICE_SETTINGS_H_ +#endif // INC_SP140_DEVICE_SETTINGS_H_ diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index 85189922..7544552f 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -74,22 +74,22 @@ void sendAck(uint16_t sector, uint16_t status, uint16_t expectedSector = 0) { // Matches esp_ble_ota_cmd_t structure: 20 bytes total void sendCommandResponse(uint16_t ackId, uint16_t status) { if (pCommandChar) { - uint8_t packet[20]; // 2 ID + 16 Payload + 2 CRC - memset(packet, 0, 20); - - packet[0] = CMD_ACK & 0xFF; // 0x03 - packet[1] = (CMD_ACK >> 8) & 0xFF; // 0x00 - + uint8_t packet[20]; // 2 ID + 16 Payload + 2 CRC + memset(packet, 0, 20); + + packet[0] = CMD_ACK & 0xFF; // 0x03 + packet[1] = (CMD_ACK >> 8) & 0xFF; // 0x00 + // Payload (Bytes 2-17): Byte 2-3 = Ack ID, Byte 4-5 = Status packet[2] = ackId & 0xFF; packet[3] = (ackId >> 8) & 0xFF; packet[4] = status & 0xFF; packet[5] = (status >> 8) & 0xFF; - + uint16_t crc = crc16_le(0, packet, 18); packet[18] = crc & 0xFF; packet[19] = (crc >> 8) & 0xFF; - + pCommandChar->setValue(packet, 20); pCommandChar->indicate(); USBSerial.printf("OTA: Sent CMD Response AckId=%d Status=%d\n", ackId, status); @@ -118,7 +118,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { } const uint8_t* data = (const uint8_t*)value.data(); - + uint16_t cmdId = data[0] | (data[1] << 8); // Verify CRC @@ -126,26 +126,26 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { uint16_t calcCrc = crc16_le(0, data, 18); if (rxCrc != calcCrc) { USBSerial.printf("OTA Error: CMD CRC Fail Exp %04X Got %04X\n", rxCrc, calcCrc); - sendCommandResponse(cmdId, 0x0001); // Reject (Status 1) + sendCommandResponse(cmdId, 0x0001); // Reject (Status 1) return; } if (cmdId == CMD_START) { USBSerial.println("OTA: CMD_START"); - + if (currentState == ARMED || currentState == ARMED_CRUISING) { USBSerial.println("OTA Blocked: Device ARMED"); - sendCommandResponse(CMD_START, 0x0001); // Reject + sendCommandResponse(CMD_START, 0x0001); // Reject return; } updatePartition = esp_ota_get_next_update_partition(nullptr); if (!updatePartition) { USBSerial.println("OTA Error: No partition"); - sendCommandResponse(CMD_START, 0x0001); + sendCommandResponse(CMD_START, 0x0001); return; } - + // Validate image length (Bytes 2-5 of payload -> data[2]..data[5]) uint32_t imageLen = data[2] | (data[3] << 8) | (data[4] << 16) | (data[5] << 24); if (imageLen > updatePartition->size) { @@ -153,12 +153,12 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { sendCommandResponse(CMD_START, 0x0001); return; } - + esp_err_t err = esp_ota_begin(updatePartition, OTA_SIZE_UNKNOWN, &updateHandle); if (err != ESP_OK) { USBSerial.printf("OTA Error: Begin failed 0x%x\n", err); - sendCommandResponse(CMD_START, 0x0001); - return; + sendCommandResponse(CMD_START, 0x0001); + return; } otaInProgress = true; @@ -166,8 +166,8 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { receivedBytes = 0; sectorBufferLen = 0; currentSectorIndex = 0; - - sendCommandResponse(CMD_START, 0x0000); // Accept + + sendCommandResponse(CMD_START, 0x0000); // Accept } else if (cmdId == CMD_END) { USBSerial.println("OTA: CMD_END"); @@ -175,7 +175,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { // Defensive: Verify total bytes received match manifest if (receivedBytes != imageTotalLen) { USBSerial.printf("OTA Error: Size Mismatch Rx:%u Exp:%u\n", receivedBytes, imageTotalLen); - sendCommandResponse(CMD_END, 0x0001); // Fail + sendCommandResponse(CMD_END, 0x0001); // Fail abortOta(); return; } @@ -183,8 +183,8 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { if (esp_ota_end(updateHandle) == ESP_OK) { if (esp_ota_set_boot_partition(updatePartition) == ESP_OK) { USBSerial.println("OTA Success. Restarting..."); - sendCommandResponse(CMD_END, 0x0000); // Success - delay(1000); // Allow BLE flush + sendCommandResponse(CMD_END, 0x0000); // Success + delay(1000); // Allow BLE flush ESP.restart(); return; } else { @@ -193,10 +193,10 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { } else { USBSerial.println("OTA Error: OTA End Failed"); } - sendCommandResponse(CMD_END, 0x0001); // Fail + sendCommandResponse(CMD_END, 0x0001); // Fail abortOta(); } else { - sendCommandResponse(CMD_END, 0x0001); // Not in progress + sendCommandResponse(CMD_END, 0x0001); // Not in progress } } } @@ -205,12 +205,12 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { class OtaDataCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { if (!otaInProgress) return; - + std::string valStr = pChar->getValue(); size_t len = valStr.length(); const uint8_t* data = (const uint8_t*)valStr.data(); - if (len < 3) return; // Header: Sector(2) + Seq(1) + if (len < 3) return; // Header: Sector(2) + Seq(1) uint16_t sector = data[0] | (data[1] << 8); uint8_t seq = data[2]; @@ -219,7 +219,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (sector != currentSectorIndex) { USBSerial.printf("OTA Warn: Sector mismatch exp %d got %d\n", currentSectorIndex, sector); - sectorBufferLen = 0; + sectorBufferLen = 0; sendAck(sector, ACK_ERR_SECTOR, currentSectorIndex); return; } @@ -227,9 +227,9 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (seq == 0xFF) { // Last packet: Payload is Data + CRC16(2) if (payloadLen < 2) return; - + size_t actualDataLen = payloadLen - 2; - + if (sectorBufferLen + actualDataLen > sizeof(sectorBuffer)) { sendAck(sector, ACK_ERR_LEN); sectorBufferLen = 0; @@ -252,10 +252,10 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { sectorBufferLen = 0; } else { USBSerial.printf("OTA Error: Write failed 0x%x\n", err); - sendAck(sector, ACK_ERR_SECTOR, currentSectorIndex); // Force retry + sendAck(sector, ACK_ERR_SECTOR, currentSectorIndex); // Force retry sectorBufferLen = 0; - // Don't hard abort yet, allow retry? - // Espressif tools usually retry logic is client side. + // Don't hard abort yet, allow retry? + // Espressif tools usually retry logic is client side. // If we return ERR_SECTOR, client re-sends sector. } } else { @@ -270,7 +270,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { USBSerial.println("OTA Error: Buffer overflow"); sectorBufferLen = 0; sendAck(sector, ACK_ERR_LEN); - return; + return; } memcpy(sectorBuffer + sectorBufferLen, payload, payloadLen); sectorBufferLen += payloadLen; @@ -281,7 +281,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { static OtaCommandCallback cmdCallback; static OtaDataCallback dataCallback; -} // namespace +} // namespace void initOtaBleService(NimBLEServer* pServer) { NimBLEService* pService = pServer->createService(OTA_SERVICE_UUID); @@ -289,28 +289,24 @@ void initOtaBleService(NimBLEServer* pServer) { // Firmware data (Write No Response + Indicate for ACKs) pRecvFwChar = pService->createCharacteristic( OTA_RECV_FW_UUID, - NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::INDICATE - ); + NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::INDICATE); pRecvFwChar->setCallbacks(&dataCallback); // Progress (Indicate only; app subscribes) pProgressChar = pService->createCharacteristic( OTA_PROGRESS_UUID, - NIMBLE_PROPERTY::INDICATE - ); + NIMBLE_PROPERTY::INDICATE); // Command (Write + Indicate) pCommandChar = pService->createCharacteristic( OTA_COMMAND_UUID, - NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::INDICATE - ); + NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::INDICATE); pCommandChar->setCallbacks(&cmdCallback); // Customer (Indicate only; app subscribes) pCustomerChar = pService->createCharacteristic( OTA_CUSTOMER_UUID, - NIMBLE_PROPERTY::INDICATE - ); + NIMBLE_PROPERTY::INDICATE); pService->start(); } diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index b1f8eeb4..2d05db27 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -539,7 +539,7 @@ void uiTask(void *pvParameters) { // BMS task: ~10 Hz polling and unified battery update void bmsTask(void *pvParameters) { TickType_t lastWake = xTaskGetTickCount(); - const TickType_t bmsTicks = pdMS_TO_TICKS(100); // 10 Hz + const TickType_t bmsTicks = pdMS_TO_TICKS(100); // 10 Hz for (;;) { #ifdef SCREEN_DEBUG float altitude = 0; @@ -618,7 +618,7 @@ void setupWatchdog() { #ifndef OPENPPG_DEBUG // Initialize Task Watchdog ESP_ERROR_CHECK(esp_task_wdt_init(3000, true)); // 3 second timeout, panic on timeout -#endif // OPENPPG_DEBUG +#endif // OPENPPG_DEBUG } #define TAG "OpenPPG" @@ -906,9 +906,7 @@ void buttonHandlerTask(void* parameter) { buttonPressStartTime = currentTime; // Reset to prevent immediate cruise activation } } - } - // Only handle other button actions if we're not in an arm sequence - else if (buttonPressed) { + } else if (buttonPressed) { // Only handle other button actions if we're not in an arm sequence uint32_t currentHoldTime = currentTime - buttonPressStartTime; // Handle performance mode (only when disarmed and held long enough) @@ -917,9 +915,8 @@ void buttonHandlerTask(void* parameter) { perfModeSwitch(); buttonPressed = false; buttonPressStartTime = currentTime; - } - // Handle cruise control (when armed or cruising and held long enough) - else if ((currentState == ARMED || currentState == ARMED_CRUISING) && currentHoldTime >= CRUISE_HOLD_TIME_MS) { + } else if ((currentState == ARMED || currentState == ARMED_CRUISING) && currentHoldTime >= CRUISE_HOLD_TIME_MS) { + // Handle cruise control (when armed or cruising and held long enough) USBSerial.println("Cruise control button activated"); toggleCruise(); buttonPressed = false; @@ -1195,7 +1192,7 @@ void afterCruiseStart() { USBSerial.println("Failed to queue initial cruise throttle PWM"); } - //pulseVibeMotor(); + // pulseVibeMotor(); } void afterCruiseEnd() { @@ -1208,7 +1205,7 @@ void afterCruiseEnd() { throttleFilterReset(currentPwmVal); cruisedPotVal = 0; - //pulseVibeMotor(); + // pulseVibeMotor(); } void playCruiseSound() { @@ -1229,7 +1226,9 @@ void audioTask(void* parameter) { for (int i = 0; i < request.size; i++) { startTone(request.notes[i]); TickType_t delayTicks = pdMS_TO_TICKS(request.duration); - if (delayTicks == 0) { delayTicks = 1; } // Ensure non-zero delay + if (delayTicks == 0) { + delayTicks = 1; + } // Ensure non-zero delay vTaskDelayUntil(&nextWakeTime, delayTicks); } stopTone(); From 17f3d0026a31d8c29eea2c512d9586343a4f5956 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Tue, 27 Jan 2026 10:42:59 -0600 Subject: [PATCH 09/22] Remove extra internal logging --- sdkconfig.OpenPPG-CESP32S3-CAN-SP140 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 index 16e31989..6e121b6c 100644 --- a/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 +++ b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 @@ -1118,16 +1118,16 @@ CONFIG_HEAP_TRACING_OFF=y # Log output # # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +CONFIG_LOG_DEFAULT_LEVEL_ERROR=y # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_DEFAULT_LEVEL=1 CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y # CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set # CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set -CONFIG_LOG_MAXIMUM_LEVEL=3 +CONFIG_LOG_MAXIMUM_LEVEL=1 CONFIG_LOG_COLORS=y CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set From d2fbb5be7298b872598dd7b681566d18b7515730 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Tue, 27 Jan 2026 20:29:21 -0600 Subject: [PATCH 10/22] Switch OTA CRC to CRC16-CCITT for compatibility Replaces the previous CRC16 implementation with CRC16-CCITT (poly 0x1021, init 0) in ota_service.cpp to match the Espressif Android app. Updates all relevant CRC checks and calculations to use the new function. --- sdkconfig.OpenPPG-CESP32S3-CAN-SP140 | 2 ++ src/sp140/ble/ota_service.cpp | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 index 6e121b6c..dec50b06 100644 --- a/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 +++ b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 @@ -1125,6 +1125,8 @@ CONFIG_LOG_DEFAULT_LEVEL_ERROR=y # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set CONFIG_LOG_DEFAULT_LEVEL=1 CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_WARN is not set +# CONFIG_LOG_MAXIMUM_LEVEL_INFO is not set # CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set # CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set CONFIG_LOG_MAXIMUM_LEVEL=1 diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index 7544552f..f54f82c6 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -40,16 +40,20 @@ NimBLECharacteristic* pProgressChar = nullptr; NimBLECharacteristic* pCommandChar = nullptr; NimBLECharacteristic* pCustomerChar = nullptr; -// CRC16 Implementation (Polynomial 0x8005, reversed for LE) -uint16_t crc16_le(uint16_t crc, const uint8_t *buffer, size_t len) { - while (len--) { - crc ^= *buffer++; - for (int i = 0; i < 8; i++) { - if (crc & 1) crc = (crc >> 1) ^ 0xA001; - else crc = (crc >> 1); +// CRC16-CCITT (poly 0x1021, init 0) to match Espressif Android app +uint16_t crc16_ccitt(const uint8_t* buffer, size_t len) { + uint16_t crc16 = 0; + for (size_t i = 0; i < len; ++i) { + crc16 ^= static_cast(buffer[i]) << 8; + for (int bit = 0; bit < 8; ++bit) { + if (crc16 & 0x8000) { + crc16 = static_cast((crc16 << 1) ^ 0x1021); + } else { + crc16 = static_cast(crc16 << 1); + } } } - return crc; + return crc16; } void sendAck(uint16_t sector, uint16_t status, uint16_t expectedSector = 0) { @@ -86,7 +90,7 @@ void sendCommandResponse(uint16_t ackId, uint16_t status) { packet[4] = status & 0xFF; packet[5] = (status >> 8) & 0xFF; - uint16_t crc = crc16_le(0, packet, 18); + uint16_t crc = crc16_ccitt(packet, 18); packet[18] = crc & 0xFF; packet[19] = (crc >> 8) & 0xFF; @@ -123,7 +127,7 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { // Verify CRC uint16_t rxCrc = data[18] | (data[19] << 8); - uint16_t calcCrc = crc16_le(0, data, 18); + uint16_t calcCrc = crc16_ccitt(data, 18); if (rxCrc != calcCrc) { USBSerial.printf("OTA Error: CMD CRC Fail Exp %04X Got %04X\n", rxCrc, calcCrc); sendCommandResponse(cmdId, 0x0001); // Reject (Status 1) @@ -241,7 +245,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { // Verify CRC uint16_t rxCrc = payload[actualDataLen] | (payload[actualDataLen + 1] << 8); - uint16_t calcCrc = crc16_le(0, sectorBuffer, sectorBufferLen); + uint16_t calcCrc = crc16_ccitt(sectorBuffer, sectorBufferLen); if (rxCrc == calcCrc) { esp_err_t err = esp_ota_write(updateHandle, sectorBuffer, sectorBufferLen); From 8dcd3f51887312ed20771ce29b67e03c6b9b8627 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Wed, 28 Jan 2026 07:54:08 -0600 Subject: [PATCH 11/22] Improve OTA BLE service and LVGL display handling Switched OTA BLE characteristics to use notify instead of indicate for ACKs and command responses, and added NOTIFY property to relevant characteristics. Added packet count logging and sector write progress in OTA service. In LVGL flush callback, ensured flush completion is signaled on SPI timeout to prevent deadlock, and moved CS pin selection after SPI bus acquisition. Added boot and running partition info logging during setup. --- src/sp140/ble/ota_service.cpp | 26 ++++++++++++++++++++++---- src/sp140/lvgl/lvgl_core.cpp | 10 ++++++---- src/sp140/main.cpp | 7 +++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index f54f82c6..9e70e442 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -33,6 +33,7 @@ uint8_t sectorBuffer[4096]; uint16_t sectorBufferLen = 0; uint16_t currentSectorIndex = 0; size_t receivedBytes = 0; +uint32_t packetCount = 0; // OTA characteristics NimBLECharacteristic* pRecvFwChar = nullptr; @@ -69,7 +70,7 @@ void sendAck(uint16_t sector, uint16_t status, uint16_t expectedSector = 0) { packet[5] = (expectedSector >> 8) & 0xFF; pRecvFwChar->setValue(packet, 6); - pRecvFwChar->indicate(); + pRecvFwChar->notify(); USBSerial.printf("OTA: Sent ACK Sector=%d Status=%d\n", sector, status); } } @@ -95,7 +96,7 @@ void sendCommandResponse(uint16_t ackId, uint16_t status) { packet[19] = (crc >> 8) & 0xFF; pCommandChar->setValue(packet, 20); - pCommandChar->indicate(); + pCommandChar->notify(); USBSerial.printf("OTA: Sent CMD Response AckId=%d Status=%d\n", ackId, status); } } @@ -216,6 +217,13 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (len < 3) return; // Header: Sector(2) + Seq(1) + packetCount++; + if (packetCount % 100 == 0) { + USBSerial.printf("OTA: Received %lu packets (sector %u)\n", + static_cast(packetCount), + static_cast(currentSectorIndex)); + } + uint16_t sector = data[0] | (data[1] << 8); uint8_t seq = data[2]; const uint8_t* payload = data + 3; @@ -252,6 +260,9 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (err == ESP_OK) { receivedBytes += sectorBufferLen; sendAck(sector, ACK_SUCCESS); + USBSerial.printf("OTA: Sector %u written (%u bytes total)\n", + static_cast(currentSectorIndex), + static_cast(receivedBytes)); currentSectorIndex++; sectorBufferLen = 0; } else { @@ -293,23 +304,30 @@ void initOtaBleService(NimBLEServer* pServer) { // Firmware data (Write No Response + Indicate for ACKs) pRecvFwChar = pService->createCharacteristic( OTA_RECV_FW_UUID, - NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::INDICATE); + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::WRITE_NR | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE); pRecvFwChar->setCallbacks(&dataCallback); // Progress (Indicate only; app subscribes) pProgressChar = pService->createCharacteristic( OTA_PROGRESS_UUID, + NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::INDICATE); // Command (Write + Indicate) pCommandChar = pService->createCharacteristic( OTA_COMMAND_UUID, - NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::INDICATE); + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE); pCommandChar->setCallbacks(&cmdCallback); // Customer (Indicate only; app subscribes) pCustomerChar = pService->createCharacteristic( OTA_CUSTOMER_UUID, + NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::INDICATE); pService->start(); diff --git a/src/sp140/lvgl/lvgl_core.cpp b/src/sp140/lvgl/lvgl_core.cpp index 9c713d4b..24f927fd 100644 --- a/src/sp140/lvgl/lvgl_core.cpp +++ b/src/sp140/lvgl/lvgl_core.cpp @@ -77,9 +77,6 @@ void setupLvglDisplay( // Optimize the flush callback to minimize SPI transfers // CS pin management is handled here where actual SPI communication occurs void lvgl_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) { - // Make sure display CS is selected - digitalWrite(displayCS, LOW); - uint32_t w = (area->x2 - area->x1 + 1); uint32_t h = (area->y2 - area->y1 + 1); @@ -89,9 +86,14 @@ void lvgl_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color if (xSemaphoreTake(spiBusMutex, pdMS_TO_TICKS(200)) != pdTRUE) { // SPI bus timeout - BMS might be doing long operation, skip display flush USBSerial.println("[DISPLAY] SPI bus timeout - skipping display flush"); - return; // Skip this display update rather than hang + // Must still signal LVGL that flush is done to avoid deadlock + lv_disp_flush_ready(disp); + return; } } + + // Make sure display CS is selected only after SPI bus is acquired + digitalWrite(displayCS, LOW); tft_driver->startWrite(); tft_driver->setAddrWindow(area->x1, area->y1, w, h); diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index 2d05db27..911cbd5c 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -647,6 +647,13 @@ void setup() { refreshDeviceData(); printBootMessage(); + const esp_partition_t* running = esp_ota_get_running_partition(); + const esp_partition_t* boot = esp_ota_get_boot_partition(); + if (running != nullptr && boot != nullptr) { + USBSerial.printf("OTA: Running partition %s @ 0x%06lx, boot %s @ 0x%06lx\n", + running->label, (unsigned long)running->address, + boot->label, (unsigned long)boot->address); + } setupBarometer(); loadHardwareConfig(); From 266f5ec15f687c6fc951183e0211eaef6d434695 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Wed, 28 Jan 2026 12:18:35 -0600 Subject: [PATCH 12/22] Update main.cpp --- src/sp140/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index 911cbd5c..bf4ca3e5 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -725,6 +725,7 @@ void setup() { initSimpleMonitor(); setupTasks(); // Create all tasks after queues and BLE are initialized + vTaskDelay(pdMS_TO_TICKS(50)); // Let tasks settle before splash screen pulseVibeMotor(); if (digitalRead(board_config.button_top) == LOW) { // LOW means pressed since it's INPUT_PULLUP From fa65b02b9f44b9f927d68765ed867df58bdad3b7 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Wed, 28 Jan 2026 12:24:57 -0600 Subject: [PATCH 13/22] Remove unused alt_wire parameter from setupAltimeter The setupAltimeter function no longer requires the alt_wire parameter, so it has been removed from the function signature and all call sites. This simplifies the interface and eliminates unused code. --- inc/sp140/altimeter.h | 4 ++-- src/sp140/altimeter.cpp | 8 +++----- src/sp140/main.cpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/inc/sp140/altimeter.h b/inc/sp140/altimeter.h index aee7c863..b78ea0d6 100644 --- a/inc/sp140/altimeter.h +++ b/inc/sp140/altimeter.h @@ -10,8 +10,8 @@ #define VARIO_BUFFER_SIZE 25 // Number of samples to average for vertical speed #define MAX_VERTICAL_SPEED 250.0f // Maximum vertical speed to display (m/s) -// Set up the barometer -bool setupAltimeter(bool alt_wire = false); +// Set up the barometer +bool setupAltimeter(); // Get the altitude (in meters) float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData); diff --git a/src/sp140/altimeter.cpp b/src/sp140/altimeter.cpp index 5fa7f2a0..fd2e33d0 100644 --- a/src/sp140/altimeter.cpp +++ b/src/sp140/altimeter.cpp @@ -75,11 +75,9 @@ float getBaroPressure() { return __FLT_MIN__; // Return a very small number if BMP is not present } -// Start the bmp3XX sensor -bool setupAltimeter(bool altWire) { - TwoWire* wire = &Wire; - - // pull down pin 40 to high to set the address +// Start the bmp3XX sensor +bool setupAltimeter() { + // pull down pin 40 to high to set the address pinMode(40, OUTPUT); digitalWrite(40, HIGH); if (!bmp.begin_I2C(BMP3XX_DEFAULT_ADDRESS, &Wire)) return false; diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index bf4ca3e5..acec6fc4 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -831,7 +831,7 @@ void setup140() { const int SDA_PIN = 44; const int SCL_PIN = 41; Wire.setPins(SDA_PIN, SCL_PIN); - if (!setupAltimeter(board_config.alt_wire)) { + if (!setupAltimeter()) { USBSerial.println("Error initializing BMP3xx barometer"); } if (ENABLE_VIBE) { From f0b0b5a4240923cbf96a6a279c5087427f3e338a Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Wed, 28 Jan 2026 12:50:58 -0600 Subject: [PATCH 14/22] Defer BLE advertising until after splash screen BLE advertising is now started after the splash screen completes, rather than during BLE setup. This change ensures the device does not advertise until the UI is ready, improving startup flow and user experience. Added log messages to clarify advertising state. --- src/sp140/ble/ble_core.cpp | 5 ++--- src/sp140/main.cpp | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/sp140/ble/ble_core.cpp b/src/sp140/ble/ble_core.cpp index 4c108e89..4da449db 100644 --- a/src/sp140/ble/ble_core.cpp +++ b/src/sp140/ble/ble_core.cpp @@ -66,10 +66,9 @@ void setupBLE() { advertising->addServiceUUID(NimBLEUUID(CONTROLLER_SERVICE_UUID)); advertising->addServiceUUID(NimBLEUUID(OTA_SERVICE_UUID)); advertising->enableScanResponse(true); - advertising->start(); + // Note: Advertising is deferred until after splash screen - USBSerial.println("BLE device ready"); - USBSerial.println("Waiting for a client connection..."); + USBSerial.println("BLE device ready (advertising deferred)"); } void restartBLEAdvertising() { diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index acec6fc4..9e15c3df 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -116,6 +116,9 @@ SemaphoreHandle_t eepromSemaphore; SemaphoreHandle_t stateMutex; SemaphoreHandle_t lvglMutex; +// Startup synchronization - tasks wait for this before starting main loops +SemaphoreHandle_t startupSyncSemaphore = NULL; + // BLE state propagation struct BLEStateUpdate { uint8_t state; @@ -526,6 +529,13 @@ void monitoringTask(void *pvParameters) { // UI task: fixed 25 Hz refresh and snapshot publish void uiTask(void *pvParameters) { + // Wait for setup to complete before starting + if (startupSyncSemaphore != NULL) { + xSemaphoreTake(startupSyncSemaphore, portMAX_DELAY); + // Give it back so other tasks can also take it + xSemaphoreGive(startupSyncSemaphore); + } + TickType_t lastWake = xTaskGetTickCount(); const TickType_t uiTicks = pdMS_TO_TICKS(50); // 20 Hz for (;;) { @@ -645,6 +655,12 @@ void setup() { USBSerial.println("Error creating LVGL mutex"); } + // Create startup sync semaphore - tasks wait for this before running + startupSyncSemaphore = xSemaphoreCreateBinary(); + if (startupSyncSemaphore == NULL) { + USBSerial.println("Error creating startup sync semaphore"); + } + refreshDeviceData(); printBootMessage(); const esp_partition_t* running = esp_ota_get_running_partition(); @@ -725,7 +741,6 @@ void setup() { initSimpleMonitor(); setupTasks(); // Create all tasks after queues and BLE are initialized - vTaskDelay(pdMS_TO_TICKS(50)); // Let tasks settle before splash screen pulseVibeMotor(); if (digitalRead(board_config.button_top) == LOW) { // LOW means pressed since it's INPUT_PULLUP @@ -766,6 +781,14 @@ void setup() { send_device_data(); // Signal that the UI is ready for updates from tasks uiReady = true; + // Signal all tasks that setup is complete - they can now start their main loops + if (startupSyncSemaphore != NULL) { + xSemaphoreGive(startupSyncSemaphore); + USBSerial.println("Tasks released - startup complete"); + } + // Start BLE advertising now that splash is complete + restartBLEAdvertising(); + USBSerial.println("BLE advertising started"); // Simple instrumentation to detect UI/BMS latency USBSerial.println("Init complete. UI + BMS loop running"); From e77322eece8545aee416c8745e972af44602a0c9 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Wed, 28 Jan 2026 13:19:32 -0600 Subject: [PATCH 15/22] Refactor boot sequence and RTOS setup for clarity Reorganize the main setup() into explicit boot phases, separating RTOS primitive creation, hardware initialization, UI setup, and task creation. Remove startup synchronization semaphores and flags in favor of a deterministic, phased boot. Move all queue and mutex creation to dedicated functions called before any tasks are started. Simplify UI task startup and ensure all dependencies are initialized before tasks run. --- src/sp140/main.cpp | 390 +++++++++++++++++++++++++-------------------- 1 file changed, 215 insertions(+), 175 deletions(-) diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index 9e15c3df..9f564dfb 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -54,9 +54,9 @@ bool armSystem(); void afterCruiseEnd(); void afterCruiseStart(); void pushTelemetrySnapshot(); -void initButtons(); -void setup140(); void setupTasks(); +void createAllSyncPrimitives(); +void createAllQueues(); void audioTask(void* parameter); void updateESCBLETask(void* parameter); void webSerialTask(void* parameter); @@ -109,16 +109,10 @@ uint32_t led_color = LED_RED; // current LED color // Global variable for device state volatile DeviceState currentState = DISARMED; -// Add volatile bool flag to control UI drawing start -volatile bool uiReady = false; - SemaphoreHandle_t eepromSemaphore; SemaphoreHandle_t stateMutex; SemaphoreHandle_t lvglMutex; -// Startup synchronization - tasks wait for this before starting main loops -SemaphoreHandle_t startupSyncSemaphore = NULL; - // BLE state propagation struct BLEStateUpdate { uint8_t state; @@ -433,8 +427,8 @@ void updateBLETask(void *pvParameters) { } void refreshDisplay() { - // Prevent drawing until setup() signals UI is ready - if (!uiReady || main_screen == NULL) { + // Guard against calls before main_screen is set up + if (main_screen == NULL) { return; } @@ -527,15 +521,8 @@ void monitoringTask(void *pvParameters) { } } -// UI task: fixed 25 Hz refresh and snapshot publish +// UI task: fixed 20 Hz refresh and snapshot publish void uiTask(void *pvParameters) { - // Wait for setup to complete before starting - if (startupSyncSemaphore != NULL) { - xSemaphoreTake(startupSyncSemaphore, portMAX_DELAY); - // Give it back so other tasks can also take it - xSemaphoreGive(startupSyncSemaphore); - } - TickType_t lastWake = xTaskGetTickCount(); const TickType_t uiTicks = pdMS_TO_TICKS(50); // 20 Hz for (;;) { @@ -633,247 +620,300 @@ void setupWatchdog() { #define TAG "OpenPPG" +// ============================================================================= +// BOOT PHASE HELPER FUNCTIONS +// ============================================================================= /** - * Initializes the necessary components and configurations for the device setup. - * This function is called once at the beginning of the program execution. + * Phase 2a: Create all RTOS mutexes and semaphores + * Must be called before any tasks are created */ +void createAllSyncPrimitives() { + USBSerial.println("Creating sync primitives..."); -void setup() { - USBSerial.begin(115200); // This is for debug output and WebSerial - USBSerial.print("Build date/time: "); - USBSerial.println(buildDate); - - // Pull CSB (pin 42) high to activate I2C mode - // temporary fix TODO remove - digitalWrite(42, HIGH); - pinMode(42, OUTPUT); - - // Initialize LVGL mutex before anything else + // LVGL display mutex - protects all LVGL operations lvglMutex = xSemaphoreCreateMutex(); if (lvglMutex == NULL) { USBSerial.println("Error creating LVGL mutex"); } - // Create startup sync semaphore - tasks wait for this before running - startupSyncSemaphore = xSemaphoreCreateBinary(); - if (startupSyncSemaphore == NULL) { - USBSerial.println("Error creating startup sync semaphore"); + // State mutex - protects device state transitions + stateMutex = xSemaphoreCreateMutex(); + if (stateMutex == NULL) { + USBSerial.println("Error creating state mutex"); } - refreshDeviceData(); - printBootMessage(); - const esp_partition_t* running = esp_ota_get_running_partition(); - const esp_partition_t* boot = esp_ota_get_boot_partition(); - if (running != nullptr && boot != nullptr) { - USBSerial.printf("OTA: Running partition %s @ 0x%06lx, boot %s @ 0x%06lx\n", - running->label, (unsigned long)running->address, - boot->label, (unsigned long)boot->address); + // EEPROM semaphore - guards Preferences writes + eepromSemaphore = xSemaphoreCreateBinary(); + if (eepromSemaphore == NULL) { + USBSerial.println("Error creating EEPROM semaphore"); + } else { + xSemaphoreGive(eepromSemaphore); // Start in "available" state } - setupBarometer(); - - loadHardwareConfig(); - setupLED(); // Defaults to RED - setupAnalogRead(); - initButtons(); - setupWatchdog(); - setup140(); - - setLEDColor(LED_YELLOW); // Booting up - - // First initialize the shared SPI bus - setupSPI(board_config); - - // Then setup the display - use LVGL display instead of the old one - // Pass hardcoded pin values for DC and RST - setupLvglDisplay(deviceData, board_config.tft_dc, board_config.tft_rst, hardwareSPI); - - // Initialise alert display aggregation & UI - initAlertDisplay(); - - #ifndef SCREEN_DEBUG - // Pass the hardware SPI instance to the BMS_CAN initialization - initBMSCAN(hardwareSPI); - #endif +} - initESC(); - eepromSemaphore = xSemaphoreCreateBinary(); - xSemaphoreGive(eepromSemaphore); - stateMutex = xSemaphoreCreateMutex(); - setESCThrottle(ESC_DISARMED_PWM); - initVibeMotor(); +/** + * Phase 2b: Create all RTOS queues + * Must be called before any tasks are created + */ +void createAllQueues() { + USBSerial.println("Creating queues..."); - // Create all queues first + // BMS telemetry queue (size 1, overwrite semantics) bmsTelemetryQueue = xQueueCreate(1, sizeof(STR_BMS_TELEMETRY_140)); if (bmsTelemetryQueue == NULL) { USBSerial.println("Error creating BMS telemetry queue"); } + // Throttle update queue throttleUpdateQueue = xQueueCreate(1, sizeof(uint16_t)); if (throttleUpdateQueue == NULL) { USBSerial.println("Error creating throttle update queue"); } + // ESC telemetry queue escTelemetryQueue = xQueueCreate(1, sizeof(STR_ESC_TELEMETRY_140)); if (escTelemetryQueue == NULL) { USBSerial.println("Error creating ESC telemetry queue"); } + // BLE state queue bleStateQueue = xQueueCreate(5, sizeof(BLEStateUpdate)); if (bleStateQueue == NULL) { USBSerial.println("Error creating BLE state queue"); } + // Device state queue deviceStateQueue = xQueueCreate(1, sizeof(uint8_t)); if (deviceStateQueue == NULL) { USBSerial.println("Error creating device state queue"); } - // Snapshot queue for monitoring (size 1) + // Telemetry snapshot queue for monitoring telemetrySnapshotQueue = xQueueCreate(1, sizeof(TelemetrySnapshot)); if (telemetrySnapshotQueue == NULL) { USBSerial.println("Error creating telemetry snapshot queue"); } - setupBLE(); + // Melody queue for audio task - MUST be created before audioTask + melodyQueue = xQueueCreate(5, sizeof(MelodyRequest)); + if (melodyQueue == NULL) { + USBSerial.println("Error creating melody queue"); + } +} - // Initialize the simple monitoring system (but keep it disabled initially) - initSimpleMonitor(); +/** + * Phase 6: Create all FreeRTOS tasks + * Called AFTER all hardware, UI, and services are initialized + * Tasks can start running immediately - no sync semaphores needed + */ +void setupTasks() { + USBSerial.println("Creating tasks..."); + + // Core 0 tasks (safety-critical, real-time) + xTaskCreatePinnedToCore(throttleTask, "throttle", 4352, NULL, 3, &throttleTaskHandle, 0); + xTaskCreatePinnedToCore(watchdogTask, "watchdog", 1536, NULL, 3, &watchdogTaskHandle, 0); + xTaskCreatePinnedToCore(buttonHandlerTask, "ButtonHandler", 4096, NULL, 2, &buttonTaskHandle, 0); + + // Core 1 tasks (UI, communications) + xTaskCreatePinnedToCore(uiTask, "UI", 5888, NULL, 2, &uiTaskHandle, 1); + xTaskCreatePinnedToCore(bmsTask, "BMS", 2304, NULL, 2, &bmsTaskHandle, 1); + xTaskCreatePinnedToCore(bleStateUpdateTask, "BLEStateUpdate", 8192, NULL, 1, &bleStateUpdateTaskHandle, 1); + xTaskCreatePinnedToCore(audioTask, "Audio", 1536, NULL, 1, &audioTaskHandle, 1); + xTaskCreatePinnedToCore(vibeTask, "Vibration", 1536, NULL, 2, &vibeTaskHandle, 1); + + // Unpinned tasks (can run on either core) + xTaskCreate(blinkLEDTask, "blinkLed", 2560, NULL, 1, &blinkLEDTaskHandle); + xTaskCreate(updateBLETask, "BLE Update Task", 8192, NULL, 1, NULL); + xTaskCreate(deviceStateUpdateTask, "State Update Task", 2048, NULL, 1, &deviceStateUpdateTaskHandle); + xTaskCreate(updateESCBLETask, "ESC BLE Update Task", 8192, NULL, 1, NULL); + xTaskCreate(webSerialTask, "WebSerial", 3072, NULL, 1, &webSerialTaskHandle); + xTaskCreate(monitoringTask, "Monitoring", 4864, NULL, 1, &monitoringTaskHandle); + + #ifdef OPENPPG_DEBUG + // Create periodic stack watermark logger (low priority) + xTaskCreatePinnedToCore(stackWatermarkLoggerTask, "StackLogger", 3072, NULL, 1, &stackLoggerTaskHandle, 1); + #endif + + USBSerial.println("All tasks created"); +} + +// ============================================================================= +// MAIN SETUP FUNCTION - PHASED BOOT SEQUENCE +// ============================================================================= + +/** + * Initializes the device in deterministic phases. + * No tasks are created until all their dependencies exist. + */ +void setup() { + // ========================================================================= + // PHASE 1: Serial and Early Config + // ========================================================================= + USBSerial.begin(115200); + USBSerial.print("Build date/time: "); + USBSerial.println(buildDate); + + // Load device config from EEPROM first - may contain pin mappings + refreshDeviceData(); + printBootMessage(); + + // Log OTA partition info + const esp_partition_t* running = esp_ota_get_running_partition(); + const esp_partition_t* boot = esp_ota_get_boot_partition(); + if (running != nullptr && boot != nullptr) { + USBSerial.printf("OTA: Running partition %s @ 0x%06lx, boot %s @ 0x%06lx\n", + running->label, (unsigned long)running->address, + boot->label, (unsigned long)boot->address); + } - setupTasks(); // Create all tasks after queues and BLE are initialized + // ========================================================================= + // PHASE 2: Create ALL RTOS Primitives (before any tasks) + // ========================================================================= + createAllSyncPrimitives(); + createAllQueues(); + // ========================================================================= + // PHASE 3: Hardware Initialization + // ========================================================================= + USBSerial.println("Initializing hardware..."); + + // Temporary fix for BMP3xx I2C mode - TODO remove + digitalWrite(42, HIGH); + pinMode(42, OUTPUT); + + // Load board-specific hardware config + loadHardwareConfig(); + + // I2C bus setup for altimeter + const int SDA_PIN = 44; + const int SCL_PIN = 41; + Wire.setPins(SDA_PIN, SCL_PIN); + + // Initialize peripherals + setupBarometer(); + if (!setupAltimeter()) { + USBSerial.println("Error initializing BMP3xx barometer"); + } + + setupLED(); // Defaults to RED + setupAnalogRead(); + pinMode(board_config.button_top, INPUT_PULLUP); // Button GPIO only, task created later + setupWatchdog(); + + // Buzzer and vibration motor + if (ENABLE_BUZZ) { + initBuzz(); + } + if (ENABLE_VIBE) { + initVibeMotor(); + } + + setLEDColor(LED_YELLOW); // Indicate boot in progress + + // SPI bus (shared between display and BMS CAN) + setupSPI(board_config); + + // Display initialization + setupLvglDisplay(deviceData, board_config.tft_dc, board_config.tft_rst, hardwareSPI); + initAlertDisplay(); + + // BMS CAN interface + #ifndef SCREEN_DEBUG + initBMSCAN(hardwareSPI); + #endif + + // ESC interface + initESC(); + setESCThrottle(ESC_DISARMED_PWM); + + // ========================================================================= + // PHASE 4: UI Startup (blocking - no tasks running yet) + // ========================================================================= + USBSerial.println("Starting UI..."); + + // Haptic feedback that boot is progressing pulseVibeMotor(); - if (digitalRead(board_config.button_top) == LOW) { // LOW means pressed since it's INPUT_PULLUP + + // Check for performance mode activation (button held during boot) + if (digitalRead(board_config.button_top) == LOW) { perfModeSwitch(); } + setLEDColor(LED_GREEN); - // Show LVGL splash screen + // Show splash screen (blocking) if (xSemaphoreTake(lvglMutex, portMAX_DELAY) == pdTRUE) { displayLvglSplash(deviceData, 2000); - // Don't release mutex here yet + // Keep mutex held through main screen setup } else { USBSerial.println("Failed to acquire LVGL mutex for splash"); - // If we failed mutex, maybe skip main screen setup? Or attempt anyway? - // Let's assume splash failed but we try to proceed. } - // Setup the main screen UI elements AFTER the splash - USBSerial.println("Setting up main screen after splash"); - if (main_screen == NULL) { // Check if it wasn't somehow created already + // Setup main screen after splash + USBSerial.println("Setting up main screen..."); + if (main_screen == NULL) { setupMainScreen(deviceData.theme == 1); } - // Explicitly load the main screen to make it active + // Load main screen if (main_screen != NULL) { - lv_scr_load(main_screen); - USBSerial.println("Main screen loaded"); + lv_scr_load(main_screen); + USBSerial.println("Main screen loaded"); } else { - USBSerial.println("Error: Main screen object is NULL after setup attempt."); + USBSerial.println("Error: Main screen object is NULL after setup attempt"); } - // Release the mutex if it was taken for the splash + // Release LVGL mutex if (lvglMutex != NULL && xSemaphoreGetMutexHolder(lvglMutex) == xTaskGetCurrentTaskHandle()) { - xSemaphoreGive(lvglMutex); + xSemaphoreGive(lvglMutex); } - // Send initial device data after setup + // ========================================================================= + // PHASE 5: Services Initialization + // ========================================================================= + USBSerial.println("Initializing services..."); + + // BLE stack (services created, advertising NOT started yet) + setupBLE(); + + // Monitoring system (disabled initially) + initSimpleMonitor(); + + // Send initial device data send_device_data(); - // Signal that the UI is ready for updates from tasks - uiReady = true; - // Signal all tasks that setup is complete - they can now start their main loops - if (startupSyncSemaphore != NULL) { - xSemaphoreGive(startupSyncSemaphore); - USBSerial.println("Tasks released - startup complete"); - } - // Start BLE advertising now that splash is complete + + // ========================================================================= + // PHASE 6: Create ALL Tasks + // Tasks can start immediately - all dependencies exist + // ========================================================================= + setupTasks(); + + // ========================================================================= + // PHASE 7: Start External Interfaces + // ========================================================================= + USBSerial.println("Starting external interfaces..."); + + // Start BLE advertising (now safe - UI is ready, tasks are running) restartBLEAdvertising(); USBSerial.println("BLE advertising started"); - // Simple instrumentation to detect UI/BMS latency - USBSerial.println("Init complete. UI + BMS loop running"); + // Enable sensor monitoring + enableMonitoring(); // Confirm successful boot to prevent OTA rollback if (esp_ota_mark_app_valid_cancel_rollback() == ESP_OK) { USBSerial.println("OTA: Firmware marked valid - Rollback cancelled"); } - // Enable sensor monitoring after splash screen and UI setup - // This gives BMS/ESC time to initialize and start providing valid data - enableMonitoring(); -} - -// set up all the main threads/tasks with core 0 affinity -void setupTasks() { - xTaskCreate(blinkLEDTask, "blinkLed", 2560, NULL, 1, &blinkLEDTaskHandle); - xTaskCreatePinnedToCore(throttleTask, "throttle", 4352, NULL, 3, &throttleTaskHandle, 0); - // Split UI and BMS into dedicated tasks - xTaskCreatePinnedToCore(uiTask, "UI", 5888, NULL, 2, &uiTaskHandle, 1); - xTaskCreatePinnedToCore(bmsTask, "BMS", 2304, NULL, 2, &bmsTaskHandle, 1); - xTaskCreate(updateBLETask, "BLE Update Task", 8192, NULL, 1, NULL); - xTaskCreate(deviceStateUpdateTask, "State Update Task", 2048, NULL, 1, &deviceStateUpdateTaskHandle); - // Create BLE update task with high priority but on core 1 - xTaskCreatePinnedToCore(bleStateUpdateTask, "BLEStateUpdate", 8192, NULL, 1, &bleStateUpdateTaskHandle, 1); - - // Create melody queue - melodyQueue = xQueueCreate(5, sizeof(MelodyRequest)); - - // Create audio task - pin to core 1 to avoid interference with throttle - xTaskCreatePinnedToCore(audioTask, "Audio", 1536, NULL, 1, &audioTaskHandle, 1); - - // Add hardware watchdog task on core 0 (highest priority among low-prio group) - xTaskCreatePinnedToCore(watchdogTask, "watchdog", 1536, NULL, 3, &watchdogTaskHandle, 0); - - xTaskCreate(updateESCBLETask, "ESC BLE Update Task", 8192, NULL, 1, NULL); - - // Create WebSerial task - xTaskCreate( - webSerialTask, - "WebSerial", - 3072, // Larger stack for ArduinoJson parsing & CDC handling - NULL, - 1, - &webSerialTaskHandle); - - // Create monitoring task - xTaskCreate(monitoringTask, "Monitoring", 4864, NULL, 1, &monitoringTaskHandle); - - // Create vibration task (centralized here after initVibeMotor) - xTaskCreatePinnedToCore(vibeTask, "Vibration", 1536, NULL, 2, &vibeTaskHandle, 1); - - #ifdef OPENPPG_DEBUG - // Create periodic stack watermark logger (low priority) - xTaskCreatePinnedToCore(stackWatermarkLoggerTask, "StackLogger", 3072, NULL, 1, &stackLoggerTaskHandle, 1); - #endif -} - -void setup140() { - if (ENABLE_BUZZ) { - initBuzz(); - } - const int SDA_PIN = 44; - const int SCL_PIN = 41; - Wire.setPins(SDA_PIN, SCL_PIN); - if (!setupAltimeter()) { - USBSerial.println("Error initializing BMP3xx barometer"); - } - if (ENABLE_VIBE) { - initVibeMotor(); - } + USBSerial.println("=== Boot complete ==="); } -// main loop all work is done in tasks +// main loop - all work is done in tasks void loop() { vTaskDelay(pdMS_TO_TICKS(25)); } -void initButtons() { - pinMode(board_config.button_top, INPUT_PULLUP); - - // Create button handling task - xTaskCreatePinnedToCore(buttonHandlerTask, "ButtonHandler", 4096, NULL, 2, &buttonTaskHandle, 0); -} - // Add new button handler task void buttonHandlerTask(void* parameter) { uint32_t lastDebounceTime = 0; From 5638fc02c208de1e5e3be051f51360373feafea7 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 29 Jan 2026 09:10:42 -0600 Subject: [PATCH 16/22] linting and cleanup --- inc/sp140/altimeter.h | 58 ++++++------- src/sp140/altimeter.cpp | 178 ++++++++++++++++++++-------------------- src/sp140/main.cpp | 5 +- 3 files changed, 121 insertions(+), 120 deletions(-) diff --git a/inc/sp140/altimeter.h b/inc/sp140/altimeter.h index b78ea0d6..f6d64853 100644 --- a/inc/sp140/altimeter.h +++ b/inc/sp140/altimeter.h @@ -1,31 +1,31 @@ -#ifndef INC_SP140_ALTIMETER_H_ -#define INC_SP140_ALTIMETER_H_ - -#include -#include -#include "sp140/structs.h" -#include "sp140/shared-config.h" - -// Constants -#define VARIO_BUFFER_SIZE 25 // Number of samples to average for vertical speed -#define MAX_VERTICAL_SPEED 250.0f // Maximum vertical speed to display (m/s) - +#ifndef INC_SP140_ALTIMETER_H_ +#define INC_SP140_ALTIMETER_H_ + +#include +#include +#include "sp140/structs.h" +#include "sp140/shared-config.h" + +// Constants +#define VARIO_BUFFER_SIZE 25 // Number of samples to average for vertical speed +#define MAX_VERTICAL_SPEED 250.0f // Maximum vertical speed to display (m/s) + // Set up the barometer bool setupAltimeter(); - -// Get the altitude (in meters) -float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData); - -// Get the vertical speed in meters per second -float getVerticalSpeed(); - -// Set the ground altitude to the current altitude to compute AGL -void setGroundAltitude(const STR_DEVICE_DATA_140_V1& deviceData); - -// Get the temperature in degrees Celsius -float getBaroTemperature(); - -// Get the pressure in hPa -float getBaroPressure(); - -#endif // INC_SP140_ALTIMETER_H_ + +// Get the altitude (in meters) +float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData); + +// Get the vertical speed in meters per second +float getVerticalSpeed(); + +// Set the ground altitude to the current altitude to compute AGL +void setGroundAltitude(const STR_DEVICE_DATA_140_V1& deviceData); + +// Get the temperature in degrees Celsius +float getBaroTemperature(); + +// Get the pressure in hPa +float getBaroPressure(); + +#endif // INC_SP140_ALTIMETER_H_ diff --git a/src/sp140/altimeter.cpp b/src/sp140/altimeter.cpp index fd2e33d0..67af3b62 100644 --- a/src/sp140/altimeter.cpp +++ b/src/sp140/altimeter.cpp @@ -1,92 +1,92 @@ -#include "sp140/altimeter.h" -#include "sp140/structs.h" - -#include - -Adafruit_BMP3XX bmp; -bool bmpPresent = false; -float groundAltitude = 0; - -struct AltitudeReading { - float altitude; - unsigned long timestamp; -}; - -// Buffer to store altitude readings with timestamps -CircularBuffer altitudeBuffer; - -float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData) { - if (bmpPresent) { - const float altitude = bmp.readAltitude(deviceData.sea_pressure); - float relativeAltitude = altitude - groundAltitude; - - // Add new reading to buffer with timestamp - AltitudeReading reading = {relativeAltitude, millis()}; - altitudeBuffer.push(reading); - - return relativeAltitude; - } - return __FLT_MIN__; -} - -float getVerticalSpeed() { - if (altitudeBuffer.size() < 2) { - return 0.0f; // Not enough readings yet - } - - // Get oldest and newest readings - const AltitudeReading& oldest = altitudeBuffer.first(); - const AltitudeReading& newest = altitudeBuffer.last(); - - // Calculate time difference in seconds - float timeDiff = (newest.timestamp - oldest.timestamp) / 1000.0f; - if (timeDiff <= 0) { - return 0.0f; // Avoid division by zero - } - - // Calculate vertical speed in meters per second - float verticalSpeed = (newest.altitude - oldest.altitude) / timeDiff; - - // Constrain to max vertical speed - return constrain(verticalSpeed, -MAX_VERTICAL_SPEED, MAX_VERTICAL_SPEED); -} - -// set the ground altitude to the current altitude -void setGroundAltitude(const STR_DEVICE_DATA_140_V1& deviceData) { - if (bmpPresent) { - groundAltitude = bmp.readAltitude(deviceData.sea_pressure); - altitudeBuffer.clear(); // Clear the buffer when resetting ground altitude - } -} - -// Get the temperature in degrees Celsius -float getBaroTemperature() { - if (bmpPresent) { - return bmp.readTemperature(); - } - return __FLT_MIN__; // Return a very small number if BMP is not present -} - -// Get the pressure in hPa -float getBaroPressure() { - if (bmpPresent) { - return bmp.readPressure() / 100.0f; // Convert Pa to hPa - } - return __FLT_MIN__; // Return a very small number if BMP is not present -} - +#include "sp140/altimeter.h" +#include "sp140/structs.h" + +#include + +Adafruit_BMP3XX bmp; +bool bmpPresent = false; +float groundAltitude = 0; + +struct AltitudeReading { + float altitude; + unsigned long timestamp; +}; + +// Buffer to store altitude readings with timestamps +CircularBuffer altitudeBuffer; + +float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData) { + if (bmpPresent) { + const float altitude = bmp.readAltitude(deviceData.sea_pressure); + float relativeAltitude = altitude - groundAltitude; + + // Add new reading to buffer with timestamp + AltitudeReading reading = {relativeAltitude, millis()}; + altitudeBuffer.push(reading); + + return relativeAltitude; + } + return __FLT_MIN__; +} + +float getVerticalSpeed() { + if (altitudeBuffer.size() < 2) { + return 0.0f; // Not enough readings yet + } + + // Get oldest and newest readings + const AltitudeReading& oldest = altitudeBuffer.first(); + const AltitudeReading& newest = altitudeBuffer.last(); + + // Calculate time difference in seconds + float timeDiff = (newest.timestamp - oldest.timestamp) / 1000.0f; + if (timeDiff <= 0) { + return 0.0f; // Avoid division by zero + } + + // Calculate vertical speed in meters per second + float verticalSpeed = (newest.altitude - oldest.altitude) / timeDiff; + + // Constrain to max vertical speed + return constrain(verticalSpeed, -MAX_VERTICAL_SPEED, MAX_VERTICAL_SPEED); +} + +// set the ground altitude to the current altitude +void setGroundAltitude(const STR_DEVICE_DATA_140_V1& deviceData) { + if (bmpPresent) { + groundAltitude = bmp.readAltitude(deviceData.sea_pressure); + altitudeBuffer.clear(); // Clear the buffer when resetting ground altitude + } +} + +// Get the temperature in degrees Celsius +float getBaroTemperature() { + if (bmpPresent) { + return bmp.readTemperature(); + } + return __FLT_MIN__; // Return a very small number if BMP is not present +} + +// Get the pressure in hPa +float getBaroPressure() { + if (bmpPresent) { + return bmp.readPressure() / 100.0f; // Convert Pa to hPa + } + return __FLT_MIN__; // Return a very small number if BMP is not present +} + // Start the bmp3XX sensor bool setupAltimeter() { // pull down pin 40 to high to set the address - pinMode(40, OUTPUT); - digitalWrite(40, HIGH); - if (!bmp.begin_I2C(BMP3XX_DEFAULT_ADDRESS, &Wire)) return false; - - bmp.setOutputDataRate(BMP3_ODR_25_HZ); - bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_2X); - bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X); - bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_15); - bmp.readPressure(); // throw away first reading - bmpPresent = true; - return true; -} + pinMode(40, OUTPUT); + digitalWrite(40, HIGH); + if (!bmp.begin_I2C(BMP3XX_DEFAULT_ADDRESS, &Wire)) return false; + + bmp.setOutputDataRate(BMP3_ODR_25_HZ); + bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_2X); + bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X); + bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_15); + bmp.readPressure(); // throw away first reading + bmpPresent = true; + return true; +} diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index 9f564dfb..d380e356 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -99,7 +99,7 @@ bool bmsCanInitialized = false; bool escTwaiInitialized = false; -UnifiedBatteryData unifiedBatteryData = {0.0f, 0.0f, 0.0f}; +UnifiedBatteryData unifiedBatteryData = {0.0f, 0.0f, 0.0f, 0.0f}; // volts, amps, power, soc // Throttle PWM smoothing buffer is managed in throttle.cpp @@ -159,6 +159,7 @@ extern TaskHandle_t deviceStateUpdateTaskHandle; extern TaskHandle_t buttonTaskHandle; extern TaskHandle_t vibeTaskHandle; +#ifdef OPENPPG_DEBUG static TaskHandle_t stackLoggerTaskHandle = NULL; static const uint32_t STACK_LOGGER_INTERVAL_MS = 5000; // Log every 5 seconds @@ -205,6 +206,7 @@ static void stackWatermarkLoggerTask(void* parameter) { vTaskDelay(pdMS_TO_TICKS(STACK_LOGGER_INTERVAL_MS)); } } +#endif // OPENPPG_DEBUG // Forward declarations for tasks defined in other compilation units extern void vibeTask(void* parameter); @@ -1210,7 +1212,6 @@ void handleThrottle() { // extern unsigned long lastEscTimeUpdateMillis; // REMOVED - Using struct member now void syncESCTelemetry() { - unsigned long currentTime = millis(); // Update ESC state based first on TWAI init, then on time since last update received if (!escTwaiInitialized) { escTelemetryData.escState = TelemetryState::NOT_CONNECTED; From 3e8a5034d443b5546182817a343a058ab7732e5b Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 29 Jan 2026 09:27:40 -0600 Subject: [PATCH 17/22] Enable app rollback and improve OTA validation logic App rollback is now enabled in the configuration. The OTA validation logic in main.cpp has been improved to only mark the firmware as valid after confirming the first boot post-update, ensuring rollback protection is properly activated. --- sdkconfig.OpenPPG-CESP32S3-CAN-SP140 | 6 ++++-- src/sp140/main.cpp | 12 +++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 index dec50b06..fee868d4 100644 --- a/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 +++ b/sdkconfig.OpenPPG-CESP32S3-CAN-SP140 @@ -65,7 +65,8 @@ CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y CONFIG_BOOTLOADER_WDT_ENABLE=y # CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set CONFIG_BOOTLOADER_WDT_TIME_MS=9000 -# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y +# CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK is not set # CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set # CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set # CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set @@ -1707,7 +1708,8 @@ CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set CONFIG_LOG_BOOTLOADER_LEVEL=3 -# CONFIG_APP_ROLLBACK_ENABLE is not set +CONFIG_APP_ROLLBACK_ENABLE=y +# CONFIG_APP_ANTI_ROLLBACK is not set # CONFIG_FLASH_ENCRYPTION_ENABLED is not set # CONFIG_FLASHMODE_QIO is not set # CONFIG_FLASHMODE_QOUT is not set diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index d380e356..13f13878 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -903,9 +903,15 @@ void setup() { // Enable sensor monitoring enableMonitoring(); - // Confirm successful boot to prevent OTA rollback - if (esp_ota_mark_app_valid_cancel_rollback() == ESP_OK) { - USBSerial.println("OTA: Firmware marked valid - Rollback cancelled"); + // Check if this is first boot after OTA update and validate if needed + esp_ota_img_states_t ota_state; + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { + // First boot after OTA - if we made it this far, firmware is good + USBSerial.println("OTA: First boot after update - validating firmware"); + esp_ota_mark_app_valid_cancel_rollback(); + USBSerial.println("OTA: Firmware marked valid - Rollback protection active"); + } } USBSerial.println("=== Boot complete ==="); From eb8ab178d4e3de34cae097847dbb88d8d0dfa2c6 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 29 Jan 2026 11:24:04 -0600 Subject: [PATCH 18/22] tweak LED ram usage --- src/sp140/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index 13f13878..e7c76b1c 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -725,7 +725,7 @@ void setupTasks() { xTaskCreatePinnedToCore(vibeTask, "Vibration", 1536, NULL, 2, &vibeTaskHandle, 1); // Unpinned tasks (can run on either core) - xTaskCreate(blinkLEDTask, "blinkLed", 2560, NULL, 1, &blinkLEDTaskHandle); + xTaskCreate(blinkLEDTask, "blinkLed", 2048, NULL, 1, &blinkLEDTaskHandle); xTaskCreate(updateBLETask, "BLE Update Task", 8192, NULL, 1, NULL); xTaskCreate(deviceStateUpdateTask, "State Update Task", 2048, NULL, 1, &deviceStateUpdateTaskHandle); xTaskCreate(updateESCBLETask, "ESC BLE Update Task", 8192, NULL, 1, NULL); From 5de79639ac22e7485b776e3eb7b8daf5b0f6fa96 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 29 Jan 2026 11:37:00 -0600 Subject: [PATCH 19/22] Refactor altimeter sensor handling to dedicated task Introduced AltimeterTelemetry struct and global instance for thread-safe sharing of barometric sensor data. Added a dedicated altimeterTask for polling the sensor at 10 Hz, reducing I2C contention and simplifying access to altitude, temperature, pressure, and vertical speed across tasks. Updated BLE and display logic to read from the shared telemetry struct instead of direct sensor calls. --- inc/sp140/globals.h | 2 ++ inc/sp140/structs.h | 10 ++++++++++ src/sp140/globals.cpp | 13 ++++++++++++- src/sp140/main.cpp | 39 +++++++++++++++++++++++++++++++++------ 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/inc/sp140/globals.h b/inc/sp140/globals.h index 4e619d3d..cd7f6a61 100644 --- a/inc/sp140/globals.h +++ b/inc/sp140/globals.h @@ -21,6 +21,8 @@ extern UnifiedBatteryData unifiedBatteryData; // Instance to hold battery data extern STR_BMS_TELEMETRY_140 bmsTelemetryData; +extern AltimeterTelemetry altimeterData; + // Hardware/Connection Status Flags extern bool bmsCanInitialized; extern bool escTwaiInitialized; diff --git a/inc/sp140/structs.h b/inc/sp140/structs.h index 7937647b..559f1545 100644 --- a/inc/sp140/structs.h +++ b/inc/sp140/structs.h @@ -119,6 +119,16 @@ typedef struct { } STR_BMS_TELEMETRY_140; #pragma pack(pop) +// Altimeter telemetry - read by multiple tasks, written by altimeter task only +struct AltimeterTelemetry { + float altitude; // Relative altitude AGL (meters) + float temperature; // Barometer temperature (°C) + float pressure; // Barometric pressure (hPa) + float verticalSpeed; // Vertical speed (m/s) + unsigned long lastUpdate; // Timestamp of last reading (millis) + bool connected; // Sensor available and responding +}; + // Add this struct definition near other structs struct MelodyRequest { uint16_t* notes; diff --git a/src/sp140/globals.cpp b/src/sp140/globals.cpp index 808c791f..b2413f13 100644 --- a/src/sp140/globals.cpp +++ b/src/sp140/globals.cpp @@ -33,4 +33,15 @@ int cruisedPotVal = 0; float watts = 0; float wattHoursUsed = 0; -STR_DEVICE_DATA_140_V1 deviceData; +STR_DEVICE_DATA_140_V1 deviceData; + +// Altimeter telemetry - single writer (altimeter task), multiple readers +AltimeterTelemetry altimeterData = { + .altitude = 0.0f, + .temperature = 0.0f, + .pressure = 0.0f, + .verticalSpeed = 0.0f, + .lastUpdate = 0, + .connected = false +}; + diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index e7c76b1c..ec9062e5 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -151,6 +151,7 @@ extern TaskHandle_t throttleTaskHandle; extern TaskHandle_t watchdogTaskHandle; extern TaskHandle_t uiTaskHandle; extern TaskHandle_t bmsTaskHandle; +extern TaskHandle_t altimeterTaskHandle; extern TaskHandle_t monitoringTaskHandle; extern TaskHandle_t audioTaskHandle; extern TaskHandle_t webSerialTaskHandle; @@ -177,6 +178,7 @@ static void stackWatermarkLoggerTask(void* parameter) { {"watchdog", &watchdogTaskHandle}, {"UI", &uiTaskHandle}, {"BMS", &bmsTaskHandle}, + {"Altimeter", &altimeterTaskHandle}, {"Monitoring", &monitoringTaskHandle}, {"Audio", &audioTaskHandle}, {"WebSerial", &webSerialTaskHandle}, @@ -310,6 +312,7 @@ TaskHandle_t throttleTaskHandle = NULL; TaskHandle_t watchdogTaskHandle = NULL; TaskHandle_t uiTaskHandle = NULL; TaskHandle_t bmsTaskHandle = NULL; +TaskHandle_t altimeterTaskHandle = NULL; // Altimeter sensor task TaskHandle_t vibeTaskHandle = NULL; // Vibration motor task TaskHandle_t monitoringTaskHandle = NULL; // Sensor monitoring task @@ -415,10 +418,10 @@ void updateBLETask(void *pvParameters) { updateBMSTelemetry(newBmsTelemetry); // Update controller telemetry at same 10Hz rate as BMS - // Gather sensor data from barometer and ESP32 - float altitude = getAltitude(deviceData); - float baro_temp = getBaroTemperature(); - float vario = getVerticalSpeed(); + // Read from altimeter telemetry (no I2C blocking) + float altitude = altimeterData.altitude; + float baro_temp = altimeterData.temperature; + float vario = altimeterData.verticalSpeed; float mcu_temp = temperatureRead(); // ESP32 internal temp sensor updateControllerPackedTelemetry(altitude, baro_temp, vario, mcu_temp); } @@ -435,8 +438,8 @@ void refreshDisplay() { } if (xSemaphoreTake(lvglMutex, pdMS_TO_TICKS(40)) == pdTRUE) { - // Get the current relative altitude (updates buffer for vario) - const float currentRelativeAltitude = getAltitude(deviceData); + // Read from altimeter telemetry (no I2C blocking) + const float currentRelativeAltitude = altimeterData.altitude; // Determine the altitude to show on the display float altitudeToShow = 0.0f; // Default to 0 @@ -581,6 +584,29 @@ void bmsTask(void *pvParameters) { } } +// Altimeter task: ~10 Hz polling of BMP3XX sensor via I2C +// Single writer - prevents I2C bus contention +void altimeterTask(void* pvParameters) { + TickType_t lastWake = xTaskGetTickCount(); + const TickType_t altimeterTicks = pdMS_TO_TICKS(100); // 10 Hz + + for (;;) { + if (bmpPresent) { + // Single I2C read cycle - no contention with other tasks + altimeterData.altitude = getAltitude(deviceData); + altimeterData.temperature = getBaroTemperature(); + altimeterData.pressure = getBaroPressure(); + altimeterData.verticalSpeed = getVerticalSpeed(); + altimeterData.lastUpdate = millis(); + altimeterData.connected = true; + } else { + altimeterData.connected = false; + } + + vTaskDelayUntil(&lastWake, altimeterTicks); + } +} + void loadHardwareConfig() { board_config = s3_config; // ESP32S3 is only supported board @@ -720,6 +746,7 @@ void setupTasks() { // Core 1 tasks (UI, communications) xTaskCreatePinnedToCore(uiTask, "UI", 5888, NULL, 2, &uiTaskHandle, 1); xTaskCreatePinnedToCore(bmsTask, "BMS", 2304, NULL, 2, &bmsTaskHandle, 1); + xTaskCreatePinnedToCore(altimeterTask, "Altimeter", 2048, NULL, 2, &altimeterTaskHandle, 1); xTaskCreatePinnedToCore(bleStateUpdateTask, "BLEStateUpdate", 8192, NULL, 1, &bleStateUpdateTaskHandle, 1); xTaskCreatePinnedToCore(audioTask, "Audio", 1536, NULL, 1, &audioTaskHandle, 1); xTaskCreatePinnedToCore(vibeTask, "Vibration", 1536, NULL, 2, &vibeTaskHandle, 1); From fd40868929e68008e2823c68e3964102b10888f0 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 29 Jan 2026 23:22:17 -0600 Subject: [PATCH 20/22] Refactor altimeter data sharing using FreeRTOS queue Introduces an AltimeterData struct and a FreeRTOS queue for thread-safe sharing of altimeter telemetry between tasks. Updates BLE and display tasks to read from the queue when available, with fallback to direct sensor reads. The altimeter task now samples at 20 Hz and publishes data to the queue, improving concurrency and reducing I2C contention. --- src/sp140/main.cpp | 78 +++++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index ec9062e5..37c119ee 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -121,6 +121,15 @@ struct BLEStateUpdate { QueueHandle_t bleStateQueue = NULL; TaskHandle_t bleStateUpdateTaskHandle = NULL; +// Altimeter telemetry for queue-based sharing +struct AltimeterData { + float altitude; + float temperature; + float verticalSpeed; +}; +QueueHandle_t altimeterQueue = NULL; +TaskHandle_t altimeterTaskHandle = NULL; + // Device state broadcast QueueHandle_t deviceStateQueue = NULL; TaskHandle_t deviceStateUpdateTaskHandle = NULL; @@ -312,7 +321,6 @@ TaskHandle_t throttleTaskHandle = NULL; TaskHandle_t watchdogTaskHandle = NULL; TaskHandle_t uiTaskHandle = NULL; TaskHandle_t bmsTaskHandle = NULL; -TaskHandle_t altimeterTaskHandle = NULL; // Altimeter sensor task TaskHandle_t vibeTaskHandle = NULL; // Vibration motor task TaskHandle_t monitoringTaskHandle = NULL; // Sensor monitoring task @@ -418,10 +426,23 @@ void updateBLETask(void *pvParameters) { updateBMSTelemetry(newBmsTelemetry); // Update controller telemetry at same 10Hz rate as BMS - // Read from altimeter telemetry (no I2C blocking) - float altitude = altimeterData.altitude; - float baro_temp = altimeterData.temperature; - float vario = altimeterData.verticalSpeed; + // Get altimeter data - from queue if available, otherwise direct read + float altitude, baro_temp, vario; + if (altimeterQueue != NULL && uxQueueMessagesWaiting(altimeterQueue) > 0) { + AltimeterData altData; + if (xQueuePeek(altimeterQueue, &altData, 0) == pdTRUE) { + altitude = altData.altitude; + baro_temp = altData.temperature; + vario = altData.verticalSpeed; + } else { + altitude = baro_temp = vario = 0.0f; + } + } else { + // Fallback to direct read (during boot or if queue empty) + altitude = getAltitude(deviceData); + baro_temp = getBaroTemperature(); + vario = getVerticalSpeed(); + } float mcu_temp = temperatureRead(); // ESP32 internal temp sensor updateControllerPackedTelemetry(altitude, baro_temp, vario, mcu_temp); } @@ -438,8 +459,17 @@ void refreshDisplay() { } if (xSemaphoreTake(lvglMutex, pdMS_TO_TICKS(40)) == pdTRUE) { - // Read from altimeter telemetry (no I2C blocking) - const float currentRelativeAltitude = altimeterData.altitude; + // Get altitude - from queue if available, otherwise direct read + float currentRelativeAltitude = 0.0f; + if (altimeterQueue != NULL && uxQueueMessagesWaiting(altimeterQueue) > 0) { + AltimeterData altData; + if (xQueuePeek(altimeterQueue, &altData, 0) == pdTRUE) { + currentRelativeAltitude = altData.altitude; + } + } else { + // Fallback to direct read (during boot or if queue empty) + currentRelativeAltitude = getAltitude(deviceData); + } // Determine the altitude to show on the display float altitudeToShow = 0.0f; // Default to 0 @@ -584,26 +614,24 @@ void bmsTask(void *pvParameters) { } } -// Altimeter task: ~10 Hz polling of BMP3XX sensor via I2C -// Single writer - prevents I2C bus contention -void altimeterTask(void* pvParameters) { +// Altimeter task: ~20 Hz sampling, writes to queue for thread-safe access +void altimeterTask(void *pvParameters) { TickType_t lastWake = xTaskGetTickCount(); - const TickType_t altimeterTicks = pdMS_TO_TICKS(100); // 10 Hz + const TickType_t altTicks = pdMS_TO_TICKS(50); // 20 Hz + AltimeterData data; for (;;) { - if (bmpPresent) { - // Single I2C read cycle - no contention with other tasks - altimeterData.altitude = getAltitude(deviceData); - altimeterData.temperature = getBaroTemperature(); - altimeterData.pressure = getBaroPressure(); - altimeterData.verticalSpeed = getVerticalSpeed(); - altimeterData.lastUpdate = millis(); - altimeterData.connected = true; - } else { - altimeterData.connected = false; + // Read from sensor (this populates the internal vario buffer too) + data.altitude = getAltitude(deviceData); + data.temperature = getBaroTemperature(); + data.verticalSpeed = getVerticalSpeed(); + + // Publish to queue for other tasks to consume + if (altimeterQueue != NULL) { + xQueueOverwrite(altimeterQueue, &data); } - vTaskDelayUntil(&lastWake, altimeterTicks); + vTaskDelayUntil(&lastWake, altTicks); } } @@ -728,6 +756,12 @@ void createAllQueues() { if (melodyQueue == NULL) { USBSerial.println("Error creating melody queue"); } + + // Altimeter data queue for thread-safe sensor sharing + altimeterQueue = xQueueCreate(1, sizeof(AltimeterData)); + if (altimeterQueue == NULL) { + USBSerial.println("Error creating altimeter queue"); + } } /** From 0e70ac17a9bbbf98a3f6bb396b2787a710364840 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 16 Feb 2026 21:54:25 -0600 Subject: [PATCH 21/22] Improve BLE OTA timeout and conn params Add OTA timeout/abort handling and dynamic BLE connection parameter switching to improve OTA reliability and normal operation battery usage. Introduces requestFastConnParams/requestNormalConnParams and activeConnHandle to tighten connection interval for OTA (15ms) and relax it for telemetry (~36ms). Implements abortOta() and checkOtaTimeout() (30s idle timeout), hooks timeout check into the BLE update task, and aborts OTA on disconnect. Other tweaks: set BLE TX power, advertise only primary UUID to fit payload, include ble_core in OTA code, and remove unused oldDeviceConnected variable. Also permit Bash(grep:*) in local claude settings. --- inc/sp140/ble.h | 1 - inc/sp140/ble/ble_core.h | 6 +++++ inc/sp140/ble/ota_service.h | 12 ++++++++++ src/sp140/ble.cpp | 1 - src/sp140/ble/ble_core.cpp | 41 ++++++++++++++++++++++++++++++----- src/sp140/ble/ota_service.cpp | 20 +++++++++++++++++ src/sp140/main.cpp | 3 +++ 7 files changed, 77 insertions(+), 7 deletions(-) diff --git a/inc/sp140/ble.h b/inc/sp140/ble.h index 7a56cfc3..abadb6db 100644 --- a/inc/sp140/ble.h +++ b/inc/sp140/ble.h @@ -10,6 +10,5 @@ extern NimBLECharacteristic* pThrottleCharacteristic; extern NimBLECharacteristic* pDeviceStateCharacteristic; extern NimBLEServer* pServer; extern volatile bool deviceConnected; -extern volatile bool oldDeviceConnected; #endif // INC_SP140_BLE_H_ diff --git a/inc/sp140/ble/ble_core.h b/inc/sp140/ble/ble_core.h index 4e786799..407d6399 100644 --- a/inc/sp140/ble/ble_core.h +++ b/inc/sp140/ble/ble_core.h @@ -8,4 +8,10 @@ void setupBLE(); // Allow modules to trigger advertising after disconnects. void restartBLEAdvertising(); +// Request fast connection parameters (15ms interval) for OTA transfers. +void requestFastConnParams(); + +// Restore normal connection parameters (~36ms interval) after OTA. +void requestNormalConnParams(); + #endif // INC_SP140_BLE_BLE_CORE_H_ diff --git a/inc/sp140/ble/ota_service.h b/inc/sp140/ble/ota_service.h index d538f694..ff6cf271 100644 --- a/inc/sp140/ble/ota_service.h +++ b/inc/sp140/ble/ota_service.h @@ -15,4 +15,16 @@ void initOtaBleService(NimBLEServer* pServer); */ bool isOtaInProgress(); +/** + * Abort any in-progress OTA update and reset state. + * Safe to call even if no OTA is in progress. + */ +void abortOta(); + +/** + * Check if OTA has been idle too long and abort if so. + * Call periodically (e.g. from a monitoring task). + */ +void checkOtaTimeout(); + #endif // INC_SP140_BLE_OTA_SERVICE_H_ diff --git a/src/sp140/ble.cpp b/src/sp140/ble.cpp index 01fadec5..b3b4789e 100644 --- a/src/sp140/ble.cpp +++ b/src/sp140/ble.cpp @@ -5,4 +5,3 @@ NimBLECharacteristic* pThrottleCharacteristic = nullptr; NimBLECharacteristic* pDeviceStateCharacteristic = nullptr; NimBLEServer* pServer = nullptr; volatile bool deviceConnected = false; -volatile bool oldDeviceConnected = false; diff --git a/src/sp140/ble/ble_core.cpp b/src/sp140/ble/ble_core.cpp index 4da449db..6569b731 100644 --- a/src/sp140/ble/ble_core.cpp +++ b/src/sp140/ble/ble_core.cpp @@ -12,10 +12,18 @@ namespace { +// Store the active connection handle for conn param updates +uint16_t activeConnHandle = 0; + class BleServerConnectionCallbacks : public NimBLEServerCallbacks { void onConnect(NimBLEServer* server, NimBLEConnInfo& connInfo) override { - (void)connInfo; + activeConnHandle = connInfo.getConnHandle(); deviceConnected = true; + // Request relaxed connection parameters for normal use: ~36ms interval + // Telemetry runs at 1-10Hz so aggressive intervals waste phone battery. + // OTA tightens to 15ms via requestFastConnParams() when it starts. + // Units: interval = 1.25ms, timeout = 10ms + server->updateConnParams(activeConnHandle, 24, 40, 0, 400); USBSerial.println("Device connected"); } @@ -23,6 +31,12 @@ class BleServerConnectionCallbacks : public NimBLEServerCallbacks { (void)connInfo; (void)reason; deviceConnected = false; + + if (isOtaInProgress()) { + abortOta(); + USBSerial.println("OTA aborted due to disconnect"); + } + USBSerial.println("Device disconnected"); NimBLEAdvertising* advertising = server->getAdvertising(); advertising->start(); @@ -39,6 +53,9 @@ void setupBLE() { // Set MTU to a phone-friendly size (185 works on iOS; Android can go higher) NimBLEDevice::setMTU(185); + // Set TX power to +9dBm for reliable connections at distance + NimBLEDevice::setPower(9); + pServer = NimBLEDevice::createServer(); static BleServerConnectionCallbacks serverCallbacks; pServer->setCallbacks(&serverCallbacks); @@ -60,17 +77,31 @@ void setupBLE() { advertising->setName("OpenPPG Controller"); advertising->setDiscoverableMode(BLE_GAP_DISC_MODE_GEN); advertising->setConnectableMode(BLE_GAP_CONN_MODE_UND); + // Advertise only 1 UUID to fit within 31+31 byte advertisement payload. + // All other services are discoverable via GATT service discovery after connection. advertising->addServiceUUID(NimBLEUUID(CONFIG_SERVICE_UUID)); - advertising->addServiceUUID(NimBLEUUID(BMS_TELEMETRY_SERVICE_UUID)); - advertising->addServiceUUID(NimBLEUUID(ESC_TELEMETRY_SERVICE_UUID)); - advertising->addServiceUUID(NimBLEUUID(CONTROLLER_SERVICE_UUID)); - advertising->addServiceUUID(NimBLEUUID(OTA_SERVICE_UUID)); advertising->enableScanResponse(true); // Note: Advertising is deferred until after splash screen USBSerial.println("BLE device ready (advertising deferred)"); } +void requestFastConnParams() { + if (pServer == nullptr || !deviceConnected) { + return; + } + // Tighten to 15ms interval for OTA throughput + pServer->updateConnParams(activeConnHandle, 12, 12, 0, 200); +} + +void requestNormalConnParams() { + if (pServer == nullptr || !deviceConnected) { + return; + } + // Relax back to ~36ms for normal telemetry + pServer->updateConnParams(activeConnHandle, 24, 40, 0, 400); +} + void restartBLEAdvertising() { if (pServer == nullptr) { return; diff --git a/src/sp140/ble/ota_service.cpp b/src/sp140/ble/ota_service.cpp index 9e70e442..2523b8fc 100644 --- a/src/sp140/ble/ota_service.cpp +++ b/src/sp140/ble/ota_service.cpp @@ -3,6 +3,7 @@ #include #include "esp_ota_ops.h" #include "esp_partition.h" +#include "sp140/ble/ble_core.h" #include "sp140/ble/ble_ids.h" #include "sp140/device_state.h" #include @@ -35,6 +36,10 @@ uint16_t currentSectorIndex = 0; size_t receivedBytes = 0; uint32_t packetCount = 0; +// Idle timeout: abort OTA if no data received within this period +const unsigned long OTA_TIMEOUT_MS = 30000; +volatile unsigned long lastOtaActivityMs = 0; + // OTA characteristics NimBLECharacteristic* pRecvFwChar = nullptr; NimBLECharacteristic* pProgressChar = nullptr; @@ -101,10 +106,13 @@ void sendCommandResponse(uint16_t ackId, uint16_t status) { } } +} // namespace + void abortOta() { if (otaInProgress) { if (updateHandle) esp_ota_abort(updateHandle); otaInProgress = false; + requestNormalConnParams(); USBSerial.println("OTA: Aborted."); } sectorBufferLen = 0; @@ -112,6 +120,15 @@ void abortOta() { receivedBytes = 0; } +void checkOtaTimeout() { + if (otaInProgress && (millis() - lastOtaActivityMs > OTA_TIMEOUT_MS)) { + USBSerial.println("OTA: Idle timeout, aborting."); + abortOta(); + } +} + +namespace { + class OtaCommandCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) override { std::string value = pChar->getValue(); @@ -171,6 +188,8 @@ class OtaCommandCallback : public NimBLECharacteristicCallbacks { receivedBytes = 0; sectorBufferLen = 0; currentSectorIndex = 0; + lastOtaActivityMs = millis(); + requestFastConnParams(); sendCommandResponse(CMD_START, 0x0000); // Accept @@ -217,6 +236,7 @@ class OtaDataCallback : public NimBLECharacteristicCallbacks { if (len < 3) return; // Header: Sector(2) + Seq(1) + lastOtaActivityMs = millis(); packetCount++; if (packetCount % 100 == 0) { USBSerial.printf("OTA: Received %lu packets (sector %u)\n", diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index 37c119ee..bfe0fdfe 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -447,6 +447,9 @@ void updateBLETask(void *pvParameters) { updateControllerPackedTelemetry(altitude, baro_temp, vario, mcu_temp); } + // Check for stalled OTA updates + checkOtaTimeout(); + // Add a small delay to prevent task starvation vTaskDelay(pdMS_TO_TICKS(20)); } From b5cd41ec986fecc7db9b945229d6033a57cd00dc Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Mon, 16 Feb 2026 22:01:37 -0600 Subject: [PATCH 22/22] Merge branch 'master' into esp-idf --- .github/workflows/config.yml | 2 +- inc/sp140/esc.h | 28 ++--- inc/sp140/simple_monitor.h | 17 +++ inc/sp140/structs.h | 22 +--- inc/sp140/throttle.h | 24 ++++- inc/sp140/utilities.h | 19 ++-- inc/sp140/vibration_pwm.h | 31 ------ src/sp140/alert_display.cpp | 3 - src/sp140/altimeter.cpp | 2 +- src/sp140/ble/esc_service.cpp | 2 + src/sp140/device_settings.cpp | 4 +- src/sp140/esc.cpp | 72 +++---------- src/sp140/esc_monitors.cpp | 1 + src/sp140/lvgl/lvgl_updates.cpp | 4 +- src/sp140/main.cpp | 12 +-- src/sp140/throttle.cpp | 36 ++++--- src/sp140/utilities.cpp | 31 ++---- src/sp140/vibration_pwm.cpp | 65 ------------ test/native_stubs/Arduino.h | 4 +- test/test_throttle/test_throttle.cpp | 151 ++++++++++++++++++++------- 20 files changed, 223 insertions(+), 307 deletions(-) diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 7d46738d..afbcf854 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -22,7 +22,7 @@ jobs: with: python-version: 3.x - run: pip install cpplint - - run: cpplint --linelength 140 --filter=-legal/copyright,-runtime/int,-build/include_subdir,-readability/casting,-readability/todo,-build/include_order,-build/include_what_you_use --recursive ./inc/ ./lib/ ./src/ + - run: cpplint --linelength 140 --filter=-legal/copyright,-runtime/int,-build/include_subdir,-readability/casting,-readability/todo,-build/include_order,-build/include_what_you_use,-whitespace/newline --recursive ./inc/ ./lib/ ./src/ pio-test: name: PlatformIO Tests diff --git a/inc/sp140/esc.h b/inc/sp140/esc.h index 595d907d..10962c04 100644 --- a/inc/sp140/esc.h +++ b/inc/sp140/esc.h @@ -2,34 +2,26 @@ #define INC_SP140_ESC_H_ #include + +// Motor temp validity range (disconnected/invalid readings are represented as NaN) +constexpr float MOTOR_TEMP_VALID_MIN_C = -20.0f; +constexpr float MOTOR_TEMP_VALID_MAX_C = 140.0f; + +inline bool isMotorTempValidC(float tempC) { + return tempC > MOTOR_TEMP_VALID_MIN_C && tempC <= MOTOR_TEMP_VALID_MAX_C; +} #include "sp140/structs.h" #include "../../inc/sp140/esp32s3-config.h" #include #include -// Temperature thresholds -#define ESC_MOS_WARN 90 -#define ESC_MOS_CRIT 110 -#define ESC_MCU_WARN 80 -#define ESC_MCU_CRIT 95 -#define ESC_CAP_WARN 85 -#define ESC_CAP_CRIT 100 -#define MOTOR_WARN 105 -#define MOTOR_CRIT 150 - void initESC(); void setESCThrottle(int throttlePWM); void readESCTelemetry(); bool setupTWAI(); -// Get the highest temperature from all ESC sensors -float getHighestTemp(const STR_ESC_TELEMETRY_140& telemetry); - -// Function declarations -TempState checkTempState(float temp, TempComponent component); - -// ESC Error Decoding Functions -String decodeRunningError(uint16_t errorCode); +// ESC Error Decoding Functions +String decodeRunningError(uint16_t errorCode); String decodeSelfCheckError(uint16_t errorCode); bool hasRunningError(uint16_t errorCode); bool hasSelfCheckError(uint16_t errorCode); diff --git a/inc/sp140/simple_monitor.h b/inc/sp140/simple_monitor.h index 3795cdec..a24f1a4f 100644 --- a/inc/sp140/simple_monitor.h +++ b/inc/sp140/simple_monitor.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "sp140/structs.h" @@ -148,6 +149,14 @@ struct SensorMonitor : public IMonitor { void check() override { float v = read(); + if (isnan(v)) { + if (last != AlertLevel::OK) { + logger->log(id, AlertLevel::OK, v); + last = AlertLevel::OK; + } + return; + } + AlertLevel now = AlertLevel::OK; if (v <= thr.critLow) now = AlertLevel::CRIT_LOW; @@ -182,6 +191,14 @@ struct HysteresisSensorMonitor : public SensorMonitor { void check() override { float v = read(); + if (isnan(v)) { + if (last != AlertLevel::OK) { + logger->log(id, AlertLevel::OK, v); + last = AlertLevel::OK; + } + return; + } + AlertLevel now = last; // Start with current state // Hysteresis logic to prevent bouncing diff --git a/inc/sp140/structs.h b/inc/sp140/structs.h index 559f1545..69478a48 100644 --- a/inc/sp140/structs.h +++ b/inc/sp140/structs.h @@ -6,21 +6,6 @@ #pragma pack(push, 1) -// Temperature component enum -enum TempComponent { - COMP_ESC_MOS = 0, - COMP_ESC_MCU = 1, - COMP_ESC_CAP = 2, - COMP_MOTOR = 3 -}; - -enum TempState { - TEMP_INVALID = -1, // Sensor reading is invalid - TEMP_NORMAL = 0, - TEMP_WARNING = 1, - TEMP_CRITICAL = 2 -}; - // Telemetry connection state enum class TelemetryState : uint8_t { NOT_CONNECTED, // Never received data since boot @@ -42,11 +27,6 @@ typedef struct { uint8_t statusFlag; word checksum; unsigned long lastUpdateMs; // Timestamp of last telemetry update - TempState mos_state; // MOS temperature state - TempState mcu_state; // MCU temperature state - TempState cap_state; // CAP temperature state - TempState motor_state; // Motor temperature state - bool temp_sensor_error; // True if any sensor is invalid TelemetryState escState; // Current connection state uint16_t running_error; // Runtime error bitmask uint16_t selfcheck_error; // Self-check error bitmask @@ -177,7 +157,7 @@ typedef struct { float mos_temp; // MOSFET temperature (°C) float cap_temp; // Capacitor temperature (°C) float mcu_temp; // MCU temperature (°C) - float motor_temp; // Motor temperature (°C) + float motor_temp; // Motor temperature (°C), NaN if sensor invalid/disconnected int32_t eRPM; // Electrical RPM uint16_t inPWM; // Input PWM value uint16_t running_error; // Runtime error bitmask diff --git a/inc/sp140/throttle.h b/inc/sp140/throttle.h index 6b8f6a6c..01b9997b 100644 --- a/inc/sp140/throttle.h +++ b/inc/sp140/throttle.h @@ -44,9 +44,22 @@ uint16_t readThrottleRaw(); /** Get the most recently sampled raw throttle value (0..4095). */ uint16_t getLastThrottleRaw(); -/** Convert raw pot reading (0..4095) to PWM microseconds. */ +/** Convert raw pot reading (0..4095) to PWM microseconds (full range). */ int potRawToPwm(uint16_t raw); +/** + * Convert raw pot reading (0..4095) to PWM microseconds using the + * mode-specific maximum. In chill mode the full physical range maps to + * ESC_MIN_PWM..CHILL_MODE_MAX_PWM; in sport mode it maps to the full + * ESC_MIN_PWM..ESC_MAX_PWM range. This gives chill mode full granular + * control over its limited output range instead of a hard clamp. + * + * @param raw Raw ADC value (0..4095) + * @param performance_mode 0 = CHILL, 1 = SPORT + * @return PWM microseconds in the mode's range + */ +int potRawToModePwm(uint16_t raw, uint8_t performance_mode); + /** * Apply mode-based ramp (us/tick) and clamp to the mode's max PWM. * Updates `prevPwm` with the final value. @@ -86,10 +99,13 @@ bool throttleEngaged(); /** * Read throttle input and return smoothed PWM value. * This is the core throttle processing pipeline without any state logic. + * Uses mode-aware mapping so the full physical range covers the mode's + * PWM output range. * + * @param performance_mode 0 = CHILL, 1 = SPORT * @return Smoothed PWM value from throttle input */ -int getSmoothedThrottlePwm(); +int getSmoothedThrottlePwm(uint8_t performance_mode); /** * Reset throttle state for clean startup/disarm. @@ -107,8 +123,8 @@ void handleThrottle(); /** * Calculate the cruise control PWM value from a raw pot reading. - * Uses the same mapping as normal throttle (full range then clamp to mode max). - * Also applies the absolute cruise max cap. + * Uses the same mode-aware mapping as normal throttle, then applies + * the absolute cruise max cap. * * @param potVal Raw potentiometer value (0..4095) * @param performance_mode 0 = CHILL, 1 = SPORT diff --git a/inc/sp140/utilities.h b/inc/sp140/utilities.h index ef1b1ada..3ab16967 100644 --- a/inc/sp140/utilities.h +++ b/inc/sp140/utilities.h @@ -1,15 +1,10 @@ -#ifndef INC_SP140_UTILITIES_H_ -#define INC_SP140_UTILITIES_H_ - -#include - -double mapd(double x, double in_min, double in_max, double out_min, double out_max); - -// Function for digital time display - adds leading zeros -String convertToDigits(byte digits); - -// Function to get unique chip ID -String chipId(); +#ifndef INC_SP140_UTILITIES_H_ +#define INC_SP140_UTILITIES_H_ + +#include + +// Function to get unique chip ID +String chipId(); // Definitions for main rainbow colors in WRGB format for NeoPixel. // The 32-bit color value is WRGB. W (White) is ignored for RGB pixels. diff --git a/inc/sp140/vibration_pwm.h b/inc/sp140/vibration_pwm.h index 2a93198e..89be3513 100644 --- a/inc/sp140/vibration_pwm.h +++ b/inc/sp140/vibration_pwm.h @@ -74,35 +74,4 @@ void pulseVibration(uint16_t duration_ms, uint8_t intensity); */ void stopVibration(); -/** - * @brief Initializes the critical alert service. - * - * This function sets up the necessary resources for handling synchronized critical alerts. - * It should be called once during system initialization. - */ -void initCriticalAlertService(); - -/** - * @brief Starts the critical alert notifications. - * - * Activates the synchronized vibration and border flashing. If alerts are - * already running, this function has no effect. - */ -void startCriticalAlerts(); - -/** - * @brief Stops the critical alert notifications. - * - * Deactivates the vibration and border flashing. If alerts are not currently - * running, this function has no effect. - */ -void stopCriticalAlerts(); - -/** - * @brief Checks if the critical alert system is currently active. - * - * @return true if critical alerts are active, false otherwise. - */ -bool isCriticalAlertActive(); - #endif // INC_SP140_VIBRATION_PWM_H_ diff --git a/src/sp140/alert_display.cpp b/src/sp140/alert_display.cpp index 20d7cef5..ed0c324e 100644 --- a/src/sp140/alert_display.cpp +++ b/src/sp140/alert_display.cpp @@ -43,9 +43,6 @@ void initAlertDisplay() { return; } - // Init the new alert service - initCriticalAlertService(); - // Create aggregation task (low priority) xTaskCreate(alertAggregationTask, "AlertAgg", 4096, NULL, 1, &alertAggregationTaskHandle); USBSerial.println("[AlertDisplay] Init complete"); diff --git a/src/sp140/altimeter.cpp b/src/sp140/altimeter.cpp index 67af3b62..d6a9b12a 100644 --- a/src/sp140/altimeter.cpp +++ b/src/sp140/altimeter.cpp @@ -77,7 +77,7 @@ float getBaroPressure() { // Start the bmp3XX sensor bool setupAltimeter() { - // pull down pin 40 to high to set the address + // Set I2C address pin pinMode(40, OUTPUT); digitalWrite(40, HIGH); if (!bmp.begin_I2C(BMP3XX_DEFAULT_ADDRESS, &Wire)) return false; diff --git a/src/sp140/ble/esc_service.cpp b/src/sp140/ble/esc_service.cpp index cbef110d..4ebf7242 100644 --- a/src/sp140/ble/esc_service.cpp +++ b/src/sp140/ble/esc_service.cpp @@ -93,6 +93,7 @@ void updateESCPackedTelemetry(const STR_ESC_TELEMETRY_140& telemetry) { packet.mos_temp = telemetry.mos_temp; packet.cap_temp = telemetry.cap_temp; packet.mcu_temp = telemetry.mcu_temp; + // motor_temp is NaN when sensor is disconnected/invalid. packet.motor_temp = telemetry.motor_temp; packet.eRPM = static_cast(telemetry.eRPM); packet.inPWM = static_cast(telemetry.inPWM); @@ -119,6 +120,7 @@ void updateESCTelemetryBLE(const STR_ESC_TELEMETRY_140& telemetry) { float voltage = telemetry.volts; float current = telemetry.amps; int32_t rpm = static_cast(telemetry.eRPM); + // motor_temp is NaN when sensor is disconnected/invalid. EscTempsPacket temps = { telemetry.mos_temp, telemetry.cap_temp, diff --git a/src/sp140/device_settings.cpp b/src/sp140/device_settings.cpp index 139b6647..a6e70be4 100644 --- a/src/sp140/device_settings.cpp +++ b/src/sp140/device_settings.cpp @@ -110,7 +110,7 @@ void refreshDeviceData() { deviceData.performance_mode = preferences.getUChar(KEY_PERFORMANCE_MODE, DEFAULT_PERFORMANCE_MODE); deviceData.theme = preferences.getUChar(KEY_THEME, DEFAULT_THEME); deviceData.armed_time = preferences.getUShort(KEY_ARMED_TIME, 0); - deviceData.revision = preferences.getUChar(KEY_REVISION, 3); // Default to ESP32-S3 + deviceData.revision = preferences.getUChar(KEY_REVISION, 0); // Default to ESP32-S3 deviceData.timezone_offset = preferences.getInt(KEY_TIMEZONE_OFFSET, 0); // Validate critical display-related settings @@ -172,7 +172,7 @@ void resetDeviceData() { deviceData = STR_DEVICE_DATA_140_V1(); // Set the revision to ESP32-S3 - deviceData.revision = 3; // Set appropriate revision for ESP32-S3 + deviceData.revision = 0; // Set appropriate revision for ESP32-S3 deviceData.version_major = VERSION_MAJOR; deviceData.version_minor = VERSION_MINOR; diff --git a/src/sp140/esc.cpp b/src/sp140/esc.cpp index 00e9046b..a0abb5f7 100644 --- a/src/sp140/esc.cpp +++ b/src/sp140/esc.cpp @@ -95,7 +95,15 @@ void readESCTelemetry() { escTelemetryData.mos_temp = res->mos_temp / 10.0f; escTelemetryData.cap_temp = res->cap_temp / 10.0f; escTelemetryData.mcu_temp = res->mcu_temp / 10.0f; - escTelemetryData.motor_temp = res->motor_temp / 10.0f; + // Filter motor temp - only update if sensor is connected (valid range: -20°C to 140°C) + // Disconnected sensor reads ~149°C (thermistor pulled high) + float rawMotorTemp = res->motor_temp / 10.0f; + if (isMotorTempValidC(rawMotorTemp)) { + escTelemetryData.motor_temp = rawMotorTemp; + } else { + // Store invalid motor temp as NaN. Downstream consumers can skip on isnan(). + escTelemetryData.motor_temp = NAN; + } escTelemetryData.eRPM = res->speed; escTelemetryData.inPWM = res->recv_pwm / 10.0f; watts = escTelemetryData.amps * escTelemetryData.volts; @@ -104,17 +112,6 @@ void readESCTelemetry() { escTelemetryData.running_error = res->running_error; escTelemetryData.selfcheck_error = res->selfcheck_error; - // Temperature states - escTelemetryData.mos_state = checkTempState(escTelemetryData.mos_temp, COMP_ESC_MOS); - escTelemetryData.mcu_state = checkTempState(escTelemetryData.mcu_temp, COMP_ESC_MCU); - escTelemetryData.cap_state = checkTempState(escTelemetryData.cap_temp, COMP_ESC_CAP); - escTelemetryData.motor_state = checkTempState(escTelemetryData.motor_temp, COMP_MOTOR); - escTelemetryData.temp_sensor_error = - (escTelemetryData.mos_state == TEMP_INVALID) || - (escTelemetryData.mcu_state == TEMP_INVALID) || - (escTelemetryData.cap_state == TEMP_INVALID) || - (escTelemetryData.motor_state == TEMP_INVALID); - // Record the time of this successful communication using the local clock lastSuccessfulCommTimeMs = millis(); } // else: Timestamp hasn't changed, treat as stale data, don't update local timer or telemetry @@ -304,53 +301,10 @@ double mapDouble(double x, double in_min, double in_max, double out_min, double return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } -/** - * Get the highest temperature reading from all ESC sensors - * @param telemetry ESC telemetry data structure - * @return The highest temperature value among motor, MOSFET, and capacitor temps - */ -float getHighestTemp(const STR_ESC_TELEMETRY_140& telemetry) { - return max(telemetry.motor_temp, max(telemetry.mos_temp, telemetry.cap_temp)); -} - -/** - * Check temperature state for a specific component - * @param temp Temperature value to check - * @param component Component type (ESC_MOS, ESC_MCU, ESC_CAP, or MOTOR) - * @return Temperature state (NORMAL, WARNING, CRITICAL, or INVALID) - */ -TempState checkTempState(float temp, TempComponent component) { - // Check for invalid temperature readings - if (temp < -50 || temp > 200) { - return TEMP_INVALID; - } - - switch (component) { - case COMP_ESC_MOS: - return temp >= ESC_MOS_CRIT ? TEMP_CRITICAL : - temp >= ESC_MOS_WARN ? TEMP_WARNING : TEMP_NORMAL; - - case COMP_ESC_MCU: - return temp >= ESC_MCU_CRIT ? TEMP_CRITICAL : - temp >= ESC_MCU_WARN ? TEMP_WARNING : TEMP_NORMAL; - - case COMP_ESC_CAP: - return temp >= ESC_CAP_CRIT ? TEMP_CRITICAL : - temp >= ESC_CAP_WARN ? TEMP_WARNING : TEMP_NORMAL; - - case COMP_MOTOR: - return temp >= MOTOR_CRIT ? TEMP_CRITICAL : - temp >= MOTOR_WARN ? TEMP_WARNING : TEMP_NORMAL; - - default: - return TEMP_INVALID; - } -} - -/** - * Decode running error bitmask into human-readable string - * @param errorCode 16-bit running error code from ESC - * @return String containing decoded error messages +/** + * Decode running error bitmask into human-readable string + * @param errorCode 16-bit running error code from ESC + * @return String containing decoded error messages */ String decodeRunningError(uint16_t errorCode) { if (errorCode == 0) { diff --git a/src/sp140/esc_monitors.cpp b/src/sp140/esc_monitors.cpp index 1d39f25f..c5c9dd51 100644 --- a/src/sp140/esc_monitors.cpp +++ b/src/sp140/esc_monitors.cpp @@ -51,6 +51,7 @@ void addESCMonitors() { monitors.push_back(escCapTemp); // Motor Temperature Monitor - with hysteresis + // Return NaN for disconnected/invalid sensor so monitor logic skips it. static HysteresisSensorMonitor* motorTemp = new HysteresisSensorMonitor( SensorID::Motor_Temp, SensorCategory::ESC, diff --git a/src/sp140/lvgl/lvgl_updates.cpp b/src/sp140/lvgl/lvgl_updates.cpp index 6680b64d..e5945590 100644 --- a/src/sp140/lvgl/lvgl_updates.cpp +++ b/src/sp140/lvgl/lvgl_updates.cpp @@ -4,6 +4,7 @@ #include "../../../inc/sp140/globals.h" #include "../../../inc/sp140/vibration_pwm.h" #include "../../../inc/sp140/shared-config.h" +#include // Flash timer globals - definitions lv_timer_t* cruise_flash_timer = NULL; @@ -704,7 +705,8 @@ void updateLvglMainScreen( lv_obj_remove_style(motor_temp_bg, &style_warning, 0); lv_obj_remove_style(motor_temp_bg, &style_critical, 0); - if (escTelemetry.escState == TelemetryState::CONNECTED && motorTemp > -20.0f) { + // Show motor temp only for a valid numeric reading while ESC is connected. + if (escTelemetry.escState == TelemetryState::CONNECTED && !isnan(motorTemp)) { lv_label_set_text_fmt(motor_temp_label, "%d", static_cast(motorTemp)); if (motorTemp >= motorTempThresholds.critHigh) { diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index bfe0fdfe..e53fce33 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -651,12 +651,6 @@ void printBootMessage() { USBSerial.println(GIT_REV); } -void setupBarometer() { - const int bmp_enabler = 9; - pinMode(bmp_enabler, OUTPUT); - digitalWrite(bmp_enabler, HIGH); // barometer fix for V2 board -} - void setupLED() { pinMode(board_config.led_sw, OUTPUT); // set up the internal LED2 pin if (board_config.enable_neopixel) { @@ -856,8 +850,6 @@ void setup() { const int SCL_PIN = 41; Wire.setPins(SDA_PIN, SCL_PIN); - // Initialize peripherals - setupBarometer(); if (!setupAltimeter()) { USBSerial.println("Error initializing BMP3xx barometer"); } @@ -1266,7 +1258,7 @@ void handleThrottle() { break; case ARMED: - int smoothedPwm = getSmoothedThrottlePwm(); + int smoothedPwm = getSmoothedThrottlePwm(deviceData.performance_mode); finalPwm = applyModeRampClamp(smoothedPwm, prevPwm, deviceData.performance_mode); break; } @@ -1341,7 +1333,7 @@ void afterCruiseEnd() { // Instead of clearing the buffer which causes throttle to drop to 0, // pre-populate it with the current throttle position to ensure smooth transition int currentPotVal = readThrottleRaw(); - int currentPwmVal = potRawToPwm(currentPotVal); + int currentPwmVal = potRawToModePwm(currentPotVal, deviceData.performance_mode); // Pre-fill the buffer with current pot value for smooth transition throttleFilterReset(currentPwmVal); diff --git a/src/sp140/throttle.cpp b/src/sp140/throttle.cpp index 8166e8c0..5c01faf5 100644 --- a/src/sp140/throttle.cpp +++ b/src/sp140/throttle.cpp @@ -61,6 +61,15 @@ int potRawToPwm(uint16_t raw) { return map(raw, POT_MIN_VALUE, POT_MAX_VALUE, ESC_MIN_PWM, ESC_MAX_PWM); } +/** Map raw ADC (0..4095) to mode-specific PWM range. + * Chill mode: full physical range -> ESC_MIN_PWM..CHILL_MODE_MAX_PWM + * Sport mode: full physical range -> ESC_MIN_PWM..ESC_MAX_PWM + */ +int potRawToModePwm(uint16_t raw, uint8_t performance_mode) { + int maxPwm = (performance_mode == 0) ? CHILL_MODE_MAX_PWM : ESC_MAX_PWM; + return map(raw, POT_MIN_VALUE, POT_MAX_VALUE, ESC_MIN_PWM, maxPwm); +} + /** * Apply mode-specific ramp limiting (in microseconds per tick) and clamp * the result to the current mode's max PWM. Updates prevPwm to the final value. @@ -110,13 +119,16 @@ int throttleFilterAverage() { /** * Read throttle input and return smoothed PWM value. * This is the core throttle processing pipeline without any state logic. + * Uses mode-aware mapping so the full physical range covers the mode's + * PWM output range (chill: 1035-1600, sport: 1035-1950). * + * @param performance_mode 0 = CHILL, 1 = SPORT * @return Smoothed PWM value from throttle input */ -int getSmoothedThrottlePwm() { - // Read and convert raw pot to PWM +int getSmoothedThrottlePwm(uint8_t performance_mode) { + // Read and convert raw pot to mode-specific PWM range int potValRaw = readThrottleRaw(); - int targetPwm = potRawToPwm(potValRaw); + int targetPwm = potRawToModePwm(potValRaw, performance_mode); // Smooth in PWM domain using ring buffer throttleFilterPush(targetPwm); @@ -134,21 +146,15 @@ void resetThrottleState(int& prevPwm) { /** * Calculate the cruise control PWM value from a raw pot reading. - * Uses the same mapping as normal throttle: map to full range first, - * then clamp to the mode maximum and cruise maximum. - * - * This ensures cruise maintains the actual throttle output level, - * not a re-mapped value that would cause throttle drop in chill mode. + * Uses the same mode-aware mapping as normal throttle so the full + * physical range maps to the mode's output range, then applies the + * absolute cruise max cap. */ uint16_t calculateCruisePwm(uint16_t potVal, uint8_t performance_mode, float cruiseMaxPct) { - // Step 1: Map to full PWM range (same as normal throttle) - uint16_t pwm = potRawToPwm(potVal); - - // Step 2: Clamp to mode maximum (chill mode caps at lower PWM) - int maxPwmForMode = (performance_mode == 0) ? CHILL_MODE_MAX_PWM : ESC_MAX_PWM; - pwm = min(pwm, (uint16_t)maxPwmForMode); + // Step 1: Map to mode-specific PWM range (same mapping as normal throttle) + uint16_t pwm = potRawToModePwm(potVal, performance_mode); - // Step 3: Apply absolute cruise max cap + // Step 2: Apply absolute cruise max cap uint16_t absoluteMaxCruisePwm = ESC_MIN_PWM + (uint16_t)((ESC_MAX_PWM - ESC_MIN_PWM) * cruiseMaxPct); pwm = min(pwm, absoluteMaxCruisePwm); diff --git a/src/sp140/utilities.cpp b/src/sp140/utilities.cpp index d5444b71..94920f3f 100644 --- a/src/sp140/utilities.cpp +++ b/src/sp140/utilities.cpp @@ -1,27 +1,10 @@ -// Copyright 2021 -#include "sp140/utilities.h" -#include "Arduino.h" - -double mapd(double x, double in_min, double in_max, double out_min, double out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - -/** - * For digital time display - prints leading 0 - * - * @param digits number to be converted to a string. - * @return string `12`, or 07 if `digits` is less than 10. - */ -String convertToDigits(byte digits) { - String digits_string = ""; - if (digits < 10) digits_string.concat("0"); - digits_string.concat(digits); - return digits_string; -} - -/** - * Gets a unique ID string for the ESP32 chip - * +// Copyright 2021 +#include "sp140/utilities.h" +#include "Arduino.h" + +/** + * Gets a unique ID string for the ESP32 chip + * * @return String representation of the chip ID */ String chipId() { diff --git a/src/sp140/vibration_pwm.cpp b/src/sp140/vibration_pwm.cpp index 096385a7..23d2ca51 100644 --- a/src/sp140/vibration_pwm.cpp +++ b/src/sp140/vibration_pwm.cpp @@ -1,6 +1,5 @@ #include "sp140/vibration_pwm.h" #include "sp140/esp32s3-config.h" -#include "sp140/lvgl/lvgl_updates.h" // Pin is configured via s3_config.vibe_pwm const int VIBE_PWM_FREQ = 1000; // Adjust as needed @@ -12,27 +11,6 @@ const int VIBE_PWM_RESOLUTION = 8; // 8-bit resolution // Channels 2-7: Available for future use const int VIBE_PWM_CHANNEL = 0; -static bool criticalVibrationActive = false; -static TaskHandle_t criticalVibeTaskHandle = NULL; - -/** - * Critical vibration task - provides continuous vibration for critical alerts - */ -void criticalVibeTask(void* parameter) { - for (;;) { - if (criticalVibrationActive && ENABLE_VIBE) { - // Pulse every 1 second for critical alerts - ledcWrite(VIBE_PWM_CHANNEL, 200); // Medium intensity for continuous - vTaskDelay(pdMS_TO_TICKS(300)); // 300ms on - ledcWrite(VIBE_PWM_CHANNEL, 0); - vTaskDelay(pdMS_TO_TICKS(700)); // 700ms off (total 1 second cycle) - } else { - // If not active, suspend task to save resources - vTaskSuspend(NULL); - } - } -} - /** * Vibration task - processes vibration requests from queue */ @@ -211,46 +189,3 @@ void customVibePattern(const uint8_t intensities[], const uint16_t durations[], } ledcWrite(VIBE_PWM_CHANNEL, 0); } - -// Service state for critical alerts -static bool g_critical_alert_active = false; - -/** - * @brief Initializes the critical alert service. - */ -void initCriticalAlertService() { - // Initialization can be expanded if needed in the future. -} - -/** - * @brief Starts the critical alert notifications. - */ -void startCriticalAlerts() { - if (g_critical_alert_active) { - return; - } - g_critical_alert_active = true; - - // Start the single master LVGL timer, which will handle both border and vibration - startCriticalBorderFlash(); -} - -/** - * @brief Stops the critical alert notifications. - */ -void stopCriticalAlerts() { - if (!g_critical_alert_active) { - return; - } - g_critical_alert_active = false; - - // Stop the master LVGL timer - stopCriticalBorderFlash(); -} - -/** - * @brief Checks if the critical alert system is currently active. - */ -bool isCriticalAlertActive() { - return g_critical_alert_active; -} diff --git a/test/native_stubs/Arduino.h b/test/native_stubs/Arduino.h index 089be601..ed8c8e60 100644 --- a/test/native_stubs/Arduino.h +++ b/test/native_stubs/Arduino.h @@ -43,7 +43,9 @@ inline void delay(unsigned long ms) { #endif inline void pinMode(int, int) {} -inline int analogRead(int) { return 0; } +inline int g_testAnalogReadValue = 0; +inline void setTestAnalogReadValue(int value) { g_testAnalogReadValue = value; } +inline int analogRead(int) { return g_testAnalogReadValue; } inline void analogReadResolution(int) {} // Math helpers diff --git a/test/test_throttle/test_throttle.cpp b/test/test_throttle/test_throttle.cpp index f182bd57..924c1c29 100644 --- a/test/test_throttle/test_throttle.cpp +++ b/test/test_throttle/test_throttle.cpp @@ -159,6 +159,22 @@ TEST(ThrottleTest, ApplyModeRampClamp) { EXPECT_EQ(result, 1867); // Should ramp by 27, not clamp yet } +// Test deceleration clamping at ESC minimum floor +TEST(ThrottleTest, ApplyModeRampClampDecelToFloor) { + int prevPwm = 1040; + + // Large decel in CHILL mode should never go below ESC_MIN_PWM + int result = applyModeRampClamp(500, prevPwm, 0); + EXPECT_EQ(result, 1035); + EXPECT_EQ(prevPwm, 1035); + + // Large decel in SPORT mode should also clamp at ESC_MIN_PWM + prevPwm = 1040; + result = applyModeRampClamp(500, prevPwm, 1); + EXPECT_EQ(result, 1035); + EXPECT_EQ(prevPwm, 1035); +} + // Test throttle filter functions TEST(ThrottleTest, ThrottleFiltering) { // Clear buffer first @@ -182,6 +198,29 @@ TEST(ThrottleTest, ThrottleFiltering) { EXPECT_EQ(throttleFilterAverage(), 1500); // Should be filled with 1500 } +// Test throttle filter ring buffer rollover (capacity = 8) +TEST(ThrottleTest, ThrottleFilterRollover) { + throttleFilterClear(); + + // Fill exactly to capacity + throttleFilterPush(1000); + throttleFilterPush(1100); + throttleFilterPush(1200); + throttleFilterPush(1300); + throttleFilterPush(1400); + throttleFilterPush(1500); + throttleFilterPush(1600); + throttleFilterPush(1700); + EXPECT_EQ(throttleFilterAverage(), 1350); // (1000..1700)/8 + + // Push beyond capacity: oldest entries should roll off + throttleFilterPush(1800); // Buffer now 1100..1800 + EXPECT_EQ(throttleFilterAverage(), 1450); + + throttleFilterPush(1900); // Buffer now 1200..1900 + EXPECT_EQ(throttleFilterAverage(), 1550); +} + // Test resetThrottleState function TEST(ThrottleTest, ResetThrottleState) { int prevPwm = 1500; @@ -198,83 +237,117 @@ TEST(ThrottleTest, ResetThrottleState) { EXPECT_EQ(throttleFilterAverage(), 0); // Buffer should be empty } -// Test calculateCruisePwm function - ensures cruise maintains actual throttle level -TEST(ThrottleTest, CalculateCruisePwmBasic) { - // Test that cruise PWM matches normal throttle mapping - // potRawToPwm(2048) = 1492 (50% of full range) - // In chill mode, 1492 < 1600 (CHILL_MODE_MAX_PWM), so no clamping - // With 60% cruise cap: 1035 + 915*0.6 = 1584 +// Test getSmoothedThrottlePwm mode-aware mapping + smoothing behavior +TEST(ThrottleTest, GetSmoothedThrottlePwmModeAwareAndSmoothing) { + // CHILL full-stick should map to chill max + throttleFilterClear(); + setTestAnalogReadValue(4095); + EXPECT_EQ(getSmoothedThrottlePwm(0), 1600); + + // SPORT full-stick should map to full ESC max + throttleFilterClear(); + setTestAnalogReadValue(4095); + EXPECT_EQ(getSmoothedThrottlePwm(1), 1950); + + // Verify smoothing in SPORT mode across two samples + throttleFilterClear(); + setTestAnalogReadValue(0); // 1035 + EXPECT_EQ(getSmoothedThrottlePwm(1), 1035); + setTestAnalogReadValue(4095); // 1950 + EXPECT_EQ(getSmoothedThrottlePwm(1), 1492); // (1035 + 1950) / 2 +} - // 50% pot in SPORT mode (mode 1) - should match potRawToPwm exactly +// Test potRawToModePwm - mode-aware mapping +TEST(ThrottleTest, PotRawToModePwmMapping) { + // SPORT mode (mode 1) should be identical to potRawToPwm (full range) + EXPECT_EQ(potRawToModePwm(0, 1), 1035); + EXPECT_EQ(potRawToModePwm(4095, 1), 1950); + EXPECT_EQ(potRawToModePwm(2047, 1), 1492); + EXPECT_EQ(potRawToModePwm(1024, 1), 1263); + EXPECT_EQ(potRawToModePwm(3072, 1), 1721); + + // CHILL mode (mode 0) maps full physical range to 1035-1600 + // Range: 1600 - 1035 = 565 + EXPECT_EQ(potRawToModePwm(0, 0), 1035); // Min ADC -> Min PWM + EXPECT_EQ(potRawToModePwm(4095, 0), 1600); // Max ADC -> CHILL max + EXPECT_EQ(potRawToModePwm(2048, 0), 1317); // 50% -> mid chill range + EXPECT_EQ(potRawToModePwm(1024, 0), 1176); // 25% -> 25% of chill range + EXPECT_EQ(potRawToModePwm(3072, 0), 1458); // 75% -> 75% of chill range +} + +// Test calculateCruisePwm function - ensures cruise uses same mode-aware mapping +TEST(ThrottleTest, CalculateCruisePwmBasic) { + // 50% pot in SPORT mode (mode 1) - should match potRawToModePwm exactly + // potRawToModePwm(2048, 1) = 1492, cruise cap at 60% = 1584 uint16_t result = calculateCruisePwm(2048, 1, 0.60); - EXPECT_EQ(result, 1492); // Same as potRawToPwm(2048) + EXPECT_EQ(result, 1492); // Below cruise cap - // 50% pot in CHILL mode (mode 0) - should also be 1492 (below chill max) + // 50% pot in CHILL mode (mode 0) - uses chill range mapping + // potRawToModePwm(2048, 0) = 1317, cruise cap at 60% = 1584 result = calculateCruisePwm(2048, 0, 0.60); - EXPECT_EQ(result, 1492); // Not clamped, same as normal throttle + EXPECT_EQ(result, 1317); // Chill mode maps full physical range to 1035-1600 } -// Test the bug that was fixed: chill mode cruise shouldn't drop throttle -TEST(ThrottleTest, CalculateCruisePwmChillModeNoDrop) { - // THE BUG: Old code did map(pot, 0, 4095, 1035, 1600) in chill mode - // which gave 1317 for 50% pot instead of 1492 +// Test that cruise in chill mode uses the same mapping as normal throttle +TEST(ThrottleTest, CalculateCruisePwmChillModeConsistency) { + // Cruise should use the same mode-aware mapping as normal throttle. + // In chill mode, full physical range maps to 1035-1600. + int normalThrottle = potRawToModePwm(2048, 0); // 1317 + EXPECT_EQ(normalThrottle, 1317); - // Verify normal throttle mapping at 50% - int normalThrottle = potRawToPwm(2048); - EXPECT_EQ(normalThrottle, 1492); - - // Cruise in chill mode should match normal throttle (no drop!) + // Cruise in chill mode should match the normal throttle mapping uint16_t cruiseThrottle = calculateCruisePwm(2048, 0, 0.60); - EXPECT_EQ(cruiseThrottle, normalThrottle); // CRITICAL: Must match! + EXPECT_EQ(cruiseThrottle, (uint16_t)normalThrottle); // Both use same mapping } -// Test chill mode max clamping -TEST(ThrottleTest, CalculateCruisePwmChillModeClamping) { - // At high throttle in chill mode, should clamp to CHILL_MODE_MAX_PWM (1600) - // potRawToPwm(4095) = 1950, but chill mode caps at 1600 - - // 100% pot in CHILL mode - should clamp to 1600 (not 1950) +// Test chill mode at full physical range +TEST(ThrottleTest, CalculateCruisePwmChillModeFullRange) { + // 100% pot in CHILL mode - maps to exactly CHILL_MODE_MAX_PWM (1600) + // potRawToModePwm(4095, 0) = 1600, cruise cap at 70% = 1675 uint16_t result = calculateCruisePwm(4095, 0, 0.70); - EXPECT_EQ(result, 1600); // Clamped to CHILL_MODE_MAX_PWM + EXPECT_EQ(result, 1600); // Full physical range -> CHILL_MODE_MAX_PWM - // 80% pot in CHILL mode - potRawToPwm(3276) ≈ 1767, clamps to 1600 + // 80% pot in CHILL mode - potRawToModePwm(3276, 0) = 1487 + // Cruise cap at 70% = 1675, so 1487 is below cap result = calculateCruisePwm(3276, 0, 0.70); - EXPECT_EQ(result, 1600); // Clamped to CHILL_MODE_MAX_PWM + EXPECT_EQ(result, 1487); // Full granular control, no clamping } // Test cruise max percentage capping TEST(ThrottleTest, CalculateCruisePwmCruiseMaxCap) { // Cruise max cap at 60%: 1035 + (1950-1035)*0.6 = 1035 + 549 = 1584 - // In SPORT mode at high throttle, cruise cap should apply - // potRawToPwm(4095) = 1950, but cruise cap at 60% = 1584 + // In SPORT mode at full throttle, cruise cap should apply + // potRawToModePwm(4095, 1) = 1950, cruise cap at 60% = 1584 uint16_t result = calculateCruisePwm(4095, 1, 0.60); - EXPECT_EQ(result, 1584); // Capped by cruise max, not mode max + EXPECT_EQ(result, 1584); // Capped by cruise max // At 70% cruise cap: 1035 + 915*0.7 = 1675 result = calculateCruisePwm(4095, 1, 0.70); EXPECT_EQ(result, 1675); // Higher cruise cap - // In chill mode, the lower of (chill max, cruise cap) applies - // Chill max = 1600, cruise cap at 70% = 1675, so 1600 wins + // In chill mode at full throttle, chill max (1600) < cruise cap (1675) + // potRawToModePwm(4095, 0) = 1600 result = calculateCruisePwm(4095, 0, 0.70); - EXPECT_EQ(result, 1600); // Chill mode max is lower than cruise cap + EXPECT_EQ(result, 1600); // Chill mode max is the limiter } // Test edge cases TEST(ThrottleTest, CalculateCruisePwmEdgeCases) { - // Minimum pot value + // Minimum pot value - same in both modes uint16_t result = calculateCruisePwm(0, 0, 0.60); EXPECT_EQ(result, 1035); // ESC_MIN_PWM result = calculateCruisePwm(0, 1, 0.60); EXPECT_EQ(result, 1035); // ESC_MIN_PWM - // Low throttle (25%) - should not be affected by any caps - // potRawToPwm(1024) = 1263 + // Low throttle (25%) in chill mode - uses chill mapping + // potRawToModePwm(1024, 0) = 1176 result = calculateCruisePwm(1024, 0, 0.60); - EXPECT_EQ(result, 1263); + EXPECT_EQ(result, 1176); // Chill mode maps to reduced range + // Low throttle (25%) in sport mode - uses full mapping + // potRawToModePwm(1024, 1) = 1263 result = calculateCruisePwm(1024, 1, 0.60); EXPECT_EQ(result, 1263); }