diff --git a/components/esp_matter/Kconfig b/components/esp_matter/Kconfig index 8aa6b0163..c77d7bf94 100644 --- a/components/esp_matter/Kconfig +++ b/components/esp_matter/Kconfig @@ -20,6 +20,13 @@ menu "ESP Matter" help The NVS Partition name for ESP Matter to store the NONVOLATILE attribues + config ESP_MATTER_DEFERRED_ATTR_PERSISTENCE_TIME_MS + int "ESP Matter deferred attribute persistence time (ms)" + default 3000 + help + Some non-volatile attributes might be changed frequently, which might result in rapid flash wearout. + For those attributes, set the flag 'ATTRIBUTE_FLAG_DEFERRED' to defer the flash-writing for the time. + choice ESP_MATTER_DAC_PROVIDER prompt "DAC Provider options" default FACTORY_PARTITION_DAC_PROVIDER if ENABLE_ESP32_FACTORY_DATA_PROVIDER diff --git a/components/esp_matter/esp_matter.h b/components/esp_matter/esp_matter.h index 3a7b40de3..ce198ba83 100644 --- a/components/esp_matter/esp_matter.h +++ b/components/esp_matter/esp_matter.h @@ -84,6 +84,10 @@ typedef enum attribute_flags { /** The attribute read and write are overridden. The attribute value will be fetched from and will be updated using the override callback. The value of this attribute is not maintained internally. */ ATTRIBUTE_FLAG_OVERRIDE = ATTRIBUTE_FLAG_NULLABLE << 1, /* 0x100 */ + /** The attribute is non-volatile but its value will be changed frequently. If an attribute has this flag, its value + will not be written to flash immediately. A timer will be started and the attribute value will be written after + timeout. */ + ATTRIBUTE_FLAG_DEFERRED = ATTRIBUTE_FLAG_NULLABLE << 2, /* 0x200 */ } attribute_flags_t; /** Command flags */ diff --git a/components/esp_matter/esp_matter_core.cpp b/components/esp_matter/esp_matter_core.cpp index aba5bebe5..dfa6cff6c 100644 --- a/components/esp_matter/esp_matter_core.cpp +++ b/components/esp_matter/esp_matter_core.cpp @@ -1233,6 +1233,17 @@ uint32_t get_id(attribute_t *attribute) return current_attribute->attribute_id; } +constexpr uint16_t k_deferred_attribute_persistence_time_ms = CONFIG_ESP_MATTER_DEFERRED_ATTR_PERSISTENCE_TIME_MS; + +static void deferred_attribute_write(chip::System::Layer *layer, void *attribute_ptr) +{ + _attribute_t *current_attribute = (_attribute_t *)attribute_ptr; + ESP_LOGI(TAG, "Store the deferred attribute 0x%" PRIx32 " of cluster 0x%" PRIX32 " on endpoint 0x%" PRIx16, + current_attribute->attribute_id, current_attribute->cluster_id, current_attribute->endpoint_id); + store_val_in_nvs(current_attribute->endpoint_id, current_attribute->cluster_id, current_attribute->attribute_id, + current_attribute->val); +} + esp_err_t set_val(attribute_t *attribute, esp_matter_attr_val_t *val) { if (!attribute) { @@ -1269,8 +1280,17 @@ esp_err_t set_val(attribute_t *attribute, esp_matter_attr_val_t *val) } if (current_attribute->flags & ATTRIBUTE_FLAG_NONVOLATILE) { - store_val_in_nvs(current_attribute->endpoint_id, current_attribute->cluster_id, - current_attribute->attribute_id, current_attribute->val); + if (current_attribute->flags & ATTRIBUTE_FLAG_DEFERRED) { + if (chip::DeviceLayer::SystemLayer().IsTimerActive(deferred_attribute_write, current_attribute)) { + chip::DeviceLayer::SystemLayer().CancelTimer(deferred_attribute_write, current_attribute); + } + auto & system_layer = chip::DeviceLayer::SystemLayer(); + system_layer.StartTimer(chip::System::Clock::Milliseconds16(k_deferred_attribute_persistence_time_ms), + deferred_attribute_write, current_attribute); + } else { + store_val_in_nvs(current_attribute->endpoint_id, current_attribute->cluster_id, + current_attribute->attribute_id, current_attribute->val); + } } return ESP_OK; } @@ -1380,6 +1400,21 @@ callback_t get_override_callback(attribute_t *attribute) return current_attribute->override_callback; } +esp_err_t set_deferred_persistence(attribute_t *attribute) +{ + if (!attribute) { + ESP_LOGE(TAG, "Attribute cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + _attribute_t *current_attribute = (_attribute_t *)attribute; + if (!(current_attribute->flags & ATTRIBUTE_FLAG_NONVOLATILE)) { + ESP_LOGE(TAG, "Attribute should be non-volatile to set a deferred persistence time"); + return ESP_ERR_INVALID_ARG; + } + current_attribute->flags |= ATTRIBUTE_FLAG_DEFERRED; + return ESP_OK; +} + } /* attribute */ namespace command { diff --git a/components/esp_matter/esp_matter_core.h b/components/esp_matter/esp_matter_core.h index d37227dec..b4d38440c 100644 --- a/components/esp_matter/esp_matter_core.h +++ b/components/esp_matter/esp_matter_core.h @@ -582,6 +582,22 @@ esp_err_t set_override_callback(attribute_t *attribute, callback_t callback); */ callback_t get_override_callback(attribute_t *attribute); +/** Set attribute deferred persistence + * + * Only non-volatile attributes can be set with deferred presistence. If an attribute is configured with deferred + * presistence, any modifications to it will be enacted in its persistent storage with a specific delay + * (CONFIG_ESP_MATTER_DEFERRED_ATTR_PERSISTENCE_TIME_MS) + * + * It could be used for the non-volatile attribues which might be changed rapidly, such as CurrentLevel in LevelControl + * cluster. + * + * @param[in] attribute Attribute handle. + * + * @return ESP_OK on success. + * @return error in case of failure. + */ +esp_err_t set_deferred_persistence(attribute_t *attribute); + } /* attribute */ namespace command { diff --git a/examples/light/main/app_main.cpp b/examples/light/main/app_main.cpp index b0c9bb2ff..3958b0666 100644 --- a/examples/light/main/app_main.cpp +++ b/examples/light/main/app_main.cpp @@ -176,6 +176,19 @@ extern "C" void app_main() light_endpoint_id = endpoint::get_id(endpoint); ESP_LOGI(TAG, "Light created with endpoint_id %d", light_endpoint_id); + /* Mark deferred persistence for some attributes that might be changed rapidly */ + cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); + attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + attribute::set_deferred_persistence(current_level_attribute); + + cluster_t *color_control_cluster = cluster::get(endpoint, ColorControl::Id); + attribute_t *current_x_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentX::Id); + attribute::set_deferred_persistence(current_x_attribute); + attribute_t *current_y_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentY::Id); + attribute::set_deferred_persistence(current_y_attribute); + attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); + attribute::set_deferred_persistence(color_temp_attribute); + #if CHIP_DEVICE_CONFIG_ENABLE_THREAD /* Set OpenThread platform config */ esp_openthread_platform_config_t config = {