Components · 14 min read

LoRa Arduino Guide: SX1278 Ra-02 Long-Range Wireless with ESP32

LoRa Arduino guide: SX1278 Ra-02 wiring, SF and bandwidth settings, sender and receiver code, ESP32 and Pico, the 3.3 V warning, range tests, LoRa vs LoRaWAN.

LoRa Arduino projects are the most practical way to send data between two boards over kilometres, on very little power and without internet or a SIM card. LoRa (Long Range) is a radio modulation developed by Semtech; with cheap modules such as the SX1278-based Ra-02 at 433 MHz you can reach 1–3 km in a city and up to 10 km in open country. The price is speed: from a few hundred bytes to a few kilobits per second. That's why LoRa isn't for audio or video but is ideal for carrying small readings such as temperature, humidity, water level, door state or GPS position over long distances.

In this guide you'll see step by step how LoRa reaches so far, the SF, BW and CR settings that decide range, the right frequency for your region, wiring the Ra-02 to an Arduino Uno, ESP32 and Raspberry Pi Pico, sender and receiver code with the LoRa library, reading RSSI and SNR, and a real range test. The final sections explain the difference between LoRa and LoRaWAN, The Things Network (TTN), and a field sensor project that runs for months on a battery.

What is LoRa and how does it reach so far?

LoRa uses a technique called chirp spread spectrum (CSS). Each symbol is sent as a "chirp" whose frequency sweeps steadily up through the band; the information is hidden in the frequency at which the chirp starts. When the receiver multiplies the incoming signal by the known chirp shape and adds it up, the noise cancels out while the signal accumulates. This lets LoRa decode signals down to 20 dB below the noise floor. Ordinary 433 MHz ASK modules need the signal to be clearly stronger than the noise, whereas LoRa makes sense of signals that are barely there; that's the secret to going much further with the same output power.

The cost is time: the longer a chirp lasts, the more energy the receiver collects and the greater the range, but the same data is sent more slowly. In LoRa, range, speed and battery life are always tied together. A 20-byte reading takes about 57 milliseconds at the fastest setting and 1.3 seconds at the longest-range one. The table below compares LoRa with the other wireless options in the Arduino world.

FeatureLoRa (SX1278)nRF24L01433 MHz ASKWi-Fi (ESP32)
Typical range1–3 km in a city, up to 10 km in the open50–100 m (about 1 km with PA+LNA)20–100 m50–100 m (to the router)
Data rate0.3–5.5 kbit/s (125 kHz)250 kbit/s – 2 Mbit/s1–10 kbit/sMbit/s range
Transmit current40–120 mA (depending on power)11–12 mA10–40 mA120–240 mA
Sleep currentBelow 1 µAAround 1 µANone (switched off)10 µA (deep sleep)
Two-wayYesYesNo (separate transmitter and receiver)Yes
InfrastructureNone neededNone neededNone neededRouter and internet

LoRa modules: Ra-02, RFM95, E32 and ready-made boards

Most LoRa modules on the market use a chip from Semtech's SX127x family. The SX1276 covers 137–1020 MHz and the SX1278 covers 137–525 MHz, which is why you'll find the SX1278 in 433 MHz modules and the SX1276 in 868/915 MHz modules. Modules fall into two groups: "bare" radios that talk over SPI, and ready-made modules with their own processor that you use like a serial port over UART.

ModuleChipFrequencyInterfaceNote
Ai-Thinker Ra-02SX1278433 MHzSPICheapest and most common; IPEX antenna connector
Ai-Thinker Ra-01SX1278433 MHzSPIRa-02 variant with a spring antenna
HopeRF RFM95WSX1276868 / 915 MHzSPIA popular choice for LoRaWAN and TTN
Ebyte E32 / E220SX1278 / LLCC68433 / 868 MHzUARTHas its own processor; works like a serial port without a library
TTGO LoRa32, Heltec LoRa 32ESP32 + SX1276/78433 / 868 / 915 MHzOn boardESP32, LoRa and OLED on one board
Ra-02 featureValue
ChipSemtech SX1278
Frequency range410–525 MHz (433 MHz is typical)
Maximum output power+18 dBm (about 63 mW)
Receiver sensitivityDown to -141 dBm (at the narrowest bandwidth)
Supply1.8–3.7 V, typically 3.3 V
CurrentAbout 40–120 mA transmitting, about 12 mA listening, below 1 µA asleep
InterfaceSPI (up to 10 MHz) + DIO0 interrupt pin
AntennaIPEX (U.FL) connector, needs a 433 MHz antenna
The SX1278 is a 3.3 V chip and its pins are not 5 V tolerant. Connecting the Ra-02 directly to the Arduino Uno's 5V pin or to 5 V SPI lines will permanently damage it. The supply must always be 3.3 V, and with an Uno you need a level shifter on the SPI lines (explained in the wiring section).

