Using BLE shutter remote to control React Native app - android

Is it possible to detect when a button has been pressed on a BLE HID device using react-native-ble-plx?
I want to use these BLE remotes to have a cheap and robust way of controlling a React Native app with external devices (avoiding making a new project using ESP32 or it's variants would be preferred). When connected through the Android OS it works as simple volume up and volume down buttons - which triggers camera shutter, as intended. However, when connected to a BLE app (tested with LightBlue and with a React native app) that functionality is gone.
I have a few of these remotes and need to detect when a button was pressed and which remote was pressed on. Because they are BLE and not Bluetooth classic more than one can be connected at the same time.
Detection of volume up and volume down actions works by using react-native-keyevent but only when the remotes are connected to the OS and there is no way to get which remote triggered the action.
By using this piece of code to try to monitor all characteristics only errors are returned:
await bleManager.connect(discoveredDevice);
let discoveredServices = (await (await discoveredDevice.discoverAllServicesAndCharacteristics()).services());
for (let iService = 0; iService < discoveredServices.length; iService++) {
let characteristics = await discoveredServices[iService].characteristics();
for (let iCharacteristic = 0; iCharacteristic < characteristics.length; iCharacteristic++) {
characteristics[iCharacteristic].monitor((error: BleError | null, characteristic: Characteristic | null): void => {
if (error) {
console.error(error.message);
return;
} else {
console.log(characteristic?.value);
}
});
}
}
The following error occurs for all monitor() calls - with different UUIDs:
ERROR Characteristic 0000ae42-0000-1000-8000-00805f9b34fb notify change failed for device ? and service 0000ae40-0000-1000-8000-00805f9b34fb
A screenshot containing the services and characteristics of the device captured within LightBlue can be found here
This question might be duplicate of this question

Related

How to use track.applyConstraints() to turn OFF torch from getUserMedia javascript on Android Studio Mega?

I have a Javascript web app using .getUserMedia(), .applyConstraints(), and related browser APIs to capture some video from mobile device web browsers and send it someplace with a websocket. The capture and send stuff is working well.
I have implemented a little user interface to let my users turn on and off the torch (the LED lamp to provide light for the camera.)
On most Android devices, this UI works fine to turn on, and then off, the torch. But not on the the Android Studio Mega (Build/O11019 running Android 8.1.0 and mobile Chrome 81.0.4044.138)
Note: Studio Mega is an Android model name, and not related to Android Studio.
On that device I can turn the torch on just fine, but it ignores my request to turn the torch off. I use code like this:
const torchstate = false /* works when torchstate = true */
const track = stream.getVideoTracks()[0]
if( track && typeof track.getCapabilities === 'function' ) {
const caps = track.getCapabilities()
if( caps.torch ) {
track.applyConstraints( { advanced: [{ torch: torchstate }] } )
.then( () => {
/* the torch should now be off, but it is not. */
} )
.catch( ( error ) => {
/* My code never hits this catch clause */
console.log( 'setTorch', torchstate ? 'on' : 'off', 'error', error )
} )
}
}
This Studio Mega device actually has two torches, one on the back camera and another on the selfie-camera. My UI turns on the one I want it to turn on (the one associated with the currently active camera). But not off.
Closing the stream turns the torch off as expected (on all Androids I've tried).
Is there some other way to turn off torches? Or is this possibly a browser, WebRTC, or device bug?
(For what it's worth iOS / mobile Safari doesn't have the torch capability. So maybe I should consider this a talking-donkey problem--in which I should be amazed it works at all, not annoyed that it works strangely.)

flutter_blue: set_notification and read_characteristic errors on Android, BLE device disconnects on first connection attempt

I've been working with flutter for a few weeks now and am attempting to build an app to communicate with a power bank over BLE. In its current state, it works correctly on iOS. On android, partly due to the BLE device's behavior, I'm having exceptions thrown during the connect/discover phases.
Details and Versioning
Flutter: 1.17.5
flutter_blue: 0.7.2
target Android device: Google Pixel 3A running Android 10
target iOS device: iPhone XS running 13.6, iPhone 6s running iOS 12
Code
void connect(String deviceId) async {
var dev = devices[deviceId];
if (connectedDevices[deviceId] != null) return;
await dev.device.connect(autoConnect: false);
dev.connection = dev.device.state.listen((state) async {
dev.deviceState = state;
notifyListeners();
if (state == BluetoothDeviceState.disconnected) {
connectedDevices[dev.id] = null;
await dev.dispose();
notifyListeners();
}
if (state == BluetoothDeviceState.connected) {
dev.services = await dev.device.discoverServices();
for (BluetoothService service in dev.services) {
// set services based on uuid
}
for (BluetoothCharacteristic characteristic
in dev.deviceInfoService.characteristics) {
// set characteristics from services
}
for (BluetoothCharacteristic characteristic
in dev.notificationService.characteristics) {
switch (characteristic.uuid.toString()) {
case notificationServiceCharacteristic:
dev.notificationServiceCharacteristic = characteristic;
if (!dev.notificationServiceCharacteristic.isNotifying) {
await dev.notificationServiceCharacteristic
.setNotifyValue(true);
dev.valueChangedSubscription = dev
.notificationServiceCharacteristic.value
.listen((value) {
_onValuesChanged(dev, value);
notifyListeners();
});
connectedDevices[dev.id] = dev;
}
break;
case writeCharacteristic:
dev.writeCharacteristic = characteristic;
break;
default:
break;
}
}
notifyListeners(); //using scopedModel for handling state
await readServiceCharacteristics(dev);
}
});
}
What should happen
The program connects to the device. Upon connecting (state == BluetoothDeviceState.connected), services and characteristics are discovered. When the notification service is found, turn notifications on with a callback to process data. After doing this, read from read-only characteristics.
What is happening
On iOS devices, there are no apparent errors. The program functions normally; albeit with the initial disconnect caused by the BLE device. Attempting to reconnect works successfully without fail.
On Android:
Exception thrown: /flutter ( 1557): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(set_notification_error, error when writing the descriptor, null)
Line responsible:
await dev.notificationServiceCharacteristic
.setNotifyValue(true);
Why is happening / BLE device bug
In my research, I seem to have figured out one of the causes of the bug. The BLE device, for whatever reason, does not complete the connection process on the first connection attempt. Typical connections work as follows:
Connect called
Device Connected
Service Discovery / Notification section of code is called
Device is disconnected
Connect called again
Discovery / Notification section runs properly and device maintains connection
Program receives notifications
Debugging Steps
Firstly, I switch from a forEach to a for(BluetoothService service in dev.services) "for-in" style as this ensures the iteration waits for callback/return.
In debugging, I can use breakpoints to assure the program doesn't throw an exception. By placing a breakpoint at the await line and waiting for a second or two, it works without issue. Running the code without a breakpoint set throws the error every time, without fail.
I've built a bare-bones PoC with just RaisedButtons to call the init, scan, connect, and disconnect functions. The error is still present
It's clear that in the StreamListener for the device's state that it goes from
Connected
Disconnected (at this point I can logically stop by disconnecting and disposing the connection or letting it attempt again by commenting this out)
In the case I don't dispose and disconnect, it switches to connected again
Thoughts
While I know that this issue is partially caused by the BLE device itself, I'm wondering if there's any way to fix this or workaround it. One idea I'm thinking of is a means to terminate the asynchronous calls and set some sort of flag so that, on the subsequent call, the program knows to cleanup and/or delay to ensure it works without issue.
There's some github issues that I've looked at that seem to be running into similar problems - solutions are generally forced delays or (in a perfect world) not having a device that disconnects initially. I'll link some for context:
PlatformException(set_notification_error, error when writing the descriptor, null) on setNotifyValue #295
Characteristics are sending duplicate notifications when device is reconnected #525 (Not exactly the same, but duplicate firing makes me think it's not too far off)
Either way, thanks for reading and I hope to be past this thorn in my side soon!
I don't want to offend you, but your code is very hard to read. First I suggest that you should use some conventions. Like Future, curly braces after if, break that enormous method into smaller ones.
I have seen this error before. Here I suggest some options to solve.
In the switch, you are starting to listen to one stream. At the end
of your function, you are trying to read a characteristic. I'm not
sure if your POC supports more than one method at the same time. I
would remove readServiceCharacteristics while you listen to the
stream or start with reading and wait until it's finished and then
start the stream listener.
It's hard to tell what is happening when
you notify the listeners perhaps you try to read or write on the BLE
device. Remove it. See what happens.
So basically try to use the characteristics one by one and try to avoid parallel requests.
Hope it help!

React Native broadcast and discover nearby devices

First off: This is more of a logical question than a code-specific question.
I am trying to create a view similar to the iOS AirDrop view where users can see other users. This means, every user needs to broadcast/advertise their custom username, which can be seen by all other nearby users that scan the area.
I have tried using react-native-ble-plx, since I read on the Apple developer forum that iPhones can act as BLE (bluetooth low energy) peripherals. (Also, I read that newer Android devices support this as well)
I've tried the following:
import ble, { BleManager, Characteristic } from 'react-native-ble-plx';
// ...
const bleManager = new BleManager();
bleManager.startDeviceScan(null, null, async (e, d) => {
if (e) console.log(`BT Error: ${JSON.stringify(e)}`);
if (d && d.id && d.isConnectable) {
console.log(`Connecting to: ${d.id} ('${d.name}')...`);
const device = await d.connect();
const services = await device.services();
console.log('services: ', services.join(', '));
const characteristic = await device.writeCharacteristicWithResponseForService(BLE_SERVICE_UUID, BLE_SERVICE_UUID, '123');
console.log(`Characteristics: ${JSON.stringify(characteristic)}`);
}
});
But I haven't found a way to broadcast a value which others can read, is that even possible with BLE?
I've also looked into beacons (specifically react-native-beacons-manager), but I'm not sure if that is what I want since an Android/iOS phone is not a 'beacon'..
So my question is: Are there technologies/libraries that allow broadcasting of a message (my custom username) which others can see? Ideally I want to communicate between them, like exchanging a token, but that's not a requirement.
I'd appreciate any help or pointings into the right direction here, thanks!
I managed to get it working after really long debugging nights.
It is possible using the Google Nearby Messages API, and setting it to use .BLE only.
I've created an open source react native module for this here: https://github.com/mrousavy/react-native-google-nearby-messages

How to enable multiple Bluetooth LE notifications of a service on Android via Kotlin?

I write my first Android app using Kotlin. This app scans and connects to my ble device. After I would like to stream some data via GATT notifications.
Currently I manage to enable the first notification, but struggle with any after. Maybe someone has an idea what went wrong.
A quick word on my own GATT service:
it is a user defined one(UUID128)
it has 5 characteristics
My characteristics:
char1 - write
char2 - read + notify
char2 CCC
char3 - read + notify
char3 CCC
char4 - read + notify
char4 CCC
char 5 write
I am using a nRF52840 dev board as device. On that dev board runs an own firmware(on Zephyr IOS). Via nRF connect I can enable notifications and read them. As well I done a Python script using bluepy that enables the notifications and successfully fetching the send data. Guess my GATT database setup is fine and as well the firmware handling the interaction. To enable the notifications from the firmware side the CCC field needs to be written.
Now I like to get these data on my Android phone. So I wrote an simple Android app. Everything goes fine, till I try to enable the notifications. I use:
for (d in gattChar.descriptors) {
val `val` =
if (enabled) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
d.value = `val`
if (false == gatt.writeDescriptor(d)) {
Log.e(LTAG,"writing descriptor ${d} failed")
}
}
Enabling the char2 goes well. But when I try to enable char3 and char4 fails after. Debugging BluetoothGatt.java file seems difficult. When steping into "writeDescriptor" it fails directly with "return false".
I maybe that the ble device is bussy(mDeviceBusyLock).
Any ideas how to solve that problem?

How to detect if a headphones / a headset is connected to an Android device in React Native

I am making a calling app where users may or may not use headphones / a headset. On an incoming video call I am using react-native-incall-manager to turn the speaker on / allow speaker phone. The basic InCallManager.start({ media: 'video' }); method works and allows detection of a new audio device being plugged in such as headphones, and if this happens, the headphone and mic plugged in work as expected.
The problem comes in the case that headphones are already plugged in to the device before a call starts, because the InCallManager.setSpeakerphoneOn(true); method is called at that time. InCallManager.start({ media: 'video' }); doesn't account for devices already connected and the headphones do not work, the normal speaker does even though headphones are plugged in.
react-native-incall-manager recommends using DeviceEventEmitter from react-native to detect native events such as changes in connected audio devices, but this module is deprecated.
Here it is recommended to use NativeEventEmitter, but this seems very complex and seems to require the native Java (Android) modules to be written and used in conjunction with it.
How can I detect changes in connected audio devices in Android in React Native?
Here it is recommended to use NativeEventEmitter, but this seems very complex and seems to require the native Java (Android) modules to be written and used in conjunction with it.
You don't need to write a native Java module for this job since NativeEventEmitter can pick up events that were sent through RCTDeviceEventEmitter.
So example from react-native-incall-manager README
import { DeviceEventEmitter } from 'react-native';
DeviceEventEmitter.addListener('Proximity', function (data) {
// --- do something with events
});
can be easily rewritten to this
import { NativeEventEmitter, NativeModules } from 'react-native';
const eventEmitter = new NativeEventEmitter(NativeModules.InCallManager);
eventListener = eventEmitter.addListener('Proximity', event => {
// do something with events
});
Just don't forget to clean up once you no longer need event listeners
eventListener.remove();
If you want to detect changes in connected audio devices you can subscribe to onAudioDeviceChanged event from react-native-incall-manager.
It sends something like this whenever there is a change in connected audio devices or device that will output sound
{"availableAudioDeviceList": "[\"BLUETOOTH\",\"SPEAKER_PHONE\",\"EARPIECE\"]", "selectedAudioDevice": "EARPIECE"}

Categories

Resources