Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Date for Version 2.0.0 #152

Closed
ahmadshoh-orc opened this issue Jun 26, 2024 · 18 comments
Closed

Release Date for Version 2.0.0 #152

ahmadshoh-orc opened this issue Jun 26, 2024 · 18 comments
Labels
question Further information is requested

Comments

@ahmadshoh-orc
Copy link

Hello,

I've read through various issues and noticed mentions of an upcoming version 2.0.0. I am about to start a new project and need to decide whether to wait for the new release or proceed with the current version of the Android-Ble-library with Java. Could you please provide an estimated release date for Version 2.0.0?

Thank you for your help.

@philips77
Copy link
Member

Hi,
I'd recommend the current Android-Ble-library with Java. I hoped to have an alpha version before I go for vacation (end of next week), but some other tasks are popping up like mushrooms after a rain, so it has a high risk.
The Java BLE library is popular and went through lots of fixes, so should be stable. I can't say when the Kotlin BLE 2.0 will reach similar status.

Try to keep BLE API isolated so future migration will be possible :)

@philips77
Copy link
Member

And sorry for the delays! We're doing everything we can to push projects fast.

@philips77 philips77 added the question Further information is requested label Jun 26, 2024
@philips77 philips77 pinned this issue Jun 26, 2024
@philips77
Copy link
Member

Hello,
I pushed my changes to version/2.0 branch.

Supported features:

  1. Client
    1. Scanner
    2. Service discovery
    3. Reading / Writing / Subscribing to values
    4. Reading RSSI
    5. Reading / setting preferred PHY
    6. Subscribing to / requesting connection parameters
    7. Refreshing cache (probably, maybe some delay may be required)
    8. Stub implementation for @Previews, etc.
  2. Server
    1. Not stable API, but no implementation.
  3. Advertiser

Not (yet) supported features:

  1. Client
    1. Bonding / GATT operations when bonded / subscribing to bond state
    2. Mock client implementation
  2. Server
    1. Implementation
    2. Mock implementation

@philips77
Copy link
Member

Scanning sample:

fun startScan() {
_devices.update { listOf(PreviewPeripheral(scope)) }
_isScanning.update { true }
centralManager
.scan(1250) {
Any {
Name("Pixel 5")
Name("Pixel 7")
Name("Nordic_LBS")
Name("DFU2A16")
// TODO Filtering by Regex and other runtime filters
}
}
.distinctByPeripheral()
.map {
it.peripheral
}
//.distinct()
.onEach { newPeripheral ->
Timber.w("Found new device: ${newPeripheral.name} (${newPeripheral.address})")
_devices.update { devices.value + newPeripheral }
}
.onEach { peripheral ->
// Track state of each peripheral.
peripheral.state
.onEach {
Timber.w("State: $it")
}
.launchIn(scope)
}
.catch { t ->
Timber.e("Scan failed: $t")
}
.onCompletion {
_isScanning.update { false }
}
.launchIn(viewModelScope)
}

@philips77
Copy link
Member

Connection sample:

