A simple, lightweight library intended to take away some of the cruft and tediousness of using the Android BLE.

Overview

Blueteeth

Android Arsenal !Travis CI build badge

What Is Blueteeth?

Blueteeth is a simple, lightweight library intended to take away some of the cruft and tediousness of using the Android BLE API. I wrote about it originally here: http://www.sureshjoshi.com/mobile/bluetooth-bluetooths-blueteeth/, with a more recent update at http://www.sureshjoshi.com/mobile/blueteeth-for-android/.

It was inspired by the simplicity and ease-of-use of LGBluetooth on iOS.

The upcoming iOS companion library is SwiftyTeeth.

High-Level

An underlying motivator of this BLE library was to build something that didn't require platform-specific hacks.

Blueteeth fixes several Android-platform-specific problems, in terms of calls sometimes working and sometimes not. Under the hood, all calls are initiated from the main thread... This pattern helps solve some manufacturer-specific defects in Android BLE implementations (looking at you, Samsung).

In the .connect() call, I have exposed the autoReconnect parameter which allows an underlying Bluetooth connection to do exactly what it suggests. However, I recommend not using it, and I might remove it entirely. This StackOverflow post explains it well, but essentially, autoReconnect is a very slow, low-power reconnect... I've noticed it can take a minute or two to actually re-connect, so it doesn't function like most developers would expect.

Additionally, when you're done with the BlueteethDevice object, call .close() on it, in order to free up resources (there is a finalizer on the BlueteethDevice object, but don't rely on it running). Calling .close() on the BLE objects is both suggested and required.

Usage

Scan for BLE devices using the BlueteethManager singleton:

BlueteethManager.with(this).scanForPeripherals(DEVICE_SCAN_MILLISECONDS, blueteethDevices -> {
    // Scan completed, iterate through received devices and log their name/mac address
    for (BlueteethDevice device : blueteethDevices) {
        if (!TextUtils.isEmpty(device.getBluetoothDevice().getName())) {
            Timber.d("%s - %s", device.getName(), device.getMacAddress());
        }
    }
});

Initiate a connection using a BlueteethDevice:

mBlueteethDevice.connect(shouldAutoreconnect, isConnected -> {
    Timber.d("Is the peripheral connected? %s", Boolean.toString(isConnected));
});

Discover Bluetooth services and characteristics:

mBlueteethDevice.discoverServices(response -> {
    if (response != BlueteethResponse.NO_ERROR) {
        Timber.e("Discovery error - %s",  response.name());
        return;
    }
    Timber.d("Discovered services... Can now try to read/write...");
});

Write to a connected BlueteethDevice:

mBlueteethDevice.writeCharacteristic(new byte[]{1, 2, 3, 4}, characteristicUUID, serviceUUID, response -> {
    if (response != BlueteethResponse.NO_ERROR) {
        Timber.e("Write error - %s",  response.name());
        return;
    }
    Timber.d("Characterisic Written...");
})

Read from a connected BlueteethDevice:

mBlueteethDevice.readCharacteristic(characteristicUUID, serviceUUID, (response, data) -> {
    if (response != BlueteethResponse.NO_ERROR) {
        Timber.e("Read error - %s",  response.name());
        return;
    }
    Timber.d("Just read the following data... %s",  Arrays.toString(data));
});

Convenience methods to connect (if not connected), and read/write... This will NOT automatically disconnect, however, so you will remain disconnected unless you try to disconnect in the callback:

BlueteethUtils.writeData(new byte[]{1, 2, 3, 4}, characteristicUUID, serviceUUID, mBlueteethDevice, response -> {
    if (response != BlueteethResponse.NO_ERROR) {
        Timber.e("Write error - %s",  response.name());
        return;
    }
    Timber.d("Connected to and wrote characteristic...");
});
BlueteethUtils.read(characteristicUUID, serviceUUID, mBlueteethDevice, (response, data) -> {
    if (response != BlueteethResponse.NO_ERROR) {
        Timber.e("Read error - %s",  response.name());
        return;
    }
    Timber.d("Just connected and read the following data... %s",  Arrays.toString(data));
});

Check out the sample app in blueteeth-sample/ to see the API in action.

Future Directions

Auto-Reconnect

As mentioned above, the stack-level auto-reconnect implementation is pretty useless because of how long the reconnect process takes. I might leave the existing API, however, use a different underlying implementation to allow fast(er) reconnects.

Queues

As mentioned in my original Blueteeth blog post, I hate Callback Hell (or rightward drift), but I haven't yet solved that in this library. The current usage for chaining calls is still, unfortunately, callbacks in callbacks.

