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>
326 lines
7.5 KiB
Markdown
326 lines
7.5 KiB
Markdown
# 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! 🎉
|