Skip to content
Merged
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
6 changes: 6 additions & 0 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,9 @@ This document provides an overview of CLI commands that can be sent to MeshCore
#### View or change RX duty-cycle power saving
**Usage:**
- `get radio.rxps`
- `get radio.rxps.rfrx_disabled`
- `get rxps.wd`
- `set radio.rxps.rfrx_disabled <state>`
- `set radio.rxps off`
- `set radio.rxps on`
- `set radio.rxps conservative`
Expand All @@ -294,9 +296,13 @@ This document provides an overview of CLI commands that can be sent to MeshCore
- `rx_us`, `sleep_us`: Receive and sleep durations in microseconds (`1000`-`30000000`).
- `level`: A power-saving level from `1` (most conservative) to `10` (least power saving).
- `preamble`: LoRa preamble length in symbols; `16` or `32`.
- `state`: `on` or `off`.

**Notes:**
- `get rxps.wd` reports the RXPS watchdog's soft and hard recovery counts.
- `radio.rxps.rfrx_disabled` is a runtime-only diagnostic setting and resets to `off` after reboot.
- Its default `off` state keeps the host-controlled SX1262 receive path enabled during RX duty-cycle mode. Setting it to `on` reproduces the old missing-RF_RX behavior and can significantly reduce receive sensitivity, making remote commands harder to receive.
- `radio.rxps.rfrx_disabled` is supported only on SX1262 targets with a host-controlled RX enable pin.
- `on` and `conservative` select level `1` with a 16-symbol preamble; `balanced` selects level `5` with a 16-symbol preamble.
- Level-based settings automatically recalculate their timings when the spreading factor or bandwidth changes. Custom `<rx_us> <sleep_us>` timings remain fixed.
- The selected mode is applied immediately, persisted, and restored after reboot.
Expand Down
17 changes: 17 additions & 0 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,23 @@ bool MyMesh::setRxPowerSaving(bool enable, uint32_t rx_us, uint32_t sleep_us) {
ok ? "" : " unsupported");
return ok;
}

bool MyMesh::supportsRxPowerSavingRfRxDisable() const {
return radio_driver.supportsRxPowerSavingRfRxDisable();
}

bool MyMesh::setRxPowerSavingRfRxDisabled(bool disabled) {
bool ok = radio_driver.setRxPowerSavingRfRxDisabled(disabled);
MESH_DEBUG_PRINTLN("RX Power Saving RF_RX control: %s, %s",
disabled ? "disabled" : "enabled",
ok ? "accepted" : "unsupported");
return ok;
}

bool MyMesh::isRxPowerSavingRfRxDisabled() const {
return radio_driver.isRxPowerSavingRfRxDisabled();
}