Which frequency: 433 MHz or 868/915 MHz?

LoRa works in licence-free ISM bands, but these bands differ from country to country and each has limits on power and duty cycle. In Europe two bands are used for LoRa: 433.05–434.79 MHz and 863–870 MHz; in North America it's 902–928 MHz. LoRaWAN networks and TTN use 868 MHz in Europe and 915 MHz in the US, while 433 MHz is very common for point-to-point hobby links with cheap Ra-02 modules. Check your region before you buy, because the antenna and chip are tuned for a specific frequency; an 868 MHz module won't work at 433 MHz.

RegionLoRa frequencyLoRaWAN plan
Europe, UK, Turkey433 MHz and 863–870 MHzEU868 (EU433 is rare)
USA, Canada, Mexico902–928 MHzUS915
Brazil, Argentina, Australia915–928 MHzAU915
India865–867 MHzIN865
Japan, Southeast Asia920–923 MHzAS923
China470–510 MHz (and 433 MHz)CN470
Under European rules the power allowed in the 433 MHz band is generally 10 mW (10 dBm), and in the 868 MHz sub-bands it's mostly 25 mW with a 1% duty cycle; in other words, a device that transmits for 1 second must stay silent for the next 99. The Ra-02's maximum of 18 dBm is above that limit, which is why the example code uses setTxPower(10). Check your national radio regulator for the exact limits.

SF, BW and CR: the three settings that decide range

Three parameters set range and speed in LoRa. The Spreading Factor (SF) chooses how many "chips" each symbol is made of: from SF7 to SF12, each step doubles the symbol time, lets the receiver decode a signal about 2.5 dB weaker, but almost doubles the time on air. The table below is calculated for 125 kHz bandwidth, 4/5 coding and a 20-byte packet; the last column is the minimum wait between two packets to respect a 1% duty cycle.

SFBit rate20-byte packet timeApprox. sensitivityLowest decodable SNRWait at 1% duty cycle
SF75.5 kbit/s57 ms-123 dBm-7.5 dB6 s
SF83.1 kbit/s103 ms-126 dBm-10 dB10 s
SF91.8 kbit/s185 ms-129 dBm-12.5 dB18 s
SF10977 bit/s371 ms-132 dBm-15 dB37 s
SF11537 bit/s741 ms-134 dBm-17.5 dB73 s
SF12293 bit/s1319 ms-137 dBm-20 dB131 s

Bandwidth (BW) is how wide a slice of spectrum the signal occupies: 125 kHz is standard; 250 and 500 kHz double and quadruple the speed but cost 3 and 6 dB of sensitivity, while 62.5 kHz and below increase range but become sensitive to the frequency drift of cheap crystals. Coding Rate (CR) is the error-correction overhead: 4/5 adds 1 correction bit for every 4 bits, 4/8 adds 4; it helps in noisy environments but makes the packet longer. The bit rate formula is Rb = SF × (BW / 2^SF) × 4 / (4 + CR); for SF9, 125 kHz and 4/5 that's 9 × 125000 / 512 × 0.8 ≈ 1758 bit/s.

SF9, 125 kHz, 4/5 is a good balance to start with. If the range isn't enough, raise SF one step at a time; if you need speed, lower it. Frequency, SF, BW, CR and the sync word must be identical on sender and receiver; if even one differs, the receiver sees no packets at all. The default sync word is 0x12 and most private networks use it, while LoRaWAN networks use 0x34. Choosing your own value such as 0xF3, as in the examples, helps filter out packets from neighbouring hobby devices and LoRaWAN at the hardware level.

Ra-02 wiring: Arduino Uno, ESP32 and Pico

Besides the four SPI lines (SCK, MISO, MOSI, NSS), the Ra-02 needs a reset pin and an interrupt pin (DIO0). DIO0 goes HIGH when a transmission finishes or a new packet arrives; the LoRa library uses it as an interrupt. The module's DIO1–DIO5 pins can stay unconnected for the code in this guide. If SPI is new to you, have a look at the What is SPI post first.

