Long-distance communication (or constant transmissions of data) has become an essential part of modern industries, the medical sector, and even the residential sector. These data include the status/outputs of sensors, operation logs of machines, images or videos, scheduled events, etc. The transfer of these data between machines via wired or wireless medium without human input is called Machine-to-Machine communication (M2M). The same communication if done using a cloud platform instead of point-to-point becomes IoT. Realizing the growing demand for faster, more secure, and more efficient communication, technologies that enable long-range communication wirelessly were developed. Out of many ones namely “LoRa” stood out by satisfying the need for low power consumption, higher communication range, interference immunity, mobility, and energy efficiency. It was designed for sensor data transmission at low speeds (around 20-50 Kbps).
In this article, we will be interfacing Arduino Nano and a special transponder module called LoRa SX1278 (which utilizes the patented “LoRa” modulation technique). After interfacing the module we will also learn to transmit some data between two LoRa sensor nodes. In this article, we will be using the DHT11 sensor to transmit humidity and temperature data as an example.
After completing this article you will be able to:
- Interface the LoRa-02 module with Arduino Nano.
- Transmit sensor data between LoRa nodes.
- Display the readings on the LCD.
Introduction to “LoRa”:
“LoRa” meaning “Long Ranged”, is a well-known radio communication method. LoRa in itself is a physical layer protocol that is derived from Chirp Spread Spectrum (CSS). CSS is a wideband linear frequency modulation in which carrier frequency varies for the defined extent of time. Whereas a chirp is a sinusoidal signal whose frequency increases or decreases over time (often with a polynomial relationship between time and frequency). Due to this, CSS is ideal for applications requiring low power usage with lower data rates (<1 Mbit/s), longer ranges, and when devices moving at high speeds are part of your network.
The “LoRa module” supports different ISM band frequencies, namely 868MHz (Europe), 915MHz (North America), and 433 MHz (Asia), and encodes information on radio waves using chirp pulses. So we can conclude that LoRa targets key: Internet of Things (IoT) requirements such as bi-directional communication, end-to-end security, mobility, and localization services.
Figure 1: LoRa SX1278 Module
For this article, we will be using a specific LoRa SX1278 module consisting of a “Ra-02” chip from AI-Thinker. This chip supports the ISM band of range: 410-525MHz, Power amplifier (PA) of +18dBm, and modulation techniques of LoRa/FSK/OOK. The features and Specification of the whole package are given as follows:
Features of SX1278 LoRa:
- works in ISM bands (Industrial Standard Medical band)
- Half-duplex SPI communication
- Low powered consumption
- High-range coverage (~10Km)
- Operates on low bandwidth
- Supports Preamble detection
- excellent blocking and interference immunity
- Built-in temperature sensor and battery indicator.
- Built-in bit synchronizer for clock recovery
Specification:
- Module Model: Ra-02
- Package: SMD-16
- Size/Dim: 17*16*(3.2 ± 0.1) mm
- Programmable bit rate up to 300 kbps
- Frequency Range: 410 MHz – 525 MHz
- Operating temperature: -30℃ to 85℃
- Power Supply: 2.5 ~ 3.7V (Typically : 3.3V)
- industry-leading 168dB max Link budget
- High sensitivity: down to -148 dBm
- +20 dBm (i.e ~ 100 mW) RF transmit power
- can use FSK, GFSK, MSK, GMSK, OOK, and LoRa (default) modulation
- Low RX current of 9.9 mA, 200 nA register retention
- Bullet-proof front end: IIP3 = -11 dBm
- 127dB RSSI Dynamic Range
Components Required
Arduino Nano x 2
LoRa-2 SX1278 x 2
DHT11 (Hum & Temp) x 1
16X2 – I2C LCD display x 2
Interfacing Sx1278 with Nano:
The table given below describes the wiring between I2C_LCD, LoRa, and Nano for both the transmitter and receiver nodes.
As the figures below demonstrate, the I2C_LCD has its VCC & GND pin connected to 5V & ground, whereas its SDA & SCL pin to analog pins A4 & A5 of Nano.
For LoRa and Nano interfacing below table is given:
LoRa-02 SX1278 | NANO pins |
VCC (3.6V max) | 3.3V |
GND | GND |
SCK (SPI clock) | D13 |
DIO0 (Digital I/O) | D2 |
RST (Reset) | D9 |
NSS (Chip select) | D10 |
MOSI (SPI data input) | D11 |
MISO (SPI data output) | D12 |
Use 3.3V of Arduino Nano to connect it to the VCC pin of the LoRa module. Connect all the GND pins to GND. Connect the RST pin to D9 and DIO0 to D2 of Arduino. Connect the SPI Pins NSS, MOSI, MISO, and SCK to Arduino D10, D11, D12, and D13 Arduino respectively as shown in the circuit diagram above. Then connect the VCC, DATA & GND pins of the DHT11 (humidity & temperature) sensor to the 5V, D3, and GND pins of Nano, respectively as shown above.
Use 3.3V of Arduino Nano to connect it to the VCC pin of the LoRa module. Connect all the GND pins to GND. Connect the RST pin to D9 and DIO0 to D2 of Arduino. Connect the SPI Pins NSS, MOSI, MISO, and SCK to Arduino D10, D11, D12, and D13 Arduino respectively as shown in the circuit diagram above. Then connect the D3 pin of nano to the cathode of LED and then to gnd via a 470Ω resistor as shown.
How to transmit Sensor Data:
Firstly we require two libraries for the operation of the LoRa-02 SX1278 module via the Nano, namely <SPI.h> and <LoRa.h>. The “SPI.h” library is responsible for serial communication between module and nano whereas “LoRa.h” is responsible for transmitting/Receiving operations.[Note: This “LoRa.h” library allows you to send data to any radios in range with the same radio parameters. All data are broadcasted without any addressing.]. For the sensor, we are using DHT11(temperature & humidity sensor). So the <DHT.h> library is required for extracting the temperature and humidity data captured by the DHT11 sensor.
After successfully interfacing all the hardware as instructed, the step-by-step explanation of the code for sensor data reading and transmission process is given below:
Software Code
Transmitter Side Code
Including necessary libraries:
Three libraries: DHT.h, SPI.h, and LoRa.h are included for sensor data generation/reading, Serially transferring that data from Nano to the LoRa module and then transmitting it.
1 2 3 4 5 |
#include <SPI.h> #include <LoRa.h> #include <DHT.h> |
Defining important parameters:
Here the incoming data from DHT11 is assigned to digital pin D3 of nano. We also have to specify the sensor model type while initializing the sensor. Then two variables to hold the humidity and temperature data are required.
1 2 3 4 5 6 7 8 9 |
#define DHTPIN A0 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); // initialize the sensor int hum; //Stores humidity value float temp; //Stores temperature value |
Initialize Setup Function:
Here we initialize the LoRa library with 433MHz frequency using LoRa.begin(). This function also scans if a message has been sent or not. If not It displays” Starting LoRa failed!”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void setup() { Serial.begin(9600); dht.begin(); while (!Serial); Serial.println("LoRa Sender"); if (!LoRa.begin(433E6)) // initialize the LoRa 433E6 => 433Mhz { Serial.println("Starting LoRa failed!"); while (1); } } |
Read and transmit sensor Data:
Read the temperature and humidity data using “dht” objects and store them into respective variables. Then transmit the using LoRa.print() function. When the transmitted message ends with a “*” character which is then used by the receiver to break the combined serial characters into a meaningful message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void loop() { temp = dht.readTemperature(); hum = dht.readHumidity(); Serial.println("........................."); Serial.println("Sending packet: "); // send Humidity packet LoRa.beginPacket(); //Start the sequence of sending a packet. LoRa.print("Humidity: "); LoRa.print(hum); LoRa.print("%"); LoRa.print(" Temperature:"); LoRa.print(temp); LoRa.print("C"); LoRa.print("*"); //this indicates the end of sending string //Display sent data in serial monitor Serial.print("Humidity: "); Serial.print(hum); Serial.print("%"); Serial.print(" Temperature:"); Serial.print(temp); Serial.println("C"); Serial.println(""); LoRa.endPacket(); delay(500); } |
After the transmission is done, now for step by step explanation of the code performing the data reception process is given below:
Receiver Side Code
Initialize the necessary libraries and parameters:
We include the SPI.h and LoRa.h libraries for serial communication and LoRa module operations. Then a String type variable (inString) captures the incoming character and merges them into one string. The counter is the count no of captured characters.
1 2 3 4 5 6 7 |
#include <SPI.h> #include <LoRa.h> String inString = ""; // string to hold input int counter = 0; // to count no of characters |
Initialize the Setup function:
Here we initialize the LoRa library with 433MHz frequency using LoRa.begin(). This function scan if a message has been sent or not. If not It displays “Starting LoRa failed!”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void setup() { Serial.begin(9600); while (!Serial); Serial.println("LoRa Receiver"); if (!LoRa.begin(433E6)) { Serial.println("Starting LoRa failed!"); while (1); } } |
Void loop:
Here we will check if a packet has been received using “LoRa.parsePacket()”. This function returns the packet size in bytes or 0 if no packet was received. Then read the character and append them to a string variable (inString). Then display the message/combined data in the serial monitor. If the “*” character is received, then it indicates the end of the message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void loop() { // try to parse packet int packetSize = LoRa.parsePacket(); if (packetSize) // continue if packet is received { // received a packet Serial.println(""); Serial.println("..................................."); Serial.println("Received packet: "); // read packet inString = ""; while (LoRa.available()) { char incoming = (char)LoRa.read(); //read each chr if( incoming == '*') { break; } //breaks the appending action; if '*' inString += incoming; //append all chr counter ++; } } //if any string is captured if (counter>0) { Serial.println(inString); //display in monitor } counter =0; } |
Complete Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/* *** Transmitter Code*** */ #include <SPI.h> #include <LoRa.h> #include <DHT.h> #define DHTPIN A0 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); int hum; float temp; //Stores temperature value void setup() { Serial.begin(9600); dht.begin(); while (!Serial); Serial.println("LoRa Sender"); if (!LoRa.begin(433E6)) // 433Mhz { Serial.println("Starting LoRa failed!"); while (1); } } void loop() { temp = dht.readTemperature(); hum = dht.readHumidity(); Serial.println("........................."); Serial.println("Sending packet: "); // send Humidity packet LoRa.beginPacket(); LoRa.print("Humidity: "); LoRa.print(hum); LoRa.print("%"); LoRa.print(" Temperature:"); LoRa.print(temp); LoRa.print("C"); //Display sent data in serial monitor Serial.print("Humidity: "); Serial.print(hum); Serial.print("%"); Serial.print(" Temperature:"); Serial.print(temp); Serial.println("C"); Serial.println(""); LoRa.endPacket(); delay(500); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/* *** Receiver Code*** */ #include <SPI.h> #include <LoRa.h> String inString = ""; // string to hold input int counter = 0; // to count no of characters void setup() { Serial.begin(9600); while (!Serial); Serial.println("LoRa Receiver"); if (!LoRa.begin(433E6)) { Serial.println("Starting LoRa failed!"); while (1); } } void loop() { // try to parse packet int packetSize = LoRa.parsePacket(); if (packetSize) // continue if packet is received { // received a packet Serial.println(""); Serial.println("..................................."); Serial.println("Received packet: "); // read packet inString = ""; while (LoRa.available()) { char incoming = (char)LoRa.read(); //read each chr inString += incoming; //append all chr counter ++; } } //if any string is captured if (counter>0) { Serial.println(inString); //display in monitor } counter =0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/* *** Receiver Code*** */ #include <SPI.h> #include <LoRa.h> String inString = ""; // string to hold input int counter = 0; // to count no of characters void setup() { Serial.begin(9600); while (!Serial); Serial.println("LoRa Receiver"); if (!LoRa.begin(433E6)) { Serial.println("Starting LoRa failed!"); while (1); } } void loop() { // try to parse packet int packetSize = LoRa.parsePacket(); if (packetSize) // continue if packet is received { // received a packet Serial.println(""); Serial.println("..................................."); Serial.println("Received packet: "); // read packet inString = ""; while (LoRa.available()) { char incoming = (char)LoRa.read(); //read each chr inString += incoming; //append all chr counter ++; } } //if any string is captured if (counter>0) { Serial.println(inString); //display in monitor } counter =0; } |
Displaying the Sensor Data in LCD
The transfer of sensor data has been modified and displayed in LCD. The code is as follows:
Transmitter Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
#include <SPI.h> #include <LoRa.h> #include <DHT.h> #include <LiquidCrystal_I2C.h> //specifyting LiquidCrystal_I2C lcd(0x27,16,2); #define DHTPIN A0 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); int hum; float temp; //Stores temperature value void setup() { Serial.begin(9600); //initialize the LCD display lcd.init(); lcd.backlight(); lcd.clear(); lcd.print("LoRa"); lcd.setCursor(0,1); lcd.print("Sender"); delay(2000); dht.begin(); //while (!Serial); //Serial.println("LoRa Sender"); if (!LoRa.begin(433E6)) // 433Mhz { Serial.println("Starting LoRa failed!"); while (1); } } void loop() { temp = dht.readTemperature(); hum = dht.readHumidity(); LoRa.beginPacket(); Serial.println("........................."); Serial.println("Sending packet: "); // send Humidity packet LoRa.print(hum); LoRa.print("%"); LoRa.print(temp); LoRa.print("C"); LoRa.print("*"); //Display in LCD lcd.clear(); lcd.setCursor(0,0); lcd.print("Hum:"); lcd.setCursor(4,0); lcd.print(hum); lcd.setCursor(7,0); lcd.print("%"); lcd.setCursor(0,1); lcd.print("Temp:"); lcd.setCursor(5,1); lcd.print(temp); lcd.setCursor(10,1); lcd.print((char)0xDF); // displaying the “degree” symbol lcd.print("C"); //Display sent data in serial monitor Serial.print("Humi: "); Serial.print(hum); Serial.println("%"); Serial.print("Temp: "); Serial.print(temp); Serial.println("C"); Serial.println(""); LoRa.endPacket(); delay(1000); } |
Receiver Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
#include <SPI.h> #include <LoRa.h> #include <LiquidCrystal_I2C.h> //specifyting LiquidCrystal_I2C lcd(0x27,16,2); String inString = ""; // string to hold input String Humi ; // string to hold Humidity input String Temp ; // string to hold Temperature input int counter = 0; void setup() { Serial.begin(9600); //pinMode(LED,OUTPUT); //initialize the LCD display lcd.init(); lcd.backlight(); lcd.clear(); lcd.print("LoRa"); lcd.setCursor(0,1); lcd.print("Receiver"); delay(2000); //setup LoRa //while (!Serial); //Serial.println("LoRa Receiver"); if (!LoRa.begin(433E6)) { Serial.println("Starting LoRa failed!"); while (1); } } void loop() { // try to parse packet int packetSize = LoRa.parsePacket(); if (packetSize) { // received a packet Serial.println(""); Serial.println("..................................."); Serial.println("Received packet: "); // read packet inString = ""; Humi = ""; Temp = ""; while (LoRa.available()) { char incoming = (char)LoRa.read(); //read a chr if( incoming == '*') {break;} //breaks the appending action at '*' inString += incoming; counter ++; } } //if any string is captured if (counter>0) { //create str buffer char str[counter+1] ; //Copy it over inString.toCharArray(str, counter+1); //int flag = 0 ; for (int i=0;i<=counter;i++) { char c = str[i]; Serial.print(c); if (i<2 and c!='%'){Humi += c; } else if (i>2 and c!='%' and c!='C' and i<9){Temp +=c;} } //display in LCD lcd.clear(); lcd.setCursor(0,0); lcd.print("Hum:"); lcd.setCursor(4,0); lcd.print(Humi); lcd.setCursor(7,0); lcd.print("%"); lcd.setCursor(0,1); lcd.print("Temp:"); lcd.setCursor(5,1); lcd.print(Temp); lcd.setCursor(10,1); lcd.print((char)0xDF); // displaying the “degree” symbol lcd.print("C"); //Serial display Serial.println(""); Serial.print("HUM: "); Serial.print(Humi); Serial.println("%"); Serial.print("Temp: "); Serial.print(Temp); Serial.println("C"); Serial.print(""); counter =0; } } |
Figure 4: Author Prototype of LoRa Interfacing Transmitter Circuit
Figure 5: Author Prototype of LoRa Interfacing Receiver Circuit