mirror of
https://github.com/esphome/esphome.git
synced 2025-09-28 08:02:23 +01:00
[nrf52, core] nrf52 core based on zephyr (#7049)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Samuel Sieb <samuel-github@sieb.net> Co-authored-by: Tomasz Duda <tomaszduda23@gmai.com> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
231
esphome/components/zephyr/__init__.py
Normal file
231
esphome/components/zephyr/__init__.py
Normal file
@@ -0,0 +1,231 @@
|
||||
import os
|
||||
from typing import Final, TypedDict
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_BOARD
|
||||
from esphome.core import CORE
|
||||
from esphome.helpers import copy_file_if_changed, write_file_if_changed
|
||||
|
||||
from .const import (
|
||||
BOOTLOADER_MCUBOOT,
|
||||
KEY_BOOTLOADER,
|
||||
KEY_EXTRA_BUILD_FILES,
|
||||
KEY_OVERLAY,
|
||||
KEY_PM_STATIC,
|
||||
KEY_PRJ_CONF,
|
||||
KEY_ZEPHYR,
|
||||
zephyr_ns,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@tomaszduda23"]
|
||||
AUTO_LOAD = ["preferences"]
|
||||
KEY_BOARD: Final = "board"
|
||||
|
||||
PrjConfValueType = bool | str | int
|
||||
|
||||
|
||||
class Section:
|
||||
def __init__(self, name, address, size, region):
|
||||
self.name = name
|
||||
self.address = address
|
||||
self.size = size
|
||||
self.region = region
|
||||
self.end_address = self.address + self.size
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
f"{self.name}:\n"
|
||||
f" address: 0x{self.address:X}\n"
|
||||
f" end_address: 0x{self.end_address:X}\n"
|
||||
f" region: {self.region}\n"
|
||||
f" size: 0x{self.size:X}"
|
||||
)
|
||||
|
||||
|
||||
class ZephyrData(TypedDict):
|
||||
board: str
|
||||
bootloader: str
|
||||
prj_conf: dict[str, tuple[PrjConfValueType, bool]]
|
||||
overlay: str
|
||||
extra_build_files: dict[str, str]
|
||||
pm_static: list[Section]
|
||||
|
||||
|
||||
def zephyr_set_core_data(config):
|
||||
CORE.data[KEY_ZEPHYR] = ZephyrData(
|
||||
board=config[CONF_BOARD],
|
||||
bootloader=config[KEY_BOOTLOADER],
|
||||
prj_conf={},
|
||||
overlay="",
|
||||
extra_build_files={},
|
||||
pm_static=[],
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
def zephyr_data() -> ZephyrData:
|
||||
return CORE.data[KEY_ZEPHYR]
|
||||
|
||||
|
||||
def zephyr_add_prj_conf(
|
||||
name: str, value: PrjConfValueType, required: bool = True
|
||||
) -> None:
|
||||
"""Set an zephyr prj conf value."""
|
||||
if not name.startswith("CONFIG_"):
|
||||
name = "CONFIG_" + name
|
||||
prj_conf = zephyr_data()[KEY_PRJ_CONF]
|
||||
if name not in prj_conf:
|
||||
prj_conf[name] = (value, required)
|
||||
return
|
||||
old_value, old_required = prj_conf[name]
|
||||
if old_value != value and old_required:
|
||||
raise ValueError(
|
||||
f"{name} already set with value '{old_value}', cannot set again to '{value}'"
|
||||
)
|
||||
if required:
|
||||
prj_conf[name] = (value, required)
|
||||
|
||||
|
||||
def zephyr_add_overlay(content):
|
||||
zephyr_data()[KEY_OVERLAY] += content
|
||||
|
||||
|
||||
def add_extra_build_file(filename: str, path: str) -> bool:
|
||||
"""Add an extra build file to the project."""
|
||||
extra_build_files = zephyr_data()[KEY_EXTRA_BUILD_FILES]
|
||||
if filename not in extra_build_files:
|
||||
extra_build_files[filename] = path
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def add_extra_script(stage: str, filename: str, path: str):
|
||||
"""Add an extra script to the project."""
|
||||
key = f"{stage}:{filename}"
|
||||
if add_extra_build_file(filename, path):
|
||||
cg.add_platformio_option("extra_scripts", [key])
|
||||
|
||||
|
||||
def zephyr_to_code(config):
|
||||
cg.add(zephyr_ns.setup_preferences())
|
||||
cg.add_build_flag("-DUSE_ZEPHYR")
|
||||
cg.set_cpp_standard("gnu++20")
|
||||
# build is done by west so bypass board checking in platformio
|
||||
cg.add_platformio_option("boards_dir", CORE.relative_build_path("boards"))
|
||||
|
||||
# c++ support
|
||||
zephyr_add_prj_conf("NEWLIB_LIBC", True)
|
||||
zephyr_add_prj_conf("CONFIG_FPU", True)
|
||||
zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True)
|
||||
zephyr_add_prj_conf("CPLUSPLUS", True)
|
||||
zephyr_add_prj_conf("CONFIG_STD_CPP20", True)
|
||||
zephyr_add_prj_conf("LIB_CPLUSPLUS", True)
|
||||
# preferences
|
||||
zephyr_add_prj_conf("SETTINGS", True)
|
||||
zephyr_add_prj_conf("NVS", True)
|
||||
zephyr_add_prj_conf("FLASH_MAP", True)
|
||||
zephyr_add_prj_conf("CONFIG_FLASH", True)
|
||||
# watchdog
|
||||
zephyr_add_prj_conf("WATCHDOG", True)
|
||||
zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False)
|
||||
# disable console
|
||||
zephyr_add_prj_conf("UART_CONSOLE", False)
|
||||
zephyr_add_prj_conf("CONSOLE", False, False)
|
||||
# use NFC pins as GPIO
|
||||
zephyr_add_prj_conf("NFCT_PINS_AS_GPIOS", True)
|
||||
|
||||
# <err> os: ***** USAGE FAULT *****
|
||||
# <err> os: Illegal load of EXC_RETURN into PC
|
||||
zephyr_add_prj_conf("MAIN_STACK_SIZE", 2048)
|
||||
|
||||
add_extra_script(
|
||||
"pre",
|
||||
"pre_build.py",
|
||||
os.path.join(os.path.dirname(__file__), "pre_build.py.script"),
|
||||
)
|
||||
|
||||
|
||||
def _format_prj_conf_val(value: PrjConfValueType) -> str:
|
||||
if isinstance(value, bool):
|
||||
return "y" if value else "n"
|
||||
if isinstance(value, int):
|
||||
return str(value)
|
||||
if isinstance(value, str):
|
||||
return f'"{value}"'
|
||||
raise ValueError
|
||||
|
||||
|
||||
def zephyr_add_cdc_acm(config, id):
|
||||
zephyr_add_prj_conf("USB_DEVICE_STACK", True)
|
||||
zephyr_add_prj_conf("USB_CDC_ACM", True)
|
||||
# prevent device to go to susspend, without this communication stop working in python
|
||||
# there should be a way to solve it
|
||||
zephyr_add_prj_conf("USB_DEVICE_REMOTE_WAKEUP", False)
|
||||
# prevent logging when buffer is full
|
||||
zephyr_add_prj_conf("USB_CDC_ACM_LOG_LEVEL_WRN", True)
|
||||
zephyr_add_overlay(
|
||||
f"""
|
||||
&zephyr_udc0 {{
|
||||
cdc_acm_uart{id}: cdc_acm_uart{id} {{
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
}};
|
||||
}};
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def zephyr_add_pm_static(section: Section):
|
||||
CORE.data[KEY_ZEPHYR][KEY_PM_STATIC].extend(section)
|
||||
|
||||
|
||||
def copy_files():
|
||||
want_opts = zephyr_data()[KEY_PRJ_CONF]
|
||||
|
||||
prj_conf = (
|
||||
"\n".join(
|
||||
f"{name}={_format_prj_conf_val(value[0])}"
|
||||
for name, value in sorted(want_opts.items())
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
write_file_if_changed(CORE.relative_build_path("zephyr/prj.conf"), prj_conf)
|
||||
|
||||
write_file_if_changed(
|
||||
CORE.relative_build_path("zephyr/app.overlay"),
|
||||
zephyr_data()[KEY_OVERLAY],
|
||||
)
|
||||
|
||||
if zephyr_data()[KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT or zephyr_data()[
|
||||
KEY_BOARD
|
||||
] in ["xiao_ble"]:
|
||||
fake_board_manifest = """
|
||||
{
|
||||
"frameworks": [
|
||||
"zephyr"
|
||||
],
|
||||
"name": "esphome nrf52",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104
|
||||
},
|
||||
"url": "https://esphome.io/",
|
||||
"vendor": "esphome"
|
||||
}
|
||||
"""
|
||||
write_file_if_changed(
|
||||
CORE.relative_build_path(f"boards/{zephyr_data()[KEY_BOARD]}.json"),
|
||||
fake_board_manifest,
|
||||
)
|
||||
|
||||
for filename, path in zephyr_data()[KEY_EXTRA_BUILD_FILES].items():
|
||||
copy_file_if_changed(
|
||||
path,
|
||||
CORE.relative_build_path(filename),
|
||||
)
|
||||
|
||||
pm_static = "\n".join(str(item) for item in zephyr_data()[KEY_PM_STATIC])
|
||||
if pm_static:
|
||||
write_file_if_changed(
|
||||
CORE.relative_build_path("zephyr/pm_static.yml"), pm_static
|
||||
)
|
14
esphome/components/zephyr/const.py
Normal file
14
esphome/components/zephyr/const.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from typing import Final
|
||||
|
||||
import esphome.codegen as cg
|
||||
|
||||
BOOTLOADER_MCUBOOT = "mcuboot"
|
||||
|
||||
KEY_BOOTLOADER: Final = "bootloader"
|
||||
KEY_EXTRA_BUILD_FILES: Final = "extra_build_files"
|
||||
KEY_OVERLAY: Final = "overlay"
|
||||
KEY_PM_STATIC: Final = "pm_static"
|
||||
KEY_PRJ_CONF: Final = "prj_conf"
|
||||
KEY_ZEPHYR = "zephyr"
|
||||
|
||||
zephyr_ns = cg.esphome_ns.namespace("zephyr")
|
86
esphome/components/zephyr/core.cpp
Normal file
86
esphome/components/zephyr/core.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifdef USE_ZEPHYR
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/watchdog.h>
|
||||
#include <zephyr/sys/reboot.h>
|
||||
#include <zephyr/random/rand32.h>
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static int wdt_channel_id = -1; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static const device *const WDT = DEVICE_DT_GET(DT_ALIAS(watchdog0));
|
||||
|
||||
void yield() { ::k_yield(); }
|
||||
uint32_t millis() { return k_ticks_to_ms_floor32(k_uptime_ticks()); }
|
||||
uint32_t micros() { return k_ticks_to_us_floor32(k_uptime_ticks()); }
|
||||
void delayMicroseconds(uint32_t us) { ::k_usleep(us); }
|
||||
void delay(uint32_t ms) { ::k_msleep(ms); }
|
||||
|
||||
void arch_init() {
|
||||
if (device_is_ready(WDT)) {
|
||||
static wdt_timeout_cfg wdt_config{};
|
||||
wdt_config.flags = WDT_FLAG_RESET_SOC;
|
||||
wdt_config.window.max = 2000;
|
||||
wdt_channel_id = wdt_install_timeout(WDT, &wdt_config);
|
||||
if (wdt_channel_id >= 0) {
|
||||
wdt_setup(WDT, WDT_OPT_PAUSE_HALTED_BY_DBG | WDT_OPT_PAUSE_IN_SLEEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void arch_feed_wdt() {
|
||||
if (wdt_channel_id >= 0) {
|
||||
wdt_feed(WDT, wdt_channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
void arch_restart() { sys_reboot(SYS_REBOOT_COLD); }
|
||||
uint32_t arch_get_cpu_cycle_count() { return k_cycle_get_32(); }
|
||||
uint32_t arch_get_cpu_freq_hz() { return sys_clock_hw_cycles_per_sec(); }
|
||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||
|
||||
Mutex::Mutex() {
|
||||
auto *mutex = new k_mutex();
|
||||
this->handle_ = mutex;
|
||||
k_mutex_init(mutex);
|
||||
}
|
||||
Mutex::~Mutex() { delete static_cast<k_mutex *>(this->handle_); }
|
||||
void Mutex::lock() { k_mutex_lock(static_cast<k_mutex *>(this->handle_), K_FOREVER); }
|
||||
bool Mutex::try_lock() { return k_mutex_lock(static_cast<k_mutex *>(this->handle_), K_NO_WAIT) == 0; }
|
||||
void Mutex::unlock() { k_mutex_unlock(static_cast<k_mutex *>(this->handle_)); }
|
||||
|
||||
IRAM_ATTR InterruptLock::InterruptLock() { state_ = irq_lock(); }
|
||||
IRAM_ATTR InterruptLock::~InterruptLock() { irq_unlock(state_); }
|
||||
|
||||
uint32_t random_uint32() { return rand(); } // NOLINT(cert-msc30-c, cert-msc50-cpp)
|
||||
bool random_bytes(uint8_t *data, size_t len) {
|
||||
sys_rand_get(data, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter)
|
||||
mac[0] = ((NRF_FICR->DEVICEADDR[1] & 0xFFFF) >> 8) | 0xC0;
|
||||
mac[1] = NRF_FICR->DEVICEADDR[1] & 0xFFFF;
|
||||
mac[2] = NRF_FICR->DEVICEADDR[0] >> 24;
|
||||
mac[3] = NRF_FICR->DEVICEADDR[0] >> 16;
|
||||
mac[4] = NRF_FICR->DEVICEADDR[0] >> 8;
|
||||
mac[5] = NRF_FICR->DEVICEADDR[0];
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
void setup();
|
||||
void loop();
|
||||
|
||||
int main() {
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
esphome::yield();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
120
esphome/components/zephyr/gpio.cpp
Normal file
120
esphome/components/zephyr/gpio.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifdef USE_ZEPHYR
|
||||
#include "gpio.h"
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace zephyr {
|
||||
|
||||
static const char *const TAG = "zephyr";
|
||||
|
||||
static int flags_to_mode(gpio::Flags flags, bool inverted, bool value) {
|
||||
int ret = 0;
|
||||
if (flags & gpio::FLAG_INPUT) {
|
||||
ret |= GPIO_INPUT;
|
||||
}
|
||||
if (flags & gpio::FLAG_OUTPUT) {
|
||||
ret |= GPIO_OUTPUT;
|
||||
if (value != inverted) {
|
||||
ret |= GPIO_OUTPUT_INIT_HIGH;
|
||||
} else {
|
||||
ret |= GPIO_OUTPUT_INIT_LOW;
|
||||
}
|
||||
}
|
||||
if (flags & gpio::FLAG_PULLUP) {
|
||||
ret |= GPIO_PULL_UP;
|
||||
}
|
||||
if (flags & gpio::FLAG_PULLDOWN) {
|
||||
ret |= GPIO_PULL_DOWN;
|
||||
}
|
||||
if (flags & gpio::FLAG_OPEN_DRAIN) {
|
||||
ret |= GPIO_OPEN_DRAIN;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ISRPinArg {
|
||||
uint8_t pin;
|
||||
bool inverted;
|
||||
};
|
||||
|
||||
ISRInternalGPIOPin ZephyrGPIOPin::to_isr() const {
|
||||
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
arg->pin = this->pin_;
|
||||
arg->inverted = this->inverted_;
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void ZephyrGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void ZephyrGPIOPin::setup() {
|
||||
const struct device *gpio = nullptr;
|
||||
if (this->pin_ < 32) {
|
||||
#define GPIO0 DT_NODELABEL(gpio0)
|
||||
#if DT_NODE_HAS_STATUS(GPIO0, okay)
|
||||
gpio = DEVICE_DT_GET(GPIO0);
|
||||
#else
|
||||
#error "gpio0 is disabled"
|
||||
#endif
|
||||
} else {
|
||||
#define GPIO1 DT_NODELABEL(gpio1)
|
||||
#if DT_NODE_HAS_STATUS(GPIO1, okay)
|
||||
gpio = DEVICE_DT_GET(GPIO1);
|
||||
#else
|
||||
#error "gpio1 is disabled"
|
||||
#endif
|
||||
}
|
||||
if (device_is_ready(gpio)) {
|
||||
this->gpio_ = gpio;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gpio %u is not ready.", this->pin_);
|
||||
return;
|
||||
}
|
||||
this->pin_mode(this->flags_);
|
||||
}
|
||||
|
||||
void ZephyrGPIOPin::pin_mode(gpio::Flags flags) {
|
||||
if (nullptr == this->gpio_) {
|
||||
return;
|
||||
}
|
||||
gpio_pin_configure(this->gpio_, this->pin_ % 32, flags_to_mode(flags, this->inverted_, this->value_));
|
||||
}
|
||||
|
||||
std::string ZephyrGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%u, P%u.%u", this->pin_, this->pin_ / 32, this->pin_ % 32);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool ZephyrGPIOPin::digital_read() {
|
||||
if (nullptr == this->gpio_) {
|
||||
return false;
|
||||
}
|
||||
return bool(gpio_pin_get(this->gpio_, this->pin_ % 32) != this->inverted_);
|
||||
}
|
||||
|
||||
void ZephyrGPIOPin::digital_write(bool value) {
|
||||
// make sure that value is not ignored since it can be inverted e.g. on switch side
|
||||
// that way init state should be correct
|
||||
this->value_ = value;
|
||||
if (nullptr == this->gpio_) {
|
||||
return;
|
||||
}
|
||||
gpio_pin_set(this->gpio_, this->pin_ % 32, value != this->inverted_ ? 1 : 0);
|
||||
}
|
||||
void ZephyrGPIOPin::detach_interrupt() const {
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // namespace zephyr
|
||||
|
||||
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
38
esphome/components/zephyr/gpio.h
Normal file
38
esphome/components/zephyr/gpio.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ZEPHYR
|
||||
#include "esphome/core/hal.h"
|
||||
struct device;
|
||||
namespace esphome {
|
||||
namespace zephyr {
|
||||
|
||||
class ZephyrGPIOPin : public InternalGPIOPin {
|
||||
public:
|
||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
|
||||
|
||||
void setup() override;
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return this->pin_; }
|
||||
bool is_inverted() const override { return this->inverted_; }
|
||||
gpio::Flags get_flags() const override { return flags_; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
uint8_t pin_;
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
const device *gpio_ = nullptr;
|
||||
bool value_ = false;
|
||||
};
|
||||
|
||||
} // namespace zephyr
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ZEPHYR
|
4
esphome/components/zephyr/pre_build.py.script
Normal file
4
esphome/components/zephyr/pre_build.py.script
Normal file
@@ -0,0 +1,4 @@
|
||||
Import("env")
|
||||
|
||||
board_config = env.BoardConfig()
|
||||
board_config.update("frameworks", ["arduino", "zephyr"])
|
156
esphome/components/zephyr/preferences.cpp
Normal file
156
esphome/components/zephyr/preferences.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#ifdef USE_ZEPHYR
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <zephyr/settings/settings.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace zephyr {
|
||||
|
||||
static const char *const TAG = "zephyr.preferences";
|
||||
|
||||
#define ESPHOME_SETTINGS_KEY "esphome"
|
||||
|
||||
class ZephyrPreferenceBackend : public ESPPreferenceBackend {
|
||||
public:
|
||||
ZephyrPreferenceBackend(uint32_t type) { this->type_ = type; }
|
||||
ZephyrPreferenceBackend(uint32_t type, std::vector<uint8_t> &&data) : data(std::move(data)) { this->type_ = type; }
|
||||
|
||||
bool save(const uint8_t *data, size_t len) override {
|
||||
this->data.resize(len);
|
||||
std::memcpy(this->data.data(), data, len);
|
||||
ESP_LOGVV(TAG, "save key: %u, len: %d", this->type_, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load(uint8_t *data, size_t len) override {
|
||||
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;
|
||||
}
|
||||
std::memcpy(data, this->data.data(), len);
|
||||
ESP_LOGVV(TAG, "load key: %u, len: %d", this->type_, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t get_type() const { return this->type_; }
|
||||
std::string get_key() const { return str_sprintf(ESPHOME_SETTINGS_KEY "/%" PRIx32, this->type_); }
|
||||
|
||||
std::vector<uint8_t> 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.", this->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 {
|
||||
for (auto *backend : this->backends_) {
|
||||
if (backend->get_type() == type) {
|
||||
return ESPPreferenceObject(backend);
|
||||
}
|
||||
}
|
||||
printf("type %u size %u\n", type, this->backends_.size());
|
||||
auto *pref = new ZephyrPreferenceBackend(type); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
ESP_LOGD(TAG, "Add new setting %s.", pref->get_key().c_str());
|
||||
this->backends_.push_back(pref);
|
||||
return ESPPreferenceObject(pref);
|
||||
}
|
||||
|
||||
bool sync() override {
|
||||
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 {
|
||||
ESP_LOGD(TAG, "Reset settings");
|
||||
for (auto *backend : this->backends_) {
|
||||
// save empty delete data
|
||||
backend->data.clear();
|
||||
}
|
||||
sync();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<ZephyrPreferenceBackend *> backends_;
|
||||
|
||||
static int load_setting(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) {
|
||||
auto type = parse_hex<uint32_t>(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<uint8_t> 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)); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
static_cast<ZephyrPreferences *>(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<ZephyrPreferences *>(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)
|
||||
global_preferences = prefs;
|
||||
prefs->open();
|
||||
}
|
||||
|
||||
} // namespace zephyr
|
||||
|
||||
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
13
esphome/components/zephyr/preferences.h
Normal file
13
esphome/components/zephyr/preferences.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ZEPHYR
|
||||
|
||||
namespace esphome {
|
||||
namespace zephyr {
|
||||
|
||||
void setup_preferences();
|
||||
|
||||
} // namespace zephyr
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user