ðŸ“Ą Membangun Jaringan Sensor LoRa: Telemetry & Monitoring

🔑 Token
📅 15 Juni 2026 📖 15 menit baca 📊 Menengah

1. Apa itu LoRa & LPWAN?

LoRa (Long Range) adalah teknologi komunikasi nirkabel spread spectrum yang dirancang untuk mengirim data dalam jarak sangat jauh dengan konsumsi daya sangat rendah. LoRa merupakan salah satu implementasi dari keluarga teknologi LPWAN (Low Power Wide Area Network) yang sangat populer di dunia IoT.

Kelebihan utama LoRa dibanding teknologi nirkabel lain:

â„đïļ Frekuensi ISM untuk LoRa

LoRa bekerja pada frekuensi ISM yang berbeda tergantung region. Di Indonesia, frekuensi yang umum digunakan adalah 920-923 MHz (AS923). Di Eropa menggunakan 868 MHz dan di Amerika 915 MHz.

Region Frekuensi Bandwidth Keterangan
Indonesia / Asia Tenggara 920-923 MHz (AS923) 125 kHz Standar yang berlaku di ID
Eropa 863-870 MHz (EU868) 125 kHz Duty cycle 1%
Amerika Utara 902-928 MHz (US915) 125 kHz Frequency hopping
Australia 915-928 MHz (AU915) 125 kHz Mirip US915
ðŸ“ķ Topologi Jaringan LoRa
FIELD SENSORS
ðŸŒĄïļ
Sensor Node 1
Suhu + RH
ESP32 + LoRa
💧
Sensor Node 2
Debit Air
ESP32 + LoRa
ðŸŒą
Sensor Node 3
Kelembaban Tanah
ESP32 + LoRa
ðŸ“Ą LoRa 920 MHz
▾
GATEWAY
ðŸ“Ą
Gateway / Receiver
ESP32 + LoRa + WiFi
Menerima semua data
🌐 WiFi / Ethernet
▾
CLOUD / DASHBOARD
ðŸ–Ĩïļ
Web Server / MQTT
Dashboard (Grafana / Node-RED)

2. Komponen Hardware yang Dibutuhkan

Untuk membangun sistem jaringan sensor LoRa, kita membutuhkan komponen-komponen berikut. Pastikan modul LoRa yang Anda beli sesuai dengan frekuensi wilayah Indonesia (920 MHz / AS923).

Modul Transceiver LoRa

Modul LoRa yang paling populer di pasaran menggunakan chip Semtech SX1276 (sub-GHz) atau SX1278 (433/470 MHz). Kedua chip ini mendukung LoRa dan FSK modulation.

Komponen Spesifikasi Fungsi Harga (Est.)
ESP32 DevKit 240MHz, WiFi+BT, 4MB Flash Controller utama Rp 45.000 - 80.000
Modul LoRa SX1276 920 MHz, SPI interface, +20dBm Transceiver LoRa Rp 35.000 - 65.000
Antena 920 MHz Îŧ/4 monopole / dipole Memperkuat sinyal RF Rp 10.000 - 25.000
Sensor DHT22 Suhu: -40~80°C, RH: 0-100% Pembacaan suhu & kelembaban Rp 20.000 - 35.000
Protoboard + Kabel 400 lubang, jumper wire M-M/M-F Prototype sirkuit Rp 15.000 - 30.000
Power Supply 3.3V-5V, LiPo / USB / Battery Sumber daya node Rp 10.000 - 50.000
⚠ïļ Perhatian: Koneksi Antena

Jangan pernah menghidupkan modul LoRa tanpa antena terpasang! Tanpa antena, energi RF yang dipantulkan kembali ke chip transmitter bisa merusak modul secara permanen. Selalu pastikan antena terhubung sebelum power on.

Koneksi Pin ESP32 ↔ SX1276

