Pure-Python Modbus TCP driver for the Inspire Robots RH56DFTP dexterous hand.
Project description
inspire_rh56dftp
Pure-Python Modbus TCP driver for the Inspire Robots RH56DFTP dexterous hand.
The official software stack ships only as a ROS package, which is heavy if all you want is to send 6 register values to a hand on the LAN. This package is just the wire protocol — three files, stdlib-only — plus a browser debug UI behind an optional dependency.
Manual: All register addresses, frame layouts and tactile map come from the official Inspire Robots RH56DFTP user manual PRJ-02-TS-U-010 V1.0.0 (Dec 2024). Section numbers (
§2.6.20etc.) in this codebase reference that document.
Install
pip install inspire_rh56dftp # core driver only (no deps)
pip install inspire_rh56dftp[viz] # + browser debug UI
Quick start
from inspire_rh56dftp import InspireHand
hand = InspireHand(ip="192.168.123.211")
hand.send_raw([0, 0, 0, 0, 0, 1000]) # close fist (thumb across palm)
hand.send_raw([1000, 1000, 1000, 1000, 1000, 0]) # open hand
hand.close()
Values are in DOF order: [pinky, ring, middle, index, thumb_bend, thumb_rotation],
each 0..1000 (raw register units, 1000 = fully extended for the 4 fingers
and thumb_bend; for thumb_rotation, 0 = retracted, 1000 = across palm).
Hardware setup
The RH56DFTP ships with factory-default IP 192.168.123.211 and exposes
Modbus TCP on port 6000. It does not run a DHCP server, so the host
needs a static IP in the same subnet:
sudo ip addr add 192.168.123.50/24 dev <eth> # one-shot
# or persist via NetworkManager:
sudo nmcli con add type ethernet ifname <eth> con-name inspire-hand \
ipv4.addresses 192.168.123.50/24 ipv4.method manual ipv6.method disabled
Verify:
ping -c 3 192.168.123.211
Two layers of API
InspireHand (client.py) InspireHandSender (scheduler.py)
────────────────────── ──────────────────────────────────
synchronous: background-thread wrapper around an
every call blocks until InspireHand. Runs writes (control)
the hand acks and reads (tactile) on independent
clocks sharing one TCP socket.
use for: scripts, use for: real-time control loops
one-shot moves, that also need continuous tactile
sequenced demos data
Both share the same lock on the underlying socket, so reads and writes never race on the wire even when issued from multiple threads.
Reading registers directly
The high-level API exposes send_raw() (writes ANGLE_SET) and the scheduler's
latest_tactile. For everything else (joint feedback, force, custom ranges):
from inspire_rh56dftp import InspireHand, registers
hand = InspireHand(ip="192.168.123.211")
actual_angles = hand.read_holding(registers.ANGLE_ACT, 6) # 6 ints
forces = hand.read_holding(registers.FORCE_ACT, 6)
registers.py is a pure transcription of manual §2.6 — see the file or
import it for the full address map.
Browser debug UI
pip install inspire_rh56dftp[viz]
python -m inspire_rh56dftp.viz 192.168.123.211 # IP defaults to factory
# open http://127.0.0.1:8765
The page has 6 horizontal sliders (one per DOF) and the live tactile heatmap laid out anatomically over a right-hand silhouette. Drag a slider to set the target; the orange "ack" number shows what the hand reports back. Useful for verifying wiring, finding mechanical stops, and watching tactile cells light up while you poke them.
The UI talks to the server over a single bidirectional WebSocket and works with the hand absent — sliders just won't move anything until you click Reconnect.
Modbus protocol quirks (read this before writing your own client)
These cost real time to discover; documenting them here so the next person doesn't repeat the work.
1. The hand mishandles transaction IDs
The RH56DFTP firmware has an off-by-one bug in the Modbus TX-ID echo back.
Strict clients (pymodbus, etc.) reject the bad IDs as "unexpected response"
and eventually close the connection. This package uses a raw socket and
deliberately does not validate the TX-ID — it just consumes the response
bytes and moves on.
2. Every write must be followed by a synchronous ACK read
The hand always sends a 12-byte ACK after a write. If you don't read it:
- Bytes pile up in the hand's TCP send buffer.
- Kernel flow control kicks in and blocks the hand's
send(). - The hand's main loop blocks on that send.
- The hand stops responding to new commands, even though your own
sendall()keeps reporting success.
Symptom: control feels great for the first few seconds, then the hand freezes — and your client has no idea anything is wrong because writes look fine. Always drain the ACK. We don't need to validate it (see #1); draining it is what matters.
3. Tactile data is big-endian, despite what the manual says
Manual §2.6.20 claims tactile cells are little-endian ("low byte first"). Empirical test on real hardware shows it's big-endian. Little-endian interpretation gives values up to 23000+; big-endian gives the documented 0..4095 range. Likely a doc issue copied from the basic RH56 RS485 protocol (where the data IS little-endian) — the Modbus TCP gateway converts to BE on the wire but the docs weren't updated.
4. The hand needs a brief grace period between TCP reconnects
Closing and immediately reopening a TCP connection sometimes yields a
connect that succeeds but then immediately drops. A 300 ms sleep between
close() and the next connect() reliably avoids this. Already handled
inside InspireHand._reconnect().
Register cheat-sheet (manual §2.6)
| Address | Block | RW | Width | Notes |
|---|---|---|---|---|
| 1486 | ANGLE_SET |
W | 6 regs | Target angle, 0..1000 per DOF |
| 1522 | SPEED_SET |
W | 6 regs | Motor speed, 0..1000 per DOF |
| 1546 | ANGLE_ACT |
R | 6 regs | Actual angle |
| 1582 | FORCE_ACT |
R | 6 regs | Gripping force per DOF |
| 3000+ | tactile (17 regions) | R | 1185 regs total | See registers.TACTILE_REGIONS |
All blocks are contiguous and follow DOF_ORDER = [pinky, ring, middle, index, thumb_bend, thumb_rotation].
Tactile sensor map (manual §2.6.20)
17 regions, 1185 cells total, each cell is one 16-bit register valued 0..4095.
| Finger | Tip | Nail/middle | Pad |
|---|---|---|---|
| Index | 3×3 | 12×8 | 10×8 |
| Middle | 3×3 | 12×8 | 10×8 |
| Ring | 3×3 | 12×8 | 10×8 |
| Pinky | 3×3 | 12×8 | 10×8 |
| Thumb | 3×3 | 12×8 (nail) + 3×3 (middle) | 12×8 |
| Palm | — | — | 8×14 |
The scheduler reads all 17 regions in 11 batched FC=03 requests (35% fewer
round-trips than naïve per-region reads). Modbus FC=03 limits each request
to 125 registers — the batching plan in registers.TACTILE_READ_BATCHES
respects that bound.
Examples
| File | What it shows |
|---|---|
examples/01_basic_control.py |
Open/close cycle — minimal InspireHand usage |
examples/02_tactile_read.py |
Bypass scheduler, read one tactile region directly |
examples/03_mixed_mode.py |
Background scheduler doing control + tactile at once |
Tested against
- RH56DFTP, factory firmware, IP
192.168.123.211 - Python 3.10–3.13 on Linux (Ubuntu 22.04, JetPack 6.2.2 on Jetson Orin Nano)
- Direct Ethernet cable; static host IP in
192.168.123.0/24
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file inspire_rh56dftp-0.1.0.tar.gz.
File metadata
- Download URL: inspire_rh56dftp-0.1.0.tar.gz
- Upload date:
- Size: 20.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6a3bb0c171c3153b17413e1fb00fbd451d9b1067ad046160797558c5456e9ef
|
|
| MD5 |
6469e97aee6396382e2c3058c6308de1
|
|
| BLAKE2b-256 |
daa338a7e00ac36238e5a70e624bc7c5240e8906721fdb167b6560e38f797183
|
File details
Details for the file inspire_rh56dftp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: inspire_rh56dftp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
644cce5ac40f9cf39d21a50a36dad8aaec23858465fd9f64aa989c550835902c
|
|
| MD5 |
5804bb9d78e7fda30f0ae1ba58881b5e
|
|
| BLAKE2b-256 |
06de84ab46eb16aa8ff46635a7b070aed00579ef74b0fcb1a30d5fd4021cec4c
|