Files
Claude-Test-App/CLAUDE.md
T

182 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## #############################################################################################
## Project Overview
BLE Indoor Positioning system consisting of two components:
1. **Android App** (Kotlin/Compose) — estimates user position via RSSI-based trilateration. Active version in `App v2/`.
2. **ESP32 Firmware** (Arduino) — iBeacon sender on Seeed XIAO ESP32-C3. Active version in `ESP32 iBeacon Code/ESP32_iBeacon_v2/`.
## Build & Run
### Android App (from `App v2/`)
```bash
./gradlew assembleDebug # Debug build
./gradlew test # Unit tests
./gradlew connectedAndroidTest # Instrumented tests (device/emulator required)
./gradlew clean assembleDebug # Clean build
# Single test class
./gradlew test --tests "com.erik.bleindoorpositioning.ExampleUnitTest"
```
- Compile/Target SDK: 36, Min SDK: 26
- Java 11 compatibility, Kotlin 2.0.21 with Compose compiler plugin
- AGP 9.0.0, Compose BOM 2024.09.00
- Dependencies managed via `gradle/libs.versions.toml`
- Additional: kotlinx-serialization (JSON config persistence), material-icons-extended (Sonne/Mond Icons)
### ESP32 Firmware
Flash via Arduino IDE (Board: XIAO_ESP32C3). Before flashing each beacon, set `BEACON_MINOR` in `ESP32_iBeacon_v2.ino` to a unique ID (1, 2, 3, ...).
**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)
## Architecture
### Android App
**Data flow:** BLE Hardware → `BleScanner` → `BeaconFilterManager` (RSSI filtering + Ausreißer-Erkennung) → `PositioningCalculator` (Trilateration + Raumbegrenzung) → `PositionSmoother` (2D-EMA Glättung) → UI via `StateFlow`
Key layers (`app/src/main/java/com/erik/bleindoorpositioning/`):
- **`ble/`**: `BleScanner` parses iBeacon packets (Apple 0x4C00 manufacturer ID), validates UUID, extracts Minor/Major/RSSI. Filtert veraltete Beacons (>3s) vor Positionsberechnung aus. Auto-Restart alle 25 Min (Android BLE Scan-Limit). Exponential Backoff bei Scan-Fehlern. `reloadConfig()` lädt Config dynamisch neu. `BeaconData` holds default calibration constants (RSSI_AT_ONE_METER, PATH_LOSS_EXPONENT, BEACON_UUID).
- **`positioning/`**: `RssiFilter` interface with `KalmanFilter`/`ExponentialMovingAverageFilter`/`MovingAverageFilter` implementations. `BeaconFilterManager` manages one filter per beacon (keyed by Minor ID) with outlier rejection (Schwellwert: 20 dBm). `PositioningCalculator` runs a 3-stage pipeline: IDW weighted centroid (exponent=4.0) → Gauss-Newton trilateration (weight=1/d³) → adaptive blending based on minimum distance (0.5m=100% IDW, 1.5m=100% Gauss-Newton), mit Raumbegrenzung (Clamping auf dynamische Stockwerk-Grenzen aus Räumen+Beacons). Beacon-spezifische `rssiAtOneMeter` und `pathLossExponent` Werte. `PositionSmoother` applies 2D EMA (alpha=0.15) on the final position to reduce jumping.
- **`map/`**: `CoordinateMapper` transforms meters↔pixels with dynamic bounding box (supports negative coordinates). `FloorplanView` draws floor with multiple rooms, beacons, distance circles, and position on a Compose Canvas. Interactive: Pinch-to-Zoom (0.5x–5x), Pan, Double-Tap reset. Zoom-kompensierte Schrift/Borders. Dark Mode kompatible Farben via MaterialTheme.
- **`ui/`**: Tab-Navigation mit `MainScreen` (Positioning + Config Tabs). Dark/Light Mode Toggle (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 (Add/Edit/Delete), 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 Config in SharedPreferences (JSON/kotlinx.serialization). Legacy-Migration für alte Configs.
- **`model/`**: `BeaconConfig` (position, rssiAtOneMeter, pathLossExponent), `RoomConfig` (name, position, size), `ScannedBeacon` (live scan data with `lastSeen` timestamp, uuid), `Position`/`PositionResult`.
`MainActivity` creates `BleScanner` + `ConfigRepository` and passes them to `MainScreen`. Lifecycle-Handling: onPause stoppt Scan, onResume startet wieder. No DI framework — direct instantiation. All state is reactive via Kotlin `StateFlow`, observed in Compose with `collectAsState()`. Material 3 Theme mit Schwarz/Lila Farbschema.
### ESP32 Firmware
Pure iBeacon advertiser (no GATT service, no connections). Key configuration:
- Advertising interval: 100ms (fixed)
- TX Power: +9 dBm (maximum for ESP32-C3), set via `esp_ble_tx_power_set()` for both ADV and DEFAULT
- Signal Power: -65 dBm @ 1m (must match `RSSI_AT_ONE_METER` in Android app)
- Advertising Flags: 0x06 (LE General Discoverable + BR/EDR Not Supported)
- BLE Sleep enabled between advertising events for battery operation
- `BEACON_MINOR` identifies each physical beacon — must match beacon Minor ID in the app's Config tab
- Optional MAC-Override (`USE_CUSTOM_MAC`) for Android BLE-Blacklisting workaround
## Calibration Parameters
### Android App
Beacon-Kalibrierung ist jetzt **pro Beacon konfigurierbar** im Config-Tab der App:
- `rssiAtOneMeter`: Measured RSSI at 1m distance (default: -65.0 dBm)
- `pathLossExponent`: Environment-dependent (default: 5.5; free-space=2.0, indoor=2.5-5.5)
- Position (x, y) in meters — supports negative coordinates
Globale Defaults in `BeaconData.kt`:
- `RSSI_AT_ONE_METER`: Default -65.0 dBm
- `PATH_LOSS_EXPONENT`: Default 5.5
- `BEACON_UUID`: Must match UUID on ESP32 beacons (`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`: Must match `RSSI_AT_ONE_METER` in the Android app (currently 0xBF = -65 dBm)
- `BEACON_MINOR`: Unique per beacon, must match beacon Minor ID configured in the app's Config tab
## Known Issues / Troubleshooting
### 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()`.
## Language
The codebase, comments, and documentation are in German. Respond in German when working with this project.