Wiring
ESP32          SX1276 Module
─────          ─────────────
GPIO 5  (SCK)  ──→  SCK
GPIO 23 (MOSI) ──→  MOSI
GPIO 19 (MISO) ←──  MISO
GPIO 18 (NSS)  ──→  NSS  (CS)
GPIO 26 (RST)  ──→  RST
GPIO 34 (DIO0) ←──  DIO0 (Interrupt)
3.3V           ──→  VCC
GND            ──→  GND
ðŸ’Ą Tips: Pin DIO0

Pin DIO0 pada modul SX1276 digunakan sebagai interrupt ketika transmisi atau penerimaan data selesai. Gunakan pin GPIO yang mendukung interrupt di ESP32. GPIO 34-39 adalah input-only, cocok untuk DIO0.

3. Setup LoRa Sender (Sensor Node)

Sensor node akan membaca data suhu dan kelembaban dari sensor DHT22, lalu mengirimkannya via LoRa ke receiver/gateway. Node ini akan dalam deep sleep untuk menghemat daya.

Library yang Dibutuhkan

Instal library berikut melalui Arduino IDE Library Manager:

C++ — LoRa Sender (Sensor Node)
#include <SPI.h>
#include <LoRa.h>
#include <DHT.h>

// ── Pin LoRa ──
#define LORA_SCK   5
#define LORA_MISO  19
#define LORA_MOSI  23
#define LORA_NSS   18
#define LORA_RST   26
#define LORA_DIO0  34

// ── Pin Sensor ──
#define DHT_PIN    4
#define DHT_TYPE   DHT22

// ── Konfigurasi LoRa ──
#define LORA_FREQ       920.0E6   // 920 MHz untuk Indonesia
#define LORA_SF         7         // Spreading Factor 7-12
#define LORA_BW         125E3     // Bandwidth 125 kHz
#define LORA_CR         5         // Coding Rate 4/5
#define LORA_TX_POWER   17        // TX Power dalam dBm (max 20)

// ── Variabel Global ──
DHT dht(DHT_PIN, DHT_TYPE);
unsigned long nodeId = 1001;
unsigned long packetCounter = 0;

void setup() {
  Serial.begin(115200);
  while (!Serial);
  Serial.println(F("=== LoRa Sender Node ==="));

  // Inisialisasi DHT sensor
  dht.begin();

  // Inisialisasi SPI dan LoRa
  SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_NSS);
  LoRa.setPins(LORA_NSS, LORA_RST, LORA_DIO0);

  if (!LoRa.begin(LORA_FREQ)) {
    Serial.println(F("[ERROR] Gagal memulai LoRa!"));
    while (1);
  }

  // Konfigurasi parameter LoRa
  LoRa.setSpreadingFactor(LORA_SF);
  LoRa.setSignalBandwidth(LORA_BW);
  LoRa.setCodingRate4(LORA_CR);
  LoRa.setTxPower(LORA_TX_POWER);
  LoRa.enableCrc();

  Serial.println(F("[OK] LoRa terinisialisasi pada 920 MHz"));
  Serial.printf("[INFO] SF=%d, BW=%d kHz, CR=4/%d\n",
                LORA_SF, (int)(LORA_BW/1000), LORA_CR);
}

void loop() {
  // Baca sensor
  float suhu = dht.readTemperature();
  float kelembaban = dht.readHumidity();

  // Validasi pembacaan sensor
  if (isnan(suhu) || isnan(kelembaban)) {
    Serial.println(F("[WARN] Gagal membaca DHT22, retry..."));
    delay(2000);
    return;
  }

  // Bangun payload
  packetCounter++;
  String payload = buildPayload(suhu, kelembaban);

  // Kirim via LoRa
  Serial.printf("[TX] Paket #%lu: %s\n", packetCounter, payload.c_str());

  LoRa.beginPacket();
  LoRa.print(payload);
  LoRa.endPacket();

  Serial.printf("[TX] Terkirim! Suhu=%.1f°C, RH=%.1f%%\n", suhu, kelembaban);

  // Delay sebelum pengiriman berikutnya
  delay(30000);  // Kirim setiap 30 detik
}

