Hackster is hosting Hackster Holidays, Ep. 4: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Wednesday!Stream Hackster Holidays, Ep. 4 on Wednesday!
Timothy Malche
Created February 26, 2023 © MIT

Touch Sensitive Home Control

This project uses PSoC 4100S Capsense Pioneer Kit to build touch interface to control home appliances.

IntermediateWork in progress10 hours53
Touch Sensitive Home Control

Things used in this project

Hardware components

Infineon PSoC 4100S Capsense Pioneer Kit
×1
1-Channel Signal Relay 1A SPDT I²C Mini Module
ControlEverything.com 1-Channel Signal Relay 1A SPDT I²C Mini Module
×1
Light Bulb
×1
Electric Wires
×1

Story

Read more

Schematics

CIrcuit Diagram

CIrcuit Diagram

Code

Main Code

C/C++
Final code to control appliances
/******************************************************************************
* File Name: main.c
*
* Version: 1.00
*
* Description: This code example demonstrates how to implement a low-power 
*              CapSense button with an average current consumption of 5uA
*              per button. 
*
* Related Document: CE214022 LP CapSense Buttons.pdf
*
* Hardware Dependency: See code example CE214022
*
******************************************************************************
* Copyright (2016), Cypress Semiconductor Corporation.
******************************************************************************
* This software, including source code, documentation and related materials
* ("Software") is owned by Cypress Semiconductor Corporation (Cypress) and is
* protected by and subject to worldwide patent protection (United States and 
* foreign), United States copyright laws and international treaty provisions. 
* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable
* license to copy, use, modify, create derivative works of, and compile the 
* Cypress source code and derivative works for the sole purpose of creating 
* custom software in support of licensee product, such licensee product to be
* used only in conjunction with Cypress's integrated circuit as specified in the
* applicable agreement. Any reproduction, modification, translation, compilation,
* or representation of this Software except as specified above is prohibited 
* without the express written permission of Cypress.
* 
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND, 
* EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* Cypress reserves the right to make changes to the Software without notice. 
* Cypress does not assume any liability arising out of the application or use
* of Software or any product or circuit described in the Software. Cypress does
* not authorize its products for use as critical components in any products 
* where a malfunction or failure may reasonably be expected to result in 
* significant injury or death ("ACTIVE Risk Product"). By including Cypress's 
* product in a ACTIVE Risk Product, the manufacturer of such system or application
* assumes all risk of such use and in doing so indemnifies Cypress against all
* liability. Use of this Software may be limited by and subject to the applicable
* Cypress software license agreement.
*****************************************************************************/
/*******************************************************************************
*   Included Headers
*******************************************************************************/
#include "project.h"

/*****************************************************************************
* MACRO Definitions
*****************************************************************************/   

/* Boolean constants */
#define TRUE						    (1u)
#define FALSE					        (0u)
#define ON						        (TRUE)
#define OFF							    (FALSE)
#define SET							    (ON)
#define CLEAR						    (OFF)

/* General Constants */    
#define ZERO					        (0u)

/* Macros to find rising and falling edge on the data passed */
#define RISING_EDGE_DET(curr, prev)		((curr ^ prev) & curr)
#define FALLING_EDGE_DET(curr, prev)	((curr ^ prev) & prev)

/* Button mask registers */
#define LFT_BUTTON_MASK					(0x01 << CapSense_BUTTON_SNS0_ID)
#define RGT_BUTTON_MASK					(0x01 << CapSense_BUTTON_SNS1_ID)

/* Device power mode related macros */
#define LOOP_TIME_FASTSCANMODE          (20u)
#define LOOP_TIME_SLOWSCANMODE          (100u)
#define MILLI_SEC_TO_MICRO_SEC          (1000u)
#define ILO_CLOCK_FACTOR                (40u)
    
/* Refresh rate control parameters */    
#define WDT_TIMEOUT_FAST_SCAN           (ILO_CLOCK_FACTOR * LOOP_TIME_FASTSCANMODE)   
#define WDT_TIMEOUT_SLOW_SCAN           (ILO_CLOCK_FACTOR * LOOP_TIME_SLOWSCANMODE)

