From 33e0ef4be8b5ba25b265fc723ac525d9e32bd1fe Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Wed, 25 Feb 2026 01:07:19 +0100 Subject: [PATCH 1/5] Fix RAK13302 1W FEM control: drive SKY66122-11 CSD/CPS/CTX directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RAK13302's SKY66122-11 FEM has three control pins (CSD, CPS, CTX) that must be actively driven by the MCU. The existing code only managed CPS via SX126X_POWER_EN and relied on DIO2 for TX/RX switching, but R25 (DIO2→CTX) is NC by default, leaving CSD floating and CTX undriven. This put the FEM in an undefined state — the PA wouldn't reliably engage during TX and the 16 dB RX LNA was never active. Add USE_SKY66122_FEM support (following the existing USE_GC1109_PA pattern) to explicitly initialize, sleep, and toggle all three FEM pins for correct TX/RX/shutdown modes. --- src/mesh/SX126xInterface.cpp | 27 ++++++++++++++++++++++- variants/nrf52840/rak3401_1watt/variant.h | 19 ++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index a9ee16545e5..62c4548c59b 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -93,6 +93,18 @@ template bool SX126xInterface::init() digitalWrite(LORA_PA_TX_EN, LOW); // Start in RX-ready state #endif +#if defined(USE_SKY66122_FEM) + // SKY66122-11 FEM initialization — start in RX mode, boost off + // See variant.h for full pin mapping and control logic documentation + pinMode(SKY66122_CSD, OUTPUT); + digitalWrite(SKY66122_CSD, HIGH); // Enable FEM + pinMode(SKY66122_CPS, OUTPUT); + digitalWrite(SKY66122_CPS, HIGH); // Active path (required for both TX and RX) + pinMode(SKY66122_CTX, OUTPUT); + digitalWrite(SKY66122_CTX, LOW); // Boost off, RX mode + delay(1); // Settling time +#endif + #ifdef RF95_FAN_EN digitalWrite(RF95_FAN_EN, HIGH); pinMode(RF95_FAN_EN, OUTPUT); @@ -431,6 +443,13 @@ template bool SX126xInterface::sleep() digitalWrite(LORA_PA_EN, LOW); digitalWrite(LORA_PA_TX_EN, LOW); #endif + +#if defined(USE_SKY66122_FEM) + // Full shutdown — all pins LOW, FEM draws <1 μA + digitalWrite(SKY66122_CTX, LOW); + digitalWrite(SKY66122_CPS, LOW); + digitalWrite(SKY66122_CSD, LOW); +#endif return true; } @@ -489,7 +508,7 @@ template void SX126xInterface::resetAGC() startReceive(); } -/** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */ +/** Control PA mode for GC1109 / SKY66122 FEM - selects TX path (txon=true) or RX path (txon=false) */ template void SX126xInterface::setTransmitEnable(bool txon) { #if defined(USE_GC1109_PA) @@ -497,6 +516,12 @@ template void SX126xInterface::setTransmitEnable(bool txon) digitalWrite(LORA_PA_EN, HIGH); // CSD=1: Chip enabled digitalWrite(LORA_PA_TX_EN, txon ? 1 : 0); // CPS: 1=full PA, 0=bypass (for RX, CPS is don't care) #endif + +#if defined(USE_SKY66122_FEM) + digitalWrite(SKY66122_CSD, HIGH); // Ensure FEM is enabled + digitalWrite(SKY66122_CPS, HIGH); // Enable active mode (TX and RX) + digitalWrite(SKY66122_CTX, txon ? 1 : 0); // HIGH=TX (boost on, PA), LOW=RX (boost off, LNA) +#endif } #endif \ No newline at end of file diff --git a/variants/nrf52840/rak3401_1watt/variant.h b/variants/nrf52840/rak3401_1watt/variant.h index 80b09cf695f..fc4ca5ba4a5 100644 --- a/variants/nrf52840/rak3401_1watt/variant.h +++ b/variants/nrf52840/rak3401_1watt/variant.h @@ -158,8 +158,23 @@ static const uint8_t SCK = PIN_SPI_SCK; #define SX126X_BUSY (9) #define SX126X_RESET (4) -#define SX126X_POWER_EN (21) -// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +/* + * SKY66122-11 Front-End Module control (RAK13302 1W module) + * + * CSD (P0.24) — Chip enable: HIGH to power up the FEM, LOW for shutdown (<1 μA) + * CPS (P0.21) — Path select: HIGH = active (required for TX and RX), LOW = shutdown + * CTX (P0.31) — TX/RX select + 5 V boost enable: + * HIGH = TX mode (boost on, PA energized at 5 V) + * LOW = RX mode (boost off, LNA on 3.3 V) + * + * R25 (DIO2→CTX) is NC by default on the RAK13302, so the MCU must drive CTX directly. + */ +#define USE_SKY66122_FEM +#define SKY66122_CSD (24) +#define SKY66122_CPS (21) +#define SKY66122_CTX (31) + +// DIO2 configures the SX1262 internal RF switch (harmless with R25 NC) #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8 From 991ad9243237bc4dd0df3396cb1c3bc9d0eb40e2 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Thu, 26 Feb 2026 09:54:19 +0100 Subject: [PATCH 2/5] Set CTX low first --- src/mesh/SX126xInterface.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 62c4548c59b..fbc3d8c32cc 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -96,13 +96,15 @@ template bool SX126xInterface::init() #if defined(USE_SKY66122_FEM) // SKY66122-11 FEM initialization — start in RX mode, boost off // See variant.h for full pin mapping and control logic documentation + // Drive CTX LOW first to prevent transient TX mode (Mode 2) while CSD/CPS + // are being enabled — the RAK13302 has no pull-downs on these pins. + pinMode(SKY66122_CTX, OUTPUT); + digitalWrite(SKY66122_CTX, LOW); // Boost off, RX mode pinMode(SKY66122_CSD, OUTPUT); digitalWrite(SKY66122_CSD, HIGH); // Enable FEM pinMode(SKY66122_CPS, OUTPUT); digitalWrite(SKY66122_CPS, HIGH); // Active path (required for both TX and RX) - pinMode(SKY66122_CTX, OUTPUT); - digitalWrite(SKY66122_CTX, LOW); // Boost off, RX mode - delay(1); // Settling time + delay(1); // Settling time #endif #ifdef RF95_FAN_EN From 2bd66afc51857e064a280a9ebff687a103ccdb97 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Thu, 26 Feb 2026 09:58:39 +0100 Subject: [PATCH 3/5] Also apply 0x8B5 register patch for SKY 66122-11 FEM --- src/mesh/SX126xInterface.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index fbc3d8c32cc..90cabc4dbc2 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -208,14 +208,14 @@ template bool SX126xInterface::init() LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d", result); } -#ifdef USE_GC1109_PA +#if defined(USE_GC1109_PA) || defined(USE_SKY66122_FEM) // Undocumented SX1262 register patch recommended by Heltec/Semtech for improved RX sensitivity - // on boards with the GC1109 FEM. Sets bit 0 of register 0x8B5. + // on boards with an external FEM (GC1109, SKY66122). Sets bit 0 of register 0x8B5. // Reference: https://github.com/meshcore-dev/MeshCore/pull/1398 if (module.SPIsetRegValue(0x8B5, 0x01, 0, 0) == RADIOLIB_ERR_NONE) { - LOG_INFO("Applied SX1262 register 0x8B5 patch for GC1109 RX improvement"); + LOG_INFO("Applied SX1262 register 0x8B5 patch for FEM RX improvement"); } else { - LOG_WARN("Failed to apply SX1262 register 0x8B5 patch for GC1109"); + LOG_WARN("Failed to apply SX1262 register 0x8B5 patch"); } #endif From 1998a0b75516de1c520338babfc1385c32903b99 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Thu, 26 Feb 2026 13:05:45 +0100 Subject: [PATCH 4/5] Unify FEM logic --- src/configuration.h | 6 - src/mesh/SX126xInterface.cpp | 124 +++++++++--------- src/sleep.cpp | 26 ++-- variants/esp32s3/heltec_v4/variant.h | 14 +- .../heltec_wireless_tracker_v2/variant.h | 16 ++- variants/nrf52840/rak3401_1watt/variant.h | 9 +- 6 files changed, 100 insertions(+), 95 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index 53ae30d51d2..1824f018813 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -143,12 +143,6 @@ along with this program. If not, see . #define SX126X_MAX_POWER 22 #endif -#ifdef USE_GC1109_PA -// Power Amps are often non-linear, so we can use an array of values for the power curve -#define NUM_PA_POINTS 22 -#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 -#endif - #ifdef RAK13302 #define NUM_PA_POINTS 22 #define TX_GAIN_LORA 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8 diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 90cabc4dbc2..7c6fd78ae9e 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -6,7 +6,7 @@ #ifdef ARCH_PORTDUINO #include "PortduinoGlue.h" #endif -#if defined(USE_GC1109_PA) && defined(ARCH_ESP32) +#if defined(USE_LORA_FEM) && defined(ARCH_ESP32) #include #include #endif @@ -56,55 +56,53 @@ template bool SX126xInterface::init() pinMode(SX126X_POWER_EN, OUTPUT); #endif -#if defined(USE_GC1109_PA) - // GC1109 FEM chip initialization +#if defined(USE_LORA_FEM) + // FEM chip initialization (GC1109 / SKY66122-11) // See variant.h for full pin mapping and control logic documentation // - // On deep sleep wake, PA_POWER and PA_EN are held HIGH by RTC latch (set in - // enableLoraInterrupt). We configure GPIO registers before releasing the hold - // so the pad transitions atomically from held-HIGH to register-HIGH with no - // power glitch. On cold boot the hold_dis is a harmless no-op. - - // VFEM_Ctrl (LORA_PA_POWER): Power enable for GC1109 LDO (always on) - pinMode(LORA_PA_POWER, OUTPUT); - digitalWrite(LORA_PA_POWER, HIGH); - rtc_gpio_hold_dis((gpio_num_t)LORA_PA_POWER); - - // TLV75733P LDO has ~550us startup time (datasheet tSTR). On cold boot, wait - // for VBAT to stabilise before driving CSD/CPS, per GC1109 requirement: - // "VBAT must be prior to CSD/CPS/CTX for the power on sequence" - // On deep sleep wake the LDO was held on via RTC latch, so no delay needed. + // On deep sleep wake (ESP32 with LORA_FEM_POWER), power and CSD are held HIGH + // by RTC latch. We configure GPIO registers before releasing the hold so the + // pad transitions atomically from held-HIGH to register-HIGH with no glitch. + +#ifdef LORA_FEM_POWER + pinMode(LORA_FEM_POWER, OUTPUT); + digitalWrite(LORA_FEM_POWER, HIGH); +#if SOC_RTCIO_HOLD_SUPPORTED + rtc_gpio_hold_dis((gpio_num_t)LORA_FEM_POWER); +#endif + // LDO startup delay — on cold boot, wait for supply to stabilise before + // driving CSD/CPS/CTX. On deep sleep wake the LDO was held on via RTC latch. #if defined(ARCH_ESP32) - if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_UNDEFINED) { + if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_UNDEFINED) delayMicroseconds(1000); - } #else delayMicroseconds(1000); #endif +#endif - // CSD (LORA_PA_EN): Chip enable - must be HIGH to enable GC1109 for both RX and TX - pinMode(LORA_PA_EN, OUTPUT); - digitalWrite(LORA_PA_EN, HIGH); - rtc_gpio_hold_dis((gpio_num_t)LORA_PA_EN); +#ifdef LORA_FEM_CTX + // MCU-driven CTX: drive LOW first to prevent transient TX mode while + // CSD/CPS are being enabled (no pull-downs on RAK13302). + pinMode(LORA_FEM_CTX, OUTPUT); + digitalWrite(LORA_FEM_CTX, LOW); +#endif - // CPS (LORA_PA_TX_EN): PA mode select - HIGH enables full PA during TX, LOW for RX (don't care) - // Note: TX/RX path switching (CTX) is handled by DIO2 via SX126X_DIO2_AS_RF_SWITCH - pinMode(LORA_PA_TX_EN, OUTPUT); - digitalWrite(LORA_PA_TX_EN, LOW); // Start in RX-ready state + pinMode(LORA_FEM_CSD, OUTPUT); + digitalWrite(LORA_FEM_CSD, HIGH); +#if SOC_RTCIO_HOLD_SUPPORTED + rtc_gpio_hold_dis((gpio_num_t)LORA_FEM_CSD); #endif -#if defined(USE_SKY66122_FEM) - // SKY66122-11 FEM initialization — start in RX mode, boost off - // See variant.h for full pin mapping and control logic documentation - // Drive CTX LOW first to prevent transient TX mode (Mode 2) while CSD/CPS - // are being enabled — the RAK13302 has no pull-downs on these pins. - pinMode(SKY66122_CTX, OUTPUT); - digitalWrite(SKY66122_CTX, LOW); // Boost off, RX mode - pinMode(SKY66122_CSD, OUTPUT); - digitalWrite(SKY66122_CSD, HIGH); // Enable FEM - pinMode(SKY66122_CPS, OUTPUT); - digitalWrite(SKY66122_CPS, HIGH); // Active path (required for both TX and RX) - delay(1); // Settling time + pinMode(LORA_FEM_CPS, OUTPUT); +#ifdef LORA_FEM_CTX + // MCU-driven CTX: CPS stays HIGH for both TX and RX + digitalWrite(LORA_FEM_CPS, HIGH); +#else + // DIO2-driven CTX: CPS selects PA mode, start in RX-ready + digitalWrite(LORA_FEM_CPS, LOW); +#endif + + delay(1); // FEM settling time #endif #ifdef RF95_FAN_EN @@ -208,9 +206,9 @@ template bool SX126xInterface::init() LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d", result); } -#if defined(USE_GC1109_PA) || defined(USE_SKY66122_FEM) +#ifdef LORA_FEM_RX_PATCH // Undocumented SX1262 register patch recommended by Heltec/Semtech for improved RX sensitivity - // on boards with an external FEM (GC1109, SKY66122). Sets bit 0 of register 0x8B5. + // on boards with an external FEM. Sets bit 0 of register 0x8B5. // Reference: https://github.com/meshcore-dev/MeshCore/pull/1398 if (module.SPIsetRegValue(0x8B5, 0x01, 0, 0) == RADIOLIB_ERR_NONE) { LOG_INFO("Applied SX1262 register 0x8B5 patch for FEM RX improvement"); @@ -436,21 +434,14 @@ template bool SX126xInterface::sleep() digitalWrite(SX126X_POWER_EN, LOW); #endif -#if defined(USE_GC1109_PA) - /* - * Do not switch the power on and off frequently. - * After turning off LORA_PA_EN, the power consumption has dropped to the uA level. - * // digitalWrite(LORA_PA_POWER, LOW); - */ - digitalWrite(LORA_PA_EN, LOW); - digitalWrite(LORA_PA_TX_EN, LOW); +#if defined(USE_LORA_FEM) +#ifdef LORA_FEM_CTX + digitalWrite(LORA_FEM_CTX, LOW); #endif - -#if defined(USE_SKY66122_FEM) - // Full shutdown — all pins LOW, FEM draws <1 μA - digitalWrite(SKY66122_CTX, LOW); - digitalWrite(SKY66122_CPS, LOW); - digitalWrite(SKY66122_CSD, LOW); + digitalWrite(LORA_FEM_CPS, LOW); + digitalWrite(LORA_FEM_CSD, LOW); + // LORA_FEM_POWER intentionally left on — avoids frequent power cycling + // and deep sleep code in sleep.cpp re-latches CSD if LNA wake is needed #endif return true; } @@ -510,19 +501,22 @@ template void SX126xInterface::resetAGC() startReceive(); } -/** Control PA mode for GC1109 / SKY66122 FEM - selects TX path (txon=true) or RX path (txon=false) */ +/** Control PA mode for FEM - selects TX path (txon=true) or RX path (txon=false) */ template void SX126xInterface::setTransmitEnable(bool txon) { -#if defined(USE_GC1109_PA) - digitalWrite(LORA_PA_POWER, HIGH); // Ensure LDO is on - digitalWrite(LORA_PA_EN, HIGH); // CSD=1: Chip enabled - digitalWrite(LORA_PA_TX_EN, txon ? 1 : 0); // CPS: 1=full PA, 0=bypass (for RX, CPS is don't care) +#if defined(USE_LORA_FEM) +#ifdef LORA_FEM_POWER + digitalWrite(LORA_FEM_POWER, HIGH); +#endif + digitalWrite(LORA_FEM_CSD, HIGH); +#ifdef LORA_FEM_CTX + // MCU-driven CTX: CPS always HIGH, toggle CTX for TX/RX + digitalWrite(LORA_FEM_CPS, HIGH); + digitalWrite(LORA_FEM_CTX, txon ? HIGH : LOW); +#else + // DIO2-driven CTX: toggle CPS for PA mode + digitalWrite(LORA_FEM_CPS, txon ? HIGH : LOW); #endif - -#if defined(USE_SKY66122_FEM) - digitalWrite(SKY66122_CSD, HIGH); // Ensure FEM is enabled - digitalWrite(SKY66122_CPS, HIGH); // Enable active mode (TX and RX) - digitalWrite(SKY66122_CTX, txon ? 1 : 0); // HIGH=TX (boost on, PA), LOW=RX (boost off, LNA) #endif } diff --git a/src/sleep.cpp b/src/sleep.cpp index 8470e9273c7..751d3acc015 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -163,11 +163,15 @@ void initDeepSleep() if (wakeCause != ESP_SLEEP_WAKEUP_UNDEFINED) { LOG_DEBUG("Disable any holds on RTC IO pads"); for (uint8_t i = 0; i <= GPIO_NUM_MAX; i++) { -#if defined(USE_GC1109_PA) - // Skip GC1109 FEM power pins - they are held HIGH during deep sleep to keep +#if defined(USE_LORA_FEM) + // Skip FEM power pins - they are held HIGH during deep sleep to keep // the LNA active for RX wake. Released later in SX126xInterface::init() after // GPIO registers are set HIGH first, avoiding a power glitch. - if (i == LORA_PA_POWER || i == LORA_PA_EN) + if (false +#ifdef LORA_FEM_POWER + || i == LORA_FEM_POWER +#endif + || i == LORA_FEM_CSD) continue; #endif if (rtc_gpio_is_valid_gpio((gpio_num_t)i)) @@ -567,15 +571,15 @@ void enableLoraInterrupt() gpio_pullup_en((gpio_num_t)LORA_CS); #endif -#if defined(USE_GC1109_PA) - // Keep GC1109 FEM powered during deep sleep so LNA remains active for RX wake. - // Set PA_POWER and PA_EN HIGH (overrides SX126xInterface::sleep() shutdown), +#if defined(USE_LORA_FEM) && defined(LORA_FEM_POWER) + // Keep FEM powered during deep sleep so LNA remains active for RX wake. + // Set POWER and CSD HIGH (overrides SX126xInterface::sleep() shutdown), // then latch with RTC hold so the state survives deep sleep. - digitalWrite(LORA_PA_POWER, HIGH); - rtc_gpio_hold_en((gpio_num_t)LORA_PA_POWER); - digitalWrite(LORA_PA_EN, HIGH); - rtc_gpio_hold_en((gpio_num_t)LORA_PA_EN); - gpio_pulldown_en((gpio_num_t)LORA_PA_TX_EN); + digitalWrite(LORA_FEM_POWER, HIGH); + rtc_gpio_hold_en((gpio_num_t)LORA_FEM_POWER); + digitalWrite(LORA_FEM_CSD, HIGH); + rtc_gpio_hold_en((gpio_num_t)LORA_FEM_CSD); + gpio_pulldown_en((gpio_num_t)LORA_FEM_CPS); #endif LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by gpio interrupt", LORA_DIO1); diff --git a/variants/esp32s3/heltec_v4/variant.h b/variants/esp32s3/heltec_v4/variant.h index 1c1168d9459..fc256a33100 100644 --- a/variants/esp32s3/heltec_v4/variant.h +++ b/variants/esp32s3/heltec_v4/variant.h @@ -47,15 +47,21 @@ // CSD (pin 4) -> GPIO2: Chip enable (HIGH=on, LOW=shutdown) // CPS (pin 5) -> GPIO46: PA mode select (HIGH=full PA, LOW=bypass) // VCC0/VCC1 -> Vfem via U3 LDO, controlled by GPIO7 -#define USE_GC1109_PA -#define LORA_PA_POWER 7 // VFEM_Ctrl - GC1109 LDO power enable -#define LORA_PA_EN 2 // CSD - GC1109 chip enable (HIGH=on) -#define LORA_PA_TX_EN 46 // CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) +#define USE_LORA_FEM +#define LORA_FEM_POWER 7 // VFEM_Ctrl - LDO power enable +#define LORA_FEM_CSD 2 // Chip enable (HIGH=on, LOW=shutdown) +#define LORA_FEM_CPS 46 // PA mode (HIGH=full PA, LOW=bypass) +// CTX handled by DIO2 via SX126X_DIO2_AS_RF_SWITCH +#define LORA_FEM_RX_PATCH // SX1262 register 0x8B5 sensitivity fix // GC1109 FEM: TX/RX path switching is handled by DIO2 -> CTX pin (via SX126X_DIO2_AS_RF_SWITCH) // GPIO46 is CPS (PA mode), not TX control - setTransmitEnable() handles it in SX126xInterface.cpp // Do NOT use SX126X_TXEN/RXEN as that would cause double-control of GPIO46 +// Power Amps are often non-linear, so we use an array of values for the power curve +#define NUM_PA_POINTS 22 +#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 + #if HAS_TFT #define USE_TFTDISPLAY 1 #endif diff --git a/variants/esp32s3/heltec_wireless_tracker_v2/variant.h b/variants/esp32s3/heltec_wireless_tracker_v2/variant.h index 0ca2dfc033f..7c450fc413f 100644 --- a/variants/esp32s3/heltec_wireless_tracker_v2/variant.h +++ b/variants/esp32s3/heltec_wireless_tracker_v2/variant.h @@ -91,11 +91,17 @@ // CSD (pin 4) -> GPIO4: Chip enable (HIGH=on, LOW=shutdown) // CPS (pin 5) -> GPIO46: PA mode select (HIGH=full PA, LOW=bypass) // VCC0/VCC1 -> Vfem via U3 LDO, controlled by GPIO7 -#define USE_GC1109_PA -#define LORA_PA_POWER 7 // VFEM_Ctrl - GC1109 LDO power enable -#define LORA_PA_EN 4 // CSD - GC1109 chip enable (HIGH=on) -#define LORA_PA_TX_EN 46 // CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) +#define USE_LORA_FEM +#define LORA_FEM_POWER 7 // VFEM_Ctrl - LDO power enable +#define LORA_FEM_CSD 4 // Chip enable (HIGH=on, LOW=shutdown) +#define LORA_FEM_CPS 46 // PA mode (HIGH=full PA, LOW=bypass) +// CTX handled by DIO2 via SX126X_DIO2_AS_RF_SWITCH +#define LORA_FEM_RX_PATCH // SX1262 register 0x8B5 sensitivity fix // GC1109 FEM: TX/RX path switching is handled by DIO2 -> CTX pin (via SX126X_DIO2_AS_RF_SWITCH) // GPIO46 is CPS (PA mode), not TX control - setTransmitEnable() handles it in SX126xInterface.cpp -// Do NOT use SX126X_TXEN/RXEN as that would cause double-control of GPIO46 \ No newline at end of file +// Do NOT use SX126X_TXEN/RXEN as that would cause double-control of GPIO46 + +// Power Amps are often non-linear, so we use an array of values for the power curve +#define NUM_PA_POINTS 22 +#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 \ No newline at end of file diff --git a/variants/nrf52840/rak3401_1watt/variant.h b/variants/nrf52840/rak3401_1watt/variant.h index fc4ca5ba4a5..b50dfbf0b2d 100644 --- a/variants/nrf52840/rak3401_1watt/variant.h +++ b/variants/nrf52840/rak3401_1watt/variant.h @@ -169,10 +169,11 @@ static const uint8_t SCK = PIN_SPI_SCK; * * R25 (DIO2→CTX) is NC by default on the RAK13302, so the MCU must drive CTX directly. */ -#define USE_SKY66122_FEM -#define SKY66122_CSD (24) -#define SKY66122_CPS (21) -#define SKY66122_CTX (31) +#define USE_LORA_FEM +#define LORA_FEM_CSD (24) // Chip enable +#define LORA_FEM_CPS (21) // Path select (HIGH for both TX and RX) +#define LORA_FEM_CTX (31) // TX/RX select + 5V boost (MCU-driven, R25 NC) +#define LORA_FEM_RX_PATCH // SX1262 register 0x8B5 sensitivity fix // DIO2 configures the SX1262 internal RF switch (harmless with R25 NC) #define SX126X_DIO2_AS_RF_SWITCH From b23348da9a971c0e9bc9d3efdc0f5b611b17f646 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Thu, 26 Feb 2026 15:40:58 +0100 Subject: [PATCH 5/5] Address copilot feedback --- src/mesh/SX126xInterface.cpp | 5 ++++- variants/nrf52840/rak3401_1watt/variant.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 7c6fd78ae9e..8c1448fa57a 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -501,7 +501,10 @@ template void SX126xInterface::resetAGC() startReceive(); } -/** Control PA mode for FEM - selects TX path (txon=true) or RX path (txon=false) */ +/** Control FEM PA mode. + * - With MCU-driven CTX (LORA_FEM_CTX defined), txon=true selects the TX path and txon=false selects the RX path. + * - With DIO2-driven CTX (no LORA_FEM_CTX), this toggles CPS (bypass vs active path); TX/RX path is controlled by the radio. + */ template void SX126xInterface::setTransmitEnable(bool txon) { #if defined(USE_LORA_FEM) diff --git a/variants/nrf52840/rak3401_1watt/variant.h b/variants/nrf52840/rak3401_1watt/variant.h index b50dfbf0b2d..ae3b031fcd1 100644 --- a/variants/nrf52840/rak3401_1watt/variant.h +++ b/variants/nrf52840/rak3401_1watt/variant.h @@ -175,7 +175,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define LORA_FEM_CTX (31) // TX/RX select + 5V boost (MCU-driven, R25 NC) #define LORA_FEM_RX_PATCH // SX1262 register 0x8B5 sensitivity fix -// DIO2 configures the SX1262 internal RF switch (harmless with R25 NC) +// Configure SX1262 DIO2 as RF-switch control output (not connected on this module, R25 NC) #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8