Browse Source

Add LoRa FEM RX-path LNA control via CLI; restore enabled default

Ported from meshcore-dev/MeshCore#2140 (Quency-D <hj_zzns@163.com>).

The upstream v1.16.0 merge changed the Heltec FEM LNA to disabled by
default (upstream commit 696aae6e).  This patch restores the enabled
default for repeater via the new "radio.fem.rxgain" preference
(defaulted to 1), while also making the setting user-configurable
and persisted across reboots.
Valentin V. Bartenev 2 months ago
parent
commit
1817248d97

+ 14 - 0
docs/cli_commands.md

@@ -263,6 +263,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore
 
 
 ---
 ---
 
 
+#### View or change the LoRa FEM receive-path gain state on supported boards
+**Usage:**
+- `get radio.fem.rxgain`
+- `set radio.fem.rxgain <state>`
+
+**Parameters:**
+- `state`: `on`|`off`
+
+**Notes:**
+- This controls the external LoRa FEM receive-path LNA where the board supports it.
+- This is separate from `radio.rxgain`, which controls the radio chip receive gain mode.
+
+---
+
 ### System
 ### System
 
 
 #### View or change this node's name
 #### View or change this node's name

+ 2 - 0
examples/simple_repeater/MyMesh.cpp

@@ -1266,6 +1266,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
   _prefs.rx_boosted_gain = 1; // enabled by default;
   _prefs.rx_boosted_gain = 1; // enabled by default;
 #endif
 #endif
 #endif
 #endif
+  _prefs.radio_fem_rxgain = 1;
 
 
   pending_discover_tag = 0;
   pending_discover_tag = 0;
   pending_discover_until = 0;
   pending_discover_until = 0;
@@ -1360,6 +1361,7 @@ void MyMesh::begin(FILESYSTEM *fs, ArchiveStorage* archive) {
   radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
   radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
   MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
   MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
                      radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
                      radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
+  board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
 
 
   updateAdvertTimer();
   updateAdvertTimer();
   updateFloodAdvertTimer();
   updateFloodAdvertTimer();

+ 3 - 0
src/MeshCore.h

@@ -69,6 +69,9 @@ public:
   virtual uint8_t getStartupReason() const = 0;
   virtual uint8_t getStartupReason() const = 0;
   virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; }
   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 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; }
 
 
   // Power management interface (boards with power management override these)
   // Power management interface (boards with power management override these)
   virtual bool isExternalPowered() { return false; }
   virtual bool isExternalPowered() { return false; }

+ 36 - 2
src/helpers/CommonCLI.cpp

@@ -106,7 +106,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
     if (file.available() >= (int)sizeof(_prefs->flood_max_advert)) {
     if (file.available() >= (int)sizeof(_prefs->flood_max_advert)) {
       file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert));          // 296
       file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert));          // 296
     }
     }
-    // next: 297
+    if (file.available() >= (int)sizeof(_prefs->radio_fem_rxgain)) {
+      file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain));          // 297
+    }
+    // next: 298
 
 
     // sanitise bad pref values
     // sanitise bad pref values
     _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
     _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -136,6 +139,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
 
 
     // sanitise settings
     // sanitise settings
     _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
     _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
+    _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean
+
     _prefs->fan_mode = constrain(_prefs->fan_mode, 0, 2);
     _prefs->fan_mode = constrain(_prefs->fan_mode, 0, 2);
     _prefs->fan_timeout_secs = constrain(_prefs->fan_timeout_secs, 0, 600);
     _prefs->fan_timeout_secs = constrain(_prefs->fan_timeout_secs, 0, 600);
 
 
@@ -204,7 +209,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
     file.write((uint8_t *)&_prefs->fan_timeout_secs, sizeof(_prefs->fan_timeout_secs));            // 293
     file.write((uint8_t *)&_prefs->fan_timeout_secs, sizeof(_prefs->fan_timeout_secs));            // 293
     file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped));        // 295
     file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped));        // 295
     file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert));            // 296
     file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert));            // 296
-    // next: 297
+    file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain));            // 297
+    // next: 298
 
 
     file.close();
     file.close();
   }
   }
@@ -597,6 +603,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
     savePrefs();
     savePrefs();
     _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain);
     _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain);
 #endif
 #endif
