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
7 changes: 7 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,13 @@ void loop()
lastRadioMissedIrqPoll = millis();
RadioLibInterface::instance->pollMissedIrqs();
}

// Periodic AGC reset — warm sleep + recalibrate to prevent stuck AGC gain
static uint32_t lastAgcReset;
if (!Throttle::isWithinTimespanMs(lastAgcReset, AGC_RESET_INTERVAL_MS)) {
lastAgcReset = millis();
RadioLibInterface::instance->resetAGC();
}
}

#ifdef DEBUG_STACK
Expand Down
32 changes: 32 additions & 0 deletions src/mesh/LR11x0Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,38 @@ template <typename T> bool LR11x0Interface<T>::isActivelyReceiving()
RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED);
}

#ifdef LR11X0_AGC_RESET
template <typename T> void LR11x0Interface<T>::resetAGC()
{
// Safety: don't reset mid-packet
if (sendingPacket != NULL || (isReceiving && isActivelyReceiving()))
return;

LOG_DEBUG("LR11x0 AGC reset: warm sleep + Calibrate(0x3F)");

// 1. Warm sleep — powers down the analog frontend, resetting AGC state
lora.sleep(true, 0);

// 2. Wake to RC standby for stable calibration
lora.standby(RADIOLIB_LR11X0_STANDBY_RC, true);

// 3. Calibrate all blocks (PLL, ADC, image, RC oscillators)
// calibrate() is protected on LR11x0, so use raw SPI (same as internal implementation)
uint8_t calData = RADIOLIB_LR11X0_CALIBRATE_ALL;
module.SPIwriteStream(RADIOLIB_LR11X0_CMD_CALIBRATE, &calData, 1, true, true);

// 4. Re-calibrate image rejection for actual operating frequency
// Calibrate(0x3F) defaults to 902-928 MHz which is wrong for other regions.
lora.calibrateImageRejection(getFreq() - 4.0f, getFreq() + 4.0f);

// 5. Re-apply RX boosted gain mode
lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);

// 6. Resume receiving
startReceive();
}
#endif

template <typename T> bool LR11x0Interface<T>::sleep()
{
// \todo Display actual typename of the adapter, not just `LR11x0`
Expand Down
4 changes: 4 additions & 0 deletions src/mesh/LR11x0Interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ template <class T> class LR11x0Interface : public RadioLibInterface

bool isIRQPending() override { return lora.getIrqFlags() != 0; }

#ifdef LR11X0_AGC_RESET
void resetAGC() override;
#endif

protected:
/**
* Specific module instance
Expand Down
5 changes: 5 additions & 0 deletions src/mesh/RadioLibInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,11 @@ void RadioLibInterface::pollMissedIrqs()
}
}

void RadioLibInterface::resetAGC()
{
// Base implementation: no-op. Override in chip-specific subclasses.
}

void RadioLibInterface::checkRxDoneIrqFlag()
{
if (iface->checkIrq(RADIOLIB_IRQ_RX_DONE)) {
Expand Down
9 changes: 9 additions & 0 deletions src/mesh/RadioLibInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
// In addition to the default Rx flags, we need the PREAMBLE_DETECTED flag to detect whether we are actively receiving
#define MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS (RADIOLIB_IRQ_RX_DEFAULT_FLAGS | (1 << RADIOLIB_IRQ_PREAMBLE_DETECTED))

#define AGC_RESET_INTERVAL_MS (60 * 1000) // 60 seconds

/**
* We need to override the RadioLib ArduinoHal class to add mutex protection for SPI bus access
*/
Expand Down Expand Up @@ -117,6 +119,13 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
void pollMissedIrqs();

/**
* Reset AGC by power-cycling the analog frontend.
* Subclasses override with chip-specific calibration sequences.
* Safe to call periodically — skips if currently sending or receiving.
*/
virtual void resetAGC();

/**
* Debugging counts
*/
Expand Down
55 changes: 55 additions & 0 deletions src/mesh/SX126xInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,61 @@ template <typename T> bool SX126xInterface<T>::sleep()
return true;
}

template <typename T> void SX126xInterface<T>::resetAGC()
{
// Safety: don't reset mid-packet
if (sendingPacket != NULL || (isReceiving && isActivelyReceiving()))
return;

LOG_DEBUG("SX126x AGC reset: warm sleep + Calibrate(0x7F)");

// 1. Warm sleep — powers down the entire analog frontend, resetting AGC state.
// A plain standby→startReceive cycle does NOT reset the AGC.
lora.sleep(true);

// 2. Wake to RC standby for stable calibration
lora.standby(RADIOLIB_SX126X_STANDBY_RC, true);

// 3. Calibrate all blocks (ADC, PLL, image, RC oscillators)
uint8_t calData = RADIOLIB_SX126X_CALIBRATE_ALL;
module.SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, &calData, 1, true, false);

// 4. Wait for calibration to complete (BUSY pin goes low)
module.hal->delay(5);
uint32_t start = millis();
while (module.hal->digitalRead(module.getGpio())) {
if (millis() - start > 50)
break;
module.hal->yield();
}

if (module.hal->digitalRead(module.getGpio())) {
LOG_WARN("SX126x AGC reset: calibration did not complete within 50ms");
startReceive();
return;
}

// 5. Re-calibrate image rejection for actual operating frequency
// Calibrate(0x7F) defaults to 902-928 MHz which is wrong for other regions.
lora.calibrateImage(getFreq());

// Re-apply settings that calibration may have reset

// DIO2 as RF switch
#ifdef SX126X_DIO2_AS_RF_SWITCH
lora.setDio2AsRfSwitch(true);
#elif defined(ARCH_PORTDUINO)
if (portduino_config.dio2_as_rf_switch)
lora.setDio2AsRfSwitch(true);
#endif

// RX boosted gain mode
lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);

// 6. Resume receiving
startReceive();
}

/** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */
template <typename T> void SX126xInterface<T>::setTransmitEnable(bool txon)
{
Expand Down
2 changes: 2 additions & 0 deletions src/mesh/SX126xInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ template <class T> class SX126xInterface : public RadioLibInterface

bool isIRQPending() override { return lora.getIrqFlags() != 0; }

void resetAGC() override;

void setTCXOVoltage(float voltage) { tcxoVoltage = voltage; }

protected:
Expand Down
Loading