* adding new CommonRadioPrefs

* refactor: moving various radio CLI handling to CommonRadioPrefs
This commit is contained in:
Scott Powell
2026-08-23 14:45:15 +10:00
parent afb969ccca
commit 41588d8052
9 changed files with 180 additions and 44 deletions
+26
View File
@@ -2062,12 +2062,38 @@ bool MyMesh::handleCommand(const char* command, uint32_t sender_timestamp, char*
command += 3;
}
if (_prefs.getRadioPrefs()->handleCommand(command, sender_timestamp, reply)) { // is radio CLI command?
if (_prefs.getRadioPrefs()->isDirty()) { savePrefs(); }
return true;
}
if (memcmp(command, "set name ", 9) == 0) {
if (AdvertDataParser::isValidName(&command[9])) {
StrHelper::strncpy(_prefs.node_name, &command[9], sizeof(_prefs.node_name));
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, bad chars");
}
return true;
}
if (strcmp(command, "get name") == 0) {
sprintf(reply, "> %s", _prefs.node_name);
return true;
}
if (memcmp(command, "set pin ", 8) == 0) {
_prefs.ble_pin = atoi(&command[8]);
savePrefs();
sprintf(reply, "> pin is now %06d", _prefs.ble_pin);
return true;
}
if (strcmp(command, "ver") == 0) {
sprintf(reply, "%s (Build: %s)", FIRMWARE_VERSION, FIRMWARE_BUILD_DATE);
return true;
}
return false; // not handled
}
+1
View File
@@ -169,6 +169,7 @@ public:
_prefs.node_lat = sensors.node_lat;
_prefs.node_lon = sensors.node_lon;
_store->savePrefs(_prefs);
_prefs.clearDirty();
}
#if ENV_INCLUDE_GPS == 1
+17 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include <cstdint> // For uint8_t, uint32_t
#include <helpers/ConfigSerializer.h>
#include <helpers/CommonRadioPrefs.h>
#define TELEM_MODE_DENY 0
#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags
@@ -42,7 +43,7 @@ public:
uint8_t default_scope_key[16];
private:
class RadioPrefs : public ConfigSerializer { // COPIED from CommonCLI (for now)
class RadioPrefs : public CommonRadioPrefs {
NodePrefs* _parent;
protected:
void structure() override {
@@ -70,6 +71,18 @@ private:
}
public:
RadioPrefs(NodePrefs* parent) : _parent(parent) { }
// CommonRadioPrefs interface
float getFreq() const override { return _parent->freq; }
void setFreq(float f) override { _parent->freq = f; markDirty(); }
float getBandwidth() const override { return _parent->bw; }
void setBandwidth(float bw) override { _parent->bw = bw; markDirty(); }
uint8_t getSpreadFactor() const override { return _parent->sf; }
void setSpreadFactor(uint8_t sf) { _parent->sf; markDirty(); }
uint8_t getCodingRate() const override { return _parent->cr; }
void setCodingRate(uint8_t cr) { _parent->cr = cr; markDirty(); }
float getAirtimeFactor() const override { return _parent->airtime_factor; }
void setAirtimeFactor(float af) override { _parent->airtime_factor = af; markDirty(); }
};
RadioPrefs radio;
@@ -142,4 +155,7 @@ public:
// new accessor methods
bool isRepeatEn() const { return repeat.disable_fwd == 0; }
void setRepeatEn(bool en) { repeat.disable_fwd = en ? 0 : 1; }
CommonRadioPrefs* getRadioPrefs() { return &radio; }
void clearDirty() { radio.clearDirty(); }
};
+8
View File
@@ -28,6 +28,14 @@
return i;
}
bool AdvertDataParser::isValidName(const char *n) {
while (*n) {
if (*n == '[' || *n == ']' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false;
n++;
}
return true;
}
AdvertDataParser::AdvertDataParser(const uint8_t app_data[], uint8_t app_data_len) {
_name[0] = 0;
_lat = _lon = 0;
+2
View File
@@ -50,6 +50,8 @@ class AdvertDataParser {
public:
AdvertDataParser(const uint8_t app_data[], uint8_t app_data_len);
static bool isValidName(const char* name);
bool isValid() const { return _valid; }
uint8_t getType() const { return _flags & 0x0F; }
uint16_t getFeat1() const { return _extra1; }
+9 -42
View File
@@ -164,6 +164,7 @@ void CommonCLI::savePrefs() {
_prefs->advert_interval = 0; // turn it off, now that device has been manually configured
}
_callbacks->savePrefs();
_prefs->clearDirty();
}
uint8_t CommonCLI::buildAdvertData(uint8_t node_type, uint8_t* app_data) {
@@ -180,6 +181,11 @@ uint8_t CommonCLI::buildAdvertData(uint8_t node_type, uint8_t* app_data) {
}
void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* reply) {
if (_prefs->getRadioPrefs()->handleCommand(command, sender_timestamp, reply)) { // is a radio CLI command?
if (_prefs->getRadioPrefs()->isDirty()) { savePrefs(); }
return;
}
if (memcmp(command, "poweroff", 8) == 0 || memcmp(command, "shutdown", 8) == 0) {
_board->powerOff(); // doesn't return
} else if (memcmp(command, "reboot", 6) == 0) {
@@ -447,19 +453,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* reply) {
const char* config = &command[4];
if (memcmp(config, "dutycycle ", 10) == 0) {
float dc = atof(&config[10]);
if (dc < 1 || dc > 100) {
strcpy(reply, "ERROR: dutycycle must be 1-100");
} else {
_prefs->airtime_factor = (100.0f / dc) - 1.0f;
savePrefs();
float actual = 100.0f / (_prefs->airtime_factor + 1.0f);
int a_int = (int)actual;
int a_frac = (int)((actual - a_int) * 10.0f + 0.5f);
sprintf(reply, "OK - %d.%d%%", a_int, a_frac);
}
} else if (memcmp(config, "af ", 3) == 0) {
if (memcmp(config, "af ", 3) == 0) {
_prefs->airtime_factor = atof(&config[3]);
savePrefs();
strcpy(reply, "OK");
@@ -585,24 +580,6 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
strcpy(reply, "Error: state must be on or off");
}
} else if (memcmp(config, "radio ", 6) == 0) {
strcpy(tmp, &config[6]);
const char *parts[4];
int num = mesh::Utils::parseTextParts(tmp, parts, 4);
float freq = num > 0 ? strtof(parts[0], nullptr) : 0.0f;
float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f;
uint8_t sf = num > 2 ? atoi(parts[2]) : 0;
uint8_t cr = num > 3 ? atoi(parts[3]) : 0;
if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) {
_prefs->sf = sf;
_prefs->cr = cr;
_prefs->freq = freq;
_prefs->bw = bw;
_callbacks->savePrefs();
strcpy(reply, "OK - reboot to apply");
} else {
strcpy(reply, "Error, invalid radio params");
}
} else if (memcmp(config, "lat ", 4) == 0) {
_prefs->node_lat = atof(&config[4]);
savePrefs();
@@ -806,12 +783,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* reply) {
const char* config = &command[4];
if (memcmp(config, "dutycycle", 9) == 0) {
float dc = 100.0f / (_prefs->airtime_factor + 1.0f);
int dc_int = (int)dc;
int dc_frac = (int)((dc - dc_int) * 10.0f + 0.5f);
sprintf(reply, "> %d.%d%%", dc_int, dc_frac);
} else if (memcmp(config, "af", 2) == 0) {
if (memcmp(config, "af", 2) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor));
} else if (memcmp(config, "int.thresh", 10) == 0) {
sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold);
@@ -856,11 +828,6 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
sprintf(reply, "> %s", _board->isLoRaFemPaGainEnabled() ? "on" : "off");
}
} else if (memcmp(config, "radio", 5) == 0) {
char freq[16], bw[16];
strcpy(freq, StrHelper::ftoa(_prefs->freq));
strcpy(bw, StrHelper::ftoa3(_prefs->bw));
sprintf(reply, "> %s,%s,%d,%d", freq, bw, (uint32_t)_prefs->sf, (uint32_t)_prefs->cr);
} else if (memcmp(config, "rxdelay", 7) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->rx_delay_base));
} else if (memcmp(config, "txdelay", 7) == 0) {
+16 -1
View File
@@ -6,6 +6,7 @@
#include <helpers/ClientACL.h>
#include <helpers/RegionMap.h>
#include <helpers/ConfigSerializer.h>
#include <helpers/CommonRadioPrefs.h>
#if defined(WITH_RS232_BRIDGE) || defined(WITH_ESPNOW_BRIDGE)
#define WITH_BRIDGE
@@ -72,7 +73,7 @@ public:
uint8_t extra_sf[4];
private:
class RadioPrefs : public ConfigSerializer {
class RadioPrefs : public CommonRadioPrefs {
NodePrefs* _parent;
protected:
void structure() override {
@@ -96,6 +97,17 @@ private:
}
public:
RadioPrefs(NodePrefs* parent) : _parent(parent) { }
// CommonRadioPrefs interface
float getFreq() const override { return _parent->freq; }
void setFreq(float f) override { _parent->freq = f; markDirty(); }
float getBandwidth() const override { return _parent->bw; }
void setBandwidth(float bw) override { _parent->bw = bw; markDirty(); }
uint8_t getSpreadFactor() const override { return _parent->sf; }
void setSpreadFactor(uint8_t sf) { _parent->sf; markDirty(); }
uint8_t getCodingRate() const override { return _parent->cr; }
void setCodingRate(uint8_t cr) { _parent->cr = cr; markDirty(); }
float getAirtimeFactor() const override { return _parent->airtime_factor; }
void setAirtimeFactor(float af) override { _parent->airtime_factor = af; markDirty(); }
};
RadioPrefs radio;
@@ -192,6 +204,9 @@ public:
bridge_secret[0] = 0;
owner_info[0] = 0;
}
CommonRadioPrefs* getRadioPrefs() { return &radio; }
void clearDirty() { radio.clearDirty(); }
};
class CommonCLICallbacks {
+56
View File
@@ -0,0 +1,56 @@
#include "CommonRadioPrefs.h"
#include "TxtDataHelpers.h"
#include "Utils.h"
bool CommonRadioPrefs::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
if (strcmp(command, "get radio") == 0) {
char freq[16], bw[16];
strcpy(freq, StrHelper::ftoa(getFreq()));
strcpy(bw, StrHelper::ftoa3(getBandwidth()));
sprintf(reply, "> %s,%s,%d,%d", freq, bw, (uint32_t)getSpreadFactor(), (uint32_t)getCodingRate());
return true;
}
if (memcmp(command, "set radio ", 10) == 0) {
char tmp[132];
strcpy(tmp, &command[10]);
const char *parts[4];
int num = mesh::Utils::parseTextParts(tmp, parts, 4);
float freq = num > 0 ? strtof(parts[0], nullptr) : 0.0f;
float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f;
uint8_t sf = num > 2 ? atoi(parts[2]) : 0;
uint8_t cr = num > 3 ? atoi(parts[3]) : 0;
if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) {
setSpreadFactor(sf);
setCodingRate(cr);
setFreq(freq);
setBandwidth(bw);
// NOTE: savePrefs() should be handled by caller
strcpy(reply, "OK - reboot to apply");
} else {
strcpy(reply, "Error, invalid radio params");
}
return true;
}
if (strcmp(command, "get dutycycle") == 0) {
float dc = 100.0f / (getAirtimeFactor() + 1.0f);
int dc_int = (int)dc;
int dc_frac = (int)((dc - dc_int) * 10.0f + 0.5f);
sprintf(reply, "> %d.%d%%", dc_int, dc_frac);
return true;
}
if (memcmp(command, "set dutycycle ", 14) == 0) {
float dc = atof(&command[14]);
if (dc < 1 || dc > 100) {
strcpy(reply, "ERROR: dutycycle must be 1-100");
} else {
setAirtimeFactor((100.0f / dc) - 1.0f);
// NOTE: savePrefs() should be handled by caller
float actual = 100.0f / (getAirtimeFactor() + 1.0f);
int a_int = (int)actual;
int a_frac = (int)((actual - a_int) * 10.0f + 0.5f);
sprintf(reply, "OK - %d.%d%%", a_int, a_frac);
}
return true;
}
return false; // not handled
}
+45
View File
@@ -0,0 +1,45 @@
#include "ConfigSerializer.h"
class CommonRadioPrefs : public ConfigSerializer {
bool _is_dirty = false;
protected:
CommonRadioPrefs() { }
public:
void markDirty() { _is_dirty = true; }
void clearDirty() { _is_dirty = false; }
bool isDirty() const { return _is_dirty; }
virtual float getFreq() const = 0;
virtual void setFreq(float f) = 0;
virtual float getBandwidth() const = 0;
virtual void setBandwidth(float bw) = 0;
virtual uint8_t getSpreadFactor() const = 0;
virtual void setSpreadFactor(uint8_t sf) = 0;
virtual uint8_t getCodingRate() const = 0;
virtual void setCodingRate(uint8_t cr) = 0;
virtual float getAirtimeFactor() const = 0;
virtual void setAirtimeFactor(float af) = 0;
// //def("cad", _parent->cad_enabled);
// //def("int_thr", _parent->interference_threshold);
// def("rxgain", _parent->rx_boosted_gain);
// #if 0
// // NOTE: these cannot be set (yet) so don't load/save until we can.
// // also, fem_rxgain WAS mapped to wrong JSON property previously
// def("fem_rxgain", _parent->radio_fem_rxgain);
// def("fem_txgain", _parent->radio_fem_txgain);
// #endif
// def("tx", _parent->tx_power_dbm);
// def("rxdelay", _parent->rx_delay_base);
// //def("f_txdelay", _parent->tx_delay_factor); currently hard-coded
// //def("d_txdelay", _parent->direct_tx_delay_factor); currently hard-coded
// //def("agc_int", _parent->agc_reset_interval);
// def("hash_mode", _parent->path_hash_mode);
// def("multi_ack", _parent->multi_acks);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply);
};