Zephyr Settings 子系统提供了一个持久的键值存储机制,用于保存配置数据、校准参数、用户偏好等。Settings 系统支持多种存储后端,包括 Flash、文件系统等。
Settings 使用分层键值模型:
category/subcategory/name例如:
- bt/id_addr → 蓝牙身份地址
- wifi/ssid → WiFi SSID
- sensor/calib/temp → 温度传感器校准值
| 后端 | 配置选项 | 适用场景 |
|---|---|---|
| Flash | CONFIG_SETTINGS_FCB | 嵌入式设备 |
| NVS | CONFIG_SETTINGS_NVS | Nordic nVS |
| 文件系统 | CONFIG_SETTINGS_FS | 有文件系统的设备 |
| 自定义 | 自定义实现 | 特殊需求 |
# 启用 Settings 系统
CONFIG_SETTINGS=y
# 启用运行时设置
CONFIG_SETTINGS_RUNTIME=y
CONFIG_SETTINGS_FCB=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_SETTINGS_NVS=y
CONFIG_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_SETTINGS_FS=y
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
#include <zephyr/settings/settings.h>
// 定义设置键
#define SETTINGS_KEY_WIFI_SSID "wifi/ssid"
#define SETTINGS_KEY_WIFI_PASS "wifi/pass"
// 设置回调函数
static int settings_set(const char *key, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
if (strcmp(key, "ssid") == 0) {
// 读取 SSID
char ssid[33];
ssize_t len = read_cb(cb_arg, ssid, sizeof(ssid) - 1);
if (len > 0) {
ssid[len] = '\0';
printk("WiFi SSID: %s\n", ssid);
}
return 0;
}
return -ENOENT;
}
// 注册设置处理程序
SETTINGS_STATIC_HANDLER_DEFINE(wifi_config,
"wifi",
NULL,
settings_set,
NULL,
NULL);
#include <zephyr/settings/settings.h>
int save_wifi_config(const char *ssid, const char *password)
{
int ret;
// 保存 SSID
ret = settings_save_one("wifi/ssid", ssid, strlen(ssid));
if (ret < 0) {
printk("Failed to save SSID: %d\n", ret);
return ret;
}
// 保存密码
ret = settings_save_one("wifi/pass", password, strlen(password));
if (ret < 0) {
printk("Failed to save password: %d\n", ret);
return ret;
}
return 0;
}
int load_wifi_config(void)
{
int ret;
// 初始化 settings 子系统
ret = settings_subsys_init();
if (ret) {
printk("Settings init failed: %d\n", ret);
return ret;
}
// 加载所有设置
ret = settings_load();
if (ret) {
printk("Settings load failed: %d\n", ret);
return ret;
}
return 0;
}
int delete_wifi_config(void)
{
int ret;
ret = settings_delete("wifi/ssid");
if (ret < 0) {
return ret;
}
ret = settings_delete("wifi/pass");
return ret;
}
// 处理程序结构
struct settings_handler {
const char *name; // 键名前缀
int (*h_get)(const char *key, char *val, int val_len_max);
int (*h_set)(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg);
int (*h_commit)(void);
int (*h_export)(int (*func)(const char *name, const void *value, size_t val_len));
};
// 完整示例
static int my_settings_get(const char *key, char *val, int val_len_max)
{
// 从内存中获取设置值
if (strcmp(key, "ssid") == 0) {
return snprintk(val, val_len_max, "%s", current_ssid);
}
return -ENOENT;
}
static int my_settings_set(const char *key, size_t len,
settings_read_cb read_cb, void *cb_arg)
{
if (strcmp(key, "ssid") == 0) {
ssize_t ret = read_cb(cb_arg, current_ssid, sizeof(current_ssid) - 1);
if (ret > 0) {
current_ssid[ret] = '\0';
}
return 0;
}
return -ENOENT;
}
static int my_settings_commit(void)
{
// 所有设置加载完成后调用
printk("Settings committed\n");
return 0;
}
static int my_settings_export(int (*func)(const char *name,
const void *value,
size_t val_len))
{
// 导出当前设置到存储
func("wifi/ssid", current_ssid, strlen(current_ssid));
return 0;
}
SETTINGS_STATIC_HANDLER_DEFINE(wifi,
"wifi",
my_settings_get,
my_settings_set,
my_settings_commit,
my_settings_export);
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* 其他分区... */
storage_partition: partition@f8000 {
label = "storage";
reg = <0x000f8000 0x00008000>;
};
};
};
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS=y
CONFIG_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
#include <zephyr/settings/settings.h>
#include <zephyr/storage/flash_map.h>
// NVS 存储区域定义
#define STORAGE_NODE_LABEL storage
#define STORAGE_PARTITION storage_partition
#define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION)
void init_settings(void)
{
int ret;
// 初始化 settings 子系统
ret = settings_subsys_init();
if (ret) {
printk("Settings init failed: %d\n", ret);
return;
}
// 加载存储的设置
ret = settings_load();
if (ret) {
printk("Settings load failed: %d\n", ret);
}
}
// 保存整数
uint32_t counter = 12345;
settings_save_one("counter", &counter, sizeof(counter));
// 读取整数
uint32_t loaded_counter;
ssize_t len = settings_value_get("counter", &loaded_counter, sizeof(loaded_counter));
// 保存二进制数据
struct sensor_calib {
int16_t offset;
int16_t scale;
} calib = { .offset = 10, .scale = 1000 };
settings_save_one("sensor/calib", &calib, sizeof(calib));
// 开始批量保存
settings_save();
// 保存多个设置
settings_save_one("wifi/ssid", ssid, strlen(ssid));
settings_save_one("wifi/pass", pass, strlen(pass));
settings_save_one("wifi/sec", &security, sizeof(security));
// 结束批量保存(某些后端需要)
settings_commit();
static struct settings_handler my_handler = {
.name = "app",
.h_set = my_settings_set,
.h_commit = my_settings_commit,
};
int register_settings_handler(void)
{
return settings_register(&my_handler);
}
| 特性 | Settings | 直接 NVS |
|---|---|---|
| 键格式 | 分层字符串 | 整数 ID |
| 多后端支持 | ✅ | ❌ |
| 回调机制 | ✅ | ❌ |
| 运行时访问 | ✅ | ✅ |
| 复杂度 | 较高 | 较低 |
选择建议:
CONFIG_BT_SETTINGS=y
CONFIG_BT_SETTINGS_CCC_STORE=y
蓝牙设置自动存储:
CONFIG_LTE_LINK_CONTROL_SETTINGS=y
存储 LTE 连接参数。
CONFIG_NRF_MODEM_LIB_SETTINGS=y
// 好的组织方式
"network/wifi/ssid"
"network/wifi/pass"
"network/wifi/security"
"sensor/temp/calib_offset"
"sensor/temp/calib_scale"
"system/sleep_timeout"
#define DEFAULT_WIFI_SSID "MyNetwork"
#define DEFAULT_SLEEP_TIMEOUT 30000
static char wifi_ssid[33] = DEFAULT_WIFI_SSID;
static uint32_t sleep_timeout = DEFAULT_SLEEP_TIMEOUT;
static int settings_set(const char *key, size_t len,
settings_read_cb read_cb, void *cb_arg)
{
if (strcmp(key, "wifi/ssid") == 0) {
ssize_t ret = read_cb(cb_arg, wifi_ssid, sizeof(wifi_ssid) - 1);
if (ret > 0) {
wifi_ssid[ret] = '\0';
} else {
// 使用默认值
strcpy(wifi_ssid, DEFAULT_WIFI_SSID);
}
return 0;
}
return -ENOENT;
}
int save_critical_config(void)
{
int ret;
ret = settings_save_one("config/critical", &config, sizeof(config));
if (ret < 0) {
// 记录错误并尝试恢复
LOG_ERR("Failed to save critical config: %d", ret);
// 可以尝试重试或使用备份
return ret;
}
return 0;
}
// 使用后台保存,避免阻塞
static struct k_work_delayable save_work;
static void save_work_handler(struct k_work *work)
{
settings_save();
}
void schedule_settings_save(void)
{
k_work_schedule(&save_work, K_MSEC(100));
}
检查:
// 调试方法
ret = settings_subsys_init();
printk("Settings init: %d\n", ret);
ret = settings_load();
printk("Settings load: %d\n", ret);
# 增大存储分区
CONFIG_NVS_SECTOR_SIZE=0x2000
CONFIG_NVS_SECTOR_COUNT=8
#include <zephyr/settings/settings.h>
#include <zephyr/kernel.h>
static struct wifi_config {
char ssid[33];
char password[64];
uint8_t security;
bool valid;
} wifi_cfg;
static int wifi_settings_set(const char *key, size_t len,
settings_read_cb read_cb, void *cb_arg)
{
if (strcmp(key, "config") == 0 && len == sizeof(wifi_cfg)) {
if (read_cb(cb_arg, &wifi_cfg, sizeof(wifi_cfg)) == sizeof(wifi_cfg)) {
wifi_cfg.valid = true;
}
return 0;
}
return -ENOENT;
}
SETTINGS_STATIC_HANDLER_DEFINE(wifi, "wifi", NULL, wifi_settings_set, NULL, NULL);
int wifi_config_save(const char *ssid, const char *pass, uint8_t sec)
{
strncpy(wifi_cfg.ssid, ssid, sizeof(wifi_cfg.ssid) - 1);
strncpy(wifi_cfg.password, pass, sizeof(wifi_cfg.password) - 1);
wifi_cfg.security = sec;
wifi_cfg.valid = true;
return settings_save_one("wifi/config", &wifi_cfg, sizeof(wifi_cfg));
}
bool wifi_config_get(char *ssid, char *pass, uint8_t *sec)
{
if (!wifi_cfg.valid) {
return false;
}
strcpy(ssid, wifi_cfg.ssid);
strcpy(pass, wifi_cfg.password);
*sec = wifi_cfg.security;
return true;
}
Zephyr Settings 系统提供了灵活的持久化存储方案:
推荐用法:
*学习日期: 2026-03-20* *小白 🤖*