Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
2a89702
renamed tau ring to OpenRing
TobiasRoeddiger Feb 11, 2026
d989da4
renamed folder
TobiasRoeddiger Feb 11, 2026
ffbf865
added missing file
TobiasRoeddiger Feb 11, 2026
1605d98
renamed value parser
TobiasRoeddiger Feb 11, 2026
9efe6be
Add OpenRing PPG sensor parsing and configuration
TobiasRoeddiger Feb 11, 2026
308fa0f
Merge pull request #111 from OpenEarable/codex/add-ppg-support-for-op…
TobiasRoeddiger Feb 11, 2026
85d291e
small changes
TobiasRoeddiger Feb 11, 2026
ba000bc
Add OpenRing payload-based config and PPG streaming command
TobiasRoeddiger Feb 11, 2026
56b7418
updates
TobiasRoeddiger Feb 11, 2026
eb470c2
Fix OpenRing IMU frame parsing and add PPG packet decoding
TobiasRoeddiger Feb 11, 2026
ab0a8e1
Swap OpenRing PPG start/stop control byte
TobiasRoeddiger Feb 11, 2026
1ec726f
Harden OpenRing IMU stream handling for mixed packets
TobiasRoeddiger Feb 11, 2026
6b90fc1
Make OpenRing parser tolerant to partial/mixed packets
TobiasRoeddiger Feb 11, 2026
2b1ecfc
Decode OpenRing PPG packet type 0x02 frames
TobiasRoeddiger Feb 11, 2026
8e25cba
Fix OpenRing IMU payload offset and flexible waveform logs
TobiasRoeddiger Feb 11, 2026
cb6aa71
Fix OpenRing sample timestamps to be strictly monotonic
TobiasRoeddiger Feb 11, 2026
9758f6b
Trace OpenRing parsed/emitted values and decouple timestamps by cmd
TobiasRoeddiger Feb 11, 2026
907aa83
Estimate IMU sample rate from packet timing for timestamp spacing
TobiasRoeddiger Feb 11, 2026
b60afdd
Use fixed IMU sample spacing for timestamps
TobiasRoeddiger Feb 11, 2026
d8df562
Align IMU accel parsing with SDK sub-opcode behavior
TobiasRoeddiger Feb 11, 2026
81d8842
Ignore OpenRing accel-only IMU packets and parse accel+gyro only
TobiasRoeddiger Feb 11, 2026
bcadb0e
fully implemented imu
TobiasRoeddiger Feb 11, 2026
31c3d0e
resolve merge conflict
TobiasRoeddiger Feb 11, 2026
bf763d7
added PPG support
TobiasRoeddiger Feb 11, 2026
4113108
all sensors working
TobiasRoeddiger Feb 11, 2026
6265086
implemented wall clock time sync for OpenRing
TobiasRoeddiger Feb 11, 2026
386018a
implemented regular battery polling
TobiasRoeddiger Feb 11, 2026
96014a2
ensure timing logic while battery and time sync
TobiasRoeddiger Feb 11, 2026
674e4e4
implemented openring
TobiasRoeddiger Feb 13, 2026
c53a9c0
added device image feature with stereo support
TobiasRoeddiger Feb 14, 2026
2f241d7
Add generic sensor forwarders and LSL bridge integration
TobiasRoeddiger Feb 15, 2026
056092d
Merge branch 'lsl' into open-ring
TobiasRoeddiger Feb 15, 2026
002f7fa
added basic lsl support
TobiasRoeddiger Feb 15, 2026
ede7579
added lsl support
TobiasRoeddiger Feb 15, 2026
63e24bf
renamed udp stuff
TobiasRoeddiger Feb 15, 2026
438129b
renamed forwarders
TobiasRoeddiger Feb 15, 2026
d36b8d8
added examples
TobiasRoeddiger Feb 15, 2026
5222e86
added off state and default states for openring
TobiasRoeddiger Feb 15, 2026
d011c1c
added documentation to the examples
TobiasRoeddiger Feb 16, 2026
a74c47c
added documentation to the examples
TobiasRoeddiger Feb 16, 2026
d4639fc
app improvements and more changes
TobiasRoeddiger Feb 16, 2026
6c3fff4
added openring image
TobiasRoeddiger Feb 16, 2026
a845cd8
more gracefully handle sensor states and data targets for openring
TobiasRoeddiger Feb 17, 2026
768cd60
more gracefully handle sensor states and data targets for openring
TobiasRoeddiger Feb 17, 2026
0570690
streamlined the system devices and bluetooth devices
TobiasRoeddiger Feb 17, 2026
b909336
made the openring implementation more stable
TobiasRoeddiger Feb 17, 2026
0aef90e
made the openring implementation more stable
TobiasRoeddiger Feb 17, 2026
f37fb2a
improved the state handling
TobiasRoeddiger Feb 18, 2026
19b96a9
rely on the 0x32 characharteristic only
TobiasRoeddiger Feb 18, 2026
3deed40
simplified the logic to pure software disable because everything else…
TobiasRoeddiger Feb 18, 2026
8b6b5c9
fixed a little bug in the battery logic
TobiasRoeddiger Feb 18, 2026
b1e3b8d
fixed a little bug in the battery logic
TobiasRoeddiger Feb 18, 2026
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
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## Unreleased

