Files
its-g5-receiver-firmware/main/config.c
T

597 lines
18 KiB
C

#include "sdkconfig.h"
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "esp_console.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "mbedtls/base64.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "argtable3/argtable3.h"
#include "config.h"
static const char TAG[] = "CONFIG";
static nvs_handle_t handle;
typedef esp_err_t (*config_u8_getter) (uint8_t *out);
typedef esp_err_t (*config_u16_getter) (uint16_t *out);
typedef esp_err_t (*config_u32_getter) (uint32_t *out);
typedef esp_err_t (*config_u64_getter) (uint64_t *out);
typedef esp_err_t (*config_i8_getter) (int8_t *out);
typedef esp_err_t (*config_i16_getter) (int16_t *out);
typedef esp_err_t (*config_i32_getter) (int32_t *out);
typedef esp_err_t (*config_i64_getter) (int64_t *out);
typedef esp_err_t (*config_str_getter) (char *out, size_t *size);
typedef esp_err_t (*config_blob_getter)(uint8_t *out, size_t *size);
typedef esp_err_t (*config_u8_setter) (uint8_t val);
typedef esp_err_t (*config_u16_setter) (uint16_t val);
typedef esp_err_t (*config_u32_setter) (uint32_t val);
typedef esp_err_t (*config_u64_setter) (uint64_t val);
typedef esp_err_t (*config_i8_setter) (int8_t val);
typedef esp_err_t (*config_i16_setter) (int16_t val);
typedef esp_err_t (*config_i32_setter) (int32_t val);
typedef esp_err_t (*config_i64_setter) (int64_t val);
typedef esp_err_t (*config_str_setter) (const char *val);
typedef esp_err_t (*config_blob_setter)(const uint8_t *val, size_t size);
typedef struct config_key {
char name[NVS_KEY_NAME_MAX_SIZE];
nvs_type_t type;
size_t buffer_size;
void *getter;
void *setter;
} config_key_t;
static esp_err_t config_get_node_id(char *out, size_t *size);
static const config_key_t config_keys[] = {
[CONFIG_INDEX_NODEID] = { "nodeid", NVS_TYPE_STR, CONFIG_NODEID_BUFFER_SIZE, config_get_node_id, NULL },
[CONFIG_INDEX_MQTT_URI] = { "mqtturi", NVS_TYPE_STR, CONFIG_MQTT_URI_BUFFER_SIZE, NULL, NULL },
};
#define CONFIG_KEYS_END (&config_keys[sizeof(config_keys)/sizeof(*config_keys)])
void config_init(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
ESP_ERROR_CHECK(nvs_open("its", NVS_READWRITE, &handle));
}
esp_err_t config_get_node_id(char *out, size_t *size)
{
size_t size_copy = *size;
esp_err_t res = nvs_get_str(handle, "nodeid", out, &size_copy);
if (res != ESP_OK)
{
if (res != ESP_ERR_NVS_NOT_FOUND)
ESP_LOGW(TAG, "nvs_get_str failed: %s", esp_err_to_name(res));
uint8_t base_mac[6];
ESP_ERROR_CHECK(esp_base_mac_addr_get(base_mac));
int print_res = snprintf(out, *size, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
base_mac[0], base_mac[1], base_mac[2], base_mac[3], base_mac[4], base_mac[5]);
if (print_res > *size - 1)
{
ESP_LOGE(TAG, "buffer too small to print");
return ESP_ERR_INVALID_SIZE;
}
if (print_res < 0)
{
ESP_LOGE(TAG, "snprintf failed");
return ESP_ERR_INVALID_STATE;
}
size_copy = print_res + 1;
}
*size = size_copy;
return ESP_OK;
}
esp_err_t config_get_str(config_index_t index, char *out, size_t *size)
{
const config_key_t *key = &config_keys[index];
if (key->type != NVS_TYPE_STR)
{
ESP_LOGE(TAG, "Attempt to get key %s with wrong type str", key->name);
return ESP_ERR_INVALID_ARG;
}
if (key->getter)
return ((config_str_getter)key->getter)(out, size);
else
return nvs_get_str(handle, key->name, out, size);
}
esp_err_t config_set_str(config_index_t index, const char *value)
{
const config_key_t *key = &config_keys[index];
if (key->type != NVS_TYPE_STR)
{
ESP_LOGE(TAG, "Attempt to set key %s with wrong type str", key->name);
return ESP_ERR_INVALID_ARG;
}
if (strlen(value) >= key->buffer_size)
return ESP_ERR_INVALID_SIZE;
if (key->setter)
return ((config_str_setter)key->setter)(value);
else
return nvs_set_str(handle, key->name, value);
}
static const config_key_t *config_key_find(const char *name)
{
for (const config_key_t *it = config_keys; it != CONFIG_KEYS_END; it++)
{
if (strcmp(name, it->name))
continue;
return it;
}
return NULL;
}
static struct {
arg_lit_t *raw;
arg_str_t *key;
arg_str_t *value;
arg_end_t *end;
} config_set_args;
static int cmd_config_set(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&config_set_args);
if (nerrors != 0) {
arg_print_errors(stderr, config_set_args.end, argv[0]);
return 1;
}
const char *key_str = config_set_args.key->sval[0];
const char *value_str = config_set_args.value->sval[0];
const config_key_t *key = config_key_find(key_str);
if (key == NULL)
{
ESP_LOGE(TAG, "No such config key: %s", key_str);
return 1;
}
esp_err_t res;
switch (key->type)
{
case NVS_TYPE_U8:
case NVS_TYPE_U16:
case NVS_TYPE_U32:
case NVS_TYPE_U64:
{
char *end;
uint64_t parsed = strtoull(value_str, &end, 0);
if (*end != '\0')
{
ESP_LOGE(TAG, "Invalid unsigned integer");
return 1;
}
if ((key->type == NVS_TYPE_U8 && parsed > UINT8_MAX) ||
(key->type == NVS_TYPE_U16 && parsed > UINT16_MAX) ||
(key->type == NVS_TYPE_U32 && parsed > UINT32_MAX))
{
ESP_LOGE(TAG, "Value out of range for type");
return 1;
}
if (key->setter == NULL || config_set_args.raw->count)
{
switch (key->type) {
case NVS_TYPE_U8:
res = nvs_set_u8(handle, key_str, (uint8_t)parsed);
break;
case NVS_TYPE_U16:
res = nvs_set_u16(handle, key_str, (uint16_t)parsed);
break;
case NVS_TYPE_U32:
res = nvs_set_u32(handle, key_str, (uint32_t)parsed);
break;
case NVS_TYPE_U64:
res = nvs_set_u64(handle, key_str, parsed);
break;
default:
__builtin_unreachable();
}
}
else
{
switch (key->type) {
case NVS_TYPE_U8:
res = ((config_u8_setter)key->setter)((uint8_t)parsed);
break;
case NVS_TYPE_U16:
res = ((config_u16_setter)key->setter)((uint16_t)parsed);
break;
case NVS_TYPE_U32:
res = ((config_u32_setter)key->setter)((uint32_t)parsed);
break;
case NVS_TYPE_U64:
res = ((config_u64_setter)key->setter)(parsed);
break;
default:
__builtin_unreachable();
}
}
}
break;
case NVS_TYPE_I8:
case NVS_TYPE_I16:
case NVS_TYPE_I32:
case NVS_TYPE_I64:
{
char *end;
int64_t parsed = strtoll(value_str, &end, 0);
if (*end != '\0')
{
ESP_LOGE(TAG, "Invalid signed integer");
return 1;
}
if ((key->type == NVS_TYPE_I8 && (parsed < INT8_MIN || parsed > INT8_MAX)) ||
(key->type == NVS_TYPE_I16 && (parsed < INT16_MIN || parsed > INT16_MAX)) ||
(key->type == NVS_TYPE_I32 && (parsed < INT32_MIN || parsed > INT32_MAX)))
{
ESP_LOGE(TAG, "Value out of range for type");
return 1;
}
if (key->setter == NULL || config_set_args.raw->count)
{
switch (key->type) {
case NVS_TYPE_U8:
res = nvs_set_i8(handle, key_str, (int8_t)parsed);
break;
case NVS_TYPE_U16:
res = nvs_set_i16(handle, key_str, (int16_t)parsed);
break;
case NVS_TYPE_U32:
res = nvs_set_i32(handle, key_str, (int32_t)parsed);
break;
case NVS_TYPE_U64:
res = nvs_set_i64(handle, key_str, parsed);
break;
default:
__builtin_unreachable();
}
}
else
{
switch (key->type) {
case NVS_TYPE_U8:
res = ((config_i8_setter)key->setter)((int8_t)parsed);
break;
case NVS_TYPE_U16:
res = ((config_i16_setter)key->setter)((int16_t)parsed);
break;
case NVS_TYPE_U32:
res = ((config_i32_setter)key->setter)((int32_t)parsed);
break;
case NVS_TYPE_U64:
res = ((config_i64_setter)key->setter)(parsed);
break;
default:
__builtin_unreachable();
}
}
}
break;
case NVS_TYPE_STR:
if (strlen(value_str) >= key->buffer_size)
{
ESP_LOGE(TAG, "Value too long");
return 1;
}
if (key->setter == NULL || config_set_args.raw->count)
res = nvs_set_str(handle, key_str, value_str);
else
res = ((config_str_setter)key->setter)(value_str);
break;
case NVS_TYPE_BLOB:
{
unsigned char *buf = malloc(key->buffer_size);
size_t olen;
int result = mbedtls_base64_decode(buf, key->buffer_size, &olen, (const uint8_t *)value_str, strlen(value_str));
if (result != 0)
{
ESP_LOGE(TAG, "mbedtls_base64_decode failed: %#x", result);
free(buf);
return 1;
}
if (olen > key->buffer_size)
{
ESP_LOGE(TAG, "Value too long");
free(buf);
return 1;
}
if (key->setter == NULL || config_set_args.raw->count)
res = nvs_set_blob(handle, key_str, buf, olen);
else
res = ((config_blob_setter)key->setter)(buf, olen);
free(buf);
}
break;
default:
__builtin_unreachable();
// avoid compiler warning
res = ESP_ERR_INVALID_STATE;
}
if (res != ESP_OK)
{
ESP_LOGE(TAG, "set failed: %s", esp_err_to_name(res));
return 1;
}
return 0;
}
void register_config_set(void)
{
config_set_args.raw = arg_lit0("R", NULL, "skip setter functions, directly write NVS");
config_set_args.key = arg_str1(NULL, NULL, "key", "the key");
config_set_args.value = arg_str1(NULL, NULL, "value", "the value to set");
config_set_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "config_set",
.help = "set a config key",
.hint = NULL,
.func = &cmd_config_set,
.argtable = &config_set_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
static struct {
arg_lit_t *raw;
arg_str_t *key;
arg_end_t *end;
} config_get_args;
static int cmd_config_get(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&config_get_args);
if (nerrors != 0) {
arg_print_errors(stderr, config_get_args.end, argv[0]);
return 1;
}
const char *key_str = config_get_args.key->sval[0];
const config_key_t *key = config_key_find(key_str);
if (key == NULL)
{
ESP_LOGE(TAG, "No such config key: %s", key_str);
return 1;
}
esp_err_t res;
switch (key->type)
{
case NVS_TYPE_U8:
case NVS_TYPE_U16:
case NVS_TYPE_U32:
case NVS_TYPE_U64:
{
uint64_t value = 0;
if (key->getter == NULL || config_get_args.raw->count)
{
switch (key->type) {
case NVS_TYPE_U8:
res = nvs_get_u8(handle, key_str, (uint8_t *)&value);
break;
case NVS_TYPE_U16:
res = nvs_get_u16(handle, key_str, (uint16_t *)&value);
break;
case NVS_TYPE_U32:
res = nvs_get_u32(handle, key_str, (uint32_t *)&value);
break;
case NVS_TYPE_U64:
res = nvs_get_u64(handle, key_str, &value);
break;
default:
__builtin_unreachable();
}
}
else
{
switch (key->type) {
case NVS_TYPE_U8:
res = ((config_u8_getter)key->getter)((uint8_t *)&value);
break;
case NVS_TYPE_U16:
res = ((config_u16_getter)key->getter)((uint16_t *)&value);
break;
case NVS_TYPE_U32:
res = ((config_u32_getter)key->getter)((uint32_t *)&value);
break;
case NVS_TYPE_U64:
res = ((config_u64_getter)key->getter)(&value);
break;
default:
__builtin_unreachable();
}
}
if (res == ESP_OK)
printf("%"PRIu64 "\n", value);
}
break;
case NVS_TYPE_I8:
case NVS_TYPE_I16:
case NVS_TYPE_I32:
case NVS_TYPE_I64:
{
int64_t value = 0;
if (key->getter == NULL || config_get_args.raw->count)
{
switch (key->type) {
case NVS_TYPE_I8:
res = nvs_get_i8(handle, key_str, (int8_t *)&value);
value = (int64_t)(int8_t)value;
break;
case NVS_TYPE_I16:
res = nvs_get_i16(handle, key_str, (int16_t *)&value);
value = (int64_t)(int16_t)value;
break;
case NVS_TYPE_I32:
res = nvs_get_i32(handle, key_str, (int32_t *)&value);
value = (int64_t)(int32_t)value;
break;
case NVS_TYPE_I64:
res = nvs_get_i64(handle, key_str, &value);
break;
default:
__builtin_unreachable();
}
}
else
{
switch (key->type) {
case NVS_TYPE_I8:
res = ((config_i8_getter)key->getter)((int8_t *)&value);
value = (int64_t)(int8_t)value;
break;
case NVS_TYPE_I16:
res = ((config_i16_getter)key->getter)((int16_t *)&value);
value = (int64_t)(int16_t)value;
break;
case NVS_TYPE_I32:
res = ((config_i32_getter)key->getter)((int32_t *)&value);
value = (int64_t)(int32_t)value;
break;
case NVS_TYPE_I64:
res = ((config_i64_getter)key->getter)(&value);
break;
default:
__builtin_unreachable();
}
}
if (res == ESP_OK)
printf("%"PRIi64 "\n", value);
}
break;
case NVS_TYPE_STR:
char *value = malloc(key->buffer_size);
size_t size = key->buffer_size;
if (key->getter == NULL || config_get_args.raw->count)
res = nvs_get_str(handle, key_str, value, &size);
else
res = ((config_str_getter)key->getter)(value, &size);
if (res == ESP_OK)
printf("%s\n", value);
free(value);
break;
case NVS_TYPE_BLOB:
{
unsigned char *buf = malloc(key->buffer_size);
size_t actual_size;
if (key->getter == NULL || config_get_args.raw->count)
res = nvs_get_blob(handle, key_str, buf, &actual_size);
else
res = ((config_blob_getter)key->getter)(buf, &actual_size);
if (res != ESP_OK)
{
free(buf);
break;
}
const size_t base64_buffer_size = (((4 * key->buffer_size / 3) + 3) & ~3) + 1;
unsigned char *base64_buf = malloc(base64_buffer_size);
size_t olen;
int result = mbedtls_base64_encode(base64_buf, base64_buffer_size, &olen, (const uint8_t *)buf, actual_size);
if (result != 0)
{
ESP_LOGE(TAG, "mbedtls_base64_encode failed: %#x", result);
free(buf);
free(base64_buf);
return 1;
}
printf("%s\n", base64_buf);
free(buf);
free(base64_buf);
}
break;
default:
__builtin_unreachable();
// avoid compiler warning
res = ESP_ERR_INVALID_STATE;
}
if (res != ESP_OK)
{
ESP_LOGE(TAG, "get failed: %s", esp_err_to_name(res));
return 1;
}
return 0;
}
void register_config_get(void)
{
config_get_args.raw = arg_lit0("R", NULL, "skip getter functions, directly read NVS");
config_get_args.key = arg_str1(NULL, NULL, "key", "the key");
config_get_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "config_get",
.help = "get a config key",
.hint = NULL,
.func = &cmd_config_get,
.argtable = &config_get_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void config_register_commands(void)
{
register_config_set();
register_config_get();
}