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

7.5 KiB
Raw Blame History

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:
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
    }
  };
}
  1. 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:
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
  };
}
  1. 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:
{
  "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 DeviceLoRaWAN frames oder Events
  3. Bei der neuen Uplink-Message solltest du nun sehen:
{
  "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:

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:

if (distance < 0) {
  distance = 0;  // Fehlerfall
}

Integration mit anderen Systemen

InfluxDB / Grafana

Nach Dekodierung kannst du die Daten in InfluxDB speichern:

// 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:

{
  "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:

// 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:

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 DeviceEvents

Deine Sensordaten sollten nun lesbar sein! 🎉