/* Interrupt number for watchdog */    
#define WDT_IRQ_NUMBER                  (6u)
    
/* Reset value of softCounter */    
#define RESET					        (0u)

/* This timeout is for changing the refresh interval from fast to slow rate
*  The timeout value is WDT_TIMEOUT_FAST_SCAN * SCANMODE_TIMEOUT_VALUE
*/
#define SCANMODE_TIMEOUT_VALUE          (150u)

/* Firmware implements two refresh rates for reducing average power consumption */
typedef enum
{
    SLOW_SCAN_MODE = 0u,
    FAST_SCAN_MODE = 1u
} SCAN_MODE;



/* Finite state machine states for device operating states */
typedef enum
{
    SENSOR_SCAN = 0x01u, /* Sensor is scanned in this state */
    WAIT_FOR_SCAN_COMPLETE = 0x02u, /* CPU is put to sleep in this state */
    PROCESS_DATA = 0x03u, /* Sensor data is processed */
    SLEEP = 0x04u /* Device is put to deep sleep */
} DEVICE_STATE;



/* Definition that combines all possible processing tasks other than applying filters */
#define CapSense_PROCESS_CUSTOM    ( CapSense_PROCESS_BASELINE      | \
                                         CapSense_PROCESS_DIFFCOUNTS    | \
                                         CapSense_PROCESS_CALC_NOISE    | \
                                         CapSense_PROCESS_THRESHOLDS    | \
                                         CapSense_PROCESS_DECONVOLUTION | \
                                         CapSense_PROCESS_STATUS )
/*******************************************************************************
*   Module Variable and Constant Declarations with Applicable Initializations
*******************************************************************************/

/* Variable to store interrupt state */
uint32 interruptState = 0u;

/* Variable to check the WDT interrupt state */
volatile uint8 wdtInterruptOccured = FALSE;

/* Contains watchdog match value to generate periodic interrupt */
volatile uint32 watchdogMatchValue = WDT_TIMEOUT_FAST_SCAN;


/*****************************************************************************
* Function Prototypes
*****************************************************************************/ 
/* API to control the RELAY module to turn bulb ON/OFF */
void RELAY_Control(uint32 buttonStatus);

/* API to get WDT match value to generate precise scan intervals */
void CalibrateWdtMatchValue(void);

/* API to prepare the device for deep sleep */
void EnterDeepSleepLowPowerMode(void);

/* API to configure the WDT timer for controlling scan intervals */
void WDT_Start(void);

