프로젝트

나도 메이커! 메이커스 여러분들의 작품/프로젝트를 공유하는 공간입니다.

터치패드로 LED제어하기

2014-08-28 13:33:53

개요

개요


튜토리얼에서 사용해본 터치패드를 응용하여 LED를 제어해보는 장치를 만들어 보자.

터치패드 자체는 눌렀을때 터치된 좌표값을 x값 y값으로 반환해 준다.
이 반환값을 사용하여 터치패드를 드래그하여 LED를 켜고 끄고 하며 불빛의 양을 조절하고 특정위치를 터치할 경우에는 On/Off가 되게 한다. 



외부 라이브러리를 사용하는데 외부라이브러리에는 단순하게 좌표값만을 읽어오는 기능밖에는 없기 때문에 나머지 기능들은 구현해야 한다.
하드웨어적인 면에서는 간단하게 구현이 가능하며 단순히 x값, y값을 가지고 응용하여 만들기 때문에 소프트웨어면에서 언어문법지식보다 알고리즘을 해결하는 능력이 필요하다.

동영상


필요한 사전 지식

터치패드 사용법
LED

부품 목록


NO 부품명 수량 상세정보
1 아두이노 보드 1  
2 브레드보드 1  
3 LED 3  
4 좌표 터치 센서 1  
5 케이블 5  
6 저항 3 330



부품명 아두이노 보드 브레드보드 LED 좌표 터치 센서 케이블 저항
부품사진

하드웨어 making

브레드 보드


전자회로도

소프트웨어 coding

#include <Wire.h>
#include <mpr121.h>

int brightness; //밝기 변수

int first_X, first_Y; //처음터치될때의 좌표를 저장하는 변수
int touch_X, touch_Y; //드래그되는 동안 좌표를 저장하는 변수

int gap_X, gap_Y; //현재 좌표와 처음터치된 좌표의 차이값을 저장하는 변수

int result_X,result_Y; 각 좌표 연산 후 결과값을 저장하는 변수

boolean stateRED, stateGREEN, stateYELLOW = false; //LED의 상태 저장

