Subscribing to two notifiable characteristics simultaneously
See original GitHub issueGreat package, was able to get some ideas going super quickly. Though I ran into a problem; Is it possible to subscribe to two notifiable characteristics at the same time?
I am working on a POC where we want to measure multiple data points simultaneously. Two of the three devices use a notifiable characteristic. I notice that subscribing to a notification stream and reading from a readable characteristic is possible. But when I try to listen to both streams things just kinda freeze.
I am going to spend more time on researching if this is me, not being able to write react. Though, if there are any limitations with regard to simultaneous subscriptions on multiple devices, it would be nice to find out.
This is my code
import React, {useEffect, useState} from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
PermissionsAndroid,
} from 'react-native';
import _ from 'lodash';
import {Colors} from 'react-native/Libraries/NewAppScreen';
import {BleManager} from 'react-native-ble-plx';
import {
DefaultTheme,
Button,
List,
Provider as PaperProvider,
Chip,
} from 'react-native-paper';
import Grid, {Row, Col} from './Grid/Grid';
const getCharacteristicContainingUuids = (
serviceUuid,
characteristicUuid,
characteristics,
) => {
// console.log(
// serviceUuid,
// characteristics[serviceUuid],
// _.find(characteristics[serviceUuid], {uuid: characteristicUuid}),
// characteristics,
// );
return _.find(characteristics[serviceUuid], {uuid: characteristicUuid});
};
const CharacteristicData = ({
name,
serviceUuid,
characteristicUuid,
bleState,
}) => {
const [value, setValue] = useState(null);
const [subscription, setSubscription] = useState(null);
const characteristic = getCharacteristicContainingUuids(
serviceUuid,
characteristicUuid,
bleState.characteristics,
);
// console.log(characteristic);
useEffect(() => {
return () => {
if (subscription) {
subscription.remove();
setSubscription(null);
}
};
}, [subscription]);
const onPress = (characteristic) => async () => {
if (characteristic) {
if (!characteristic.isNotifiable) {
if (characteristic.isReadable) {
const result = await characteristic.read();
if (result) {
setValue(result.value);
}
}
} else {
if (!characteristic.isNotifying) {
const sub = characteristic.monitor((error, result) => {
// console.log('hoi', name, error, result);
if (result) {
setValue(result.value);
}
});
setSubscription(sub);
} else {
if (subscription) {
subscription.remove();
}
}
}
}
};
return (
<List.Item
description={name}
title={value}
style={characteristic && characteristic.isNotifying && styles.highlight}
onPress={onPress(characteristic)}
/>
);
};
const theme = {
...DefaultTheme,
roundness: 2,
colors: {
...DefaultTheme.colors,
primary: '#3498db',
accent: '#f1c40f',
},
};
// console.log(BleManager);
const bleManager = new BleManager();
const requestBLEPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Cool BLE Permission',
message: 'Cool BLE App needs access to your bluetooth adapter ',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('You can use BLE');
return true;
} else {
console.log('BLE permission denied');
return false;
}
} catch (err) {
// console.warn(err);
return false;
}
};
const App = () => {
const [adapterState, setAdapterState] = useState(false);
const [bleDevices, setBleDevices] = useState([]);
const [isScanning, setIsScanning] = useState(false);
const [connectedDevices, setConnectedDevices] = useState({
ecFlex: null,
Temperature: null,
GDX: null,
});
useEffect(() => {
const subscription = bleManager.onStateChange((state) => {
if (state === 'PoweredOn') {
setAdapterState(true);
} else {
setAdapterState(false);
}
}, true);
return () => subscription.remove();
}, []);
const toggleScanDevices = () => {
setIsScanning(true);
setBleDevices([]);
bleManager.startDeviceScan(null, {}, (bleError, device) => {
// note devices may be scanned multiple times
if (device && _.findIndex(bleDevices, {id: device.id}) < 0) {
bleDevices.push(device);
setBleDevices(bleDevices);
}
});
setTimeout(() => {
setIsScanning(false);
bleManager.stopDeviceScan();
}, 5000);
};
const permission = requestBLEPermission();
const findDeviceWhereNameContains = (name) => {
let device = null;
_.each(bleDevices, (item) => {
if (String(item.name).includes(name)) {
device = item;
}
});
return device;
};
const getIconForSensor = (name) => {
if (bleDevices.length === 0) {
return 'access-point';
}
if (findDeviceWhereNameContains(name)) {
return 'access-point-network';
} else {
return 'access-point-network-off';
}
};
const connectDevice = async (name) => {
let device = findDeviceWhereNameContains(name);
if (device == null) {
setConnectedDevices({...connectedDevices, [name]: null});
return false;
}
let isConnected = await device.isConnected();
if (!isConnected) {
device = await bleManager.connectToDevice(device.id);
isConnected = await device.isConnected();
}
// console.log(isConnected, device);
device = await device.discoverAllServicesAndCharacteristics();
device.onDisconnected((error, device) => {
setConnectedDevices({...connectedDevices, [name]: null});
});
const services = await device.services();
const characteristics = {};
const descriptors = {};
await _.forEach(services, async (service) => {
characteristics[service.uuid] = await device.characteristicsForService(
service.uuid,
);
// descriptors[service.uuid] = device.
});
setConnectedDevices({
...connectedDevices,
[name]: {
connected: isConnected,
services,
device,
characteristics,
// descriptors,
},
});
};
const toggleConnectDevice = (name) => async () => {
if (!connectedDevices[name]) {
await connectDevice(name);
} else {
const device = await connectedDevices[name].device.cancelConnection();
if (!(await device.isConnected())) {
setConnectedDevices({...connectedDevices, [name]: null});
}
}
};
console.log(bleDevices.length + ' devices found');
console.log(connectedDevices);
return (
<PaperProvider theme={theme}>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
{global.HermesInternal == null ? null : (
<View style={styles.engine}>
<Text style={styles.footer}>Engine: Hermes</Text>
</View>
)}
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Step One Permissions</Text>
<Text style={styles.sectionDescription}>
{permission
? 'we have permission to use BLE'
: "we don't have permission to use BLE"}
</Text>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Step two Adapter</Text>
<Text style={styles.sectionDescription}>
The bluetooth adapter is {adapterState ? 'on' : 'off'}
</Text>
<Button
icon={!adapterState ? 'bluetooth-connect' : 'bluetooth-off'}
mode="contained"
onPress={() => {
// todo android only
if (adapterState) {
bleManager.disable();
} else {
bleManager.enable();
}
}}>
Toggle Bluetooth adapter
</Button>
</View>
{adapterState && (
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Step three scan devices</Text>
{!isScanning && (
<Button
icon={adapterState ? 'bluetooth-connect' : 'bluetooth-off'}
mode="contained"
onPress={toggleScanDevices}>
Scan for devices
</Button>
)}
{bleDevices.length > 0 && (
<Button mode="contained" onPress={() => setBleDevices([])}>
Clear
</Button>
)}
{bleDevices.length > 0 && (
<List.Section>
<List.Accordion title="Devices found">
{_.map(bleDevices, (device, index) => (
<List.Item
key={index}
title={
(device.name || 'device') + ` (${device.rssi})`
}
description={device.localName}
/>
))}
</List.Accordion>
</List.Section>
)}
</View>
)}
{adapterState && (
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>
Step four connect to WSS devices
</Text>
<Grid>
<Col>
<Chip
icon={getIconForSensor('GDX')}
selected={!!connectedDevices.GDX}
onPress={toggleConnectDevice('GDX')}>
Vernier
</Chip>
</Col>
<Col>
<Chip
icon={getIconForSensor('Temperature')}
selected={!!connectedDevices.Temperature}
onPress={toggleConnectDevice('Temperature')}>
Temperature
</Chip>
</Col>
<Col>
<Chip
icon={getIconForSensor('ecFlex')}
selected={!!connectedDevices.ecFlex}
onPress={toggleConnectDevice('ecFlex')}>
EcFlex
</Chip>
</Col>
</Grid>
</View>
)}
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>
Step five read data from WSS devices
</Text>
{!!connectedDevices.Temperature && (
<List.Section>
<List.Subheader>Temperature board data</List.Subheader>
<CharacteristicData
name="temperature"
serviceUuid="b067f00d-744d-8db5-9b42-aae2d7041e3c"
characteristicUuid="b067beef-744d-8db5-9b42-aae2d7041e3c"
bleState={connectedDevices.Temperature}
/>
</List.Section>
)}
{!!connectedDevices.ecFlex && (
<List.Section>
<List.Subheader>ecFlex data</List.Subheader>
<CharacteristicData
name="ecflex-raw"
serviceUuid="00002d8d-0000-1000-8000-00805f9b34fb"
characteristicUuid="00002da7-0000-1000-8000-00805f9b34fb"
bleState={connectedDevices.ecFlex}
/>
</List.Section>
)}
{!!connectedDevices.GDX && (
<List.Section>
<List.Subheader>Vernier data</List.Subheader>
<CharacteristicData
name="vernier-raw"
serviceUuid="8e6f0f58-5819-11e6-8b77-86f30ca893d3"
characteristicUuid="8e6f094a-5819-11e6-8b77-86f30ca893d3"
bleState={connectedDevices.GDX}
/>
</List.Section>
)}
</View>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>
Step six collect multiple data streams in one set
</Text>
<Text style={styles.sectionTitle}>TODO</Text>
</View>
</ScrollView>
</SafeAreaView>
</PaperProvider>
);
};
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
engine: {
position: 'absolute',
right: 0,
},
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
color: Colors.dark,
},
highlight: {
fontWeight: '700',
},
footer: {
color: Colors.dark,
fontSize: 12,
fontWeight: '600',
padding: 4,
paddingRight: 12,
textAlign: 'right',
},
});
export default App;
I hope anyone can help me! Thank you
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:11
Top GitHub Comments
Thanks, man! I’ll check this out!
I did solve it but not with this library instead I went with https://github.com/innoveit/react-native-ble-manager