/******************************************************************************
* Function Name: main
*******************************************************************************

*
*******************************************************************************/
int main()
{	
    /* Variable to hold current button status */
	uint32 buttonStatus = OFF;		

    /* Variable to hold the current device state 
    *  State machine starts with sensor scan state after power-up
    */
    DEVICE_STATE currentState = SENSOR_SCAN; 
	
    /* This variable is used to indicate the current power mode */
    SCAN_MODE deviceScanMode = FAST_SCAN_MODE;
    
    /* This variable is used to implement a software counter. If the value 
    *  of this counter is greater than SCANMODE_TIMEOUT_VALUE, it indicates that the button sensor 
    *  was inactive for more than 3s. 
    */
  
    /* Set Appliances of */
    RELAY_Write(0);

    /* Enable global interrupts. */ 
    CyGlobalIntEnable;    
    
    /* Initialize I2C component for CapSense tuner */
    EZI2C_Start();
    
    /* Set up communication data buffer to CapSense data structure to 
    *  expose to I2C master at primary slave address request		
    */
    EZI2C_EzI2CSetBuffer1(sizeof(CapSense_dsRam), sizeof(CapSense_dsRam),\
                         (uint8 *)&CapSense_dsRam);
    
    /* Initialize the CapSense CSD block */
	CapSense_Start();
    
    /* Configure CapSense parameters for button widget to scan them individually */
    CapSense_SetupWidget(CapSense_BUTTON_WDGT_ID);
                        
             
    /* Watchdog is used to control the loop time in this project and watchdog
    *  is set to generate interrupt at every LOOP_TIME_FASTSCANMODE in fast scan mode  
    *  and at LOOP_TIME_SLOWSCANMODE in slow scan mode
    */
    WDT_Start();

    for(;;)
    {  
        /* Switch between sensor-scan -> wait-for-scan -> process -> sleep states */
        switch(currentState)
        {
            case SENSOR_SCAN:                 
                /* Initiate new scan only if the CapSense hardware is idle */
                if(CapSense_NOT_BUSY == CapSense_IsBusy())
                {   
                    /* Update CapSense parameters set via CapSense tuner before the 
                    *  beginning of CapSense scan. This check if required if we are using 
                    *  CapSense Extension (Ext) APIs in the project.
                    */
                    if(CapSense_STATUS_RESTART_DONE == CapSense_RunTuner())
                    {      
                        if(deviceScanMode == FAST_SCAN_MODE)
                        {
                            /* Configure button widget after parameter is updated via tuner */
                            CapSense_CSDSetupWidget(CapSense_BUTTON_WDGT_ID);
                        }
                        else
                        {
                            /* Configure ganged widget after parameter is updated via tuner */
                            CapSense_CSDSetupWidgetExt(CapSense_GANGEDBUTTON_WDGT_ID, CapSense_GANGEDBUTTON_SNS0_ID);
                        }
                    }
                    /* In fast scan mode, left button and right button is scanned individually */
                    if(deviceScanMode == FAST_SCAN_MODE)
                    {
                        /* Scan the sensors which are in the button widget i.e left button and right button */
                        CapSense_CSDScan();
                    }
                    /* In slow scan mode, only ganged sensor is scanned. Since we have only one ganged sensor,
                    *  the sensor is always connected to AMUXBUS in this mode.
                    *  This avoids time taken to connect the sensor to AMUXBUS every time the ganged sensor is scanned. 
                    */
                    else
                    {
                        CapSense_CSDScanExt();
                    } 
                    /* Put CPU to sleep while sensor scanning is in progress */
                    currentState = WAIT_FOR_SCAN_COMPLETE;                    
                }
            break;

            case WAIT_FOR_SCAN_COMPLETE:
                /* Device is in CPU Sleep until CapSense scanning is complete or
                *  device is woken-up by either CapSense interrupt or I2C interrupt 
                */
                /* Disable interrupts, so that ISR is not serviced while
                *  checking for CapSense scan status.
                */
                interruptState = CyEnterCriticalSection();
                            
                /* Check if CapSense scanning is complete */
                if(CapSense_NOT_BUSY != CapSense_IsBusy())
                {
                    /* If CapSense scannning is in progress, put CPU to sleep */
                    CySysPmSleep();
                }
                /* If CapSense scanning is complete, process the CapSense data */
                else
                {
                    currentState = PROCESS_DATA;
                }
                /* Enable interrupts for servicing ISR */
                CyExitCriticalSection(interruptState);
            break;
        
            case PROCESS_DATA:
                /* Set next state to SLEEP */
                currentState = SLEEP;
                
                /* Clear the variable that is used for both the button status */
                buttonStatus = CLEAR;
                
                if(deviceScanMode == FAST_SCAN_MODE)
                {
                    /* Update baseline and sensor status for both the button sensors */
                    CapSense_ProcessWidgetExt(CapSense_BUTTON_WDGT_ID, CapSense_PROCESS_CUSTOM);
                    
                    /* If button widget is reported active, process the 
                    *  button active status 
                    */
                    if(CapSense_IsWidgetActive(CapSense_BUTTON_WDGT_ID))
        			{	
                        /* Set left button state */
                        if(CapSense_IsSensorActive(CapSense_BUTTON_WDGT_ID, CapSense_BUTTON_SNS0_ID))
                        {
                            buttonStatus |= LFT_BUTTON_MASK;
                        }
                        /* Set right button state */
                        if(CapSense_IsSensorActive(CapSense_BUTTON_WDGT_ID, CapSense_BUTTON_SNS1_ID))
                        {
                            buttonStatus |= RGT_BUTTON_MASK;
                        }
                        /* Control RELAY based on button status */
                        RELAY_Control(buttonStatus);
                        
                        /* Reset the software counter if any button is active. */
        			   
        			}
                    /* If none of the buttons are active, execute below code */
        			
                }   
                /* If deviceScanMode is SLOW_SCAN_MODE, perform the following tasks */    
                else
                {   
                    /* Update baselines of ganged button widget */
                    CapSense_ProcessWidget(CapSense_GANGEDBUTTON_WDGT_ID);
                    
                    /* If ganged widget is active in slow scan mode move to 
                    *  fast scan mode where sensors are scanned individually at LOOP_TIME_FASTSCANMODE
                    */
                    if(CapSense_IsWidgetActive(CapSense_GANGEDBUTTON_WDGT_ID))
                    {
                        /* If sensor is active in slow-scan mode, skip sleep
                        *  and perform sensor scan
                        */
                        currentState = SENSOR_SCAN;
                        
                        /* Set watchdog match value to fast scan mode */
    			        watchdogMatchValue = WDT_TIMEOUT_FAST_SCAN;      
                        
                        /* Change the device mode to fast scan mode to provide fast touch response */
                        deviceScanMode = FAST_SCAN_MODE; 
                        
                        /* Configure button widgets parameters for scanning in fast scan mode */
                        CapSense_SetupWidget(CapSense_BUTTON_WDGT_ID);
  
                    }
                }
            break;
            
            case SLEEP:             
                /* In fast scan mode, put the device to only CPU sleep as TCPMW block is ON
                *  
                */
                if(deviceScanMode == FAST_SCAN_MODE)
                {   
                    CySysPmSleep();
                }
                /* In slow scan mode, device can be put to deep-sleep as TCPWM is off */
                else
                {
                    EnterDeepSleepLowPowerMode();
                }
                /* Start scanning the sensors only if interrupt occured due to WDT.
                   Interrupt can also occur due to I2C interrupt while tuner is running.
                   In such cases, sensor is not scanned until WDT interrupt has occured
                */
                if(wdtInterruptOccured)
                {
                    /* Set state to scan sensor after device wakes up from sleep */
                    currentState = SENSOR_SCAN;
                    
                    wdtInterruptOccured = FALSE;  
                    
                    /* Calibrate the ILO to ensure accurate scan intervals */
                    CalibrateWdtMatchValue();
                }
            break;
            
            default:
                /*******************************************************************
                 * Unknown power mode state. Unexpected situation.
                 ******************************************************************/
                CYASSERT(0);
            break;
        } 
    }
    
}

