DexHand Protocol

The DexHand protocol implements the CANFD communication layer between host and hand hardware.

Core Protocol

Message Types

class pyzlg_dexhand.dexhand_protocol.MessageType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

CAN message type identifiers and their base addresses

CONFIG_COMMAND = 0
CONFIG_RESPONSE = 128
ERROR_MESSAGE = 1536
INVALID = 4096
MOTION_COMMAND = 256
MOTION_FEEDBACK = 384
UNKNOWN = 4097

Board IDs

class pyzlg_dexhand.dexhand_protocol.BoardID(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Base ID values for left and right hand boards

LEFT_HAND_BASE = 1
RIGHT_HAND_BASE = 7

Commands

Command Types

class pyzlg_dexhand.dexhand_protocol.commands.CommandType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Command type identifiers

CLEAR_ERROR = 164
CONFIG_FEEDBACK = 116
MOTOR_COMMAND = 0

Control Modes

class pyzlg_dexhand.dexhand_protocol.commands.ControlMode(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Motor control modes

CASCADED_PID = 68
CURRENT = 17
HALL_POSITION = 51
PROTECT_HALL_POSITION = 85
SPEED = 34
ZERO_TORQUE = 0

Feedback Modes

class pyzlg_dexhand.dexhand_protocol.commands.FeedbackMode(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Feedback operation modes

ON_CHANGE = 3
PERIODIC = 1
QUERY = 2

Command Classes

class pyzlg_dexhand.dexhand_protocol.commands.MotorCommand(control_mode, motor_enable, motor1_pos, motor2_pos)[source]

Command for a pair of motors

control_mode: ControlMode
motor1_pos: int
motor2_pos: int
motor_enable: int
class pyzlg_dexhand.dexhand_protocol.commands.ClearErrorCommand[source]

Command to clear error state

class pyzlg_dexhand.dexhand_protocol.commands.FeedbackConfigCommand(mode, period_ms, enable)[source]

Command to configure feedback behavior

enable: bool
mode: FeedbackMode
period_ms: int

Command Encoding

pyzlg_dexhand.dexhand_protocol.commands.encode_command(command)[source]

Encode a command for transmission

Parameters:

command (Union[MotorCommand, ClearErrorCommand, FeedbackConfigCommand]) – Command to encode

Return type:

tuple[MessageType, bytes]

Returns:

Tuple of (message_type, encoded_bytes)

Raises:

ValueError – If command parameters are invalid

Messages

Error Types

class pyzlg_dexhand.dexhand_protocol.messages.BoardError(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Motor error status

BOTH_MOTORS_ERROR = 3
MOTOR1_ERROR = 1
MOTOR2_ERROR = 2
class pyzlg_dexhand.dexhand_protocol.messages.ErrorCode(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Error codes from protocol

MOTOR1_CURRENT_OVERLOAD = 1
MOTOR1_HALL_ERROR = 2
MOTOR1_PARAM_ERROR = 8
MOTOR1_STALL_ERROR = 4
MOTOR2_CURRENT_OVERLOAD = 16
MOTOR2_HALL_ERROR = 32
MOTOR2_PARAM_ERROR = 128
MOTOR2_STALL_ERROR = 64

Feedback Classes

class pyzlg_dexhand.dexhand_protocol.messages.MotorFeedback(current, velocity, position, angle)[source]

Feedback data for a single motor

angle: float
current: int
position: int
velocity: int
class pyzlg_dexhand.dexhand_protocol.messages.TactileFeedback(normal_force, normal_force_delta, tangential_force, tangential_force_delta, direction, proximity, temperature)[source]

Feedback from tactile sensor

direction: int
normal_force: float
normal_force_delta: int
proximity: int
tangential_force: float
tangential_force_delta: int
temperature: int
class pyzlg_dexhand.dexhand_protocol.messages.BoardFeedback(motor1, motor2, position_sensor1, position_sensor2, tactile=None, encoder1=0, encoder2=0)[source]

Complete feedback from a motor control board

encoder1: int = 0
encoder2: int = 0
motor1: MotorFeedback
motor2: MotorFeedback
position_sensor1: float
position_sensor2: float
tactile: Optional[TactileFeedback] = None
class pyzlg_dexhand.dexhand_protocol.messages.ErrorInfo(error_type, error_code, description)[source]

Error information from a board

description: str
error_code: int
error_type: BoardError

Message Processing

class pyzlg_dexhand.dexhand_protocol.messages.ProcessedMessage(sender_id, msg_type, feedback=None, error=None)[source]

Processed message from board

error: Optional[ErrorInfo] = None
feedback: Optional[BoardFeedback] = None
msg_type: MessageType
sender_id: int
pyzlg_dexhand.dexhand_protocol.messages.process_message(can_id, data)[source]

Process a received CAN message

Parameters:
  • can_id (int) – CAN ID of the message

  • data (bytes) – Raw message bytes

Return type:

ProcessedMessage

Returns:

ProcessedMessage containing decoded information

Raises:

ValueError – If message cannot be decoded

Protocol Specification

Message Structure

The protocol uses standard CANFD frames with extended identifiers. Message IDs are constructed as:

  • Base message type (e.g., 0x100 for motion commands)

  • Board ID offset (0x01-0x06 for left hand, 0x07-0x0C for right hand)

Command Format

Motor Command:

Byte 0: Control mode
Byte 1: Motor enable flags (0x01=motor1, 0x02=motor2, 0x03=both)
Byte 2-3: Motor 1 position (little endian)
Byte 4-5: Motor 2 position (little endian)

Clear Error Command:

Byte 0: 0x03 (Command prefix)
Byte 1: 0xA4 (Clear error command)

Feedback Config Command:

Byte 0: 0x03 (Command prefix)
Byte 1: 0x74 (Feedback config command)
Byte 2: Feedback mode
Byte 3: Period (10ms units)
Byte 4: Enable flag

Example Usage

Command Generation

from pyzlg_dexhand.dexhand_protocol.commands import (
    MotorCommand,
    ControlMode,
    encode_command
)

# Create motor command
command = MotorCommand(
    control_mode=ControlMode.CASCADED_PID,
    motor_enable=0x03,  # Both motors enabled
    motor1_pos=1000,
    motor2_pos=-2000
)

# Encode for transmission
msg_type, data = encode_command(command)

Message Processing

from pyzlg_dexhand.dexhand_protocol.messages import process_message

# Process received message
can_id = 0x181  # Example: Board 1 feedback
data = b'...'   # Raw message data

result = process_message(can_id, data)

if result.msg_type == MessageType.MOTION_FEEDBACK:
    print(f"Motor 1 position: {result.feedback.motor1.position}")
    print(f"Motor 2 position: {result.feedback.motor2.position}")
elif result.msg_type == MessageType.ERROR_MESSAGE:
    print(f"Error: {result.error.description}")

Notes

Message Flow

  1. Host sends commands using appropriate message type

  2. Hand processes command and returns feedback/response

  3. Any errors are reported via error messages

Error Handling

  1. Check message type from CAN ID

  2. Process messages according to type

  3. Handle errors by: - Parsing error information - Clearing errors when safe - Resuming normal operation