Last active
January 3, 2023 19:38
-
-
Save bus710/0bf083093dafa6cd8c3bf6f955a1a012 to your computer and use it in GitHub Desktop.
How to use flutter_blue in general
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
/* If there is an error that points about the minSdkVersion, | |
change the number (minSdkVersion 16) to 19 in $PROJECT_ROOT/android/app/build.gradle */ | |
// example: https://github.com/pauldemarco/flutter_blue/tree/master/example/lib | |
import 'package:flutter_blue/flutter_blue.dart'; // for BLE | |
import 'package:flutter/material.dart'; | |
import 'utils/enums.dart'; | |
class BLEData { | |
// "00010000-0002-1000-8000-00805f9b34fb" | |
List<BluetoothDevice> devicesScanned; | |
List<BluetoothService> servicesFound; | |
BluetoothDevice targetDevice; | |
BluetoothCharacteristic targetChar; | |
BLEData() { | |
devicesScanned = List<BluetoothDevice>(); | |
servicesFound = List<BluetoothService>(); | |
targetDevice = null; | |
targetChar = null; | |
} | |
} | |
class BLECore { | |
// Main instance and flag | |
FlutterBlue _flutterBlue; | |
bool _flutterBlueState; | |
BLEActivityStatus _bleActivityState; | |
bool _debugMode; | |
// Scanned/discoverred items | |
BLEData _bleData; | |
// handlers | |
StreamSubscription<BluetoothState> _stateSubscription; | |
Function _doneHandler; | |
Function _dataHandler; | |
BLECore( | |
bool debugMode, Function givenDoneHandler, Function givenDataHandler) { | |
_bleData = BLEData(); | |
_flutterBlue = FlutterBlue.instance; | |
_flutterBlueState = false; | |
_bleActivityState = BLEActivityStatus.init; | |
_debugMode = debugMode; | |
_doneHandler = givenDoneHandler; | |
_dataHandler = givenDataHandler; | |
_stateSubscription = | |
_flutterBlue.state.listen((data) => this.stateHandler(data)); | |
} | |
getFlutterBlueState() => _flutterBlueState; | |
cancelStateSubscription() => _stateSubscription.cancel(); | |
void stateHandler(BluetoothState state) { | |
switch (state) { | |
case BluetoothState.on: | |
if (_bleActivityState == BLEActivityStatus.notAvailable) { | |
_bleActivityState = BLEActivityStatus.init; | |
} | |
_flutterBlueState = true; | |
break; | |
case BluetoothState.off: | |
case BluetoothState.turningOff: | |
case BluetoothState.turningOn: | |
case BluetoothState.unauthorized: | |
case BluetoothState.unavailable: | |
case BluetoothState.unknown: | |
default: | |
_bleActivityState = BLEActivityStatus.notAvailable; | |
_flutterBlueState = false; | |
break; | |
} | |
} | |
bool checkBleActivityState() { | |
if (_debugMode) { | |
debugPrint("[BLE Core] >> " + _bleActivityState.toString()); | |
} | |
if (_bleActivityState != BLEActivityStatus.init || | |
_bleActivityState == BLEActivityStatus.notAvailable) { | |
return false; | |
} | |
return true; | |
} | |
void scan() async { | |
if (!checkBleActivityState()) { | |
// Don't allow multiple BLE activities at a moment | |
return; | |
} | |
// Set and reset the variables | |
_bleActivityState = BLEActivityStatus.scanning; | |
_bleData.devicesScanned.clear(); | |
// Scan! | |
var scanResult; | |
try { | |
scanResult = await _flutterBlue.startScan(timeout: Duration(seconds: 4)); | |
} catch (e) { | |
debugPrint(e.toString()); | |
_bleActivityState = BLEActivityStatus.init; | |
return; | |
} | |
for (var r in scanResult) { | |
var device = r.device; | |
var deviceName = r.device.name.toString(); | |
// Filter the anonymous devices out | |
if (deviceName.length > 0) { | |
_bleData.devicesScanned.add(device); | |
if (_debugMode) { | |
debugPrint("[BLE Core] >> " + deviceName); | |
} | |
} | |
} | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.scan); | |
} | |
List<BluetoothDevice> getBleDevicesScanned() { | |
if (!checkBleActivityState()) { | |
// Don't allow multiple BLE activities at a moment | |
return []; | |
} | |
if (_bleData.devicesScanned.length == 0) { | |
return []; | |
} | |
if (_bleActivityState == BLEActivityStatus.scanning) { | |
return []; | |
} | |
// Leave this here for debugging later. | |
// if (_debugMode && _bleData.devicesScanned.length > 0) { | |
// _bleData.devicesScanned.forEach( | |
// (device) => { | |
// debugPrint( | |
// "[BLE Core] >> " + | |
// device.id.toString() + | |
// " / " + | |
// device.name.toString() + | |
// " / " + | |
// device.type.toString(), | |
// ), | |
// }, | |
// ); | |
// } | |
return _bleData.devicesScanned; | |
} | |
void connect(String device) async { | |
if (!checkBleActivityState()) { | |
// Don't allow multiple BLE activities at a moment | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.connecting; | |
// Find and disconnect if there is any connected BLE device | |
List<BluetoothDevice> connectedDevices = | |
await _flutterBlue.connectedDevices; | |
for (var c in connectedDevices) { | |
await c.disconnect(); | |
} | |
// Find and connect if there is a matched device in the list | |
for (var d in _bleData.devicesScanned) { | |
if (device.contains(d.id.toString())) { | |
_bleData.targetDevice = d; | |
break; | |
} | |
} | |
try { | |
await _bleData.targetDevice.connect(timeout: Duration(seconds: 10)); | |
await _bleData.targetDevice.requestMtu(192); | |
} catch (e) { | |
debugPrint(e.toString()); | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.failed); | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.connect); | |
} | |
void discoverServices() async { | |
if (!checkBleActivityState()) { | |
// Don't allow multiple BLE activities at a moment | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.discoverring; | |
// Discover services if there is a connected device | |
_bleData.servicesFound.clear(); | |
if (_bleData.targetDevice != null) { | |
try { | |
_bleData.servicesFound = await _bleData.targetDevice | |
.discoverServices() | |
.timeout(const Duration(seconds: 1)); | |
} catch (e) { | |
debugPrint(e.toString()); | |
} | |
} | |
_bleActivityState = BLEActivityStatus.init; | |
// Service discovery fails a lot. | |
// To get back to the state of CONNECTED, | |
// the length of the found service list should be checked. | |
// Also, don't report it is failed. | |
if (_bleData.servicesFound.length == 0) { | |
_doneHandler(Request.connect); | |
} else { | |
_doneHandler(Request.discoverServices); | |
} | |
} | |
void readTargetCharacteristic(String targetUuid) async { | |
if (!checkBleActivityState()) { | |
// Don't allow multiple BLE activities at a moment | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.reading; | |
_bleData.targetChar = null; | |
for (int i = 0; i < _bleData.servicesFound.length; i++) { | |
for (BluetoothCharacteristic c | |
in _bleData.servicesFound[i].characteristics) { | |
try { | |
await Future.delayed(Duration(milliseconds: 10)); | |
if (c.properties.read && c.uuid.toString().contains(targetUuid)) { | |
_bleData.targetChar = c; | |
} | |
} catch (e) { | |
debugPrint("Error!: " + e.toString()); | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.failed); | |
return; | |
} | |
} | |
} | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.readCharacteristics); | |
} | |
void readTargetValue(String targetUuid) async { | |
if (!checkBleActivityState()) { | |
// Don't allow multiple BLE activities at a moment | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.reading; | |
try { | |
String tmp = ""; | |
var val = | |
await _bleData.targetChar.read().timeout(const Duration(seconds: 1)); | |
// Int to ascii string | |
val.forEach((v) => {tmp += String.fromCharCode(v)}); | |
debugPrint(tmp.toString()); | |
_dataHandler(Request.readValues, tmp.toString()); | |
} catch (e) { | |
debugPrint(e.toString()); | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.failed); | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.readValues); | |
} | |
void writeTargetValue(List<int> data) async { | |
debugPrint(data.toString()); | |
// Check the type and length of the incoming param | |
if (data == null) { | |
return; | |
} else if (data.length != 192) { | |
return; | |
} | |
if (!checkBleActivityState()) { | |
// Don't allow multiple BLE activities at a moment | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.writing; | |
int mtu = await _bleData.targetDevice.mtu.first; | |
if (mtu == 192) { | |
// Never put NULL value to the write API | |
for (int i = 0; i < data.length; i++) { | |
if (data[i] == null) { | |
data[i] = 0; | |
} | |
} | |
try { | |
await _bleData.targetChar | |
.write(data) | |
.timeout(const Duration(seconds: 1)); | |
} catch (e) { | |
debugPrint(e.toString()); | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.failed); | |
return; | |
} | |
} | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.writeValues); | |
} | |
void setNotifier() { | |
if (_bleData.targetChar != null) { | |
try { | |
_bleData.targetChar.setNotifyValue(true); | |
_bleData.targetChar.value.listen( | |
(data) => { | |
debugPrint(""), | |
}, | |
); | |
} catch (e) { | |
debugPrint(e.toString()); | |
} | |
} | |
} | |
void disconnect() async { | |
if (_bleActivityState == BLEActivityStatus.notAvailable) { | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.disconnecting; | |
try { | |
_bleData.targetDevice.disconnect(); | |
List<BluetoothDevice> connectedDevices = | |
await _flutterBlue.connectedDevices; | |
for (var c in connectedDevices) { | |
c.disconnect(); | |
} | |
} catch (e) { | |
debugPrint(e.toString()); | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.failed); | |
return; | |
} | |
_bleActivityState = BLEActivityStatus.init; | |
_doneHandler(Request.disconnect); | |
} | |
} |
That would be great if u can show BLEActivityStatus file for us to see.
// This can be used for the BLECore
enum BLEActivityStatus {
notAvailable,
init,
scanning,
connecting,
disconnecting,
discovering,
reading,
writing,
}
It is just an enum that can be used as a flag to prevent calling a same API (i.e. calling the scan API while a previous scan is being used).
That would be great if u can show BLEActivityStatus file for us to see.
// This can be used for the BLECore enum BLEActivityStatus { notAvailable, init, scanning, connecting, disconnecting, discovering, reading, writing, }
It is just an enum that can be used as a flag to prevent calling a same API (i.e. calling the scan API while a previous scan is being used).
thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
That would be great if u can show BLEActivityStatus file for us to see.