kiss_modem: reach FEM gain through the board's radio.fem CLI commands

Boards own their FEM control through handleCommand() ("get/set
radio.fem.rxgain|txgain"). Use that instead of a parallel set of FEM
virtuals on MainBoard, so any board that implements the commands works
in KISS with no extra code.

- Remove the six FEM virtuals from MainBoard and the board overrides.
- KissModem talks to the board in one adapter. A gain counts as
  controllable only if "get" replies exactly "> on" or "> off". "set"
  is checked by reading the state back, never by its reply wording.
- SetFemState always replies with the read-back state once the
  requested bits pass the capability check.
- Guard the boards' prefs writes against a NULL store. KISS attaches
  no prefs, so "set radio.fem.*" would dereference NULL.
- Wio Tracker L1 1W TX gain is exposed again, matching its CLI.
This commit is contained in:
agessaman committed 2026-09-23 17:31:20 -07:00
1 parent c387fc22c3
commit 720f0f1528
18 files changed
+161 -100

No files matched your search

+1 -1
View File
@@ -283,7 +283,7 @@ FemState response data:
Example: enable RX gain without changing TX gain: apply `0x01`, value `0x01`.
If any bit in the apply mask is not in the board's capability mask (including reserved bits), the modem replies `Unsupported` and changes nothing. On success, SetFemState replies with the FemState response carrying the new state. A SetFemState received while a packet is on air (before its TxDone is queued) is held and applied after TxDone, so its FemState response follows TxDone. While a SetFemState is held or its response is still waiting for output space, GetFemState responses are queued behind it in order, and a further SetFemState returns `TxBusy`. Capabilities come from the board, so a board with a fixed, board-managed FEM (for example RAK3401) reports none.
If any bit in the apply mask is not in the board's capability mask (including reserved bits), the modem replies `Unsupported` and changes nothing. Otherwise SetFemState replies with the FemState response carrying the state read back from the board; if a bit differs from the request, that change did not take effect. A SetFemState received while a packet is on air (before its TxDone is queued) is held and applied after TxDone, so its FemState response follows TxDone. While a SetFemState is held or its response is still waiting for output space, GetFemState responses are queued behind it in order, and a further SetFemState returns `TxBusy`. Capabilities come from the board's own `get/set radio.fem.rxgain` and `radio.fem.txgain` CLI commands (see [cli_commands.md](./cli_commands.md)): a gain is controllable when the board answers `get` with `> on` or `> off`. Any board that implements those commands is supported without KISS changes, and a board with a fixed, board-managed FEM (for example RAK3401) reports none. Changes made over KISS are not saved to the board's preferences.
The setting is not persisted; the board's power-on default applies after reboot.
+61 -31
View File
@@ -1,5 +1,6 @@
#include "KissModem.h"
#include <CayenneLPP.h>
#include <stdio.h>
KissModem::KissModem(Stream& serial, mesh::LocalIdentity& identity, mesh::RNG& rng,
mesh::Radio& radio, mesh::MainBoard& board, SensorManager& sensors)
@@ -31,7 +32,6 @@ KissModem::KissModem(Stream& serial, mesh::LocalIdentity& identity, mesh::RNG& r
_fem_deferred_apply = 0;
_fem_deferred_value = 0;
_fem_reply_pending = false;
_fem_reply_ok = false;
_fem_deferred_gets = 0;
resetOutputQueue();
}
@@ -785,8 +785,10 @@ void KissModem::handleGetSignalReport() {
void KissModem::handleGetCapabilities() {
uint32_t caps = HW_CAP_AGC_RESET;
if (_board.canControlLoRaFemLna()) caps |= HW_CAP_FEM_RX_GAIN;
if (_board.canControlLoRaFemPaGain()) caps |= HW_CAP_FEM_TX_GAIN;
uint8_t fem_caps, fem_values;
readFemState(&fem_caps, &fem_values);
if (fem_caps & HW_FEM_RX_GAIN) caps |= HW_CAP_FEM_RX_GAIN;
if (fem_caps & HW_FEM_TX_GAIN) caps |= HW_CAP_FEM_TX_GAIN;
if (_setRxBoostedGainCallback && _getRxBoostedGainCallback) caps |= HW_CAP_RX_BOOSTED_GAIN;
uint8_t buf[4] = { (uint8_t)caps, (uint8_t)(caps >> 8), (uint8_t)(caps >> 16), (uint8_t)(caps >> 24) };
writeHardwareFrame(HW_RESP(HW_CMD_GET_CAPABILITIES), buf, 4);
@@ -812,55 +814,81 @@ void KissModem::handleGetAgcResetInterval() {
writeHardwareFrame(HW_RESP(HW_CMD_GET_AGC_RESET_INTERVAL), buf, 2);
}
uint8_t KissModem::femCapabilityMask() const {
uint8_t mask = 0;
if (_board.canControlLoRaFemLna()) mask |= HW_FEM_RX_GAIN;
if (_board.canControlLoRaFemPaGain()) mask |= HW_FEM_TX_GAIN;
return mask;
// FEM gain is reached through the board's own "radio.fem.*" CLI commands, the same ones users run.
static const char* femGainName(uint8_t bit) {
return bit == HW_FEM_RX_GAIN ? "rxgain" : "txgain";
}
uint8_t KissModem::femValueMask() const {
uint8_t mask = 0;
if (_board.canControlLoRaFemLna() && _board.isLoRaFemLnaEnabled()) mask |= HW_FEM_RX_GAIN;
if (_board.canControlLoRaFemPaGain() && _board.isLoRaFemPaGainEnabled()) mask |= HW_FEM_TX_GAIN;
return mask;
}
bool KissModem::queueFemReply(bool ok, bool mark_busy_error) {
if (!ok) {
const uint8_t err = HW_ERR_UNSUPPORTED;
return queueHardwareFrame(HW_RESP_ERROR, &err, 1, mark_busy_error);
// Anything but an exact "> on" / "> off" (not handled, "Error: ...", unexpected text) means unsupported.
bool KissModem::queryFemGain(uint8_t bit, bool* enabled) {
char cmd[32];
char reply[KISS_BOARD_REPLY_SIZE] = { 0 };
snprintf(cmd, sizeof(cmd), "get radio.fem.%s", femGainName(bit));
if (!_board.handleCommand(cmd, 0, reply)) return false;
if (strcmp(reply, "> on") == 0) {
*enabled = true;
} else if (strcmp(reply, "> off") == 0) {
*enabled = false;
} else {
return false;
}
uint8_t buf[2] = { femCapabilityMask(), femValueMask() };
return true;
}
// Success is judged by reading the state back, not by the wording of the set reply.
bool KissModem::setFemGain(uint8_t bit, bool enable) {
char cmd[32];
char reply[KISS_BOARD_REPLY_SIZE] = { 0 };
snprintf(cmd, sizeof(cmd), "set radio.fem.%s %s", femGainName(bit), enable ? "on" : "off");
_board.handleCommand(cmd, 0, reply);
bool now;
return queryFemGain(bit, &now) && now == enable;
}
void KissModem::readFemState(uint8_t* caps, uint8_t* values) {
*caps = 0;
*values = 0;
const uint8_t bits[] = { HW_FEM_RX_GAIN, HW_FEM_TX_GAIN };
for (uint8_t bit : bits) {
bool enabled;
if (queryFemGain(bit, &enabled)) {
*caps |= bit;
if (enabled) *values |= bit;
}
}
}
bool KissModem::queueFemReply(bool mark_busy_error) {
uint8_t buf[2];
readFemState(&buf[0], &buf[1]);
return queueHardwareFrame(HW_RESP(HW_CMD_GET_FEM_STATE), buf, 2, mark_busy_error);
}
bool KissModem::applyFemState(uint8_t apply_mask, uint8_t value_mask) {
bool ok = true;
// The reply always carries the read-back state, so a bit that failed to apply shows its real value.
void KissModem::applyFemState(uint8_t apply_mask, uint8_t value_mask) {
if (apply_mask & HW_FEM_RX_GAIN) {
ok &= _board.setLoRaFemLnaEnabled((value_mask & HW_FEM_RX_GAIN) != 0);
setFemGain(HW_FEM_RX_GAIN, (value_mask & HW_FEM_RX_GAIN) != 0);
}
if (apply_mask & HW_FEM_TX_GAIN) {
ok &= _board.setLoRaFemPaGainEnabled((value_mask & HW_FEM_TX_GAIN) != 0);
setFemGain(HW_FEM_TX_GAIN, (value_mask & HW_FEM_TX_GAIN) != 0);
}
return ok;
}
void KissModem::processDeferredFem() {
// wait for TxDone to be queued so the host sees the TX result before the FEM change
if (_fem_deferred && _tx_state != TX_SENDING && _tx_state != TX_DONE_PENDING) {
_fem_reply_ok = applyFemState(_fem_deferred_apply, _fem_deferred_value);
applyFemState(_fem_deferred_apply, _fem_deferred_value);
_fem_deferred = false;
_fem_reply_pending = true;
}
if (_fem_deferred) return;
// replies are retained until queued, and GetFemState replies stay behind the Set reply
if (_fem_reply_pending) {
if (!queueFemReply(_fem_reply_ok, false)) return;
if (!queueFemReply(false)) return;
_fem_reply_pending = false;
}
while (_fem_deferred_gets > 0) {
if (!queueFemReply(true, false)) return;
if (!queueFemReply(false)) return;
_fem_deferred_gets--;
}
}
@@ -873,7 +901,9 @@ void KissModem::handleSetFemState(const uint8_t* data, uint16_t len) {
uint8_t apply_mask = data[0];
uint8_t value_mask = data[1];
// validate every requested bit before touching hardware so a request is all-or-nothing
if (apply_mask & ~femCapabilityMask()) {
uint8_t fem_caps, fem_values;
readFemState(&fem_caps, &fem_values);
if (apply_mask & ~fem_caps) {
writeHardwareError(HW_ERR_UNSUPPORTED);
return;
}
@@ -888,7 +918,7 @@ void KissModem::handleSetFemState(const uint8_t* data, uint16_t len) {
_fem_deferred_value = value_mask;
return;
}
_fem_reply_ok = applyFemState(apply_mask, value_mask);
applyFemState(apply_mask, value_mask);
_fem_reply_pending = true;
processDeferredFem();
}
@@ -902,7 +932,7 @@ void KissModem::handleGetFemState() {
_fem_deferred_gets++;
return;
}
queueFemReply(true, true);
queueFemReply(true);
}
void KissModem::handleSetRxBoostedGain(const uint8_t* data, uint16_t len) {
+8 -5
View File
@@ -100,6 +100,9 @@
#define HW_FEM_RX_GAIN (1 << 0)
#define HW_FEM_TX_GAIN (1 << 1)
/* Matches the CLI reply buffer boards write into from handleCommand() */
#define KISS_BOARD_REPLY_SIZE 160
#define KISS_AGC_RESET_DEFAULT_SEC 30
#define KISS_AGC_RESET_MAX_SEC 1020
#define KISS_AGC_RESET_STEP_SEC 4
@@ -173,7 +176,6 @@ class KissModem {
uint8_t _fem_deferred_apply;
uint8_t _fem_deferred_value;
bool _fem_reply_pending;
bool _fem_reply_ok;
uint8_t _fem_deferred_gets;
uint8_t _tx_frame_buf[KISS_TX_FRAME_QUEUE_DEPTH][KISS_MAX_ENCODED_FRAME_SIZE];
uint16_t _tx_frame_len[KISS_TX_FRAME_QUEUE_DEPTH];
@@ -202,10 +204,11 @@ class KissModem {
void handleHardwareCommand(uint8_t sub_cmd, const uint8_t* data, uint16_t len);
void processTx();
void maybeResetAgc();
uint8_t femCapabilityMask() const;
uint8_t femValueMask() const;
bool queueFemReply(bool ok, bool mark_busy_error);
bool applyFemState(uint8_t apply_mask, uint8_t value_mask);
bool queryFemGain(uint8_t bit, bool* enabled);
bool setFemGain(uint8_t bit, bool enable);
void readFemState(uint8_t* caps, uint8_t* values);
bool queueFemReply(bool mark_busy_error);
void applyFemState(uint8_t apply_mask, uint8_t value_mask);
bool isFemReplyQueued() const { return _fem_deferred || _fem_reply_pending || _fem_deferred_gets > 0; }
void processDeferredFem();
-8
View File
@@ -77,14 +77,6 @@ public:
virtual bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) { return false; }
// External LoRa front-end (FEM) gain control. Boards without a controllable FEM keep these defaults.
virtual bool canControlLoRaFemLna() const { return false; }
virtual bool setLoRaFemLnaEnabled(bool enable) { return false; }
virtual bool isLoRaFemLnaEnabled() const { return false; }
virtual bool canControlLoRaFemPaGain() const { return false; }
virtual bool setLoRaFemPaGainEnabled(bool enable) { return false; }
virtual bool isLoRaFemPaGainEnabled() const { return false; }
// Called from the example main loops. Lets a board feed its watchdog and
// run periodic housekeeping. Default no-op.
virtual void loop() { /* no op */ }
+1 -6
View File
@@ -23,12 +23,7 @@ public:
virtual float getMCUTemperature() { return 25.0f; }
virtual const char* getManufacturerName() { return "mock-board"; }
virtual void reboot() {}
virtual bool canControlLoRaFemLna() const { return false; }
virtual bool setLoRaFemLnaEnabled(bool) { return false; }
virtual bool isLoRaFemLnaEnabled() const { return false; }
virtual bool canControlLoRaFemPaGain() const { return false; }
virtual bool setLoRaFemPaGainEnabled(bool) { return false; }
virtual bool isLoRaFemPaGainEnabled() const { return false; }
virtual bool handleCommand(const char*, uint32_t, char*) { return false; }
};
}
+60 -16
View File
@@ -72,31 +72,50 @@ public:
int agc_resets = 0;
};
// Implements FEM through the "radio.fem.*" CLI text, worded like the Heltec / Station G3 boards.
class FemBoard : public mesh::MainBoard {
public:
const char* getManufacturerName() override { return "fem-board"; }
bool canControlLoRaFemLna() const override { return has_lna; }
bool setLoRaFemLnaEnabled(bool enable) override {
if (!has_lna) return false;
lna = enable;
lna_sets++;
return true;
bool handleCommand(const char* command, uint32_t, char* reply) override {
commands++;
if (handleGain(command, reply, "rxgain", has_lna, lna, lna_sets, lna_sticks)) return true;
if (handleGain(command, reply, "txgain", has_pa, pa, pa_sets, pa_sticks)) return true;
return false;
}
bool isLoRaFemLnaEnabled() const override { return lna; }
bool canControlLoRaFemPaGain() const override { return has_pa; }
bool setLoRaFemPaGainEnabled(bool enable) override {
if (!has_pa) return false;
pa = enable;
pa_sets++;
return true;
}
bool isLoRaFemPaGainEnabled() const override { return pa; }
bool has_lna = false, has_pa = false;
bool lna = false, pa = false;
int lna_sets = 0, pa_sets = 0;
bool lna_sticks = false, pa_sticks = false; // set reports OK but the state does not change
bool unhandled = false; // board does not implement radio.fem.* at all
const char* odd_get_reply = nullptr; // unexpected get wording
int commands = 0;
private:
bool handleGain(const char* command, char* reply, const char* name, bool has, bool& state, int& sets, bool sticks) {
if (unhandled) return false;
char get_cmd[32], set_cmd[32];
snprintf(get_cmd, sizeof(get_cmd), "get radio.fem.%s", name);
snprintf(set_cmd, sizeof(set_cmd), "set radio.fem.%s ", name);
if (strcmp(command, get_cmd) == 0) {
if (!has) strcpy(reply, "Error: unsupported");
else if (odd_get_reply) strcpy(reply, odd_get_reply);
else sprintf(reply, "> %s", state ? "on" : "off");
return true;
}
if (strncmp(command, set_cmd, strlen(set_cmd)) == 0) {
if (!has) {
strcpy(reply, "Error: unsupported");
} else {
sets++;
if (!sticks) state = strcmp(command + strlen(set_cmd), "on") == 0;
strcpy(reply, "OK - LoRa FEM gain changed");
}
return true;
}
return false;
}
};
class NoSensors : public SensorManager {
@@ -276,6 +295,31 @@ TEST_F(KissHwControlTest, FemUnknownBitsAreUnsupported) {
EXPECT_EQ(hw1({HW_CMD_SET_FEM_STATE, 0x04, 0x04}), ERR_UNSUPPORTED);
}
TEST_F(KissHwControlTest, FemUnhandledCommandsMeanUnsupported) {
board.has_lna = board.has_pa = true;
board.unhandled = true;
EXPECT_EQ(hw1({HW_CMD_GET_FEM_STATE}), (std::vector<uint8_t>{0x9F, 0x00, 0x00}));
EXPECT_EQ(hw1({HW_CMD_GET_CAPABILITIES}), (std::vector<uint8_t>{0x9B, 0x01, 0x00, 0x00, 0x00}));
EXPECT_EQ(hw1({HW_CMD_SET_FEM_STATE, HW_FEM_RX_GAIN, HW_FEM_RX_GAIN}), ERR_UNSUPPORTED);
}
TEST_F(KissHwControlTest, FemUnexpectedGetWordingMeansUnsupported) {
board.has_lna = true;
board.odd_get_reply = "> enabled";
EXPECT_EQ(hw1({HW_CMD_GET_FEM_STATE}), (std::vector<uint8_t>{0x9F, 0x00, 0x00}));
EXPECT_EQ(hw1({HW_CMD_SET_FEM_STATE, HW_FEM_RX_GAIN, HW_FEM_RX_GAIN}), ERR_UNSUPPORTED);
EXPECT_EQ(board.lna_sets, 0);
}
TEST_F(KissHwControlTest, FemSetThatDoesNotTakeRepliesWithReadBackState) {
board.has_lna = board.has_pa = true;
board.lna_sticks = true; // board says OK but LNA stays off
EXPECT_EQ(hw1({HW_CMD_SET_FEM_STATE, HW_FEM_RX_GAIN | HW_FEM_TX_GAIN, HW_FEM_RX_GAIN | HW_FEM_TX_GAIN}),
(std::vector<uint8_t>{0x9F, 0x03, 0x02}));
EXPECT_FALSE(board.lna);
EXPECT_TRUE(board.pa);
}
TEST_F(KissHwControlTest, FemShortPayloadIsInvalidLength) {
EXPECT_EQ(hw1({HW_CMD_SET_FEM_STATE, HW_FEM_RX_GAIN}), (std::vector<uint8_t>{HW_RESP_ERROR, HW_ERR_INVALID_LENGTH}));
}
+2 -2
View File
@@ -158,14 +158,14 @@ bool T096Board::handleCommand(const char* command, uint32_t sender_timestamp, ch
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
if (_prefs) _prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
if (_prefs) _prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
+3 -3
View File
@@ -16,6 +16,9 @@ protected:
#endif
void variant_shutdown();
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
public:
RefCountedDigitalPin periph_power;
LoRaFEMControl loRaFEMControl;
@@ -24,9 +27,6 @@ public:
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
bool canControlLoRaFemLna() const override { return loRaFEMControl.isLnaCanControl(); }
bool setLoRaFemLnaEnabled(bool enable) override;
bool isLoRaFemLnaEnabled() const override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
@@ -99,14 +99,14 @@ bool HeltecTrackerV2Board::handleCommand(const char* command, uint32_t sender_ti
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
if (_prefs) _prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
if (_prefs) _prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
@@ -8,6 +8,9 @@
class HeltecTrackerV2Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
public:
RefCountedDigitalPin periph_power;
LoRaFEMControl loRaFEMControl;
@@ -17,9 +20,6 @@ public:
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
bool canControlLoRaFemLna() const override { return loRaFEMControl.isLnaCanControl(); }
bool setLoRaFemLnaEnabled(bool enable) override;
bool isLoRaFemLnaEnabled() const override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
+2 -2
View File
@@ -100,14 +100,14 @@ bool HeltecV4Board::handleCommand(const char* command, uint32_t sender_timestamp
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
if (_prefs) _prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
if (_prefs) _prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
+3 -3
View File
@@ -12,6 +12,9 @@
class HeltecV4Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
protected:
float adc_mult = ADC_MULTIPLIER;
@@ -23,9 +26,6 @@ public:
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
bool canControlLoRaFemLna() const override { return loRaFEMControl.isLnaCanControl(); }
bool setLoRaFemLnaEnabled(bool enable) override;
bool isLoRaFemLnaEnabled() const override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
+2 -2
View File
@@ -122,14 +122,14 @@ bool HeltecV4R8Board::handleCommand(const char* command, uint32_t sender_timesta
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
if (_prefs) _prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
if (_prefs) _prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
+2 -3
View File
@@ -13,6 +13,8 @@
class HeltecV4R8Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
protected:
float adc_mult = ADC_MULTIPLIER;
@@ -25,9 +27,6 @@ public:
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
bool canControlLoRaFemLna() const override { return loRaFEMControl.isLnaCanControl(); }
bool setLoRaFemLnaEnabled(bool enable) override;
bool isLoRaFemLnaEnabled() const override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
+1 -1
View File
@@ -15,7 +15,7 @@ public:
void setRxModeEnable(void);
void setRxModeEnableWhenMCUSleep(void);
void setLNAEnable(bool enabled);
bool isLnaCanControl(void) const { return true; }
bool isLnaCanControl(void) { return true; }
void setLnaCanControl(bool can_control) { }
bool isLNAEnabled(void) const { return lna_enabled; }
LoRaFEMType getFEMType(void) const { return KCT8103L_PA; }
+4 -4
View File
@@ -65,14 +65,14 @@ bool StationG3Board::handleCommand(const char* command, uint32_t sender_timestam
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
if (_prefs) _prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
if (_prefs) _prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
@@ -96,14 +96,14 @@ bool StationG3Board::handleCommand(const char* command, uint32_t sender_timestam
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemPaGainEnabled(true)) {
_prefs->setByKey("fem_txgain", "1");
if (_prefs) _prefs->setByKey("fem_txgain", "1");
strcpy(reply, "OK - LoRa FEM TX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemPaGainEnabled(false)) {
_prefs->setByKey("fem_txgain", "0");
if (_prefs) _prefs->setByKey("fem_txgain", "0");
strcpy(reply, "OK - LoRa FEM TX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
+4 -6
View File
@@ -8,6 +8,10 @@
class StationG3Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
bool setLoRaFemPaGainEnabled(bool enable);
bool isLoRaFemPaGainEnabled() const;
public:
LoRaFEMControl loRaFEMControl;
@@ -30,12 +34,6 @@ public:
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
bool canControlLoRaFemLna() const override { return loRaFEMControl.canControlLNA(); }
bool setLoRaFemLnaEnabled(bool enable) override;
bool isLoRaFemLnaEnabled() const override;
bool canControlLoRaFemPaGain() const override { return loRaFEMControl.canControlPAGain(); }
bool setLoRaFemPaGainEnabled(bool enable) override;
bool isLoRaFemPaGainEnabled() const override;
void setPrimaryLNAEnable(bool enabled) {
loRaFEMControl.setLNAEnable(enabled);
@@ -65,14 +65,14 @@ bool WioTrackerL1Board::handleCommand(const char* command, uint32_t sender_times
if (memcmp(command, "set radio.fem.txgain ", 21) == 0) {
if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemPaGainEnabled(true)) {
_prefs->setByKey("fem_txgain", "1");
if (_prefs) _prefs->setByKey("fem_txgain", "1");
strcpy(reply, "OK - LoRa FEM TX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemPaGainEnabled(false)) {
_prefs->setByKey("fem_txgain", "0");
if (_prefs) _prefs->setByKey("fem_txgain", "0");
strcpy(reply, "OK - LoRa FEM TX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");