WiFi is the default answer for IoT because it is already deployed everywhere and works indoors well enough. It is also the wrong answer for a surprising number of projects: a greenhouse 80 meters from the house, a barn at the other end of a farm, soil sensors scattered across an acre, air quality nodes through a six-floor building. In all of those, WiFi ends up either not reaching or needing mains power for a range extender that defeats the purpose.
LoRa solves those cases. The radio does a few hundred kilometers at low bitrates in ideal conditions and two to ten kilometers through buildings or foliage in normal conditions. A node wakes up, transmits a ten-byte packet in a few hundred milliseconds, and sleeps for ten minutes. Years on a pair of AA batteries.
This article builds a three-sensor plus one-gateway LoRa network. The sensors measure temperature and humidity; the gateway forwards readings to WiFi and onward to a backend. The pattern generalises to any low-bandwidth telemetry network.
LoRa vs LoRaWAN — the one distinction that matters
Two terms get confused. LoRa is the radio modulation (chirp spread spectrum) and the physical layer, made by Semtech, implemented in chips like SX1262 and SX1276. LoRaWAN is a network protocol that sits on top of LoRa, defines gateways and network servers, handles encryption and addressing, and connects to public networks like The Things Network and Helium.
For a private network where you own all the nodes and the gateway, you almost never want full LoRaWAN. Its overhead (join procedure, encryption, duty cycle shaping, message retries) is designed for multi-tenant public deployments. Raw LoRa with a simple custom packet format is ten times simpler and better suited.
This article uses raw LoRa. For connecting to a public network, look at The Things Network's documentation instead.
Bill of materials
For 3 sensor nodes + 1 gateway:
- 4 × Heltec WiFi LoRa 32 V3 (ESP32-S3 + SX1262 + 0.96" OLED) — $20 each. Alternative: 4 × LILYGO T3S3, or 4 × bare ESP32 + SX1262 module (cheaper but more wiring).
- 4 × 868 MHz (EU) or 915 MHz (US) helical or rubber-duck antennas — $3 each
- 3 × BME280 sensors for the nodes — $5 each
- 3 × 18650 battery holders + cells — $9 each
- Enclosures for outdoor nodes — $5 each
Around $140 for the full 4-node network. Scales linearly for more nodes.
Frequency is regulated: use 868 MHz in Europe, 915 MHz in North and South America, 433 MHz in some Asian countries. Buying the wrong antenna or the wrong chip variant is common; check your region before ordering.
Star topology: why, and the alternative
Greenhouse
BME280] -.->|LoRa| Gateway[[Gateway
LoRa + WiFi]] Node2[Node 2
Barn
BME280] -.->|LoRa| Gateway Node3[Node 3
Orchard
BME280] -.->|LoRa| Gateway Gateway -->|WiFi HTTP| Cloud[Backend
MQTT or REST] Cloud --> Dashboard[Grafana
dashboard]
Star topology: each node speaks directly to one gateway. Simple, power-efficient, easy to debug. Scales to dozens of nodes per gateway.
A mesh topology, where each node can relay for other nodes, extends range at the cost of dramatically more complex firmware and higher power consumption per node (relaying means listening, which means not sleeping). For most projects you do not want a mesh. If your gateway is not in line-of-sight of a node, add a second gateway rather than a mesh relay.
Packet format
A minimal custom packet. LoRa modems handle preamble, sync word, and CRC automatically; our payload is what we control:
Byte Field Description
---- ----------- -----------------------------------------
0 Node ID 1..255, identifies the sensor node
1 Sensor type 0x01 = BME280, 0x02 = SHT30, etc.
2-3 Temperature int16, degrees Celsius * 100 (0x04E2 = 12.50 C)
4-5 Humidity uint16, % RH * 100
6-7 Pressure uint16, hPa * 10 (optional)
8 Battery uint8, % (0..100)
9 Sequence uint8, wraps at 255
----
10 bytes totalTen bytes transmits in under 100 ms at spreading factor 7. Even at SF12 (longest range, slowest), it transmits in under a second. The 1% duty cycle limit in EU868 allows about 36 seconds of transmission per hour per channel — well over what a ten-minute-interval sensor needs.
Addressing each node with a single byte limits the network to 255 nodes. If you need more, use 2 bytes and shuffle fields.
Sensor node firmware
Each node uses about 80 mA for 200 ms during TX and around 15 µA in deep sleep. Average current is well under 1 mA, which translates to more than a year on two AA cells.
Gateway firmware
#include <SPI.h>
#include <LoRa.h>
#include <WiFi.h>
#include <HTTPClient.h>
#define LORA_CS 8
#define LORA_RST 12
#define LORA_DIO1 14
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";
const char* ENDPOINT = "https://your-backend/ingest";
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(200);
LoRa.setPins(LORA_CS, LORA_RST, LORA_DIO1);
if (!LoRa.begin(868E6)) while (1);
LoRa.setSpreadingFactor(9);
LoRa.setSignalBandwidth(125E3);
LoRa.setCodingRate4(5);
}
void forward(uint8_t node, int16_t tc100, uint16_t rh100, uint16_t hpa10, uint8_t batt, int rssi) {
HTTPClient http;
http.begin(ENDPOINT);
http.addHeader("Content-Type", "application/json");
char body[160];
snprintf(body, sizeof body,
"{\"node\":%u,\"temp_c\":%.2f,\"humidity\":%.2f,\"pressure_hpa\":%.1f,\"batt\":%u,\"rssi\":%d}",
node, tc100 / 100.0, rh100 / 100.0, hpa10 / 10.0, batt, rssi);
http.POST(body);
http.end();
}
void loop() {
int len = LoRa.parsePacket();
if (len == 10) {
uint8_t b[10];
for (int i = 0; i < 10; i++) b[i] = LoRa.read();
uint8_t node = b[0];
int16_t tc100 = (int16_t)((b[2] << 8) | b[3]);
uint16_t rh100 = (b[4] << 8) | b[5];
uint16_t hpa10 = (b[6] << 8) | b[7];
uint8_t batt = b[8];
int rssi = LoRa.packetRssi();
forward(node, tc100, rh100, hpa10, batt, rssi);
}
}The gateway must be powered continuously. It cannot sleep between receptions because it does not know when nodes will transmit. WiFi adds another 80 mA baseline. Run the gateway on mains or on a solar + battery setup sized accordingly.
Spreading factor and the range/time trade-off
LoRa's key parameter is spreading factor (SF), from SF7 (fastest, shortest range) to SF12 (slowest, longest range). Each step up roughly doubles airtime and adds about 2.5 dB of link budget.
- SF7: 5 kbps, 2–3 km urban, 10 km line-of-sight. Good for dense networks with short distances.
- SF9 (our default): 1.7 kbps, 5–7 km urban, 15–20 km LOS. Sweet spot for most private networks.
- SF12: 250 bps, 10–15 km urban, 30–40 km LOS. Use when range matters more than anything else.
All nodes and the gateway must use the same SF, bandwidth, and coding rate to communicate. Set them explicitly in firmware rather than relying on defaults.
Practical range tips
- Antenna matters more than the chip. A $2 rubber duck antenna gets 500 m; a $15 5 dBi gain antenna gets 2 km in the same scenario.
- Mount antennas vertically. LoRa is linearly polarised; mismatched orientations cost 10–20 dB.
- Elevation helps enormously. A gateway at roof height reaches four times the area of one at ground level.
- Concrete and water kill range. A basement sensor may struggle where an open-air sensor works fine.
- Measure RSSI. Gateway logs RSSI per packet. Anything above −100 dBm is rock solid; below −120 dBm is on the edge. Move the antenna or increase SF if you see many packets near the limit.
Going further
- Encryption. Raw LoRa payloads are in the clear. For sensitive data, encrypt with AES-128 in the node and decrypt in the gateway. About 20 extra lines of code.
- Acknowledgements. Current code is fire-and-forget. For critical readings, the gateway can transmit an ACK back and the node can retry on missing ACK.
- Over-the-air updates for nodes. Harder on raw LoRa due to bandwidth; usually handled by sending firmware in chunks over many transmissions, or by keeping WiFi on nodes for occasional OTA only.
- Solar-powered nodes. A 1 W solar panel + TP4056 + 18650 runs a node indefinitely in most climates.
- Mesh networking. Meshtastic and Reticulum are open-source mesh stacks that run on ESP32 + LoRa. Useful for off-grid communications, rarely needed for sensor networks.
Frequently Asked Questions
How many nodes can one gateway handle?
Practically: 30–50 nodes transmitting once every ten minutes at SF9. The limit is collision probability: the longer a transmission takes, and the more nodes there are, the higher the chance two nodes transmit at the same time. Stagger transmission times across nodes to reduce collisions.
What is the difference between The Things Network and my own LoRa network?
The Things Network (TTN) is a public LoRaWAN infrastructure — thousands of community-run gateways cover major cities. You join it as a device, it forwards your data to your own backend via MQTT. Useful when you do not want to run a gateway. The trade-off is you must use LoRaWAN's full protocol stack and respect its fair-use policy. For private networks you own, raw LoRa is simpler.
Is LoRa legal everywhere?
In the ISM bands (868 MHz EU, 915 MHz US, 433 MHz some Asia), yes. Duty cycle limits apply in Europe (1% typical). Power limits apply in most regions (14 dBm / 25 mW in EU). Buying a wrong-region module and transmitting on a licensed band is illegal; the frequency imprinted on the module matters.
Can LoRa penetrate walls?
Better than WiFi, worse than line-of-sight. A single brick wall attenuates the signal by 10–15 dB. Through a building with many walls, expect 300–500 m. Outside, around buildings, expect 1–3 km. Line-of-sight from a rooftop to a rooftop, 10–30 km is routine.
Why not just use cellular IoT (NB-IoT, LTE-M)?
Cellular IoT is the right answer when nodes are scattered beyond any gateway you could practically run (vehicle trackers, shipping container monitoring, remote weather stations). It costs roughly $0.50–2 per node per month in data plans. LoRa is free to operate but requires you to run a gateway. For a farm, a campus, or a neighborhood, LoRa wins on cost. For nationwide fleets, cellular wins on infrastructure.
Get the complete project package
The article above shows the core firmware and the principles behind it. The complete project package — assembled, tested, and ready to flash — is available by email request. We send it manually, and we read every request.
- Complete Arduino sketch (.ino) with full error handling
- List of required libraries with version numbers
- Printable wiring diagram (PDF)
- Bill of materials with current part numbers
- Build guide and troubleshooting tips
- Configuration template (WiFi, MQTT, etc.)
Share your thoughts
Worked with this in production and have a story to share, or disagree with a tradeoff? Email us at support@mybytenest.com — we read everything.