Introduction

Interfacing ESP32 with AdaFruit LoRa RFM69HCW

LoRa technology was developed by a company called Semtech and it is a new wireless protocol designed specifically for long-range, low-power communications. LoRa stands for Long Range Radio and is mainly targeted for M2M and IoT networks. This technology will enable public or multi-tenant networks to connect a number of applications running on the same network. LoRa Alliance was formed to standardize LPWAN (Low Power Wide Area Networks) for IoT and is a non-profit association which features membership from a number of key market shareholders such as CISCO, actility, MicroChip, IBM, STMicro, SEMTECH, Orange mobile and many more. This alliance is key to providing interoperability among multiple nationwide networks.I will also post some tutorial links where I have interfaced Adafruit AdaFruit LoRa RFM69HCW with other microcontrollers. If you have any queries about it, ask in the comments and I will resolve it.

ESP32 has plethora of wireless communication capabilities. It supports WIFI and BLE out of box. ESP32 has capabilities of WIFI and BLE mesh Also. However these wireless features has low range of 50-100meters at best, While Lora practically supports 10km with 100mw of transmit power. So Adding a Lora Module can really make the ESP32 a true Wireless capability both in short and Long Range.

Hardware Introduction

ESP32 MODULE

ESP32-WROOM-32 is a powerful, generic Wi-Fi+BT+BLE MCU module that targets a wide variety of applications, ranging from low-power sensor networks to the most demanding tasks, such as voice encoding, music streaming and MP3 decoding.

At the core of this module is the ESP32-D0WDQ6 chip*. The chip embedded is designed to be scalable and adaptive. There are two CPU cores that can be individually controlled, and the CPU clock frequency is adjustable from 80 MHz to 240 MHz. The chip also has a low-power co-processor that can be used instead of the CPU to save power while performing tasks that do not require much computing power, such as monitoring of peripherals. ESP32 integrates a rich set of peripherals, ranging from capacitive touch sensors, Hall sensors, SD card interface, Ethernet, high-speed SPI, UART, I²S and I²C.

A screenshot of a computer

Description automatically generated with low confidence

Picture 1. Esp32 devkit  pin out

Features

Table

Description automatically generated

ESP32 Processor

  • • Xtensa® single-/dual-core 32-bit LX6 microprocessor(s)
  • • CoreMark® score:
  • – 1 core at 240 MHz: 504.85 CoreMark; 2.10 CoreMark/MHz
  • – 2 cores at 240 MHz: 994.26 CoreMark; 4.14 CoreMark/MHz
  • • 448 KB ROM
  • • 520 KB SRAMMemory

Security

  • Secure boot
  • Flash encryption
  • 1024-bit OTP, up to 768-bit for customers
  • Cryptographic hardware acceleration

Peripherals

  • 34 × programmable GPIOs
  • 12-bit SAR ADC up to 18 channels
  • 2 × 8-bit DAC
  • 10 × touch sensors
  • 4 × SPI
  • 2 × I2S
  • 2 × I2C
  • 3 × UART
  • 1 host (SD/eMMC/SDIO)
  • 1 slave (SDIO/SPI)
  • Ethernet MAC interface with dedicated DMA and IEEE 1588 support
  • TWAI®, compatible with ISO 11898-1 (CAN Specification 2.0)
  • RMT (TX/RX)
  • Motor PWM
  • LED PWM up to 16 channels
  • Hall sensor

Power

  • 2.7-5.5 volts

ESP32 Pin Description

Sensor 

Adafruit-RFM-69-Lora
Adafruit-RFM-69-Lora Module

Adafruit LoRa RFM69HCW (Sensor Description) 

The RFM69HCW is a transceiver module capable of operation over a wide frequency range, including the 315,433,868 and 915MHz license-free ISM (Industry Scientific and Medical) frequency bands. All major RF communication parameters are programmable and most of them  can  be  dynamically set.  The  RFM69HCW  offers  the unique advantage of programmable narrow-band and wide- band communication modes.The RFM69HCW is optimized for low power consumption while offering high RF output power and channelized operation. Compliance  ETSI and FCC regulations.

