Files
Claude-Test-App/CLAUDE.md
T

9.8 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.

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/)

./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.