String buildPayload(float suhu, float kelembaban) {
  // Format: NODE_ID|PACKET_NUM|SUHU|KELEMBABAN|RSSI
  String payload = "";
  payload += String(nodeId);
  payload += "|" + String(packetCounter);
  payload += "|" + String(suhu, 1);
  payload += "|" + String(kelembaban, 1);
  payload += "|" + String(millis() / 1000);
  return payload;
}
=== LoRa Sender Node === [OK] LoRa terinisialisasi pada 920 MHz [INFO] SF=7, BW=125 kHz, CR=4/5 [TX] Paket #1: 1001|1|28.5|72.3|30 [TX] Terkirim! Suhu=28.5°C, RH=72.3% [TX] Paket #2: 1001|2|28.7|71.8|60 [TX] Terkirim! Suhu=28.7°C, RH=71.8%

4. Setup LoRa Receiver (Gateway)

Receiver/gateway akan menerima data dari semua sensor node via LoRa, lalu menampilkannya di Serial Monitor dan menyiapkan data untuk dikirim ke web server via WiFi. Gateway bertugas sebagai jembatan antara jaringan LoRa dan jaringan internet.

C++ — LoRa Receiver (Gateway)
#include <SPI.h>
#include <LoRa.h>
#include <WiFi.h>
#include <WebServer.h>

// ── Pin LoRa ──
#define LORA_SCK   5
#define LORA_MISO  19
#define LORA_MOSI  23
#define LORA_NSS   18
#define LORA_RST   26
#define LORA_DIO0  34

// ── Konfigurasi WiFi ──
const char* WIFI_SSID = "NamaWiFi";
const char* WIFI_PASS = "PasswordWiFi";

// ── Konfigurasi LoRa ──
#define LORA_FREQ  920.0E6
#define LORA_SF    7

// ── Variabel Global ──
WebServer server(80);
String lastPayload = "";
String lastSenderInfo = "";
unsigned long rxCount = 0;

// ── Buffer data sensor (max 10 node) ──
struct SensorData {
  unsigned long nodeId;
  unsigned long packetNum;
  float suhu;
  float kelembaban;
  int rssi;
  unsigned long uptime;
  unsigned long lastUpdate;
};

SensorData nodes[10];
int nodeCount = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(F("=== LoRa Gateway ==="));

  // Inisialisasi LoRa
  SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_NSS);
  LoRa.setPins(LORA_NSS, LORA_RST, LORA_DIO0);

  if (!LoRa.begin(LORA_FREQ)) {
    Serial.println(F("[ERROR] Gagal memulai LoRa!"));
    while (1);
  }
  LoRa.setSpreadingFactor(LORA_SF);
  LoRa.setSignalBandwidth(125E3);
  LoRa.enableCrc();

  // Koneksi WiFi
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.printf("[WiFi] Menghubungkan ke %s", WIFI_SSID);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf("\n[WiFi] Terhubung! IP: %s\n",
                WiFi.localIP().toString().c_str());

  // Setup Web Server
  server.on("/", handleRoot);
  server.on("/api/data", handleApiData);
  server.begin();
  Serial.println("[HTTP] Web server aktif di port 80");

  Serial.println(F("[OK] Gateway siap menerima data LoRa\n"));
}

void loop() {
  // Cek paket LoRa masuk
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    String incoming = "";
    while (LoRa.available()) {
      incoming += (char)LoRa.read();
    }

    int rssi = LoRa.packetRssi();
    float snr = LoRa.packetSnr();

    rxCount++;
    Serial.printf("[RX] #%lu | RSSI: %d dBm | SNR: %.1f dB\n",
                  rxCount, rssi, snr);
    Serial.printf("[RX] Payload: %s\n", incoming.c_str());

    // Parse payload
    parsePayload(incoming, rssi);
  }

  // Handle HTTP client
  server.handleClient();
}

