Fix RX_EN handling during RX power saving

This commit is contained in:
Jarosław Domański committed 2026-08-10 14:50:34 +02:00
1 parent a1d632d16c
commit fc660cc061
8 files changed
+106 -1

No files matched your search

+6
View File
@@ -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`
@@ -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.
+17
View File
@@ -1076,6 +1076,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();
+3
View File
@@ -220,6 +220,9 @@ public:
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;
+23
View File
@@ -761,6 +761,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) {
const char* value = &config[11];
uint8_t enable = _prefs->rx_powersaving_enabled;
@@ -1121,6 +1138,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) {
ensureRxPowerSavingDefaults(_prefs);
sprintf(reply, "> %s,%lu,%lu", _prefs->rx_powersaving_enabled ? "on" : "off",
+7
View File
@@ -137,6 +137,13 @@ public:
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;
};
+24 -1
View File
@@ -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) { }
@@ -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() {
@@ -124,4 +147,4 @@ class CustomSX1262 : public SX1262 {
readRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1);
return (rxGain == RADIOLIB_SX126X_RX_GAIN_BOOSTED);
}
};
};
@@ -41,6 +41,29 @@ public:
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) {
+3
View File
@@ -101,6 +101,9 @@ public:
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 {