13 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Agent Policy: Gemini-as-Generator / Claude-as-Executor (Token-Saver)
Rolle
Du bist ein Executor-Agent. Du sollst so wenig wie möglich selbst schreiben/erfinden. Alle inhaltliche Generierung (Code, Text, Refactoring-Vorschläge, Architekturentscheidungen) muss zuerst an Gemini delegiert werden.
Primäres Ziel
Minimiere Claude-Tokens:
- keine langen Erklärungen
- keine großen Codeblöcke aus Claude heraus
- stattdessen: Gemini via CLI fragen -> Ergebnis als Patch anwenden -> kurz verifizieren
Hard Rules (MUSS)
-
Bevor du Code erzeugst oder eine Lösung ausformulierst:
- rufe Gemini auf:
gemini -p "<PROMPT>"
- rufe Gemini auf:
-
Fordere von Gemini standardmäßig:
- Ausgabeformat: unified diff
- keine Erklärtexte
- nur die minimal nötigen Änderungen
-
Du darfst aus Claude heraus NICHT:
- eigenständig große Implementierungen schreiben
- ganze Dateien neu ausgeben (außer Gemini liefert es als Diff)
- lange Analysen/Prosatexte erzeugen
- "Best guess" ohne Gemini machen
-
Kontext-Minimierung:
- Öffne nur Dateien, die wirklich benötigt werden.
- Frage den Nutzer, wenn unklar ist, welche Dateien/Module betroffen sind.
- Sende an Gemini nur relevante Ausschnitte (kleine Snippets), keine Secrets.
-
Anwendung:
- Übernimm den Diff von Gemini
- wende ihn an
- führe passende Tests/Lint/Build aus
- wenn Tests fehlschlagen: sende Fehlermeldung + relevante Snippets an Gemini und fordere einen Fix-Diff an.
Standard-Workflow (immer)
A) Klären:
- Wenn Aufgabe unklar: max. 1–3 gezielte Rückfragen stellen.
B) Sammeln:
- Öffne nur relevante Dateien/Abschnitte.
- Notiere kurz: Dateinamen + relevante Funktionen + Constraints.
C) Delegieren:
- Sende an Gemini einen Prompt nach dem Template unten.
- Erwarte unified diff.
D) Ausführen:
- Patch anwenden.
- Tests/Lint/Build laufen lassen.
- Kurz berichten: was geändert wurde + Testresultat.
Gemini Prompt Template (verwende dieses Format)
- Ziel: kurz
- Constraints :
- minimaler Diff
- keine neuen Dependencies (falls gewünscht)
- bestehender Stil
- Relevante Dateien/Snippets:
- File: path/to/file.ext
<kurzer relevanter Ausschnitt>
- File: path/to/file.ext
- Gewünschte Ausgabe:
- "OUTPUT ONLY unified diff, no explanations."
Ausgabe-Regeln (Claude)
- Antworte kurz.
- Keine langen Erklärungen.
- Wenn du Code zeigen musst: zeige nur den Diff oder die wichtigsten geänderten Stellen.
Projektübersicht
BLE-basiertes Indoor-Positionierungssystem, bestehend aus zwei Komponenten:
- Android App (Kotlin/Compose) — schätzt die Nutzerposition mittels RSSI-basierter Trilateration. Die aktive Version befindet sich in
App v2/. Die Version inApp v1_2/ist eine Legacy-Version und soll NICHT bearbeitet werden. - ESP32 Firmware (Arduino) — iBeacon-Sender auf einem Seeed XIAO ESP32-C3. Die aktive Version befindet sich in
ESP32 iBeacon Code/ESP32_iBeacon_v2/.
Build & Run
Android App
Alle Gradle-Kommandos müssen aus dem App v2/-Verzeichnis ausgeführt werden (cd App v2/).
./gradlew assembleDebug # Debug-Build
./gradlew test # Unit-Tests
./gradlew connectedAndroidTest # Instrumentierte Tests (Gerät/Emulator erforderlich)
./gradlew clean assembleDebug # Clean-Build
# Einzelne Testklasse
./gradlew test --tests "com.erik.bleindoorpositioning.ExampleUnitTest"
- Compile/Target SDK: 36, Min SDK: 26
- Java 11-kompatibel, Kotlin 2.0.21 mit Compose Compiler-Plugin
- AGP 9.0.0, Compose BOM 2024.09.00
- Abhängigkeiten werden über
gradle/libs.versions.tomlverwaltet. - Zusätzlich: kotlinx-serialization (Persistenz der JSON-Konfiguration), material-icons-extended (Sonne/Mond-Icons)
ESP32 Firmware
Flashen über die Arduino IDE (Board: XIAO_ESP32C3). Vor dem Flashen jedes Beacons muss BEACON_MINOR in ESP32_iBeacon_v2.ino auf eine eindeutige ID (1, 2, 3, ...) gesetzt werden.
ESP32-C3 Besonderheiten:
- Nutzt native USB-CDC →
delay(2000)am Anfang vonsetup()nötig, damit Serial-Ausgaben nicht verloren gehen - TX-Power muss via
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ...)gesetzt werden, NICHT viaBLEDevice::setPower()(setzt nur DEFAULT, nicht ADV) - Header:
esp_mac.hstattesp_system.h(ESP-IDF v4.0+),esp_bt_device.hexistiert nicht auf ESP32-C3 - Optional:
USE_CUSTOM_MACaktivieren falls ein Android-Gerät den Beacon nicht findet (MAC-Blacklisting-Workaround)
Architektur
Android App
Datenfluss:
BLE Hardware → BleScanner → BeaconFilterManager (RSSI-Filterung + Ausreißer-Erkennung)
→ PositionCalculator (Trilateration + Raumbegrenzung) → EKF update() ─┐
├→ EKF.getPosition() → PositionSmoother (2D-EMA) → UI via StateFlow
Step Detector → SensorFusionManager (PDR: Heading + Schrittlänge) → EKF predict() ─┘
Wichtige Layer (app/src/main/java/com/erik/bleindoorpositioning/):
ble/:BleScannerparst iBeacon-Pakete (Apple 0x4C00 Manufacturer-ID), validiert die UUID und extrahiert Minor/Major/RSSI. Veraltete Beacons (>3s) werden vor der Positionsberechnung ausgefiltert. Auto-Restart alle 25 Min (Android BLE Scan-Limit). Exponentielles Backoff bei Scan-Fehlern.reloadConfig()lädt die Konfiguration dynamisch neu.BeaconDataenthält dieBEACON_UUIDund Standard-Kalibrierungskonstanten.positioning/:RssiFilter-Interface mitKalmanFilter-,ExponentialMovingAverageFilter- undMovingAverageFilter-Implementierungen. Aktuell wirdMovingAverageFilter(windowSize=20)verwendet. DerBeaconFilterManagerverwaltet einen Filter pro Beacon (Schlüssel: Minor ID) mit Ausreißer-Erkennung (Schwellwert: 20 dBm).PositionCalculatorführt eine 3-stufige Pipeline aus: IDW Weighted Centroid (Exponent=4.0) → Gauss-Newton-Trilateration (Gewicht=1/d³) → adaptives Blending basierend auf der minimalen Distanz (0.5m=100% IDW, 1.5m=100% Gauss-Newton), mit Raumbegrenzung (Clamping auf dynamische Stockwerk-Grenzen). Er verwendet Beacon-spezifische Kalibrierungswerte (siehe Sektion "Kalibrierungsparameter").PositionSmootherwendet einen 2D-EMA (alpha=0.25) auf die finale Position an, um Sprünge zu reduzieren.PositionKalmanFilterimplementiert einen Extended Kalman Filter (EKF) zur Sensorfusion:predict()mit PDR-Schritten,update()mit BLE-Positionen (Zustand=[x,y], Prozessrauschen=0.5, Messrauschen=2.0, minimale Kovarianz=0.1 um Einfrieren über Zeit zu verhindern).SensorFusionManagerist optional und stellt PDR (Pedestrian Dead Reckoning) über Android-Sensoren (TYPE_STEP_DETECTOR,TYPE_ROTATION_VECTOR,TYPE_LINEAR_ACCELERATION) bereit. Er verfügt über Stillstandserkennung (Varianz-Schwellwert=0.05, Fenster=10 Samples) und eine dynamische Schrittlängenschätzung (0.5–1.0m). Sensor-Delays:SENSOR_DELAY_FASTESTfür Step Detector und Linear Acceleration,SENSOR_DELAY_GAMEfür Rotation Vector. Graceful Degradation: Wenn die benötigten Sensoren auf dem Gerät nicht verfügbar sind, läuft die App in einem reinen BLE-Modus ohne Schrittverfolgung. Die Verfügbarkeit der Sensoren wird über dieisAvailable-Property geprüft.map/:CoordinateMappertransformiert Meter↔Pixel mit einer dynamischen Bounding Box (unterstützt negative Koordinaten).FloorplanViewzeichnet den Grundriss mit mehreren Räumen, Beacons, Distanzkreisen und der Position auf einer Compose Canvas. Interaktiv: Pinch-to-Zoom (0.5x–5x), Schwenken, Doppeltipp zum Zurücksetzen. Zoom-kompensierte Schriftgrößen und Strichstärken. Dark-Mode-kompatible Farben über MaterialTheme.ui/: Tab-Navigation mitMainScreen(Positionierungs- und Konfigurations-Tabs). Dark/Light-Mode-Umschalter (Sonne/Mond-Icon, persistent).PositioningScreen: Karte mit Vollbild-Modus, einklappbare Beacon-Liste, Scan-Kontrolle.ConfigScreen: Scrollbar mit einklappbaren Sektionen (Stockwerk, Räume, Beacons, Anlernen). Auto-Save bei Änderungen. Raum-Verwaltung (Hinzufügen/Bearbeiten/Löschen), Beacon-Verwaltung mit Detail-Dialog (Position, RSSI @1m, Path Loss Exponent), Beacon-Anlern-Funktion via BLE-Scan.data/:AppConfig(floorWidth/Height, rooms, beacons).ConfigRepositorypersistiert die Konfiguration in den SharedPreferences (JSON/kotlinx.serialization). Inklusive Legacy-Migration für alte Konfigurationsformate.model/:BeaconConfig(Position, rssiAtOneMeter, pathLossExponent),RoomConfig(Name, Position, Größe),ScannedBeacon(Live-Scan-Daten mitlastSeen-Zeitstempel, UUID),Position/PositionResult.
MainActivity erzeugt BleScanner und ConfigRepository und übergibt sie an MainScreen. Lifecycle-Handling: onPause stoppt den Scan, onResume startet ihn wieder. Kein DI-Framework — direkte Instanziierung. Der gesamte Zustand ist reaktiv über Kotlin StateFlow und wird in Compose mit collectAsState() beobachtet. Material 3 Theme mit einem Schwarz/Lila-Farbschema.
ESP32 Firmware
Reiner iBeacon-Advertiser (kein GATT-Service, keine Verbindungen). Wichtige Konfiguration:
- Advertising-Intervall: 100ms (fest)
- TX Power: +9 dBm (Maximum für ESP32-C3), gesetzt über
esp_ble_tx_power_set()für ADV und DEFAULT - Signal Power: -65 dBm @ 1m (muss mit
rssiAtOneMeterin der Android-App übereinstimmen) - Advertising-Flags: 0x06 (LE General Discoverable + BR/EDR Not Supported)
- BLE Sleep zwischen den Advertising-Events für Batteriebetrieb aktiviert
BEACON_MINORidentifiziert jeden physischen Beacon — muss mit der Beacon Minor ID in der App-Konfiguration übereinstimmen- Optionaler MAC-Override (
USE_CUSTOM_MAC) als Workaround für Android BLE-Blacklisting
Kalibrierungsparameter
Android App
Die Beacon-Kalibrierung ist pro Beacon konfigurierbar im Config-Tab der App:
rssiAtOneMeter: Gemessener RSSI bei 1m Abstand (Standard: -65.0 dBm)pathLossExponent: Umgebungsabhängig (Standard: 5.5; Freifeld=2.0, Innenraum=2.5-5.5)- Position (x, y) in Metern — unterstützt negative Koordinaten
Globale Standardwerte in BeaconData.kt:
RSSI_AT_ONE_METER: Standardwert -65.0 dBmPATH_LOSS_EXPONENT: Standardwert 5.5BEACON_UUID: Muss mit der UUID auf den ESP32-Beacons übereinstimmen (87b99b2c-9080-4d04-8f00-a5d9a3f9a0e7)
Stockwerk-Konfiguration (Config-Tab):
floorWidth/floorHeight: Stockwerk-Dimensionen in Meternrooms: Liste von Räumen (Name, Position, Größe) — beliebig viele, auch an negativen Koordinaten
ESP32 (ESP32_iBeacon_v2.ino)
SIGNAL_POWER_1M: Muss mitrssiAtOneMeterin der Android-App übereinstimmen (aktuell 0xBF = -65 dBm)BEACON_MINOR: Eindeutig pro Beacon, muss mit der Beacon Minor ID übereinstimmen, die im Config-Tab der App konfiguriert ist.
Tuning-Parameter (Positionierungspipeline)
Die Positionierung durchläuft mehrere Glättungsstufen. Beim Tuning beachten:
Pipeline: RSSI → MovingAverageFilter → PositionCalculator → EKF → PositionSmoother → UI
| Parameter | Datei | Aktuell | Effekt |
|---|---|---|---|
MovingAverageFilter(windowSize) |
BleScanner.kt | 20 | Mehr = stabilere RSSI, aber trägere Reaktion |
BeaconFilterManager(outlierThreshold) |
BleScanner.kt | 20.0 dBm | Höher = mehr RSSI-Sprünge erlaubt |
PositionKalmanFilter(processNoise) |
BleScanner.kt | 0.5 | Höher = EKF folgt BLE schneller, PDR weniger Gewicht |
positionKalmanFilter.update(measurementNoise) |
BleScanner.kt | 2.0 | Niedriger = mehr Vertrauen in BLE-Position |
PositionSmoother(alpha) |
BleScanner.kt | 0.25 | Höher = reaktiver, niedriger = glatter |
BEACON_MAX_AGE_MS |
BleScanner.kt | 3000 | Kürzer = weniger Einfluss veralteter Beacons |
minCovariance |
PositionKalmanFilter.kt | 0.1 | Verhindert EKF-Einfrieren über Zeit |
STATIONARY_VARIANCE_THRESHOLD |
SensorFusionManager.kt | 0.05 | Niedriger = weniger Schritte als Stillstand gefiltert |
ACCELERATION_WINDOW_SIZE |
SensorFusionManager.kt | 10 | Weniger = schnellere Stillstandserkennung |
Bekanntes Problem: Zu viele Glättungsstufen hintereinander können die Position extrem träge machen. Beim Debugging einzelne Stufen testweise deaktivieren (EKF bypass / Smoother bypass), um die Ursache zu isolieren. Der EKF kann über Zeit einfrieren wenn die Kovarianz zu klein wird — daher minCovariance-Clamp.
Bekannte Probleme / Fehlerbehebung
ESP32 Beacon nicht von Android erkannt
Wenn ein ESP32-C3 Beacon auf einem bestimmten Android-Gerät nicht erkannt wird (auch nicht in nRF Connect), aber auf anderen Geräten sichtbar ist:
- Android Netzwerkeinstellungen zurücksetzen (Einstellungen → Zurücksetzen → Netzwerkeinstellungen)
- Ab Android 13: Bluetooth System-Cache kann nur via ADB gelöscht werden (
adb shell pm clear com.android.bluetooth) USE_CUSTOM_MACim ESP32-Code aktivieren um dem Beacon eine neue MAC-Identität zu geben- ESP32 mit "Erase All Flash Before Sketch Upload" neu flashen
ESP32-C3 Serial Monitor leer
Der XIAO ESP32-C3 nutzt native USB-CDC. Frühe Serial.print()-Aufrufe gehen verloren. Fix: delay(2000) nach Serial.begin().
Sprache
Codebasis, Kommentare und Dokumentation sind auf Deutsch. Antworte auf Deutsch, wenn du mit diesem Projekt arbeitest.