LoRa trades speed for range: a few bytes at a time, but over hundreds of metres to kilometres on very little power — ideal for a farm sensor or a water-tank level sender far from the house’s Wi-Fi.
The SX1278 talks SPI, and the ESP32 DevKit V1’s VSPI pins D18/D19/D23 are its natural bus. Chip select, reset and DIO0 go on pins that stay quiet at boot (D4, D27, D26). Flash the same sketch on two boards and each prints the other’s pings with signal strength — the quickest way to test range before building a real node.
Wiring
| ESP32 DevKit V1 | SX1278 LoRa module (Ra-02, 433 MHz) | Wire | Notes |
|---|---|---|---|
| 3V3 | 3.3V | red | 3.3 V only — never VIN. The module draws about 120 mA while sending; a 10 µF capacitor at the module helps on long wires. |
| GND | GND | black | |
| D18 | SCK | yellow | VSPI clock |
| D19 | MISO | blue | VSPI MISO |
| D23 | MOSI | green | VSPI MOSI |
| D4 | NSS | purple | Chip select. Not D5: GPIO5 is a strapping pin. |
| D27 | RST | orange | Not D14 or D12 — those toggle or strap at boot. |
| D26 | DIO0 | white | Signals “packet sent / received”. Any non-strapping pin works. |
3.3 V only (1.8–3.7 V) part, 3.3 V board. The SX1278 is a 3.3 V part and its pins are not 5 V tolerant. ESP32 and Pico connect directly. With an Uno or Nano, level-shift NSS, SCK, MOSI and RST, and power the module from a 3.3 V regulator that can supply about 120 mA while transmitting.
Board quirks
- Both sides are 3.3 V, so the SX1278 wires straight to the ESP32 — no level shifting as on an Uno. The ESP32’s 3V3 regulator handles the 120 mA transmit peaks from USB.
- Frequency must match the hardware. The SX1278 chip covers 137–525 MHz; the Ra-02 sold in India is tuned for 433 MHz, so the sketch uses
433E6. India’s LoRaWAN band is 865–867 MHz — for that you need an SX1276-based module (RFM95, Ra-01H) and866E6, not an SX1278 set to a higher number. - Never transmit without the antenna. The power amplifier reflects its own output back and can be damaged. Solder the spring or screw on the SMA antenna first.
- The sketch keeps power at 10 dBm (10 mW) with SF9. That reaches a few hundred metres in a town and keeps you within the low-power limits of the licence-exempt bands; check current WPC rules before raising it.
- Most Ra-02 modules have 2 mm pitch pads, not 2.54 mm. Solder short wires or use an adapter board — they don’t fit a breadboard.
Code
// SX1278 (Ra-02, 433 MHz) LoRa ping node on an ESP32 DevKit V1.
// 3.3V → 3V3, GND → GND, SCK → D18, MISO → D19, MOSI → D23 (VSPI),
// NSS → D4, RST → D27, DIO0 → D26. Flash the same sketch on two boards: each sends
// "ping N" every 10 s and prints whatever it hears, with RSSI and SNR.
// Screw the antenna on BEFORE powering up — transmitting without one can damage the module.
#include <SPI.h>
#include <LoRa.h>
constexpr uint8_t LORA_NSS = 4;
constexpr uint8_t LORA_RST = 27;
constexpr uint8_t LORA_DIO0 = 26;
constexpr long FREQ_HZ = 433E6; // must match your module: Ra-02 = 433 MHz.
// 865–867 MHz needs an SX1276 module (e.g. RFM95), not an SX1278.
uint32_t counter = 0;
uint32_t lastSend = 0;
uint32_t interval = 10000;
void setup() {
Serial.begin(115200);
SPI.begin(18, 19, 23); // SCK, MISO, MOSI — the VSPI defaults, written out
LoRa.setPins(LORA_NSS, LORA_RST, LORA_DIO0);
if (!LoRa.begin(FREQ_HZ)) {
Serial.println("LoRa module not found — check 3.3V, NSS on D4 and RST on D27");
while (true) delay(1000);
}
LoRa.setTxPower(10); // 10 dBm (10 mW): plenty for a few hundred metres
LoRa.setSpreadingFactor(9); // SF9: more range than the default SF7, still quick
LoRa.enableCrc();
Serial.printf("LoRa ready at %.1f MHz, SF9, 10 dBm\n", FREQ_HZ / 1e6);
}
void loop() {
// Receive: parsePacket() returns the packet size, or 0 if nothing arrived.
int size = LoRa.parsePacket();
if (size) {
String msg;
while (LoRa.available()) msg += (char)LoRa.read();
Serial.printf("Got \"%s\" RSSI %d dBm SNR %.1f dB\n", msg.c_str(), LoRa.packetRssi(), LoRa.packetSnr());
}
// Send every 10 s, with a little random offset so two nodes don't stay in step.
if (millis() - lastSend > interval) {
lastSend = millis();
interval = 10000 + random(0, 1000);
LoRa.beginPacket();
LoRa.printf("ping %lu", (unsigned long)counter);
LoRa.endPacket(); // blocks until sent (~0.2 s at SF9)
Serial.printf("Sent \"ping %lu\"\n", (unsigned long)counter++);
}
}// SX1278 (Ra-02, 433 MHz) LoRa ping node on an ESP32 DevKit V1.
// 3.3V → 3V3, GND → GND, SCK → D18, MISO → D19, MOSI → D23 (VSPI),
// NSS → D4, RST → D27, DIO0 → D26. Flash the same sketch on two boards: each sends
// "ping N" every 10 s and prints whatever it hears, with RSSI and SNR.
// Screw the antenna on BEFORE powering up — transmitting without one can damage the module.
#include <SPI.h>
#include <LoRa.h>
constexpr uint8_t LORA_NSS = 4;
constexpr uint8_t LORA_RST = 27;
constexpr uint8_t LORA_DIO0 = 26;
constexpr long FREQ_HZ = 433E6; // must match your module: Ra-02 = 433 MHz.
// 865–867 MHz needs an SX1276 module (e.g. RFM95), not an SX1278.
uint32_t counter = 0;
uint32_t lastSend = 0;
uint32_t interval = 10000;
void setup() {
Serial.begin(115200);
SPI.begin(18, 19, 23); // SCK, MISO, MOSI — the VSPI defaults, written out
LoRa.setPins(LORA_NSS, LORA_RST, LORA_DIO0);
if (!LoRa.begin(FREQ_HZ)) {
Serial.println("LoRa module not found — check 3.3V, NSS on D4 and RST on D27");
while (true) delay(1000);
}
LoRa.setTxPower(10); // 10 dBm (10 mW): plenty for a few hundred metres
LoRa.setSpreadingFactor(9); // SF9: more range than the default SF7, still quick
LoRa.enableCrc();
Serial.printf("LoRa ready at %.1f MHz, SF9, 10 dBm\n", FREQ_HZ / 1e6);
}
void loop() {
// Receive: parsePacket() returns the packet size, or 0 if nothing arrived.
int size = LoRa.parsePacket();
if (size) {
String msg;
while (LoRa.available()) msg += (char)LoRa.read();
Serial.printf("Got \"%s\" RSSI %d dBm SNR %.1f dB\n", msg.c_str(), LoRa.packetRssi(), LoRa.packetSnr());
}
// Send every 10 s, with a little random offset so two nodes don't stay in step.
if (millis() - lastSend > interval) {
lastSend = millis();
interval = 10000 + random(0, 1000);
LoRa.beginPacket();
LoRa.printf("ping %lu", (unsigned long)counter);
LoRa.endPacket(); // blocks until sent (~0.2 s at SF9)
Serial.printf("Sent \"ping %lu\"\n", (unsigned long)counter++);
}
}Library: LoRa (Sandeep Mistry) — compiled with LoRa 0.8.0.
Expected serial output
LoRa ready at 433.0 MHz, SF9, 10 dBm
Sent "ping 0"
Got "ping 0" RSSI -61 dBm SNR 9.5 dB
Sent "ping 1"
Got "ping 1" RSSI -63 dBm SNR 9.0 dBLoRa ready at 433.0 MHz, SF9, 10 dBm Sent "ping 0" Got "ping 0" RSSI -61 dBm SNR 9.5 dB Sent "ping 1" Got "ping 1" RSSI -63 dBm SNR 9.0 dB
Common errors
“LoRa module not found”
LoRa.begin() reads the chip version over SPI and got nothing. Check 3.3V and GND, that NSS is on D4 and RST on D27, and that MISO/MOSI are not swapped (D19 = MISO, D23 = MOSI).
Each board sends, but neither receives
Both must use the same frequency, spreading factor and sync word — flash the identical sketch on both. Also check both modules are the same band (433 vs 868 MHz look identical).
Range is only a few metres
No antenna, the wrong band’s antenna, or a module damaged by transmitting without one. Hold the antennas vertical and away from the ESP32 board.