From e9666652d4e9a410b08a513c4067d1034094dbae Mon Sep 17 00:00:00 2001 From: Noran Raskin Date: Sat, 1 Nov 2025 18:17:13 +0100 Subject: [PATCH 1/2] add export.fish --- export.fish | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 export.fish diff --git a/export.fish b/export.fish new file mode 100644 index 0000000000..cae1276da1 --- /dev/null +++ b/export.fish @@ -0,0 +1,76 @@ +# This script should be sourced, not executed. + +function realpath_int --description "Simplified realpath helper" --argument-names arg + if test -z "$arg" + return 1 + end + + set -l wdir $PWD + if test "$PWD" = "/" + set wdir "" + end + + if string match -rq '^~' -- $arg + set arg (string replace -r '^~' $HOME -- $arg) + end + + set -l scriptdir "" + switch $arg + case '/*' + set scriptdir $arg + case '*' + set -l stripped (string replace -r '^\./' '' -- $arg) + set scriptdir "$wdir/$stripped" + end + + set scriptdir (string replace -r '/[^/]*$' '' -- $scriptdir) + if test -z "$scriptdir" + set scriptdir "/" + end + echo $scriptdir +end + +function esp_matter_export_main --description "Initialize ESP Matter environment variables for fish" + if not set -q ESP_MATTER_PATH + set -l self_path (status --current-filename) + + if test -z "$self_path" + echo "Could not detect ESP_MATTER_PATH. Please set it before sourcing this script:" 1>&2 + echo " set -gx ESP_MATTER_PATH " 1>&2 + return 1 + end + + set -l uname_out (uname -s) + set -l script_dir "" + if string match -q 'Darwin*' -- $uname_out + set -l script_dir_guess (realpath_int "$self_path") + set script_dir (begin; builtin cd "$script_dir_guess"; pwd; end) + else + set -l script_name (command readlink -f "$self_path" 2>/dev/null) + if test $status -eq 0; and test -n "$script_name" + set script_dir (command dirname "$script_name") + end + if test -z "$script_dir" + set script_dir (realpath_int "$self_path") + set script_dir (begin; builtin cd "$script_dir"; pwd; end) + end + end + + set -gx ESP_MATTER_PATH "$script_dir" + echo "Setting ESP_MATTER_PATH to '$ESP_MATTER_PATH'" + end + + set -l gn_path "$ESP_MATTER_PATH/connectedhomeip/connectedhomeip/.environment/cipd/packages/pigweed/" + if not contains -- $gn_path $PATH + set -gx PATH $PATH $gn_path + end + + set -l host_path "$ESP_MATTER_PATH/connectedhomeip/connectedhomeip/out/host" + if not contains -- $host_path $PATH + set -gx PATH $PATH $host_path + end + + set -gx ZAP_INSTALL_PATH "$ESP_MATTER_PATH/connectedhomeip/connectedhomeip/.environment/cipd/packages/zap" +end + +esp_matter_export_main From d70ef57373fc6fe87b4dfb2f30dad7b27a5c351e Mon Sep 17 00:00:00 2001 From: Noran Raskin Date: Thu, 29 Jan 2026 23:31:53 +0100 Subject: [PATCH 2/2] add stateful light switch (a composite device with dimmer switch and dimmable light on the same endpoint) --- examples/stateful_light_switch/CMakeLists.txt | 34 ++ examples/stateful_light_switch/README.md | 87 +++++ .../stateful_light_switch/main/CMakeLists.txt | 11 + .../main/Kconfig.projbuild | 39 +++ .../stateful_light_switch/main/app_driver.cpp | 325 ++++++++++++++++++ .../stateful_light_switch/main/app_main.cpp | 138 ++++++++ .../stateful_light_switch/main/app_priv.h | 56 +++ .../dynamic_commissionable_data_provider.cpp | 116 +++++++ .../dynamic_commissionable_data_provider.h | 39 +++ .../main/idf_component.yml | 12 + examples/stateful_light_switch/partitions.csv | 10 + .../stateful_light_switch/partitions_c6.csv | 9 + .../stateful_light_switch/sdkconfig.defaults | 51 +++ .../sdkconfig.defaults.esp32c2 | 188 ++++++++++ .../sdkconfig.defaults.esp32c2.v5.1 | 4 + .../sdkconfig.defaults.esp32c2.v5.2 | 4 + .../sdkconfig.defaults.esp32c2.v5.4 | 4 + .../sdkconfig.defaults.esp32c3 | 8 + .../sdkconfig.defaults.esp32c5 | 8 + .../sdkconfig.defaults.esp32c6 | 8 + .../sdkconfig.defaults.esp32c61 | 10 + .../sdkconfig.defaults.esp32h2 | 28 ++ .../sdkconfig.defaults.esp32p4 | 21 ++ .../sdkconfig.defaults.esp32s3 | 8 + 24 files changed, 1218 insertions(+) create mode 100644 examples/stateful_light_switch/CMakeLists.txt create mode 100644 examples/stateful_light_switch/README.md create mode 100644 examples/stateful_light_switch/main/CMakeLists.txt create mode 100644 examples/stateful_light_switch/main/Kconfig.projbuild create mode 100644 examples/stateful_light_switch/main/app_driver.cpp create mode 100644 examples/stateful_light_switch/main/app_main.cpp create mode 100644 examples/stateful_light_switch/main/app_priv.h create mode 100644 examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.cpp create mode 100644 examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.h create mode 100644 examples/stateful_light_switch/main/idf_component.yml create mode 100644 examples/stateful_light_switch/partitions.csv create mode 100644 examples/stateful_light_switch/partitions_c6.csv create mode 100644 examples/stateful_light_switch/sdkconfig.defaults create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c2 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.1 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.2 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.4 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c3 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c5 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c6 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32c61 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32h2 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32p4 create mode 100644 examples/stateful_light_switch/sdkconfig.defaults.esp32s3 diff --git a/examples/stateful_light_switch/CMakeLists.txt b/examples/stateful_light_switch/CMakeLists.txt new file mode 100644 index 0000000000..11e0c889aa --- /dev/null +++ b/examples/stateful_light_switch/CMakeLists.txt @@ -0,0 +1,34 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +if(NOT DEFINED ENV{ESP_MATTER_PATH}) + message(FATAL_ERROR "Please set ESP_MATTER_PATH to the path of esp-matter repo") +endif(NOT DEFINED ENV{ESP_MATTER_PATH}) + +set(PROJECT_VER "1.0") +set(PROJECT_VER_NUMBER 1) + +set(ESP_MATTER_PATH $ENV{ESP_MATTER_PATH}) +set(MATTER_SDK_PATH ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip) + +# This should be done before using the IDF_TARGET variable. +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +include(${ESP_MATTER_PATH}/examples/common/cmake_common/components_include.cmake) + +set(EXTRA_COMPONENT_DIRS + "${ESP_MATTER_PATH}/examples/common" + "${MATTER_SDK_PATH}/config/esp32/components" + "${ESP_MATTER_PATH}/components" + ${extra_components_dirs_append}) + +project(stateful_light_switch) +if(CONFIG_IDF_TARGET_ESP32C2) + include(relinker) +endif() + +idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H;-Wno-overloaded-virtual" APPEND) +idf_build_set_property(C_COMPILE_OPTIONS "-Os" APPEND) +# For RISCV chips, project_include.cmake sets -Wno-format, but does not clear various +# flags that depend on -Wformat +idf_build_set_property(COMPILE_OPTIONS "-Wno-format-nonliteral;-Wno-format-security" APPEND) diff --git a/examples/stateful_light_switch/README.md b/examples/stateful_light_switch/README.md new file mode 100644 index 0000000000..feabb2a6a9 --- /dev/null +++ b/examples/stateful_light_switch/README.md @@ -0,0 +1,87 @@ +# Stateful Light Switch + +A composite Matter device that combines: +- **Dimmable Light (0x0101)**: Server clusters for On/Off and Level Control, controls local WS2812 LED +- **Dimmer Switch (0x0104)**: Client clusters for sending commands to bound devices + +Both device types are on a single endpoint, allowing the device to act as both a controllable light and a switch controller. + +## Hardware Requirements + +- ESP32-C6 development board +- WS2812 RGB LED on **GPIO 8** (used as white light only) +- Button on **GPIO 9** + +## Device Functionality + +### Matter Clusters + +| Cluster | Role | Purpose | +|---------|------|---------| +| On/Off | Server | Receives on/off commands, controls local LED | +| On/Off | Client | Sends on/off commands to bound devices | +| LevelControl | Server | Receives brightness commands, controls LED brightness | +| LevelControl | Client | Sends brightness commands to bound devices | +| Binding | Server | Stores bindings to remote devices | + +### Button Operations + +| Press Pattern | Action | Description | +|--------------|--------|-------------| +| **Single Short Press** | Toggle | Toggles local LED on/off and sends Toggle command to bound devices | +| **Hold** | Dim Up | Increases brightness gradually. Sends Move command to bound devices at start, Stop on release | +| **Short Press + Hold** | Dim Down | Decreases brightness gradually. Press briefly, then hold within 1.5s to dim down | + +### LED Behavior + +- LED brightness follows the LevelControl CurrentLevel attribute (0-254) +- LED turns off completely when On/Off attribute is OFF +- LED can be controlled locally or remotely from a Matter controller (e.g., Home Assistant) + +## Building and Flashing + +```bash +cd examples/stateful_light_switch +idf.py set-target esp32c6 +idf.py build flash monitor +``` + +## Post Commissioning Setup + +### Bind a Light to the Switch + +After commissioning both devices, update the light's ACL and the switch's binding: + +1. Update light's ACL to allow switch access: +``` +chip-tool accesscontrol write acl '[{"privilege": 5, "authMode": 2, "subjects": [112233, ], "targets": null}]' 0 +``` + +2. Add binding on switch to control the light: +``` +chip-tool binding write binding '[{"node": , "endpoint": 1, "cluster": 6}, {"node": , "endpoint": 1, "cluster": 8}]' 1 +``` + +Note: Cluster 6 = On/Off, Cluster 8 = Level Control + +## Console Commands + +If console is enabled (`CONFIG_ENABLE_CHIP_SHELL=y`): + +### Send commands to bound devices +``` +matter esp bound invoke [parameters] +``` + +Examples: +- Toggle: `matter esp bound invoke 0x1 0x6 0x2` +- On: `matter esp bound invoke 0x1 0x6 0x1` +- Off: `matter esp bound invoke 0x1 0x6 0x0` + +## Troubleshooting + +### "failed to notify the bound cluster changed" +This error appears when no devices are bound to the switch. It is expected behavior and will disappear once you configure bindings. + +### Brightness changes from controller don't work +Ensure the device is commissioned and the controller has proper ACL access. The device processes attribute updates in the PRE_UPDATE callback. diff --git a/examples/stateful_light_switch/main/CMakeLists.txt b/examples/stateful_light_switch/main/CMakeLists.txt new file mode 100644 index 0000000000..36b1bba3aa --- /dev/null +++ b/examples/stateful_light_switch/main/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SRC_DIRS_LIST "." "${ESP_MATTER_PATH}/examples/common/app_reset") + +if (CONFIG_DYNAMIC_PASSCODE_COMMISSIONABLE_DATA_PROVIDER) + list(APPEND SRC_DIRS_LIST "custom_provider") +endif() + + +idf_component_register(SRC_DIRS ${SRC_DIRS_LIST} + PRIV_INCLUDE_DIRS "." "${ESP_MATTER_PATH}/examples/common/utils" "${ESP_MATTER_PATH}/examples/common/app_reset") + +target_compile_options(${COMPONENT_LIB} PRIVATE "-DCHIP_HAVE_CONFIG_H") diff --git a/examples/stateful_light_switch/main/Kconfig.projbuild b/examples/stateful_light_switch/main/Kconfig.projbuild new file mode 100644 index 0000000000..6e3d76f020 --- /dev/null +++ b/examples/stateful_light_switch/main/Kconfig.projbuild @@ -0,0 +1,39 @@ +menu "Example Configuration" + + visible if CUSTOM_COMMISSIONABLE_DATA_PROVIDER + + config DYNAMIC_PASSCODE_COMMISSIONABLE_DATA_PROVIDER + bool "Enable Dynamic Passcode Commissionable Data Provider" + depends on CUSTOM_COMMISSIONABLE_DATA_PROVIDER + default y + + config DYNAMIC_PASSCODE_PROVIDER_DISCRIMINATOR + int "Discriminator in Dynamic Passcode Commissionable Data Provider" + depends on DYNAMIC_PASSCODE_COMMISSIONABLE_DATA_PROVIDER + default 3840 + range 0 4095 + help + Fixed discriminator in custom dynamic passcode commissionable data provider + + config DYNAMIC_PASSCODE_PROVIDER_ITERATIONS + int "Iterations in Dynamic Passcode Commissionable Data Provider" + depends on DYNAMIC_PASSCODE_COMMISSIONABLE_DATA_PROVIDER + default 10000 + range 1000 100000 + help + Fixed iterations in custom dynamic passcode commissionable data provider + + config DYNAMIC_PASSCODE_PROVIDER_SALT_BASE64 + string "Base64-Encoded Salt in Dynamic Passcode Commissionable Data Provider" + depends on DYNAMIC_PASSCODE_COMMISSIONABLE_DATA_PROVIDER + default "0NHS09TV1tfY2drb3N3e36ChoqOkpaanqKmqq6ytrq8=" + help + Fixed salt in custom dynamic passcode commissionable data provider. It should be a Base64-Encoded string. + + config SUBSCRIBE_TO_ON_OFF_SERVER_AFTER_BINDING + bool "Enable subscribe to on/off server after binding" + default n + help + "Enables auto subscription to on/off server from client on change in binding" + +endmenu diff --git a/examples/stateful_light_switch/main/app_driver.cpp b/examples/stateful_light_switch/main/app_driver.cpp new file mode 100644 index 0000000000..080601983e --- /dev/null +++ b/examples/stateful_light_switch/main/app_driver.cpp @@ -0,0 +1,325 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "app_priv.h" +#include "button_gpio.h" +#include "esp_matter.h" +#include "esp_matter_client.h" +#include "iot_button.h" +#include "led_strip.h" +#include +#include +#include +#include +#include "bsp/esp-bsp.h" + +using namespace chip::app::Clusters; +using namespace esp_matter; +using namespace esp_matter::cluster; + +static const char *TAG = "app_driver"; +extern uint16_t switch_endpoint_id; + +// GPIO Config +#define BUTTON_GPIO_PIN GPIO_NUM_9 +#define LED_GPIO_PIN GPIO_NUM_8 + +// Button Timings +#define BUTTON_SHORT_PRESS_TIME_MS 500 +#define BUTTON_LONG_PRESS_TIME_MS 1000 + +// Dimming Config +#define DIMMING_RATE 42 // Levels per second (Target ~6s for 255 levels) +#define DIMMING_TIMER_PERIOD_MS 100 +#define DIMMING_STEP (DIMMING_RATE / (1000 / DIMMING_TIMER_PERIOD_MS)) // ~4 levels per tick + +static led_strip_handle_t s_led_strip = NULL; +static TimerHandle_t s_dimming_timer = NULL; +static int s_dimming_direction = 0; // 1 = Up, -1 = Down +static int64_t s_last_short_press_time = 0; +static bool s_is_dimming = false; // Track if dimming is in progress +static bool s_pending_dim_direction_set = false; // Flag to know if we should dim + +// Command Data Helpers +struct MoveParams { + uint8_t move_mode; + uint8_t rate; + uint8_t options_mask; + uint8_t options_override; +}; + +static MoveParams s_move_params; + +static void app_driver_update_led(uint8_t val) +{ + if (s_led_strip) { + // Simple mapping: val is brightness. If OnOff is OFF, this function might be called with 0 or we handle it in + // attribute_update Current implementation assumes this is called with the effective brightness + led_strip_set_pixel(s_led_strip, 0, val, val, val); // White + led_strip_refresh(s_led_strip); + } +} + +static void dimming_timer_callback(TimerHandle_t xTimer) +{ + // Get current level + node_t *node = node::get(); + endpoint_t *endpoint = endpoint::get(node, switch_endpoint_id); + cluster_t *cluster = cluster::get(endpoint, LevelControl::Id); + attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + uint8_t current_level = val.val.u8; + + int new_level = current_level + (s_dimming_direction * DIMMING_STEP); + if (new_level > 254) + new_level = 254; + if (new_level < 1) + new_level = 1; + + if (new_level != current_level) { + val.val.u8 = (uint8_t)new_level; + attribute::update(switch_endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val); + } + + if (new_level == 254 || new_level == 1) { + // Stop timer if reached limits + xTimerStop(s_dimming_timer, 0); + } +} + +// Client Callback +static void app_driver_client_invoke_command_callback(client::peer_device_t *peer_device, + client::request_handle_t *req_handle, void *priv_data) +{ + if (req_handle->type == esp_matter::client::INVOKE_CMD) { + char command_data_str[128]; + strcpy(command_data_str, "{}"); + + if (req_handle->command_path.mClusterId == OnOff::Id) { + // Toggle usually has no fields + } else if (req_handle->command_path.mClusterId == LevelControl::Id) { + if (req_handle->command_path.mCommandId == LevelControl::Commands::Move::Id) { + MoveParams *params = (MoveParams *)req_handle->request_data; + snprintf(command_data_str, sizeof(command_data_str), + "{\"0:U8\": %d, \"1:U8\": %d, \"2:U8\": %d, \"3:U8\": %d}", params->move_mode, params->rate, + params->options_mask, params->options_override); + } else if (req_handle->command_path.mCommandId == LevelControl::Commands::Stop::Id) { + snprintf(command_data_str, sizeof(command_data_str), + "{\"0:U8\": 0, \"1:U8\": 0}"); // OptionsMask, OptionsOverride + } + } + + client::interaction::invoke::send_request(NULL, peer_device, req_handle->command_path, command_data_str, NULL, + NULL, chip::NullOptional); + } +} + +static void app_driver_client_callback(client::peer_device_t *peer_device, client::request_handle_t *req_handle, + void *priv_data) +{ + if (req_handle->type == esp_matter::client::INVOKE_CMD) { + app_driver_client_invoke_command_callback(peer_device, req_handle, priv_data); + } +} + +static void app_driver_client_group_invoke_callback(uint8_t fabric_index, client::request_handle_t *req_handle, + void *priv_data) +{ + if (req_handle->type != esp_matter::client::INVOKE_CMD) { + return; + } + char command_data_str[128]; + strcpy(command_data_str, "{}"); + + if (req_handle->command_path.mClusterId == OnOff::Id) { + // Toggle command has no fields + } else if (req_handle->command_path.mClusterId == LevelControl::Id) { + if (req_handle->command_path.mCommandId == LevelControl::Commands::Move::Id) { + MoveParams *params = (MoveParams *)req_handle->request_data; + snprintf(command_data_str, sizeof(command_data_str), + "{\"0:U8\": %d, \"1:U8\": %d, \"2:U8\": %d, \"3:U8\": %d}", params->move_mode, params->rate, + params->options_mask, params->options_override); + } else if (req_handle->command_path.mCommandId == LevelControl::Commands::Stop::Id) { + snprintf(command_data_str, sizeof(command_data_str), + "{\"0:U8\": 0, \"1:U8\": 0}"); + } + } else { + ESP_LOGW(TAG, "Unsupported cluster for group command"); + return; + } + client::interaction::invoke::send_group_request(fabric_index, req_handle->command_path, command_data_str); +} + +// Button Callbacks +static void app_driver_button_press_down_cb(void *arg, void *data) +{ + // Determine dimming direction when button is pressed + // If there was a recent short press (within 1.5s), we'll dim down + int64_t now = esp_timer_get_time() / 1000; + bool recent_short_press = (now - s_last_short_press_time) < 1500; + s_dimming_direction = recent_short_press ? -1 : 1; + s_pending_dim_direction_set = true; + ESP_LOGI(TAG, "Button Press Down (dim direction: %s)", recent_short_press ? "down" : "up"); +} + +static void app_driver_button_toggle_cb(void *arg, void *data) +{ + ESP_LOGI(TAG, "Button Toggle (Short Press)"); + s_last_short_press_time = esp_timer_get_time() / 1000; + s_pending_dim_direction_set = false; // Cancel any pending dim + + // Local Update + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute::get(switch_endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id), &val); + val.val.b = !val.val.b; + attribute::update(switch_endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val); + + // Client Command + client::request_handle_t req_handle; + req_handle.type = esp_matter::client::INVOKE_CMD; + req_handle.command_path.mClusterId = OnOff::Id; + req_handle.command_path.mCommandId = OnOff::Commands::Toggle::Id; + + lock::chip_stack_lock(portMAX_DELAY); + client::cluster_update(switch_endpoint_id, &req_handle); + lock::chip_stack_unlock(); +} + +static void app_driver_button_press_hold_cb(void *arg, void *data) +{ + ESP_LOGI(TAG, "Button Hold Start (direction: %s)", s_dimming_direction == -1 ? "down" : "up"); + s_is_dimming = true; + + // Start Local Timer + if (s_dimming_timer) { + xTimerStart(s_dimming_timer, 0); + } + + // Client Command + client::request_handle_t req_handle; + req_handle.type = esp_matter::client::INVOKE_CMD; + req_handle.command_path.mClusterId = LevelControl::Id; + req_handle.command_path.mCommandId = LevelControl::Commands::Move::Id; + + s_move_params.move_mode = (s_dimming_direction == -1) ? 1 : 0; // 0=Up, 1=Down + s_move_params.rate = DIMMING_RATE; + s_move_params.options_mask = 0; + s_move_params.options_override = 0; + req_handle.request_data = &s_move_params; + + lock::chip_stack_lock(portMAX_DELAY); + client::cluster_update(switch_endpoint_id, &req_handle); + lock::chip_stack_unlock(); +} + +static void app_driver_button_release_cb(void *arg, void *data) +{ + ESP_LOGI(TAG, "Button Release"); + + // Stop Local Timer + if (s_dimming_timer) { + xTimerStop(s_dimming_timer, 0); + } + + // Only send Stop command if we were actually dimming + if (!s_is_dimming) { + return; + } + s_is_dimming = false; + + client::request_handle_t req_handle; + req_handle.type = esp_matter::client::INVOKE_CMD; + req_handle.command_path.mClusterId = LevelControl::Id; + req_handle.command_path.mCommandId = LevelControl::Commands::Stop::Id; + + lock::chip_stack_lock(portMAX_DELAY); + client::cluster_update(switch_endpoint_id, &req_handle); + lock::chip_stack_unlock(); +} + +esp_err_t app_driver_attribute_update(app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id, + uint32_t attribute_id, esp_matter_attr_val_t *val) +{ + if (endpoint_id == switch_endpoint_id) { + if (cluster_id == OnOff::Id && attribute_id == OnOff::Attributes::OnOff::Id) { + bool on = val->val.b; + if (on) { + // Restore brightness? Or just Max? + // Get current level + esp_matter_attr_val_t level_val = esp_matter_invalid(NULL); + attribute::get_val( + attribute::get(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id), + &level_val); + app_driver_update_led(level_val.val.u8); + } else { + app_driver_update_led(0); + } + } else if (cluster_id == LevelControl::Id && attribute_id == LevelControl::Attributes::CurrentLevel::Id) { + uint8_t level = val->val.u8; + // Check OnOff state? + esp_matter_attr_val_t onoff_val = esp_matter_invalid(NULL); + attribute::get_val(attribute::get(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id), &onoff_val); + if (onoff_val.val.b) { + app_driver_update_led(level); + } + } + } + return ESP_OK; +} + +app_driver_handle_t app_driver_init() +{ + // LED Init + led_strip_config_t strip_config = { + .strip_gpio_num = LED_GPIO_PIN, + .max_leds = 1, + .led_pixel_format = LED_PIXEL_FORMAT_GRB, + .led_model = LED_MODEL_WS2812, + .flags = {.invert_out = false}, // Correct struct initialization + }; + // RMT backend config + led_strip_rmt_config_t rmt_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .resolution_hz = 10 * 1000 * 1000, // 10MHz + .flags = {.with_dma = false}, + }; + ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &s_led_strip)); + led_strip_clear(s_led_strip); + + // Button Init + button_config_t btn_cfg = { + .long_press_time = BUTTON_LONG_PRESS_TIME_MS, + .short_press_time = BUTTON_SHORT_PRESS_TIME_MS, + }; + button_gpio_config_t btn_gpio_cfg = { + .gpio_num = BUTTON_GPIO_PIN, + .active_level = 0, + }; + + button_handle_t btn = NULL; + esp_err_t err = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn); + if (err == ESP_OK) { + iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, app_driver_button_press_down_cb, NULL); + iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, app_driver_button_toggle_cb, NULL); + iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, app_driver_button_press_hold_cb, NULL); + iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, app_driver_button_release_cb, NULL); + } else { + ESP_LOGE(TAG, "Button create failed"); + } + + // Timer Init + s_dimming_timer = + xTimerCreate("DimTimer", pdMS_TO_TICKS(DIMMING_TIMER_PERIOD_MS), pdTRUE, NULL, dimming_timer_callback); + + // Client Config - register both unicast and group callbacks + client::set_request_callback(app_driver_client_callback, app_driver_client_group_invoke_callback, NULL); + + return (app_driver_handle_t)btn; +} diff --git a/examples/stateful_light_switch/main/app_main.cpp b/examples/stateful_light_switch/main/app_main.cpp new file mode 100644 index 0000000000..5f8501b117 --- /dev/null +++ b/examples/stateful_light_switch/main/app_main.cpp @@ -0,0 +1,138 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#include +#endif + +static const char *TAG = "app_main"; +uint16_t switch_endpoint_id = 0; + +using namespace esp_matter; +using namespace esp_matter::attribute; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; + +static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) +{ + switch (event->Type) { + case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: + ESP_LOGI(TAG, "Commissioning complete"); + break; + default: + break; + } +} + +static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id, + uint8_t effect_variant, void *priv_data) +{ + ESP_LOGI(TAG, "Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant); + return ESP_OK; +} + +static esp_err_t app_attribute_update_cb(callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id, + uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data) +{ + if (type == PRE_UPDATE) { + app_driver_handle_t driver_handle = (app_driver_handle_t)priv_data; + app_driver_attribute_update(driver_handle, endpoint_id, cluster_id, attribute_id, val); + } + return ESP_OK; +} + +extern "C" void app_main() +{ + esp_err_t err = ESP_OK; + + /* Initialize the ESP NVS layer */ + nvs_flash_init(); + + /* Initialize driver */ + app_driver_handle_t driver_handle = app_driver_init(); + // Factory reset via button disabled - use console command if needed + + /* Create a Matter node */ + node::config_t node_config; + node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb); + ABORT_APP_ON_FAILURE(node != nullptr, ESP_LOGE(TAG, "Failed to create Matter node")); + + /* Create Dimmable Light Endpoint (Server Clusters: OnOff, LevelControl, Groups, Scenes, Descriptor, Identify) */ + dimmable_light::config_t dimmable_light_config; + dimmable_light_config.on_off.on_off = true; // Default On + dimmable_light_config.level_control.current_level = 254; // Default Max brightness + dimmable_light_config.level_control.on_level = 254; + + endpoint_t *endpoint = + dimmable_light::create(node, &dimmable_light_config, ENDPOINT_FLAG_NONE, driver_handle); + ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create dimmable light endpoint")); + + switch_endpoint_id = endpoint::get_id(endpoint); + ESP_LOGI(TAG, "Dimmable Light created with endpoint_id %d", switch_endpoint_id); + + /* Add Dimmer Switch device type to the same endpoint (creates a composite device) */ + dimmer_switch::config_t dimmer_switch_config; + err = dimmer_switch::add(endpoint, &dimmer_switch_config); + ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to add dimmer switch device type")); + ESP_LOGI(TAG, "Added Dimmer Switch device type to endpoint %d", switch_endpoint_id); + + /* Add Client Clusters for sending commands to bound devices */ + cluster::on_off::config_t on_off_client_config; + cluster::on_off::create(endpoint, &on_off_client_config, CLUSTER_FLAG_CLIENT); + + cluster::level_control::config_t level_control_client_config; + cluster::level_control::create(endpoint, &level_control_client_config, CLUSTER_FLAG_CLIENT); + + cluster::identify::config_t identify_client_config; + cluster::identify::create(endpoint, &identify_client_config, CLUSTER_FLAG_CLIENT); + + /* Add Binding Cluster (Server) - Required to store bindings for client clusters */ + cluster::binding::config_t binding_config; + cluster::binding::create(endpoint, &binding_config, CLUSTER_FLAG_SERVER); + + /* Initialize Binding Manager (Required for sending commands to bound devices) */ + // Note: BindingManager initialization is typically handled internally or lazily, + // but ensuring the structure is ready for the driver is key. + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + esp_openthread_platform_config_t config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }; + set_openthread_platform_config(&config); +#endif + + /* Matter start */ + err = esp_matter::start(app_event_cb); + ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to start Matter, err:%d", err)); + +#if CONFIG_ENABLE_CHIP_SHELL + esp_matter::console::diagnostics_register_commands(); + esp_matter::console::wifi_register_commands(); + esp_matter::console::factoryreset_register_commands(); + esp_matter::console::init(); +#endif +} diff --git a/examples/stateful_light_switch/main/app_priv.h b/examples/stateful_light_switch/main/app_priv.h new file mode 100644 index 0000000000..36a03c1615 --- /dev/null +++ b/examples/stateful_light_switch/main/app_priv.h @@ -0,0 +1,56 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#include "esp_openthread_types.h" +#endif + +typedef void *app_driver_handle_t; + +/** Initialize the application driver (Button + LED) + * + * @return Handle on success. + * @return NULL in case of failure. + */ +app_driver_handle_t app_driver_init(); + +/** Helper to handle attribute updates from the Matter stack + * + * @param endpoint_id Endpoint ID + * @param cluster_id Cluster ID + * @param attribute_id Attribute ID + * @param val Value pointer + */ +esp_err_t app_driver_attribute_update(app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id, + uint32_t attribute_id, esp_matter_attr_val_t *val); + +extern uint16_t switch_endpoint_id; + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_NATIVE, \ + } + +#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ + { \ + .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ + } + +#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ + { \ + .storage_partition_name = "nvs", \ + .netif_queue_size = 10, \ + .task_queue_size = 10, \ + } +#endif diff --git a/examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.cpp b/examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.cpp new file mode 100644 index 0000000000..851b3d97c2 --- /dev/null +++ b/examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.cpp @@ -0,0 +1,116 @@ +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include + +using namespace ::chip::DeviceLayer::Internal; +using namespace ::chip; + +constexpr char *TAG = "custom_provider"; + +CHIP_ERROR dynamic_commissionable_data_provider::GetSetupDiscriminator(uint16_t &setupDiscriminator) +{ + setupDiscriminator = CONFIG_DYNAMIC_PASSCODE_PROVIDER_DISCRIMINATOR; + return CHIP_NO_ERROR; +} + +CHIP_ERROR dynamic_commissionable_data_provider::GetSpake2pIterationCount(uint32_t &iterationCount) +{ + iterationCount = CONFIG_DYNAMIC_PASSCODE_PROVIDER_ITERATIONS; + return CHIP_NO_ERROR; +} + +static bool is_valid_base64_str(const char *str) +{ + const char *base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (!str) { + return false; + } + size_t len = strlen(str); + if (len % 4 != 0) { + return false; + } + size_t padding_len = 0; + if (str[len - 1] == '=') { + padding_len++; + if (str[len - 2] == '=') { + padding_len++; + } + } + for (size_t i = 0; i < len - padding_len; ++i) { + if (strchr(base64_chars, str[i]) == NULL) { + return false; + } + } + return true; +} + +CHIP_ERROR dynamic_commissionable_data_provider::GetSpake2pSalt(MutableByteSpan &saltBuf) +{ + const char *saltB64 = CONFIG_DYNAMIC_PASSCODE_PROVIDER_SALT_BASE64; + VerifyOrReturnError(is_valid_base64_str(saltB64), CHIP_ERROR_INVALID_ARGUMENT); + size_t saltB64Len = strlen(saltB64); + uint8_t salt[chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length]; + size_t saltLen = chip::Base64Decode32(saltB64, saltB64Len, salt); + VerifyOrReturnError(saltLen >= chip::Crypto::kSpake2p_Min_PBKDF_Salt_Length, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(saltLen <= saltBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(saltBuf.data(), salt, saltLen); + saltBuf.reduce_size(saltLen); + return CHIP_NO_ERROR; +} + +CHIP_ERROR dynamic_commissionable_data_provider::GetSpake2pVerifier(MutableByteSpan &verifierBuf, size_t &verifierLen) +{ + uint32_t setupPasscode = 0; + uint32_t iterationCount = 0; + uint8_t salt[Crypto::kSpake2p_Max_PBKDF_Salt_Length] = {0}; + chip::MutableByteSpan saltSpan(salt, Crypto::kSpake2p_Max_PBKDF_Salt_Length); + ReturnErrorOnFailure(GetSetupPasscode(setupPasscode)); + ReturnErrorOnFailure(GetSpake2pIterationCount(iterationCount)); + ReturnErrorOnFailure(GetSpake2pSalt(saltSpan)); + chip::Crypto::Spake2pVerifier verifier; + ReturnErrorOnFailure(verifier.Generate(iterationCount, saltSpan, setupPasscode)); + ReturnErrorOnFailure(verifier.Serialize(verifierBuf)); + verifierLen = verifierBuf.size(); + return CHIP_NO_ERROR; +} + +CHIP_ERROR dynamic_commissionable_data_provider::GetSetupPasscode(uint32_t &setupPasscode) +{ + if (mSetupPasscode == 0) { + ReturnErrorOnFailure(GenerateRandomPasscode(mSetupPasscode)); + } + setupPasscode = mSetupPasscode; + return CHIP_NO_ERROR; +} + +CHIP_ERROR dynamic_commissionable_data_provider::GenerateRandomPasscode(uint32_t &passcode) +{ + ReturnErrorOnFailure(chip::Crypto::DRBG_get_bytes(reinterpret_cast(&passcode), sizeof(passcode))); + // Passcode MUST be 1 to 99999998 + passcode = (passcode % chip::kSetupPINCodeMaximumValue) + 1; + if (!chip::SetupPayload::IsValidSetupPIN(passcode)) { + // if the generated passcode is invalid (11111111, 22222222, 33333333, 44444444, 55555555, 66666666, + // 77777777, 88888888, 12345678, 87654321), increase it by 1 to make it valid. + passcode = passcode + 1; + } + return CHIP_NO_ERROR; +} diff --git a/examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.h b/examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.h new file mode 100644 index 0000000000..602ba24ad9 --- /dev/null +++ b/examples/stateful_light_switch/main/custom_provider/dynamic_commissionable_data_provider.h @@ -0,0 +1,39 @@ +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +using chip::MutableByteSpan; +using chip::DeviceLayer::CommissionableDataProvider; + +class dynamic_commissionable_data_provider : public CommissionableDataProvider { +public: + dynamic_commissionable_data_provider() + : CommissionableDataProvider() {} + + // Members functions that implement the CommissionableDataProvider + CHIP_ERROR GetSetupDiscriminator(uint16_t &setupDiscriminator) override; + CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + CHIP_ERROR GetSpake2pIterationCount(uint32_t &iterationCount) override; + CHIP_ERROR GetSpake2pSalt(MutableByteSpan &saltBuf) override; + CHIP_ERROR GetSpake2pVerifier(MutableByteSpan &verifierBuf, size_t &verifierLen) override; + CHIP_ERROR GetSetupPasscode(uint32_t &setupPasscode) override; + CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override { return CHIP_ERROR_NOT_IMPLEMENTED; } +private: + CHIP_ERROR GenerateRandomPasscode(uint32_t &passcode); + uint32_t mSetupPasscode = 0; +}; diff --git a/examples/stateful_light_switch/main/idf_component.yml b/examples/stateful_light_switch/main/idf_component.yml new file mode 100644 index 0000000000..5901678a56 --- /dev/null +++ b/examples/stateful_light_switch/main/idf_component.yml @@ -0,0 +1,12 @@ +dependencies: + espressif/cmake_utilities: + version: "^1" + rules: # will add "optional_component" only when all if clauses are True + - if: "idf_version >=5.0" + - if: "target in [esp32c2]" + esp_bsp_devkit: + version: "^3" + espressif/led_strip: + version: "^2.0.0" + espressif/button: + version: "^4" diff --git a/examples/stateful_light_switch/partitions.csv b/examples/stateful_light_switch/partitions.csv new file mode 100644 index 0000000000..ffe5f242e7 --- /dev/null +++ b/examples/stateful_light_switch/partitions.csv @@ -0,0 +1,10 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table +esp_secure_cert, 0x3F, ,0xd000, 0x2000, encrypted +nvs, data, nvs, 0x10000, 0xC000, +nvs_keys, data, nvs_keys,, 0x1000, encrypted +otadata, data, ota, , 0x2000 +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, 0x20000, 0x1E0000, +ota_1, app, ota_1, 0x200000, 0x1E0000, +fctry, data, nvs, 0x3E0000, 0x6000 diff --git a/examples/stateful_light_switch/partitions_c6.csv b/examples/stateful_light_switch/partitions_c6.csv new file mode 100644 index 0000000000..a3ae9ab238 --- /dev/null +++ b/examples/stateful_light_switch/partitions_c6.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table +esp_secure_cert, 0x3F, ,0xd000, 0x2000, encrypted +nvs, data, nvs, 0x10000, 0xC000, +nvs_keys, data, nvs_keys, , 0x1000, encrypted +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, , 0x1C0000, +ota_1, app, ota_1, , 0x1C0000, +ot_storage,data, fat, , 0x2000, diff --git a/examples/stateful_light_switch/sdkconfig.defaults b/examples/stateful_light_switch/sdkconfig.defaults new file mode 100644 index 0000000000..74858049cf --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults @@ -0,0 +1,51 @@ +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +#enable BT +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y + +#disable BT connection reattempt +CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n + +#enable lwip ipv6 autoconfig +CONFIG_LWIP_IPV6_AUTOCONFIG=y + +# Use a custom partition table +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0xC000 + +# Enable chip shell +CONFIG_ENABLE_CHIP_SHELL=y + +# This example only use 2 dynamic endpoints +CONFIG_ESP_MATTER_MAX_DYNAMIC_ENDPOINT_COUNT=2 + +CONFIG_ENABLE_WIFI_AP=n + +#enable lwIP route hooks +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y + +# Button +CONFIG_BUTTON_PERIOD_TIME_MS=20 +CONFIG_BUTTON_LONG_PRESS_TIME_MS=5000 + +# disable softap by default +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n + +# Enable OTA Requestor +CONFIG_ENABLE_OTA_REQUESTOR=y + +# Increase CASE session timeout to fix BLE to wifi flakiness +CONFIG_CHIP_CASE_SESSION_TIMEOUT=5000 + +# Enable HKDF in mbedtls +CONFIG_MBEDTLS_HKDF_C=y + +# Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1) +# unique local addresses for fabrics(MAX_FABRIC), a link local address(1) +CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 + +# Buttons +CONFIG_BSP_BUTTONS_NUM=1 diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c2 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..17c1b63a68 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2 @@ -0,0 +1,188 @@ +CONFIG_IDF_TARGET="esp32c2" + +# Compiler options +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_SAVE_RESTORE_LIBCALLS=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y + +## NimBLE Options +CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1 +CONFIG_BT_NIMBLE_MAX_BONDS=2 +CONFIG_BT_NIMBLE_MAX_CCCDS=2 +CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=3072 +CONFIG_BT_NIMBLE_ROLE_CENTRAL=n +CONFIG_BT_NIMBLE_ROLE_OBSERVER=n +CONFIG_BT_NIMBLE_SECURITY_ENABLE=n +CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n +CONFIG_BT_NIMBLE_WHITELIST_SIZE=1 +CONFIG_BT_NIMBLE_GATT_MAX_PROCS=1 +CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=10 +CONFIG_BT_NIMBLE_MSYS_1_BLOCK_SIZE=100 +CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=4 +CONFIG_BT_NIMBLE_MSYS_2_BLOCK_SIZE=320 +CONFIG_BT_NIMBLE_ACL_BUF_COUNT=5 +CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=5 +CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=3 +CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n + +## Controller Options +CONFIG_BT_LE_CONTROLLER_TASK_STACK_SIZE=3072 +CONFIG_BT_LE_LL_RESOLV_LIST_SIZE=1 +CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT=1 + +# Release BT IRAM memory +CONFIG_BT_RELEASE_IRAM=y + +# SPI Configuration +CONFIG_SPI_MASTER_ISR_IN_IRAM=n +CONFIG_SPI_SLAVE_ISR_IN_IRAM=n + +# Ethernet +CONFIG_ETH_USE_SPI_ETHERNET=n + +# Event Loop Library +CONFIG_ESP_EVENT_POST_FROM_ISR=n + +# Chip revision +CONFIG_ESP32C2_REV2_DEVELOPMENT=y + +# Main XTAL Config +CONFIG_XTAL_FREQ_26=y +CONFIG_XTAL_FREQ_40=n + +# ESP Ringbuf +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y + +# ESP System Settings +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=16 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2048 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3072 + +## Memory protection +CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=n + +# High resolution timer (esp_timer) +CONFIG_ESP_TIMER_TASK_STACK_SIZE=2048 + +# Wi-Fi +CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=n +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=3 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=6 +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=6 +CONFIG_ESP32_WIFI_IRAM_OPT=n +CONFIG_ESP32_WIFI_RX_IRAM_OPT=n +CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=n +CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=n +CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=n +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n + +# FreeRTOS +## Kernel +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +## Port +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=n +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y + +# Hardware Abstraction Layer (HAL) and Low Level (LL) +CONFIG_HAL_ASSERTION_DISABLE=y + +# LWIP +CONFIG_LWIP_IPV4=n +CONFIG_LWIP_MAX_SOCKETS=5 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=16 +CONFIG_LWIP_DHCPS=n +CONFIG_LWIP_IPV6_AUTOCONFIG=y +CONFIG_LWIP_MAX_ACTIVE_TCP=5 +CONFIG_LWIP_MAX_LISTENING_TCP=5 +CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=n +CONFIG_LWIP_TCP_SYNMAXRTX=12 +CONFIG_LWIP_TCP_MSL=40000 +CONFIG_LWIP_TCP_FIN_WAIT_TIMEOUT=16000 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=4096 +CONFIG_LWIP_TCP_WND_DEFAULT=2440 +CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS=y +CONFIG_LWIP_TCP_RTO_TIME=1500 +CONFIG_LWIP_MAX_UDP_PCBS=8 +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2560 +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y + +# mbedTLS +CONFIG_MBEDTLS_DYNAMIC_BUFFER=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT=y +CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH=y +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=n +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y +CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y +CONFIG_MBEDTLS_SSL_PROTO_SSL3=n +CONFIG_MBEDTLS_SSL_PROTO_TLS1=n +CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=n + +# ESP-MQTT Configurations +CONFIG_MQTT_PROTOCOL_311=n + +# Protocomm +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=n +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=n + +# SPI Flash driver +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=n +CONFIG_SPI_FLASH_ROM_IMPL=y + +# Websocket +CONFIG_WS_TRANSPORT=n + +# Virtual file system +CONFIG_VFS_SUPPORT_DIR=n +CONFIG_VFS_SUPPORT_SELECT=n +CONFIG_VFS_SUPPORT_TERMIOS=n + +# Wear Levelling +CONFIG_WL_SECTOR_SIZE_512=y + +# CHIP Core +## General Options +CONFIG_MAX_EXCHANGE_CONTEXTS=6 +CONFIG_MAX_BINDINGS=6 +CONFIG_MAX_PEER_NODES=12 +CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS=6 +CONFIG_ENABLE_CHIP_SHELL=n +CONFIG_DISABLE_IPV4=y +CONFIG_BUILD_CHIP_TESTS=n +## Networking Options +CONFIG_NUM_TCP_ENDPOINTS=1 +CONFIG_NUM_UDP_ENDPOINTS=6 +## System Options +CONFIG_NUM_TIMERS=24 +CONFIG_ENABLE_OTA_REQUESTOR=y + +# CHIP Device Layer +## General Options +CONFIG_CHIP_TASK_STACK_SIZE=7168 +CONFIG_MAX_EVENT_QUEUE_SIZE=20 +## Event Logging Options +CONFIG_EVENT_LOGGING_CRIT_BUFFER_SIZE=256 +CONFIG_EVENT_LOGGING_INFO_BUFFER_SIZE=256 +CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE=256 + +# ESP Matter +CONFIG_ESP_MATTER_MAX_DEVICE_TYPE_COUNT=4 +CONFIG_ESP_MATTER_MAX_DYNAMIC_ENDPOINT_COUNT=4 + +# ESP32-C2-DevKitM-1 Settings +# Buttons +CONFIG_BSP_BUTTONS_NUM=1 +CONFIG_BSP_BUTTON_1_TYPE_GPIO=y +CONFIG_BSP_BUTTON_1_GPIO=0 +CONFIG_BSP_BUTTON_1_LEVEL=0 +# LEDs +CONFIG_BSP_LEDS_NUM=1 +CONFIG_BSP_LED_TYPE_RGB_CLASSIC=y +CONFIG_BSP_LED_RGB_RED_GPIO=0 +CONFIG_BSP_LED_RGB_GREEN_GPIO=1 +CONFIG_BSP_LED_RGB_BLUE_GPIO=8 + diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.1 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.1 new file mode 100644 index 0000000000..245ec1227a --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.1 @@ -0,0 +1,4 @@ +# CMake Utilities +CONFIG_CU_RELINKER_ENABLE=y +CONFIG_CU_RELINKER_ENABLE_CUSTOMIZED_CONFIGURATION_FILES=y +CONFIG_CU_RELINKER_CUSTOMIZED_CONFIGURATION_FILES_PATH="../common/relinker/esp32c2_v5.1" diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.2 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.2 new file mode 100644 index 0000000000..5d4ed95fe3 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.2 @@ -0,0 +1,4 @@ +# CMake Utilities +CONFIG_CU_RELINKER_ENABLE=y +CONFIG_CU_RELINKER_ENABLE_CUSTOMIZED_CONFIGURATION_FILES=y +CONFIG_CU_RELINKER_CUSTOMIZED_CONFIGURATION_FILES_PATH="../common/relinker/esp32c2_v5.2" diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.4 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.4 new file mode 100644 index 0000000000..ae37ff83e9 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c2.v5.4 @@ -0,0 +1,4 @@ +# CMake Utilities +CONFIG_CU_RELINKER_ENABLE=y +CONFIG_CU_RELINKER_ENABLE_CUSTOMIZED_CONFIGURATION_FILES=y +CONFIG_CU_RELINKER_CUSTOMIZED_CONFIGURATION_FILES_PATH="../common/relinker/esp32c2_v5.4" diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c3 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..7e8be3dfe3 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c3 @@ -0,0 +1,8 @@ +CONFIG_IDF_TARGET="esp32c3" + +# ESP32-C3-DevKitC-02 Settings +# Buttons +CONFIG_BSP_BUTTONS_NUM=1 +CONFIG_BSP_BUTTON_1_TYPE_GPIO=y +CONFIG_BSP_BUTTON_1_GPIO=9 +CONFIG_BSP_BUTTON_1_LEVEL=0 diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c5 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c5 new file mode 100644 index 0000000000..2686bf2548 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c5 @@ -0,0 +1,8 @@ +CONFIG_IDF_TARGET="esp32c5" + +# ESP32-C5-DevKitC-1 Settings +# Buttons +CONFIG_BSP_BUTTONS_NUM=1 +CONFIG_BSP_BUTTON_1_TYPE_GPIO=y +CONFIG_BSP_BUTTON_1_GPIO=28 +CONFIG_BSP_BUTTON_1_LEVEL=0 diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c6 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c6 new file mode 100644 index 0000000000..47cc3d9918 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c6 @@ -0,0 +1,8 @@ +CONFIG_IDF_TARGET="esp32c6" + +# ESP32-C6-DevKitM-1 Settings +# Buttons +CONFIG_BSP_BUTTONS_NUM=1 +CONFIG_BSP_BUTTON_1_TYPE_GPIO=y +CONFIG_BSP_BUTTON_1_GPIO=9 +CONFIG_BSP_BUTTON_1_LEVEL=0 diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32c61 b/examples/stateful_light_switch/sdkconfig.defaults.esp32c61 new file mode 100644 index 0000000000..d1821a5991 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32c61 @@ -0,0 +1,10 @@ +CONFIG_IDF_TARGET="esp32c61" + +# ESP Ringbuf +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y +# FreeRTOS +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y + +CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32h2 b/examples/stateful_light_switch/sdkconfig.defaults.esp32h2 new file mode 100644 index 0000000000..5d25f8dcbb --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32h2 @@ -0,0 +1,28 @@ +CONFIG_IDF_TARGET="esp32h2" + +# Enable OpenThread +CONFIG_OPENTHREAD_ENABLED=y +CONFIG_OPENTHREAD_SRP_CLIENT=y +CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC=n +CONFIG_OPENTHREAD_LOG_LEVEL_NOTE=y +CONFIG_OPENTHREAD_CLI=n + +# Disable lwip ipv6 autoconfig +CONFIG_LWIP_IPV6_AUTOCONFIG=n + +# LwIP config for OpenThread +CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 +CONFIG_LWIP_MULTICAST_PING=y + +# MDNS platform +CONFIG_USE_MINIMAL_MDNS=n + +# Disable STA for ESP32H2 +CONFIG_ENABLE_WIFI_STATION=n + +# ESP32-H2-DevKitM-1 Settings +# Buttons +CONFIG_BSP_BUTTONS_NUM=1 +CONFIG_BSP_BUTTON_1_TYPE_GPIO=y +CONFIG_BSP_BUTTON_1_GPIO=9 +CONFIG_BSP_BUTTON_1_LEVEL=0 diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32p4 b/examples/stateful_light_switch/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000000..36fd9c46fa --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32p4 @@ -0,0 +1,21 @@ +CONFIG_IDF_TARGET="esp32p4" + +# Enable BLE Host but use remote controller +CONFIG_BT_NIMBLE_TRANSPORT_UART=n +CONFIG_ESP_ENABLE_BT=y + +# Increase main task stack size +CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 + +# Disable Wi-Fi Soft AP +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n + +# Do not deinit BLE after commissioning +CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n + +# ESP32-P4 Function EV Board use ESP32C6 as slave device +CONFIG_SLAVE_IDF_TARGET_ESP32C6=y + +# BSP button +CONFIG_BSP_BUTTONS_NUM=1 +CONFIG_BSP_BUTTON_1_GPIO=35 diff --git a/examples/stateful_light_switch/sdkconfig.defaults.esp32s3 b/examples/stateful_light_switch/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..6fc87d4c31 --- /dev/null +++ b/examples/stateful_light_switch/sdkconfig.defaults.esp32s3 @@ -0,0 +1,8 @@ +CONFIG_IDF_TARGET="esp32s3" + +# ESP32-S3-DevKitC-1 Settings +# Buttons +CONFIG_BSP_BUTTONS_NUM=1 +CONFIG_BSP_BUTTON_1_TYPE_GPIO=y +CONFIG_BSP_BUTTON_1_GPIO=0 +CONFIG_BSP_BUTTON_1_LEVEL=0