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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,3 +9,4 @@ dist/
build/
*.egg-info/
.venv/
acceptance-results/
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,20 @@ All notable user-facing changes are recorded here. Versions follow

## [Unreleased]

### Added

- `oglo acceptance`, an owner-facing USB pair test that uses public SDK APIs,
separates read-only, interactive, reversible mutation, zero, and long-soak gates,
and writes non-overwriting Markdown/JSON evidence bundles

### Changed

- consolidated development, issues, tags, and releases in the public
`OpenGraphLabs/oglo-python` repository; private and staging repositories are no
longer active upstreams
- documented firmware 0.9.10/schema 6 as the only supported live-glove baseline;
retained 0.9.9/schema-6 captures solely as historical decoder provenance

## [0.1.0rc1] - 2026-08-07

First public release candidate.
Expand Down
8 changes: 7 additions & 1 deletion CONTRIBUTING.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,10 @@

Thank you for improving the OGLO Python SDK.

[`OpenGraphLabs/oglo-python`](https://github.com/OpenGraphLabs/oglo-python) is the
sole canonical repository. Fork and branch from its `main`; do not use a private,
staging, or pre-public repository as an upstream.

## Set up

```bash
Expand All@@ -28,7 +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.
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.

## Hardware tests

Expand Down
39 changes: 31 additions & 8 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,16 +2,22 @@

[![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.9+](https://img.shields.io/badge/firmware-0.9.9%2B-5C2D91.svg)](docs/06_compatibility.md)
[![Firmware 0.9.10](https://img.shields.io/badge/firmware-0.9.10-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.0rc1` is a USB-first research SDK for firmware
> `0.9.9+`, schema 6. USB with firmware 0.9.10 is hardware-validated. BLE is
> available as an experimental transport and is not release-qualified.
> **Release candidate:** `0.1.0rc1` 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.

This public repository is the sole canonical source for the SDK. Development,
issues, pull requests, tags, and releases all belong under
[`OpenGraphLabs/oglo-python`](https://github.com/OpenGraphLabs/oglo-python); no
private or staging repository is an active upstream.

## What is included

Expand DownExpand Up@@ -50,7 +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.
recording data. Upgrade any live glove that does not report firmware 0.9.10 and
schema 6.

## Read one glove

Expand DownExpand Up@@ -82,9 +89,9 @@ finally:
right.close()
```

The devices must report opposite sides, distinct logical serials, and the same
non-empty `pair_id`. Samples from two gloves are not hardware-synchronised; see the
[two-hand example](examples/04_two_hands.py) before aligning a dataset.
The devices must report opposite sides and distinct logical serials. Samples from
two gloves are not hardware-synchronised; see the [two-hand
example](examples/04_two_hands.py) before aligning a dataset.

## Calibration changes device state

Expand All@@ -101,6 +108,22 @@ motion range before capturing a new zero.
- [Calibration](docs/03_calibration.md)
- [Recording and replay](docs/04_recording.md)
- [Troubleshooting](docs/05_troubleshooting.md)
- [Test your own glove pair](docs/07_acceptance.md)

## Test the complete physical pair

The owner-facing acceptance runner exercises the installed SDK's public API and
writes a Markdown/JSON evidence bundle. Its default is read-only with respect to
device calibration and settings:

```bash
oglo acceptance
```

Add `--interactive` for guided finger/IMU actions, `--mutations` for reversible
RAW/CLEAN/rate changes, `--zero` to deliberately replace calibration, or
`--soak 75m` for the two-hand device-clock rollover gate. See the
[acceptance guide](docs/07_acceptance.md) before enabling state-changing options.

## Development

Expand Down
7 changes: 5 additions & 2 deletions SECURITY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,12 +4,15 @@

| Version | Security updates |
| --- | --- |
| 0.1.x | 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.

## Reporting a vulnerability

Use GitHub's private vulnerability reporting for this repository:
Use GitHub's private vulnerability reporting for the sole canonical repository:

<https://github.com/OpenGraphLabs/oglo-python/security/advisories/new>

Expand Down
17 changes: 8 additions & 9 deletions docs/01_quickstart.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,9 @@ python3 -m pip install \
"oglo @ git+https://github.com/OpenGraphLabs/oglo-python.git@v0.1.0rc1"
```

Python 3.10 or newer is required. The SDK accepts firmware 0.9.9 or newer with
schema 6 and rejects older or incompatible wire contracts at connection time.
Python 3.10 or newer is required. Supported live gloves run firmware 0.9.10 with
schema 6. `0.1.0rc1` 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.

## Diagnose before collecting data

Expand DownExpand Up@@ -70,9 +71,9 @@ 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.9 and
0.9.10 do 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.
The SDK verifies the active recipe immediately after capture. Firmware 0.9.10 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.

## Two hands
Expand All@@ -86,10 +87,8 @@ finally:
right.close()
```

Which device is left or right comes from CONFIG, not cable order. By default both
devices must also report the same non-empty `pair_id`; this prevents unrelated
gloves from being silently combined. For a deliberate unprovisioned bench setup,
opt in with `oglo.connect_pair(allow_unpaired=True)`.
Which device is left or right comes from CONFIG, not cable order. The devices must
report opposite sides and distinct logical serials.

Relate two hands on `host_t`, never on `t_us`. Each glove has an independent device
clock and there is no hardware synchronisation contract. Read each hand on its own
Expand Down
20 changes: 10 additions & 10 deletions docs/02_data_reference.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,23 +21,23 @@ 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 SDK requires firmware 0.9.9 or newer and schema 6. It rejects any other wire contract at
connect time instead of guessing packet lengths or silently changing stream rates.
The supported live contract is firmware 0.9.10/schema 6. `0.1.0rc1` 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.

## Identity and pairing
## Identity and side

`g.info.serial` is the logical glove serial reported by CONFIG. It is distinct from
the USB chip/descriptor serial and from a BLE address or advertisement name.
`oglo.connect(serial=...)` matches this logical value and verifies it after opening a
specific `port=` or BLE address.

`g.info.side` chooses left versus right. `g.info.pair_id` names the intended two-glove
set. `connect_pair()` requires one left, one right, distinct logical serials and the
same non-empty pair ID. `allow_unpaired=True` bypasses only the empty-ID check for a
deliberate bench setup; it does not allow mismatched IDs or sides.
`g.info.side` chooses left versus right. `connect_pair()` requires one left glove,
one right glove, and distinct logical serials.

`g.info.has_mag` means firmware successfully initialised the magnetometer at boot.
Firmware 0.9.9 cannot distinguish an intentionally absent part from one that failed
Firmware 0.9.10 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,9 @@ 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.9
## Integrity limit of firmware 0.9.10

The 0.9.9 tagged USB frame has a magic and length but no checksum/CRC. The TinyUSB
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
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.9 does
`g.info.zero_valid` reports the active state after connection. Firmware 0.9.10 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.9 sends the start/completion lines
`zero()` is deliberately USB-only: firmware 0.9.10 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.9 does not report
That proves the active firmware recipe is consistent. Firmware 0.9.10 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.
16 changes: 8 additions & 8 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.9 has no end-to-end CRC or
sustained freshness gap is observable. Firmware 0.9.10 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,15 +118,15 @@ 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.9 multi-hour hardware capture. Release qualification must
presented as proof of a 0.9.10 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.

## Metadata worth knowing about

`meta.json` carries the logical board serial, side, `pair_id`, hardware and firmware
revision, the finger order, **`stream_thr` and `stream_clean` as they were at capture
time**, both clocks, and the host loss counters. It also carries start/end `GET
STATUS`, device counter deltas, and `complete`/`error`. Without the threshold the
counts cannot be interpreted later, since the device's current value is not the one
the data was taken under.
`meta.json` carries the logical board serial, side, hardware and firmware revision,
the finger order, **`stream_thr` and `stream_clean` as they were at capture time**,
both clocks, and the host loss counters. It also carries start/end `GET STATUS`,
device counter deltas, and `complete`/`error`. Without the threshold the counts cannot
be interpreted later, since the device's current value is not the one the data was
taken under.
21 changes: 9 additions & 12 deletions docs/05_troubleshooting.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,11 +36,11 @@ 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.

Firmware 0.9.9 still appears to the OS as `XIAO_ESP32S3` from `Espressif Systems`;
the `OGLO` / `OpenGraphLabs` USB strings begin at 0.9.10. Discovery therefore uses
the Seeed VID and then proves identity with `GET CONFIG`. Another XIAO board with the
same VID can briefly appear as a candidate, but it is rejected when that handshake
does not return the strict OGLO schema.
Supported firmware 0.9.10 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
rejected when the handshake does not return the strict OGLO schema.

## Port already held

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.9 uses TinyUSB, which will not transmit until the host raises DTR.
Firmware 0.9.10 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 DownExpand Up@@ -130,13 +130,10 @@ Side is stored on the device. Fix it there:
g.send("SET SIDE left")
```

## Two hands refuse to pair
## Two-hand connection is refused

`connect_pair()` also refuses duplicate logical serials, different `pair_id` values,
or two empty pair IDs. Provision the intended pair with the same non-empty `SET PAIR`
value on both gloves. For a deliberate internal bench test only, two empty IDs can be
opened with `connect_pair(allow_unpaired=True)`; different non-empty IDs are still
rejected.
`connect_pair()` refuses duplicate logical serials or two devices reporting the same
side. Correct `SET SERIAL` or `SET SIDE` on the affected glove before reconnecting.

## Something else

Expand Down
12 changes: 8 additions & 4 deletions docs/06_compatibility.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,15 +8,19 @@ physical gloves. They are different claims.
| Component | Status in 0.1.0rc1 |
| --- | --- |
| Python | 3.10 or newer |
| Firmware | 0.9.9 or newer |
| Live-glove firmware | 0.9.10 |
| 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 |

Unknown schema versions and older firmware fail closed during connection. There is
no best-effort decoder that guesses packet lengths.
The `0.1.0rc1` 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.

## Physical validation for this release candidate

Expand DownExpand Up@@ -50,7 +54,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.9/0.9.10 USB frames do not include an end-to-end payload CRC.
- Firmware 0.9.10 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
Loading