void MyMesh::getRxPsWatchdogCounts(uint32_t* soft, uint32_t* hard) {
*soft = radio_driver.getRxPsWatchdogSoftCount();
*hard = radio_driver.getRxPsWatchdogHardCount();
Expand Down
3 changes: 3 additions & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
void dumpLogFile() override;
void setTxPower(int8_t power_dbm) override;
bool setRxPowerSaving(bool enable, uint32_t rx_us, uint32_t sleep_us) override;
bool supportsRxPowerSavingRfRxDisable() const override;
bool setRxPowerSavingRfRxDisabled(bool disabled) override;
bool isRxPowerSavingRfRxDisabled() const override;
void getRxPsWatchdogCounts(uint32_t* soft, uint32_t* hard) override;
void formatNeighborsReply(char *reply) override;
void removeNeighbor(const uint8_t* pubkey, int key_len) override;
Expand Down
23 changes: 23 additions & 0 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,23 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
strcpy(reply, "Error: state must be on or off");
}
} else if (strncmp(config, "radio.rxps.rfrx_disabled ", 25) == 0) {
const char* value = &config[25];
bool disabled;
if (strcmp(value, "on") == 0) {
disabled = true;
} else if (strcmp(value, "off") == 0) {
disabled = false;
} else {
strcpy(reply, "Error: state must be on or off");
return;
}

if (!_callbacks->setRxPowerSavingRfRxDisabled(disabled)) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "OK - radio.rxps.rfrx_disabled %s", disabled ? "on" : "off");
}
} else if (memcmp(config, "radio.rxps ", 11) == 0) { // RX PowerSaving
const char* value = &config[11];
uint8_t enable = _prefs->rx_powersaving_enabled;
Expand Down Expand Up @@ -1054,6 +1071,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off");
}
} else if (strcmp(config, "radio.rxps.rfrx_disabled") == 0) {
if (!_callbacks->supportsRxPowerSavingRfRxDisable()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", _callbacks->isRxPowerSavingRfRxDisabled() ? "on" : "off");
}
} else if (memcmp(config, "radio.rxps", 10) == 0) { // RX PowerSaving
ensureRxPowerSavingDefaults(&_prefs->rx_ps_rx_us, &_prefs->rx_ps_sleep_us);
sprintf(reply, "> %s,%lu,%lu", _prefs->rx_powersaving_enabled ? "on" : "off",
Expand Down
7 changes: 7 additions & 0 deletions src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ class CommonCLICallbacks {
return !enable;
};

virtual bool supportsRxPowerSavingRfRxDisable() const { return false; }
virtual bool setRxPowerSavingRfRxDisabled(bool disabled) {
(void)disabled;
return false;
}
virtual bool isRxPowerSavingRfRxDisabled() const { return false; }

virtual void getRxPsWatchdogCounts(uint32_t* soft, uint32_t* hard) {
*soft = 0; *hard = 0;
};
Expand Down
25 changes: 24 additions & 1 deletion src/helpers/radiolib/CustomSX1262.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define SX126X_IRQ_PREAMBLE_DETECTED 0x04

class CustomSX1262 : public SX1262 {
bool _rx_ps_rf_rx_disabled = false;

public:
CustomSX1262(Module *mod) : SX1262(mod) { }

Expand Down Expand Up @@ -86,6 +88,27 @@ class CustomSX1262 : public SX1262 {
return true; // success
}

int16_t startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod,
RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS,
RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK) {
int16_t state = SX1262::startReceiveDutyCycle(rxPeriod, sleepPeriod, irqFlags, irqMask);
if (state == RADIOLIB_ERR_NONE && !_rx_ps_rf_rx_disabled) {
// RadioLib stages RX duty-cycle through standby, which leaves a
// host-controlled RXEN switch in IDLE. Keep the receive path enabled
// while the SX1262 alternates between its RX and sleep windows.
this->mod->setRfSwitchState(Module::MODE_RX);
}
return state;
}

void setRxPowerSavingRfRxDisabled(bool disabled) {
_rx_ps_rf_rx_disabled = disabled;
}

bool isRxPowerSavingRfRxDisabled() const {
return _rx_ps_rf_rx_disabled;
}

// BUSY high means the chip is asleep (RX duty-cycle sleep window) or mid
// command; any SPI access would stall until the chip's next listen window.
bool isChipBusy() {
Expand Down Expand Up @@ -124,4 +147,4 @@ class CustomSX1262 : public SX1262 {
readRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1);
return (rxGain == RADIOLIB_SX126X_RX_GAIN_BOOSTED);
}
};
};
23 changes: 23 additions & 0 deletions src/helpers/radiolib/CustomSX1262Wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@ class CustomSX1262Wrapper : public RadioLibWrapper {

bool supportsRxPowerSaving() const override { return true; }

bool supportsRxPowerSavingRfRxDisable() const override {
#if defined(SX126X_RXEN)
return SX126X_RXEN != RADIOLIB_NC;
#else
return false;
#endif
}

bool setRxPowerSavingRfRxDisabled(bool disabled) override {
if (!supportsRxPowerSavingRfRxDisable()) return false;

if (_rx_ps_armed) stopReceiveDutyCycle();
((CustomSX1262 *)_radio)->setRxPowerSavingRfRxDisabled(disabled);

// Re-arm the configured receive mode immediately without disturbing an
// unread RX interrupt or an in-flight transmission.
return setRxPowerSaving(_rx_ps_enabled, _rx_ps_rx_us, _rx_ps_sleep_us);
}

bool isRxPowerSavingRfRxDisabled() const override {
return ((CustomSX1262 *)_radio)->isRxPowerSavingRfRxDisabled();
}

protected:
int startReceiveMode() override {
if (_rx_ps_armed) {
Expand Down
3 changes: 3 additions & 0 deletions src/helpers/radiolib/RadioLibWrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ class RadioLibWrapper : public mesh::Radio {
void onSendFinished() override;
bool isInRecvMode() const override;
bool setRxPowerSaving(bool enabled, uint32_t rx_us, uint32_t sleep_us) override;
virtual bool supportsRxPowerSavingRfRxDisable() const { return false; }
virtual bool setRxPowerSavingRfRxDisabled(bool) { return false; }
virtual bool isRxPowerSavingRfRxDisabled() const { return false; }
bool isChannelActive();

bool isReceiving() override {
Expand Down
Loading