I'm developing an application to pair with two kind of sensors with the app and also these sensors are pairing with each other, we developed our custom bluetooth communication protocol. The connection is working great usually, but there are still some errors that I'm having hard time to fix it.
Sensor 1 paring alone is working great, but every time I'm pairing both of them, then i close the app, pair it again with first sensor, I got disconnected with status 19 just after the connection is established, after I try again one or two times the connection will be established properly. I was thinking that was a problem with Gatt refresh, but I already tried one solution found here and I keep reproducing this error every time.
fun connectToDevice(device: BraincareDevice, pairColor: Int) {
BleLogHelper.writeLog("Connecting to ${device.name}")
isConnecting = true
val deviceType = if (device is Sensor) DeviceType.SENSOR else DeviceType.DONGLE
if (deviceType == DeviceType.SENSOR) {
sensorConnectionSubscription?.dispose()
} else {
dongleConnectionSubscription?.dispose()
}
val connectionSubscription = device.device.establishConnection(false)
.flatMapSingle { connection ->
if (device is Sensor) {
sensorConnection = connection
connectedSensor = device
} else if (device is Dongle) {
dongleConnection = connection
connectedDongle = device
}
connection.queue(CustomRefresh())
?.observeOn(Schedulers.io())
?.doOnComplete{
BleLogHelper.writeLog("GATT REFRESHED")
}
?.subscribe ({
BleLogHelper.writeLog("GATT REFRESHED")
},{
BleLogHelper.writeLog("FAIL REFRESHING GATT")
})
BleLogHelper.writeLog("Send Request Connection Command $deviceType")
val command = BraincareBluetoothCommandProtocol.createRequestConnectionCommandFrame(deviceType)
connection.writeCharacteristic(BraincareBluetoothProtocol.rxCharacteristicUUID, command)
}
.delay(300, TimeUnit.MILLISECONDS)
.subscribe({
BleLogHelper.writeLog("Connection Established ${device.type}")
val iscon= this.isConnecting
startBlinkingDeviceLed(deviceType, pairColor)
connectionFlowListeners.forEach { it.onConnectionEstablished(device) }
}, {
BleLogHelper.writeError("Connection Lost ${device.type}", it)
BleLogHelper.writeError("Retrying...", it)
val iscon= this.isConnecting
if (isMonitoring || isConnecting || deviceType == DeviceType.DONGLE){
connectionStateListeners.forEach {
if (deviceType == DeviceType.SENSOR) {
sensorNotificationSubscription?.dispose()
sensorRssiSubscription?.dispose()
blinkingDeviceLedsSubscription?.dispose()
disconnectFromDevice(DeviceType.SENSOR)
} else {
dongleRssiSubscription?.dispose()
disconnectFromDevice(DeviceType.DONGLE)
}
isConnecting = false
it.onConnectionLost(device)
}
}else{
reconnectToDevice(device, pairColor)
}
})
if (deviceType == DeviceType.SENSOR) {
sensorConnectionSubscription = connectionSubscription
} else {
dongleConnectionSubscription = connectionSubscription
}
}
The exception is firing just after connection.writeCharacteristic(BraincareBluetoothProtocol.rxCharacteristicUUID, command)
Log error:
2019-05-21 10:54:11.816 11797-11889/io.b4c.brain4care.debug E/BLEBC: 21/05/2019 10:54:11.810 - Connection Lost SENSOR
com.polidea.rxandroidble2.exceptions.BleDisconnectedException: Disconnected from D4:57:4F:53:44:E7 with status 19 (UNKNOWN)
at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback$2.onConnectionStateChange(RxBleGattCallback.java:77)
at android.bluetooth.BluetoothGatt$1$4.run(BluetoothGatt.java:268)
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:789)
at android.bluetooth.BluetoothGatt.-wrap0(Unknown Source:0)
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:264)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
at android.os.Binder.execTransact(Binder.java:682)
status=19 is GATT_CONN_TERMINATE_PEER_USER. This library since version 1.8.1 lists the reason properly. It is always advised to use the newest available version of basically any library as they bring improvements over time.
There is no clear question in the post. With the provided information it is not possible to tell more than the above — the peripheral you use disconnects from the app. Why this happens is up to your peripheral and you can possibly find an answer in the documentation.
Bear in mind that newer Android versions do not allow concurrent pairing procedures to more than one BLE device at a time. Pairing of two devices should be performed sequentially i.e.
Peripheral A starts pairing
Peripheral A finishes pairing
Peripheral B starts pairing
Peripheral B finishes pairing
Peripherals may be connected at the same time but only a single pairing procedure may be held at a time.
Related
I'm working on a cross-platform (iOS/Android) Xamarin app, in which I need to scan IBeacon devices to assert the distance between them and the phone device.
On iOS, I use the native iOS iBeacon API, which works flawlessly and as expected.
On Android, since Android does not natively support iBeacon, I use a mix of my own code and a library "UniversalBeacon". This approach works, but when it comes to scanning (or "ranging") for Beacons over a period of time, in order to constantly assess the distance of the phone device, the experince proves very unreliable.
I am experiencing that incoming BLE packets come in as expected, but only in intervals.
Roughly summarized: Packets will come in, in a steady stream, for a seemingly random amount of time, before the packets eventually stop coming in entirely. Then, after another seemingly random amount time, packets will start coming in again. This process repeats indefinitely.
So my question is: What is causing this issue? Is it an Android quirk that I somehow have to work around?
Initiating the scan:
_ScanCallback.OnAdvertisementPacketReceived += ScanCallback_OnAdvertisementPacketReceived;
var settings = new ScanSettings.Builder()
.SetScanMode(ScanMode.LowLatency)
.Build();
_Adapter.BluetoothLeScanner.StartScan(null, settings, _ScanCallback);
Callback implementation:
internal class BLEScanCallback : ScanCallback
{
public event EventHandler<BLEAdvertisementPacketArgs> OnAdvertisementPacketReceived;
public override void OnScanFailed([GeneratedEnum] ScanFailure errorCode)
{
base.OnScanFailed(errorCode);
}
public override void OnScanResult([GeneratedEnum] ScanCallbackType callbackType, ScanResult result)
{
base.OnScanResult(callbackType, result);
switch (result.Device.Type)
{
case BluetoothDeviceType.Le:
case BluetoothDeviceType.Unknown:
try
{
var p = new BLEAdvertisementPacket
{
BluetoothAddress = result.Device.Address.ToNumericAddress(),
RawSignalStrengthInDBm = (short)result.Rssi,
Timestamp = DateTimeOffset.FromUnixTimeMilliseconds(result.TimestampNanos / 1000000),
AdvertisementType = (BLEAdvertisementType)result.ScanRecord.AdvertiseFlags,
Advertisement = new BLEAdvertisement
{
LocalName = result.ScanRecord.DeviceName
}
};
if (result.ScanRecord.ServiceUuids != null)
{
foreach (var svc in result.ScanRecord.ServiceUuids)
{
var guid = new Guid(svc.Uuid.ToString());
var data = result.ScanRecord.GetServiceData(svc);
p.Advertisement.ServiceUuids.Add(guid);
}
}
var recordData = result.ScanRecord.GetBytes();
var rec = RecordParser.Parse(recordData);
foreach (var curRec in rec)
{
if (curRec is BLEManufacturerData md)
{
p.Advertisement.ManufacturerData.Add(md);
}
if (curRec is BLEAdvertisementDataSection sd)
{
p.Advertisement.DataSections.Add(sd);
}
}
OnAdvertisementPacketReceived?.Invoke(this, new BLEAdvertisementPacketArgs(p));
}
catch (Exception ex)
{
Debugger.Break();
}
break;
default:
break;
}
}
}
I've read in various articles that this could be caused by Android automatically suspending the scan in order to save power. Whether this is the case is not obvious to me, as there does not seem to be much support on the subject.
I've already tried the following:
Changing ScanMode to LowPower/Balanced - no change
Scanning with a filter set to the specific Beacon I was testing with - filter worked, but no change in regards to the issue
Implementing logic that restarts the scan in set intervals to work around potentional limits for scan duration imposed by Android - did not affect the issue
Using other, more broadly used libraries, such as Shiny.Beacons - same experience
The issue is not caused by the Beacon device itself not advertising correctly - I've made sure of this by scanning it on another device, at the same time as my app. It is the app itself that stops scanning and/or receiving its advertisement packets.
Thanks for your time :)
I encountered a strange problem when trying to unpair and pair again with my smartphone. Currently I write a C# application on UWP (Windows 10) for BLE connection with a remote device. I use my smartphone with nRF Connect App as the peripheral.
After being paired for a while (and being inactive), when unpairing and pairing again, the application gets stuck when trying to pair again.
I broke everything down to the most basal application I could create. This is a Console App that scans the existing devices and then unpairs and pairs with the selected device:
using BLE_Test;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
Dictionary<ulong, string> uuidDict = BleModule.Scan().Result;
Console.WriteLine("Devices found:");
int i = 0;
foreach (var uuid in uuidDict.Keys)
Console.WriteLine(string.Format("ID: {0}, UUID: {1}, Local Name: {2}", i++, uuid, uuidDict[uuid]));
Console.WriteLine("Select ID!");
int id = int.Parse(Console.ReadLine());
ulong selectedUuid = (uuidDict.ElementAt(id)).Key;
await BleModule.Unpair(selectedUuid);
await BleModule.Pair(selectedUuid);
Console.ReadLine();
}
}
}
It calls an UWP DLL called "BLE_Test" with the class "BleModule":
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Enumeration;
namespace BLE_Test
{
public class BleModule
{
public static async Task<Dictionary<ulong, string>> Scan()
{
var uuidDict = new Dictionary<ulong, string>();
BluetoothLEAdvertisementWatcher watcher = new BluetoothLEAdvertisementWatcher();
watcher.Received += (BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
=> AddAdvertisement(eventArgs, uuidDict);
watcher.Start();
await Task.Delay(10000);
watcher.Stop();
return uuidDict;
}
private static void AddAdvertisement(BluetoothLEAdvertisementReceivedEventArgs eventArgs, Dictionary<ulong, string> uuidDict)
{
if (uuidDict.ContainsKey(eventArgs.BluetoothAddress) == false)
uuidDict.Add(eventArgs.BluetoothAddress, eventArgs.Advertisement.LocalName);
}
public static async Task Pair(ulong uuid)
{
Console.WriteLine("Pairing...");
var bluetoothLEDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(uuid);
if (bluetoothLEDevice == null)
{
Console.WriteLine("UUID not found!");
return;
}
DeviceInformationCustomPairing customPairing = bluetoothLEDevice.DeviceInformation.Pairing.Custom;
customPairing.PairingRequested += (DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args) => args.Accept(); // We auto-accept numeric comparison result for the sake of simplicity
DevicePairingResult result = await customPairing.PairAsync(DevicePairingKinds.ConfirmPinMatch);
Console.WriteLine("Pairing Result: " + result.Status.ToString());
}
public static async Task Unpair(ulong uuid)
{
Console.WriteLine("Unpairing...");
var bluetoothLEDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(uuid);
if (bluetoothLEDevice == null)
{
Console.WriteLine("UUID not found!");
return;
}
DeviceUnpairingResult result = await bluetoothLEDevice.DeviceInformation.Pairing.UnpairAsync();
Console.WriteLine("Unpairing Result: " + result.Status.ToString());
}
}
}
If I start the program for the first time, the pairing works fine (the unpairing will be ignored as the devices are not paired yet). If I start it immediately again afterwards, it also works fine. Unpairing and pairing will both take place. But if I wait a while (typically 5-10 minutes) while not doing anything, when I start the program again, it will unpair, but then it will wait indefinitely for PairAsync() to return. No coupling request will show on the nRF Connect App, and no PairingRequested event will appear.
Aborting the stuck program and restarting it won't help. In this case, even though the smartphone is found by the BluetoothLEAdvertisementWatcher, BluetoothLEDevice.FromBluetoothAddressAsync(uuid) will return null and the device can't be paired anymore. This can only be resolved by restarting the computer or switching off and on the advertisement in the nRF Connect App, as in this case a new random BLE Address is created for the device.
I have taken a snapshot of the BLE events using Btetlparse and Wireshark. It seems that there is a problem with a malformed package:
However, I don't really understand what is going wrong. Is this a problem of the nRF Connect App? Or the UWP commands? Or did I do something wrong? I tried two different smartphones (a Samsung Galaxy and an Oppo), so I doubt that it is a problem of the smartphone. I also added a DeviceWatcher, but this didn't change anything. Can anyone help me here?
I'm android developer and facing some autoConnect issue.
I'm using Arduino nano IOT and developed some simple application to communicate.
but I don't know why autoConnect doesn't work in this case.
Arduino nano continuously sends gyroscope sensing data(but only when app connected to module)
below is sample code.
#include <ArduinoBLE.h>
#include <Arduino_LSM6DS3.h>
BLEService sensorService("66df5109-edde-4f8a-a5e1-02e02a69cbd5");
BLEStringCharacteristic xSensorLevel("741c12b9-e13c-4992-8a5e-fce46dec0bff", BLERead | BLENotify,15);
BLEStringCharacteristic ySensorLevel("baad41b2-f12e-4322-9ba6-22cd9ce09832", BLERead | BLENotify,15);
BLEStringCharacteristic zSensorLevel("5748a25d-1834-4c68-a49b-81bf3aeb2e50", BLERead | BLENotify,15);
// last sensor data
float oldXLevel = 0;
float oldYLevel = 0;
float oldZLevel = 0;
long previousMillis = 0;
void setup() {
Serial.begin(9600);
while (!Serial);
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
pinMode(LED_BUILTIN, OUTPUT);
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
BLE.setLocalName("Demo Gyroscope");
BLE.setAdvertisedService(sensorService);
sensorService.addCharacteristic(xSensorLevel);
sensorService.addCharacteristic(ySensorLevel);
sensorService.addCharacteristic(zSensorLevel);
BLE.addService(sensorService);
BLE.advertise();
Serial.println("Bluetooth device active, waiting for connections...");
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
Serial.print("Connected to central: ");
Serial.println(central.address());
digitalWrite(LED_BUILTIN, HIGH);
while (central.connected()) {
//long currentMillis = millis();
updateGyroscopeLevel();
delay(300);
}
digitalWrite(LED_BUILTIN, LOW);
Serial.print("Disconnected from central: ");
Serial.println(central.address());
}
}
void updateGyroscopeLevel() {
float x, y, z;
if (IMU.gyroscopeAvailable()) {
IMU.readGyroscope(x, y, z);
if (x != oldXLevel) {
xSensorLevel.writeValue(String(x));
oldXLevel = x;
}
if (y != oldYLevel) {
ySensorLevel.writeValue(String(y));
oldYLevel = y;
}
if (z != oldZLevel) {
zSensorLevel.writeValue(String(z));
oldZLevel = z;
}
Serial.print(x);
Serial.print('\t');
Serial.print(y);
Serial.print('\t');
Serial.println(z);
}
}
and in my android app, I have set autoConnect true
private fun connectDevice(device: BluetoothDevice?) {
// update the status
broadcastUpdate(Actions.STATUS_MSG, "Connecting to ${device?.address}")
bleGatt = device?.connectGatt(context, true, gattClientCallback)
}
App can connect to module and read/write some data using UUID but when I turn off the module and turn on again, App cannot automatically connect it.
As far as I know, once I set it true, android store bt info as cache and trying to reconnect repeatedly.
(fyi, I'm not using Service to maintain connection)
but in my case, when I turn the module on again, it just shows below message in serial monitor
Bluetooth device active, waiting for connections...
It seems like app doesn't retry to connect.
I have read related questions and answers over here but couldn't find clue for my case.
My question over here is am I doing wrong? or this is normal behaviour?
when it comes to bt earphone, it's automatically connected when turn on. so I'm thinking something like that.
Please share any idea for this.
Really appreciate it!
After reading more articles, I found what's the issue.
Not sure if anyone reaching out to this question but in my case, I want app to reconnect to ble automatically even after turning off and on the ble.
But I found turning off/on the phone, turning off/on ble clear the cache in android internally. so Cannot automatically reconnect in this scenario.
Please have a look. this is really helpful for me
https://medium.com/#martijn.van.welie/making-android-ble-work-part-2-47a3cdaade07
This is not an answer but I found autoConnect is working when I use nRF Connect application. that means BT module itself has no issue and my application has issue.
I am using RSocket channel and trying to implement resume functionality, in case the connection to the server is lost. After trying different combinations of resume(), resumeSessionDuration(), resumeStreamTimeout() and resumeCleanupOnKeepAlive() /documentation: https://javadoc.io/static/io.rsocket/rsocket-core/1.0.0-RC3/io/rsocket/RSocketFactory.ServerRSocketFactory.html#resume--/ on the server and the mobile device, still nothing happens. On the mobile device I send data to the server each 10 seconds and when the connection is lost, the socket is disposed at the 3rd retry. Does anyone know what all these methods exactly do server- and client-side? There is almost no documentation at all...
This is the code on the server:
RSocketFactory.receive()
.resume()
.acceptor((setupPayload, reactiveSocket) -> Mono.just(responseHandler))
.transport(TcpServerTransport.create(buildSecuredTcpServer(address, port,
sslContext)))
.start()
.block();
The is the code on the mobile device:
socket = RSocketFactory.connect()
.errorConsumer(throwable -> {
if (throwable instanceof RejectedResumeException){
Log.d("tagg", "error: " + throwable.getMessage());
}
})
.resume()
.resumeStreamTimeout(Duration.ofSeconds(120))
.resumeSessionDuration(Duration.ofSeconds(600))
.keepAlive(Duration.ofSeconds(120), Duration.ofSeconds(120), 120)
.resumeStrategy(() -> new PeriodicResumeStrategy(Duration.ofSeconds(1)))
.metadataMimeType(BuildConfig.RSOCKET_METADATA_MIME_TYPE)
.dataMimeType(BuildConfig.RSOCKET_DATA_MIME_TYPE)
.transport(TcpClientTransport.create(tcpClient))
.start()
.block();
Objects.requireNonNull(socket)
.requestChannel(Flux.from(this::onSubscribe))
.doOnNext(this::receiveData)
.retryBackoff(Integer.MAX_VALUE, Duration.ofSeconds(1))
.subscribe();
I am trying to build a BLE Gatt Server with multiple custom services and multiple characteristics.
To begin with I used the Google Example: https://github.com/androidthings/sample-bluetooth-le-gattserver/tree/master/kotlin
This was straight forward and worked very well. I modified the UUIDs to fit mine and I could receive notifications and write to the chars with no problem.
This is where I define the services and chars:
fun createTimeService(): BluetoothGattService {
val service = BluetoothGattService(TIME_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
// Current Time characteristic
val currentTime = BluetoothGattCharacteristic(CURRENT_TIME,
//Read-only characteristic, supports notifications
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ)
val configDescriptor = BluetoothGattDescriptor(CLIENT_CONFIG,
//Read/write descriptor
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE)
currentTime.addDescriptor(configDescriptor)
// Local Time Information characteristic
val localTime = BluetoothGattCharacteristic(LOCAL_TIME_INFO,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(currentTime)
service.addCharacteristic(localTime)
return service
}
fun createSerialService(): BluetoothGattService {
val service = BluetoothGattService(serialPortServiceID,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
val serialData = BluetoothGattCharacteristic(serialDataCharacteristicID,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(serialData)
return service
}
And here I am applying them to my server:
private fun startServer() {
bluetoothGattServer = bluetoothManager.openGattServer(this, gattServerCallback)
bluetoothGattServer?.addService(TimeProfile.createTimeService())
?: Log.w(TAG, "Unable to create GATT server")
bluetoothGattServer?.addService(TimeProfile.createSerialService())
?: Log.w(TAG, "Unable to create GATT server")
// Initialize the local UI
updateLocalUi(System.currentTimeMillis())
}
I would expect that everything would be working like before after adding the second service. But now if I try to write/subscribe to any of the characteristics (doesn't matter in which service) I just receive this:
W/BluetoothGattServer: onCharacteristicWriteRequest() no char for handle 42
W/BluetoothGattServer: onDescriptorWriteRequest() no desc for handle 43
I found what was going wrong. Apparently you cannot just add all services at once like I did. Adding the second service before the first one was confirmed lead to an Exception setting the services to null.
In the end I solved this by adding only one service initially.
Then in the onServiceAdded() Callback of the BleGattServerCallback() I started one after another.