Showing Muscles with Capacitive Sensing

Follow us as we downscale an existing humanoid robotics demo to tabletop size and control the movement using capacitive sensing!

AdvancedFull instructions provided10 hours186
Showing Muscles with Capacitive Sensing

Things used in this project

Hardware components

Infineon CY8CPROTO-040T PSOC™ 4000T CAPSENSE™ prototyping kit
×1
Servo motor e.g. S-7361
Other servos can be used; Please check in this case the mounting holes and opening; Mounting screws for the base and arm, should be provided with the servo motor;
×1
USB Cable, USB Type C Plug
USB Cable, USB Type C Plug
Used for programming and power supply.
×1
Threaded insert M3
Insert for upper arm
×1
M3x10 cylinder head screw
Connecting base to upper arm
×1
Rubber feet
You can also reuse the ones which come on the bottom of the eval board
×1
Acrylic glass
Optional part, thickness to be added and checked
×1

Software apps and online services

ModusToolbox™ Software
Infineon ModusToolbox™ Software

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)
Laser cutter (generic)
Laser cutter (generic)
Optional, only needed if casing is wanted

Story

Read more

Custom parts and enclosures

PMMA Housing Top

PMMA Housing Front

PMMA Housing Left

PMMA Housing Rear

Forearm STL File

STL file of the forearm

PMMA Housing Right

Upper arm STL File

STL file of the upper arm

Base STL File

STL file of the base

Base STEP File

Step file of the base

Upper arm STEP File

Step file of the upper arm

Code

main.c

C/C++
Primary source file, containing the main application code
/*******************************************************************************
 * File Name: main.c
 * Author: P. Haselwanter
 * Date: 22-04-2026
 * Company: Infineon Technologies Austria AG
 *******************************************************************************
 Firmware for the Mini Robotic Arm Project using the CY8CPROTO-040T PSoC 4000T
 CAPSENSE Prototyping Kit. This software controls a 3D printed robotic arm
 actuated by a servo motor, utilizing the TCPWM block. The system reads input
 from CAPSENSE buttons and sliders, allowing for modification of the robotic
 arm's position based on sensor data.
 ******************************************************************************/

/*******************************************************************************
 * Include header files
 ******************************************************************************/
#include "cy_result.h"
#include "cy_syslib.h"
#include "cy_tcpwm.h"
#include "cy_tcpwm_counter.h"
#include "cy_tcpwm_pwm.h"
#include "cybsp.h"
#include "cycfg_capsense.h"
#include "cy_capsense_sensing.h"
#include "cy_capsense_structure.h"
#include "cy_gpio.h"
#include "cycfg_peripherals.h"
#include "gpio_psoc4000t_24_qfn.h"

/*******************************************************************************
 * Macros
 ******************************************************************************/
/* Define macros for interrupt priorities */
#define CAPSENSE_MSC0_INTR_PRIORITY     (3u)
#define TCPWM_COUNTER1_INTR_PRIORITY	(2u)

#define CY_ASSERT_FAILED                (0u)

/* Define macros for LED states */
#define CYBSP_LED_OFF                   (0u)
#define CYBSP_LED_ON                    (1u)

/* Define macros for minimum and maximum duty cycles */
#define MIN_DUTY_CYCLE					(45.0f)
#define MAX_DUTY_CYCLE					(58.0f)

/*******************************************************************************
 * Global variables
 ******************************************************************************/
/* Define a struct to hold servo data */
typedef struct
{
	float32_t target_duty;
	float32_t current_duty;
} servo_data_t;
/* Initialize a servo_data_t struct with duty cycle values */
servo_data_t servo = {MIN_DUTY_CYCLE,MIN_DUTY_CYCLE};

/*******************************************************************************
 * Function prototypes
 ******************************************************************************/
static void initialize_gpio(void);
static void initialize_pwm(void);
static uint32_t get_pwm_compare_value(float32_t);
static void initialize_timer(void);
static void tcpwm_counter1_isr(void);
static void initialize_capsense(void);
static void capsense_msc0_isr(void);
static void process_touch(void);

/*******************************************************************************
 * Function Name: main
 ******************************************************************************/
int main(void)
{
	cy_rslt_t result;
	/* Initialize the board */
	result = cybsp_init();

	/* Check if board initialization was successful */
	if(result != CY_RSLT_SUCCESS)
	{
		/* If not, stop program execution */
		CY_ASSERT(CY_ASSERT_FAILED);
	}

	/* Enable global interrupts*/
	__enable_irq();
	/* Initialize peripherals */
	initialize_gpio();
	initialize_pwm();
	initialize_timer();
	initialize_capsense();
	/* Main loop: scan CAPSENSE widgets and process touch events */
	while(1)
	{
		Cy_CapSense_ScanAllSlots(&cy_capsense_context);
		/* Wait for scan to finish */
		while(Cy_CapSense_IsBusy(&cy_capsense_context));
		/* Process touch events */
		process_touch();
	}
	return 0;
}

