Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,24 @@ All notable user-facing changes are recorded here. Versions follow

## [Unreleased]

## [0.1.0rc3] - 2026-08-09

### Changed

- raised the single firmware floor to 0.9.10 for live connections, replay, hardware
vector capture, and acceptance while retaining schema 6 and accepting current
firmware 0.9.11
- replaced the checked-in physical tagged-stream vectors with a redacted firmware
0.9.10 capture and removed the unsupported legacy interleaved BIN capture
- synchronized README, quickstart, data, calibration, recording, troubleshooting,
compatibility, acceptance, contribution, and security documentation with the
current 0.9.10+ contract and 0.9.11 golden firmware

### Fixed

- made acceptance compare firmware numerically against a minimum so 0.9.11 does
not fail an exact-0.9.10 check

## [0.1.0rc2] - 2026-08-09

### Added
Expand DownExpand Up@@ -55,6 +73,7 @@ First public release candidate.
- zero persistence requires a power-cycle read-back when it is a release gate
- multi-hour and slow-storage target-host qualification remain deployment tasks

[Unreleased]: https://github.com/OpenGraphLabs/oglo-python/compare/v0.1.0rc2...HEAD
[Unreleased]: https://github.com/OpenGraphLabs/oglo-python/compare/v0.1.0rc3...HEAD
[0.1.0rc3]: https://github.com/OpenGraphLabs/oglo-python/compare/v0.1.0rc2...v0.1.0rc3
[0.1.0rc2]: https://github.com/OpenGraphLabs/oglo-python/compare/v0.1.0rc1...v0.1.0rc2
[0.1.0rc1]: https://github.com/OpenGraphLabs/oglo-python/releases/tag/v0.1.0rc1
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,9 +32,9 @@ must not mutate attached devices.

Hardware-specific changes should include the firmware revision, schema, host OS,
test duration, and before/after loss counters. Do not present an automated test as
physical validation unless a physical glove was actually exercised. New live-device
qualification targets firmware 0.9.10/schema 6. Historical 0.9.9 captures may remain
as decoder fixtures only and must be labelled as such.
physical validation unless a physical glove was actually exercised. Live-device
qualification requires firmware 0.9.10 or newer with schema 6. Checked-in wire
vectors must come from that same supported contract and redact real device serials.

## Hardware tests

Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,17 +2,17 @@

