Christian Mueller e706b22f31 Add design specification for LoRaWAN Web Portal
Based on Pflichtenheft v2.2, documents the full system design
including daemon, API, frontend, and infrastructure with agreed
deviations (Axum 0.8, SQLx 0.8, Vite 6, Tailwind 4).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 22:27:49 +01:00

326 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ChirpStack Decoder Einrichtung
## Problem
Die Payload wird als Hex-String angezeigt (`frm_payload:"008b"`), aber nicht dekodiert.
## Lösung: Decoder im Device Profile einrichten
### Schritt 1: Device Profile öffnen
1. Gehe zu **Device Profiles** in ChirpStack
2. Wähle das Device Profile aus, das dein Gerät verwendet
3. Falls du nicht weißt welches: Gehe zu deinem Device und schaue unter "Device Profile"
### Schritt 2: Codec konfigurieren
#### Für ChirpStack v4:
1. Öffne dein **Device Profile**
2. Klicke auf den Tab **"Codec"**
3. Wähle bei **Codec**: `JavaScript`
4. Füge folgenden Code in das **Uplink decoder** Feld ein:
```javascript
function decodeUplink(input) {
// Robuster Decoder für HC-SR04 Ultraschallsensor
var bytes = input.bytes;
var fPort = input.fPort;
// Debug-Informationen
var debug = {
bytes_received: bytes.length,
fPort: fPort,
raw_hex: ""
};
// Bytes als Hex-String für Debug
for (var i = 0; i < bytes.length; i++) {
debug.raw_hex += bytes[i].toString(16).padStart(2, '0');
}
// Validierung
if (bytes.length !== 2) {
return {
errors: ["Invalid payload length - expected 2 bytes, got " + bytes.length + " bytes. Hex: " + debug.raw_hex]
};
}
// Port 1 = Distanz
if (fPort !== 1) {
return {
errors: ["Invalid fPort - expected 1, got " + fPort]
};
}
// Distanz aus 2 Bytes (Big Endian) auslesen
var distanceMm = (bytes[0] << 8) | bytes[1];
// Umrechnung in verschiedene Einheiten
var distanceCm = distanceMm / 10.0;
var distanceM = distanceMm / 1000.0;
return {
data: {
distance_mm: distanceMm,
distance_cm: distanceCm,
distance_m: distanceM,
debug: debug
}
};
}
```
5. Klicke auf **"Submit"** oder **"Save"**
#### Für ChirpStack v3:
1. Öffne dein **Device Profile**
2. Klicke auf den Tab **"Codec"**
3. Wähle bei **Payload codec**: `Custom JavaScript codec functions`
4. Füge folgenden Code in das **Decode function** Feld ein:
```javascript
function Decode(fPort, bytes) {
if (bytes.length !== 2) {
return {
error: "Invalid payload length - expected 2 bytes, got " + bytes.length
};
}
// Distanz aus 2 Bytes (Big Endian) auslesen
var distanceMm = (bytes[0] << 8) | bytes[1];
// Umrechnung in verschiedene Einheiten
var distanceCm = distanceMm / 10.0;
var distanceM = distanceMm / 1000.0;
return {
distance_mm: distanceMm,
distance_cm: distanceCm,
distance_m: distanceM
};
}
```
5. Klicke auf **"Update device-profile"**
### Schritt 3: Decoder testen
1. Im Device Profile, unter **Codec**
2. Klicke auf **"Test decoder"** (falls vorhanden)
3. Gib ein: `008b` (deine aktuelle Payload)
4. Erwartetes Ergebnis:
```json
{
"data": {
"distance_mm": 139,
"distance_cm": 13.9,
"distance_m": 0.139
}
}
```
### Schritt 4: Neue Daten empfangen
1. Warte auf die nächste Uplink-Nachricht (alle 30 Sekunden)
2. Gehe zu deinem **Device****LoRaWAN frames** oder **Events**
3. Bei der neuen Uplink-Message solltest du nun sehen:
```json
{
"object": {
"distance_mm": 139,
"distance_cm": 13.9,
"distance_m": 0.139
}
}
```
## Deine aktuelle Payload dekodiert
**Raw Payload:** `008b`
- Byte 0: `00` (hex) = 0 (dezimal)
- Byte 1: `8b` (hex) = 139 (dezimal)
- **Distanz:** 0×256 + 139 = **139 mm = 13.9 cm**
Das bedeutet, dein Sensor misst aktuell eine Distanz von **13.9 cm** zu einem Objekt!
## Weitere Beispiele
| Hex Payload | Berechnung | Distanz |
|-------------|------------|---------|
| `008b` | (0×256) + 139 = 139 | 13.9 cm |
| `04d2` | (4×256) + 210 = 1234 | 123.4 cm |
| `0064` | (0×256) + 100 = 100 | 10.0 cm |
| `03e8` | (3×256) + 232 = 1000 | 100.0 cm = 1 m |
| `ffff` | (255×256) + 255 = 65535 | 6553.5 cm = 65.5 m |
## Alternative: Minimaler Decoder (ohne Validierung)
Falls der obige Decoder Probleme macht, versuche diesen **minimalen Decoder**:
```javascript
function decodeUplink(input) {
var bytes = input.bytes;
var distanceMm = (bytes[0] << 8) | bytes[1];
return {
data: {
distance_mm: distanceMm,
distance_cm: distanceMm / 10.0,
distance_m: distanceMm / 1000.0
}
};
}
```
Dieser Decoder hat **keine Fehlerprüfung** und sollte immer funktionieren.
## Troubleshooting
### Decoder wird nicht angewendet
**Problem:** Payload wird immer noch als Hex angezeigt
**Lösungen:**
1. ✅ Stelle sicher, dass der Decoder im **Device Profile** gespeichert wurde
2. ✅ Warte auf eine **neue** Uplink-Message (alte Messages werden nicht neu dekodiert)
3. ✅ Prüfe, ob JavaScript-Fehler im Decoder vorhanden sind
4. ✅ Stelle sicher, dass das richtige Device Profile verwendet wird
### JavaScript Fehler im Decoder
**Fehler:** `SyntaxError` oder `Unexpected token`
**Lösung:**
- Kopiere den Code genau wie oben angegeben
- Achte auf korrekte Klammern `{ }` und Semikolons `;`
- Verwende keinen Kommentar-Stil, der nicht unterstützt wird
### Payload hat falsche Länge
**Fehler:** `Invalid payload length`
**Problem:** Die Payload hat nicht genau 2 Bytes
**Lösung:**
- Überprüfe den Arduino-Code in `do_send()` Funktion
- Stelle sicher, dass `sizeof(payload)` = 2 ist
- Prüfe Serial Monitor Ausgabe
### Negative Distanzwerte
**Problem:** Distanz wird als sehr große Zahl angezeigt
**Ursache:** HC-SR04 liefert Fehler (-1), wird aber als uint16_t interpretiert
**Lösung:** Der Code fängt dies bereits ab und sendet 0 bei Fehlern:
```cpp
if (distance < 0) {
distance = 0; // Fehlerfall
}
```
## Integration mit anderen Systemen
### InfluxDB / Grafana
Nach Dekodierung kannst du die Daten in InfluxDB speichern:
```javascript
// ChirpStack v4 Integration
{
"measurement": "distance_sensor",
"tags": {
"device": "hc-sr04-01"
},
"fields": {
"distance_cm": {{.object.distance_cm}},
"distance_m": {{.object.distance_m}}
}
}
```
### MQTT
Die dekodierten Daten werden automatisch über MQTT publiziert:
**Topic:** `application/{application_id}/device/{dev_eui}/event/up`
**Payload:**
```json
{
"deviceName": "hc-sr04-sensor-01",
"devEUI": "70b3d57ed005a1b2",
"object": {
"distance_mm": 139,
"distance_cm": 13.9,
"distance_m": 0.139
}
}
```
### Node-RED
Du kannst die Daten in Node-RED empfangen und verarbeiten:
```javascript
// Node-RED Function Node
var distance_cm = msg.payload.object.distance_cm;
if (distance_cm < 20) {
msg.payload = "Warnung: Objekt zu nah!";
return [msg, null];
} else {
msg.payload = "Distanz OK: " + distance_cm + " cm";
return [null, msg];
}
```
## Decoder mit erweiterten Features (Optional)
Wenn du später mehr Daten senden möchtest, kannst du den Decoder erweitern:
```javascript
function decodeUplink(input) {
var bytes = input.bytes;
var data = {};
// Port 1: Distanz (2 Bytes)
if (input.fPort === 1 && bytes.length === 2) {
var distanceMm = (bytes[0] << 8) | bytes[1];
data.distance_mm = distanceMm;
data.distance_cm = distanceMm / 10.0;
data.distance_m = distanceMm / 1000.0;
}
// Port 2: Temperatur (optional für später)
else if (input.fPort === 2 && bytes.length === 2) {
var temp = ((bytes[0] << 8) | bytes[1]) / 100.0;
data.temperature = temp;
}
else {
return {
errors: ["Invalid fPort or payload length"]
};
}
return {
data: data
};
}
```
## Zusammenfassung
1. ✅ Gehe zu **Device Profiles** → Dein Profil → **Codec**
2. ✅ Wähle **JavaScript** als Codec
3. ✅ Kopiere den Decoder-Code ein
4. ✅ Speichere das Device Profile
5. ✅ Warte auf nächste Uplink-Message
6. ✅ Überprüfe dekodierte Daten unter **Device****Events**
Deine Sensordaten sollten nun lesbar sein! 🎉