/*******************************************************************************
* Function Name: RELAY_Control
********************************************************************************
* Summary:
* Controls the RELAY module.
*
* Parameters:
* 	buttonStatus: Variable indicates the button status of left-button and right-button
*
* Return:
*  void

*
*******************************************************************************/

void RELAY_Control(uint32 buttonStatus)
{
    
     
	/* Variable to store previous button status */
	static uint32 prevButtonStatus = OFF;		
			
	    
    /* If any button is active */
	if(buttonStatus != OFF)
	{
        
        	/* Detect the rising edge on buttonStatus */
    		switch(RISING_EDGE_DET(buttonStatus, prevButtonStatus))
    		{
    			/* Upon left button activation */
    			case (LFT_BUTTON_MASK):
                {
                    RELAY_Write(1);
                }
    			break;
                                                                            
    			/* Right button activation */
    			case (RGT_BUTTON_MASK):
                {
                     RELAY_Write(0);
                }    
    			break;
    									
    			default:
                    break;
    		}
    	
            
      
    }
    /* Store the current button status to previous status */
	prevButtonStatus = buttonStatus;

}

/******************************************************************************
* Function Name: Timer_Interrupt
*******************************************************************************
*
* Summary:
*  Handles the Interrupt Service Routine for the WDT timer.
*
* Parameters:
*  None.
*
* Return:
*  None.
*
* Theory: The interrupt is cleared on the ISR as watchdog in this project is 
*         used for timing maintenance only. Match value is updated to maintain 
*         the loop time. 
*
* Side Effects: None
*
* Note:
*
*******************************************************************************/
CY_ISR(Timer_Interrupt)
{
    /* Clear the watchdog interrupt */
    CySysWdtClearInterrupt();    
    
    /* WDT match value is updated in order to obtain periodic interrupts */
    CySysWdtWriteMatch(CySysWdtReadMatch() + watchdogMatchValue); 
    
    /* Set to variable that indicates that WDT interrupt had triggered*/
    wdtInterruptOccured = TRUE;   
}

