Skip to content

C++ SDK - Revo2 Dexterous Hand

System Requirements

  • Linux: Ubuntu 20.04/22.04 LTS (x86_64/aarch64), glibc ≥ 2.31
  • macOS: 10.15+
  • Windows: 10/11

Quick Start

Clone Repository

shell
# Use HTTPS method
git clone https://github.com/BrainCoTech/stark-serialport-example.git

# Or use SSH method
git clone git@github.com:BrainCoTech/stark-serialport-example.git

Installation & Configuration

shell
# Install dependencies
# Required: build-essential, cmake, make, gcc, g++, etc.

# Install SDK dependencies
./download-lib.sh

# Configure device permissions
# In Linux, serial devices (e.g., /dev/ttyUSB0) typically belong to dialout or tty group
# Only group members can access the device, for example:
# crw-rw---- 1 root dialout 188, 0 Aug 14 12:00 /dev/ttyUSB0

# Add current user to dialout group
sudo usermod -aG dialout $USER
# Note: You need to log out and log back in for this to take effect

# Build and run example (unified cross-platform directory)
cd c && make
cd demo && ./hand_demo.exe    # Auto-detect device and run
shell
# Install Xcode or Xcode Command Line Tools
xcode-select --install

# Install SDK dependencies
./download-lib.sh

# Build and run example (unified cross-platform directory)
cd c && make
cd demo && ./hand_demo.exe    # Auto-detect device and run
shell
# Requires MinGW or MSVC build environment

# Install SDK dependencies
./download-lib.sh

# Build and run example (unified cross-platform directory)
cd c && make
cd demo && hand_demo.exe      # Auto-detect device and run

Example Code

The SDK provides comprehensive example code covering different communication protocols and application scenarios.

Unified cross-platform examples with auto-detection and multi-protocol support.

shell
cd c
make
cd demo
./hand_demo.exe       # Auto-detect device and run demo
ExampleDescription
hand_demo.cppComprehensive demo (8 modes: position/speed/current control, action sequences, touch sensors, etc.)
hand_monitor.cppReal-time data monitor (motor status, touch data)
hand_dfu.cppFirmware upgrade
auto_detect.cppDevice auto-detection

Supported initialization methods:

shell
./hand_demo.exe                                          # Auto-detect (recommended)
./hand_demo.exe -m /dev/ttyUSB0 460800 127               # Modbus
./hand_demo.exe -c /dev/cu.usbmodem14201 1000000 1       # CAN 2.0 (ZQWL)
./hand_demo.exe -f /dev/cu.usbmodem14201 1000000 5000000 127  # CANFD (ZQWL)
./hand_demo.exe -s can0 1                                # SocketCAN (Linux)

Modbus-RTU Protocol

Single/Dual Hand Control

Tactile Sensor

Firmware Upgrade

CANFD Protocol

Single Hand Control Examples

Firmware Upgrade

EtherCAT Protocol

API Reference

Initialize Configuration

c
#ifndef SETUP_H
#define SETUP_H

#include <stdint.h>

/**
 * @brief Log level enumeration
 * Defines supported log output levels, higher levels show more detailed log information
 */
enum LogLevel : uint8_t {
  LOG_LEVEL_ERROR = 0,  ///< Error level: only shows error messages
  LOG_LEVEL_WARN = 1,   ///< Warning level: shows warning and above level messages
  LOG_LEVEL_INFO = 2,   ///< Info level: shows general information and above levels
  LOG_LEVEL_DEBUG = 3,  ///< Debug level: shows debug information and above levels
  LOG_LEVEL_TRACE = 4,  ///< Trace level: shows all levels of detailed trace information
};

/**
 * @brief Communication protocol type enumeration
 * Defines supported communication protocol types
 */
enum StarkProtocolType : uint8_t {
  STARK_PROTOCOL_TYPE_MODBUS = 1,    ///< Modbus protocol: serial communication protocol
  STARK_PROTOCOL_TYPE_CAN = 2,       ///< CAN 2.0 protocol: Controller Area Network (1Mbps)
  STARK_PROTOCOL_TYPE_CAN_FD = 3,    ///< CAN FD protocol: CAN with Flexible Data-Rate (1Mbps arb, 5Mbps data)
  STARK_PROTOCOL_TYPE_ETHER_CAT = 4, ///< EtherCAT protocol: Ethernet fieldbus (Linux only)
  STARK_PROTOCOL_TYPE_PROTOBUF = 5,  ///< Protobuf protocol: Legacy protocol (Revo1 only)
};

/**
 * @brief Initialize SDK logging
 * Sets log output level for SDK operations
 * @param log_level Log output level, default is LOG_LEVEL_INFO
 * @note Call this function before using other APIs to enable logging
 * @example
 * init_logging(LOG_LEVEL_INFO);
 */
void init_logging(LogLevel log_level);

/**
 * @brief List all available serial ports
 * Prints available serial ports to stdout
 */
void list_available_ports();

#endif // SETUP_H

Auto-Detect Device

c
#ifndef AUTO_DETECT_H
#define AUTO_DETECT_H

#include <stdint.h>
#include <stdbool.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;
typedef struct CDeviceConfig CDeviceConfig;
typedef struct CDetectedDevice CDetectedDevice;
typedef struct CDetectedDeviceList CDetectedDeviceList;
typedef struct ZqwlDeviceList ZqwlDeviceList;
typedef struct CZqwlDeviceInfo CZqwlDeviceInfo;

enum StarkProtocolType : uint8_t {
  STARK_PROTOCOL_TYPE_MODBUS = 1,
  STARK_PROTOCOL_TYPE_CAN = 2,
  STARK_PROTOCOL_TYPE_CAN_FD = 3,
  STARK_PROTOCOL_TYPE_ETHER_CAT = 4,
  STARK_PROTOCOL_TYPE_PROTOBUF = 5,
};

enum StarkHardwareType : uint8_t {
  STARK_HARDWARE_TYPE_REVO1_PROTOBUF = 0,
  STARK_HARDWARE_TYPE_REVO1_BASIC = 1,
  STARK_HARDWARE_TYPE_REVO1_TOUCH = 2,
  STARK_HARDWARE_TYPE_REVO1_ADVANCED = 3,
  STARK_HARDWARE_TYPE_REVO1_ADVANCED_TOUCH = 4,
  STARK_HARDWARE_TYPE_REVO2_BASIC = 5,
  STARK_HARDWARE_TYPE_REVO2_TOUCH = 6,
  STARK_HARDWARE_TYPE_REVO2_TOUCH_PRESSURE = 7,
};

enum SkuType : uint8_t {
  SKU_TYPE_MEDIUM_RIGHT = 1,
  SKU_TYPE_MEDIUM_LEFT = 2,
  SKU_TYPE_SMALL_RIGHT = 3,
  SKU_TYPE_SMALL_LEFT = 4,
};

/**
 * @brief Detected device information (C interface)
 */
struct CDetectedDevice {
  StarkProtocolType protocol;      ///< Protocol type
  char *port_name;                 ///< Port name (serial port or CAN adapter port)
  uint8_t slave_id;                ///< Slave ID on the bus
  uint32_t baudrate;               ///< Baudrate (for Modbus/serial, or CAN arbitration)
  uint32_t data_baudrate;          ///< Data baudrate (for CANFD only, 0 for others)
  StarkHardwareType hardware_type; ///< Hardware type
  SkuType sku_type;                ///< SKU type
  char *serial_number;             ///< Serial number (NULL if not detected)
  char *firmware_version;          ///< Firmware version (NULL if not detected)
};

/**
 * @brief Detected device list (C interface)
 */
struct CDetectedDeviceList {
  CDetectedDevice *devices;  ///< Device array pointer
  size_t count;              ///< Device count
};

/**
 * @brief ZQWL device info struct (C interface)
 */
struct CZqwlDeviceInfo {
  uint16_t device_type;    ///< Device type
  char *port_name;         ///< Serial port name
  uint16_t vid;            ///< USB VID
  uint16_t pid;            ///< USB PID
  bool supports_canfd;     ///< Whether CANFD is supported
  uint8_t channel_count;   ///< Number of channels
};

/**
 * @brief ZQWL device list struct (C interface)
 */
struct ZqwlDeviceList {
  CZqwlDeviceInfo *devices;  ///< Device array pointer
  size_t count;              ///< Device count
};

// ============================================================================
// Recommended: High-Level Auto-Detect API (Supports All Protocols)
// ============================================================================

/**
 * @brief Auto-detect Stark devices across all protocols (Recommended)
 * 
 * Scans for devices in priority order:
 * 1. ZQWL CANFD (Revo2: IDs 0x7E, 0x7F)
 * 2. ZQWL CAN 2.0 (Revo1/Revo2: IDs 1, 2)
 * 3. Modbus/RS485 (IDs 0x7E, 0x7F, 1, 2, 10)
 * 
 * @param scan_all If true, scan for all devices. If false, stop at first found.
 * @param port Optional port name to scan. If NULL, scans all available ports.
 * @param protocol Protocol to use (0=auto, 1=Modbus, 2=Can, 3=CanFd)
 * @return Pointer to CDetectedDeviceList. Call free_detected_device_list to free.
 * @retval Empty list (count=0) if no devices found.
 * 
 * @example
 * // Simple auto-detect (recommended)
 * CDetectedDeviceList *list = stark_auto_detect(false, NULL, 0);
 * if (list && list->count > 0) {
 *     CDetectedDevice *dev = &list->devices[0];
 *     printf("Found: %s on %s, ID: %d\n", 
 *            dev->protocol == STARK_PROTOCOL_TYPE_CAN_FD ? "CANFD" : "Modbus",
 *            dev->port_name, dev->slave_id);
 *     
 *     // Initialize from detected device
 *     DeviceHandler *handle = init_from_detected(dev);
 *     // ... use handle ...
 *     close_device_handler(handle, dev->protocol);
 * }
 * free_detected_device_list(list);
 */
CDetectedDeviceList *stark_auto_detect(bool scan_all, const char *port, uint8_t protocol);

/**
 * @brief Initialize device handler from detected device info (Recommended)
 * 
 * This function initializes the appropriate transport (Modbus, CAN, CANFD)
 * based on the detected device's protocol and returns a ready-to-use handler.
 * 
 * @param device Pointer to CDetectedDevice from stark_auto_detect()
 * @return Pointer to DeviceHandler on success, NULL on failure
 * 
 * @note For CAN/CANFD: Initializes ZQWL adapter automatically
 * @note For Modbus: Opens serial port with detected baudrate
 * @note Call close_device_handler() to cleanup
 */
DeviceHandler *init_from_detected(const CDetectedDevice *device);

/**
 * @brief Close device handler based on protocol type
 * 
 * Convenience function that handles cleanup for any protocol type.
 * Automatically closes ZQWL adapter for CAN/CANFD protocols.
 * 
 * @param handle Device handler to close
 * @param protocol Protocol type (from CDetectedDevice.protocol)
 */
void close_device_handler(DeviceHandler *handle, uint8_t protocol);

/**
 * @brief Free detected device list memory
 * @param list CDetectedDeviceList pointer returned by stark_auto_detect
 */
void free_detected_device_list(CDetectedDeviceList *list);

// ============================================================================
// ZQWL CAN/CANFD Adapter API
// ============================================================================

/**
 * @brief List all ZQWL USB CAN/CANFD devices
 * 
 * @return Device list pointer. Call free_zqwl_device_list to free.
 * @retval List with count=0 if no devices found.
 * 
 * @note This is a low-level API. Recommend using stark_auto_detect + init_from_detected.
 */
