Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,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

#### View or change this node's name
Expand Down
2 changes: 2 additions & 0 deletions examples/companion_radio/DataStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no
file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
file.read((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88
file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89
file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 90

file.close();
}
Expand Down Expand Up @@ -269,6 +270,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
file.write((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88
file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89
file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 90

file.close();
}
Expand Down
31 changes: 30 additions & 1 deletion examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
#define CMD_SET_CUSTOM_VAR 41
#define CMD_GET_ADVERT_PATH 42
#define CMD_GET_TUNING_PARAMS 43
// NOTE: CMD range 44..49 parked, potentially for WiFi operations
#define CMD_GET_RADIO_FEM_RXGAIN 44
#define CMD_SET_RADIO_FEM_RXGAIN 45
// NOTE: CMD range 46..49 parked, potentially for WiFi operations
#define CMD_SEND_BINARY_REQ 50
#define CMD_FACTORY_RESET 51
#define CMD_SEND_PATH_DISCOVERY_REQ 52
Expand Down Expand Up @@ -828,6 +830,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.rx_boosted_gain = 1; // enabled by default
#endif
#endif
_prefs.radio_fem_rxgain = 1;
}

void MyMesh::begin(bool has_display) {
Expand Down Expand Up @@ -866,6 +869,7 @@ void MyMesh::begin(bool has_display) {
_prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER);
_prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1
_prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours
_prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1);

#ifdef BLE_PIN_CODE // 123456 by default
if (_prefs.ble_pin == 0) {
Expand Down Expand Up @@ -895,6 +899,7 @@ void MyMesh::begin(bool has_display) {
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
}
Expand Down Expand Up @@ -1697,6 +1702,30 @@ void MyMesh::handleCmdFrame(size_t len) {
} else {
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
}
} else if (cmd_frame[0] == CMD_GET_RADIO_FEM_RXGAIN) {
if (!board.canControlLoRaFemLna()) {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
} else {
out_frame[0] = RESP_CODE_OK;
uint32_t value = board.isLoRaFemLnaEnabled() ? 1 : 0;
memcpy(&out_frame[1], &value, 4);
_serial->writeFrame(out_frame, 5);
}
} else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) {
uint8_t value = cmd_frame[1];
if (!board.canControlLoRaFemLna()) {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
} else if (value <= 1) {
_prefs.radio_fem_rxgain = value;
if (board.setLoRaFemLnaEnabled(value != 0)) {
savePrefs();
writeOKFrame();
} else {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
}
} else {
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
}
} else if (cmd_frame[0] == CMD_GET_ADVERT_PATH && len >= PUB_KEY_SIZE+2) {
// FUTURE use: uint8_t reserved = cmd_frame[1];
uint8_t *pub_key = &cmd_frame[2];
Expand Down
3 changes: 2 additions & 1 deletion examples/companion_radio/NodePrefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ struct NodePrefs { // persisted to file
uint32_t gps_interval; // GPS read interval in seconds
uint8_t autoadd_config; // bitmask for auto-add contacts config
uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting
uint8_t client_repeat;
uint8_t path_hash_mode; // which path mode to use when sending
uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64)
};
};
2 changes: 2 additions & 0 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.rx_boosted_gain = 1; // enabled by default;
#endif
#endif
_prefs.radio_fem_rxgain = 1;

pending_discover_tag = 0;
pending_discover_until = 0;
Expand All @@ -922,6 +923,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);

updateAdvertTimer();
updateFloodAdvertTimer();
Expand Down
3 changes: 2 additions & 1 deletion examples/simple_repeater/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi
}

// v1.2.3 (1 Jan 2025)
sprintf(_version_info, "%s (%s)", version, build_date);
snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date);
free(version);
}

void UITask::renderCurrScreen() {
Expand Down
2 changes: 2 additions & 0 deletions examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.gps_enabled = 0;
_prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
_prefs.radio_fem_rxgain = 1;

next_post_idx = 0;
next_client_idx = 0;
Expand All @@ -649,6 +650,7 @@ void MyMesh::begin(FILESYSTEM *fs) {

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);

updateAdvertTimer();
updateFloodAdvertTimer();
Expand Down
3 changes: 2 additions & 1 deletion examples/simple_room_server/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi
}