void parsePayload(String payload, int rssi) {
  // Format: NODE_ID|PACKET_NUM|SUHU|KELEMBABAN|UPTIME
  int idx = 0;
  String parts[5];
  int partCount = 0;

  while (payload.indexOf('|') >= 0 && partCount < 5) {
    idx = payload.indexOf('|');
    parts[partCount++] = payload.substring(0, idx);
    payload = payload.substring(idx + 1);
  }
  if (partCount < 5) parts[partCount++] = payload;

  if (partCount >= 5) {
    unsigned long nId = parts[0].toInt();
    SensorData* node = findOrCreateNode(nId);
    node->nodeId       = nId;
    node->packetNum    = parts[1].toInt();
    node->suhu         = parts[2].toFloat();
    node->kelembaban   = parts[3].toFloat();
    node->uptime       = parts[4].toInt();
    node->rssi         = rssi;
    node->lastUpdate   = millis();

    lastPayload = payload;
    Serial.printf("[PARSE] Node %lu: Suhu=%.1f°C RH=%.1f%% RSSI=%d\n",
                  nId, node->suhu, node->kelembaban, rssi);
  }
}

SensorData* findOrCreateNode(unsigned long nodeId) {
  for (int i = 0; i < nodeCount; i++) {
    if (nodes[i].nodeId == nodeId) return &nodes[i];
  }
  if (nodeCount < 10) {
    nodes[nodeCount].nodeId = nodeId;
    return &nodes[nodeCount++];
  }
  return &nodes[0];
}

