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 directly into your Python code 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.


Requirements

  • Python 3.9 or later
  • The Digity exohand connected via USB

Installation

# SDK only
pip install digity

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

Real-time dashboard

digity[viz] includes a full web-based dashboard that visualises live hand data, records sessions, and relays data from a remote agent — all locally, no internet required.

pip install "digity[viz]"

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

Or launch from Python:

import digity.viz
digity.viz.start()

The dashboard runs on http://localhost:5001/chiros/ and opens automatically. No login needed — it is intended for single-user local use.


Stream as Agent (cloud relay)

If you have access to the cloud dashboard (app.digity.de) and want to stream the glove from your local PC without downloading agent.exe, you can use the SDK directly as an agent.

pip install "digity[agent]"

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

# or use the digity-viz command with --agent flag
digity-viz --agent --token YOUR_TOKEN

# explicit serial 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 connects to the cloud server, auto-detects the glove, and streams frames in real time. It reconnects automatically if the glove or server connection drops.


Checking for updates

From the dashboard Setup page, click Check for updates to see if a newer version is available on PyPI.

To update from the terminal:

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

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.


Specifying the USB port

The SDK auto-detects the glove port on all platforms. If auto-detection fails, pass the port explicitly:

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

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

AnglesSensor

Joint angles for one finger node.

Field Type Description
finger int Finger index (0=thumb, 1=index, …, 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 a file

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[0] > 60:
                print("Fist detected!")

Stop the stream 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)

Remote mode (ZMQ)

If the glove is connected to a remote machine running glove-core, you can receive frames over the network:

GloveStream(host="192.168.1.10")          # default port 5555
GloveStream(host="192.168.1.10", zmq_port=5556)

No USB connection is needed on the client machine in this mode.


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.


Connection speed

The glove streams at approximately 50 Hz. Each call to for frame in stream 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 that your processing code never blocks the serial buffer. If your code is slower than 50 Hz, frames are dropped to keep the stream live rather than accumulating memory.


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.0.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.0-py3-none-any.whl (2.1 MB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: digity-0.2.0.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.0.tar.gz
Algorithm Hash digest
SHA256 a011501108025e7e50512afaf4b71c9f0647e8e465077849f2d860377418e7e3
MD5 a91ac6d46b711020e8cec31e1a804800
BLAKE2b-256 e59afb7cbb6ab32e24766606a7aa65b020b76050de8ee7a777c3a845f8fdbf2f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: digity-0.2.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6cc465c83d793706ad5ee71b3116fe4396cc2d05365e67c3d03074687b0f3967
MD5 a5612983077c7c85d15901445fcf26db
BLAKE2b-256 4f284e81b3f3d2b620695b40b64c6b336fe74793befe6b7521a585ac771cd983

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