From c58c9b2f31798b468bad861b021b395ed857c110 Mon Sep 17 00:00:00 2001 From: Adam Gessaman Date: Sat, 8 Aug 2026 14:53:57 -0700 Subject: [PATCH] fix(station-g3): apply FEM PA level at TX start PA PL1 re-targets the PA's DC-DC supply rail rather than selecting a logic-level gain, and the serial CLI is serviced on every main-loop pass regardless of whether a transmit is in flight. A `set radio.fem.txgain` write could therefore move the rail mid-transmit, while the SX1262 was still driving the PA at full input power. Record the requested level in setPAGainEnable() and drive the pin from setTxModeEnable(), which runs from onBeforeTransmit() ahead of startTransmit(). The level only matters while transmitting, so deferring costs nothing. Document that the pref is saved immediately but applied at the next transmit, so `get radio.fem.txgain` can lead the hardware until then. --- docs/cli_commands.md | 1 + variants/station_g3_esp32/LoRaFEMControl.cpp | 14 ++++++++++++-- variants/station_g3_esp32/LoRaFEMControl.h | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 390c8e04..8772b929 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -303,6 +303,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - This controls a software-selectable external LoRa FEM transmit gain where the board supports it. - On Station G3, remove the PA PL1 jumper to allow software control. `on` selects PA PL1 high/short and `off` selects PA PL1 low/open. The PA PL2 hardware jumper determines whether this switches between power levels 1/3 or 2/4. - Select an operating level and SX1262 transmit power that comply with local RF limits and the Station G3 power-supply requirements. +- The setting is saved immediately, but on Station G3 the level is applied to the hardware at the start of the next transmit, so that the PA supply rail is never re-targeted while the PA is being driven. `get` reports the configured state, which may lead the hardware until the node next transmits. --- diff --git a/variants/station_g3_esp32/LoRaFEMControl.cpp b/variants/station_g3_esp32/LoRaFEMControl.cpp index 04ac2ff8..8d4f90f6 100644 --- a/variants/station_g3_esp32/LoRaFEMControl.cpp +++ b/variants/station_g3_esp32/LoRaFEMControl.cpp @@ -7,7 +7,7 @@ void LoRaFEMControl::init() { #ifdef P_PA1_EN rtc_gpio_hold_dis((gpio_num_t)P_PA1_EN); pinMode(P_PA1_EN, OUTPUT); - setPAGainEnable(pa_gain_enabled); + applyPAGain(); #endif #ifdef P_PRIMARY_LNA_EN @@ -29,6 +29,10 @@ void LoRaFEMControl::setSleepModeEnable() { } void LoRaFEMControl::setTxModeEnable() { + // Latch the requested PA level here, before the SX1262 starts driving the PA. PA PL1 + // retargets the PA's DC-DC rail, so moving it mid-transmit collapses the supply while + // the PA is still driven at full input power. + applyPAGain(); #ifdef P_PRIMARY_LNA_EN digitalWrite(P_PRIMARY_LNA_EN, !P_PRIMARY_LNA_EN_ACTIVE); #endif @@ -46,9 +50,15 @@ void LoRaFEMControl::setLNAEnable(bool enabled) { } void LoRaFEMControl::setPAGainEnable(bool enabled) { + // Recorded only -- the pin is driven from setTxModeEnable(). The PA level only matters + // while transmitting, so deferring costs nothing and keeps the rail change out of an + // in-flight transmit (the CLI runs on every main-loop pass, including mid-TX). pa_gain_enabled = enabled; +} + +void LoRaFEMControl::applyPAGain() { #ifdef P_PA1_EN - digitalWrite(P_PA1_EN, enabled ? P_PA1_EN_ACTIVE : !P_PA1_EN_ACTIVE); + digitalWrite(P_PA1_EN, pa_gain_enabled ? P_PA1_EN_ACTIVE : !P_PA1_EN_ACTIVE); #endif } diff --git a/variants/station_g3_esp32/LoRaFEMControl.h b/variants/station_g3_esp32/LoRaFEMControl.h index 429d6127..f622de91 100644 --- a/variants/station_g3_esp32/LoRaFEMControl.h +++ b/variants/station_g3_esp32/LoRaFEMControl.h @@ -15,6 +15,8 @@ public: bool isPAGainEnabled() const { return pa_gain_enabled; } private: + void applyPAGain(); + bool lna_enabled = true; bool pa_gain_enabled = false; };