[Heltec WiFi LoRa 32 V4] Need Help: Custom firmware TX reports success but RX never receives (Heltec V4 → Heltec V4)

Hello Community,

I’m working on a custom LoRa project with two Heltec WiFi LoRa 32 V4 boards and have run into a frustrating issue that I can’t solve despite extensive testing. I’m hoping someone here can help me figure out what I’m missing!

The Problem

I have two Heltec WiFi LoRa 32 V4 boards that work perfectly with Meshtastic firmware (bidirectional communication, good RSSI/SNR values). However, when I use custom firmware (RadioLib + raw SX1262 initialization), the TX side always reports success but the RX side never receives any packets.

  • :white_check_mark: With Meshtastic: Both boards communicate perfectly

  • :cross_mark: With custom firmware: TX reports ERR_NONE, but RX never sees packets

  • :cross_mark: This happens even at very close distance (< 1 meter)

This is a simple Heltec V4 → Heltec V4 point-to-point test, no other devices involved.


My Hardware Setup

  • 2 × Heltec WiFi LoRa 32 V4 (ESP32-S3 + SX1262 + GC1109 front-end, EU868 version)

  • PlatformIO, Arduino framework, RadioLib 7.4.0, U8g2

  • Original 868 MHz antennas supplied with the boards

  • Both boards are identical hardware


LoRa Parameters (Identical on Both Boards)

I’m using the same parameters on both TX and RX:

Frequency: 869.525 MHz Bandwidth: 250 kHz Spreading Factor: 11 Coding Rate: 4/8 Sync Word: 0x12 (Meshtastic privatenetwork) Preamble: 8 Header: explicitCRC: enabled TX Power: 22 dBm

Both boards call radio.begin(869.525, 250.0, 11, 8, 0x12, 22, 8) followed by radio.setCRC(true) and radio.explicitHeader().


Pin Configuration (from Heltec V4 documentation)

// SX1262 SPI pinsLORA_SCK = 9 LORA_MISO = 11 LORA_MOSI = 10 LORA_NSS = 8 LORA_DIO1 = 14 LORA_RST = 12 LORA_BUSY = 13 // GC1109 Front-end controlFEM_EN = 2 // CSD - Frontend enable (set HIGH)FEM_PA = 46 // CTX - TX/RX switch (controlled by DIO2?)


My Initialization Sequence

Based on research in Meshtastic source code and community forums, I’ve implemented this initialization on both TX and RX:

1. Hardware Reset

  • Pull RST low, then high, wait for BUSY to go low

2. Raw SX1262 Commands (before RadioLib begin!)

SetDio3AsTcxoCtrl (0x97) // 3.3V TCXO, ~2.5ms delaySetDio2AsRfSwitch (0x9D, 0x01)// Auto RF switch via DIO2SetStandby (0x80) // STDBY_RCSetRegulatorMode (0x96, 0x00) // DC-DC modeCalibrate (0x89, 0x7F) // Calibrate allCalibrateImage (0x98, 0xE1, 0xE9)// 860-880 MHzSetPaConfig (0x95, {0x04, 0x07, 0x00, 0x01})// High powerSetTxParams (0x8E, {0x1F, 0x03})// 31 = 22dBm + 9 offsetSetBufferBaseAddress (0x8F, {0x00, 0x00})

3. GPIO Setup

digitalWrite(FEM_EN, HIGH); // GC1109 permanently enabled

4. RadioLib Initialization

radio.begin(869.525, 250.0, 11, 8, 0x12, 22, 8); radio.setCRC(true); radio.explicitHeader();

5. TX Loop

radio.transmit("HELTEC_" + String(count)); // Always returns ERR_NONE, realistic TX duration

6. RX Loop

radio.startReceive(); // Called once in setup() // In loop(): if(radio.available()) { radio.readData(msg); // Never called - available() is always false}


What I’ve Already Tested

I’ve spent 15+ hours debugging this and tried:

  1. Different TCXO voltages: 1.7V, 2.4V, 3.0V, 3.3V with various delays

  2. Different SyncWords: 0x12 and 0x34, matching on both sides

  3. With/without DIO2 RF switch: Manual GPIO46 control vs automatic DIO2

  4. Different power levels: 0-22 dBm

  5. Initialization order: Raw init before/after RadioLib begin

  6. Role swap: Tested both boards as TX and RX

  7. Frequency variations: 868.0 MHz, 869.525 MHz

  8. Parameter variations: Different SF/BW combinations

  9. CPS pin tests: Tried GPIOs 3, 4, 5, 6, 7, 45, 47 set HIGH (no CPS found in V4 docs)

Result: Always the same - TX reports success, RX never receives anything.


What Works vs. What Doesn’t

:white_check_mark: Works perfectly:

  • Meshtastic on both boards (bidirectional, good RSSI/SNR)

  • This proves the hardware, antennas, and RF path are functional

:cross_mark: Doesn’t work:

  • Custom firmware on both boards (identical parameters)

  • TX side shows success, RX side shows nothing

  • No packets at any distance, even touching antennas


My Questions

I would really appreciate help with these questions:

  1. Is there a known-good minimal example for Heltec V4 ↔ V4 point-to-point LoRa?
    Even pseudo-code or key settings would help!

  2. Is my SX1262 + GC1109 initialization sequence correct for V4?
    Specifically the TCXO, DIO2, and PA configuration?

  3. Is DIO2 really wired to GC1109 CTX on the V4?
    Should SetDio2AsRfSwitch handle the TX/RX switching automatically?

  4. Is there a hidden CPS pin or other GC1109 control pin I’m missing?
    The GC1109 datasheet mentions CPS (power select) but I can’t find it in V4 docs.

  5. Could RadioLib be overwriting my raw SX1262 settings?
    Is there a V4-specific way to use RadioLib that preserves the critical settings?

  6. What TCXO voltage does the V4 actually use?
    I’ve tried 1.7V, 2.4V, 3.0V, and 3.3V without success.