In order to better use RFM69HCW modules, this specification also involves a large number of the parameters and functions of its core chip RF69H’s,including those IC pins which are not leaded out. All of these can help customers gain a better understanding of the performance of RFM69HCW modules, and enhance the application skills.

Adafruit LoRa RFM69HCW specification:

  • +20 dBm – 100 mW Power Output Capability
  • High Sensitivity: down to -120 dBm at 1.2 kbps
  • High Selectivity: 16-tap FIR Channel Filter
  • Bullet-proof front end: IIP3 = -18 dBm, IIP2 = +35 dBm,80 dB Blocking Immunity, no Image
  • Frequency response
  • Low current: Rx = 16 mA, 100nA register retention
  • Programmable Pout: -18 to +20 dBm in 1dB steps
  • Constant RF performance over voltage range of module
  • FSK Bit rates up to 300 kb/s
  • Fully integrated synthesizer with a resolution of 61 Hz
  • FSK, GFSK, MSK, GMSK and OOK modulations
  • Built-in Bit Synchronizer performing Clock Recovery
  • Incoming Sync Word Recognition
  • 115 dB+ Dynamic Range RSSI
  • Automatic RF Sense with ultra-fast AFC
  • Packet engine with CRC-16, AES-128, 66-byte FIFO
  • Built-in temperature sensor
  • Module Size:16X16mm

Operating specifications:

All pins going into the breakout have level shifting circuitry to make them 3-5V logic level safe. Use whatever logic level is on Vin!

SCK – This is the SPI Clock pin, its an input to the chip

MISO – this is the Microcontroller In Serial Out pin, for data sent from the radio to your processor, 3.3V logic level

MOSI – this is the Microcontroller Out Serial In pin, for data sent from your processor to the radio

CS – this is the Chip Select pin, drop it low to start an SPI transaction. Its an input to the chip

RST – this is the Reset pin for the radio. It’s pulled high by default which is reset. Pull LOW to turn on the radio

G0 – the radio’s “GPIO 0” pin, also known as the IRQ pin, used for interrupt request notification from the radio to the microcontroller, 3.3V logic level

Schematic Diagram

Pin Connection Table

Source Code

FOR TRANSMITTER: 

#include <SPI.h>
#include <RH_RF95.h>

#define RFM95_CS 34
#define RFM95_RST 27

// Change to 434.0 or other frequency, must match RX's freq!
#define RF95_FREQ 915.0

// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);

// Blinky on receipt
#define LED 13

void setup() 
{
  pinMode(LED, OUTPUT);     
  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);

  while (!Serial);
  Serial.begin(9600);
  delay(100);

  Serial.println("Arduino LoRa RX Test!");
  
  // manual reset
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  while (!rf95.init()) {
    Serial.println("LoRa radio init failed");
    while (1);
  }
  Serial.println("LoRa radio init OK!");

  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  if (!rf95.setFrequency(RF95_FREQ)) {
    Serial.println("setFrequency failed");
    while (1);
  }
  Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);

  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on

  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(23, false);
}

void loop()
{
  if (rf95.available())
  {
    // Should be a message for us now   
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);
    
    if (rf95.recv(buf, &len))
    {
      digitalWrite(LED, HIGH);
      RH_RF95::printBuffer("Received: ", buf, len);
      Serial.print("Got: ");
      Serial.println((char*)buf);
       Serial.print("RSSI: ");
      Serial.println(rf95.lastRssi(), DEC);
      
      // Send a reply
      uint8_t data[] = "And hello back to you";
      rf95.send(data, sizeof(data));
      rf95.waitPacketSent();
      Serial.println("Sent a reply");
      digitalWrite(LED, LOW);
    }
    else
    {
      Serial.println("Receive failed");
    }
  }
}

FOR RECEIVER:

#include <SPI.h>
#include <RH_RF95.h>

#define RFM95_CS 34
#define RFM95_RST 27

// Change to 434.0 or other frequency, must match RX's freq!
#define RF95_FREQ 915.0

// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);

// Blinky on receipt
#define LED 13

