* new DynamicConfigSerializer

* board/variant KeyValueStore now can write to 'custom' object in Json prefs
This commit is contained in:
Scott Powell
2026-08-24 17:12:56 +10:00
parent a1cf5bd806
commit 5a162ff4c6
11 changed files with 139 additions and 20 deletions
+1 -1
View File
@@ -990,7 +990,7 @@ void MyMesh::begin(bool has_display) {
radio_driver.setTxPower(_prefs.tx_power_dbm);
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
board.attachDynamicPrefs(_prefs.getRadioPrefs());
board.attachDynamicPrefs(_prefs.getCustom());
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
+7 -1
View File
@@ -2,6 +2,7 @@
#include <cstdint> // For uint8_t, uint32_t
#include <helpers/ConfigSerializer.h>
#include <helpers/CommonRadioPrefs.h>
#include <helpers/DynamicConfigSerializer.h>
#define TELEM_MODE_DENY 0
#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags
@@ -154,6 +155,8 @@ private:
};
CompanionPrefs companion;
DynamicConfigSerializer custom;
protected:
void structure() override {
def("name", node_name, sizeof(node_name));
@@ -165,9 +168,10 @@ protected:
def("gps", gps);
def("repeat", repeat);
def("comp", companion);
def("custom", custom);
}
public:
NodePrefs() : radio(this), gps(this), companion(this) {
NodePrefs() : radio(this), gps(this), companion(this), custom(&radio) {
node_name[0] = 0;
default_scope_name[0] = 0;
memset(default_scope_key, 0, sizeof(default_scope_key));
@@ -177,6 +181,8 @@ public:
void setRepeatEn(bool en) { repeat.disable_fwd = en ? 0 : 1; }
CommonRadioPrefs* getRadioPrefs() { return &radio; }
KeyValueStore* getCustom() { return &custom; }
bool isDirty() const override { return ConfigSerializer::isDirty() || radio.isDirty(); }
void clearDirty() override { ConfigSerializer::clearDirty(); radio.clearDirty(); }
};
+1 -1
View File
@@ -983,7 +983,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
board.attachDynamicPrefs(_prefs.getRadioPrefs());
board.attachDynamicPrefs(_prefs.getCustom());
updateAdvertTimer();
updateFloodAdvertTimer();
+1 -1
View File
@@ -726,7 +726,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
radio_driver.setTxPower(_prefs.tx_power_dbm);
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
board.attachDynamicPrefs(_prefs.getRadioPrefs());
board.attachDynamicPrefs(_prefs.getCustom());
updateAdvertTimer();
updateFloodAdvertTimer();
+1 -1
View File
@@ -772,7 +772,7 @@ void SensorMesh::begin(FILESYSTEM* fs) {
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_driver.setTxPower(_prefs.tx_power_dbm);
board.attachDynamicPrefs(_prefs.getRadioPrefs());
board.attachDynamicPrefs(_prefs.getCustom());
updateAdvertTimer();
updateFloodAdvertTimer();
+7 -1
View File
@@ -7,6 +7,7 @@
#include <helpers/RegionMap.h>
#include <helpers/ConfigSerializer.h>
#include <helpers/CommonRadioPrefs.h>
#include <helpers/DynamicConfigSerializer.h>
#if defined(WITH_RS232_BRIDGE) || defined(WITH_ESPNOW_BRIDGE)
#define WITH_BRIDGE
@@ -202,6 +203,8 @@ private:
};
RoomPrefs room;
DynamicConfigSerializer custom;
protected:
void structure() override {
def("name", node_name, sizeof(node_name));
@@ -218,10 +221,11 @@ protected:
def("repeat", repeat);
def("room", room);
def("power", power);
def("custom", custom);
}
public:
NodePrefs() : ConfigSerializer(), bridge(this), gps(this), radio(this), power(this), repeat(this), room(this) {
NodePrefs() : ConfigSerializer(), bridge(this), gps(this), radio(this), power(this), repeat(this), room(this), custom(&radio) {
node_name[0] = 0;
password[0] = 0;
guest_password[0] = 0;
@@ -230,6 +234,8 @@ public:
}
CommonRadioPrefs* getRadioPrefs() { return &radio; }
KeyValueStore* getCustom() { return &custom; }
bool isDirty() const override { return ConfigSerializer::isDirty() || radio.isDirty(); }
void clearDirty() override { ConfigSerializer::clearDirty(); radio.clearDirty(); }
};
+13 -9
View File
@@ -3,25 +3,29 @@
#include "Utils.h"
#include "target.h"
void CommonRadioPrefs::getByKey(const char* key, char* value, size_t max_len) {
bool CommonRadioPrefs::getByKey(const char* key, char* value, size_t max_len) {
if (strcmp(key, "fem_rxgain") == 0) {
snprintf(value, max_len, "%d", (uint32_t)getFEMRxGain());
} else if (strcmp(key, "fem_txgain") == 0) {
snprintf(value, max_len, "%d", (uint32_t)getFEMTxGain());
} else {
MESH_DEBUG_PRINTLN("Error: getBykey() unknown key: %s", key);
return true;
}
if (strcmp(key, "fem_txgain") == 0) {
snprintf(value, max_len, "%d", (uint32_t)getFEMTxGain());
return true;
}
return false;
}
void CommonRadioPrefs::setByKey(const char* key, const char* value) {
bool CommonRadioPrefs::setByKey(const char* key, const char* value) {
if (strcmp(key, "fem_rxgain") == 0) {
setFEMRxGain(atoi(value));
markDirty();
} else if (strcmp(key, "fem_txgain") == 0) {
return true;
}
if (strcmp(key, "fem_txgain") == 0) {
setFEMTxGain(atoi(value));
markDirty();
} else {
MESH_DEBUG_PRINTLN("Error: setBykey() unknown key: %s", key);
return true;
}
return false;
}
bool CommonRadioPrefs::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
+3 -3
View File
@@ -2,7 +2,7 @@
#include "ConfigSerializer.h"
#include "KeyValueStore.h"
class CommonRadioPrefs : public ConfigSerializer, public KeyValueStore{
class CommonRadioPrefs : public ConfigSerializer, public KeyValueStore {
bool _is_dirty = false;
protected:
CommonRadioPrefs() { }
@@ -64,6 +64,6 @@ public:
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply);
void setByKey(const char* key, const char* value) override; // for dynamic key/value access
void getByKey(const char* key, char* value, size_t max_len) override;
bool setByKey(const char* key, const char* value) override; // for dynamic key/value access
bool getByKey(const char* key, char* value, size_t max_len) override;
};
+83
View File
@@ -0,0 +1,83 @@
#include "DynamicConfigSerializer.h"
#include <Utils.h>
#define PROP_SEP_CHAR '|'
#define PROP_SEP_STR "|"
#define KEY_SEP_CHAR ':'
#define KEY_SEP_STR ":"
bool DynamicConfigSerializer::setByKey(const char* key, const char* value) {
if (_fallback && _fallback->setByKey(key, value)) return true;
// TODO: guard for bad chars (':' or '|')
char tmp[MAX_DYNAMIC_CONFG];
strcpy(tmp, _config); // make a (modifiable) copy
const char* parts[8];
int n = mesh::Utils::parseTextParts(tmp, parts, 8, PROP_SEP_CHAR);
// add/replace in _config[]
char new_config[MAX_DYNAMIC_CONFG];
new_config[0] = 0;
int keylen = strlen(key);
for (int i = 0; i < n; i++) {
const char* item = parts[i];
if (item[keylen] == KEY_SEP_CHAR && memcmp(item, key, keylen) == 0) {
// key exists, so omit old value from this pass (will append new value at end)
} else {
if (new_config[0]) {
strcat(new_config, PROP_SEP_STR);
}
strcat(new_config, item);
}
}
// now append new key/value (if it fits)
if (strlen(new_config) + strlen(key) + strlen(value) + 2 < sizeof(_config)-1) {
strcat(new_config, key);
strcat(new_config, KEY_SEP_STR);
strcat(new_config, value);
strcpy(_config, new_config); // commit new serialized string
return true;
}
return false; // didn't fit in _config[]
}
bool DynamicConfigSerializer::getByKey(const char* key, char* value, size_t max_len) {
if (_fallback && _fallback->getByKey(key, value, max_len)) return true;
char tmp[MAX_DYNAMIC_CONFG];
strcpy(tmp, _config); // make a (modifiable) copy
const char* parts[8];
int n = mesh::Utils::parseTextParts(tmp, parts, 8, PROP_SEP_CHAR);
int keylen = strlen(key);
for (int i = 0; i < n; i++) {
const char* item = parts[i];
if (item[keylen] == KEY_SEP_CHAR && memcmp(item, key, keylen) == 0) {
strncpy(value, &item[keylen+1], max_len);
value[max_len] = 0;
return true;
}
}
return false;
}
void DynamicConfigSerializer::structure() {
char tmp[MAX_DYNAMIC_CONFG];
strcpy(tmp, _config); // make a (modifiable) copy
const char* parts[8];
int n = mesh::Utils::parseTextParts(tmp, parts, 8, PROP_SEP_CHAR);
// dynamically call def()'s
for (int i = 0; i < n; i++) {
char* item = (char *) parts[i];
char* eq = strchr(item, KEY_SEP_CHAR);
if (eq) {
*eq = 0; // replace separator with null terminator
def(item, eq + 1, MAX_DYNAMIC_CONFG/2); // maximum HALF of total for individual property value
}
}
}
+20
View File
@@ -0,0 +1,20 @@
#include "ConfigSerializer.h"
#include "KeyValueStore.h"
#ifndef MAX_DYNAMIC_CONFG
#define MAX_DYNAMIC_CONFG 128
#endif
class DynamicConfigSerializer : public ConfigSerializer, public KeyValueStore {
char _config[MAX_DYNAMIC_CONFG];
KeyValueStore* _fallback;
protected:
void structure() override;
public:
DynamicConfigSerializer(KeyValueStore* fallback = NULL) : _fallback(fallback) { _config[0] = 0; }
bool setByKey(const char* key, const char* value) override;
bool getByKey(const char* key, char* value, size_t max_len) override;
};
+2 -2
View File
@@ -6,6 +6,6 @@ class KeyValueStore {
protected:
KeyValueStore() { }
public:
virtual void setByKey(const char* key, const char* value) { }
virtual void getByKey(const char* key, char* value, size_t max_len) { }
virtual bool setByKey(const char* key, const char* value) { return false; }
virtual bool getByKey(const char* key, char* value, size_t max_len) { return false; }
};