Sense_the_Rhythm/lib/utils/esense_input.dart

189 lines
6.5 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:esense_flutter/esense.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sense_the_rhythm/models/arrow_direction.dart';
import 'package:sense_the_rhythm/models/input_direction.dart';
class ESenseInput {
// create singleton that is available on all widgets so it does not have to be
// carried down in the widget tree
static final instance = ESenseInput._();
ESenseManager eSenseManager = ESenseManager('unknown');
// valuenotifier allows widgets to rerender when the value changes
ValueNotifier<String> deviceStatus = ValueNotifier('Disconnected');
StreamSubscription? _subscription;
String eSenseDeviceName = '';
bool connected = false;
final int _sampleRate = 20;
final InputDirection _inputDirection = InputDirection();
int _x = 0;
int _y = 0;
int _z = 0;
ESenseInput._() {
_listenToESense();
}
/// ask and check if permissions are enabled and granted
Future<bool> _askForPermissions() async {
// is desktop
if (!Platform.isAndroid && !Platform.isIOS) return false;
// is bluetooth even enabled?
if (!await Permission.bluetooth.serviceStatus.isEnabled) {
deviceStatus.value = "Bluetooth is disabled!";
return false;
}
if (!(await Permission.bluetoothScan.request().isGranted &&
await Permission.bluetoothConnect.request().isGranted &&
await Permission.bluetooth.request().isGranted)) {
print(
'WARNING - no permission to use Bluetooth granted. Cannot access eSense device.');
deviceStatus.value = "Insufficiant Permissions";
return false;
}
// for some strange reason, Android requires permission to location for Bluetooth to work.....?
if (Platform.isAndroid) {
if (!(await Permission.locationWhenInUse.request().isGranted)) {
print(
'WARNING - no permission to access location granted. Cannot access eSense device.');
deviceStatus.value = "Insufficiant Permissions";
return false;
}
}
return true;
}
/// listen to connectionEvents and set deviceStatus
void _listenToESense() {
// if you want to get the connection events when connecting,
// set up the listener BEFORE connecting...
eSenseManager.connectionEvents.listen((event) {
print('CONNECTION event: $event');
// when we're connected to the eSense device, we can start listening to events from it
// if (event.type == ConnectionType.connected) _listenToESenseEvents();
connected = false;
switch (event.type) {
case ConnectionType.connected:
deviceStatus.value = 'Connected';
connected = true;
_startListenToSensorEvents();
break;
case ConnectionType.unknown:
deviceStatus.value = 'Unknown';
break;
case ConnectionType.disconnected:
deviceStatus.value = 'Disconnected';
_pauseListenToSensorEvents();
break;
case ConnectionType.device_found:
deviceStatus.value = 'Device_found';
break;
case ConnectionType.device_not_found:
deviceStatus.value = 'Device_not_found';
break;
}
});
}
/// get eSenseEvent stream only containung button events
Stream<ButtonEventChanged> buttonEvents() {
return eSenseManager.eSenseEvents
.where((event) => event.runtimeType == ButtonEventChanged)
.cast();
}
/// sets sampling rate and listens to sensorEvents
void _startListenToSensorEvents() async {
// // any changes to the sampling frequency must be done BEFORE listening to sensor events
print('setting sampling frequency...');
bool successs = await eSenseManager.setSamplingRate(_sampleRate);
if (successs) {
print('setSamplingRate success');
} else {
print('setSamplingRate fail');
}
// subscribe to sensor event from the eSense device
_subscription = eSenseManager.sensorEvents.listen((event) {
// print('SENSOR event: $event');
if (event.gyro != null) {
_parseGyroData(event.gyro!);
}
});
}
/// cancels the sensorEvents listening
void _pauseListenToSensorEvents() async {
_subscription?.cancel();
}
/// add up all new gyro [data] in the form of deg/s multiplied by scaling factor
/// to get real angles
void _parseGyroData(List<int> data) {
// Float value in deg/s = Gyro value / Gyro scale factor
// The default configuration is +- 500deg/s for the gyroscope.
_x = (_x + (15 * data[0] ~/ (500 * _sampleRate))) % 360;
_y = (_y + (15 * data[1] ~/ (500 * _sampleRate))) % 360;
_z = (_z + (15 * data[2] ~/ (500 * _sampleRate))) % 360;
print('$_x, $_y, $_z');
// print('${(z.toDouble() / 500.0 * (1.0 / sampleRate.toDouble())) * 7.5}');
// print('${z.toDouble() / 500.0 * (1.0 / 10.0)}');
}
/// nulls all angles and reset inputDirection
void resetAngles() {
_inputDirection.reset();
_x = 0;
_y = 0;
_z = 0;
}
/// get InputDirection by checking if angels are in defined ranges and
/// calibrating based on the [expect]ed direction from ArrowDirection
InputDirection getInputDirection(ArrowDirection expect) {
// check if angle is in range
_inputDirection.up = _z > 180 && _z < 340;
_inputDirection.down = _z > 20 && _z < 180;
_inputDirection.left = _y > 0 && _y < 180;
_inputDirection.right = _y > 180 && _y < 360;
// calibrate based on expected directoin from ArrowDirection
if (expect == ArrowDirection.up && _inputDirection.up ||
expect == ArrowDirection.down && _inputDirection.down) {
_y = 0;
}
if (expect == ArrowDirection.left && _inputDirection.left ||
expect == ArrowDirection.right && _inputDirection.right) {
_z = 0;
}
return _inputDirection;
}
/// connect to ESense with [deviceName] by first asking for permissions
Future<void> connectToESense(String deviceName) async {
if (!connected) {
bool permissionSuccessfull = await _askForPermissions();
if (!permissionSuccessfull) return;
print('Trying to connect to eSense device namend \'$deviceName\'');
eSenseDeviceName = deviceName;
eSenseManager.deviceName = deviceName;
bool connecting = await eSenseManager.connect();
print(
'Trying to connect to eSense device namend \'${eSenseManager.deviceName}\'');
deviceStatus.value = connecting ? 'connecting...' : 'connection failed';
print(deviceStatus.value);
}
}
}