diff --git a/docs/kiss_modem_protocol.md b/docs/kiss_modem_protocol.md index cb7be8b3..a37c4561 100644 --- a/docs/kiss_modem_protocol.md +++ b/docs/kiss_modem_protocol.md @@ -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. diff --git a/examples/kiss_modem/KissModem.cpp b/examples/kiss_modem/KissModem.cpp index 221c40b1..a6950bff 100644 --- a/examples/kiss_modem/KissModem.cpp +++ b/examples/kiss_modem/KissModem.cpp @@ -1,5 +1,6 @@ #include "KissModem.h" #include +#include 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) { diff --git a/examples/kiss_modem/KissModem.h b/examples/kiss_modem/KissModem.h index ffe89191..572999c4 100644 --- a/examples/kiss_modem/KissModem.h +++ b/examples/kiss_modem/KissModem.h @@ -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(); diff --git a/src/MeshCore.h b/src/MeshCore.h index 5fdb064c..2a3582de 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -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 */ } diff --git a/test/mocks/Mesh.h b/test/mocks/Mesh.h index 29a29b82..33c437f2 100644 --- a/test/mocks/Mesh.h +++ b/test/mocks/Mesh.h @@ -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; } }; } diff --git a/test/test_kiss_modem/test_hw_controls.cpp b/test/test_kiss_modem/test_hw_controls.cpp index 37b1b929..16ef50df 100644 --- a/test/test_kiss_modem/test_hw_controls.cpp +++ b/test/test_kiss_modem/test_hw_controls.cpp @@ -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{0x9F, 0x00, 0x00})); + EXPECT_EQ(hw1({HW_CMD_GET_CAPABILITIES}), (std::vector{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{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{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{HW_RESP_ERROR, HW_ERR_INVALID_LENGTH})); } diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index ea26d2cc..47ad8d96 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -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"); diff --git a/variants/heltec_t096/T096Board.h b/variants/heltec_t096/T096Board.h index 66fa5047..e926c8a6 100644 --- a/variants/heltec_t096/T096Board.h +++ b/variants/heltec_t096/T096Board.h @@ -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; diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index 753824a0..95d56699 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -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"); diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h index 34454cac..5fed2b65 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -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; diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 794257d7..2d941bd8 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -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"); diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index bd3bdb25..79a690e8 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -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; diff --git a/variants/heltec_v4_r8/HeltecV4R8Board.cpp b/variants/heltec_v4_r8/HeltecV4R8Board.cpp index ea2cfbed..d4860ab4 100644 --- a/variants/heltec_v4_r8/HeltecV4R8Board.cpp +++ b/variants/heltec_v4_r8/HeltecV4R8Board.cpp @@ -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"); diff --git a/variants/heltec_v4_r8/HeltecV4R8Board.h b/variants/heltec_v4_r8/HeltecV4R8Board.h index c34ee449..7ab29f96 100644 --- a/variants/heltec_v4_r8/HeltecV4R8Board.h +++ b/variants/heltec_v4_r8/HeltecV4R8Board.h @@ -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; diff --git a/variants/heltec_v4_r8/LoRaFEMControl.h b/variants/heltec_v4_r8/LoRaFEMControl.h index 52d7a0f8..4b74c2e2 100644 --- a/variants/heltec_v4_r8/LoRaFEMControl.h +++ b/variants/heltec_v4_r8/LoRaFEMControl.h @@ -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; } diff --git a/variants/station_g3_esp32/StationG3Board.cpp b/variants/station_g3_esp32/StationG3Board.cpp index 277c2b8b..9e8ef112 100644 --- a/variants/station_g3_esp32/StationG3Board.cpp +++ b/variants/station_g3_esp32/StationG3Board.cpp @@ -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"); diff --git a/variants/station_g3_esp32/StationG3Board.h b/variants/station_g3_esp32/StationG3Board.h index f8cb75cb..b4c5838c 100644 --- a/variants/station_g3_esp32/StationG3Board.h +++ b/variants/station_g3_esp32/StationG3Board.h @@ -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); diff --git a/variants/wio-tracker-l1-1w/WioTrackerL1Board.cpp b/variants/wio-tracker-l1-1w/WioTrackerL1Board.cpp index c6dbdbf5..69b33f75 100644 --- a/variants/wio-tracker-l1-1w/WioTrackerL1Board.cpp +++ b/variants/wio-tracker-l1-1w/WioTrackerL1Board.cpp @@ -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");