Gripper version of the Grabette data collection system. gRPC motor+camera service for the gripper, running on a Raspberry Pi Zero 2W.
Streams camera frames (JPEG) at ~10Hz synchronized with motor positions, and accepts motor commands for two Feetech STS3215 servos over the network.
- Raspberry Pi Zero 2W
- RPi camera module (1296x972, fisheye lens)
- Two Feetech STS3215 servos on
/dev/serial0(baudrate 1000000, IDs 1 and 2)
uv sync --extra dev
uv run python generate_proto.py # only needed if you modify gripper.proto
uv run python main.pyRequires system Python 3.11 with libcamera and numpy installed at the system level.
The Pi Zero 2W has two UARTs: PL011 (ttyAMA0, full-featured, reliable at 1Mbaud) and mini UART (ttyS0, clock-dependent, unreliable at high baudrates). By default Bluetooth uses PL011 and GPIO gets the mini UART — this must be swapped for reliable servo communication.
Edit /boot/firmware/config.txt and add (or copy from config/config.txt):
dtoverlay=miniuart-bt
enable_uart=1Edit /boot/firmware/cmdline.txt and remove console=serial0,115200 (the kernel serial console conflicts with the servo bus). The file must remain a single line — do not add line breaks or the Pi will fail to boot.
Before:
console=serial0,115200 console=tty1 root=PARTUUID=... rootfstype=ext4 ...
After:
console=tty1 root=PARTUUID=... rootfstype=ext4 ...
Reboot, then verify:
ls -l /dev/serial0 # should point to ttyAMA0Add your user to the dialout group for serial port access:
sudo usermod -aG dialout raspLog out and back in (or reboot) for the group change to take effect.
sudo apt install libcap-dev
uv venv --python /usr/bin/python3 --system-site-packages
uv sync --extra rpi --no-install-package numpy
uv run python main.pyAll settings via environment variables with GRIPPER_ prefix:
| Variable | Default | Description |
|---|---|---|
GRIPPER_HOST |
0.0.0.0 |
Server bind address |
GRIPPER_PORT |
50051 |
gRPC port |
GRIPPER_MOTOR_PORT |
/dev/serial0 |
Serial port for servos |
GRIPPER_MOTOR_BAUDRATE |
1000000 |
Serial baudrate |
GRIPPER_MOTOR_ID_1 |
1 |
First servo ID |
GRIPPER_MOTOR_ID_2 |
2 |
Second servo ID |
GRIPPER_JPEG_QUALITY |
70 |
JPEG compression quality |
GRIPPER_LOG_LEVEL |
INFO |
Logging level |
from gripette.client import GripperClient
with GripperClient("192.168.1.36:50051") as g:
print(g.ping())
g.torque_on()
g.move(-0.5, -1.0) # goal positions in radians
for frame in g.stream(): # 10Hz camera + motor state
print(f"Frame {frame.sequence}: {len(frame.jpeg_data)}B, "
f"motors=({frame.motor1:.2f}, {frame.motor2:.2f})")
break
m1, m2 = g.read_motors() # lightweight, no camera
g.torque_off()Reads angle sensors from the grabette glove (Pi 4) and forwards them as motor commands to the gripper:
uv run python scripts/teleop_bridge.py --dry-run # preview without moving motors
uv run python scripts/teleop_bridge.py # live controlRequires the grabette service running on 192.168.1.35:8000.
Sends a 1Hz sinusoidal command and records feedback positions for delay analysis:
uv run python scripts/sinus_test.py
# Outputs sinus_test.csv and sinus_test.pngMeasures stream framerate and saves a sample frame:
uv run python scripts/camera_test.py
# Outputs camera_test.jpgsudo cp systemd/gripette.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable gripette
sudo systemctl start gripetteA standalone BLE GATT service allows configuring WiFi credentials on the enclosed Pi Zero 2W without SSH or a screen. A phone or laptop connects via Bluetooth Low Energy, authenticates with a PIN, and sends WiFi credentials.
sudo apt install python3-dbus python3-gi # system deps (usually pre-installed)
sudo cp systemd/gripette-bluetooth.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable gripette-bluetooth
sudo systemctl start gripette-bluetoothRuns as root (required by BlueZ DBus GATT registration). PIN is configurable via GRIPPER_BT_PIN env var (default: 00000).
BLE commands (written as UTF-8 to the COMMAND characteristic):
| Command | Response | Description |
|---|---|---|
PING |
PONG |
Health check |
PIN_xxxxx |
OK: Connected / ERROR: Incorrect PIN |
Authenticate (required before WIFI/WIFI_RESET) |
WIFI ssid password |
OK: Connecting to <ssid> / ERROR: ... |
Connect to WiFi via nmcli |
WIFI_RESET |
OK: WiFi connections cleared |
Delete all saved WiFi networks |
Network status is also readable from a dedicated BLE characteristic (auto-updates every 10s).
Web Bluetooth client: open the Gripette BT Tool in Chrome/Edge on a phone or laptop (requires HTTPS — served via GitHub Pages from docs/index.html).
See docs/bluetooth_setup.md for the full setup guide (BlueZ configuration, troubleshooting, etc.).
The gRPC service contract is defined in proto/gripper.proto. To regenerate the Python files after modifying it:
uv sync --extra dev
uv run python generate_proto.pyGenerated files in gripette/proto/ are committed to the repository.