// v1.2.3 (1 Jan 2025)
sprintf(_version_info, "%s (%s)", version, build_date);
snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date);
free(version);
}

void UITask::renderCurrScreen() {
Expand Down
2 changes: 2 additions & 0 deletions examples/simple_sensor/SensorMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
_prefs.gps_enabled = 0;
_prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
_prefs.radio_fem_rxgain = 1;
}

void SensorMesh::begin(FILESYSTEM* fs) {
Expand All @@ -741,6 +742,7 @@ void SensorMesh::begin(FILESYSTEM* fs) {

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);

updateAdvertTimer();
updateFloodAdvertTimer();
Expand Down
3 changes: 2 additions & 1 deletion examples/simple_sensor/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi
}

// v1.2.3 (1 Jan 2025)
sprintf(_version_info, "%s (%s)", version, build_date);
snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date);
free(version);
}

void UITask::renderCurrScreen() {
Expand Down
5 changes: 4 additions & 1 deletion src/MeshCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class MainBoard {
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; }

// Power management interface (boards with power management override these)
virtual bool isExternalPowered() { return false; }
Expand Down Expand Up @@ -100,4 +103,4 @@ class RTCClock {
}
};

}
}
39 changes: 36 additions & 3 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 291
// next: 292

// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
Expand Down Expand Up @@ -118,6 +119,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {

// sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
_prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean

file.close();
}
Expand Down Expand Up @@ -179,7 +181,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 291
// next: 292

file.close();
}
Expand Down Expand Up @@ -327,6 +330,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else if (memcmp(config, "radio.rxgain", 12) == 0) {
sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off");
#endif
} else if (memcmp(config, "radio.fem.rxgain", 16) == 0) {
if (!_board->canControlLoRaFemLna()) {
strcpy(reply, "Error: unsupported by this board");
} else {
sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off");
}
} else if (memcmp(config, "radio", 5) == 0) {
char freq[16], bw[16];
strcpy(freq, StrHelper::ftoa(_prefs->freq));
Expand Down Expand Up @@ -520,13 +529,37 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_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");
#if defined(USE_SX1262) || defined(USE_SX1268)
} else if (memcmp(config, "radio.rxgain ", 13) == 0) {
#if defined(USE_SX1262) || defined(USE_SX1268)
_prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0;
strcpy(reply, "OK");
savePrefs();
_callbacks->setRxBoostedGain(_prefs->rx_boosted_gain);
#else
strcpy(reply, "Error: unsupported by this board");
#endif
} else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) {
if (!_board->canControlLoRaFemLna()) {
strcpy(reply, "Error: unsupported by this board");
} 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) {
strcpy(tmp, &config[6]);
const char *parts[4];
Expand Down
1 change: 1 addition & 0 deletions src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct NodePrefs { // persisted to file
float adc_multiplier;
char owner_info[120];
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 loop_detect;
};
Expand Down
3 changes: 2 additions & 1 deletion variants/heltec_t096/LoRaFEMControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ class LoRaFEMControl
void setRxModeEnable(void);
void setRxModeEnableWhenMCUSleep(void);
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; }
bool isLNAEnabled(void) const { return lna_enabled; }

private:
bool lna_enabled = false;
Expand Down
20 changes: 19 additions & 1 deletion variants/heltec_t096/T096Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,22 @@ void T096Board::powerOff() {

const char* T096Board::getManufacturerName() const {
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 changes: 3 additions & 0 deletions variants/heltec_t096/T096Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ class T096Board : public NRF52BoardDCDC {
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ;
void powerOff() override;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
};
18 changes: 18 additions & 0 deletions variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,21 @@ void HeltecTrackerV2Board::begin() {
const char* HeltecTrackerV2Board::getManufacturerName() const {
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 changes: 3 additions & 0 deletions variants/heltec_tracker_v2/HeltecTrackerV2Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@ class HeltecTrackerV2Board : public ESP32Board {
void powerOff() override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;

};
3 changes: 2 additions & 1 deletion variants/heltec_tracker_v2/LoRaFEMControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ class LoRaFEMControl
void setRxModeEnable(void);
void setRxModeEnableWhenMCUSleep(void);
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; }
bool isLNAEnabled(void) const { return lna_enabled; }

private:
bool lna_enabled = false;
Expand Down
Loading
Loading