ZqwlDeviceList *list_zqwl_devices(void);

/**
 * @brief Free ZQWL device list memory
 * @param list ZqwlDeviceList pointer
 */
void free_zqwl_device_list(ZqwlDeviceList *list);

/**
 * @brief Initialize ZQWL CAN device (CAN 2.0 mode)
 * 
 * @param port_name Serial port name (e.g., "/dev/cu.usbmodem*" or "COM3")
 * @param arb_baudrate Arbitration baudrate (bps), typically 1000000
 * @return 0 on success, -1 on failure
 * 
 * @note This is a low-level API. Recommend using stark_auto_detect + init_from_detected.
 */
int init_zqwl_can(const char *port_name, uint32_t arb_baudrate);

/**
 * @brief Initialize ZQWL CANFD device
 * 
 * @param port_name Serial port name
 * @param arb_baudrate Arbitration baudrate (bps), typically 1000000
 * @param data_baudrate Data baudrate (bps), typically 5000000
 * @return 0 on success, -1 on failure
 * 
 * @note This is a low-level API. Recommend using stark_auto_detect + init_from_detected.
 */
int init_zqwl_canfd(const char *port_name, uint32_t arb_baudrate, uint32_t data_baudrate);

/**
 * @brief Close ZQWL CAN/CANFD device
 * 
 * @note If device was initialized with init_from_detected, use close_device_handler instead.
 */
void close_zqwl(void);

/**
 * @brief Scan CAN bus for devices
 * 
 * @param candidate_ids Pointer to array of candidate device IDs
 * @param candidate_count Number of candidate device IDs
 * @param timeout_ms Timeout in milliseconds
 * @return Found device ID (1-255), or 0 if no device found
 */
uint8_t scan_can_devices(const uint8_t *candidate_ids, size_t candidate_count, uint64_t timeout_ms);

// ============================================================================
// Legacy Auto-Detect API (Modbus Only)
// ============================================================================

/**
 * @brief Legacy device configuration structure
 * @deprecated Use CDetectedDevice instead
 */
struct CDeviceConfig {
  StarkProtocolType protocol;  ///< Protocol type
  const char *port_name;       ///< Port name
  uint32_t baudrate;           ///< Baud rate
  uint8_t slave_id;            ///< Slave device ID
};

/**
 * @brief Automatically detect a Stark device on a serial port
 * 
 * @deprecated Use stark_auto_detect + init_from_detected instead for multi-protocol support.
 * 
 * @param port Serial port name; when NULL, auto-detect ports
 * @param quick Whether to use quick detection
 * @return Pointer to CDeviceConfig; call free_device_config to free
 * @retval NULL Detection failed
 * 
 * @note IMPORTANT: Only supports Modbus-RTU protocol detection
 */
CDeviceConfig *auto_detect_device(const char *port, bool quick);

/**
 * @brief Automatically detect a Revo1 hand over Modbus-RTU
 * @deprecated Use stark_auto_detect + init_from_detected instead.
 */
CDeviceConfig *auto_detect_modbus_revo1(const char *port, bool quick);

/**
 * @brief Automatically detect a Revo2 hand over Modbus-RTU
 * @deprecated Use stark_auto_detect + init_from_detected instead.
 */
CDeviceConfig *auto_detect_modbus_revo2(const char *port, bool quick);

/**
 * @brief Free device configuration structure
 * @param config CDeviceConfig pointer
 */
void free_device_config(CDeviceConfig *config);

#endif // AUTO_DETECT_H

Device Settings

c
#ifndef DEVICE_SETTINGS_H
#define DEVICE_SETTINGS_H

#include <stdint.h>
#include <stdbool.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Set device slave ID
 * 
 * Changes the slave ID of the device. This is useful when controlling multiple
 * devices on the same bus.
 * 
 * @param handle Device handler
 * @param slave_id Current slave device ID
 * @param new_id New slave device ID to set
 * @note Default is 1, valid range 1~247; 0 is the broadcast address
 * @note Revo1 default ID is 1; Revo2 default left/right IDs are 0x7E and 0x7F
 * @note When controlling multiple devices on the same bus, assign different IDs,
 *       e.g. left=1, right=2
 * @warning Using broadcast ID 0 controls all devices on the bus; per Modbus spec,
 *          broadcast commands do not receive responses
 * @example
 * // Set left hand to ID 1, right hand to ID 2
 * stark_set_slave_id(handle, 0x7E, 1);  // Left hand
 * stark_set_slave_id(handle, 0x7F, 2);  // Right hand
 */
void stark_set_slave_id(DeviceHandler *handle, uint8_t slave_id, uint8_t new_id);

/**
 * @brief Get LED enabled state
 * @param handle Device handler
 * @param slave_id Device ID
 * @return true if LED is enabled, false otherwise
 */