Ra-02 pinArduino UnoESP32Raspberry Pi Pico
3.3VExternal 3.3 V regulator3V33V3 (OUT)
GNDGNDGNDGND
SCKD13 (via level shifter)GPIO 18GP18
MISOD12 (direct)GPIO 19GP16
MOSID11 (via level shifter)GPIO 23GP19
NSSD10 (via level shifter)GPIO 5GP17
RESETD9 (via level shifter)GPIO 14GP20
DIO0D2 (direct, interrupt pin)GPIO 26GP21
There are two problems with the Uno. The first is voltage: the Uno's SCK, MOSI, NSS and RESET outputs are 5 V; you must bring them down to 3.3 V with a level shifter (e.g. 74HC4050) or a 2.2 kΩ + 3.3 kΩ voltage divider on each line. MISO and DIO0 go from the module to the Uno, so they can be connected directly. The second is current: the Uno's 3.3V pin can supply about 50 mA, while the Ra-02 can draw over 100 mA when transmitting. Power the module from a separate 3.3 V regulator such as an AMS1117. None of these issues exist with the ESP32, the Pico or a 3.3 V Arduino Pro Mini.

The antenna matters as much as the wiring. Fit an antenna tuned for 433 MHz to the Ra-02's IPEX connector. Making your own is easy: the wavelength is 300 / 433 = 0.693 metres, and a straight 17.3 cm wire is enough for a quarter-wave antenna (8.6 cm for 868 MHz, 8.2 cm for 915 MHz). Keep the antenna vertical, don't put it inside a metal box, and make sure the antennas on both sides point the same way (both vertical); antennas at right angles to each other lose most of the signal.

Step by step: a sender with the LoRa library

  1. Open the Library Manager in the Arduino IDE, type LoRa and install the LoRa library by Sandeep Mistry.
  2. Wire the module as in the table, and never transmit without the antenna fitted.
  3. Upload the code below to the board that will be the sender; it detects whether it's an ESP32 or an Uno and picks the pins accordingly.
  4. Open the Serial Monitor at 115200 baud; you should see a "Gonderildi / sent" line every 5 seconds.
  5. If LoRa.begin() fails, check the wires, the 3.3 V supply and the NSS and RESET pins.
C++ (Arduino IDE)
1#include <SPI.h>
2#include <LoRa.h>
3
4#if defined(ESP32)
5const int PIN_SS = 5, PIN_RST = 14, PIN_DIO0 = 26;   // ESP32 (VSPI: SCK 18, MISO 19, MOSI 23)
6#else
7const int PIN_SS = 10, PIN_RST = 9, PIN_DIO0 = 2;    // Uno (SCK 13, MISO 12, MOSI 11) + seviye dönüştürücü / level shifter
8#endif
9
10const long FREKANS = 433E6;      // Ra-02: 433 MHz; RFM95 için 868E6 / 868E6 for an RFM95
11unsigned long sayac = 0;
12
13void setup() {
14  Serial.begin(115200);
15  LoRa.setPins(PIN_SS, PIN_RST, PIN_DIO0);
16  if (!LoRa.begin(FREKANS)) {               // SX1278 bulunamadı / SX1278 not found
17    Serial.println("LoRa baslatilamadi / LoRa init failed");
18    while (true) delay(1000);
19  }
20  LoRa.setSpreadingFactor(9);               // SF9: menzil ile hız dengesi / range vs speed balance
21  LoRa.setSignalBandwidth(125E3);           // 125 kHz bant genişliği / bandwidth
22  LoRa.setCodingRate4(5);                   // hata düzeltme 4/5 / error coding 4/5
23  LoRa.setSyncWord(0xF3);                   // iki tarafta aynı olmalı / must match on both sides
24  LoRa.enableCrc();                         // bozuk paketi ele / drop corrupted packets
25  LoRa.setTxPower(10);                      // 10 dBm (433 MHz bandında yasal sınır) / legal limit on 433 MHz
26  Serial.println("Verici hazir / sender ready");
27}
28
29void loop() {
30  LoRa.beginPacket();
31  LoRa.print("BG #");
32  LoRa.print(sayac);                        // paket numarası / packet number
33  LoRa.endPacket();                         // gönderim bitene kadar bekler / blocks until sent
34  Serial.print("Gonderildi / sent #");
35  Serial.println(sayac);
36  sayac++;
37  delay(5000);                              // havada kalma süresine göre bekle / wait according to airtime
38}
39
FunctionWhat does it do?
LoRa.setPins(ss, reset, dio0)Defines the NSS, RESET and DIO0 pins; call it before begin()
LoRa.begin(433E6)Starts the module and sets the frequency in Hz; returns 0 if the chip isn't found
setSpreadingFactor(6–12)SF setting, default 7
setSignalBandwidth(125E3)Bandwidth, default 125 kHz
setCodingRate4(5–8)Coding rate 4/5 – 4/8, default 4/5
setSyncWord(0xF3)Sync word, default 0x12 (LoRaWAN uses 0x34)
enableCrc()Adds a CRC to the packet; off by default in the library
setTxPower(2–20)Output power in dBm, default 17
beginPacket() / print() / endPacket()Prepares, writes and sends the packet

