Skip to content

Instantly share code, notes, and snippets.

@bus710
Last active January 3, 2023 19:38
Show Gist options
  • Save bus710/0bf083093dafa6cd8c3bf6f955a1a012 to your computer and use it in GitHub Desktop.
Save bus710/0bf083093dafa6cd8c3bf6f955a1a012 to your computer and use it in GitHub Desktop.
How to use flutter_blue in general
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);
}
}
@bus710
Copy link
Author

bus710 commented Nov 3, 2019

This is a simple and dirty example to use the flutter_blue plugin.
There is a upper layer as a provider's model. This and that code utilize some enums to deal with the work.

@chaochaox1990
Copy link

That would be great if u can show BLEActivityStatus file for us to see.

@bus710
Copy link
Author

bus710 commented Dec 9, 2019

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).

@chaochaox1990
Copy link

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