diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6bbc9024c..90f116c36 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,10 @@ +# 2-November-2023 + +All of the non-volatile attribute values now are stored in the namespace `esp_matter_kvs` with the attribute key base64-encoded of bytes (`endpoint-id`+`cluster-id`+`attribute-id`). For the devices that store the non-volatile attribute values in the previous namespace with previous attribute-key, the values will be moved and the previous keys will be erased. + # 29-August-2023 -- `ot_storage` partition is no longere required for thread devices as the mechanism for storing data related to the Thread network has been changed in the `openthread` component in ESP-IDF. +- `ot_storage` partition is no longer required for thread devices as the mechanism for storing data related to the Thread network has been changed in the `openthread` component in ESP-IDF. # 14-April-2023 diff --git a/components/esp_matter/esp_matter_core.cpp b/components/esp_matter/esp_matter_core.cpp index ce50f07f9..2041ed3f1 100644 --- a/components/esp_matter/esp_matter_core.cpp +++ b/components/esp_matter/esp_matter_core.cpp @@ -67,7 +67,6 @@ using chip::DeviceLayer::ThreadStackMgr; #define ESP_MATTER_NVS_PART_NAME CONFIG_ESP_MATTER_NVS_PART_NAME #define ESP_MATTER_MAX_DEVICE_TYPE_COUNT CONFIG_ESP_MATTER_MAX_DEVICE_TYPE_COUNT -#define ESP_MATTER_NVS_NODE_NAMESPACE "node" static const char *TAG = "esp_matter_core"; static bool esp_matter_started = false; @@ -210,7 +209,7 @@ static esp_err_t store_min_unused_endpoint_id() } nvs_handle_t handle; - esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, ESP_MATTER_NVS_NODE_NAMESPACE, + esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, ESP_MATTER_KVS_NAMESPACE, NVS_READWRITE, &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open the node nvs_namespace"); @@ -230,18 +229,39 @@ static esp_err_t read_min_unused_endpoint_id() } nvs_handle_t handle; - esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, ESP_MATTER_NVS_NODE_NAMESPACE, + esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, ESP_MATTER_KVS_NAMESPACE, NVS_READONLY, &handle); + if (err == ESP_OK) { + err = nvs_get_u16(handle, "min_uu_ep_id", &node->min_unused_endpoint_id); + nvs_close(handle); + } + if (err == ESP_ERR_NVS_NOT_FOUND) { - ESP_LOGI(TAG, "Cannot find the node nvs namespace"); - return err; + ESP_LOGI(TAG, "Cannot find minimum unused endpoint_id, try to find in the previous namespace"); + // Try to read the minimum unused endpoint_id from the previous node namespace. + err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, "node", NVS_READONLY, &handle); + if (err != ESP_OK) { + ESP_LOGI(TAG, "Failed to open node namespace"); + return err; + } + err = nvs_get_u16(handle, "min_uu_ep_id", &node->min_unused_endpoint_id); + nvs_close(handle); + if (err == ESP_OK) { + // If the minimum unused endpoint_id is got, we will erase it from the previous namespace + // and store it to the new namespace. + if (nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, "node", NVS_READWRITE, &handle) == ESP_OK) { + if (nvs_erase_key(handle, "min_uu_ep_id") != ESP_OK) { + ESP_LOGE(TAG, "Failed to erase minimum unused endpoint_id"); + } else { + nvs_commit(handle); + } + nvs_close(handle); + } + return store_min_unused_endpoint_id(); + } + } else if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get minimum unused endpoint_id in the %s nvs_namespace", ESP_MATTER_KVS_NAMESPACE); } - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to open the node nvs_namespace"); - return err; - } - err = nvs_get_u16(handle, "min_uu_ep_id", &node->min_unused_endpoint_id); - nvs_close(handle); return err; } @@ -414,27 +434,6 @@ static int get_next_index() return 0xFFFF; } -static esp_err_t erase_persistent_data(endpoint_t *endpoint) -{ - uint16_t endpoint_id = endpoint::get_id(endpoint); - char nvs_namespace[16] = {0}; - snprintf(nvs_namespace, 16, "endpoint_%" PRIX16 "", endpoint_id); /* endpoint_id */ - - nvs_handle_t handle; - esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, nvs_namespace, NVS_READWRITE, &handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error opening partition: %s, %d", nvs_namespace, err); - return err; - } - err = nvs_erase_all(handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error erasing partition: %s, %d", nvs_namespace, err); - } - nvs_commit(handle); - nvs_close(handle); - return err; -} - static esp_err_t disable(endpoint_t *endpoint) { if (!endpoint) { @@ -504,8 +503,7 @@ static esp_err_t disable(endpoint_t *endpoint) esp_matter_mem_free(endpoint_type); current_endpoint->endpoint_type = NULL; - /* Clear endpoint persistent data in nvs flash */ - return erase_persistent_data(endpoint); + return ESP_OK; } esp_err_t enable(endpoint_t *endpoint) @@ -1056,29 +1054,18 @@ esp_err_t factory_reset() node_t *node = node::get(); if (node) { /* ESP Matter data model is used. Erase all the data that we have added in nvs. */ - nvs_handle_t node_handle; - err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, ESP_MATTER_NVS_NODE_NAMESPACE, - NVS_READWRITE, &node_handle); - if (err == ESP_OK) { - nvs_erase_all(node_handle); - } - nvs_commit(node_handle); - nvs_close(node_handle); - - nvs_commit(node_handle); - nvs_close(node_handle); - - endpoint_t *endpoint = endpoint::get_first(node); - while (endpoint) { - err = endpoint::erase_persistent_data(endpoint); + nvs_handle_t handle; + err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, ESP_MATTER_KVS_NAMESPACE, NVS_READWRITE, &handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to open esp_matter nvs partition "); + } else { + err = nvs_erase_all(handle); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error erasing persistent data of endpoint %" PRIu16 "", endpoint::get_id(endpoint)); - continue; + ESP_LOGE(TAG, "Failed to erase esp_matter nvs namespace"); + } else { + nvs_commit(handle); } - endpoint = endpoint::get_next(endpoint); - } - if (err == ESP_OK) { - ESP_LOGI(TAG, "Erasing attribute data completed"); + nvs_close(handle); } } @@ -1188,6 +1175,11 @@ static esp_err_t destroy(attribute_t *attribute) esp_matter_mem_free(current_attribute->bounds); } + /* Erase the persistent data */ + if (current_attribute->flags & ATTRIBUTE_FLAG_NONVOLATILE) { + erase_val_in_nvs(current_attribute->endpoint_id, current_attribute->cluster_id, current_attribute->attribute_id); + } + /* Free */ esp_matter_mem_free(current_attribute); return ESP_OK; @@ -1860,11 +1852,11 @@ endpoint_t *resume(node_t *node, uint8_t flags, uint16_t endpoint_id, void *priv current_endpoint = current_endpoint->next; } - /* Check */ - if (endpoint_id >= current_node->min_unused_endpoint_id) { + /* Check */ + if (endpoint_id >= current_node->min_unused_endpoint_id) { ESP_LOGE(TAG, "The endpoint_id of the resumed endpoint should have been used"); return NULL; - } + } /* Allocate */ _endpoint_t *endpoint = (_endpoint_t *)esp_matter_mem_calloc(1, sizeof(_endpoint_t)); diff --git a/components/esp_matter/esp_matter_core.h b/components/esp_matter/esp_matter_core.h index fd36a23a0..5971dd5ff 100644 --- a/components/esp_matter/esp_matter_core.h +++ b/components/esp_matter/esp_matter_core.h @@ -582,29 +582,6 @@ esp_err_t set_override_callback(attribute_t *attribute, callback_t callback); */ callback_t get_override_callback(attribute_t *attribute); -/** Store in NVS - * - * Store the current attribute val in NVS. - * - * @param[in] attribute Attribute handle. - * - * @return ESP_OK on success. - * @return error in case of failure. - */ -esp_err_t store_val_in_nvs(attribute_t *attribute); - -/** Get from NVS - * - * Get the val for current attribute from NVS. - * - * @param[in] attribute Attribute handle. - * @param[out] val Pointer to `esp_matter_attr_val_t`. Use appropriate elements as per the value type. - * - * @return ESP_OK on success. - * @return error in case of failure. - */ -esp_err_t get_val_from_nvs(attribute_t *attribute, esp_matter_attr_val_t *val); - } /* attribute */ namespace command { diff --git a/components/esp_matter/private/esp_matter_nvs.cpp b/components/esp_matter/private/esp_matter_nvs.cpp index e5bba6db9..1597b6662 100644 --- a/components/esp_matter/private/esp_matter_nvs.cpp +++ b/components/esp_matter/private/esp_matter_nvs.cpp @@ -18,6 +18,9 @@ #include #include #include +#include + +#include #define ESP_MATTER_NVS_PART_NAME CONFIG_ESP_MATTER_NVS_PART_NAME @@ -26,21 +29,30 @@ namespace attribute { const char * TAG = "mtr_nvs"; -esp_err_t get_val_from_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t & val) +static void get_attribute_key(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, char *attribute_key) { - /* Get keys */ - char nvs_namespace[16] = {0}; - char attribute_key[16] = {0}; - snprintf(nvs_namespace, 16, "endpoint_%" PRIX16 "", endpoint_id); /* endpoint_id */ - snprintf(attribute_key, 16, "%" PRIX32 ":%" PRIX32 "", cluster_id, attribute_id); /* cluster_id:attribute_id */ + // Convert the the endpoint_id, cluster_id, attribute_id to base64 string + uint8_t encode_buf[10] = {0}; + char base64_str[17] = {0}; + memcpy(&encode_buf[0], &endpoint_id, sizeof(endpoint_id)); + memcpy(&encode_buf[2], &cluster_id, sizeof(cluster_id)); + memcpy(&encode_buf[6], &attribute_id, sizeof(attribute_id)); + chip::Base64Encode(encode_buf, 10, base64_str); + // The last two character must be '=' + assert(base64_str[14] == '=' && base64_str[15] == '='); + // Copy the string before '=' + strncpy(attribute_key, base64_str, 14); + attribute_key[14] = 0; +} +static esp_err_t nvs_get_val(const char *nvs_namespace, const char *attribute_key, esp_matter_attr_val_t & val) +{ nvs_handle_t handle; esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, nvs_namespace, NVS_READONLY, &handle); if (err != ESP_OK) { return err; } - ESP_LOGD(TAG, "read attribute from nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 ", attribute_id-0x%" PRIx32 "", - endpoint_id, cluster_id, attribute_id); + if (val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING || val.type == ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING || val.type == ESP_MATTER_VAL_TYPE_OCTET_STRING || @@ -175,20 +187,14 @@ esp_err_t get_val_from_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t a return err; } -esp_err_t store_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, const esp_matter_attr_val_t & val) +static esp_err_t nvs_store_val(const char *nvs_namespace, const char *attribute_key, const esp_matter_attr_val_t & val) { - char nvs_namespace[16] = {0}; - char attribute_key[16] = {0}; - snprintf(nvs_namespace, 16, "endpoint_%" PRIX16 "", endpoint_id); /* endpoint_id */ - snprintf(attribute_key, 16, "%" PRIX32 ":%" PRIX32 "", cluster_id, attribute_id); /* cluster_id:attribute_id */ - nvs_handle_t handle; esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, nvs_namespace, NVS_READWRITE, &handle); if (err != ESP_OK) { return err; } - ESP_LOGD(TAG, "Store attribute in nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 ", attribute_id-0x%" PRIx32 "", - endpoint_id, cluster_id, attribute_id); + if (val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING || val.type == ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING || val.type == ESP_MATTER_VAL_TYPE_OCTET_STRING || @@ -310,5 +316,69 @@ esp_err_t store_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t a return err; } +static esp_err_t nvs_erase_val(const char *nvs_namespace, const char *attribute_key) +{ + nvs_handle_t handle; + esp_err_t err = nvs_open_from_partition(ESP_MATTER_NVS_PART_NAME, nvs_namespace, NVS_READWRITE, &handle); + if (err != ESP_OK) { + return err; + } + err = nvs_erase_key(handle, attribute_key); + nvs_commit(handle); + nvs_close(handle); + return err; +} + +esp_err_t get_val_from_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t & val) +{ + /* Get attribute key */ + char attribute_key[16] = {0}; + get_attribute_key(endpoint_id, cluster_id, attribute_id, attribute_key); + + ESP_LOGD(TAG, "read attribute from nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 "," + " attribute_id-0x%" PRIx32 "", endpoint_id, cluster_id, attribute_id); + esp_err_t err = nvs_get_val(ESP_MATTER_KVS_NAMESPACE, attribute_key, val); + if (err == ESP_ERR_NVS_NOT_FOUND) { + // If we don't find attribute key in the esp_matter_kvs namespace, we will try to get the attribute value + // with the previous key from the previous namespace. + char nvs_namespace[16] = {0}; + char old_attribute_key[16] = {0}; + snprintf(nvs_namespace, 16, "endpoint_%" PRIX16 "", endpoint_id); /* endpoint_id */ + snprintf(old_attribute_key, 16, "%" PRIX32 ":%" PRIX32 "", cluster_id, attribute_id); /* cluster_id:attribute_id */ + err = nvs_get_val(nvs_namespace, old_attribute_key, val); + if (err == ESP_OK) { + // If we get the attribute value with the previous key, we will erase it and store it in current namespace + // with the new attribute key. + if (nvs_erase_val(nvs_namespace, old_attribute_key) != ESP_OK) { + ESP_LOGE(TAG, "Failed to erase old attribute key"); + } + if (nvs_store_val(ESP_MATTER_KVS_NAMESPACE, attribute_key, val) != ESP_OK) { + ESP_LOGE(TAG, "Failed to store attribute_val with new attribute key"); + } + } + } + return err; +} + +esp_err_t store_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, const esp_matter_attr_val_t & val) +{ + /* Get attribute key */ + char attribute_key[16] = {0}; + get_attribute_key(endpoint_id, cluster_id, attribute_id, attribute_key); + ESP_LOGD(TAG, "Store attribute in nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 ", attribute_id-0x%" PRIx32 "", + endpoint_id, cluster_id, attribute_id); + return nvs_store_val(ESP_MATTER_KVS_NAMESPACE, attribute_key, val); +} + +esp_err_t erase_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id) +{ + /* Get attribute key */ + char attribute_key[16] = {0}; + get_attribute_key(endpoint_id, cluster_id, attribute_id, attribute_key); + ESP_LOGD(TAG, "Erase attribute in nvs: endpoint_id-0x%" PRIx16 ", cluster_id-0x%" PRIx32 ", attribute_id-0x%" PRIx32 "", + endpoint_id, cluster_id, attribute_id); + return nvs_erase_val(ESP_MATTER_KVS_NAMESPACE, attribute_key); +} + } // namespace attribute } // namespace esp_matter diff --git a/components/esp_matter/private/esp_matter_nvs.h b/components/esp_matter/private/esp_matter_nvs.h index cb6f0ded8..b8b46e591 100644 --- a/components/esp_matter/private/esp_matter_nvs.h +++ b/components/esp_matter/private/esp_matter_nvs.h @@ -20,9 +20,10 @@ namespace esp_matter { namespace attribute { +#define ESP_MATTER_KVS_NAMESPACE "esp_matter_kvs" + /** - * @brief Gets the attribute value from the NVS, it generates the namespace and key based on endpoint, cluster, - * and attribute id. + * @brief Gets the attribute value from the NVS, it generates the key based on endpoint, cluster, and attribute id. * * @param endpoint_id Endpoint Id * @param cluster_id Cluster Id @@ -33,8 +34,7 @@ namespace attribute { esp_err_t get_val_from_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t & val); /** - * @brief Stores the attribute value in NVS, it generates the namespace and key based on endpoint, cluster, - * and attribute id. + * @brief Stores the attribute value in NVS, it generates the key based on endpoint, cluster, and attribute id. * * @param endpoint_id Endpoint Id * @param cluster_id Cluster Id @@ -44,5 +44,16 @@ esp_err_t get_val_from_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t a */ esp_err_t store_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, const esp_matter_attr_val_t & val); +/** + * @brief Erases the attribute value in NVS, it generates the key based on endpoint, cluster, and attribute id. + * + * @param endpoint_id Endpoint Id + * @param cluster_id Cluster Id + * @param attribute_id Attribute Id + * + * @return ESP_OK on success, appropriate error code otherwise + */ +esp_err_t erase_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id); + } // namespace attribute } // namespace esp_matter diff --git a/components/esp_matter_bridge/esp_matter_bridge.cpp b/components/esp_matter_bridge/esp_matter_bridge.cpp index 34636dcb9..f221317f5 100644 --- a/components/esp_matter_bridge/esp_matter_bridge.cpp +++ b/components/esp_matter_bridge/esp_matter_bridge.cpp @@ -20,10 +20,8 @@ #include #include +#include #if MAX_BRIDGED_DEVICE_COUNT > 0 -#define ESP_MATTER_BRIDGE_PESISTENT_INFO_KEY "persistent_info" -#define ESP_MATTER_BRIDGE_NAMESPACE "bridge" -#define ESP_MATTER_BRIDGE_ENDPOINT_ID_ARRAY_KEY "ep_id_array" static const char *TAG = "esp_matter_bridge"; @@ -35,31 +33,7 @@ namespace esp_matter_bridge { static uint16_t bridged_endpoint_id_array[MAX_BRIDGED_DEVICE_COUNT]; /** Persistent Bridged Device Info **/ -static esp_err_t store_device_persistent_info(device_t *device) -{ - esp_err_t err = ESP_OK; - if (!device) { - ESP_LOGE(TAG, "device cannot be NULL"); - return ESP_ERR_INVALID_ARG; - } - - nvs_handle_t handle; - char namespace_name[16] = {0}; - snprintf(namespace_name, 16, "bridge_ep_%X", device->persistent_info.device_endpoint_id); - err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, namespace_name, NVS_READWRITE, &handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, - namespace_name, err); - return err; - } - err = nvs_set_blob(handle, ESP_MATTER_BRIDGE_PESISTENT_INFO_KEY, &device->persistent_info, - sizeof(device_persistent_info_t)); - nvs_commit(handle); - nvs_close(handle); - return err; -} - -static esp_err_t read_device_persistent_info(device_persistent_info_t *persistent_info, uint16_t endpoint_id) +static esp_err_t store_device_persistent_info(device_persistent_info_t *persistent_info) { esp_err_t err = ESP_OK; if (!persistent_info) { @@ -68,19 +42,79 @@ static esp_err_t read_device_persistent_info(device_persistent_info_t *persisten } nvs_handle_t handle; - char namespace_name[16] = {0}; - snprintf(namespace_name, 16, "bridge_ep_%X", endpoint_id); - err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, namespace_name, NVS_READONLY, &handle); + err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, NVS_READWRITE, + &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, - namespace_name, err); + ESP_MATTER_BRIDGE_NAMESPACE, err); + return err; + } + uint16_t endpoint_id = persistent_info->device_endpoint_id; + err = nvs_set_blob(handle, nvs_key_allocator::endpoint_pesistent_info(endpoint_id).KeyName(), + persistent_info, sizeof(device_persistent_info_t)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed on nvs_set_blob when storing device_persistent_info"); + } + err = nvs_commit(handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed on nvs_commit when storing device_persistent_info"); + } + nvs_close(handle); + return err; +} + +static esp_err_t nvs_get_device_persistent_info(const char *nvs_namespace, const char *nvs_key, + device_persistent_info_t *persistent_info) +{ + esp_err_t err = ESP_OK; + nvs_handle_t handle; + err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, nvs_namespace, NVS_READONLY, &handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, + nvs_namespace, err); return err; } size_t len = sizeof(device_persistent_info_t); - err = nvs_get_blob(handle, ESP_MATTER_BRIDGE_PESISTENT_INFO_KEY, persistent_info, &len); + err = nvs_get_blob(handle, nvs_key, persistent_info, &len); nvs_close(handle); return err; } +static esp_err_t read_device_persistent_info(device_persistent_info_t *persistent_info, uint16_t endpoint_id) +{ + if (!persistent_info) { + ESP_LOGE(TAG, "persistent_info cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = nvs_get_device_persistent_info(ESP_MATTER_BRIDGE_NAMESPACE, + nvs_key_allocator::endpoint_pesistent_info(endpoint_id).KeyName(), + persistent_info); + if (err == ESP_ERR_NVS_NOT_FOUND) { + // If we don't find persistent_info key in the bridge namespace, we will try to get the persistent_info + // with the previous key from the previous namespace. + char nvs_namespace[16] = {0}; + snprintf(nvs_namespace, 16, "bridge_ep_%X", endpoint_id); + err = nvs_get_device_persistent_info(nvs_namespace, "persistent_info", persistent_info); + if (err == ESP_OK) { + nvs_handle_t handle; + // If we get the persistent_info with the previous key, we will erase it and store it in current namespace + // with the new persistent_info key. + if (nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, nvs_namespace, NVS_READWRITE, + &handle) != ESP_OK) { + ESP_LOGE(TAG, "Failed to open %s namespace", nvs_namespace); + } else { + if (nvs_erase_key(handle, "persistent_info") != ESP_OK) { + ESP_LOGE(TAG, "Failed to erase persistent_info"); + } else { + nvs_commit(handle); + } + nvs_close(handle); + } + store_device_persistent_info(persistent_info); + } + } + return err; +} static esp_err_t store_bridged_endpoint_ids() { @@ -93,27 +127,61 @@ static esp_err_t store_bridged_endpoint_ids() ESP_MATTER_BRIDGE_NAMESPACE, err); return err; } - err = nvs_set_blob(handle, ESP_MATTER_BRIDGE_ENDPOINT_ID_ARRAY_KEY, bridged_endpoint_id_array, + err = nvs_set_blob(handle, nvs_key_allocator::endpoint_ids_array().KeyName(), bridged_endpoint_id_array, sizeof(bridged_endpoint_id_array)); - nvs_commit(handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed on nvs_set_blob when storing bridged_endpoint_ids"); + } + err = nvs_commit(handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed on nvs_commit when storing bridged_endpoint_ids"); + } + nvs_close(handle); + return err; +} + +static esp_err_t nvs_get_bridged_endpoint_ids(const char *nvs_namespace, const char *nvs_key) +{ + esp_err_t err = ESP_OK; + nvs_handle_t handle; + err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, nvs_namespace, NVS_READONLY, &handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, + nvs_namespace, err); + return err; + } + size_t len = sizeof(bridged_endpoint_id_array); + err = nvs_get_blob(handle, nvs_key, bridged_endpoint_id_array, &len); nvs_close(handle); return err; } static esp_err_t read_bridged_endpoint_ids() { - esp_err_t err = ESP_OK; - nvs_handle_t handle; - err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, NVS_READONLY, - &handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, - ESP_MATTER_BRIDGE_NAMESPACE, err); - return err; + esp_err_t err = + nvs_get_bridged_endpoint_ids(ESP_MATTER_BRIDGE_NAMESPACE, nvs_key_allocator::endpoint_ids_array().KeyName()); + if (err == ESP_ERR_NVS_NOT_FOUND) { + err = nvs_get_bridged_endpoint_ids(ESP_MATTER_BRIDGE_NAMESPACE, "ep_id_array"); + // If we don't find endpoint_ids_array in the bridge namespace, we will try to get the attribute value + // with the previous key from the previous namespace. + if (err == ESP_OK) { + // If we get endpoint_ids_array with the previous key, we will erase it and store it in current namespace + // with the new endpoint_ids_array key. + nvs_handle_t handle; + if (nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, + NVS_READWRITE, &handle) != ESP_OK) { + ESP_LOGE(TAG, "Failed to open bridge namespace"); + } else { + if (nvs_erase_key(handle, "ep_id_array") != ESP_OK) { + ESP_LOGE(TAG, "Failed to erase old key"); + } else { + nvs_commit(handle); + } + nvs_close(handle); + } + store_bridged_endpoint_ids(); + } } - size_t len = sizeof(bridged_endpoint_id_array); - err = nvs_get_blob(handle, ESP_MATTER_BRIDGE_ENDPOINT_ID_ARRAY_KEY, bridged_endpoint_id_array, &len); - nvs_close(handle); return err; } @@ -143,15 +211,15 @@ esp_err_t erase_bridged_device_info(uint16_t endpoint_id) } // Clear the persistent information of the removed endpoint nvs_handle_t handle; - char namespace_name[16] = {0}; - snprintf(namespace_name, 16, "bridge_ep_%X", endpoint_id); - err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, namespace_name, NVS_READWRITE, &handle); + err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, NVS_READWRITE, + &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, - namespace_name, err); + ESP_MATTER_BRIDGE_NAMESPACE, err); return err; } - err = nvs_erase_all(handle); + err = nvs_erase_key(handle, nvs_key_allocator::endpoint_pesistent_info(endpoint_id).KeyName()); + nvs_commit(handle); nvs_close(handle); return err; } @@ -298,7 +366,7 @@ device_t *create_device(node_t *node, uint16_t parent_endpoint_id, uint32_t devi // Store the persistent information dev->persistent_info.device_endpoint_id = esp_matter::endpoint::get_id(dev->endpoint); dev->persistent_info.device_type_id = device_type_id; - if (store_device_persistent_info(dev) != ESP_OK) { + if (store_device_persistent_info(&dev->persistent_info) != ESP_OK) { ESP_LOGE(TAG, "Failed to store the persistent info for the bridged device"); remove_device(dev); return NULL; @@ -405,20 +473,22 @@ esp_err_t initialize(node_t *node) return err; } -// TODO: Add a factory_reset_cb_register so that when we call esp_matter::factory_reset, we can erase other namespaces/partitions. +// TODO: Add a factory_reset_cb_register so that when we call esp_matter::factory_reset, we can erase other +// namespaces/partitions. esp_err_t factory_reset() { - if (read_bridged_endpoint_ids() != ESP_OK) { - ESP_LOGE(TAG, "Failed to read the endpoint id array"); + nvs_handle_t handle; + esp_err_t err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, + NVS_READWRITE, &handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, + ESP_MATTER_BRIDGE_NAMESPACE, err); + return err; } - - for (size_t idx = 0; idx < MAX_BRIDGED_DEVICE_COUNT; ++idx) { - if (bridged_endpoint_id_array[idx] != chip::kInvalidEndpointId) { - erase_bridged_device_info(bridged_endpoint_id_array[idx]); - bridged_endpoint_id_array[idx] = chip::kInvalidEndpointId; - } - } - return store_bridged_endpoint_ids(); + err = nvs_erase_all(handle); + nvs_commit(handle); + nvs_close(handle); + return err; } } // namespace esp_matter_bridge diff --git a/components/esp_matter_bridge/esp_matter_bridge.h b/components/esp_matter_bridge/esp_matter_bridge.h index 6a69fe98a..9edae700f 100644 --- a/components/esp_matter_bridge/esp_matter_bridge.h +++ b/components/esp_matter_bridge/esp_matter_bridge.h @@ -40,7 +40,8 @@ esp_err_t get_bridged_endpoint_ids(uint16_t *matter_endpoint_id_array); esp_err_t erase_bridged_device_info(uint16_t matter_endpoint_id); -device_t *create_device(esp_matter::node_t *node, uint16_t parent_endpoint_id, uint32_t device_type_id, void *priv_data); +device_t *create_device(esp_matter::node_t *node, uint16_t parent_endpoint_id, uint32_t device_type_id, + void *priv_data); device_t *resume_device(esp_matter::node_t *node, uint16_t device_endpoint_id, void *priv_data); diff --git a/components/esp_matter_bridge/nvs_key_allocator.h b/components/esp_matter_bridge/nvs_key_allocator.h new file mode 100644 index 000000000..0577ecffe --- /dev/null +++ b/components/esp_matter_bridge/nvs_key_allocator.h @@ -0,0 +1,38 @@ +// 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 + +#define ESP_MATTER_BRIDGE_NAMESPACE "bridge" + +using chip::StorageKeyName; + +namespace esp_matter_bridge { + +namespace nvs_key_allocator { + +inline StorageKeyName endpoint_ids_array() +{ + return StorageKeyName::FromConst("b/epida"); +} +inline StorageKeyName endpoint_pesistent_info(uint16_t endpoint_id) +{ + return StorageKeyName::Formatted("b/%x/pi", endpoint_id); +} + +} // namespace nvs_key_allocator + +} // namespace esp_matter_bridge diff --git a/examples/common/app_bridge/app_bridged_device.cpp b/examples/common/app_bridge/app_bridged_device.cpp index c454772d6..fd2d2c3c7 100644 --- a/examples/common/app_bridge/app_bridged_device.cpp +++ b/examples/common/app_bridge/app_bridged_device.cpp @@ -19,11 +19,25 @@ #include #include +#include // The bridge app can be used only when MAX_BRIDGED_DEVICE_COUNT > 0 #if defined(MAX_BRIDGED_DEVICE_COUNT) && MAX_BRIDGED_DEVICE_COUNT > 0 -#define APP_BRIDGE_BRIDGED_DEVICE_ADDR_KEY "dev_addr" -#define APP_BRIDGE_BRIDGED_DEVICE_TYPE_KEY "dev_type" + +namespace esp_matter_bridge { +namespace nvs_key_allocator { +static inline StorageKeyName endpoint_dev_addr(uint16_t endpoint_id) +{ + return StorageKeyName::Formatted("b/%x/da", endpoint_id); +} + +static inline StorageKeyName endpoint_dev_type(uint16_t endpoint_id) +{ + return StorageKeyName::Formatted("b/%x/dt", endpoint_id); +} + +} // namespace nvs_key_allocator +} // namespace esp_matter_bridge using namespace esp_matter; @@ -41,21 +55,21 @@ static esp_err_t app_bridge_store_bridged_device_info(app_bridged_device_t *brid return ESP_ERR_INVALID_ARG; } nvs_handle_t handle; - char namespace_name[16] = {0}; - snprintf(namespace_name, 16, "bridge_ep_%X", bridged_device->dev->persistent_info.device_endpoint_id); - err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, namespace_name, NVS_READWRITE, &handle); + err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, NVS_READWRITE, + &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, - namespace_name, err); + ESP_MATTER_BRIDGE_NAMESPACE, err); return err; } - err = nvs_set_blob(handle, APP_BRIDGE_BRIDGED_DEVICE_ADDR_KEY, &bridged_device->dev_addr, - sizeof(app_bridged_device_address_t)); + uint16_t endpoint_id = endpoint::get_id(bridged_device->dev->endpoint); + err = nvs_set_blob(handle, esp_matter_bridge::nvs_key_allocator::endpoint_dev_addr(endpoint_id).KeyName(), + &bridged_device->dev_addr, sizeof(app_bridged_device_address_t)); if (err != ESP_OK) { ESP_LOGE(TAG, "Error storing the device address"); } - err = nvs_set_blob(handle, APP_BRIDGE_BRIDGED_DEVICE_TYPE_KEY, &bridged_device->dev_type, - sizeof(app_bridged_device_type_t)); + err = nvs_set_blob(handle, esp_matter_bridge::nvs_key_allocator::endpoint_dev_type(endpoint_id).KeyName(), + &bridged_device->dev_type, sizeof(app_bridged_device_type_t)); if (err != ESP_OK) { ESP_LOGE(TAG, "Error storing the device type"); } @@ -74,21 +88,22 @@ static esp_err_t app_bridge_read_bridged_device_info(app_bridged_device_type_t * return ESP_ERR_INVALID_ARG; } nvs_handle_t handle; - char namespace_name[16] = {0}; - snprintf(namespace_name, 16, "bridge_ep_%X", matter_endpoint_id); - err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, namespace_name, NVS_READONLY, &handle); + err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, NVS_READONLY, + &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, - namespace_name, err); + ESP_MATTER_BRIDGE_NAMESPACE, err); return err; } size_t len = sizeof(app_bridged_device_address_t); - err = nvs_get_blob(handle, APP_BRIDGE_BRIDGED_DEVICE_ADDR_KEY, device_addr, &len); + err = nvs_get_blob(handle, esp_matter_bridge::nvs_key_allocator::endpoint_dev_addr(matter_endpoint_id).KeyName(), + device_addr, &len); if (err != ESP_OK) { ESP_LOGE(TAG, "Error reading the device address"); } len = sizeof(app_bridged_device_type_t); - err = nvs_get_blob(handle, APP_BRIDGE_BRIDGED_DEVICE_TYPE_KEY, device_type, &len); + err = nvs_get_blob(handle, esp_matter_bridge::nvs_key_allocator::endpoint_dev_type(matter_endpoint_id).KeyName(), + device_type, &len); if (err != ESP_OK) { ESP_LOGE(TAG, "Error reading the device type"); } @@ -96,6 +111,30 @@ static esp_err_t app_bridge_read_bridged_device_info(app_bridged_device_type_t * return err; } +static esp_err_t app_bridge_erase_bridged_device_info(uint16_t endpoint_id) +{ + esp_err_t err = ESP_OK; + nvs_handle_t handle; + err = nvs_open_from_partition(CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, ESP_MATTER_BRIDGE_NAMESPACE, NVS_READWRITE, + &handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error opening partition %s namespace %s. Err: %d", CONFIG_ESP_MATTER_BRIDGE_INFO_PART_NAME, + ESP_MATTER_BRIDGE_NAMESPACE, err); + return err; + } + err = nvs_erase_key(handle, esp_matter_bridge::nvs_key_allocator::endpoint_dev_addr(endpoint_id).KeyName()); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error erasing the device address"); + } + err = nvs_erase_key(handle, esp_matter_bridge::nvs_key_allocator::endpoint_dev_type(endpoint_id).KeyName()); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error erasing the device type"); + } + nvs_commit(handle); + nvs_close(handle); + return err; +} + /** Bridged Device's Address APIs */ app_bridged_device_address_t app_bridge_zigbee_address(uint8_t zigbee_endpointid, uint16_t zigbee_shortaddr) { @@ -181,7 +220,8 @@ esp_err_t app_bridge_initialize(node_t *node) matter_endpoint_id_array[idx]); continue; } - app_bridged_device_t *new_dev = (app_bridged_device_t *)esp_matter_mem_calloc(1, sizeof(app_bridged_device_t)); + app_bridged_device_t *new_dev = + (app_bridged_device_t *)esp_matter_mem_calloc(1, sizeof(app_bridged_device_t)); if (!new_dev) { ESP_LOGE(TAG, "Failed to alloc memory for the resumed bridged device"); continue; @@ -198,7 +238,7 @@ esp_err_t app_bridge_initialize(node_t *node) g_bridged_device_list = new_dev; g_current_bridged_device_count++; - //Enable the resumed endpoint + // Enable the resumed endpoint esp_matter::endpoint::enable(new_dev->dev->endpoint); } } @@ -230,6 +270,9 @@ esp_err_t app_bridge_remove_device(app_bridged_device_t *bridged_device) } } + uint16_t endpoint_id = endpoint::get_id(bridged_device->dev->endpoint); + app_bridge_erase_bridged_device_info(endpoint_id); + // Remove the bridged device from the node. error = esp_matter_bridge::remove_device(bridged_device->dev); if (error != ESP_OK) { @@ -325,9 +368,8 @@ app_bridged_device_t *app_bridge_get_device_by_espnow_macaddr(uint8_t espnow_mac { app_bridged_device_t *current_dev = g_bridged_device_list; while (current_dev) { - if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW) && current_dev->dev - && !memcmp(current_dev->dev_addr.espnow_macaddr, espnow_macaddr, 6) - ) { + if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW) && current_dev->dev && + !memcmp(current_dev->dev_addr.espnow_macaddr, espnow_macaddr, 6)) { return current_dev; } current_dev = current_dev->next; @@ -339,9 +381,8 @@ uint16_t app_bridge_get_matter_endpointid_by_espnow_macaddr(uint8_t espnow_macad { app_bridged_device_t *current_dev = g_bridged_device_list; while (current_dev) { - if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW) && current_dev->dev - && !memcmp(current_dev->dev_addr.espnow_macaddr, espnow_macaddr, 6) - ) { + if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW) && current_dev->dev && + !memcmp(current_dev->dev_addr.espnow_macaddr, espnow_macaddr, 6)) { return esp_matter::endpoint::get_id(current_dev->dev->endpoint); } current_dev = current_dev->next; @@ -349,7 +390,7 @@ uint16_t app_bridge_get_matter_endpointid_by_espnow_macaddr(uint8_t espnow_macad return chip::kInvalidEndpointId; } -uint8_t* app_bridge_get_espnow_macaddr_by_matter_endpointid(uint16_t matter_endpointid) +uint8_t *app_bridge_get_espnow_macaddr_by_matter_endpointid(uint16_t matter_endpointid) { app_bridged_device_t *current_dev = g_bridged_device_list; while (current_dev) {