//LED 핀번호 int redLED = 9; int greenLED = 10; int yellowLED = 11; // ========= setup ========= void setup() { //시리얼통신, I2C통신, 터치센서, 핀모드 초기화 Serial.begin(19200); Wire.begin(); CapaTouch.begin(); pinMode(redLED, OUTPUT); pinMode(greenLED, OUTPUT); pinMode(yellowLED, OUTPUT); delay(500); Serial.println("START"); } // ========= loop ========= void loop() {
//처음 터치된 곳의 좌표를 받는다 first_X=CapaTouch.getX(); first_Y=CapaTouch.getY(); touch_X = 0; touch_Y = 0;
//처음 터치되고 계속적인 터치가 이루어지면(드래그) 계속 값을 받는다 while(touch_X != -1 && touch_Y != -1) { touch_X = CapaTouch.getX(); touch_Y = CapaTouch.getY();
//처음 터치된 곳의 좌표와 현재 좌표와의 차이값을 구한다 gap_X = touch_X - first_X; gap_Y = touch_Y - first_Y; if(touch_X != -1 && touch_Y != -1) { //왼쪽으로 오른쪽으로 드래그 되었을 때(LED를 켠다) if(gap_Y <= 2 && gap_X > gap_Y && gap_Y >= 0) {
//좌표값의 차이값을 0부터 11로 세분화 시켜서 LED를 켜고 끈다 result_X = map(gap_X, 1, 9, 0, 11);
//0에서 2사이 일 경우 LED는 전부 꺼진상태 if(result_X >= 0 && result_X <= 2&&!stateRED&&!stateYELLOW&&!stateGREEN) { analogWrite(redLED, 0); analogWrite(greenLED, 0); analogWrite(yellowLED, 0); stateRED = false; stateGREEN = false; stateYELLOW = false; Serial.println("OFF"); }
//2초과 5이하일 경우 redLED만 켠다 else if(result_X > 2 && result_X <= 5 &&!stateYELLOW&&!stateGREEN) { analogWrite(redLED, result_Y); analogWrite(greenLED, 0); analogWrite(yellowLED, 0); stateRED = true; stateGREEN = false; stateYELLOW = false; Serial.println("redLED ON1"); }
//5초과 8미만 일 경우 redLED, greenLED를 켠다 else if(result_X > 5 && result_X < 8&&!stateYELLOW) { analogWrite(redLED, result_Y); analogWrite(greenLED, result_Y); analogWrite(yellowLED, 0); stateRED = true; stateGREEN = true; stateYELLOW = false; Serial.println("redLED, greenLED ON"); }
//8이상 11이하일경우 모든 LED를 켠다 else if(result_X >= 8 && result_X <= 11) { analogWrite(redLED, result_Y); analogWrite(greenLED, result_Y); analogWrite(yellowLED, result_Y); stateRED = true; stateGREEN = true; stateYELLOW = true; Serial.println("redLED, greenLED, yellowLED ON"); } } //오른쪽에서 왼쪽으로 드래그 할 경우(LED를 끈다) else if(gap_Y <= 2 && gap_X < gap_Y && abs(gap_X) > gap_Y && gap_Y >= 0) {
//드래그 거리에 따라 0부터 11까지 세분화 result_X = map(abs(gap_X), 1, 9, 0, 11);
//0이상 2이하일 경우 모든 LED ON상태 유지 if(result_X >= 0 && result_X <= 2 && stateRED && stateGREEN && stateYELLOW) { analogWrite(redLED, brightness); analogWrite(greenLED, brightness); analogWrite(yellowLED, brightness); stateRED = true; stateGREEN = true; stateYELLOW = true; Serial.println("redLED, greenLED, yellowLED ON"); }
//2초과 5이하일 경우 yellowLED를 끈다 else if(result_X > 2 && result_X <= 5 && stateRED && stateGREEN) { analogWrite(redLED, result_Y); analogWrite(greenLED, result_Y); analogWrite(yellowLED, 0); stateRED = true; stateGREEN = true; stateYELLOW = false; Serial.println("redLED, greenLED ON"); }
// 5초과 8미만일 경우 yellowLED와 greenLED를 끈다 else if(result_X > 5 && result_X < 8 && stateRED) { analogWrite(redLED, result_Y); analogWrite(greenLED, 0); analogWrite(yellowLED, 0); stateRED = true; stateGREEN = false; stateYELLOW = false; Serial.println("redLED ON2"); }
//8이상 11이하일 경우 모든 LED를 끈다 else if(result_X >= 8 && result_X <= 11) { analogWrite(redLED, 0); analogWrite(greenLED, 0); analogWrite(yellowLED, 0); stateRED = false; stateGREEN = false; stateYELLOW = false; Serial.println("ALL OFF"); } } //위에서 아래로 드래그할 경우(밝기를 올린다) else if(gap_X >= 0 && gap_X <= 3 && gap_Y > gap_X) { if(gap_Y > 0) { //드래그 되었을때 드래그 된 거리를 현재 밝기부터 255까지의 범위로 세분화 시킨다 brightness = map(gap_Y, 1, 13, result_Y, 255); constrain(brightness, 0, 255); }
//켜진 LED만 밝기를 올린다 if(stateRED) analogWrite(redLED, brightness); if(stateGREEN) analogWrite(greenLED, brightness); if(stateYELLOW) analogWrite(yellowLED, brightness); } //아래에서 위로 드래그할 경우(밝기를 내린다) else if(gap_X >= 0 && gap_X <= 3 && abs(gap_Y) > gap_X && gap_Y < gap_X) { if(abs(gap_Y) >= 0) { //드래그 되었을때 드래그 된 거리를 0부터 현재 밝기까지의 범위로 세분화 시킨다 brightness = map(abs(gap_Y), 1, 13, 0, result_Y); constrain(brightness, 0, 255); }
//켜진 LED만 밝기를 내린다 if(stateRED) analogWrite(redLED, result_Y - brightness); if(stateGREEN) analogWrite(greenLED, result_Y - brightness); if(stateYELLOW) analogWrite(yellowLED, result_Y - brightness); brightness = result_Y - brightness; } //오른쪽 아래부분을 눌렀을 때 모든 LED를 끈다 else if(first_X == touch_X && first_Y == touch_Y && first_X >= 8 && first_Y >= 12) { analogWrite(redLED, 0); analogWrite(greenLED, 0); analogWrite(yellowLED, 0); stateRED = false; stateGREEN = false; stateYELLOW = false; } //왼쪽 아래를 눌렀을 때 모든 LED를 마지막으로 설정된 밝기로 켠다 else if(first_X == touch_X && first_Y == touch_Y && first_X <= 2 && first_Y >= 12) { analogWrite(redLED, result_Y); analogWrite(greenLED, result_Y); analogWrite(yellowLED, result_Y); stateRED = true; stateGREEN = true; stateYELLOW = true; } //왼쪽 위를 눌렀을 때 현재 켜진 LED의 밝기를 최대로 올린다 else if((first_X == touch_X && first_Y == touch_Y && first_X <= 2 && first_Y <= 2) && (stateRED||stateGREEN||stateYELLOW)) { brightness = 255; if(stateRED) analogWrite(redLED, brightness); if(stateGREEN) analogWrite(greenLED, brightness); if(stateYELLOW) analogWrite(yellowLED, brightness); } //오른쪽 위를 눌렀을 때 현재 켜진 LED의 밝기를 희미하게 바꾼다 else if(first_X == touch_X && first_Y == touch_Y && first_X >= 8 && first_Y <= 2 && (stateRED||stateGREEN||stateYELLOW)) { brightness = 21; if(stateRED) analogWrite(redLED, brightness); if(stateGREEN) analogWrite(greenLED, brightness); if(stateYELLOW) analogWrite(yellowLED, brightness); } } result_Y = brightness; delay(20); } }
이 프로젝트에서  터치패드에 구현된 기능은