Receiver: reading packets, RSSI and SNR

The receiver code checks on every loop with LoRa.parsePacket() whether a new packet has arrived; if so, it returns its size and the content is read byte by byte with LoRa.read(). The code also counts gaps in the packet numbers to calculate the loss rate, which is the basis of the range test we'll do shortly.

C++ (Arduino IDE)
1#include <SPI.h>
2#include <LoRa.h>
3
4#if defined(ESP32)
5const int PIN_SS = 5, PIN_RST = 14, PIN_DIO0 = 26;
6#else
7const int PIN_SS = 10, PIN_RST = 9, PIN_DIO0 = 2;
8#endif
9
10long sonNo = -1;                 // son alınan paket numarası / last packet number
11unsigned long alinan = 0, kayip = 0;
12
13void setup() {
14  Serial.begin(115200);
15  LoRa.setPins(PIN_SS, PIN_RST, PIN_DIO0);
16  if (!LoRa.begin(433E6)) {
17    Serial.println("LoRa baslatilamadi / LoRa init failed");
18    while (true) delay(1000);
19  }
20  LoRa.setSpreadingFactor(9);    // verici ile aynı ayarlar / same settings as the sender
21  LoRa.setSignalBandwidth(125E3);
22  LoRa.setCodingRate4(5);
23  LoRa.setSyncWord(0xF3);
24  LoRa.enableCrc();
25  Serial.println("Alici hazir / receiver ready");
26}
27
28void loop() {
29  int boyut = LoRa.parsePacket();           // paket geldi mi? / did a packet arrive?
30  if (boyut == 0) return;
31
32  String mesaj = "";
33  while (LoRa.available()) mesaj += (char)LoRa.read();
34
35  int diyez = mesaj.indexOf('#');           // "BG #123" içinden numara / number from "BG #123"
36  long no = (diyez >= 0) ? mesaj.substring(diyez + 1).toInt() : -1;
37  if (sonNo >= 0 && no > sonNo + 1) kayip += no - sonNo - 1;   // atlanan numaralar kayıp / skipped numbers are lost
38  if (no >= 0) sonNo = no;
39  alinan++;
40
41  Serial.print(mesaj);
42  Serial.print("  RSSI: ");
43  Serial.print(LoRa.packetRssi());          // dBm, -120'ye yaklaştıkça zayıf / weaker towards -120
44  Serial.print(" dBm  SNR: ");
45  Serial.print(LoRa.packetSnr(), 1);        // dB, eksi değerler de çözülebilir / negative values still decode
46  Serial.print(" dB  kayip / lost: ");
47  Serial.print(100.0 * kayip / (alinan + kayip), 1);
48  Serial.println(" %");
49}
50

RSSI (Received Signal Strength Indicator) is the strength of the received signal, a negative number in dBm: -40 dBm is very strong, -120 dBm very weak. SNR (Signal to Noise Ratio) shows how much stronger the signal is than the noise. What makes LoRa special is that SNR can go negative: at SF9, packets are still decoded down to -12.5 dB. To tell whether you're near the edge of your range, look at SNR more than RSSI; in a noisy city the SNR may already be at the limit even with an RSSI of -100 dBm.

ReadingValueMeaning
RSSI-30 to -70 dBmVery strong; modules are close
RSSI-70 to -110 dBmGood, stable link
RSSI-110 to -125 dBmWeak; consider raising SF
SNR+5 dB and aboveComfortable; you could gain speed with a lower SF
SNR0 to -10 dBNormal LoRa operating zone
SNRNear the SF limit (-12 dB at SF9)About to drop; raise SF or improve the antenna

LoRa with the ESP32: a bridge to Wi-Fi

