From c52f4fa8650e379fafb738b18855c1043b998aab Mon Sep 17 00:00:00 2001 From: WanqQixiang Date: Tue, 20 Aug 2024 16:43:12 +0800 Subject: [PATCH 1/3] component: Remove esp_matter_thread_br component and use logic from upstream repo --- components/esp_matter_console/CMakeLists.txt | 2 +- .../esp_matter_thread_br/CMakeLists.txt | 17 - components/esp_matter_thread_br/Kconfig | 11 - components/esp_matter_thread_br/README.md | 78 ----- .../esp_matter_thread_br_cluster.cpp | 251 --------------- .../esp_matter_thread_br_cluster.h | 72 ----- .../esp_matter_thread_br_console.cpp | 50 --- .../esp_matter_thread_br_console.h | 24 -- .../esp_matter_thread_br_launcher.cpp | 297 ------------------ .../esp_matter_thread_br_launcher.h | 49 --- .../esp_matter_thread_br/idf_component.yml | 10 - components/esp_matter_thread_br/thread_br.xml | 56 ---- examples/controller/CMakeLists.txt | 3 + examples/controller/main/app_main.cpp | 51 ++- examples/controller/main/esp_ot_config.h | 9 + examples/controller/partitions_br.csv | 1 + examples/controller/sdkconfig.defaults.otbr | 12 +- 17 files changed, 59 insertions(+), 934 deletions(-) delete mode 100644 components/esp_matter_thread_br/CMakeLists.txt delete mode 100644 components/esp_matter_thread_br/Kconfig delete mode 100644 components/esp_matter_thread_br/README.md delete mode 100644 components/esp_matter_thread_br/esp_matter_thread_br_cluster.cpp delete mode 100644 components/esp_matter_thread_br/esp_matter_thread_br_cluster.h delete mode 100644 components/esp_matter_thread_br/esp_matter_thread_br_console.cpp delete mode 100644 components/esp_matter_thread_br/esp_matter_thread_br_console.h delete mode 100644 components/esp_matter_thread_br/esp_matter_thread_br_launcher.cpp delete mode 100644 components/esp_matter_thread_br/esp_matter_thread_br_launcher.h delete mode 100644 components/esp_matter_thread_br/idf_component.yml delete mode 100644 components/esp_matter_thread_br/thread_br.xml diff --git a/components/esp_matter_console/CMakeLists.txt b/components/esp_matter_console/CMakeLists.txt index d33d809ba..6555e6a32 100644 --- a/components/esp_matter_console/CMakeLists.txt +++ b/components/esp_matter_console/CMakeLists.txt @@ -4,4 +4,4 @@ if (CONFIG_ENABLE_CHIP_SHELL) endif() idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS . - PRIV_REQUIRES chip mbedtls esp_timer bt openthread) \ No newline at end of file + PRIV_REQUIRES chip mbedtls esp_timer bt openthread esp_rcp_update) diff --git a/components/esp_matter_thread_br/CMakeLists.txt b/components/esp_matter_thread_br/CMakeLists.txt deleted file mode 100644 index 60eab8bd6..000000000 --- a/components/esp_matter_thread_br/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -set(SRCS_LIST ) - -if (CONFIG_OPENTHREAD_BORDER_ROUTER) - list(APPEND SRCS_LIST "esp_matter_thread_br_cluster.cpp" - "esp_matter_thread_br_launcher.cpp") -if (CONFIG_ENABLE_CHIP_SHELL AND CONFIG_OPENTHREAD_CLI) - list(APPEND SRCS_LIST "esp_matter_thread_br_console.cpp") -endif() -endif() - -idf_component_register(SRCS ${SRCS_LIST} - INCLUDE_DIRS "." - REQUIRES esp_matter openthread esp_netif vfs) - -if(CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP) - idf_component_optional_requires(PRIVATE spiffs esp_rcp_update) -endif() \ No newline at end of file diff --git a/components/esp_matter_thread_br/Kconfig b/components/esp_matter_thread_br/Kconfig deleted file mode 100644 index 875c9fafd..000000000 --- a/components/esp_matter_thread_br/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -menu "ESP Matter with Thread BR" - depends on OPENTHREAD_BORDER_ROUTER - - config OPENTHREAD_BR_AUTO_UPDATE_RCP - bool 'Update RCP automatically' - default n - help - If enabled, the Thread Border Router will store the RCP image in its firmware and - compare the stored image version with the running RCP image upon boot. The RCP - will be automatically updated upon version mismatch. -endmenu diff --git a/components/esp_matter_thread_br/README.md b/components/esp_matter_thread_br/README.md deleted file mode 100644 index 2ea9a1eb4..000000000 --- a/components/esp_matter_thread_br/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Thread Border Router Cluster - -The Thread Border Router (BR) Cluster offers an interface for managing the ESP Thread BR. It allows users to perform various tasks such as configuring the dataset of the Thread network that the BR will form or join, start or stop Thread network. - -## 1. Cluster Identifiers - -| Identifier | Name | -|------------|--------------------------| -| 0x131BFC02 | **Thread Border Router** | - -## 2. Data Types - -### 2.1 ThreadRoleEnum Type - -This data type is derived from enum8. - -| Value | Name | Summary | Conformance | -|-------|----------|-------------------------------------------|-------------| -| 0 | Disabled | The Thread is disabled | M | -| 1 | Detached | The Node is detached to a Thread network | M | -| 2 | Child | The Node acts as a Child Role | M | -| 3 | Router | The Node acts as a Router Role | M | -| 4 | Leader | The Node acts as a Leader Role | M | - -## 2. Attributes - -| ID | Name | Type | Constranint | Quality | Default | Access | Conformance | -|--------|-------------------|----------------|-------------|---------|---------|--------|-------------| -| 0x0000 | **DatasetTlvs** | octstr | max254 | N | | R V | M | -| 0x0001 | **Role** | ThreadRoleEnum | | | | R V | M | -| 0x0002 | **BorderAgentId** | octstr | 16 | N | | R V | M | - -### 2.1 DatasetTlvs Attribute - -This attribute stores the dataset Tlvs of the Thread network that the BR will form or join. It will be updated after the ConfigureDatasetTlvs command is handled and the dataset is successfully committed. - -### 2.2 Role Attribute - -This attribute stores the Thread network role of the Thread BR. - -### 2.3 BorderAgentId Attribute - -This attribute stores the the randomly generated Border Agent ID. The typical use case of the ID is to be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this Border Router/Agent device. - -## 3. Commands - -| ID | Name | Direction | Response | Access | Conformance | -|--------|--------------------------|----------------|----------|--------|-------------| -| 0x0000 | **ConfigureDatasetTlvs** | client->server | Y | A | M | -| 0x0001 | **StartThread** | client->server | Y | A | M | -| 0x0002 | **StopThread** | client->server | Y | A | M | - - -### 3.1 ConfigureDatasetTlvs Command - -The ConfigureDatasetTlvs command allows the Thread BR to configure the dataset Tlvs of its Thread network. The DatasetTlvs Attribute will be updated after the dataset is commited. - -The ConfigureDatasetTlvs command SHALL have the following data fields: - -| ID | Name | Type | Constraint | Quality | Default | Comformance | -|----|--------------------|--------|------------|---------|---------|-------------| -| 0 | **DatasetTlvsStr** | string | max508 | | | M | - -#### 3.1.1 DatasetTlvsStr Field - -This field is the dataset tlvs string which will be conmmited. - -### 3.2 StartThread Command - -The StartThread command allows devices to form or join Thread network. - -The StartThread command has no data field. - -### 3.3 StopThread Command - -The StopThread command allows devices to stop its Thread network. - -The StopThread command has no data field. diff --git a/components/esp_matter_thread_br/esp_matter_thread_br_cluster.cpp b/components/esp_matter_thread_br/esp_matter_thread_br_cluster.cpp deleted file mode 100644 index 4cd09f6e8..000000000 --- a/components/esp_matter_thread_br/esp_matter_thread_br_cluster.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// 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 -#include - -#define TAG "thread_br_custom_cluster" - -namespace esp_matter { - -static int hex_digit_to_int(char hex) -{ - if ('A' <= hex && hex <= 'F') { - return 10 + hex - 'A'; - } else if ('a' <= hex && hex <= 'f') { - return 10 + hex - 'a'; - } else if ('0' <= hex && hex <= '9') { - return hex - '0'; - } - return -1; -} - -static size_t hex_str_to_binary(const char *str, size_t str_len, uint8_t *buf, uint8_t buf_size) -{ - if (str_len % 2 != 0 || str_len / 2 > buf_size) { - return 0; - } - for (size_t index = 0; index < str_len / 2; ++index) { - int byte_h = hex_digit_to_int(str[2 * index]); - int byte_l = hex_digit_to_int(str[2 * index + 1]); - if (byte_h < 0 || byte_l < 0) { - return 0; - } - buf[index] = (byte_h << 4) + byte_l; - } - return str_len / 2; -} - -namespace cluster { -namespace thread_br { - -namespace attribute { - -attribute_t *create_dataset_tlvs(cluster_t *cluster, uint8_t *value, uint16_t length) -{ - return esp_matter::attribute::create(cluster, dataset_tlvs::Id, ATTRIBUTE_FLAG_NONVOLATILE, - esp_matter_octet_str(value, length)); -} - -attribute_t *create_role(cluster_t *cluster, uint8_t value) -{ - return esp_matter::attribute::create(cluster, role::Id, ATTRIBUTE_FLAG_NONE, esp_matter_enum8(value)); -} - -attribute_t *create_border_agent_id(cluster_t *cluster, uint8_t *value, uint16_t length) -{ - return esp_matter::attribute::create(cluster, border_agent_id::Id, ATTRIBUTE_FLAG_NONVOLATILE, - esp_matter_octet_str(value, length)); -} - -} // namespace attribute - -namespace command { - -static esp_err_t configure_dataset_tlvs_command_callback(const ConcreteCommandPath &command_path, TLVReader &tlv_data, - void *opaque_ptr) -{ - uint32_t cluster_id = command_path.mClusterId; - uint32_t command_id = command_path.mCommandId; - - if (cluster_id != cluster::thread_br::Id || command_id != cluster::thread_br::command::configure_dataset_tlvs::Id) { - ESP_LOGE(TAG, "Got thread_br command callback for some other command. This should not happen."); - return ESP_FAIL; - } - - chip::CharSpan dataset_tlvs_str; - chip::TLV::TLVType outer; - ESP_RETURN_ON_FALSE(tlv_data.GetType() == chip::TLV::kTLVType_Structure, ESP_FAIL, TAG, - "TLV data is not a structure"); - ESP_RETURN_ON_FALSE(tlv_data.EnterContainer(outer) == CHIP_NO_ERROR, ESP_FAIL, TAG, "Failed to enter container"); - while (tlv_data.Next() == CHIP_NO_ERROR) { - if (!chip::TLV::IsContextTag(tlv_data.GetTag())) { - continue; - } - if (chip::TLV::TagNumFromTag(tlv_data.GetTag()) == 0 && tlv_data.GetType() == chip::TLV::kTLVType_UTF8String) { - chip::app::DataModel::Decode(tlv_data, dataset_tlvs_str); - } - } - ESP_RETURN_ON_FALSE(tlv_data.ExitContainer(outer) == CHIP_NO_ERROR, ESP_FAIL, TAG, "Failed to exit container"); - const char *data = dataset_tlvs_str.data(); - size_t size = dataset_tlvs_str.size(); - otOperationalDatasetTlvs dataset_tlvs; - dataset_tlvs.mLength = hex_str_to_binary(data, size, dataset_tlvs.mTlvs, sizeof(dataset_tlvs.mTlvs)); - ESP_RETURN_ON_FALSE(dataset_tlvs.mLength > 0, ESP_ERR_INVALID_ARG, TAG, "Failed to parse dataset tlvs"); - ESP_RETURN_ON_ERROR(set_thread_dataset_tlvs(&dataset_tlvs), TAG, "Failed to set Thread DatasetTlvs"); - - return ESP_OK; -} - -static esp_err_t start_thread_command_callback(const ConcreteCommandPath &command_path, TLVReader &tlv_data, - void *opaque_ptr) -{ - uint32_t cluster_id = command_path.mClusterId; - uint32_t command_id = command_path.mCommandId; - - if (cluster_id != cluster::thread_br::Id || command_id != cluster::thread_br::command::start_thread::Id) { - ESP_LOGE(TAG, "Got thread_br command callback for some other command. This should not happen."); - return ESP_FAIL; - } - - return set_thread_enabled(true); -} - -static esp_err_t stop_thread_command_callback(const ConcreteCommandPath &command_path, TLVReader &tlv_data, - void *opaque_ptr) -{ - uint32_t cluster_id = command_path.mClusterId; - uint32_t command_id = command_path.mCommandId; - - if (cluster_id != cluster::thread_br::Id || command_id != cluster::thread_br::command::stop_thread::Id) { - ESP_LOGE(TAG, "Got thread_br command callback for some other command. This should not happen."); - return ESP_FAIL; - } - - return set_thread_enabled(false); -} - -command_t *create_configure_dataset_tlvs(cluster_t *cluster) -{ - return esp_matter::command::create(cluster, configure_dataset_tlvs::Id, COMMAND_FLAG_ACCEPTED | COMMAND_FLAG_CUSTOM, - configure_dataset_tlvs_command_callback); -} - -command_t *create_start_thread(cluster_t *cluster) -{ - return esp_matter::command::create(cluster, start_thread::Id, COMMAND_FLAG_ACCEPTED | COMMAND_FLAG_CUSTOM, - start_thread_command_callback); -} - -command_t *create_stop_thread(cluster_t *cluster) -{ - return esp_matter::command::create(cluster, stop_thread::Id, COMMAND_FLAG_ACCEPTED | COMMAND_FLAG_CUSTOM, - stop_thread_command_callback); -} - -} // namespace command - -using chip::app::AttributeAccessInterface; -using chip::app::AttributeValueDecoder; -using chip::app::AttributeValueEncoder; -using chip::app::ConcreteDataAttributePath; -using chip::app::ConcreteReadAttributePath; - -class ThreadBRAttrAccess : public AttributeAccessInterface { -public: - ThreadBRAttrAccess() - : AttributeAccessInterface(chip::Optional::Missing(), cluster::thread_br::Id) - { - } - - CHIP_ERROR Read(const ConcreteReadAttributePath &aPath, AttributeValueEncoder &aEncoder) override - { - if (aPath.mClusterId != cluster::thread_br::Id) { - return CHIP_ERROR_INVALID_ARGUMENT; - } - esp_err_t err = ESP_OK; - if (aPath.mAttributeId == cluster::thread_br::attribute::dataset_tlvs::Id) { - otOperationalDatasetTlvs dataset_tlvs; - err = get_thread_dataset_tlvs(&dataset_tlvs); - if (err != ESP_OK) { - return CHIP_ERROR_INTERNAL; - } - return aEncoder.Encode(chip::ByteSpan(dataset_tlvs.mTlvs, dataset_tlvs.mLength)); - } else if (aPath.mAttributeId == cluster::thread_br::attribute::role::Id) { - uint8_t role = get_thread_role(); - return aEncoder.Encode(role); - } else if (aPath.mAttributeId == cluster::thread_br::attribute::border_agent_id::Id) { - otBorderAgentId border_agent_id; - err = get_border_agent_id(&border_agent_id); - if (err != ESP_OK) { - return CHIP_ERROR_INTERNAL; - } - return aEncoder.Encode(chip::ByteSpan(border_agent_id.mId, sizeof(border_agent_id.mId))); - } - return CHIP_NO_ERROR; - } - - CHIP_ERROR Write(const ConcreteDataAttributePath &aPath, AttributeValueDecoder &aDecoder) override - { - return CHIP_NO_ERROR; - } -}; - -ThreadBRAttrAccess g_attr_access; - -void thread_br_cluster_plugin_server_init_callback() -{ - chip::app::AttributeAccessInterfaceRegistry::Instance().Register(&g_attr_access); -} - -const function_generic_t function_list[] = {}; -const int function_flags = CLUSTER_FLAG_NONE; - -cluster_t *create(endpoint_t *endpoint, uint8_t flags) -{ - cluster_t *cluster = esp_matter::cluster::create(endpoint, Id, CLUSTER_FLAG_SERVER); - if (!cluster) { - ESP_LOGE(TAG, "Could not create cluster"); - return NULL; - } - - set_plugin_server_init_callback(cluster, thread_br_cluster_plugin_server_init_callback); - add_function_list(cluster, function_list, function_flags); - - global::attribute::create_cluster_revision(cluster, 1); - global::attribute::create_feature_map(cluster, 0); - - // Attribute managed internally - attribute::create_dataset_tlvs(cluster, NULL, 0); - attribute::create_role(cluster, 0); - attribute::create_border_agent_id(cluster, NULL, 0); - - command::create_configure_dataset_tlvs(cluster); - command::create_start_thread(cluster); - command::create_stop_thread(cluster); - - return cluster; -} - -} // namespace thread_br -} // namespace cluster - -} // namespace esp_matter diff --git a/components/esp_matter_thread_br/esp_matter_thread_br_cluster.h b/components/esp_matter_thread_br/esp_matter_thread_br_cluster.h deleted file mode 100644 index 754358f7b..000000000 --- a/components/esp_matter_thread_br/esp_matter_thread_br_cluster.h +++ /dev/null @@ -1,72 +0,0 @@ -// 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 -#include - -namespace esp_matter { - -namespace cluster { -namespace thread_br { - -static constexpr chip::ClusterId Id = 0x131BFC02; - -namespace attribute { - -namespace dataset_tlvs { -static constexpr chip::AttributeId Id = 0x00000000; -} // namespace dataset_tlvs -attribute_t *create_dataset_tlvs(cluster_t *cluster, uint8_t *value, uint16_t length); - -namespace role { -static constexpr chip::AttributeId Id = 0x00000001; -} // namespace role -attribute_t *create_role(cluster_t *cluster, uint8_t role); - -namespace border_agent_id { -static constexpr chip::AttributeId Id = 0x00000002; -} // namespace border_agent_id -attribute_t *create_border_agent_id(cluster_t *cluster, uint8_t *value, uint16_t length); - - -} // namespace attribute - -namespace command { - -namespace configure_dataset_tlvs { -static constexpr chip::CommandId Id = 0x00000000; -} // namespace configure_dataset_tlvs -command_t *create_configure_dataset_tlvs(cluster_t *cluster); - -namespace start_thread { -static constexpr chip::CommandId Id = 0x00000001; -} // namespace start_thread -command_t *create_start_thread(cluster_t *cluster); - -namespace stop_thread { -static constexpr chip::CommandId Id = 0x00000002; -} // namespace stop_thread -command_t *create_stop_thread(cluster_t *cluster); - -} // namespace command - -cluster_t *create(endpoint_t *endpoint, uint8_t flags); - -} // namespace thread_br -} // namespace cluster - -} // namespace esp_matter diff --git a/components/esp_matter_thread_br/esp_matter_thread_br_console.cpp b/components/esp_matter_thread_br/esp_matter_thread_br_console.cpp deleted file mode 100644 index 0242f9726..000000000 --- a/components/esp_matter_thread_br/esp_matter_thread_br_console.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// 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 - -namespace esp_matter { -namespace console { - -static esp_err_t thread_br_cli_handler(int argc, char **argv) -{ - ot_cli_buffer_t cli_buf; - memset(&cli_buf, 0, sizeof(cli_buf)); - for (size_t i = 0; i < argc; ++i) { - cli_buf.buf_len += strlen(argv[i]) + (i == argc - 1 ? 0 : 1); - if (cli_buf.buf_len > OPENTHREAD_CLI_BUFFER_LENGTH) { - return ESP_ERR_INVALID_ARG; - } - strcat(cli_buf.buf, argv[i]); - if (i < argc - 1) { - strcat(cli_buf.buf, " "); - } - } - return cli_transmit_task_post(cli_buf); -} - -esp_err_t thread_br_cli_register_command() -{ - static const command_t command = { - .name = "ot_cli", - .description = "OpenThread Cli commands. Usage: matter esp ot_cli .", - .handler = thread_br_cli_handler, - }; - - return add_commands(&command, 1); -} - -} // namespace console -} // namespace esp_matter diff --git a/components/esp_matter_thread_br/esp_matter_thread_br_console.h b/components/esp_matter_thread_br/esp_matter_thread_br_console.h deleted file mode 100644 index a25ce2f6f..000000000 --- a/components/esp_matter_thread_br/esp_matter_thread_br_console.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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 - -namespace esp_matter { -namespace console { - -esp_err_t thread_br_cli_register_command(); - -} // namespace console -} // namespace esp_matter diff --git a/components/esp_matter_thread_br/esp_matter_thread_br_launcher.cpp b/components/esp_matter_thread_br/esp_matter_thread_br_launcher.cpp deleted file mode 100644 index 778309455..000000000 --- a/components/esp_matter_thread_br/esp_matter_thread_br_launcher.cpp +++ /dev/null @@ -1,297 +0,0 @@ -// 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 -#include -#if CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP -#include -#endif -#include -#include -#include -#include - -#include -#include - -#define TAG "thread_br_launcher" - -namespace esp_matter { - -class scoped_thread_lock { -public: - scoped_thread_lock() { esp_openthread_lock_acquire(portMAX_DELAY); } - ~scoped_thread_lock() { esp_openthread_lock_release(); } -}; - -#if CONFIG_OPENTHREAD_CLI -static TaskHandle_t cli_transmit_task = NULL; -static QueueHandle_t cli_transmit_task_queue = NULL; - -static int cli_output_callback(void *context, const char *format, va_list args) -{ - int ret = 0; - char prompt_check[3]; - vsnprintf(prompt_check, sizeof(prompt_check), format, args); - if (!strncmp(prompt_check, "> ", sizeof(prompt_check))) { - if (cli_transmit_task) { - xTaskNotifyGive(cli_transmit_task); - } - } else { - ret = vprintf(format, args); - } - return ret; -} - -static void cli_transmit_worker(void *context) -{ - cli_transmit_task_queue = xQueueCreate(4, sizeof(ot_cli_buffer_t)); - if (!cli_transmit_task_queue) { - vTaskDelete(NULL); - return; - } - while (true) { - ot_cli_buffer_t buffer; - if (xQueueReceive(cli_transmit_task_queue, &buffer, portMAX_DELAY) == pdTRUE) { - esp_openthread_cli_input(buffer.buf); - xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); - } - } -} - -esp_err_t cli_transmit_task_post(ot_cli_buffer_t &cli_buf) -{ - if (!cli_transmit_task_queue || xQueueSend(cli_transmit_task_queue, &cli_buf, portMAX_DELAY) != pdTRUE) { - return ESP_FAIL; - } - return ESP_OK; -} -#endif // CONFIG_OPENTHREAD_CLI - -#if CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP -#define RCP_VERSION_MAX_SIZE 100 - -static void update_rcp(void) -{ - // Deinit uart to transfer UART to the serial loader - esp_openthread_rcp_deinit(); - if (esp_rcp_update() == ESP_OK) { - esp_rcp_mark_image_verified(true); - } else { - esp_rcp_mark_image_verified(false); - } - esp_restart(); -} - -static void try_update_ot_rcp(void) -{ - ESP_LOGW(TAG, "Try to update openthread rcp"); - char internal_rcp_version[RCP_VERSION_MAX_SIZE]; - const char *running_rcp_version = otPlatRadioGetVersionString(esp_openthread_get_instance()); - - if (esp_rcp_load_version_in_storage(internal_rcp_version, sizeof(internal_rcp_version)) == ESP_OK) { - ESP_LOGI(TAG, "Internal RCP Version: %s", internal_rcp_version); - ESP_LOGI(TAG, "Running RCP Version: %s", running_rcp_version); - if (strcmp(internal_rcp_version, running_rcp_version) == 0) { - esp_rcp_mark_image_verified(true); - } else { - update_rcp(); - } - } else { - ESP_LOGI(TAG, "RCP firmware not found in storage, will reboot to try next image"); - esp_rcp_mark_image_verified(false); - esp_restart(); - } -} - -static void rcp_failure_handler(void) -{ - esp_rcp_mark_image_unusable(); - try_update_ot_rcp(); - esp_rcp_reset(); -} - -esp_err_t thread_rcp_update_init(const esp_rcp_update_config_t *update_config) -{ - return esp_rcp_update_init(update_config); -} - -#endif // CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP - -static void ot_task_worker(void *aContext) -{ - esp_openthread_platform_config_t *config = static_cast(aContext); - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - esp_netif_t *openthread_netif = esp_netif_new(&cfg); - assert(openthread_netif != NULL); - - // Initialize the OpenThread stack -#if CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP - esp_openthread_register_rcp_failure_handler(rcp_failure_handler); -#endif - ESP_ERROR_CHECK(esp_openthread_init(config)); -#if CONFIG_OPENTHREAD_CLI - otCliInit(esp_openthread_get_instance(), cli_output_callback, NULL); -#endif - // Initialize border routing features - esp_openthread_lock_acquire(portMAX_DELAY); -#if CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP - try_update_ot_rcp(); -#endif - ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(config))); - ESP_ERROR_CHECK(esp_openthread_border_router_init()); -#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC - (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); -#endif - otInstance *instance = esp_openthread_get_instance(); - if (otDatasetIsCommissioned(instance)) { - (void)otIp6SetEnabled(instance, true); - (void)otThreadSetEnabled(instance, true); - } - esp_openthread_lock_release(); - free(config); - -#if CONFIG_OPENTHREAD_CLI - xTaskCreate(cli_transmit_worker, "ot_cli_task", 3072, NULL, 5, &cli_transmit_task); -#endif - esp_openthread_launch_mainloop(); - // Clean up - esp_netif_destroy(openthread_netif); - esp_openthread_netif_glue_deinit(); - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - -static esp_err_t init_spiffs(void) -{ -#if CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP - esp_vfs_spiffs_conf_t rcp_fw_conf = { - .base_path = "/rcp_fw", .partition_label = "rcp_fw", .max_files = 10, .format_if_mount_failed = false}; - ESP_RETURN_ON_ERROR(esp_vfs_spiffs_register(&rcp_fw_conf), TAG, "Failed to mount rcp firmware storage"); -#endif - return ESP_OK; -} - -esp_err_t thread_br_init(esp_openthread_platform_config_t *config) -{ - static bool thread_br_initialized = false; - if (thread_br_initialized) { - return ESP_OK; - } - // Used eventfds: - // * netif - // * task_queue - // * border router - esp_vfs_eventfd_config_t eventfd_config = { -#if CONFIG_OPENTHREAD_RADIO_NATIVE - // * radio driver - .max_fds = 4, -#else - .max_fds = 3, -#endif - }; - ESP_RETURN_ON_ERROR(esp_vfs_eventfd_register(&eventfd_config), TAG, "Failed to register eventfd"); - ESP_ERROR_CHECK(init_spiffs()); - esp_openthread_set_backbone_netif(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF")); - - esp_openthread_platform_config_t *config_copy = - (esp_openthread_platform_config_t *)malloc(sizeof(esp_openthread_platform_config_t)); - if (!config_copy) { - ESP_LOGE(TAG, "Failed to allocate memory for openthread_platform_config"); - return ESP_ERR_NO_MEM; - } - memcpy(config_copy, config, sizeof(esp_openthread_platform_config_t)); - if (xTaskCreate(ot_task_worker, "ot_br", 8192, config_copy, 5, NULL) != pdTRUE) { - free(config_copy); - return ESP_FAIL; - } - - thread_br_initialized = true; - return ESP_OK; -} - -esp_err_t set_thread_enabled(bool enabled) -{ - scoped_thread_lock lock; - otInstance *instance = esp_openthread_get_instance(); - if (!instance) { - ESP_LOGE(TAG, "Thread not initialized"); - return ESP_ERR_INVALID_STATE; - } - bool is_enabled = (otThreadGetDeviceRole(instance) != OT_DEVICE_ROLE_DISABLED); - bool is_ip6_enabled = otIp6IsEnabled(instance); - - if (enabled && !is_ip6_enabled) { - ESP_RETURN_ON_FALSE(otIp6SetEnabled(instance, enabled) == OT_ERROR_NONE, ESP_FAIL, TAG, "Failed to %s netif", - enabled ? "enable" : "disable"); - } - if (enabled != is_enabled) { - ESP_RETURN_ON_FALSE(otThreadSetEnabled(instance, enabled) == OT_ERROR_NONE, ESP_FAIL, TAG, - "Failed to %s thread", enabled ? "enable" : "disable"); - } - if (!enabled && is_ip6_enabled) { - ESP_RETURN_ON_FALSE(otIp6SetEnabled(instance, enabled) == OT_ERROR_NONE, ESP_FAIL, TAG, "Failed to %s netif", - enabled ? "enable" : "disable"); - } - return ESP_OK; -} - -esp_err_t set_thread_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs) -{ - if (!dataset_tlvs) { - return ESP_ERR_INVALID_ARG; - } - scoped_thread_lock lock; - ESP_RETURN_ON_FALSE(otDatasetSetActiveTlvs(esp_openthread_get_instance(), dataset_tlvs) == OT_ERROR_NONE, ESP_FAIL, - TAG, "Failed to set Thread DatasetTlvs"); - return ESP_OK; -} - -esp_err_t get_thread_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs) -{ - if (!dataset_tlvs) { - return ESP_ERR_INVALID_ARG; - } - scoped_thread_lock lock; - ESP_RETURN_ON_FALSE(otDatasetGetActiveTlvs(esp_openthread_get_instance(), dataset_tlvs) == OT_ERROR_NONE, ESP_FAIL, - TAG, "Failed to get Thread DatasetTlvs"); - return ESP_OK; -} - - -esp_err_t get_border_agent_id(otBorderAgentId *border_agent_id) -{ - if (!border_agent_id) { - return ESP_ERR_INVALID_ARG; - } - scoped_thread_lock lock; - ESP_RETURN_ON_FALSE(otBorderAgentGetId(esp_openthread_get_instance(), border_agent_id) == OT_ERROR_NONE, ESP_FAIL, - TAG, "Failed to get Border Agent Id"); - return ESP_OK; -} - -uint8_t get_thread_role() -{ - scoped_thread_lock lock; - otDeviceRole role = otThreadGetDeviceRole(esp_openthread_get_instance()); - return role; -} - -} // namespace esp_matter diff --git a/components/esp_matter_thread_br/esp_matter_thread_br_launcher.h b/components/esp_matter_thread_br/esp_matter_thread_br_launcher.h deleted file mode 100644 index 2e55b82ee..000000000 --- a/components/esp_matter_thread_br/esp_matter_thread_br_launcher.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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 -#if CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP -#include -#endif -#include - -#define OPENTHREAD_CLI_BUFFER_LENGTH 255 - -namespace esp_matter { - -typedef struct { - char buf[OPENTHREAD_CLI_BUFFER_LENGTH + 1]; - uint8_t buf_len; -} ot_cli_buffer_t; - -esp_err_t thread_br_init(esp_openthread_platform_config_t *config); - -#if CONFIG_OPENTHREAD_BR_AUTO_UPDATE_RCP -esp_err_t thread_rcp_update_init(const esp_rcp_update_config_t *update_config); -#endif - -esp_err_t set_thread_enabled(bool enabled); - -esp_err_t set_thread_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs); - -esp_err_t get_thread_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs); - -esp_err_t get_border_agent_id(otBorderAgentId *border_agent_id); - -uint8_t get_thread_role(); - -esp_err_t cli_transmit_task_post(ot_cli_buffer_t &cli_buf); -} diff --git a/components/esp_matter_thread_br/idf_component.yml b/components/esp_matter_thread_br/idf_component.yml deleted file mode 100644 index d46217f4d..000000000 --- a/components/esp_matter_thread_br/idf_component.yml +++ /dev/null @@ -1,10 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - espressif/esp_rcp_update: - version: "~1.2.0" - rules: - - if: "idf_version >=5.0" - espressif/esp-serial-flasher: - version: "~0.0.0" - rules: - - if: "idf_version >=5.0" diff --git a/components/esp_matter_thread_br/thread_br.xml b/components/esp_matter_thread_br/thread_br.xml deleted file mode 100644 index d588f4e4f..000000000 --- a/components/esp_matter_thread_br/thread_br.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - ThreadBR - CHIP - 0x131BFC02 - THREAD_BR_CLUSTER - - Attributes and commands for ThreadBR cluster. - - - - DatasetTlvs - Role - BorderAgentId - - - ConfigureDatasetTlvs command. - - - - - StartThread command. - - - - StopThread command. - - - - diff --git a/examples/controller/CMakeLists.txt b/examples/controller/CMakeLists.txt index 9b122e1d5..d83cf47cb 100644 --- a/examples/controller/CMakeLists.txt +++ b/examples/controller/CMakeLists.txt @@ -37,6 +37,9 @@ project(controller) 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) +# We don't need Thread Network Commissioning Driver +idf_build_set_property(COMPILE_OPTIONS "-D_NO_GENERIC_THREAD_NETWORK_COMMISSIONING_DRIVER_" 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;-Wformat=0" APPEND) diff --git a/examples/controller/main/app_main.cpp b/examples/controller/main/app_main.cpp index 4acc6accb..0b3802627 100644 --- a/examples/controller/main/app_main.cpp +++ b/examples/controller/main/app_main.cpp @@ -11,19 +11,20 @@ #include #include -#include #include +#include #include #include #include #if CONFIG_OPENTHREAD_BORDER_ROUTER -#include -#include -#include +#include +#include #include +#include +#include #endif // CONFIG_OPENTHREAD_BORDER_ROUTER -#include #include +#include #include #include @@ -45,13 +46,14 @@ static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) if (event->Platform.ESPSystemEvent.Base == IP_EVENT && event->Platform.ESPSystemEvent.Id == IP_EVENT_STA_GOT_IP) { #if CONFIG_OPENTHREAD_BORDER_ROUTER - 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(), - }; - printf("init thread br\n"); - esp_matter::thread_br_init(&config); + static bool sThreadBRInitialized = false; + if (!sThreadBRInitialized) { + esp_openthread_set_backbone_netif(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF")); + esp_openthread_lock_acquire(portMAX_DELAY); + esp_openthread_border_router_init(); + esp_openthread_lock_release(); + sThreadBRInitialized = true; + } #endif } break; @@ -73,10 +75,29 @@ extern "C" void app_main() #if CONFIG_ESP_MATTER_CONTROLLER_ENABLE esp_matter::console::controller_register_commands(); #endif // CONFIG_ESP_MATTER_CONTROLLER_ENABLE -#if CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_OPENTHREAD_CLI - esp_matter::console::thread_br_cli_register_command(); -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_OPENTHREAD_CLI +#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER + esp_matter::console::otcli_register_commands(); +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER #endif // CONFIG_ENABLE_CHIP_SHELL +#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER + esp_vfs_spiffs_conf_t rcp_fw_conf = { + .base_path = "/rcp_fw", .partition_label = "rcp_fw", .max_files = 10, .format_if_mount_failed = false}; + if (ESP_OK != esp_vfs_spiffs_register(&rcp_fw_conf)) { + ESP_LOGE(TAG, "Failed to mount rcp firmware storage"); + return; + } +#ifdef CONFIG_AUTO_UPDATE_RCP + esp_rcp_update_config_t rcp_update_config = ESP_OPENTHREAD_RCP_UPDATE_CONFIG(); + openthread_init_br_rcp(&rcp_update_config); +#endif + /* Set OpenThread platform config */ + 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 // CONFIG_OPENTHREAD_BORDER_ROUTER /* 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)); diff --git a/examples/controller/main/esp_ot_config.h b/examples/controller/main/esp_ot_config.h index dbe997704..76d8faf23 100644 --- a/examples/controller/main/esp_ot_config.h +++ b/examples/controller/main/esp_ot_config.h @@ -81,4 +81,13 @@ { \ .storage_partition_name = "nvs", .netif_queue_size = 10, .task_queue_size = 10, \ } + +#include + +#define ESP_OPENTHREAD_RCP_UPDATE_CONFIG() \ + { \ + .rcp_type = RCP_TYPE_ESP32H2_UART, .uart_rx_pin = 17, .uart_tx_pin = 18, .uart_port = 1, .uart_baudrate = 115200, \ + .reset_pin = 7, .boot_pin = 8, .update_baudrate = 460800, .firmware_dir = "/rcp_fw/ot_rcp", .target_chip = ESP32H2_CHIP, \ + } + #endif // CONFIG_OPENTHREAD_BORDER_ROUTER diff --git a/examples/controller/partitions_br.csv b/examples/controller/partitions_br.csv index 3f1100afa..3d4e1b22f 100644 --- a/examples/controller/partitions_br.csv +++ b/examples/controller/partitions_br.csv @@ -3,3 +3,4 @@ nvs, data, nvs, , 0x6000, phy_init, data, phy, , 0x1000, factory, app, factory, , 3000K, +rcp_fw, data, spiffs, , 600K, diff --git a/examples/controller/sdkconfig.defaults.otbr b/examples/controller/sdkconfig.defaults.otbr index 6e3aa7ea0..0aca1f6d4 100644 --- a/examples/controller/sdkconfig.defaults.otbr +++ b/examples/controller/sdkconfig.defaults.otbr @@ -2,12 +2,17 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -# Enable Openthread Border Router, but don't run matter over Thread +# Enable Openthread Border Router CONFIG_OPENTHREAD_ENABLED=y CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC=n CONFIG_OPENTHREAD_LOG_LEVEL_NOTE=y CONFIG_OPENTHREAD_BORDER_ROUTER=y -CONFIG_ENABLE_MATTER_OVER_THREAD=n +CONFIG_OPENTHREAD_SRP_CLIENT=n +CONFIG_OPENTHREAD_DNS_CLIENT=n +CONFIG_THREAD_TASK_STACK_SIZE=8192 + +# Auto update RCP firmware +CONFIG_AUTO_UPDATE_RCP=y #enable BT CONFIG_BT_ENABLED=y @@ -93,5 +98,6 @@ CONFIG_MBEDTLS_HKDF_C=y # Increase console buffer length CONFIG_CHIP_SHELL_CMD_LINE_BUF_MAX_LENGTH=512 -# Disable WiFi network commissioning driver +# Disable WiFi&Thread network commissioning driver CONFIG_WIFI_NETWORK_COMMISSIONING_DRIVER=n +CONFIG_THREAD_NETWORK_COMMISSIONING_DRIVER=n From 0b13dbb216d2869db4b5523424df027a97ef9962 Mon Sep 17 00:00:00 2001 From: WanqQixiang Date: Wed, 21 Aug 2024 16:04:44 +0800 Subject: [PATCH 2/3] esp_matter: Add thread border router management support and thread border router example --- SUPPORTED_DEVICE_TYPES.md | 3 + .../esp_matter/esp_matter_attribute.cpp | 42 +++++++ components/esp_matter/esp_matter_attribute.h | 11 ++ components/esp_matter/esp_matter_cluster.cpp | 47 +++++++ components/esp_matter/esp_matter_cluster.h | 9 ++ components/esp_matter/esp_matter_command.cpp | 36 ++++++ components/esp_matter/esp_matter_command.h | 10 ++ .../esp_matter_delegate_callbacks.cpp | 21 ++++ .../esp_matter_delegate_callbacks.h | 1 + components/esp_matter/esp_matter_endpoint.cpp | 34 +++++ components/esp_matter/esp_matter_endpoint.h | 16 +++ components/esp_matter/esp_matter_feature.cpp | 31 +++++ components/esp_matter/esp_matter_feature.h | 13 ++ .../private/esp_matter_cluster_revisions.h | 5 + components/esp_matter_console/CMakeLists.txt | 8 +- examples/.build-rules.yml | 6 + .../all_device_types_app/main/app_main.cpp | 29 +++-- .../all_device_types_app/main/device_types.h | 4 +- .../main/esp_matter_console_helpers.cpp | 25 ++++ .../partitions_thread.csv | 6 +- .../sdkconfig.defaults.otbr | 13 +- examples/controller/main/app_main.cpp | 2 +- examples/controller/sdkconfig.defaults.otbr | 3 - examples/thread_border_router/CMakeLists.txt | 38 ++++++ examples/thread_border_router/README.md | 47 +++++++ .../thread_border_router/main/CMakeLists.txt | 5 + .../thread_border_router/main/app_main.cpp | 119 ++++++++++++++++++ .../thread_border_router/main/esp_ot_config.h | 90 +++++++++++++ examples/thread_border_router/partitions.csv | 6 + .../thread_border_router/sdkconfig.defaults | 64 ++++++++++ 30 files changed, 717 insertions(+), 27 deletions(-) create mode 100644 examples/thread_border_router/CMakeLists.txt create mode 100644 examples/thread_border_router/README.md create mode 100644 examples/thread_border_router/main/CMakeLists.txt create mode 100644 examples/thread_border_router/main/app_main.cpp create mode 100644 examples/thread_border_router/main/esp_ot_config.h create mode 100644 examples/thread_border_router/partitions.csv create mode 100644 examples/thread_border_router/sdkconfig.defaults diff --git a/SUPPORTED_DEVICE_TYPES.md b/SUPPORTED_DEVICE_TYPES.md index bbb7ff795..b8a98923c 100644 --- a/SUPPORTED_DEVICE_TYPES.md +++ b/SUPPORTED_DEVICE_TYPES.md @@ -79,3 +79,6 @@ i. Appliances j. Energy 1. EVSE (Electric Vehicle Supply Equipment) + +k. Network Infrastructure +1. Thread Border Router diff --git a/components/esp_matter/esp_matter_attribute.cpp b/components/esp_matter/esp_matter_attribute.cpp index 68e2f712c..6ba96f314 100644 --- a/components/esp_matter/esp_matter_attribute.cpp +++ b/components/esp_matter/esp_matter_attribute.cpp @@ -4705,5 +4705,47 @@ attribute_t *create_allowed_vendor_list(cluster_t *cluster, uint8_t *value, uint } /* attribute */ } /* application_basic */ +namespace thread_border_router_management { +namespace attribute { + +attribute_t *create_border_router_name(cluster_t *cluster, char *value, uint16_t length) +{ + return esp_matter::attribute::create(cluster, ThreadBorderRouterManagement::Attributes::BorderRouterName::Id, + ATTRIBUTE_FLAG_NONE, esp_matter_char_str(value, length)); +} + +attribute_t *create_border_agent_id(cluster_t *cluster, uint8_t *value, uint16_t length) +{ + return esp_matter::attribute::create(cluster, ThreadBorderRouterManagement::Attributes::BorderAgentID::Id, + ATTRIBUTE_FLAG_NONE, esp_matter_octet_str(value, length)); +} + +attribute_t *create_thread_version(cluster_t *cluster, uint16_t value) +{ + return esp_matter::attribute::create(cluster, ThreadBorderRouterManagement::Attributes::ThreadVersion::Id, + ATTRIBUTE_FLAG_NONE, esp_matter_uint16(value)); +} + +attribute_t *create_interface_enabled(cluster_t *cluster, bool value) +{ + return esp_matter::attribute::create(cluster, ThreadBorderRouterManagement::Attributes::InterfaceEnabled::Id, + ATTRIBUTE_FLAG_NONE, esp_matter_bool(value)); +} + +attribute_t *create_active_dataset_timestamp(cluster_t *cluster, nullable value) +{ + return esp_matter::attribute::create(cluster, ThreadBorderRouterManagement::Attributes::ActiveDatasetTimestamp::Id, + ATTRIBUTE_FLAG_NONE, esp_matter_nullable_uint64(value)); +} + +attribute_t *create_pending_dataset_timestamp(cluster_t *cluster, nullable value) +{ + return esp_matter::attribute::create(cluster, ThreadBorderRouterManagement::Attributes::PendingDatasetTimestamp::Id, + ATTRIBUTE_FLAG_NONE, esp_matter_nullable_uint64(value)); +} + +} /* attribute */ +} /* thread_border_router_management */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_attribute.h b/components/esp_matter/esp_matter_attribute.h index 614e5d0d0..f02391309 100644 --- a/components/esp_matter/esp_matter_attribute.h +++ b/components/esp_matter/esp_matter_attribute.h @@ -1121,5 +1121,16 @@ attribute_t *create_allowed_vendor_list(cluster_t *cluster, uint8_t *value, uint } /* attribute */ } /* application_basic */ +namespace thread_border_router_management { +namespace attribute { +attribute_t *create_border_router_name(cluster_t *cluster, char *value, uint16_t length); +attribute_t *create_border_agent_id(cluster_t *cluster, uint8_t *value, uint16_t length); +attribute_t *create_thread_version(cluster_t *cluster, uint16_t value); +attribute_t *create_interface_enabled(cluster_t *cluster, bool value); +attribute_t *create_active_dataset_timestamp(cluster_t *cluster, nullable value); +attribute_t *create_pending_dataset_timestamp(cluster_t *cluster, nullable value); +} /* attribute */ +} /* thread_border_router_management */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_cluster.cpp b/components/esp_matter/esp_matter_cluster.cpp index b728c5481..0aa9f0482 100644 --- a/components/esp_matter/esp_matter_cluster.cpp +++ b/components/esp_matter/esp_matter_cluster.cpp @@ -3545,6 +3545,53 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags, uint32_ } } /* application_basic */ +namespace thread_border_router_management { +const function_generic_t *function_list = NULL; +const int function_flags = CLUSTER_FLAG_NONE; + +cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags, uint32_t features) +{ + cluster_t *cluster = cluster::create(endpoint, ThreadBorderRouterManagement::Id, flags); + if (!cluster) { + ESP_LOGE(TAG, "Could not create cluster"); + return NULL; + } + if (flags & CLUSTER_FLAG_SERVER) { + if (config -> delegate != nullptr) { + static const auto delegate_init_cb = ThreadBorderRouterManagementDelegateInitCB; + set_delegate_and_init_callback(cluster, delegate_init_cb, config->delegate); + } + static const auto plugin_server_init_cb = CALL_ONCE(MatterThreadBorderRouterManagementPluginServerInitCallback); + set_plugin_server_init_callback(cluster, plugin_server_init_cb); + add_function_list(cluster, function_list, function_flags); + + /* Attributes managed internally */ + global::attribute::create_feature_map(cluster, 0); + attribute::create_border_router_name(cluster, nullptr, 0); + attribute::create_border_agent_id(cluster, nullptr, 0); + attribute::create_thread_version(cluster, 0); + attribute::create_interface_enabled(cluster, false); + nullable timestamp; + attribute::create_active_dataset_timestamp(cluster, timestamp); + + /** Attributes not managed internally **/ + global::attribute::create_cluster_revision(cluster, cluster_revision); + } + + command::create_get_active_dataset_request(cluster); + command::create_get_pending_dataset_request(cluster); + command::create_dataset_response(cluster); + command::create_set_active_dataset_request(cluster); + + if (features & feature::pan_change::get_id()) { + feature::pan_change::add(cluster); + } + + return cluster; +} + +} /* thread_border_router_management */ + // namespace binary_input_basic { // // ToDo // } /* binary_input_basic */ diff --git a/components/esp_matter/esp_matter_cluster.h b/components/esp_matter/esp_matter_cluster.h index 38ad2d3af..9d5c96184 100644 --- a/components/esp_matter/esp_matter_cluster.h +++ b/components/esp_matter/esp_matter_cluster.h @@ -864,5 +864,14 @@ typedef struct config { cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags); } /* application_basic */ +namespace thread_border_router_management { +typedef struct config { + void *delegate; + config() : delegate(nullptr) {} +} config_t; + +cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags, uint32_t features); +} /* thread_border_router_management */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_command.cpp b/components/esp_matter/esp_matter_command.cpp index ba8fe2f5d..49f5ab391 100644 --- a/components/esp_matter/esp_matter_command.cpp +++ b/components/esp_matter/esp_matter_command.cpp @@ -2858,6 +2858,42 @@ command_t *create_cancel_request(cluster_t *cluster) } /* command */ } /* device_energy_management */ +namespace thread_border_router_management { +namespace command { + +command_t *create_get_active_dataset_request(cluster_t *cluster) +{ + return esp_matter::command::create(cluster, ThreadBorderRouterManagement::Commands::GetActiveDatasetRequest::Id, + COMMAND_FLAG_ACCEPTED, NULL); +} + +command_t *create_get_pending_dataset_request(cluster_t *cluster) +{ + return esp_matter::command::create(cluster, ThreadBorderRouterManagement::Commands::GetPendingDatasetRequest::Id, + COMMAND_FLAG_ACCEPTED, NULL); +} + +command_t *create_dataset_response(cluster_t *cluster) +{ + return esp_matter::command::create(cluster, ThreadBorderRouterManagement::Commands::DatasetResponse::Id, + COMMAND_FLAG_GENERATED, NULL); +} + +command_t *create_set_active_dataset_request(cluster_t *cluster) +{ + return esp_matter::command::create(cluster, ThreadBorderRouterManagement::Commands::SetActiveDatasetRequest::Id, + COMMAND_FLAG_ACCEPTED, NULL); +} + +command_t *create_set_pending_dataset_request(cluster_t *cluster) +{ + return esp_matter::command::create(cluster, ThreadBorderRouterManagement::Commands::SetPendingDatasetRequest::Id, + COMMAND_FLAG_ACCEPTED, NULL); +} + +} /* command */ +} /* thread_border_router_management */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_command.h b/components/esp_matter/esp_matter_command.h index 16cef8e46..fffb2f06a 100644 --- a/components/esp_matter/esp_matter_command.h +++ b/components/esp_matter/esp_matter_command.h @@ -417,5 +417,15 @@ command_t *create_cancel_request(cluster_t *cluster); } /* command */ } /* device_energy_management */ +namespace thread_border_router_management { +namespace command { +command_t *create_get_active_dataset_request(cluster_t *cluster); +command_t *create_get_pending_dataset_request(cluster_t *cluster); +command_t *create_dataset_response(cluster_t *cluster); +command_t *create_set_active_dataset_request(cluster_t *cluster); +command_t *create_set_pending_dataset_request(cluster_t *cluster); +} /* command */ +} /* thread_border_router_management */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_delegate_callbacks.cpp b/components/esp_matter/esp_matter_delegate_callbacks.cpp index 360fcac24..1989951d8 100644 --- a/components/esp_matter/esp_matter_delegate_callbacks.cpp +++ b/components/esp_matter/esp_matter_delegate_callbacks.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include using namespace chip::app::Clusters; namespace esp_matter { @@ -354,6 +356,25 @@ void ModeSelectDelegateInitCB(void *delegate, uint16_t endpoint_id) ModeSelect::setSupportedModesManager(supported_modes_manager); } +void ThreadBorderRouterManagementDelegateInitCB(void *delegate, uint16_t endpoint_id) +{ + assert(delegate != nullptr); + esp_matter::cluster_t *cluster = esp_matter::cluster::get(endpoint_id, ThreadBorderRouterManagement::Id); + assert(cluster != nullptr); + /* Get the attribute */ + attribute_t *attribute = attribute::get(cluster, Globals::Attributes::FeatureMap::Id); + assert(attribute != nullptr); + /* Update the value if the attribute already exists */ + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + bool pan_change_supported = (val.val.u32 & thread_border_router_management::feature::pan_change::get_id()) ? true : false; + ThreadBorderRouterManagement::Delegate *thread_br_delegate = static_cast(delegate); + assert(thread_br_delegate->GetPanChangeSupported() == pan_change_supported); + ThreadBorderRouterManagement::ServerInstance *server_instance = + chip::Platform::New(endpoint_id, thread_br_delegate, chip::Server::GetInstance().GetFailSafeContext()); + server_instance->Init(); +} + } // namespace delegate_cb } // namespace cluster diff --git a/components/esp_matter/esp_matter_delegate_callbacks.h b/components/esp_matter/esp_matter_delegate_callbacks.h index a4310cfe4..888837517 100644 --- a/components/esp_matter/esp_matter_delegate_callbacks.h +++ b/components/esp_matter/esp_matter_delegate_callbacks.h @@ -45,6 +45,7 @@ void WindowCoveringDelegateInitCB(void *delegate, uint16_t endpoint_id); void DishwasherAlarmDelegateInitCB(void *delegate, uint16_t endpoint_id); void KeypadInputDelegateInitCB(void *delegate, uint16_t endpoint_id); void ModeSelectDelegateInitCB(void *delegate, uint16_t endpoint_id); +void ThreadBorderRouterManagementDelegateInitCB(void *delegate, uint16_t endpoint_id); } // namespace delegate_cb } // namespace cluster diff --git a/components/esp_matter/esp_matter_endpoint.cpp b/components/esp_matter/esp_matter_endpoint.cpp index 4e469c0dd..a4521f9fd 100644 --- a/components/esp_matter/esp_matter_endpoint.cpp +++ b/components/esp_matter/esp_matter_endpoint.cpp @@ -1885,6 +1885,40 @@ esp_err_t add(endpoint_t *endpoint, config_t *config) } } /** device_energy_management **/ +namespace thread_border_router { + +uint32_t get_device_type_id() +{ + return ESP_MATTER_THREAD_BORDER_ROUTER_DEVICE_TYPE_ID; +} + +uint8_t get_device_type_version() +{ + return ESP_MATTER_THREAD_BORDER_ROUTER_DEVICE_TYPE_VERSION; +} + +endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data) +{ + return common::create(node, config, flags, priv_data, add); +} + +esp_err_t add(endpoint_t *endpoint, config_t *config) +{ + VerifyOrReturnError(endpoint != nullptr, ESP_ERR_INVALID_ARG, ESP_LOGE(TAG, "Endpoint cannot be NULL")); + esp_err_t err = add_device_type(endpoint, get_device_type_id(), get_device_type_version()); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to add device type id:%" PRIu32 ",err: %d", get_device_type_id(), err); + return err; + } + + thread_network_diagnostics::create(endpoint, &(config->thread_network_diagnostics), CLUSTER_FLAG_SERVER); + thread_border_router_management::create(endpoint, &(config->thread_border_router_management), CLUSTER_FLAG_SERVER, + thread_border_router_management::feature::pan_change::get_id()); + return ESP_OK; +} + +} /* thread_border_router */ + } /* endpoint */ namespace node { diff --git a/components/esp_matter/esp_matter_endpoint.h b/components/esp_matter/esp_matter_endpoint.h index 43b972a91..e5b0c0efb 100644 --- a/components/esp_matter/esp_matter_endpoint.h +++ b/components/esp_matter/esp_matter_endpoint.h @@ -131,6 +131,9 @@ #define ESP_MATTER_DEVICE_ENERGY_MANAGEMENT_DEVICE_TYPE_ID 0x050D #define ESP_MATTER_DEVICE_ENERGY_MANAGEMENT_DEVICE_TYPE_VERSION 1 +#define ESP_MATTER_THREAD_BORDER_ROUTER_DEVICE_TYPE_ID 0x0091 +#define ESP_MATTER_THREAD_BORDER_ROUTER_DEVICE_TYPE_VERSION 1 + namespace esp_matter { /** Specific endpoint (device type) create APIs @@ -853,6 +856,19 @@ endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_dat esp_err_t add(endpoint_t *endpoint, config_t *config); } /* device_energy_management */ +namespace thread_border_router { +typedef struct config { + cluster::descriptor::config_t descriptor; + cluster::thread_network_diagnostics::config_t thread_network_diagnostics; + cluster::thread_border_router_management::config_t thread_border_router_management; +} config_t; + +uint32_t get_device_type_id(); +uint8_t get_device_type_version(); +endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data); +esp_err_t add(endpoint_t *endpoint, config_t *config); +} /* thread_border_router */ + } /* endpoint */ namespace node { diff --git a/components/esp_matter/esp_matter_feature.cpp b/components/esp_matter/esp_matter_feature.cpp index 004fd5dc2..6add4c96b 100644 --- a/components/esp_matter/esp_matter_feature.cpp +++ b/components/esp_matter/esp_matter_feature.cpp @@ -4752,5 +4752,36 @@ esp_err_t add(cluster_t *cluster) } /* feature */ } /* device_energy_management */ +namespace thread_border_router_management { +namespace feature { + +namespace pan_change { + +uint32_t get_id() +{ + return static_cast(ThreadBorderRouterManagement::Feature::kPANChange); +} + +esp_err_t add(cluster_t *cluster) +{ + if (!cluster) { + ESP_LOGE(TAG, "Cluster cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + update_feature_map(cluster, get_id()); + /* attribute */ + nullable timestamp; + attribute::create_pending_dataset_timestamp(cluster, timestamp); + + /* command */ + command::create_set_pending_dataset_request(cluster); + return ESP_OK; +} + +} /* pan_change */ + +} /* feature */ +} /* thread_border_router_management */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_feature.h b/components/esp_matter/esp_matter_feature.h index e331a9b08..bc829c01e 100644 --- a/components/esp_matter/esp_matter_feature.h +++ b/components/esp_matter/esp_matter_feature.h @@ -2084,5 +2084,18 @@ esp_err_t add(cluster_t *cluster); } /* feature */ } /* device_energy_management */ +namespace thread_border_router_management { +namespace feature { + +namespace pan_change { + +uint32_t get_id(); +esp_err_t add(cluster_t *cluster); + +} /* pan_change */ + +} /* feature */ +} /* thread_border_router_management */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/private/esp_matter_cluster_revisions.h b/components/esp_matter/private/esp_matter_cluster_revisions.h index 52414564b..9d7c1e4d6 100644 --- a/components/esp_matter/private/esp_matter_cluster_revisions.h +++ b/components/esp_matter/private/esp_matter_cluster_revisions.h @@ -367,6 +367,11 @@ namespace application_basic { constexpr uint16_t cluster_revision = 1; } // namespace application_basic +namespace thread_border_router_management { +constexpr uint16_t cluster_revision = 1; +} // namespace thread_border_router_management + + } // namespace cluster } // namespace esp_matter diff --git a/components/esp_matter_console/CMakeLists.txt b/components/esp_matter_console/CMakeLists.txt index 6555e6a32..bbfad816e 100644 --- a/components/esp_matter_console/CMakeLists.txt +++ b/components/esp_matter_console/CMakeLists.txt @@ -2,6 +2,12 @@ set(src_dirs) if (CONFIG_ENABLE_CHIP_SHELL) list(APPEND src_dirs ".") endif() + +set(priv_req chip mbedtls esp_timer bt openthread) +if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0") + list(APPEND priv_req esp_rcp_update) +endif() + idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS . - PRIV_REQUIRES chip mbedtls esp_timer bt openthread esp_rcp_update) + PRIV_REQUIRES ${priv_req}) diff --git a/examples/.build-rules.yml b/examples/.build-rules.yml index 12ccdb6ce..3932b7c9a 100644 --- a/examples/.build-rules.yml +++ b/examples/.build-rules.yml @@ -116,3 +116,9 @@ examples/light_wifi_prov: - if: IDF_TARGET in ["esp32s3", "esp32c3"] temporary: true reason: the other targets are not tested yet + +examples/thread_border_router: + enable: + - if: IDF_TARGET in ["esp32s3"] + temporary: true + reason: the other targets are not tested yet diff --git a/examples/all_device_types_app/main/app_main.cpp b/examples/all_device_types_app/main/app_main.cpp index dbfa52704..add9c41dd 100644 --- a/examples/all_device_types_app/main/app_main.cpp +++ b/examples/all_device_types_app/main/app_main.cpp @@ -30,12 +30,10 @@ #include #if CHIP_DEVICE_CONFIG_ENABLE_THREAD #include +#include +#include +#include #endif -#if CONFIG_OPENTHREAD_BORDER_ROUTER -#include -#include -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER - static const char *TAG = "app_main"; @@ -121,14 +119,15 @@ static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) case chip::DeviceLayer::DeviceEventType::kESPSystemEvent: if (event->Platform.ESPSystemEvent.Base == IP_EVENT && event->Platform.ESPSystemEvent.Id == IP_EVENT_STA_GOT_IP) { -#if CONFIG_OPENTHREAD_BORDER_ROUTER - 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(), - }; - ESP_LOGI(TAG, "init thread br"); - esp_matter::thread_br_init(&config); +#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER + static bool sThreadBRInitialized = false; + if (!sThreadBRInitialized) { + esp_openthread_set_backbone_netif(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF")); + esp_openthread_lock_acquire(portMAX_DELAY); + esp_openthread_border_router_init(); + esp_openthread_lock_release(); + sThreadBRInitialized = true; + } #endif } break; @@ -218,8 +217,8 @@ extern "C" void app_main() esp_matter::console::diagnostics_register_commands(); esp_matter::console::wifi_register_commands(); esp_matter::console::init(); -#if CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_OPENTHREAD_CLI - esp_matter::console::thread_br_cli_register_command(); +#ifdef CONFIG_OPENTHREAD_CLI + esp_matter::console::otcli_register_commands(); #endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_OPENTHREAD_CLI #endif diff --git a/examples/all_device_types_app/main/device_types.h b/examples/all_device_types_app/main/device_types.h index 5f2899805..e0493260c 100644 --- a/examples/all_device_types_app/main/device_types.h +++ b/examples/all_device_types_app/main/device_types.h @@ -52,6 +52,7 @@ enum device_type_enum { ESP_MATTER_WATER_VALVE, ESP_MATTER_DEVICE_ENERGY_MANAGEMENT, ESP_MATTER_PUMP_CONTROLLER, + ESP_MATTER_THREAD_BORDER_ROUTER, ESP_MATTER_DEVICE_TYPE_MAX }; @@ -109,6 +110,7 @@ const device_type_name device_type_list[ESP_MATTER_DEVICE_TYPE_MAX] = { {"laundry_dryer", ESP_MATTER_LAUNDRY_DRYER}, {"water_valve", ESP_MATTER_WATER_VALVE}, {"device_energy_management", ESP_MATTER_DEVICE_ENERGY_MANAGEMENT}, - {"pump_controller", ESP_MATTER_PUMP_CONTROLLER} + {"pump_controller", ESP_MATTER_PUMP_CONTROLLER}, + {"thread_border_router", ESP_MATTER_THREAD_BORDER_ROUTER}, }; } /* namespace esp_matter */ diff --git a/examples/all_device_types_app/main/esp_matter_console_helpers.cpp b/examples/all_device_types_app/main/esp_matter_console_helpers.cpp index cb831e55d..4a25d333b 100644 --- a/examples/all_device_types_app/main/esp_matter_console_helpers.cpp +++ b/examples/all_device_types_app/main/esp_matter_console_helpers.cpp @@ -29,6 +29,13 @@ #include #include +#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#include +#include +using chip::app::Clusters::ThreadBorderRouterManagement::GenericOpenThreadBorderRouterDelegate; +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER + + using namespace esp_matter; extern uint16_t app_endpoint_id; @@ -494,6 +501,24 @@ int create(uint8_t device_type_index) endpoint = esp_matter::endpoint::pump_controller::create(node, &pump_controller_config, ENDPOINT_FLAG_NONE, NULL); break; } +#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER + case ESP_MATTER_THREAD_BORDER_ROUTER: { + static chip::KvsPersistentStorageDelegate tbr_storage_delegate; + chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr(); + tbr_storage_delegate.Init(&kvsManager); + GenericOpenThreadBorderRouterDelegate *delegate = chip::Platform::New(&tbr_storage_delegate); + if (!delegate) { + ESP_LOGE(TAG, "Failed to create thread_border_router delegate"); + return 1; + } + char threadBRName[] = "Espressif-ThreadBR"; + delegate->SetThreadBorderRouterName(chip::CharSpan(threadBRName)); + esp_matter::endpoint::thread_border_router::config_t tbr_config; + tbr_config.thread_border_router_management.delegate = delegate; + endpoint = esp_matter::endpoint::thread_border_router::create(node, &tbr_config, ENDPOINT_FLAG_NONE, NULL); + break; + } +#endif default: { ESP_LOGE(TAG, "Please input a valid device type"); break; diff --git a/examples/all_device_types_app/partitions_thread.csv b/examples/all_device_types_app/partitions_thread.csv index e5f11bdd4..40b0afb0f 100644 --- a/examples/all_device_types_app/partitions_thread.csv +++ b/examples/all_device_types_app/partitions_thread.csv @@ -5,7 +5,5 @@ nvs, data, nvs, 0x10000, 0x6000, nvs_keys, data, nvs_keys, , 0x1000, encrypted otadata, data, ota, , 0x2000 phy_init, data, phy, , 0x1000, -ota_0, app, ota_0, , 0x1C0000, -ota_1, app, ota_1, , 0x1C0000, -# For Wi-Fi device, this partition can be deleted. -ot_storage,data, fat, , 0x2000, +ota_0, app, ota_0, , 1920K, +ota_1, app, ota_1, , 1920K, diff --git a/examples/all_device_types_app/sdkconfig.defaults.otbr b/examples/all_device_types_app/sdkconfig.defaults.otbr index ad74bff3b..5c3555e07 100644 --- a/examples/all_device_types_app/sdkconfig.defaults.otbr +++ b/examples/all_device_types_app/sdkconfig.defaults.otbr @@ -2,12 +2,17 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_thread.csv" + # Enable Openthread Border Router, but don't run matter over Thread CONFIG_OPENTHREAD_ENABLED=y CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC=n -CONFIG_OPENTHREAD_LOG_LEVEL_NOTE=y +CONFIG_OPENTHREAD_LOG_LEVEL_WARN=y CONFIG_OPENTHREAD_BORDER_ROUTER=y -CONFIG_ENABLE_MATTER_OVER_THREAD=n +CONFIG_OPENTHREAD_SRP_CLIENT=n +CONFIG_OPENTHREAD_DNS_CLIENT=n +CONFIG_THREAD_NETWORK_COMMISSIONING_DRIVER=n #enable BT CONFIG_BT_ENABLED=y @@ -74,3 +79,7 @@ CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y CONFIG_ENABLE_OTA_REQUESTOR=n CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=y + +# Flash optimization +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_NEWLIB_NANO_FORMAT=y diff --git a/examples/controller/main/app_main.cpp b/examples/controller/main/app_main.cpp index 0b3802627..a074ecc90 100644 --- a/examples/controller/main/app_main.cpp +++ b/examples/controller/main/app_main.cpp @@ -80,13 +80,13 @@ extern "C" void app_main() #endif // CONFIG_OPENTHREAD_BORDER_ROUTER #endif // CONFIG_ENABLE_CHIP_SHELL #ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#ifdef CONFIG_AUTO_UPDATE_RCP esp_vfs_spiffs_conf_t rcp_fw_conf = { .base_path = "/rcp_fw", .partition_label = "rcp_fw", .max_files = 10, .format_if_mount_failed = false}; if (ESP_OK != esp_vfs_spiffs_register(&rcp_fw_conf)) { ESP_LOGE(TAG, "Failed to mount rcp firmware storage"); return; } -#ifdef CONFIG_AUTO_UPDATE_RCP esp_rcp_update_config_t rcp_update_config = ESP_OPENTHREAD_RCP_UPDATE_CONFIG(); openthread_init_br_rcp(&rcp_update_config); #endif diff --git a/examples/controller/sdkconfig.defaults.otbr b/examples/controller/sdkconfig.defaults.otbr index 0aca1f6d4..7b7334803 100644 --- a/examples/controller/sdkconfig.defaults.otbr +++ b/examples/controller/sdkconfig.defaults.otbr @@ -11,9 +11,6 @@ CONFIG_OPENTHREAD_SRP_CLIENT=n CONFIG_OPENTHREAD_DNS_CLIENT=n CONFIG_THREAD_TASK_STACK_SIZE=8192 -# Auto update RCP firmware -CONFIG_AUTO_UPDATE_RCP=y - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/thread_border_router/CMakeLists.txt b/examples/thread_border_router/CMakeLists.txt new file mode 100644 index 000000000..3a1f2cc6b --- /dev/null +++ b/examples/thread_border_router/CMakeLists.txt @@ -0,0 +1,38 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +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}) + +if(NOT DEFINED ENV{ESP_MATTER_DEVICE_PATH}) + if("${IDF_TARGET}" STREQUAL "esp32s3") + set(ENV{ESP_MATTER_DEVICE_PATH} $ENV{ESP_MATTER_PATH}/device_hal/device/esp32s3_devkit_c) + else() + message(FATAL_ERROR "Unsupported IDF_TARGET") + endif() +endif(NOT DEFINED ENV{ESP_MATTER_DEVICE_PATH}) + +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($ENV{ESP_MATTER_DEVICE_PATH}/esp_matter_device.cmake) + +set(EXTRA_COMPONENT_DIRS + "../common" + "${MATTER_SDK_PATH}/config/esp32/components" + "${ESP_MATTER_PATH}/components" + "${ESP_MATTER_PATH}/device_hal/device" + ${extra_components_dirs_append}) + +project(thread_border_router) + +idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H" 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/thread_border_router/README.md b/examples/thread_border_router/README.md new file mode 100644 index 000000000..ffc1368a3 --- /dev/null +++ b/examples/thread_border_router/README.md @@ -0,0 +1,47 @@ +# Thread Border Router + +This example creates a Matter Thread Border Router device using the ESP Matter data model. + + +See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html) for more information about building and flashing the firmware. + +## 1. Additional Environment Setup + +### 1.1 Hardware Platform + +The [ESP Thread Border Router board](https://github.com/espressif/esp-thread-br?tab=readme-ov-file#esp-thread-border-router-board) which provides an integrated module of an ESP32-S3 and an ESP32-H2 is required for this example. + +### 1.2 Firmware for RCP + +The [OpenThread RCP](https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_rcp) should be run on ESP32-H2 of the Border Router board. You can flash it directly: + + +``` +$ cd /path/to/esp-idf/examples/openthread/ot_rcp +$ idf.py set-target esp32h2 build +$ idf.py -p erase-flash flash +``` + +Or you can flash the firmware of ESP32-H2 with [esp_rcp_update](https://github.com/espressif/esp-thread-br/tree/main/components/esp_rcp_update) after enabling `AUTO_UPDATE_RCP` in menuconfig: + +``` +$ cd /path/to/esp-idf/examples/openthread/ot_rcp +$ idf.py set-target esp32h2 build +``` + +After flashing the Thread Border Router firmware to ESP32-S3, it will flash the RCP firmware to ESP32-H2 automatically. + +## 2. Post Commissioning Setup + +After commissioning the Border Router with chip-tool, you can set up the Thread network with ThreadBorderRouterManagement cluster. + +``` +$ ./chip-tool generalcommissioning arm-fail-safe 180 1 0x7283 0 +$ ./chip-tool threadborderroutermanagement set-active-dataset-request hex: 0x7283 1 +$ ./chip-tool generalcommissioning commissioning-complete 0x7283 0 +``` +Then the Thread Border Router will form/join a Thread network and you can commission a Thread End-device to the Thread network with chip-tool. + +``` +$ ./chip-tool pairing ble-thread 0x7384 hex: 20202021 3840 +``` diff --git a/examples/thread_border_router/main/CMakeLists.txt b/examples/thread_border_router/main/CMakeLists.txt new file mode 100644 index 000000000..6b91a8cf5 --- /dev/null +++ b/examples/thread_border_router/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS ".") + +set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17) +target_compile_options(${COMPONENT_LIB} PRIVATE "-DCHIP_HAVE_CONFIG_H") diff --git a/examples/thread_border_router/main/app_main.cpp b/examples/thread_border_router/main/app_main.cpp new file mode 100644 index 000000000..bf7a1756c --- /dev/null +++ b/examples/thread_border_router/main/app_main.cpp @@ -0,0 +1,119 @@ +/* + 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 +#include +#include +#include + +static const char *TAG = "app_main"; + +using namespace esp_matter; +using namespace esp_matter::attribute; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; +using chip::app::Clusters::ThreadBorderRouterManagement::GenericOpenThreadBorderRouterDelegate; + +static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) +{ + switch (event->Type) { + case chip::DeviceLayer::DeviceEventType::PublicEventTypes::kInterfaceIpAddressChanged: + ESP_LOGI(TAG, "Interface IP Address changed"); + break; + case chip::DeviceLayer::DeviceEventType::kESPSystemEvent: + if (event->Platform.ESPSystemEvent.Base == IP_EVENT && + event->Platform.ESPSystemEvent.Id == IP_EVENT_STA_GOT_IP) { +#if CONFIG_OPENTHREAD_BORDER_ROUTER + static bool sThreadBRInitialized = false; + if (!sThreadBRInitialized) { + esp_openthread_set_backbone_netif(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF")); + esp_openthread_lock_acquire(portMAX_DELAY); + esp_openthread_border_router_init(); + esp_openthread_lock_release(); + sThreadBRInitialized = true; + } +#endif + } + break; + default: + break; + } +} + +extern "C" void app_main() +{ + esp_err_t err = ESP_OK; + + /* Initialize the ESP NVS layer */ + nvs_flash_init(); + // If there is no commissioner in the controller, we need a default node so that the controller can be commissioned + // to a specific fabric. + node::config_t node_config; + node_t *node = node::create(&node_config, NULL, NULL); + static chip::KvsPersistentStorageDelegate tbr_storage_delegate; + chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr(); + tbr_storage_delegate.Init(&kvsManager); + GenericOpenThreadBorderRouterDelegate *delegate = chip::Platform::New(&tbr_storage_delegate); + char threadBRName[] = "Espressif-ThreadBR"; + delegate->SetThreadBorderRouterName(chip::CharSpan(threadBRName)); + if (!delegate) { + ESP_LOGE(TAG, "Failed to create thread_border_router delegate"); + return; + } + thread_border_router::config_t tbr_config; + tbr_config.thread_border_router_management.delegate = delegate; + endpoint_t *tbr_endpoint = thread_border_router::create(node, &tbr_config, ENDPOINT_FLAG_NONE, NULL); + if (!node || !tbr_endpoint) { + ESP_LOGE(TAG, "Failed to create data model"); + return; + } +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP) + esp_vfs_spiffs_conf_t rcp_fw_conf = { + .base_path = "/rcp_fw", .partition_label = "rcp_fw", .max_files = 10, .format_if_mount_failed = false}; + if (ESP_OK != esp_vfs_spiffs_register(&rcp_fw_conf)) { + ESP_LOGE(TAG, "Failed to mount rcp firmware storage"); + return; + } + esp_rcp_update_config_t rcp_update_config = ESP_OPENTHREAD_RCP_UPDATE_CONFIG(); + openthread_init_br_rcp(&rcp_update_config); +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP + /* Set OpenThread platform config */ + 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 // CHIP_DEVICE_CONFIG_ENABLE_THREAD + + /* Matter start */ + err = esp_matter::start(app_event_cb); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Matter start failed: %d", err); + } +#if CONFIG_ENABLE_CHIP_SHELL + esp_matter::console::diagnostics_register_commands(); + esp_matter::console::wifi_register_commands(); + esp_matter::console::init(); +#endif // CONFIG_ENABLE_CHIP_SHELL +} diff --git a/examples/thread_border_router/main/esp_ot_config.h b/examples/thread_border_router/main/esp_ot_config.h new file mode 100644 index 000000000..e73f8cf96 --- /dev/null +++ b/examples/thread_border_router/main/esp_ot_config.h @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Border Router Example + * + * 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 "sdkconfig.h" +#if CONFIG_OPENTHREAD_BORDER_ROUTER +#include "esp_openthread_types.h" +#include + +#if CONFIG_OPENTHREAD_RADIO_NATIVE +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_NATIVE, \ + } +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_UART +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_UART_RCP, \ + .radio_uart_config = { \ + .port = UART_NUM_1, \ + .uart_config = \ + { \ + .baud_rate = 460800, \ + .data_bits = UART_DATA_8_BITS, \ + .parity = UART_PARITY_DISABLE, \ + .stop_bits = UART_STOP_BITS_1, \ + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ + .rx_flow_ctrl_thresh = 0, \ + .source_clk = UART_SCLK_DEFAULT, \ + }, \ + .rx_pin = GPIO_NUM_17, \ + .tx_pin = GPIO_NUM_18, \ + }, \ + } +#else +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_SPI_RCP, \ + .radio_spi_config = { \ + .host_device = SPI2_HOST, \ + .dma_channel = 2, \ + .spi_interface = \ + { \ + .mosi_io_num = 11, \ + .sclk_io_num = 12, \ + .miso_io_num = 13, \ + }, \ + .spi_device = \ + { \ + .cs_ena_pretrans = 2, \ + .input_delay_ns = 100, \ + .mode = 0, \ + .clock_speed_hz = 2500 * 1000, \ + .spics_io_num = 10, \ + .queue_size = 5, \ + }, \ + .intr_pin = 8, \ + }, \ + } +#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI + +#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, \ + } + +#define ESP_OPENTHREAD_RCP_UPDATE_CONFIG() \ + { \ + .rcp_type = RCP_TYPE_ESP32H2_UART, .uart_rx_pin = 17, .uart_tx_pin = 18, .uart_port = 1, .uart_baudrate = 115200, \ + .reset_pin = 7, .boot_pin = 8, .update_baudrate = 460800, .firmware_dir = "/rcp_fw/ot_rcp", .target_chip = ESP32H2_CHIP, \ + } + +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER diff --git a/examples/thread_border_router/partitions.csv b/examples/thread_border_router/partitions.csv new file mode 100644 index 000000000..3d4e1b22f --- /dev/null +++ b/examples/thread_border_router/partitions.csv @@ -0,0 +1,6 @@ +# 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 +nvs, data, nvs, , 0x6000, +phy_init, data, phy, , 0x1000, +factory, app, factory, , 3000K, +rcp_fw, data, spiffs, , 600K, diff --git a/examples/thread_border_router/sdkconfig.defaults b/examples/thread_border_router/sdkconfig.defaults new file mode 100644 index 000000000..a026d3008 --- /dev/null +++ b/examples/thread_border_router/sdkconfig.defaults @@ -0,0 +1,64 @@ +# Default to 921600 baud when flashing and monitoring device +CONFIG_ESPTOOLPY_BAUD_921600B=y +CONFIG_ESPTOOLPY_BAUD=921600 +CONFIG_ESPTOOLPY_COMPRESSED=y +CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +#enable BT +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n + +# Increase some stack size +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=7200 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=7200 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=5120 +CONFIG_THREAD_TASK_STACK_SIZE=8192 +CONFIG_CHIP_TASK_STACK_SIZE=10240 + +# partition table +CONFIG_PARTITION_TABLE_CUSTOM=y + +# USB console for Thread border board +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# Disable SoftAP +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n + +# LWIP +CONFIG_LWIP_IPV6_FORWARD=y +CONFIG_LWIP_IPV6_AUTOCONFIG=y +CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 +CONFIG_LWIP_MULTICAST_PING=y +CONFIG_LWIP_NETIF_STATUS_CALLBACK=y +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y +CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM=y +CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM=y + +# ESP Border Router will also enable a route hook so we don't need the route hook in matter +CONFIG_ENABLE_ROUTE_HOOK=n + +# mbedTLS +CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS=y +CONFIG_MBEDTLS_ECJPAKE_C=y +CONFIG_MBEDTLS_HKDF_C=y + +# OpenThread +CONFIG_OPENTHREAD_ENABLED=y +CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC=n +CONFIG_OPENTHREAD_LOG_LEVEL_NOTE=y +CONFIG_OPENTHREAD_SRP_CLIENT=n +CONFIG_OPENTHREAD_DNS_CLIENT=n +CONFIG_OPENTHREAD_BORDER_ROUTER=y +CONFIG_THREAD_NETWORK_COMMISSIONING_DRIVER=n + +# Use platform mDNS +CONFIG_USE_MINIMAL_MDNS=n + +# Enable Matter shell +CONFIG_ENABLE_CHIP_SHELL=y +CONFIG_ESP_MATTER_CONSOLE_TASK_STACK=4096 From 4b8d838a9bc3d4bca88dec8cfb5c16722b059dc0 Mon Sep 17 00:00:00 2001 From: WanqQixiang Date: Fri, 20 Sep 2024 10:58:58 +0800 Subject: [PATCH 3/3] esp-matter: Unique has been changed to be mandatory --- components/esp_matter/esp_matter_cluster.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esp_matter/esp_matter_cluster.cpp b/components/esp_matter/esp_matter_cluster.cpp index 0aa9f0482..b0a80b88b 100644 --- a/components/esp_matter/esp_matter_cluster.cpp +++ b/components/esp_matter/esp_matter_cluster.cpp @@ -252,6 +252,7 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) attribute::create_hardware_version_string(cluster, NULL, 0); attribute::create_software_version(cluster, 0); attribute::create_software_version_string(cluster, NULL, 0); + attribute::create_unique_id(cluster, NULL, 0); attribute::create_capability_minima(cluster, NULL, 0, 0); attribute::create_specification_version(cluster, 0); attribute::create_max_paths_per_invoke(cluster, 0);