Files
Claude-Test-App/CLAUDE.md
T

13 KiB
Raw Blame History

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)

  1. Bevor du Code erzeugst oder eine Lösung ausformulierst:

    • rufe Gemini auf: gemini -p "<PROMPT>"
  2. Fordere von Gemini standardmäßig:

    • Ausgabeformat: unified diff
    • keine Erklärtexte
    • nur die minimal nötigen Änderungen
  3. 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
  4. 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.
  5. 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>
  • 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:

  1. Android App (Kotlin/Compose) — schätzt die Nutzerposition mittels RSSI-basierter Trilateration. Die aktive Version befindet sich in App v2/. Die Version in App v1_2/ ist eine Legacy-Version und soll NICHT bearbeitet werden.
  2. 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.toml verwaltet.
  • 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 von setup() 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 via BLEDevice::setPower() (setzt nur DEFAULT, nicht ADV)
  • Header: esp_mac.h statt esp_system.h (ESP-IDF v4.0+), esp_bt_device.h existiert nicht auf ESP32-C3
  • Optional: USE_CUSTOM_MAC aktivieren 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/: BleScanner parst 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. BeaconData enthält die BEACON_UUID und Standard-Kalibrierungskonstanten.
  • positioning/: RssiFilter-Interface mit KalmanFilter-, ExponentialMovingAverageFilter- und MovingAverageFilter-Implementierungen. Aktuell wird MovingAverageFilter(windowSize=20) verwendet. Der BeaconFilterManager verwaltet einen Filter pro Beacon (Schlüssel: Minor ID) mit Ausreißer-Erkennung (Schwellwert: 20 dBm). PositionCalculator fü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"). PositionSmoother wendet einen 2D-EMA (alpha=0.25) auf die finale Position an, um Sprünge zu reduzieren. PositionKalmanFilter implementiert 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). SensorFusionManager ist 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_FASTEST für Step Detector und Linear Acceleration, SENSOR_DELAY_GAME fü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 die isAvailable-Property geprüft.
  • map/: CoordinateMapper transformiert Meter↔Pixel mit einer dynamischen Bounding Box (unterstützt negative Koordinaten). FloorplanView zeichnet 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 mit MainScreen (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). ConfigRepository persistiert 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 mit lastSeen-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 rssiAtOneMeter in 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_MINOR identifiziert 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 dBm
  • PATH_LOSS_EXPONENT: Standardwert 5.5
  • BEACON_UUID: Muss mit der UUID auf den ESP32-Beacons übereinstimmen (87b99b2c-9080-4d04-8f00-a5d9a3f9a0e7)

Stockwerk-Konfiguration (Config-Tab):

  • floorWidth / floorHeight: Stockwerk-Dimensionen in Metern
  • rooms: Liste von Räumen (Name, Position, Größe) — beliebig viele, auch an negativen Koordinaten

ESP32 (ESP32_iBeacon_v2.ino)

  • SIGNAL_POWER_1M: Muss mit rssiAtOneMeter in 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:

  1. Android Netzwerkeinstellungen zurücksetzen (Einstellungen → Zurücksetzen → Netzwerkeinstellungen)
  2. Ab Android 13: Bluetooth System-Cache kann nur via ADB gelöscht werden (adb shell pm clear com.android.bluetooth)
  3. USE_CUSTOM_MAC im ESP32-Code aktivieren um dem Beacon eine neue MAC-Identität zu geben
  4. 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.