📡 Heltec V3 : tester l’Ă©metteur-rĂ©cepteur LoRa (SX1262)

0
(0)

Dans un prĂ©cĂ©dent tutoriel, je vous prĂ©sentais Meshtastic, une solution pour crĂ©er des rĂ©seaux maillĂ©s dĂ©centralisĂ©s et autonomes. La brique Ă©lĂ©mentaire de ce rĂ©seau est le nĹ“ud, gĂ©nĂ©ralement composĂ© d’un microcontrĂ´leur, d’une puce radio LoRa et d’une antenne.

Cependant, la connexion entre la carte et l’antenne mĂ©rite une attention particulière. De nombreux modules utilisent des connecteurs de type IPEX (ou U.FL). S’ils ont l’avantage d’ĂŞtre compacts, ces connecteurs sont relativement fragiles et n’offrent pas toujours une liaison parfaitement fiable. Il arrive ainsi que l’antenne soit dĂ©connectĂ©e sans que l’utilisateur s’en rende compte.

Connecteur d’antenne de type IPEX sur une carte HELTEC V3

Cette situation n’est pas anodine. Sur certains forums, des utilisateurs rapportent qu’Ă©mettre sans antenne peut, dans certaines conditions, endommager l’amplificateur de puissance (PA) du module radio, voire le rendre inutilisable.

Pour vous Ă©viter toute inquiĂ©tude — ou pour diagnostiquer un comportement suspect — j’ai conçu un petit programme de test simple et efficace. Il permet de vĂ©rifier rapidement la chaĂ®ne complète d’Ă©mission et de rĂ©ception d’un nĹ“ud Heltec V3 (avec une adaptation possible Ă  d’autres modules LoRa).

Le principe est très simple, mais nécessite deux modules Heltec V3 :

  • Le premier module envoie des trames Ă  diffĂ©rentes puissances d’Ă©mission.
  • Le second module les reçoit et affiche la puissance reçue (RSSI) et le rapport signal/bruit (SNR).

Ce test vous permettra de valider le bon fonctionnement de vos cartes et de vous assurer que leur amplificateur de puissance fonctionne correctement.

Pour ce tutoriel, j’ai choisi l’IDE Arduino : un environnement accessible Ă  tous, mĂŞme sans compĂ©tences avancĂ©es en programmation.

Installation du gestionnaire de carte Heltec ESP32

Ajouter l’URL https://resource.heltec.cn/download/package_heltec_esp32_index.json Ă  la liste des gestionnaires de cartes supplĂ©mentaires :

Et installer le gestionnaire de cartes Heltec ESP32 Series Arduino Develop Environment par Heltec Automation :

Installation de la librairie RadioLib

La librairie RadioLib par Jan Gromes est une bibliothèque de communication sans fil universelle pour diffĂ©rents microcontrĂ´leurs qui supporte une grande variĂ©tĂ© de modules radio (SX1262, SX127x, RF69, CC1101, nRF24L01, etc.) et permet d’utiliser facilement le protocole LoRa.

Le programme

Comme je l’ai indiquĂ© plus haut, le principe du test est très simple, mais nĂ©cessite deux modules Heltec V3. Le premier module envoie des trames Ă  diffĂ©rentes puissances d’Ă©mission (-2 dBm, 5 dBm et 10 dBm) alors que le second module les reçoit et affiche la puissance reçue (RSSI) et le rapport signal/bruit (SNR).

Afin de simplifier la comprĂ©hension, j’ai dĂ©cidĂ© de fournir deux codes diffĂ©rents. Un pour le module Ă©metteur, un autre pour le module rĂ©cepteur.

Voici le code pour le module Heltec V3 en mode émetteur LoRa :

C++
/*
  Heltec V3 LoRa Transceiver Test

  Code for transmitter module

  https://tutoduino.fr/

*/

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

// LED pin definition for Heltec V3 (GPIO 35)
#define TX_LED_PIN 35
#define USER_BUTTON 0

// Heltec V3 SX1262 radio module configuration
// cs = NSS (pin 8) - Chip Select for SPI communication
// irq = DIO1 (pin 14) - Interrupt request line
// rst = NRST (pin 12) - Reset pin for the radio module
// gpio = BUSY (pin 13) - Busy status pin
SX1262 radio = new Module(8, 14, 12, 13);

// Flag to indicate that packet sending is completed
// Volatile because it's modified inside an interrupt handler (setFlag)
volatile bool packetSendingCompleted = false;

// Store transmission state between loop iterations
// Values can be RADIOLIB_ERR_NONE (success) or error codes
int transmissionState = RADIOLIB_ERR_NONE;

