/******************************************************************************* * * CapTouch_BoosterPack_UserExperience_GUI.pde * - PC demo application for establishing a serial connection * with the LaunchPad CapTouch BoosterPack. * * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ******************************************************************************/ /****************************************************************************** * MSP430G2-LaunchPad CapTouch BoosterPack User Experience GUI * Desc: * This PC GUI application communicates with the LaunchPad specifically to * receive capacitive touch data from the LaunchPad CapTouch BoosterPack and * provides the visualization of said information in the GUI. * * The GUI uses a small .NET utility (FindAppUART.exe) to automatically detect * a proper LaunchPad/430Emulator device connected to the PC USB port. Upon * correct USB COM port discovery, the application initiates a 9600baud UART * connection and starts receiving data. * * Upon each LaunchPad event, data is transmitted [always] via a simple '2-byte' * protocol as described below. * [LaunchPad] Wake up : 0xBE 0xEF * [LaunchPad] Sleep : 0xDE 0xAD * [CapTouch] Center Button : 0x80 0x80 * [CapTouch] Wheel Tap : WT WT = pos. on wheel [0-0x0F] + 0x30 * [CapTouch] Gesture Start : 0xFC POS = pos. on wheel [0-0x0F] + 0x20 * [CapTouch] Gesture Stop : 0xFB POS = pos. on wheel [0-0x0F] + 0x20 * [CapTouch] Gesture Update: GES POS * Gesture = [0-0x0F] --> Clockwise gesture * = 0x10+ [0-0x0F] --> Counter-clockwise gesture * * The GUI grays out during sleep mode and returns to active mode upon wake up. * The 'Center Button' press data toggles the center circle color, mimicking the * behavior of the center LED on the BoosterPack. * The 'Wheel Tap' is represented by lighting up a single slice on the wheel. * The gesture tracking [Start, Stop, Update] is visualized on the wheel with * the coloration of the wheel slices. Gesture can be tracked for several * revolutions of the wheel, in both clockwise and counter-clockwise directions. * * A hidden code/lock is embedded in the wheel. Correct sequence [similar to a * rotational combination lock] reveals a secret address. * * D. Dang * Texas Instruments, Inc. * Ver 0.90 Feb 2011 ******************************************************************************/ import pitaru.sonia_v2_9.*; import processing.serial.*; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; final int TIME_OUT = 140; /*--------Dimensions & coordinates------------*/ final int CANVAS_SIZE_X = 900; final int CANVAS_SIZE_Y = 580; final int OUT_CIRCLE_RADIUS = 248; final int IN_CIRCLE_RADIUS = 101; final int CENTER_CIRCLE_RADIUS = 37; final int CIRCLE_CENTER_X = 450; final int CIRCLE_CENTER_Y = 285; final int SOUND_ICON_X = 820; final int SOUND_ICON_Y = 20; /*--------Drawing definitions------------*/ final int NUMBER_OF_SLICES = 16; final int BACKGROUND_COLOR = 170; final int TAP_SLICE_COLOR = -100; final int SLICE_TRANSPARENCY = 30; final int SLICE_TRANSPARENCY_OFFSET = 80; /*--------UART protocol definitions------------*/ final int WAKE_UP_UART_CODE = 0xBE; final int WAKE_UP_UART_CODE2 = 0xEF; final int SLEEP_MODE_UART_CODE = 0xDE; final int SLEEP_MODE_UART_CODE2 = 0xAD; final int CENTER_BUTTON_CODE = 0x80; final int INVALID_WHEEL_POSITION = 0xFE; final int INVALID_GESTURE = 0xFD; final int GESTURE_START = 0xFC; final int GESTURE_STOP = 0xFB; final int GESTURE_POSITION_OFFSET = 0x20; final int WHEEL_POSITION_OFFSET = 0x30; final int NUMBER_OF_WHEEL_POSITIONS = 16; //final int INVALID_WHEEL_POSITION = -100; final int INVALID_GESTURE_DIRECTION = -100; final int GESTURE_CLOCKWISE = 1; final int GESTURE_COUNTERCLOCKWISE = -1; /*----------CapTouch-related variables----------------*/ int gestureStartingPosition = INVALID_WHEEL_POSITION, gestureStoppingPosition = INVALID_WHEEL_POSITION; int[] gestureCoverPositions = new int[16]; int gestureDirection = INVALID_GESTURE_DIRECTION; int gestureImmediateDirection = INVALID_GESTURE_DIRECTION; int allLit = 0; int inactivityCounter=0, sleeping=0, tapping=0, centerButton=0; int CenterButtonToggle=0; /*------Serial communication----------------*/ int LaunchPadComPortFound = 0, numberOfLookingDots=0; Serial LaunchPad; /*--------- Visual & audio elements-----------*/ PImage backgroundImage, innerCircleImage,innerCircleSelectedImage, innerCircleUnlockedImage ; int drawNumberEnabled = 0; Sample click,clickFound, clickOpen; int soundEnabled = 0; void drawSlice(int sliceIndex, int sliceLevel) { int sliceAfter, sliceLabel; sliceLabel = sliceIndex; if (sliceIndex < 3) sliceIndex = sliceIndex + 16 - 4; else sliceIndex = sliceIndex - 4; sliceAfter = sliceIndex + 1; if (sliceAfter == NUMBER_OF_SLICES) sliceAfter = 0; noStroke(); stroke(255); strokeWeight(4); if (sliceLevel == TAP_SLICE_COLOR) fill(160,160,160, 225); else fill(252,236,54, sliceLevel * SLICE_TRANSPARENCY + SLICE_TRANSPARENCY_OFFSET); arc(CIRCLE_CENTER_X, CIRCLE_CENTER_Y, OUT_CIRCLE_RADIUS*2, OUT_CIRCLE_RADIUS*2, (((float)sliceIndex)-0.5) * 2 * PI /16, (((float)sliceIndex)+0.5) * 2 * PI /16); line( CIRCLE_CENTER_X + cos( (((float)sliceIndex)-0.5) * 2 * PI /16 ) * OUT_CIRCLE_RADIUS, CIRCLE_CENTER_Y + sin((((float)sliceIndex)-0.5) * 2 * PI /16 ) * OUT_CIRCLE_RADIUS, CIRCLE_CENTER_X + cos( (((float)sliceIndex)-0.5) * 2 * PI /16 ) * IN_CIRCLE_RADIUS, CIRCLE_CENTER_Y + sin((((float)sliceIndex)-0.5) * 2 * PI /16 ) * IN_CIRCLE_RADIUS); line( CIRCLE_CENTER_X + cos( (((float)sliceIndex)+0.5) * 2 * PI /16 ) * OUT_CIRCLE_RADIUS, CIRCLE_CENTER_Y + sin((((float)sliceIndex)+0.5) * 2 * PI /16 ) * OUT_CIRCLE_RADIUS, CIRCLE_CENTER_X + cos( (((float)sliceIndex)+0.5) * 2 * PI /16 ) * IN_CIRCLE_RADIUS, CIRCLE_CENTER_Y + sin((((float)sliceIndex)+0.5) * 2 * PI /16 ) * IN_CIRCLE_RADIUS); if (drawNumberEnabled == 1) { fill(0); text( sliceLabel, CIRCLE_CENTER_X + cos( (((float)sliceIndex)) * 2 * PI /16 ) * (OUT_CIRCLE_RADIUS-40)-7, CIRCLE_CENTER_Y + sin( (((float)sliceIndex)) * 2 * PI /16 ) * (OUT_CIRCLE_RADIUS-40)); } } void drawCanvas() { int i; background(BACKGROUND_COLOR); image(backgroundImage, 0 ,0); fill(0,0,0,255); if (unlocked == 1) { fill(0); text("BoosterPack Unlocked",300,30); for (i=0;i<4;i++) drawSlice(code[i],4); fill(255); ellipse(CIRCLE_CENTER_X, CIRCLE_CENTER_Y, IN_CIRCLE_RADIUS*2 , IN_CIRCLE_RADIUS*2); image(innerCircleUnlockedImage, 0, 0); } } void goToSleep() { tint(120,120,120,180); drawCanvas(); tint(120,120,120,180); fill(255); textSize(20); text(".",20,30); text(".",28,30); text(".",35,30); textSize(25); text("z",47,30); textSize(30); text("z",65,30); textSize(35); text("z",85,30); textSize(40); text("z",108,30); textSize(30); noTint(); sleeping = 1; inactivityCounter = TIME_OUT; } void findLaunchPad() { String ComPortName =""; try { Process proc = Runtime.getRuntime().exec("FindAppUART.exe"); proc.waitFor(); int exitVal = proc.exitValue(); // Get the first line from the process' STDOUT BufferedReader buf = new BufferedReader(new InputStreamReader(proc.getInputStream())); ComPortName = buf.readLine(); if (ComPortName.substring(0,3).equals("COM") != true) ComPortName = ""; else { LaunchPadComPortFound = 1; LaunchPad = new Serial(this, ComPortName, 9600); } } catch(Exception e) { println(e); } } void promptLookingForLaunchPad() { fill(255,0,0); noStroke(); rect(100,CANVAS_SIZE_Y/2-100,CANVAS_SIZE_X-120,200); fill(255); textSize(50); numberOfLookingDots++; if (numberOfLookingDots==5) numberOfLookingDots = 1; switch(numberOfLookingDots) { case 1: text("Looking for LaunchPad ", 175,CANVAS_SIZE_Y/2 ); break; case 2: text("Looking for LaunchPad . ", 175,CANVAS_SIZE_Y/2 ); break; case 3: text("Looking for LaunchPad .. ", 175,CANVAS_SIZE_Y/2 ); break; case 4: text("Looking for LaunchPad ...", 175,CANVAS_SIZE_Y/2 ); break; } } void setup() { size(CANVAS_SIZE_X, CANVAS_SIZE_Y); background(255,0,0); Sonia.start(this); click = new Sample("click1.aiff"); clickOpen = new Sample("open.aiff"); clickFound = new Sample("unlock.aiff"); click.setVolume(3); clickFound.setVolume(3); frameRate(3); backgroundImage = loadImage("background.png"); innerCircleImage = loadImage("innerCircle.png"); innerCircleSelectedImage = loadImage("innerCircleSelected.png"); innerCircleUnlockedImage = loadImage("innerCircleUnlocked.png"); findLaunchPad(); if (LaunchPadComPortFound == 0) { fill(255); textSize(40); text("LaunchPad Capacitive Touch BoosterPack", 70,50); textSize(55); fill(0); text("User Experience Demo", 150,100); fill(0); textSize(25); text("1. Plug your Capacitive Touch BoosterPack into the LaunchPad", 150,CANVAS_SIZE_Y - 70); text("2. Connect your LaunchPad to the PC via USB", 150,CANVAS_SIZE_Y - 45); promptLookingForLaunchPad(); } else { frameRate(30); goToSleep(); } } void draw() { int i; if (LaunchPadComPortFound==0) { findLaunchPad(); if (LaunchPadComPortFound==1) { goToSleep(); frameRate(30); } else promptLookingForLaunchPad(); } else if(LaunchPad.available() >= 0) { int buf, buf1; buf = LaunchPad.read(); buf1 = -1; if (buf>=0) { drawCanvas(); textSize(30); sleeping = 0; inactivityCounter=0; tapping = 0 ; centerButton = 0; switch(buf) { case WAKE_UP_UART_CODE: while (buf1 <0) buf1 = LaunchPad.read(); if (buf1==WAKE_UP_UART_CODE2) text("Proximity Sensor Wake Up",20,30); else { print("Error: invalid UART Wake up == "); println(buf1); } break; case SLEEP_MODE_UART_CODE: while (buf1 <0) buf1 = LaunchPad.read(); if (buf1==SLEEP_MODE_UART_CODE2) { //tint(120,0,0,180); CenterButtonToggle = 0; backgroundImage = loadImage("background.png"); innerCircleImage = loadImage("innerCircle.png"); tint(120,120,120,180); drawCanvas(); text("Good night!",20,30); sleeping = 1; noTint(); } else { print("Error: invalid UART Sleep == "); println(buf1); } break; case CENTER_BUTTON_CODE: while (buf1 <0) buf1 = LaunchPad.read(); if (buf==buf1) { if (unlocked==1) { unlocked = 0; link(secretURL); } CenterButtonToggle = 1 - CenterButtonToggle; if (CenterButtonToggle == 1) { innerCircleImage= loadImage("innerCircleSelected.png"); backgroundImage = loadImage("backgroundSelected.png"); } else { innerCircleImage= loadImage("innerCircle.png"); backgroundImage = loadImage("background.png"); } drawCanvas(); centerButton = 1; } else { print("Error: invalid CENTER BUTTON code == "); println(buf1); } break; case GESTURE_STOP: while (buf1 <0) buf1 = LaunchPad.read(); if (buf==buf1) { text("Gesture Released",20,30); gestureStartingPosition = INVALID_WHEEL_POSITION; gestureStoppingPosition = INVALID_WHEEL_POSITION; gestureDirection = INVALID_GESTURE_DIRECTION; allLit = 0; codeCheck = 0; codeLevel = 0; //unlocked = 0; } else { print("Error: invalid GESTURE_STOP code == "); println(buf1); } break; case GESTURE_START: while (buf1 <0) buf1 = LaunchPad.read(); if ( (buf1 < GESTURE_POSITION_OFFSET ) || (buf1 > GESTURE_POSITION_OFFSET + NUMBER_OF_WHEEL_POSITIONS)) { print("Error: invalid gesture start position == "); println(buf1); } else { text("Gesture Detected",20,30); buf1 = buf1 - GESTURE_POSITION_OFFSET; if (buf1<0) println(buf); gestureStartingPosition = buf1; gestureStoppingPosition = buf1; if (buf1 == code[0]) codeCheck = 1; else codeCheck = -1; codeLevel = 0; changeDirection = 0; drawSlice(buf1, 1); fill(255); ellipse(CIRCLE_CENTER_X, CIRCLE_CENTER_Y, IN_CIRCLE_RADIUS*2 , IN_CIRCLE_RADIUS*2); image(innerCircleImage,0,0); } break; default: // data from LaunchPad is not code, but value if (buf > WHEEL_POSITION_OFFSET + NUMBER_OF_WHEEL_POSITIONS ) // Invalid Code { print("Error: invalid UART code == "); println(buf); } else /*---------------------WHEEL POSITION-----------------------*/ if ((buf <= WHEEL_POSITION_OFFSET + NUMBER_OF_WHEEL_POSITIONS) && (buf >= WHEEL_POSITION_OFFSET )) // Tapping { while (buf1 <0) buf1 = LaunchPad.read(); if (buf==buf1) { buf = buf - WHEEL_POSITION_OFFSET; text("Press @ ",10,30); text(buf,125,30); tapping = 1; drawSlice(buf, TAP_SLICE_COLOR); fill(255); ellipse(CIRCLE_CENTER_X, CIRCLE_CENTER_Y, IN_CIRCLE_RADIUS*2 , IN_CIRCLE_RADIUS*2); image(innerCircleImage, 0,0); } else { print("Error: invalid WHEEL POSITION code == "); println(buf); } } else /*---------------------GESTURE DATA-----------------------*/ if (buf < GESTURE_POSITION_OFFSET ) // Gesturing if (gestureStartingPosition != INVALID_WHEEL_POSITION) { if (buf>=NUMBER_OF_WHEEL_POSITIONS) // Determine orientation: binary value 00000xxx = CW, 00001xxx = CC { buf -= NUMBER_OF_WHEEL_POSITIONS; text(buf, 20,30); text("Counter-Clockwise",55,30); gestureImmediateDirection = GESTURE_COUNTERCLOCKWISE; } else { text(buf, 20,30); text("Clockwise",55,30); gestureImmediateDirection = GESTURE_CLOCKWISE; } while (buf1 <0) buf1 = LaunchPad.read(); if ( (buf1 >= GESTURE_POSITION_OFFSET ) && (buf1 < GESTURE_POSITION_OFFSET + NUMBER_OF_WHEEL_POSITIONS)) { buf1 = buf1 - GESTURE_POSITION_OFFSET; if (gestureDirection == INVALID_GESTURE_DIRECTION) gestureDirection = gestureImmediateDirection; // if (gestureImmediateDirection != gestureDirection) // gestureCoverPositions[gestureStoppingPosition] = 0; // while (buf-->0) { if (gestureStoppingPosition == gestureStartingPosition) //If moving from the starting position if (gestureDirection != gestureImmediateDirection) //If moving against the current direction { if (allLit == 0) //Starting at zero? Change direction gestureDirection = gestureImmediateDirection; else //Back to a previous revolution? allLit--; } gestureStoppingPosition += gestureImmediateDirection; if (gestureStoppingPosition == NUMBER_OF_WHEEL_POSITIONS) gestureStoppingPosition = 0; if (gestureStoppingPosition < 0) gestureStoppingPosition = NUMBER_OF_WHEEL_POSITIONS-1; if (gestureStoppingPosition == gestureStartingPosition) //If moving to the starting position { if (gestureImmediateDirection == gestureDirection) //complete a revolution? allLit += 1; else if (allLit == 0) //Back to zero gestureDirection = INVALID_GESTURE_DIRECTION; //Undo } } for ( i = 0; i < NUMBER_OF_WHEEL_POSITIONS; i++) gestureCoverPositions[i] = allLit; i = gestureStartingPosition; while (i != gestureStoppingPosition) { gestureCoverPositions[i]= allLit + 1; i += gestureDirection; if (i<0) i = NUMBER_OF_WHEEL_POSITIONS-1; if (i == NUMBER_OF_WHEEL_POSITIONS) i = 0; } gestureCoverPositions[i]= allLit + 1; for ( i = 0; i < NUMBER_OF_WHEEL_POSITIONS; i++) if (gestureCoverPositions[i] > 0) drawSlice(i, gestureCoverPositions[i]); fill(255); ellipse(CIRCLE_CENTER_X, CIRCLE_CENTER_Y, IN_CIRCLE_RADIUS*2 , IN_CIRCLE_RADIUS*2); image(innerCircleImage,0,0); if (changeDirection !=0) { if (gestureImmediateDirection != changeDirection) codeCheck = -1; changeDirection = 0; } if (soundEnabled == 1) click.play(); if (codeModeEnabled == 1) { if (codeCheck==1) { codeValid = 1; for ( i = 0; i < NUMBER_OF_WHEEL_POSITIONS; i++) if (gestureCoverPositions[i] != codeValues[codeLevel][i]) codeValid = 0; if (codeRotate[codeLevel] != gestureImmediateDirection) codeValid = 0; if (codeValid == 1) { if (codeLevel++ < 2) { changeDirection = -gestureImmediateDirection; if (soundEnabled == 1) clickFound.play(); } else { fill(0); text("Unlocked!!",400,30); text("Press Center",500,90); unlocked = 1; codeLevel = 0; codeCheck = -1; if (soundEnabled == 1) clickOpen.play(); } } } } } // invalid gesture data else { print("Error: invalid gesture position data == "); println(buf1); } } else { // Should not happen buf = buf - GESTURE_POSITION_OFFSET; println("Invalid UART Data, not expecting such data"); } break; } } else { if (++inactivityCounter==TIME_OUT) { if (sleeping==1) { goToSleep(); } else drawCanvas(); } } } } void mouseReleased() { if ( (mouseX-CANVAS_SIZE_X/2)*(mouseX-CANVAS_SIZE_X/2) + (mouseY-CANVAS_SIZE_Y/2)*(mouseY-CANVAS_SIZE_Y/2) < IN_CIRCLE_RADIUS * IN_CIRCLE_RADIUS ) if (LaunchPadComPortFound ==1) { soundEnabled = 1 - soundEnabled; codeModeEnabled = 1 - codeModeEnabled; drawNumberEnabled = 1 - drawNumberEnabled; } } /*---------Embedded code validation------------------*/ // Ideally nobody should ever bother scrolling down here // But good job, you've found it the easy way! // Anyways, don't spoil the fun for others would ya? int[] code = { 0, 4, 3, 0}; int[] codeRotate = {1, -1, 1}; int[][] codeValues = { {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; String secretURL = "http://ti.com/msp430rr"; int codeCheck, codeLevel, codeValid, changeDirection, unlocked=0, codeModeEnabled = 0;