Add persistent storage for Mesh config.

This commit is contained in:
h2zero 2022-05-09 19:32:57 -06:00
parent 507f146477
commit 4786960902
9 changed files with 728 additions and 21 deletions

View file

@ -12,6 +12,10 @@ elseif("nimble" IN_LIST BUILD_COMPONENTS OR "__nimble" IN_LIST __hack_component_
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
nimble
)
elseif("bt" IN_LIST BUILD_COMPONENTS OR "__bt" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
bt
)
endif()
if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf_arduino")
@ -20,15 +24,7 @@ if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf
)
endif()
idf_component_register(
REQUIRED_IDF_TARGETS
"esp32"
"esp32s3"
"esp32c3"
INCLUDE_DIRS
"src"
SRCS
"src/NimBLE2904.cpp"
set(srcs "src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp"
@ -41,10 +37,6 @@ idf_component_register(
"src/NimBLEEddystoneURL.cpp"
"src/NimBLEExtAdvertising.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLEMeshCreateModel.c"
"src/NimBLEMeshElement.cpp"
"src/NimBLEMeshModel.cpp"
"src/NimBLEMeshNode.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
@ -53,10 +45,36 @@ idf_component_register(
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
"src/NimBLEUUID.cpp")
if(CONFIG_BT_NIMBLE_MESH)
list(APPEND srcs "src/NimBLEMeshCreateModel.c"
"src/NimBLEMeshElement.cpp"
"src/NimBLEMeshModel.cpp"
"src/NimBLEMeshNode.cpp")
endif()
if(CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS)
list(APPEND srcs "src/mesh_config_store/config/config_store.c")
endif()
idf_component_register(
REQUIRED_IDF_TARGETS
"esp32"
"esp32s3"
"esp32c3"
INCLUDE_DIRS
"src"
SRCS "${srcs}"
REQUIRES
bt
nvs_flash
PRIV_REQUIRES
${ESP_NIMBLE_PRIV_REQUIRES}
)
if(CONFIG_BT_NIMBLE_MESH AND CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS)
idf_build_set_property(COMPILE_OPTIONS "-DMYNEWT_VAL_BLE_MESH_SETTINGS=1" APPEND)
idf_build_set_property(COMPILE_OPTIONS "-I${COMPONENT_DIR}/src/mesh_config_store" APPEND)
endif()

14
Kconfig
View file

@ -33,7 +33,7 @@ config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
Enabling this option will display return code values as text
messages in the debug log. This will use approximately 8kB
of flash memory.
config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
bool "Show NimBLE gap events as text in debug log."
default "n"
@ -47,7 +47,7 @@ config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
default "n"
help
Enabling this option will display advertisment types recieved
while scanning as text messages in the debug log.
while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
@ -68,5 +68,13 @@ config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
config NIMBLE_CPP_PERSIST_MESH_SETTINGS
bool "Enable persistent storage of mesh config settings."
default "n"
depends on BT_NIMBLE_MESH
help
Enabling this option will store the provisioning and app key settings
in non-volatile storage when using NimBLE Mesh.
endmenu

View file

@ -6,6 +6,9 @@
*
*/
#include "nimconfig.h"
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshCreateModel.h"
static struct bt_mesh_model_cb mod_cb = {
@ -28,3 +31,5 @@ struct bt_mesh_model createGenModel(int16_t _id, struct bt_mesh_model_op* op,
struct bt_mesh_model mod = BT_MESH_MODEL_CB(_id, op, pub, udata, &mod_cb);
return mod;
}
#endif // CONFIG_BT_NIMBLE_MESH

View file

@ -6,6 +6,9 @@
*
*/
#include "nimconfig.h"
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshElement.h"
#include "NimBLELog.h"
#include "NimBLEMeshCreateModel.h"
@ -139,4 +142,4 @@ bt_mesh_elem* NimBLEMeshElement::start() {
return m_pElem;
}
#endif // CONFIG_BT_NIMBLE_MESH

View file

@ -6,6 +6,9 @@
*
*/
#include "nimconfig.h"
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshModel.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
@ -693,3 +696,5 @@ void NimBLEHealthSrvCallbacks::attentionOff(bt_mesh_model *model)
NimBLEMeshModel* pModel = NimBLEDevice::getMeshNode()->getHealthModel(model);
pModel->m_callbacks->attentionOff(pModel);
}
#endif // CONFIG_BT_NIMBLE_MESH

View file

@ -7,7 +7,7 @@
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshNode.h"
#include "NimBLELog.h"
@ -206,5 +206,4 @@ bool NimBLEMeshNode::start() {
return true;
}
#endif // CONFIG_BT_ENABLED
#endif // CONFIG_BT_NIMBLE_MESH

View file

@ -0,0 +1,295 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef __UTIL_BASE64_H
#define __UTIL_BASE64_H
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
struct base64_decoder {
/*** public */
const char *src;
void *dst;
int src_len; /* <=0 if src ends with '\0' */
int dst_len; /* <=0 if dst unbounded */
/*** private */
char buf[4];
int buf_len;
};
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int
pos(char c)
{
const char *p;
for (p = base64_chars; *p; p++)
if (*p == c)
return p - base64_chars;
return -1;
}
int
base64_encode(const void *data, int size, char *s, uint8_t should_pad)
{
char *p;
int i;
int c;
const unsigned char *q;
char *last;
int diff;
p = s;
q = (const unsigned char *) data;
last = NULL;
i = 0;
while (i < size) {
c = q[i++];
c *= 256;
if (i < size)
c += q[i];
i++;
c *= 256;
if (i < size)
c += q[i];
i++;
p[0] = base64_chars[(c & 0x00fc0000) >> 18];
p[1] = base64_chars[(c & 0x0003f000) >> 12];
p[2] = base64_chars[(c & 0x00000fc0) >> 6];
p[3] = base64_chars[(c & 0x0000003f) >> 0];
last = p;
p += 4;
}
if (last) {
diff = i - size;
if (diff > 0) {
if (should_pad) {
memset(last + (4 - diff), '=', diff);
} else {
p = last + (4 - diff);
}
}
}
*p = 0;
return (p - s);
}
int
base64_pad(char *buf, int len)
{
int remainder;
remainder = len % 4;
if (remainder == 0) {
return (0);
}
memset(buf, '=', 4 - remainder);
return (4 - remainder);
}
#define DECODE_ERROR -1
static unsigned int
token_decode(const char *token, int len)
{
int i;
unsigned int val = 0;
int marker = 0;
if (len < 4) {
return DECODE_ERROR;
}
for (i = 0; i < 4; i++) {
val *= 64;
if (token[i] == '=') {
marker++;
} else if (marker > 0) {
return DECODE_ERROR;
} else {
val += pos(token[i]);
}
}
if (marker > 2) {
return DECODE_ERROR;
}
return (marker << 24) | val;
}
int
base64_decoder_go(struct base64_decoder *dec)
{
unsigned int marker;
unsigned int val;
uint8_t *dst;
char sval;
int read_len;
int src_len;
int src_rem;
int src_off;
int dst_len;
int dst_off;
int i;
dst = dec->dst;
dst_off = 0;
src_off = 0;
/* A length <= 0 means "unbounded". */
if (dec->src_len <= 0) {
src_len = INT_MAX;
} else {
src_len = dec->src_len;
}
if (dec->dst_len <= 0) {
dst_len = INT_MAX;
} else {
dst_len = dec->dst_len;
}
while (1) {
src_rem = src_len - src_off;
if (src_rem == 0) {
/* End of source input. */
break;
}
if (dec->src[src_off] == '\0') {
/* End of source string. */
break;
}
/* Account for possibility of partial token from previous call. */
read_len = 4 - dec->buf_len;
/* Detect invalid input. */
for (i = 0; i < read_len; i++) {
sval = dec->src[src_off + i];
if (sval == '\0') {
/* Incomplete input. */
return -1;
}
if (sval != '=' && strchr(base64_chars, sval) == NULL) {
/* Invalid base64 character. */
return -1;
}
}
if (src_rem < read_len) {
/* Input contains a partial token. Stash it for use during the
* next call.
*/
memcpy(&dec->buf[dec->buf_len], &dec->src[src_off], src_rem);
dec->buf_len += src_rem;
break;
}
/* Copy full token into buf and decode it. */
memcpy(&dec->buf[dec->buf_len], &dec->src[src_off], read_len);
val = token_decode(dec->buf, read_len);
if (val == DECODE_ERROR) {
return -1;
}
src_off += read_len;
dec->buf_len = 0;
marker = (val >> 24) & 0xff;
if (dst_off >= dst_len) {
break;
}
dst[dst_off] = (val >> 16) & 0xff;
dst_off++;
if (marker < 2) {
if (dst_off >= dst_len) {
break;
}
dst[dst_off] = (val >> 8) & 0xff;
dst_off++;
}
if (marker < 1) {
if (dst_off >= dst_len) {
break;
}
dst[dst_off] = val & 0xff;
dst_off++;
}
}
return dst_off;
}
int
base64_decode(const char *str, void *data)
{
struct base64_decoder dec = {
.src = str,
.dst = data,
};
return base64_decoder_go(&dec);
}
int
base64_decode_maxlen(const char *str, void *data, int len)
{
struct base64_decoder dec = {
.src = str,
.dst = data,
.dst_len = len,
};
return base64_decoder_go(&dec);
}
int
base64_decode_len(const char *str)
{
int len;
len = strlen(str);
while (len && str[len - 1] == '=') {
len--;
}
return len * 3 / 4;
}
#define BASE64_ENCODE_SIZE(__size) (((((__size) - 1) / 3) * 4) + 4)
#ifdef __cplusplus
}
#endif
#endif /* __UTIL_BASE64_H__ */

View file

@ -0,0 +1,238 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef __SYS_CONFIG_H_
#define __SYS_CONFIG_H_
#include "../../nimconfig.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include <os/queue.h>
#else
# include "nimble/porting/nimble/include/os/queue.h"
#endif
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CONF_MAX_DIR_DEPTH 8 /* max depth of config tree */
#define CONF_MAX_NAME_LEN (8 * CONF_MAX_DIR_DEPTH)
/**
* Type of configuration value.
*/
typedef enum conf_type {
CONF_NONE = 0,
CONF_DIR,
/** 8-bit signed integer */
CONF_INT8,
/** 16-bit signed integer */
CONF_INT16,
/** 32-bit signed integer */
CONF_INT32,
/** 64-bit signed integer */
CONF_INT64,
/** String */
CONF_STRING,
/** Bytes */
CONF_BYTES,
/** Floating point */
CONF_FLOAT,
/** Double precision */
CONF_DOUBLE,
/** Boolean */
CONF_BOOL,
/** 8-bit unsigned integer */
CONF_UINT8,
/** 16-bit unsigned integer */
CONF_UINT16,
/** 32-bit unsigned integer */
CONF_UINT32,
/** 64-bit unsigned integer */
CONF_UINT64,
} __attribute__((__packed__)) conf_type_t;
/**
* Parameter to commit handler describing where data is going to.
*/
enum conf_export_tgt {
/** Value is to be persisted */
CONF_EXPORT_PERSIST,
/** Value is to be display */
CONF_EXPORT_SHOW
};
typedef enum conf_export_tgt conf_export_tgt_t;
/**
* Handler for getting configuration items, this handler is called
* per-configuration section. Configuration sections are delimited
* by '/', for example:
*
* - section/name/value
*
* Would be passed as:
*
* - argc = 3
* - argv[0] = section
* - argv[1] = name
* - argv[2] = value
*
* The handler returns the value into val, null terminated, up to
* val_len_max.
*
* @param argc The number of sections in the configuration variable
* @param argv The array of configuration sections
* @param val A pointer to the buffer to return the configuration
* value into.
* @param val_len_max The maximum length of the val buffer to copy into.
*
* @return A pointer to val or NULL if error.
*/
typedef char *(*conf_get_handler_t)(int argc, char **argv, char *val, int val_len_max);
typedef char *(*conf_get_handler_ext_t)(int argc, char **argv, char *val, int val_len_max, void *arg);
/**
* Set the configuration variable pointed to by argc and argv. See
* description of ch_get_handler_t for format of these variables. This sets the
* configuration variable to the shadow value, but does not apply the configuration
* change. In order to apply the change, call the ch_commit() handler.
*
* @param argc The number of sections in the configuration variable.
* @param argv The array of configuration sections
* @param val The value to configure that variable to
*
* @return 0 on success, non-zero error code on failure.
*/
typedef int (*conf_set_handler_t)(int argc, char **argv, char *val);
typedef int (*conf_set_handler_ext_t)(int argc, char **argv, char *val, void *arg);
/**
* Commit shadow configuration state to the active configuration.
*
* @return 0 on success, non-zero error code on failure.
*/
typedef int (*conf_commit_handler_t)(void);
typedef int (*conf_commit_handler_ext_t)(void *arg);
/**
* Called per-configuration variable being exported.
*
* @param name The name of the variable to export
* @param val The value of the variable to export
*/
typedef void (*conf_export_func_t)(char *name, char *val);
/**
* Export all of the configuration variables, calling the export_func
* per variable being exported.
*
* @param export_func The export function to call.
* @param tgt The target of the export, either for persistence or display.
*
* @return 0 on success, non-zero error code on failure.
*/
typedef int (*conf_export_handler_t)(conf_export_func_t export_func,
conf_export_tgt_t tgt);
typedef int (*conf_export_handler_ext_t)(conf_export_func_t export_func,
conf_export_tgt_t tgt, void *arg);
/**
* Configuration handler, used to register a config item/subtree.
*/
struct conf_handler {
SLIST_ENTRY(conf_handler) ch_list;
/**
* The name of the conifguration item/subtree
*/
char *ch_name;
/**
* Whether to use the extended callbacks.
* false: standard
* true: extended
*/
bool ch_ext;
/** Get configuration value */
union {
conf_get_handler_t ch_get;
conf_get_handler_ext_t ch_get_ext;
};
/** Set configuration value */
union {
conf_set_handler_t ch_set;
conf_set_handler_ext_t ch_set_ext;
};
/** Commit configuration value */
union {
conf_commit_handler_t ch_commit;
conf_commit_handler_ext_t ch_commit_ext;
};
/** Export configuration value */
union {
conf_export_handler_t ch_export;
conf_export_handler_ext_t ch_export_ext;
};
/** Custom argument that gets passed to the extended callbacks */
void *ch_arg;
};
/**
* Register a handler for configurations items.
*
* @param cf Structure containing registration info.
*
* @return 0 on success, non-zero on failure.
*/
int conf_register(struct conf_handler *cf);
/**
* Load configuration from registered persistence sources. Handlers for
* configuration subtrees registered earlier will be called for encountered
* values.
*
* @return 0 on success, non-zero on failure.
*/
int conf_load(void);
/**
* Write a single configuration value to persisted storage (if it has
* changed value).
*
* @param name Name/key of the configuration item.
* @param var Value of the configuration item.
*
* @return 0 on success, non-zero on failure.
*/
int conf_save_one(const char *name, char *var);
#ifdef __cplusplus
}
#endif
#define SYSINIT_PANIC_ASSERT_MSG(rc, msg) assert(rc)
#endif /* __SYS_CONFIG_H_ */

View file

@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "nimconfig.h"
#ifdef ESP_PLATFORM
#if CONFIG_BT_NIMBLE_MESH && CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS
#include "config.h"
#include "nvs.h"
#include <string.h>
static struct conf_handler* config_handler;
int conf_parse_name(char *name, int *name_argc, char *name_argv[])
{
char *tok;
char *tok_ptr;
const char *sep = "/";
int i;
tok = strtok_r(name, sep, &tok_ptr);
i = 0;
while (tok) {
name_argv[i++] = tok;
tok = strtok_r(NULL, sep, &tok_ptr);
}
*name_argc = i;
return 0;
}
int conf_load(void)
{
esp_err_t err;
nvs_handle handle;
err = nvs_open(config_handler->ch_name, NVS_READONLY, &handle);
if (err != ESP_OK) return err;
nvs_iterator_t it = nvs_entry_find("nvs", config_handler->ch_name, NVS_TYPE_ANY);
while (it != NULL) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
it = nvs_entry_next(it);
size_t required_size = 0;
err = nvs_get_str(handle, info.key, NULL, &required_size);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
char* val = malloc(required_size);
if (required_size > 0) {
err = nvs_get_str(handle, info.key, val, &required_size);
if (err != ESP_OK) {
free(val);
return err;
}
}
int name_argc;
char *name_argv[8];
conf_parse_name(info.key, &name_argc, name_argv);
config_handler->ch_set(name_argc, &name_argv[0], val);
free(val);
}
nvs_close(handle);
config_handler->ch_commit();
return ESP_OK;
}
int conf_save_one(const char *name, char *var)
{
esp_err_t err;
nvs_handle_t handle;
int name_argc;
char *name_argv[CONF_MAX_DIR_DEPTH];
char n[CONF_MAX_NAME_LEN];
strcpy(n, name);
conf_parse_name(n, &name_argc, name_argv);
err = nvs_open(name_argv[0], NVS_READWRITE, &handle);
if (err != ESP_OK) return err;
const char* key = name_argv[1];
if (name_argc > 2) {
key = name;
while (*key != '/') {
key++;
}
key++;
}
if (var) {
err = nvs_set_str(handle, key, var);
if (err != ESP_OK) return err;
} else {
err = nvs_erase_key(handle, key);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
}
err = nvs_commit(handle);
if (err != ESP_OK) return err;
nvs_close(handle);
return ESP_OK;
}
int conf_register(struct conf_handler *cf)
{
config_handler = cf;
return 0;
}
#endif // CONFIG_BT_NIMBLE_MESH && MYNEWT_VAL_BLE_MESH_SETTINGS
#endif // ESP_PLATFORM