void setup() 
{
  pinMode(LED, OUTPUT);     
  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);

  while (!Serial);
  Serial.begin(9600);
  delay(100);

  Serial.println("Arduino LoRa RX Test!");
  
  // manual reset
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  while (!rf95.init()) {
    Serial.println("LoRa radio init failed");
    while (1);
  }
  Serial.println("LoRa radio init OK!");

  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  if (!rf95.setFrequency(RF95_FREQ)) {
    Serial.println("setFrequency failed");
    while (1);
  }
  Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);

  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on

  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(23, false);
}

void loop()
{
  if (rf95.available())
  {
    // Should be a message for us now   
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);
    
    if (rf95.recv(buf, &len))
    {
      digitalWrite(LED, HIGH);
      RH_RF95::printBuffer("Received: ", buf, len);
      Serial.print("Got: ");
      Serial.println((char*)buf);
       Serial.print("RSSI: ");
      Serial.println(rf95.lastRssi(), DEC);
      
      // Send a reply
      uint8_t data[] = "And hello back to you";
      rf95.send(data, sizeof(data));
      rf95.waitPacketSent();
      Serial.println("Sent a reply");
      digitalWrite(LED, LOW);
    }
    else
    {
      Serial.println("Receive failed");
    }
  }
}

CODE EXPLANATION:

For both Transmitter and Receiver the void Setup Code will be the same. Import the libraries SPI and RF_RH95. Define the pins and initialize.

Coming the loop of transmitter and receiver:

TRANSMISSION CODE:

void loop()
{
  delay(1000); // Wait 1 second between transmits, could also 'sleep' here!
  Serial.println("Transmitting..."); // Send a message to rf95_server
  
  char radiopacket[20] = "Hello World #      ";
  itoa(packetnum++, radiopacket+13, 10);
  Serial.print("Sending "); Serial.println(radiopacket);
  radiopacket[19] = 0;
  
  Serial.println("Sending..."); delay(10);
  rf95.send((uint8_t *)radiopacket, 20);

  Serial.println("Waiting for packet to complete..."); delay(10);
  rf95.waitPacketSent();

If you are using the transmitter, this code will wait 1 second, then transmit a packet with “Hello World #” and an incrementing packet number. Its pretty simple, the delay does the waiting, you can replace that with low power sleep code. Then it generates the packet and appends a number that increases every tx. Then it simply calls send to transmit the data, and passes in the array of data and the length of the data.

Note that this does not do any addressing or subnetworking – if you want to make sure the packet goes to a particular radio, you may have to add an identifier/address byte on your own!

Then you call waitPacketSent() to wait until the radio is done transmitting. You will not get an automatic acknowledgement from the other radio unless it knows to send back a packet. Think of it like the ‘UDP’ of radio – the data is sent, but it’s not certain it was received! Also, there will not be any automatic retries.

RECEIVER CODE:

void loop()
{
  if (rf95.available())
  {
    // Should be a message for us now   
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);
    
    if (rf95.recv(buf, &len))
    {
      digitalWrite(LED, HIGH);
      RH_RF95::printBuffer("Received: ", buf, len);
      Serial.print("Got: ");
      Serial.println((char*)buf);
       Serial.print("RSSI: ");
      Serial.println(rf95.lastRssi(), DEC);

The Receiver has the same exact setup code, but the loop is different. Instead of transmitting, it is constantly checking if there’s any data packets that have been received. available() will return true if a packet with proper error-correction was received. If so, the receiver prints it out in hex and also as a ‘character string’

It also prints out the RSSI which is the receiver signal strength indicator. This number will range from about -15 to about -100. The larger the number (-15 being the highest you’ll likely see) the stronger the signal.

Once done it will automatically reply, which is a way for the radios to know that there was an acknowledgement. 

GITHUB LINK

https://github.com/iottrends/iottrends/tree/main/LoRa%20Modules/Adafruit%20RFM69HCW

0 0 votes
Article Rating
Previous articleHow to Interface Raspberry Pi PICO with ADAFRUIT GPS
Next articleGetting Started with Mqtt on ESP32 | MicroPython
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments