← Work/BLE Hardware · Mobile Credential · 2025–2026

BLE Access Control

A phone that opens doors over Bluetooth Low Energy.

A Flutter mobile credential for a BLE relay controller: scan for the device, connect, resolve the GATT command characteristic and write two bytes to fire the lock. Native BLE clients in Kotlin and Swift sit behind a five-method platform channel, with access managed centrally in Firestore.

5 methods

Channel surface

2 bytes

Door command

iOS + Android

Platforms

Assigned doors with access schedules; tap to open

Assigned doors with access schedules; tap to open

The problem

The client sells a relay controller that sits beside a door and switches the lock. It advertises over BLE and accepts short binary commands on a GATT characteristic. They needed a mobile credential — stand at the door, tap, the relay fires — with access managed centrally instead of with key fobs.

The outcome

One Flutter codebase driving the same relay controller from iOS and Android. connect() reports success only once a writable command characteristic is in hand, so a connected result means ready to send, not just link up.

What we built

  • Flutter UI with Riverpod state; the app never touches Bluetooth directly.
  • One platform channel, relay_sdk — configure, connect(timeout), triggerRelay1/2(withResponse), disconnect. That is the entire contract between Dart and native code.
  • Native BLE clients on both platforms: android.bluetooth LE in Kotlin, CoreBluetooth in a standalone Swift Package on iOS.
  • Discovery by advertised name prefix, with short-UUID suffix matching for firmware whose 128-bit base was not yet fixed.
  • Firestore decides who may open which door — single-use activation codes, per-user assignments and schedules, streamed live to the app.
Architecture — Flutter, platform channel, native BLE clients

Architecture — Flutter, platform channel, native BLE clients

Stack

FlutterRiverpodCoreBluetoothKotlin BLEFirebaseSwift Package

Next project

NafaPay →