715 lines
19 KiB
Markdown
715 lines
19 KiB
Markdown
# 📡 Smart Rack LoRa Communication API
|
|
|
|
Backend API untuk komunikasi LoRa (Long Range) yang dapat menerima dan mengirim data dari/ke sensor-sensor IoT dengan jangkauan jauh dan konsumsi daya rendah.
|
|
|
|
## 📡 **API Endpoints**
|
|
|
|
### 1. **Terima Message LoRa dari Gateway**
|
|
```http
|
|
POST /api/lora/receive
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"node_id": "LORA_001",
|
|
"gateway_id": "GATEWAY_001",
|
|
"payload": "SENSOR|VIBRATION|1.5,2.3,1.8|2.0",
|
|
"rssi": -85.5,
|
|
"snr": 8.2,
|
|
"spreading_factor": 7,
|
|
"frequency": 868.1,
|
|
"bandwidth": 125000,
|
|
"received_at": "2026-04-28T10:30:00Z",
|
|
"metadata": {
|
|
"gateway_location": "Building_A",
|
|
"channel": 0
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response Success:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "LoRa message received successfully",
|
|
"data": {
|
|
"message_id": 129,
|
|
"node_id": "LORA_001",
|
|
"message_type": "sensor_data",
|
|
"signal_quality": "good",
|
|
"estimated_distance": 2.5,
|
|
"processing_result": {
|
|
"success": true,
|
|
"action": "vibration_data_saved",
|
|
"vibration_reading_id": 45,
|
|
"magnitude": 3.21,
|
|
"status": "warning",
|
|
"is_abnormal": true
|
|
},
|
|
"received_at": "2026-04-28T10:30:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. **Kirim Command ke LoRa Node**
|
|
```http
|
|
POST /api/lora/send-command
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"node_id": "LORA_001",
|
|
"action": "set_threshold",
|
|
"parameters": ["2.5", "vibration"],
|
|
"gateway_id": "GATEWAY_001",
|
|
"priority": "high"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Command sent to LoRa node",
|
|
"data": {
|
|
"message_id": 130,
|
|
"command_id": "CMD_507f1f77bcf86cd799439011",
|
|
"node_id": "LORA_001",
|
|
"action": "set_threshold",
|
|
"payload": "COMMAND|set_threshold|2.5,vibration|CMD_507f1f77bcf86cd799439011",
|
|
"transmission_result": {
|
|
"success": true,
|
|
"simulated": true
|
|
},
|
|
"transmitted_at": "2026-04-28T10:35:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. **Kirim Konfigurasi ke LoRa Node**
|
|
```http
|
|
POST /api/lora/send-config
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"node_id": "LORA_001",
|
|
"parameter": "sleep_time",
|
|
"value": "300",
|
|
"gateway_id": "GATEWAY_001"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Configuration sent to LoRa node",
|
|
"data": {
|
|
"message_id": 131,
|
|
"config_id": "CFG_507f1f77bcf86cd799439012",
|
|
"node_id": "LORA_001",
|
|
"parameter": "sleep_time",
|
|
"value": "300",
|
|
"payload": "CONFIG|sleep_time|300|CFG_507f1f77bcf86cd799439012",
|
|
"transmission_result": {
|
|
"success": true,
|
|
"simulated": true
|
|
},
|
|
"transmitted_at": "2026-04-28T10:40:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. **Ambil Messages LoRa Terbaru**
|
|
```http
|
|
GET /api/lora/messages?node_id=LORA_001&direction=inbound&message_type=sensor_data&limit=50
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"id": 129,
|
|
"node_id": "LORA_001",
|
|
"gateway_id": "GATEWAY_001",
|
|
"direction": "inbound",
|
|
"message_type": "sensor_data",
|
|
"payload": "SENSOR|VIBRATION|1.5,2.3,1.8|2.0",
|
|
"parsed_data": {
|
|
"sensor_type": "VIBRATION",
|
|
"x_axis": 1.5,
|
|
"y_axis": 2.3,
|
|
"z_axis": 1.8,
|
|
"threshold": 2.0
|
|
},
|
|
"rssi": -85.5,
|
|
"snr": 8.2,
|
|
"spreading_factor": 7,
|
|
"frequency": 868.1,
|
|
"signal_quality": "good",
|
|
"estimated_distance": 2.5,
|
|
"is_processed": true,
|
|
"status": "processed",
|
|
"received_at": "2026-04-28T10:30:00Z",
|
|
"device": {
|
|
"id": 1,
|
|
"name": "LoRa Vibration Sensor A",
|
|
"location": "Ruang Server"
|
|
}
|
|
}
|
|
],
|
|
"count": 1
|
|
}
|
|
```
|
|
|
|
### 5. **Ambil Statistik LoRa Communication**
|
|
```http
|
|
GET /api/lora/statistics?node_id=LORA_001&hours=24
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"total_messages": 150,
|
|
"inbound_messages": 120,
|
|
"outbound_messages": 30,
|
|
"processed_messages": 145,
|
|
"failed_messages": 5,
|
|
"acknowledged_messages": 25,
|
|
"success_rate": 96.67,
|
|
"avg_rssi": -82.3,
|
|
"avg_snr": 7.8,
|
|
"message_types": {
|
|
"sensor_data": 100,
|
|
"heartbeat": 20,
|
|
"command": 25,
|
|
"ack": 3,
|
|
"config": 2
|
|
},
|
|
"node_statistics": {
|
|
"LORA_001": {
|
|
"message_count": 85,
|
|
"avg_rssi": -80.5,
|
|
"last_seen": "2026-04-28T10:30:00Z"
|
|
},
|
|
"LORA_002": {
|
|
"message_count": 65,
|
|
"avg_rssi": -88.2,
|
|
"last_seen": "2026-04-28T10:25:00Z"
|
|
}
|
|
},
|
|
"signal_quality_distribution": {
|
|
"excellent": 15,
|
|
"good": 45,
|
|
"fair": 60,
|
|
"poor": 25,
|
|
"unknown": 5
|
|
},
|
|
"latest_message": {
|
|
"id": 129,
|
|
"node_id": "LORA_001",
|
|
"message_type": "sensor_data",
|
|
"received_at": "2026-04-28T10:30:00Z"
|
|
}
|
|
},
|
|
"period_hours": 24
|
|
}
|
|
```
|
|
|
|
### 6. **Process Unprocessed Messages**
|
|
```http
|
|
POST /api/lora/process-messages
|
|
```
|
|
|
|
## 📊 **LoRa Message Format**
|
|
|
|
### **Sensor Data Payload Format:**
|
|
```
|
|
SENSOR|{SENSOR_TYPE}|{DATA}|{ADDITIONAL_PARAMS}
|
|
```
|
|
|
|
**Contoh:**
|
|
- **Vibration**: `SENSOR|VIBRATION|1.5,2.3,1.8|2.0`
|
|
- **PIR**: `SENSOR|PIR|1|85|120|front`
|
|
- **Door**: `SENSOR|DOOR|1|EMP-1234|45|keycard|front_door`
|
|
|
|
### **Command Payload Format:**
|
|
```
|
|
COMMAND|{ACTION}|{PARAMETERS}|{COMMAND_ID}
|
|
```
|
|
|
|
**Contoh:**
|
|
- `COMMAND|reboot||CMD_123456`
|
|
- `COMMAND|set_threshold|2.5,vibration|CMD_789012`
|
|
- `COMMAND|read_sensors||CMD_345678`
|
|
|
|
### **Config Payload Format:**
|
|
```
|
|
CONFIG|{PARAMETER}|{VALUE}|{CONFIG_ID}
|
|
```
|
|
|
|
**Contoh:**
|
|
- `CONFIG|sleep_time|300|CFG_123456`
|
|
- `CONFIG|tx_power|14|CFG_789012`
|
|
- `CONFIG|threshold|2.0|CFG_345678`
|
|
|
|
### **Heartbeat Payload Format:**
|
|
```
|
|
HEARTBEAT|{BATTERY_LEVEL}|{SIGNAL_STRENGTH}|{UPTIME}
|
|
```
|
|
|
|
**Contoh:**
|
|
- `HEARTBEAT|85|75|3600`
|
|
|
|
### **ACK Payload Format:**
|
|
```
|
|
ACK|{COMMAND_ID}|{STATUS}|{MESSAGE}
|
|
```
|
|
|
|
**Contoh:**
|
|
- `ACK|CMD_123456|success|Command executed`
|
|
- `ACK|CFG_789012|failed|Invalid parameter`
|
|
|
|
## 🔧 **LoRa Parameters**
|
|
|
|
### **Signal Quality Levels:**
|
|
- **Excellent**: RSSI ≥ -70 dBm
|
|
- **Good**: -85 dBm ≤ RSSI < -70 dBm
|
|
- **Fair**: -100 dBm ≤ RSSI < -85 dBm
|
|
- **Poor**: RSSI < -100 dBm
|
|
|
|
### **Spreading Factor (SF):**
|
|
- **SF7**: Fastest data rate, shortest range
|
|
- **SF8-SF10**: Balanced speed and range
|
|
- **SF11-SF12**: Slowest data rate, longest range
|
|
|
|
### **Frequency Bands:**
|
|
- **EU868**: 868.1 - 868.5 MHz (Europe)
|
|
- **US915**: 902 - 928 MHz (North America)
|
|
- **AS923**: 923 MHz (Asia)
|
|
|
|
### **Bandwidth Options:**
|
|
- **125 kHz**: Standard bandwidth
|
|
- **250 kHz**: Higher data rate
|
|
- **500 kHz**: Highest data rate
|
|
|
|
## 📡 **Database Schema**
|
|
|
|
### **lo_ra_messages table:**
|
|
```sql
|
|
- id (bigint, primary key)
|
|
- device_id (foreign key to devices, nullable)
|
|
- node_id (string) - LoRa node identifier
|
|
- gateway_id (string) - LoRa gateway identifier
|
|
- direction (enum: inbound, outbound)
|
|
- message_type (enum: sensor_data, command, heartbeat, ack, config)
|
|
- payload (text) - Raw LoRa message
|
|
- parsed_data (json) - Parsed sensor data
|
|
- rssi (float) - Signal strength
|
|
- snr (float) - Signal-to-noise ratio
|
|
- spreading_factor (integer) - LoRa SF (7-12)
|
|
- frequency (float) - Frequency in MHz
|
|
- bandwidth (integer) - Bandwidth in Hz
|
|
- is_processed (boolean) - Processing status
|
|
- is_acknowledged (boolean) - ACK status
|
|
- status (string) - Message status
|
|
- error_message (text) - Error details
|
|
- metadata (json) - Additional parameters
|
|
- transmitted_at (timestamp) - Transmission time
|
|
- received_at (timestamp) - Reception time
|
|
- created_at, updated_at
|
|
```
|
|
|
|
## 🔌 **Integrasi LoRa Device**
|
|
|
|
### **Arduino/ESP32 + LoRa Module Example:**
|
|
```cpp
|
|
#include <SPI.h>
|
|
#include <LoRa.h>
|
|
#include <ArduinoJson.h>
|
|
|
|
// LoRa pins
|
|
#define SS 18
|
|
#define RST 14
|
|
#define DIO0 26
|
|
|
|
String nodeId = "LORA_001";
|
|
float vibrationThreshold = 2.0;
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
|
|
// Initialize LoRa
|
|
LoRa.setPins(SS, RST, DIO0);
|
|
if (!LoRa.begin(868E6)) {
|
|
Serial.println("Starting LoRa failed!");
|
|
while (1);
|
|
}
|
|
|
|
// LoRa configuration
|
|
LoRa.setSpreadingFactor(7);
|
|
LoRa.setSignalBandwidth(125E3);
|
|
LoRa.setCodingRate4(5);
|
|
LoRa.setTxPower(14);
|
|
|
|
Serial.println("LoRa Sensor Node Started");
|
|
}
|
|
|
|
void loop() {
|
|
// Read sensors
|
|
float x = readAccelX();
|
|
float y = readAccelY();
|
|
float z = readAccelZ();
|
|
|
|
// Send sensor data
|
|
sendVibrationData(x, y, z, vibrationThreshold);
|
|
|
|
// Check for incoming commands
|
|
checkForCommands();
|
|
|
|
// Send heartbeat every 5 minutes
|
|
static unsigned long lastHeartbeat = 0;
|
|
if (millis() - lastHeartbeat > 300000) {
|
|
sendHeartbeat();
|
|
lastHeartbeat = millis();
|
|
}
|
|
|
|
delay(10000); // Send data every 10 seconds
|
|
}
|
|
|
|
void sendVibrationData(float x, float y, float z, float threshold) {
|
|
String payload = "SENSOR|VIBRATION|" + String(x) + "," + String(y) + "," + String(z) + "|" + String(threshold);
|
|
|
|
LoRa.beginPacket();
|
|
LoRa.print(payload);
|
|
LoRa.endPacket();
|
|
|
|
Serial.println("Sent: " + payload);
|
|
}
|
|
|
|
void sendHeartbeat() {
|
|
int batteryLevel = readBatteryLevel();
|
|
int signalStrength = LoRa.packetRssi();
|
|
unsigned long uptime = millis() / 1000;
|
|
|
|
String payload = "HEARTBEAT|" + String(batteryLevel) + "|" + String(signalStrength) + "|" + String(uptime);
|
|
|
|
LoRa.beginPacket();
|
|
LoRa.print(payload);
|
|
LoRa.endPacket();
|
|
|
|
Serial.println("Heartbeat sent: " + payload);
|
|
}
|
|
|
|
void checkForCommands() {
|
|
int packetSize = LoRa.parsePacket();
|
|
if (packetSize) {
|
|
String command = "";
|
|
while (LoRa.available()) {
|
|
command += (char)LoRa.read();
|
|
}
|
|
|
|
Serial.println("Received command: " + command);
|
|
processCommand(command);
|
|
}
|
|
}
|
|
|
|
void processCommand(String command) {
|
|
// Parse command: COMMAND|action|parameters|command_id
|
|
int firstPipe = command.indexOf('|');
|
|
int secondPipe = command.indexOf('|', firstPipe + 1);
|
|
int thirdPipe = command.indexOf('|', secondPipe + 1);
|
|
|
|
if (firstPipe == -1) return;
|
|
|
|
String type = command.substring(0, firstPipe);
|
|
String action = command.substring(firstPipe + 1, secondPipe);
|
|
String parameters = command.substring(secondPipe + 1, thirdPipe);
|
|
String commandId = command.substring(thirdPipe + 1);
|
|
|
|
if (type == "COMMAND") {
|
|
if (action == "reboot") {
|
|
sendAck(commandId, "success", "Rebooting");
|
|
ESP.restart();
|
|
} else if (action == "set_threshold") {
|
|
vibrationThreshold = parameters.toFloat();
|
|
sendAck(commandId, "success", "Threshold updated");
|
|
} else if (action == "read_sensors") {
|
|
float x = readAccelX();
|
|
float y = readAccelY();
|
|
float z = readAccelZ();
|
|
sendVibrationData(x, y, z, vibrationThreshold);
|
|
sendAck(commandId, "success", "Sensors read");
|
|
} else {
|
|
sendAck(commandId, "failed", "Unknown command");
|
|
}
|
|
}
|
|
}
|
|
|
|
void sendAck(String commandId, String status, String message) {
|
|
String payload = "ACK|" + commandId + "|" + status + "|" + message;
|
|
|
|
LoRa.beginPacket();
|
|
LoRa.print(payload);
|
|
LoRa.endPacket();
|
|
|
|
Serial.println("ACK sent: " + payload);
|
|
}
|
|
|
|
float readAccelX() { return random(-300, 300) / 100.0; }
|
|
float readAccelY() { return random(-300, 300) / 100.0; }
|
|
float readAccelZ() { return random(-300, 300) / 100.0; }
|
|
int readBatteryLevel() { return random(20, 100); }
|
|
```
|
|
|
|
### **Python LoRa Gateway Example:**
|
|
```python
|
|
import serial
|
|
import requests
|
|
import json
|
|
import time
|
|
from datetime import datetime
|
|
|
|
class LoRaGateway:
|
|
def __init__(self, serial_port='/dev/ttyUSB0', api_url='http://localhost:8000/api/lora'):
|
|
self.serial_port = serial_port
|
|
self.api_url = api_url
|
|
self.gateway_id = 'GATEWAY_001'
|
|
|
|
def start_listening(self):
|
|
try:
|
|
ser = serial.Serial(self.serial_port, 115200, timeout=1)
|
|
print(f"LoRa Gateway started on {self.serial_port}")
|
|
|
|
while True:
|
|
if ser.in_waiting > 0:
|
|
line = ser.readline().decode('utf-8').strip()
|
|
if line:
|
|
self.process_lora_message(line)
|
|
|
|
time.sleep(0.1)
|
|
|
|
except Exception as e:
|
|
print(f"Gateway error: {e}")
|
|
|
|
def process_lora_message(self, raw_message):
|
|
try:
|
|
# Parse LoRa message format: NODE_ID:PAYLOAD:RSSI:SNR
|
|
parts = raw_message.split(':')
|
|
if len(parts) >= 4:
|
|
node_id = parts[0]
|
|
payload = parts[1]
|
|
rssi = float(parts[2])
|
|
snr = float(parts[3])
|
|
|
|
# Send to backend API
|
|
self.send_to_backend(node_id, payload, rssi, snr)
|
|
|
|
except Exception as e:
|
|
print(f"Message processing error: {e}")
|
|
|
|
def send_to_backend(self, node_id, payload, rssi, snr):
|
|
try:
|
|
data = {
|
|
'node_id': node_id,
|
|
'gateway_id': self.gateway_id,
|
|
'payload': payload,
|
|
'rssi': rssi,
|
|
'snr': snr,
|
|
'spreading_factor': 7,
|
|
'frequency': 868.1,
|
|
'bandwidth': 125000,
|
|
'received_at': datetime.now().isoformat(),
|
|
'metadata': {
|
|
'gateway_location': 'Building_A',
|
|
'channel': 0
|
|
}
|
|
}
|
|
|
|
response = requests.post(f"{self.api_url}/receive", json=data)
|
|
|
|
if response.status_code == 201:
|
|
print(f"Message sent to backend: {node_id} -> {payload}")
|
|
else:
|
|
print(f"Backend error: {response.status_code}")
|
|
|
|
except Exception as e:
|
|
print(f"Backend communication error: {e}")
|
|
|
|
# Usage
|
|
if __name__ == "__main__":
|
|
gateway = LoRaGateway()
|
|
gateway.start_listening()
|
|
```
|
|
|
|
## 🧪 **Testing API**
|
|
|
|
### **Test dengan cURL:**
|
|
```bash
|
|
# Test receive LoRa message (vibration sensor)
|
|
curl -X POST http://localhost:8000/api/lora/receive \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"node_id": "LORA_001",
|
|
"gateway_id": "GATEWAY_001",
|
|
"payload": "SENSOR|VIBRATION|2.5,3.2,1.8|2.0",
|
|
"rssi": -82.5,
|
|
"snr": 7.8,
|
|
"spreading_factor": 7,
|
|
"frequency": 868.1
|
|
}'
|
|
|
|
# Test receive LoRa message (PIR sensor)
|
|
curl -X POST http://localhost:8000/api/lora/receive \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"node_id": "LORA_002",
|
|
"payload": "SENSOR|PIR|1|85|120|front",
|
|
"rssi": -88.2,
|
|
"snr": 6.5
|
|
}'
|
|
|
|
# Test receive LoRa message (door access)
|
|
curl -X POST http://localhost:8000/api/lora/receive \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"node_id": "LORA_003",
|
|
"payload": "SENSOR|DOOR|1|EMP-1234|45|keycard|front_door",
|
|
"rssi": -75.8,
|
|
"snr": 9.2
|
|
}'
|
|
|
|
# Send command to LoRa node
|
|
curl -X POST http://localhost:8000/api/lora/send-command \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"node_id": "LORA_001",
|
|
"action": "set_threshold",
|
|
"parameters": ["2.5", "vibration"],
|
|
"priority": "high"
|
|
}'
|
|
|
|
# Send config to LoRa node
|
|
curl -X POST http://localhost:8000/api/lora/send-config \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"node_id": "LORA_001",
|
|
"parameter": "sleep_time",
|
|
"value": "300"
|
|
}'
|
|
|
|
# Get LoRa messages
|
|
curl "http://localhost:8000/api/lora/messages?node_id=LORA_001&limit=10"
|
|
|
|
# Get LoRa statistics
|
|
curl "http://localhost:8000/api/lora/statistics?hours=24"
|
|
```
|
|
|
|
## 📈 **LoRa Dashboard Features**
|
|
|
|
### **Real-time Monitoring:**
|
|
- Active LoRa nodes status
|
|
- Signal quality per node
|
|
- Message throughput
|
|
- Battery levels
|
|
- Network coverage map
|
|
|
|
### **Communication Management:**
|
|
- Send commands to nodes
|
|
- Configure node parameters
|
|
- Monitor acknowledgments
|
|
- Retry failed transmissions
|
|
|
|
## 🔒 **Security & Best Practices**
|
|
|
|
### **LoRa Security:**
|
|
- **Payload Encryption**: Encrypt sensitive sensor data
|
|
- **Node Authentication**: Validate node IDs
|
|
- **Message Integrity**: Check message checksums
|
|
- **Replay Protection**: Prevent message replay attacks
|
|
|
|
### **Network Optimization:**
|
|
- **Adaptive Data Rate (ADR)**: Optimize SF and TX power
|
|
- **Duty Cycle Compliance**: Respect regional regulations
|
|
- **Gateway Load Balancing**: Distribute traffic across gateways
|
|
- **Message Prioritization**: Handle urgent messages first
|
|
|
|
## 🚀 **Deployment & Configuration**
|
|
|
|
### **Environment Variables:**
|
|
```env
|
|
# LoRa Gateway Settings
|
|
LORA_GATEWAY_URL=http://localhost:8080/lora/send
|
|
LORA_DEFAULT_FREQUENCY=868.1
|
|
LORA_DEFAULT_SF=7
|
|
LORA_DEFAULT_TX_POWER=14
|
|
|
|
# LoRa Network Settings
|
|
LORA_NETWORK_ID=1
|
|
LORA_APP_KEY=your_app_key_here
|
|
LORA_DEVICE_EUI_PREFIX=LORA_
|
|
|
|
# Processing Settings
|
|
LORA_AUTO_PROCESS=true
|
|
LORA_RETRY_FAILED_MESSAGES=true
|
|
LORA_MAX_RETRY_ATTEMPTS=3
|
|
```
|
|
|
|
### **Konfigurasi LoRa Network:**
|
|
```php
|
|
// config/lora.php
|
|
return [
|
|
'gateway' => [
|
|
'url' => env('LORA_GATEWAY_URL', 'http://localhost:8080/lora/send'),
|
|
'timeout' => 10,
|
|
'retry_attempts' => 3
|
|
],
|
|
'network' => [
|
|
'frequency' => env('LORA_DEFAULT_FREQUENCY', 868.1),
|
|
'spreading_factor' => env('LORA_DEFAULT_SF', 7),
|
|
'tx_power' => env('LORA_DEFAULT_TX_POWER', 14),
|
|
'bandwidth' => 125000
|
|
],
|
|
'processing' => [
|
|
'auto_process' => env('LORA_AUTO_PROCESS', true),
|
|
'batch_size' => 100,
|
|
'retry_failed' => env('LORA_RETRY_FAILED_MESSAGES', true)
|
|
]
|
|
];
|
|
```
|
|
|
|
## 📞 **Troubleshooting**
|
|
|
|
### **Common Issues:**
|
|
1. **Poor signal quality**: Adjust SF or TX power
|
|
2. **Message loss**: Check gateway connectivity
|
|
3. **Processing delays**: Increase batch processing
|
|
4. **Node offline**: Check battery and coverage
|
|
|
|
### **Monitoring Commands:**
|
|
```bash
|
|
# Check LoRa logs
|
|
tail -f storage/logs/laravel.log | grep LoRa
|
|
|
|
# Process unprocessed messages
|
|
curl -X POST http://localhost:8000/api/lora/process-messages
|
|
|
|
# Check node statistics
|
|
curl "http://localhost:8000/api/lora/statistics?node_id=LORA_001"
|
|
```
|
|
|
|
---
|
|
|
|
**Backend Smart Rack LoRa Communication siap digunakan!** 🎉
|
|
|
|
Sistem akan otomatis:
|
|
- ✅ Menerima data sensor via LoRa dengan jangkauan jauh
|
|
- ✅ Parsing dan processing data sensor secara otomatis
|
|
- ✅ Mengirim command dan konfigurasi ke LoRa nodes
|
|
- ✅ Monitoring kualitas sinyal dan status nodes
|
|
- ✅ Integrasi seamless dengan sensor vibration, PIR, dan door access
|
|
- ✅ Menyediakan statistik dan analytics LoRa network
|
|
- ✅ Support multiple LoRa gateways dan nodes
|
|
- ✅ Battery monitoring dan low power management |