1. 왼쪽에서 오른쪽으로 드래그 하였을 때 LED를 점차적으로 켜는 기능
2. 오른쪽에서 왼쪽으로 드래그 하였을 때 LED를 점차적으로 끄는 기능
3. 위에서 아래로 드래그 하였을 때 LED의 밝기를 올리는 기능
4. 아래에서 위로 드래그 하였을 때 LED의 밝기를 내리는 기능
5. 왼쪽 아래를 눌렀을 경우 모든 LED를 켜는 기능
6. 오른쪽 아래를 눌렀을 경우 모든 LED를 끄는 기능
7. 왼쪽 위를 눌렀을 경우 현재 켜진 LED의 밝기를 최대로 만드는 기능
8. 오른쪽 위를 눌렀을 경우 현재 켜진 LED의 밝기를 희미하게 만드는 기능

을 구현하였다.

Drag기능

사실 터치패드 라이브러리에 드래그와 관련된 멤버함수가 존재하지 않아 직접 구현했기 때문에 감도나 안정성면에서는 떨어지지만 확실히 드래그 기능은 된다.
드래그을 구현할 때에는
1. 처음 터치된 좌표를 기억하고 있어야 하며
2. 마지막으로 눌려진 좌표값도 기억해야 하고
3. 계속적으로 눌려지고 있는 상태인지도 기억해야 한다.
위 3개를 알고 있어야 어디에서부터 어느방향으로 드래그되는 것인지 기억 할 수 있다. 

위 터치 좌표센서에서는 계속 눌려지고 있는 상태인지는 센서자체에서 알려주지 않기 때문에 센서에서 반환되어 오는 값이 -1(터치되고 있지 않은 상태)이 아닌 이상에는 계속적으로 눌려지고 있는 상태로 가정하고 드래그를 구현하였다.
예시
터치센서에서 반환된 좌표값이 순서대로 (1,2), (2,2), (4,5), (5,6), (-1,-1), (6,7)일 경우에는
도중에 (-1,-1)이 나왔기 때문에 -1이 반환된 시점으로 드래그는 끊긴 것으로 간주된다.
(1,2)가 시작점이 되고 (5,6)점이 끝점이 되는것이다.




이렇게 되면 유저는 (1,2)점에서 (5,6)점으로 x축으로 4만큼 y축으로 4만큼 이동시킨것이 된다.
예시가 이해가 된다면 드래그를 구현하는 방법에 대해 조금은 이해가 됐을 것이라 생각한다.

1. LED 점등 제어

      //왼쪽으로 오른쪽으로 드래그 되었을 때(LED를 켠다)
        if(gap_Y <= 2 && gap_X > gap_Y && gap_Y >= 0) {
//좌표값의 차이값을 0부터 11로 세분화 시켜서 LED를 켜고 끈다 result_X = map(gap_X, 1, 9, 0, 11);
//0에서 2사이 일 경우 LED는 전부 꺼진상태 if(result_X >= 0 && result_X <= 2&&!stateRED&&!stateYELLOW&&!stateGREEN) { analogWrite(redLED, 0); analogWrite(greenLED, 0); analogWrite(yellowLED, 0); stateRED = false; stateGREEN = false; stateYELLOW = false; Serial.println("OFF"); }
//2초과 5이하일 경우 redLED만 켠다 else if(result_X > 2 && result_X <= 5 &&!stateYELLOW&&!stateGREEN) { analogWrite(redLED, result_Y); analogWrite(greenLED, 0); analogWrite(yellowLED, 0); stateRED = true; stateGREEN = false; stateYELLOW = false; Serial.println("redLED ON1"); }
//5초과 8미만 일 경우 redLED, greenLED를 켠다 else if(result_X > 5 && result_X < 8&&!stateYELLOW) { analogWrite(redLED, result_Y); analogWrite(greenLED, result_Y); analogWrite(yellowLED, 0); stateRED = true; stateGREEN = true; stateYELLOW = false; Serial.println("redLED, greenLED ON"); }
//8이상 11이하일경우 모든 LED를 켠다 else if(result_X >= 8 && result_X <= 11) { analogWrite(redLED, result_Y); analogWrite(greenLED, result_Y); analogWrite(yellowLED, result_Y); stateRED = true; stateGREEN = true; stateYELLOW = true; Serial.println("redLED, greenLED, yellowLED ON"); } }
위 소스는 왼쪽에서 오른쪽으로 드래그 되었을 때 드래그 거리에 따라 LED를 점차 켜는 기능이다.
드래그의 방향 판별은 처음 점과 끝점을 비교하여 드래그의 방향을 구할 수 있다.
왼쪽에서 오른쪽으로 드래그 되었을 경우 x좌표는 계속 증가하기 때문에 끝점의 x좌표와 처음점의 x좌표를 비교하면 양수가 나올 것이다.
반대로 오른쪽에서 왼쪽으로 드래그 되었을 경우에는 x좌표는 계속 감소하기 때문에 끝점의 x좌표와 처음점의 x좌표를 비교하면 음수가 나올 것이다.

수학에서 배우는 x좌표와 y좌표는 왼쪽아래를 기준으로 한 1사분면을 보통으로 쓰지만 프로그램에서는 보통 왼쪽 상단을 (0,0)으로 두고 좌표를 사용한다. 그렇기 때문에 오른쪽으로 갈 수록 x좌표값은 증가하고 아래로 갈 수록 y좌표값은 증가한다.


끝점의 x좌표와 처음 점의 x좌표를 비교하여 나온 값의 크기를 0부터 11의 사이의 값으로 상대변환 시킨다. 값이 클수록 11에 가까운 값이 뜨고 값이 작을수록 0에 가까운 값이 뜬다. 0부터 11의 값으로 거리에 따라 LED를 점차 켜면 드래그한 거리에 따라 LED가 점점 켜지는 것과 같은 효과를 줄 수 있다.

이 방법을 역으로 적용하면 LED를 점점 꺼지는 것과 같은 효과를 줄 수 있으며 이 효과는 오른쪽에서 왼쪽으로 드래그할 때 구현돼 있다. 

2. LED 밝기 제어

//위에서 아래로 드래그할 경우(밝기를 올린다)
        else if(gap_X >= 0 && gap_X <= 3 && gap_Y > gap_X) {
          if(gap_Y > 0) {  //드래그 되었을때 드래그 된 거리를 현재 밝기부터 255까지의 범위로 세분화 시킨다
            brightness = map(gap_Y, 1, 13, result_Y, 255);  
            constrain(brightness, 0, 255);
          }
//켜진 LED만 밝기를 올린다 if(stateRED) analogWrite(redLED, brightness); if(stateGREEN) analogWrite(greenLED, brightness); if(stateYELLOW) analogWrite(yellowLED, brightness); }
LED의 점등제어와 마찬가지로 Y좌표값의 변화값으로 드래그를 어느방향으로 했는지 판별한다. 위에서 아래로 드래그 할 경우 y값은 증가하기 때문에
마지막으로 누른 y좌표값에서 처음으로 누른 y좌표값을 빼면 양수가 나온다. 나중y좌표값과 처음 y좌표값의 차이가 양수가 나올 때 위에서 아래로 드래그한 상태로 인지하면 드래그의 방향을 읽을 수 있다.

밝기 조절도 LED점등과 마찬가지로 드래그의 거리를 밝기 조절값으로 변환한다. 0부터 255사이의 숫자로 변환하여 LED의 밝기를 제어하게 된다.
드래그한 거리가 길수록 밝기값은 255에 가까워진다.
LED의 밝기를 제어할 때는 현재 밝기에서 이어 나갈 수 있도록 초기값을 현재 밝기로 지정한다.
예를 들어 현재 밝기가 50일 경우 최소 50의 밝기부터 최대 255의 밝기까지 증가할 수 있게 한다.

Click 기능

스마트폰에서의 터치 기능은 보통 패널아래 있는 버튼의 좌표값을 통해 터치가 이루어 진다. 
(20,20)에 버튼이 존재할 경우 (20,20)을 눌렀을 경우 그 버튼을 누른것으로 간주하고 버튼을 눌렀을 때의 이벤트가 실행된다.
위 방법과 같이 터치센서에는 패널이 없기 때문에 임의적으로 제일 손이 덜 가는 양 꼭지점 부근을 버튼으로 잡고 클릭기능을 만들었다.

클릭기능은 간단하게 처음 눌려진 좌표와 그 다음 눌려진 좌표값이 같고 그 위치가 각 꼭지점 부근일 경우 클릭된 것으로 인식하고 이벤트 발생을 실행 시켰다.
예시
좌표센서에서 반환된 값이 (1,2), (1,2)를 반환할 경우 두번의 인식이 이루어졌고 그 두번의 좌표값이 모두 같기 때문에 클릭된 것으로 간주된다.
(1,1), (1,2), (2,2), (2,2)가 반환됐을 경우 4번 다 왼쪽 위를 누르고 있는 상태이지만 실제적인 클릭 인식은 (2,2)가 두번 반환된 시점부터 클릭된 것으로 인식된다. 드래그 기능이 있기 때문에 그 전에는 (1,1)에서 부터 드래그를 하고 있는 상태로 인식된다.

//오른쪽 아래부분을 눌렀을 때 모든 LED를 끈다
        else if(first_X == touch_X && first_Y == touch_Y && first_X >= 8 && first_Y >= 12) {
          analogWrite(redLED, 0);
          analogWrite(greenLED, 0);
          analogWrite(yellowLED, 0);
          stateRED = false;
          stateGREEN = false;
          stateYELLOW = false;
        }
위 부분은 오른쪽 아래를 클릭하였을 때 모든 LED를 끄는 부분의 소스이다.
first_X와 first_Y는 각각 처음 클릭 된 좌표값을 나타내고 touch_X와 touch_Y는 처음 클릭 이후 first_X와 first_Y다음으로 반환되는 값을 저장하는 변수이다. 
클릭은 두 변수의 값이 일치할 때 이루어 지는 것으로 간주한다. 
오른쪽 아래를 인식하는 방법은 누른 x좌표값이 8 이상 y좌표값이 12 이상일 때, 오른쪽 아래를 누른것으로 인식하게 하였다.

실질적인 클릭개념은 한번터치가 이루어 졌을 때 클릭으로 인식되어야 하지만 구현방법상 어쩔 수 없이 똑같은 좌표값이 두 번 인식되었을 때 클릭으로 인식되게 하였다.






수박쨈

아두이노, 터치패드, LED