ESP32 development board
ESP32: its 3.3 V pins connect to the Ra-02 without a level shifter, and it can forward LoRa data to the internet over Wi-Fi at the same time.

The ESP32 is the easiest board for LoRa: its pins are already 3.3 V, its 3V3 pin can supply the module's current and the VSPI bus (GPIO 18, 19, 23) is used directly. The sender and receiver code above compiles on the ESP32 unchanged; the #if defined(ESP32) line selects NSS = 5, RESET = 14 and DIO0 = 26. We connect DIO0 to GPIO 26 rather than GPIO 2 because GPIO 2 is a strapping pin that affects how the chip boots. The ESP32's real strength is acting as a bridge: it can forward data arriving over LoRa from sensors in the field to a web page, an MQTT server or Telegram over Wi-Fi. Boards like the TTGO LoRa32 and Heltec LoRa 32 come with the ESP32, the LoRa chip and an OLED display on one board; their pin numbers are different, so check the board's schematic.

LoRa with the Raspberry Pi Pico (MicroPython)

On the Pico you connect the Ra-02 to the SPI0 bus (GP16 MISO, GP18 SCK, GP19 MOSI, GP17 NSS). The official micropython-lib repository has a LoRa driver for SX127x chips; you can install it from Thonny's package manager or with mpremote mip install lora-sync lora-sx127x. The driver takes its settings as a dictionary; as long as SF, bandwidth and sync word match the Arduino side, the Pico reads the packets from the Arduino sender above without any trouble. The driver reports SNR as stored in the chip's register, four times the dB value, so the code divides it by 4. Because the Ra-02's antenna is connected to the chip's PA_BOOST output, the tx_ant setting must be "PA_BOOST"; otherwise transmissions will be very weak.