/*******************************************************************************
 * Function Name: initialize_gpio
 ******************************************************************************/
static void initialize_gpio(void)
{
	/* Set LED2 to off state */
	Cy_GPIO_Write(P3_0_PORT,P3_0_NUM,CYBSP_LED_OFF);
	/* Set LED3 to off state */
	Cy_GPIO_Write(P1_0_PORT,P1_0_NUM,CYBSP_LED_ON);
}

/*******************************************************************************
 * Function Name: initialize_pwm
 ******************************************************************************/
static void initialize_pwm(void)
{
	Cy_TCPWM_PWM_Init(CYBSP_PWM_HW,CYBSP_PWM_NUM, &CYBSP_PWM_config);
	/* Enable the initialized PWM */
	Cy_TCPWM_PWM_Enable(CYBSP_PWM_HW,CYBSP_PWM_NUM);
	/* Set initial compare value*/
	Cy_TCPWM_PWM_SetCompare0(CYBSP_PWM_HW,CYBSP_PWM_NUM,get_pwm_compare_value(MIN_DUTY_CYCLE));
	/* Then start the PWM */
	Cy_TCPWM_TriggerReloadOrIndex(CYBSP_PWM_HW,CYBSP_PWM_MASK);
}

/*******************************************************************************
 * Function Name: get_pwm_compare_value
 ******************************************************************************/
static uint32_t get_pwm_compare_value(float32_t duty_cycle)
{
	return (uint32_t)(CYBSP_PWM_config.period0*duty_cycle/100.0);
}


/*******************************************************************************
 * Function Name: initialize_timer
 ******************************************************************************/
static void initialize_timer(void)
{
	Cy_TCPWM_Counter_Init(CYBSP_TIMER_HW,CYBSP_TIMER_NUM,&CYBSP_TIMER_config);
	/* Enable the initialized counter */
	Cy_TCPWM_Counter_Enable(CYBSP_TIMER_HW,CYBSP_TIMER_NUM);
	/* Then start the counter */
	 Cy_TCPWM_TriggerStart(CYBSP_TIMER_HW,CYBSP_TIMER_MASK);
	 /* Interrupt configuration*/
	 const cy_stc_sysint_t tcpwm_counter1_interrupt_config =
    {
		.intrSrc = CYBSP_TIMER_IRQ,
		.intrPriority = TCPWM_COUNTER1_INTR_PRIORITY,
    };
	 /* Initialize interrupt */
	 Cy_SysInt_Init(&tcpwm_counter1_interrupt_config,tcpwm_counter1_isr);
	 NVIC_ClearPendingIRQ(tcpwm_counter1_interrupt_config.intrSrc);
	 NVIC_EnableIRQ(tcpwm_counter1_interrupt_config.intrSrc);
}

static void tcpwm_counter1_isr(void)
{
	static int led_ctr = 0;
	static int servo_inact_ctr = 0;
	
	Cy_TCPWM_ClearInterrupt(CYBSP_TIMER_HW,CYBSP_TIMER_NUM, CY_TCPWM_INT_ON_TC);
	
	if(servo.current_duty < (servo.target_duty - 0.15))
	{
		/* Target duty cycle not reached - increment current duty cycle */
		servo.current_duty += 0.2;
		Cy_TCPWM_PWM_Enable(CYBSP_PWM_HW,CYBSP_PWM_NUM);
		Cy_TCPWM_PWM_SetCompare0(CYBSP_PWM_HW,CYBSP_PWM_NUM,get_pwm_compare_value(servo.current_duty));
		Cy_TCPWM_TriggerReloadOrIndex(CYBSP_PWM_HW,CYBSP_PWM_MASK);
		servo_inact_ctr = 0;
	}
	else if(servo.current_duty > (servo.target_duty + 0.15))
	{
		/* Target duty cycle not reached - decrement current duty cycle */
		servo.current_duty -= 0.2;
		Cy_TCPWM_PWM_Enable(CYBSP_PWM_HW,CYBSP_PWM_NUM);
		Cy_TCPWM_PWM_SetCompare0(CYBSP_PWM_HW,CYBSP_PWM_NUM,get_pwm_compare_value(servo.current_duty));
		Cy_TCPWM_TriggerReloadOrIndex(CYBSP_PWM_HW,CYBSP_PWM_MASK);
		servo_inact_ctr = 0;
	}
	else
	{
		servo_inact_ctr++;
	}
	/* Disable the PWM if the servo is inactive for the given period */
	if(servo_inact_ctr == 50)
	{
		Cy_TCPWM_PWM_Disable(CYBSP_PWM_HW,CYBSP_PWM_NUM);
	}
	
	if(led_ctr == 200)
	{	
		/* LED2 toggle */
		Cy_GPIO_Inv(P3_0_PORT,P3_0_NUM);
		/* LED3 toggle */
		Cy_GPIO_Inv(P1_0_PORT,P1_0_NUM);
		led_ctr = 0;
	}
	led_ctr++;
}

