LoRa (Long Range) wireless technology has transformed the way makers and engineers build IoT sensor networks across large areas. Using an SX1278-based module with your Arduino, you can transmit sensor data over distances of 1–10 km in open terrain — without Wi-Fi, without cellular data, and without a monthly SIM bill.
The SX1278 module (sold as Ra-02, AI-Thinker LoRa, or various other names) operates in the 433 MHz band, which is legal for ISM use in India. At just ₹200–400 per module, a pair of them can replace expensive telemetry systems for agricultural monitoring, water tank level sensing, remote weather stations, and industrial remote I/O applications across large properties.
This tutorial covers everything from wiring and library setup to a complete two-node sensor transmission system you can build and deploy today.
What Is LoRa and Why Use the SX1278?
LoRa is a proprietary spread-spectrum modulation technique developed by Semtech. Unlike conventional radio, LoRa uses Chirp Spread Spectrum (CSS) modulation, which spreads the transmission across a wide frequency range. This makes LoRa signals extremely resistant to interference and capable of being detected below the noise floor — hence the remarkable range.
Key characteristics of LoRa at 433 MHz:
- Range: 1–3 km in typical urban environments, 5–15 km in open terrain with elevated antennas
- Data rate: 0.3 to 37.5 kbps (lower data rate = longer range)
- Power: +20 dBm (100 mW) maximum transmit power; receive current ~10 mA
- Frequency: 433 MHz (India ISM band)
- Interface: SPI
The SX1278 is a Semtech transceiver IC that implements LoRa modulation. The popular Ra-02 module from AI-Thinker combines the SX1278 IC with an SMA antenna connector, RF matching network, and decoupling capacitors on a compact 16mm × 16mm board. It runs at 3.3V logic.
Important: The SX1278 operates at 3.3V logic. Connecting SPI pins directly to an Arduino Uno (5V logic) will damage the module. You must use a level shifter or a 3.3V Arduino.
Hardware Required
For a basic two-node LoRa link, you need:
- 2× Arduino Uno (or Nano, or Mega) boards
- 2× SX1278 LoRa module (Ra-02 or compatible)
- 2× Logic level converter (bidirectional, 5V↔3.3V) — since Arduino Uno is 5V
- 2× 3.3V power source (the Arduino’s 3.3V pin can supply ~50 mA which is sufficient for receive mode but marginal for TX — add an AMS1117-3.3 for robust TX power)
- 2× 433 MHz antenna (wire antenna: 164 mm quarter-wave, or SMA antenna if your module has a connector)
- Sensor of your choice (DHT11, DS18B20, HC-SR04, etc.) for the transmitter node
- Jumper wires and breadboard
Level Shifter Note: The SX1278 SPI pins (MOSI, MISO, SCK, NSS, RESET, DIO0) are all 3.3V. Driving them from a 5V Arduino directly will eventually damage the SX1278. A bidirectional level shifter (such as the TXS0108E or BSS138-based modules) must be used on all these lines.
Wiring SX1278 to Arduino
The SX1278 module uses SPI for communication. Here is the complete wiring for Arduino Uno with a level shifter:
Arduino Uno → Level Shifter (HV side) → Level Shifter (LV side) → SX1278:
- 5V → HV | LV → 3.3V (from Arduino 3.3V pin or external 3.3V regulator)
- GND → GND (common ground throughout)
- D13 (SCK) → HV1 | LV1 → SCK
- D12 (MISO) → HV2 | LV2 → MISO
- D11 (MOSI) → HV3 | LV3 → MOSI
- D10 (NSS/CS) → HV4 | LV4 → NSS
- D9 (RESET) → HV5 | LV5 → RST
- D2 (DIO0) → HV6 | LV6 → DIO0
If you are using a 3.3V Arduino (Nano 33 IoT, Nano RP2040, Pro Mini 3.3V), connect directly:
- 3.3V → VCC
- GND → GND
- D13 → SCK, D12 → MISO, D11 → MOSI, D10 → NSS, D9 → RST, D2 → DIO0
Antenna: ALWAYS connect an antenna before powering on the LoRa module. Transmitting without an antenna damages the RF power amplifier. For a simple wire antenna, cut 164 mm (quarter wavelength at 433 MHz) of single-core wire and connect it to the ANT pad.
LoRa Library Installation and Configuration
The best library for SX1278/SX1276-based modules is the LoRa library by Sandeep Mistry, available via the Arduino Library Manager:
- Open Arduino IDE → Sketch → Include Library → Manage Libraries
- Search “LoRa” and install the one by Sandeep Mistry
Basic initialisation:
#include <SPI.h>
#include <LoRa.h>
#define SS_PIN 10
#define RST_PIN 9
#define DIO0_PIN 2
#define FREQUENCY 433E6 // 433 MHz
void setup() {
Serial.begin(115200);
LoRa.setPins(SS_PIN, RST_PIN, DIO0_PIN);
if (!LoRa.begin(FREQUENCY)) {
Serial.println("LoRa init failed!");
while (true);
}
// Optional configuration
LoRa.setSpreadingFactor(10); // 7-12, higher = longer range, lower speed
LoRa.setSignalBandwidth(125E3); // 125 kHz typical
LoRa.setCodingRate4(5); // 4/5 coding rate
LoRa.setTxPower(17); // dBm, 2-20
Serial.println("LoRa ready!");
}
Key parameters to understand:
- Spreading Factor (SF): SF7 is fastest (shortest range), SF12 is slowest (longest range). Each step up doubles the airtime. Start with SF10 for moderate range.
- Bandwidth: 125 kHz is standard. Narrower bandwidth increases sensitivity but requires accurate frequency calibration. Stick with 125 kHz unless you have specific requirements.
- Tx Power: 17–20 dBm for maximum range. Reduce to 2 dBm for short-range testing to avoid saturating the receiver.
Basic Transmitter and Receiver
Transmitter Sketch
#include <SPI.h>
#include <LoRa.h>
int counter = 0;
void setup() {
Serial.begin(115200);
LoRa.setPins(10, 9, 2);
if (!LoRa.begin(433E6)) {
Serial.println("LoRa init failed");
while (true);
}
LoRa.setSpreadingFactor(10);
LoRa.setTxPower(17);
Serial.println("Transmitter ready");
}
void loop() {
Serial.print("Sending packet: ");
Serial.println(counter);
LoRa.beginPacket();
LoRa.print("PKT:");
LoRa.print(counter);
LoRa.endPacket(); // blocking by default
counter++;
delay(3000); // send every 3 seconds
}
Receiver Sketch
#include <SPI.h>
#include <LoRa.h>
void setup() {
Serial.begin(115200);
LoRa.setPins(10, 9, 2);
if (!LoRa.begin(433E6)) {
Serial.println("LoRa init failed");
while (true);
}
LoRa.setSpreadingFactor(10);
Serial.println("Receiver ready");
}
void loop() {
int packetSize = LoRa.parsePacket();
if (packetSize) {
String received = "";
while (LoRa.available()) {
received += (char)LoRa.read();
}
Serial.print("Received: ");
Serial.print(received);
Serial.print(" RSSI: ");
Serial.print(LoRa.packetRssi());
Serial.print(" dBm SNR: ");
Serial.println(LoRa.packetSnr());
}
}
Upload the transmitter to one Arduino+SX1278 combo and the receiver to another. Open both Serial Monitors and you should see packets arriving with RSSI (signal strength) and SNR (signal-to-noise ratio) values. RSSI closer to 0 is better — typically -60 to -90 dBm for good links, -110 to -130 dBm at the sensitivity limit.
Sending Real Sensor Data
For real applications, you need to send structured data. A lightweight approach is to format sensor readings as a compact string or use a binary struct. Here is a sensor node transmitting DHT11 temperature and humidity plus a battery voltage reading:
Sensor Transmitter with Struct Payload
#include <SPI.h>
#include <LoRa.h>
#include <DHT.h>
#define DHT_PIN 4
DHT dht(DHT_PIN, DHT11);
struct SensorPayload {
uint8_t nodeId; // 1 byte: sensor node ID
int16_t temp10; // 2 bytes: temp * 10 (e.g. 265 = 26.5°C)
uint16_t hum10; // 2 bytes: humidity * 10
uint16_t batt_mv; // 2 bytes: battery millivolts
uint16_t packetNum; // 2 bytes: packet counter
};
void setup() {
Serial.begin(115200);
dht.begin();
LoRa.setPins(10, 9, 2);
LoRa.begin(433E6);
LoRa.setSpreadingFactor(10);
LoRa.setTxPower(17);
}
void loop() {
float t = dht.readTemperature();
float h = dht.readHumidity();
int batt = analogRead(A0) * (5000.0 / 1023.0); // crude battery read
if (!isnan(t) && !isnan(h)) {
SensorPayload payload;
payload.nodeId = 1;
payload.temp10 = (int16_t)(t * 10);
payload.hum10 = (uint16_t)(h * 10);
payload.batt_mv = (uint16_t)batt;
payload.packetNum++;
LoRa.beginPacket();
LoRa.write((uint8_t*)&payload, sizeof(payload));
LoRa.endPacket();
Serial.printf("Sent: T=%.1f H=%.1f Batt=%dmVn", t, h, batt);
}
delay(10000); // send every 10 seconds
}
A binary struct payload is only 9 bytes — much more efficient than a CSV string and leaves plenty of room for the LoRa header overhead. Always use integer representations of float values (multiply by 10 or 100) to avoid floating-point size overhead.
Maximising Range and Reliability
Getting the most from your SX1278 link requires attention to antenna, placement, and radio parameters:
Antenna Selection
- Wire antenna (quarter-wave, 164 mm): Free, simple, omnidirectional. Best for most applications. Straighten the wire completely for best results.
- Spring/rubber duck antenna: Compact, 2–3 dBi gain, good for modules with SMA connectors.
- Yagi or directional antenna: For point-to-point links where both ends are fixed, a directional antenna can add 6–12 dBi of gain, dramatically extending range.
Height Matters Most
At 433 MHz, the signal is primarily line-of-sight. Even 2–3 m of additional height can double or triple the practical range. Mount gateway antennas at the highest accessible point — a rooftop, water tank top, or elevated pole dramatically improves coverage.
Spreading Factor Trade-offs
Each SF step (SF7 → SF8 etc.) provides about 2.5 dB more link budget but doubles packet airtime. For sensor networks sending data every few minutes, SF10 or SF11 is a good compromise. For time-critical data, use SF7.
Avoid Metallic Enclosures
Placing the LoRa module inside a metal enclosure blocks the RF signal. Use a plastic (ABS) enclosure, or route the antenna outside the enclosure through an SMA bulkhead connector.
Project: Multi-Node Sensor Gateway
A typical LoRa deployment has multiple sensor nodes sending data to one central gateway. The gateway receives all packets and forwards the data to a computer, display, or internet connection.
Gateway Architecture
- Node 1 (field): DHT11 sensor, solar-powered, sends temperature/humidity every 5 minutes
- Node 2 (water tank): HC-SR04 ultrasonic sensor, battery-powered, sends water level every 10 minutes
- Gateway (house): Arduino Mega + SX1278, USB to PC, logs data to SD card and/or serial monitor
Gateway Receiver Code
#include <SPI.h>
#include <LoRa.h>
struct SensorPayload {
uint8_t nodeId;
int16_t temp10;
uint16_t hum10;
uint16_t batt_mv;
uint16_t packetNum;
};
void setup() {
Serial.begin(115200);
LoRa.setPins(10, 9, 2);
LoRa.begin(433E6);
LoRa.setSpreadingFactor(10);
Serial.println("Gateway ready");
}
void loop() {
int pktSize = LoRa.parsePacket();
if (pktSize == sizeof(SensorPayload)) {
SensorPayload payload;
LoRa.readBytes((uint8_t*)&payload, sizeof(payload));
Serial.print("Node:"); Serial.print(payload.nodeId);
Serial.print(" T:"); Serial.print(payload.temp10 / 10.0, 1);
Serial.print(" H:"); Serial.print(payload.hum10 / 10.0, 1);
Serial.print(" Batt:"); Serial.print(payload.batt_mv);
Serial.print("mV Pkt#:"); Serial.print(payload.packetNum);
Serial.print(" RSSI:"); Serial.print(LoRa.packetRssi());
Serial.println("dBm");
}
}
Frequently Asked Questions
Is LoRa 433 MHz legal to use in India?
Yes. The 433 MHz band (430–440 MHz) is designated as an ISM (Industrial, Scientific, and Medical) band in India under WPC (Wireless Planning and Coordination) rules. Low-power devices (less than 1 watt ERP) operating in this band do not require a licence for non-commercial use. For commercial deployments, consult WPC guidelines. LoRaWAN networks in India primarily use the IN865–867 MHz band, but 433 MHz LoRa point-to-point links are widely used and generally permitted for ISM purposes.
What is the difference between LoRa and LoRaWAN?
LoRa is the physical layer radio modulation (what the SX1278 implements). LoRaWAN is a network protocol built on top of LoRa, defining how multiple devices share a network, how data is routed through gateways to application servers, and how devices are secured and managed. In this tutorial, we are using raw LoRa peer-to-peer communication without LoRaWAN. For large-scale IoT deployments with many nodes and a public network, LoRaWAN (via The Things Network in India) is more appropriate.
Why is my SX1278 not initialising? LoRa.begin() returns false.
The most common causes are: (1) Voltage level mismatch — the SX1278 is 3.3V and you are driving it from 5V without a level shifter. (2) Wrong pin assignments for SS, RST, and DIO0 in LoRa.setPins(). (3) Poor SPI connections — check all four SPI lines plus SS. (4) Damaged module from previous 5V connection. (5) Power supply issue — the 3.3V pin on an Arduino Uno may not supply enough current. Use an external AMS1117-3.3 regulator. Test with a multimeter to confirm 3.3V on the module’s VCC pin.
How far can the SX1278 reach in a typical Indian city or farm?
In open agricultural land (farms, fields), expect 3–8 km range with wire antennas at moderate height. In dense urban environments (Mumbai, Delhi city centre), range drops to 500–1500 m due to buildings. Elevated antennas dramatically improve range — mounting a gateway antenna 10 m above ground level can provide 5 km coverage in suburban areas. For farm monitoring across large properties in rural India, LoRa at 433 MHz is highly effective.
Can I use the SX1278 for two-way communication (ACK/response)?
Yes, the SX1278 is a full transceiver — it can both transmit and receive. Implement a simple request-response protocol by having the receiver wait briefly after each reception and transmit an acknowledgement packet. The sender can then wait for the ACK and retransmit if it does not arrive within a timeout. Avoid simultaneous transmission from both nodes as they would collide — use time slots or TDMA if you need multiple nodes to respond.
Build Your Long-Range Sensor Network Today
The SX1278 LoRa module with Arduino opens up a world of applications that simply are not possible with Wi-Fi or Bluetooth: farm monitoring, pipeline pressure sensing, water level logging, wildlife camera alerts, and remote machinery monitoring — all without any infrastructure beyond two small boards and a pair of wire antennas.
Find all the Arduino boards, sensors, and wireless modules you need at Zbotic.in — Arduino & Microcontrollers, with delivery across India.
Add comment