feat: read gyroscope as InputDirection and buttonChange to pause/resume

This commit is contained in:
Orangerot 2025-01-10 20:33:31 +01:00
parent f589cf8e92
commit 3682fa551a
2 changed files with 122 additions and 4 deletions

View file

@ -4,17 +4,27 @@ import 'dart:io';
import 'package:esense_flutter/esense.dart'; import 'package:esense_flutter/esense.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:sense_the_rhythm/arrows.dart';
import 'package:sense_the_rhythm/level.dart';
class ESenseInput { class ESenseInput {
static final instance = ESenseInput._(); static final instance = ESenseInput._();
ESenseManager eSenseManager = ESenseManager('unknown'); ESenseManager eSenseManager = ESenseManager('unknown');
ValueNotifier<String> deviceStatus = ValueNotifier(''); ValueNotifier<String> deviceStatus = ValueNotifier('');
StreamSubscription? subscription;
String eSenseDeviceName = ''; String eSenseDeviceName = '';
bool connected = false; bool connected = false;
bool sampling = false; bool sampling = false;
int sampleRate = 20;
InputDirection inputDirection = InputDirection();
int x = 0;
int y = 0;
int z = 0;
ESenseInput._() { ESenseInput._() {
_listenToESense(); _listenToESense();
} }
@ -22,7 +32,8 @@ class ESenseInput {
Future<void> _askForPermissions() async { Future<void> _askForPermissions() async {
if (!Platform.isAndroid && !Platform.isIOS) return; if (!Platform.isAndroid && !Platform.isIOS) return;
if (!(await Permission.bluetoothScan.request().isGranted && if (!(await Permission.bluetoothScan.request().isGranted &&
await Permission.bluetoothConnect.request().isGranted)) { await Permission.bluetoothConnect.request().isGranted &&
await Permission.bluetooth.request().isGranted)) {
print( print(
'WARNING - no permission to use Bluetooth granted. Cannot access eSense device.'); 'WARNING - no permission to use Bluetooth granted. Cannot access eSense device.');
} }
@ -35,10 +46,10 @@ class ESenseInput {
} }
} }
StreamSubscription _listenToESense() { void _listenToESense() {
// if you want to get the connection events when connecting, // if you want to get the connection events when connecting,
// set up the listener BEFORE connecting... // set up the listener BEFORE connecting...
return eSenseManager.connectionEvents.listen((event) { eSenseManager.connectionEvents.listen((event) {
print('CONNECTION event: $event'); print('CONNECTION event: $event');
// when we're connected to the eSense device, we can start listening to events from it // when we're connected to the eSense device, we can start listening to events from it
@ -49,6 +60,7 @@ class ESenseInput {
case ConnectionType.connected: case ConnectionType.connected:
deviceStatus.value = 'connected'; deviceStatus.value = 'connected';
connected = true; connected = true;
_startListenToSensorEvents();
break; break;
case ConnectionType.unknown: case ConnectionType.unknown:
deviceStatus.value = 'unknown'; deviceStatus.value = 'unknown';
@ -56,6 +68,7 @@ class ESenseInput {
case ConnectionType.disconnected: case ConnectionType.disconnected:
deviceStatus.value = 'disconnected'; deviceStatus.value = 'disconnected';
sampling = false; sampling = false;
_pauseListenToSensorEvents();
break; break;
case ConnectionType.device_found: case ConnectionType.device_found:
deviceStatus.value = 'device_found'; deviceStatus.value = 'device_found';
@ -67,6 +80,75 @@ class ESenseInput {
}); });
} }
Stream<ButtonEventChanged> buttonEvents() {
return eSenseManager.eSenseEvents
.where((event) => event.runtimeType == ButtonEventChanged)
.cast();
}
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!);
}
});
sampling = true;
}
void _pauseListenToSensorEvents() async {
subscription?.cancel();
sampling = false;
}
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)}');
}
void resetAngles() {
inputDirection.reset();
x = 0;
y = 0;
z = 0;
}
InputDirection getInputDirection(ArrowDirection expect) {
inputDirection.up = z > 270 && z < 340;
inputDirection.down = z > 40 && z < 180;
inputDirection.left = y > 40 && y < 180;
inputDirection.right = y > 270 && y < 340;
if (expect == ArrowDirection.up && inputDirection.up ||
expect == ArrowDirection.down && inputDirection.down) {
y = 0;
print("ehit");
}
if (expect == ArrowDirection.left && inputDirection.left ||
expect == ArrowDirection.right && inputDirection.right) {
z = 0;
print("ehit");
}
return inputDirection;
}
Future<void> connectToESense(String deviceName) async { Future<void> connectToESense(String deviceName) async {
if (!connected) { if (!connected) {
await _askForPermissions(); await _askForPermissions();
@ -74,7 +156,8 @@ class ESenseInput {
eSenseDeviceName = deviceName; eSenseDeviceName = deviceName;
eSenseManager.deviceName = deviceName; eSenseManager.deviceName = deviceName;
connected = await eSenseManager.connect(); connected = await eSenseManager.connect();
print('Trying to connect to eSense device namend \'${eSenseManager.deviceName}\''); print(
'Trying to connect to eSense device namend \'${eSenseManager.deviceName}\'');
deviceStatus.value = connected ? 'connecting...' : 'connection failed'; deviceStatus.value = connected ? 'connecting...' : 'connection failed';
print(deviceStatus.value); print(deviceStatus.value);

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart'; import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:sense_the_rhythm/arrows.dart'; import 'package:sense_the_rhythm/arrows.dart';
import 'package:sense_the_rhythm/esense_input.dart';
import 'package:sense_the_rhythm/game_over_stats.dart'; import 'package:sense_the_rhythm/game_over_stats.dart';
import 'package:sense_the_rhythm/simfile.dart'; import 'package:sense_the_rhythm/simfile.dart';
@ -20,6 +21,13 @@ class InputDirection {
bool down = false; bool down = false;
bool left = false; bool left = false;
bool right = false; bool right = false;
void reset() {
up = false;
down = false;
left = false;
right = false;
}
} }
class _LevelState extends State<Level> with SingleTickerProviderStateMixin { class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
@ -30,6 +38,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
StreamSubscription? _durationSubscription; StreamSubscription? _durationSubscription;
StreamSubscription? _positionSubscription; StreamSubscription? _positionSubscription;
StreamSubscription? _buttonSubscription;
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
InputDirection inputDirection = InputDirection(); InputDirection inputDirection = InputDirection();
@ -53,6 +62,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
ESenseInput.instance.resetAngles();
_animationController = AnimationController( _animationController = AnimationController(
vsync: this, vsync: this,
@ -77,6 +87,22 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
setState(() => _duration = duration); setState(() => _duration = duration);
}); });
_buttonSubscription = ESenseInput.instance.buttonEvents().listen((event) {
if (!event.pressed) {
if (_isPlaying) {
player.pause();
setState(() {
_isPlaying = false;
});
} else {
player.resume();
setState(() {
_isPlaying = true;
});
}
}
});
_positionSubscription = player.onPositionChanged.listen( _positionSubscription = player.onPositionChanged.listen(
(p) => setState(() => _position = p), (p) => setState(() => _position = p),
); );
@ -104,6 +130,12 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
continue; continue;
} }
if (note.position.abs() < 0.5 * 1.0 / 60.0) { if (note.position.abs() < 0.5 * 1.0 / 60.0) {
InputDirection esenseDirection =
ESenseInput.instance.getInputDirection(note.direction);
inputDirection.up |= esenseDirection.up;
inputDirection.down |= esenseDirection.down;
inputDirection.left |= esenseDirection.left;
inputDirection.right |= esenseDirection.right;
bool keypressCorrect = false; bool keypressCorrect = false;
switch (note.direction) { switch (note.direction) {
case ArrowDirection.up: case ArrowDirection.up:
@ -124,6 +156,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
note.wasHit = true; note.wasHit = true;
_animationController.reset(); _animationController.reset();
_animationController.forward(); _animationController.forward();
inputDirection.reset();
setState(() { setState(() {
hitOrMissMessage = 'Great!'; hitOrMissMessage = 'Great!';
}); });
@ -133,6 +166,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
note.wasHit = false; note.wasHit = false;
_animationController.reset(); _animationController.reset();
_animationController.forward(); _animationController.forward();
inputDirection.reset();
setState(() { setState(() {
hitOrMissMessage = 'Missed'; hitOrMissMessage = 'Missed';
}); });
@ -254,6 +288,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
_animationController.dispose(); _animationController.dispose();
_durationSubscription?.cancel(); _durationSubscription?.cancel();
_positionSubscription?.cancel(); _positionSubscription?.cancel();
_buttonSubscription?.cancel();
player.dispose(); player.dispose();
super.dispose(); super.dispose();
} }