diff --git a/.gitignore b/.gitignore index bcf7e0c..3d0676a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,8 @@ venv/ EMG_Arm/.pio/ EMG_Arm/.vscode/ -# Data (uncomment if you want to exclude) -# collected_data/ -# models/ +# Large data files (too big for GitHub) +assets/*.hdf5 # OS .DS_Store diff --git a/EMG_Arm/src/gestures.c b/EMG_Arm/src/gestures.c new file mode 100644 index 0000000..4154fea --- /dev/null +++ b/EMG_Arm/src/gestures.c @@ -0,0 +1,140 @@ +/** + * @file gestures.c + * @brief Gesture control implementation for the EMG-controlled robotic hand. + * + * This module implements high-level gesture functions using the low-level + * servo control interface. Each gesture function translates intuitive + * commands into appropriate servo positions. + */ + +#include "gestures.h" +#include "servo.h" +#include +#include + +/******************************************************************************* + * Individual Finger Control - Flex Functions + ******************************************************************************/ + +void flex_thumb(void) +{ + servo_set_finger(FINGER_THUMB, SERVO_POS_MAX); +} + +void flex_index(void) +{ + servo_set_finger(FINGER_INDEX, SERVO_POS_MAX); +} + +void flex_middle(void) +{ + servo_set_finger(FINGER_MIDDLE, SERVO_POS_MAX); +} + +void flex_ring(void) +{ + servo_set_finger(FINGER_RING, SERVO_POS_MAX); +} + +void flex_pinky(void) +{ + servo_set_finger(FINGER_PINKY, SERVO_POS_MAX); +} + +/******************************************************************************* + * Individual Finger Control - Unflex Functions + ******************************************************************************/ + +void unflex_thumb(void) +{ + servo_set_finger(FINGER_THUMB, SERVO_POS_MIN); +} + +void unflex_index(void) +{ + servo_set_finger(FINGER_INDEX, SERVO_POS_MIN); +} + +void unflex_middle(void) +{ + servo_set_finger(FINGER_MIDDLE, SERVO_POS_MIN); +} + +void unflex_ring(void) +{ + servo_set_finger(FINGER_RING, SERVO_POS_MIN); +} + +void unflex_pinky(void) +{ + servo_set_finger(FINGER_PINKY, SERVO_POS_MIN); +} + +/******************************************************************************* + * Composite Gestures + ******************************************************************************/ + +void gesture_make_fist(void) +{ + /* Flex all fingers simultaneously */ + flex_thumb(); + flex_index(); + flex_middle(); + flex_ring(); + flex_pinky(); +} + +void gesture_open_hand(void) +{ + /* Extend all fingers simultaneously */ + unflex_thumb(); + unflex_index(); + unflex_middle(); + unflex_ring(); + unflex_pinky(); +} + +/******************************************************************************* + * Demo/Test Sequences + ******************************************************************************/ + +void demo_individual_fingers(uint32_t delay_ms) +{ + /* Thumb */ + flex_thumb(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + unflex_thumb(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + + /* Index */ + flex_index(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + unflex_index(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + + /* Middle */ + flex_middle(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + unflex_middle(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + + /* Ring */ + flex_ring(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + unflex_ring(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + + /* Pinky */ + flex_pinky(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + unflex_pinky(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); +} + +void demo_close_open(uint32_t delay_ms) +{ + gesture_make_fist(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + gesture_open_hand(); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); +} diff --git a/EMG_Arm/src/gestures.h b/EMG_Arm/src/gestures.h new file mode 100644 index 0000000..c1b869f --- /dev/null +++ b/EMG_Arm/src/gestures.h @@ -0,0 +1,128 @@ +/** + * @file gestures.h + * @brief Gesture control interface for the EMG-controlled robotic hand. + * + * This module provides high-level gesture functions for controlling the + * robotic hand. It builds upon the low-level servo control module to + * implement intuitive finger movements and hand gestures. + * + * Gesture Categories: + * - Individual finger control (flex/unflex each finger) + * - Composite gestures (fist, open hand, etc.) + * - Demo/test sequences + */ + +#ifndef GESTURES_H +#define GESTURES_H + +#include + +/******************************************************************************* + * Individual Finger Control - Flex Functions + * + * Flex functions move a finger to the closed (180 degree) position. + ******************************************************************************/ + +/** + * @brief Flex the thumb (move to closed position). + */ +void flex_thumb(void); + +/** + * @brief Flex the index finger (move to closed position). + */ +void flex_index(void); + +/** + * @brief Flex the middle finger (move to closed position). + */ +void flex_middle(void); + +/** + * @brief Flex the ring finger (move to closed position). + */ +void flex_ring(void); + +/** + * @brief Flex the pinky finger (move to closed position). + */ +void flex_pinky(void); + +/******************************************************************************* + * Individual Finger Control - Unflex Functions + * + * Unflex functions move a finger to the extended (0 degree) position. + ******************************************************************************/ + +/** + * @brief Unflex the thumb (move to extended position). + */ +void unflex_thumb(void); + +/** + * @brief Unflex the index finger (move to extended position). + */ +void unflex_index(void); + +/** + * @brief Unflex the middle finger (move to extended position). + */ +void unflex_middle(void); + +/** + * @brief Unflex the ring finger (move to extended position). + */ +void unflex_ring(void); + +/** + * @brief Unflex the pinky finger (move to extended position). + */ +void unflex_pinky(void); + +/******************************************************************************* + * Composite Gestures + * + * These functions control multiple fingers simultaneously to form gestures. + ******************************************************************************/ + +/** + * @brief Close all fingers to form a fist. + * + * All five fingers move to the flexed position simultaneously. + */ +void gesture_make_fist(void); + +/** + * @brief Open the hand fully. + * + * All five fingers move to the extended position simultaneously. + */ +void gesture_open_hand(void); + +/******************************************************************************* + * Demo/Test Sequences + * + * These functions provide demonstration sequences for testing servo operation. + ******************************************************************************/ + +/** + * @brief Demo sequence: flex and unflex each finger individually. + * + * Cycles through each finger, flexing and unflexing with a delay + * between each movement. Useful for testing individual servo operation. + * + * @param delay_ms Delay in milliseconds between each movement. + */ +void demo_individual_fingers(uint32_t delay_ms); + +/** + * @brief Demo sequence: repeatedly close and open the hand. + * + * Alternates between making a fist and opening the hand. + * Useful for testing simultaneous servo operation. + * + * @param delay_ms Delay in milliseconds between fist and open positions. + */ +void demo_close_open(uint32_t delay_ms); + +#endif /* GESTURES_H */ diff --git a/EMG_Arm/src/main.c b/EMG_Arm/src/main.c index d78ff57..6f32d43 100644 --- a/EMG_Arm/src/main.c +++ b/EMG_Arm/src/main.c @@ -1,180 +1,50 @@ +/** + * @file main.c + * @brief Main application entry point for the EMG-controlled robotic hand. + * + * This application controls a 5-finger robotic hand using servo motors. + * The servos are driven by PWM signals generated through the ESP32's LEDC + * peripheral. Future versions will integrate EMG signal processing to + * translate muscle activity into hand gestures. + * + * Hardware Platform: ESP32-S3-DevKitC-1 + * Framework: ESP-IDF with FreeRTOS + */ #include -#include "driver/gpio.h" -#include "driver/ledc.h" +#include +#include "servo.h" +#include "gestures.h" -// Finger servo pin mappings -#define thumbServoPin GPIO_NUM_1 -#define indexServoPin GPIO_NUM_4 -#define middleServoPin GPIO_NUM_5 -#define ringServoPin GPIO_NUM_6 -#define pinkyServoPin GPIO_NUM_7 +/******************************************************************************* + * Configuration + ******************************************************************************/ -// LEDC channels (one per servo) -#define thumbChannel LEDC_CHANNEL_0 -#define indexChannel LEDC_CHANNEL_1 -#define middleChannel LEDC_CHANNEL_2 -#define ringChannel LEDC_CHANNEL_3 -#define pinkyChannel LEDC_CHANNEL_4 -#define deg180 2048 -#define deg0 430 +/** @brief Delay between demo movements in milliseconds. */ +#define DEMO_DELAY_MS 1000 -// Arrays for cleaner initialization -const int servoPins[] = {thumbServoPin, indexServoPin, middleServoPin, ringServoPin, pinkyServoPin}; -const int servoChannels[] = {thumbChannel, indexChannel, middleChannel, ringChannel, pinkyChannel}; -const int numServos = 5; +/******************************************************************************* + * Application Entry Point + ******************************************************************************/ -void servoInit() { - // LEDC timer configuration (shared by all servos) - ledc_timer_config_t ledc_timer = {}; - ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; - ledc_timer.timer_num = LEDC_TIMER_0; - ledc_timer.duty_resolution = LEDC_TIMER_14_BIT; - ledc_timer.freq_hz = 50; - ledc_timer.clk_cfg = LEDC_AUTO_CLK; - ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); +/** + * @brief Main application entry point. + * + * Initializes the servo hardware and runs a demo sequence. + * The demo can be configured to test individual finger control + * or simultaneous hand gestures. + */ +void app_main(void) +{ + /* Initialize servo motors */ + servo_init(); - // Initialize each finger servo channel - for (int i = 0; i < numServos; i++) { - ledc_channel_config_t ledc_channel = {}; - ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE; - ledc_channel.channel = servoChannels[i]; - ledc_channel.timer_sel = LEDC_TIMER_0; - ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.gpio_num = servoPins[i]; - ledc_channel.duty = deg0; // Start with fingers open - ledc_channel.hpoint = 0; - ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); - } + /* Run demo sequence */ + while (1) { + /* Option 1: Test individual finger control */ + // demo_individual_fingers(DEMO_DELAY_MS); + + /* Option 2: Test simultaneous hand gestures */ + demo_close_open(DEMO_DELAY_MS); + } } - -// Flex functions (move to 180 degrees - finger closed) -void flexThumb() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, thumbChannel, deg180); - ledc_update_duty(LEDC_LOW_SPEED_MODE, thumbChannel); -} - -void flexIndex() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, indexChannel, deg180); - ledc_update_duty(LEDC_LOW_SPEED_MODE, indexChannel); -} - -void flexMiddle() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, middleChannel, deg180); - ledc_update_duty(LEDC_LOW_SPEED_MODE, middleChannel); -} - -void flexRing() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, ringChannel, deg180); - ledc_update_duty(LEDC_LOW_SPEED_MODE, ringChannel); -} - -void flexPinky() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, pinkyChannel, deg180); - ledc_update_duty(LEDC_LOW_SPEED_MODE, pinkyChannel); -} - -// Unflex functions (move to 0 degrees - finger open) -void unflexThumb() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, thumbChannel, deg0); - ledc_update_duty(LEDC_LOW_SPEED_MODE, thumbChannel); -} - -void unflexIndex() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, indexChannel, deg0); - ledc_update_duty(LEDC_LOW_SPEED_MODE, indexChannel); -} - -void unflexMiddle() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, middleChannel, deg0); - ledc_update_duty(LEDC_LOW_SPEED_MODE, middleChannel); -} - -void unflexRing() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, ringChannel, deg0); - ledc_update_duty(LEDC_LOW_SPEED_MODE, ringChannel); -} - -void unflexPinky() { - ledc_set_duty(LEDC_LOW_SPEED_MODE, pinkyChannel, deg0); - ledc_update_duty(LEDC_LOW_SPEED_MODE, pinkyChannel); -} - -// Combo functions -void makeFist() { - // Set all duties first - ledc_set_duty(LEDC_LOW_SPEED_MODE, thumbChannel, deg180); - ledc_set_duty(LEDC_LOW_SPEED_MODE, indexChannel, deg180); - ledc_set_duty(LEDC_LOW_SPEED_MODE, middleChannel, deg180); - ledc_set_duty(LEDC_LOW_SPEED_MODE, ringChannel, deg180); - ledc_set_duty(LEDC_LOW_SPEED_MODE, pinkyChannel, deg180); - // Update all at once - ledc_update_duty(LEDC_LOW_SPEED_MODE, thumbChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, indexChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, middleChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, ringChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, pinkyChannel); -} - -void openHand() { - // Set all duties first - ledc_set_duty(LEDC_LOW_SPEED_MODE, thumbChannel, deg0); - ledc_set_duty(LEDC_LOW_SPEED_MODE, indexChannel, deg0); - ledc_set_duty(LEDC_LOW_SPEED_MODE, middleChannel, deg0); - ledc_set_duty(LEDC_LOW_SPEED_MODE, ringChannel, deg0); - ledc_set_duty(LEDC_LOW_SPEED_MODE, pinkyChannel, deg0); - // Update all at once - ledc_update_duty(LEDC_LOW_SPEED_MODE, thumbChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, indexChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, middleChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, ringChannel); - ledc_update_duty(LEDC_LOW_SPEED_MODE, pinkyChannel); -} - -void individualFingerDemo(int delay_ms){ - flexThumb(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - unflexThumb(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - - flexIndex(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - unflexIndex(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - - flexMiddle(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - unflexMiddle(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - - flexRing(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - unflexRing(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - - flexPinky(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - unflexPinky(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); -} - -void closeOpenDemo(int delay_ms){ - makeFist(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); - openHand(); - vTaskDelay(pdMS_TO_TICKS(delay_ms)); -} - -void app_main() { - servoInit(); - - // Demo: flex and unflex each finger in sequence - // while(1) { - // individualFingerDemo(1000); - // } - - // Demo: close and open hand - while(1) { - closeOpenDemo(1000); - } -} \ No newline at end of file diff --git a/EMG_Arm/src/servo.c b/EMG_Arm/src/servo.c new file mode 100644 index 0000000..345bc70 --- /dev/null +++ b/EMG_Arm/src/servo.c @@ -0,0 +1,125 @@ +/** + * @file servo.c + * @brief Servo motor control implementation for the EMG-controlled robotic hand. + * + * This module implements low-level servo control using the ESP32's LEDC peripheral. + * The LEDC is configured to generate 50Hz PWM signals suitable for standard hobby servos. + */ + +#include "servo.h" +#include "esp_err.h" + +/******************************************************************************* + * Private Constants + ******************************************************************************/ + +/** @brief PWM frequency for servo control (standard is 50Hz). */ +#define SERVO_PWM_FREQ_HZ 50 + +/** @brief PWM resolution in bits (14-bit = 16384 levels). */ +#define SERVO_PWM_RESOLUTION LEDC_TIMER_14_BIT + +/** @brief LEDC speed mode (ESP32-S3 only supports low-speed mode). */ +#define SERVO_SPEED_MODE LEDC_LOW_SPEED_MODE + +/** @brief LEDC timer used for all servos. */ +#define SERVO_TIMER LEDC_TIMER_0 + +/******************************************************************************* + * Private Data + ******************************************************************************/ + +/** + * @brief Mapping of finger indices to GPIO pins. + */ +static const int servo_pins[FINGER_COUNT] = { + THUMB_SERVO_PIN, + INDEX_SERVO_PIN, + MIDDLE_SERVO_PIN, + RING_SERVO_PIN, + PINKY_SERVO_PIN +}; + +/** + * @brief Mapping of finger indices to LEDC channels. + */ +static const ledc_channel_t servo_channels[FINGER_COUNT] = { + THUMB_CHANNEL, + INDEX_CHANNEL, + MIDDLE_CHANNEL, + RING_CHANNEL, + PINKY_CHANNEL +}; + +/******************************************************************************* + * Public Function Implementations + ******************************************************************************/ + +void servo_init(void) +{ + /* Configure LEDC timer (shared by all servo channels) */ + ledc_timer_config_t timer_config = { + .speed_mode = SERVO_SPEED_MODE, + .timer_num = SERVO_TIMER, + .duty_resolution = SERVO_PWM_RESOLUTION, + .freq_hz = SERVO_PWM_FREQ_HZ, + .clk_cfg = LEDC_AUTO_CLK + }; + ESP_ERROR_CHECK(ledc_timer_config(&timer_config)); + + /* Configure LEDC channel for each finger servo */ + for (int i = 0; i < FINGER_COUNT; i++) { + ledc_channel_config_t channel_config = { + .speed_mode = SERVO_SPEED_MODE, + .channel = servo_channels[i], + .timer_sel = SERVO_TIMER, + .intr_type = LEDC_INTR_DISABLE, + .gpio_num = servo_pins[i], + .duty = SERVO_POS_MIN, /* Start with fingers extended */ + .hpoint = 0 + }; + ESP_ERROR_CHECK(ledc_channel_config(&channel_config)); + } +} + +void servo_set_position(ledc_channel_t channel, uint32_t duty) +{ + ledc_set_duty(SERVO_SPEED_MODE, channel, duty); + ledc_update_duty(SERVO_SPEED_MODE, channel); +} + +void servo_set_finger(finger_t finger, uint32_t duty) +{ + if (finger >= FINGER_COUNT) { + return; /* Invalid finger index */ + } + servo_set_position(servo_channels[finger], duty); +} + +uint32_t servo_degrees_to_duty(float degrees) +{ + /* Clamp input to valid range */ + if (degrees < 0.0f) { + degrees = 0.0f; + } else if (degrees > 180.0f) { + degrees = 180.0f; + } + + /* + * Linear interpolation formula: + * duty = min + (degrees / 180) * (max - min) + * + * Where: + * - min = SERVO_POS_MIN (duty at 0 degrees) + * - max = SERVO_POS_MAX (duty at 180 degrees) + */ + float duty = SERVO_POS_MIN + (degrees / 180.0f) * (SERVO_POS_MAX - SERVO_POS_MIN); + + return (uint32_t)duty; +} + +void servo_set_finger_degrees(finger_t finger, float degrees) +{ + uint32_t duty = servo_degrees_to_duty(degrees); + servo_set_finger(finger, duty); +} diff --git a/EMG_Arm/src/servo.h b/EMG_Arm/src/servo.h new file mode 100644 index 0000000..ff0ae0b --- /dev/null +++ b/EMG_Arm/src/servo.h @@ -0,0 +1,135 @@ +/** + * @file servo.h + * @brief Servo motor control interface for the EMG-controlled robotic hand. + * + * This module provides low-level servo motor control using the ESP32's LEDC + * (LED Controller) peripheral for PWM generation. It handles initialization + * and position control for all five finger servos. + * + * Hardware Configuration: + * - Thumb: GPIO 1, LEDC Channel 0 + * - Index: GPIO 4, LEDC Channel 1 + * - Middle: GPIO 5, LEDC Channel 2 + * - Ring: GPIO 6, LEDC Channel 3 + * - Pinky: GPIO 7, LEDC Channel 4 + * + * @note All servos share a single LEDC timer configured for 50Hz (standard servo frequency). + */ + +#ifndef SERVO_H +#define SERVO_H + +#include "driver/ledc.h" +#include "driver/gpio.h" + +/******************************************************************************* + * GPIO Pin Definitions + ******************************************************************************/ + +#define THUMB_SERVO_PIN GPIO_NUM_1 +#define INDEX_SERVO_PIN GPIO_NUM_4 +#define MIDDLE_SERVO_PIN GPIO_NUM_5 +#define RING_SERVO_PIN GPIO_NUM_6 +#define PINKY_SERVO_PIN GPIO_NUM_7 + +/******************************************************************************* + * LEDC Channel Definitions + ******************************************************************************/ + +#define THUMB_CHANNEL LEDC_CHANNEL_0 +#define INDEX_CHANNEL LEDC_CHANNEL_1 +#define MIDDLE_CHANNEL LEDC_CHANNEL_2 +#define RING_CHANNEL LEDC_CHANNEL_3 +#define PINKY_CHANNEL LEDC_CHANNEL_4 + +/******************************************************************************* + * Servo Position Definitions + * + * These duty cycle values correspond to servo positions at 14-bit resolution. + * At 50Hz with 14-bit resolution (16384 counts per 20ms period): + * - 1ms pulse (~0 degrees) = ~819 counts + * - 2ms pulse (~180 degrees) = ~1638 counts + * + * Actual values may vary based on specific servo characteristics. + ******************************************************************************/ + +#define SERVO_POS_MIN 430 /**< Duty cycle for 0 degrees (finger extended) */ +#define SERVO_POS_MAX 2048 /**< Duty cycle for 180 degrees (finger flexed) */ + +/******************************************************************************* + * Finger Index Enumeration + ******************************************************************************/ + +/** + * @brief Enumeration for finger identification. + */ +typedef enum { + FINGER_THUMB = 0, + FINGER_INDEX, + FINGER_MIDDLE, + FINGER_RING, + FINGER_PINKY, + FINGER_COUNT /**< Total number of fingers (5) */ +} finger_t; + +/******************************************************************************* + * Public Function Declarations + ******************************************************************************/ + +/** + * @brief Initialize all servo motors. + * + * Configures the LEDC timer and channels for all five finger servos. + * All servos are initialized to the extended (open) position. + * + * @note Must be called before any other servo functions. + */ +void servo_init(void); + +/** + * @brief Set a specific servo to a given duty cycle position. + * + * @param channel The LEDC channel corresponding to the servo. + * @param duty The duty cycle value (use SERVO_POS_MIN to SERVO_POS_MAX). + */ +void servo_set_position(ledc_channel_t channel, uint32_t duty); + +/** + * @brief Set a finger servo to a specific position. + * + * @param finger The finger to control (use finger_t enumeration). + * @param duty The duty cycle value (use SERVO_POS_MIN to SERVO_POS_MAX). + */ +void servo_set_finger(finger_t finger, uint32_t duty); + +/** + * @brief Convert an angle in degrees to the corresponding duty cycle value. + * + * Performs linear interpolation between SERVO_POS_MIN (0 degrees) and + * SERVO_POS_MAX (180 degrees). Input values are clamped to the valid range. + * + * @param degrees The desired angle in degrees (0 to 180). + * @return The corresponding duty cycle value for the LEDC peripheral. + * + * @example + * uint32_t duty = servo_degrees_to_duty(90); // Get duty for 90 degrees + * servo_set_finger(FINGER_INDEX, duty); + */ +uint32_t servo_degrees_to_duty(float degrees); + +/** + * @brief Set a finger servo to a specific angle in degrees. + * + * Convenience function that combines degree-to-duty conversion with + * finger positioning. This is the recommended function for most use cases. + * + * @param finger The finger to control (use finger_t enumeration). + * @param degrees The desired angle in degrees (0 = extended, 180 = flexed). + * + * @example + * servo_set_finger_degrees(FINGER_THUMB, 90); // Move thumb to 90 degrees + * servo_set_finger_degrees(FINGER_INDEX, 45); // Move index to 45 degrees + */ +void servo_set_finger_degrees(finger_t finger, float degrees); + +#endif /* SERVO_H */