fun connect(peripheral: Peripheral, autoConnect: Boolean) {
if (!peripheral.isDisconnected) { return }
scope.launch {
try {
withTimeout(5000) {
Timber.w("Connecting to ${peripheral.name}...")
centralManager.connect(
peripheral = peripheral,
options = if (autoConnect) {
CentralManager.ConnectionOptions.AutoConnect
} else {
CentralManager.ConnectionOptions.Direct(
timeout = 24.seconds,
retry = 2,
retryDelay = 1.seconds,
Phy.PHY_LE_2M,
)
},
)
}
Timber.w("Connected to ${peripheral.name}!")
// Observe PHY
peripheral.phy
.onEach {
Timber.w("PHY changed to: $it")
}
.launchIn(scope)
// Observe connection parameters
peripheral.connectionParameters
.onEach {
Timber.w("Connection parameters changed to: $it")
}
.launchIn(scope)
// Request MTU
peripheral.requestHighestValueLength()
// Check maximum write length
val writeType = WriteType.WITHOUT_RESPONSE
val length = peripheral.maximumWriteValueLength(writeType)
Timber.w("Maximum write length for $writeType: $length")
// Read RSSI
val rssi = peripheral.readRssi()
Timber.w("RSSI: $rssi dBm")
peripheral.requestConnectionPriority(ConnectionPriority.HIGH)
Timber.w("Connection priority changed to HIGH")
// Discover services and do some GATT operations.
peripheral.services()
.onEach {
Timber.w("Services changed: $it")
// Read values of all characteristics.
it.forEach { remoteService ->
remoteService.characteristics.forEach { remoteCharacteristic ->
try {
val value = remoteCharacteristic.read()
Timber.w("Value of ${remoteCharacteristic.uuid}: 0x${value.toHexString()}")
} catch (e: Exception) {
Timber.e("Failed to read ${remoteCharacteristic.uuid}: ${e.message}")
}
}
}
it.forEach { remoteService ->
remoteService.characteristics.forEach { remoteCharacteristic ->
try {
Timber.w("Subscribing to ${remoteCharacteristic.uuid}...")
remoteCharacteristic.subscribe()
.onEach { newValue ->
Timber.w("Value of ${remoteCharacteristic.uuid} changed: 0x${newValue.toHexString()}")
}
.launchIn(scope)
Timber.w("Subscribing to ${remoteCharacteristic.uuid} complete")
} catch (e: Exception) {
Timber.e("Failed to subscribe to ${remoteCharacteristic.uuid}: ${e.message}")
}
}
}
}
.launchIn(scope)
// When the above operations are in progress, do some other operations in parallel.
// This time service discovery won't be initiated and we're just subscribing to the
// service flow.
peripheral.services()
.onEach {
Timber.w("-- Services changed: $it")
// Read values of all characteristics.
it.forEach { remoteService ->
remoteService.characteristics.forEach { remoteCharacteristic ->
try {
val value = remoteCharacteristic.read()
Timber.w("-- Value of ${remoteCharacteristic.uuid}: 0x${value.toHexString()}")
} catch (e: Exception) {
Timber.e("-- Failed to read ${remoteCharacteristic.uuid}: ${e.message}")
}
}
}
}
.launchIn(scope)
} catch (e: Exception) {
Timber.e(e, "OMG!")
}
}
}

@philips77
Copy link
Member

How to get an instance of a CentralManager?
Here's how:

There's also CentralManager.Factory.mock:

/**
* Creates an implementation of the [CentralManager] which is using native Android API to
* scan and connect to physical Bluetooth LE devices.
*
* @param scope The coroutine scope.
* @property initialState The initial state of the Central Manager,
* defaults to [Manager.State.POWERED_ON].
* @property environment The environment to use for the mock, defaults to the latest supported API.
*/
fun CentralManager.Factory.mock(
scope: CoroutineScope,
initialState: Manager.State = Manager.State.POWERED_ON,
environment: MockEnvironment = MockEnvironment.Api31()
) =
CentralManager(MockCentralManagerEngine(scope, initialState, environment))

but it's not working yet.
The idea is that using the environment one may emulate any platform and possible issues.

@juliankotrba
Copy link

Hi @philips77, really looking forward to try out the version 2 of the library!
Just wanted to ask, have you planned to release the current state on maven central as a 2.0.0-alpha-xxx or so?
Or is there an other easy way to get an aar without checking out the project and building it ourselves?
Thanks!

@philips77
Copy link
Member

philips77 commented Feb 13, 2025

There is a very easy way to try it yourself.

You need to clone the Kotlin BLE library to the same root folder (e.g. StudioProjects) and just add this 3 lines to settings.gradle.kts:
https://github.com/NordicSemiconductor/Android-nRF-Toolbox/blob/1a1f82f017de8f67fd949769b9d9c8fde5b4b92b/settings.gradle.kts#L75-L77

