Skip to content

BLE Scanning & Positioning

The mobile app uses Bluetooth Low Energy (BLE) to detect nearby beacons and estimate the user's indoor position.

BLE Scanner

File: mobile/lib/services/ble_scanner.dart

The scanner wraps flutter_blue_plus to detect WTK beacons and collect RSSI (signal strength) readings.

Beacon Filtering

  • Only processes beacons with the WTK_ name prefix
  • Filters to a set of "active" beacons registered via setActiveBeacons(unames)
  • Ignores packets with serviceUuids (e.g. battery service advertisements)

RSSI Buffer

Maintains a circular buffer of the last 50 RSSI readings per beacon. This provides:

  • getAverageRssi(uname) — Average RSSI for smooth positioning
  • getDetectedBeaconsSorted() — All detected beacons ordered by signal strength (strongest first)

Scanning

  • startScan() — Starts continuous BLE scanning with a 4-second timeout
  • stopScan() — Stops scanning
  • onUpdate — Optional callback invoked when scan results change

Positioning Engine

File: mobile/lib/core/positioning.dart

Converts BLE RSSI readings into a 2D position estimate on the floor map.

Setup

loadBeacons(floors) processes the navigation bundle:

  • Registers all non-static beacons by their uname
  • Sets the scanner's active beacon filter
  • Builds an internal map of beacon positions (x, y coordinates on the floor map)

Position Calculation

calculatePosition({isNavigating}) returns a PositionEstimate based on the number of detected beacons:

Beacons DetectedMethodConfidenceAlgorithm
1singleBeacon0.3Snap to beacon position
2midpoint0.5Weighted midpoint (navigating) or strongest beacon (idle)
3+trilateration0.8Cramer's rule trilateration

RSSI to Distance

The path-loss model converts RSSI to estimated distance:

distance = 10^((txPower - rssi) / (10 * n)) * 50

Where:

  • txPower = -59 dBm (calibrated transmit power)
  • n = 2.0 (path-loss exponent)
  • * 50 scales to pixel coordinates on the map

Trilateration

When 3 or more beacons are detected, the engine uses Cramer's rule to solve the trilateration equations:

  1. Takes the 3 strongest beacons
  2. Converts RSSI to distances
  3. Solves the system of circle intersection equations
  4. Returns the estimated (x, y) position

Static Beacons

Beacons with isStatic == 1 are excluded from positioning calculations. Static beacons are used as fixed reference points (POIs) rather than for positioning.

Position Update Cycle

During navigation, the position updates every 2 seconds:

  1. BleScanner provides latest RSSI readings
  2. PositioningEngine.calculatePosition() estimates position
  3. If navigating, position is projected onto the nearest graph edge via NavigationGraph.projectOntoNearestEdge()
  4. NavigationGame.updatePlayerPosition() moves the player arrow
  5. Camera optionally centers on the new position