/******************************************************************************
* Function Name: WDT_Start
*******************************************************************************
*
* Summary:
*  Configures WDT.
*
* Parameters:
*  None.
*
* Return:
*  None.
*
* Theory: This API unmasks the WDT interrupt to route the interrupt to CPU and 
*         configures the ISR.
*
* Side Effects: None
*
* Note:
*
*******************************************************************************/
void WDT_Start(void)
{
    /* Setup ISR for watchdog interrupt */
    CyIntSetVector(WDT_IRQ_NUMBER, Timer_Interrupt);
   
    /* Get the actual match value required to generate a given delay */
    CalibrateWdtMatchValue();
    
     /* WDT match value is updated in order to obtain periodic interrupts */
    CySysWdtWriteMatch(CySysWdtReadMatch() + watchdogMatchValue); 
    
    /* Pass the WDT interrupt from SRSS to NVIC */
    CySysWdtUnmaskInterrupt();
    
    /* Enable WDT interrupt in NVIC to pass it to CPU */
    CyIntEnable(WDT_IRQ_NUMBER);
}

/*******************************************************************************
* Function Name: CalibrateWdtMatchValue
********************************************************************************
* Summary: 
*  This function calibrates the match value of the Watchdog Timer 
*
* Parameter:
*  None
*
* Return:
*  void
*
* Theory: The ILO is calibrated using IMO to improve the accuracy of ILO.
*
* Side Effects: None
*
* Note:
*
*******************************************************************************/
void CalibrateWdtMatchValue()
{    
    /* Contains ILO Trimmed value */
    uint32 tempIloCounts = 0u;
    
    /* Desired delay in microseconds for ILO Trimming */
    uint32 desiredDelay = ((watchdogMatchValue / ILO_CLOCK_FACTOR) * MILLI_SEC_TO_MICRO_SEC);  
    
    /* Get the ILO compensated counts i.e. the actual counts for the desired ILO frequency 
	*  Trimming is done to improve ILO accuracy using IMO; ILO default accuracy is +/- 60% 
    */
    if(CYRET_SUCCESS == CySysClkIloCompensate(desiredDelay, &tempIloCounts))
    {    
        watchdogMatchValue = (uint32)tempIloCounts;
    }    
}

/*******************************************************************************
* Function Name: EnterDeepSleepLowPowerMode
********************************************************************************
* Summary:
*  Put the device to DeepSleep power mode. Reconfigures the Components for 
*  normal operation after wake-up. 
*
* Parameters:
*  void
*
* Return:
*  void
*
* Theory: Before going to deep sleep, the API checks for any
*         I2C activity and waits till the I2C transaction is complete before 
*         the device is put to deep sleep. 
*
* Side Effects: None
*
* Note:
*
*******************************************************************************/
void EnterDeepSleepLowPowerMode(void)
{
    /* EZI2C_Sleep routine should be called only after on-going I2C 
    *  transaction is complete
    *  Enter critical section to lock slave state 
    */
    interruptState = CyEnterCriticalSection();
    
    /* Check if I2C is busy. If so, skip deep sleep until I2C is free */
    if(!(EZI2C_EzI2CGetActivity() & EZI2C_EZI2C_STATUS_BUSY))
    {
        /* Prepare for deep sleep - stop ILO calibration */
        CySysClkIloStopMeasurement();
        
        /* Configure slave to be wakeup source */
        EZI2C_Sleep();
        
    	/* Enter DeepSleep. */
    	CySysPmDeepSleep();	
       
        /* WDT or any I2C communication wakes up device from deep sleep. */

        /* Configure slave for active mode operation */
        EZI2C_Wakeup();
        
        /* Start the ILO accuracy measurement for accurate scan interval generation */
        CySysClkIloStartMeasurement();
    }
    /* Enable interrupts */
    CyExitCriticalSection(interruptState);
}

/* [] END OF FILE */

Credits

Timothy Malche

Timothy Malche

17 projects • 22 followers
Maker, Educator, Researcher

Comments