/*******************************************************************************
 * Function Name: initialize_capsense
 ******************************************************************************/
static void initialize_capsense(void)
{
	cy_capsense_status_t status = CY_CAPSENSE_STATUS_SUCCESS;

    /* CAPSENSE&trade; interrupt configuration MSCLP 0 */
    const cy_stc_sysint_t capsense_msc0_interrupt_config =
    {
		.intrSrc = CY_MSCLP0_LP_IRQ,
		.intrPriority = CAPSENSE_MSC0_INTR_PRIORITY,
    };

    /* Capture the MSC HW block and initialize it to the default state. */
    status = Cy_CapSense_Init(&cy_capsense_context);

    if(CY_CAPSENSE_STATUS_SUCCESS == status)
    {
		/* Initialize CAPSENSE&trade; interrupt for MSC 0 */
        Cy_SysInt_Init(&capsense_msc0_interrupt_config, capsense_msc0_isr);
        NVIC_ClearPendingIRQ(capsense_msc0_interrupt_config.intrSrc);
        NVIC_EnableIRQ(capsense_msc0_interrupt_config.intrSrc);

        status = Cy_CapSense_Enable(&cy_capsense_context);
    }

    if(status != CY_CAPSENSE_STATUS_SUCCESS)
    {
        /* This status could fail before tuning the sensors correctly.
         * Ensure that this function passes after the CAPSENSE&trade; sensors are tuned
         * as per procedure give in the Readme.md file */
    }
}

/*******************************************************************************
 * Function Name: capsense_msc0_isr
 ******************************************************************************/
static void capsense_msc0_isr(void)
{
    Cy_CapSense_InterruptHandler(CY_MSCLP0_HW, &cy_capsense_context);
}

/*******************************************************************************
 * Function Name: process_touch
 ******************************************************************************/
static void process_touch(void)
{
	uint32_t button0_status;
    uint32_t button1_status;
	cy_stc_capsense_touch_t *slider_touch_info;
	uint16_t slider_pos;
    uint8_t slider_touch_status;
	
	/* Initialize static variables to store previous touch status */
	static uint32_t button0_status_prev;
    static uint32_t button1_status_prev;
    static uint16_t slider_pos_prev;
	
	Cy_CapSense_ProcessAllWidgets(&cy_capsense_context);
	/* Get button0 status*/
	button0_status = Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON0_WDGT_ID, &cy_capsense_context);
	/* Get button1 status*/
	button1_status = Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON1_WDGT_ID, &cy_capsense_context);
	/* Get slider status */
	slider_touch_info = Cy_CapSense_GetTouchInfo(CY_CAPSENSE_LINEARSLIDER0_WDGT_ID, &cy_capsense_context);
	slider_touch_status = slider_touch_info->numPosition;
   	slider_pos = slider_touch_info->ptrPosition->x;

	/* Detect new touch on button0 */
	if((0u != button0_status) && (0u == button0_status_prev))
	{
		servo.target_duty = MAX_DUTY_CYCLE;
	}
	/* Detect new touch on button1*/
	if ((0u != button1_status) && (0u == button1_status_prev))
	{
		servo.target_duty = MIN_DUTY_CYCLE;
	}
	/* Detect new touches on the slider*/
	if ((0 != slider_touch_status) && (slider_pos != slider_pos_prev))
	{
		/* Set the target duty cycle based on the slider position */
		servo.target_duty = (float32_t)(slider_pos*100)/cy_capsense_context.ptrWdConfig[CY_CAPSENSE_LINEARSLIDER0_WDGT_ID].xResolution;
		servo.target_duty = ((MAX_DUTY_CYCLE-MIN_DUTY_CYCLE)*(1-servo.target_duty/100)) + MIN_DUTY_CYCLE;
	}
	
	/* Update previous touch status */
    button0_status_prev = button0_status;
    button1_status_prev = button1_status;
    slider_pos_prev = slider_pos;
}
/* [] END OF FILE */

MiniRobotArm Archive File

C/C++
Archive file containing the ModusToolbox project for the MiniRobotArm
No preview (download only).

Credits

Infineon Team
131 projects • 209 followers
Hands-on projects, tutorials, and code for sensors, MCUs, connectivity, security, power, and IoT. Follow for new builds and ideas.
Martin Schiestl
1 project • 1 follower
Patrick Haselwanter
1 project • 1 follower

Comments