Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 36 additions & 32 deletions lib/src/managers/open_ring_sensor_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import 'dart:typed_data';
import 'package:open_earable_flutter/src/models/devices/open_ring.dart';

import '../../open_earable_flutter.dart';
import 'sensor_handler.dart';
import '../utils/sensor_value_parser/sensor_value_parser.dart';
import 'sensor_handler.dart';

class OpenRingSensorHandler extends SensorHandler<OpenRingSensorConfig> {
final DiscoveredDevice _discoveredDevice;
Expand All @@ -17,9 +17,9 @@ class OpenRingSensorHandler extends SensorHandler<OpenRingSensorConfig> {
required DiscoveredDevice discoveredDevice,
required BleGattManager bleManager,
required SensorValueParser sensorValueParser,
}) : _discoveredDevice = discoveredDevice,
_bleManager = bleManager,
_sensorValueParser = sensorValueParser;
}) : _discoveredDevice = discoveredDevice,
_bleManager = bleManager,
_sensorValueParser = sensorValueParser;

@override
Stream<Map<String, dynamic>> subscribeToSensorData(int sensorId) {
Expand All @@ -31,20 +31,25 @@ class OpenRingSensorHandler extends SensorHandler<OpenRingSensorConfig> {
StreamController();
_bleManager
.subscribe(
deviceId: _discoveredDevice.id,
serviceId: OpenRingGatt.service,
characteristicId: OpenRingGatt.rxChar,
).listen(
(data) async {
List<Map<String, dynamic>> parsedData = await _parseData(data);
for (var d in parsedData) {
streamController.add(d);
}
},
onError: (error) {
logger.e("Error while subscribing to sensor data: $error");
},
);
deviceId: _discoveredDevice.id,
serviceId: OpenRingGatt.service,
characteristicId: OpenRingGatt.rxChar,
)
.listen(
(data) async {
try {
List<Map<String, dynamic>> parsedData = await _parseData(data);
for (var d in parsedData) {
streamController.add(d);
}
} catch (error) {
logger.e("Error while parsing OpenRing sensor packet: $error");
}
},
onError: (error) {
logger.e("Error while subscribing to sensor data: $error");
},
);

return streamController.stream;
}
Expand All @@ -65,31 +70,30 @@ class OpenRingSensorHandler extends SensorHandler<OpenRingSensorConfig> {
);
}

/// Parses raw sensor data bytes into a [Map] of sensor values.
/// Parses raw sensor data bytes into a [Map] of sensor values.
Future<List<Map<String, dynamic>>> _parseData(List<int> data) async {
ByteData byteData = ByteData.sublistView(Uint8List.fromList(data));

return _sensorValueParser.parse(byteData, []);

final parsed = _sensorValueParser.parse(byteData, []);
if (parsed.isNotEmpty) {
logger.t(
"OpenRingSensorHandler parsed ${parsed.length} sample(s), first=${parsed.first}, last=${parsed.last}",
);
}

return parsed;
}
}