void handleRoot() {
  String html = "<html><body style='font-family:monospace;background:#111;color:#0f0;padding:20px'>";
  html += "<h1>ðŸ“Ą LoRa Gateway Dashboard</h1>";
  html += "<p>Total diterima: " + String(rxCount) + " paket</p>";
  html += "<hr>";
  for (int i = 0; i < nodeCount; i++) {
    html += "<div style='margin:10px 0;padding:10px;border:1px solid #333'>";
    html += "<b>Node " + String(nodes[i].nodeId) + "</b><br>";
    html += "Suhu: " + String(nodes[i].suhu, 1) + " °C<br>";
    html += "Kelembaban: " + String(nodes[i].kelembaban, 1) + " %<br>";
    html += "RSSI: " + String(nodes[i].rssi) + " dBm<br>";
    html += "</div>";
  }
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleApiData() {
  String json = "{\"nodes\":[";
  for (int i = 0; i < nodeCount; i++) {
    if (i > 0) json += ",";
    json += "{\"id\":" + String(nodes[i].nodeId);
    json += ",\"suhu\":" + String(nodes[i].suhu, 1);
    json += ",\"kelembaban\":" + String(nodes[i].kelembaban, 1);
    json += ",\"rssi\":" + String(nodes[i].rssi);
    json += ",\"uptime\":" + String(nodes[i].uptime) + "}";
  }
  json += "],\"total_rx\":" + String(rxCount) + "}";
  server.send(200, "application/json", json);
}
=== LoRa Gateway === [WiFi] Menghubungkan ke NamaWiFi.... [WiFi] Terhubung! IP: 192.168.1.50 [HTTP] Web server aktif di port 80 [OK] Gateway siap menerima data LoRa [RX] #1 | RSSI: -68 dBm | SNR: 9.5 dB [RX] Payload: 1001|1|28.5|72.3|30 [PARSE] Node 1001: Suhu=28.5°C RH=72.3% RSSI=-68

5. Protokol Data & Payload Format

Dalam tutorial ini kita menggunakan format payload teks sederhana yang dipisahkan dengan delimiter pipe (|). Format ini mudah di-debug dan cocok untuk prototyping. Untuk produksi, pertimbangkan format binary yang lebih efisien.

ðŸ“Ķ Struktur Payload LoRa
NODE_ID uint32
PACKET_NUM uint32
SUHU float
KELEMBABAN float
UPTIME uint32
Contoh: "1001|1|28.5|72.3|30" ≈ 25 byte
ðŸ“Ķ Format Binary (Produksi)
[NODE_ID]4B
[PKT]4B
[TEMP]2B
[RH]2B
[UP]4B
= 16 byte

Untuk aplikasi yang lebih serius, pertimbangkan menggunakan protokol seperti CBOR (Concise Binary Object Representation) atau MessagePack untuk serialisasi data yang lebih ringkas.

ðŸ’Ą Tips: Panjang Payload

LoRa memiliki batas panjang payload sekitar 256 byte per paket. Semakin pendek payload, semakin cepat transmisi dan semakin hemat daya. Hindari mengirim data teks berlebihan — kirim hanya nilai yang diperlukan.

6. Jarak & Faktor Spreading

Spreading Factor (SF) adalah parameter paling penting dalam LoRa yang menentukan trade-off antara jarak komunikasi dan data rate. SF yang lebih tinggi berarti sinyal lebih kuat (jangkauan lebih jauh) tetapi data rate lebih lambat.

⚡ Cepat

SF7

Data Rate5.47 kbps
Chips/Symbol1.024
Jarak2 km (urban)
Time on Air~56 ms
↔ Sedang

SF8

Data Rate3.13 kbps
Chips/Symbol2.048
Jarak3 km (urban)
Time on Air~103 ms
↔ Sedang

SF9

Data Rate1.76 kbps
Chips/Symbol4.096
Jarak4 km (urban)
Time on Air~185 ms
↔ Sedang

SF10

Data Rate0.98 kbps
Chips/Symbol8.192
Jarak6 km (urban)
Time on Air~371 ms
ðŸĒ Lambat

SF11

Data Rate0.54 kbps
Chips/Symbol16.384
Jarak9 km (rural)
Time on Air~741 ms
ðŸ“Ą Jarak Jauh

SF12

Data Rate0.29 kbps
Chips/Symbol32.768
Jarak15+ km (rural)
Time on Air~1483 ms
⚠ïļ Spreading Factor & Duty Cycle

Semakin tinggi SF, semakin lama waktu transmisi (Time on Air). Pada band AS923, perangkat harus mematuhi aturan duty cycle. Jika Time on Air terlalu lama, interval pengiriman harus diperpanjang untuk tetap mematuhi regulasi.

RSSI (Received Signal Strength Indicator) dan SNR (Signal-to-Noise Ratio) adalah metrik penting untuk mengevaluasi kualitas link LoRa:

7. Monitoring Dashboard dengan Web Server

Setelah gateway menerima data LoRa, langkah selanjutnya adalah menampilkannya di dashboard yang bisa diakses dari browser. Kita bisa memanfakan ESP32 yang sudah terhubung WiFi sebagai web server, atau mengirim data ke platform cloud seperti MQTT, Grafana, atau ThingSpeak.

Dashboard Sederhana dengan ESP32 Web Server

ESP32 gateway yang sudah terkoneksi WiFi bisa langsung menjalankan web server. Di tutorial ini kita sudah menyertakan endpoint /api/data yang mengembalikan data JSON dari semua node.

Menghubungkan ke MQTT Broker

Untuk integrasi yang lebih robust, kirim data dari gateway ke MQTT broker agar bisa diproses oleh Node-RED, Grafana, atau aplikasi lainnya.

C++ — MQTT Publish di Gateway
#include <PubSubClient.h>

// ── Konfigurasi MQTT ──
const char* MQTT_SERVER = "broker.hivemq.com";
const int   MQTT_PORT   = 1883;
const char* MQTT_TOPIC  = "iothub/lora/nodes/";

WiFiClient espClient;
PubSubClient mqttClient(espClient);

void mqttConnect() {
  while (!mqttClient.connected()) {
    Serial.print("[MQTT] Menghubungkan...");
    String clientId = "lora-gw-" + String(random(0xffff), HEX);

    if (mqttClient.connect(clientId.c_str())) {
      Serial.println(" terhubung!");
    } else {
      Serial.printf(" gagal (rc=%d). Retry...\n", mqttClient.state());
      delay(5000);
    }
  }
}

void publishSensorData(SensorData &node) {
  // Publish ke topik per-node
  String topic = String(MQTT_TOPIC) + String(node.nodeId);

  String json = "{";
  json += "\"suhu\":" + String(node.suhu, 1) + ",";
  json += "\"kelembaban\":" + String(node.kelembaban, 1) + ",";
  json += "\"rssi\":" + String(node.rssi) + ",";
  json += "\"uptime\":" + String(node.uptime) + ",";
  json += "\"timestamp\":" + String(millis());
  json += "}";

  mqttClient.publish(topic.c_str(), json.c_str());
  Serial.printf("[MQTT] Published ke %s\n", topic.c_str());
}

Arsitektur Sistem Lengkap

🏗ïļ Arsitektur End-to-End LoRa Monitoring
FIELD SENSORS Battery Powered
ðŸŒĄïļ
Node 1
Suhu / RH
💧
Node 2
Debit Air
ðŸŒą
Node 3
pH Tanah
ðŸ“Ą LoRa (920 MHz)
▾
GATEWAY Powered 24/7
ðŸ“Ą
ESP32 Gateway
LoRa ↔ WiFi Bridge
CLOUD SERVICES
ðŸ“Ļ
MQTT Broker
Pub/Sub messaging
🌐
Web Server
REST API / Dashboard
📊
ThingSpeak
REST API / Analytics
↓
VISUALIZATION
🔄
Node-RED Flow
Data processing
📈
Grafana Dashboard
Real-time charts
🔔
Alerting
Telegram Notifications

8. Tips Optimasi Jarak & Battery Life

Mengoptimalkan jangkauan dan masa pakai baterai adalah dua tantangan utama dalam deployment jaringan sensor LoRa. Berikut tips-tips yang sudah terbukti efektif:

Optimasi Jarak Komunikasi

Optimasi Battery Life

C++ — Deep Sleep di ESP32 Sensor Node
#define SLEEP_DURATION  300  // 5 menit dalam detik
#define uS_TO_S_FACTOR  1000000ULL

void setup() {
  Serial.begin(115200);

  // Nonaktifkan WiFi & Bluetooth untuk hemat daya
  WiFi.mode(WIFI_OFF);
  btStop();

  // ... inisialisasi LoRa dan baca sensor ...
  // ... kirim data ...

  Serial.printf("[SLEEP] Tidur selama %d detik...\n", SLEEP_DURATION);
  Serial.flush();

  // Masuk deep sleep
  esp_sleep_enable_timer_wakeup(SLEEP_DURATION * uS_TO_S_FACTOR);
  esp_deep_sleep_start();
}

void loop() {
  // Tidak akan pernah sampai sini saat deep sleep
  // Semua logic dijalankan di setup() lalu sleep
}
Parameter Mode Hemat Daya Mode Performance
Spreading Factor SF10-SF12 (jarak jauh) SF7-SF8 (data rate tinggi)
TX Power +10 dBm (hemat) +20 dBm (jarak maks)
Interval Kirim 5-10 menit 10-30 detik
Payload Size < 20 byte (binary) < 50 byte (text)
WiFi (Gateway) Connect per-kirim Always connected
ðŸ’Ą Tips: Estimasi Battery Life

Dengan konfigurasi deep sleep 5 menit dan pengiriman data ~100ms, node sensor bisa bertahan 1-2 tahun dengan baterai LiPo 3.7V 2000mAh. Gunakan rumus: Life = Capacity(mAh) / Average_Current(mA) untuk menghitung estimasi.

9. Uji Pemahaman: Quiz

Setelah membaca tutorial ini, uji pemahamanmu dengan menjawab 5 pertanyaan berikut:

🧠 Quiz: Jaringan Sensor LoRa

1. Frekuensi LoRa yang digunakan di Indonesia adalah...

2. Spreading Factor yang lebih tinggi (SF12 vs SF7) menghasilkan...

3. Mengapa antena harus selalu terpasang sebelum menghidupkan modul LoRa?

4. Pin DIO0 pada modul SX1276 berfungsi sebagai...

5. Fitur ESP32 apa yang paling efektif untuk menghemat daya pada sensor node?