bool get_led_enabled(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Get buzzer enabled state
 * @param handle Device handler
 * @param slave_id Device ID
 * @return true if buzzer is enabled, false otherwise
 */
bool get_buzzer_enabled(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Get vibration enabled state
 * @param handle Device handler
 * @param slave_id Device ID
 * @return true if vibration is enabled, false otherwise
 */
bool get_vibration_enabled(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Set LED enabled state
 * @param handle Device handler
 * @param slave_id Device ID
 * @param enabled true to enable LED, false to disable
 */
void set_led_enabled(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/**
 * @brief Set buzzer enabled state
 * @param handle Device handler
 * @param slave_id Device ID
 * @param enabled true to enable buzzer, false to disable
 */
void set_buzzer_enabled(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/**
 * @brief Set vibration enabled state
 * @param handle Device handler
 * @param slave_id Device ID
 * @param enabled true to enable vibration, false to disable
 */
void set_vibration_enabled(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/**
 * @brief Query whether automatic position calibration on power-up is enabled
 * @param handle Device handler
 * @param slave_id Device ID
 * @return true if auto-calibration is enabled, false otherwise
 */
bool stark_get_auto_calibration(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Enable or disable automatic position calibration on power-up
 * @param handle Device handler
 * @param slave_id Device ID
 * @param enabled true to enable auto-calibration, false to disable
 */
void stark_set_auto_calibration(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/**
 * @brief Send manual position-calibration command
 * 
 * Triggers a manual calibration of finger positions.
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 * @note Ensure fingers are in a known position before calibrating
 */
void stark_send_calibrate_position(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Reset default gestures to factory settings
 * 
 * Restores all built-in gestures (open, fist, pinch, etc.) to their
 * factory default configurations.
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 */
void stark_reset_default_gesture(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Reset all settings to factory defaults
 * 
 * Resets all device settings including:
 * - Default IDs: left hand → 0x7E (126); right hand → 0x7F (127)
 * - RS485 baud rate: 460800 bps
 * - CAN FD baud rate: 5 Mbps
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 * @warning This will reset ALL device settings. Use with caution.
 */
void stark_reset_default_settings(DeviceHandler *handle, uint8_t slave_id);

#endif // DEVICE_SETTINGS_H

Communication Protocols

Modbus-RTU

c
#ifndef MODBUS_REVO2_H
#define MODBUS_REVO2_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Open serial connection (Revo2 only)
 * Initializes and establishes high-speed serial communication connection with Revo2 device
 * @param port Serial port device name, e.g., "/dev/ttyUSB0" (Linux), "COM1" (Windows)
 * @param baudrate Serial baud rate, Revo2 supports: 460800 (default), 1M, 2M, 5M
 * @return DeviceHandler structure pointer (must be freed with modbus_close)
 * @retval NULL Connection failed or invalid parameters
 * @note The returned pointer points to dynamically allocated memory and must be freed
 * @note Revo2 supports higher baud rates, suitable for high-speed data transfer scenarios
 * @warning Using unsupported baud rate may cause communication failure
 */
DeviceHandler *modbus_open(const char *port, uint32_t baudrate);

/**
 * @brief Close serial connection
 * Disconnects serial connection and releases memory allocated by modbus_open
 * @param handle DeviceHandler structure pointer returned by modbus_open
 * @note Passing NULL is safe, the function will return directly
 * @note After calling this function, the handle pointer becomes invalid and should not be used
 */
void modbus_close(DeviceHandler *handle);

#endif // MODBUS_REVO2_H

CANFD

c

#ifndef CANFD_H
#define CANFD_H

#include <stdint.h>
#include <stdbool.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

// ============================================================================
// Recommended: Use stark_auto_detect + init_from_detected (see auto_detect.h)
// ============================================================================

/**
 * @brief Create a new CANFD DeviceHandler
 * 
 * @param protocol_type Protocol type (STARK_PROTOCOL_TYPE_CAN_FD or STARK_PROTOCOL_TYPE_CAN)
 * @param master_id Master device ID (usually 0)
 * @return Pointer to DeviceHandler (must be freed with close_device_handler)
 * 
 * @note Before calling this, you must either:
 *       1. Call init_zqwl_canfd() for ZQWL adapter, or
 *       2. Set custom CAN callbacks with set_can_tx_callback/set_can_rx_callback
 * 
 * @example
 * // Using ZQWL adapter
 * init_zqwl_canfd("/dev/cu.usbmodem12345", 1000000, 5000000);
 * DeviceHandler *handle = init_device_handler(STARK_PROTOCOL_TYPE_CAN_FD, 0);
 * // ... use handle ...
 * close_device_handler(handle, STARK_PROTOCOL_TYPE_CAN_FD);
 */
DeviceHandler *init_device_handler(uint8_t protocol_type, uint8_t master_id);

/**
 * @brief Create a DeviceHandler with hardware type pre-set
 * 
 * Useful when you already know the hardware type (e.g., from auto_detect)
 * and want to avoid an extra get_device_info call.
 * 
 * @param protocol_type Protocol type
 * @param master_id Master device ID
 * @param slave_id Slave ID to set hardware type for
 * @param hw_type Hardware type (StarkHardwareType enum value)
 * @return Pointer to DeviceHandler
 */
DeviceHandler *init_device_handler_with_hw_type(uint8_t protocol_type,
                                                uint8_t master_id,
                                                uint8_t slave_id,
                                                uint8_t hw_type);

// ============================================================================
// CAN/CANFD Communication Callbacks (for custom adapters like ZLG)
// ============================================================================

/**
 * @brief CAN/CANFD TX callback type
 * 
 * @param slave_id Slave ID
 * @param can_id CAN ID
 * @param data Data buffer
 * @param data_len Data length
 * @return 0 on success, non-zero on failure
 */
typedef int32_t (*CanTxCallback)(uint8_t slave_id,
                                 uint32_t can_id,
                                 const uint8_t *data,
                                 size_t data_len);

/**
 * @brief CAN/CANFD RX callback type
 * 
 * @param slave_id Slave ID
 * @param expected_can_id Expected CAN ID to filter responses
 * @param expected_frames Expected frame count (0=auto-detect)
 * @param can_id_out Output CAN ID
 * @param data_out Output data buffer (must be at least 512 bytes)
 * @param data_len_out Output data length
 * @return 0 on success, non-zero on failure
 */
typedef int32_t (*CanRxCallback)(uint8_t slave_id,
                                 uint32_t expected_can_id,
                                 uint8_t expected_frames,
                                 uint32_t *can_id_out,
                                 uint8_t *data_out,
                                 size_t *data_len_out);

/**
 * @brief Set CAN/CANFD TX callback
 * @param cb TX callback function
 */
void set_can_tx_callback(CanTxCallback cb);

/**
 * @brief Set CAN/CANFD RX callback
 * @param cb RX callback function
 */
void set_can_rx_callback(CanRxCallback cb);

#endif // CANFD_H

EtherCAT

c
#ifndef ETHERCAT_H
#define ETHERCAT_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief EtherCAT FoE (File over EtherCAT) firmware type
 */
enum EtherCATFoeType : uint8_t {
  ETHER_CAT_FOE_TYPE_WRIST = 1,   ///< Wrist module firmware
  ETHER_CAT_FOE_TYPE_CONTROL = 2, ///< Main controller firmware
};

// ================================
// EtherCAT Connection (Linux only)
// ================================

/**
 * @brief Open the EtherCAT IgH Master
 * @param master_pos Master position (typically 0)
 * @return Pointer to DeviceHandler, NULL on failure
 * @note Requires IgH EtherCAT Master installed on Linux
 */
DeviceHandler *ethercat_open_master(uint32_t master_pos);

/**
 * @brief Close the EtherCAT IgH Master
 * @param handle DeviceHandler from ethercat_open_master
 */
void ethercat_close(DeviceHandler *handle);

/**
 * @brief Reserve the EtherCAT master for exclusive use
 * Prevents other processes from accessing the master.
 * @param handle DeviceHandler
 */
void ethercat_reserve_master(DeviceHandler *handle);

/**
 * @brief Configure SDO for an EtherCAT slave device
 * @param handle DeviceHandler
 * @param slave_pos Slave position on the bus
 */
void ethercat_setup_sdo(DeviceHandler *handle, uint16_t slave_pos);

/**
 * @brief Start EtherCAT cyclic loop with PDO communication
 * 
 * @param handle DeviceHandler
 * @param slave_positions Array of slave positions
 * @param count Number of slaves
 * @param dc_assign_activate DC flags (0x0000 = DC disabled, 0x0300 = typical)
 * @param sync0_cycle_time SYNC0 cycle time in nanoseconds (loop period)
 * @param sync0_shift_time SYNC0 phase shift in nanoseconds
 * @param sync1_cycle_time SYNC1 cycle time in nanoseconds
 * @param sync1_shift_time SYNC1 phase shift in nanoseconds
 */
void ethercat_start_loop(DeviceHandler *handle,
                         const uint16_t *slave_positions,
                         int count,
                         uint16_t dc_assign_activate,
                         uint32_t sync0_cycle_time,
                         int32_t sync0_shift_time,
                         uint32_t sync1_cycle_time,
                         int32_t sync1_shift_time);

/**
 * @brief Stop the EtherCAT cyclic loop
 * @param handle DeviceHandler
 */
void ethercat_stop_loop(DeviceHandler *handle);

/**
 * @brief EtherCAT DFU: upgrade firmware via FoE protocol
 * 
 * @param handle DeviceHandler
 * @param slave_pos Slave position
 * @param dfu_type DFU type (WRIST or CONTROL)
 * @param file_path Firmware file path
 * 
 * @note Revo2 EtherCAT requires two firmware files:
 *       - Wrist: revo2_ec_wrist_*.bin
 *       - Control: revo2_ec_ctrl_*.bin
 */
void ethercat_start_dfu(DeviceHandler *handle,
                        uint16_t slave_pos,
                        EtherCATFoeType dfu_type,
                        const char *file_path);

#endif // ETHERCAT_H

Device Information

c
#ifndef DEVICE_INFO_H
#define DEVICE_INFO_H

#include <stdint.h>
#include <stdbool.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;
typedef struct CDeviceInfo CDeviceInfo;

/**
 * @brief Hardware type enumeration
 * Defines supported hardware version types
 */
enum StarkHardwareType : uint8_t {
  STARK_HARDWARE_TYPE_REVO1_PROTOBUF = 0,      ///< Revo1 with legacy Protobuf protocol
  STARK_HARDWARE_TYPE_REVO1_BASIC = 1,         ///< Revo1 basic version (no touch)
  STARK_HARDWARE_TYPE_REVO1_TOUCH = 2,         ///< Revo1 with capacitive touch
  STARK_HARDWARE_TYPE_REVO1_ADVANCED = 3,      ///< Revo1 Advanced (Revo2 motor API)
  STARK_HARDWARE_TYPE_REVO1_ADVANCED_TOUCH = 4,///< Revo1 Advanced with capacitive touch
  STARK_HARDWARE_TYPE_REVO2_BASIC = 5,         ///< Revo2 basic version (no touch)
  STARK_HARDWARE_TYPE_REVO2_TOUCH = 6,         ///< Revo2 with capacitive touch
  STARK_HARDWARE_TYPE_REVO2_TOUCH_PRESSURE = 7,///< Revo2 with pressure/modulus touch
};

/**
 * @brief SKU type enumeration
 * Defines device hand type (left/right)
 */
enum SkuType : uint8_t {
  SKU_TYPE_MEDIUM_RIGHT = 1,  ///< Medium size right hand
  SKU_TYPE_MEDIUM_LEFT = 2,   ///< Medium size left hand
  SKU_TYPE_SMALL_RIGHT = 3,   ///< Small size right hand (reserved)
  SKU_TYPE_SMALL_LEFT = 4,    ///< Small size left hand (reserved)
};

/**
 * @brief Device information structure
 * Contains hardware type, SKU type, serial number, and firmware version
 */
struct CDeviceInfo {
  SkuType sku_type;                 ///< SKU type (1: right hand, 2: left hand)
  StarkHardwareType hardware_type;  ///< Hardware type enum
  const char *serial_number;        ///< Device serial number
  const char *firmware_version;     ///< Firmware version string
};

/**
 * @brief Get device information
 * @param handle Device handler
 * @param slave_id Device ID
 * @return CDeviceInfo* - Device information (must be freed with free_device_info)
 * @retval NULL Failed to get device info
 * 
 * @example
 * auto info = stark_get_device_info(handle, slave_id);
 * if (info != NULL) {
 *     printf("SN: %s, FW: %s\n", info->serial_number, info->firmware_version);
 *     
 *     // Check device capabilities
 *     if (stark_is_touch_device(info->hardware_type)) {
 *         printf("Touch-enabled device\n");
 *         if (stark_uses_pressure_touch_api(info->hardware_type)) {
 *             printf("Using pressure/modulus touch sensors\n");
 *         }
 *     }
 *     free_device_info(info);
 * }
 */
CDeviceInfo *stark_get_device_info(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Free device information structure
 * @param info CDeviceInfo pointer
 */
void free_device_info(CDeviceInfo *info);

// ============================================================================
// Device Type Helper Functions
// ============================================================================

/**
 * @brief Check if hardware type has touch sensor
 * @param hw_type Hardware type enum value
 * @return true if device has touch sensor
 */
bool stark_is_touch_device(uint8_t hw_type);

/**
 * @brief Check if hardware type uses Revo1 Motor API
 * 
 * Revo1 Basic/Touch use Revo1 Motor API.
 * Revo1 Advanced/AdvancedTouch and all Revo2 use Revo2 Motor API.
 * 
 * @param hw_type Hardware type enum value
 * @return true if uses Revo1 Motor API
 * @note To check Revo2 Motor API, use: !stark_uses_revo1_motor_api(hw_type)
 */
bool stark_uses_revo1_motor_api(uint8_t hw_type);

/**
 * @brief Check if hardware type uses Revo1 Touch API
 * 
 * Revo1 Touch API has different sensor counts per finger (capacitive).
 * Includes Revo1Touch and Revo1AdvancedTouch.
 * 
 * @param hw_type Hardware type enum value
 * @return true if uses Revo1 Touch API
 */
bool stark_uses_revo1_touch_api(uint8_t hw_type);

/**
 * @brief Check if hardware type uses Revo2 Touch API
 * 
 * Revo2 Touch API has uniform sensor counts per finger (capacitive).
 * 
 * @param hw_type Hardware type enum value
 * @return true if uses Revo2 Touch API
 */
bool stark_uses_revo2_touch_api(uint8_t hw_type);

/**
 * @brief Check if hardware type uses Pressure/Modulus Touch API
 * 
 * Pressure Touch API uses completely different data structure.
 * Only Revo2TouchPressure uses this API.
 * 
 * @param hw_type Hardware type enum value
 * @return true if uses Pressure Touch API
 */
bool stark_uses_pressure_touch_api(uint8_t hw_type);

/**
 * @brief Get touch sensor type from hardware type
 * @param hw_type Hardware type enum value
 * @return 0=None, 1=Capacitive, 2=Pressure
 */
uint8_t stark_get_touch_sensor_type(uint8_t hw_type);

/**
 * @brief Get touch sensor vendor type by reading register
 * @param handle Device handler
 * @param slave_id Device ID
 * @return 0=Unknown, 1=Capacitive, 2=Pressure
 * @note Call this for Revo2 Touch devices to determine actual sensor type
 */
uint8_t stark_get_touch_vendor(DeviceHandler *handle, uint8_t slave_id);

// ============================================================================
// CDeviceInfo Helper Functions
// ============================================================================

/**
 * @brief Check if device uses Revo1 Motor API (from CDeviceInfo)
 */
bool device_info_uses_revo1_motor_api(const CDeviceInfo *info);

/**
 * @brief Check if device uses Revo1 Touch API (from CDeviceInfo)
 */
bool device_info_uses_revo1_touch_api(const CDeviceInfo *info);

/**
 * @brief Check if device uses Revo2 Touch API (from CDeviceInfo)
 */
bool device_info_uses_revo2_touch_api(const CDeviceInfo *info);

/**
 * @brief Check if device has any touch sensor (from CDeviceInfo)
 */
bool device_info_is_touch(const CDeviceInfo *info);

// ============================================================================
// Device Configuration
// ============================================================================

/**
 * @brief Get device battery voltage in mV
 * @param handle Device handler
 * @param slave_id Device ID
 * @return Voltage in millivolts
 */
uint16_t stark_get_voltage(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Get RS-485 baud rate
 * @param handle Device handler
 * @param slave_id Device ID
 * @return Baud rate value
 * @note Revo1: 115200, 57600, 19200, 460800 (default: 115200)
 * @note Revo2: 460800, 1M, 2M, 5M (default: 460800)
 */
uint32_t stark_get_rs485_baudrate(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Set RS-485 baud rate
 * @param handle Device handler
 * @param slave_id Device ID
 * @param baudrate Baud rate to set
 */
void stark_set_rs485_baudrate(DeviceHandler *handle, uint8_t slave_id, uint32_t baudrate);

/**
 * @brief Get CANFD data baud rate
 * @param handle Device handler
 * @param slave_id Device ID
 * @return Baud rate value (1M, 2M, 4M, 5M)
 */
uint32_t stark_get_canfd_baudrate(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Set CANFD data baud rate
 * @param handle Device handler
 * @param slave_id Device ID
 * @param baudrate Baud rate to set (1M, 2M, 4M, 5M)
 */
void stark_set_canfd_baudrate(DeviceHandler *handle, uint8_t slave_id, uint32_t baudrate);

/**
 * @brief Set device slave ID
 * @param handle Device handler
 * @param slave_id Current slave ID
 * @param new_id New slave ID to set
 * @note Revo1 default: 1, Revo2 default: 0x7E (left), 0x7F (right)
 */
void stark_set_slave_id(DeviceHandler *handle, uint8_t slave_id, uint8_t new_id);

#endif // DEVICE_INFO_H

Joint Control

Position/Speed/Current Control

c
#ifndef FINGER_POSITION_REVO2_H
#define FINGER_POSITION_REVO2_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Finger unit mode enumeration
 */
enum FingerUnitMode : uint8_t {
  FINGER_UNIT_MODE_NORMALIZED = 0,  ///< Normalized mode
  FINGER_UNIT_MODE_PHYSICAL = 1,    ///< Physical units mode
};

/**
 * @brief Finger ID enumeration
 * Defines unique identifiers for each finger of the dexterous hand
 */
enum StarkFingerId : uint8_t {
  STARK_FINGER_ID_THUMB = 1,      ///< Thumb
  STARK_FINGER_ID_THUMB_AUX = 2,  ///< Thumb Auxiliary
  STARK_FINGER_ID_INDEX = 3,      ///< Index Finger
  STARK_FINGER_ID_MIDDLE = 4,     ///< Middle Finger
  STARK_FINGER_ID_RING = 5,       ///< Ring Finger
  STARK_FINGER_ID_PINKY = 6,      ///< Pinky Finger
};

/**
 * @brief Set finger control unit mode (Revo2 only)
 * Configures the unit mode for finger control, resets to default after power-on
 * @param handle Device handler
 * @param slave_id Device ID
 * @param mode Unit mode, supports normalized mode and physical units mode
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_unit_mode(DeviceHandler *handle, uint8_t slave_id, FingerUnitMode mode);

/**
 * @brief Get finger control unit mode (Revo2 only)
 * Retrieves the currently set finger control unit mode
 * @param handle Device handler
 * @param slave_id Device ID
 * @return Current unit mode
 * @retval FINGER_UNIT_MODE_NORMALIZED Default return value on read failure
 * @note Only applicable to Revo2 devices
 */
FingerUnitMode stark_get_finger_unit_mode(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Set finger maximum position (Revo2 only)
 * Configures the maximum position limit for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param max_pos Maximum position value, unit: degrees (°)
 * @note Default values: [75, 94, 88, 88, 88, 88] for [Thumb, Thumb Aux, Index, Middle, Ring, Pinky]
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_max_position(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    StarkFingerId finger_id,
                                    uint16_t max_pos);

/**
 * @brief Get finger maximum position (Revo2 only)
 * Retrieves the maximum position limit setting for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @return Maximum position value, unit: degrees (°)
 * @retval 0 Returns 0 on read failure
 * @note Only applicable to Revo2 devices
 */
uint16_t stark_get_finger_max_position(DeviceHandler *handle,
                                        uint8_t slave_id,
                                        StarkFingerId finger_id);

/**
 * @brief Set finger minimum position (Revo2 only)
 * Configures the minimum position limit for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param min_pos Minimum position value, unit: degrees (°)
 * @note Default values: [0, 0, 0, 0, 0, 0] for all fingers
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_min_position(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    StarkFingerId finger_id,
                                    uint16_t min_pos);

/**
 * @brief Get finger minimum position (Revo2 only)
 * Retrieves the minimum position limit setting for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @return Minimum position value, unit: degrees (°)
 * @note Default values: [0, 0, 0, 0, 0, 0] for all fingers
 * @retval 0 Returns 0 on read failure (note: normal minimum value can also be 0)
 * @note Only applicable to Revo2 devices
 */
uint16_t stark_get_finger_min_position(DeviceHandler *handle,
                                        uint8_t slave_id,
                                        StarkFingerId finger_id);

/**
 * @brief Set finger maximum speed (Revo2 only)
 * Configures the maximum movement speed limit for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param max_speed Maximum speed value, unit: degrees per second (°/s)
 * @note Default values: [145, 150, 130, 130, 130, 130] for [Thumb, Thumb Aux, Index, Middle, Ring, Pinky]
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_max_speed(DeviceHandler *handle,
                                 uint8_t slave_id,
                                 StarkFingerId finger_id,
                                 uint16_t max_speed);

/**
 * @brief Get finger maximum speed (Revo2 only)
 * Retrieves the maximum speed limit for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @return Maximum speed value, unit: degrees per second (°/s)
 * @retval 0 Returns 0 on read failure
 * @note Only applicable to Revo2 devices
 */
uint16_t stark_get_finger_max_speed(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     StarkFingerId finger_id);

/**
 * @brief Set finger maximum current (Revo2 only)
 * Configures the maximum drive current limit for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param max_current Maximum current value, unit: milliamps (mA)
 * @note Default value: 1000 mA (all fingers)
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_max_current(DeviceHandler *handle,
                                   uint8_t slave_id,
                                   StarkFingerId finger_id,
                                   uint16_t max_current);

/**
 * @brief Get finger maximum current (Revo2 only)
 * Retrieves the maximum current limit for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @return Maximum current value, unit: milliamps (mA)
 * @retval 0 Returns 0 on read failure
 * @note Only applicable to Revo2 devices
 */
uint16_t stark_get_finger_max_current(DeviceHandler *handle,
                                       uint8_t slave_id,
                                       StarkFingerId finger_id);

/**
 * @brief Set finger protection current (Revo2 only)
 * Configures the protection current threshold for a specified finger to prevent overload damage
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param protected_current Protection current value, unit: milliamps (mA), range 100~1500
 * @note Default values: [500, 500, 500, 500, 500, 500] for all fingers
 * @note When current exceeds this value, the system triggers protection mechanism
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_protected_current(DeviceHandler *handle,
                                       uint8_t slave_id,
                                       StarkFingerId finger_id,
                                       uint16_t protected_current);

/**
 * @brief Get finger protection current (Revo2 only)
 * Retrieves the protection current threshold for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @return Protection current value, unit: milliamps (mA), range 100~1500
 * @retval 0 Returns 0 on read failure
 * @note Only applicable to Revo2 devices
 */
uint16_t stark_get_finger_protected_current(DeviceHandler *handle,
                                           uint8_t slave_id,
                                           StarkFingerId finger_id);

/**
 * @brief Set thumb auxiliary joint lock current (Revo2 only)
 * Configures the lock current for thumb auxiliary joint to maintain joint position
 * @param handle Device handler
 * @param slave_id Device ID
 * @param aux_lock_current Lock current value, unit: milliamps (mA), range 100~500
 * @note Default value: 200 mA
 * @note Only applicable to Revo2 devices
 * @note This function is specifically for thumb auxiliary joint position holding
 */
void stark_set_thumb_aux_lock_current(DeviceHandler *handle,
  uint8_t slave_id,
  uint16_t aux_lock_current);

/**
 * @brief Get thumb auxiliary joint lock current (Revo2 only)
 * Retrieves the lock current setting for thumb auxiliary joint
 * @param handle Device handler
 * @param slave_id Device ID
 * @return Lock current value, unit: milliamps (mA)
 * @note Only applicable to Revo2 devices
 */
uint16_t stark_get_thumb_aux_lock_current(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Set single finger position (Revo2 only)
 * Controls specified finger to move to target position
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param position Position value
 * @note Revo1: range 0~100 (percentage)
 * @note Revo2: range 0~1000 (normalized mode) or min-max position angle (physical mode, unit: degrees)
 * @note Position control is the most basic finger control method
 */
void stark_set_finger_position(DeviceHandler *handle,
                                uint8_t slave_id,
                                StarkFingerId finger_id,
                                uint16_t position);

/**
 * @brief Set single finger position with duration (Revo2 only)
 * Controls specified finger to move to target position within specified time
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param position Position value, range 0~1000 (normalized mode) or min-max position angle (physical mode)
 * @param millis Expected duration, unit: milliseconds, range 1~2000ms
 * @note Only applicable to Revo2 devices
 * @note System automatically calculates appropriate movement speed based on expected duration
 * @example
 * stark_set_finger_position_with_millis(handle, slave_id, 
 *                                       STARK_FINGER_ID_THUMB, 1000, 1000);
 */
void stark_set_finger_position_with_millis(DeviceHandler *handle,
                                            uint8_t slave_id,
                                            StarkFingerId finger_id,
                                            uint16_t position,
                                            uint16_t millis);

/**
 * @brief Set single finger position with speed (Revo2 only)
 * Controls specified finger to move to target position at specified speed
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param position Position value, range 0~1000 (normalized mode) or min-max position angle (physical mode)
 * @param speed Speed value, range 1~1000 (normalized mode) or min~max speed (°/s) (physical mode)
 * @note Only applicable to Revo2 devices
 * @note Provides more precise speed control
 * @example
 * stark_set_finger_position_with_speed(handle, slave_id, 
 *                                      STARK_FINGER_ID_MIDDLE, 1000, 50);
 */
void stark_set_finger_position_with_speed(DeviceHandler *handle,
                                           uint8_t slave_id,
                                           StarkFingerId finger_id,
                                           uint16_t position,
                                           uint16_t speed);

/**
 * @brief Set multiple finger positions
 * Batch sets positions for all fingers
 * @param handle Device handler
 * @param slave_id Device ID
 * @param positions Position value array, length 6, range 0~1000
 * @param len Array length, must be 6
 * @note Array order: [Thumb, Thumb Aux, Index, Middle, Ring, Pinky]
 */
void stark_set_finger_positions(DeviceHandler *handle,
                                 uint8_t slave_id,
                                 const uint16_t *positions,
                                 uintptr_t len);

/**
 * @brief Set multiple finger positions with durations (Revo2 only)
 * Batch sets positions and expected durations for all fingers
 * @param handle Device handler
 * @param slave_id Device ID
 * @param positions Position value array, length 6, range 0~1000 or min~max position (°)
 * @param millis Expected duration array, length 6, range 1~2000ms
 * @param len Array length, must be 6
 * @note Position values and duration values correspond one-to-one
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_positions_and_durations(DeviceHandler *handle,
                                               uint8_t slave_id,
                                               const uint16_t *positions,
                                               const uint16_t *millis,
                                               uintptr_t len);

/**
 * @brief Set multiple finger positions with speeds (Revo2 only)
 * Batch sets positions and expected speeds for all fingers
 * @param handle Device handler
 * @param slave_id Device ID
 * @param positions Position value array, length 6, range 0~1000 or min-max position (°)
 * @param speeds Speed value array, length 6, range 1~1000 or min~max speed (°/s)
 * @param len Array length, must be 6
 * @note Position values and speed values correspond one-to-one
 * @note Only applicable to Revo2 devices
 * @example
 * uint16_t positions[] = {300, 300, 500, 500, 500, 500};
 * uint16_t speeds[] = {500, 500, 500, 500, 500, 500};
 * stark_set_finger_positions_and_speeds(handle, slave_id, positions, speeds, 6);
 */
void stark_set_finger_positions_and_speeds(DeviceHandler *handle,
                                            uint8_t slave_id,
                                            const uint16_t *positions,
                                            const uint16_t *speeds,
                                            uintptr_t len);

/**
 * @brief Set single finger speed (Revo2 only)
 * Sets movement speed for a specified finger
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param speed Speed value, Revo1 range -100~100, Revo2 range -1000~1000 or -max speed~max speed (°/s)
 * @note Sign indicates direction, positive for close direction, negative for open direction
 * @example
 * stark_set_finger_speed(handle, slave_id, STARK_FINGER_ID_MIDDLE, 500);
 */
void stark_set_finger_speed(DeviceHandler *handle,
                             uint8_t slave_id,
                             StarkFingerId finger_id,
                             int16_t speed);

/**
 * @brief Set single finger current (Revo2 only)
 * Sets current for a specified finger (current control mode)
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param current Current value, range -1000~1000 or -max current~max current (mA)
 * @note Sign indicates direction, positive for close direction, negative for open direction
 * @note Only applicable to Revo2 devices
 * @example
 * stark_set_finger_current(handle, slave_id, STARK_FINGER_ID_INDEX, -300);
 */
void stark_set_finger_current(DeviceHandler *handle,
                               uint8_t slave_id,
                               StarkFingerId finger_id,
                               int16_t current);

/**
 * @brief Set single finger PWM (Revo2 only)
 * Sets PWM for a specified finger (PWM control mode)
 * @param handle Device handler
 * @param slave_id Device ID
 * @param finger_id Finger ID
 * @param pwm PWM value, range -1000~1000
 * @note Sign indicates direction, positive for close direction, negative for open direction
 * @note Only applicable to Revo2 devices
 */
void stark_set_finger_pwm(DeviceHandler *handle,
                           uint8_t slave_id,
                           StarkFingerId finger_id,
                           int16_t pwm);

/**
 * @brief Set multiple finger speeds
 * Batch sets movement speeds for all fingers
 * @param handle Device handler
 * @param slave_id Device ID
 * @param speeds Speed value array, length 6, Revo1 range -100~100, Revo2 range -1000~1000 or -max speed~max speed (°/s)
 * @param len Array length, must be 6
 * @note Sign indicates direction, positive for close direction, negative for open direction
 */
void stark_set_finger_speeds(DeviceHandler *handle,
                              uint8_t slave_id,
                              const int16_t *speeds,
                              uintptr_t len);

/**
 * @brief Set multiple finger currents (Revo2 only)
 * Batch sets currents for all fingers
 * @param handle Device handler
 * @param slave_id Device ID
 * @param currents Current value array, length 6, range -1000~1000 or -max current~max current (mA)
 * @param len Array length, must be 6
 * @note Sign indicates direction, positive for close direction, negative for open direction
 * @note Only applicable to Revo2 devices
 * @example
 * int16_t currents[] = {-300, -300, -300, -300, -300, -300};
 * stark_set_finger_currents(handle, slave_id, currents, 6);
 */
void stark_set_finger_currents(DeviceHandler *handle,
                                uint8_t slave_id,
                                const int16_t *currents,
                                uintptr_t len);

/**
 * @brief Set multiple finger PWMs (Revo2 only)
 * Directly controls PWM output values for multiple fingers
 * @param handle Device handler
 * @param slave_id Device ID
 * @param pwms PWM value array, length 6, range -1000~1000
 * @param len Array length, must be 6
 * @note Positive values indicate close direction, negative values indicate open direction
 * @note Array order: [Thumb, Thumb Aux, Index, Middle, Ring, Pinky]
 * @note Only applicable to Revo2 devices
 * @warning PWM control is low-level control and should be used with caution
 */
void stark_set_finger_pwms(DeviceHandler *handle,
                            uint8_t slave_id,
                            const int16_t *pwms,
                            uintptr_t len);

#endif // FINGER_POSITION_REVO2_H

Motor Status

c
#ifndef MOTOR_STATUS_H
#define MOTOR_STATUS_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Motor status data structure
 * Contains current positions, speeds, currents, and states for all motors
 */
struct MotorStatusData {
  uint16_t positions[6];  ///< Current positions [0-1000]
  int16_t speeds[6];      ///< Current speeds
  int16_t currents[6];    ///< Current values
  uint8_t states[6];      ///< Motor state flags
};

/**
 * @brief Get motor status
 * Retrieves current motor status including positions, speeds, currents, and states
 * @param handle Device handler
 * @param slave_id Device ID
 * @return MotorStatusData* - Motor status structure (must be freed with free_motor_status_data)
 * @retval NULL Failed to get status or invalid parameters
 * @note The returned pointer points to dynamically allocated memory and must be freed
 * @example
 * auto status = stark_get_motor_status(handle, slave_id);
 * if (status != NULL) {
 *     printf("Positions: %hu, %hu, %hu, %hu, %hu, %hu\n",
 *            status->positions[0], status->positions[1],
 *            status->positions[2], status->positions[3],
 *            status->positions[4], status->positions[5]);
 *     free_motor_status_data(status);
 * }
 */
MotorStatusData *stark_get_motor_status(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Free motor status data structure
 * Releases memory allocated by stark_get_motor_status
 * @param status MotorStatusData structure pointer returned by stark_get_motor_status
 * @note Passing NULL is safe, the function will return directly
 */
void free_motor_status_data(MotorStatusData *status);

#endif // MOTOR_STATUS_H

Sensors

Tactile Information

c
#ifndef TOUCH_H
#define TOUCH_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Touch sensor raw data structure
 * Contains raw channel data from tactile sensors
 */
struct TouchRawData {
  uint32_t thumb[7];    ///< Thumb raw data (7 channels)
  uint32_t index[11];   ///< Index finger raw data (11 channels)
  uint32_t middle[11];  ///< Middle finger raw data (11 channels)
  uint32_t ring[11];    ///< Ring finger raw data (11 channels)
  uint32_t pinky[7];    ///< Pinky finger raw data (7 channels)
};

/**
 * @brief Enable touch sensor function
 * Enables specified touch sensors, all sensors are disabled by default after power-on
 * @param handle Device handler
 * @param slave_id Device ID
 * @param mask Enabled touch sensor bitmask, range 0~31
 * @note Uses bitmask representation, each bit corresponds to one sensor
 * @example 0x01 enables only sensor 1 (thumb position)
 * @example 0x1F enables all 5 sensors
 * @note Only applicable to touch-enabled devices
 * @example
 * // Enable all touch sensors
 * stark_enable_touch_sensor(handle, slave_id, 0x1F);
 */
void stark_enable_touch_sensor(DeviceHandler *handle, uint8_t slave_id, uint8_t mask);

/**
 * @brief Get raw channel data from the tactile sensors
 * 
 * Retrieves raw capacitance channel data from all tactile sensors.
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 * @return TouchRawData* - Raw data structure (must be freed with free_touch_raw_data)
 * @retval NULL Failed to get data or device does not support touch function
 * @note The returned pointer points to dynamically allocated memory and must be freed
 * @note Only applicable to touch-enabled devices
 */
TouchRawData *stark_get_touch_raw_data(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Read 3D force, proximity and status for a single finger
 * 
 * Retrieves tactile sensor data for one specific finger.
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 * @param index Finger index: 0~4 for thumb, index, middle, ring, pinky
 * @return TouchFingerItem* - Single finger data (must be freed with free_touch_finger_item)
 * @retval NULL Failed to get data or invalid index
 * @note The returned pointer points to dynamically allocated memory and must be freed
 * @note Only applicable to touch-enabled devices
 */
TouchFingerItem *stark_get_single_touch_status(DeviceHandler *handle,
                                               uint8_t slave_id,
                                               uint8_t index);

/**
 * @brief Reset touch sensor acquisition channels
 * Reinitializes specified touch sensor acquisition channels, clearing possible error states
 * @param handle Device handler
 * @param slave_id Device ID
 * @param mask Reset touch sensor bitmask, range 0~31
 * @note When executing this command, finger sensors should avoid force to ensure reset effectiveness
 * @example 0x01 resets sensor 1
 * @example 0xFF resets all 8 sensors
 * @warning Sensor data may be inaccurate during reset process
 */
void stark_reset_touch_sensor(DeviceHandler *handle, uint8_t slave_id, uint8_t mask);

/**
 * @brief Touch sensor parameter calibration
 * Calibrates specified touch sensors, eliminating static offset errors
 * @param handle Device handler
 * @param slave_id Device ID
 * @param mask Calibration touch sensor bitmask, range 0~31
 * @note When 3D force values are not zero in idle state, use this method for calibration
 * @note Ensure sensors are in force-free state during calibration
 * @example 0x01 calibrates sensor 1
 * @warning Do not touch sensors during calibration to avoid affecting calibration results
 */
void stark_calibrate_touch_sensor(DeviceHandler *handle, uint8_t slave_id, uint8_t mask);

/**
 * @brief Touch sensor data structure
 * Contains complete tactile information for a single sensor, including 3D force, capacitance values, and status
 *
 * @note Revo1 Touch: Contains complete 3D force data and capacitance values
 * @note Revo2 Touch: Only contains normal force, tangential force, tangential direction, and proximity values; other values are 0
 */
struct TouchFingerItem {
  // Normal force data (pressure perpendicular to contact surface)
  uint16_t normal_force1;      ///< Normal force 1, unit: 100×N (e.g., 1000 = 10N), measurement range: 0~25N
  uint16_t normal_force2;      ///< Normal force 2, unit: 100×N, measurement range: 0~25N
  uint16_t normal_force3;      ///< Normal force 3, unit: 100×N, invalid for thumb and pinky

  // Tangential force data (friction force parallel to contact surface)
  uint16_t tangential_force1;  ///< Tangential force 1, unit: 100×N (e.g., 1000 = 10N), measurement range: 0~25N
  uint16_t tangential_force2;  ///< Tangential force 2, unit: 100×N, measurement range: 0~25N
  uint16_t tangential_force3;  ///< Tangential force 3, unit: 100×N, invalid for thumb and pinky

  // Tangential force direction data
  uint16_t tangential_direction1; ///< Tangential direction 1, unit: degrees, range: 0~359°, 0xFFFF = invalid
  uint16_t tangential_direction2; ///< Tangential direction 2, unit: degrees, 0° toward fingertip, increases clockwise
  uint16_t tangential_direction3; ///< Tangential direction 3, unit: degrees, invalid for thumb and pinky

  // Proximity sensor data (capacitance values)
  uint32_t self_proximity1;   ///< Self-proximity capacitance 1, used to detect object approach
  uint32_t self_proximity2;   ///< Self-proximity capacitance 2, invalid for thumb and pinky
  uint32_t mutual_proximity;  ///< Mutual-proximity capacitance, invalid for thumb and pinky

  uint16_t status;            ///< Sensor status: 0=data normal, 1=data abnormal, 2=communication abnormal
};

/**
 * @brief Touch sensor status data collection
 * Contains tactile sensor data for all fingers
 */
struct TouchFingerData {
  TouchFingerItem items[5];  ///< Tactile data array for 5 fingers: [Thumb, Index, Middle, Ring, Pinky]
};

/**
 * @brief Get touch sensor status
 * Retrieves tactile sensor data for all fingers, including 3D force, capacitance values, and status information
 * @param handle Device handler
 * @param slave_id Device ID
 * @return TouchFingerData* - Structure pointer containing tactile data for 5 fingers (must be freed with free_touch_finger_data)
 * @retval NULL Failed to get data or device does not support touch function
 * @note The returned pointer points to dynamically allocated memory and must be freed
 * @note Only applicable to touch-enabled devices
 * @note Data order: [Thumb, Index, Middle, Ring, Pinky]
 */
TouchFingerData *stark_get_touch_status(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Free touch sensor status data
 * Releases memory allocated by stark_get_touch_status
 * @param status TouchFingerData structure pointer returned by stark_get_touch_status
 * @note Passing NULL is safe, the function will return directly
 * @note Do not use the pointer after freeing to avoid accessing freed memory
 */
void free_touch_finger_data(TouchFingerData *status);

/**
 * @brief Free single touch finger item
 * Releases memory allocated by stark_get_single_touch_status
 * @param item TouchFingerItem structure pointer returned by stark_get_single_touch_status
 * @note Passing NULL is safe, the function will return directly
 */
void free_touch_finger_item(TouchFingerItem *item);

/**
 * @brief Free touch raw data
 * Releases memory allocated by stark_get_touch_raw_data
 * @param data TouchRawData structure pointer returned by stark_get_touch_raw_data
 * @note Passing NULL is safe, the function will return directly
 */
void free_touch_raw_data(TouchRawData *data);

#endif // TOUCH_H

High-Performance Data Collection ⭐

DataCollector

c
#ifndef DATA_COLLECTOR_H
#define DATA_COLLECTOR_H

#include <stdint.h>
#include <stdbool.h>

// Forward declarations
typedef struct DeviceHandler DeviceHandler;
typedef struct CDataCollector CDataCollector;
typedef struct CMotorStatusBuffer CMotorStatusBuffer;
typedef struct CTouchStatusBuffer CTouchStatusBuffer;
typedef struct CPressureSummaryBuffer CPressureSummaryBuffer;
typedef struct CPressureDetailedBuffer CPressureDetailedBuffer;

// ============================================================================
// Motor Status Data
// ============================================================================

/**
 * @brief Motor status data for all 6 finger joints
 * Array order: [Thumb, ThumbAux, Index, Middle, Ring, Pinky]
 */
typedef struct CMotorStatusData {
    uint16_t positions[6];  ///< Position values (0-1000)
    int16_t speeds[6];      ///< Speed values (-1000 to +1000)
    int16_t currents[6];    ///< Current values (-1000 to +1000)
    uint8_t states[6];      ///< Motor states (MotorState enum)
} CMotorStatusData;

/**
 * @brief Touch sensor data for a single finger
 */
typedef struct CTouchFingerItem {
    uint16_t normal_force1;
    uint16_t normal_force2;
    uint16_t normal_force3;
    uint16_t tangential_force1;
    uint16_t tangential_force2;
    uint16_t tangential_force3;
    uint16_t tangential_direction1;
    uint16_t tangential_direction2;
    uint16_t tangential_direction3;
    uint32_t self_proximity1;
    uint32_t self_proximity2;
    uint32_t mutual_proximity;
    uint16_t status;
} CTouchFingerItem;

/**
 * @brief Pressure sensor detailed data
 */
typedef struct PressureDetailedItem {
    uint8_t sensor_count;       ///< Number of sensors (fingers: 9, palm: 46)
    uint16_t sensors_data[46];  ///< Sensor data array
} PressureDetailedItem;

// ============================================================================
// Buffer Creation and Management
// ============================================================================

/**
 * @brief Create a new motor status buffer
 * @param max_size Maximum buffer capacity (default 1000)
 * @return Pointer to buffer, or NULL on failure
 */
CMotorStatusBuffer *motor_buffer_new(size_t max_size);

/**
 * @brief Free a motor status buffer
 * @param buffer Buffer pointer
 */
void motor_buffer_free(CMotorStatusBuffer *buffer);

/**
 * @brief Get all motor status data from buffer
 * @param buffer Buffer pointer
 * @param out_data Output array (caller must allocate)
 * @param max_count Maximum items to return
 * @param out_count Actual items returned
 * @return 0 on success, -1 on failure
 */
int motor_buffer_pop_all(CMotorStatusBuffer *buffer,
                         CMotorStatusData *out_data,
                         size_t max_count,
                         size_t *out_count);

/**
 * @brief Get buffer length
 * @param buffer Buffer pointer
 * @return Number of items in buffer
 */
size_t motor_buffer_len(const CMotorStatusBuffer *buffer);

/**
 * @brief Check if buffer is empty
 * @param buffer Buffer pointer
 * @return 1 if empty, 0 if not, -1 on error
 */
int motor_buffer_is_empty(const CMotorStatusBuffer *buffer);

/**
 * @brief Clear all data from buffer
 * @param buffer Buffer pointer
 */
void motor_buffer_clear(CMotorStatusBuffer *buffer);

/**
 * @brief Create a new touch status buffer
 * @param max_size Maximum buffer capacity per finger
 * @return Pointer to buffer, or NULL on failure
 */
CTouchStatusBuffer *touch_buffer_new(size_t max_size);

/**
 * @brief Free a touch status buffer
 * @param buffer Buffer pointer
 */
void touch_buffer_free(CTouchStatusBuffer *buffer);

/**
 * @brief Create a new pressure summary buffer
 * @param max_size Maximum buffer capacity per part
 * @return Pointer to buffer, or NULL on failure
 */
CPressureSummaryBuffer *pressure_summary_buffer_new(size_t max_size);

/**
 * @brief Free a pressure summary buffer
 * @param buffer Buffer pointer
 */
void pressure_summary_buffer_free(CPressureSummaryBuffer *buffer);

/**
 * @brief Create a new pressure detailed buffer
 * @param max_size Maximum buffer capacity per part
 * @return Pointer to buffer, or NULL on failure
 */
CPressureDetailedBuffer *pressure_detailed_buffer_new(size_t max_size);

/**
 * @brief Free a pressure detailed buffer
 * @param buffer Buffer pointer
 */
void pressure_detailed_buffer_free(CPressureDetailedBuffer *buffer);

// ============================================================================
// DataCollector Creation
// ============================================================================

/**
 * @brief Create a basic data collector (motor only)
 * @param handle Device handler
 * @param motor_buffer Motor status buffer
 * @param slave_id Slave ID (default 1)
 * @param motor_frequency Motor sampling frequency in Hz (default 1000)
 * @param enable_stats Whether to print statistics (1=true, 0=false)
 * @return Pointer to collector, or NULL on failure
 */
CDataCollector *data_collector_new_basic(DeviceHandler *handle,
                                         CMotorStatusBuffer *motor_buffer,
                                         uint8_t slave_id,
                                         uint32_t motor_frequency,
                                         int enable_stats);

/**
 * @brief Create a capacitive touch data collector
 * @param handle Device handler
 * @param motor_buffer Motor status buffer
 * @param touch_buffer Touch status buffer
 * @param slave_id Slave ID
 * @param motor_frequency Motor sampling frequency in Hz
 * @param touch_frequency Touch sampling frequency in Hz
 * @param enable_stats Whether to print statistics
 * @return Pointer to collector, or NULL on failure
 */
CDataCollector *data_collector_new_capacitive(DeviceHandler *handle,
                                              CMotorStatusBuffer *motor_buffer,
                                              CTouchStatusBuffer *touch_buffer,
                                              uint8_t slave_id,
                                              uint32_t motor_frequency,
                                              uint32_t touch_frequency,
                                              int enable_stats);

/**
 * @brief Create a pressure summary data collector
 * @param handle Device handler
 * @param motor_buffer Motor status buffer
 * @param pressure_summary_buffer Pressure summary buffer
 * @param slave_id Slave ID
 * @param motor_frequency Motor sampling frequency in Hz
 * @param touch_frequency Touch sampling frequency in Hz
 * @param enable_stats Whether to print statistics
 * @return Pointer to collector, or NULL on failure
 */
CDataCollector *data_collector_new_pressure_summary(DeviceHandler *handle,
                                                    CMotorStatusBuffer *motor_buffer,
                                                    CPressureSummaryBuffer *pressure_summary_buffer,
                                                    uint8_t slave_id,
                                                    uint32_t motor_frequency,
                                                    uint32_t touch_frequency,
                                                    int enable_stats);

/**
 * @brief Create a pressure detailed data collector
 * @param handle Device handler
 * @param motor_buffer Motor status buffer
 * @param pressure_detailed_buffer Pressure detailed buffer
 * @param slave_id Slave ID
 * @param motor_frequency Motor sampling frequency in Hz
 * @param touch_frequency Touch sampling frequency in Hz (default 10)
 * @param enable_stats Whether to print statistics
 * @return Pointer to collector, or NULL on failure
 */
CDataCollector *data_collector_new_pressure_detailed(DeviceHandler *handle,
                                                     CMotorStatusBuffer *motor_buffer,
                                                     CPressureDetailedBuffer *pressure_detailed_buffer,
                                                     uint8_t slave_id,
                                                     uint32_t motor_frequency,
                                                     uint32_t touch_frequency,
                                                     int enable_stats);

/**
 * @brief Create a pressure hybrid data collector (summary + detailed)
 * @param handle Device handler
 * @param motor_buffer Motor status buffer
 * @param pressure_summary_buffer Pressure summary buffer
 * @param pressure_detailed_buffer Pressure detailed buffer
 * @param slave_id Slave ID
 * @param motor_frequency Motor sampling frequency in Hz
 * @param summary_frequency Summary sampling frequency in Hz
 * @param detailed_frequency Detailed sampling frequency in Hz
 * @param enable_stats Whether to print statistics
 * @return Pointer to collector, or NULL on failure
 */
CDataCollector *data_collector_new_pressure_hybrid(DeviceHandler *handle,
                                                   CMotorStatusBuffer *motor_buffer,
                                                   CPressureSummaryBuffer *pressure_summary_buffer,
                                                   CPressureDetailedBuffer *pressure_detailed_buffer,
                                                   uint8_t slave_id,
                                                   uint32_t motor_frequency,
                                                   uint32_t summary_frequency,
                                                   uint32_t detailed_frequency,
                                                   int enable_stats);

// ============================================================================
// DataCollector Control
// ============================================================================

/**
 * @brief Start data collection
 * @param collector Collector pointer
 * @return 0 on success, -1 on failure
 */
int data_collector_start(CDataCollector *collector);

/**
 * @brief Stop data collection (non-blocking)
 * @param collector Collector pointer
 * @return 0 on success, -1 on failure
 */
int data_collector_stop(CDataCollector *collector);

/**
 * @brief Wait for data collection thread to finish
 * @param collector Collector pointer
 * @return 0 on success, -1 on failure
 */
int data_collector_wait(CDataCollector *collector);

/**
 * @brief Check if data collector is running
 * @param collector Collector pointer
 * @return 1 if running, 0 if not, -1 on error
 */
int data_collector_is_running(const CDataCollector *collector);

/**
 * @brief Free a data collector
 * @param collector Collector pointer
 */
void data_collector_free(CDataCollector *collector);

#endif // DATA_COLLECTOR_H

Interactive Features

Action Sequences (Gestures)

c
#ifndef ACTION_SEQUENCE_H
#define ACTION_SEQUENCE_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Action sequence ID enumeration
 * Defines IDs for built-in gestures and custom action sequences
 *
 * Built-in gestures (ID 1-6):
 * - Open hand, fist, two-finger pinch, three-finger pinch, side pinch, single-finger point
 *
 * Custom action sequences:
 * - Revo1: Supports up to 6 custom action sequences (ID 10-15)
 * - Revo2: Supports up to 24 custom action sequences (ID 7-30)
 */
enum ActionSequenceId : uint8_t {
  ACTION_SEQUENCE_ID_DEFAULT_GESTURE_OPEN = 1,       ///< Built-in gesture: open hand
  ACTION_SEQUENCE_ID_DEFAULT_GESTURE_FIST = 2,       ///< Built-in gesture: fist
  ACTION_SEQUENCE_ID_DEFAULT_GESTURE_PINCH_TWO = 3,  ///< Built-in gesture: two-finger pinch
  ACTION_SEQUENCE_ID_DEFAULT_GESTURE_PINCH_THREE = 4,///< Built-in gesture: three-finger pinch
  ACTION_SEQUENCE_ID_DEFAULT_GESTURE_PINCH_SIDE = 5, ///< Built-in gesture: side pinch
  ACTION_SEQUENCE_ID_DEFAULT_GESTURE_POINT = 6,      ///< Built-in gesture: single-finger point
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE22 = 7,           ///< Custom gesture 22 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE23 = 8,           ///< Custom gesture 23 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE24 = 9,           ///< Custom gesture 24 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE1 = 10,           ///< Custom gesture 1
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE2 = 11,           ///< Custom gesture 2
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE3 = 12,           ///< Custom gesture 3
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE4 = 13,           ///< Custom gesture 4
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE5 = 14,           ///< Custom gesture 5
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE6 = 15,           ///< Custom gesture 6
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE7 = 16,           ///< Custom gesture 7 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE8 = 17,           ///< Custom gesture 8 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE9 = 18,           ///< Custom gesture 9 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE10 = 19,          ///< Custom gesture 10 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE11 = 20,          ///< Custom gesture 11 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE12 = 21,          ///< Custom gesture 12 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE13 = 22,          ///< Custom gesture 13 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE14 = 23,          ///< Custom gesture 14 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE15 = 24,          ///< Custom gesture 15 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE16 = 25,          ///< Custom gesture 16 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE17 = 26,          ///< Custom gesture 17 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE18 = 27,          ///< Custom gesture 18 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE19 = 28,          ///< Custom gesture 19 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE20 = 29,          ///< Custom gesture 20 (Revo2 only)
  ACTION_SEQUENCE_ID_CUSTOM_GESTURE21 = 30,          ///< Custom gesture 21 (Revo2 only)
};

/**
 * @brief Run an action sequence
 * Executes a predefined or custom action sequence by ID
 * @param handle Device handler
 * @param slave_id Device ID
 * @param action_id Action sequence ID, see ActionSequenceId enumeration
 * @note Custom action sequences must be set via stark_set_action_sequence before execution
 * @note Built-in gestures (ID 1-6) can be executed directly without prior setup
 */
void stark_run_action_sequence(DeviceHandler *handle, uint8_t slave_id, ActionSequenceId action_id);

/**
 * @brief Transfer action sequences to the device
 *
 * This function sends multiple action sequences. Each sequence is represented
 * by a fixed-length array of u16 parameters.
 *
 * @param handle Device handler
 * @param slave_id Device ID
 * @param is_revo2 Whether the device is Revo2
 * @param action_id Action sequence ID that uniquely identifies the sequence
 * @param sequences Flattened parameter array containing multiple sequences
 * @param len Number of sequences (number of rows in the flattened array)
 *
 * @section revo1_format Revo1 Action Sequence Format
 * Each action sequence contains 20 uint16_t elements:
 * - [0] Sequence index: position in the queue
 * - [1] Duration: execution time in milliseconds
 * - [2-7] Finger positions: 6 finger position values (0-100)
 * - [8-13] Finger speeds: 6 finger speed values (0-100)
 * - [14-19] Finger forces: 6 finger force values (0-100)
 *
 * @section revo2_format Revo2 Action Sequence Format
 * Each action sequence contains 27 uint16_t elements:
 * - [0] Sequence index: position in the queue
 * - [1] Duration: execution time in milliseconds
 * - [2] Control mode: 1=position+time, 2=position+speed, 3=current, 4=speed
 * - [3-8] Finger positions: 6 finger positions in degrees (°), 0xFFFF=keep current angle
 * - [9-14] Finger speeds: 6 finger rotational speeds (°/s)
 * - [15-20] Finger currents: 6 finger current values (mA)
 *
 * @example
 * Revo1 action sequence example:
 * @code
 * uint16_t sequence[20] = {
 *     0,                              // Sequence index
 *     2000,                           // Duration: 2 seconds
 *     0, 0, 100, 100, 100, 100,      // 6 finger positions
 *     10, 20, 30, 40, 50, 60,        // 6 finger speeds
 *     5, 10, 15, 20, 25, 30          // 6 finger forces
 * };
 * @endcode
 *
 * @note Revo1 supports maximum 32 action sequences, Revo2 supports maximum 8
 * @warning If handle or sequences is NULL, the function returns immediately
 * @warning If len exceeds the maximum limit, a warning is logged and the function returns
 */
void stark_set_action_sequence(DeviceHandler *handle,
                               uint8_t slave_id,
                               bool is_revo2,
                               ActionSequenceId action_id,
                               const uint16_t *sequences,
                               uintptr_t len);

#endif // ACTION_SEQUENCE_H

LED Control

c
#ifndef LED_H
#define LED_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief LED color enumeration
 * Defines supported LED color combinations based on RGB
 */
typedef enum {
    LED_COLOR_UNCHANGED = 0,  ///< Color unchanged (Note: not equivalent to off, mainly controlled by LedMode)
    LED_COLOR_R = 1,          ///< Red
    LED_COLOR_G = 2,          ///< Green
    LED_COLOR_RG = 3,         ///< Red-Green (Yellow)
    LED_COLOR_B = 4,          ///< Blue
    LED_COLOR_RB = 5,         ///< Red-Blue (Purple)
    LED_COLOR_GB = 6,         ///< Green-Blue (Cyan)
    LED_COLOR_RGB = 7         ///< Red-Green-Blue (White)
} LedColor;

/**
 * @brief LED mode enumeration
 * Defines LED operating modes including blink frequency and duration control
 */
typedef enum {
    LED_MODE_NONE = 0,        ///< No mode / undefined
    LED_MODE_SHUTDOWN = 1,    ///< Turn off LED
    LED_MODE_KEEP = 2,        ///< Keep LED on
    LED_MODE_BLINK = 3,       ///< Standard blink: 1Hz frequency, 50% duty cycle
    LED_MODE_ONE_SHOT = 4,    ///< Single pulse: on for 100ms then off, repeated commands reset timer
    LED_MODE_BLINK0_5HZ = 5,  ///< Slow blink: 0.5Hz frequency, 50% duty cycle
    LED_MODE_BLINK2_HZ = 6    ///< Fast blink: 2Hz frequency, 50% duty cycle
} LedMode;

/**
 * @brief LED information structure
 * Contains LED color and operating mode
 */
struct LedInfo {
  LedColor color;  ///< LED color
  LedMode mode;    ///< LED operating mode
};

/**
 * @brief Get LED information
 * Retrieves LED status information including color and operating mode
 * @param handle Device handler
 * @param slave_id Device ID
 * @return LedInfo* - LED information structure (must be freed with free_led_info)
 * @retval NULL Failed to get LED info or invalid parameters
 * @note The returned pointer points to dynamically allocated memory and must be freed
 */
LedInfo *stark_get_led_info(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Free LED information structure
 * Releases memory allocated by stark_get_led_info
 * @param info LedInfo structure pointer returned by stark_get_led_info
 * @note Passing NULL is safe, the function will return directly
 */
void free_led_info(LedInfo *info);

#endif // LED_H

Button

c
#ifndef BUTTON_H
#define BUTTON_H

#include <stdint.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Button press state enumeration
 * Defines three button states: none, pressed, released
 */
enum PressState : uint8_t {
  PRESS_STATE_NONE = 0,  ///< No button state
  PRESS_STATE_DOWN = 1,  ///< Button pressed state
  PRESS_STATE_UP = 2,    ///< Button released state
};

/**
 * @brief Button event structure
 * Contains button event timestamp, button ID, and press state
 */
struct ButtonPressEvent {
  int32_t timestamp;       ///< Event timestamp (milliseconds)
  int32_t button_id;       ///< Button identifier
  PressState press_state;  ///< Press state
};

/**
 * @brief Get button event
 * Retrieves button event information including timestamp, button ID, and state
 * @param handle Device handler
 * @param slave_id Device ID
 * @return ButtonPressEvent* - Button event structure (must be freed with free_button_event)
 * @retval NULL Failed to get event, no event available, or invalid parameters
 * @note The returned pointer points to dynamically allocated memory and must be freed
 * @note May return NULL if no new button events are available
 */
ButtonPressEvent *stark_get_button_event(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Free button event structure
 * Releases memory allocated by stark_get_button_event
 * @param event ButtonPressEvent structure pointer returned by stark_get_button_event
 * @note Passing NULL is safe, the function will return directly
 */
void free_button_event(ButtonPressEvent *event);

#endif // BUTTON_H

Firmware Upgrade

c
/// DFU state callback, `state` corresponds to `DfuState`.
using DfuStateCallback = void(*)(uint8_t slave_id, uint8_t state);

/// DFU progress callback.
using DfuProgressCallback = void(*)(uint8_t slave_id, float progress);

/// Set DFU state callback.
void set_dfu_state_callback(DfuStateCallback cb);

/// Set DFU progress callback.
void set_dfu_progress_callback(DfuProgressCallback cb);

/// Start the DFU (Device Firmware Update) process.
/// - `handle`: Device handler pointer.
/// - `slave_id`: Slave device ID.
/// - `dfu_file_path`: DFU file path, as a C string.
/// - `wait_secs`: Timeout for entering DFU mode, in seconds (default 5).
void start_dfu(DeviceHandler *handle,
               uint8_t slave_id,
               const char *dfu_file_path,
               uintptr_t wait_secs);

/// Stop the DFU process.
/// - `slave_id`: Slave device ID.
/// This stops an ongoing DFU session.
/// Note: If DFU has not started or has already completed, this function has no effect.
void stop_dfu(uint8_t slave_id);

Advanced Features

Device Helper Functions

c
#ifndef DEVICE_HELPERS_H
#define DEVICE_HELPERS_H

#include <stdbool.h>

/**
 * @brief Determine whether the hand is a touch hand by serial number
 * 
 * Checks if the device is a touch-enabled version based on its serial number.
 * 
 * @param sn_ptr Pointer to serial number string
 * @return true if the device is a touch hand, false otherwise
 * @note This is a helper function that parses the serial number format
 * @example
 * auto info = stark_get_device_info(handle, slave_id);
 * if (is_touch_hand_by_sn(info->serial_number)) {
 *     printf("Touch-enabled device detected\n");
 * }
 */
bool is_touch_hand_by_sn(const char *sn_ptr);

/**
 * @brief Determine whether the hand is Revo2 by serial number
 * 
 * Checks if the device is a Revo2 version based on its serial number.
 * 
 * @param sn_ptr Pointer to serial number string
 * @return true if the device is Revo2, false if Revo1
 * @note This is a helper function that parses the serial number format
 * @example
 * auto info = stark_get_device_info(handle, slave_id);
 * if (is_revo2_hand_by_sn(info->serial_number)) {
 *     printf("Revo2 device detected\n");
 * } else {
 *     printf("Revo1 device detected\n");
 * }
 */
bool is_revo2_hand_by_sn(const char *sn_ptr);

#endif // DEVICE_HELPERS_H

Turbo Mode

c
#ifndef TURBO_MODE_H
#define TURBO_MODE_H

#include <stdint.h>
#include <stdbool.h>

// Forward declaration
typedef struct DeviceHandler DeviceHandler;

/**
 * @brief Turbo mode configuration structure
 * 
 * Turbo mode allows the hand to continuously squeeze with specified
 * interval and duration parameters.
 */
struct TurboConfig {
  uint16_t interval;  ///< Interval time in milliseconds
  uint16_t duration;  ///< Duration time in milliseconds
};

/**
 * @brief Check whether Turbo mode is enabled
 * 
 * When Turbo mode is enabled, the hand will keep squeezing continuously
 * according to the configured interval and duration.
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 * @return true if Turbo mode is enabled, false otherwise
 */
bool stark_get_turbo_mode_enabled(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Enable or disable Turbo mode
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 * @param enabled true to enable Turbo mode, false to disable
 * @note When enabled, the hand will continuously squeeze based on TurboConfig settings
 */
void stark_set_turbo_mode_enabled(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/**
 * @brief Get Turbo mode configuration
 * 
 * Retrieves the current Turbo mode interval and duration settings.
 * 
 * @param handle Device handler
 * @param slave_id Device ID
 * @return Pointer to TurboConfig structure (must be freed with free_turbo_config)
 * @retval NULL Failed to get configuration or invalid parameters
 * @note The returned pointer points to dynamically allocated memory and must be freed
 */
TurboConfig *stark_get_turbo_config(DeviceHandler *handle, uint8_t slave_id);

/**
 * @brief Free Turbo mode configuration structure
 * 
 * Releases memory allocated by stark_get_turbo_config.
 * 
 * @param config TurboConfig structure pointer returned by stark_get_turbo_config
 * @note Passing NULL is safe, the function will return directly
 */
void free_turbo_config(TurboConfig *config);

#endif // TURBO_MODE_H

Custom Callbacks

c
#ifndef CALLBACKS_H
#define CALLBACKS_H

#include <stdint.h>

/**
 * @brief Modbus async read/write result callback type
 * 
 * This callback processes results of asynchronous Modbus operations.
 * 
 * @param data Pointer to data buffer
 * @param len Length of data
 * @param error_code Error code (0 on success, non-zero on failure)
 * @param user_data User-defined data pointer
 */
using ModbusOperationResultCallback = void(*)(uint8_t* data, 
                                              int len, 
                                              int error_code, 
                                              void* user_data);

/**
 * @brief Modbus async read/write callback type
 * 
 * @param values Pointer to values array
 * @param len Length of values array
 * @param callback Result callback function
 * @param user_data User-defined data pointer
 * @return 0 on success, non-zero on failure
 */
using ModbusOperationCallback = int32_t(*)(const uint8_t *values,
                                           int len,
                                           ModbusOperationResultCallback callback,
                                           void *user_data);

/**
 * @brief Custom Modbus RX callback
 * 
 * Callback for reading Modbus registers.
 * 
 * @param slave_id Slave device ID
 * @param register_address Starting register address
 * @param data_out Output data buffer
 * @param count Number of registers to read
 * @return 0 on success, non-zero on failure
 */
using ModbusRxCallback = int32_t(*)(uint8_t slave_id,
                                    uint16_t register_address,
                                    uint16_t *data_out,
                                    uint16_t count);

/**
 * @brief Custom Modbus TX callback
 * 
 * Callback for writing Modbus registers.
 * 
 * @param slave_id Slave device ID
 * @param register_address Starting register address
 * @param data Input data buffer
 * @param count Number of registers to write
 * @return 0 on success, non-zero on failure
 */
using ModbusTxCallback = int32_t(*)(uint8_t slave_id,
                                    uint16_t register_address,
                                    const uint16_t *data,
                                    uint16_t count);

/**
 * @brief CAN/CANFD RX callback
 * 
 * Callback for receiving CAN/CANFD messages.
 * 
 * @param slave_id Slave device ID
 * @param can_id_out Output CAN ID
 * @param data_out Output data buffer (max 64 bytes)
 * @param data_len_out Output data length
 * @return 0 on success, non-zero on failure
 * @note data_out length must be at most 64 bytes
 */
using CanRxCallback = int32_t(*)(uint8_t slave_id,
                                 uint32_t *can_id_out,
                                 uint8_t *data_out,
                                 uintptr_t *data_len_out);

/**
 * @brief CAN/CANFD TX callback
 * 
 * Callback for transmitting CAN/CANFD messages.
 * 
 * @param slave_id Slave device ID
 * @param can_id CAN ID
 * @param data Input data buffer
 * @param data_len Data length
 * @return 0 on success, non-zero on failure
 */
using CanTxCallback = int32_t(*)(uint8_t slave_id,
                                 uint32_t can_id,
                                 const uint8_t *data,
                                 uintptr_t data_len);

/**
 * @brief Set Modbus async read/write callback
 * 
 * Registers a callback for asynchronous Modbus operations.
 * 
 * @param cb Callback function pointer
 */
void set_modbus_operation_callback(ModbusOperationCallback cb);

/**
 * @brief Set Modbus read callback for input registers
 * 
 * Registers a callback for reading Modbus input registers.
 * 
 * @param cb Callback function pointer
 */
void set_modbus_read_input_callback(ModbusRxCallback cb);

/**
 * @brief Set Modbus read callback for holding registers
 * 
 * Registers a callback for reading Modbus holding registers.
 * 
 * @param cb Callback function pointer
 */
void set_modbus_read_holding_callback(ModbusRxCallback cb);

/**
 * @brief Set Modbus write callback
 * 
 * Registers a callback for writing Modbus registers.
 * 
 * @param cb Callback function pointer
 */
void set_modbus_write_callback(ModbusTxCallback cb);

/**
 * @brief Set CAN/CANFD RX callback
 * 
 * Registers a callback for receiving CAN/CANFD messages.
 * 
 * @param cb Callback function pointer
 */
void set_can_rx_callback(CanRxCallback cb);

/**
 * @brief Set CAN/CANFD TX callback
 * 
 * Registers a callback for transmitting CAN/CANFD messages.
 * 
 * @param cb Callback function pointer
 */
void set_can_tx_callback(CanTxCallback cb);

#endif // CALLBACKS_H

API Quick Reference

Initialization and Configuration

APIDescription
init_logging()Initialize SDK logging
APIDescription
stark_auto_detect()Auto-detect devices (all protocols) ⭐
init_from_detected()Initialize from detected device ⭐
close_device_handler()Close device connection (unified) ⭐
list_zqwl_devices()List ZQWL CAN/CANFD devices ⭐
init_zqwl_canfd()Initialize ZQWL CANFD device
init_zqwl_can()Initialize ZQWL CAN 2.0 device
close_zqwl()Close ZQWL device
scan_can_devices()Scan CAN bus for devices

Connection Management (Legacy)

APIDescription
modbus_open()Open Modbus connection
modbus_close()Close Modbus connection
init_device_handler()Create device handler (CAN/CANFD/EtherCAT, manual adapter management)
init_device_handler_with_hw_type()Create device handler with pre-set hardware type (skip get_device_info) ⭐
auto_detect_device()Auto-detect device (Modbus only)
auto_detect_modbus_revo2()Auto-detect Revo2 device (Modbus only)
list_available_ports()List available serial ports

Device Information

APIDescription
stark_get_device_info()Get complete device information
stark_is_touch_device()Check if touch-enabled ⭐
stark_uses_revo1_motor_api()Check if uses Revo1 Motor API ⭐
stark_uses_pressure_touch_api()Check if uses Pressure Touch API ⭐
stark_get_touch_vendor()Get touch sensor type ⭐
stark_get_voltage()Get device voltage
stark_get_rs485_baudrate()Get RS485 baud rate
stark_set_rs485_baudrate()Set RS485 baud rate
stark_get_canfd_baudrate()Get CANFD baud rate
stark_set_canfd_baudrate()Set CANFD baud rate
stark_set_slave_id()Set slave ID

Motor Control - Position (Unified Range 0-1000)

APIDescription
stark_set_finger_position()Set single finger position
stark_set_finger_position_with_millis()Set position with duration ⭐
stark_set_finger_position_with_speed()Set position with speed ⭐
stark_set_finger_positions()Set all finger positions
stark_set_finger_positions_and_durations()Set positions with durations ⭐
stark_set_finger_positions_and_speeds()Set positions with speeds ⭐
stark_get_finger_positions()Get all finger positions

Motor Control - Speed (Unified Range -1000~+1000)

APIDescription
stark_set_finger_speed()Set single finger speed
stark_set_finger_speeds()Set all finger speeds
stark_get_finger_speeds()Get all finger speeds

Motor Control - Current (Unified Range -1000~+1000)

APIDescription
stark_set_finger_current()Set single finger current
stark_set_finger_currents()Set all finger currents
stark_get_finger_currents()Get all finger currents

Motor Control - PWM (Unified Range -1000~+1000) ⭐

APIDescription
stark_set_finger_pwm()Set single finger PWM
stark_set_finger_pwms()Set all finger PWMs

Motor Status

APIDescription
stark_get_motor_status()Get comprehensive motor status
stark_get_motor_state()Get motor running state

Motor Settings ⭐

APIDescription
stark_get_finger_unit_mode()Get unit mode
stark_set_finger_unit_mode()Set unit mode
stark_get_finger_min_position()Get minimum position limit
stark_set_finger_min_position()Set minimum position limit
stark_get_finger_max_position()Get maximum position limit
stark_set_finger_max_position()Set maximum position limit
stark_get_finger_max_speed()Get maximum speed limit
stark_set_finger_max_speed()Set maximum speed limit
stark_get_finger_max_current()Get maximum current limit
stark_set_finger_max_current()Set maximum current limit
stark_get_finger_protected_current()Get protected current
stark_set_finger_protected_current()Set protected current
stark_get_thumb_aux_lock_current()Get thumb aux lock current
stark_set_thumb_aux_lock_current()Set thumb aux lock current

Touch Sensors

APIDescription
stark_get_touch_sensor_enabled()Get touch sensor enabled status
stark_get_touch_sensor_fw_versions()Get touch sensor firmware versions
stark_get_touch_sensor_raw_data()Get touch raw data
stark_get_touch_sensor_status()Get touch sensor status
stark_touch_sensor_setup()Setup touch sensors
stark_touch_sensor_reset()Reset touch sensors
stark_touch_sensor_calibrate()Calibrate touch sensors

High-Performance Data Collection ⭐

APIDescription
motor_buffer_new()Create motor status buffer
motor_buffer_free()Free motor status buffer
motor_buffer_pop_all()Get all motor data
touch_buffer_new()Create touch status buffer
touch_buffer_free()Free touch status buffer
pressure_summary_buffer_new()Create pressure summary buffer
pressure_detailed_buffer_new()Create pressure detailed buffer
data_collector_new_basic()Create basic collector
data_collector_new_capacitive()Create capacitive touch collector
data_collector_new_pressure_summary()Create pressure summary collector
data_collector_new_pressure_detailed()Create pressure detailed collector
data_collector_new_pressure_hybrid()Create hybrid mode collector
data_collector_start()Start data collection
data_collector_stop()Stop data collection
data_collector_wait()Wait for collection thread
data_collector_free()Free data collector

LED, Buzzer, Vibration ⭐

APIDescription
stark_get_led_enabled()Get LED enabled status
stark_set_led_enabled()Set LED enabled status
stark_get_buzzer_enabled()Get buzzer enabled status
stark_set_buzzer_enabled()Set buzzer enabled status
stark_get_vibration_enabled()Get vibration enabled status
stark_set_vibration_enabled()Set vibration enabled status

Action Sequences

APIDescription
stark_get_action_sequence()Get action sequence
stark_transfer_action_sequence()Upload action sequence
stark_run_action_sequence()Execute action sequence

Device Configuration

APIDescription
stark_get_force_level()Get force level
stark_set_force_level()Set force level
stark_get_auto_calibration_enabled()Get auto-calibration status
stark_set_auto_calibration()Set auto-calibration
stark_calibrate_position()Manual position calibration
stark_get_turbo_mode_enabled()Get turbo mode status
stark_set_turbo_mode_enabled()Set turbo mode
stark_get_turbo_config()Get turbo configuration
stark_set_turbo_config()Set turbo configuration
stark_reboot()Reboot device

EtherCAT Specific ⭐

APIDescription
stark_ec_setup_sdo()Setup SDO
stark_ec_start_loop()Start cyclic loop
stark_ec_stop_loop()Stop cyclic loop
stark_ec_start_dfu()Start firmware upgrade

Firmware Upgrade

APIDescription
stark_start_dfu()Start firmware upgrade

⭐ Indicates Revo2-specific or enhanced features