33void HeltecV4Board::begin () {
44 ESP32Board::begin ();
55
6-
76 pinMode (PIN_ADC_CTRL, OUTPUT);
8- digitalWrite (PIN_ADC_CTRL, LOW); // Initially inactive
7+ digitalWrite (PIN_ADC_CTRL, LOW);
98
10- // Set up digital GPIO registers before releasing RTC hold. The hold latches
11- // the pad state including function select, so register writes accumulate
12- // without affecting the pad. On hold release, all changes apply atomically
13- // (IO MUX switches to digital GPIO with output already HIGH — no glitch).
9+ // Power on FEM LDO — set registers before releasing RTC hold for
10+ // atomic transition (no glitch on deep sleep wake).
1411 pinMode (P_LORA_PA_POWER, OUTPUT);
15- digitalWrite (P_LORA_PA_POWER,HIGH);
12+ digitalWrite (P_LORA_PA_POWER, HIGH);
1613 rtc_gpio_hold_dis ((gpio_num_t )P_LORA_PA_POWER);
1714
18- pinMode (P_LORA_PA_EN, OUTPUT);
19- digitalWrite (P_LORA_PA_EN,HIGH);
20- rtc_gpio_hold_dis ((gpio_num_t )P_LORA_PA_EN);
21- pinMode (P_LORA_PA_TX_EN, OUTPUT);
22- digitalWrite (P_LORA_PA_TX_EN,LOW);
23-
2415 esp_reset_reason_t reason = esp_reset_reason ();
2516 if (reason != ESP_RST_DEEPSLEEP) {
26- delay (1 ); // GC1109 startup time after cold power-on
17+ delay (1 ); // FEM startup time after cold power-on
18+ }
19+
20+ // Auto-detect FEM type via GPIO2 default pull level.
21+ // GC1109 CSD: internal pull-down → reads LOW
22+ // KCT8103L CSD: internal pull-up → reads HIGH
23+ rtc_gpio_hold_dis ((gpio_num_t )P_LORA_PA_EN);
24+ pinMode (P_LORA_PA_EN, INPUT);
25+ delay (1 );
26+ is_kct8103l_ = (digitalRead (P_LORA_PA_EN) == HIGH);
27+
28+ // CSD/enable: HIGH for both FEM types
29+ pinMode (P_LORA_PA_EN, OUTPUT);
30+ digitalWrite (P_LORA_PA_EN, HIGH);
31+
32+ if (is_kct8103l_) {
33+ // V4.3 — KCT8103L: CTX on GPIO5 controls TX/RX path
34+ rtc_gpio_hold_dis ((gpio_num_t )P_LORA_PA_CTX);
35+ pinMode (P_LORA_PA_CTX, OUTPUT);
36+ digitalWrite (P_LORA_PA_CTX, LOW); // RX mode (LNA enabled)
37+ } else {
38+ // V4.2 — GC1109: CPS on GPIO46 controls PA mode
39+ pinMode (P_LORA_PA_TX_EN, OUTPUT);
40+ digitalWrite (P_LORA_PA_TX_EN, LOW); // RX bypass mode
2741 }
2842
2943 periph_power.begin ();
44+
3045 if (reason == ESP_RST_DEEPSLEEP) {
3146 long wakeup_source = esp_sleep_get_ext1_wakeup_status ();
32- if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
47+ if (wakeup_source & (1 << P_LORA_DIO_1)) {
3348 startup_reason = BD_STARTUP_RX_PACKET;
3449 }
35-
3650 rtc_gpio_hold_dis ((gpio_num_t )P_LORA_NSS);
3751 rtc_gpio_deinit ((gpio_num_t )P_LORA_DIO_1);
3852 }
3953 }
4054
4155 void HeltecV4Board::onBeforeTransmit (void ) {
42- digitalWrite (P_LORA_TX_LED, HIGH); // turn TX LED on
43- digitalWrite (P_LORA_PA_TX_EN,HIGH);
56+ digitalWrite (P_LORA_TX_LED, HIGH);
57+ if (is_kct8103l_) {
58+ digitalWrite (P_LORA_PA_CTX, HIGH); // CTX: TX path
59+ } else {
60+ digitalWrite (P_LORA_PA_TX_EN, HIGH); // CPS: full PA
61+ }
4462 }
4563
4664 void HeltecV4Board::onAfterTransmit (void ) {
47- digitalWrite (P_LORA_TX_LED, LOW); // turn TX LED off
48- digitalWrite (P_LORA_PA_TX_EN,LOW);
65+ digitalWrite (P_LORA_TX_LED, LOW);
66+ if (is_kct8103l_) {
67+ digitalWrite (P_LORA_PA_CTX, LOW); // CTX: RX path (LNA on)
68+ } else {
69+ digitalWrite (P_LORA_PA_TX_EN, LOW); // CPS: bypass
70+ }
4971 }
5072
5173 void HeltecV4Board::enterDeepSleep (uint32_t secs, int pin_wake_btn) {
@@ -57,10 +79,16 @@ void HeltecV4Board::begin() {
5779
5880 rtc_gpio_hold_en ((gpio_num_t )P_LORA_NSS);
5981
60- // Hold GC1109 FEM pins during sleep to keep LNA active for RX wake
82+ // Hold FEM pins during sleep to keep LNA active for RX wake
6183 rtc_gpio_hold_en ((gpio_num_t )P_LORA_PA_POWER);
6284 rtc_gpio_hold_en ((gpio_num_t )P_LORA_PA_EN);
6385
86+ if (is_kct8103l_) {
87+ // Hold CTX LOW during deep sleep for RX wake (LNA enabled)
88+ digitalWrite (P_LORA_PA_CTX, LOW);
89+ rtc_gpio_hold_en ((gpio_num_t )P_LORA_PA_CTX);
90+ }
91+
6492 if (pin_wake_btn < 0 ) {
6593 esp_sleep_enable_ext1_wakeup ( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
6694 } else {
@@ -96,8 +124,8 @@ void HeltecV4Board::begin() {
96124
97125 const char * HeltecV4Board::getManufacturerName () const {
98126 #ifdef HELTEC_LORA_V4_TFT
99- return " Heltec V4 TFT" ;
127+ return is_kct8103l_ ? " Heltec V4.3 TFT " : " Heltec V4 TFT" ;
100128 #else
101- return " Heltec V4 OLED" ;
129+ return is_kct8103l_ ? " Heltec V4.3 OLED " : " Heltec V4 OLED" ;
102130 #endif
103131 }
0 commit comments