[![CI](https://github.com/OpenGraphLabs/oglo-python/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenGraphLabs/oglo-python/actions/workflows/ci.yml)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB.svg)](https://www.python.org/)
[![Firmware 0.9.10](https://img.shields.io/badge/firmware-0.9.10-5C2D91.svg)](docs/06_compatibility.md)
[![Firmware 0.9.10+](https://img.shields.io/badge/firmware-0.9.10%2B-5C2D91.svg)](docs/06_compatibility.md)
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)

Python access to the OGLO five-finger tactile glove: 80 taxels per hand at a
nominal 250 Hz over USB, plus accelerometer, gyroscope, and optional magnetometer
streams.

> **Release candidate:** `0.1.0rc2` is a USB-first research SDK for the supported
> live-glove baseline, firmware 0.9.10/schema 6. The decoder retains historical
> 0.9.9/schema-6 vector compatibility, but 0.9.9 is not a deployment target. BLE
> is available as an experimental transport and is not release-qualified.
> **Release candidate:** `0.1.0rc3` is a USB-first research SDK for firmware
> 0.9.10 or newer with CONFIG schema 6. The current golden firmware for new flashes
> is 0.9.11; deployed 0.9.10 gloves remain supported. Older firmware is rejected
> for both live connections and replay. BLE is experimental and not release-qualified.

This public repository is the sole canonical source for the SDK. Development,
issues, pull requests, tags, and releases all belong under
Expand All@@ -34,14 +34,14 @@ private or staging repository is an active upstream.
Download the wheel from the matching [GitHub Release](https://github.com/OpenGraphLabs/oglo-python/releases), then install it locally:

```bash
python3 -m pip install ./oglo-0.1.0rc2-py3-none-any.whl
python3 -m pip install ./oglo-0.1.0rc3-py3-none-any.whl
```

To install the tagged source instead:

```bash
python3 -m pip install \
"oglo @ git+https://github.com/OpenGraphLabs/oglo-python.git@v0.1.0rc2"
"oglo @ git+https://github.com/OpenGraphLabs/oglo-python.git@v0.1.0rc3"
```

Python 3.10 or newer is required.
Expand All@@ -56,8 +56,8 @@ oglo doctor

`doctor` measures the attached device and host rather than assuming the nominal
rates. Resolve any reported identity, firmware, loss, or throughput failure before
recording data. Upgrade any live glove that does not report firmware 0.9.10 and
schema 6.
recording data. Upgrade any live glove that reports firmware older than 0.9.10 or
anything other than schema 6. New flashes should use the current 0.9.11 golden image.

## Read one glove

Expand Down
6 changes: 3 additions & 3 deletions SECURITY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,11 @@

| Version | Security updates |
| --- | --- |
| 0.1.x with firmware 0.9.10/schema 6 | Yes |
| 0.1.x with firmware 0.9.10+/schema 6 | Yes |
| development snapshots and older versions | No |

Historical 0.9.9 wire captures are retained for decoder regression tests; they do
not make a live 0.9.9 glove a supported deployment.
Firmware older than 0.9.10 is outside the supported live, replay, and vector-capture
contract.

## Reporting a vulnerability

Expand Down
13 changes: 7 additions & 6 deletions docs/01_quickstart.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,19 +6,20 @@ Download the wheel from the matching
[GitHub Release](https://github.com/OpenGraphLabs/oglo-python/releases), then:

```bash
python3 -m pip install ./oglo-0.1.0rc2-py3-none-any.whl
python3 -m pip install ./oglo-0.1.0rc3-py3-none-any.whl
```

Or install the immutable source tag:

```bash
python3 -m pip install \
"oglo @ git+https://github.com/OpenGraphLabs/oglo-python.git@v0.1.0rc2"
"oglo @ git+https://github.com/OpenGraphLabs/oglo-python.git@v0.1.0rc3"
```

Python 3.10 or newer is required. Supported live gloves run firmware 0.9.10 with
schema 6. `0.1.0rc2` can still parse historical 0.9.9/schema-6 captures, but that
parser tolerance is not deployment support; upgrade a live 0.9.9 glove before use.
Python 3.10 or newer is required. Supported live gloves run firmware 0.9.10 or
newer with schema 6. The current golden firmware for new flashes is 0.9.11;
deployed 0.9.10 gloves remain supported. `0.1.0rc3` rejects older firmware in both
live connections and recorded episodes.

## Diagnose before collecting data

Expand DownExpand Up@@ -71,7 +72,7 @@ Wear the glove, open and close the hand through its full motion range for the fi
seconds, and touch nothing. Bending a finger presses the sensor by itself, so a
still-hand baseline creates false contacts during later motion.

The SDK verifies the active recipe immediately after capture. Firmware 0.9.10 does
The SDK verifies the active recipe immediately after capture. Supported firmware does
not expose enough information to prove that the flash write survived a power cycle;
reboot and read it back when persistence is a release or factory gate.
See [Calibration](03_calibration.md) before changing an externally supplied glove.
Expand Down
19 changes: 10 additions & 9 deletions docs/02_data_reference.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,10 +21,10 @@ The IMU packet cadence is not the physical sensor ODR. Firmware configures the
accelerometer/gyroscope at 200 Hz but polls/emits its latest value on a nominal 2 ms
schedule, so adjacent 500-packet/s records may contain the same physical measurement.

The supported live contract is firmware 0.9.10/schema 6. `0.1.0rc2` retains parser
tolerance for historical 0.9.9/schema-6 vectors and recordings, but live collection
must use 0.9.10. Other schemas and older firmware fail closed instead of inviting a
best-effort packet guess.
The supported contract is firmware 0.9.10 or newer with schema 6. The current
golden firmware for new flashes is 0.9.11, while deployed 0.9.10 gloves remain
supported. `0.1.0rc3` rejects older firmware in live connections, vector capture,
and replay instead of selecting a best-effort decoder.

## Identity and side

Expand All@@ -37,7 +37,7 @@ specific `port=` or BLE address.
one right glove, and distinct logical serials.

`g.info.has_mag` means firmware successfully initialised the magnetometer at boot.
Firmware 0.9.10 cannot distinguish an intentionally absent part from one that failed
Supported firmware cannot distinguish an intentionally absent part from one that failed
boot detection, and it has no runtime read-failure/freshness counter. Therefore a
clean status snapshot is not proof that every magnetometer value is fresh; applications
that require heading-quality data need a firmware freshness flag and a physical field
Expand DownExpand Up@@ -166,9 +166,10 @@ scheduling still contribute unknown delay.
Recordings store both, plus a wall-clock anchor, because each answers a question the
others cannot.

## Integrity limit of firmware 0.9.10
## Integrity limit of supported firmware

The 0.9.10 tagged USB frame has a magic and length but no checksum/CRC. The TinyUSB
whole-frame queue removes the known pre-0.9.9 truncation path, but the SDK cannot
mathematically prove that every plausible payload bit is intact. A future protocol
The supported tagged USB frame has a magic and length but no checksum/CRC. Firmware
0.9.11 bounds TinyUSB writes so a stopped host cannot hold the TX path forever, but
that deadline is not payload integrity. The SDK cannot mathematically prove that
every plausible payload bit is intact. A future protocol
needs framed CRC protection for that guarantee.
6 changes: 3 additions & 3 deletions docs/03_calibration.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,7 @@ capture path, carry it yourself.
## What is intended to survive a power cycle

The firmware stores the zero and stream mode in device flash, and
`g.info.zero_valid` reports the active state after connection. Firmware 0.9.10 does
`g.info.zero_valid` reports the active state after connection. Supported firmware does
not report the flash-write result or perform a power-cycle readback, so the SDK alone
cannot prove persistence. Reboot, reconnect and compare the recipe when that is a
release or factory gate.
Expand All@@ -77,7 +77,7 @@ release or factory gate.
`clean()` and tactile-rate changes can be confirmed by re-reading config. Firmware
does not expose the applied IMU period in BLE config, so `rates(imu=...)` is also
USB-only rather than returning an unverified success.
`zero()` is deliberately USB-only: firmware 0.9.10 sends the start/completion lines
`zero()` is deliberately USB-only: supported firmware sends the start/completion lines
and full `GET ZERO` recipe only over serial. Without those, BLE can send the command
but cannot prove capture or persistence completed, so the SDK fails immediately
instead of waiting and then pretending success.
Expand All@@ -86,7 +86,7 @@ Over USB, `zero()` requires the start acknowledgement, validates all 80 baseline
noise values, re-reads them with `GET ZERO`, and finally verifies `zero_valid` in
config before returning.

That proves the active firmware recipe is consistent. Firmware 0.9.10 does not report
That proves the active firmware recipe is consistent. Supported firmware does not report
the NVS write result or re-read flash before replying, so the SDK cannot prove power-
cycle persistence without an actual reboot/reconnect check. Do that as a release or
factory gate; do not interpret a successful call as an atomic-flash guarantee.
4 changes: 2 additions & 2 deletions docs/04_recording.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -104,7 +104,7 @@ This bounds SDK memory, but it is not proof of unlimited recording. A chunk flus
a synchronous write and `fsync` on the same thread that drains USB; a slow Raspberry
Pi SD-card stall can still cause receive loss. The SDK refuses to mark the episode
complete when a sequence gap, overflow, malformed frame or
sustained freshness gap is observable. Firmware 0.9.10 has no end-to-end CRC or
sustained freshness gap is observable. Supported firmware has no end-to-end CRC or
read-failure counters, so that is not proof that every short tail loss is detectable;
release qualification must measure it on the target storage. A hard process/power
loss can also lose the not-yet-flushed RAM tail; there is not yet a recovery command
Expand All@@ -118,7 +118,7 @@ for i in range(12):
```

The repository does not contain a raw current long-soak report, so unit tests are not
presented as proof of a 0.9.10 multi-hour hardware capture. Release qualification must
presented as proof of a multi-hour hardware capture. Release qualification must
include two hands for more than 72 minutes to cross the device-clock rollover, plus a
slow-storage stress run.

Expand Down
4 changes: 2 additions & 2 deletions docs/05_troubleshooting.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,7 +36,7 @@ In order of likelihood:
3. Something else already holds the port. `doctor` lists non-glove serial devices it
saw and skipped, which is often the clue.

Supported firmware 0.9.10 appears to the OS as `OGLO` from `OpenGraphLabs`. A glove
Supported firmware 0.9.10 or newer appears to the OS as `OGLO` from `OpenGraphLabs`. A glove
that still appears as `XIAO_ESP32S3` from `Espressif Systems` is running an older
build and must be upgraded. Discovery still proves identity with `GET CONFIG`; a
different XIAO using the same VID can briefly appear as a candidate, but it is
Expand All@@ -55,7 +55,7 @@ network device and serves several subscribers at once.
## The board answers nothing at all

If you are writing your own serial code rather than using this SDK: **assert DTR.**
Firmware 0.9.10 uses TinyUSB, which will not transmit until the host raises DTR.
Supported firmware uses TinyUSB, which will not transmit until the host raises DTR.
With DTR low the board returns literally zero bytes and looks dead. It is not.

Keep RTS low. The two together are what a USB-UART bridge decodes as a reset request.
Expand Down
22 changes: 10 additions & 12 deletions docs/06_compatibility.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,26 +5,24 @@ physical gloves. They are different claims.

## Supported contract

| Component | Status in 0.1.0rc2 |
| Component | Status in 0.1.0rc3 |
| --- | --- |
| Python | 3.10 or newer |
| Live-glove firmware | 0.9.10 |
| Minimum supported firmware | 0.9.10 |
| Current golden firmware for new flashes | 0.9.11 |
| CONFIG schema | exactly 6 |
| USB tagged stream | supported and hardware-validated |
| USB legacy interleaved frame | decoder and captured-vector compatibility |
| BLE schema-6 notifications | experimental; parser-tested, not release-qualified |
| Firmware 0.9.9 | historical parser/vector compatibility only; upgrade before live use |
| Firmware 0.9.8 and older | rejected |
| Firmware older than 0.9.10 | rejected for connect, replay, and vector capture |

The `0.1.0rc2` parser floor remains 0.9.9 so historical schema-6 golden vectors and
recordings stay readable. That tolerance is not a live-device support claim. The
deployed fleet and physical release qualification use 0.9.10/schema 6; unknown
schemas and firmware older than 0.9.9 fail closed rather than selecting a
best-effort decoder.
`0.1.0rc3` has one firmware floor: 0.9.10. Live devices, checked-in vectors, and
recorded episodes below that floor fail closed. Firmware 0.9.11 keeps schema 6 and
the same SDK wire contract while adding a bounded TinyUSB write path; it is the
current image for new flashes. Deployed 0.9.10 gloves remain compatible.

## Physical validation for this release candidate

The release candidate was exercised on one left and one right glove running
The release candidate was exercised on one left and one right deployed glove running
firmware 0.9.10/schema 6 over USB on macOS. The measured default delivery was about
250 tactile packets/s, 500 IMU packets/s, and 125 magnetometer packets/s per hand,
with no capture-window sequence gaps, malformed frames, or host queue overflow in
Expand DownExpand Up@@ -54,7 +52,7 @@ mutation through `GET ZERO`.
- The two gloves do not share a hardware clock or trigger.
- A nominal 500 IMU packets/s is transport cadence, not proof of 500 fresh physical
sensor measurements per second.
- Firmware 0.9.10 USB frames do not include an end-to-end payload CRC.
- Supported firmware USB frames do not include an end-to-end payload CRC.
- Multi-hour recording, slow-storage stress, and device-clock rollover remain target
deployment qualification items.

Expand Down
6 changes: 3 additions & 3 deletions docs/07_acceptance.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ The default run does **not** change zero, threshold, RAW/CLEAN mode, or stream r
It checks:

- one left and one right glove with distinct logical serials
- firmware 0.9.10, CONFIG schema 6, USB transport, dimensions, and finger order
- firmware 0.9.10 or newer, CONFIG schema 6, USB transport, dimensions, and finger order
- sensor health and existing zero state
- public tactile, IMU, magnetometer, `stop()`, `start()`, and `read_batch()` paths
- simultaneous two-hand rate, timestamps, sequence gaps, malformed data, and overflow
Expand DownExpand Up@@ -93,7 +93,7 @@ For each glove the runner prints the serial and requires typing `ZERO <serial>`.
that glove, touch nothing, and repeatedly open and close the hand during the sweep.

The SDK validates the completion recipe, all 80 baseline/noise entries, `GET ZERO`,
and CONFIG `zero_valid`. Firmware 0.9.10 cannot prove that flash survived a power
and CONFIG `zero_valid`. Supported firmware cannot prove that flash survived a power
cycle, so the report leaves that gate as `SKIP`. Unplug/replug the glove and run the
safe default again to provide separate read-back evidence.

Expand DownExpand Up@@ -124,7 +124,7 @@ It does not claim:
- hardware synchronisation between the two gloves
- Newton/force calibration
- fused orientation or validated magnetometer axes
- payload integrity beyond what firmware 0.9.10 exposes
- payload integrity beyond what supported firmware exposes
- power-cycle zero persistence unless that physical cycle was separately performed

Run the default acceptance check before an important capture and attach its JSON
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "oglo"
version = "0.1.0rc2"
version = "0.1.0rc3"
description = "Python SDK for the OGLO five-finger tactile glove"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
21 changes: 8 additions & 13 deletions spec/vectors/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,21 +5,16 @@ Each `.bin` is one whole packet captured from a physical glove. Its paired
reference decoder in `tools/capture_vectors.py`. The SDK decoder does not generate
its own expected answers.

The currently checked-in packets were captured from physical glove
`OGLO-R-TEST04` running firmware 0.9.9. The tactile capture is an all-zero clean
frame, so it proves the real packet length/framing but does not independently excite
every packed12 nibble. `tests/test_capture_vectors.py` therefore also pins a literal
non-zero `0x123, 0xabc` pair through the independent reference decoder.

Firmware 0.9.9 is not a supported live-glove target. These immutable captures remain
only because they are physical provenance for the schema-6 decoder; supported live
devices run firmware 0.9.10/schema 6.
The currently checked-in packets were captured on 2026-08-09 from a physical left
glove running firmware 0.9.10/schema 6. The public metadata redacts its logical
serial as `OGLO-L-GOLDEN`; raw packet bytes and decoded values are unchanged. The
tactile frame includes non-zero counts, and `tests/test_capture_vectors.py` also
pins a literal non-zero `0x123, 0xabc` pair through the independent reference
decoder so every packed12 nibble order is testable without relying on one pose.

The capture tool requires tactile and IMU packets, plus magnetometer packets when the
board reports `has_mag=true`. It writes nothing on an incomplete capture and removes
obsolete `tag_*_<length>b` variants only after the replacement set is ready.

`usb_frame_v6_191b.*` is a physical 0.9.9 capture of the legacy interleaved BIN
stream retained as provenance for the browser viewer and hardware repository. The
SDK runtime intentionally does not decode BIN, and `tests/test_vectors.py` therefore
does not treat that file as a supported decoder contract.
Only the supported tagged USB contract is kept here. Legacy interleaved BIN captures
and decoders are not part of this SDK.
Binary file modifiedspec/vectors/tag_imu_25b.bin
Binary file not shown.
Loading