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

348 lines
9.6 KiB
Markdown

# HC-SR04 Ultraschallsensor mit LoRaWAN (LilyGO LoRa32 T3 V1.6.1)
Dieses Projekt liest Distanzmessungen vom HC-SR04 Ultraschallsensor aus und sendet die Daten über LoRaWAN. Die Messwerte werden zusätzlich auf einem OLED-Display angezeigt.
## Hardware-Komponenten
- **LilyGO LoRa32 T3 V1.6.1** (ESP32 mit SX1276/SX1278 LoRa-Modul)
- **HC-SR04 Ultraschallsensor**
- **OLED Display 128x64** (I2C, SSD1306)
## Anschlussplan
### HC-SR04 Ultraschallsensor
```
HC-SR04 LilyGO LoRa32 T3 V1.6.1
-------- -----------------------
VCC <--> 5V (Pin mit 5V-Label)
TRIG <--> GPIO 12
ECHO <--> GPIO 13
GND <--> GND
```
**Wichtig:**
- Der HC-SR04 sollte mit 5V betrieben werden für beste Ergebnisse
- Das ECHO-Signal gibt 5V aus! Um den ESP32 zu schützen, verwende einen **Spannungsteiler**:
- ECHO → 1kΩ Widerstand → GPIO 13 → 2kΩ Widerstand → GND
- Alternativ: Viele HC-SR04 Module haben bereits einen integrierten Spannungsregler
- GPIO 13 ist als INPUT sicher, solange das Signal nicht über 3.6V geht
### OLED Display (SSD1306, 128x64, I2C)
```
OLED Display LilyGO LoRa32 T3 V1.6.1
------------ -----------------------
VCC <--> 3.3V
GND <--> GND
SDA <--> GPIO 21
SCL <--> GPIO 22
```
**Hinweis:** Beim LilyGO LoRa32 T3 V1.6.1 sind die I2C-Pins standardmäßig:
- SDA: GPIO 21
- SCL: GPIO 22
### LoRa-Modul (bereits auf Board integriert)
Das LoRa-Modul SX1276/SX1278 ist bereits auf dem LilyGO Board integriert und nutzt folgende Pins:
```
LoRa SX1276 ESP32
----------- -----
NSS (CS) <--> GPIO 18
RST <--> GPIO 23
DIO0 <--> GPIO 26
DIO1 <--> GPIO 33
DIO2 <--> GPIO 32
MOSI <--> GPIO 27
MISO <--> GPIO 19
SCK <--> GPIO 5
```
## Pinout-Übersicht LilyGO LoRa32 T3 V1.6.1
```
+------------------------+
| LilyGO LoRa32 T3 V1.6 |
| (ESP32) |
+------------------------+
| |
HC-SR04 TRIG -> | GPIO 12 GPIO 21| <- OLED SDA
HC-SR04 ECHO -> | GPIO 13 GPIO 22| <- OLED SCL
| GPIO 15 GPIO 23| <- LoRa RST
| GPIO 2 GPIO 5| <- LoRa SCK
| GPIO 4 GPIO 19| <- LoRa MISO
| GND GPIO 27| <- LoRa MOSI
| 3.3V GPIO 18| <- LoRa NSS
| 5V (VIN) GPIO 26| <- LoRa DIO0
| GPIO 33| <- LoRa DIO1
| GPIO 32| <- LoRa DIO2
+------------------------+
```
**Verfügbare freie GPIOs** (falls du weitere Sensoren anschließen möchtest):
- GPIO 2, 4, 15, 25, 34, 35, 36, 39 (36/39 nur Input)
## LoRaWAN-Konfiguration
### 1. The Things Network (TTN) / ChirpStack einrichten
#### Application erstellen
1. Melde dich bei TTN oder deinem ChirpStack-Server an
2. Erstelle eine neue Application
#### Device registrieren
1. Erstelle ein neues Device in deiner Application
2. Wähle **OTAA** (Over-The-Air Activation)
3. Notiere die folgenden Keys:
- **Device EUI (DevEUI)** - 8 Bytes
- **Application EUI (AppEUI)** - 8 Bytes
- **Application Key (AppKey)** - 16 Bytes
### 2. Keys im Code eintragen
Öffne die Datei [src/main.cpp](src/main.cpp) und trage deine Keys ein:
```cpp
// LoRaWAN Keys (hier deine echten Werte eintragen)
static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };
static const u1_t PROGMEM DEVEUI[8] = { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };
static const u1_t PROGMEM APPKEY[16] = { 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x38, 0x47, 0x56, 0xAB, 0xCD, 0xEF, 0x12 };
```
**Wichtig:** Die Keys müssen in **LSB (Least Significant Byte)** Format eingetragen werden!
#### Beispiel: TTN Keys konvertieren
Wenn TTN dir die Keys so anzeigt:
```
DevEUI: 70B3D57ED005A1B2
AppEUI: 0000000000000001
AppKey: A1B2C3D4E5F6071829...
```
Dann musst du sie für LMIC **umkehren** (LSB):
```cpp
// DevEUI: 70B3D57ED005A1B2 -> LSB
static const u1_t PROGMEM DEVEUI[8] = { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };
// AppEUI: 0000000000000001 -> LSB
static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// AppKey: bleibt in MSB Format (wird nicht umgedreht)
static const u1_t PROGMEM APPKEY[16] = { 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, ... };
```
### 3. Sendeintervall anpassen
Das Sendeintervall kann in [src/main.cpp:32](src/main.cpp#L32) angepasst werden:
```cpp
const unsigned TX_INTERVAL = 30; // Sendeintervall in Sekunden
```
## Payload-Format
Der Sensor sendet die Distanz als **2 Bytes** (uint16_t):
| Byte | Beschreibung |
|------|--------------|
| 0 | High Byte (Distanz in mm) |
| 1 | Low Byte (Distanz in mm) |
**Beispiel:**
- Distanz: 123.4 cm = 1234 mm
- Hex: `0x04D2`
- Payload: `04 D2`
**Messbereich:** 0 - 65535 mm (0 - 65.5 m)
## ChirpStack Decoder
Füge folgenden Decoder in ChirpStack unter **Device Profile → Codec** ein:
### JavaScript Decoder (ChirpStack v3)
```javascript
function Decode(fPort, bytes) {
// Decoder für HC-SR04 Ultraschallsensor
// Payload: 2 Bytes - Distanz in Millimeter (uint16_t)
if (bytes.length !== 2) {
return {
error: "Invalid payload 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
};
}
```
### JavaScript Decoder (ChirpStack v4)
```javascript
function decodeUplink(input) {
// Decoder für HC-SR04 Ultraschallsensor
// Payload: 2 Bytes - Distanz in Millimeter (uint16_t)
var bytes = input.bytes;
if (bytes.length !== 2) {
return {
errors: ["Invalid payload 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 {
data: {
distance_mm: distanceMm,
distance_cm: distanceCm,
distance_m: distanceM
}
};
}
```
### Decoder testen
**Test-Payload:** `04 D2` (1234 mm)
**Erwartetes Ergebnis:**
```json
{
"distance_mm": 1234,
"distance_cm": 123.4,
"distance_m": 1.234
}
```
## The Things Network (TTN) Payload Formatter
Für TTN unter **Applications → Payload Formatters → Uplink**:
```javascript
function decodeUplink(input) {
var bytes = input.bytes;
if (bytes.length !== 2) {
return {
errors: ["Invalid payload length"]
};
}
// Distanz aus 2 Bytes (Big Endian) auslesen
var distanceMm = (bytes[0] << 8) | bytes[1];
return {
data: {
distance_mm: distanceMm,
distance_cm: distanceMm / 10.0,
distance_m: distanceMm / 1000.0
}
};
}
```
## Installation und Verwendung
### Voraussetzungen
- PlatformIO (VS Code Extension oder CLI)
- USB-Treiber für CP2102 (falls nicht installiert)
### Projekt kompilieren und hochladen
```bash
# Projekt bauen
pio run
# Auf Board hochladen
pio run --target upload
# Serial Monitor öffnen
pio device monitor
```
### Serial Monitor Ausgabe
Nach dem Upload solltest du folgende Ausgabe sehen:
```
Starte HC-SR04 + OLED + LoRa
Beitritt läuft...
Netzwerkbeitritt erfolgreich
Distanz gesendet:
Distanz: 123.4 cm
Distanz: 1234.0 mm
Nachricht übertragen.
```
## Bibliotheken
Die benötigten Bibliotheken werden automatisch über PlatformIO installiert (siehe [platformio.ini](platformio.ini)):
- `gamegine/HCSR04 ultrasonic sensor` - HC-SR04 Treiber
- `adafruit/Adafruit SSD1306` - OLED Display
- `adafruit/Adafruit GFX Library` - Grafik-Bibliothek
- `mcci-catena/MCCI LoRaWAN LMIC library` - LoRaWAN Stack (wird automatisch vom Framework geladen)
## Fehlerbehebung
### Device joint nicht
- Überprüfe die Keys (DevEUI, AppEUI, AppKey)
- Achte auf LSB-Format bei DevEUI und AppEUI
- Prüfe die LoRaWAN-Frequenz (EU868, US915, etc.)
- Stelle sicher, dass ein Gateway in Reichweite ist
### HC-SR04 liefert -1 oder fehlerhafte Werte
- Überprüfe die Verkabelung (TRIG → GPIO 15, ECHO → GPIO 13)
- Prüfe die Stromversorgung (min. 5V für HC-SR04 empfohlen)
- Halte Hindernisse im Messbereich (2 cm - 4 m)
- Verwende ggf. einen Spannungsteiler für ECHO (5V → 3.3V)
### OLED Display zeigt nichts an
- Überprüfe die I2C-Verbindung (SDA → GPIO 21, SCL → GPIO 22)
- Prüfe die I2C-Adresse (Standard: 0x3C)
- Stelle sicher, dass die richtigen I2C-Pins verwendet werden (LilyGO T3 V1.6.1 nutzt GPIO 21/22)
- Verwende einen I2C-Scanner zur Diagnose
### Kompilierungsfehler
```bash
# Cache löschen und neu bauen
pio run --target clean
pio run
```
## Energieverbrauch
- **Deep Sleep:** Nicht implementiert (kontinuierlicher Betrieb)
- **Stromverbrauch:** ~100-150 mA (ESP32 + LoRa TX)
- **Empfehlung:** Für batteriebetriebenen Einsatz Deep Sleep zwischen Messungen implementieren
## Lizenz
Dieses Projekt ist Open Source und steht unter der MIT-Lizenz.
## Weitere Informationen
- [LilyGO LoRa32 Hardware Repository](https://github.com/Xinyuan-LilyGO/LilyGO-LoRa-Series)
- [LilyGO T3 V1.6.1 Pinout](https://github.com/Xinyuan-LilyGO/LilyGO-LoRa-Series/blob/master/schematic/T3_V1.6.1.pdf)
- [The Things Network](https://www.thethingsnetwork.org/)
- [ChirpStack Dokumentation](https://www.chirpstack.io/)
- [MCCI LoRaWAN LMIC](https://github.com/mcci-catena/arduino-lmic)
- [HC-SR04 Datasheet](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf)