* T096, and Station G3: refactoring the 'FEM' prefs to variant-specific code

* CommonCLI: 'FEM' commands removed
* introduced static no-op attachDynamicPrefs() for all boards
This commit is contained in:
Scott Powell
2026-08-24 01:50:22 +10:00
parent e485d01dcb
commit 47b0b7b3b8
21 changed files with 243 additions and 101 deletions
+2 -7
View File
@@ -64,13 +64,6 @@ public:
virtual uint8_t getStartupReason() const = 0;
virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; }
virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported
virtual bool setLoRaFemLnaEnabled(bool enable) { return false; }
virtual bool canControlLoRaFemLna() const { return false; }
virtual bool isLoRaFemLnaEnabled() const { return false; }
// Software-selectable external FEM transmit gain. This is not a PA power switch.
virtual bool setLoRaFemPaGainEnabled(bool enable) { return false; }
virtual bool canControlLoRaFemPaGain() const { return false; }
virtual bool isLoRaFemPaGainEnabled() const { return false; }
// Power management interface (boards with power management override these)
virtual bool isExternalPowered() { return false; }
@@ -79,6 +72,8 @@ public:
virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; }
virtual uint8_t getShutdownReason() const { return 0; }
virtual const char* getShutdownReasonString(uint8_t reason) { return "Not available"; }
virtual bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) { return false; }
};
/**
+5 -56
View File
@@ -185,6 +185,11 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
if (_prefs->getRadioPrefs()->isDirty()) { savePrefs(); }
return;
}
// hook for variant-specific CLI processing
if (_board->handleCommand(command, sender_timestamp, reply)) {
if (_prefs->isDirty()) { savePrefs(); }
return;
}
if (memcmp(command, "poweroff", 8) == 0 || memcmp(command, "shutdown", 8) == 0) {
_board->powerOff(); // doesn't return
@@ -507,50 +512,6 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0;
savePrefs();
strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON");
} else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) {
if (!_board->canControlLoRaFemLna()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&config[17], "on", 2) == 0) {
if (_board->setLoRaFemLnaEnabled(true)) {
_prefs->radio_fem_rxgain = 1;
savePrefs();
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&config[17], "off", 3) == 0) {
if (_board->setLoRaFemLnaEnabled(false)) {
_prefs->radio_fem_rxgain = 0;
savePrefs();
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
} else if (memcmp(config, "radio.fem.txgain ", 17) == 0) {
if (!_board->canControlLoRaFemPaGain()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&config[17], "on", 2) == 0) {
if (_board->setLoRaFemPaGainEnabled(true)) {
_prefs->radio_fem_txgain = 1;
savePrefs();
strcpy(reply, "OK - LoRa FEM TX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
}
} else if (memcmp(&config[17], "off", 3) == 0) {
if (_board->setLoRaFemPaGainEnabled(false)) {
_prefs->radio_fem_txgain = 0;
savePrefs();
strcpy(reply, "OK - LoRa FEM TX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
} else if (memcmp(config, "lat ", 4) == 0) {
_prefs->node_lat = atof(&config[4]);
savePrefs();
@@ -733,18 +694,6 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lat));
} else if (memcmp(config, "lon", 3) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lon));
} else if (memcmp(config, "radio.fem.rxgain", 16) == 0) {
if (!_board->canControlLoRaFemLna()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off");
}
} else if (memcmp(config, "radio.fem.txgain", 16) == 0) {
if (!_board->canControlLoRaFemPaGain()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", _board->isLoRaFemPaGainEnabled() ? "on" : "off");
}
} else if (memcmp(config, "flood.max.advert", 16) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max_advert);
} else if (memcmp(config, "flood.max.unscoped", 18) == 0) {
+6 -1
View File
@@ -128,6 +128,10 @@ private:
void setFloodTxDelay(float d) override { _parent->tx_delay_factor = d; markDirty(); }
float getDirectTxDelay() const override { return _parent->direct_tx_delay_factor; }
void setDirectTxDelay(float d) override { _parent->direct_tx_delay_factor = d; markDirty(); }
uint8_t getFEMRxGain() const override { return _parent->radio_fem_rxgain; }
void setFEMRxGain(uint8_t g) override { _parent->radio_fem_rxgain = g; markDirty(); }
uint8_t getFEMTxGain() const override { return _parent->radio_fem_txgain; }
void setFEMTxGain(uint8_t g) override { _parent->radio_fem_txgain = g; markDirty(); }
};
RadioPrefs radio;
@@ -226,7 +230,8 @@ public:
}
CommonRadioPrefs* getRadioPrefs() { return &radio; }
void clearDirty() { radio.clearDirty(); }
bool isDirty() const override { return ConfigSerializer::isDirty() || radio.isDirty(); }
void clearDirty() override { ConfigSerializer::clearDirty(); radio.clearDirty(); }
};
class CommonCLICallbacks {
+21
View File
@@ -3,6 +3,27 @@
#include "Utils.h"
#include "target.h"
void 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);
}
}
void 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) {
setFEMTxGain(atoi(value));
markDirty();
} else {
MESH_DEBUG_PRINTLN("Error: setBykey() unknown key: %s", key);
}
}
bool CommonRadioPrefs::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
if (strcmp(command, "get radio") == 0) {
char freq[16], bw[16];
+12 -1
View File
@@ -1,6 +1,8 @@
#pragma once
#include "ConfigSerializer.h"
#include "KeyValueStore.h"
class CommonRadioPrefs : public ConfigSerializer {
class CommonRadioPrefs : public ConfigSerializer, public KeyValueStore{
bool _is_dirty = false;
protected:
CommonRadioPrefs() { }
@@ -54,5 +56,14 @@ public:
virtual float getDirectTxDelay() const = 0;
virtual void setDirectTxDelay(float d) = 0;
virtual uint8_t getFEMRxGain() const = 0;
virtual void setFEMRxGain(uint8_t g) = 0;
virtual uint8_t getFEMTxGain() const = 0;
virtual void setFEMTxGain(uint8_t g) = 0;
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;
};
+6
View File
@@ -17,6 +17,7 @@
class ConfigSerializer {
bool _first;
int8_t _depth;
bool _dirty = false;
enum OP { READ, WRITE };
@@ -62,7 +63,12 @@ protected:
virtual void structure() = 0;
void markDirty() { _dirty = true; }
public:
bool loadSerial(Stream& s);
bool saveSerial(Stream& s);
virtual bool isDirty() const { return _dirty; }
virtual void clearDirty() { _dirty = false; }
};
+3
View File
@@ -15,6 +15,7 @@
#include "soc/rtc.h"
#include "esp_system.h"
#include <driver/rtc_io.h>
#include <helpers/KeyValueStore.h>
class ESP32Board : public mesh::MainBoard {
protected:
@@ -51,6 +52,8 @@ public:
#endif
}
void attachDynamicPrefs(KeyValueStore* prefs) { } // no-op
// Temperature from ESP32 MCU
float getMCUTemperature() override {
uint32_t raw = 0;
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
#include <string.h>
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) { }
};
+3
View File
@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <MeshCore.h>
#include <helpers/KeyValueStore.h>
#if defined(NRF52_PLATFORM)
@@ -57,6 +58,8 @@ public:
virtual void sleep(uint32_t secs) override;
bool isExternalPowered() override;
void attachDynamicPrefs(KeyValueStore* prefs) { } // no-op
#ifdef NRF52_POWER_MANAGEMENT
uint16_t getBootVoltage() override { return boot_voltage_mv; }
virtual uint32_t getResetReason() const override { return reset_reason; }
+3
View File
@@ -2,6 +2,7 @@
#include <MeshCore.h>
#include <Arduino.h>
#include <helpers/KeyValueStore.h>
class STM32Board : public mesh::MainBoard {
protected:
@@ -12,6 +13,8 @@ public:
startup_reason = BD_STARTUP_NORMAL;
}
void attachDynamicPrefs(KeyValueStore* prefs) { } // no-op
uint8_t getStartupReason() const override { return startup_reason; }
uint16_t getBattMilliVolts() override {