Skip to main content

Python SDK for the Digity sensorized exohand

Project description

digity SDK

Python SDK for the Digity sensorized exohand — plug in the glove, stream live sensor data in three lines.

The Digity exohand is a sensorized glove that captures real-time hand motion data: joint angles for every finger, 6-axis IMU (accelerometer + gyroscope), and 6-channel capacitive touch per node. This SDK handles the USB connection, binary protocol decoding, and threading — so you get clean, typed Python objects at ~50 Hz without any low-level plumbing.

from digity import GloveStream, AnglesSensor

with GloveStream() as stream:
    for frame in stream:
        for sensor in frame.sensors:
            if isinstance(sensor, AnglesSensor):
                print(sensor.finger, sensor.samples[-1].angles_deg)

Use cases: motion capture, rehabilitation, robotics control, gesture recognition, HCI research, data collection.


Contents


Requirements

All platforms

  • Python 3.9 or later
  • Digity exohand connected via USB

Windows

No additional system dependencies. Install Python from python.org or the Microsoft Store, then install the SDK directly with pip.

Note: If digity-viz or digity-agent is not recognised after install, your Python Scripts folder may not be on PATH. Use python -m digity.viz as a drop-in replacement.

Linux (Debian / Ubuntu)

The digity[viz] dashboard uses pywebview to open a desktop window. On Linux, pywebview requires GTK and its Python bindings (gi), which must be installed via the system package manager — they are not available on PyPI.

Before creating your virtual environment:

sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.1

Then create the venv and install:

python3 -m venv .venv
source .venv/bin/activate
pip install "digity[viz]"
digity-viz

Alternatively, create the venv with --system-site-packages so it inherits the system gi module:

python3 -m venv .venv --system-site-packages
source .venv/bin/activate
pip install "digity[viz]"
digity-viz

Headless / no display — if running on a server without a desktop, the dashboard still starts and prints the URL. Access it via SSH tunnel from your local machine:

ssh -L 5001:127.0.0.1:5001 user@server
# then open http://localhost:5001/chiros/ in your local browser

The core SDK (pip install digity) has no extra system dependencies on any platform.


Installation

# Core SDK — sensor data in Python only
pip install digity

# SDK + real-time local dashboard
pip install "digity[viz]"

# SDK + cloud agent relay
pip install "digity[agent]"

Quick start

Plug in the glove, then run:

from digity import GloveStream, AnglesSensor, ImuSensor, TouchSensor

with GloveStream() as stream:
    for frame in stream:
        print(f"side={frame.side}  node={frame.node_id}  seq={frame.seq}")

        for sensor in frame.sensors:

            if isinstance(sensor, AnglesSensor):
                angles = sensor.samples[-1].angles_deg
                print(f"  finger {sensor.finger} angles: {angles}")

            elif isinstance(sensor, ImuSensor):
                s = sensor.samples[-1]
                print(f"  finger {sensor.finger} acc={s.acc}  gyro={s.gyro}")

            elif isinstance(sensor, TouchSensor):
                print(f"  finger {sensor.finger} touch={sensor.channels}")

The with block handles connection and cleanup automatically. Press Ctrl+C to stop.


GloveStream

GloveStream(
    port=None,       # serial port e.g. "COM3" / "/dev/ttyUSB0" — None = auto-detect
    baud=921600,     # baud rate
    *,
    host=None,       # remote host IP for ZMQ mode
    zmq_port=5555,   # ZMQ port when using host
)
Method Description
connect() Open the connection. Called automatically by with.
disconnect() Close the connection. Called automatically on with exit.
__iter__() Yields GloveFrame objects. Blocks until the next frame arrives.

Specifying the USB port — the SDK auto-detects on all platforms. If auto-detection fails:

GloveStream(port="/dev/ttyUSB0")          # Linux
GloveStream(port="/dev/tty.usbserial-0001")  # macOS
GloveStream(port="COM3")                  # Windows

Stop from another thread:

import threading
from digity import GloveStream

stream = GloveStream()
stream.connect()

def stop_after(seconds):
    import time; time.sleep(seconds)
    stream.disconnect()

threading.Thread(target=stop_after, args=(10,), daemon=True).start()

for frame in stream:
    print(frame.seq)

Data types

GloveFrame

Every iteration of GloveStream yields one GloveFrame:

