Skip to content

NordicSemiconductor/Kotlin-BLE-Library

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kotlin BLE Library for Android

The library simplifies usage of Android Bluetooth Low Energy on Android. It is a wrapper around native API and uses Kotlin Coroutines for asynchronous operations. The usage is designed to be more natural according to the BLE specification.

Important

This library is in still under construction. Scanning and client should work, but the API may still change. Any feedback is welcome.

Expected changes:

  • Logging
    • Migrating from SLF4J to log delegates to allow logging for different devices separately.
    • Improving logged events
  • Native vs Mock
    • Instead of .native(Context, Scope) and .mock(Environment, Scope) there will be one method taking Environment as parameter. The distinction will be made based on the environment instead.
  • Higher level API
    • We found the library to be difficult to use with multiple independent services. We are trying to come up with a solution that would allow use single Peripheral with different service implementations.

Version 2

We are working on version 2 of the library. The new version will be a complete rewrite of the library.

Current status:

  • Central role
    • Scanning
      • Scanning for nearby Bluetooth LE devices
      • Ranging and monitoring device
      • Obtaining list of connected devices
    • Establishing connection
      • Direct (autoConnect = false)
      • Using AutoConnect feature
      • Service discovery and subscribing to services changes
    • Basic GATT operations
      • Reading / writing characteristics
      • Enabling notifications / indications
      • Subscribing to value changes
      • Requesting highest MTU
    • Advanced GATT operations
      • Requesting PHY
      • Subscribing to PHY changes
      • Requesting connection priority
      • Subscribing to connection parameter changes
      • Reliable write
    • Mock implementation
  • Peripheral role
    • Advertising
    • Setting up GATT server
    • GATT operations
    • Mock implementation

Documentation

Dokka documentation can be found here.

Scanning

val centralManager = CentralManager.Factory.native(context, scope)
centralManager
    .scan(1250.milliseconds) {
        ServiceUUID(someServiceUUID)
        Any {
            Name("MyName")
            Name("OtherName")
        }
    }
    .distinctByPeripheral()
    .map {
        it.peripheral
    }
    .onEach { peripheral ->
        // Do something with the peripheral
    }
    .launchIn(scope)

Connecting

scope.launch {
    try {
        withTimeout(10000) {
            centralManager.connect(
                peripheral = peripheral,
                options = CentralManager.ConnectionOptions.Direct(
                    timeout = 3.seconds,
                    retry = 2,
                    retryDelay = 1.seconds,
                    Phy.PHY_LE_1M,
                ),
                // options = CentralManager.ConnectionOptions.AutoConnect,
            )
            Timber.i("Connected to ${peripheral.name}!")
        }
    
        // The first time the app connects to the peripheral it needs to initiate
        // observers for various parameters.
        // The observers will get cancelled when the connection scope gets cancelled,
        // that is when the device is manually disconnected in case of auto connect,
        // or disconnects for any reason when auto connect was false.
        peripheral.phy
            .onEach {
                Timber.i("PHY changed to: $it")
            }
            .onEmpty {
                Timber.w("PHY didn't change")
            }
            .onCompletion {
                Timber.d("PHY collection completed")
            }
            .launchIn(this)
    } catch (e: Exception) {
        Timber.e(e, "Connection attempt failed")
    }
}

Service discovery

peripheral.services()
    .onEach { services ->
        Timber.i("Services changed: $services")

        services.forEach { remoteService ->
            // Do something with the service.
        }
    }
    .onEmpty {
        Timber.w("No services found")
    }
    .onCompletion {
        Timber.d("Service collection completed")
    }
    .launchIn(scope)

GATT operations

Reading characteristic value

remoteService.characteristics.forEach { remoteCharacteristic ->
    try {
        Timber.w("Reading value of ${remoteCharacteristic.uuid}...")
        val value = remoteCharacteristic.read()
        Timber.i("Value of ${remoteCharacteristic.uuid}: 0x${value.toHexString()}")
    } catch (e: Exception) {
        // An exception is thrown when a characteristic is not readable, or an error occurs.
        Timber.e("Failed to read ${remoteCharacteristic.uuid}: ${e.message}")
    }
}

Subscribing to value changes

remoteService.characteristics.forEach { remoteCharacteristic ->
    try {
        Timber.w("Subscribing to ${remoteCharacteristic.uuid}...")
        remoteCharacteristic.subscribe()
            .onEach { newValue ->
                Timber.i("Value of ${remoteCharacteristic.uuid} changed: 0x${newValue.toHexString()}")
            }
            .onEmpty {
                Timber.w("No updates from ${remoteCharacteristic.uuid}")
            }
            .onCompletion {
                Timber.d("Stopped observing updates from ${remoteCharacteristic.uuid}")
            }
            .launchIn(scope)
    } catch (e: Exception) {
        // An exception is thrown when a characteristic does not have NOTFY or INDICATE property,
        // has no Client Characterisitc Configuration descriptor, or an error occurs.
        Timber.e("Failed to subscribe to ${remoteCharacteristic.uuid}: ${e.message}")
    }
}

For more, see sample.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 12