// State machine enumeration for tracking transmission sequence
enum State {
  INIT_STATE,                // Initial state, ready to send first packet
  FIRST_PACKET_SENT_STATE,   // First packet sent, waiting for completion
  SECOND_PACKET_SENT_STATE,  // Second packet sent, waiting for completion
  THIRD_PACKET_SENT_STATE    // Third packet sent, waiting for completion
};

// Current state of the transmitter state machine
State state;

// Interrupt Service Routine (ISR) called when packet transmission completes
// This function is triggered by the DIO1 pin interrupt
void setFlag(void) {
  // Packet was sent successfully, set the flag for the main loop
  packetSendingCompleted = true;
  // Turn off the transmission LED to indicate transmission is complete
  digitalWrite(TX_LED_PIN, LOW);
}

void setup() {
  // Initialize serial communication for debug output
  Serial.begin(115200);
  delay(1000);  // Allow serial to stabilize

  Serial.println("Heltec V3 LoRa Transceiver Test - Transmitter module");
  Serial.print("ChipId = ");
  Serial.println(ESP.getEfuseMac());

  // Configure the transmission LED pin
  pinMode(TX_LED_PIN, OUTPUT);
  digitalWrite(TX_LED_PIN, LOW);  // Ensure LED starts in OFF state

  pinMode(USER_BUTTON, INPUT);

  // Initialize SX1262 radio module
  float frequency = 869.4;
  float bandwidth = 125.0;
  uint8_t spreadingFactor = 9;
  uint8_t codingRate = 5;
  uint8_t syncWord = 0x12;
  int8_t power = -2;
  uint16_t preambleLength = 8;
  int rc = radio.begin(frequency, bandwidth, spreadingFactor, codingRate, syncWord, power, preambleLength);
  if (rc == RADIOLIB_ERR_NONE) {
    Serial.println(F("SX1262 init success!"));
  } else {
    Serial.print(F("SX1262 init failed, code "));
    Serial.println(rc);          // Print error code for debugging
    while (true) { delay(10); }  // Halt execution on failure
  }

  // Register the interrupt handler function
  // This will be called automatically when DIO1 pin triggers
  radio.setDio1Action(setFlag);

  // Initialize state machine to starting state
  state = INIT_STATE;
}

void loop() {
  // State machine handles the sequential transmission of 3 packets
  switch (state) {
    case INIT_STATE:
      // Wait user button push to start sending packets
      Serial.println("Push PRG button to start test");
      while (digitalRead(USER_BUTTON) == HIGH) {
        delay(10);
      }

      Serial.println("Start sending packets...");

      // Prepare and send first packet at very low power (-9 dBm)
      digitalWrite(TX_LED_PIN, HIGH);  // Turn LED ON before transmission
      Serial.println("Sending packet at -9 dBm \tPacket data: \"Packet #1 at -9 dBm\"");
      radio.setOutputPower(-9);  // Very low power for close-range testing
      transmissionState = radio.startTransmit("Packet #1 at -9 dBm");
      state = FIRST_PACKET_SENT_STATE;
      // IMPORTANT: No delay here - we wait for interrupt to continue
      break;

    case FIRST_PACKET_SENT_STATE:
      // Wait for the previous transmission to complete
      if (packetSendingCompleted) {
        // Reset flag for next transmission
        packetSendingCompleted = false;

        // Check if transmission was successful
        if (transmissionState != RADIOLIB_ERR_NONE) {
          Serial.print("Packet #1 sending failed, code ");
          Serial.println(transmissionState);
        }

        // Prepare and send second packet at low power (5 dBm)
        digitalWrite(TX_LED_PIN, HIGH);  // Turn LED ON for next transmission
        Serial.println("Sending packet at 0 dBm \tPacket data: \"Packet #2 at 0 dBm\"");
        radio.setOutputPower(0);  // Low power
        transmissionState = radio.startTransmit("Packet #2 at 0 dBm");
        state = SECOND_PACKET_SENT_STATE;
        // No delay - wait for interrupt notification
      }
      break;

    case SECOND_PACKET_SENT_STATE:
      // Wait for the previous transmission to complete
      if (packetSendingCompleted) {
        // Reset flag for next transmission
        packetSendingCompleted = false;

        // Check if transmission was successful
        if (transmissionState != RADIOLIB_ERR_NONE) {
          Serial.print("Packet #2 sending failed, code ");
          Serial.println(transmissionState);
        }

        // Prepare and send third packet at medium power (5 dBm)
        digitalWrite(TX_LED_PIN, HIGH);  // Turn LED ON for next transmission
        Serial.println("Sending packet at 5 dBm \tPacket data: \"Packet #3 at 5 dBm\"");
        radio.setOutputPower(5);  // Medium power for better range
        transmissionState = radio.startTransmit("Packet #3 at 5 dBm");
        state = THIRD_PACKET_SENT_STATE;
        // No delay - wait for interrupt notification
      }
      break;

    case THIRD_PACKET_SENT_STATE:
      // Wait for the previous transmission to complete
      if (packetSendingCompleted) {
        // Reset flag for next transmission
        packetSendingCompleted = false;

        // Check if transmission was successful
        if (transmissionState != RADIOLIB_ERR_NONE) {
          Serial.print("Packet #3 sending failed, code ");
          Serial.println(transmissionState);
        }

        // Wait 5 seconds before starting the entire sequence again
        // This gives the receiver time to process and display packets
        delay(5000);
        state = INIT_STATE;  // Reset to beginning
      }
      break;

    default:
      // Fallback handler for unexpected states
      delay(5000);
      state = INIT_STATE;
      break;
  }
}