MicroPython
1from machine import Pin, SPI
2from lora import SX1278          # mip: lora-sync + lora-sx127x paketleri / packages
3import time
4
5lora_cfg = {
6    "freq_khz": 433000,          # Ra-02: 433 MHz
7    "sf": 9,                     # Arduino tarafıyla aynı / same as the Arduino side
8    "bw": "125",                 # kHz
9    "coding_rate": 5,            # 4/5
10    "preamble_len": 8,
11    "syncword": 0xF3,
12    "tx_ant": "PA_BOOST",        # Ra-02 anteni PA_BOOST çıkışında / Ra-02 antenna is on PA_BOOST
13    "output_power": 10,          # dBm
14}
15
16spi = SPI(0, baudrate=2_000_000, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
17modem = SX1278(spi=spi, cs=Pin(17), dio0=Pin(21), reset=Pin(20), lora_cfg=lora_cfg)
18
19while True:
20    paket = modem.recv(timeout_ms=10000)       # 10 sn dinle / listen for 10 s
21    if paket and paket.valid_crc:
22        # snr kayıtta 4 katı tutulur / snr is stored as 4x in the register
23        print(bytes(paket), "RSSI:", paket.rssi, "dBm  SNR:", paket.snr / 4, "dB")
24    elif paket:
25        print("CRC hatali paket / bad CRC")
26    else:
27        print("Paket yok / no packet")
28    time.sleep_ms(10)
29

Range test: how many km does LoRa reach?

The only way to know the real range is to test it. Place the sender at a window or on the roof at home, connect the receiver to a laptop or to an ESP32 + OLED powered by a power bank, and note RSSI, SNR and loss rate as you move away. When the loss rate goes above 10% or the SNR gets close to the SF limit, you've reached the end of your practical range. Change only one thing per test (SF, antenna, height) so you can see where the difference comes from. You can measure the straight-line distance between two points with a map app on your phone.

EnvironmentTypical range (Ra-02, SF9–SF12, simple antenna)Deciding factor
Indoors, inside buildingsA few floors, 100–500 mConcrete walls and metal
Dense city1–3 kmBuildings, no line of sight
Suburb, village3–6 kmTrees and low buildings
Open countryside, fields5–10 kmAntenna height above ground
Hilltop to hilltop line of sight15 km and moreEarth's curvature and antenna gain
  • Raise the antenna: nothing increases range more than antenna height. An antenna 5 metres up instead of 1 metre stretches the line of sight many times over.
  • Use a proper antenna: the small spring antennas that come with modules are convenient but inefficient; a 17.3 cm quarter-wave wire or an outdoor whip antenna makes a clear difference.
  • Raise SF: going from SF9 to SF12 gives about 8 dB of gain, which in open country means roughly more than double the distance.
  • Narrow the bandwidth: dropping from 125 kHz to 62.5 kHz gains 3 dB, but crystal drift between the two modules can cause trouble.
  • Clear the Fresnel zone: even when the line of sight looks clear, trees or buildings in the ellipse-shaped zone between the two antennas weaken the signal; raising the antennas a little solves this.
  • Don't neglect the power supply: the current surge during transmission can pull a weak 3.3 V rail down and reset the module; add a 100 µF capacitor to the supply.

LoRa vs LoRaWAN: what is TTN?

These two terms are often confused. LoRa is only the radio modulation, the physical layer: two modules send packets directly to each other, and you decide who sends what to whom. All the examples in this guide are point-to-point LoRa. LoRaWAN is a network protocol built on top of LoRa: devices (end nodes) send their packets to nearby gateways, the gateways pass them over the internet to a network server, and the data reaches your application from there. LoRaWAN handles device identities, AES-128 encryption, automatic adjustment of frequency and SF (ADR) and duty-cycle rules by itself.

FeatureLoRa (point-to-point)LoRaWAN
LayerRadio only (physical layer)Radio + network protocol (MAC)
InfrastructureTwo modules are enoughNeeds gateways and a network server
EncryptionNone, you add your ownAES-128, mandatory
Arduino libraryLoRa (Sandeep Mistry)MCCI LoRaWAN LMIC, RadioLib
Frequency in Europe433 or 868 MHz, free choiceEU868 channel plan
Best suited forYour own garden, between two buildings, remote controlsCity-wide sensor networks, many devices

The Things Network (TTN) is a free, community-based LoRaWAN network made up of gateways set up by volunteers. If there's a TTN gateway near you, you can register your device and read its data on the internet without building your own gateway. Free use comes with a fair use policy: about 30 seconds of total transmit time per device per day and at most 10 downlink messages per day. Because TTN in Europe uses EU868 and in the US US915, a 433 MHz Ra-02 isn't suitable for it; for TTN, get an RFM95 for your region or a ready-made LoRaWAN module. If there's no gateway nearby, point-to-point LoRa is much simpler.

Project: a battery-powered field and garden sensor

Soil moisture sensor
Soil moisture sensor: the LoRa node in the field measures with this sensor and sends a packet home every 10 minutes.

The classic use of LoRa is getting data from a field with no power or Wi-Fi, or from a greenhouse at the far end of the garden. The ESP32 code below measures soil moisture, sends a small packet in the form "T1,42,37" (node ID, packet number, moisture percentage), then puts both the LoRa module and the ESP32 to sleep and wakes up again 10 minutes later. The sensor is powered through GPIO 25 only during the measurement, so it draws no current while asleep, and with resistive sensors electrode corrosion is reduced too. Sensor calibration is covered in the Soil Moisture Sensor post and deep sleep details in the ESP32 Deep Sleep post. The receiver at home can use the receiver code above and split the values at the commas.

C++ (Arduino IDE)
1#include <SPI.h>
2#include <LoRa.h>
3
4const int PIN_SS = 5, PIN_RST = 14, PIN_DIO0 = 26;
5const int NEM_PIN = 34;                      // toprak nem sensörü AO (yalnız giriş pini) / soil sensor AO (input-only pin)
6const int SENSOR_GUC = 25;                   // sensörü yalnız ölçerken besle / power the sensor only while measuring
7const int KURU = 3000, ISLAK = 1200;         // kendi sensörünle kalibre et / calibrate with your sensor
8const uint64_t UYKU_US = 10ULL * 60 * 1000000;   // 10 dakika / 10 minutes
9
10RTC_DATA_ATTR uint32_t paketNo = 0;          // derin uykuda korunur / kept during deep sleep
11
12void setup() {
13  pinMode(SENSOR_GUC, OUTPUT);
14  digitalWrite(SENSOR_GUC, HIGH);
15  delay(200);                                // sensör otursun / let the sensor settle
16  int ham = analogRead(NEM_PIN);
17  digitalWrite(SENSOR_GUC, LOW);
18  int nem = constrain(map(ham, KURU, ISLAK, 0, 100), 0, 100);   // % nem / % moisture
19
20  LoRa.setPins(PIN_SS, PIN_RST, PIN_DIO0);
21  if (LoRa.begin(433E6)) {
22    LoRa.setSpreadingFactor(10);             // uzak tarla için daha yüksek SF / higher SF for a distant field
23    LoRa.setSignalBandwidth(125E3);
24    LoRa.setSyncWord(0xF3);
25    LoRa.enableCrc();
26    LoRa.setTxPower(10);
27    LoRa.beginPacket();
28    LoRa.print("T1,");                       // düğüm kimliği / node id
29    LoRa.print(paketNo++);
30    LoRa.print(",");
31    LoRa.print(nem);                         // ör. "T1,42,37" / e.g. "T1,42,37"
32    LoRa.endPacket();
33    LoRa.sleep();                            // SX1278 uykuda 1 uA altı / under 1 uA asleep
34  }
35  esp_sleep_enable_timer_wakeup(UYKU_US);
36  esp_deep_sleep_start();                    // 10 dk sonra setup() baştan başlar / setup() restarts after 10 min
37}
38
39void loop() {}
40

Let's estimate the battery life. Every 10 minutes (600 seconds) the node wakes for about 0.5 seconds and draws about 100 mA on average during that time; asleep, a bare ESP32 module and the Ra-02 together draw around 15 µA. The average current is (0.5 × 100) / 600 + 0.015 ≈ 0.1 mA; a 2000 mAh 18650 cell would in theory last 20,000 hours, more than two years. In reality the battery's self-discharge and regulator losses shorten this. Watch out: ready-made ESP32 DevKit boards can draw several milliamps even asleep because of the USB-serial chip and regulator on them; a sleep current of 5 mA brings the same battery down to 17 days. For long battery life, use a low-quiescent regulator or a bare module. You can run the numbers with your own values in the battery life calculator.

LoRa project ideas

  • Remote water tank level: measure how full a distant tank is with a water level sensor and send it home.
  • Mailbox notification: detect the mailbox at the garden gate opening with a reed switch and show it on a display indoors.
  • Beehive scale: send the hive's weight a few times a day with an HX711 load cell.
  • GPS tracker: track a bike or animals kilometres away with a NEO-6M GPS.
  • Greenhouse climate monitor: receive greenhouse temperature and humidity over LoRa and raise an alert at home when a threshold is crossed.
  • Your own gateway: forward data from several LoRa sensors to Wi-Fi with a single ESP32 and watch it on your phone.

Common mistakes and fixes

SymptomLikely causeFix
"LoRa init failed"Wrong SPI wire, NSS or RESET pin; no powerCheck the wiring and the setPins() values, measure the 3.3 V
Sender transmits, receiver sees nothingDifferent frequency, SF, BW, CR or sync wordMake the settings identical on both sides
Module gets hot or doesn't work at allPowered from 5 V or received 5 V signalsUse a 3.3 V supply and a level shifter; the module may be damaged
Board resets when transmittingThe 3.3 V rail can't handle the current surgeSeparate regulator and a 100 µF capacitor on the supply
Range of a few metresNo antenna or an antenna for the wrong frequencyFit a 433 MHz antenna; transmitting without one can damage the PA
Packets contain garbled charactersCRC off, noisy packets acceptedCall enableCrc() on both sides
Some packets never arrivePackets too frequent, colliding on airIncrease the packet interval according to time on air
Do your first test with both modules on the same desk at low power (setTxPower(2)). At very short distances, high power can saturate the receiver's input and cause packet loss. Once it works on the desk, move on to increasing the distance.

A LoRa module is the cheapest way to take Arduino projects out of the house, into the field, the garden and the building next door: with the right frequency, a suitable SF and a good antenna you can build sensors that run for months on a battery kilometres away. For fast data over shorter distances, the nRF24L01 or ESP-NOW may suit you better. In the BlueGrays app you'll find one-way and two-way communication between two Arduinos, smart watering and ESP32 deep sleep projects with step-by-step circuit diagrams and code, and you can add LoRa on top of them.

Frequently asked questions

What is the range of a LoRa module?

An SX1278 module like the Ra-02 with a simple antenna usually reaches 1–3 km in a city and 5–10 km in open country. With antennas mounted high and a clear line of sight, more than 15 km is possible. Range depends mostly on antenna height, antenna quality and the SF setting.

What is the difference between LoRa and LoRaWAN?

LoRa is only the radio modulation, and two modules send packets directly to each other. LoRaWAN is a network protocol built on LoRa: devices send to gateways, the data reaches a network server over the internet, and encryption and device management are handled by the protocol. LoRa suits your own garden; LoRaWAN suits city-wide networks.

Which LoRa frequency should I use: 433, 868 or 915 MHz?

It depends on your region. Europe uses 433 MHz and 863–870 MHz (EU868 for LoRaWAN), North America uses 902–928 MHz (US915), and other regions have their own plans such as AU915 or AS923. Buy a module and antenna for your band and keep to the local power and duty-cycle limits.

Can I connect a LoRa module to a 5 V Arduino?

Not directly; the SX1278 is a 3.3 V chip and 5 V signals damage it. To use it with an Arduino Uno, put a level shifter on the SCK, MOSI, NSS and RESET lines and power the module from a separate 3.3 V regulator. The ESP32 and Raspberry Pi Pico run at 3.3 V, so they connect directly.

Can LoRa send audio or images?

In practice, no. LoRa's data rate at 125 kHz bandwidth is about 0.3–5.5 kilobits per second, and duty-cycle rules limit how often you can transmit. Even a small photo would take minutes; LoRa is designed for small data such as sensor readings, positions and status messages.

What is the spreading factor (SF) and which value should I use?

SF sets how long each LoRa symbol is transmitted; from SF7 to SF12, each step increases range but halves the data rate and uses more battery. SF9 is a good balance to start with. Raise SF if range isn't enough, drop to SF7 if you need speed at short range; sender and receiver must use the same SF.

See all projects
One-Way Communication Between 2 Arduinos
#57Intermediate
Premium

One-Way Communication Between 2 Arduinos

You get two Arduinos talking over a single wire: press the button on one and the LED on the other lights up, press it again and it goes off. The sending board reports the button press with a short mes

4.4(37)
50
Two-Way Communication Between 2 Arduinos
#58Intermediate
Premium

Two-Way Communication Between 2 Arduinos

Two Arduinos are wired together and talk to each other: press the button on one and the light on the other comes on, press it again and it goes off. Both boards run the same program, so each one both

4.5(20)
26
Smart Irrigation Station
#83Advanced
Premium

Smart Irrigation Station

A gardener that waters your plants on its own, so they don't dry out while you're on vacation. A soil moisture sensor measures how wet the soil is; when it gets dry, the Arduino runs the water pump fo

5.0(42)
57
ESP32 Deep Sleep — Battery Saving (OLED + RTC RAM Counter)
#135Advanced
Premium

ESP32 Deep Sleep — Battery Saving (OLED + RTC RAM Counter)

The ESP32's Deep Sleep mode reduces current consumption to ~10 µA by shutting down the CPU and most peripherals, making it critical for battery-powered IoT devices. In this project, we use three different wake-up sources: a timer (15 seconds), an external pin (EXT0 — button GPIO 33), and touch (T0/GPIO 4). Variables marked with RTC_DATA_ATTR are preserved during deep sleep—the boot counter, timer wake counter, and button wake counter are continuously tracked. On each wake-up, the SSD1306 OLED

4.3(10)
16
ESP32 Motion Alarm System (PIR + Buzzer + LED + Telegram)
#145Advanced
Premium

ESP32 Motion Alarm System (PIR + Buzzer + LED + Telegram)

A smart security alarm system that activates the buzzer and LED when the PIR sensor detects motion, while simultaneously sending an instant notification to the owner via Telegram. It includes a 30-second cooldown to limit the frequency of repeated alarms; this mechanism does not validate PIR detections or eliminate false triggers. Suitable for home security, garage monitoring, warehouses, or workrooms. With ESP32-CAM, photo notifications can be added; with the classic ESP32 DevKit V1, text notif

0.0(0)
9
ESP32 Temperature Data Logger (DHT22 + DS1307 RTC + microSD + OLED)
#144Advanced
Premium

ESP32 Temperature Data Logger (DHT22 + DS1307 RTC + microSD + OLED)

Build a timestamped temperature logger with ESP32! Read DHT22 temperature/humidity every minute, get real-world time from DS1307 RTC, and log to microSD card as CSV. The OLED displays live temperature (large font), humidity percentage, timestamp, and log counter. The /temp_log.csv file on the SD card opens in Excel, Google Sheets, or Python pandas for analysis — perfect for greenhouse monitoring, home/room temperature tracking, laboratory data collection, food storage conditions, or beehiv

4.7(7)
12
Control an LED from Your Phone via Bluetooth
#34Beginner
Premium

Control an LED from Your Phone via Bluetooth

A small rehearsal for switching the lights in your room on and off from your phone. The Bluetooth module passes the letters sent from your phone to the Arduino; one letter turns on the red LED, anothe

4.8(68)
89