The rest gradle files you configure normally, using implementation "no.nordicsemi.kotlin.ble...." as if you'd use them from Maven Central. Android Studio is smart enough that it matches the group names to the included library:
https://github.com/NordicSemiconductor/Android-nRF-Toolbox/blob/1a1f82f017de8f67fd949769b9d9c8fde5b4b92b/lib_profile/build.gradle.kts#L10

@philips77
Copy link
Member

Just remember to checkout the version2 branch!

@juliankotrba
Copy link

@philips77 nice, thx for the quick answer!

@philips77
Copy link
Member

I'm very intersted in your feedback. You'll be one of the first users except our team.

@cullub
Copy link

cullub commented Feb 21, 2025

Hi @philips77 I'm with a device manufacturer that uses Nordic chips and we're hoping to start a migration to a new native Android app starting in the next 6 months or so. If version 2.0 will be in a beta state soon and stable state in the next 6-9 months, we'd love to use this rather than implementing the Android-BLE library. Any ideas as far as timeline for this project?

We don't need a server implementation, but we would need a feature-complete client implementation sooner rather than later.

@philips77
Copy link
Member

Client implementation is almost feature complete.

All the "common" features should be already operational:

  • scanning with/without filters using Low Latency mode
  • connecting / disconnecting
  • service discovery
  • writing / reading characteristics
  • enabling / disabling notifications / indications
  • subscribing to notifications / indications as flow
  • writing / reading descriptors
  • requesting maximum MTU
  • requesting PHY / listening to PHY changes
  • requesting "connection priority" / listening to connection param updates as flow
  • reading RSSI

Unimplemented features:

  • scanning with other modes than Low Latency, including one with PendingIntent
  • reliable write
  • mock implementation of a peripheral

@cullub
Copy link

cullub commented Feb 26, 2025

That's awesome. Once everything is feature complete, it would be great to have a 2.0.0-alpha release available through maven central

@philips77
Copy link
Member

Sure thing!

@juliankotrba
Copy link

juliankotrba commented Mar 5, 2025

Hi @philips77! Just wanted to let you know that I have started integrating the new version of the library. After solving all the dependency and build setup issues/conflicts I am progressing very slowly because there is not much documentation yet. For example, I tried to connect to one of our devices. As far as I have found out I need to create a Peripheral for do that. Since we do not scan and connect directly, I checked how to create a Peripheral and saw, that you have to pass an Executor, which holds the relevant connection details. The NativeCentralManagerImpl just creates an instance of a NativeExecutor based on the scan result. However, since the class is internal, I am not able to do that in the parent project. Probably I am just missing something but sadly there is currently no time to "play around" more. I will wait for a more advanced state of the library and documentation but I really appreciate all your efforts you are putting into this new version. Thanks and keep up the good work!

@philips77
Copy link
Member

Hello,
The API is a bit similar to the one from iOS, which is much better in my opinion that from Android.
On iOS you get a CBPeripheral using CBCentralManager.retrievePeripherals(withIdentifiers:).

Ini this library you should also use CentralManager instance and this method:

fun getPeripheralById(id: ID): P? = getPeripheralsById(listOf(id)).firstOrNull()

Here's a snipet:

val centralManager = CentralManagerFactory.native(context, scope)
val peripheral = centralManager.getPeripheralById(bluetoothDevice.address)

Internally, it will call this method:

override fun getPeripheralsById(ids: List<String>): List<Peripheral> {
// Ensure the central manager has not been closed.
ensureOpen()
val adapter = manager?.adapter ?: throw BluetoothUnavailableException()
return ids.map { id ->
peripheral(id) {
Peripheral(
scope = scope,
impl = NativeExecutor(applicationContext, adapter.getRemoteDevice(it), null)
.apply {
_bondState
.filter { (address, _) -> address == id }
.onEach { (_, state) -> onBondStateChanged(state) }
.launchIn(scope)
}
)
}
}
}

@philips77
Copy link
Member

For a given CentralManager it returns the same Peripheral instance for a given ID (MAC address).
It also subscribes it to listen to bond states and Bluetooth state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants