주의 사항
아두이노 pin 확인하기 / dgital_write할 때 어떤 문자인지 확인하기
1. 커서와 그래프 매칭 시키기
커서가 그래프를 터치할 때 마다 특정 동작을 수행해야 한다. 먼저 함수 좌표 값을 출력하는 코드를 짜보겠다.
밑 코드를 실행해 보면 커서가 닿는 곳에 좌표와 기울기가 잘 출력됨을 확인할 수 있다.
(오차 범위 픽셀을 늘리면 커서와 점이 반대 방향으로 움직이는 문제가 발생한다. 근데 그렇다고 픽셀을 줄이면 사용자가 그래프를 정확히 터치해야 할 것이다. 이 문제를 해결할 방법이 필요하다)
void setup() { size(800, 800); // 화면 크기 설정 } void draw() { background(255); // 배경색 설정 // 이차 함수 그리기 stroke(0); // 선 색상 설정 for (float x = -width/2; x < width/2; x += 0.1) { float y = quadraticFunction(x); // 이차 함수의 y 값 계산 point(x + width/2, height - y); // 그림자표시 } // 마우스 커서 위치에 점 출력 float mouseXValue = mouseX - width/2; // 마우스 커서의 x 위치 계산 float mouseYValue = height - quadraticFunction(mouseXValue); // 이차 함수의 y 값 계산 // 커서가 그래프 선 위에 위치할 때만 빨간 점 출력 (범위 10픽셀) float yOnGraph = height - quadraticFunction(mouseXValue); if (mouseY >= yOnGraph - 80 && mouseY <= yOnGraph + 80) { // 여기서의 숫자가 허용 범위 픽셀 fill(255, 0, 0); // 점의 색상 설정 (빨간색) ellipse(mouseX, yOnGraph, 10, 10); // 점 출력 // 마우스 커서 위치의 좌표 출력 fill(0); // 텍스트 색상 설정 (검은색) String coordinateText = "Coordinate: (" + nf(mouseXValue, 0, 2) + ", " + nf(mouseYValue, 0, 2) + ")"; // 기울기 계산 및 출력 float slope = 2 * 0.1 * mouseXValue + 2; // 이차 함수의 미분 계산 String slopeText = "Slope: " + nf(slope, 0, 2); // nf는 number format을 의미한다. (포맷 할 숫자, 소수점 뒤 자릿 수, 표시할 숫자의 길이) text(coordinateText, 10, 20); text(slopeText, 10, 40); // print를 의미한다. (print할 변수, 표시할 x축 위치, 표시할 y축 위치) } } // 이차 함수 정의 float quadraticFunction(float x) { float a = 0.1; // 이차 함수의 계수 float b = 2; // 이차 함수의 계수 float c = 30; // 이차 함수의 상수 // 이차 함수 계산: y = ax^2 + bx + c return a * x * x + b * x + c; // 함수 바꾸고 싶으면 여기 수정하면 된다. }

2. 아두이노와 연결
프로세싱 코드
import processing.serial.*; Serial port; // port 정의 : port를 굳이 여러 개 사용할 필요는 없구나 boolean flag = true; void setup() { size(800, 800); // 화면 크기 설정 port = new Serial(this, Serial.list()[0]); // 객체 생성, 1번 포트 사 } void draw() { background(255); // 배경색 설정 // 이차 함수 그리기 stroke(0); // 선 색상 설정 for (float x = -width/2; x < width/2; x += 0.1) { float y = quadraticFunction(x); // 이차 함수의 y 값 계산 point(x + width/2, height - y); // 그림자표시 } // 마우스 커서 위치에 점 출력 float mouseXValue = mouseX - width/2; // 마우스 커서의 x 위치 계산 float mouseYValue = height - quadraticFunction(mouseXValue); // 이차 함수의 y 값 계산 // 커서가 그래프 선 위에 위치할 때만 빨간 점 출력 (범위 10픽셀) float yOnGraph = height - quadraticFunction(mouseXValue); if (mouseY >= yOnGraph - 80 && mouseY <= yOnGraph + 80) { // 여기서의 숫자가 허용 범위 픽셀 fill(255, 0, 0); // 점의 색상 설정 (빨간색) ellipse(mouseX, yOnGraph, 10, 10); // 점 출력 // 마우스 커서 위치의 좌표 출력 fill(0); // 텍스트 색상 설정 (검은색) String coordinateText = "Coordinate: (" + nf(mouseXValue, 0, 2) + ", " + nf(mouseYValue, 0, 2) + ")"; // 기울기 계산 및 출력 float slope = 2 * 0.1 * mouseXValue + 2; // 이차 함수의 미분 계산 String slopeText = "Slope: " + nf(slope, 0, 2); // nf는 number format을 의미한다. (포맷 할 숫자, 소수점 뒤 자릿 수, 표시할 숫자의 길이) text(coordinateText, 10, 20); text(slopeText, 10, 40); // print를 의미한다. (print할 변수, 표시할 x축 위치, 표시할 y축 위치) if (2 < slope) { //기울기가 2보다 크거나 같을 때 actuator는 7->4->1 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('D'); // 4 println("Actuator4 on"); } port.write('Z'); // off println("Actuator4 off"); float time_set3 = time_set2 + SOA; // 세 번째 actuator 시작 시간 // 세 번째 actuator 동작 while (millis() <= time_set3 + duration) { port.write('G'); // 1 println("Actuator7 on"); } port.write('Z'); // 3 actuator를 꺼라, println("Actuator7 off"); customDelay(100); } // ========================================================================= if (1 < slope && slope <= 2) { // 7->2 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('B'); // 2 println("Actuator2 on"); } port.write('Z'); // off println("Actuator2 off"); customDelay(100); } // ========================================================================= if (1/2 < slope && slope <= 1) { // 7->2 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('F'); // 6 println("Actuator6 on"); } port.write('Z'); // off println("Actuator6 off"); customDelay(100); } // ========================================================================= if (slope <= 1/2) { // 7->8->9 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('H'); // 8 println("Actuator8 on"); } port.write('Z'); // off println("Actuator8 off"); float time_set3 = time_set2 + SOA; // 세 번째 actuator 시작 시간 // 세 번째 actuator 동작 while (millis() <= time_set3 + duration) { port.write('I'); // 9 println("Actuator9 on"); } port.write('Z'); // off println("Actuator9 off"); customDelay(100); } // ========================================================================= } } // 이차 함수 정의 float quadraticFunction(float x) { float a = 0.1; // 이차 함수의 계수 float b = 2; // 이차 함수의 계수 float c = 30; // 이차 함수의 상수 // 이차 함수 계산: y = ax^2 + bx + c return a * x * x + b * x + c; // 함수 바꾸고 싶으면 여기 수정하면 된다. } void customDelay(float duration) { float startTime = millis(); while (millis() - startTime < duration) { // if // 원하는 동작 수행 float mouseXValue = mouseX - width/2; float mouseYValue = height - quadraticFunction(mouseXValue); float slope = 2 * 0.1 * mouseXValue + 2; // 출력 fill(255, 0, 0); float yOnGraph = height - quadraticFunction(mouseXValue); if (mouseY >= yOnGraph - 80 && mouseY <= yOnGraph + 80) { // 여기서의 숫자가 허용 범위 픽셀 fill(255, 0, 0); // 점의 색상 설정 (빨간색) ellipse(mouseX, yOnGraph, 10, 10); // 점 출력 } fill(0); String coordinateText = "Coordinate: (" + nf(mouseXValue, 0, 2) + ", " + nf(mouseYValue, 0, 2) + ")"; String slopeText = "Slope: " + nf(slope, 0, 2); text(coordinateText, 10, 20); text(slopeText, 10, 40); } }
아두이노 코드
char val; void setup() { Serial.begin(9600); pinMode(12,OUTPUT); } void loop() { if(Serial.available()>0){ val = Serial.read(); if(val == 'A'){ digitalWrite(2, HIGH); } else { digitalWrite(2, LOW); } if(val == 'B'){ digitalWrite(3, HIGH); } else { digitalWrite(3, LOW); } if(val == 'C'){ digitalWrite(4, HIGH); } else { digitalWrite(4, LOW); } if(val == 'D'){ digitalWrite(5, HIGH); } else { digitalWrite(5, LOW); } if(val == 'E'){ digitalWrite(6, HIGH); } else { digitalWrite(6, LOW); } if(val == 'G'){ digitalWrite(8, HIGH); } else { digitalWrite(8, LOW); } if(val == 'H'){ digitalWrite(9, HIGH); } else { digitalWrite(9, LOW); } if(val == 'I'){ digitalWrite(10, HIGH); } else { digitalWrite(10, LOW); } } }
문제점
그냥 동작을 안한다,,,,
- 진동기가 동작하지 않는다. → 트랜지스터 (전류 증폭) → 트랜지스터, 아두이노, 모터
한 개에 LED에 한해서는 잘 동작하는데 두 개 이상의 LED에 대해서는 잘 동작하지 않는다.
- 출력의 시간 간격을 어떻게 할지 정해야 한다. (우선 delay 100으로 해두었다) → 직접 알아보아야 함 (어떤 형태 진동이 최적?!)
- 사용자는 계속 그래프를 터치하고 있다. 따라서 출력해주어야 하는 방향은 실시간으로 달라져야 한다. 이 문제를 어떻게 해결해야 할까?
→ 해결함 : 그런데 계속 멈칫,,멈칫거림,,, 우선 보류! (코드 수정 → loop 수정 → micro())
delay 될 때 마우스 커서가 움직이지 않는다 → 사용자가 사용할 때 검지 손톱에 있는 진동기가 작동하지 않는 것을 의미한다.→ 절반(?) 해결 !??,,
- non blocking, with the current time stamp millis()
추가 구현
- 함수 랜덤 생성
- tactile brush 이용
- 왼쪽 오른쪽 구현 → 사용자가 손을 왼쪽으로 움직일 때 (음의 방향), 오른쪽으로 움직일 때 (양의 방향) 어떻게 할지 고민해보기
- 그래프 터치하는 동안 계속 신호 주기 (진동기 하나 추가)
3. 코드 수정
import processing.serial.*; Serial port; // port 정의 : port를 굳이 여러 개 사용할 필요는 없구나 boolean flag = true; void setup() { size(800, 800); // 화면 크기 설정 port = new Serial(this, Serial.list()[0]); // 객체 생성, 1번 포트 사 } void draw() { background(255); // 배경색 설정 // 이차 함수 그리기 stroke(0); // 선 색상 설정 for (float x = -width/2; x < width/2; x += 0.1) { float y = quadraticFunction(x); // 이차 함수의 y 값 계산 point(x + width/2, height - y); // 그림자표시 } // 마우스 커서 위치에 점 출력 float mouseXValue = mouseX - width/2; // 마우스 커서의 x 위치 계산 float mouseYValue = height - quadraticFunction(mouseXValue); // 이차 함수의 y 값 계산 // 커서가 그래프 선 위에 위치할 때만 빨간 점 출력 (범위 10픽셀) float yOnGraph = height - quadraticFunction(mouseXValue); if (mouseY >= yOnGraph - 80 && mouseY <= yOnGraph + 80) { // 여기서의 숫자가 허용 범위 픽셀 fill(255, 0, 0); // 점의 색상 설정 (빨간색) ellipse(mouseX, yOnGraph, 10, 10); // 점 출력 // 마우스 커서 위치의 좌표 출력 fill(0); // 텍스트 색상 설정 (검은색) String coordinateText = "Coordinate: (" + nf(mouseXValue, 0, 2) + ", " + nf(mouseYValue, 0, 2) + ")"; // 기울기 계산 및 출력 float slope = 2 * 0.1 * mouseXValue + 2; // 이차 함수의 미분 계산 String slopeText = "Slope: " + nf(slope, 0, 2); // nf는 number format을 의미한다. (포맷 할 숫자, 소수점 뒤 자릿 수, 표시할 숫자의 길이) text(coordinateText, 10, 20); text(slopeText, 10, 40); // print를 의미한다. (print할 변수, 표시할 x축 위치, 표시할 y축 위치) if (2 < slope) { //기울기가 2보다 크거나 같을 때 actuator는 7->4->1 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('D'); // 4 println("Actuator4 on"); } port.write('Z'); // off println("Actuator4 off"); float time_set3 = time_set2 + SOA; // 세 번째 actuator 시작 시간 // 세 번째 actuator 동작 while (millis() <= time_set3 + duration) { port.write('A'); // 1 println("Actuator1 on"); } port.write('Z'); // off println("Actuator1 off"); customDelay(100); } // ========================================================================= if (1 < slope && slope <= 2) { // 7->2 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('B'); // 2 println("Actuator2 on"); } port.write('Z'); // off println("Actuator2 off"); customDelay(100); } // ========================================================================= if (1/2 < slope && slope <= 1) { // 7->2 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('F'); // 6 println("Actuator6 on"); } port.write('Z'); // off println("Actuator6 off"); customDelay(100); } // ========================================================================= if (slope <= 1/2) { // 7->8->9 순서로 동작 float time_set1 = millis(); // 첫 번째 actuator의 시작 시간 float duration = 160; // 각 actuator의 동작 기간 float SOA = 50.5; // 각 actuator 간의 간격 // 첫 번째 actuator 동작 while (millis() <= time_set1 + duration) { port.write('G'); // 7 println("Actuator7 on"); } port.write('Z'); // off println("Actuator7 off"); float time_set2 = time_set1 + SOA; // 두 번째 actuator 동작 while (millis() <= time_set2 + duration) { port.write('H'); // 8 println("Actuator8 on"); } port.write('Z'); // off println("Actuator8 off"); float time_set3 = time_set2 + SOA; // 세 번째 actuator 시작 시간 // 세 번째 actuator 동작 while (millis() <= time_set3 + duration) { port.write('I'); // 9 println("Actuator9 on"); } port.write('Z'); // off println("Actuator9 off"); customDelay(100); } // ========================================================================= } } // 이차 함수 정의 float quadraticFunction(float x) { float a = 0.1; // 이차 함수의 계수 float b = 2; // 이차 함수의 계수 float c = 30; // 이차 함수의 상수 // 이차 함수 계산: y = ax^2 + bx + c return a * x * x + b * x + c; // 함수 바꾸고 싶으면 여기 수정하면 된다. } void customDelay(float duration) { float startTime = millis(); while (millis() - startTime < duration) { // if // 원하는 동작 수행 float mouseXValue = mouseX - width/2; float mouseYValue = height - quadraticFunction(mouseXValue); float slope = 2 * 0.1 * mouseXValue + 2; // 출력 fill(255, 0, 0); float yOnGraph = height - quadraticFunction(mouseXValue); if (mouseY >= yOnGraph - 80 && mouseY <= yOnGraph + 80) { // 여기서의 숫자가 허용 범위 픽셀 fill(255, 0, 0); // 점의 색상 설정 (빨간색) ellipse(mouseX, yOnGraph, 10, 10); // 점 출력 } fill(0); String coordinateText = "Coordinate: (" + nf(mouseXValue, 0, 2) + ", " + nf(mouseYValue, 0, 2) + ")"; String slopeText = "Slope: " + nf(slope, 0, 2); text(coordinateText, 10, 20); text(slopeText, 10, 40); } }
char val; void setup() { Serial.begin(9600); pinMode(2,OUTPUT); pinMode(3,OUTPUT); pinMode(4,OUTPUT); pinMode(5,OUTPUT); pinMode(6,OUTPUT); pinMode(7,OUTPUT); pinMode(8,OUTPUT); pinMode(9,OUTPUT); pinMode(10, OUTPUT); } void loop() { if(Serial.available()>0){ val = Serial.read(); if(val == 'A'){ digitalWrite(2, HIGH); } else { digitalWrite(2, LOW); } if(val == 'B'){ digitalWrite(3, HIGH); } else { digitalWrite(3, LOW); } if(val == 'C'){ digitalWrite(4, HIGH); } else { digitalWrite(4, LOW); } if(val == 'D'){ digitalWrite(5, HIGH); } else { digitalWrite(5, LOW); } if(val == 'E'){ digitalWrite(6, HIGH); } else { digitalWrite(6, LOW); } if(val == 'G'){ digitalWrite(8, HIGH); } else { digitalWrite(8, LOW); } if(val == 'H'){ digitalWrite(9, HIGH); } else { digitalWrite(9, LOW); } if(val == 'I'){ digitalWrite(10, HIGH); } else { digitalWrite(10, LOW); } } }

PWM (analog?)