+  } 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 ", 6) == 0) {
   } else if (memcmp(config, "radio ", 6) == 0) {
     strcpy(tmp, &config[6]);
     strcpy(tmp, &config[6]);
     const char *parts[4];
     const char *parts[4];
@@ -834,6 +862,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
   } else if (memcmp(config, "radio.rxgain", 12) == 0) {
   } else if (memcmp(config, "radio.rxgain", 12) == 0) {
     sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off");
     sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off");
 #endif
 #endif
+  } 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", 5) == 0) {
   } else if (memcmp(config, "radio", 5) == 0) {
     char freq[16], bw[16];
     char freq[16], bw[16];
     strcpy(freq, StrHelper::ftoa(_prefs->freq));
     strcpy(freq, StrHelper::ftoa(_prefs->freq));

+ 1 - 0
src/helpers/CommonCLI.h

@@ -62,6 +62,7 @@ struct NodePrefs { // persisted to file
   char owner_info[120];
   char owner_info[120];
   uint8_t reserved_290; // reserved to preserve on-disk prefs layout
   uint8_t reserved_290; // reserved to preserve on-disk prefs layout
   uint8_t rx_boosted_gain; // power settings
   uint8_t rx_boosted_gain; // power settings
+  uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting
   uint8_t path_hash_mode;   // which path mode to use when sending
   uint8_t path_hash_mode;   // which path mode to use when sending
   uint8_t loop_detect;
   uint8_t loop_detect;
   uint8_t fan_mode;
   uint8_t fan_mode;

+ 2 - 1
variants/heltec_t096/LoRaFEMControl.h

@@ -12,8 +12,9 @@ class LoRaFEMControl
     void setRxModeEnable(void);
     void setRxModeEnable(void);
     void setRxModeEnableWhenMCUSleep(void);
     void setRxModeEnableWhenMCUSleep(void);
     void setLNAEnable(bool enabled);
     void setLNAEnable(bool enabled);
-    bool isLnaCanControl(void) { return lna_can_control; }
+    bool isLnaCanControl(void) const { return lna_can_control; }
     void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
     void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
+    bool isLNAEnabled(void) const { return lna_enabled; }
 
 
   private:
   private:
     bool lna_enabled = false;
     bool lna_enabled = false;

+ 19 - 1
variants/heltec_t096/T096Board.cpp

@@ -123,4 +123,22 @@ void T096Board::powerOff() {
 
 
 const char* T096Board::getManufacturerName() const {
 const char* T096Board::getManufacturerName() const {
   return "Heltec T096";
   return "Heltec T096";
-}
+}
+
+bool T096Board::setLoRaFemLnaEnabled(bool enable) {
+  if (!loRaFEMControl.isLnaCanControl()) {
+    return false;
+  }
+
+  loRaFEMControl.setLNAEnable(enable);
+  loRaFEMControl.setRxModeEnable();
+  return true;
+}
+
+bool T096Board::canControlLoRaFemLna() const {
+  return loRaFEMControl.isLnaCanControl();
+}
+
+bool T096Board::isLoRaFemLnaEnabled() const {
+  return loRaFEMControl.isLNAEnabled();
+}

+ 3 - 0
variants/heltec_t096/T096Board.h

@@ -25,4 +25,7 @@ public:
   uint16_t getBattMilliVolts() override;
   uint16_t getBattMilliVolts() override;
   const char* getManufacturerName() const override ;
   const char* getManufacturerName() const override ;
   void powerOff() override;
   void powerOff() override;
+  bool setLoRaFemLnaEnabled(bool enable) override;
+  bool canControlLoRaFemLna() const override;
+  bool isLoRaFemLnaEnabled() const override;
 };
 };

+ 18 - 0
variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp

@@ -82,3 +82,21 @@ void HeltecTrackerV2Board::begin() {
   const char* HeltecTrackerV2Board::getManufacturerName() const {
   const char* HeltecTrackerV2Board::getManufacturerName() const {
     return "Heltec Tracker V2";
     return "Heltec Tracker V2";
   }
   }
+
+  bool HeltecTrackerV2Board::setLoRaFemLnaEnabled(bool enable) {
+    if (!loRaFEMControl.isLnaCanControl()) {
+      return false;
+    }
+
+    loRaFEMControl.setLNAEnable(enable);
+    loRaFEMControl.setRxModeEnable();
+    return true;
+  }
+
+  bool HeltecTrackerV2Board::canControlLoRaFemLna() const {
+    return loRaFEMControl.isLnaCanControl();
+  }
+
+  bool HeltecTrackerV2Board::isLoRaFemLnaEnabled() const {
+    return loRaFEMControl.isLNAEnabled();
+  }

+ 3 - 0
variants/heltec_tracker_v2/HeltecTrackerV2Board.h

@@ -21,5 +21,8 @@ public:
   void powerOff() override;
   void powerOff() override;
   uint16_t getBattMilliVolts() override;
   uint16_t getBattMilliVolts() override;
   const char* getManufacturerName() const override ;
   const char* getManufacturerName() const override ;
+  bool setLoRaFemLnaEnabled(bool enable) override;
+  bool canControlLoRaFemLna() const override;
+  bool isLoRaFemLnaEnabled() const override;
 
 
 };
 };