Additional Info

  • The boards are definitely functional - Meshtastic proves this

  • I’ve verified with a logic analyzer that SPI commands are being sent correctly

  • Status registers show no errors after initialization

  • Both TX and RX init sequences complete successfully

I’m clearly missing something that Meshtastic does correctly but I haven’t replicated. Any insights, code examples, or schematic details for the Heltec V4 would be incredibly helpful!

Thank you in advance for any guidance! :folded_hands:


Hi there, great report, great detail, totally the wrong place!

We only do LoRaWAN with a focus on TTN/TTI.

We definitely don’t do Meshtastic - as you may have realised from a forum search.

And whilst the RadioLib LoRaWAN team visit often, the forum doesn’t do Point 2 Point.

So I’d direct you to the RadioLib discussions page but I’d make you aware of an issue with the pre-amp that Heltec put in to v4 that has been shown to cause issue, so you should read up on that on the Heltec wiki page &/or post on that forum.

not tried a Heltec LoRa V4 but have used Radiolib library with Heltec LoRa V3 for LoRa P2P communication

using the Arduino IDE V2.3.4 ESP32 core 3.3.5 RadioLib V7.4.0

using the RadioLib examples File>Examples>RadioLib>SX126x>SX126x_Transmit_Interrupt and Receive_interrupt

transmitter code

// Heltec LoRa SX1262 transmit test

// File>Examples>RadioLib>SX126x>SX126x_Transmit_Interrupt

// EDITs:
//  radio.begin frequency set to 868.0
//  transmitting byte array and printing it

/*
  RadioLib SX126x Transmit with Interrupts Example

  This example transmits LoRa packets with one second delays
  between them. Each packet contains up to 256 bytes
  of data, in the form of:
  - Arduino String
  - null-terminated char array (C-string)
  - arbitrary binary data (byte array)

  Other modules from SX126x family can also be used.

  For default module settings, see the wiki page
  https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem

  For full API reference, see the GitHub Pages
  https://jgromes.github.io/RadioLib/
*/

// include the library
#include <RadioLib.h>

// Heltec LoRa V3  SX1262 has the following connections:
// NSS pin:   8
// DIO1 pin:  14
// NRST pin:  12
// BUSY pin:  13
SX1262 radio = new Module(8, 14, 12, 13);

// or detect the pinout automatically using RadioBoards
// https://github.com/radiolib-org/RadioBoards
/*
#define RADIO_BOARD_AUTO
#include <RadioBoards.h>
Radio radio = new RadioModule();
*/

// save transmission state between loops
int transmissionState = RADIOLIB_ERR_NONE;

// flag to indicate that a packet was sent
volatile bool transmittedFlag = false;

// this function is called when a complete packet
// is transmitted by the module
// IMPORTANT: this function MUST be 'void' type
//            and MUST NOT have any arguments!
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void setFlag(void) {
  // we sent a packet, set the flag
  transmittedFlag = true;
}

void setup() {
  Serial.begin(115200);
  delay(2000);
  // initialize SX1262 with default settings
  Serial.print(F("\n\nHeltec LoRa V3 [SX1262] Initializing ... "));
  int state = radio.begin(868.0);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true) { delay(10); }
  }

  // set the function that will be called
  // when packet transmission is finished
  radio.setPacketSentAction(setFlag);

  // start transmitting the first packet
  Serial.print(F("[SX1262] Sending first packet ... "));

  // you can transmit C-string or Arduino string up to
  // 256 characters long
  transmissionState = radio.startTransmit("Hello World!");

  // you can also transmit byte array up to 256 bytes long
  /*
    byte byteArr[] = {0x01, 0x23, 0x45, 0x67,
                      0x89, 0xAB, 0xCD, 0xEF};
    state = radio.startTransmit(byteArr, 8);
  */
}

// counter to keep track of transmitted packets
int count = 0;

void loop() {
  // check if the previous transmission finished
  if (transmittedFlag) {
    // reset flag
    transmittedFlag = false;

    if (transmissionState == RADIOLIB_ERR_NONE) {
      // packet was successfully sent
      Serial.println(F("transmission finished!"));

      // NOTE: when using interrupt-driven transmit method,
      //       it is not possible to automatically measure
      //       transmission data rate using getDataRate()

    } else {
      Serial.print(F("failed, code "));
      Serial.println(transmissionState);
    }

    // clean up after transmission is finished
    // this will ensure transmitter is disabled,
    // RF switch is powered down etc.
    radio.finishTransmit();

    // wait a second before transmitting again
    delay(1000);

    // send another one
    Serial.print(F("[SX1262] Sending another packet ... "));

    // you can transmit C-string or Arduino string up to
    // 256 characters long
    //String str = "Hello World! #" + String(count++);
    //transmissionState = radio.startTransmit(str);

    // you can also transmit byte array up to 256 bytes long
    static byte byteArr[] = { 0x01, 0x23, 0x45, 0x67,
                              0x89, 0xAB, 0xCD, 0xEF };
    for (int i = 0; i < sizeof(byteArr); i++)
      Serial.printf("%d ", byteArr[i]);
    transmissionState = radio.startTransmit(byteArr, 8);
    byteArr[0]++;
  }
}

SPI pins assumed to be

MOSI: 10  LORA
MISO: 11  LORA
SCK: 9    LORA
SS: 8     LORA

@BrianBramerAtEPAS, not sure if you read my reply, but we don’t do P2P on this LoRaWAN forum. Plus there are additional issues with the v4 that need exploring.

Closing thread to prevent further confusion.