Wireless communication over long distances is a critical requirement in modern IoT systems. While technologies like Wi-Fi, Bluetooth, and traditional RF modules are widely used, they all come with limitations—high power consumption, short range, or costly infrastructure. This is where LoRa (Long Range) technology stands out.
In this guide, we demonstrate how to interface the Reyax RYLR999 LoRa module with an Arduino UNO to create a reliable, low-power, long-range wireless communication link. This article is a concise version of our complete hands-on tutorial, designed to help you understand the working principle, wiring, and communication flow before diving into the full project.
What is LoRa and Why Is It Used?
LoRa is a low-power wireless communication technology developed for transmitting small packets of data over very long distances—often 15–20 km in open environments. It operates in license-free ISM bands such as 433 MHz, 868 MHz, and 915 MHz, making it ideal for battery-powered IoT devices like environmental sensors, alarms, and remote monitoring systems.
Instead of focusing on high data rates, LoRa prioritizes range, reliability and power efficiency, allowing devices to remain in sleep mode for most of their operating life.
Introduction to the Reyax RYLR999 Module
The RYLR999 by Reyax Technology is a versatile wireless module that combines LoRa (868/915 MHz) and Bluetooth Low Energy (BLE 2.4 GHz) in a single compact device. What makes this module unique is its dual UART architecture—one interface dedicated to LoRa communication and another for BLE.
The module is configured using AT commands, allowing users to set device addresses, network IDs, frequencies, and transmission parameters without complex radio programming. It can even function as a BLE-to-LoRa bridge, enabling a smartphone to send data over LoRa indirectly.
RYLR999 Pin Overview
VDD – 5V regulated power supply
GND – Common ground
RXD_LoRa – Receives AT commands and data (3.3V logic)
TXD_LoRa – Outputs received LoRa data and command responses
RST – Active-low reset pin
Interfacing RYLR999 with Arduino UNO
To demonstrate LoRa communication, two Arduino-based nodes are used:
Initiator Node (Transmitter): Periodically sends the message “Are you there?”
Responder Node (Receiver): Waits for incoming data and replies with “Yes”
This two-way communication verifies successful LoRa transmission and reception using AT commands.
Wiring Connections – Initiator (Transmitting Side)
In the initiator setup, the Arduino UNO communicates with the RYLR999 module via UART and displays transmission status on a 16×2 I2C LCD.
Power Connections
- Arduino 5V → RYLR999 VDD
- Arduino GND → RYLR999 GND
UART Connections (Using Voltage Level Shifter)
Since the Arduino UNO uses 5V logic and the RYLR999 operates at 3.3V logic, a bi-directional voltage shifter is required:
RYLR999 TXD_LoRa → LV1 pin of Voltage shifter
Arduino RX → HV1 output of voltage shifter module
TXD pin of Arduino → HV2 pin of voltage shifter
RxD LoRa pin → LV2 output of voltage shifter
The voltage shifter itself must be powered correctly:
- Arduino 5V → HV
- Arduino 3.3V → LV
I2C LCD Connections
- VCC → 5V
- GND → GND
- SDA → A4
- SCL → A5
Ensure the I2C address jumpers (A0, A1, A2) are not shorted when using address 0x27, as used in the code.
Wiring Connections – Responder (Receiving Side)
The responder circuit is simpler because no LCD is used. The Arduino continuously listens for LoRa messages and sends a reply when data is received.
Power and UART connections remain identical to the initiator setup. The voltage level shifter is still required for safe UART communication. Only the LCD wiring is omitted.
Arduino Code for Initiator Setup
/*
Code to send “Are you there?” from one Arduino to another Arduino using RLYR999 Module
and receive reply and display complete communication on I2C LCD by platwithcircuit.com
*/
#include <LiquidCrystal_I2C.h>
#define REPLY_TIMEOUT_IN_MS 300
#define REPLY_END_CHAR '\n'
#define MODULE_ADDRESS 0
#define RECEIVERS_ADDRESS 1
#define MIN_CHAR_TO_RCV 1
#define WAIT_FOR_RECIVERS_REPLY 3000
#define DELAY_BW_REPS 1000
// Init LCD at 0x27, 16x2
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
boolean boRetVal = false;
// begin serial communication at baud 115200,n,8,1
// to communicate with the RF module
Serial.begin(115200);
// initialize the LCD
lcd.init();
// Turn ON the Backlight
lcd.backlight();
// Clear the display buffer
lcd.clear();
flushBuffer(); // clear rx data
// Reset settings to factory defaults
boRetVal = boRestoreFactoryDefaults();
// setting the address if reset successfully
if (boRetVal == true) {
flushBuffer(); // clear rx data
boRetVal = boSetAddress();
}
if (boRetVal == true) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Module Init");
lcd.setCursor(0, 1);
lcd.print("Successful");
delay(1000);
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Module Init");
lcd.setCursor(0, 1);
lcd.print("Failed");
while (1)
;
}
}
void loop() {
String request = "Are you there?";
String expected_reply = "Yes";
bool boRetVal = false;
flushBuffer(); // clear rx data
// transmits String named request
boRetVal = boSendData(request);
if (boRetVal == true) {
// Displaying Sent Msg
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Msg Sent:");
lcd.setCursor(0, 1);
lcd.print(request);
delay(1000);
boRetVal = chkReply(expected_reply, REPLY_END_CHAR, WAIT_FOR_RECIVERS_REPLY);
if (boRetVal == true) {
// Displaying received Msg
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Msg Received:");
lcd.setCursor(0, 1);
lcd.print(expected_reply);
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("No reply received.");
}
} else {
// Displaying Failed Msg
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Msg Sending");
lcd.setCursor(0, 1);
lcd.print("Failed");
}
delay(DELAY_BW_REPS); // wait before sending again
}
void sendCrLf(void) {
Serial.write(0x0D); // Carriage Return
Serial.write(0x0A); // Line Feed
}
void flushBuffer(void) {
while (Serial.available() > 0) {
Serial.read();
}
}
bool chkReply(String chkString, char receiveUntil, unsigned int timeout) {
String receivedString; // save received data in this string object
bool boReturnValue = false; // function's return value
// wait for reply
do {
timeout--;
delay(1); // delay of 1 ms
} while ((Serial.available() < MIN_CHAR_TO_RCV) && (timeout > 0));
if (timeout) {
// if timeout is left then a reply is received check for the string in the reply
receivedString = Serial.readStringUntil(receiveUntil);
if (receivedString.indexOf(chkString) != -1) {
boReturnValue = true;
} else {
boReturnValue = false;
}
} else {
boReturnValue = false;
}
// return result
return boReturnValue;
}
bool boRestoreFactoryDefaults(void) {
const char factoryDefaultCmd[] = "AT+FACTORY"; // command to be sent
bool boReturnValue = false; // function's return value
char downCounter = 100; // Down counter to wait for reply
String receivedString; // save received data in this string object
String chkRcvString1 = "+FACTORY";
String chkRcvString2 = "+READY";
// send command
Serial.print(factoryDefaultCmd);
sendCrLf();
// check first string in reply
boReturnValue = chkReply(chkRcvString1, REPLY_END_CHAR, REPLY_TIMEOUT_IN_MS);
if (boReturnValue == true) {
// check second string in reply
boReturnValue = chkReply(chkRcvString2, REPLY_END_CHAR, REPLY_TIMEOUT_IN_MS);
}
// return result
return boReturnValue;
}
bool boSetAddress(void) {
const char setAddressCmd[] = "AT+ADDRESS="; // command to be sent
bool boReturnValue = false; // function's return value
String chkRcvString = "+OK";
// send command
Serial.print(setAddressCmd);
Serial.print(MODULE_ADDRESS);
sendCrLf();
// check reply
boReturnValue = chkReply(chkRcvString, REPLY_END_CHAR, REPLY_TIMEOUT_IN_MS);
// return result
return boReturnValue;
}
bool boSendData(String data) {
const char sendDataCmd[] = "AT+SEND="; // command to be sent
bool boReturnValue = false; // function's return value
String chkRcvString = "+OK";
// send command
Serial.print(sendDataCmd);
Serial.print(RECEIVERS_ADDRESS);
Serial.print(',');
Serial.print(data.length());
Serial.print(',');
Serial.print(data);
sendCrLf();
// check reply
boReturnValue = chkReply(chkRcvString, REPLY_END_CHAR, REPLY_TIMEOUT_IN_MS);
// return result
return boReturnValue;
}
For Arduino Code for Responder Setup visit: How to Interface Reyax RYLR999 LoRa Module with Arduino

Top comments (0)