Voici le code pour le module Heltec V3 en mode récepteur LoRa :

C++
/*
  Heltec V3 LoRa Transceiver Test

  Code for receiver module

  https://tutoduino.fr/

*/

// Include the RadioLib library for LoRa communication
#include <RadioLib.h>

// Heltec V3 SX1262 radio module configuration
// cs = NSS (pin 8) - Chip Select for SPI communication
// irq = DIO1 (pin 14) - Interrupt request line (signals packet received)
// rst = NRST (pin 12) - Reset pin for the radio module
// gpio = BUSY (pin 13) - Busy status pin (indicates radio operation)
SX1262 radio = new Module(8, 14, 12, 13);

// Flag to indicate that a packet was received
// Volatile because it's modified inside an interrupt handler (setFlag)
// and read in the main loop (prevents compiler optimization)
volatile bool operationDone = false;

// Interrupt Service Routine (ISR)
// This function is automatically called when a complete packet
// is received by the module (triggered by DIO1 pin interrupt)
void setFlag(void) {
  // A packet was received, set the flag for the main loop to process
  // Keep this function as short as possible (no Serial.print here!)
  operationDone = true;
}

void setup() {
  // Variable to store return codes from RadioLib functions
  // Typical values: RADIOLIB_ERR_NONE (0) = success, negative = error
  int rc;

  // Initialize serial communication for debug output
  Serial.begin(115200);
  delay(1000);  // Allow serial connection to stabilize

  // Print program header information
  Serial.println("Heltec V3 LoRa Transceiver Test - Receiver module");
  Serial.print("ChipId = ");
  Serial.println(ESP.getEfuseMac());

  // --------------------------------------------------------------------
  // RADIO INITIALIZATION
  // --------------------------------------------------------------------

  // Initialize SX1262 radio module
  float frequency = 869.4;
  float bandwidth = 125.0;      
  uint8_t spreadingFactor = 9;  
  uint8_t codingRate = 5;       
  uint8_t syncWord = 0x12;      
  int8_t power = -2;            
  uint16_t preambleLength = 8; 
  rc = radio.begin(frequency, bandwidth, spreadingFactor, codingRate, syncWord, power, preambleLength);
  if (rc == RADIOLIB_ERR_NONE) {
    Serial.println("SX1262 init success!");
  } else {
    // Initialization failed - print error code and halt execution
    Serial.print("SX1262 init failed, code ");
    Serial.println(rc);
    while (true) { delay(10); }  // Infinite loop stops program here
  }

  // Register the interrupt handler function with the radio module
  // The setFlag() function will be called automatically when DIO1 pin
  // triggers (i.e., when a packet is fully received)
  radio.setDio1Action(setFlag);

  // --------------------------------------------------------------------
  // START RECEPTION
  // --------------------------------------------------------------------

  // Start listening for LoRa packets in continuous receive mode
  // The radio stays in receive mode and generates an interrupt
  // whenever a complete packet arrives
  Serial.println("Ready to receive packets...");
  rc = radio.startReceive();
  if (rc != RADIOLIB_ERR_NONE) {
    // Failed to start reception - print error and halt
    Serial.print("startReceive failed, code ");
    Serial.println(rc);
    while (true) { delay(10); }
  }
  // Note: No need to restart receive here - radio stays in receive mode
  // automatically after startReceive() is called once
}