Field Type Description
ts float Host timestamp (seconds since epoch) when the frame arrived
side str "right" or "left"
group str "hand" or "arm"
node_id int PCB node number on the glove
seq int Packet counter 0–65535, wraps around
sensors list[Sensor] List of sensor readings in this frame

Sensor is Union[AnglesSensor, ImuSensor, TouchSensor].


AnglesSensor

Joint angles for one finger node.

Field Type Description
finger int Finger index — 0 = thumb, 1 = index, 2 = middle, 3 = ring, 4 = pinky
com int Communication line index
samples list[AnglesSample] One or more timestamped angle readings

AnglesSample

Field Type Description
ts_us int Sensor timestamp in microseconds
angles_deg list[float] Joint angles in degrees (5 values for hand group)
if isinstance(sensor, AnglesSensor):
    latest = sensor.samples[-1]
    print(latest.angles_deg)   # e.g. [12.3, 45.1, 30.0, 5.5, 2.0]

ImuSensor

6-axis IMU (accelerometer + gyroscope) for one finger node.

Field Type Description
finger int Finger index
com int Communication line index
samples list[ImuSample] One or more timestamped IMU readings

ImuSample

Field Type Description
ts_us int Sensor timestamp in microseconds
acc tuple[int, int, int] Accelerometer x/y/z — raw i16 counts
gyro tuple[int, int, int] Gyroscope x/y/z — raw i16 counts
if isinstance(sensor, ImuSensor):
    s = sensor.samples[-1]
    print(s.acc)    # e.g. (312, -128, 16384)
    print(s.gyro)   # e.g. (5, -12, 3)

TouchSensor

6-channel capacitive touch for one finger node.

Field Type Description
finger int Finger index
com int Communication line index
ts_us int Sensor timestamp in microseconds
channels list[float] 6 values normalised 0.0–1.0
channels_raw list[int] 6 raw ADC counts 0–4095
if isinstance(sensor, TouchSensor):
    print(sensor.channels)      # [0.0, 0.82, 0.0, 0.0, 0.41, 0.0]
    print(sensor.channels_raw)  # [0, 3358, 0, 0, 1680, 0]

Type annotation helper

from digity import Sensor  # Union[AnglesSensor, ImuSensor, TouchSensor]

def process(sensor: Sensor) -> None:
    if isinstance(sensor, AnglesSensor):
        ...

Common patterns

Record data to CSV

import csv
from digity import GloveStream, AnglesSensor

