Rename config module to settings
This commit is contained in:
5
components/settings/CMakeLists.txt
Normal file
5
components/settings/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "settings.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES fatal nvs_flash
|
||||
)
|
||||
250
components/settings/settings.c
Normal file
250
components/settings/settings.c
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Copyright (c) Camden Dixie O'Brien
|
||||
*/
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include "fatal.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "Settings"
|
||||
|
||||
#define NAMESPACE "settings"
|
||||
#define MAX_CALLBACKS 8
|
||||
|
||||
typedef enum {
|
||||
HOSTNAME,
|
||||
SSID,
|
||||
PSK,
|
||||
TIMEZONE,
|
||||
ITEM_COUNT,
|
||||
} ItemIndex;
|
||||
|
||||
typedef struct {
|
||||
const char *id;
|
||||
const char *default_value;
|
||||
char value[SETTINGS_MAX_VALUE_SIZE];
|
||||
struct {
|
||||
SettingsCallback funcs[MAX_CALLBACKS];
|
||||
unsigned count;
|
||||
} callbacks;
|
||||
} Item;
|
||||
|
||||
static Item state[ITEM_COUNT] = {
|
||||
[HOSTNAME] = {
|
||||
.id = "hostname",
|
||||
.default_value = CONFIG_DEFAULT_HOSTNAME,
|
||||
},
|
||||
[SSID] = {
|
||||
.id = "ssid",
|
||||
.default_value = CONFIG_DEFAULT_SSID,
|
||||
},
|
||||
[PSK] = {
|
||||
.id = "psk",
|
||||
.default_value = CONFIG_DEFAULT_PSK,
|
||||
},
|
||||
[TIMEZONE] = {
|
||||
.id = "timezone",
|
||||
.default_value = CONFIG_DEFAULT_TIMEZONE,
|
||||
},
|
||||
};
|
||||
|
||||
static bool load(ItemIndex item)
|
||||
{
|
||||
esp_err_t error;
|
||||
|
||||
nvs_handle_t handle;
|
||||
error = nvs_open(NAMESPACE, NVS_READONLY, &handle);
|
||||
if (error == ESP_ERR_NVS_NOT_FOUND)
|
||||
return false;
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error opening NVS: %04x", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size = SETTINGS_MAX_VALUE_SIZE;
|
||||
error = nvs_get_str(handle, state[item].id, state[item].value, &size);
|
||||
if (error == ESP_ERR_NVS_NOT_FOUND) {
|
||||
nvs_close(handle);
|
||||
return false;
|
||||
} else if (error != ESP_OK) {
|
||||
ESP_LOGE(
|
||||
TAG, "Error loading %s from storage: %04x", state[item].id,
|
||||
error);
|
||||
nvs_close(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void save(ItemIndex item)
|
||||
{
|
||||
esp_err_t error;
|
||||
|
||||
nvs_handle_t handle;
|
||||
error = nvs_open(NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (error == ESP_ERR_NVS_NOT_FOUND)
|
||||
return;
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error opening NVS: %04x", error);
|
||||
return;
|
||||
}
|
||||
|
||||
error = nvs_set_str(handle, state[item].id, state[item].value);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(
|
||||
TAG, "Error loading %s from storage: %04x", state[item].id,
|
||||
error);
|
||||
nvs_close(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
error = nvs_commit(handle);
|
||||
if (error != ESP_OK)
|
||||
ESP_LOGE(TAG, "Error commiting NVS update: %04x", error);
|
||||
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
static void set(ItemIndex item, const char *value)
|
||||
{
|
||||
if (value == NULL) {
|
||||
ESP_LOGW(
|
||||
TAG, "Attempt to set %s to null pointer; ignored",
|
||||
state[item].id);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = strlen(value);
|
||||
if (len >= SETTINGS_MAX_VALUE_SIZE) {
|
||||
ESP_LOGW(
|
||||
TAG, "%s value \"%s\" exceeds maximum size; truncated",
|
||||
state[item].id, value);
|
||||
len = SETTINGS_MAX_VALUE_SIZE - 1;
|
||||
}
|
||||
|
||||
memcpy(state[item].value, value, len);
|
||||
state[item].value[len] = '\0';
|
||||
|
||||
save(item);
|
||||
for (unsigned i = 0; i < state[item].callbacks.count; ++i)
|
||||
state[item].callbacks.funcs[i](state[item].value);
|
||||
}
|
||||
|
||||
static size_t get(ItemIndex item, char *buffer, size_t buffer_size)
|
||||
{
|
||||
size_t len = strlen(state[item].value);
|
||||
if (len < buffer_size) {
|
||||
memcpy(buffer, state[item].value, len);
|
||||
buffer[len] = '\0';
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void add_callback(ItemIndex item, SettingsCallback callback)
|
||||
{
|
||||
if (callback == NULL) {
|
||||
ESP_LOGW(
|
||||
TAG, "Attempt to add null callback for %s; ignored",
|
||||
state[item].id);
|
||||
} else if (state[item].callbacks.count >= MAX_CALLBACKS) {
|
||||
ESP_LOGE(
|
||||
TAG, "Max callbacks exceeded for %s; callback discarded",
|
||||
state[item].id);
|
||||
} else {
|
||||
const unsigned pos = state[item].callbacks.count;
|
||||
state[item].callbacks.funcs[pos] = callback;
|
||||
++state[item].callbacks.count;
|
||||
}
|
||||
}
|
||||
|
||||
void settings_init()
|
||||
{
|
||||
esp_err_t error;
|
||||
|
||||
error = nvs_flash_init();
|
||||
if (error == ESP_ERR_NVS_NO_FREE_PAGES
|
||||
|| error == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_LOGI(TAG, "NVS partition full or outdated; erasing");
|
||||
(void)nvs_flash_erase();
|
||||
error = nvs_flash_init();
|
||||
}
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error initializing NVS store: %04x", error);
|
||||
FATAL();
|
||||
}
|
||||
|
||||
for (ItemIndex item = (ItemIndex)0; item < ITEM_COUNT; ++item) {
|
||||
if (!load(item)) {
|
||||
set(item, state[item].default_value);
|
||||
save(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void settings_set_hostname(const char *hostname)
|
||||
{
|
||||
set(HOSTNAME, hostname);
|
||||
}
|
||||
|
||||
size_t settings_get_hostname(char *buffer, size_t buffer_size)
|
||||
{
|
||||
return get(HOSTNAME, buffer, buffer_size);
|
||||
}
|
||||
|
||||
void settings_add_hostname_callback(SettingsCallback callback)
|
||||
{
|
||||
add_callback(HOSTNAME, callback);
|
||||
}
|
||||
|
||||
void settings_set_ssid(const char *ssid)
|
||||
{
|
||||
set(SSID, ssid);
|
||||
}
|
||||
|
||||
size_t settings_get_ssid(char *buffer, size_t buffer_size)
|
||||
{
|
||||
return get(SSID, buffer, buffer_size);
|
||||
}
|
||||
|
||||
void settings_add_ssid_callback(SettingsCallback callback)
|
||||
{
|
||||
add_callback(SSID, callback);
|
||||
}
|
||||
|
||||
void settings_set_psk(const char *psk)
|
||||
{
|
||||
set(PSK, psk);
|
||||
}
|
||||
|
||||
size_t settings_get_psk(char *buffer, size_t buffer_size)
|
||||
{
|
||||
return get(PSK, buffer, buffer_size);
|
||||
}
|
||||
|
||||
void settings_add_psk_callback(SettingsCallback callback)
|
||||
{
|
||||
add_callback(PSK, callback);
|
||||
}
|
||||
|
||||
void settings_set_timezone(const char *timezone)
|
||||
{
|
||||
set(TIMEZONE, timezone);
|
||||
}
|
||||
|
||||
size_t settings_get_timezone(char *buffer, size_t buffer_size)
|
||||
{
|
||||
return get(TIMEZONE, buffer, buffer_size);
|
||||
}
|
||||
|
||||
void settings_add_timezone_callback(SettingsCallback callback)
|
||||
{
|
||||
add_callback(TIMEZONE, callback);
|
||||
}
|
||||
139
components/settings/settings.h
Normal file
139
components/settings/settings.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Copyright (c) Camden Dixie O'Brien
|
||||
*/
|
||||
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define SETTINGS_MAX_VALUE_SIZE 32U
|
||||
|
||||
/**
|
||||
* Callback type for settings updates
|
||||
*/
|
||||
typedef void (*SettingsCallback)(const char *value);
|
||||
|
||||
/**
|
||||
* Initialize the settings module.
|
||||
*
|
||||
* If there are saved settings, they will be loaded. Otherwise,
|
||||
* the default settings will be loaded and saved.
|
||||
*/
|
||||
void settings_init(void);
|
||||
|
||||
/**
|
||||
* Set the device's hostname.
|
||||
*
|
||||
* The argument should be a null-terminated string. If the maximum
|
||||
* length is exceeded, the value will still be used, but will be
|
||||
* truncated.
|
||||
*/
|
||||
void settings_set_hostname(const char *hostname);
|
||||
|
||||
/**
|
||||
* Write the device's hostname into the given buffer.
|
||||
*
|
||||
* The length of the hostname is returned. If the value's size exceeds
|
||||
* the size of the buffer, nothing will be written to the buffer but
|
||||
* the length is still returned.
|
||||
*/
|
||||
size_t settings_get_hostname(char *buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* Add a callback for hostname updates.
|
||||
*
|
||||
* The function specified in the argument will be invoked whenever
|
||||
* the hostname is updated, with the new value as its argument. The
|
||||
* lifetime of the passed argument will be static, but the value may
|
||||
* be modified once the callback returns.
|
||||
*/
|
||||
void settings_add_hostname_callback(SettingsCallback callback);
|
||||
|
||||
/**
|
||||
* Set the SSID of the WiFi network.
|
||||
*
|
||||
* The argument should be a null-terminated string. If the maximum
|
||||
* length is exceeded, the value will still be used, but will be
|
||||
* truncated.
|
||||
*/
|
||||
void settings_set_ssid(const char *ssid);
|
||||
|
||||
/**
|
||||
* Write the SSID of the WiFi network into the given buffer.
|
||||
*
|
||||
* The length of the SSID is returned. If the value's size exceeds
|
||||
* the size of the buffer, nothing will be written to the buffer but
|
||||
* the length is still returned.
|
||||
*/
|
||||
size_t settings_get_ssid(char *buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* Add a callback for SSID updates.
|
||||
*
|
||||
* The function specified in the argument will be invoked whenever
|
||||
* the SSID is updated, with the new value as its argument. The
|
||||
* lifetime of the passed argument will be static, but the value may
|
||||
* be modified once the callback returns.
|
||||
*/
|
||||
void settings_add_ssid_callback(SettingsCallback callback);
|
||||
|
||||
/**
|
||||
* Set the PSK for the WiFi network.
|
||||
*
|
||||
* The argument should be a null-terminated string. If the maximum
|
||||
* length is exceeded, the value will still be used, but will be
|
||||
* truncated.
|
||||
*/
|
||||
void settings_set_psk(const char *psk);
|
||||
|
||||
/**
|
||||
* Write the PSK for the WiFi network into the given buffer.
|
||||
*
|
||||
* The length of the psk is returned. If the value's size exceeds
|
||||
* the size of the buffer, nothing will be written to the buffer but
|
||||
* the length is still returned.
|
||||
*/
|
||||
size_t settings_get_psk(char *buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* Add a callback for PSK updates.
|
||||
*
|
||||
* The function specified in the argument will be invoked whenever the
|
||||
* PSK is updated, with the new value as its argument. The lifetime of
|
||||
* the passed argument will be static, but the value may be modified
|
||||
* once the callback returns.
|
||||
*/
|
||||
void settings_add_psk_callback(SettingsCallback callback);
|
||||
|
||||
/**
|
||||
* Set the timezone.
|
||||
*
|
||||
* The argument should be a null-terminated string, containing a
|
||||
* timezone spec in the format expected by tzset(). If the maximum
|
||||
* length is exceeded, the value will still be used, but will be
|
||||
* truncated.
|
||||
*/
|
||||
void settings_set_timezone(const char *psk);
|
||||
|
||||
/**
|
||||
* Write the timezone into the given buffer.
|
||||
*
|
||||
* The length of the timezone is returned. If the value's size exceeds
|
||||
* the size of the buffer, nothing will be written to the buffer but
|
||||
* the length is still returned.
|
||||
*/
|
||||
size_t settings_get_timezone(char *buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* Add a callback for timezone updates.
|
||||
*
|
||||
* The function specified in the argument will be invoked whenever the
|
||||
* timezone is updated, with the new value as its argument. The
|
||||
* lifetime of the passed argument will be static, but the value may
|
||||
* be modified once the callback returns.
|
||||
*/
|
||||
void settings_add_timezone_callback(SettingsCallback callback);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user