diff --git a/esphome/components/zephyr/__init__.py b/esphome/components/zephyr/__init__.py index 17becbf067..e782b4871f 100644 --- a/esphome/components/zephyr/__init__.py +++ b/esphome/components/zephyr/__init__.py @@ -17,6 +17,8 @@ from esphome.const import ( CONF_BOARD, ) + +AUTO_LOAD = ["preferences"] KEY_BOARD = "board" @@ -80,6 +82,9 @@ def zephyr_to_code(conf): zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True) zephyr_add_prj_conf("CPLUSPLUS", True) zephyr_add_prj_conf("LIB_CPLUSPLUS", True) + # preferences + zephyr_add_prj_conf("SETTINGS", True) + zephyr_add_prj_conf("NVS", True) # watchdog zephyr_add_prj_conf("WATCHDOG", True) zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False) @@ -93,8 +98,10 @@ def zephyr_to_code(conf): zephyr_add_prj_conf("USE_SEGGER_RTT", True) zephyr_add_prj_conf("RTT_CONSOLE", True) zephyr_add_prj_conf("LOG", True) + zephyr_add_prj_conf("LOG_BLOCK_IN_THREAD", True) + zephyr_add_prj_conf("LOG_BUFFER_SIZE", 4096) + zephyr_add_prj_conf("SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL", True) - # zephyr_add_prj_conf("USB_DEVICE_LOG_LEVEL_ERR", True) zephyr_add_prj_conf("USB_CDC_ACM_LOG_LEVEL_WRN", True) diff --git a/esphome/components/zephyr/preferences.cpp b/esphome/components/zephyr/preferences.cpp index 408cbeb347..b8c4be467c 100644 --- a/esphome/components/zephyr/preferences.cpp +++ b/esphome/components/zephyr/preferences.cpp @@ -2,105 +2,148 @@ #include "esphome/core/preferences.h" #include "esphome/core/log.h" +#include namespace esphome { namespace zephyr { -static const char *const TAG = "esp32.preferences"; +static const char *const TAG = "zephyr.preferences"; -struct NVSData { - std::string key; - std::vector data; -}; - -static std::vector s_pending_save; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +#define ESPHOME_SETTINGS_KEY "esphome" class ZephyrPreferenceBackend : public ESPPreferenceBackend { public: - std::string key; - // uint32_t nvs_handle; + ZephyrPreferenceBackend(uint32_t type) { this->type_ = type; } + ZephyrPreferenceBackend(uint32_t type, std::vector &&data) : data(std::move(data)) { this->type_ = type; } + bool save(const uint8_t *data, size_t len) override { - // try find in pending saves and update that - for (auto &obj : s_pending_save) { - if (obj.key == key) { - obj.data.assign(data, data + len); - return true; - } - } - NVSData save{}; - save.key = key; - save.data.assign(data, data + len); - s_pending_save.emplace_back(save); - ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %d", key.c_str(), len); + this->data.resize(len); + std::memcpy(this->data.data(), data, len); + ESP_LOGVV(TAG, "save key: %u, len: %d", type_, len); return true; } bool load(uint8_t *data, size_t len) override { - // try find in pending saves and load from that - for (auto &obj : s_pending_save) { - if (obj.key == key) { - if (obj.data.size() != len) { - // size mismatch - return false; - } - memcpy(data, obj.data.data(), len); - return true; - } + if (len != this->data.size()) { + ESP_LOGE(TAG, "size of setting key %s changed, from: %u, to: %u", get_key().c_str(), this->data.size(), len); + return false; } - - // TODO - - // size_t actual_len; - // esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len); - // if (err != 0) { - // ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err)); - // return false; - // } - // if (actual_len != len) { - // ESP_LOGVV(TAG, "NVS length does not match (%u!=%u)", actual_len, len); - // return false; - // } - // err = nvs_get_blob(nvs_handle, key.c_str(), data, &len); - // if (err != 0) { - // ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err)); - // return false; - // } else { - // ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %d", key.c_str(), len); - // } + std::memcpy(data, this->data.data(), len); + ESP_LOGVV(TAG, "load key: %u, len: %d", type_, len); return true; } + + const uint32_t get_type() const { return type_; } + const std::string get_key() const { return str_sprintf(ESPHOME_SETTINGS_KEY "/%" PRIx32, type_); } + + std::vector data; + + protected: + uint32_t type_ = 0; }; class ZephyrPreferences : public ESPPreferences { public: + void open() { + int err = settings_subsys_init(); + if (err) { + ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err); + return; + } + + static struct settings_handler settings_cb = { + .name = ESPHOME_SETTINGS_KEY, + .h_set = load_setting_, + .h_export = export_settings_, + }; + + err = settings_register(&settings_cb); + if (err) { + ESP_LOGE(TAG, "setting_register failed, err, %d", err); + return; + } + + err = settings_load_subtree(ESPHOME_SETTINGS_KEY); + if (err) { + ESP_LOGE(TAG, "Cannot load settings, err: %d", err); + return; + } + ESP_LOGD(TAG, "Loaded %u settings.", backends_.size()); + } + ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { return make_preference(length, type); } ESPPreferenceObject make_preference(size_t length, uint32_t type) override { - auto *pref = new ZephyrPreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) - // pref->nvs_handle = nvs_handle; - - uint32_t keyval = type; - pref->key = str_sprintf("%" PRIu32, keyval); - + for (auto backend : backends_) { + if (backend->get_type() == type) { + return ESPPreferenceObject(backend); + } + } + printf("type %u size %u\n", type, backends_.size()); + auto *pref = new ZephyrPreferenceBackend(type); + ESP_LOGD(TAG, "Add new setting %s.", pref->get_key().c_str()); + backends_.push_back(pref); return ESPPreferenceObject(pref); } bool sync() override { - // TODO + ESP_LOGD(TAG, "Save settings"); + int err = settings_save(); + if (err) { + ESP_LOGE(TAG, "Cannot save settings, err: %d", err); + return false; + } return true; } bool reset() override { - // TODO + ESP_LOGD(TAG, "Reset settings"); + for (auto backend : backends_) { + // save empty delete data + backend->data.clear(); + } + sync(); return true; } + + protected: + std::vector backends_; + + static int load_setting_(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { + auto type = parse_hex(name); + if (!type.has_value()) { + std::string full_name(ESPHOME_SETTINGS_KEY); + full_name += "/"; + full_name += name; + // Delete unusable keys. Otherwise it will stay in flash forever. + settings_delete(full_name.c_str()); + return 1; + } + std::vector data(len); + int err = read_cb(cb_arg, data.data(), len); + + ESP_LOGD(TAG, "load setting, name: %s(%u), len %u, err %u", name, *type, len, err); + auto *pref = new ZephyrPreferenceBackend(*type, std::move(data)); + static_cast(global_preferences)->backends_.push_back(pref); + return 0; + } + + static int export_settings_(int (*cb)(const char *name, const void *value, size_t val_len)) { + for (auto backend : static_cast(global_preferences)->backends_) { + auto name = backend->get_key(); + int err = cb(name.c_str(), backend->data.data(), backend->data.size()); + ESP_LOGD(TAG, "save in flash, name %s, len %u, err %d", name.c_str(), backend->data.size(), err); + } + return 0; + } }; void setup_preferences() { - auto *prefs = new ZephyrPreferences(); // NOLINT(cppcoreguidelines-owning-memory) + auto *prefs = new ZephyrPreferences(); global_preferences = prefs; + prefs->open(); } } // namespace zephyr diff --git a/esphome/zephyr_tools.py b/esphome/zephyr_tools.py index 8bde2aa2c8..eef18eea82 100644 --- a/esphome/zephyr_tools.py +++ b/esphome/zephyr_tools.py @@ -11,6 +11,7 @@ from smpclient import SMPClient from smpclient.mcuboot import IMAGE_TLV, ImageInfo, TLVNotFound from smpclient.requests.image_management import ImageStatesRead, ImageStatesWrite from smpclient.requests.os_management import ResetWrite +from smp.exceptions import SMPBadStartDelimiter from smpclient.generics import error, success from esphome.espota2 import ProgressBar @@ -102,9 +103,13 @@ async def smpmgr_upload(config, host, firmware): _LOGGER.info(f"Connected {host}...") - image_state = await asyncio.wait_for( - smp_client.request(ImageStatesRead()), timeout=SMPClient.MEDIUM_TIMEOUT - ) + try: + image_state = await asyncio.wait_for( + smp_client.request(ImageStatesRead()), timeout=SMPClient.MEDIUM_TIMEOUT + ) + except SMPBadStartDelimiter as e: + _LOGGER.error(f"mcumgr is not supported by device ({e})") + return 1 already_uploaded = False