+ 2 - 1
variants/heltec_tracker_v2/LoRaFEMControl.h

@@ -12,8 +12,9 @@ class LoRaFEMControl
     void setRxModeEnable(void);
     void setRxModeEnable(void);
     void setRxModeEnableWhenMCUSleep(void);
     void setRxModeEnableWhenMCUSleep(void);
     void setLNAEnable(bool enabled);
     void setLNAEnable(bool enabled);
-    bool isLnaCanControl(void) { return lna_can_control; }
+    bool isLnaCanControl(void) const { return lna_can_control; }
     void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
     void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
+    bool isLNAEnabled(void) const { return lna_enabled; }
 
 
   private:
   private:
     bool lna_enabled = false;
     bool lna_enabled = false;

+ 18 - 0
variants/heltec_v4/HeltecV4Board.cpp

@@ -83,3 +83,21 @@ void HeltecV4Board::begin() {
     return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED";
     return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED";
 #endif
 #endif
   }
   }
+
+  bool HeltecV4Board::setLoRaFemLnaEnabled(bool enable) {
+    if (!loRaFEMControl.isLnaCanControl()) {
+      return false;
+    }
+
+    loRaFEMControl.setLNAEnable(enable);
+    loRaFEMControl.setRxModeEnable();
+    return true;
+  }
+
+  bool HeltecV4Board::canControlLoRaFemLna() const {
+    return loRaFEMControl.isLnaCanControl();
+  }
+
+  bool HeltecV4Board::isLoRaFemLnaEnabled() const {
+    return loRaFEMControl.isLNAEnabled();
+  }

+ 3 - 0
variants/heltec_v4/HeltecV4Board.h

@@ -25,6 +25,9 @@ public:
   void onAfterTransmit(void) override;
   void onAfterTransmit(void) override;
   void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
   void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
   void powerOff() override;
   void powerOff() override;
+  bool setLoRaFemLnaEnabled(bool enable) override;
+  bool canControlLoRaFemLna() const override;
+  bool isLoRaFemLnaEnabled() const override;
   uint16_t getBattMilliVolts() override;
   uint16_t getBattMilliVolts() override;
   bool setAdcMultiplier(float multiplier) override {
   bool setAdcMultiplier(float multiplier) override {
     if (multiplier == 0.0f) {
     if (multiplier == 0.0f) {

+ 2 - 1
variants/heltec_v4/LoRaFEMControl.h

@@ -18,8 +18,9 @@ class LoRaFEMControl
     void setRxModeEnable(void);
     void setRxModeEnable(void);
     void setRxModeEnableWhenMCUSleep(void);
     void setRxModeEnableWhenMCUSleep(void);
     void setLNAEnable(bool enabled);
     void setLNAEnable(bool enabled);
-    bool isLnaCanControl(void) { return lna_can_control; }
+    bool isLnaCanControl(void) const { return lna_can_control; }
     void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
     void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
+    bool isLNAEnabled(void) const { return lna_enabled; }
     LoRaFEMType getFEMType(void) const { return fem_type; }
     LoRaFEMType getFEMType(void) const { return fem_type; }
   private:
   private:
     LoRaFEMType fem_type=OTHER_FEM_TYPES;
     LoRaFEMType fem_type=OTHER_FEM_TYPES;