Mocks and Tests

Out-of-band of this repo, I have been working on a small mocking framework to a) allow unit/functional testing of classes that call BlueteethManager (singleton), and b) allow a runtime implementation of Blueteeth, so it can be tested in a simulator, and preloaded with 'fake' results.

a) is quick and easy, b) is hard and painful.

I will incorporate that code into this repo when it is stable.

Reactive Everything!

Now that this library is released and progressively becoming more stable, the next step in the process is to create Reactive bindings (RxAndroid bindings specifically). They will be created in a separate repo, so that there isn't a forced, heavy dependency on the Rx framework in any app that just wants to use Blueteeth.

Download

compile 'com.robotpajamas.blueteeth:blueteeth:0.2.0'

Issues

Lastly, I have a few lingering issues that I want to fix in the near future. 1 bug, and a few points just for my sanity: https://github.com/RobotPajamas/Blueteeth/issues

License

The Apache License (Apache)

Copyright (c) 2016 Robot Pajamas

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Comments
  • Devices not found using Android Q

    Devices not found using Android Q

    I pulled the code and ran the blueteeth-sample and debugged into it. See that the DeviceScanViewModel devices = 0 when the stateHandler.notScanning() is hit. Tried it several times and verified that several bluetooth devices were active and within range using another app.

    Note that I updated the compileSdkVersion to 28 and made the target that as well. Also, I converted to use AndroidX as I saw that was an ask. Shouldn't be impacting it though.

    opened by darrinps 9
  • add notify characteristic functionality

    add notify characteristic functionality

    Now its possible to listen to characteristics via onCharacteristicChangedListener:

    Usage:

    STEP 1: blueteethDevice.connect(false, onConnectionChangedListener, onCharacteristicChangedListener, SERVICE_UUID, NOTIFY_CHARACTERISTIC_UUID);

    STEP2: After successful connection trigger: blueteethDevice.registerNotifyOnCharacteristicChanged();

    Enjoy receiving notifications via BLE.

    opened by andacaydin 9
  • No 'BlueteethManager' Found

    No 'BlueteethManager' Found

    The cover page you presented with the example of how to connect looks very promising, although, I can not find any 'blueteeth' or 'bluetooth' manager in any of your files which leaves me clueless as to how to proceed.

    -Also, where you use the Kotlin files blueteethDevice and blueteeth, are those, supposed to be implemented in the java example you have posted that I am trying to make use of?

    opened by elb5465 6
  • Replace Listener calls with 'call'

    Replace Listener calls with 'call'

    Just for consistency, instead of having OnCharacteristicWriteListener.onCharacteristicWritten, replace that with OnCharacteristicWriteListener.call across the board. On one hand, it's less specific - but on the flipside, the listener name makes it clear what's actually happening, so the onCharacteristicWritten is redundant.

    Also, my personal ideal is to use lambdas everywhere (and since the libraries are already there internally, it's exposed externally), the onCharacteristicWritten disappears anyways.

    enhancement 
    opened by sureshjoshi 4
  • Investigate a port to Kotlin to reduce code cruft

    Investigate a port to Kotlin to reduce code cruft

    I've been working with Kotlin a bit recently, and it leads to a lot less cruft vs vanilla Java.

    Also, this would allow aligning external APIs with https://github.com/RobotPajamas/SwiftyTeeth (as Swift/Kotlin are pretty similar - but CoreBluetooth is nothing like Android's BLE)

    opened by sureshjoshi 3
  • Bump lodash from 4.17.10 to 4.17.20 in /test-peripheral/nodejs

    Bump lodash from 4.17.10 to 4.17.20 in /test-peripheral/nodejs

    ⚠️ Dependabot is rebasing this PR ⚠️

    If you make any changes to it yourself then they will take precedence over the rebase.


    Bumps lodash from 4.17.10 to 4.17.20.

    Commits
    Maintainer changes

    This version was pushed to npm by bnjmnt4n, a new releaser for lodash since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • Bump elliptic from 6.4.1 to 6.5.3 in /test-peripheral/nodejs

    Bump elliptic from 6.4.1 to 6.5.3 in /test-peripheral/nodejs

    ⚠️ Dependabot is rebasing this PR ⚠️

    If you make any changes to it yourself then they will take precedence over the rebase.


    Bumps elliptic from 6.4.1 to 6.5.3.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • Bump mixin-deep from 1.3.1 to 1.3.2 in /test-peripheral/nodejs

    Bump mixin-deep from 1.3.1 to 1.3.2 in /test-peripheral/nodejs

    ⚠️ Dependabot is rebasing this PR ⚠️

    If you make any changes to it yourself then they will take precedence over the rebase.


    Bumps mixin-deep from 1.3.1 to 1.3.2.

    Commits
    Maintainer changes

    This version was pushed to npm by doowb, a new releaser for mixin-deep since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • Bump acorn from 5.7.2 to 5.7.4 in /test-peripheral/nodejs

    Bump acorn from 5.7.2 to 5.7.4 in /test-peripheral/nodejs

    ⚠️ Dependabot is rebasing this PR ⚠️

    If you make any changes to it yourself then they will take precedence over the rebase.


    Bumps acorn from 5.7.2 to 5.7.4.

    Commits
    • 6370e90 Mark version 5.7.4
    • fbc15b1 More rigorously check surrogate pairs in regexp validator
    • 910e62b Mark version 5.7.3
    • 3442a80 Make generate-identifier-regex capable of rewriting src/identifier.js
    • 22b22f3 Raise specific errors for unterminated template literals
    • 1461c7c Fix a lint error
    • 0c12f63 Fix tokenizing of regexps after .of
    • 832c308 Fix 404 url
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • Scan resulting in no device found

    Scan resulting in no device found

    This is the code:

    BlueteethManager.with(this).scanForPeripherals(DEVICE_SCAN_MILLISECONDS, bleDevices -> { Timber.d("On Scan completed"); Log.e("SahajLOG", "scan complete" +bleDevices); mProgressBar.setVisibility(View.GONE); for (BlueteethDevice device : bleDevices) { Log.e("SahajLOG", "device.getBluetoothDevice().getName(): " +device.getBluetoothDevice().getName()); if (!TextUtils.isEmpty(device.getBluetoothDevice().getName())) { Timber.d("%s - %s", device.getName(), device.getMacAddress()); mRecyclerView.post(new Runnable() { @Override public void run() { Device_recycleradapter_item mDevice= new Device_recycleradapter_item(); mDevice.setContext(MainActivity.this); mDevice.setmDeviceName(device.getName()); mDevice.setmMacAddress(device.getMacAddress()); mFastItemAdapter.add(mDevice); } }); } } });

    in logcat:

    sahaj.bluice E/SahajLOG: scan complete[]

    the devices returned is empty.

    opened by SahajRana 2
  • not able to retrieve the name of the device

    not able to retrieve the name of the device

    have been trying a few libraries and I like a lot yours but I had the issue that I am not able to print the name of my device while with other libraries they do.

    BlueteethManager.with(this).scanForPeripherals(DEVICE_SCAN_MILLISECONDS, blueteethDevices -> { // Scan completed, iterate through received devices and log their name/mac address for (BlueteethDevice device : blueteethDevices) { if (!TextUtils.isEmpty(device.getBluetoothDevice().getName())) { Timber.d("David %s - %s", device.getName(), device.getMacAddress()); } } });

    opened by dabitdev 2
  • Allow access to call `BluetoothGatt#requestConnectionPriority`

    Allow access to call `BluetoothGatt#requestConnectionPriority`

    Thanks for building a really neat way to access BLE quickly!

    I'd love to be able to change the connection priority, but can't see a way to do that using the library at the moment as the gatt handler is private.

    enhancement 
    opened by simmerz 1
  • Sample does not connect when pairing is required

    Sample does not connect when pairing is required

    I have a device that requires you to pair to it. You cannot pair ahead of time as it stops broadcasting once it is paired. It would be great if the sample were made to work like BLE Scanner (available on Google Play) that pairs then connects.

    opened by darrinps 3
  • Investigate using co-routines for lower level concurrency

    Investigate using co-routines for lower level concurrency

    Using Dispatchito is neat, and has a lot of functionality, but there is also a lot of cruft and it's not easy to test.

    Need to examine if there is an easy port to co-routines to reduce the amount of code. Having a synchronous API would make a lot of handshaking easier, and there could still be a queue API.

    Alternatively, could use a co-routine extension, like there is an Rx extension.

    enhancement 
    opened by sureshjoshi 0
Allows Android apps to interact with BLE beacons

Android Beacon Library An Android library providing APIs to interact with beacons. Please visit the project website for how to use this library. IMPOR

AltBeacon 2.7k Dec 28, 2022
User-friendly Lightweight TPM Remote Attestation over Bluetooth

Ultrablue Ultrablue (User-friendly Lightweight TPM Remote Attestation over Bluetooth) is a solution to allow individual users to perform boot state at

ANSSI 32 Jan 2, 2023
A non-trivial Bluetooth LE app using Kable and app architecture best practices

kable_mvvm_demo The intention of this project is to demonstrate a non-trivial Bluetooth LE app using Kable and app architecture best practices. ⚠️ The

Chris Laplante 14 Aug 18, 2022
Kotlin Asynchronous Bluetooth Low Energy provides a simple Coroutines-powered API for interacting with Bluetooth Low Energy devices.

Kotlin Asynchronous Bluetooth Low Energy provides a simple Coroutines-powered API for interacting with Bluetooth Low Energy devices.

JUUL Labs 275 Sep 14, 2021
Simple bluetooth flutter project

bluetooth_simple Simple bluetooth implementation. Getting Started This project is a starting point for a Flutter application. A few resources to get y

Aleksey Vasiliev 0 Nov 25, 2021
A Bluetooth kotlin multiplatform "Cross-Platform" library for iOS and Android

Blue-Falcon A Bluetooth "Cross Platform" Kotlin Multiplatform library for iOS, Android, MacOS, Raspberry Pi and Javascript. Bluetooth in general has t

Andrew Reed 220 Dec 28, 2022
This library allows for easy access to a Bluetooth LE device's AdRecord and RSSI value. It offers additional functionality for iBeacons.

Bluetooth LE Library for Android This library allows for easy access to a Bluetooth LE device's Advertisement Records. It also offers: A simple runnin

Alexandros Schillings 843 Dec 13, 2022
An Android Library for handling Bluetooth Low Energy on Android Easy

An Android Library for handling Bluetooth Low Energy on Android Easy

Leandro SQ 42 Jan 3, 2023
An Android library that solves a lot of Android's Bluetooth Low Energy problems

A library that makes working with Bluetooth LE on Android a pleasure. Seriously.

Nordic Semiconductor 1.4k Jan 7, 2023
A reactive, interface-driven central role Bluetooth LE library for Android

RxCentralBle RxCentralBle provides a simple reactive paradigm for connecting to and communicating with Bluetooth LE peripherals from the central role.

Uber Open Source 198 Nov 29, 2022
Android Bluetooth Helper Library, Bluetooth Device Finder

Bluetooth Helper Allows you to access the Bluetooth of your mobile device, manage turn-on - turn off, and discover bluetooth devices around you. Getti

Tolga Bolatcan 44 Jul 15, 2022
🍔 Meals is a small demo app based on modern Android technologies and MVVM architecture

Meals ?? Meals is a small demo app based on modern Android technologies and MVVM architecture. built-in Kotlin, Coroutine, Flow, Retrofit, and Jetpack

Amr Jyniat 4 Nov 6, 2022
BluePass extracts two factor authentication codes (2FA) from SMS and sends them to a paired device via Bluetooth RFCOMM.

BluePass extracts two factor authentication codes (2FA) from SMS and sends them to a paired device via Bluetooth RFCOMM.

Manuel Huber 15 Dec 4, 2022
Open-source weight and body metrics tracker, with support for Bluetooth scales

Open-source weight and body metrics tracker, with support for Bluetooth scales

OliE 1.3k Jan 4, 2023
[UNMAINTAINED][Android] Bluetooth Serial Port Profile which comfortable to developer application to communication with microcontroller via bluetooth

⚠ WARNING: This project is no longer being maintained Android-BluetoothSPPLibrary Bluetooth Serial Port Profile which comfortable to developer applica

Akexorcist 1.7k Dec 31, 2022
Smooth communication via bluetooth with other android devices or microcontrollers such as Arduino.

Android Smooth Bluetooth Smooth communication via bluetooth with other android devices or microcontrollers such as Arduino. Getting Started Add Gradle

Mantas Palaima 191 Nov 28, 2022
RxBle: Use Android Bluetooth API in Rx way

RxBle: Use Android Bluetooth API in Rx way A lightweight encapsulation of Android Bluetooth API. Use Android Bluetooth API in Rx way. Support multiple

null 3 Dec 2, 2022
kotlin android - network connection

MarsPhotos - Starter Code Starter code for Android Basics in Kotlin. Introduction Using this stater code you will create MarsPhotos is a demo app that

RICKY_SENSEI 1 Oct 25, 2022
BLESSED Coroutines, a Bluetooth Low Energy (BLE) library for Android using Kotlin Coroutines

BLESSED for Android with Coroutines - BLE made easy BLESSED is a very compact Bluetooth Low Energy (BLE) library for Android 8 and higher, that makes

Martijn van Welie 82 Jan 1, 2023