with GloveStream() as stream, open("recording.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["ts", "finger", "a0", "a1", "a2", "a3", "a4"])

    for frame in stream:
        for sensor in frame.sensors:
            if isinstance(sensor, AnglesSensor):
                a = sensor.samples[-1].angles_deg
                writer.writerow([frame.ts, sensor.finger] + a)

Detect a closed fist

from digity import GloveStream, AnglesSensor

with GloveStream() as stream:
    finger_angles = {}

    for frame in stream:
        for sensor in frame.sensors:
            if isinstance(sensor, AnglesSensor):
                finger_angles[sensor.finger] = sensor.samples[-1].angles_deg

        if len(finger_angles) == 5:
            avg_flex = sum(finger_angles[f][1] for f in range(1, 5)) / 4
            if avg_flex > 60:
                print("Fist detected!")

Read a saved recording

import json
from pathlib import Path

session_dir = Path("~/digity-recordings/admin/user01_grasp_2025-04-22T10-30-00").expanduser()
meta = json.loads((session_dir / "meta.json").read_text())
print(f"Duration: {meta['duration_s']:.1f}s  Frames: {meta['frame_count']}")

with open(session_dir / "recording.jsonl") as f:
    for line in f:
        frame = json.loads(line)
        print(frame["ts"], frame["side"])

Error handling

from digity import GloveStream, GloveNotFoundError

try:
    with GloveStream() as stream:
        for frame in stream:
            ...
except GloveNotFoundError:
    print("Glove not found — check the USB cable")
except KeyboardInterrupt:
    pass

GloveNotFoundError is raised at connection time if the glove port cannot be found.


Real-time dashboard (digity[viz])

digity[viz] ships a full local dashboard that visualises live hand data in 3-D, records labelled sessions, and serves as a local relay for remote agents — no internet required.

pip install "digity[viz]"

digity-viz              # opens as a desktop window
digity-viz --port COM3  # explicit serial port
digity-viz --browser    # open in system browser instead

Or from Python:

import digity.viz
digity.viz.start()

The dashboard runs at http://localhost:5001/chiros/ and opens automatically.

Dashboard pages:

Page Description
Viewer Live 3-D hand model with real-time joint angles
Record Start/stop sessions, download .jsonl files, manage recordings
Setup Recordings folder, agent token, update checker

Session recordings are saved to ~/digity-recordings/ by default. Each session creates a directory containing meta.json (duration, frame count, task label) and recording.jsonl (one JSON frame per line at ~50 Hz).


Agent mode (digity[agent])

Stream the glove directly to the cloud dashboard — no separate agent.exe needed.

pip install "digity[agent]"

# copy your token from the dashboard → Setup page
digity-agent --token YOUR_TOKEN

# or via the digity-viz command
digity-viz --agent --token YOUR_TOKEN

# explicit port or custom server URL
digity-agent --token YOUR_TOKEN --port COM3
digity-agent --token YOUR_TOKEN --url https://app.digity.de/chiros

The agent auto-detects the glove, connects to the server, and streams frames in real time. It reconnects automatically if either connection drops.

If digity-agent is not on PATH (common with Windows Store Python), use:

python -m digity.viz --agent --token YOUR_TOKEN

Publish over the network (ZMQ)

GlovePublisher broadcasts frames over a ZMQ PUB socket so multiple processes or machines can subscribe to the same glove.

from digity import GlovePublisher

# Standalone — blocks until Ctrl+C
with GlovePublisher() as pub:
    pub.run()

# Embedded in a custom loop
from digity import GloveStream, GlovePublisher

with GlovePublisher() as pub, GloveStream() as stream:
    for frame in stream:
        pub.publish(frame)
        # ... your processing here
GlovePublisher(
    zmq_port=5555,     # port to bind
    bind="tcp://*",    # bind address
)
Method Description
start() Open ZMQ PUB socket. Returns self.
stop() Close socket.
publish(frame) Publish one frame. Non-blocking, thread-safe.
run(port, baud) Read from serial and publish. Blocks until Ctrl+C.

Remote ZMQ receive

On any machine that can reach the publisher over the network:

from digity import GloveStream

with GloveStream(host="192.168.1.10") as stream:        # default port 5555
    for frame in stream:
        print(frame.side, frame.sensors)

# Custom ZMQ port
with GloveStream(host="192.168.1.10", zmq_port=5556) as stream:
    ...

No USB connection is needed on the receiving machine.


Connection speed

The glove streams at approximately 50 Hz. Each for frame in stream call blocks until the next frame arrives (up to 1 second timeout before checking for errors).

The SDK uses a background thread and an internal queue so your processing code never stalls the serial buffer. If your code is slower than 50 Hz, frames are dropped to keep the stream live rather than accumulating memory.


Updating

pip install --upgrade digity
pip install --upgrade "digity[viz]"    # if using the dashboard
pip install --upgrade "digity[agent]"  # if using agent mode

License

© Digity. All rights reserved.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

digity-0.2.3.tar.gz (2.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

digity-0.2.3-py3-none-any.whl (2.1 MB view details)

Uploaded Python 3

File details

Details for the file digity-0.2.3.tar.gz.

File metadata

  • Download URL: digity-0.2.3.tar.gz
  • Upload date:
  • Size: 2.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for digity-0.2.3.tar.gz
Algorithm Hash digest
SHA256 d0a7e550b15b51eb75b1782c93aee9a8306d3f26af0aed922a60b73d59bafc6f
MD5 03e357460cbd24ca9ad9ae060a7b9c68
BLAKE2b-256 96ebecc7ae26d5f1c620c23e4bea8c90e8a4af99f2423fff708093dfd0f58abb

See more details on using hashes here.

File details

Details for the file digity-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: digity-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 2.1 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for digity-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 eb4a11cd29c7c83eb8138e5b9065925cdf20bae52f0f04704c0d14947104d8ad
MD5 24e3951683cf15541fd6a070bb3463f3
BLAKE2b-256 f9a1aed8adf3397589426cf29ce6e681669576309d80d2afb48178e1c8f6a66d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page