From a73e1fc50abaa0fa5d868ee24b0125b5f06aa79a Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Sat, 25 Apr 2026 13:55:31 -0300 Subject: [PATCH 1/7] build: update sdkconfig and deps for IDF 5.5.3 Support files included. Signed-off-by: Raffael Rostagno --- dependencies.lock | 278 ++++++++-- sdkconfig | 1280 +++++++++++++++++++++++++++++++++------------ 2 files changed, 1188 insertions(+), 370 deletions(-) diff --git a/dependencies.lock b/dependencies.lock index 0f9e5fc..a9c3e09 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,81 +1,261 @@ dependencies: + espressif/button: + component_hash: d0afa32f0e50d60bc0c6fc23f7eea98adc6b02cfe70b590bc52c23c506745287 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 4.1.6 espressif/cbor: - component_hash: 440f4ee4504841cc9b4f3a8ef755776a612ac9dace355514c68b999868f990ff + component_hash: dad9ca860963e930366510a10b422b3125a4fb27b979712ed65efcbcd742de50 + dependencies: + - name: idf + require: private + version: '>=5.0' source: - service_url: https://api.components.espressif.com/ + registry_url: https://components.espressif.com/ type: service - version: 0.6.0~1 - espressif/esp_diag_data_store: - component_hash: null + version: 0.6.1~4 + espressif/cmake_utilities: + component_hash: 05165f30922b422b4b90c08845e6d449329b97370fbd06309803d8cb539d79e3 + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.1 + espressif/esp_daylight: + component_hash: e27629eb7e12da9e6d01c646e0e4ce6358b977b6f89d4f291a4cc1aea755af94 + dependencies: + - name: idf + require: private + version: '>=5.1' source: - path: /home/ralph/.espressif/esp-rainmaker/components/esp-insights/components/esp_diag_data_store - type: local + registry_url: https://components.espressif.com + type: service version: 1.0.1 + espressif/esp_diag_data_store: + component_hash: 93f20de81abb791b4482f1bc99afdc24b25c3ee42aacceeb636e9943e677b1c5 + dependencies: + - name: idf + require: private + version: '>=5.1' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.0 espressif/esp_diagnostics: - component_hash: null + component_hash: 32f2e8b9fea1e055a8460fb8afdc96360c40dd461128ee13ceed44296c068e90 + dependencies: + - name: espressif/rmaker_common + registry_url: https://components.espressif.com + require: private + version: ^1.5 + - name: idf + require: private + version: '>=5.1' source: - path: /home/ralph/.espressif/esp-rainmaker/components/esp-insights/components/esp_diagnostics - type: local - version: 1.0.1 + registry_url: https://components.espressif.com + type: service + version: 1.3.3 espressif/esp_insights: - component_hash: null + component_hash: ed93cb82d424d9c651d656ef19b6de5b78bb356fefaa5b4003c136ef188846e3 + dependencies: + - name: espressif/cbor + registry_url: https://components.espressif.com + require: private + version: ~0.6 + - name: espressif/esp_diag_data_store + registry_url: https://components.espressif.com + require: private + version: 1.1.0 + - name: espressif/esp_diagnostics + registry_url: https://components.espressif.com + require: private + version: '>=1.3.0' + - name: espressif/rmaker_common + registry_url: https://components.espressif.com + require: private + version: ^1.5 + - name: idf + require: private + version: '>=5.1' source: - path: /home/ralph/.espressif/esp-rainmaker/components/esp-insights/components/esp_insights - type: local - version: 1.0.1 + registry_url: https://components.espressif.com/ + type: service + version: 1.3.3 espressif/esp_rainmaker: - component_hash: null + component_hash: 006d95ee136e9b912484c1b942d620e0f33399af153ab684c39478ebb1c16bbb + dependencies: + - name: espressif/cbor + registry_url: https://components.espressif.com + require: private + version: ~0.6 + - name: espressif/esp_schedule + registry_url: https://components.espressif.com + require: private + version: ~1.3.2 + - name: espressif/esp_secure_cert_mgr + registry_url: https://components.espressif.com + require: private + version: ^2.2.1 + - name: espressif/json_generator + registry_url: https://components.espressif.com + require: private + version: ~1.1.1 + - name: espressif/json_parser + registry_url: https://components.espressif.com + require: private + version: ~1.0.3 + - name: espressif/mdns + registry_url: https://components.espressif.com + require: private + version: ^1.9.0 + - name: espressif/network_provisioning + registry_url: https://components.espressif.com + require: private + version: '>=1.*' + - name: espressif/rmaker_common + registry_url: https://components.espressif.com + require: private + version: '>=1.7.1' + - name: idf + require: private + version: '>=5.1' source: - path: /home/ralph/.espressif/esp-rainmaker/components/esp_rainmaker - type: local - version: 1.1.0 + registry_url: https://components.espressif.com/ + type: service + version: 1.12.4 espressif/esp_schedule: - component_hash: null + component_hash: 024e1ee23cab447e800519f704193cb5b5652297d86811e6e6c653dea42d8bb1 + dependencies: + - name: espressif/esp_daylight + registry_url: https://components.espressif.com + require: private + version: ~1.0.1 + - name: idf + require: private + version: '>=5.1' source: - path: /home/ralph/.espressif/esp-rainmaker/components/esp_schedule - type: local - version: 1.1.0 + registry_url: https://components.espressif.com/ + type: service + version: 1.3.2 espressif/esp_secure_cert_mgr: - component_hash: a20007d67e65a000670ab77e45d7554c943eb8dcb0abeada0a57dd9adac3a703 + component_hash: 40a986c1f45d91eb6d339ca51dcab90c4ecce2a6622c268edaaa05887852d464 + dependencies: + - name: idf + require: private + version: '>=4.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.9.2 + espressif/jsmn: + component_hash: d80350c41bbaa827c98a25b6072df00884e72f54885996fab4a4f0aebce6b6c3 + dependencies: + - name: idf + require: private + version: '>=4.3' source: - service_url: https://api.components.espressif.com/ + registry_url: https://components.espressif.com type: service - version: 2.4.1 + version: 1.1.0 espressif/json_generator: - component_hash: null + component_hash: 45033e1c199b13f1c8c1b544fb7d4e2df6a8e3071ebdcb1b22582b61a7974ff2 + dependencies: [] source: - path: /home/ralph/.espressif/esp-rainmaker/components/json_generator - type: local - version: 1.1.1 + registry_url: https://components.espressif.com/ + type: service + version: 1.1.2 espressif/json_parser: - component_hash: null + component_hash: d74b81729ad06ec11ff5eb5b1b0d7df1d00e6027fc11471f4b139c70dcf1b1e4 + dependencies: + - name: espressif/jsmn + registry_url: https://components.espressif.com + require: private + rules: + - if: idf_version >=5.0 + version: ~1.1 source: - path: /home/ralph/.espressif/esp-rainmaker/components/json_parser - type: local + registry_url: https://components.espressif.com/ + type: service version: 1.0.3 espressif/mdns: - component_hash: d96d277207c4fec77ec290b7a8b0ca4043e10f407df0ff6aa52e54a94f0c54a5 + component_hash: 1ebe3bd675bb9d1c58f52bc0b609b32f74e572b01c328f9e61282040c775495c + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.11.0 + espressif/network_provisioning: + component_hash: 72d27784e3daf807418a34fb00be136ec50c6db49d989ce981d22e031fc0e7f8 + dependencies: + - name: espressif/cjson + registry_url: https://components.espressif.com + require: private + rules: + - if: idf_version >= 6.0 + version: ^1.7.19 + - name: idf + require: private + version: '>=5.1' source: - service_url: https://api.components.espressif.com/ + registry_url: https://components.espressif.com/ type: service version: 1.2.4 + espressif/qrcode: + component_hash: 31e70991ce22f28d73a4386417fc9c0993f61e1aab1832474fd9e3db8da9dcbd + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.2.0 espressif/rmaker_common: - component_hash: null + component_hash: b299bc04ea98a0b1c065f37c565d3f364b83e4af68bfb69032e058666751fc7e + dependencies: + - name: espressif/mqtt + registry_url: https://components.espressif.com + require: private + rules: + - if: idf_version >=6.0.0 + version: '*' + - name: idf + require: private + version: '>=5.1' source: - path: /home/ralph/.espressif/esp-rainmaker/components/esp-insights/components/rmaker_common - type: local - version: 1.4.3 + registry_url: https://components.espressif.com/ + type: service + version: 1.8.0 idf: - component_hash: null source: type: idf - version: 5.2.0 - jsmn: - component_hash: null - source: - path: /home/ralph/.espressif/esp-rainmaker/components/jsmn - type: local - version: 1.1.0 -manifest_hash: 5b24c4102956148ff0920f14bd2d7a48609749296faa780b7ffa74f36befd011 + version: 5.5.3 +direct_dependencies: +- espressif/button +- espressif/cbor +- espressif/esp_insights +- espressif/esp_rainmaker +- espressif/esp_schedule +- espressif/esp_secure_cert_mgr +- espressif/json_generator +- espressif/json_parser +- espressif/mdns +- espressif/network_provisioning +- espressif/qrcode +- espressif/rmaker_common +- idf +manifest_hash: 8593e20503d25fc4ad67dff9f4dcf885e3a65b617f4ea48a1bddd9e92fb39d05 target: esp32 -version: 1.0.0 +version: 2.0.0 diff --git a/sdkconfig b/sdkconfig index 6dbfa13..fe4b1c1 100644 --- a/sdkconfig +++ b/sdkconfig @@ -1,10 +1,7 @@ # # Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) 5.2.0 Project Configuration +# Espressif IoT Development Framework (ESP-IDF) 5.5.3 Project Configuration # -CONFIG_SOC_BROWNOUT_RESET_SUPPORTED="Not determined" -CONFIG_SOC_TWAI_BRP_DIV_SUPPORTED="Not determined" -CONFIG_SOC_DPORT_WORKAROUND="Not determined" CONFIG_SOC_CAPS_ECO_VER_MAX=301 CONFIG_SOC_ADC_SUPPORTED=y CONFIG_SOC_DAC_SUPPORTED=y @@ -14,6 +11,7 @@ CONFIG_SOC_GPTIMER_SUPPORTED=y CONFIG_SOC_SDMMC_HOST_SUPPORTED=y CONFIG_SOC_BT_SUPPORTED=y CONFIG_SOC_PCNT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y CONFIG_SOC_WIFI_SUPPORTED=y CONFIG_SOC_SDIO_SLAVE_SUPPORTED=y CONFIG_SOC_TWAI_SUPPORTED=y @@ -43,6 +41,11 @@ CONFIG_SOC_CLK_TREE_SUPPORTED=y CONFIG_SOC_MPU_SUPPORTED=y CONFIG_SOC_WDT_SUPPORTED=y CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y CONFIG_SOC_DPORT_WORKAROUND_DIS_INTERRUPT_LVL=5 CONFIG_SOC_XTAL_SUPPORT_26M=y CONFIG_SOC_XTAL_SUPPORT_40M=y @@ -65,6 +68,7 @@ CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=20 CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=9 CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y CONFIG_SOC_SHARED_IDCACHE_SUPPORTED=y CONFIG_SOC_IDCACHE_PER_CORE=y CONFIG_SOC_CPU_CORES_NUM=2 @@ -73,7 +77,7 @@ CONFIG_SOC_CPU_HAS_FPU=y CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 -CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=64 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 CONFIG_SOC_DAC_CHAN_NUM=2 CONFIG_SOC_DAC_RESOLUTION=8 CONFIG_SOC_DAC_DMA_16BIT_ALIGN=y @@ -84,11 +88,14 @@ CONFIG_SOC_GPIO_IN_RANGE_MAX=39 CONFIG_SOC_GPIO_OUT_RANGE_MAX=33 CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0xEF0FEA CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 CONFIG_SOC_I2C_NUM=2 +CONFIG_SOC_HP_I2C_NUM=2 CONFIG_SOC_I2C_FIFO_LEN=32 CONFIG_SOC_I2C_CMD_REG_NUM=16 CONFIG_SOC_I2C_SUPPORT_SLAVE=y CONFIG_SOC_I2C_SUPPORT_APB=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y CONFIG_SOC_I2C_STOP_INDEPENDENT=y CONFIG_SOC_I2S_NUM=2 CONFIG_SOC_I2S_HW_VERSION_1=y @@ -96,13 +103,16 @@ CONFIG_SOC_I2S_SUPPORTS_APLL=y CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y CONFIG_SOC_I2S_SUPPORTS_PDM=y CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y -CONFIG_SOC_I2S_PDM_MAX_TX_LINES=1 +CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=1 CONFIG_SOC_I2S_PDM_MAX_RX_LINES=1 CONFIG_SOC_I2S_SUPPORTS_ADC_DAC=y CONFIG_SOC_I2S_SUPPORTS_ADC=y CONFIG_SOC_I2S_SUPPORTS_DAC=y CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA=y +CONFIG_SOC_I2S_MAX_DATA_WIDTH=24 CONFIG_SOC_I2S_TRANS_SIZE_ALIGN_WORD=y CONFIG_SOC_I2S_LCD_I80_VARIANT=y CONFIG_SOC_LCD_I80_SUPPORTED=y @@ -112,6 +122,7 @@ CONFIG_SOC_LEDC_HAS_TIMER_SPECIFIC_MUX=y CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y CONFIG_SOC_LEDC_SUPPORT_REF_TICK=y CONFIG_SOC_LEDC_SUPPORT_HS_MODE=y +CONFIG_SOC_LEDC_TIMER_NUM=4 CONFIG_SOC_LEDC_CHANNEL_NUM=8 CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=20 CONFIG_SOC_MCPWM_GROUPS=2 @@ -144,6 +155,7 @@ CONFIG_SOC_RTCIO_PIN_COUNT=18 CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y +CONFIG_SOC_RTC_CNTL_NEEDS_ATOMIC_ACCESS=y CONFIG_SOC_SDM_GROUPS=1 CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 CONFIG_SOC_SDM_CLK_SUPPORT_APB=y @@ -164,10 +176,16 @@ CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=64 CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y -CONFIG_SOC_TOUCH_VERSION_1=y +CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 +CONFIG_SOC_TOUCH_SENSOR_VERSION=1 CONFIG_SOC_TOUCH_SENSOR_NUM=10 -CONFIG_SOC_TOUCH_PAD_MEASURE_WAIT_MAX=0xFF +CONFIG_SOC_TOUCH_MIN_CHAN_ID=0 +CONFIG_SOC_TOUCH_MAX_CHAN_ID=9 +CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y +CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 CONFIG_SOC_TWAI_BRP_MIN=2 CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y CONFIG_SOC_TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT=y @@ -177,6 +195,7 @@ CONFIG_SOC_UART_SUPPORT_APB_CLK=y CONFIG_SOC_UART_SUPPORT_REF_TICK=y CONFIG_SOC_UART_FIFO_LEN=128 CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y CONFIG_SOC_SPIRAM_SUPPORTED=y CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y CONFIG_SOC_SHA_SUPPORT_PARALLEL_ENG=y @@ -186,13 +205,13 @@ CONFIG_SOC_SHA_SUPPORT_SHA256=y CONFIG_SOC_SHA_SUPPORT_SHA384=y CONFIG_SOC_SHA_SUPPORT_SHA512=y CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 -CONFIG_SOC_MPI_OPERATIONS_NUM=y +CONFIG_SOC_MPI_OPERATIONS_NUM=1 CONFIG_SOC_RSA_MAX_BIT_LEN=4096 CONFIG_SOC_AES_SUPPORT_AES_128=y CONFIG_SOC_AES_SUPPORT_AES_192=y CONFIG_SOC_AES_SUPPORT_AES_256=y CONFIG_SOC_SECURE_BOOT_V1=y -CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=1 CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=32 CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y @@ -206,11 +225,13 @@ CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y CONFIG_SOC_PM_SUPPORT_MODEM_PD=y CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y CONFIG_SOC_CLK_APLL_SUPPORTED=y CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D4=y CONFIG_SOC_SDMMC_USE_IOMUX=y CONFIG_SOC_SDMMC_NUM_SLOTS=2 CONFIG_SOC_WIFI_WAPI_SUPPORT=y @@ -222,14 +243,18 @@ CONFIG_SOC_BLE_SUPPORTED=y CONFIG_SOC_BLE_MESH_SUPPORTED=y CONFIG_SOC_BT_CLASSIC_SUPPORTED=y CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_BT_H2C_ENC_KEY_CTRL_ENH_VSC_SUPPORTED=y +CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION=y CONFIG_SOC_ULP_HAS_ADC=y CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK=y CONFIG_IDF_CMAKE=y CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y CONFIG_IDF_TARGET_ARCH_XTENSA=y CONFIG_IDF_TARGET_ARCH="xtensa" CONFIG_IDF_TARGET="esp32" -CONFIG_IDF_INIT_VERSION="5.3.0" +CONFIG_IDF_INIT_VERSION="5.5.3" CONFIG_IDF_TARGET_ESP32=y CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 @@ -258,11 +283,29 @@ CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y CONFIG_BOOTLOADER_PROJECT_VER=1 # end of Bootloader manager +# +# Application Rollback +# +CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y +# CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK is not set +# end of Application Rollback + +# +# Recovery Bootloader and Rollback +# +# end of Recovery Bootloader and Rollback + CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 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 + +# +# Log +# +CONFIG_BOOTLOADER_LOG_VERSION_1=y +CONFIG_BOOTLOADER_LOG_VERSION=1 # CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set @@ -271,6 +314,21 @@ CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set CONFIG_BOOTLOADER_LOG_LEVEL=3 +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format + +# +# Settings +# +CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y +CONFIG_BOOTLOADER_LOG_MODE_TEXT=y +# end of Settings +# end of Log + # # Serial Flash Configurations # @@ -286,8 +344,6 @@ 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=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 @@ -320,8 +376,14 @@ CONFIG_ESP_ROM_HAS_MZ_CRC32=y CONFIG_ESP_ROM_HAS_JPEG_DECODE=y CONFIG_ESP_ROM_HAS_UART_BUF_SWITCH=y CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_NEWLIB=y CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_USB_OTG_NUM=-1 +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=-1 +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y +CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y # # Serial flasher config @@ -363,6 +425,7 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 # CONFIG_PARTITION_TABLE_SINGLE_APP is not set # CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set # CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" @@ -383,11 +446,16 @@ CONFIG_EXAMPLE_OUTPUT_GPIO=19 # # CONFIG_ESP_RMAKER_NO_CLAIM is not set CONFIG_ESP_RMAKER_ASSISTED_CLAIM=y +# CONFIG_ESP_RMAKER_CLAIM_KEY_RSA is not set +CONFIG_ESP_RMAKER_CLAIM_KEY_ECDSA=y +CONFIG_ESP_RMAKER_CLAIM_KEY_TYPE=1 CONFIG_ESP_RMAKER_USE_NVS=y CONFIG_ESP_RMAKER_CLAIM_TYPE=2 +# CONFIG_ESP_RMAKER_CLAIM_VIDEOSTREAM_SUPPORT is not set # CONFIG_ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG is not set # CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN is not set CONFIG_ESP_RMAKER_MQTT_HOST="a1p72mufdu6064-ats.iot.us-east-1.amazonaws.com" +CONFIG_ESP_RMAKER_MQTT_CRED_HOST="c10yordfvrjdjb.credentials.iot.us-east-1.amazonaws.com" CONFIG_ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS=y CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING=y CONFIG_ESP_RMAKER_MQTT_DEFAULT_BUDGET=100 @@ -396,7 +464,9 @@ CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD=5 CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT=1 CONFIG_ESP_RMAKER_MAX_PARAM_DATA_SIZE=1024 # CONFIG_ESP_RMAKER_DISABLE_USER_MAPPING_PROV is not set -CONFIG_ESP_RMAKER_USER_ID_CHECK=y +CONFIG_ESP_RMAKER_FACTORY_RESET_REPORTING=y +CONFIG_ESP_RMAKER_ENABLE_CHALLENGE_RESPONSE=y +# CONFIG_ESP_RMAKER_ENABLE_PROV_LOCAL_CTRL is not set # CONFIG_RMAKER_NAME_PARAM_CB is not set CONFIG_ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE=y CONFIG_ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE=y @@ -405,9 +475,11 @@ CONFIG_ESP_RMAKER_LOCAL_CTRL_STACK_SIZE=6144 # CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY_0 is not set CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY_1=y CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY=1 +# CONFIG_ESP_RMAKER_LOCAL_CTRL_CHAL_RESP_ENABLE is not set CONFIG_ESP_RMAKER_CONSOLE_UART_NUM_0=y # CONFIG_ESP_RMAKER_CONSOLE_UART_NUM_1 is not set CONFIG_ESP_RMAKER_CONSOLE_UART_NUM=0 +# CONFIG_ESP_RMAKER_CONSOLE_PARAM_CMDS_ENABLE is not set CONFIG_ESP_RMAKER_USE_CERT_BUNDLE=y # @@ -419,16 +491,30 @@ CONFIG_ESP_RMAKER_OTA_AUTOFETCH_PERIOD=0 # CONFIG_ESP_RMAKER_SKIP_VERSION_CHECK is not set # CONFIG_ESP_RMAKER_SKIP_SECURE_VERSION_CHECK is not set # CONFIG_ESP_RMAKER_SKIP_PROJECT_NAME_CHECK is not set -CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE=1024 CONFIG_ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD=90 +# CONFIG_ESP_RMAKER_OTA_ROLLBACK_REPORT_FAILED is not set # CONFIG_ESP_RMAKER_OTA_DISABLE_AUTO_REBOOT is not set CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT=y +# CONFIG_ESP_RMAKER_OTA_PROGRESS_SUPPORT is not set +CONFIG_ESP_RMAKER_OTA_MAX_RETRIES=3 +CONFIG_ESP_RMAKER_OTA_RETRY_DELAY_MINUTES=5 +CONFIG_ESP_RMAKER_HTTP_OTA_RESUMPTION=y +CONFIG_ESP_RMAKER_OTA_USE_HTTPS=y +# CONFIG_ESP_RMAKER_OTA_USE_MQTT is not set +CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE=1024 # end of ESP RainMaker OTA Config +# +# ESP RainMaker Time Service +# +# CONFIG_ESP_RMAKER_TIME_CURRENT_TIME_PARAM is not set +# end of ESP RainMaker Time Service + # # ESP RainMaker Scheduling # CONFIG_ESP_RMAKER_SCHEDULING_MAX_SCHEDULES=10 +CONFIG_ESP_RMAKER_SCHEDULE_ENABLE_DAYLIGHT=y # end of ESP RainMaker Scheduling # @@ -438,26 +524,48 @@ CONFIG_ESP_RMAKER_SCENES_MAX_SCENES=10 # CONFIG_ESP_RMAKER_SCENES_DEACTIVATE_SUPPORT is not set # end of ESP RainMaker Scenes +# +# ESP RainMaker Connectivity Service +# +CONFIG_ESP_RMAKER_CONNECTIVITY_REPORT_DELAY=5 +# end of ESP RainMaker Connectivity Service + # # ESP RainMaker Command-Response # CONFIG_ESP_RMAKER_CMD_RESP_ENABLE=y +CONFIG_ESP_RMAKER_PARAM_CMD_RESP_ENABLE=y +CONFIG_ESP_RMAKER_PARAM_CMD_RESP_BUFFER_SIZE=2048 # CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE is not set # end of ESP RainMaker Command-Response + +CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI=y # end of ESP RainMaker Config # # ESP RainMaker App Wi-Fi Provisioning # -CONFIG_APP_WIFI_PROV_SHOW_QR=y -# CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP is not set -CONFIG_APP_WIFI_PROV_TRANSPORT_BLE=y -CONFIG_APP_WIFI_PROV_TRANSPORT=2 -CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE=y -CONFIG_APP_WIFI_PROV_MAX_RETRY_CNT=5 -# CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT is not set -CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD=30 -CONFIG_APP_WIFI_PROV_NAME_PREFIX="PROV" +CONFIG_APP_NETWORK_PROV_SHOW_QR=y +# CONFIG_APP_NETWORK_PROV_COMPACT_QR is not set +CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH=5 +# CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP is not set +CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE=y +CONFIG_APP_NETWORK_PROV_TRANSPORT=2 +CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE=y +CONFIG_APP_NETWORK_PROV_MAX_RETRY_CNT=3 +# CONFIG_APP_NETWORK_SHOW_DEMO_INTRO_TEXT is not set +CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD=30 +CONFIG_APP_NETWORK_PROV_NAME_PREFIX="PROV" +# CONFIG_APP_NETWORK_ASYNCHRONOUS_CONNECTION is not set +CONFIG_APP_WIFI_PROV_COMPAT=y + +# +# Factory Data NVS Configuration +# +CONFIG_APP_NETWORK_FACTORY_NVS_PARTITION_NAME="fctry" +CONFIG_APP_NETWORK_PROV_DATA_NAMESPACE="rmaker_creds" +CONFIG_APP_NETWORK_PROV_RANDOM_NVS_KEY="random" +# end of Factory Data NVS Configuration # end of ESP RainMaker App Wi-Fi Provisioning # @@ -470,6 +578,7 @@ CONFIG_COMPILER_OPTIMIZATION_DEBUG=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 # CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set @@ -480,12 +589,18 @@ 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_NO_MERGE_CONSTANTS is not set # CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y # CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set # CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set # CONFIG_COMPILER_DUMP_RTL_FILES is not set CONFIG_COMPILER_RT_LIB_GCCLIB=y CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set # end of Compiler options # @@ -517,38 +632,83 @@ CONFIG_BT_CONTROLLER_ENABLED=y # # NimBLE Options # + +# +# General +# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y # CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set -# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set -# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set -# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set -CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y -# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set -CONFIG_BT_NIMBLE_LOG_LEVEL=1 -CONFIG_BT_NIMBLE_MAX_CONNECTIONS=3 -CONFIG_BT_NIMBLE_MAX_BONDS=3 -CONFIG_BT_NIMBLE_MAX_CCCDS=8 -CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=0 +CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y # CONFIG_BT_NIMBLE_PINNED_TO_CORE_1 is not set -CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096 +CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=y +# end of General + +# +# Roles and Profiles +# CONFIG_BT_NIMBLE_ROLE_CENTRAL=y CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y CONFIG_BT_NIMBLE_ROLE_OBSERVER=y -# CONFIG_BT_NIMBLE_NVS_PERSIST is not set +CONFIG_BT_NIMBLE_GATT_CLIENT=y +CONFIG_BT_NIMBLE_GATT_SERVER=y +# end of Roles and Profiles + +# +# Security (SMP) +# CONFIG_BT_NIMBLE_SECURITY_ENABLE=y CONFIG_BT_NIMBLE_SM_LEGACY=y CONFIG_BT_NIMBLE_SM_SC=y # CONFIG_BT_NIMBLE_SM_SC_DEBUG_KEYS is not set CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_ENCRYPTION=y -# CONFIG_BT_NIMBLE_DEBUG is not set -# CONFIG_BT_NIMBLE_DYNAMIC_SERVICE is not set -CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" -CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 +CONFIG_BT_NIMBLE_SM_LVL=0 +CONFIG_BT_NIMBLE_SM_SC_ONLY=0 +# CONFIG_BT_NIMBLE_SMP_ID_RESET is not set +# CONFIG_BT_NIMBLE_NVS_PERSIST is not set +CONFIG_BT_NIMBLE_MAX_BONDS=3 +# CONFIG_BT_NIMBLE_HANDLE_REPEAT_PAIRING_DELETION is not set +# end of Security (SMP) + +# +# GAP +# +CONFIG_BT_NIMBLE_RPA_TIMEOUT=900 +CONFIG_BT_NIMBLE_WHITELIST_SIZE=12 +# CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT is not set +CONFIG_BT_NIMBLE_HS_PVCY=y +# CONFIG_BT_NIMBLE_HOST_BASED_PRIVACY is not set +# CONFIG_BT_NIMBLE_HOST_ALLOW_CONNECT_WITH_SCAN is not set +# CONFIG_BT_NIMBLE_HOST_QUEUE_CONG_CHECK is not set +CONFIG_BT_NIMBLE_MAX_CONNECTIONS=3 +CONFIG_BT_NIMBLE_MAX_CCCDS=8 +# CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS is not set +CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000 +CONFIG_BT_NIMBLE_USE_ESP_TIMER=y +CONFIG_BT_NIMBLE_HS_FLOW_CTRL=y +CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL=1000 +CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH=2 +CONFIG_BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT=y +# end of GAP + +# +# GATT / ATT +# CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=256 -CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE=0 +CONFIG_BT_NIMBLE_ATT_MAX_PREP_ENTRIES=64 +CONFIG_BT_NIMBLE_GATT_MAX_PROCS=4 +# CONFIG_BT_NIMBLE_BLE_GATT_BLOB_TRANSFER is not set +# CONFIG_BT_NIMBLE_GATT_CACHING is not set +# CONFIG_BT_NIMBLE_INCL_SVC_DISCOVERY is not set +# end of GATT / ATT + +# +# L2CAP +# +CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=0 +# end of L2CAP # # Memory Settings @@ -562,44 +722,64 @@ CONFIG_BT_NIMBLE_TRANSPORT_ACL_SIZE=255 CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70 CONFIG_BT_NIMBLE_TRANSPORT_EVT_COUNT=30 CONFIG_BT_NIMBLE_TRANSPORT_EVT_DISCARD_COUNT=8 +CONFIG_BT_NIMBLE_L2CAP_COC_SDU_BUFF_COUNT=1 +# CONFIG_BT_NIMBLE_MEMPOOL_RUNTIME_ALLOC is not set # end of Memory Settings -CONFIG_BT_NIMBLE_GATT_MAX_PROCS=4 -CONFIG_BT_NIMBLE_HS_FLOW_CTRL=y -CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL=1000 -CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH=2 -CONFIG_BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT=y -CONFIG_BT_NIMBLE_RPA_TIMEOUT=900 -# CONFIG_BT_NIMBLE_MESH is not set -CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=y -CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000 -# CONFIG_BT_NIMBLE_HOST_BASED_PRIVACY is not set -# CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT is not set -CONFIG_BT_NIMBLE_WHITELIST_SIZE=12 -# CONFIG_BT_NIMBLE_TEST_THROUGHPUT_TEST is not set -# CONFIG_BT_NIMBLE_BLUFI_ENABLE is not set -CONFIG_BT_NIMBLE_USE_ESP_TIMER=y -CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=y -# CONFIG_BT_NIMBLE_BLE_GATT_BLOB_TRANSFER is not set +# +# BLE 5.x Features +# +# end of BLE 5.x Features # -# GAP Service +# Services # +CONFIG_BT_NIMBLE_PROX_SERVICE=y +CONFIG_BT_NIMBLE_ANS_SERVICE=y +CONFIG_BT_NIMBLE_CTS_SERVICE=y +CONFIG_BT_NIMBLE_HTP_SERVICE=y +CONFIG_BT_NIMBLE_IPSS_SERVICE=y +CONFIG_BT_NIMBLE_TPS_SERVICE=y +CONFIG_BT_NIMBLE_IAS_SERVICE=y +CONFIG_BT_NIMBLE_LLS_SERVICE=y +CONFIG_BT_NIMBLE_SPS_SERVICE=y +CONFIG_BT_NIMBLE_HR_SERVICE=y +# CONFIG_BT_NIMBLE_HID_SERVICE is not set +CONFIG_BT_NIMBLE_BAS_SERVICE=y +# CONFIG_BT_NIMBLE_SVC_BAS_BATTERY_LEVEL_NOTIFY is not set +CONFIG_BT_NIMBLE_DIS_SERVICE=y +# CONFIG_BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME is not set +# CONFIG_BT_NIMBLE_SVC_DIS_SERIAL_NUMBER is not set +# CONFIG_BT_NIMBLE_SVC_DIS_HARDWARE_REVISION is not set +# CONFIG_BT_NIMBLE_SVC_DIS_FIRMWARE_REVISION is not set +# CONFIG_BT_NIMBLE_SVC_DIS_SOFTWARE_REVISION is not set +# CONFIG_BT_NIMBLE_SVC_DIS_SYSTEM_ID is not set +# CONFIG_BT_NIMBLE_SVC_DIS_PNP_ID is not set +# CONFIG_BT_NIMBLE_SVC_DIS_INCLUDED is not set +CONFIG_BT_NIMBLE_GAP_SERVICE=y +CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" +CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 +CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE=0 +CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM=0 +CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_ENC=0 +CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHEN=0 +CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHOR=0 +# CONFIG_BT_NIMBLE_SVC_GAP_GATT_SECURITY_LEVEL is not set +# CONFIG_BT_NIMBLE_SVC_GAP_RPA_ONLY is not set +CONFIG_BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP=y +# CONFIG_BT_NIMBLE_SVC_GAP_CAR_NOT_SUPP is not set +# CONFIG_BT_NIMBLE_SVC_GAP_CAR_SUPP is not set +CONFIG_BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION=-1 # # GAP Appearance write permissions # # CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE is not set -# end of GAP Appearance write permissions - CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM=0 CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ENC=0 CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHN=0 CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHR=0 -CONFIG_BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP=y -# CONFIG_BT_NIMBLE_SVC_GAP_CAR_NOT_SUPP is not set -# CONFIG_BT_NIMBLE_SVC_GAP_CAR_SUPP is not set -CONFIG_BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION=-1 +# end of GAP Appearance write permissions # # GAP device name write permissions @@ -607,25 +787,83 @@ CONFIG_BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION=-1 # CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE is not set # end of GAP device name write permissions -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM=0 -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_ENC=0 -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHEN=0 -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHOR=0 +# +# Peripheral Preferred Connection Parameters (PPCP) settings +# CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL=0 CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL=0 CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SLAVE_LATENCY=0 CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SUPERVISION_TMO=0 -# end of GAP Service +# end of Peripheral Preferred Connection Parameters (PPCP) settings +# end of Services # -# BLE Services +# Extra Features # -# CONFIG_BT_NIMBLE_HID_SERVICE is not set -# end of BLE Services +# CONFIG_BT_NIMBLE_DYNAMIC_SERVICE is not set +# CONFIG_BT_NIMBLE_BLUFI_ENABLE is not set +# CONFIG_BT_NIMBLE_ENC_ADV_DATA is not set +# CONFIG_BT_NIMBLE_ADV_UUID_CONCAT is not set +# CONFIG_BT_NIMBLE_GATTC_PROC_PREEMPTION_PROTECT is not set +# CONFIG_BT_NIMBLE_GATTC_AUTO_PAIR is not set +CONFIG_BT_NIMBLE_EATT_CHAN_NUM=0 +# CONFIG_BT_NIMBLE_SUBRATE is not set +# CONFIG_BT_NIMBLE_STATIC_PASSKEY is not set +CONFIG_BT_NIMBLE_DTM_MODE_TEST=y +CONFIG_BT_NIMBLE_MEM_OPTIMIZATION=y +CONFIG_BT_NIMBLE_STATIC_TO_DYNAMIC=y +CONFIG_BT_NIMBLE_SM_SIGN_CNT=y +CONFIG_BT_NIMBLE_CPFD_CAFD=y +CONFIG_BT_NIMBLE_RECONFIG_MTU=y +# CONFIG_BT_NIMBLE_LOW_SPEED_MODE is not set +# end of Extra Features + +# +# NimBLE Mesh +# +# CONFIG_BT_NIMBLE_MESH is not set +# end of NimBLE Mesh + +# +# Host-controller Transport +# +CONFIG_UART_HW_FLOWCTRL_DISABLE=y +# CONFIG_UART_HW_FLOWCTRL_CTS_RTS is not set +CONFIG_BT_NIMBLE_HCI_UART_FLOW_CTRL=0 +CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=19 +CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23 +# end of Host-controller Transport + +# +# Debugging/Testing +# +# CONFIG_BT_NIMBLE_MEM_DEBUG is not set +# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set +# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set +# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set +CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y +# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set +CONFIG_BT_NIMBLE_LOG_LEVEL=1 +CONFIG_BT_NIMBLE_PRINT_ERR_NAME=y +# CONFIG_BT_NIMBLE_DEBUG is not set +# CONFIG_BT_NIMBLE_TEST_THROUGHPUT_TEST is not set +# end of Debugging/Testing +# +# Vendor / Optimization +# # CONFIG_BT_NIMBLE_VS_SUPPORT is not set +# CONFIG_BT_NIMBLE_OPTIMIZE_MULTI_CONN is not set # CONFIG_BT_NIMBLE_HIGH_DUTY_ADV_ITVL is not set -# CONFIG_BT_NIMBLE_HOST_QUEUE_CONG_CHECK is not set +# end of Vendor / Optimization + +# +# Helper Utils +# +CONFIG_BT_NIMBLE_CHK_HOST_STATUS=y +CONFIG_BT_NIMBLE_UTIL_API=y +CONFIG_BT_NIMBLE_EXTRA_ADV_FIELDS=y +# end of Helper Utils # end of NimBLE Options # @@ -638,9 +876,10 @@ CONFIG_BTDM_CTRL_BLE_MAX_CONN=3 CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=0 CONFIG_BTDM_CTRL_PCM_ROLE_EFF=0 CONFIG_BTDM_CTRL_PCM_POLAR_EFF=0 +CONFIG_BTDM_CTRL_PCM_FSYNCSHP_EFF=0 CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=3 +CONFIG_BTDM_CTRL_BR_EDR_MIN_ENC_KEY_SZ_DFT_EFF=0 CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=0 -CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0 CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y # CONFIG_BTDM_CTRL_PINNED_TO_CORE_1 is not set CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 @@ -667,143 +906,148 @@ CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE=100 CONFIG_BTDM_SCAN_DUPL_CACHE_REFRESH_PERIOD=0 # CONFIG_BTDM_BLE_MESH_SCAN_DUPL_EN is not set CONFIG_BTDM_CTRL_FULL_SCAN_SUPPORTED=y +# CONFIG_BTDM_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set +# CONFIG_BTDM_CTRL_CHECK_CONNECT_IND_ACCESS_ADDRESS is not set CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM=100 CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 + +# +# BLE disconnects when Instant Passed (0x28) occurs +# +# CONFIG_BTDM_BLE_LLCP_CONN_UPDATE is not set +# CONFIG_BTDM_BLE_LLCP_CHAN_MAP_UPDATE is not set +# end of BLE disconnects when Instant Passed (0x28) occurs + +CONFIG_BTDM_BLE_CHAN_ASS_EN=y +CONFIG_BTDM_BLE_PING_EN=y +# CONFIG_BTDM_CTRL_CONTROLLER_DEBUG_MODE_1 is not set CONFIG_BTDM_RESERVE_DRAM=0xdb5c CONFIG_BTDM_CTRL_HLI=y # end of Controller Options -# end of Bluetooth - -# CONFIG_BLE_MESH is not set # -# Driver Configurations +# Common Options # +CONFIG_BT_ALARM_MAX_NUM=50 +CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y +# CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS is not set # -# Legacy ADC Configuration +# BLE Log # -CONFIG_ADC_DISABLE_DAC=y -# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_BLE_LOG_ENABLED is not set +# end of BLE Log + +# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set +# CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED is not set +# CONFIG_BT_LE_USED_MEM_STATISTICS_ENABLED is not set +# end of Common Options + +# CONFIG_BT_HCI_LOG_DEBUG_EN is not set +# end of Bluetooth + +# CONFIG_BLE_MESH is not set # -# Legacy ADC Calibration Configuration +# Console Library # -CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y -CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y -CONFIG_ADC_CAL_LUT_ENABLE=y -# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set -# end of Legacy ADC Calibration Configuration -# end of Legacy ADC Configuration +# CONFIG_CONSOLE_SORTED_HELP is not set +# end of Console Library # -# SPI Configuration +# Driver Configurations # -# CONFIG_SPI_MASTER_ISR_IN_IRAM is not set -# CONFIG_SPI_SLAVE_IN_IRAM is not set -# CONFIG_SPI_SLAVE_ISR_IN_IRAM is not set -# end of SPI Configuration # -# TWAI Configuration +# Legacy TWAI Driver Configurations # -# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC=y CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST=y CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID=y CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT=y CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y -# end of TWAI Configuration +# end of Legacy TWAI Driver Configurations # -# UART Configuration +# Legacy ADC Driver Configuration # -# CONFIG_UART_ISR_IN_IRAM is not set -# end of UART Configuration +CONFIG_ADC_DISABLE_DAC=y +# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set # -# GPIO Configuration +# Legacy ADC Calibration Configuration # -# CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL is not set -# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set -# end of GPIO Configuration +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y +# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy ADC Calibration Configuration +# end of Legacy ADC Driver Configuration # -# Sigma Delta Modulator Configuration +# Legacy DAC Driver Configurations # -# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set -# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_SDM_ENABLE_DEBUG_LOG is not set -# end of Sigma Delta Modulator Configuration +# CONFIG_DAC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_DAC_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy DAC Driver Configurations # -# GPTimer Configuration +# Legacy MCPWM Driver Configurations # -CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y -# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set -# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set -# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set -# end of GPTimer Configuration +# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy MCPWM Driver Configurations # -# PCNT Configuration +# Legacy Timer Group Driver Configurations # -# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set -# CONFIG_PCNT_ISR_IRAM_SAFE is not set -# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set -# end of PCNT Configuration +# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Timer Group Driver Configurations # -# RMT Configuration +# Legacy RMT Driver Configurations # -# CONFIG_RMT_ISR_IRAM_SAFE is not set -# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set # CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_RMT_ENABLE_DEBUG_LOG is not set -# end of RMT Configuration +# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy RMT Driver Configurations # -# MCPWM Configuration +# Legacy I2S Driver Configurations # -# CONFIG_MCPWM_ISR_IRAM_SAFE is not set -# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set -# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set -# end of MCPWM Configuration +# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2S Driver Configurations # -# I2S Configuration +# Legacy I2C Driver Configurations # -# CONFIG_I2S_ISR_IRAM_SAFE is not set -# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_I2S_ENABLE_DEBUG_LOG is not set -# end of I2S Configuration +# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2C Driver Configurations # -# DAC Configuration +# Legacy PCNT Driver Configurations # -# CONFIG_DAC_CTRL_FUNC_IN_IRAM is not set -# CONFIG_DAC_ISR_IRAM_SAFE is not set -# CONFIG_DAC_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_DAC_ENABLE_DEBUG_LOG is not set -CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=y -# end of DAC Configuration +# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy PCNT Driver Configurations # -# LEDC Configuration +# Legacy SDM Driver Configurations # -# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set -# end of LEDC Configuration +# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy SDM Driver Configurations # -# I2C Configuration +# Legacy Touch Sensor Driver Configurations # -# CONFIG_I2C_ISR_IRAM_SAFE is not set -# CONFIG_I2C_ENABLE_DEBUG_LOG is not set -# end of I2C Configuration +# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Touch Sensor Driver Configurations # end of Driver Configurations # @@ -823,12 +1067,12 @@ CONFIG_EFUSE_MAX_BLK_LEN=192 CONFIG_ESP_TLS_USING_MBEDTLS=y # CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set # CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set -CONFIG_ESP_TLS_SERVER=y # CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set # CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set # CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set # CONFIG_ESP_TLS_PSK_VERIFICATION is not set # CONFIG_ESP_TLS_INSECURE is not set +CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y # end of ESP-TLS # @@ -846,12 +1090,16 @@ CONFIG_ADC_CALI_LUT_ENABLE=y # end of ADC Calibration Configurations CONFIG_ADC_DISABLE_DAC_OUTPUT=y +# CONFIG_ADC_ENABLE_DEBUG_LOG is not set # end of ADC and ADC Calibration # # Wireless Coexistence # +CONFIG_ESP_COEX_ENABLED=y CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y +# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set +# CONFIG_ESP_COEX_GPIO_DEBUG is not set # end of Wireless Coexistence # @@ -860,6 +1108,133 @@ CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y CONFIG_ESP_ERR_TO_NAME_LOOKUP=y # end of Common ESP-related +# +# ESP-Driver:DAC Configurations +# +# CONFIG_DAC_CTRL_FUNC_IN_IRAM is not set +# CONFIG_DAC_ISR_IRAM_SAFE is not set +# CONFIG_DAC_ENABLE_DEBUG_LOG is not set +CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=y +# end of ESP-Driver:DAC Configurations + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL is not set +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# ESP-Driver:GPTimer Configurations +# +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y +# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set +CONFIG_GPTIMER_OBJ_CACHE_SAFE=y +# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:GPTimer Configurations + +# +# ESP-Driver:I2C Configurations +# +# CONFIG_I2C_ISR_IRAM_SAFE is not set +# CONFIG_I2C_ENABLE_DEBUG_LOG is not set +# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y +# end of ESP-Driver:I2C Configurations + +# +# ESP-Driver:I2S Configurations +# +# CONFIG_I2S_ISR_IRAM_SAFE is not set +# CONFIG_I2S_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:I2S Configurations + +# +# ESP-Driver:LEDC Configurations +# +# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:LEDC Configurations + +# +# ESP-Driver:MCPWM Configurations +# +CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y +# CONFIG_MCPWM_ISR_CACHE_SAFE is not set +# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set +CONFIG_MCPWM_OBJ_CACHE_SAFE=y +# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:MCPWM Configurations + +# +# ESP-Driver:PCNT Configurations +# +# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_PCNT_ISR_IRAM_SAFE is not set +# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:PCNT Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y +CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y +CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y +# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set +# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set +# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set +CONFIG_RMT_OBJ_CACHE_SAFE=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# end of ESP-Driver:RMT Configurations + +# +# ESP-Driver:Sigma Delta Modulator Configurations +# +# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set +# CONFIG_SDM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Sigma Delta Modulator Configurations + +# +# ESP-Driver:SPI Configurations +# +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# ESP-Driver:Touch Sensor Configurations +# +# CONFIG_TOUCH_CTRL_FUNC_IN_IRAM is not set +# CONFIG_TOUCH_ISR_IRAM_SAFE is not set +# CONFIG_TOUCH_ENABLE_DEBUG_LOG is not set +# CONFIG_TOUCH_SKIP_FSM_CHECK is not set +# end of ESP-Driver:Touch Sensor Configurations + +# +# ESP-Driver:TWAI Configurations +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_IO_FUNC_IN_IRAM is not set +# CONFIG_TWAI_ISR_CACHE_SAFE is not set +# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:TWAI Configurations + +# +# ESP-Driver:UART Configurations +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of ESP-Driver:UART Configurations + +# +# ESP-Driver:UHCI Configurations +# +# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set +# CONFIG_UHCI_ISR_CACHE_SAFE is not set +# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:UHCI Configurations + # # Ethernet # @@ -892,27 +1267,40 @@ CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y # # GDB Stub # +CONFIG_ESP_GDBSTUB_ENABLED=y # CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME is not set +CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y +CONFIG_ESP_GDBSTUB_MAX_TASKS=32 # end of GDB Stub +# +# ESP HID +# +CONFIG_ESPHID_TASK_SIZE_BT=2048 +CONFIG_ESPHID_TASK_SIZE_BLE=4096 +# end of ESP HID + # # 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 is not set +# CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT is not set +CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT=2000 # end of ESP HTTP client # # HTTP Server # -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 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 # CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set +CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000 # end of HTTP Server # @@ -920,17 +1308,21 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32 # # CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set # CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set +CONFIG_ESP_HTTPS_OTA_EVENT_POST_TIMEOUT=2000 # end of ESP HTTPS OTA # # ESP HTTPS server # CONFIG_ESP_HTTPS_SERVER_ENABLE=y +CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT=2000 +# CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK is not set # end of ESP HTTPS server # # Hardware Settings # +CONFIG_ESP_HW_SUPPORT_FUNC_IN_IRAM=y # # Chip revision @@ -950,6 +1342,12 @@ CONFIG_ESP_REV_MIN_FULL=0 # CONFIG_ESP32_REV_MAX_FULL=399 CONFIG_ESP_REV_MAX_FULL=399 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=99 + +# +# Maximum Supported ESP32 eFuse Block Revision (eFuse Block Rev v0.99) +# # end of Chip revision # @@ -960,6 +1358,7 @@ CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES=4 # CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 @@ -989,50 +1388,76 @@ CONFIG_RTC_CLK_SRC_INT_RC=y # CONFIG_RTC_CLK_SRC_EXT_OSC is not set # CONFIG_RTC_CLK_SRC_INT_8MD256 is not set CONFIG_RTC_CLK_CAL_CYCLES=1024 +CONFIG_RTC_CLK_FUNC_IN_IRAM=y +CONFIG_RTC_TIME_FUNC_IN_IRAM=y # end of RTC Clock Config # # Peripheral Control # -CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y # end of Peripheral Control # # Main XTAL Config # # CONFIG_XTAL_FREQ_26 is not set +# CONFIG_XTAL_FREQ_32 is not set CONFIG_XTAL_FREQ_40=y # CONFIG_XTAL_FREQ_AUTO is not set CONFIG_XTAL_FREQ=40 # end of Main XTAL Config -# end of Hardware Settings # -# LCD and Touch Panel +# Power Supplier # # -# LCD Touch Drivers are maintained in the IDF Component Registry +# Brownout Detector # +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=0 +CONFIG_ESP_BROWNOUT_USE_INTR=y +# end of Brownout Detector +# end of Power Supplier + +CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y +CONFIG_ESP_INTR_IN_IRAM=y +# end of Hardware Settings # -# LCD Peripheral Configuration +# ESP-Driver:LCD Controller Configurations # -CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE=32 # CONFIG_LCD_ENABLE_DEBUG_LOG is not set -# end of LCD Peripheral Configuration -# end of LCD and Touch Panel +# end of ESP-Driver:LCD Controller Configurations + +# +# ESP-MM: Memory Management Configurations +# +# end of ESP-MM: Memory Management Configurations # # ESP NETIF Adapter # CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 +# CONFIG_ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION is not set CONFIG_ESP_NETIF_TCPIP_LWIP=y # CONFIG_ESP_NETIF_LOOPBACK is not set CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y +CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y # CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS is not set # CONFIG_ESP_NETIF_L2_TAP is not set # CONFIG_ESP_NETIF_BRIDGE_EN is not set +# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set # end of ESP NETIF Adapter # @@ -1043,21 +1468,30 @@ CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y # # PHY # +CONFIG_ESP_PHY_ENABLED=y 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_CERT_TEST is not set 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 +CONFIG_ESP_PHY_PLL_TRACK_PERIOD_MS=1000 +# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set +# CONFIG_ESP_PHY_RECORD_USED_TIME is not set +CONFIG_ESP_PHY_IRAM_OPT=y +# CONFIG_ESP_PHY_DEBUG is not set # end of PHY # # Power Management # +# CONFIG_PM_SLEEP_FUNC_IN_IRAM is not set # CONFIG_PM_ENABLE is not set +# CONFIG_PM_SLP_IRAM_OPT is not set # end of Power Management # @@ -1067,10 +1501,21 @@ CONFIG_ESP_PHY_CALIBRATION_MODE=0 # end of ESP PSRAM # -# ESP Ringbuf +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP-ROM +# +CONFIG_ESP_ROM_PRINT_IN_IRAM=y +# end of ESP-ROM + +# +# ESP Security Specific # -# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set -# end of ESP Ringbuf +# end of ESP Security Specific # # ESP System Settings @@ -1099,6 +1544,7 @@ CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 # end of Trace memory +CONFIG_ESP_SYSTEM_IN_IRAM=y # 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 @@ -1123,49 +1569,37 @@ CONFIG_ESP_CONSOLE_UART_DEFAULT=y # CONFIG_ESP_CONSOLE_NONE is not set CONFIG_ESP_CONSOLE_UART=y CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 -# CONFIG_ESP_INT_WDT is not set +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y CONFIG_ESP_TASK_WDT_EN=y CONFIG_ESP_TASK_WDT_INIT=y # CONFIG_ESP_TASK_WDT_PANIC is not set CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 -# CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set -# CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 is not set +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_DEBUG_OCDAWARE=y CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5=y - -# -# Brownout Detector -# -CONFIG_ESP_BROWNOUT_DET=y -CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7 is not set -CONFIG_ESP_BROWNOUT_DET_LVL=0 -# end of Brownout Detector - # CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set -CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y # end of ESP System Settings # # IPC (Inter-Processor Call) # +CONFIG_ESP_IPC_ENABLE=y CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y CONFIG_ESP_IPC_ISR_ENABLE=y # end of IPC (Inter-Processor Call) # -# High resolution timer (esp_timer) +# ESP Timer (High Resolution Timer) # +CONFIG_ESP_TIMER_IN_IRAM=y # CONFIG_ESP_TIMER_PROFILING is not set CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y @@ -1174,11 +1608,10 @@ CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 # CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y -CONFIG_ESP_TIMER_ISR_AFFINITY=0x1 CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y # CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set CONFIG_ESP_TIMER_IMPL_TG0_LAC=y -# end of High resolution timer (esp_timer) +# end of ESP Timer (High Resolution Timer) # # Wi-Fi @@ -1206,14 +1639,19 @@ CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752 CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32 CONFIG_ESP_WIFI_IRAM_OPT=y # CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set -CONFIG_ESP_WIFI_RX_IRAM_OPT=y +# CONFIG_ESP_WIFI_RX_IRAM_OPT is not set CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y CONFIG_ESP_WIFI_ENABLE_SAE_PK=y +CONFIG_ESP_WIFI_ENABLE_SAE_H2E=y CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y # CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set +CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50 +# CONFIG_ESP_WIFI_BSS_MAX_IDLE_SUPPORT is not set +CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10 +CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15 CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=y -# CONFIG_ESP_WIFI_GMAC_SUPPORT is not set +CONFIG_ESP_WIFI_GMAC_SUPPORT=y CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y # CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 @@ -1232,11 +1670,12 @@ CONFIG_ESP_WIFI_MBEDTLS_TLS_CLIENT=y # # CONFIG_ESP_WIFI_WPS_STRICT is not set # CONFIG_ESP_WIFI_WPS_PASSPHRASE is not set +# CONFIG_ESP_WIFI_WPS_RECONNECT_ON_FAIL is not set # end of WPS Configuration Options # CONFIG_ESP_WIFI_DEBUG_PRINT is not set -# CONFIG_ESP_WIFI_TESTING_OPTIONS is not set CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=y +# CONFIG_ESP_WIFI_ENT_FREE_DYNAMIC_BUFFER is not set # end of Wi-Fi # @@ -1283,8 +1722,21 @@ CONFIG_FATFS_FS_LOCK=0 CONFIG_FATFS_TIMEOUT_MS=10000 CONFIG_FATFS_PER_FILE_CACHE=y # CONFIG_FATFS_USE_FASTSEEK is not set +CONFIG_FATFS_USE_STRFUNC_NONE=y +# CONFIG_FATFS_USE_STRFUNC_WITHOUT_CRLF_CONV is not set +# CONFIG_FATFS_USE_STRFUNC_WITH_CRLF_CONV is not set CONFIG_FATFS_VFS_FSTAT_BLKSIZE=0 # CONFIG_FATFS_IMMEDIATE_FSYNC is not set +# CONFIG_FATFS_USE_LABEL is not set +CONFIG_FATFS_LINK_LOCK=y +# CONFIG_FATFS_USE_DYN_BUFFERS is not set + +# +# File system free space calculation behavior +# +CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT=0 +CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0 +# end of File system free space calculation behavior # end of FAT Filesystem support # @@ -1306,14 +1758,21 @@ CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 # CONFIG_FREERTOS_USE_TICK_HOOK is not set CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 # CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set +CONFIG_FREERTOS_USE_TIMERS=y CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 # CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set # CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set # end of Kernel # @@ -1336,12 +1795,19 @@ CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set # end of Port +# +# Extra +# +# end of Extra + CONFIG_FREERTOS_PORT=y CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y CONFIG_FREERTOS_DEBUG_OCDAWARE=y CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=2 +CONFIG_FREERTOS_IN_IRAM=y # end of FreeRTOS # @@ -1370,7 +1836,14 @@ CONFIG_HEAP_TRACING_OFF=y # end of Heap memory debugging # -# Log output +# Log +# +CONFIG_LOG_VERSION_1=y +# CONFIG_LOG_VERSION_2 is not set +CONFIG_LOG_VERSION=1 + +# +# Log Level # # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set # CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set @@ -1383,18 +1856,44 @@ 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 + +# +# Level Settings +# # CONFIG_LOG_MASTER_LEVEL is not set -CONFIG_LOG_COLORS=y +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set -# end of Log output +# end of Format + +# +# Settings +# +CONFIG_LOG_MODE_TEXT_EN=y +CONFIG_LOG_MODE_TEXT=y +# end of Settings + +CONFIG_LOG_IN_IRAM=y +# end of Log # # LWIP # CONFIG_LWIP_ENABLE=y 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 @@ -1426,10 +1925,12 @@ 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_DOES_ACD_CHECK is not set +# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set # 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_OPTIONS_LEN=69 CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0 CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1 @@ -1440,6 +1941,7 @@ CONFIG_LWIP_DHCPS=y CONFIG_LWIP_DHCPS_LEASE_UNIT=60 CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y +CONFIG_LWIP_DHCPS_ADD_DNS=y # end of DHCP server # CONFIG_LWIP_AUTOIP is not set @@ -1467,6 +1969,7 @@ 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_ACCEPTMBOX_SIZE=6 CONFIG_LWIP_TCP_QUEUE_OOSEQ=y CONFIG_LWIP_TCP_OOSEQ_TIMEOUT=6 CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4 @@ -1497,9 +2000,13 @@ 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_IPV6_ND6_NUM_PREFIXES=5 +CONFIG_LWIP_IPV6_ND6_NUM_ROUTERS=3 +CONFIG_LWIP_IPV6_ND6_NUM_DESTINATIONS=10 +# CONFIG_LWIP_IPV6_ND6_ROUTE_INFO_OPTION_SUPPORT is not set +# CONFIG_LWIP_PPP_SUPPORT is not set # CONFIG_LWIP_SLIP_SUPPORT is not set # @@ -1522,8 +2029,20 @@ CONFIG_LWIP_MAX_RAW_PCBS=16 CONFIG_LWIP_SNTP_MAX_SERVERS=1 # CONFIG_LWIP_DHCP_GET_NTP_SRV is not set CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 +CONFIG_LWIP_SNTP_STARTUP_DELAY=y +CONFIG_LWIP_SNTP_MAXIMUM_STARTUP_DELAY=5000 # end of SNTP +# +# DNS +# +CONFIG_LWIP_DNS_MAX_HOST_IP=1 +CONFIG_LWIP_DNS_MAX_SERVERS=3 +# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set +# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set +# CONFIG_LWIP_USE_ESP_GETADDRINFO is not set +# end of DNS + CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7 CONFIG_LWIP_ESP_LWIP_ASSERT=y @@ -1542,11 +2061,16 @@ CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y # CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set # CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set +CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_NONE=y +# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_DEFAULT is not set +# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_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 -CONFIG_LWIP_HOOK_IP6_INPUT_NONE=y -# CONFIG_LWIP_HOOK_IP6_INPUT_DEFAULT is not set +CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_NONE=y +# CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_CUSTOM is not set +# CONFIG_LWIP_HOOK_IP6_INPUT_NONE is not set +CONFIG_LWIP_HOOK_IP6_INPUT_DEFAULT=y # CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM is not set # end of Hooks @@ -1570,11 +2094,12 @@ CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT=y # # mbedTLS v3.x related # +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set # CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set -CONFIG_MBEDTLS_ECDH_LEGACY_CONTEXT=y # CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set # CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set CONFIG_MBEDTLS_PKCS7_C=y # end of mbedTLS v3.x related @@ -1586,13 +2111,16 @@ CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y # CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set # CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 # end of Certificate Bundle -CONFIG_MBEDTLS_ECP_RESTARTABLE=y +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set CONFIG_MBEDTLS_CMAC_C=y CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y CONFIG_MBEDTLS_HARDWARE_MPI=y +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set CONFIG_MBEDTLS_HARDWARE_SHA=y CONFIG_MBEDTLS_ROM_MD5=y # CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set @@ -1601,7 +2129,9 @@ CONFIG_MBEDTLS_HAVE_TIME=y # CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA1_C=y CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y # CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set # CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set @@ -1655,6 +2185,8 @@ CONFIG_MBEDTLS_X509_CSR_PARSE_C=y # end of Certificates CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y # CONFIG_MBEDTLS_DHM_C is not set CONFIG_MBEDTLS_ECDH_C=y CONFIG_MBEDTLS_ECDSA_C=y @@ -1672,12 +2204,14 @@ 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_ECP_FIXED_POINT_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set # 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_ERROR_STRINGS=y +CONFIG_MBEDTLS_FS_IO=y +# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set # end of mbedTLS # @@ -1697,20 +2231,25 @@ CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y # end of ESP-MQTT Configurations # -# Newlib +# LibC # -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 -CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y -# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set -# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set -# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set -# end of Newlib +CONFIG_LIBC_NEWLIB=y +# CONFIG_LIBC_PICOLIBC is not set +CONFIG_LIBC_MISC_IN_IRAM=y +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y +CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y +# CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set +# CONFIG_LIBC_STDOUT_LINE_ENDING_CR is not set +# CONFIG_LIBC_STDIN_LINE_ENDING_CRLF is not set +# CONFIG_LIBC_STDIN_LINE_ENDING_LF is not set +CONFIG_LIBC_STDIN_LINE_ENDING_CR=y +# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set +CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set +CONFIG_LIBC_ASSERT_BUFFER_SIZE=200 +# end of LibC # # NVS @@ -1725,18 +2264,12 @@ CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y # CONFIG_OPENTHREAD_ENABLED is not set # -# Thread Operational Dataset +# OpenThread Spinel # -CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread-ESP" -CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX="fd00:db8:a0:0::/64" -CONFIG_OPENTHREAD_NETWORK_CHANNEL=15 -CONFIG_OPENTHREAD_NETWORK_PANID=0x1234 -CONFIG_OPENTHREAD_NETWORK_EXTPANID="dead00beef00cafe" -CONFIG_OPENTHREAD_NETWORK_MASTERKEY="00112233445566778899aabbccddeeff" -CONFIG_OPENTHREAD_NETWORK_PSKC="104810e2315100afd6bc9215a6bfac53" -# end of Thread Operational Dataset +# CONFIG_OPENTHREAD_SPINEL_ONLY is not set +# end of OpenThread Spinel -CONFIG_OPENTHREAD_XTAL_ACCURACY=130 +# CONFIG_OPENTHREAD_DEBUG is not set # end of OpenThread # @@ -1745,6 +2278,7 @@ CONFIG_OPENTHREAD_XTAL_ACCURACY=130 CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=y CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=y CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION=y # end of Protocomm # @@ -1786,6 +2320,10 @@ CONFIG_SPI_FLASH_BROWNOUT_RESET=y # # Features here require specific hardware (READ DOCS FIRST!) # +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set +CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y # end of Optional and Experimental Features (READ DOCS FIRST) # end of Main Flash configuration @@ -1811,11 +2349,11 @@ CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 # # Auto-detect flash chips # -CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORTED=y -CONFIG_SPI_FLASH_VENDOR_GD_SUPPORTED=y -CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORTED=y -CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORTED=y -CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORTED=y +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y @@ -1880,6 +2418,11 @@ CONFIG_WS_BUFFER_SIZE=1024 # Ultra Low Power (ULP) Co-processor # # CONFIG_ULP_COPROC_ENABLED is not set + +# +# ULP Debugging Options +# +# end of ULP Debugging Options # end of Ultra Low Power (ULP) Co-processor # @@ -1892,13 +2435,9 @@ CONFIG_UNITY_ENABLE_DOUBLE=y CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y # CONFIG_UNITY_ENABLE_FIXTURE is not set # CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set +# CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE is not set # end of Unity unit testing library -# -# Root Hub configuration -# -# end of Root Hub configuration - # # Virtual file system # @@ -1915,6 +2454,8 @@ CONFIG_VFS_MAX_COUNT=8 # CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 # end of Host File System I/O (Semihosting) + +CONFIG_VFS_INITIALIZE_DEV_NULL=y # end of Virtual file system # @@ -1933,16 +2474,61 @@ CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 # CONFIG_WIFI_PROV_BLE_BONDING is not set CONFIG_WIFI_PROV_BLE_SEC_CONN=y # CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION is not set +# CONFIG_WIFI_PROV_BLE_NOTIFY is not set # CONFIG_WIFI_PROV_KEEP_BLE_ON_AFTER_PROV is not set CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y # CONFIG_WIFI_PROV_STA_FAST_SCAN is not set # end of Wi-Fi Provisioning Manager +# +# GPIO Button +# +CONFIG_IO_GLITCH_FILTER_TIME_MS=50 +# end of GPIO Button + +# +# RainMaker User API Configuration +# +CONFIG_RM_USER_SUPPORT_REUSE_HTTP_SESSION=y +# CONFIG_ENABLE_RM_USER_HELPER_API is not set +# end of RainMaker User API Configuration + +# +# WS2812 RGB LED +# +# CONFIG_WS2812_LED_ENABLE is not set +# end of WS2812 RGB LED + +# +# IoT Button +# +CONFIG_BUTTON_PERIOD_TIME_MS=5 +CONFIG_BUTTON_DEBOUNCE_TICKS=2 +CONFIG_BUTTON_SHORT_PRESS_TIME_MS=180 +CONFIG_BUTTON_LONG_PRESS_TIME_MS=1500 +CONFIG_BUTTON_LONG_PRESS_HOLD_SERIAL_TIME_MS=20 +CONFIG_ADC_BUTTON_MAX_CHANNEL=3 +CONFIG_ADC_BUTTON_MAX_BUTTON_PER_CHANNEL=8 +CONFIG_ADC_BUTTON_SAMPLE_TIMES=1 +# end of IoT Button + +# +# CMake Utilities +# +# CONFIG_CU_RELINKER_ENABLE is not set +# CONFIG_CU_DIAGNOSTICS_COLOR_NEVER is not set +CONFIG_CU_DIAGNOSTICS_COLOR_ALWAYS=y +# CONFIG_CU_DIAGNOSTICS_COLOR_AUTO is not set +# CONFIG_CU_GCC_LTO_ENABLE is not set +# CONFIG_CU_GCC_STRING_1BYTE_ALIGN is not set +# end of CMake Utilities + # # Diagnostics data store # CONFIG_DIAG_DATA_STORE_RTC=y # CONFIG_DIAG_DATA_STORE_FLASH is not set +# CONFIG_DIAG_DATA_STORE_DBG_PRINTS is not set CONFIG_DIAG_DATA_STORE_REPORTING_WATERMARK_PERCENT=80 # @@ -1960,15 +2546,18 @@ CONFIG_DIAG_LOG_MSG_ARG_FORMAT_TLV=y # CONFIG_DIAG_LOG_MSG_ARG_FORMAT_STRING is not set CONFIG_DIAG_LOG_MSG_ARG_MAX_SIZE=64 CONFIG_DIAG_LOG_DROP_WIFI_LOGS=y +# CONFIG_DIAG_ENABLE_WRAP_LOG_FUNCTIONS is not set CONFIG_DIAG_ENABLE_METRICS=y CONFIG_DIAG_METRICS_MAX_COUNT=20 CONFIG_DIAG_ENABLE_HEAP_METRICS=y +CONFIG_DIAG_HEAP_POLLING_INTERVAL=30 CONFIG_DIAG_ENABLE_WIFI_METRICS=y +CONFIG_DIAG_WIFI_POLLING_INTERVAL=30 CONFIG_DIAG_ENABLE_VARIABLES=y CONFIG_DIAG_VARIABLES_MAX_COUNT=20 CONFIG_DIAG_ENABLE_NETWORK_VARIABLES=y # CONFIG_DIAG_MORE_NETWORK_VARS is not set -# CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP is not set +CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y # end of Diagnostics # @@ -1980,32 +2569,21 @@ CONFIG_ESP_INSIGHTS_TRANSPORT_HTTPS=y CONFIG_ESP_INSIGHTS_TRANSPORT_HTTPS_HOST="https://client.insights.espressif.com" CONFIG_ESP_INSIGHTS_CLOUD_POST_MIN_INTERVAL_SEC=60 CONFIG_ESP_INSIGHTS_CLOUD_POST_MAX_INTERVAL_SEC=240 +CONFIG_ESP_INSIGHTS_META_VERSION_10=y # end of ESP Insights # -# ESP RainMaker Common +# ESP Schedule Configuration # -CONFIG_ESP_RMAKER_LIB_ESP_MQTT=y -# CONFIG_ESP_RMAKER_LIB_AWS_IOT is not set -CONFIG_ESP_RMAKER_MQTT_GLUE_LIB=1 -CONFIG_ESP_RMAKER_MQTT_PORT_443=y -# CONFIG_ESP_RMAKER_MQTT_PORT_8883 is not set -CONFIG_ESP_RMAKER_MQTT_PORT=1 -# CONFIG_ESP_RMAKER_MQTT_PERSISTENT_SESSION is not set -CONFIG_ESP_RMAKER_MQTT_SEND_USERNAME=y -CONFIG_ESP_RMAKER_MQTT_PRODUCT_NAME="RMDev" -CONFIG_ESP_RMAKER_MQTT_PRODUCT_VERSION="1x0" -CONFIG_ESP_RMAKER_MQTT_PRODUCT_SKU="EX00" -CONFIG_ESP_RMAKER_MQTT_USE_CERT_BUNDLE=y -CONFIG_ESP_RMAKER_MAX_MQTT_SUBSCRIPTIONS=10 -CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=4096 -CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_PRIORITY=5 -CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME="fctry" -CONFIG_ESP_RMAKER_FACTORY_NAMESPACE="rmaker_creds" -CONFIG_ESP_RMAKER_DEF_TIMEZONE="Asia/Shanghai" -CONFIG_ESP_RMAKER_SNTP_SERVER_NAME="pool.ntp.org" -CONFIG_ESP_RMAKER_MAX_COMMANDS=10 -# end of ESP RainMaker Common +CONFIG_ESP_SCHEDULE_ENABLE_DAYLIGHT=y +# end of ESP Schedule Configuration + +# +# ESP Secure Cert Manager +# +# CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS is not set +# CONFIG_ESP_SECURE_CERT_WRITE_ENABLE_LOGGING is not set +# end of ESP Secure Cert Manager # # jsmn @@ -2015,18 +2593,6 @@ CONFIG_ESP_RMAKER_MAX_COMMANDS=10 # CONFIG_JSMN_STATIC is not set # end of jsmn -# -# GPIO Button -# -CONFIG_IO_GLITCH_FILTER_TIME_MS=50 -# end of GPIO Button - -# -# WS2812 RGB LED -# -# CONFIG_WS2812_LED_ENABLE is not set -# end of WS2812 RGB LED - # # mDNS # @@ -2039,11 +2605,21 @@ CONFIG_MDNS_TASK_STACK_SIZE=4096 CONFIG_MDNS_TASK_AFFINITY_CPU0=y # CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set CONFIG_MDNS_TASK_AFFINITY=0x0 + +# +# MDNS Memory Configuration +# +CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL=y +CONFIG_MDNS_MEMORY_ALLOC_INTERNAL=y +# CONFIG_MDNS_MEMORY_CUSTOM_IMPL is not set +# end of MDNS Memory Configuration + CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000 CONFIG_MDNS_TIMER_PERIOD_MS=100 # CONFIG_MDNS_NETWORKING_SOCKET is not set # CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES is not set # CONFIG_MDNS_ENABLE_DEBUG_PRINTS is not set +CONFIG_MDNS_ENABLE_CONSOLE_CLI=y # CONFIG_MDNS_RESPOND_REVERSE_QUERIES is not set CONFIG_MDNS_MULTIPLE_INSTANCE=y @@ -2057,10 +2633,48 @@ CONFIG_MDNS_PREDEF_NETIF_ETH=y # end of mDNS # -# ESP Secure Cert Manager +# Network Provisioning Manager # -# CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS is not set -# end of ESP Secure Cert Manager +CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI=y +CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES=16 +CONFIG_NETWORK_PROV_AUTOSTOP_TIMEOUT=30 +# CONFIG_NETWORK_PROV_BLE_BONDING is not set +CONFIG_NETWORK_PROV_BLE_SEC_CONN=y +# CONFIG_NETWORK_PROV_BLE_FORCE_ENCRYPTION is not set +# CONFIG_NETWORK_PROV_BLE_NOTIFY is not set +# CONFIG_NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV is not set +CONFIG_NETWORK_PROV_WIFI_STA_ALL_CHANNEL_SCAN=y +# CONFIG_NETWORK_PROV_WIFI_STA_FAST_SCAN is not set +# end of Network Provisioning Manager + +# +# ESP RainMaker Common +# +CONFIG_ESP_RMAKER_LIB_ESP_MQTT=y +# CONFIG_ESP_RMAKER_LIB_AWS_IOT is not set +CONFIG_ESP_RMAKER_MQTT_GLUE_LIB=1 +CONFIG_ESP_RMAKER_MQTT_PORT_443=y +# CONFIG_ESP_RMAKER_MQTT_PORT_8883 is not set +CONFIG_ESP_RMAKER_MQTT_PORT=1 +# CONFIG_ESP_RMAKER_MQTT_PERSISTENT_SESSION is not set +CONFIG_ESP_RMAKER_MQTT_SEND_USERNAME=y +CONFIG_ESP_RMAKER_MQTT_PRODUCT_NAME="RMDev" +CONFIG_ESP_RMAKER_MQTT_PRODUCT_VERSION="1x0" +CONFIG_ESP_RMAKER_MQTT_PRODUCT_SKU="EX00" +CONFIG_ESP_RMAKER_MQTT_USE_CERT_BUNDLE=y +CONFIG_ESP_RMAKER_MAX_MQTT_SUBSCRIPTIONS=10 +CONFIG_ESP_RMAKER_MQTT_KEEP_ALIVE_INTERVAL=120 +CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=4096 +CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_PRIORITY=5 +CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME="fctry" +CONFIG_ESP_RMAKER_FACTORY_NAMESPACE="rmaker_creds" +CONFIG_ESP_RMAKER_DEF_TIMEZONE="Asia/Shanghai" +CONFIG_ESP_RMAKER_SNTP_SERVER_NAME="pool.ntp.org" +CONFIG_ESP_RMAKER_MAX_COMMANDS=10 +CONFIG_ESP_RMAKER_CONSOLE_ENABLED=y +CONFIG_ESP_RMAKER_CONSOLE_TASK_STACK=4096 +CONFIG_ESP_RMAKER_CONSOLE_TASK_PRIORITY=5 +# end of ESP RainMaker Common # end of Component config # CONFIG_IDF_EXPERIMENTAL_FEATURES is not set @@ -2071,6 +2685,8 @@ CONFIG_MDNS_PREDEF_NETIF_ETH=y # CONFIG_ESP32_NO_BLOBS is not set # CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set # CONFIG_ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +CONFIG_APP_ROLLBACK_ENABLE=y +# CONFIG_APP_ANTI_ROLLBACK is not set # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set @@ -2078,8 +2694,6 @@ 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=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 @@ -2087,6 +2701,15 @@ CONFIG_FLASHMODE_DIO=y # CONFIG_FLASHMODE_DOUT is not set CONFIG_MONITOR_BAUD=115200 CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE=y +CONFIG_APP_WIFI_PROV_SHOW_QR=y +CONFIG_APP_WIFI_PROV_MAX_POP_MISMATCH=5 +# CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP is not set +CONFIG_APP_WIFI_PROV_TRANSPORT_BLE=y +CONFIG_APP_WIFI_PROV_TRANSPORT=2 +CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE=y +# CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT is not set +CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD=30 +CONFIG_APP_WIFI_PROV_NAME_PREFIX="PROV" CONFIG_OPTIMIZATION_LEVEL_DEBUG=y CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y @@ -2109,48 +2732,48 @@ CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y CONFIG_NIMBLE_ENABLED=y CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y # CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set -CONFIG_NIMBLE_MAX_CONNECTIONS=3 -CONFIG_NIMBLE_MAX_BONDS=3 -CONFIG_NIMBLE_MAX_CCCDS=8 -CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0 +CONFIG_NIMBLE_PINNED_TO_CORE=0 CONFIG_NIMBLE_PINNED_TO_CORE_0=y # CONFIG_NIMBLE_PINNED_TO_CORE_1 is not set -CONFIG_NIMBLE_PINNED_TO_CORE=0 CONFIG_NIMBLE_TASK_STACK_SIZE=4096 CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096 CONFIG_NIMBLE_ROLE_CENTRAL=y CONFIG_NIMBLE_ROLE_PERIPHERAL=y CONFIG_NIMBLE_ROLE_BROADCASTER=y CONFIG_NIMBLE_ROLE_OBSERVER=y -# CONFIG_NIMBLE_NVS_PERSIST is not set CONFIG_NIMBLE_SM_LEGACY=y CONFIG_NIMBLE_SM_SC=y # CONFIG_NIMBLE_SM_SC_DEBUG_KEYS is not set -# CONFIG_NIMBLE_DEBUG is not set -CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" -CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 +CONFIG_BT_NIMBLE_SM_SC_LVL=0 +# CONFIG_NIMBLE_NVS_PERSIST is not set +CONFIG_NIMBLE_MAX_BONDS=3 +CONFIG_NIMBLE_RPA_TIMEOUT=900 +CONFIG_NIMBLE_MAX_CONNECTIONS=3 +CONFIG_NIMBLE_MAX_CCCDS=8 +# CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS is not set +CONFIG_NIMBLE_HS_FLOW_CTRL=y +CONFIG_NIMBLE_HS_FLOW_CTRL_ITVL=1000 +CONFIG_NIMBLE_HS_FLOW_CTRL_THRESH=2 +CONFIG_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT=y CONFIG_NIMBLE_ATT_PREFERRED_MTU=256 -CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0 +CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0 CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=12 CONFIG_BT_NIMBLE_ACL_BUF_COUNT=24 CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255 CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30 CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8 -CONFIG_NIMBLE_HS_FLOW_CTRL=y -CONFIG_NIMBLE_HS_FLOW_CTRL_ITVL=1000 -CONFIG_NIMBLE_HS_FLOW_CTRL_THRESH=2 -CONFIG_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT=y -CONFIG_NIMBLE_RPA_TIMEOUT=900 +CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" +CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 +CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0 # CONFIG_NIMBLE_MESH is not set -CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y +# CONFIG_NIMBLE_DEBUG is not set CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y # CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY is not set # CONFIG_BTDM_CONTROLLER_MODE_BTDM is not set CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=3 CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=3 CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=0 -CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0 CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y # CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4 is not set @@ -2167,13 +2790,16 @@ CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_SUPPORTED=y CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_NUM=100 CONFIG_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 CONFIG_ADC2_DISABLE_DAC=y -# CONFIG_MCPWM_ISR_IN_IRAM is not set CONFIG_SW_COEXIST_ENABLE=y CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_MCPWM_ISR_IRAM_SAFE is not set # CONFIG_EVENT_LOOP_PROFILING is not set CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y +CONFIG_GDBSTUB_SUPPORT_TASKS=y +CONFIG_GDBSTUB_MAX_TASKS=32 # CONFIG_OTA_ALLOW_HTTP is not set # CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y @@ -2190,10 +2816,32 @@ CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y # CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set # CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y # CONFIG_ESP32_XTAL_FREQ_26 is not set CONFIG_ESP32_XTAL_FREQ_40=y # CONFIG_ESP32_XTAL_FREQ_AUTO is not set CONFIG_ESP32_XTAL_FREQ=40 +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_BROWNOUT_DET_LVL=0 +CONFIG_ESP32_BROWNOUT_DET_LVL=0 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y 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 @@ -2221,35 +2869,17 @@ CONFIG_CONSOLE_UART_DEFAULT=y CONFIG_CONSOLE_UART=y CONFIG_CONSOLE_UART_NUM=0 CONFIG_CONSOLE_UART_BAUDRATE=115200 -# CONFIG_INT_WDT is not set +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y CONFIG_TASK_WDT=y CONFIG_ESP_TASK_WDT=y # CONFIG_TASK_WDT_PANIC is not set CONFIG_TASK_WDT_TIMEOUT_S=5 -# CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set -# CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 is not set +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_ESP32_DEBUG_OCDAWARE=y -CONFIG_BROWNOUT_DET=y -CONFIG_ESP32_BROWNOUT_DET=y -CONFIG_BROWNOUT_DET_LVL_SEL_0=y -CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y -# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set -# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set -CONFIG_BROWNOUT_DET_LVL=0 -CONFIG_ESP32_BROWNOUT_DET_LVL=0 # CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set CONFIG_IPC_TASK_STACK_SIZE=1024 CONFIG_TIMER_TASK_STACK_SIZE=3584 @@ -2264,8 +2894,6 @@ CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y CONFIG_ESP32_WIFI_TX_BA_WIN=6 CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y -CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y -CONFIG_ESP32_WIFI_RX_BA_WIN=6 CONFIG_ESP32_WIFI_RX_BA_WIN=6 CONFIG_ESP32_WIFI_NVS_ENABLED=y CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y @@ -2273,7 +2901,7 @@ CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y 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_RX_IRAM_OPT is not set CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=y CONFIG_WPA_MBEDTLS_CRYPTO=y @@ -2286,7 +2914,6 @@ CONFIG_WPA_MBEDTLS_TLS_CLIENT=y # CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set # CONFIG_WPA_WPS_STRICT is not set # CONFIG_WPA_DEBUG_PRINT is not set -# CONFIG_WPA_TESTING_OPTIONS is not set # 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 @@ -2317,11 +2944,22 @@ CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y # CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF # CONFIG_PPP_SUPPORT is not set +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 +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set # CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set # CONFIG_ESP32_TIME_SYSCALL_USE_HRT is not set # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set # CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 From 41db44226442b57d9f6534c5bc6ede5165fb1d7f Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Sat, 18 Apr 2026 21:30:43 -0300 Subject: [PATCH 2/7] feat: boiler safety, temp diagnostics, and RainMaker Status Trust MAX6675 reads before heating; hold heater on SPI/open/range/stuck. Stuck-temp diag (>=20% duty, 3 frozen samples); LOOKUP overshoot learn discarded when stuck latches. RainMaker Status (Okay + worst latch) and temp line stays Ready/Low/High. LOOKUP trim scales full duty above 50%. Version 1.6.0, project espresso; defaults-only sdkconfig policy in PR. Signed-off-by: Raffael Rostagno --- CMakeLists.txt | 8 +- Makefile | 4 +- .../esp32-triac-dimmer-driver.c | 29 +- .../esp32-triac-dimmer-driver.h | 4 - main/app_main.c | 800 ++++++++++++++++-- main/app_priv.h | 5 + main/temp_pid.c | 3 +- memalloc.sh | 2 +- sdkconfig.defaults | 9 + 9 files changed, 757 insertions(+), 107 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ed858f..a420d0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,9 @@ else() set(RMAKER_PATH ${CMAKE_CURRENT_LIST_DIR}/../..) endif(DEFINED ENV{RMAKER_PATH}) -# Add RainMaker components and other common application components -set(EXTRA_COMPONENT_DIRS ${RMAKER_PATH}/components/esp-insights/components ${RMAKER_PATH}/components ${RMAKER_PATH}/examples/common ${CMAKE_CURRENT_LIST_DIR}/components) +# RainMaker components + example helpers (app_wifi, app_insights, …) +set(EXTRA_COMPONENT_DIRS ${RMAKER_PATH}/components ${RMAKER_PATH}/examples/common ${CMAKE_CURRENT_LIST_DIR}/components) -set(PROJECT_VER "1.0") +set(PROJECT_VER "1.9.0") include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(switch) +project(espresso) diff --git a/Makefile b/Makefile index a65c656..8d5ceba 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ # project subdirectory. # -PROJECT_NAME := switch -PROJECT_VER := 1.0 +PROJECT_NAME := espresso +PROJECT_VER := 1.9.0 # Add RainMaker components and other common application components EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../components $(PROJECT_PATH)/../common diff --git a/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.c b/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.c index 6b5826c..c404f81 100644 --- a/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.c +++ b/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.c @@ -1,6 +1,9 @@ #include "esp32-triac-dimmer-driver.h" +static void isr_ext(void *arg); +static bool onTimerISR(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx); + static const char *TAG = "Esp32idfDimmer"; int pulseWidth = 4; @@ -16,8 +19,6 @@ static int toggleReload = 25; volatile bool _initDone = false; volatile int _steps = 0; -static unsigned long long lastEdgeTime = 0; - static dimmertyp *dimmer[ALL_DIMMERS]; volatile bool firstSetup = false; volatile uint16_t dimPower[ALL_DIMMERS]; @@ -62,13 +63,13 @@ dimmertyp *createDimmer(gpio_num_t user_dimmer_pin, gpio_num_t zc_dimmer_pin) return dimmer[current_dim - 1]; } -#define TIMER_BASE_CLK 1 * 1000 * 1000, // 1MHz, 1 tick = 1us +#define TIMER_BASE_CLK (1000000u) /* 1 MHz, 1 tick = 1 us */ /** * @brief Configure the timer alarm */ -void config_alarm(gptimer_handle_t *timer, int ACfreq) -{ +void config_alarm(gptimer_handle_t timer, int ACfreq) +{ /*self regulation 50/60 Hz*/ double m_calculated_interval = (1 / (double)(ACfreq * 2)) / 100; ESP_LOGI(TAG, "Interval between wave calculated for frequency : %3dHz = %5f", ACfreq, m_calculated_interval); @@ -81,13 +82,13 @@ void config_alarm(gptimer_handle_t *timer, int ACfreq) .flags.auto_reload_on_alarm = true, // enable auto-reload }; ESP_LOGI(TAG, "Timer configuration - set alarm action"); - ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config)); + ESP_ERROR_CHECK(gptimer_set_alarm_action(timer, &alarm_config)); gptimer_event_callbacks_t cbs = { .on_alarm = onTimerISR, // register user callback }; ESP_LOGI(TAG, "Timer configuration - register event callbacks"); - ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL)); + ESP_ERROR_CHECK(gptimer_register_event_callbacks(timer, &cbs, NULL)); ESP_LOGI(TAG, "Timer configuration - configuration completed"); } @@ -155,6 +156,7 @@ void ext_int_init(dimmertyp *ptr) ESP_LOGI(TAG, "Triac command configuration"); gpio_set_direction(dimOutPin[ptr->current_num], GPIO_MODE_OUTPUT); + gpio_set_level(dimOutPin[ptr->current_num], 0); ESP_LOGI(TAG, "Triac command configuration - completed"); } @@ -324,14 +326,19 @@ static int k; #if DEBUG_ISR_TIMER == ISR_DEBUG_ON static int counter = 0; #endif -/* Execution on timer event */ -static void IRAM_ATTR onTimerISR(void *para) +/* Execution on timer event (GPTimer ISR contract: IDF 5.x+) */ +static bool IRAM_ATTR onTimerISR(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) { + (void)timer; + (void)edata; + (void)user_ctx; + BaseType_t hp_task_awoken = pdFALSE; + /**********************************/ #if DEBUG_ISR_TIMER == ISR_DEBUG_ON counter++; uint32_t info = (uint32_t)counter; - xQueueSendFromISR(timer_event_queue, &info, NULL); + xQueueSendFromISR(timer_event_queue, &info, &hp_task_awoken); #endif toggleCounter++; @@ -391,4 +398,6 @@ static void IRAM_ATTR onTimerISR(void *para) } if (toggleCounter >= toggleReload) toggleCounter = 1; + + return hp_task_awoken == pdTRUE; } diff --git a/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.h b/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.h index 76e4d44..f65defc 100644 --- a/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.h +++ b/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.h @@ -8,7 +8,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "driver/gpio.h" -#include "driver/periph_ctrl.h" #include "driver/gptimer.h" #include "freertos/task.h" #include "math.h" @@ -80,9 +79,6 @@ void port_init(dimmertyp *ptr); void config_timer(int freq); void ext_int_init(dimmertyp *ptr); -static void IRAM_ATTR isr_ext(void* arg); -static void IRAM_ATTR onTimerISR(void* arg); - extern unsigned long long getAbsTime1us(void); #endif diff --git a/main/app_main.c b/main/app_main.c index 8a822c9..896ef69 100644 --- a/main/app_main.c +++ b/main/app_main.c @@ -8,13 +8,16 @@ improved espresso extraction and consistency. https://github.com/raffarost/espresso - March 2024 + March 2024 - April 2026 Raffael Rostagno raffael.rostagno@gmail.com */ +#include +#include #include +#include #include #include "esp_err.h" #include "soc/soc_caps.h" @@ -63,7 +66,8 @@ #define GPIO_OUTPUT_IO_0 GPIO_NUM_4 #define GPIO_OUTPUT_IO_1 0 -#define GPIO_OUTPUT_PIN_SEL (1ULL << GPIO_OUTPUT_IO_0) +/* Pump + triac gate: outputs defined early so pads are not floating through Wi-Fi / RainMaker init. */ +#define GPIO_OUTPUT_PIN_SEL ((1ULL << GPIO_OUTPUT_IO_0) | (1ULL << TRIAC_1_GPIO)) static const gpio_config_t gpio_outcfg = { .intr_type = GPIO_INTR_DISABLE, @@ -92,6 +96,12 @@ if (ret != ESP_OK) \ #define CONTROL_TYPE LOOKUP +/* Overshoot learn/trim for LOOKUP only; on by default. Off: -DOVERSHOOT_DETECT_ENABLE=0 or #define 0 above. */ +#if (CONTROL_TYPE == LOOKUP) && !defined(OVERSHOOT_DETECT_ENABLE) +#define OVERSHOOT_DETECT_ENABLE 1 +#endif + + #define BKP_NUM 10 static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 100}; /* temperature delta */ @@ -104,20 +114,52 @@ static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 80, 100, 100} #define BREW_PREINF_OFF 2 #define BREW_ON 3 #define BREW_POWER_OFF 4 +#define BREW_FLUSH 5 + +#define FLUSH_PUMP_SEC 5 /* group-head flush: pump on duration */ +/* Pump-phase full-heat windows (us); preInfOnTime may change at runtime (RainMaker). */ +#define TIME_TEMP_BUFF_US ((unsigned long long)SEC_TO_US((float)preInfOnTime / 2.f)) +#define TIME_FLUSH_HEAT_BUFF_US ((unsigned long long)SEC_TO_US((float)FLUSH_PUMP_SEC / 2.f)) #define TEMP_DELTA 2 /* temp delta from setpoint for good brewing temperature */ +#define TEMP_SETPOINT_MIN 88 +#define TEMP_SETPOINT_MAX 96 #define MAX_TEMP_THR 110 /* maximum temperature threshold for safety control */ +#define TEMP_READ_OK_STREAK 3 /* consecutive good samples required before trusting */ +#define TEMP_VALID_MIN_C (-2.0f) +#define TEMP_VALID_MAX_C (125.0f) +#define TEMP_DIAG_SPI (1u << 0) +#define TEMP_DIAG_OPEN_TC (1u << 1) +#define TEMP_DIAG_RANGE (1u << 2) + +/* Stuck sensor: high heater demand but temp unchanged for STUCK_DIAG_SAMPLES consecutive reads. + * Warmup grace delays counting until the sensor has proven responsive or the grace period elapses + * (the heater is physically far from the sensor, so a cold ramp reads flat for several seconds). */ +#define STUCK_DIAG_MIN_POWER_PCT 10 +#define STUCK_DIAG_SAMPLES 5 /* 2.5s @ 500ms cadence */ +#define STUCK_DIAG_TEMP_EPS 0.21f /* < half MAX6675 LSB (0.25°C) */ +#define STUCK_DIAG_CLEAR_MAX_C 110.f +#define STUCK_DIAG_WARMUP_GRACE_SEC 10.f + /* divisor factors for power reduction around temperature target */ #define POWER_50 2 #define POWER_33 3 #define POWER_25 4 #define POWER_FACTOR POWER_33 -#define TEMP_PWR_TOGGLE 10 /* temperature range around setpoint in which power is reduced to - a factor (above) of calibrated values for better stability */ +#define TEMP_PWR_TOGGLE 10 /* °C window around setpoint where power is dithered */ + +#define POWERON_MIN (15 * SEC_TO_US(60)) /* standby timeout (15 min) */ + +#if OVERSHOOT_DETECT_ENABLE +#define OVERSHOOT_SOFT_DISARM_SEC 90.f +#define OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD 20.0f /* apply trim when control (%) is above this */ +#define OVERSHOOT_TRIM_FRAC_PER_DEG 0.02f /* trim fraction added per °C of measured excursion */ +#define OVERSHOOT_TRIM_FRAC_MAX 0.15f /* max cumulative trim fraction (0.15 = 15% cut) */ +#define OVERSHOOT_COLD_START_MAX_C 60.0f /* arm learn only if boiler at/below this temp (°C) */ +#endif -#define POWERON_MIN (15 * SEC_TO_US(60)) /* 15 minutes before entering standby (power off) mode */ /***************************************************************************** * Module variables @@ -126,14 +168,21 @@ static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 80, 100, 100} static const char *TAG = "espresso"; esp_rmaker_device_t *espresso_device; -/* these are used to report events/data to UI */ esp_rmaker_param_t *primary; -esp_rmaker_param_t *tempOk_param; +static esp_rmaker_param_t *status_param; + +static char s_temp_line_str[40]; +static char s_status_str[96]; + +/* Status latch: worst-severity issue shown until ~60s of clean Okay reports */ +#define STATUS_PRI_ZERO 25 +#define STATUS_PRI_UNTRUSTED 50 +#define STATUS_PRI_TOO_HIGH 75 +#define STATUS_PRI_STUCK 100 +#define STATUS_WORST_CLEAR_OK_REPORTS 12 /* Task5000ms ticks (~60s) */ -/* Heating element control */ dimmertyp *ptr_dimmer; -/* Task control */ static int count100ms = 0; static int count500ms = 0; @@ -147,7 +196,7 @@ spi_transaction_t tM = { }; static float tempCelsius; -static int tempSetpoint = 96; +static int32_t tempSetpoint = 96; static float pidOut; static float delta; static int control; @@ -155,14 +204,45 @@ static int powerToggle = 0; static unsigned long long pumpTimer = 0; static unsigned long long powerOnTimer = 0; static bool brewSignal = false; -static int brewState = BREW_OFF; -static int brewTime = 6; +static bool flushSignal = false; +static int brewState = BREW_POWER_OFF; +static int32_t brewTime = 6; static bool tempRangeOk = false; static bool tempLock = true; -static bool powerOn = true; +static bool powerOn = false; static bool preInfusion = true; -static int preInfOnTime = 3; -static int preInfOffTime = 10; +static int32_t preInfOnTime = 3; +static int32_t preInfOffTime = 10; + +static bool temp_read_trusted; +static uint8_t temp_read_good_streak; +static uint32_t temp_read_fault_latch; + +static int s_status_worst_pri; +static char s_status_worst_str[48]; +static uint16_t s_status_ok_clean_reports; + +static bool temp_stuck_diag; +static float temp_stuck_prev_c; +static uint8_t temp_stuck_same_ct; +static bool temp_sensor_responsive; /* latched once the sensor moves under heat demand */ +static unsigned long long temp_stuck_warmup_start_us; /* t0 of the cold-start warmup grace window */ + +#if OVERSHOOT_DETECT_ENABLE +static esp_rmaker_param_t *overshoot_disp_param; +static char s_overshoot_disp_str[28]; +static float overshoot_trim_stored; /* NVS-persisted power cut fraction (0..TRIM_FRAC_MAX) */ +static float overshoot_excursion_pk; /* peak (temp - setpoint) °C of the active excursion */ +static bool overshoot_excursion_latched; /* clears at temp <= setpoint; triggers commit once */ +static bool overshoot_detected; /* sticky: crossed setpoint+TEMP_DELTA this arm cycle */ +static bool overshoot_learn_allowed = false; +static bool overshoot_learn_ever_armed; /* cold arm succeeded at least once this power session */ +#define OS_DISARM_SOFT_TIMEOUT (1u << 0) +#define OS_DISARM_COMMIT (1u << 1) +static uint32_t overshoot_learn_disarm_mask; +static bool overshoot_boot_temp_sampled; +static unsigned long long overshoot_soft_timer_start_us; +#endif static esp_rmaker_param_t *poweron_param; @@ -179,6 +259,20 @@ void heatingControl(void); void brewProgram(void); void nvsRead(void); void nvsWrite(void); +static void temp_stuck_diag_update(int heating_demand_pct); +static bool boiler_heater_holdoff(void); +static void temp_status_line_report(void); +static void boiler_status_report(void); +#if OVERSHOOT_DETECT_ENABLE +static void overshoot_learn_try_arm_cold(const char *site); +static void overshoot_peak_detector_update(void); +static void overshoot_disp_format_str(void); +static void overshoot_disp_report(void); +static void nvs_read_overshoot_trim(nvs_handle_t h); +static void nvs_persist_overshoot_trim(void); +static void overshoot_apply_trim_to_control(int *p_control); +static void overshoot_learn_set_disallowed(uint32_t reason_bits); +#endif /***************************************************************************** * Function declaration @@ -193,13 +287,19 @@ static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_pa } /* Save to local variable */ - if (strcmp(esp_rmaker_param_get_name(param), "Temperature Setpoint") == 0) + if (strcmp(esp_rmaker_param_get_name(param), "Temperature setpoint (\xc2\xb0""C)") == 0) { - tempSetpoint = val.val.i; + int32_t sp = val.val.i; + if (sp < TEMP_SETPOINT_MIN) { + sp = TEMP_SETPOINT_MIN; + } else if (sp > TEMP_SETPOINT_MAX) { + sp = TEMP_SETPOINT_MAX; + } + tempSetpoint = sp; ESP_LOGI(TAG, "New temperature setpoint: %d", tempSetpoint); } - if (strcmp(esp_rmaker_param_get_name(param), "Brew Time") == 0) + if (strcmp(esp_rmaker_param_get_name(param), "Brew time (s)") == 0) { brewTime = val.val.i; ESP_LOGI(TAG, "Brew Time: %d s", brewTime); @@ -211,6 +311,12 @@ static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_pa ESP_LOGI(TAG, "Brew %s!", brewSignal ? "start" : "stop"); } + if (strcmp(esp_rmaker_param_get_name(param), "Flush") == 0) + { + flushSignal = val.val.b ? !flushSignal : flushSignal; + ESP_LOGI(TAG, "Flush %s!", flushSignal ? "on" : "off"); + } + if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion") == 0) { preInfusion = val.val.b; @@ -219,8 +325,17 @@ static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_pa if (strcmp(esp_rmaker_param_get_name(param), "Power") == 0) { + const bool was_on = powerOn; powerOn = val.val.b; ESP_LOGI(TAG, "Power is set to %s", powerOn ? "ON" : "OFF"); +#if OVERSHOOT_DETECT_ENABLE + if (!powerOn && was_on) { + overshoot_learn_ever_armed = false; + } + if (powerOn && !was_on) { + overshoot_learn_try_arm_cold("Power cb"); + } +#endif } if (strcmp(esp_rmaker_param_get_name(param), "Temp Lock") == 0) @@ -229,18 +344,37 @@ static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_pa ESP_LOGI(TAG, "Temp Lock set to %s", tempLock ? "ON" : "OFF"); } - if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion On Time") == 0) + if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion on time (s)") == 0) { preInfOnTime = val.val.i; ESP_LOGI(TAG, "Pre-Infusion On Time: %d s", preInfOnTime); } - if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion Off Time") == 0) + if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion off time (s)") == 0) { preInfOffTime = val.val.i; ESP_LOGI(TAG, "Pre-Infusion Off Time: %d s", preInfOffTime); } +#if OVERSHOOT_DETECT_ENABLE + if (strcmp(esp_rmaker_param_get_name(param), "Reset power trim") == 0) + { + if (val.val.b) { + overshoot_trim_stored = 0.f; + overshoot_learn_allowed = true; + overshoot_learn_ever_armed = false; + overshoot_learn_disarm_mask = 0u; + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + overshoot_excursion_latched = false; + overshoot_excursion_pk = 0.f; + nvs_persist_overshoot_trim(); + ESP_LOGI(TAG, "Warmup trim reset (NVS cleared, learn re-armed if cold)"); + overshoot_disp_report(); + } + } +#endif + return ESP_OK; } @@ -352,8 +486,10 @@ void dimInit(void) void gpioConfig(void) { - /* configure GPIO output pins */ - gpio_config(&gpio_outcfg); + /* Outputs: pump off, triac gate held low (dimmer driver re-configures GPIO33 later). */ + gpio_config(&gpio_outcfg); + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + gpio_set_level(TRIAC_1_GPIO, 0); } void nvsRead(void) @@ -369,14 +505,26 @@ void nvsRead(void) } else { - /* Read */ - nvs_get_u8(nvs_handle, "tempLock", &tempLock); + /* Read (NVS APIs use fixed-width types; not bool* / int*) */ + uint8_t u8 = 0; + nvs_get_u8(nvs_handle, "tempLock", &u8); + tempLock = u8 != 0; nvs_get_i32(nvs_handle, "tempSetpoint", &tempSetpoint); + if (tempSetpoint < TEMP_SETPOINT_MIN) { + tempSetpoint = TEMP_SETPOINT_MIN; + } else if (tempSetpoint > TEMP_SETPOINT_MAX) { + tempSetpoint = TEMP_SETPOINT_MAX; + } nvs_get_i32(nvs_handle, "brewTime", &brewTime); - nvs_get_u8(nvs_handle, "preInfusion", &preInfusion); + nvs_get_u8(nvs_handle, "preInfusion", &u8); + preInfusion = u8 != 0; nvs_get_i32(nvs_handle, "preInfOnTime", &preInfOnTime); nvs_get_i32(nvs_handle, "preInfOffTime", &preInfOffTime); +#if OVERSHOOT_DETECT_ENABLE + nvs_read_overshoot_trim(nvs_handle); +#endif + nvs_close(nvs_handle); } } @@ -395,13 +543,15 @@ void nvsWrite(void) else { /* Write */ - ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "tempLock", tempLock)); + ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "tempLock", (uint8_t)tempLock)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "tempSetpoint", tempSetpoint)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "brewTime", brewTime)); - ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "preInfusion", preInfusion)); + ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "preInfusion", (uint8_t)preInfusion)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOnTime", preInfOnTime)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOffTime", preInfOffTime)); + /* trimMp is written only from nvs_persist_overshoot_trim() on learn commit. */ + ESP_LOGI(TAG, "Committing updates in NVS ... "); ESP_ERROR_CHECK(nvs_commit(nvs_handle)); @@ -410,6 +560,347 @@ void nvsWrite(void) } } +#if OVERSHOOT_DETECT_ENABLE +/* Trim stored as milli-percent in NVS (int32); 100 = 0.1% */ +#define OVERSHOOT_TRIM_FRAC_TO_MPCT(f) ((int32_t)lroundf((f) * 100000.0f)) +#define OVERSHOOT_TRIM_MPCT_TO_FRAC(i) ((float)(i) * 0.00001f) + +static void nvs_read_overshoot_trim(nvs_handle_t h) +{ + int32_t mpct = 0; + if (nvs_get_i32(h, "trimMp", &mpct) == ESP_OK && mpct >= 0) { + overshoot_trim_stored = fminf(OVERSHOOT_TRIM_MPCT_TO_FRAC(mpct), OVERSHOOT_TRIM_FRAC_MAX); + ESP_LOGI(TAG, "Warmup trim loaded: -%.2f%%", (double)(overshoot_trim_stored * 100.0f)); + return; + } + + /* Legacy: "pkOsm" was cumulative °C; migrate once to "trimMp" then erase. */ + int32_t milli_c = 0; + if (nvs_get_i32(h, "pkOsm", &milli_c) == ESP_OK && milli_c > 0) { + const float cumulative_c = (float)milli_c * 0.001f; + overshoot_trim_stored = fminf(OVERSHOOT_TRIM_FRAC_PER_DEG * cumulative_c, + OVERSHOOT_TRIM_FRAC_MAX); + esp_err_t e = nvs_set_i32(h, "trimMp", + OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); + if (e == ESP_OK) { + nvs_erase_key(h, "pkOsm"); + nvs_commit(h); + } + ESP_LOGI(TAG, "Warmup trim migrated: pkOsm=%.2f°C -> -%.2f%%", + (double)cumulative_c, (double)(overshoot_trim_stored * 100.0f)); + return; + } + + overshoot_trim_stored = 0.f; +} + +static void nvs_persist_overshoot_trim(void) +{ + nvs_handle_t h; + if (nvs_open("storage", NVS_READWRITE, &h) != ESP_OK) { + return; + } + esp_err_t e = nvs_set_i32(h, "trimMp", OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); + if (e == ESP_OK) { + e = nvs_commit(h); + } + if (e != ESP_OK) { + ESP_LOGW(TAG, "trimMp NVS save failed: %s", esp_err_to_name(e)); + } + nvs_close(h); +} + +static void overshoot_apply_trim_to_control(int *p_control) +{ + if ((float)*p_control > (float)OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD) { + float cf = (float)*p_control * (1.f - overshoot_trim_stored); + *p_control = (int)(cf + 0.5f); + } +} + +static void overshoot_learn_set_disallowed(uint32_t reason_bits) +{ + overshoot_learn_disarm_mask |= reason_bits; + overshoot_learn_allowed = false; +} + +static void overshoot_learn_try_arm_cold(const char *site) +{ + if (!powerOn) { + return; + } + if (!temp_read_trusted) { + return; + } + if (tempCelsius > OVERSHOOT_COLD_START_MAX_C) { + return; + } + overshoot_learn_allowed = true; + overshoot_learn_ever_armed = true; + overshoot_learn_disarm_mask = 0u; + overshoot_excursion_latched = false; + overshoot_excursion_pk = 0.f; + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + ESP_LOGI(TAG, "OS arm[%s] OK: cold start @ %.1f°C (trim pre-loaded -%.2f%%)", + site, (double)tempCelsius, (double)(overshoot_trim_stored * 100.0f)); +} + + +static void overshoot_peak_detector_update(void) +{ + if (!overshoot_learn_allowed) { + return; + } + if (temp_stuck_diag) { + return; + } + + const float temp_ok_max_threshold = (float)tempSetpoint + (float)TEMP_DELTA; + + if (tempCelsius > temp_ok_max_threshold) { + overshoot_detected = true; + overshoot_excursion_latched = true; + } + + if (!overshoot_detected) { + if (overshoot_soft_timer_start_us == 0ULL && tempCelsius >= (float)tempSetpoint) { + overshoot_soft_timer_start_us = getAbsTime1us(); + } else if (overshoot_soft_timer_start_us != 0ULL && + (getAbsTime1us() - overshoot_soft_timer_start_us) >= + (unsigned long long)SEC_TO_US(OVERSHOOT_SOFT_DISARM_SEC)) { + overshoot_learn_set_disallowed(OS_DISARM_SOFT_TIMEOUT); + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + overshoot_excursion_latched = false; + overshoot_excursion_pk = 0.f; + ESP_LOGI(TAG, + "OS learn disarmed (soft): no temp above setpoint+%d°C within %.0fs", + TEMP_DELTA, (double)OVERSHOOT_SOFT_DISARM_SEC); + overshoot_disp_report(); + return; + } + } + + if (overshoot_excursion_latched && tempCelsius > (float)tempSetpoint) { + const float temp_above_setpoint_c = tempCelsius - (float)tempSetpoint; + overshoot_excursion_pk = fmaxf(overshoot_excursion_pk, temp_above_setpoint_c); + } + + if (tempCelsius <= (float)tempSetpoint) { + if (overshoot_excursion_latched) { + if (overshoot_excursion_pk > 0.01f) { + const float peak_c = overshoot_excursion_pk; + const float trim_add = OVERSHOOT_TRIM_FRAC_PER_DEG * peak_c; + const float trim_prev = overshoot_trim_stored; + overshoot_trim_stored = fminf(overshoot_trim_stored + trim_add, + OVERSHOOT_TRIM_FRAC_MAX); + ESP_LOGI(TAG, + "OS commit: peak +%.2f°C -> cut +%.2f%% (-%.2f%% -> -%.2f%%%s)", + (double)peak_c, + (double)(trim_add * 100.0f), + (double)(trim_prev * 100.0f), + (double)(overshoot_trim_stored * 100.0f), + (overshoot_trim_stored >= OVERSHOOT_TRIM_FRAC_MAX - 1e-6f) ? ", CAPPED" : ""); + nvs_persist_overshoot_trim(); + overshoot_learn_set_disallowed(OS_DISARM_COMMIT); + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + } + overshoot_excursion_pk = 0.f; + } + overshoot_excursion_latched = false; + } +} + +static void overshoot_disp_format_str(void) +{ + const float cut_pct = overshoot_trim_stored * 100.0f; + if (overshoot_learn_allowed && overshoot_excursion_latched && overshoot_excursion_pk > 0.01f) { + snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), + "-%.1f%% +%.1f°C", (double)cut_pct, (double)overshoot_excursion_pk); + } else { + snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), "-%.1f%%", (double)cut_pct); + } +} + +static void overshoot_disp_report(void) +{ + if (!powerOn) { + return; + } + overshoot_disp_format_str(); + if (overshoot_disp_param) { + esp_err_t e = esp_rmaker_param_update_and_report(overshoot_disp_param, + esp_rmaker_str(s_overshoot_disp_str)); + if (e != ESP_OK) { + ESP_LOGW(TAG, "Overshoot display: %s", esp_err_to_name(e)); + } + } +} + +#endif + +static void temp_stuck_diag_update(int heating_demand_pct) +{ + const float t = tempCelsius; + + /* Reset session state on power off; stuck latch clears only when the reading moves. */ + if (!powerOn) { + temp_stuck_same_ct = 0u; + temp_stuck_warmup_start_us = 0ULL; + temp_sensor_responsive = false; + temp_stuck_prev_c = t; + return; + } + + if (temp_stuck_diag) { + if (temp_read_trusted && t > 0.f && t < STUCK_DIAG_CLEAR_MAX_C && + fabsf(t - temp_stuck_prev_c) >= STUCK_DIAG_TEMP_EPS) { + temp_stuck_diag = false; + temp_stuck_same_ct = 1u; + temp_stuck_prev_c = t; + temp_sensor_responsive = true; + ESP_LOGI(TAG, "Boiler temp stuck diag cleared (reading moved in range)"); + } + return; + } + + if (!temp_read_trusted || t <= 0.f || t >= STUCK_DIAG_CLEAR_MAX_C) { + temp_stuck_same_ct = 0u; + temp_stuck_prev_c = t; + return; + } + + if (heating_demand_pct < STUCK_DIAG_MIN_POWER_PCT) { + temp_stuck_same_ct = 0u; + temp_stuck_prev_c = t; + if (!temp_sensor_responsive) { + temp_stuck_warmup_start_us = 0ULL; + } + return; + } + + /* First heating sample pre-responsive: anchor and start warmup timer. */ + if (!temp_sensor_responsive && temp_stuck_warmup_start_us == 0ULL) { + temp_stuck_warmup_start_us = getAbsTime1us(); + temp_stuck_prev_c = t; + temp_stuck_same_ct = 0u; + return; + } + + if (fabsf(t - temp_stuck_prev_c) >= STUCK_DIAG_TEMP_EPS) { + if (!temp_sensor_responsive) { + ESP_LOGI(TAG, "Boiler temp sensor responsive (%.2f -> %.2f C)", + (double)temp_stuck_prev_c, (double)t); + } + temp_sensor_responsive = true; + temp_stuck_same_ct = 1u; + temp_stuck_prev_c = t; + return; + } + + /* Pre-responsive: skip counting until warmup grace elapses. */ + if (!temp_sensor_responsive) { + const unsigned long long warmup_elapsed = getAbsTime1us() - temp_stuck_warmup_start_us; + if (warmup_elapsed < (unsigned long long)SEC_TO_US(STUCK_DIAG_WARMUP_GRACE_SEC)) { + return; + } + } + + if (temp_stuck_same_ct < 255) { + temp_stuck_same_ct++; + } + if (temp_stuck_same_ct >= STUCK_DIAG_SAMPLES) { + temp_stuck_diag = true; + ESP_LOGW(TAG, + "Boiler temp stuck diag: demand >= %d%%, %d identical samples (~%.2f C, %s)", + STUCK_DIAG_MIN_POWER_PCT, STUCK_DIAG_SAMPLES, (double)t, + temp_sensor_responsive ? "post-movement" : "warmup grace elapsed"); + } +} + +static bool boiler_heater_holdoff(void) +{ + return !temp_read_trusted || (tempCelsius == 0.f) || (tempCelsius > (float)MAX_TEMP_THR) || + temp_stuck_diag; +} + +static void temp_status_line_report(void) +{ + const char *state; + if (tempRangeOk) { + state = "Ready"; + } else if (tempCelsius < (float)(tempSetpoint - TEMP_DELTA)) { + state = "Low"; + } else { + state = "High"; + } + snprintf(s_temp_line_str, sizeof(s_temp_line_str), "%.1f \xc2\xb7 %s", (double)tempCelsius, state); + if (primary) { + esp_err_t e = esp_rmaker_param_update_and_report(primary, esp_rmaker_str(s_temp_line_str)); + if (e != ESP_OK) { + ESP_LOGW(TAG, "Temperature line: %s", esp_err_to_name(e)); + } + } +} + +static void boiler_status_report(void) +{ + int cur_pri = 0; + const char *cur_msg = "Okay"; + + if (temp_stuck_diag) { + cur_pri = STATUS_PRI_STUCK; + cur_msg = "Temp sensor stuck"; + } else if (!temp_read_trusted) { + cur_pri = STATUS_PRI_UNTRUSTED; + cur_msg = "Temp not trusted"; + } else if (tempCelsius <= 0.f) { + cur_pri = STATUS_PRI_ZERO; + cur_msg = "Temp zero"; + } else if (tempCelsius > (float)MAX_TEMP_THR) { + cur_pri = STATUS_PRI_TOO_HIGH; + cur_msg = "Temp too high"; + } + + if (cur_pri > s_status_worst_pri) { + s_status_worst_pri = cur_pri; + snprintf(s_status_worst_str, sizeof(s_status_worst_str), "%s", cur_msg); + } + + if (cur_pri == 0) { + if (s_status_worst_pri > 0) { + if (s_status_ok_clean_reports + 1u >= (uint16_t)STATUS_WORST_CLEAR_OK_REPORTS) { + s_status_worst_pri = 0; + s_status_worst_str[0] = '\0'; + s_status_ok_clean_reports = 0; + snprintf(s_status_str, sizeof(s_status_str), "Okay"); + } else { + s_status_ok_clean_reports++; + snprintf(s_status_str, sizeof(s_status_str), "Okay (%s)", s_status_worst_str); + } + } else { + snprintf(s_status_str, sizeof(s_status_str), "Okay"); + s_status_ok_clean_reports = 0; + } + } else { + s_status_ok_clean_reports = 0; + if (s_status_worst_pri > cur_pri && s_status_worst_str[0] != '\0') { + snprintf(s_status_str, sizeof(s_status_str), "%s (%s)", cur_msg, s_status_worst_str); + } else { + snprintf(s_status_str, sizeof(s_status_str), "%s", cur_msg); + } + } + + if (status_param) { + esp_err_t e = esp_rmaker_param_update_and_report(status_param, esp_rmaker_str(s_status_str)); + if (e != ESP_OK) { + ESP_LOGW(TAG, "Status: %s", esp_err_to_name(e)); + } + } +} + unsigned long long getAbsTime1us(void) { unsigned long long timerVal = 0; @@ -419,30 +910,82 @@ unsigned long long getAbsTime1us(void) return timerVal; } -void spiComm(void) +static void temp_read_note_fault(uint32_t bit) { - esp_err_t ret; + temp_read_good_streak = 0; + temp_read_trusted = false; + const uint32_t prev = temp_read_fault_latch; + temp_read_fault_latch |= bit; + if (temp_read_fault_latch != prev) { + ESP_LOGW(TAG, "Boiler temp read fault (latch 0x%02" PRIx32 ")", temp_read_fault_latch); + } +} - ret = spi_device_acquire_bus(spi, portMAX_DELAY); - CHECK_RET(ret, "Error opening SPI"); +static void temp_read_note_good(float celsius) +{ + tempCelsius = celsius; + if (temp_read_good_streak < 255) { + temp_read_good_streak++; + } + if (temp_read_good_streak >= TEMP_READ_OK_STREAK) { + if (!temp_read_trusted) { + ESP_LOGI(TAG, "Boiler temp read now trusted"); + } + temp_read_trusted = true; + temp_read_fault_latch = 0; + } +} + +void spiComm(void) +{ + esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "SPI acquire bus failed: %s", esp_err_to_name(ret)); + temp_read_note_fault(TEMP_DIAG_SPI); + return; + } ret = spi_device_transmit(spi, &tM); - CHECK_RET(ret, "Error TX SPI"); spi_device_release_bus(spi); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "SPI transmit failed: %s", esp_err_to_name(ret)); + temp_read_note_fault(TEMP_DIAG_SPI); + return; + } - int16_t res = (int16_t) SPI_SWAP_DATA_RX(data, 16); + const int16_t res = (int16_t) SPI_SWAP_DATA_RX(data, 16); if (res & (1 << 2)) { - res = 0; - ESP_LOGE(TAG, "Sensor is not connected"); + ESP_LOGE(TAG, "Thermocouple open (MAX6675 fault bit)"); + temp_read_note_fault(TEMP_DIAG_OPEN_TC); + return; } - else { - res >>= 3; - tempCelsius = (float)(res * 0.25); + + const int16_t shifted = (int16_t)(res >> 3); + const float c = (float)shifted * 0.25f; + if (c < TEMP_VALID_MIN_C || c > TEMP_VALID_MAX_C) { + ESP_LOGW(TAG, "Boiler temp out of range: %.2f C", (double)c); + temp_read_note_fault(TEMP_DIAG_RANGE); + return; } + + temp_read_note_good(c); +} + +bool boiler_temp_is_trusted(void) +{ + return temp_read_trusted; +} + +uint32_t boiler_temp_fault_bits(void) +{ + return temp_read_fault_latch; } void app_main() { + /* Pump + triac gate: outputs low before any timers, NVS, or network (pads not floating). */ + gpioConfig(); + /* Init general purpose free running timer (1us resolution)*/ ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &freeRunTimer)); ESP_ERROR_CHECK(gptimer_enable(freeRunTimer)); @@ -503,19 +1046,14 @@ void app_main() */ esp_rmaker_device_add_cb(espresso_device, write_cb, NULL); - /* Adding temperature display to device */ - esp_rmaker_device_add_param(espresso_device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, "Espresso")); + /* No ESP_RMAKER_DEF_NAME_PARAM — saves a redundant name row in the phone UI. */ - primary = esp_rmaker_param_create("Temperature", ESP_RMAKER_PARAM_TEMPERATURE, esp_rmaker_float(0), PROP_FLAG_READ); + snprintf(s_temp_line_str, sizeof(s_temp_line_str), "--"); + primary = esp_rmaker_param_create("Temperature (\xc2\xb0""C)", NULL, esp_rmaker_str(s_temp_line_str), PROP_FLAG_READ); esp_rmaker_param_add_ui_type(primary, ESP_RMAKER_UI_TEXT); esp_rmaker_device_add_param(espresso_device, primary); esp_rmaker_device_assign_primary_param(espresso_device, primary); - /* Creating temperature setpoint OK */ - tempOk_param = esp_rmaker_param_create("Temperature OK", NULL, esp_rmaker_bool(false), PROP_FLAG_READ); - esp_rmaker_param_add_ui_type(tempOk_param, ESP_RMAKER_UI_TOGGLE); - esp_rmaker_device_add_param(espresso_device, tempOk_param); - /* Creating Power Up toggle switch */ poweron_param = esp_rmaker_param_create("Power", NULL, esp_rmaker_bool(powerOn), PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(poweron_param, ESP_RMAKER_UI_TOGGLE); @@ -525,6 +1063,11 @@ void app_main() esp_rmaker_param_t *brewSig_param = esp_rmaker_param_create("Brew Signal", NULL, esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(brewSig_param, ESP_RMAKER_UI_TRIGGER); esp_rmaker_device_add_param(espresso_device, brewSig_param); + + esp_rmaker_param_t *flush_param = esp_rmaker_param_create("Flush", NULL, esp_rmaker_bool(false), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(flush_param, ESP_RMAKER_UI_TRIGGER); + esp_rmaker_device_add_param(espresso_device, flush_param); /* Creating Temperature Lock toggle switch */ esp_rmaker_param_t *templock_param = esp_rmaker_param_create("Temp Lock", NULL, esp_rmaker_bool(tempLock), PROP_FLAG_READ | PROP_FLAG_WRITE); @@ -532,13 +1075,16 @@ void app_main() esp_rmaker_device_add_param(espresso_device, templock_param); /* Creating slider object */ - esp_rmaker_param_t *temp_param = esp_rmaker_param_create("Temperature Setpoint", NULL, esp_rmaker_int(tempSetpoint), PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_t *temp_param = esp_rmaker_param_create("Temperature setpoint (\xc2\xb0""C)", NULL, + esp_rmaker_int(tempSetpoint), PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(temp_param, ESP_RMAKER_UI_SLIDER); - esp_rmaker_param_add_bounds(temp_param, esp_rmaker_int(90), esp_rmaker_int(110), esp_rmaker_int(1)); + esp_rmaker_param_add_bounds(temp_param, esp_rmaker_int(TEMP_SETPOINT_MIN), esp_rmaker_int(TEMP_SETPOINT_MAX), + esp_rmaker_int(1)); esp_rmaker_device_add_param(espresso_device, temp_param); /* Creating slider object */ - esp_rmaker_param_t *brewtime_param = esp_rmaker_param_create("Brew Time", NULL, esp_rmaker_int(brewTime), PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_t *brewtime_param = esp_rmaker_param_create("Brew time (s)", NULL, esp_rmaker_int(brewTime), + PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(brewtime_param, ESP_RMAKER_UI_SLIDER); esp_rmaker_param_add_bounds(brewtime_param, esp_rmaker_int(6), esp_rmaker_int(20), esp_rmaker_int(1)); esp_rmaker_device_add_param(espresso_device, brewtime_param); @@ -549,17 +1095,40 @@ void app_main() esp_rmaker_device_add_param(espresso_device, preinf_param); /* Pre-Infusion On Time slider object */ - esp_rmaker_param_t *preinfOn_param = esp_rmaker_param_create("Pre-Infusion On Time", NULL, esp_rmaker_int(preInfOnTime), PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_t *preinfOn_param = esp_rmaker_param_create("Pre-Infusion on time (s)", NULL, + esp_rmaker_int(preInfOnTime), PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(preinfOn_param, ESP_RMAKER_UI_SLIDER); esp_rmaker_param_add_bounds(preinfOn_param, esp_rmaker_int(2), esp_rmaker_int(10), esp_rmaker_int(1)); esp_rmaker_device_add_param(espresso_device, preinfOn_param); /* Pre-Infusion Off Time slider object */ - esp_rmaker_param_t *preinfOff_param = esp_rmaker_param_create("Pre-Infusion Off Time", NULL, esp_rmaker_int(preInfOffTime), PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_t *preinfOff_param = esp_rmaker_param_create("Pre-Infusion off time (s)", NULL, + esp_rmaker_int(preInfOffTime), PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(preinfOff_param, ESP_RMAKER_UI_SLIDER); esp_rmaker_param_add_bounds(preinfOff_param, esp_rmaker_int(2), esp_rmaker_int(30), esp_rmaker_int(1)); esp_rmaker_device_add_param(espresso_device, preinfOff_param); +#if OVERSHOOT_DETECT_ENABLE + /* Build initial string from loaded NVS trim (nvsRead ran earlier). */ + overshoot_disp_format_str(); + /* Stored cumulative trim (%). While an excursion is latched, appends live peak in °C. */ + overshoot_disp_param = esp_rmaker_param_create("Warmup power trim", NULL, esp_rmaker_str(s_overshoot_disp_str), + PROP_FLAG_READ); + esp_rmaker_param_add_ui_type(overshoot_disp_param, ESP_RMAKER_UI_TEXT); + esp_rmaker_device_add_param(espresso_device, overshoot_disp_param); + + esp_rmaker_param_t *reset_os_param = esp_rmaker_param_create("Reset power trim", NULL, esp_rmaker_bool(false), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(reset_os_param, ESP_RMAKER_UI_TRIGGER); + esp_rmaker_device_add_param(espresso_device, reset_os_param); + +#endif + + snprintf(s_status_str, sizeof(s_status_str), "Okay"); + status_param = esp_rmaker_param_create("Status", NULL, esp_rmaker_str(s_status_str), PROP_FLAG_READ); + esp_rmaker_param_add_ui_type(status_param, ESP_RMAKER_UI_TEXT); + esp_rmaker_device_add_param(espresso_device, status_param); + /* Enable OTA */ esp_rmaker_ota_enable_default(); @@ -582,7 +1151,7 @@ void app_main() /* Start the ESP RainMaker Agent */ esp_rmaker_start(); - err = app_wifi_set_custom_mfg_data(MGF_DATA_DEVICE_TYPE_SWITCH, MFG_DATA_DEVICE_SUBTYPE_SWITCH); + err = app_wifi_set_custom_mfg_data(MFG_DATA_DEVICE_TYPE_SWITCH, MFG_DATA_DEVICE_SUBTYPE_SWITCH); /* Start the Wi-Fi. * If the node is provisioned, it will start connection attempts, @@ -597,15 +1166,13 @@ void app_main() abort(); } - gpioConfig(); - /* Init SPI driver */ spi = spi_init(); /* PID Controller init */ - #if (CONTROL_TYPE == PID) || (CONTROL_TYPE == PID_LOOKUP) +#if (CONTROL_TYPE == PID) || (CONTROL_TYPE == PID_LOOKUP) pidInit(); - #endif +#endif /* initialize heating element control (dimmer) */ dimInit(); @@ -676,39 +1243,53 @@ void Task500ms(void) void heatingControl(void) { - #if (CONTROL_TYPE == PID) +#if (CONTROL_TYPE == PID) pidOut = pidUpdate((float)tempSetpoint, tempCelsius); control = (int)pidOut; - #elif (CONTROL_TYPE == LOOKUP) - +#elif (CONTROL_TYPE == LOOKUP) + delta = ((float)tempSetpoint - tempCelsius); float ir = indexRatio(deltaBkp, BKP_NUM, delta); - control = (int)interp1D(controlSet, BKP_NUM, ir); - + const int control_lookup = (int)interp1D(controlSet, BKP_NUM, ir); +#if OVERSHOOT_DETECT_ENABLE + if (temp_read_trusted) { + if (!overshoot_boot_temp_sampled) { + overshoot_boot_temp_sampled = true; + overshoot_learn_try_arm_cold("boot trusted"); + } + overshoot_peak_detector_update(); + } + int control_after_trim = control_lookup; + overshoot_apply_trim_to_control(&control_after_trim); + control = control_after_trim; +#else + control = control_lookup; +#endif + + /* reduce power to a factor by toggling power each task */ if ((delta > 0) && (delta <= TEMP_PWR_TOGGLE)) { - /* reduce power to a factor by toggling power each task */ powerToggle = (int)((count500ms % POWER_FACTOR) == 0); control = (control * powerToggle); } - #elif (CONTROL_TYPE == PID_LOOKUP) +#elif (CONTROL_TYPE == PID_LOOKUP) pidOut = pidUpdate((float)tempSetpoint, tempCelsius); float ir = indexRatio(deltaBkp, BKP_NUM, pidOut); control = (int)interp1D(controlSet, BKP_NUM, ir); - #endif +#endif - if ((tempCelsius == 0) || (tempCelsius > MAX_TEMP_THR)) - { - /* switch off heating element when temperature sensor is disconnected or in overheat condition */ + temp_stuck_diag_update(control); + + if (boiler_heater_holdoff()) { control = 0; - ESP_LOGE(TAG, "Recovery mode, overheat detected or temperature sensor failed!"); + ESP_LOGW(TAG, "Boiler heater held off: unsafe read, range, or stuck-temp diag."); } else if (brewState == BREW_POWER_OFF) { @@ -718,19 +1299,25 @@ void heatingControl(void) else { /* - * Set full power while water pump is ON for to keep temperature stable. - * To better buffer temperature after starting pre-infusion, we'll leave heating element ON for - * half the time of preinfusion ON at the start of pre-infusion OFF. + * Full heat offsets cold-water dip only while temp < setpoint + TEMP_DELTA. + * Preinfusion OFF: full heat for first half of preinfusion ON time (pump off). + * Flush: full heat for first half of flush only; pump runs full FLUSH_PUMP_SEC in brewProgram. */ - #define TIME_TEMP_BUFF (unsigned long long)SEC_TO_US((float)preInfOnTime / 2) + const bool pump_phase_full_heat = + (brewState == BREW_PREINF_ON) || (brewState == BREW_ON) || + ((brewState == BREW_FLUSH) && ((getAbsTime1us() - pumpTimer) < TIME_FLUSH_HEAT_BUFF_US)) || + ((brewState == BREW_PREINF_OFF) && ((getAbsTime1us() - pumpTimer) < TIME_TEMP_BUFF_US)); + const float temp_full_heat_max = (float)tempSetpoint + (float)TEMP_DELTA; - control = ((brewState == BREW_PREINF_ON) || (brewState == BREW_ON) || - ((brewState == BREW_PREINF_OFF) && ((getAbsTime1us() - pumpTimer) < TIME_TEMP_BUFF))) ? (int)100 : control; + if (pump_phase_full_heat && (tempCelsius < temp_full_heat_max)) { + control = 100; + } } setPower(ptr_dimmer, (int)control); + ESP_LOGI(TAG, "%d | %.2f | %.2f | %d | %d", tempSetpoint, tempCelsius, pidOut, control, getPower(ptr_dimmer)); } @@ -739,8 +1326,11 @@ void Task5000ms(void) if (powerOn) { /* only report data to app when device is on, to save MQTT budget */ - esp_rmaker_param_update_and_report(primary, esp_rmaker_float(tempCelsius)); - esp_rmaker_param_update_and_report(tempOk_param, esp_rmaker_bool(tempRangeOk)); + temp_status_line_report(); + boiler_status_report(); +#if OVERSHOOT_DETECT_ENABLE + overshoot_disp_report(); +#endif } } @@ -751,7 +1341,9 @@ void brewProgram(void) * brewing attempt if temperature is not within an acceptable range (calibrated * by TEMP_DELTA). */ - if ((tempCelsius < (tempSetpoint - TEMP_DELTA)) || (tempCelsius > (tempSetpoint + TEMP_DELTA))) + if (!temp_read_trusted || temp_stuck_diag) { + tempRangeOk = false; + } else if ((tempCelsius < (tempSetpoint - TEMP_DELTA)) || (tempCelsius > (tempSetpoint + TEMP_DELTA))) { tempRangeOk = false; } @@ -760,12 +1352,19 @@ void brewProgram(void) tempRangeOk = true; } - /* A brewing operation is stopped if user presses Brew Signal again */ - if ((brewSignal == false) && (brewState != BREW_POWER_OFF)) - { - /* abort brew at any stage */ - gpio_set_level(GPIO_OUTPUT_IO_0, 0); - brewState = BREW_OFF; + /* Stop pump: power off; or user cleared Brew during brew; or cleared Flush during flush. */ + if (brewState != BREW_POWER_OFF) { + const bool stop_brew = !brewSignal && + (brewState == BREW_PREINF_ON || brewState == BREW_PREINF_OFF || + brewState == BREW_ON); + const bool stop_flush = !flushSignal && brewState == BREW_FLUSH; + + if (!powerOn || stop_brew || stop_flush) { + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + brewSignal = false; + flushSignal = false; + brewState = BREW_OFF; + } } /* @@ -798,8 +1397,16 @@ void brewProgram(void) /* abort brew attempt and reset button */ ESP_LOGI(TAG, "Brew aborted! Setpoint temperature not reached"); brewSignal = false; + flushSignal = false; } } + else if (flushSignal == true) + { + pumpTimer = getAbsTime1us(); + brewState = BREW_FLUSH; + powerOnTimer = getAbsTime1us(); + ESP_LOGI(TAG, "Flush started"); + } else { /* turn power off if Brew Signal is not pressed after POWERON_MIN (default 15 min) */ @@ -809,6 +1416,9 @@ void brewProgram(void) brewState = BREW_POWER_OFF; powerOn = false; +#if OVERSHOOT_DETECT_ENABLE + overshoot_learn_ever_armed = false; +#endif esp_rmaker_param_update_and_report(poweron_param, esp_rmaker_bool(powerOn)); ESP_LOGI(TAG, "Switching power OFF!"); @@ -860,7 +1470,8 @@ void brewProgram(void) ESP_LOGI(TAG, "Brew cycle completed!"); brewSignal = false; /* reset for next press */ - + flushSignal = false; + /* Save brewing parameters to NVS */ nvsWrite(); } @@ -869,17 +1480,36 @@ void brewProgram(void) case BREW_POWER_OFF: - /* Standby mode. Leaving here only if Power switch is toggled */ + /* Standby: leaving on Power on — reset latches so a clean idle is guaranteed. */ if (powerOn == true) { powerOnTimer = getAbsTime1us(); brewState = BREW_OFF; + brewSignal = false; + flushSignal = false; +#if OVERSHOOT_DETECT_ENABLE + overshoot_learn_try_arm_cold("standby->on"); +#endif ESP_LOGI(TAG, "Switching power ON!"); } break; + case BREW_FLUSH: + + if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(FLUSH_PUMP_SEC)) { + gpio_set_level(GPIO_OUTPUT_IO_0, 1); + } else { + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + brewState = BREW_OFF; + brewSignal = false; + flushSignal = false; + ESP_LOGI(TAG, "Flush completed"); + } + + break; + default: break; } diff --git a/main/app_priv.h b/main/app_priv.h index 30d23f0..c558626 100644 --- a/main/app_priv.h +++ b/main/app_priv.h @@ -17,3 +17,8 @@ void gpioConfig(void); extern float indexRatio(float vector[], int size, float input); extern float interp1D(float vector[], int size, float ir); extern unsigned long long getAbsTime1us(void); + +/** Boiler MAX6675 path: true after several consecutive good samples; false on any SPI/open/range fault. */ +bool boiler_temp_is_trusted(void); +/** Latched fault bits (TEMP_DIAG_*); cleared when read becomes fully trusted again. */ +uint32_t boiler_temp_fault_bits(void); diff --git a/main/temp_pid.c b/main/temp_pid.c index e7e2029..442f517 100644 --- a/main/temp_pid.c +++ b/main/temp_pid.c @@ -23,7 +23,8 @@ PIDController pid = { PID_KP, PID_KI, PID_KD, PID_TAU, PID_LIM_MIN, PID_LIM_MAX, PID_LIM_MIN_INT, PID_LIM_MAX_INT, - SAMPLE_TIME_S }; + SAMPLE_TIME_S, + 0, 0, 0, 0, 0 }; void pidInit(void) { diff --git a/memalloc.sh b/memalloc.sh index 2c2df2c..ef05d77 100755 --- a/memalloc.sh +++ b/memalloc.sh @@ -1 +1 @@ -python3 -m esp_idf_size build/switch.map +python3 -m esp_idf_size build/espresso.map diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 100a77f..9f2a676 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -31,3 +31,12 @@ CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY_1=y # Application Rollback CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y + +# esp_diagnostics adds INTERFACE -Wl,--wrap=esp_log_write; __wrap_* can be GC'd from the +# static archive → undefined reference. External-wrap mode disables those wraps (no log hook). +CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y + +# IRAM headroom (RainMaker + NimBLE + Wi-Fi + GPTimer ISR) +# CONFIG_LOG_IN_IRAM is not set +# CONFIG_ESP_WIFI_RX_IRAM_OPT is not set +# CONFIG_ESP32_WIFI_RX_IRAM_OPT is not set From 3955539e6bbee330f54e9615fbabe7ef28020f1f Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Tue, 21 Apr 2026 10:39:06 -0300 Subject: [PATCH 3/7] heat control: Improve heat buffer vs pump control Improve the way heat buffering when water pump is on is implemented. A heat buffer vector is implemented to allow calibrating a heat profile following the amount of time in which the pump is on, in order to improve temperature stability in preinfusion, brew and flush operations. Signed-off-by: Raffael Rostagno --- README.md | 20 ++++++++++++ main/app_main.c | 82 ++++++++++++++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index f0267ae..45971e2 100644 --- a/README.md +++ b/README.md @@ -114,3 +114,23 @@ static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 80, 100, 100} The first vector corresponds to the temperature difference between target and actual reading. The second vector is the power factor (0 to 100%) to apply for each (interpolated) delta. + +#### Pump heat buffer calibration + +When the pump is active (pre-infusion, brew, or flush), cold water entering the boiler causes a +temperature drop. To compensate, a time-indexed power profile is applied instead of the idle +temperature controller: + +``` +static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { + 80, 80, 60, 60, 40, 40, 40, ... +}; +``` + +Each entry is the heater power (0–100%) applied at the corresponding second of pump-on time. +Index 0 is the first second, index 1 the second, and so on up to `PUMP_ON_HEAT_BUFF_LEN - 1`. +The vector length must be at least as long as the maximum configured brew time and flush time. + +The default calibration likely needs to be tuned for each machine, also considering the position the +temperature sensor is located for the particular project. The recommended calibration procedure is to +use the **Flush** button to experimentally calibrate the vector. diff --git a/main/app_main.c b/main/app_main.c index 896ef69..8502e9f 100644 --- a/main/app_main.c +++ b/main/app_main.c @@ -101,7 +101,6 @@ if (ret != ESP_OK) \ #define OVERSHOOT_DETECT_ENABLE 1 #endif - #define BKP_NUM 10 static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 100}; /* temperature delta */ @@ -116,10 +115,13 @@ static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 80, 100, 100} #define BREW_POWER_OFF 4 #define BREW_FLUSH 5 -#define FLUSH_PUMP_SEC 5 /* group-head flush: pump on duration */ -/* Pump-phase full-heat windows (us); preInfOnTime may change at runtime (RainMaker). */ -#define TIME_TEMP_BUFF_US ((unsigned long long)SEC_TO_US((float)preInfOnTime / 2.f)) -#define TIME_FLUSH_HEAT_BUFF_US ((unsigned long long)SEC_TO_US((float)FLUSH_PUMP_SEC / 2.f)) +#define PUMP_ON_HEAT_BUFF_LEN 20 /* must be >= max brew time (s) and max flush time (s) */ + +/* Each BKP is seconds of pump-on time; output is heater power (%). */ +static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { +/* s: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 */ + 100, 100, 80, 80, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70 +}; #define TEMP_DELTA 2 /* temp delta from setpoint for good brewing temperature */ #define TEMP_SETPOINT_MIN 88 @@ -202,6 +204,8 @@ static float delta; static int control; static int powerToggle = 0; static unsigned long long pumpTimer = 0; +static bool pump_active; +static unsigned long long pump_start_us; static unsigned long long powerOnTimer = 0; static bool brewSignal = false; static bool flushSignal = false; @@ -213,6 +217,7 @@ static bool powerOn = false; static bool preInfusion = true; static int32_t preInfOnTime = 3; static int32_t preInfOffTime = 10; +static int32_t flushTime = 5; static bool temp_read_trusted; static uint8_t temp_read_good_streak; @@ -305,6 +310,12 @@ static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_pa ESP_LOGI(TAG, "Brew Time: %d s", brewTime); } + if (strcmp(esp_rmaker_param_get_name(param), "Flush time (s)") == 0) + { + flushTime = val.val.i; + ESP_LOGI(TAG, "Flush Time: %d s", flushTime); + } + if (strcmp(esp_rmaker_param_get_name(param), "Brew Signal") == 0) { brewSignal = val.val.b ? !brewSignal : brewSignal; @@ -516,6 +527,7 @@ void nvsRead(void) tempSetpoint = TEMP_SETPOINT_MAX; } nvs_get_i32(nvs_handle, "brewTime", &brewTime); + nvs_get_i32(nvs_handle, "flushTime", &flushTime); nvs_get_u8(nvs_handle, "preInfusion", &u8); preInfusion = u8 != 0; nvs_get_i32(nvs_handle, "preInfOnTime", &preInfOnTime); @@ -546,6 +558,7 @@ void nvsWrite(void) ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "tempLock", (uint8_t)tempLock)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "tempSetpoint", tempSetpoint)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "brewTime", brewTime)); + ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "flushTime", flushTime)); ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "preInfusion", (uint8_t)preInfusion)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOnTime", preInfOnTime)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOffTime", preInfOffTime)); @@ -1088,7 +1101,7 @@ void app_main() esp_rmaker_param_add_ui_type(brewtime_param, ESP_RMAKER_UI_SLIDER); esp_rmaker_param_add_bounds(brewtime_param, esp_rmaker_int(6), esp_rmaker_int(20), esp_rmaker_int(1)); esp_rmaker_device_add_param(espresso_device, brewtime_param); - + /* Creating Pre-Infusion toggle switch */ esp_rmaker_param_t *preinf_param = esp_rmaker_param_create("Pre-Infusion", NULL, esp_rmaker_bool(preInfusion), PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(preinf_param, ESP_RMAKER_UI_TOGGLE); @@ -1108,6 +1121,12 @@ void app_main() esp_rmaker_param_add_bounds(preinfOff_param, esp_rmaker_int(2), esp_rmaker_int(30), esp_rmaker_int(1)); esp_rmaker_device_add_param(espresso_device, preinfOff_param); + esp_rmaker_param_t *flushtime_param = esp_rmaker_param_create("Flush time (s)", NULL, esp_rmaker_int(flushTime), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(flushtime_param, ESP_RMAKER_UI_SLIDER); + esp_rmaker_param_add_bounds(flushtime_param, esp_rmaker_int(3), esp_rmaker_int(20), esp_rmaker_int(1)); + esp_rmaker_device_add_param(espresso_device, flushtime_param); + #if OVERSHOOT_DETECT_ENABLE /* Build initial string from loaded NVS trim (nvsRead ran earlier). */ overshoot_disp_format_str(); @@ -1255,7 +1274,7 @@ void heatingControl(void) float ir = indexRatio(deltaBkp, BKP_NUM, delta); const int control_lookup = (int)interp1D(controlSet, BKP_NUM, ir); #if OVERSHOOT_DETECT_ENABLE - if (temp_read_trusted) { + if (temp_read_trusted && !pump_active) { if (!overshoot_boot_temp_sampled) { overshoot_boot_temp_sampled = true; overshoot_learn_try_arm_cold("boot trusted"); @@ -1269,8 +1288,8 @@ void heatingControl(void) control = control_lookup; #endif - /* reduce power to a factor by toggling power each task */ - if ((delta > 0) && (delta <= TEMP_PWR_TOGGLE)) + /* reduce power to a factor by toggling power each task (idle only) */ + if (!pump_active && (delta > 0) && (delta <= TEMP_PWR_TOGGLE)) { powerToggle = (int)((count500ms % POWER_FACTOR) == 0); control = (control * powerToggle); @@ -1285,7 +1304,9 @@ void heatingControl(void) #endif - temp_stuck_diag_update(control); + if (!pump_active) { + temp_stuck_diag_update(control); + } if (boiler_heater_holdoff()) { control = 0; @@ -1296,23 +1317,10 @@ void heatingControl(void) /* switch off heating element and remain in standby */ control = 0; } - else + else if (pump_active) { - /* - * Full heat offsets cold-water dip only while temp < setpoint + TEMP_DELTA. - * Preinfusion OFF: full heat for first half of preinfusion ON time (pump off). - * Flush: full heat for first half of flush only; pump runs full FLUSH_PUMP_SEC in brewProgram. - */ - - const bool pump_phase_full_heat = - (brewState == BREW_PREINF_ON) || (brewState == BREW_ON) || - ((brewState == BREW_FLUSH) && ((getAbsTime1us() - pumpTimer) < TIME_FLUSH_HEAT_BUFF_US)) || - ((brewState == BREW_PREINF_OFF) && ((getAbsTime1us() - pumpTimer) < TIME_TEMP_BUFF_US)); - const float temp_full_heat_max = (float)tempSetpoint + (float)TEMP_DELTA; - - if (pump_phase_full_heat && (tempCelsius < temp_full_heat_max)) { - control = 100; - } + unsigned s = (unsigned)((getAbsTime1us() - pump_start_us) / 1000000ULL); + control = pumpOnHeatBuff[s]; } setPower(ptr_dimmer, (int)control); @@ -1361,6 +1369,7 @@ void brewProgram(void) if (!powerOn || stop_brew || stop_flush) { gpio_set_level(GPIO_OUTPUT_IO_0, 0); + pump_active = false; brewSignal = false; flushSignal = false; brewState = BREW_OFF; @@ -1379,7 +1388,7 @@ void brewProgram(void) if ((tempRangeOk) || (tempLock == false)) { pumpTimer = getAbsTime1us(); - + if (preInfusion) { brewState = BREW_PREINF_ON; @@ -1388,7 +1397,9 @@ void brewProgram(void) { brewState = BREW_ON; } - + pump_active = true; + pump_start_us = pumpTimer; + /* reset power on timer */ powerOnTimer = getAbsTime1us(); } @@ -1404,6 +1415,8 @@ void brewProgram(void) { pumpTimer = getAbsTime1us(); brewState = BREW_FLUSH; + pump_active = true; + pump_start_us = pumpTimer; powerOnTimer = getAbsTime1us(); ESP_LOGI(TAG, "Flush started"); } @@ -1436,8 +1449,9 @@ void brewProgram(void) else { brewState = BREW_PREINF_OFF; + pump_active = false; pumpTimer = getAbsTime1us(); - } + } break; @@ -1452,7 +1466,9 @@ void brewProgram(void) { brewState = BREW_ON; pumpTimer = getAbsTime1us(); - } + pump_active = true; + pump_start_us = pumpTimer; + } break; @@ -1466,6 +1482,7 @@ void brewProgram(void) else { gpio_set_level(GPIO_OUTPUT_IO_0, 0); + pump_active = false; brewState = BREW_OFF; ESP_LOGI(TAG, "Brew cycle completed!"); @@ -1474,7 +1491,7 @@ void brewProgram(void) /* Save brewing parameters to NVS */ nvsWrite(); - } + } break; @@ -1498,10 +1515,11 @@ void brewProgram(void) case BREW_FLUSH: - if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(FLUSH_PUMP_SEC)) { + if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(flushTime)) { gpio_set_level(GPIO_OUTPUT_IO_0, 1); } else { gpio_set_level(GPIO_OUTPUT_IO_0, 0); + pump_active = false; brewState = BREW_OFF; brewSignal = false; flushSignal = false; From f9d0cc5a23e6e1f5c1689629f80edb3fbca3db7b Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Tue, 21 Apr 2026 11:34:19 -0300 Subject: [PATCH 4/7] calib: Update stock heat calibration Update stock heat calibration for improved overshoot control. Signed-off-by: Raffael Rostagno --- README.md | 4 ++-- main/app_main.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 45971e2..fe75751 100644 --- a/README.md +++ b/README.md @@ -108,8 +108,8 @@ To calibrate the PID controller, the following symbols can be optimized for each To calibrate the P controller, the following vectors can be changed: ``` -static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 100}; -static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 80, 100, 100}; +static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; +static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 25, 60, 100}; ``` The first vector corresponds to the temperature difference between target and actual reading. diff --git a/main/app_main.c b/main/app_main.c index 8502e9f..1584f3f 100644 --- a/main/app_main.c +++ b/main/app_main.c @@ -103,8 +103,8 @@ if (ret != ESP_OK) \ #define BKP_NUM 10 -static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 100}; /* temperature delta */ -static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 80, 100, 100}; /* power setpoint in percentage */ +static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; /* temperature delta */ +static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 25, 60, 100}; /* power setpoint in percentage */ #define SEC_TO_US(x) (x * 1000000) From 54ce27f4911a67d2097285cb43ce1de154a20264 Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Sat, 25 Apr 2026 13:55:13 -0300 Subject: [PATCH 5/7] refactor: split into modules, RTOS tasks, v2.0.1 Files split for better organization. Rudimentary tasks converted to FreeRTOS tasks. Signed-off-by: Raffael Rostagno --- CMakeLists.txt | 2 +- Makefile | 2 +- README.md | 19 +- main/CMakeLists.txt | 6 +- main/app_main.c | 1534 -------------------------------- main/control.c | 852 ++++++++++++++++++ main/control.h | 119 +++ main/main.c | 174 ++++ main/{functions.c => math.c} | 26 +- main/nvs.c | 147 +++ main/nvs.h | 13 + main/pid.c | 35 + main/{app_priv.h => private.h} | 11 +- main/rainmaker.c | 350 ++++++++ main/tasks.c | 84 ++ main/tasks.h | 7 + main/temp_pid.c | 40 - 17 files changed, 1813 insertions(+), 1608 deletions(-) delete mode 100644 main/app_main.c create mode 100644 main/control.c create mode 100644 main/control.h create mode 100644 main/main.c rename main/{functions.c => math.c} (56%) create mode 100644 main/nvs.c create mode 100644 main/nvs.h create mode 100644 main/pid.c rename main/{app_priv.h => private.h} (51%) create mode 100644 main/rainmaker.c create mode 100644 main/tasks.c create mode 100644 main/tasks.h delete mode 100644 main/temp_pid.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a420d0b..6cb5845 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,6 @@ endif(DEFINED ENV{RMAKER_PATH}) # RainMaker components + example helpers (app_wifi, app_insights, …) set(EXTRA_COMPONENT_DIRS ${RMAKER_PATH}/components ${RMAKER_PATH}/examples/common ${CMAKE_CURRENT_LIST_DIR}/components) -set(PROJECT_VER "1.9.0") +set(PROJECT_VER "2.0.1") include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(espresso) diff --git a/Makefile b/Makefile index 8d5ceba..c9326e5 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := espresso -PROJECT_VER := 1.9.0 +PROJECT_VER := 2.0.1 # Add RainMaker components and other common application components EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../components $(PROJECT_PATH)/../common diff --git a/README.md b/README.md index fe75751..85039ba 100644 --- a/README.md +++ b/README.md @@ -108,8 +108,8 @@ To calibrate the PID controller, the following symbols can be optimized for each To calibrate the P controller, the following vectors can be changed: ``` -static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; -static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 25, 60, 100}; +static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; +static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 2, 15, 30, 60, 80}; ``` The first vector corresponds to the temperature difference between target and actual reading. @@ -123,7 +123,7 @@ temperature controller: ``` static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { - 80, 80, 60, 60, 40, 40, 40, ... + 100, 100, 100, 100, 80, 80, 40, 40, 30, 30, 30, 40, 40, 40, 40, 60, 60, 60, 60, 60 }; ``` @@ -131,6 +131,13 @@ Each entry is the heater power (0–100%) applied at the corresponding second of Index 0 is the first second, index 1 the second, and so on up to `PUMP_ON_HEAT_BUFF_LEN - 1`. The vector length must be at least as long as the maximum configured brew time and flush time. -The default calibration likely needs to be tuned for each machine, also considering the position the -temperature sensor is located for the particular project. The recommended calibration procedure is to -use the **Flush** button to experimentally calibrate the vector. +##### Extraction flow dynamics + +The power profile follows the natural resistance the coffee puck offers to water flow during extraction: + +- **Phase 1 (~4 s)** — Free-flow: water saturates the dry puck with little resistance. Flow rate is high and the boiler cools quickly, so high heater power is needed. +- **Phase 2 (~4 s)** — Puck compressing: swelling grounds start restricting flow. Less water moves through, so less compensation is required. +- **Phase 3 (~6 s)** — Maximum compression: the puck is fully saturated and tightly packed. Flow is most restricted and the boiler loses less heat, so power demand is at its lowest. +- **Phase 4 (~6 s+)** — Puck deterioration: channels begin to form as the grounds break down, flow gradually recovers, and power demand rises again. + +This is a general profile — it can vary significantly depending on basket type (single vs. double) and dimensions, coffee dose, grind size, and temperature sensor position within the machine. diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index dc88d8d..4960bc0 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,2 +1,4 @@ -idf_component_register(SRCS ./app_main.c ./temp_pid.c ./functions.c - INCLUDE_DIRS "." "../components/max6675") +idf_component_register( + SRCS ./main.c ./control.c ./rainmaker.c ./tasks.c ./nvs.c + ./pid.c ./math.c + INCLUDE_DIRS "." "../components/max6675") diff --git a/main/app_main.c b/main/app_main.c deleted file mode 100644 index 1584f3f..0000000 --- a/main/app_main.c +++ /dev/null @@ -1,1534 +0,0 @@ -/* - Espresso Machine PID/P Controller - - This project implements a PID/P controller to effectively control the - temperature of an Espresso machine boiler, for a stable setpoint. - Another feature implemented is the control of the water pump and - pre-infusion settings. Combined, these functionalities allow for - improved espresso extraction and consistency. - - https://github.com/raffarost/espresso - March 2024 - April 2026 - - Raffael Rostagno - raffael.rostagno@gmail.com -*/ - -#include -#include -#include -#include -#include -#include "esp_err.h" -#include "soc/soc_caps.h" -#include -#include -#include -#include "esp32-triac-dimmer-driver.h" -#include "driver/gpio.h" -#include "driver/spi_master.h" -#include "pins.h" -#include "spi_mod.h" -#include "driver/gptimer.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "app_priv.h" - -/***************************************************************************** - * Configuration defines - *****************************************************************************/ - -/* heating element control */ -#define GRID_FREQ 60 /* power grid frequency for power control */ -#define ZEROCROSS_GPIO GPIO_NUM_5 -#define TRIAC_1_GPIO GPIO_NUM_33 - -/* Tasks config */ -#define TASK_100ms_PERIOD_MS 100 -#define TASK_500ms_PERIOD_MS 500 -#define TASK_100ms_PRIORITY tskIDLE_PRIORITY -#define TASK_500ms_PRIORITY tskIDLE_PRIORITY - -/* GPIO config */ - -#define GPIO_OUTPUT_IO_0 GPIO_NUM_4 -#define GPIO_OUTPUT_IO_1 0 -/* Pump + triac gate: outputs defined early so pads are not floating through Wi-Fi / RainMaker init. */ -#define GPIO_OUTPUT_PIN_SEL ((1ULL << GPIO_OUTPUT_IO_0) | (1ULL << TRIAC_1_GPIO)) - -static const gpio_config_t gpio_outcfg = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = GPIO_OUTPUT_PIN_SEL, - .pull_down_en = 0, - .pull_up_en = 0 -}; - -gptimer_handle_t freeRunTimer = NULL; -gptimer_config_t timer_config = { - .clk_src = GPTIMER_CLK_SRC_DEFAULT, - .direction = GPTIMER_COUNT_UP, - .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us -}; - -#define CHECK_RET(ret,msg) \ -if (ret != ESP_OK) \ -{ \ - ESP_LOGE(TAG, msg); \ -} - -#define PID 0 -#define LOOKUP 1 -#define PID_LOOKUP 2 - -#define CONTROL_TYPE LOOKUP - -/* Overshoot learn/trim for LOOKUP only; on by default. Off: -DOVERSHOOT_DETECT_ENABLE=0 or #define 0 above. */ -#if (CONTROL_TYPE == LOOKUP) && !defined(OVERSHOOT_DETECT_ENABLE) -#define OVERSHOOT_DETECT_ENABLE 1 -#endif - -#define BKP_NUM 10 - -static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; /* temperature delta */ -static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 1, 1, 25, 60, 100}; /* power setpoint in percentage */ - -#define SEC_TO_US(x) (x * 1000000) - -#define BREW_OFF 0 -#define BREW_PREINF_ON 1 -#define BREW_PREINF_OFF 2 -#define BREW_ON 3 -#define BREW_POWER_OFF 4 -#define BREW_FLUSH 5 - -#define PUMP_ON_HEAT_BUFF_LEN 20 /* must be >= max brew time (s) and max flush time (s) */ - -/* Each BKP is seconds of pump-on time; output is heater power (%). */ -static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { -/* s: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 */ - 100, 100, 80, 80, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70 -}; - -#define TEMP_DELTA 2 /* temp delta from setpoint for good brewing temperature */ -#define TEMP_SETPOINT_MIN 88 -#define TEMP_SETPOINT_MAX 96 -#define MAX_TEMP_THR 110 /* maximum temperature threshold for safety control */ - -#define TEMP_READ_OK_STREAK 3 /* consecutive good samples required before trusting */ -#define TEMP_VALID_MIN_C (-2.0f) -#define TEMP_VALID_MAX_C (125.0f) -#define TEMP_DIAG_SPI (1u << 0) -#define TEMP_DIAG_OPEN_TC (1u << 1) -#define TEMP_DIAG_RANGE (1u << 2) - -/* Stuck sensor: high heater demand but temp unchanged for STUCK_DIAG_SAMPLES consecutive reads. - * Warmup grace delays counting until the sensor has proven responsive or the grace period elapses - * (the heater is physically far from the sensor, so a cold ramp reads flat for several seconds). */ -#define STUCK_DIAG_MIN_POWER_PCT 10 -#define STUCK_DIAG_SAMPLES 5 /* 2.5s @ 500ms cadence */ -#define STUCK_DIAG_TEMP_EPS 0.21f /* < half MAX6675 LSB (0.25°C) */ -#define STUCK_DIAG_CLEAR_MAX_C 110.f -#define STUCK_DIAG_WARMUP_GRACE_SEC 10.f - -/* divisor factors for power reduction around temperature target */ -#define POWER_50 2 -#define POWER_33 3 -#define POWER_25 4 - -#define POWER_FACTOR POWER_33 -#define TEMP_PWR_TOGGLE 10 /* °C window around setpoint where power is dithered */ - -#define POWERON_MIN (15 * SEC_TO_US(60)) /* standby timeout (15 min) */ - -#if OVERSHOOT_DETECT_ENABLE -#define OVERSHOOT_SOFT_DISARM_SEC 90.f -#define OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD 20.0f /* apply trim when control (%) is above this */ -#define OVERSHOOT_TRIM_FRAC_PER_DEG 0.02f /* trim fraction added per °C of measured excursion */ -#define OVERSHOOT_TRIM_FRAC_MAX 0.15f /* max cumulative trim fraction (0.15 = 15% cut) */ -#define OVERSHOOT_COLD_START_MAX_C 60.0f /* arm learn only if boiler at/below this temp (°C) */ -#endif - - -/***************************************************************************** - * Module variables - *****************************************************************************/ - -static const char *TAG = "espresso"; -esp_rmaker_device_t *espresso_device; - -esp_rmaker_param_t *primary; -static esp_rmaker_param_t *status_param; - -static char s_temp_line_str[40]; -static char s_status_str[96]; - -/* Status latch: worst-severity issue shown until ~60s of clean Okay reports */ -#define STATUS_PRI_ZERO 25 -#define STATUS_PRI_UNTRUSTED 50 -#define STATUS_PRI_TOO_HIGH 75 -#define STATUS_PRI_STUCK 100 -#define STATUS_WORST_CLEAR_OK_REPORTS 12 /* Task5000ms ticks (~60s) */ - -dimmertyp *ptr_dimmer; - -static int count100ms = 0; -static int count500ms = 0; - -spi_device_handle_t spi; -uint16_t data; -spi_transaction_t tM = { - .tx_buffer = NULL, - .rx_buffer = &data, - .length = 16, - .rxlength = 16, -}; - -static float tempCelsius; -static int32_t tempSetpoint = 96; -static float pidOut; -static float delta; -static int control; -static int powerToggle = 0; -static unsigned long long pumpTimer = 0; -static bool pump_active; -static unsigned long long pump_start_us; -static unsigned long long powerOnTimer = 0; -static bool brewSignal = false; -static bool flushSignal = false; -static int brewState = BREW_POWER_OFF; -static int32_t brewTime = 6; -static bool tempRangeOk = false; -static bool tempLock = true; -static bool powerOn = false; -static bool preInfusion = true; -static int32_t preInfOnTime = 3; -static int32_t preInfOffTime = 10; -static int32_t flushTime = 5; - -static bool temp_read_trusted; -static uint8_t temp_read_good_streak; -static uint32_t temp_read_fault_latch; - -static int s_status_worst_pri; -static char s_status_worst_str[48]; -static uint16_t s_status_ok_clean_reports; - -static bool temp_stuck_diag; -static float temp_stuck_prev_c; -static uint8_t temp_stuck_same_ct; -static bool temp_sensor_responsive; /* latched once the sensor moves under heat demand */ -static unsigned long long temp_stuck_warmup_start_us; /* t0 of the cold-start warmup grace window */ - -#if OVERSHOOT_DETECT_ENABLE -static esp_rmaker_param_t *overshoot_disp_param; -static char s_overshoot_disp_str[28]; -static float overshoot_trim_stored; /* NVS-persisted power cut fraction (0..TRIM_FRAC_MAX) */ -static float overshoot_excursion_pk; /* peak (temp - setpoint) °C of the active excursion */ -static bool overshoot_excursion_latched; /* clears at temp <= setpoint; triggers commit once */ -static bool overshoot_detected; /* sticky: crossed setpoint+TEMP_DELTA this arm cycle */ -static bool overshoot_learn_allowed = false; -static bool overshoot_learn_ever_armed; /* cold arm succeeded at least once this power session */ -#define OS_DISARM_SOFT_TIMEOUT (1u << 0) -#define OS_DISARM_COMMIT (1u << 1) -static uint32_t overshoot_learn_disarm_mask; -static bool overshoot_boot_temp_sampled; -static unsigned long long overshoot_soft_timer_start_us; -#endif - -static esp_rmaker_param_t *poweron_param; - -/***************************************************************************** - * Function headers - *****************************************************************************/ - -void TaskBackground(void); -void Task100ms(void); -void Task500ms(void); -void Task5000ms(void); -void spiComm(void); -void heatingControl(void); -void brewProgram(void); -void nvsRead(void); -void nvsWrite(void); -static void temp_stuck_diag_update(int heating_demand_pct); -static bool boiler_heater_holdoff(void); -static void temp_status_line_report(void); -static void boiler_status_report(void); -#if OVERSHOOT_DETECT_ENABLE -static void overshoot_learn_try_arm_cold(const char *site); -static void overshoot_peak_detector_update(void); -static void overshoot_disp_format_str(void); -static void overshoot_disp_report(void); -static void nvs_read_overshoot_trim(nvs_handle_t h); -static void nvs_persist_overshoot_trim(void); -static void overshoot_apply_trim_to_control(int *p_control); -static void overshoot_learn_set_disallowed(uint32_t reason_bits); -#endif - -/***************************************************************************** - * Function declaration - *****************************************************************************/ - -/* Callback to handle commands received from the RainMaker cloud */ -static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param, - const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx) -{ - if (ctx) { - ESP_LOGI(TAG, "Received write request via : %s", esp_rmaker_device_cb_src_to_str(ctx->src)); - } - - /* Save to local variable */ - if (strcmp(esp_rmaker_param_get_name(param), "Temperature setpoint (\xc2\xb0""C)") == 0) - { - int32_t sp = val.val.i; - if (sp < TEMP_SETPOINT_MIN) { - sp = TEMP_SETPOINT_MIN; - } else if (sp > TEMP_SETPOINT_MAX) { - sp = TEMP_SETPOINT_MAX; - } - tempSetpoint = sp; - ESP_LOGI(TAG, "New temperature setpoint: %d", tempSetpoint); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Brew time (s)") == 0) - { - brewTime = val.val.i; - ESP_LOGI(TAG, "Brew Time: %d s", brewTime); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Flush time (s)") == 0) - { - flushTime = val.val.i; - ESP_LOGI(TAG, "Flush Time: %d s", flushTime); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Brew Signal") == 0) - { - brewSignal = val.val.b ? !brewSignal : brewSignal; - ESP_LOGI(TAG, "Brew %s!", brewSignal ? "start" : "stop"); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Flush") == 0) - { - flushSignal = val.val.b ? !flushSignal : flushSignal; - ESP_LOGI(TAG, "Flush %s!", flushSignal ? "on" : "off"); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion") == 0) - { - preInfusion = val.val.b; - ESP_LOGI(TAG, "Pre-Infusion set to %s", preInfusion ? "ON" : "OFF"); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Power") == 0) - { - const bool was_on = powerOn; - powerOn = val.val.b; - ESP_LOGI(TAG, "Power is set to %s", powerOn ? "ON" : "OFF"); -#if OVERSHOOT_DETECT_ENABLE - if (!powerOn && was_on) { - overshoot_learn_ever_armed = false; - } - if (powerOn && !was_on) { - overshoot_learn_try_arm_cold("Power cb"); - } -#endif - } - - if (strcmp(esp_rmaker_param_get_name(param), "Temp Lock") == 0) - { - tempLock = val.val.b; - ESP_LOGI(TAG, "Temp Lock set to %s", tempLock ? "ON" : "OFF"); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion on time (s)") == 0) - { - preInfOnTime = val.val.i; - ESP_LOGI(TAG, "Pre-Infusion On Time: %d s", preInfOnTime); - } - - if (strcmp(esp_rmaker_param_get_name(param), "Pre-Infusion off time (s)") == 0) - { - preInfOffTime = val.val.i; - ESP_LOGI(TAG, "Pre-Infusion Off Time: %d s", preInfOffTime); - } - -#if OVERSHOOT_DETECT_ENABLE - if (strcmp(esp_rmaker_param_get_name(param), "Reset power trim") == 0) - { - if (val.val.b) { - overshoot_trim_stored = 0.f; - overshoot_learn_allowed = true; - overshoot_learn_ever_armed = false; - overshoot_learn_disarm_mask = 0u; - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; - overshoot_excursion_latched = false; - overshoot_excursion_pk = 0.f; - nvs_persist_overshoot_trim(); - ESP_LOGI(TAG, "Warmup trim reset (NVS cleared, learn re-armed if cold)"); - overshoot_disp_report(); - } - } -#endif - - return ESP_OK; -} - -/* Event handler for catching RainMaker events */ -static void event_handler(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) -{ - if (event_base == RMAKER_EVENT) { - switch (event_id) { - case RMAKER_EVENT_INIT_DONE: - ESP_LOGI(TAG, "RainMaker Initialised."); - break; - case RMAKER_EVENT_CLAIM_STARTED: - ESP_LOGI(TAG, "RainMaker Claim Started."); - break; - case RMAKER_EVENT_CLAIM_SUCCESSFUL: - ESP_LOGI(TAG, "RainMaker Claim Successful."); - break; - case RMAKER_EVENT_CLAIM_FAILED: - ESP_LOGI(TAG, "RainMaker Claim Failed."); - break; - case RMAKER_EVENT_LOCAL_CTRL_STARTED: - ESP_LOGI(TAG, "Local Control Started."); - break; - case RMAKER_EVENT_LOCAL_CTRL_STOPPED: - ESP_LOGI(TAG, "Local Control Stopped."); - break; - default: - ESP_LOGW(TAG, "Unhandled RainMaker Event: %"PRIi32, event_id); - } - } else if (event_base == RMAKER_COMMON_EVENT) { - switch (event_id) { - case RMAKER_EVENT_REBOOT: - ESP_LOGI(TAG, "Rebooting in %d seconds.", *((uint8_t *)event_data)); - break; - case RMAKER_EVENT_WIFI_RESET: - ESP_LOGI(TAG, "Wi-Fi credentials reset."); - break; - case RMAKER_EVENT_FACTORY_RESET: - ESP_LOGI(TAG, "Node reset to factory defaults."); - break; - case RMAKER_MQTT_EVENT_CONNECTED: - ESP_LOGI(TAG, "MQTT Connected."); - break; - case RMAKER_MQTT_EVENT_DISCONNECTED: - ESP_LOGI(TAG, "MQTT Disconnected."); - break; - case RMAKER_MQTT_EVENT_PUBLISHED: - ESP_LOGI(TAG, "MQTT Published. Msg id: %d.", *((int *)event_data)); - break; - default: - ESP_LOGW(TAG, "Unhandled RainMaker Common Event: %"PRIi32, event_id); - } - } else if (event_base == APP_WIFI_EVENT) { - switch (event_id) { - case APP_WIFI_EVENT_QR_DISPLAY: - ESP_LOGI(TAG, "Provisioning QR : %s", (char *)event_data); - break; - case APP_WIFI_EVENT_PROV_TIMEOUT: - ESP_LOGI(TAG, "Provisioning Timed Out. Please reboot."); - break; - case APP_WIFI_EVENT_PROV_RESTART: - ESP_LOGI(TAG, "Provisioning has restarted due to failures."); - break; - default: - ESP_LOGW(TAG, "Unhandled App Wi-Fi Event: %"PRIi32, event_id); - break; - } - } else if (event_base == RMAKER_OTA_EVENT) { - switch(event_id) { - case RMAKER_OTA_EVENT_STARTING: - ESP_LOGI(TAG, "Starting OTA."); - break; - case RMAKER_OTA_EVENT_IN_PROGRESS: - ESP_LOGI(TAG, "OTA is in progress."); - break; - case RMAKER_OTA_EVENT_SUCCESSFUL: - ESP_LOGI(TAG, "OTA successful."); - break; - case RMAKER_OTA_EVENT_FAILED: - ESP_LOGI(TAG, "OTA Failed."); - break; - case RMAKER_OTA_EVENT_REJECTED: - ESP_LOGI(TAG, "OTA Rejected."); - break; - case RMAKER_OTA_EVENT_DELAYED: - ESP_LOGI(TAG, "OTA Delayed."); - break; - case RMAKER_OTA_EVENT_REQ_FOR_REBOOT: - ESP_LOGI(TAG, "Firmware image downloaded. Please reboot your device to apply the upgrade."); - break; - default: - ESP_LOGW(TAG, "Unhandled OTA Event: %"PRIi32, event_id); - break; - } - } else { - ESP_LOGW(TAG, "Invalid event received!"); - } -} - -void dimInit(void) -{ - ptr_dimmer = createDimmer(TRIAC_1_GPIO, ZEROCROSS_GPIO); - begin(ptr_dimmer, NORMAL_MODE, ON, GRID_FREQ); - setPower(ptr_dimmer, 0); // set power to 0% - - ESP_LOGI(TAG, "Dimmer initialized"); -} - -void gpioConfig(void) -{ - /* Outputs: pump off, triac gate held low (dimmer driver re-configures GPIO33 later). */ - gpio_config(&gpio_outcfg); - gpio_set_level(GPIO_OUTPUT_IO_0, 0); - gpio_set_level(TRIAC_1_GPIO, 0); -} - -void nvsRead(void) -{ - nvs_handle_t nvs_handle; - esp_err_t err; - - err = nvs_open("storage", NVS_READWRITE, &nvs_handle); - - if (err != ESP_OK) - { - printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err)); - } - else - { - /* Read (NVS APIs use fixed-width types; not bool* / int*) */ - uint8_t u8 = 0; - nvs_get_u8(nvs_handle, "tempLock", &u8); - tempLock = u8 != 0; - nvs_get_i32(nvs_handle, "tempSetpoint", &tempSetpoint); - if (tempSetpoint < TEMP_SETPOINT_MIN) { - tempSetpoint = TEMP_SETPOINT_MIN; - } else if (tempSetpoint > TEMP_SETPOINT_MAX) { - tempSetpoint = TEMP_SETPOINT_MAX; - } - nvs_get_i32(nvs_handle, "brewTime", &brewTime); - nvs_get_i32(nvs_handle, "flushTime", &flushTime); - nvs_get_u8(nvs_handle, "preInfusion", &u8); - preInfusion = u8 != 0; - nvs_get_i32(nvs_handle, "preInfOnTime", &preInfOnTime); - nvs_get_i32(nvs_handle, "preInfOffTime", &preInfOffTime); - -#if OVERSHOOT_DETECT_ENABLE - nvs_read_overshoot_trim(nvs_handle); -#endif - - nvs_close(nvs_handle); - } -} - -void nvsWrite(void) -{ - nvs_handle_t nvs_handle; - esp_err_t err; - - err = nvs_open("storage", NVS_READWRITE, &nvs_handle); - - if (err != ESP_OK) - { - printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err)); - } - else - { - /* Write */ - ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "tempLock", (uint8_t)tempLock)); - ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "tempSetpoint", tempSetpoint)); - ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "brewTime", brewTime)); - ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "flushTime", flushTime)); - ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "preInfusion", (uint8_t)preInfusion)); - ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOnTime", preInfOnTime)); - ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOffTime", preInfOffTime)); - - /* trimMp is written only from nvs_persist_overshoot_trim() on learn commit. */ - - ESP_LOGI(TAG, "Committing updates in NVS ... "); - - ESP_ERROR_CHECK(nvs_commit(nvs_handle)); - - nvs_close(nvs_handle); - } -} - -#if OVERSHOOT_DETECT_ENABLE -/* Trim stored as milli-percent in NVS (int32); 100 = 0.1% */ -#define OVERSHOOT_TRIM_FRAC_TO_MPCT(f) ((int32_t)lroundf((f) * 100000.0f)) -#define OVERSHOOT_TRIM_MPCT_TO_FRAC(i) ((float)(i) * 0.00001f) - -static void nvs_read_overshoot_trim(nvs_handle_t h) -{ - int32_t mpct = 0; - if (nvs_get_i32(h, "trimMp", &mpct) == ESP_OK && mpct >= 0) { - overshoot_trim_stored = fminf(OVERSHOOT_TRIM_MPCT_TO_FRAC(mpct), OVERSHOOT_TRIM_FRAC_MAX); - ESP_LOGI(TAG, "Warmup trim loaded: -%.2f%%", (double)(overshoot_trim_stored * 100.0f)); - return; - } - - /* Legacy: "pkOsm" was cumulative °C; migrate once to "trimMp" then erase. */ - int32_t milli_c = 0; - if (nvs_get_i32(h, "pkOsm", &milli_c) == ESP_OK && milli_c > 0) { - const float cumulative_c = (float)milli_c * 0.001f; - overshoot_trim_stored = fminf(OVERSHOOT_TRIM_FRAC_PER_DEG * cumulative_c, - OVERSHOOT_TRIM_FRAC_MAX); - esp_err_t e = nvs_set_i32(h, "trimMp", - OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); - if (e == ESP_OK) { - nvs_erase_key(h, "pkOsm"); - nvs_commit(h); - } - ESP_LOGI(TAG, "Warmup trim migrated: pkOsm=%.2f°C -> -%.2f%%", - (double)cumulative_c, (double)(overshoot_trim_stored * 100.0f)); - return; - } - - overshoot_trim_stored = 0.f; -} - -static void nvs_persist_overshoot_trim(void) -{ - nvs_handle_t h; - if (nvs_open("storage", NVS_READWRITE, &h) != ESP_OK) { - return; - } - esp_err_t e = nvs_set_i32(h, "trimMp", OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); - if (e == ESP_OK) { - e = nvs_commit(h); - } - if (e != ESP_OK) { - ESP_LOGW(TAG, "trimMp NVS save failed: %s", esp_err_to_name(e)); - } - nvs_close(h); -} - -static void overshoot_apply_trim_to_control(int *p_control) -{ - if ((float)*p_control > (float)OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD) { - float cf = (float)*p_control * (1.f - overshoot_trim_stored); - *p_control = (int)(cf + 0.5f); - } -} - -static void overshoot_learn_set_disallowed(uint32_t reason_bits) -{ - overshoot_learn_disarm_mask |= reason_bits; - overshoot_learn_allowed = false; -} - -static void overshoot_learn_try_arm_cold(const char *site) -{ - if (!powerOn) { - return; - } - if (!temp_read_trusted) { - return; - } - if (tempCelsius > OVERSHOOT_COLD_START_MAX_C) { - return; - } - overshoot_learn_allowed = true; - overshoot_learn_ever_armed = true; - overshoot_learn_disarm_mask = 0u; - overshoot_excursion_latched = false; - overshoot_excursion_pk = 0.f; - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; - ESP_LOGI(TAG, "OS arm[%s] OK: cold start @ %.1f°C (trim pre-loaded -%.2f%%)", - site, (double)tempCelsius, (double)(overshoot_trim_stored * 100.0f)); -} - - -static void overshoot_peak_detector_update(void) -{ - if (!overshoot_learn_allowed) { - return; - } - if (temp_stuck_diag) { - return; - } - - const float temp_ok_max_threshold = (float)tempSetpoint + (float)TEMP_DELTA; - - if (tempCelsius > temp_ok_max_threshold) { - overshoot_detected = true; - overshoot_excursion_latched = true; - } - - if (!overshoot_detected) { - if (overshoot_soft_timer_start_us == 0ULL && tempCelsius >= (float)tempSetpoint) { - overshoot_soft_timer_start_us = getAbsTime1us(); - } else if (overshoot_soft_timer_start_us != 0ULL && - (getAbsTime1us() - overshoot_soft_timer_start_us) >= - (unsigned long long)SEC_TO_US(OVERSHOOT_SOFT_DISARM_SEC)) { - overshoot_learn_set_disallowed(OS_DISARM_SOFT_TIMEOUT); - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; - overshoot_excursion_latched = false; - overshoot_excursion_pk = 0.f; - ESP_LOGI(TAG, - "OS learn disarmed (soft): no temp above setpoint+%d°C within %.0fs", - TEMP_DELTA, (double)OVERSHOOT_SOFT_DISARM_SEC); - overshoot_disp_report(); - return; - } - } - - if (overshoot_excursion_latched && tempCelsius > (float)tempSetpoint) { - const float temp_above_setpoint_c = tempCelsius - (float)tempSetpoint; - overshoot_excursion_pk = fmaxf(overshoot_excursion_pk, temp_above_setpoint_c); - } - - if (tempCelsius <= (float)tempSetpoint) { - if (overshoot_excursion_latched) { - if (overshoot_excursion_pk > 0.01f) { - const float peak_c = overshoot_excursion_pk; - const float trim_add = OVERSHOOT_TRIM_FRAC_PER_DEG * peak_c; - const float trim_prev = overshoot_trim_stored; - overshoot_trim_stored = fminf(overshoot_trim_stored + trim_add, - OVERSHOOT_TRIM_FRAC_MAX); - ESP_LOGI(TAG, - "OS commit: peak +%.2f°C -> cut +%.2f%% (-%.2f%% -> -%.2f%%%s)", - (double)peak_c, - (double)(trim_add * 100.0f), - (double)(trim_prev * 100.0f), - (double)(overshoot_trim_stored * 100.0f), - (overshoot_trim_stored >= OVERSHOOT_TRIM_FRAC_MAX - 1e-6f) ? ", CAPPED" : ""); - nvs_persist_overshoot_trim(); - overshoot_learn_set_disallowed(OS_DISARM_COMMIT); - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; - } - overshoot_excursion_pk = 0.f; - } - overshoot_excursion_latched = false; - } -} - -static void overshoot_disp_format_str(void) -{ - const float cut_pct = overshoot_trim_stored * 100.0f; - if (overshoot_learn_allowed && overshoot_excursion_latched && overshoot_excursion_pk > 0.01f) { - snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), - "-%.1f%% +%.1f°C", (double)cut_pct, (double)overshoot_excursion_pk); - } else { - snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), "-%.1f%%", (double)cut_pct); - } -} - -static void overshoot_disp_report(void) -{ - if (!powerOn) { - return; - } - overshoot_disp_format_str(); - if (overshoot_disp_param) { - esp_err_t e = esp_rmaker_param_update_and_report(overshoot_disp_param, - esp_rmaker_str(s_overshoot_disp_str)); - if (e != ESP_OK) { - ESP_LOGW(TAG, "Overshoot display: %s", esp_err_to_name(e)); - } - } -} - -#endif - -static void temp_stuck_diag_update(int heating_demand_pct) -{ - const float t = tempCelsius; - - /* Reset session state on power off; stuck latch clears only when the reading moves. */ - if (!powerOn) { - temp_stuck_same_ct = 0u; - temp_stuck_warmup_start_us = 0ULL; - temp_sensor_responsive = false; - temp_stuck_prev_c = t; - return; - } - - if (temp_stuck_diag) { - if (temp_read_trusted && t > 0.f && t < STUCK_DIAG_CLEAR_MAX_C && - fabsf(t - temp_stuck_prev_c) >= STUCK_DIAG_TEMP_EPS) { - temp_stuck_diag = false; - temp_stuck_same_ct = 1u; - temp_stuck_prev_c = t; - temp_sensor_responsive = true; - ESP_LOGI(TAG, "Boiler temp stuck diag cleared (reading moved in range)"); - } - return; - } - - if (!temp_read_trusted || t <= 0.f || t >= STUCK_DIAG_CLEAR_MAX_C) { - temp_stuck_same_ct = 0u; - temp_stuck_prev_c = t; - return; - } - - if (heating_demand_pct < STUCK_DIAG_MIN_POWER_PCT) { - temp_stuck_same_ct = 0u; - temp_stuck_prev_c = t; - if (!temp_sensor_responsive) { - temp_stuck_warmup_start_us = 0ULL; - } - return; - } - - /* First heating sample pre-responsive: anchor and start warmup timer. */ - if (!temp_sensor_responsive && temp_stuck_warmup_start_us == 0ULL) { - temp_stuck_warmup_start_us = getAbsTime1us(); - temp_stuck_prev_c = t; - temp_stuck_same_ct = 0u; - return; - } - - if (fabsf(t - temp_stuck_prev_c) >= STUCK_DIAG_TEMP_EPS) { - if (!temp_sensor_responsive) { - ESP_LOGI(TAG, "Boiler temp sensor responsive (%.2f -> %.2f C)", - (double)temp_stuck_prev_c, (double)t); - } - temp_sensor_responsive = true; - temp_stuck_same_ct = 1u; - temp_stuck_prev_c = t; - return; - } - - /* Pre-responsive: skip counting until warmup grace elapses. */ - if (!temp_sensor_responsive) { - const unsigned long long warmup_elapsed = getAbsTime1us() - temp_stuck_warmup_start_us; - if (warmup_elapsed < (unsigned long long)SEC_TO_US(STUCK_DIAG_WARMUP_GRACE_SEC)) { - return; - } - } - - if (temp_stuck_same_ct < 255) { - temp_stuck_same_ct++; - } - if (temp_stuck_same_ct >= STUCK_DIAG_SAMPLES) { - temp_stuck_diag = true; - ESP_LOGW(TAG, - "Boiler temp stuck diag: demand >= %d%%, %d identical samples (~%.2f C, %s)", - STUCK_DIAG_MIN_POWER_PCT, STUCK_DIAG_SAMPLES, (double)t, - temp_sensor_responsive ? "post-movement" : "warmup grace elapsed"); - } -} - -static bool boiler_heater_holdoff(void) -{ - return !temp_read_trusted || (tempCelsius == 0.f) || (tempCelsius > (float)MAX_TEMP_THR) || - temp_stuck_diag; -} - -static void temp_status_line_report(void) -{ - const char *state; - if (tempRangeOk) { - state = "Ready"; - } else if (tempCelsius < (float)(tempSetpoint - TEMP_DELTA)) { - state = "Low"; - } else { - state = "High"; - } - snprintf(s_temp_line_str, sizeof(s_temp_line_str), "%.1f \xc2\xb7 %s", (double)tempCelsius, state); - if (primary) { - esp_err_t e = esp_rmaker_param_update_and_report(primary, esp_rmaker_str(s_temp_line_str)); - if (e != ESP_OK) { - ESP_LOGW(TAG, "Temperature line: %s", esp_err_to_name(e)); - } - } -} - -static void boiler_status_report(void) -{ - int cur_pri = 0; - const char *cur_msg = "Okay"; - - if (temp_stuck_diag) { - cur_pri = STATUS_PRI_STUCK; - cur_msg = "Temp sensor stuck"; - } else if (!temp_read_trusted) { - cur_pri = STATUS_PRI_UNTRUSTED; - cur_msg = "Temp not trusted"; - } else if (tempCelsius <= 0.f) { - cur_pri = STATUS_PRI_ZERO; - cur_msg = "Temp zero"; - } else if (tempCelsius > (float)MAX_TEMP_THR) { - cur_pri = STATUS_PRI_TOO_HIGH; - cur_msg = "Temp too high"; - } - - if (cur_pri > s_status_worst_pri) { - s_status_worst_pri = cur_pri; - snprintf(s_status_worst_str, sizeof(s_status_worst_str), "%s", cur_msg); - } - - if (cur_pri == 0) { - if (s_status_worst_pri > 0) { - if (s_status_ok_clean_reports + 1u >= (uint16_t)STATUS_WORST_CLEAR_OK_REPORTS) { - s_status_worst_pri = 0; - s_status_worst_str[0] = '\0'; - s_status_ok_clean_reports = 0; - snprintf(s_status_str, sizeof(s_status_str), "Okay"); - } else { - s_status_ok_clean_reports++; - snprintf(s_status_str, sizeof(s_status_str), "Okay (%s)", s_status_worst_str); - } - } else { - snprintf(s_status_str, sizeof(s_status_str), "Okay"); - s_status_ok_clean_reports = 0; - } - } else { - s_status_ok_clean_reports = 0; - if (s_status_worst_pri > cur_pri && s_status_worst_str[0] != '\0') { - snprintf(s_status_str, sizeof(s_status_str), "%s (%s)", cur_msg, s_status_worst_str); - } else { - snprintf(s_status_str, sizeof(s_status_str), "%s", cur_msg); - } - } - - if (status_param) { - esp_err_t e = esp_rmaker_param_update_and_report(status_param, esp_rmaker_str(s_status_str)); - if (e != ESP_OK) { - ESP_LOGW(TAG, "Status: %s", esp_err_to_name(e)); - } - } -} - -unsigned long long getAbsTime1us(void) -{ - unsigned long long timerVal = 0; - - gptimer_get_raw_count(freeRunTimer, &timerVal); - - return timerVal; -} - -static void temp_read_note_fault(uint32_t bit) -{ - temp_read_good_streak = 0; - temp_read_trusted = false; - const uint32_t prev = temp_read_fault_latch; - temp_read_fault_latch |= bit; - if (temp_read_fault_latch != prev) { - ESP_LOGW(TAG, "Boiler temp read fault (latch 0x%02" PRIx32 ")", temp_read_fault_latch); - } -} - -static void temp_read_note_good(float celsius) -{ - tempCelsius = celsius; - if (temp_read_good_streak < 255) { - temp_read_good_streak++; - } - if (temp_read_good_streak >= TEMP_READ_OK_STREAK) { - if (!temp_read_trusted) { - ESP_LOGI(TAG, "Boiler temp read now trusted"); - } - temp_read_trusted = true; - temp_read_fault_latch = 0; - } -} - -void spiComm(void) -{ - esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "SPI acquire bus failed: %s", esp_err_to_name(ret)); - temp_read_note_fault(TEMP_DIAG_SPI); - return; - } - ret = spi_device_transmit(spi, &tM); - spi_device_release_bus(spi); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "SPI transmit failed: %s", esp_err_to_name(ret)); - temp_read_note_fault(TEMP_DIAG_SPI); - return; - } - - const int16_t res = (int16_t) SPI_SWAP_DATA_RX(data, 16); - - if (res & (1 << 2)) { - ESP_LOGE(TAG, "Thermocouple open (MAX6675 fault bit)"); - temp_read_note_fault(TEMP_DIAG_OPEN_TC); - return; - } - - const int16_t shifted = (int16_t)(res >> 3); - const float c = (float)shifted * 0.25f; - if (c < TEMP_VALID_MIN_C || c > TEMP_VALID_MAX_C) { - ESP_LOGW(TAG, "Boiler temp out of range: %.2f C", (double)c); - temp_read_note_fault(TEMP_DIAG_RANGE); - return; - } - - temp_read_note_good(c); -} - -bool boiler_temp_is_trusted(void) -{ - return temp_read_trusted; -} - -uint32_t boiler_temp_fault_bits(void) -{ - return temp_read_fault_latch; -} - -void app_main() -{ - /* Pump + triac gate: outputs low before any timers, NVS, or network (pads not floating). */ - gpioConfig(); - - /* Init general purpose free running timer (1us resolution)*/ - ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &freeRunTimer)); - ESP_ERROR_CHECK(gptimer_enable(freeRunTimer)); - ESP_ERROR_CHECK(gptimer_start(freeRunTimer)); - - /* Initialize Application specific hardware drivers and - * set initial state. - */ - esp_rmaker_console_init(); - - /* Initialize NVS. */ - esp_err_t err = nvs_flash_init(); - - if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - err = nvs_flash_init(); - } - - ESP_ERROR_CHECK( err ); - - /* Reading app stored NVS data */ - nvsRead(); - - /* Initialize Wi-Fi. Note that, this should be called before esp_rmaker_node_init() - */ - app_wifi_init(); - - /* Register an event handler to catch RainMaker events */ - ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(APP_WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_OTA_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - - /* Initialize the ESP RainMaker Agent. - * Note that this should be called after app_wifi_init() but before app_wifi_start() - */ - esp_rmaker_config_t rainmaker_cfg = { - .enable_time_sync = false, - }; - - esp_rmaker_node_t *node = esp_rmaker_node_init(&rainmaker_cfg, "ESP RainMaker Device", "Espresso"); - - if (!node) { - ESP_LOGE(TAG, "Could not initialise node. Aborting!!!"); - vTaskDelay(5000/portTICK_PERIOD_MS); - abort(); - } - - /* Create a Switch device. - * You can optionally use the helper API esp_rmaker_espresso_device_create() to - * avoid writing code for adding the name and power parameters. - */ - espresso_device = esp_rmaker_device_create("Espresso", ESP_RMAKER_DEVICE_TEMP_SENSOR, NULL); - esp_rmaker_node_add_device(node, espresso_device); - - /* Add the write callback for the device. We aren't registering any read callback yet as - * it is for future use. - */ - esp_rmaker_device_add_cb(espresso_device, write_cb, NULL); - - /* No ESP_RMAKER_DEF_NAME_PARAM — saves a redundant name row in the phone UI. */ - - snprintf(s_temp_line_str, sizeof(s_temp_line_str), "--"); - primary = esp_rmaker_param_create("Temperature (\xc2\xb0""C)", NULL, esp_rmaker_str(s_temp_line_str), PROP_FLAG_READ); - esp_rmaker_param_add_ui_type(primary, ESP_RMAKER_UI_TEXT); - esp_rmaker_device_add_param(espresso_device, primary); - esp_rmaker_device_assign_primary_param(espresso_device, primary); - - /* Creating Power Up toggle switch */ - poweron_param = esp_rmaker_param_create("Power", NULL, esp_rmaker_bool(powerOn), PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(poweron_param, ESP_RMAKER_UI_TOGGLE); - esp_rmaker_device_add_param(espresso_device, poweron_param); - - /* Creating brew button */ - esp_rmaker_param_t *brewSig_param = esp_rmaker_param_create("Brew Signal", NULL, esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(brewSig_param, ESP_RMAKER_UI_TRIGGER); - esp_rmaker_device_add_param(espresso_device, brewSig_param); - - esp_rmaker_param_t *flush_param = esp_rmaker_param_create("Flush", NULL, esp_rmaker_bool(false), - PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(flush_param, ESP_RMAKER_UI_TRIGGER); - esp_rmaker_device_add_param(espresso_device, flush_param); - - /* Creating Temperature Lock toggle switch */ - esp_rmaker_param_t *templock_param = esp_rmaker_param_create("Temp Lock", NULL, esp_rmaker_bool(tempLock), PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(templock_param, ESP_RMAKER_UI_TOGGLE); - esp_rmaker_device_add_param(espresso_device, templock_param); - - /* Creating slider object */ - esp_rmaker_param_t *temp_param = esp_rmaker_param_create("Temperature setpoint (\xc2\xb0""C)", NULL, - esp_rmaker_int(tempSetpoint), PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(temp_param, ESP_RMAKER_UI_SLIDER); - esp_rmaker_param_add_bounds(temp_param, esp_rmaker_int(TEMP_SETPOINT_MIN), esp_rmaker_int(TEMP_SETPOINT_MAX), - esp_rmaker_int(1)); - esp_rmaker_device_add_param(espresso_device, temp_param); - - /* Creating slider object */ - esp_rmaker_param_t *brewtime_param = esp_rmaker_param_create("Brew time (s)", NULL, esp_rmaker_int(brewTime), - PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(brewtime_param, ESP_RMAKER_UI_SLIDER); - esp_rmaker_param_add_bounds(brewtime_param, esp_rmaker_int(6), esp_rmaker_int(20), esp_rmaker_int(1)); - esp_rmaker_device_add_param(espresso_device, brewtime_param); - - /* Creating Pre-Infusion toggle switch */ - esp_rmaker_param_t *preinf_param = esp_rmaker_param_create("Pre-Infusion", NULL, esp_rmaker_bool(preInfusion), PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(preinf_param, ESP_RMAKER_UI_TOGGLE); - esp_rmaker_device_add_param(espresso_device, preinf_param); - - /* Pre-Infusion On Time slider object */ - esp_rmaker_param_t *preinfOn_param = esp_rmaker_param_create("Pre-Infusion on time (s)", NULL, - esp_rmaker_int(preInfOnTime), PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(preinfOn_param, ESP_RMAKER_UI_SLIDER); - esp_rmaker_param_add_bounds(preinfOn_param, esp_rmaker_int(2), esp_rmaker_int(10), esp_rmaker_int(1)); - esp_rmaker_device_add_param(espresso_device, preinfOn_param); - - /* Pre-Infusion Off Time slider object */ - esp_rmaker_param_t *preinfOff_param = esp_rmaker_param_create("Pre-Infusion off time (s)", NULL, - esp_rmaker_int(preInfOffTime), PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(preinfOff_param, ESP_RMAKER_UI_SLIDER); - esp_rmaker_param_add_bounds(preinfOff_param, esp_rmaker_int(2), esp_rmaker_int(30), esp_rmaker_int(1)); - esp_rmaker_device_add_param(espresso_device, preinfOff_param); - - esp_rmaker_param_t *flushtime_param = esp_rmaker_param_create("Flush time (s)", NULL, esp_rmaker_int(flushTime), - PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(flushtime_param, ESP_RMAKER_UI_SLIDER); - esp_rmaker_param_add_bounds(flushtime_param, esp_rmaker_int(3), esp_rmaker_int(20), esp_rmaker_int(1)); - esp_rmaker_device_add_param(espresso_device, flushtime_param); - -#if OVERSHOOT_DETECT_ENABLE - /* Build initial string from loaded NVS trim (nvsRead ran earlier). */ - overshoot_disp_format_str(); - /* Stored cumulative trim (%). While an excursion is latched, appends live peak in °C. */ - overshoot_disp_param = esp_rmaker_param_create("Warmup power trim", NULL, esp_rmaker_str(s_overshoot_disp_str), - PROP_FLAG_READ); - esp_rmaker_param_add_ui_type(overshoot_disp_param, ESP_RMAKER_UI_TEXT); - esp_rmaker_device_add_param(espresso_device, overshoot_disp_param); - - esp_rmaker_param_t *reset_os_param = esp_rmaker_param_create("Reset power trim", NULL, esp_rmaker_bool(false), - PROP_FLAG_READ | PROP_FLAG_WRITE); - esp_rmaker_param_add_ui_type(reset_os_param, ESP_RMAKER_UI_TRIGGER); - esp_rmaker_device_add_param(espresso_device, reset_os_param); - -#endif - - snprintf(s_status_str, sizeof(s_status_str), "Okay"); - status_param = esp_rmaker_param_create("Status", NULL, esp_rmaker_str(s_status_str), PROP_FLAG_READ); - esp_rmaker_param_add_ui_type(status_param, ESP_RMAKER_UI_TEXT); - esp_rmaker_device_add_param(espresso_device, status_param); - - /* Enable OTA */ - esp_rmaker_ota_enable_default(); - - /* Enable timezone service which will be require for setting appropriate timezone - * from the phone apps for scheduling to work correctly. - * For more information on the various ways of setting timezone, please check - * https://rainmaker.espressif.com/docs/time-service.html - */ - esp_rmaker_timezone_service_enable(); - - /* Enable scheduling. */ - esp_rmaker_schedule_enable(); - - /* Enable Scenes */ - esp_rmaker_scenes_enable(); - - /* Enable Insights. Requires CONFIG_ESP_INSIGHTS_ENABLED=y */ - app_insights_enable(); - - /* Start the ESP RainMaker Agent */ - esp_rmaker_start(); - - err = app_wifi_set_custom_mfg_data(MFG_DATA_DEVICE_TYPE_SWITCH, MFG_DATA_DEVICE_SUBTYPE_SWITCH); - - /* Start the Wi-Fi. - * If the node is provisioned, it will start connection attempts, - * else, it will start Wi-Fi provisioning. The function will return - * after a connection has been successfully established - */ - err = app_wifi_start(POP_TYPE_RANDOM); - - if (err != ESP_OK) { - ESP_LOGE(TAG, "Could not start Wifi. Aborting!!!"); - vTaskDelay(5000/portTICK_PERIOD_MS); - abort(); - } - - /* Init SPI driver */ - spi = spi_init(); - - /* PID Controller init */ -#if (CONTROL_TYPE == PID) || (CONTROL_TYPE == PID_LOOKUP) - pidInit(); -#endif - - /* initialize heating element control (dimmer) */ - dimInit(); - - /* Start counting time to enter standby mode */ - powerOnTimer = getAbsTime1us(); - esp_rmaker_param_update_and_report(poweron_param, esp_rmaker_bool(powerOn)); - - ESP_LOGI(TAG, "Setpoint (C) | Temp (C) | pidOut (C) | control (%%) | power (%%)"); - - while(1) - { - TaskBackground(); - - count100ms++; - vTaskDelay(100/portTICK_PERIOD_MS); - } -} - -void TaskBackground(void) -{ - /* - * This simple scheduler is used because creating FreeRTOS tasks - * caused some issues with RainMaker. As it is a simple application - * it is enough for the implemented features. - */ - if (count100ms > 0) - { - if ((count100ms % 10) == 0) - { - /* Each 100 ms */ - Task100ms(); - } - - if ((count100ms % 5) == 0) - { - /* Each 500 ms*/ - Task500ms(); - - /* Task counter */ - count500ms++; - } - - if ((count100ms % 50) == 0) - { - /* Each 5000 ms */ - Task5000ms(); - } - } -} - -void Task100ms(void) -{ - /* empty for now */ -} - -void Task500ms(void) -{ - /* temperature acquisition */ - spiComm(); - - /* brewing program (and water pump) control */ - brewProgram(); - - /* heating element control */ - heatingControl(); -} - -void heatingControl(void) -{ -#if (CONTROL_TYPE == PID) - - pidOut = pidUpdate((float)tempSetpoint, tempCelsius); - control = (int)pidOut; - -#elif (CONTROL_TYPE == LOOKUP) - - delta = ((float)tempSetpoint - tempCelsius); - - float ir = indexRatio(deltaBkp, BKP_NUM, delta); - const int control_lookup = (int)interp1D(controlSet, BKP_NUM, ir); -#if OVERSHOOT_DETECT_ENABLE - if (temp_read_trusted && !pump_active) { - if (!overshoot_boot_temp_sampled) { - overshoot_boot_temp_sampled = true; - overshoot_learn_try_arm_cold("boot trusted"); - } - overshoot_peak_detector_update(); - } - int control_after_trim = control_lookup; - overshoot_apply_trim_to_control(&control_after_trim); - control = control_after_trim; -#else - control = control_lookup; -#endif - - /* reduce power to a factor by toggling power each task (idle only) */ - if (!pump_active && (delta > 0) && (delta <= TEMP_PWR_TOGGLE)) - { - powerToggle = (int)((count500ms % POWER_FACTOR) == 0); - control = (control * powerToggle); - } - -#elif (CONTROL_TYPE == PID_LOOKUP) - - pidOut = pidUpdate((float)tempSetpoint, tempCelsius); - - float ir = indexRatio(deltaBkp, BKP_NUM, pidOut); - control = (int)interp1D(controlSet, BKP_NUM, ir); - -#endif - - if (!pump_active) { - temp_stuck_diag_update(control); - } - - if (boiler_heater_holdoff()) { - control = 0; - ESP_LOGW(TAG, "Boiler heater held off: unsafe read, range, or stuck-temp diag."); - } - else if (brewState == BREW_POWER_OFF) - { - /* switch off heating element and remain in standby */ - control = 0; - } - else if (pump_active) - { - unsigned s = (unsigned)((getAbsTime1us() - pump_start_us) / 1000000ULL); - control = pumpOnHeatBuff[s]; - } - - setPower(ptr_dimmer, (int)control); - - - ESP_LOGI(TAG, "%d | %.2f | %.2f | %d | %d", tempSetpoint, tempCelsius, pidOut, control, getPower(ptr_dimmer)); -} - -void Task5000ms(void) -{ - if (powerOn) - { - /* only report data to app when device is on, to save MQTT budget */ - temp_status_line_report(); - boiler_status_report(); -#if OVERSHOOT_DETECT_ENABLE - overshoot_disp_report(); -#endif - } -} - -void brewProgram(void) -{ - /* - * This signal (enabled or disabled by the Temp Lock switch) will cancel a - * brewing attempt if temperature is not within an acceptable range (calibrated - * by TEMP_DELTA). - */ - if (!temp_read_trusted || temp_stuck_diag) { - tempRangeOk = false; - } else if ((tempCelsius < (tempSetpoint - TEMP_DELTA)) || (tempCelsius > (tempSetpoint + TEMP_DELTA))) - { - tempRangeOk = false; - } - else - { - tempRangeOk = true; - } - - /* Stop pump: power off; or user cleared Brew during brew; or cleared Flush during flush. */ - if (brewState != BREW_POWER_OFF) { - const bool stop_brew = !brewSignal && - (brewState == BREW_PREINF_ON || brewState == BREW_PREINF_OFF || - brewState == BREW_ON); - const bool stop_flush = !flushSignal && brewState == BREW_FLUSH; - - if (!powerOn || stop_brew || stop_flush) { - gpio_set_level(GPIO_OUTPUT_IO_0, 0); - pump_active = false; - brewSignal = false; - flushSignal = false; - brewState = BREW_OFF; - } - } - - /* - * Brewing phases and water pump control - */ - switch (brewState) - { - case BREW_OFF: - - if (brewSignal == true) - { - if ((tempRangeOk) || (tempLock == false)) - { - pumpTimer = getAbsTime1us(); - - if (preInfusion) - { - brewState = BREW_PREINF_ON; - } - else - { - brewState = BREW_ON; - } - pump_active = true; - pump_start_us = pumpTimer; - - /* reset power on timer */ - powerOnTimer = getAbsTime1us(); - } - else - { - /* abort brew attempt and reset button */ - ESP_LOGI(TAG, "Brew aborted! Setpoint temperature not reached"); - brewSignal = false; - flushSignal = false; - } - } - else if (flushSignal == true) - { - pumpTimer = getAbsTime1us(); - brewState = BREW_FLUSH; - pump_active = true; - pump_start_us = pumpTimer; - powerOnTimer = getAbsTime1us(); - ESP_LOGI(TAG, "Flush started"); - } - else - { - /* turn power off if Brew Signal is not pressed after POWERON_MIN (default 15 min) */ - if ((powerOn == false) || ((getAbsTime1us() - powerOnTimer) > POWERON_MIN)) - { - /* switch off heating element and remain in standby */ - brewState = BREW_POWER_OFF; - - powerOn = false; -#if OVERSHOOT_DETECT_ENABLE - overshoot_learn_ever_armed = false; -#endif - esp_rmaker_param_update_and_report(poweron_param, esp_rmaker_bool(powerOn)); - - ESP_LOGI(TAG, "Switching power OFF!"); - } - } - - break; - - case BREW_PREINF_ON: - - if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(preInfOnTime)) - { - gpio_set_level(GPIO_OUTPUT_IO_0, 1); - } - else - { - brewState = BREW_PREINF_OFF; - pump_active = false; - pumpTimer = getAbsTime1us(); - } - - break; - - case BREW_PREINF_OFF: - - if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(preInfOffTime)) - { - /* keep pump off */ - gpio_set_level(GPIO_OUTPUT_IO_0, 0); - } - else - { - brewState = BREW_ON; - pumpTimer = getAbsTime1us(); - pump_active = true; - pump_start_us = pumpTimer; - } - - break; - - case BREW_ON: - - if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(brewTime)) - { - /* brew */ - gpio_set_level(GPIO_OUTPUT_IO_0, 1); - } - else - { - gpio_set_level(GPIO_OUTPUT_IO_0, 0); - pump_active = false; - brewState = BREW_OFF; - - ESP_LOGI(TAG, "Brew cycle completed!"); - brewSignal = false; /* reset for next press */ - flushSignal = false; - - /* Save brewing parameters to NVS */ - nvsWrite(); - } - - break; - - case BREW_POWER_OFF: - - /* Standby: leaving on Power on — reset latches so a clean idle is guaranteed. */ - if (powerOn == true) - { - powerOnTimer = getAbsTime1us(); - brewState = BREW_OFF; - brewSignal = false; - flushSignal = false; -#if OVERSHOOT_DETECT_ENABLE - overshoot_learn_try_arm_cold("standby->on"); -#endif - - ESP_LOGI(TAG, "Switching power ON!"); - } - - break; - - case BREW_FLUSH: - - if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(flushTime)) { - gpio_set_level(GPIO_OUTPUT_IO_0, 1); - } else { - gpio_set_level(GPIO_OUTPUT_IO_0, 0); - pump_active = false; - brewState = BREW_OFF; - brewSignal = false; - flushSignal = false; - ESP_LOGI(TAG, "Flush completed"); - } - - break; - - default: - break; - } -} diff --git a/main/control.c b/main/control.c new file mode 100644 index 0000000..8cafb2d --- /dev/null +++ b/main/control.c @@ -0,0 +1,852 @@ +/* + Espresso — Control logic + + Owns all boiler temperature and brew-program state. Provides: + - Shared state variables (extern'd in control.h) + - SPI thermocouple acquisition (spiComm) + - Heating element control via lookup / PID (heatingControl) + - Brew and pre-infusion state machine (brewProgram) + - Temperature diagnostics and stuck-sensor detection + - Overshoot learn / trim (LOOKUP mode only) + - RainMaker status reporting helpers +*/ + +#include +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp_rmaker_core.h" +#include "esp_rmaker_standard_types.h" +#include "esp_rmaker_standard_params.h" +#include "esp32-triac-dimmer-driver.h" +#include "spi_mod.h" +#include "private.h" +#include "nvs.h" +#include "control.h" + +static const char *TAG = "espresso"; + +/***************************************************************************** + * Private configuration defines + *****************************************************************************/ + +#define BKP_NUM 10 + +#define SEC_TO_US(x) ((x) * 1000000ULL) + +#define BREW_OFF 0 +#define BREW_PREINF_ON 1 +#define BREW_PREINF_OFF 2 +#define BREW_ON 3 +#define BREW_POWER_OFF 4 +#define BREW_FLUSH 5 + +#define PUMP_ON_HEAT_BUFF_LEN 20 /* must be >= max brew time (s) */ + +#define TEMP_DELTA 2 /* ±°C window around setpoint for ready state */ +#define MAX_TEMP_THR 110 /* hard safety cut-off (°C) */ + +#define TEMP_READ_OK_STREAK 3 /* consecutive good samples before trusting */ +#define TEMP_VALID_MIN_C (-2.0f) +#define TEMP_VALID_MAX_C (125.0f) +#define TEMP_DIAG_SPI (1u << 0) +#define TEMP_DIAG_OPEN_TC (1u << 1) +#define TEMP_DIAG_RANGE (1u << 2) + +/* Stuck sensor: high demand but temp unchanged for STUCK_DIAG_SAMPLES reads */ +#define STUCK_DIAG_MIN_POWER_PCT 10 +#define STUCK_DIAG_SAMPLES 5 /* 2.5 s @ 500 ms cadence */ +#define STUCK_DIAG_TEMP_EPS 0.21f /* < half MAX6675 LSB (0.25 °C) */ +#define STUCK_DIAG_CLEAR_MAX_C 110.f +#define STUCK_DIAG_WARMUP_GRACE_SEC 10.f + +/* Power dithering around setpoint */ +#define POWER_50 2 +#define POWER_33 3 +#define POWER_25 4 +#define POWER_FACTOR POWER_50 +#define TEMP_PWR_TOGGLE 2 /* °C window where power is dithered */ + +#define POWERON_MIN (15 * SEC_TO_US(60)) /* standby timeout (15 min) */ + +/* Status severity priorities */ +#define STATUS_PRI_ZERO 25 +#define STATUS_PRI_UNTRUSTED 50 +#define STATUS_PRI_TOO_HIGH 75 +#define STATUS_PRI_STUCK 100 +#define STATUS_WORST_CLEAR_OK_REPORTS 12 /* Task5000ms ticks (~60 s) */ + +#if OVERSHOOT_DETECT_ENABLE +#define OVERSHOOT_SOFT_DISARM_SEC 90.f +#define OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD 30.0f +#define OVERSHOOT_COLD_START_MAX_C 60.0f +#define OS_DISARM_SOFT_TIMEOUT (1u << 0) +#define OS_DISARM_COMMIT (1u << 1) +#endif + +/***************************************************************************** + * Lookup tables + *****************************************************************************/ + +/* Temperature delta vs power setpoint breakpoints (LOOKUP mode) */ +static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; +static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 2, 15, 30, 60, 80}; + +/* + * Per-second heater power (%) while the pump is on. + * pumpTimer resets at BREW_PREINF_ON and again at BREW_ON. + * + * Phase 1 (~4 s) : free-flow before puck resistance builds + * Phase 2 (~4 s) : puck compressing, flow becoming restricted + * Phase 3 (~6 s) : puck at maximum compression + * Phase 4 (~6 s+): puck deteriorates, flow recovers + */ +static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { +/* s: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 */ + 100, 100, 100, 100, 80, 80, 40, 40, 30, 30, 30, 40, 40, 40, 40, 60, 60, 60, 60, 60 +}; + +/***************************************************************************** + * Exported shared state (extern'd in control.h) + *****************************************************************************/ + +spi_device_handle_t spi; +dimmertyp *ptr_dimmer; + +int32_t tempSetpoint = 96; +int32_t brewTime = 6; +int32_t flushTime = 5; +int32_t preInfOnTime = 3; +int32_t preInfOffTime = 10; +bool brewSignal = false; +bool flushSignal = false; +bool powerOn = false; +bool tempLock = true; +bool preInfusion = true; +float tempCelsius; + +#if OVERSHOOT_DETECT_ENABLE +float overshoot_trim_stored; +#endif + +/* RainMaker param handles — defined here, created by rainmaker_init() */ +esp_rmaker_param_t *primary; +esp_rmaker_param_t *status_param; +esp_rmaker_param_t *poweron_param; +#if OVERSHOOT_DETECT_ENABLE +esp_rmaker_param_t *overshoot_disp_param; +#endif + +/***************************************************************************** + * Private module state + *****************************************************************************/ + +static spi_transaction_t s_spi_txn = { + .tx_buffer = NULL, + .rx_buffer = NULL, /* set to &s_spi_data in spiComm() */ + .length = 16, + .rxlength = 16, +}; +static uint16_t s_spi_data; + +static float delta; +static float pidOut; +static int control; +static int powerToggle; +static unsigned long long pumpTimer; +static bool pump_active; +static unsigned long long powerOnTimer; +static int brewState = BREW_POWER_OFF; +static bool tempRangeOk; + +static bool temp_read_trusted; +static uint8_t temp_read_good_streak; +static uint32_t temp_read_fault_latch; + +static bool temp_stuck_diag; +static float temp_stuck_prev_c; +static uint8_t temp_stuck_same_ct; +static bool temp_sensor_responsive; +static unsigned long long temp_stuck_warmup_start_us; + +static char s_temp_line_str[40]; +static char s_status_str[96]; +static int s_status_worst_pri; +static char s_status_worst_str[48]; +static uint16_t s_status_ok_clean_reports; + +#if OVERSHOOT_DETECT_ENABLE +static char s_overshoot_disp_str[28]; +static float overshoot_excursion_pk; +static bool overshoot_excursion_latched; +static bool overshoot_detected; +static bool overshoot_learn_allowed; +static bool overshoot_learn_ever_armed; +static uint32_t overshoot_learn_disarm_mask; +static bool overshoot_boot_temp_sampled; +static unsigned long long overshoot_soft_timer_start_us; +#endif + +/***************************************************************************** + * Forward declarations (private) + *****************************************************************************/ + +static void temp_read_note_fault(uint32_t bit); +static void temp_read_note_good(float celsius); +static void temp_stuck_diag_update(int heating_demand_pct); +static bool boiler_heater_holdoff(void); + +#if OVERSHOOT_DETECT_ENABLE +static void overshoot_learn_try_arm_cold(const char *site); +static void overshoot_peak_detector_update(void); +static void overshoot_disp_format_str(void); +static void overshoot_apply_trim_to_control(int *p_control); +static void overshoot_learn_set_disallowed(uint32_t reason_bits); +#endif + +/***************************************************************************** + * Hardware init + *****************************************************************************/ + +void dimInit(void) +{ + ptr_dimmer = createDimmer(TRIAC_1_GPIO, ZEROCROSS_GPIO); + begin(ptr_dimmer, NORMAL_MODE, ON, GRID_FREQ); + setPower(ptr_dimmer, 0); + ESP_LOGI(TAG, "Dimmer initialized"); +} + +/***************************************************************************** + * Temperature read helpers + *****************************************************************************/ + +static void temp_read_note_fault(uint32_t bit) +{ + temp_read_good_streak = 0; + temp_read_trusted = false; + const uint32_t prev = temp_read_fault_latch; + temp_read_fault_latch |= bit; + if (temp_read_fault_latch != prev) { + ESP_LOGW(TAG, "Boiler temp read fault (latch 0x%02" PRIx32 ")", + temp_read_fault_latch); + } +} + +static void temp_read_note_good(float celsius) +{ + tempCelsius = celsius; + if (temp_read_good_streak < 255) { + temp_read_good_streak++; + } + if (temp_read_good_streak >= TEMP_READ_OK_STREAK) { + if (!temp_read_trusted) { + ESP_LOGI(TAG, "Boiler temp read now trusted"); + } + temp_read_trusted = true; + temp_read_fault_latch = 0; + } +} + +bool boiler_temp_is_trusted(void) +{ + return temp_read_trusted; +} + +uint32_t boiler_temp_fault_bits(void) +{ + return temp_read_fault_latch; +} + +/***************************************************************************** + * SPI thermocouple acquisition + *****************************************************************************/ + +void spiComm(void) +{ + s_spi_txn.rx_buffer = &s_spi_data; + + esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "SPI acquire bus failed: %s", esp_err_to_name(ret)); + temp_read_note_fault(TEMP_DIAG_SPI); + return; + } + + ret = spi_device_transmit(spi, &s_spi_txn); + spi_device_release_bus(spi); + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "SPI transmit failed: %s", esp_err_to_name(ret)); + temp_read_note_fault(TEMP_DIAG_SPI); + return; + } + + const int16_t res = (int16_t)SPI_SWAP_DATA_RX(s_spi_data, 16); + + if (res & (1 << 2)) { + ESP_LOGE(TAG, "Thermocouple open (MAX6675 fault bit)"); + temp_read_note_fault(TEMP_DIAG_OPEN_TC); + return; + } + + const int16_t shifted = (int16_t)(res >> 3); + const float c = (float)shifted * 0.25f; + + if (c < TEMP_VALID_MIN_C || c > TEMP_VALID_MAX_C) { + ESP_LOGW(TAG, "Boiler temp out of range: %.2f C", (double)c); + temp_read_note_fault(TEMP_DIAG_RANGE); + return; + } + + temp_read_note_good(c); +} + +/***************************************************************************** + * Stuck-sensor diagnostic + *****************************************************************************/ + +static void temp_stuck_diag_update(int heating_demand_pct) +{ + const float t = tempCelsius; + + /* Reset session state on power off; latch clears only when reading moves. */ + if (!powerOn) { + temp_stuck_same_ct = 0u; + temp_stuck_warmup_start_us = 0ULL; + temp_sensor_responsive = false; + temp_stuck_prev_c = t; + return; + } + + if (temp_stuck_diag) { + if (temp_read_trusted && t > 0.f && t < STUCK_DIAG_CLEAR_MAX_C && + fabsf(t - temp_stuck_prev_c) >= STUCK_DIAG_TEMP_EPS) { + temp_stuck_diag = false; + temp_stuck_same_ct = 1u; + temp_stuck_prev_c = t; + temp_sensor_responsive = true; + ESP_LOGI(TAG, "Boiler temp stuck diag cleared (reading moved in range)"); + } + return; + } + + if (!temp_read_trusted || t <= 0.f || t >= STUCK_DIAG_CLEAR_MAX_C) { + temp_stuck_same_ct = 0u; + temp_stuck_prev_c = t; + return; + } + + if (heating_demand_pct < STUCK_DIAG_MIN_POWER_PCT) { + temp_stuck_same_ct = 0u; + temp_stuck_prev_c = t; + if (!temp_sensor_responsive) { + temp_stuck_warmup_start_us = 0ULL; + } + return; + } + + /* First heating sample before sensor responsive: start warmup grace timer. */ + if (!temp_sensor_responsive && temp_stuck_warmup_start_us == 0ULL) { + temp_stuck_warmup_start_us = getAbsTime1us(); + temp_stuck_prev_c = t; + temp_stuck_same_ct = 0u; + return; + } + + if (fabsf(t - temp_stuck_prev_c) >= STUCK_DIAG_TEMP_EPS) { + if (!temp_sensor_responsive) { + ESP_LOGI(TAG, "Boiler temp sensor responsive (%.2f -> %.2f C)", + (double)temp_stuck_prev_c, (double)t); + } + temp_sensor_responsive = true; + temp_stuck_same_ct = 1u; + temp_stuck_prev_c = t; + return; + } + + /* Pre-responsive: skip counting until warmup grace elapses. */ + if (!temp_sensor_responsive) { + const unsigned long long elapsed = getAbsTime1us() - temp_stuck_warmup_start_us; + if (elapsed < (unsigned long long)SEC_TO_US(STUCK_DIAG_WARMUP_GRACE_SEC)) { + return; + } + } + + if (temp_stuck_same_ct < 255) { + temp_stuck_same_ct++; + } + if (temp_stuck_same_ct >= STUCK_DIAG_SAMPLES) { + temp_stuck_diag = true; + ESP_LOGW(TAG, + "Boiler temp stuck: demand >= %d%%, %d identical samples (~%.2f C, %s)", + STUCK_DIAG_MIN_POWER_PCT, STUCK_DIAG_SAMPLES, (double)t, + temp_sensor_responsive ? "post-movement" : "warmup grace elapsed"); + } +} + +static bool boiler_heater_holdoff(void) +{ + return !temp_read_trusted || (tempCelsius == 0.f) || + (tempCelsius > (float)MAX_TEMP_THR) || temp_stuck_diag; +} + +/***************************************************************************** + * RainMaker status reporting + *****************************************************************************/ + +void temp_status_line_report(void) +{ + const char *state; + + if (tempRangeOk) { + state = "Ready"; + } else if (tempCelsius < (float)(tempSetpoint - TEMP_DELTA)) { + state = "Low"; + } else { + state = "High"; + } + + snprintf(s_temp_line_str, sizeof(s_temp_line_str), + "%.1f \xc2\xb7 %s", (double)tempCelsius, state); + + if (primary) { + esp_err_t e = esp_rmaker_param_update_and_report(primary, + esp_rmaker_str(s_temp_line_str)); + if (e != ESP_OK) { + ESP_LOGW(TAG, "Temperature line report: %s", esp_err_to_name(e)); + } + } +} + +void boiler_status_report(void) +{ + int cur_pri = 0; + const char *cur_msg = "Okay"; + + if (temp_stuck_diag) { + cur_pri = STATUS_PRI_STUCK; + cur_msg = "Temp sensor stuck"; + } else if (!temp_read_trusted) { + cur_pri = STATUS_PRI_UNTRUSTED; + cur_msg = "Temp not trusted"; + } else if (tempCelsius <= 0.f) { + cur_pri = STATUS_PRI_ZERO; + cur_msg = "Temp zero"; + } else if (tempCelsius > (float)MAX_TEMP_THR) { + cur_pri = STATUS_PRI_TOO_HIGH; + cur_msg = "Temp too high"; + } + + if (cur_pri > s_status_worst_pri) { + s_status_worst_pri = cur_pri; + snprintf(s_status_worst_str, sizeof(s_status_worst_str), "%s", cur_msg); + } + + if (cur_pri == 0) { + if (s_status_worst_pri > 0) { + if (s_status_ok_clean_reports + 1u >= (uint16_t)STATUS_WORST_CLEAR_OK_REPORTS) { + s_status_worst_pri = 0; + s_status_worst_str[0] = '\0'; + s_status_ok_clean_reports = 0; + snprintf(s_status_str, sizeof(s_status_str), "Okay"); + } else { + s_status_ok_clean_reports++; + snprintf(s_status_str, sizeof(s_status_str), + "Okay (%s)", s_status_worst_str); + } + } else { + snprintf(s_status_str, sizeof(s_status_str), "Okay"); + s_status_ok_clean_reports = 0; + } + } else { + s_status_ok_clean_reports = 0; + if (s_status_worst_pri > cur_pri && s_status_worst_str[0] != '\0') { + snprintf(s_status_str, sizeof(s_status_str), + "%s (%s)", cur_msg, s_status_worst_str); + } else { + snprintf(s_status_str, sizeof(s_status_str), "%s", cur_msg); + } + } + + if (status_param) { + esp_err_t e = esp_rmaker_param_update_and_report(status_param, + esp_rmaker_str(s_status_str)); + if (e != ESP_OK) { + ESP_LOGW(TAG, "Status report: %s", esp_err_to_name(e)); + } + } +} + +/***************************************************************************** + * Overshoot learn / trim (LOOKUP mode only) + *****************************************************************************/ + +#if OVERSHOOT_DETECT_ENABLE + +static void overshoot_learn_set_disallowed(uint32_t reason_bits) +{ + overshoot_learn_disarm_mask |= reason_bits; + overshoot_learn_allowed = false; +} + +static void overshoot_learn_try_arm_cold(const char *site) +{ + if (!powerOn || !temp_read_trusted || tempCelsius > OVERSHOOT_COLD_START_MAX_C) { + return; + } + overshoot_learn_allowed = true; + overshoot_learn_ever_armed = true; + overshoot_learn_disarm_mask = 0u; + overshoot_excursion_latched = false; + overshoot_excursion_pk = 0.f; + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + ESP_LOGI(TAG, "OS arm[%s] OK: cold start @ %.1f°C (trim pre-loaded -%.2f%%)", + site, (double)tempCelsius, (double)(overshoot_trim_stored * 100.0f)); +} + +static void overshoot_peak_detector_update(void) +{ + if (!overshoot_learn_allowed || temp_stuck_diag) { + return; + } + + const float temp_ok_max = (float)tempSetpoint + (float)TEMP_DELTA; + + if (tempCelsius > temp_ok_max) { + overshoot_detected = true; + overshoot_excursion_latched = true; + } + + if (!overshoot_detected) { + if (overshoot_soft_timer_start_us == 0ULL && tempCelsius >= (float)tempSetpoint) { + overshoot_soft_timer_start_us = getAbsTime1us(); + } else if (overshoot_soft_timer_start_us != 0ULL && + (getAbsTime1us() - overshoot_soft_timer_start_us) >= + (unsigned long long)SEC_TO_US(OVERSHOOT_SOFT_DISARM_SEC)) { + overshoot_learn_set_disallowed(OS_DISARM_SOFT_TIMEOUT); + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + overshoot_excursion_latched = false; + overshoot_excursion_pk = 0.f; + ESP_LOGI(TAG, "OS learn disarmed (soft): no excursion above setpoint+%d°C " + "within %.0f s", TEMP_DELTA, (double)OVERSHOOT_SOFT_DISARM_SEC); + overshoot_disp_report(); + return; + } + } + + if (overshoot_excursion_latched && tempCelsius > (float)tempSetpoint) { + const float above = tempCelsius - (float)tempSetpoint; + overshoot_excursion_pk = fmaxf(overshoot_excursion_pk, above); + } + + if (tempCelsius <= (float)tempSetpoint) { + if (overshoot_excursion_latched && overshoot_excursion_pk > 0.01f) { + const float peak_c = overshoot_excursion_pk; + const float trim_add = OVERSHOOT_TRIM_FRAC_PER_DEG * peak_c; + const float trim_prev = overshoot_trim_stored; + overshoot_trim_stored = fminf(overshoot_trim_stored + trim_add, + OVERSHOOT_TRIM_FRAC_MAX); + ESP_LOGI(TAG, + "OS commit: peak +%.2f°C -> cut +%.2f%% (-%.2f%% -> -%.2f%%%s)", + (double)peak_c, + (double)(trim_add * 100.0f), + (double)(trim_prev * 100.0f), + (double)(overshoot_trim_stored * 100.0f), + (overshoot_trim_stored >= OVERSHOOT_TRIM_FRAC_MAX - 1e-6f) + ? ", CAPPED" : ""); + nvs_persist_overshoot_trim(); + overshoot_learn_set_disallowed(OS_DISARM_COMMIT); + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + overshoot_excursion_pk = 0.f; + } + overshoot_excursion_latched = false; + } +} + +static void overshoot_disp_format_str(void) +{ + const float cut_pct = overshoot_trim_stored * 100.0f; + + if (overshoot_learn_allowed && overshoot_excursion_latched && + overshoot_excursion_pk > 0.01f) { + snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), + "%s%.1f%% +%.1f°C", + cut_pct > 0.05f ? "-" : "", + (double)cut_pct, (double)overshoot_excursion_pk); + } else { + snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), + "%s%.1f%%", + cut_pct > 0.05f ? "-" : "", (double)cut_pct); + } +} + +void overshoot_disp_report(void) +{ + if (!powerOn) { + return; + } + overshoot_disp_format_str(); + if (overshoot_disp_param) { + esp_err_t e = esp_rmaker_param_update_and_report(overshoot_disp_param, + esp_rmaker_str(s_overshoot_disp_str)); + if (e != ESP_OK) { + ESP_LOGW(TAG, "Overshoot display report: %s", esp_err_to_name(e)); + } + } +} + +static void overshoot_apply_trim_to_control(int *p_control) +{ + if ((float)*p_control > (float)OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD) { + float cf = (float)*p_control * (1.f - overshoot_trim_stored); + *p_control = (int)(cf + 0.5f); + } +} + +/* Public wrappers called from write_cb (rainmaker.c) */ +void control_on_power_on(void) +{ + overshoot_learn_try_arm_cold("Power cb"); +} + +void control_on_power_off(void) +{ + overshoot_learn_ever_armed = false; +} + +void control_reset_overshoot_trim(void) +{ + overshoot_trim_stored = 0.f; + overshoot_learn_allowed = true; + overshoot_learn_ever_armed = false; + overshoot_learn_disarm_mask = 0u; + overshoot_soft_timer_start_us = 0; + overshoot_detected = false; + overshoot_excursion_latched = false; + overshoot_excursion_pk = 0.f; + nvs_persist_overshoot_trim(); + ESP_LOGI(TAG, "Warmup trim reset (NVS cleared, learn re-armed if cold)"); + overshoot_disp_report(); +} + +#else /* !OVERSHOOT_DETECT_ENABLE */ + +void control_on_power_on(void) {} +void control_on_power_off(void) {} + +#endif /* OVERSHOOT_DETECT_ENABLE */ + +/***************************************************************************** + * Heating element control + *****************************************************************************/ + +void heatingControl(void) +{ + static int tick = 0; + tick++; + +#if (CONTROL_TYPE == PID) + pidOut = pidUpdate((float)tempSetpoint, tempCelsius); + control = (int)pidOut; + +#elif (CONTROL_TYPE == LOOKUP) + delta = ((float)tempSetpoint - tempCelsius); + + float ir = indexRatio(deltaBkp, BKP_NUM, delta); + const int control_lookup = (int)interp1D(controlSet, BKP_NUM, ir); + +#if OVERSHOOT_DETECT_ENABLE + if (temp_read_trusted && !pump_active) { + if (!overshoot_boot_temp_sampled) { + overshoot_boot_temp_sampled = true; + overshoot_learn_try_arm_cold("boot trusted"); + } + overshoot_peak_detector_update(); + } + int control_after_trim = control_lookup; + overshoot_apply_trim_to_control(&control_after_trim); + control = control_after_trim; +#else + control = control_lookup; +#endif + + /* Dither power within TEMP_PWR_TOGGLE window around setpoint (idle only) */ + if (!pump_active && (delta > 0) && (delta <= TEMP_PWR_TOGGLE)) { + powerToggle = (int)((tick % POWER_FACTOR) == 0); + control = control * powerToggle; + } + +#elif (CONTROL_TYPE == PID_LOOKUP) + pidOut = pidUpdate((float)tempSetpoint, tempCelsius); + float ir = indexRatio(deltaBkp, BKP_NUM, pidOut); + control = (int)interp1D(controlSet, BKP_NUM, ir); +#endif + + if (!pump_active) { + temp_stuck_diag_update(control); + } + + if (boiler_heater_holdoff()) { + control = 0; + ESP_LOGW(TAG, "Boiler heater held off: unsafe read, range, or stuck-temp diag."); + } else if (brewState == BREW_POWER_OFF) { + control = 0; + } else if (pump_active) { + if (brewState == BREW_FLUSH) { + /* Flush: free-flow with no puck, run heater at full power. */ + control = 100; + } else { + /* Pre-infusion / brew: puck restricts flow, use calibrated buffer. */ + unsigned s = (unsigned)((getAbsTime1us() - pumpTimer) / 1000000ULL); + if (s >= PUMP_ON_HEAT_BUFF_LEN) { + s = PUMP_ON_HEAT_BUFF_LEN - 1; + } + control = pumpOnHeatBuff[s]; + } + } + + setPower(ptr_dimmer, control); + + ESP_LOGI(TAG, "%d | %.2f | %.2f | %d | %d", + tempSetpoint, tempCelsius, pidOut, control, getPower(ptr_dimmer)); +} + +/***************************************************************************** + * Brew and pre-infusion state machine + *****************************************************************************/ + +void brewProgram(void) +{ + /* Evaluate temperature readiness. */ + if (!temp_read_trusted || temp_stuck_diag) { + tempRangeOk = false; + } else if ((tempCelsius < (tempSetpoint - TEMP_DELTA)) || + (tempCelsius > (tempSetpoint + TEMP_DELTA))) { + tempRangeOk = false; + } else { + tempRangeOk = true; + } + + /* Unified stop condition: power off, or user cleared Brew/Flush signal. */ + if (brewState != BREW_POWER_OFF) { + const bool stop_brew = !brewSignal && + (brewState == BREW_PREINF_ON || + brewState == BREW_PREINF_OFF || + brewState == BREW_ON); + const bool stop_flush = !flushSignal && brewState == BREW_FLUSH; + + if (!powerOn || stop_brew || stop_flush) { + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + pump_active = false; + brewSignal = false; + flushSignal = false; + brewState = BREW_OFF; + } + } + + switch (brewState) { + case BREW_OFF: + if (brewSignal) { + if (tempRangeOk || !tempLock) { + pumpTimer = getAbsTime1us(); + brewState = preInfusion ? BREW_PREINF_ON : BREW_ON; + pump_active = true; + powerOnTimer = getAbsTime1us(); + } else { + ESP_LOGI(TAG, "Brew aborted! Setpoint temperature not reached"); + brewSignal = false; + flushSignal = false; + } + } else if (flushSignal) { + pumpTimer = getAbsTime1us(); + brewState = BREW_FLUSH; + pump_active = true; + powerOnTimer = getAbsTime1us(); + ESP_LOGI(TAG, "Flush started"); + } else { + /* Auto standby after POWERON_MIN or explicit power-off. */ + if (!powerOn || (getAbsTime1us() - powerOnTimer) > POWERON_MIN) { + brewState = BREW_POWER_OFF; + powerOn = false; +#if OVERSHOOT_DETECT_ENABLE + overshoot_learn_ever_armed = false; +#endif + esp_rmaker_param_update_and_report(poweron_param, + esp_rmaker_bool(powerOn)); + ESP_LOGI(TAG, "Switching power OFF!"); + } + } + break; + + case BREW_PREINF_ON: + if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(preInfOnTime)) { + gpio_set_level(GPIO_OUTPUT_IO_0, 1); + } else { + brewState = BREW_PREINF_OFF; + pump_active = false; + pumpTimer = getAbsTime1us(); + } + break; + + case BREW_PREINF_OFF: + if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(preInfOffTime)) { + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + } else { + brewState = BREW_ON; + pumpTimer = getAbsTime1us(); + pump_active = true; + } + break; + + case BREW_ON: + if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(brewTime)) { + gpio_set_level(GPIO_OUTPUT_IO_0, 1); + } else { + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + pump_active = false; + brewState = BREW_OFF; + brewSignal = false; + flushSignal = false; + ESP_LOGI(TAG, "Brew cycle completed!"); + nvsWrite(); + } + break; + + case BREW_POWER_OFF: + if (powerOn) { + powerOnTimer = getAbsTime1us(); + brewState = BREW_OFF; + brewSignal = false; + flushSignal = false; +#if OVERSHOOT_DETECT_ENABLE + overshoot_learn_try_arm_cold("standby->on"); +#endif + ESP_LOGI(TAG, "Switching power ON!"); + } + break; + + case BREW_FLUSH: + if ((getAbsTime1us() - pumpTimer) < SEC_TO_US(flushTime)) { + gpio_set_level(GPIO_OUTPUT_IO_0, 1); + } else { + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + pump_active = false; + brewState = BREW_OFF; + brewSignal = false; + flushSignal = false; + ESP_LOGI(TAG, "Flush completed"); + } + break; + + default: + break; + } +} diff --git a/main/control.h b/main/control.h new file mode 100644 index 0000000..ec7cc68 --- /dev/null +++ b/main/control.h @@ -0,0 +1,119 @@ +/* + Espresso — shared state, configuration, and control API + + This header is included by all modules that share control state or call + control functions. It owns the CONTROL_TYPE / OVERSHOOT_DETECT_ENABLE + compile-time knobs so every translation unit sees the same setting. +*/ +#pragma once + +#include +#include +#include +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp32-triac-dimmer-driver.h" +#include "esp_rmaker_core.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +/***************************************************************************** + * Control mode selection + *****************************************************************************/ +#define PID 0 +#define LOOKUP 1 +#define PID_LOOKUP 2 + +#define CONTROL_TYPE LOOKUP + +/* Overshoot learn/trim for LOOKUP only; on by default. */ +#if (CONTROL_TYPE == LOOKUP) && !defined(OVERSHOOT_DETECT_ENABLE) +#define OVERSHOOT_DETECT_ENABLE 1 +#endif + +/***************************************************************************** + * Hardware pin assignments + *****************************************************************************/ +#define GRID_FREQ 60 /* power grid frequency (Hz) */ +#define ZEROCROSS_GPIO GPIO_NUM_5 +#define TRIAC_1_GPIO GPIO_NUM_33 +#define GPIO_OUTPUT_IO_0 GPIO_NUM_4 /* water pump relay */ +#define GPIO_OUTPUT_PIN_SEL ((1ULL << GPIO_OUTPUT_IO_0) | (1ULL << TRIAC_1_GPIO)) + +/***************************************************************************** + * Temperature setpoint limits (shared with write_cb and NVS clamping) + *****************************************************************************/ +#define TEMP_SETPOINT_MIN 88 +#define TEMP_SETPOINT_MAX 96 + +/***************************************************************************** + * Overshoot NVS encoding helpers (also used by nvs.c) + *****************************************************************************/ +#if OVERSHOOT_DETECT_ENABLE +#define OVERSHOOT_TRIM_FRAC_MAX 0.20f +#define OVERSHOOT_TRIM_FRAC_PER_DEG 0.04f +#define OVERSHOOT_TRIM_FRAC_TO_MPCT(f) ((int32_t)lroundf((f) * 100000.0f)) +#define OVERSHOOT_TRIM_MPCT_TO_FRAC(i) ((float)(i) * 0.00001f) +#endif + +/***************************************************************************** + * Shared mutex — created in main.c before tasks_start() + *****************************************************************************/ +extern SemaphoreHandle_t g_state_mutex; + +/***************************************************************************** + * Hardware handles — initialised in main.c / control.c init functions + *****************************************************************************/ +extern spi_device_handle_t spi; +extern dimmertyp *ptr_dimmer; + +/***************************************************************************** + * Shared parameters + * Written from write_cb (rainmaker.c), read by control tasks. + * Callers must hold g_state_mutex when accessing from a task context. + *****************************************************************************/ +extern int32_t tempSetpoint; +extern int32_t brewTime; +extern int32_t flushTime; +extern int32_t preInfOnTime; +extern int32_t preInfOffTime; +extern bool brewSignal; +extern bool flushSignal; +extern bool powerOn; +extern bool tempLock; +extern bool preInfusion; +extern float tempCelsius; + +#if OVERSHOOT_DETECT_ENABLE +extern float overshoot_trim_stored; +#endif + +/***************************************************************************** + * RainMaker param handles + * Defined in rainmaker.c, used by control.c reporting functions. + *****************************************************************************/ +extern esp_rmaker_param_t *primary; +extern esp_rmaker_param_t *status_param; +extern esp_rmaker_param_t *poweron_param; +#if OVERSHOOT_DETECT_ENABLE +extern esp_rmaker_param_t *overshoot_disp_param; +#endif + +/***************************************************************************** + * Control API + *****************************************************************************/ +void dimInit(void); +void spiComm(void); +void heatingControl(void); +void brewProgram(void); +void temp_status_line_report(void); +void boiler_status_report(void); + +/* Called from write_cb when the Power parameter changes */ +void control_on_power_on(void); +void control_on_power_off(void); + +#if OVERSHOOT_DETECT_ENABLE +void overshoot_disp_report(void); +void control_reset_overshoot_trim(void); +#endif diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..9c1196b --- /dev/null +++ b/main/main.c @@ -0,0 +1,174 @@ +/* + Espresso PID/P Controller v2.0.1 + + This project implements a PID/P controller to effectively control the + temperature of an Espresso machine boiler for a stable setpoint. + A pre-infusion and brew program controls the water pump, and combined + with the temperature control delivers improved espresso extraction. + + https://github.com/raffarost/espresso + March 2024 – April 2026 + + Raffael Rostagno + raffael.rostagno@gmail.com +*/ + +#include +#include "esp_log.h" +#include "esp_err.h" +#include "esp_event.h" +#include "nvs_flash.h" +#include "driver/gpio.h" +#include "driver/gptimer.h" +#include "driver/spi_master.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_rmaker_core.h" +#include "esp_rmaker_ota.h" +#include "esp_rmaker_schedule.h" +#include "esp_rmaker_scenes.h" +#include "esp_rmaker_console.h" +#include "app_wifi.h" +#include "app_insights.h" +#include "spi_mod.h" +#include "private.h" +#include "control.h" +#include "nvs.h" +#include "tasks.h" + +static const char *TAG = "espresso"; + +/***************************************************************************** + * Free-running 1 µs timer + *****************************************************************************/ + +static gptimer_handle_t s_free_run_timer; + +static const gptimer_config_t s_timer_cfg = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, /* 1 MHz → 1 tick = 1 µs */ +}; + +unsigned long long getAbsTime1us(void) +{ + unsigned long long val = 0; + gptimer_get_raw_count(s_free_run_timer, &val); + return val; +} + +/***************************************************************************** + * GPIO configuration — call first so pads are never floating + *****************************************************************************/ + +void gpioConfig(void) +{ + const gpio_config_t out_cfg = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = GPIO_OUTPUT_PIN_SEL, + .pull_down_en = 0, + .pull_up_en = 0, + }; + gpio_config(&out_cfg); + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + gpio_set_level(TRIAC_1_GPIO, 0); +} + +/***************************************************************************** + * Shared mutex + *****************************************************************************/ + +SemaphoreHandle_t g_state_mutex; + +/***************************************************************************** + * Application entry point + * + * Init sequence (strict order — do NOT reorder): + * 1. gpioConfig() outputs low; pads never float + * 2. gptimer init 1 µs free-running clock + * 3. NVS flash init + nvsRead() restores persisted params + * 4. app_wifi_init() + * 5. rainmaker_init() registers events, creates device + params + * 6. RainMaker services OTA, timezone, schedule, scenes, insights + * 7. esp_rmaker_start() RainMaker agent running + * 8. app_wifi_start() BLOCKS until connected / provisioned + * 9. spi_init() SPI driver for thermocouple + * 10. pidInit() PID state (no-op for LOOKUP mode) + * 11. dimInit() triac dimmer driver + * 12. mutex create g_state_mutex + * 13. tasks_start() spawn application tasks — app begins here + *****************************************************************************/ + +void app_main(void) +{ + /* 1 — GPIO outputs low before anything else */ + gpioConfig(); + + /* 2 — Free-running 1 µs timer */ + ESP_ERROR_CHECK(gptimer_new_timer(&s_timer_cfg, &s_free_run_timer)); + ESP_ERROR_CHECK(gptimer_enable(s_free_run_timer)); + ESP_ERROR_CHECK(gptimer_start(s_free_run_timer)); + + /* 3 — NVS */ + esp_rmaker_console_init(); + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK(err); + nvsRead(); + + /* 4 — Wi-Fi (must precede esp_rmaker_node_init) */ + app_wifi_init(); + + /* 5 — RainMaker node + device + params */ + esp_rmaker_config_t rmaker_cfg = { + .enable_time_sync = false, + }; + esp_rmaker_node_t *node = esp_rmaker_node_init(&rmaker_cfg, + "ESP RainMaker Device", "Espresso"); + if (!node) { + ESP_LOGE(TAG, "Could not initialise RainMaker node. Aborting!"); + vTaskDelay(pdMS_TO_TICKS(5000)); + abort(); + } + rainmaker_init(node); /* registers events, creates device, params, write_cb */ + + /* 6 — RainMaker services */ + esp_rmaker_ota_enable_default(); + esp_rmaker_timezone_service_enable(); + esp_rmaker_schedule_enable(); + esp_rmaker_scenes_enable(); + app_insights_enable(); + + /* 7 — Start RainMaker agent */ + esp_rmaker_start(); + + /* 8 — Start Wi-Fi (blocks until connected or provisioned) */ + err = app_wifi_set_custom_mfg_data(MFG_DATA_DEVICE_TYPE_SWITCH, + MFG_DATA_DEVICE_SUBTYPE_SWITCH); + err = app_wifi_start(POP_TYPE_RANDOM); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Could not start Wi-Fi. Aborting!"); + vTaskDelay(pdMS_TO_TICKS(5000)); + abort(); + } + + /* 9-11 — Hardware drivers (after WiFi so SPI is not racing with init) */ + spi = spi_init(); + +#if (CONTROL_TYPE == PID) || (CONTROL_TYPE == PID_LOOKUP) + pidInit(); +#endif + + dimInit(); + + /* 12 — Shared mutex (before any task touches shared state) */ + g_state_mutex = xSemaphoreCreateMutex(); + + /* 13 — Spawn application tasks; app_main returns, scheduler takes over */ + ESP_LOGI(TAG, "Setpoint (C) | Temp (C) | pidOut | control (%%) | power (%%)"); + tasks_start(); +} diff --git a/main/functions.c b/main/math.c similarity index 56% rename from main/functions.c rename to main/math.c index cb9af67..a24850c 100644 --- a/main/functions.c +++ b/main/math.c @@ -6,22 +6,17 @@ float indexRatio(float vector[], int size, float input) int i; float out; - for (i=0; i < size; i++) - { + for (i = 0; i < size; i++) { /* search for first element greater than input */ - if (vector[i] > input) - { + if (vector[i] > input) { break; } } - if (i > 0) - { + if (i > 0) { /* we have two points for linear interpolation */ - out = ((i - 1) + ((input - vector[i-1]) / (vector[i] - vector[i-1]))); /* index + fraction */ - } - else - { + out = ((i - 1) + ((input - vector[i - 1]) / (vector[i] - vector[i - 1]))); /* index + fraction */ + } else { out = 0; /* first element is already bigger and we won't extrapolate */ } @@ -34,13 +29,10 @@ float interp1D(float vector[], int size, float ir) float ratio = (ir - index); float out; - if (index < (size-2)) - { - out = ((vector[index+1] - vector[index]) * ratio) + vector[index]; - } - else - { - out = vector[size-1]; /* saturate and return last cell */ + if (index < (size - 2)) { + out = ((vector[index + 1] - vector[index]) * ratio) + vector[index]; + } else { + out = vector[size - 1]; /* saturate and return last cell */ } return out; diff --git a/main/nvs.c b/main/nvs.c new file mode 100644 index 0000000..31625c1 --- /dev/null +++ b/main/nvs.c @@ -0,0 +1,147 @@ +/* + Espresso — NVS persistence + + Stores and restores user-configurable parameters and the overshoot + power-trim value that survives power cycles. +*/ + +#include +#include +#include "esp_log.h" +#include "esp_err.h" +/* IDF NVS API — included before our local nvs.h to avoid shadowing */ +#include "nvs_flash.h" +#include "control.h" + +static const char *TAG = "espresso"; + +#if OVERSHOOT_DETECT_ENABLE +static void nvs_read_overshoot_trim(nvs_handle_t h); +#endif + +/***************************************************************************** + * Public functions + *****************************************************************************/ + +void nvsRead(void) +{ + nvs_handle_t nvs_handle; + esp_err_t err = nvs_open("storage", NVS_READWRITE, &nvs_handle); + + if (err != ESP_OK) { + printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return; + } + + uint8_t u8 = 0; + nvs_get_u8(nvs_handle, "tempLock", &u8); + tempLock = u8 != 0; + + nvs_get_i32(nvs_handle, "tempSetpoint", &tempSetpoint); + if (tempSetpoint < TEMP_SETPOINT_MIN) { + tempSetpoint = TEMP_SETPOINT_MIN; + } else if (tempSetpoint > TEMP_SETPOINT_MAX) { + tempSetpoint = TEMP_SETPOINT_MAX; + } + + nvs_get_i32(nvs_handle, "brewTime", &brewTime); + nvs_get_i32(nvs_handle, "flushTime", &flushTime); + + u8 = 0; + nvs_get_u8(nvs_handle, "preInfusion", &u8); + preInfusion = u8 != 0; + + nvs_get_i32(nvs_handle, "preInfOnTime", &preInfOnTime); + nvs_get_i32(nvs_handle, "preInfOffTime", &preInfOffTime); + +#if OVERSHOOT_DETECT_ENABLE + nvs_read_overshoot_trim(nvs_handle); +#endif + + nvs_close(nvs_handle); +} + +void nvsWrite(void) +{ + nvs_handle_t nvs_handle; + esp_err_t err = nvs_open("storage", NVS_READWRITE, &nvs_handle); + + if (err != ESP_OK) { + printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return; + } + + ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "tempLock", (uint8_t)tempLock)); + ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "tempSetpoint", tempSetpoint)); + ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "brewTime", brewTime)); + ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "flushTime", flushTime)); + ESP_ERROR_CHECK(nvs_set_u8(nvs_handle, "preInfusion", (uint8_t)preInfusion)); + ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOnTime", preInfOnTime)); + ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOffTime", preInfOffTime)); + + /* overshoot trim is written only from nvs_persist_overshoot_trim() on commit */ + + ESP_LOGI(TAG, "Committing updates in NVS..."); + ESP_ERROR_CHECK(nvs_commit(nvs_handle)); + nvs_close(nvs_handle); +} + +/***************************************************************************** + * Overshoot trim NVS helpers + *****************************************************************************/ + +#if OVERSHOOT_DETECT_ENABLE + +/* Trim stored as milli-percent in NVS (int32); 100 = 0.1% */ +static void nvs_read_overshoot_trim(nvs_handle_t h) +{ + int32_t mpct = 0; + + if (nvs_get_i32(h, "trimMp", &mpct) == ESP_OK && mpct >= 0) { + overshoot_trim_stored = fminf(OVERSHOOT_TRIM_MPCT_TO_FRAC(mpct), + OVERSHOOT_TRIM_FRAC_MAX); + ESP_LOGI(TAG, "Warmup trim loaded: -%.2f%%", + (double)(overshoot_trim_stored * 100.0f)); + return; + } + + /* Legacy: "pkOsm" stored cumulative °C; migrate once to "trimMp" then erase. */ + int32_t milli_c = 0; + if (nvs_get_i32(h, "pkOsm", &milli_c) == ESP_OK && milli_c > 0) { + const float cumulative_c = (float)milli_c * 0.001f; + overshoot_trim_stored = fminf(OVERSHOOT_TRIM_FRAC_PER_DEG * cumulative_c, + OVERSHOOT_TRIM_FRAC_MAX); + esp_err_t e = nvs_set_i32(h, "trimMp", + OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); + if (e == ESP_OK) { + nvs_erase_key(h, "pkOsm"); + nvs_commit(h); + } + ESP_LOGI(TAG, "Warmup trim migrated: pkOsm=%.2f°C -> -%.2f%%", + (double)cumulative_c, (double)(overshoot_trim_stored * 100.0f)); + return; + } + + overshoot_trim_stored = 0.f; +} + +void nvs_persist_overshoot_trim(void) +{ + nvs_handle_t h; + + if (nvs_open("storage", NVS_READWRITE, &h) != ESP_OK) { + return; + } + + esp_err_t e = nvs_set_i32(h, "trimMp", + OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); + if (e == ESP_OK) { + e = nvs_commit(h); + } + if (e != ESP_OK) { + ESP_LOGW(TAG, "trimMp NVS save failed: %s", esp_err_to_name(e)); + } + nvs_close(h); +} + +#endif /* OVERSHOOT_DETECT_ENABLE */ diff --git a/main/nvs.h b/main/nvs.h new file mode 100644 index 0000000..41bab11 --- /dev/null +++ b/main/nvs.h @@ -0,0 +1,13 @@ +/* + Espresso — NVS persistence API +*/ +#pragma once + +#include "control.h" /* for OVERSHOOT_DETECT_ENABLE */ + +void nvsRead(void); +void nvsWrite(void); + +#if OVERSHOOT_DETECT_ENABLE +void nvs_persist_overshoot_trim(void); +#endif diff --git a/main/pid.c b/main/pid.c new file mode 100644 index 0000000..1da3776 --- /dev/null +++ b/main/pid.c @@ -0,0 +1,35 @@ +/* + Espresso — PID controller +*/ + +#include +#include +#include "PID.h" + +#define PID_KP 4.0f +#define PID_KI 2.0f +#define PID_KD 1.0f +#define PID_TAU 0.02f +#define PID_LIM_MIN 0.0f +#define PID_LIM_MAX 100.0f +#define PID_LIM_MIN_INT (-5.0f) +#define PID_LIM_MAX_INT 5.0f +#define SAMPLE_TIME_S 0.5f + +PIDController pid = { PID_KP, PID_KI, PID_KD, + PID_TAU, + PID_LIM_MIN, PID_LIM_MAX, + PID_LIM_MIN_INT, PID_LIM_MAX_INT, + SAMPLE_TIME_S, + 0, 0, 0, 0, 0 }; + +void pidInit(void) +{ + PIDController_Init(&pid); +} + +float pidUpdate(float setpoint, float measurement) +{ + PIDController_Update(&pid, setpoint, measurement); + return pid.out; +} diff --git a/main/app_priv.h b/main/private.h similarity index 51% rename from main/app_priv.h rename to main/private.h index c558626..d128e18 100644 --- a/main/app_priv.h +++ b/main/private.h @@ -1,13 +1,10 @@ /* - Include file only - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. + Espresso — internal shared declarations */ #pragma once #include #include +#include "esp_rmaker_core.h" extern void pidInit(void); extern float pidUpdate(float setpoint, float measurement); @@ -18,7 +15,7 @@ extern float indexRatio(float vector[], int size, float input); extern float interp1D(float vector[], int size, float ir); extern unsigned long long getAbsTime1us(void); -/** Boiler MAX6675 path: true after several consecutive good samples; false on any SPI/open/range fault. */ +void rainmaker_init(esp_rmaker_node_t *node); + bool boiler_temp_is_trusted(void); -/** Latched fault bits (TEMP_DIAG_*); cleared when read becomes fully trusted again. */ uint32_t boiler_temp_fault_bits(void); diff --git a/main/rainmaker.c b/main/rainmaker.c new file mode 100644 index 0000000..08735d9 --- /dev/null +++ b/main/rainmaker.c @@ -0,0 +1,350 @@ +/* + Espresso — ESP RainMaker integration + + Owns the RainMaker device, all parameter handles, the write callback, and + the event handler. rainmaker_init() must be called after + esp_rmaker_node_init() but before esp_rmaker_start(). +*/ + +#include +#include +#include "esp_log.h" +#include "esp_rmaker_core.h" +#include "esp_rmaker_standard_types.h" +#include "esp_rmaker_standard_params.h" +#include "esp_rmaker_standard_devices.h" +#include "esp_rmaker_ota.h" +#include "esp_rmaker_common_events.h" +#include "app_wifi.h" +#include "control.h" + +static const char *TAG = "espresso"; + +static esp_rmaker_device_t *espresso_device; + +/***************************************************************************** + * Write callback — called from RainMaker's MQTT task + *****************************************************************************/ + +static esp_err_t write_cb(const esp_rmaker_device_t *device, + const esp_rmaker_param_t *param, + const esp_rmaker_param_val_t val, + void *priv_data, + esp_rmaker_write_ctx_t *ctx) +{ + if (ctx) { + ESP_LOGI(TAG, "Received write request via: %s", + esp_rmaker_device_cb_src_to_str(ctx->src)); + } + + const char *name = esp_rmaker_param_get_name(param); + + /* Take mutex with a short timeout; if the control task is busy, don't stall MQTT. */ + if (xSemaphoreTake(g_state_mutex, pdMS_TO_TICKS(10)) != pdTRUE) { + ESP_LOGW(TAG, "write_cb: could not acquire mutex (param: %s)", name); + return ESP_OK; + } + + if (strcmp(name, "Temperature setpoint (\xc2\xb0""C)") == 0) { + int32_t sp = val.val.i; + if (sp < TEMP_SETPOINT_MIN) { + sp = TEMP_SETPOINT_MIN; + } else if (sp > TEMP_SETPOINT_MAX) { + sp = TEMP_SETPOINT_MAX; + } + tempSetpoint = sp; + ESP_LOGI(TAG, "New temperature setpoint: %d", tempSetpoint); + + } else if (strcmp(name, "Brew time (s)") == 0) { + brewTime = val.val.i; + ESP_LOGI(TAG, "Brew time: %d s", brewTime); + + } else if (strcmp(name, "Flush time (s)") == 0) { + flushTime = val.val.i; + ESP_LOGI(TAG, "Flush time: %d s", flushTime); + + } else if (strcmp(name, "Brew Signal") == 0) { + brewSignal = val.val.b ? !brewSignal : brewSignal; + ESP_LOGI(TAG, "Brew %s!", brewSignal ? "start" : "stop"); + + } else if (strcmp(name, "Flush") == 0) { + flushSignal = val.val.b ? !flushSignal : flushSignal; + ESP_LOGI(TAG, "Flush %s!", flushSignal ? "on" : "off"); + + } else if (strcmp(name, "Pre-Infusion") == 0) { + preInfusion = val.val.b; + ESP_LOGI(TAG, "Pre-Infusion: %s", preInfusion ? "ON" : "OFF"); + + } else if (strcmp(name, "Power") == 0) { + const bool was_on = powerOn; + powerOn = val.val.b; + ESP_LOGI(TAG, "Power: %s", powerOn ? "ON" : "OFF"); + if (!powerOn && was_on) { + control_on_power_off(); + } else if (powerOn && !was_on) { + control_on_power_on(); + } + + } else if (strcmp(name, "Temp Lock") == 0) { + tempLock = val.val.b; + ESP_LOGI(TAG, "Temp Lock: %s", tempLock ? "ON" : "OFF"); + + } else if (strcmp(name, "Pre-Infusion on time (s)") == 0) { + preInfOnTime = val.val.i; + ESP_LOGI(TAG, "Pre-Infusion on time: %d s", preInfOnTime); + + } else if (strcmp(name, "Pre-Infusion off time (s)") == 0) { + preInfOffTime = val.val.i; + ESP_LOGI(TAG, "Pre-Infusion off time: %d s", preInfOffTime); + +#if OVERSHOOT_DETECT_ENABLE + } else if (strcmp(name, "Reset power trim") == 0) { + if (val.val.b) { + control_reset_overshoot_trim(); + } +#endif + } + + xSemaphoreGive(g_state_mutex); + return ESP_OK; +} + +/***************************************************************************** + * Event handler + *****************************************************************************/ + +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == RMAKER_EVENT) { + switch (event_id) { + case RMAKER_EVENT_INIT_DONE: + ESP_LOGI(TAG, "RainMaker initialised."); + break; + case RMAKER_EVENT_CLAIM_STARTED: + ESP_LOGI(TAG, "RainMaker claim started."); + break; + case RMAKER_EVENT_CLAIM_SUCCESSFUL: + ESP_LOGI(TAG, "RainMaker claim successful."); + break; + case RMAKER_EVENT_CLAIM_FAILED: + ESP_LOGI(TAG, "RainMaker claim failed."); + break; + case RMAKER_EVENT_LOCAL_CTRL_STARTED: + ESP_LOGI(TAG, "Local control started."); + break; + case RMAKER_EVENT_LOCAL_CTRL_STOPPED: + ESP_LOGI(TAG, "Local control stopped."); + break; + default: + ESP_LOGW(TAG, "Unhandled RainMaker event: %" PRIi32, event_id); + } + } else if (event_base == RMAKER_COMMON_EVENT) { + switch (event_id) { + case RMAKER_EVENT_REBOOT: + ESP_LOGI(TAG, "Rebooting in %d seconds.", *((uint8_t *)event_data)); + break; + case RMAKER_EVENT_WIFI_RESET: + ESP_LOGI(TAG, "Wi-Fi credentials reset."); + break; + case RMAKER_EVENT_FACTORY_RESET: + ESP_LOGI(TAG, "Node reset to factory defaults."); + break; + case RMAKER_MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT connected."); + break; + case RMAKER_MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT disconnected."); + break; + case RMAKER_MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT published. Msg id: %d.", *((int *)event_data)); + break; + default: + ESP_LOGW(TAG, "Unhandled RainMaker common event: %" PRIi32, event_id); + } + } else if (event_base == APP_WIFI_EVENT) { + switch (event_id) { + case APP_WIFI_EVENT_QR_DISPLAY: + ESP_LOGI(TAG, "Provisioning QR: %s", (char *)event_data); + break; + case APP_WIFI_EVENT_PROV_TIMEOUT: + ESP_LOGI(TAG, "Provisioning timed out. Please reboot."); + break; + case APP_WIFI_EVENT_PROV_RESTART: + ESP_LOGI(TAG, "Provisioning restarted due to failures."); + break; + default: + ESP_LOGW(TAG, "Unhandled Wi-Fi event: %" PRIi32, event_id); + } + } else if (event_base == RMAKER_OTA_EVENT) { + switch (event_id) { + case RMAKER_OTA_EVENT_STARTING: + ESP_LOGI(TAG, "Starting OTA."); + break; + case RMAKER_OTA_EVENT_IN_PROGRESS: + ESP_LOGI(TAG, "OTA in progress."); + break; + case RMAKER_OTA_EVENT_SUCCESSFUL: + ESP_LOGI(TAG, "OTA successful."); + break; + case RMAKER_OTA_EVENT_FAILED: + ESP_LOGI(TAG, "OTA failed."); + break; + case RMAKER_OTA_EVENT_REJECTED: + ESP_LOGI(TAG, "OTA rejected."); + break; + case RMAKER_OTA_EVENT_DELAYED: + ESP_LOGI(TAG, "OTA delayed."); + break; + case RMAKER_OTA_EVENT_REQ_FOR_REBOOT: + ESP_LOGI(TAG, "Firmware downloaded. Reboot to apply."); + break; + default: + ESP_LOGW(TAG, "Unhandled OTA event: %" PRIi32, event_id); + } + } else { + ESP_LOGW(TAG, "Invalid event received!"); + } +} + +/***************************************************************************** + * Public init — call after esp_rmaker_node_init(), before esp_rmaker_start() + *****************************************************************************/ + +void rainmaker_init(esp_rmaker_node_t *node) +{ + static char temp_line_str[40]; + static char status_str[96]; + + /* Register event handlers */ + ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_EVENT, ESP_EVENT_ANY_ID, + &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, + &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(APP_WIFI_EVENT, ESP_EVENT_ANY_ID, + &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_OTA_EVENT, ESP_EVENT_ANY_ID, + &event_handler, NULL)); + + /* Create device */ + espresso_device = esp_rmaker_device_create("Espresso", + ESP_RMAKER_DEVICE_TEMP_SENSOR, NULL); + esp_rmaker_node_add_device(node, espresso_device); + esp_rmaker_device_add_cb(espresso_device, write_cb, NULL); + + /* Temperature display (primary param) */ + snprintf(temp_line_str, sizeof(temp_line_str), "--"); + primary = esp_rmaker_param_create("Temperature (\xc2\xb0""C)", NULL, + esp_rmaker_str(temp_line_str), PROP_FLAG_READ); + esp_rmaker_param_add_ui_type(primary, ESP_RMAKER_UI_TEXT); + esp_rmaker_device_add_param(espresso_device, primary); + esp_rmaker_device_assign_primary_param(espresso_device, primary); + + /* Power toggle */ + poweron_param = esp_rmaker_param_create("Power", NULL, + esp_rmaker_bool(powerOn), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(poweron_param, ESP_RMAKER_UI_TOGGLE); + esp_rmaker_device_add_param(espresso_device, poweron_param); + + /* Brew trigger */ + esp_rmaker_param_t *p; + p = esp_rmaker_param_create("Brew Signal", NULL, + esp_rmaker_bool(false), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_TRIGGER); + esp_rmaker_device_add_param(espresso_device, p); + + /* Flush trigger */ + p = esp_rmaker_param_create("Flush", NULL, + esp_rmaker_bool(false), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_TRIGGER); + esp_rmaker_device_add_param(espresso_device, p); + + /* Temp Lock toggle */ + p = esp_rmaker_param_create("Temp Lock", NULL, + esp_rmaker_bool(tempLock), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_TOGGLE); + esp_rmaker_device_add_param(espresso_device, p); + + /* Temperature setpoint slider */ + p = esp_rmaker_param_create("Temperature setpoint (\xc2\xb0""C)", NULL, + esp_rmaker_int(tempSetpoint), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_SLIDER); + esp_rmaker_param_add_bounds(p, esp_rmaker_int(TEMP_SETPOINT_MIN), + esp_rmaker_int(TEMP_SETPOINT_MAX), esp_rmaker_int(1)); + esp_rmaker_device_add_param(espresso_device, p); + + /* Brew time slider */ + p = esp_rmaker_param_create("Brew time (s)", NULL, + esp_rmaker_int(brewTime), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_SLIDER); + esp_rmaker_param_add_bounds(p, esp_rmaker_int(6), esp_rmaker_int(20), + esp_rmaker_int(1)); + esp_rmaker_device_add_param(espresso_device, p); + + /* Pre-Infusion toggle */ + p = esp_rmaker_param_create("Pre-Infusion", NULL, + esp_rmaker_bool(preInfusion), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_TOGGLE); + esp_rmaker_device_add_param(espresso_device, p); + + /* Pre-Infusion on time slider */ + p = esp_rmaker_param_create("Pre-Infusion on time (s)", NULL, + esp_rmaker_int(preInfOnTime), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_SLIDER); + esp_rmaker_param_add_bounds(p, esp_rmaker_int(2), esp_rmaker_int(10), + esp_rmaker_int(1)); + esp_rmaker_device_add_param(espresso_device, p); + + /* Pre-Infusion off time slider */ + p = esp_rmaker_param_create("Pre-Infusion off time (s)", NULL, + esp_rmaker_int(preInfOffTime), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_SLIDER); + esp_rmaker_param_add_bounds(p, esp_rmaker_int(2), esp_rmaker_int(30), + esp_rmaker_int(1)); + esp_rmaker_device_add_param(espresso_device, p); + + /* Flush time slider */ + p = esp_rmaker_param_create("Flush time (s)", NULL, + esp_rmaker_int(flushTime), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_SLIDER); + esp_rmaker_param_add_bounds(p, esp_rmaker_int(3), esp_rmaker_int(10), + esp_rmaker_int(1)); + esp_rmaker_device_add_param(espresso_device, p); + +#if OVERSHOOT_DETECT_ENABLE + /* Warmup power trim display */ + static char os_disp_str[28]; + snprintf(os_disp_str, sizeof(os_disp_str), "%.1f%%", + (double)(overshoot_trim_stored * 100.0f)); + overshoot_disp_param = esp_rmaker_param_create("Warmup power trim", NULL, + esp_rmaker_str(os_disp_str), + PROP_FLAG_READ); + esp_rmaker_param_add_ui_type(overshoot_disp_param, ESP_RMAKER_UI_TEXT); + esp_rmaker_device_add_param(espresso_device, overshoot_disp_param); + + /* Reset trim trigger */ + p = esp_rmaker_param_create("Reset power trim", NULL, + esp_rmaker_bool(false), + PROP_FLAG_READ | PROP_FLAG_WRITE); + esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_TRIGGER); + esp_rmaker_device_add_param(espresso_device, p); +#endif + + /* Status text */ + snprintf(status_str, sizeof(status_str), "Okay"); + status_param = esp_rmaker_param_create("Status", NULL, + esp_rmaker_str(status_str), + PROP_FLAG_READ); + esp_rmaker_param_add_ui_type(status_param, ESP_RMAKER_UI_TEXT); + esp_rmaker_device_add_param(espresso_device, status_param); +} diff --git a/main/tasks.c b/main/tasks.c new file mode 100644 index 0000000..2889427 --- /dev/null +++ b/main/tasks.c @@ -0,0 +1,84 @@ +/* + Espresso — RTOS application tasks + + task_500ms: temperature acquisition, brew state machine, heating control. + task_5000ms: RainMaker status reporting. + + Both tasks are spawned by tasks_start() after all hardware and network + initialisation is complete (called as the last step of app_main). +*/ + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "control.h" +#include "tasks.h" + +static const char *TAG = "espresso"; + +#define TASK_500MS_STACK_DEPTH 8192 +#define TASK_5000MS_STACK_DEPTH 4096 +#define TASK_500MS_PRIORITY (tskIDLE_PRIORITY + 1) +#define TASK_5000MS_PRIORITY (tskIDLE_PRIORITY) + +/***************************************************************************** + * Task implementations + *****************************************************************************/ + +static void task_500ms(void *arg) +{ + TickType_t last_wake = xTaskGetTickCount(); + + while (1) { + vTaskDelayUntil(&last_wake, pdMS_TO_TICKS(500)); + + xSemaphoreTake(g_state_mutex, portMAX_DELAY); + spiComm(); + brewProgram(); + heatingControl(); + xSemaphoreGive(g_state_mutex); + } +} + +static void task_5000ms(void *arg) +{ + TickType_t last_wake = xTaskGetTickCount(); + + while (1) { + vTaskDelayUntil(&last_wake, pdMS_TO_TICKS(5000)); + + xSemaphoreTake(g_state_mutex, portMAX_DELAY); + if (powerOn) { + /* Only report when the device is on to conserve MQTT budget. */ + temp_status_line_report(); + boiler_status_report(); +#if OVERSHOOT_DETECT_ENABLE + overshoot_disp_report(); +#endif + } + xSemaphoreGive(g_state_mutex); + } +} + +/***************************************************************************** + * Public init — call as the last step in app_main() + *****************************************************************************/ + +void tasks_start(void) +{ + BaseType_t ret; + + ret = xTaskCreate(task_500ms, "ctrl500ms", TASK_500MS_STACK_DEPTH, + NULL, TASK_500MS_PRIORITY, NULL); + if (ret != pdPASS) { + ESP_LOGE(TAG, "Failed to create task_500ms!"); + } + + ret = xTaskCreate(task_5000ms, "rpt5000ms", TASK_5000MS_STACK_DEPTH, + NULL, TASK_5000MS_PRIORITY, NULL); + if (ret != pdPASS) { + ESP_LOGE(TAG, "Failed to create task_5000ms!"); + } + + ESP_LOGI(TAG, "Application tasks started."); +} diff --git a/main/tasks.h b/main/tasks.h new file mode 100644 index 0000000..12040dd --- /dev/null +++ b/main/tasks.h @@ -0,0 +1,7 @@ +/* + Espresso — RTOS task API +*/ +#pragma once + +/* Spawn all application tasks. Call after full hardware and network init. */ +void tasks_start(void); diff --git a/main/temp_pid.c b/main/temp_pid.c deleted file mode 100644 index 442f517..0000000 --- a/main/temp_pid.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -#include "PID.h" - -/* Controller parameters */ -#define PID_KP 4.0f -#define PID_KI 2.0f -#define PID_KD 1.0f - -#define PID_TAU 0.02f - -#define PID_LIM_MIN 0.0f -#define PID_LIM_MAX 100.0f - -#define PID_LIM_MIN_INT -5.0f -#define PID_LIM_MAX_INT 5.0f - -#define SAMPLE_TIME_S 0.5f - -/* PID controller constants init */ -PIDController pid = { PID_KP, PID_KI, PID_KD, - PID_TAU, - PID_LIM_MIN, PID_LIM_MAX, - PID_LIM_MIN_INT, PID_LIM_MAX_INT, - SAMPLE_TIME_S, - 0, 0, 0, 0, 0 }; - -void pidInit(void) -{ - PIDController_Init(&pid); -} - -float pidUpdate(float setpoint, float measurement) -{ - /* Compute new control signal */ - PIDController_Update(&pid, setpoint, measurement); - - return (pid.out); -} From 54f6b918b5e7ec70bc3f025c4efc87c6dcb2fc98 Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Sat, 25 Apr 2026 16:38:31 -0300 Subject: [PATCH 6/7] control: fix TRIAC control minimum power TRIAC driver control only works with control values above a certain value. Update calibration and add notes for better control and temperature stability. Signed-off-by: Raffael Rostagno --- CMakeLists.txt | 4 ++- Makefile | 2 +- README.md | 49 +++++++++++++++++++++++++----- VERSION | 1 + main/control.c | 81 +++++++++++++++++++++++++++----------------------- main/control.h | 4 +-- main/main.c | 5 +++- 7 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 VERSION diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cb5845..f767908 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ endif(DEFINED ENV{RMAKER_PATH}) # RainMaker components + example helpers (app_wifi, app_insights, …) set(EXTRA_COMPONENT_DIRS ${RMAKER_PATH}/components ${RMAKER_PATH}/examples/common ${CMAKE_CURRENT_LIST_DIR}/components) -set(PROJECT_VER "2.0.1") +# Firmware version: root VERSION file (see README) +file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/VERSION" PROJECT_VER LIMIT_COUNT 1) +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_LIST_DIR}/VERSION") include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(espresso) diff --git a/Makefile b/Makefile index c9326e5..1295563 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := espresso -PROJECT_VER := 2.0.1 +PROJECT_VER := $(shell sed -n '1p' "$(dir $(abspath $(lastword $(MAKEFILE_LIST))))VERSION" | tr -d '\r') # Add RainMaker components and other common application components EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../components $(PROJECT_PATH)/../common diff --git a/README.md b/README.md index 85039ba..c045f8e 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ This project implements a PID/P controller to effectively control the temperatur Another feature implemented is the control of the water pump and pre-infusion settings. Combined, these functionalities allow for improved espresso extraction and consistency. -March 2024 -https://github.com/raffarost/espresso +**Firmware version** is defined in the repository root file [`VERSION`](VERSION); CMake and the legacy `Makefile` set `PROJECT_VER` from it, and the running image reports it via ESP-IDF (`esp_app_get_description()->version`). + +April 2026 — https://github.com/raffarost/espresso Raffael Rostagno raffael.rostagno@gmail.com @@ -107,13 +108,47 @@ To calibrate the PID controller, the following symbols can be optimized for each To calibrate the P controller, the following vectors can be changed: -``` -static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; -static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 2, 15, 30, 60, 80}; +```c +/* delta °C: -10 0 0.5 1 2 4 10 25 50 70 */ +static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; +static float controlSet[BKP_NUM] = { 0, 0, 5, 5, 5, 8, 15, 30, 80, 100}; ``` -The first vector corresponds to the temperature difference between target and actual reading. -The second vector is the power factor (0 to 100%) to apply for each (interpolated) delta. +The first vector is the temperature difference between setpoint and actual reading (°C). +The second vector is the driver power value (0–100) applied for each (interpolated) delta. + +##### TRIAC driver hardware constraint + +The dimmer driver uses phase-angle control with a **fixed gate pulse width of 4 timer steps**. +For driver values 1–4, the gate pulse extends beyond the AC half-cycle boundary, re-latching +the TRIAC at the start of the next half-cycle and delivering ~25 % average power regardless +of the intended setting. + +**Minimum safe value in `controlSet[]` is 5. Never use values 1–4.** +Use 0 (heater fully off) or ≥ 5. + +##### Actual power delivery vs. driver value + +Phase-angle control is highly nonlinear. The driver value does **not** map linearly to +delivered power — most of the useful range is concentrated above 20. + +| Driver value | Approx. actual power (% of rated) | +|---|---| +| 5 | ~0.1 % | +| 10 | ~0.6 % | +| 15 | ~2 % | +| 20 | ~5 % | +| 25 | ~9 % | +| 30 | ~15 % | +| 40 | ~31 % | +| 50 | ~50 % | +| 60 | ~69 % | +| 80 | ~95 % | +| 99 | ~100 % | + +Values below ~15 deliver negligible heat and are only useful in `controlSet[]` as a defined +floor to avoid the gate-overflow bug (see above). Practical maintenance and warmup +calibration should use values in the 15–99 range. #### Pump heat buffer calibration diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..0a69206 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.0.10 diff --git a/main/control.c b/main/control.c index 8cafb2d..754b96e 100644 --- a/main/control.c +++ b/main/control.c @@ -65,14 +65,18 @@ static const char *TAG = "espresso"; #define STUCK_DIAG_CLEAR_MAX_C 110.f #define STUCK_DIAG_WARMUP_GRACE_SEC 10.f -/* Power dithering around setpoint */ -#define POWER_50 2 -#define POWER_33 3 -#define POWER_25 4 -#define POWER_FACTOR POWER_50 -#define TEMP_PWR_TOGGLE 2 /* °C window where power is dithered */ +/* Minimum safe driver value — see controlSet[] comment for the gate-overflow rationale */ +#define CONTROL_MIN_NONZERO 5 -#define POWERON_MIN (15 * SEC_TO_US(60)) /* standby timeout (15 min) */ +/* Power dithering around setpoint (set PWR_TOGGLE_ENABLE to 1 to activate) */ +#define PWR_TOGGLE_ENABLE 1 +#define POWER_50 2 /* 50 % */ +#define POWER_33 3 /* 33 % */ +#define POWER_25 4 /* 25 % */ +#define POWER_FACTOR POWER_33 +#define DELTA_PWR_TOGGLE 10 /* delta °C window where power is dithered */ + +#define POWERON_MIN (15 * SEC_TO_US(60)) /* standby timeout (15 min) */ /* Status severity priorities */ #define STATUS_PRI_ZERO 25 @@ -82,20 +86,25 @@ static const char *TAG = "espresso"; #define STATUS_WORST_CLEAR_OK_REPORTS 12 /* Task5000ms ticks (~60 s) */ #if OVERSHOOT_DETECT_ENABLE -#define OVERSHOOT_SOFT_DISARM_SEC 90.f -#define OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD 30.0f -#define OVERSHOOT_COLD_START_MAX_C 60.0f -#define OS_DISARM_SOFT_TIMEOUT (1u << 0) -#define OS_DISARM_COMMIT (1u << 1) +#define OVERSHOOT_SOFT_DISARM_SEC 90.0f +#define OVERSHOOT_APPLY_DELTA_MAX_C 15.0f /* trim only in last N °C approach */ +#define OS_DISARM_SOFT_TIMEOUT (1u << 0) +#define OS_DISARM_COMMIT (1u << 1) #endif /***************************************************************************** * Lookup tables *****************************************************************************/ +/* + * Driver constraint: use 0 (fully off) or ≥ 5. Values 1–4 are not functional. + * The fixed gate pulse (4 timer steps) overflows the half-cycle boundary for + * those values, re-latching the TRIAC and delivering ~25 % unintended power. + * Actual power delivery is highly nonlinear — see README for the reference table. + */ /* Temperature delta vs power setpoint breakpoints (LOOKUP mode) */ -static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; -static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 2, 15, 30, 60, 80}; +static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; +static float controlSet[BKP_NUM] = { 0, 0, 5, 5, 5, 8, 25, 40, 80, 100}; /* * Per-second heater power (%) while the pump is on. @@ -107,7 +116,7 @@ static float controlSet[BKP_NUM] = { 0, 0, 1, 1, 1, 2, 15, 30, 60, 80 * Phase 4 (~6 s+): puck deteriorates, flow recovers */ static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { -/* s: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 */ +/* s: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 */ 100, 100, 100, 100, 80, 80, 40, 40, 30, 30, 30, 40, 40, 40, 40, 60, 60, 60, 60, 60 }; @@ -157,7 +166,9 @@ static uint16_t s_spi_data; static float delta; static float pidOut; static int control; +#if PWR_TOGGLE_ENABLE static int powerToggle; +#endif static unsigned long long pumpTimer; static bool pump_active; static unsigned long long powerOnTimer; @@ -202,10 +213,9 @@ static void temp_stuck_diag_update(int heating_demand_pct); static bool boiler_heater_holdoff(void); #if OVERSHOOT_DETECT_ENABLE -static void overshoot_learn_try_arm_cold(const char *site); +static void overshoot_learn_try_arm_cold(void); static void overshoot_peak_detector_update(void); static void overshoot_disp_format_str(void); -static void overshoot_apply_trim_to_control(int *p_control); static void overshoot_learn_set_disallowed(uint32_t reason_bits); #endif @@ -494,9 +504,9 @@ static void overshoot_learn_set_disallowed(uint32_t reason_bits) overshoot_learn_allowed = false; } -static void overshoot_learn_try_arm_cold(const char *site) +static void overshoot_learn_try_arm_cold(void) { - if (!powerOn || !temp_read_trusted || tempCelsius > OVERSHOOT_COLD_START_MAX_C) { + if (!powerOn || !temp_read_trusted) { return; } overshoot_learn_allowed = true; @@ -506,8 +516,6 @@ static void overshoot_learn_try_arm_cold(const char *site) overshoot_excursion_pk = 0.f; overshoot_soft_timer_start_us = 0; overshoot_detected = false; - ESP_LOGI(TAG, "OS arm[%s] OK: cold start @ %.1f°C (trim pre-loaded -%.2f%%)", - site, (double)tempCelsius, (double)(overshoot_trim_stored * 100.0f)); } static void overshoot_peak_detector_update(void) @@ -548,7 +556,7 @@ static void overshoot_peak_detector_update(void) if (tempCelsius <= (float)tempSetpoint) { if (overshoot_excursion_latched && overshoot_excursion_pk > 0.01f) { - const float peak_c = overshoot_excursion_pk; + const float peak_c = ceilf(overshoot_excursion_pk); const float trim_add = OVERSHOOT_TRIM_FRAC_PER_DEG * peak_c; const float trim_prev = overshoot_trim_stored; overshoot_trim_stored = fminf(overshoot_trim_stored + trim_add, @@ -603,18 +611,11 @@ void overshoot_disp_report(void) } } -static void overshoot_apply_trim_to_control(int *p_control) -{ - if ((float)*p_control > (float)OVERSHOOT_TRIM_DEMAND_PCT_THRESHOLD) { - float cf = (float)*p_control * (1.f - overshoot_trim_stored); - *p_control = (int)(cf + 0.5f); - } -} /* Public wrappers called from write_cb (rainmaker.c) */ void control_on_power_on(void) { - overshoot_learn_try_arm_cold("Power cb"); + overshoot_learn_try_arm_cold(); } void control_on_power_off(void) @@ -667,22 +668,24 @@ void heatingControl(void) if (temp_read_trusted && !pump_active) { if (!overshoot_boot_temp_sampled) { overshoot_boot_temp_sampled = true; - overshoot_learn_try_arm_cold("boot trusted"); + overshoot_learn_try_arm_cold(); } overshoot_peak_detector_update(); } - int control_after_trim = control_lookup; - overshoot_apply_trim_to_control(&control_after_trim); - control = control_after_trim; + control = (control_lookup > 0 && delta <= OVERSHOOT_APPLY_DELTA_MAX_C) + ? (int)((float)control_lookup * (1.f - overshoot_trim_stored) + 0.5f) + : control_lookup; #else control = control_lookup; #endif - /* Dither power within TEMP_PWR_TOGGLE window around setpoint (idle only) */ - if (!pump_active && (delta > 0) && (delta <= TEMP_PWR_TOGGLE)) { +#if PWR_TOGGLE_ENABLE + /* Dither power within DELTA_PWR_TOGGLE window around setpoint (idle only) */ + if (!pump_active && (delta > 0) && (delta <= DELTA_PWR_TOGGLE)) { powerToggle = (int)((tick % POWER_FACTOR) == 0); control = control * powerToggle; } +#endif #elif (CONTROL_TYPE == PID_LOOKUP) pidOut = pidUpdate((float)tempSetpoint, tempCelsius); @@ -713,6 +716,10 @@ void heatingControl(void) } } + if (control > 0 && control < CONTROL_MIN_NONZERO) { + control = CONTROL_MIN_NONZERO; + } + setPower(ptr_dimmer, control); ESP_LOGI(TAG, "%d | %.2f | %.2f | %d | %d", @@ -827,7 +834,7 @@ void brewProgram(void) brewSignal = false; flushSignal = false; #if OVERSHOOT_DETECT_ENABLE - overshoot_learn_try_arm_cold("standby->on"); + overshoot_learn_try_arm_cold(); #endif ESP_LOGI(TAG, "Switching power ON!"); } diff --git a/main/control.h b/main/control.h index ec7cc68..670a696 100644 --- a/main/control.h +++ b/main/control.h @@ -50,8 +50,8 @@ * Overshoot NVS encoding helpers (also used by nvs.c) *****************************************************************************/ #if OVERSHOOT_DETECT_ENABLE -#define OVERSHOOT_TRIM_FRAC_MAX 0.20f -#define OVERSHOOT_TRIM_FRAC_PER_DEG 0.04f +#define OVERSHOOT_TRIM_FRAC_MAX 0.50f +#define OVERSHOOT_TRIM_FRAC_PER_DEG 0.10f #define OVERSHOOT_TRIM_FRAC_TO_MPCT(f) ((int32_t)lroundf((f) * 100000.0f)) #define OVERSHOOT_TRIM_MPCT_TO_FRAC(i) ((float)(i) * 0.00001f) #endif diff --git a/main/main.c b/main/main.c index 9c1196b..9bacd5d 100644 --- a/main/main.c +++ b/main/main.c @@ -1,5 +1,8 @@ /* - Espresso PID/P Controller v2.0.1 + Espresso PID/P Controller + + Firmware version comes from the build (PROJECT_VER / esp_app_desc); see + the VERSION file at the repository root and README. This project implements a PID/P controller to effectively control the temperature of an Espresso machine boiler for a stable setpoint. From 548b6f1bdf70271a79a316211529bf7b64fe2077 Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Fri, 8 May 2026 15:01:35 -0300 Subject: [PATCH 7/7] control: Update adaptive warmup strategy Update adaptive warmup strategy (adding warmup stall detector to overshoot detector) to manage both temperature overshoot condition or warmup stall. Tweak power dither near setpoint to improve temperature control during warmup. Signed-off-by: Raffael Rostagno --- README.md | 51 +++++- VERSION | 2 +- main/control.c | 409 ++++++++++++++++++++++++++++++----------------- main/control.h | 37 +++-- main/nvs.c | 66 +++----- main/nvs.h | 6 +- main/rainmaker.c | 23 ++- main/tasks.c | 4 +- 8 files changed, 369 insertions(+), 229 deletions(-) diff --git a/README.md b/README.md index c045f8e..75ac913 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ To calibrate the P controller, the following vectors can be changed: ```c /* delta °C: -10 0 0.5 1 2 4 10 25 50 70 */ static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; -static float controlSet[BKP_NUM] = { 0, 0, 5, 5, 5, 8, 15, 30, 80, 100}; +static float controlSet[BKP_NUM] = { 0, 0, 5, 5, 8, 20, 30, 50, 80, 100}; ``` The first vector is the temperature difference between setpoint and actual reading (°C). @@ -150,6 +150,53 @@ Values below ~15 deliver negligible heat and are only useful in `controlSet[]` a floor to avoid the gate-overflow bug (see above). Practical maintenance and warmup calibration should use values in the 15–99 range. +#### Adaptive warmup (`ADAPTIVE_WARMUP_ENABLE`) + +When `CONTROL_TYPE == LOOKUP`, the adaptive warmup strategy is enabled automatically. It replaces the old fixed-duty power toggle with a self-tuning mechanism that adjusts heater duty during the final approach to setpoint. + +##### Dither step table + +Within `DELTA_PWR_TOGGLE` (10 °C below setpoint), the heater is pulsed at a duty cycle determined by the current *step index* (0–4): + +| Step | Duty | Period / on-count | +|------|-------|-------------------| +| 0 | 100 % | 1 / 1 | +| 1 | 75 % | 4 / 3 | +| 2 | 50 % | 2 / 1 — **default** | +| 3 | 25 % | 4 / 1 | + +Within `TEMP_DELTA` (2 °C) of setpoint, the step is overridden to `POWER_NEAR_SETPOINT` (default: step 3, 33 %) regardless of the learned index, to prevent overshoot and overly aggressive fighting between the stall and overshoot detectors near the target. + +The current duty is reported in the RainMaker UI as **Power factor (dither)**. The step index is persisted to NVS (`dithStep`) and restored on each reboot, so the machine retains its seasonal calibration across power cycles. + +##### Overshoot detection + +After the approach phase, if the temperature exceeds `tempSetpoint + TEMP_DELTA`, an overshoot is latched. The peak excursion above setpoint is tracked until the temperature drops back to setpoint, then the step is incremented (less power next warmup): + +- Peak < 5 °C → `step + 1` +- Peak ≥ 5 °C (`OVERSHOOT_SEVERE_PEAK_C`) → `step + 2` + +The step is clamped at 4 (25 %). The NVS value is written after the commit. + +##### Warmup stall detection + +During the approach window, the detector tracks the best (smallest) delta seen since entering the window. A 30-second timer (`DITHER_STALL_SEC`) is reset every time a new delta minimum is recorded. If the timer expires — meaning temperature has not improved in 30 s — a **warmup stall** is declared: + +- `step - 1` (more heater power) is applied immediately in RAM. +- The NVS write is deferred until the end of the warmup cycle (overshoot settle or soft-settle). +- The **Status** diagnostic field shows `"Warmup stall"` while the stall is active, clearing automatically once the setpoint is reached. + +The stall detector is inhibited while `temp_stuck_diag` is active (frozen sensor) to avoid false step adjustments based on stale readings. + +##### Self-correcting behaviour + +The two detectors naturally balance each other: + +- Stall fires → `step - 1` → more power → may cause overshoot → `step + 1` → net 0 (learning discarded) +- Stall fires → `step - 1` → severe overshoot → `step + 2` → net +1 (learned: needs less power overall) + +The step is reset to `DITHER_STEP_DEFAULT` (50 %) via the **Reset power factor** button in the RainMaker UI. + #### Pump heat buffer calibration When the pump is active (pre-infusion, brew, or flush), cold water entering the boiler causes a @@ -158,7 +205,7 @@ temperature controller: ``` static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { - 100, 100, 100, 100, 80, 80, 40, 40, 30, 30, 30, 40, 40, 40, 40, 60, 60, 60, 60, 60 + 100, 100, 100, 100, 80, 80, 50, 50, 40, 40, 40, 50, 50, 50, 50, 70, 70, 70, 70, 70 }; ``` diff --git a/VERSION b/VERSION index 0a69206..18f812f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.10 +2.0.23 diff --git a/main/control.c b/main/control.c index 754b96e..5c899da 100644 --- a/main/control.c +++ b/main/control.c @@ -7,7 +7,7 @@ - Heating element control via lookup / PID (heatingControl) - Brew and pre-infusion state machine (brewProgram) - Temperature diagnostics and stuck-sensor detection - - Overshoot learn / trim (LOOKUP mode only) + - Adaptive dither: overshoot and stall detection (LOOKUP mode only) - RainMaker status reporting helpers */ @@ -68,28 +68,33 @@ static const char *TAG = "espresso"; /* Minimum safe driver value — see controlSet[] comment for the gate-overflow rationale */ #define CONTROL_MIN_NONZERO 5 -/* Power dithering around setpoint (set PWR_TOGGLE_ENABLE to 1 to activate) */ -#define PWR_TOGGLE_ENABLE 1 -#define POWER_50 2 /* 50 % */ -#define POWER_33 3 /* 33 % */ -#define POWER_25 4 /* 25 % */ -#define POWER_FACTOR POWER_33 -#define DELTA_PWR_TOGGLE 10 /* delta °C window where power is dithered */ +/* Approach window: dithering and stall detection active within this delta */ +#define DELTA_PWR_TOGGLE 10 /* °C below setpoint */ #define POWERON_MIN (15 * SEC_TO_US(60)) /* standby timeout (15 min) */ /* Status severity priorities */ +#define STATUS_PRI_WARMUP_STALL 10 #define STATUS_PRI_ZERO 25 #define STATUS_PRI_UNTRUSTED 50 #define STATUS_PRI_TOO_HIGH 75 #define STATUS_PRI_STUCK 100 #define STATUS_WORST_CLEAR_OK_REPORTS 12 /* Task5000ms ticks (~60 s) */ -#if OVERSHOOT_DETECT_ENABLE -#define OVERSHOOT_SOFT_DISARM_SEC 90.0f -#define OVERSHOOT_APPLY_DELTA_MAX_C 15.0f /* trim only in last N °C approach */ -#define OS_DISARM_SOFT_TIMEOUT (1u << 0) -#define OS_DISARM_COMMIT (1u << 1) +#if ADAPTIVE_WARMUP_ENABLE +#define DITHER_STALL_SEC 30.0f /* no delta progress → stall */ +#define DITHER_SOFT_SETTLE_SEC 60.0f /* wait at setpoint before clean commit */ +#define OVERSHOOT_SEVERE_PEAK_C 5.0f /* peak ≥ this → step+2, else step+1 */ + +/* Named aliases for dither_steps[] indices */ +#define POWER_100 0 +#define POWER_75 1 +#define POWER_50 2 +#define POWER_25 3 + +/* Power factor applied within TEMP_DELTA of setpoint for accurate + * temperature maintenance — lower than the adaptive approach step. */ +#define POWER_NEAR_SETPOINT POWER_50 #endif /***************************************************************************** @@ -104,7 +109,7 @@ static const char *TAG = "espresso"; */ /* Temperature delta vs power setpoint breakpoints (LOOKUP mode) */ static float deltaBkp[BKP_NUM] = {-10, 0, 0.5, 1, 2, 4, 10, 25, 50, 70}; -static float controlSet[BKP_NUM] = { 0, 0, 5, 5, 5, 8, 25, 40, 80, 100}; +static float controlSet[BKP_NUM] = { 0, 0, 10, 10, 20, 30, 40, 50, 80, 100}; /* * Per-second heater power (%) while the pump is on. @@ -117,7 +122,7 @@ static float controlSet[BKP_NUM] = { 0, 0, 5, 5, 5, 8, 25, 40, */ static int pumpOnHeatBuff[PUMP_ON_HEAT_BUFF_LEN] = { /* s: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 */ - 100, 100, 100, 100, 80, 80, 40, 40, 30, 30, 30, 40, 40, 40, 40, 60, 60, 60, 60, 60 + 100, 100, 100, 100, 80, 80, 50, 50, 40, 40, 40, 50, 50, 50, 50, 70, 70, 70, 70, 70 }; /***************************************************************************** @@ -139,15 +144,15 @@ bool tempLock = true; bool preInfusion = true; float tempCelsius; -#if OVERSHOOT_DETECT_ENABLE -float overshoot_trim_stored; +#if ADAPTIVE_WARMUP_ENABLE +int dither_step = DITHER_STEP_DEFAULT; #endif /* RainMaker param handles — defined here, created by rainmaker_init() */ esp_rmaker_param_t *primary; esp_rmaker_param_t *status_param; esp_rmaker_param_t *poweron_param; -#if OVERSHOOT_DETECT_ENABLE +#if ADAPTIVE_WARMUP_ENABLE esp_rmaker_param_t *overshoot_disp_param; #endif @@ -166,9 +171,6 @@ static uint16_t s_spi_data; static float delta; static float pidOut; static int control; -#if PWR_TOGGLE_ENABLE -static int powerToggle; -#endif static unsigned long long pumpTimer; static bool pump_active; static unsigned long long powerOnTimer; @@ -191,16 +193,22 @@ static int s_status_worst_pri; static char s_status_worst_str[48]; static uint16_t s_status_ok_clean_reports; -#if OVERSHOOT_DETECT_ENABLE -static char s_overshoot_disp_str[28]; -static float overshoot_excursion_pk; -static bool overshoot_excursion_latched; -static bool overshoot_detected; -static bool overshoot_learn_allowed; -static bool overshoot_learn_ever_armed; -static uint32_t overshoot_learn_disarm_mask; -static bool overshoot_boot_temp_sampled; -static unsigned long long overshoot_soft_timer_start_us; +#if ADAPTIVE_WARMUP_ENABLE +static char s_dither_disp_str[20]; +static float overshoot_excursion_pk; +static bool overshoot_excursion_latched; +static bool overshoot_detected; +static bool learn_armed; +static bool learn_ever_armed; +static bool boot_temp_sampled; +static unsigned long long soft_timer_us; +static bool stall_active; +static float stall_delta_min; +static unsigned long long stall_timer_us; +static bool stall_armed; +static bool stall_nvs_pending; +static bool warmup_nv_committed; /* true after first NVS write; maintenance mode */ +static int dither_step_nv; /* shadow of the last NVS-persisted step */ #endif /***************************************************************************** @@ -212,11 +220,10 @@ static void temp_read_note_good(float celsius); static void temp_stuck_diag_update(int heating_demand_pct); static bool boiler_heater_holdoff(void); -#if OVERSHOOT_DETECT_ENABLE -static void overshoot_learn_try_arm_cold(void); +#if ADAPTIVE_WARMUP_ENABLE +static void adaptive_try_arm_cold(void); +static void stall_detector_update(void); static void overshoot_peak_detector_update(void); -static void overshoot_disp_format_str(void); -static void overshoot_learn_set_disallowed(uint32_t reason_bits); #endif /***************************************************************************** @@ -450,6 +457,11 @@ void boiler_status_report(void) } else if (tempCelsius > (float)MAX_TEMP_THR) { cur_pri = STATUS_PRI_TOO_HIGH; cur_msg = "Temp too high"; +#if ADAPTIVE_WARMUP_ENABLE + } else if (stall_active) { + cur_pri = STATUS_PRI_WARMUP_STALL; + cur_msg = "Warmup stall"; +#endif } if (cur_pri > s_status_worst_pri) { @@ -493,157 +505,251 @@ void boiler_status_report(void) } /***************************************************************************** - * Overshoot learn / trim (LOOKUP mode only) + * Adaptive dither — overshoot and stall detection (LOOKUP mode only) *****************************************************************************/ -#if OVERSHOOT_DETECT_ENABLE +#if ADAPTIVE_WARMUP_ENABLE -static void overshoot_learn_set_disallowed(uint32_t reason_bits) -{ - overshoot_learn_disarm_mask |= reason_bits; - overshoot_learn_allowed = false; -} +/* Duty step table: (period, on_count) → duty = on_count / period */ +typedef struct { uint8_t period; uint8_t on_count; } dither_step_t; +static const dither_step_t dither_steps[DITHER_STEP_COUNT] = { + {1, 1}, /* step 0: 100 % */ + {4, 3}, /* step 1: 75 % */ + {2, 1}, /* step 2: 50 % — default */ + {4, 1}, /* step 3: 25 % */ +}; +_Static_assert(sizeof(dither_steps) / sizeof(dither_steps[0]) == DITHER_STEP_COUNT, + "dither_steps[] size mismatch with DITHER_STEP_COUNT"); -static void overshoot_learn_try_arm_cold(void) +/* ---- arm / disarm ---------------------------------------------------- */ + +static void adaptive_try_arm_cold(void) { if (!powerOn || !temp_read_trusted) { return; } - overshoot_learn_allowed = true; - overshoot_learn_ever_armed = true; - overshoot_learn_disarm_mask = 0u; + if (!learn_ever_armed) { + dither_step_nv = dither_step; /* capture the NVS value loaded at boot */ + } + learn_armed = true; + learn_ever_armed = true; overshoot_excursion_latched = false; - overshoot_excursion_pk = 0.f; - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; + overshoot_excursion_pk = 0.f; + soft_timer_us = 0; + overshoot_detected = false; + stall_armed = false; + stall_active = false; + stall_nvs_pending = false; + warmup_nv_committed = false; } -static void overshoot_peak_detector_update(void) +/* ---- stall detector -------------------------------------------------- */ + +static void stall_detector_update(void) { - if (!overshoot_learn_allowed || temp_stuck_diag) { + if (!learn_armed || temp_stuck_diag) { + stall_armed = false; return; } - const float temp_ok_max = (float)tempSetpoint + (float)TEMP_DELTA; + const bool in_approach = (delta > 0.f && delta <= (float)DELTA_PWR_TOGGLE); - if (tempCelsius > temp_ok_max) { - overshoot_detected = true; - overshoot_excursion_latched = true; + if (!in_approach) { + stall_armed = false; + stall_active = false; /* left approach zone — clear status */ + return; } - if (!overshoot_detected) { - if (overshoot_soft_timer_start_us == 0ULL && tempCelsius >= (float)tempSetpoint) { - overshoot_soft_timer_start_us = getAbsTime1us(); - } else if (overshoot_soft_timer_start_us != 0ULL && - (getAbsTime1us() - overshoot_soft_timer_start_us) >= - (unsigned long long)SEC_TO_US(OVERSHOOT_SOFT_DISARM_SEC)) { - overshoot_learn_set_disallowed(OS_DISARM_SOFT_TIMEOUT); - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; - overshoot_excursion_latched = false; - overshoot_excursion_pk = 0.f; - ESP_LOGI(TAG, "OS learn disarmed (soft): no excursion above setpoint+%d°C " - "within %.0f s", TEMP_DELTA, (double)OVERSHOOT_SOFT_DISARM_SEC); - overshoot_disp_report(); - return; - } + if (!stall_armed) { + /* Entering approach window: snapshot best delta and start timer */ + stall_delta_min = delta; + stall_timer_us = getAbsTime1us(); + stall_armed = true; + return; } - if (overshoot_excursion_latched && tempCelsius > (float)tempSetpoint) { - const float above = tempCelsius - (float)tempSetpoint; - overshoot_excursion_pk = fmaxf(overshoot_excursion_pk, above); - } - - if (tempCelsius <= (float)tempSetpoint) { - if (overshoot_excursion_latched && overshoot_excursion_pk > 0.01f) { - const float peak_c = ceilf(overshoot_excursion_pk); - const float trim_add = OVERSHOOT_TRIM_FRAC_PER_DEG * peak_c; - const float trim_prev = overshoot_trim_stored; - overshoot_trim_stored = fminf(overshoot_trim_stored + trim_add, - OVERSHOOT_TRIM_FRAC_MAX); - ESP_LOGI(TAG, - "OS commit: peak +%.2f°C -> cut +%.2f%% (-%.2f%% -> -%.2f%%%s)", - (double)peak_c, - (double)(trim_add * 100.0f), - (double)(trim_prev * 100.0f), - (double)(overshoot_trim_stored * 100.0f), - (overshoot_trim_stored >= OVERSHOOT_TRIM_FRAC_MAX - 1e-6f) - ? ", CAPPED" : ""); - nvs_persist_overshoot_trim(); - overshoot_learn_set_disallowed(OS_DISARM_COMMIT); - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; - overshoot_excursion_pk = 0.f; + if (delta < stall_delta_min) { + /* Progress: reset timer */ + stall_delta_min = delta; + stall_timer_us = getAbsTime1us(); + return; + } + + if ((getAbsTime1us() - stall_timer_us) >= + (unsigned long long)SEC_TO_US(DITHER_STALL_SEC)) { + stall_armed = false; /* one fire per approach entry */ + if (dither_step > 0) { + dither_step--; + if (!warmup_nv_committed) { + stall_nvs_pending = true; /* deferred NVS write for cold warmup only */ + } } - overshoot_excursion_latched = false; + stall_active = true; + ESP_LOGI(TAG, "Warmup stall: step -> %d (%d%% duty)%s", + dither_step, + dither_steps[dither_step].on_count * 100 / dither_steps[dither_step].period, + warmup_nv_committed ? " [maintenance]" : ""); + dither_disp_report(); } } -static void overshoot_disp_format_str(void) +/* ---- overshoot peak detector ----------------------------------------- */ + +static void overshoot_peak_detector_update(void) { - const float cut_pct = overshoot_trim_stored * 100.0f; - - if (overshoot_learn_allowed && overshoot_excursion_latched && - overshoot_excursion_pk > 0.01f) { - snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), - "%s%.1f%% +%.1f°C", - cut_pct > 0.05f ? "-" : "", - (double)cut_pct, (double)overshoot_excursion_pk); - } else { - snprintf(s_overshoot_disp_str, sizeof(s_overshoot_disp_str), - "%s%.1f%%", - cut_pct > 0.05f ? "-" : "", (double)cut_pct); + if (!learn_armed || temp_stuck_diag) { + return; + } + + /* Discard excursion state on setpoint change — prevents false latch + * when setpoint is moved down below the current temperature. */ + static int32_t last_sp = 0; + if (tempSetpoint != last_sp) { + last_sp = tempSetpoint; + overshoot_excursion_latched = false; + overshoot_excursion_pk = 0.f; + overshoot_detected = false; + soft_timer_us = 0; + } + + const float sp = (float)tempSetpoint; + + if (tempCelsius > sp + (float)TEMP_DELTA) { + overshoot_detected = true; + overshoot_excursion_latched = true; + soft_timer_us = 0; /* cancel soft timer */ + } + + /* Soft timer: cold warmup only — started on first clean reach of setpoint. + * If no overshoot fires within DITHER_SOFT_SETTLE_SEC, commit any pending + * stall step and disarm. Skipped in maintenance (warmup_nv_committed). */ + if (!overshoot_detected && !warmup_nv_committed) { + if (soft_timer_us == 0ULL && tempCelsius >= sp) { + soft_timer_us = getAbsTime1us(); + } else if (soft_timer_us != 0ULL && + (getAbsTime1us() - soft_timer_us) >= + (unsigned long long)SEC_TO_US(DITHER_SOFT_SETTLE_SEC)) { + if (stall_nvs_pending) { + stall_nvs_pending = false; + nvs_persist_dither_step(); + dither_step_nv = dither_step; + ESP_LOGI(TAG, "Stall step committed: step %d (%d%%)", + dither_step, + dither_steps[dither_step].on_count * 100 / + dither_steps[dither_step].period); + } + warmup_nv_committed = true; + soft_timer_us = 0; + dither_disp_report(); /* learn_armed stays true — maintenance begins */ + } + } + + /* Track peak excursion above setpoint */ + if (overshoot_excursion_latched && tempCelsius > sp) { + overshoot_excursion_pk = fmaxf(overshoot_excursion_pk, tempCelsius - sp); + } + + /* Commit when temp drops back to setpoint after an overshoot */ + if (tempCelsius <= sp && overshoot_excursion_latched && + overshoot_excursion_pk > 0.01f) { + const float peak = overshoot_excursion_pk; + const int step_inc = (peak >= OVERSHOOT_SEVERE_PEAK_C) ? 2 : 1; + const int new_step = dither_step + step_inc; + dither_step = (new_step < DITHER_STEP_COUNT) ? new_step + : DITHER_STEP_COUNT - 1; + stall_nvs_pending = false; + soft_timer_us = 0; + overshoot_detected = false; + overshoot_excursion_pk = 0.f; + overshoot_excursion_latched = false; + if (!warmup_nv_committed) { + nvs_persist_dither_step(); + dither_step_nv = dither_step; + warmup_nv_committed = true; /* learn_armed stays true — maintenance begins */ + } + /* maintenance: learn_armed stays true, step change is RAM-only */ + ESP_LOGI(TAG, "Overshoot: peak +%.2f°C -> step+%d -> step %d (%d%%)%s", + (double)peak, step_inc, dither_step, + dither_steps[dither_step].on_count * 100 / + dither_steps[dither_step].period, + warmup_nv_committed ? " [maintenance]" : ""); + dither_disp_report(); + return; + } + + if (tempCelsius <= sp) { + overshoot_excursion_latched = false; } } -void overshoot_disp_report(void) +/* ---- display --------------------------------------------------------- */ + +void dither_disp_report(void) { if (!powerOn) { return; } - overshoot_disp_format_str(); + snprintf(s_dither_disp_str, sizeof(s_dither_disp_str), "%d%%", + dither_steps[dither_step].on_count * 100 / + dither_steps[dither_step].period); if (overshoot_disp_param) { esp_err_t e = esp_rmaker_param_update_and_report(overshoot_disp_param, - esp_rmaker_str(s_overshoot_disp_str)); + esp_rmaker_str(s_dither_disp_str)); if (e != ESP_OK) { - ESP_LOGW(TAG, "Overshoot display report: %s", esp_err_to_name(e)); + ESP_LOGW(TAG, "Dither display report: %s", esp_err_to_name(e)); } } } +int dither_step_pct(void) +{ + return dither_steps[dither_step].on_count * 100 / dither_steps[dither_step].period; +} + +/* ---- public API ------------------------------------------------------ */ -/* Public wrappers called from write_cb (rainmaker.c) */ void control_on_power_on(void) { - overshoot_learn_try_arm_cold(); + adaptive_try_arm_cold(); } void control_on_power_off(void) { - overshoot_learn_ever_armed = false; + dither_step = dither_step_nv; /* discard maintenance-only RAM adjustments */ + learn_armed = false; + learn_ever_armed = false; + stall_armed = false; + stall_active = false; + stall_nvs_pending = false; + warmup_nv_committed = false; } -void control_reset_overshoot_trim(void) +void control_reset_dither(void) { - overshoot_trim_stored = 0.f; - overshoot_learn_allowed = true; - overshoot_learn_ever_armed = false; - overshoot_learn_disarm_mask = 0u; - overshoot_soft_timer_start_us = 0; - overshoot_detected = false; + dither_step = DITHER_STEP_DEFAULT; + learn_armed = false; + learn_ever_armed = false; + soft_timer_us = 0; + overshoot_detected = false; overshoot_excursion_latched = false; - overshoot_excursion_pk = 0.f; - nvs_persist_overshoot_trim(); - ESP_LOGI(TAG, "Warmup trim reset (NVS cleared, learn re-armed if cold)"); - overshoot_disp_report(); + overshoot_excursion_pk = 0.f; + stall_armed = false; + stall_active = false; + stall_nvs_pending = false; + nvs_persist_dither_step(); + dither_step_nv = dither_step; + ESP_LOGI(TAG, "Dither reset: step %d (%d%%)", DITHER_STEP_DEFAULT, + dither_steps[DITHER_STEP_DEFAULT].on_count * 100 / + dither_steps[DITHER_STEP_DEFAULT].period); + dither_disp_report(); } -#else /* !OVERSHOOT_DETECT_ENABLE */ +#else /* !ADAPTIVE_WARMUP_ENABLE */ void control_on_power_on(void) {} void control_on_power_off(void) {} -#endif /* OVERSHOOT_DETECT_ENABLE */ +#endif /* ADAPTIVE_WARMUP_ENABLE */ /***************************************************************************** * Heating element control @@ -664,29 +770,32 @@ void heatingControl(void) float ir = indexRatio(deltaBkp, BKP_NUM, delta); const int control_lookup = (int)interp1D(controlSet, BKP_NUM, ir); -#if OVERSHOOT_DETECT_ENABLE +#if ADAPTIVE_WARMUP_ENABLE if (temp_read_trusted && !pump_active) { - if (!overshoot_boot_temp_sampled) { - overshoot_boot_temp_sampled = true; - overshoot_learn_try_arm_cold(); + if (!boot_temp_sampled) { + boot_temp_sampled = true; + adaptive_try_arm_cold(); } - overshoot_peak_detector_update(); + if (learn_ever_armed) { + stall_detector_update(); + overshoot_peak_detector_update(); + } + } + /* Adaptive dithering — always active in the approach window. + * Within TEMP_DELTA of setpoint, lock to step 3 (33 %) to cap power + * and prevent stall/overshoot from fighting each other near target. */ + if (!pump_active && delta > 0.f && delta <= (float)DELTA_PWR_TOGGLE) { + const dither_step_t *s = (delta <= (float)TEMP_DELTA) + ? &dither_steps[POWER_NEAR_SETPOINT] + : &dither_steps[dither_step]; + control = ((tick % (int)s->period) < (int)s->on_count) ? control_lookup : 0; + } else { + control = control_lookup; } - control = (control_lookup > 0 && delta <= OVERSHOOT_APPLY_DELTA_MAX_C) - ? (int)((float)control_lookup * (1.f - overshoot_trim_stored) + 0.5f) - : control_lookup; #else control = control_lookup; #endif -#if PWR_TOGGLE_ENABLE - /* Dither power within DELTA_PWR_TOGGLE window around setpoint (idle only) */ - if (!pump_active && (delta > 0) && (delta <= DELTA_PWR_TOGGLE)) { - powerToggle = (int)((tick % POWER_FACTOR) == 0); - control = control * powerToggle; - } -#endif - #elif (CONTROL_TYPE == PID_LOOKUP) pidOut = pidUpdate((float)tempSetpoint, tempCelsius); float ir = indexRatio(deltaBkp, BKP_NUM, pidOut); @@ -783,8 +892,8 @@ void brewProgram(void) if (!powerOn || (getAbsTime1us() - powerOnTimer) > POWERON_MIN) { brewState = BREW_POWER_OFF; powerOn = false; -#if OVERSHOOT_DETECT_ENABLE - overshoot_learn_ever_armed = false; +#if ADAPTIVE_WARMUP_ENABLE + learn_ever_armed = false; #endif esp_rmaker_param_update_and_report(poweron_param, esp_rmaker_bool(powerOn)); @@ -833,8 +942,8 @@ void brewProgram(void) brewState = BREW_OFF; brewSignal = false; flushSignal = false; -#if OVERSHOOT_DETECT_ENABLE - overshoot_learn_try_arm_cold(); +#if ADAPTIVE_WARMUP_ENABLE + adaptive_try_arm_cold(); #endif ESP_LOGI(TAG, "Switching power ON!"); } diff --git a/main/control.h b/main/control.h index 670a696..81ea35c 100644 --- a/main/control.h +++ b/main/control.h @@ -2,8 +2,14 @@ Espresso — shared state, configuration, and control API This header is included by all modules that share control state or call - control functions. It owns the CONTROL_TYPE / OVERSHOOT_DETECT_ENABLE + control functions. It owns the CONTROL_TYPE / ADAPTIVE_WARMUP_ENABLE compile-time knobs so every translation unit sees the same setting. + + Adaptive dither (ADAPTIVE_WARMUP_ENABLE): + Replaces the old fixed-duty toggle and overshoot trim fraction with a + 5-step duty table (75 → 25 %). Stall detection and overshoot peak + detection adjust the step index each warmup cycle; the result is + persisted to NVS so the machine adapts across seasons. */ #pragma once @@ -26,9 +32,9 @@ #define CONTROL_TYPE LOOKUP -/* Overshoot learn/trim for LOOKUP only; on by default. */ -#if (CONTROL_TYPE == LOOKUP) && !defined(OVERSHOOT_DETECT_ENABLE) -#define OVERSHOOT_DETECT_ENABLE 1 +/* Adaptive warmup strategy for LOOKUP only; on by default. */ +#if (CONTROL_TYPE == LOOKUP) && !defined(ADAPTIVE_WARMUP_ENABLE) +#define ADAPTIVE_WARMUP_ENABLE 1 #endif /***************************************************************************** @@ -47,13 +53,11 @@ #define TEMP_SETPOINT_MAX 96 /***************************************************************************** - * Overshoot NVS encoding helpers (also used by nvs.c) + * Adaptive dither — shared constants (used by control.c and nvs.c) *****************************************************************************/ -#if OVERSHOOT_DETECT_ENABLE -#define OVERSHOOT_TRIM_FRAC_MAX 0.50f -#define OVERSHOOT_TRIM_FRAC_PER_DEG 0.10f -#define OVERSHOOT_TRIM_FRAC_TO_MPCT(f) ((int32_t)lroundf((f) * 100000.0f)) -#define OVERSHOOT_TRIM_MPCT_TO_FRAC(i) ((float)(i) * 0.00001f) +#if ADAPTIVE_WARMUP_ENABLE +#define DITHER_STEP_DEFAULT 2 /* index into step table → 50 % duty */ +#define DITHER_STEP_COUNT 4 /* steps 0–3: 100 / 75 / 50 / 25 % */ #endif /***************************************************************************** @@ -84,8 +88,8 @@ extern bool tempLock; extern bool preInfusion; extern float tempCelsius; -#if OVERSHOOT_DETECT_ENABLE -extern float overshoot_trim_stored; +#if ADAPTIVE_WARMUP_ENABLE +extern int dither_step; /* current step index, 0 = most power */ #endif /***************************************************************************** @@ -95,7 +99,7 @@ extern float overshoot_trim_stored; extern esp_rmaker_param_t *primary; extern esp_rmaker_param_t *status_param; extern esp_rmaker_param_t *poweron_param; -#if OVERSHOOT_DETECT_ENABLE +#if ADAPTIVE_WARMUP_ENABLE extern esp_rmaker_param_t *overshoot_disp_param; #endif @@ -113,7 +117,8 @@ void boiler_status_report(void); void control_on_power_on(void); void control_on_power_off(void); -#if OVERSHOOT_DETECT_ENABLE -void overshoot_disp_report(void); -void control_reset_overshoot_trim(void); +#if ADAPTIVE_WARMUP_ENABLE +void dither_disp_report(void); +void control_reset_dither(void); +int dither_step_pct(void); /* current duty in % (75 / 66 / 50 / 33 / 25) */ #endif diff --git a/main/nvs.c b/main/nvs.c index 31625c1..5fc8f9a 100644 --- a/main/nvs.c +++ b/main/nvs.c @@ -1,8 +1,8 @@ /* Espresso — NVS persistence - Stores and restores user-configurable parameters and the overshoot - power-trim value that survives power cycles. + Stores and restores user-configurable parameters and the adaptive + dither step that survives power cycles. */ #include @@ -15,8 +15,8 @@ static const char *TAG = "espresso"; -#if OVERSHOOT_DETECT_ENABLE -static void nvs_read_overshoot_trim(nvs_handle_t h); +#if ADAPTIVE_WARMUP_ENABLE +static void nvs_read_dither_step(nvs_handle_t h); #endif /***************************************************************************** @@ -54,8 +54,8 @@ void nvsRead(void) nvs_get_i32(nvs_handle, "preInfOnTime", &preInfOnTime); nvs_get_i32(nvs_handle, "preInfOffTime", &preInfOffTime); -#if OVERSHOOT_DETECT_ENABLE - nvs_read_overshoot_trim(nvs_handle); +#if ADAPTIVE_WARMUP_ENABLE + nvs_read_dither_step(nvs_handle); #endif nvs_close(nvs_handle); @@ -79,7 +79,7 @@ void nvsWrite(void) ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOnTime", preInfOnTime)); ESP_ERROR_CHECK(nvs_set_i32(nvs_handle, "preInfOffTime", preInfOffTime)); - /* overshoot trim is written only from nvs_persist_overshoot_trim() on commit */ + /* dither step is written only from nvs_persist_dither_step() on commit */ ESP_LOGI(TAG, "Committing updates in NVS..."); ESP_ERROR_CHECK(nvs_commit(nvs_handle)); @@ -87,45 +87,26 @@ void nvsWrite(void) } /***************************************************************************** - * Overshoot trim NVS helpers + * Adaptive dither step NVS helpers *****************************************************************************/ -#if OVERSHOOT_DETECT_ENABLE +#if ADAPTIVE_WARMUP_ENABLE -/* Trim stored as milli-percent in NVS (int32); 100 = 0.1% */ -static void nvs_read_overshoot_trim(nvs_handle_t h) +static void nvs_read_dither_step(nvs_handle_t h) { - int32_t mpct = 0; - - if (nvs_get_i32(h, "trimMp", &mpct) == ESP_OK && mpct >= 0) { - overshoot_trim_stored = fminf(OVERSHOOT_TRIM_MPCT_TO_FRAC(mpct), - OVERSHOOT_TRIM_FRAC_MAX); - ESP_LOGI(TAG, "Warmup trim loaded: -%.2f%%", - (double)(overshoot_trim_stored * 100.0f)); - return; - } - - /* Legacy: "pkOsm" stored cumulative °C; migrate once to "trimMp" then erase. */ - int32_t milli_c = 0; - if (nvs_get_i32(h, "pkOsm", &milli_c) == ESP_OK && milli_c > 0) { - const float cumulative_c = (float)milli_c * 0.001f; - overshoot_trim_stored = fminf(OVERSHOOT_TRIM_FRAC_PER_DEG * cumulative_c, - OVERSHOOT_TRIM_FRAC_MAX); - esp_err_t e = nvs_set_i32(h, "trimMp", - OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); - if (e == ESP_OK) { - nvs_erase_key(h, "pkOsm"); - nvs_commit(h); - } - ESP_LOGI(TAG, "Warmup trim migrated: pkOsm=%.2f°C -> -%.2f%%", - (double)cumulative_c, (double)(overshoot_trim_stored * 100.0f)); - return; + int32_t step = DITHER_STEP_DEFAULT; + if (nvs_get_i32(h, "dithStep", &step) == ESP_OK) { + if (step < 0) step = 0; + if (step >= DITHER_STEP_COUNT) step = DITHER_STEP_COUNT - 1; + dither_step = (int)step; + ESP_LOGI(TAG, "Dither step loaded: %d", dither_step); + } else { + dither_step = DITHER_STEP_DEFAULT; } - - overshoot_trim_stored = 0.f; + /* Legacy keys (trimMp, pkOsm) are left in NVS and ignored. */ } -void nvs_persist_overshoot_trim(void) +void nvs_persist_dither_step(void) { nvs_handle_t h; @@ -133,15 +114,14 @@ void nvs_persist_overshoot_trim(void) return; } - esp_err_t e = nvs_set_i32(h, "trimMp", - OVERSHOOT_TRIM_FRAC_TO_MPCT(overshoot_trim_stored)); + esp_err_t e = nvs_set_i32(h, "dithStep", (int32_t)dither_step); if (e == ESP_OK) { e = nvs_commit(h); } if (e != ESP_OK) { - ESP_LOGW(TAG, "trimMp NVS save failed: %s", esp_err_to_name(e)); + ESP_LOGW(TAG, "dithStep NVS save failed: %s", esp_err_to_name(e)); } nvs_close(h); } -#endif /* OVERSHOOT_DETECT_ENABLE */ +#endif /* ADAPTIVE_WARMUP_ENABLE */ diff --git a/main/nvs.h b/main/nvs.h index 41bab11..0e23007 100644 --- a/main/nvs.h +++ b/main/nvs.h @@ -3,11 +3,11 @@ */ #pragma once -#include "control.h" /* for OVERSHOOT_DETECT_ENABLE */ +#include "control.h" /* for ADAPTIVE_WARMUP_ENABLE */ void nvsRead(void); void nvsWrite(void); -#if OVERSHOOT_DETECT_ENABLE -void nvs_persist_overshoot_trim(void); +#if ADAPTIVE_WARMUP_ENABLE +void nvs_persist_dither_step(void); #endif diff --git a/main/rainmaker.c b/main/rainmaker.c index 08735d9..3284de6 100644 --- a/main/rainmaker.c +++ b/main/rainmaker.c @@ -97,10 +97,10 @@ static esp_err_t write_cb(const esp_rmaker_device_t *device, preInfOffTime = val.val.i; ESP_LOGI(TAG, "Pre-Infusion off time: %d s", preInfOffTime); -#if OVERSHOOT_DETECT_ENABLE - } else if (strcmp(name, "Reset power trim") == 0) { +#if ADAPTIVE_WARMUP_ENABLE + } else if (strcmp(name, "Reset power factor") == 0) { if (val.val.b) { - control_reset_overshoot_trim(); + control_reset_dither(); } #endif } @@ -321,19 +321,18 @@ void rainmaker_init(esp_rmaker_node_t *node) esp_rmaker_int(1)); esp_rmaker_device_add_param(espresso_device, p); -#if OVERSHOOT_DETECT_ENABLE - /* Warmup power trim display */ - static char os_disp_str[28]; - snprintf(os_disp_str, sizeof(os_disp_str), "%.1f%%", - (double)(overshoot_trim_stored * 100.0f)); - overshoot_disp_param = esp_rmaker_param_create("Warmup power trim", NULL, - esp_rmaker_str(os_disp_str), +#if ADAPTIVE_WARMUP_ENABLE + /* Approach dither display */ + static char dith_disp_str[20]; + snprintf(dith_disp_str, sizeof(dith_disp_str), "%d%%", dither_step_pct()); + overshoot_disp_param = esp_rmaker_param_create("Power factor (dither)", NULL, + esp_rmaker_str(dith_disp_str), PROP_FLAG_READ); esp_rmaker_param_add_ui_type(overshoot_disp_param, ESP_RMAKER_UI_TEXT); esp_rmaker_device_add_param(espresso_device, overshoot_disp_param); - /* Reset trim trigger */ - p = esp_rmaker_param_create("Reset power trim", NULL, + /* Reset dither trigger */ + p = esp_rmaker_param_create("Reset power factor", NULL, esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE); esp_rmaker_param_add_ui_type(p, ESP_RMAKER_UI_TRIGGER); diff --git a/main/tasks.c b/main/tasks.c index 2889427..4e45b43 100644 --- a/main/tasks.c +++ b/main/tasks.c @@ -52,8 +52,8 @@ static void task_5000ms(void *arg) /* Only report when the device is on to conserve MQTT budget. */ temp_status_line_report(); boiler_status_report(); -#if OVERSHOOT_DETECT_ENABLE - overshoot_disp_report(); +#if ADAPTIVE_WARMUP_ENABLE + dither_disp_report(); #endif } xSemaphoreGive(g_state_mutex);