diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle
index 4e62f489..6795bb69 100644
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -38,7 +38,7 @@ android {
defaultConfig {
applicationId "com.PolyCortex.Polydodo"
- minSdkVersion 19
+ minSdkVersion 24
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml
index 98c8e964..22862eb9 100644
--- a/mobile/android/app/src/main/AndroidManifest.xml
+++ b/mobile/android/app/src/main/AndroidManifest.xml
@@ -44,8 +44,5 @@
android:name="flutterEmbedding"
android:value="2" />
-
-
-
diff --git a/mobile/ios/Flutter/AppFrameworkInfo.plist b/mobile/ios/Flutter/AppFrameworkInfo.plist
index f2872cf4..beffb28c 100644
--- a/mobile/ios/Flutter/AppFrameworkInfo.plist
+++ b/mobile/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 9.0
+ 10.0
diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj
index bbd0e334..a81b9d8b 100644
--- a/mobile/ios/Runner.xcodeproj/project.pbxproj
+++ b/mobile/ios/Runner.xcodeproj/project.pbxproj
@@ -272,7 +272,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -354,7 +354,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -403,7 +403,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/mobile/lib/src/application/device/device_selector_cubit.dart b/mobile/lib/src/application/device/device_selector_cubit.dart
index 02a657c2..90412b90 100644
--- a/mobile/lib/src/application/device/device_selector_cubit.dart
+++ b/mobile/lib/src/application/device/device_selector_cubit.dart
@@ -30,13 +30,16 @@ class DeviceSelectorCubit extends Cubit {
Future connect(AcquisitionDevice device) async {
emit(DeviceConnectionInProgress());
- try {
- await _deviceRepository.connect(device);
- _acquisitionDeviceStream.cancel();
- emit(DeviceConnectionSuccess());
- } catch (e) {
+ _deviceRepository.connect(device, connectionCallback);
+ }
+
+ void connectionCallback(bool connected, Exception e) {
+ if (e != null) {
emit(DeviceConnectionFailure(e));
resetSearch();
+ } else if (connected) {
+ _acquisitionDeviceStream.cancel();
+ emit(DeviceConnectionSuccess());
}
}
diff --git a/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart b/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart
index 04067b8e..4c6148a7 100644
--- a/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart
+++ b/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart
@@ -3,7 +3,7 @@ import 'acquisition_device.dart';
abstract class IAcquisitionDeviceRepository {
void initializeRepository();
- Future connect(AcquisitionDevice device);
+ void connect(AcquisitionDevice device, Function(bool, Exception) callback);
void disconnect();
Future>> startDataStream();
diff --git a/mobile/lib/src/domain/eeg_data/eeg_data.dart b/mobile/lib/src/domain/eeg_data/eeg_data.dart
index 6ba58bbf..b90928c7 100644
--- a/mobile/lib/src/domain/eeg_data/eeg_data.dart
+++ b/mobile/lib/src/domain/eeg_data/eeg_data.dart
@@ -2,8 +2,7 @@
import 'package:polydodo/src/domain/entity.dart';
class EEGData extends Entity {
- List _values;
- int sampleCounter = 0;
+ final List _values;
EEGData(id, this._values)
: assert(_values != null),
diff --git a/mobile/lib/src/infrastructure/bluetooth_repository.dart b/mobile/lib/src/infrastructure/bluetooth_repository.dart
index 371a2670..57e3f812 100644
--- a/mobile/lib/src/infrastructure/bluetooth_repository.dart
+++ b/mobile/lib/src/infrastructure/bluetooth_repository.dart
@@ -1,55 +1,41 @@
import 'dart:async';
-import 'package:flutter/services.dart';
-import 'package:flutter_blue/flutter_blue.dart';
+import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:polydodo/src/domain/acquisition_device/acquisition_device.dart';
import 'package:polydodo/src/domain/acquisition_device/i_acquisition_device_repository.dart';
import 'package:polydodo/src/domain/unique_id.dart';
class BluetoothRepository implements IAcquisitionDeviceRepository {
- static const String BLE_SERVICE = "fe84";
- static const String BLE_RECEIVE = "2d30c082";
- static const String BLE_SEND = "2d30c083";
+ static const String BLE_SERVICE = "0000fe84-0000-1000-8000-00805f9b34fb";
+ static const String BLE_RECEIVE = "2d30c082-f39f-4ce6-923f-3484ea480596";
+ static const String BLE_SEND = "2d30c083-f39f-4ce6-923f-3484ea480596";
static const startStreamChar = 'b';
static const stopStreamChar = 's';
- BluetoothDevice _selectedDevice;
- BluetoothCharacteristic _sendCharacteristic;
- BluetoothCharacteristic _receiveCharacteristic;
+ AcquisitionDevice _selectedDevice;
+ QualifiedCharacteristic _sendCharacteristic;
+ QualifiedCharacteristic _receiveCharacteristic;
- FlutterBlue flutterBlue;
- StreamSubscription> _bluetoothScanSubscription;
+ FlutterReactiveBle flutterReactiveBle;
+ StreamSubscription _connectedDeviceStream;
+ StreamSubscription _bluetoothScanSubscription;
List _acquisitionDevicePersistency = [];
- List _bluetoothDevices = [];
final streamController = StreamController>();
BluetoothRepository();
void initializeRepository() {
if (_bluetoothScanSubscription == null) {
- flutterBlue = FlutterBlue.instance;
-
- flutterBlue.connectedDevices
- .asStream()
- .asBroadcastStream()
- .listen((List devices) {
- for (BluetoothDevice device in devices) {
- addDevice(device);
- }
- });
- _bluetoothScanSubscription =
- flutterBlue.scanResults.listen((List results) {
- for (ScanResult result in results) {
- addDevice(result.device);
- }
- });
+ flutterReactiveBle = FlutterReactiveBle();
+
+ _bluetoothScanSubscription = flutterReactiveBle.scanForDevices(
+ withServices: []).listen((device) => addDevice(device));
} else {
_bluetoothScanSubscription.resume();
}
- flutterBlue.startScan();
}
- void addDevice(BluetoothDevice bluetoothDevice) {
+ void addDevice(DiscoveredDevice bluetoothDevice) {
AcquisitionDevice device = AcquisitionDevice(
UniqueId.from(bluetoothDevice.id.toString()), bluetoothDevice.name);
@@ -57,73 +43,70 @@ class BluetoothRepository implements IAcquisitionDeviceRepository {
if (idx == -1) {
_acquisitionDevicePersistency.add(device);
- _bluetoothDevices.add(bluetoothDevice);
} else {
_acquisitionDevicePersistency[idx] = device;
- _bluetoothDevices[idx] = bluetoothDevice;
}
streamController.add(_acquisitionDevicePersistency);
}
- Future connect(AcquisitionDevice device) async {
- _selectedDevice =
- _bluetoothDevices[_acquisitionDevicePersistency.indexOf(device)];
-
+ void connect(
+ AcquisitionDevice device, Function(bool, Exception) callback) async {
+ _selectedDevice = device;
_acquisitionDevicePersistency.clear();
- _bluetoothDevices.clear();
_bluetoothScanSubscription.pause();
- flutterBlue.stopScan();
-
- try {
- await _selectedDevice.connect().timeout(Duration(seconds: 6),
- onTimeout: () =>
- {disconnect(), throw Exception("Connection Timed out")});
-
- await findRelevantCharacteristics();
- } catch (e) {
- if (e is PlatformException) {
- if (e.code != "already_connected") throw Exception(e.details);
- } else
- throw e;
- }
+
+ _connectedDeviceStream = flutterReactiveBle
+ .connectToDevice(
+ id: _selectedDevice.id.toString(),
+ connectionTimeout: Duration(seconds: 10))
+ .listen((event) {
+ if (event.connectionState == DeviceConnectionState.connected) {
+ setupCharacteristics();
+ callback(true, null);
+ } else if (event.connectionState == DeviceConnectionState.disconnected) {
+ disconnect();
+ callback(false, Exception("Failed to connect to device"));
+ }
+ });
}
- Future disconnect() async {
+ void disconnect() async {
if (_selectedDevice != null) {
- await _selectedDevice.disconnect();
_selectedDevice = null;
+ _connectedDeviceStream.cancel();
}
}
- Future findRelevantCharacteristics() async {
- var characteristics = (await _selectedDevice.discoverServices())
- .firstWhere((service) => service.uuid.toString().contains(BLE_SERVICE))
- .characteristics;
- for (BluetoothCharacteristic characteristic in characteristics) {
- if (characteristic.uuid.toString().contains(BLE_RECEIVE)) {
- _receiveCharacteristic = characteristic;
- } else if (characteristic.uuid.toString().contains(BLE_SEND)) {
- _sendCharacteristic = characteristic;
- }
- }
+ void setupCharacteristics() async {
+ _sendCharacteristic = QualifiedCharacteristic(
+ characteristicId: Uuid.parse(BLE_SEND),
+ serviceId: Uuid.parse(BLE_SERVICE),
+ deviceId: _selectedDevice.id.toString());
- if (_receiveCharacteristic == null)
- throw Exception('Device is missing receive Characteristic');
- if (_sendCharacteristic == null)
- throw Exception('Device is missing send Characteristic');
+ _receiveCharacteristic = QualifiedCharacteristic(
+ characteristicId: Uuid.parse(BLE_RECEIVE),
+ serviceId: Uuid.parse(BLE_SERVICE),
+ deviceId: _selectedDevice.id.toString());
}
Future>> startDataStream() async {
- await _receiveCharacteristic.setNotifyValue(true);
+ await flutterReactiveBle.requestConnectionPriority(
+ deviceId: _selectedDevice.id.toString(),
+ priority: ConnectionPriority.highPerformance);
+
+ flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic,
+ value: startStreamChar.codeUnits);
- await _sendCharacteristic.write(startStreamChar.codeUnits);
- return _receiveCharacteristic.value;
+ return flutterReactiveBle.subscribeToCharacteristic(_receiveCharacteristic);
}
Future stopDataStream() async {
- await _receiveCharacteristic.setNotifyValue(false);
- await _sendCharacteristic.write(stopStreamChar.codeUnits);
+ await flutterReactiveBle.requestConnectionPriority(
+ deviceId: _selectedDevice.id.toString(),
+ priority: ConnectionPriority.balanced);
+ flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic,
+ value: stopStreamChar.codeUnits);
}
@override
diff --git a/mobile/lib/src/infrastructure/eeg_data_repository.dart b/mobile/lib/src/infrastructure/eeg_data_repository.dart
index 7e29ca01..c24e2a26 100644
--- a/mobile/lib/src/infrastructure/eeg_data_repository.dart
+++ b/mobile/lib/src/infrastructure/eeg_data_repository.dart
@@ -8,20 +8,17 @@ import 'package:polydodo/src/domain/unique_id.dart';
import 'constants.dart';
class EEGDataRepository implements IEEGDataRepository {
- bool initializeStream = false;
- EEGData recordingData;
- List lastSampleData = [0, 0, 0, 0, 0];
- int packetLoss;
- int totalPackets;
- int nextPacketId;
+ bool _streamInitialized = false;
+ EEGData _recordingData;
+ List _lastSampleData = [0, 0, 0, 0, 0];
+ int _sampleCounter;
void createRecordingFromStream(Stream> stream) {
- recordingData =
- new EEGData(UniqueId.from(DateTime.now().toString()), new List());
- packetLoss = 0;
- totalPackets = 0;
- if (!initializeStream) {
- initializeStream = true;
+ _recordingData =
+ EEGData(UniqueId.from(DateTime.now().toString()), List());
+ _sampleCounter = 0;
+ if (!_streamInitialized) {
+ _streamInitialized = true;
stream.listen((value) {
addData(value);
});
@@ -30,13 +27,14 @@ class EEGDataRepository implements IEEGDataRepository {
Future stopRecordingFromStream() async {
// todo: move save future to another file
+
final directory = await getExternalStorageDirectory();
final pathOfTheFileToWrite =
- directory.path + '/' + recordingData.fileName + ".txt";
+ directory.path + '/' + _recordingData.fileName + ".txt";
File file = File(pathOfTheFileToWrite);
- List fileContent = new List();
+ var fileContent = [];
fileContent.addAll(OPEN_BCI_HEADER);
- fileContent.addAll(recordingData.values);
+ fileContent.addAll(_recordingData.values);
String csv = const ListToCsvConverter().convert(fileContent);
await file.writeAsString(csv);
}
@@ -46,42 +44,31 @@ class EEGDataRepository implements IEEGDataRepository {
void exportData() {}
Future addData(List event) async {
- //print("Lost packets: " + packetLoss.toString());
- //print("Total packets: " + totalPackets.toString());
- //print("Lost percentage: " +
- // (packetLoss.toDouble() / totalPackets.toDouble()).toString());
if (event.length != 20) {
print("Invalid Event");
return;
}
- totalPackets++;
int packetID = event[0];
// todo: handle packet id 0 (raw data) and possibly impedence for signal validation
if (packetID == 0) {
- nextPacketId = 101;
-
List data = parseRaw(event);
data = convertToMicrovolts(data, false);
- recordingData.values.add(data.sublist(0, 15));
+ _recordingData.values.add(data.sublist(0, 15));
} else if (packetID >= 101 && packetID <= 200) {
- // print(packetID);
- // print(nextPacketId);
- packetLoss += packetID - nextPacketId;
- nextPacketId = packetID + 1;
List data = parse19Bit(event);
data = convertToMicrovolts(data, true);
- recordingData.values.add(data.sublist(0, 15));
- recordingData.values.add(data.sublist(15, 30));
+ _recordingData.values.add(data.sublist(0, 15));
+ _recordingData.values.add(data.sublist(15, 30));
}
}
List parseRaw(event) {
List data = getListForCSV();
- data[0] = recordingData.sampleCounter++;
+ data[0] = _sampleCounter++;
data[1] = (event[1] << 16) | (event[2] << 8) | event[3];
data[2] = (event[4] << 16) | (event[5] << 8) | event[6];
data[3] = (event[7] << 16) | (event[8] << 8) | event[9];
@@ -99,7 +86,7 @@ class EEGDataRepository implements IEEGDataRepository {
List data = getListForCSV();
- data[0] = recordingData.sampleCounter;
+ data[0] = _sampleCounter;
data[1] = (event[1] << 11) | (event[2] << 3) | (event[3] >> 5);
data[2] = ((event[3] & 31) << 14) | (event[4] << 6) | (event[5] >> 2);
data[3] = ((event[5] & 3) << 17) |
@@ -107,7 +94,7 @@ class EEGDataRepository implements IEEGDataRepository {
(event[7] << 1) |
(event[8] >> 7);
data[4] = ((event[8] & 127) << 12) | (event[9] << 4) | (event[10] >> 4);
- data[15] = recordingData.sampleCounter++;
+ data[15] = _sampleCounter++;
data[16] = ((event[10] & 15) << 15) | (event[11] << 7) | (event[12] >> 1);
data[17] = ((event[12] & 1) << 18) |
(event[13] << 10) |
@@ -120,7 +107,7 @@ class EEGDataRepository implements IEEGDataRepository {
}
List getListForCSV() {
- List data = new List(30);
+ List data = List(30);
for (int i = 5; i < 15; ++i) {
data[i] = 0;
@@ -147,9 +134,9 @@ class EEGDataRepository implements IEEGDataRepository {
data[i + offset].toDouble() * (1200000 / (8388607.0 * 1.5 * 51.0));
// Convert delta
- if (isDelta) data[i + offset] = lastSampleData[i] - data[i + offset];
+ if (isDelta) data[i + offset] = _lastSampleData[i] - data[i + offset];
- lastSampleData[i] = data[i + offset];
+ _lastSampleData[i] = data[i + offset];
}
}
diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock
index 920435e6..5c26b06c 100644
--- a/mobile/pubspec.lock
+++ b/mobile/pubspec.lock
@@ -160,18 +160,25 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.6"
- flutter_blue:
+ flutter_reactive_ble:
dependency: "direct main"
description:
- name: flutter_blue
+ name: flutter_reactive_ble
url: "https://pub.dartlang.org"
source: hosted
- version: "0.7.2"
+ version: "2.5.2"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
+ functional_data:
+ dependency: transitive
+ description:
+ name: functional_data
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.0"
get_it:
dependency: "direct main"
description:
@@ -263,6 +270,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+1"
+ pedantic:
+ dependency: transitive
+ description:
+ name: pedantic
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.9.0"
+ plain_optional:
+ dependency: transitive
+ description:
+ name: plain_optional
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.3"
platform:
dependency: transitive
description:
@@ -298,13 +319,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.2+2"
- rxdart:
- dependency: transitive
- description:
- name: rxdart
- url: "https://pub.dartlang.org"
- source: hosted
- version: "0.24.1"
share:
dependency: "direct main"
description:
diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml
index d082d4d9..10bc9657 100644
--- a/mobile/pubspec.yaml
+++ b/mobile/pubspec.yaml
@@ -28,20 +28,20 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
archive: ^2.0.13
battery: ^1.0.5
+ binary: ^2.0.0
bloc: ^6.0.3
+ csv: ^4.0.3
cupertino_icons: ^0.1.3
equatable: ^1.2.5
flutter_bloc: ^6.0.5
- flutter_blue: ^0.7.2
+ flutter_reactive_ble : ^2.5.2
get_it: ^4.0.4
hive: ^1.4.4
meta: ^1.1.8
+ path_provider: ^1.6.16
share: ^0.6.5
uuid: ^2.2.2
usb_serial: ^0.2.4
- binary: ^2.0.0
- csv: ^4.0.3
- path_provider: ^1.6.16
dev_dependencies:
flutter_test: