9.8 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.
Project Overview
BLE Indoor Positioning system consisting of two components:
- Android App (Kotlin/Compose) — estimates user position via RSSI-based trilateration. Active version in
App v2/. - 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/)
./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 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)
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/:BleScannerparses 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.BeaconDataholds default calibration constants (RSSI_AT_ONE_METER, PATH_LOSS_EXPONENT, BEACON_UUID).positioning/:RssiFilterinterface withKalmanFilter/ExponentialMovingAverageFilter/MovingAverageFilterimplementations.BeaconFilterManagermanages one filter per beacon (keyed by Minor ID) with outlier rejection (Schwellwert: 20 dBm).PositioningCalculatorruns 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-spezifischerssiAtOneMeterundpathLossExponentWerte.PositionSmootherapplies 2D EMA (alpha=0.15) on the final position to reduce jumping.map/:CoordinateMappertransforms meters↔pixels with dynamic bounding box (supports negative coordinates).FloorplanViewdraws 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 mitMainScreen(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).ConfigRepositorypersistiert 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 withlastSeentimestamp, 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_METERin Android app) - Advertising Flags: 0x06 (LE General Discoverable + BR/EDR Not Supported)
- BLE Sleep enabled between advertising events for battery operation
BEACON_MINORidentifies 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 dBmPATH_LOSS_EXPONENT: Default 5.5BEACON_UUID: Must match UUID on ESP32 beacons (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: Must matchRSSI_AT_ONE_METERin 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:
- 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().
Language
The codebase, comments, and documentation are in German. Respond in German when working with this project.