* added global LSL forwarding support for sensor streams
* introduced `UdpBridgeForwarder` implementation for UDP -> LSL bridge forwarding
* forwards active sensor samples to a configurable UDP bridge endpoint (`host:port`)
* introduced generic sensor forwarding abstractions (`SensorForwarder`) with global dependency injection and per-forwarder enable/disable support
* added LSL forwarding documentation (`doc/LSL.md`)
* bundled Python UDP->LSL bridge script (`tools/lsl_bridge.py`) that prints local IPs for app setup

## 2.3.3

* renamed TauRing to OpenRing
* added support for OpenRing temperature sensors (`temp0`, `temp1`, `temp2`) as one 3-channel `Temperature` sensor (`°C`) with software-only on/off control

## 2.3.2

* fixed some bugs with Esense devices
Expand Down Expand Up @@ -73,4 +87,4 @@ Connecting to earable now retries after first failure.

## 0.0.1

* TODO: Describe initial release.
* TODO: Describe initial release.
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,65 @@ To get started with the OpenEarable Flutter package, follow these steps:
> [!WARNING]
> Checking for capabilities using `is <Capability>` is deprecated. Please use `hasCapability<T>()` instead. You can learn more about capabilities in the [Capabilities](doc/CAPABILITIES.md) documentation.

### 8. Forward Sensor Streams to LSL (Optional)
Forwarding is global and protocol-agnostic. Configure LSL as one forwarder:

```dart
final udpBridgeForwarder = UdpBridgeForwarder.instance;
udpBridgeForwarder.configure(
host: "192.168.1.42", // IP/hostname of the bridge machine
port: 16571, // UDP port of the bridge
enabled: true,
);

final manager = WearableManager(
sensorForwarders: [udpBridgeForwarder],
);
```

You can enable/disable/add/remove forwarders at runtime:

```dart
manager.setSensorForwarderEnabled(udpBridgeForwarder, false); // disable
manager.setSensorForwarderEnabled(udpBridgeForwarder, true); // enable
manager.removeSensorForwarder(udpBridgeForwarder); // remove
manager.addSensorForwarder(udpBridgeForwarder); // add again
```

You can also check forwarding connectivity state:

```dart
final state = manager.getSensorForwarderConnectionState(udpBridgeForwarder);
// SensorForwarderConnectionState.active (default)
// A probe runs after configure/enable.
// SensorForwarderConnectionState.unreachable (after detected send fault)
// While enabled + configured, health probes run every 1 second.

manager.getSensorForwarderConnectionStateStream(udpBridgeForwarder)?.listen((state) {
// react to state changes
});

final error = manager.getSensorForwarderConnectionErrorMessage(udpBridgeForwarder);
// null while active, otherwise a human-readable unreachable error
```

This package forwards sensor samples as UDP JSON packets. A middleware process must run on the target machine and publish those samples to Lab Streaming Layer.
By default, the stream prefix is the local phone/device name. You can still override it via `streamPrefix` if needed.

This repository includes the **OpenWearables LSL Dashboard Example** at `tools/lsl_receive_and_ploty.py`:

```bash
pip install pylsl
python tools/lsl_receive_and_ploty.py --port 16571 --dashboard-port 8765
```

At startup it prints the local IP addresses you can use as `host` in your app config and the dashboard URL(s). Open the dashboard URL in a browser to see live stream cards and latest values. The bridge publishes LSL outlets and forwards the same samples to the web dashboard. See [LSL Forwarding](doc/LSL.md) for details.

Minimal network relay consumer example (placeholder hooks for your own handling):

```bash
python tools/lsl_receive_minimal.py
```

## Add custom Wearable Support
Learn more about how to add support for your own wearable devices in the [Adding Custom Wearable Support](doc/ADD_CUSTOM_WEARABLE.md) documentation.
Learn more about how to add support for your own wearable devices in the [Adding Custom Wearable Support](doc/ADD_CUSTOM_WEARABLE.md) documentation.
Binary file added assets/wearable_icons/open_earable_v2/left.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/wearable_icons/open_earable_v2/pair.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/wearable_icons/open_earable_v2/right.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/wearable_icons/open_ring/openring.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion example/lib/widgets/sensor_configuration_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class _SensorConfigurationViewState extends State<SensorConfigurationView> {
@override
void initState() {
super.initState();
_selectedValue = widget.configuration.values.first;
_selectedValue =
widget.configuration.offValue ?? widget.configuration.values.first;
}

@override
Expand Down
Loading