class OpenRingSensorConfig extends SensorConfig {
int cmd;
int subOpcode;
List<int> payload;

OpenRingSensorConfig({
required this.cmd,
required this.subOpcode,
});
OpenRingSensorConfig({required this.cmd, required this.payload});

Uint8List toBytes() {
int randomByte = DateTime.now().microsecondsSinceEpoch & 0xFF;

return Uint8List.fromList([
0x00,
randomByte,
cmd,
subOpcode,
]);
return Uint8List.fromList([0x00, randomByte, cmd, ...payload]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import 'package:open_earable_flutter/src/managers/open_ring_sensor_handler.dart'

import '../sensor_configuration.dart';

class OpenRingSensorConfiguration extends SensorConfiguration<OpenRingSensorConfigurationValue> {

class OpenRingSensorConfiguration
extends SensorConfiguration<OpenRingSensorConfigurationValue> {
final OpenRingSensorHandler _sensorHandler;

OpenRingSensorConfiguration({required super.name, required super.values, required OpenRingSensorHandler sensorHandler})
: _sensorHandler = sensorHandler;
OpenRingSensorConfiguration({
required super.name,
required super.values,
required OpenRingSensorHandler sensorHandler,
}) : _sensorHandler = sensorHandler;

@override
void setConfiguration(OpenRingSensorConfigurationValue value) {
OpenRingSensorConfig config = OpenRingSensorConfig(
cmd: value.cmd,
subOpcode: value.subOpcode,
payload: value.payload,
);

_sensorHandler.writeSensorConfig(config);
Expand All @@ -22,12 +25,12 @@ class OpenRingSensorConfiguration extends SensorConfiguration<OpenRingSensorConf

class OpenRingSensorConfigurationValue extends SensorConfigurationValue {
final int cmd;
final int subOpcode;
final List<int> payload;

OpenRingSensorConfigurationValue({
required super.key,
required this.cmd,
required this.subOpcode,
required this.payload,
});

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import '../../../../../open_earable_flutter.dart' show logger;
import '../../../../managers/sensor_handler.dart';
import '../../sensor.dart';

Expand Down Expand Up @@ -33,27 +34,52 @@ class OpenRingSensor extends Sensor<SensorIntValue> {
@override
Stream<SensorIntValue> get sensorStream {
StreamController<SensorIntValue> streamController = StreamController();
sensorHandler.subscribeToSensorData(sensorId).listen(
(data) {
int timestamp = data["timestamp"];
sensorHandler.subscribeToSensorData(sensorId).listen((data) {
if (!data.containsKey(sensorName)) {
return;
}

List<int> values = [];
for (var entry in (data[sensorName] as Map).entries) {
final sensorData = data[sensorName];
final timestamp = data["timestamp"];
if (sensorData is! Map || timestamp is! int) {
return;
}

final Map sensorDataMap = sensorData;
List<int> values = [];
for (final axisName in _axisNames) {
final dynamic axisValue = sensorDataMap[axisName];
if (axisValue is int) {
values.add(axisValue);
}
}

if (values.isEmpty) {
for (var entry in sensorDataMap.entries) {
if (entry.key == 'units') {
continue;
}

values.add(entry.value);
if (entry.value is int) {
values.add(entry.value as int);
}
}
}

if (values.isEmpty) {
return;
}

logger.t(
"OpenRingSensor[$sensorName] emit timestamp=$timestamp values=$values raw=$sensorDataMap",
);

SensorIntValue sensorValue = SensorIntValue(
values: values,
timestamp: timestamp,
);
SensorIntValue sensorValue = SensorIntValue(
values: values,
timestamp: timestamp,
);

streamController.add(sensorValue);
},
);
streamController.add(sensorValue);
});
return streamController.stream;
}
}
53 changes: 43 additions & 10 deletions lib/src/models/devices/open_ring_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ import 'wearable.dart';

class OpenRingFactory extends WearableFactory {
@override
Future<Wearable> createFromDevice(DiscoveredDevice device, {Set<ConnectionOption> options = const {}}) {
Future<Wearable> createFromDevice(
DiscoveredDevice device, {
Set<ConnectionOption> options = const {},
}) {
if (bleManager == null) {
throw Exception("Can't create τ-Ring instance: bleManager not set in factory");
}
if (disconnectNotifier == null) {
throw Exception("Can't create τ-Ring instance: disconnectNotifier not set in factory");
throw Exception(
"Can't create τ-Ring instance: disconnectNotifier not set in factory",
);
}

final sensorHandler = OpenRingSensorHandler(
discoveredDevice: device,
bleManager: bleManager!,
Expand All @@ -31,16 +36,44 @@ class OpenRingFactory extends WearableFactory {
OpenRingSensorConfiguration(
name: "6-Axis IMU",
values: [
OpenRingSensorConfigurationValue(key: "On", cmd: 0x40, subOpcode: 0x06),
OpenRingSensorConfigurationValue(key: "Off", cmd: 0x40, subOpcode: 0x00),
OpenRingSensorConfigurationValue(
key: "On",
cmd: 0x40,
payload: [0x06],
),
OpenRingSensorConfigurationValue(
key: "Off",
cmd: 0x40,
payload: [0x00],
),
],
sensorHandler: sensorHandler,
),
OpenRingSensorConfiguration(
name: "PPG",
values: [
OpenRingSensorConfigurationValue(key: "On", cmd: OpenRingGatt.cmdPPGQ2, subOpcode: 0x01),
OpenRingSensorConfigurationValue(key: "Off", cmd: OpenRingGatt.cmdPPGQ2, subOpcode: 0x00),
OpenRingSensorConfigurationValue(
key: "On",
cmd: OpenRingGatt.cmdPPGQ2,
payload: [
0x01, // start
0x00, // collectionTime (continuous)
0x19, // acquisition parameter (firmware-fixed)
0x01, // enable waveform streaming
0x01, // enable progress packets
],
),
OpenRingSensorConfigurationValue(
key: "Off",
cmd: OpenRingGatt.cmdPPGQ2,
payload: [
0x00, // stop
0x00, // collectionTime
0x19, // acquisition parameter
0x00, // disable waveform streaming
0x00, // disable progress packets
],
),
],
sensorHandler: sensorHandler,
),
Expand Down Expand Up @@ -69,8 +102,8 @@ class OpenRingFactory extends WearableFactory {
sensorName: "PPG",
chartTitle: "PPG",
shortChartTitle: "PPG",
axisNames: ["Green", "Red", "Infrared"],
axisUnits: ["raw", "raw", "raw"],
axisNames: ["Red", "Infrared", "AccX", "AccY", "AccZ"],
axisUnits: ["raw", "raw", "raw", "raw", "raw"],
sensorHandler: sensorHandler,
),
];
Expand All @@ -86,7 +119,7 @@ class OpenRingFactory extends WearableFactory {
);
return Future.value(w);
}

@override
Future<bool> matches(DiscoveredDevice device, List<BleService> services) async {
return services.any((s) => s.uuid.toLowerCase() == OpenRingGatt.service);
Expand Down
Loading
Loading