void loop() {
  // Check if a packet was received (flag set by interrupt)
  if (operationDone) {
    // Reset flag immediately to avoid missing subsequent packets
    // Must be done before processing to prevent race conditions
    operationDone = false;

    // Buffer to store the received packet data as a String
    String str;

    // Read the received packet data from the radio
    // The data is copied into the 'str' variable
    int rc = radio.readData(str);

    if (rc == RADIOLIB_ERR_NONE) {
      // ----------------------------------------------------------------
      // SUCCESSFUL PACKET RECEPTION
      // ----------------------------------------------------------------

      Serial.print("Packet received");

      // Print RSSI (Received Signal Strength Indicator)
      // Typical range: -30 dBm (very strong) to -120 dBm (very weak)
      // "\t" adds a tab character for formatted column alignment
      Serial.print("\tRSSI: ");
      Serial.print(radio.getRSSI());
      Serial.print(" dBm");

      // Print SNR (Signal-to-Noise Ratio)
      // Positive values = signal stronger than noise
      // LoRa can decode with SNR as low as -20 dB (high spreading factor)
      Serial.print("\tSNR: ");
      Serial.print(radio.getSNR());
      Serial.print(" dB");

      // Print the actual packet data content, enclosed in quotes
      // The \" escapes the double quotes in the output
      Serial.print("\tPacket data: \"");
      Serial.print(str);
      Serial.println("\"");  // println adds newline after closing quote

      // CRITICAL: Restart reception for the next packet
      // After readData(), the radio automatically exits receive mode
      // Must explicitly restart to listen for more packets
      radio.startReceive();

    } else {
      // ----------------------------------------------------------------
      // RECEPTION ERROR HANDLING
      // ----------------------------------------------------------------
      // Possible errors:
      // - RADIOLIB_ERR_RX_TIMEOUT: No packet received within timeout
      // - RADIOLIB_ERR_CRC_MISMATCH: Packet corrupted (CRC check failed)
      // - Other negative codes for various hardware errors

      Serial.print("Reception failed, code ");
      Serial.println(rc);

      // Attempt to restart reception despite the error
      // This keeps the radio listening for future packets
      radio.startReceive();
    }
  }
  // If operationDone == false, do nothing and loop again
  // The CPU is free to do other tasks while waiting for packets
}

Résultats du test

Voici un exemple de traces observĂ©es dans le moniteur sĂ©rie. Ces rĂ©sultats confirment le bon fonctionnement des deux modules. Pour une validation complète, je vous recommande d’inverser leurs rĂ´les et de vĂ©rifier que les mesures sont similaires. Vous testerez ainsi l’intĂ©gralitĂ© de la chaĂ®ne d’Ă©mission et de rĂ©ception LoRa sur les deux cartes.

Plaintext
Heltec V3 LoRa Transceiver Test - Transmitter module
SX1262 init success!
Start sending packets...
Sending packet at -2 dBm 	Packet data: "Packet #1 at -2 dBm"
Sending packet at 5 dBm 	Packet data: "Packet #2 at 5 dBm"
Sending packet at 10 dBm 	Packet data: "Packet #3 at 10 dBm"
Plaintext
Heltec V3 LoRa Transceiver Test - Receiver module
SX1262 init success!
Start receiving packets... 
Packet received	RSSI: -63.00 dBm	SNR: 11.00 dB	Packet data: "Packet #1 at -2 dBm"
Packet received	RSSI: -56.00 dBm	SNR: 11.50 dB	Packet data: "Packet #2 at 5 dBm"
Packet received	RSSI: -52.00 dBm	SNR: 10.75 dB	Packet data: "Packet #3 at 10 dBm"

Interprétation de ces résultats

Il faut déjà noter que les trois paquets sont envoyés et reçus sans erreur. Ce qui signifie que la chaîne complète (émission → réception) est opérationnelle.

Le RSSI augmente avec la puissance Ă©mise, ce qui signifie que le PA (amplificateur d’Ă©mission) rĂ©pond correctement aux rĂ©glages et qu’il n’est pas endommagĂ© (sa rĂ©ponse est bien calibrĂ©e).

Le SNR est stable autour de 10-11 dB, indiquant que le LNA (amplificateur de réception) a un gain linéaire et le bruit de fond est faible.

L’attĂ©nuation constante d’environ 61-62 dB est parfaitement normale et s’explique par la perte en espace libre Ă  1 mètre (32 dB) et probablement le rendement des antennes (25 dB au mieux).

Note : Les modules Heltec V3, Ă©quipĂ©s du chip SX1262, intègrent un mĂ©canisme de protection de l’amplificateur de puissance (PA) en cas de dĂ©sadaptation d’antenne (y compris une antenne dĂ©connectĂ©e). Ce circuit, appelĂ© « PA clamping », dĂ©tecte les surtensions internes causĂ©es par une mauvaise adaptation et rĂ©duit automatiquement la puissance de sortie du PA afin de protĂ©ger les composants internes et garantir la fiabilitĂ© Ă  long terme du chip.

Extrait de la documentation du module SX1262

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?