189 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			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);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |