Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into jump-height-test
Browse files Browse the repository at this point in the history
  • Loading branch information
probstlukas committed Dec 26, 2023
2 parents 1dec4d2 + fa468af commit 66b397c
Show file tree
Hide file tree
Showing 22 changed files with 674 additions and 529 deletions.
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@

[OpenEarable](https://open-earable.teco.edu) is a new, open-source, Arduino-based platform for ear-based sensing applications. It provides a versatile prototyping platform with support for various sensors and actuators, making it suitable for earable research and development.

<kbd> <br> [Download iOS beta app!](https://testflight.apple.com/join/Kht3e1Cb) <br> </kbd>
<p>
<a href="https://testflight.apple.com/join/Kht3e1Cb">
⬇️ Download iOS beta app
</a>
</p>

<p>
<a href="https://github.com/OpenEarable/app/releases">
⬇️ Download Android beta app
</a>
</p>


<kbd> <br> [Get OpenEarable device now!](https://forms.gle/R3LMcqtyKwVH7PZB9) <br> </kbd>
<p>
<a href="https://forms.gle/R3LMcqtyKwVH7PZB9">
🦻 Get OpenEarable device now
</a>
</p>

## Table of Contents
- [Introduction](#Introduction)
Expand Down
Binary file added open_earable/assets/digital-7-mono.ttf
Binary file not shown.
8 changes: 7 additions & 1 deletion open_earable/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
PODS:
- app_settings (5.1.1):
- Flutter
- Flutter (1.0.0)
- flutter_gl (0.0.3):
- Flutter
Expand All @@ -21,6 +23,7 @@ PODS:
- three3d_egl (0.1.3)

DEPENDENCIES:
- app_settings (from `.symlinks/plugins/app_settings/ios`)
- Flutter (from `Flutter`)
- flutter_gl (from `.symlinks/plugins/flutter_gl/ios`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
Expand All @@ -36,6 +39,8 @@ SPEC REPOS:
- three3d_egl

EXTERNAL SOURCES:
app_settings:
:path: ".symlinks/plugins/app_settings/ios"
Flutter:
:path: Flutter
flutter_gl:
Expand All @@ -52,6 +57,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/reactive_ble_mobile/ios"

SPEC CHECKSUMS:
app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_gl: 5a5603f35db897697f064027864a32b15d0c421d
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
Expand All @@ -65,4 +71,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189

COCOAPODS: 1.14.3
COCOAPODS: 1.12.1
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ class PostureTimestamps {

class BadPostureReminder {
BadPostureSettings _settings = BadPostureSettings(
rollAngleThreshold: 7,
pitchAngleThreshold: 15,
rollAngleThreshold: 20,
pitchAngleThreshold: 20,
timeThreshold: 10,
resetTimeThreshold: 2
resetTimeThreshold: 1
);
final OpenEarable _openEarable;
final AttitudeTracker _attitudeTracker;
Expand All @@ -50,6 +50,8 @@ class BadPostureReminder {

BadPostureReminder(this._openEarable, this._attitudeTracker);

bool? _lastPostureWasBad = null;

void start() {
_timestamps.lastReset = DateTime.now();
_timestamps.lastBadPosture = null;
Expand All @@ -64,6 +66,10 @@ class BadPostureReminder {

DateTime now = DateTime.now();
if (_isBadPosture(attitude)) {
if (!(_lastPostureWasBad ?? false)) {
_openEarable.rgbLed.writeLedColor(r: 255, g: 0, b: 0);
}

// If this is the first time the program enters the bad state, store the current time
if (_timestamps.lastBadPosture == null) {
_timestamps.lastBadPosture = now;
Expand All @@ -81,6 +87,10 @@ class BadPostureReminder {
// Reset the last good state time
_timestamps.lastGoodPosture = null;
} else {
if (_lastPostureWasBad ?? false) {
_openEarable.rgbLed.writeLedColor(r: 0, g: 255, b: 0);
}

// If this is the first time the program enters the good state, store the current time
if (_timestamps.lastGoodPosture == null) {
_timestamps.lastGoodPosture = now;
Expand All @@ -90,13 +100,23 @@ class BadPostureReminder {
// Calculate the duration in seconds
int duration = now.difference(_timestamps.lastGoodPosture!).inSeconds;
// If the duration exceeds the minimum required, reset the last bad state time
if (duration > _settings.resetTimeThreshold) {
if (duration >= _settings.resetTimeThreshold) {
print("duration: $duration, reset time threshold: ${_settings.resetTimeThreshold}");
print("resetting last bad posture time");
_timestamps.lastBadPosture = null;
}
}
}
_lastPostureWasBad = _isBadPosture(attitude);
});
}

void stop() {
_timestamps.lastBadPosture = null;
_timestamps.lastGoodPosture = null;
_openEarable.rgbLed.writeLedColor(r: 0, g: 0, b: 0);
_attitudeTracker.stop();
}

void setSettings(BadPostureSettings settings) {
_settings = settings;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';

import 'package:open_earable/apps/posture_tracker/model/attitude_tracker.dart';
import 'package:open_earable/apps/posture_tracker/model/ewma.dart';
import 'package:open_earable_flutter/src/open_earable_flutter.dart';

class EarableAttitudeTracker extends AttitudeTracker {
Expand All @@ -12,6 +13,10 @@ class EarableAttitudeTracker extends AttitudeTracker {
@override
bool get isAvailable => _openEarable.bleManager.connected;

EWMA _rollEWMA = EWMA(0.5);
EWMA _pitchEWMA = EWMA(0.5);
EWMA _yawEWMA = EWMA(0.5);

EarableAttitudeTracker(this._openEarable) {
_openEarable.bleManager.connectionStateStream.listen((connected) {
didChangeAvailability(this);
Expand All @@ -21,7 +26,6 @@ class EarableAttitudeTracker extends AttitudeTracker {
});
}


@override
void start() {
if (_subscription?.isPaused ?? false) {
Expand All @@ -32,9 +36,9 @@ class EarableAttitudeTracker extends AttitudeTracker {
_openEarable.sensorManager.writeSensorConfig(_buildSensorConfig());
_subscription = _openEarable.sensorManager.subscribeToSensorData(0).listen((event) {
updateAttitude(
roll: event["EULER"]["ROLL"],
pitch: event["EULER"]["PITCH"],
yaw: event["EULER"]["YAW"]
roll: _rollEWMA.update(event["EULER"]["ROLL"]),
pitch: _pitchEWMA.update(event["EULER"]["PITCH"]),
yaw: _yawEWMA.update(event["EULER"]["YAW"])
);
});
}
Expand All @@ -46,6 +50,7 @@ class EarableAttitudeTracker extends AttitudeTracker {

@override
void cancle() {
stop();
_subscription?.cancel();
super.cancle();
}
Expand Down
11 changes: 11 additions & 0 deletions open_earable/lib/apps/posture_tracker/model/ewma.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class EWMA {
double _alpha;
double _oldValue = 0;

EWMA(this._alpha);

double update(double newValue) {
_oldValue = _alpha * newValue + (1 - _alpha) * _oldValue;
return _oldValue;
}
}
69 changes: 35 additions & 34 deletions open_earable/lib/apps/posture_tracker/view/posture_roll_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,45 @@ class PostureRollView extends StatelessWidget {
final String neckAssetPath;
final AlignmentGeometry headAlignment;

const PostureRollView({Key? key,
required this.roll,
this.angleThreshold = 0,
required this.headAssetPath,
required this.neckAssetPath,
this.headAlignment = Alignment.center}) : super(key: key);
const PostureRollView(
{Key? key,
required this.roll,
this.angleThreshold = 0,
required this.headAssetPath,
required this.neckAssetPath,
this.headAlignment = Alignment.center})
: super(key: key);

@override
Widget build(BuildContext context) {
return Column(children: [
Text(
"${(this.roll * 180 / 3.14).abs().toStringAsFixed(0)}°",
style: TextStyle(
// use proper color matching the background
color: Theme.of(context).colorScheme.onBackground,
fontSize: 30,
fontWeight: FontWeight.bold
)
),
Text("${(this.roll * 180 / 3.14).abs().toStringAsFixed(0)}°",
style: TextStyle(
// use proper color matching the background
color: Theme.of(context).colorScheme.onBackground,
fontSize: 30,
fontWeight: FontWeight.bold)),
CustomPaint(
painter: ArcPainter(angle: this.roll, angleThreshold: this.angleThreshold),
child: Padding(
padding: EdgeInsets.all(10),
child: ClipOval(
child: Container(
color: roll.abs() > _MAX_ROLL ? Colors.red.withOpacity(0.5) : Colors.transparent,
child: Stack(children: [
Image.asset(this.neckAssetPath),
Transform.rotate(
angle: this.roll.isFinite ? roll.abs() < _MAX_ROLL ? this.roll : roll.sign * _MAX_ROLL : 0,
alignment: this.headAlignment,
child: Image.asset(this.headAssetPath)
),
])
)
)
)
),
painter:
ArcPainter(angle: this.roll, angleThreshold: this.angleThreshold),
child: Padding(
padding: EdgeInsets.all(10),
child: ClipOval(
child: Container(
color: roll.abs() > _MAX_ROLL
? Colors.red.withOpacity(0.5)
: Colors.transparent,
child: Stack(children: [
Image.asset(this.neckAssetPath),
Transform.rotate(
angle: this.roll.isFinite
? roll.abs() < _MAX_ROLL
? this.roll
: roll.sign * _MAX_ROLL
: 0,
alignment: this.headAlignment,
child: Image.asset(this.headAssetPath)),
]))))),
]);
}
}
}
Loading

0 comments on commit 66b397c

Please sign in to comment.