Scaling Up: A Multi-Node LoRa Sensor Network with a Raspberry Pi Gateway
Our LoRa sensor node guide covered a single node reporting to a single gateway — the simplest useful LoRa setup. This project scales that pattern to a full property-wide sensor network: multiple ESP32 nodes scattered across buildings and outdoor areas, all reporting through a Raspberry Pi gateway into one central dashboard.
When You Actually Need a Mesh/Multi-Node Network
A single node-to-gateway link is fine for one sensor. Once you're covering multiple outbuildings, garden zones, or a property large enough that a single gateway doesn't have clean line-of-sight to every node, you're into genuine network design territory — node addressing, a central data pipeline, and a dashboard that can handle an arbitrary, growing number of sensors rather than one hardcoded endpoint.
Architecture
LayerComponentRole Sensor nodesMultiple ESP32 + LoRa boards, battery/solar poweredEach monitors its local sensors (temperature, soil moisture, door state, whatever's relevant to its location) and transmits periodically with a unique node ID GatewayRaspberry Pi + LoRa HAT/moduleListens continuously for LoRa packets from all nodes, parses and forwards them into persistent storage Storage/dashboardInfluxDB + Grafana on the same Pi, or Home AssistantTime-series storage and visualization — if you've built our Pi weather station, this reuses the exact same InfluxDB/Grafana pattern for a different data sourceNode Addressing at Scale
With multiple nodes, consistent identification is essential — extend the single-node pattern with a structured payload rather than the simple string concatenation that's fine for one node:
#include <ArduinoJson.h> void sendReading(String nodeId, float temperature, float moisture, int battery) { JsonDocument doc; doc["node"] = nodeId; doc["temp"] = temperature; doc["moisture"] = moisture; doc["battery"] = battery; doc["timestamp"] = millis(); // or real time if node has RTC/NTP sync String payload; serializeJson(doc, payload); LoRa.beginPacket(); LoRa.print(payload); LoRa.endPacket(); }JSON payloads cost slightly more airtime than raw string concatenation, but the reliability and extensibility gain (easy to add new fields per-node without breaking a fragile parsing scheme) is worth it once you're managing more than a couple of nodes.
Gateway: Parsing and Routing
The Pi gateway's job expands from "forward one stream" to "receive from many nodes, parse, and store each appropriately":
import serial import json from influxdb_client import InfluxDBClient, Point client = InfluxDBClient(url="http://localhost:8086", token="your-token", org="your-org") write_api = client.write_api() ser = serial.Serial('/dev/ttyUSB0', 115200) # adjust to your LoRa gateway's actual interface while True: line = ser.readline().decode('utf-8').strip() if not line: continue try: data = json.loads(line) point = ( Point("sensor_reading") .tag("node", data["node"]) .field("temperature", data["temp"]) .field("moisture", data.get("moisture", 0)) .field("battery", data["battery"]) ) write_api.write(bucket="lora-sensors", record=point) except (json.JSONDecodeError, KeyError) as e: print(f"Skipped malformed packet: {line}")Tagging each point with the node ID (rather than putting it in a separate field) is what lets Grafana later filter/group by node cleanly — InfluxDB tags are indexed for exactly this kind of query pattern.
Handling Range Gaps: Repeater Nodes
If your property has a dead zone the gateway can't reach directly — a detached building behind other structures, for instance — a simple repeater node can bridge the gap: a node that listens for packets from far nodes and immediately re-transmits them, effectively extending range at the cost of some added latency and complexity.
void loop() { int packetSize = LoRa.parsePacket(); if (packetSize) { String received = ""; while (LoRa.available()) { received += (char)LoRa.read(); } // Only re-transmit if this looks like a valid sensor payload, // to avoid a repeater re-broadcasting noise or malformed packets if (received.startsWith("{")) { delay(100); // small delay to avoid immediate collision with the original transmission LoRa.beginPacket(); LoRa.print(received); LoRa.endPacket(); } } }This is a genuinely simple repeater pattern, not true mesh routing — for larger, more complex networks, established mesh protocols (Meshtastic is a popular pre-built option worth investigating if you outgrow simple point-to-point/repeater patterns) handle multi-hop routing more robustly than a hand-rolled repeater.
Dashboard: Visualizing Multiple Nodes
With node-tagged data in InfluxDB, Grafana panels can filter and group by node dynamically — a single "Temperature by Location" panel showing all nodes as separate lines, rather than needing a hand-built dashboard per sensor. This is where the JSON-with-tags approach from earlier pays off directly: adding a new node to the network means it just appears as a new option in your existing dashboard's node filter, no dashboard rebuilding required.
Power Planning Across a Whole Network
Node TypePower Approach Near-building nodesWired power where practical — removes battery maintenance entirely for easily-reachable locations Remote/outdoor nodesBattery + solar trickle charge, using the deep-sleep pattern from the single-node guide Repeater nodesWired power strongly preferred if feasible — a repeater going offline breaks connectivity for every node behind it, so battery-only repeaters are a network reliability riskScaling Checklist
- Start with 2–3 nodes and confirm the full pipeline (node → gateway → InfluxDB → Grafana) works end to end before scaling up
- Add nodes incrementally, verifying each new node's packets are being received and correctly tagged before moving to the next
- Monitor for packet collisions as node count grows — LoRa is a shared medium, and enough simultaneously-transmitting nodes can start colliding; staggering transmission intervals slightly per-node (rather than having every node transmit on the exact same schedule) reduces this
- Add repeater nodes only where range testing actually shows a gap, not preemptively — unnecessary repeaters add complexity and airtime congestion without benefit
The step from one sensor to a real property-wide network is mostly about data structure and dashboard design rather than fundamentally new radio concepts — get the JSON-tagged-payload pattern right early, and adding node #12 is exactly as easy as adding node #2 was.
Related Guides
- Monitoring Your Home Network with Raspberry Pi and Grafana
- MQTT and Node-RED on Raspberry Pi: Visual Automation for ESP32 Sensor Networks
- Build a Wall-Mounted Maker Dashboard: Aggregating Your Print Farm, Security, and Home Server on One CYD Panel
- DIY Home Security System: Combining Raspberry Pi, ESP32 Sensors, and Flipper Zero
- Build a Meshtastic Off-Grid Mesh Messaging Node with ESP32 and LoRa
- I2C Wiring and Protocol Guide for Arduino, ESP32, and Raspberry Pi
- Tying It Together: Pi + ESP32 + Flipper Home Automation Hub
- ESP-NOW Mesh: Wireless Sensor Networks Without WiFi