[아두이노] 조이스틱+processing 3D 도형 회전(1)

IOT/아두이노|2019. 4. 27. 09:00

[아두이노] 조이스틱+processing 3D 도형 회전(1)



오늘은 조이스틱의 키 값을 가지고 지난 시간에 포스트한 3D 도형을 회전시키는 실험을 해보겠습니다. 참고로 오늘 접근 방식은 아두이노는 기존 조이스틱 제어 코딩을 그대로 동작하고 processing에서 조이스틱을 제어한 값을 시리얼통신을 통해서 값을 받아 3D 도형을 회전 시킵니다. 즉, 아두이노는 아두이노 역할에 충실하고 processing은 processing의 역할을 충실한 코딩으로 생각하시면 됩니다.


1. 아두이노 소스


지난 시간에 아두이노 방향키를 만든 소스입니다. 시리얼모니터로 해당 방향 알파벳을 출력한 소스인데 그대로 적용할 예정입니다.

const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 3; 
 
void setup() {
  Serial.begin(9600);
  pinMode(SW_P,INPUT_PULLUP);
}
 void loop() {
  //X축 방향값
  if(analogRead(AXIS_X)<=300){
    Serial.println('a');
  }
  else if(analogRead(AXIS_X)>=700){
    Serial.println('d');   
  }
  //Y축 방향값
  if(analogRead(AXIS_Y)<=300){
    Serial.println('w');      
  }
  else if(analogRead(AXIS_Y)>=700){
    Serial.println('s');
  }
  delay(20);
}

2. processing 소스


지난 시간에 3D box를 제어한 포스트에서 다룬 소스를 기반으로 아두이노의 시리얼 통신 값을 가지고 3D box를 제어 해보기 위해서 몇가지 지난 소스에서 수정을 해야 합니다.

복습 : 시리얼 통신



시리얼통신을 사용하기 위해서는 Serial를 프로세싱에 import해야 합니다. 그리고 사용 할 객체 변수를 하나 선언해야겠죠.

import processing.serial.*;

Serial myPort;  

다음으로 myPort를 인스턴스화 해야 합니다.

 println(Serial.list()); //연결된 포트정보
 myPort = new Serial(this, Serial.list()[0], 9600); //시리얼통신 세팅

연결된 포트정보를 list()함수로 찾을 수 있고 그 정보를 시리얼모니터로 출력합니다. 그리고 myPort는 객체변수를 선언했다고 해서 바로 사용되는게 아닙니다. 객체변수 선언은 비유로 들자면 껍때기만 만들어 놓은 거고 인스턴스를 해야 합니다. 할당한다는 의미로 생각하면 될 듯 싶네요. 비유로 알맹이를 채운다고 생각하면 좋을 듯 싶네요.

이렇게 해서 시리얼통신의 준비 작업은 끝났습니다.

다음으로 시리얼통신에서 들어온 데이터를 읽는 방법은 두가지 방법이 있습니다. 아두이노에서 시리얼통신으로 읽는 방식과 동일하게 코딩하는 방법과 processing 자체에서 시리얼통신에서 데이터가 들어오면 자동으로 호출되는 함수가 있습니다. 두번째 호출함수로 코딩을 해보겠습니다.

void serialEvent(Serial p) { 
    명령;
}

SerialEvent()함수가 바로 시리얼통신으로 데이터가 들어오면 바로 호출되는 함수입니다.

void serialEvent(Serial p) { 
  String inString = myPort.readStringUntil('\n');
  char ch=inString.charAt(0);
}

시리얼 통신에서 일부러 문자열로 받았습니다. myPort.readString()함수는 문자열로 읽는 함수입니다. 여기서 myPort.readStringUntile('\n') 함수는 찾고자 하는 인자가 없을 때 null을 반환합니다. 문자열 라인 단위로 읽는다고 생각하면 될 듯 싶네요.

참고로, 아두이노에서는 시리얼통신으로 println('a') 함수로 보내면 "a\n"이렇게 전송되게 됩니다. 받는 쪽에서 만약 문자니깐 함수를 myPort.readChar() 함수를 쓰게 되면 한 글짜씩 개별로 읽게 됩니다. 'a'라는 문자를 보냈지만 추가로 옆에 기호까지 같이 보내지기 때문에 Char으로 읽으면 안됩니다. 나중에 여러개 값을 한번에 보낼 때를 사용하시라고 String 문자열로 읽겠습니다.
'a'라는 한글자지만 문자열이기 때문에 해당 글자를 한글자 문자로 읽어서 변수에 저장하겠습니다. charAt(0)으로 0번째 위치의 문자를 읽는 함수인데 'a'라는 문자를 읽게 됩니다. 그걸 char 문자 변수에 저장하면 되겠죠.

진짜 번거로운 과정을 거쳐서 코딩을 했네요. 그 이유는 나중에 여러분들이 자이로센서같은 것을 다루게 되면 한번에 x,y,z 값을 받아서 읽게 되는데 이때 문자열로 읽고 해당 위치의 값만 빼내서 제어하는데 활용하시라고 미리 이렇게 번거로운 코딩을 하게 되었네요.

시리얼 통신에서 데이터를 읽는 함수의 종류는 다양합니다. 그 다양한 함수들은 위에 링크한 라퍼런스을 보고 한번 여러분들이 따로 코딩해 보셨으면 합니다.

추가로,

inString = trim(inString);

문자열의 시작과 끝에서 공백 문자를 제거하는 함수입니다.("nbsp" 제거) 이 명령라인을 추가해 주시면 더 안정적으로 데이터를 읽겠지만 실험에서는 그냥 사용하지 않았네요. 이런 함수가 있다는 정도만 이해하시고 나중에 활용해 보세요.

ch변수에 겨우 방향키 값을 저장할 수 있게 되었습니다. 지난 3D box 제어에서 키보드로 제어했던 것을 기억하실 지 모르겠네요.

[ 키보드 제어 원 소스]

void keyPressed() {
  if (key == CODED) {
    if (keyCode == UP) {
      y+=1;
    } 
    else if (keyCode == DOWN) {
      y-=1;
    } 
    else if (keyCode == LEFT) {
      x-=1;
    } 
    else if (keyCode == RIGHT) {
      x+=1;
    }
  }
  else if (key == 'z' || key == 'Z') {
      z+=10;
  } 
  else if (key == 'x' || key == 'X') {
      z-=10;
  } 
}

여기서, 수정을 하면은 다음과 같습니다.

void serialEvent(Serial p) { 
    String inString = myPort.readStringUntil('\n');
    char ch=inString.charAt(0);
    println(ch); //시리얼모니터로 방향키 값 출력
    
    if (ch == 'w') {
        y+=1;
    } 
    else if (ch == 's') {
        y-=1;
     } 
     else if (ch == 'a') {
        x-=1;
     } 
     else if (ch == 'd') {
        x+=1;
     }  
}

여기서는 X, Y축을 한꺼번에 if~ else if문으로 표현했습니다. 지난 시간에 방향키 값을 만들 때는 X축과 Y축을 따로 if문을 했는데 여기서 묶여서 처리한 이유가 뭘까요. 방향키 ch변수에는 하나의 값만 존재합니다. 하나의 값만 순서대로 읽어오기 때문에 X,Y축을 구별 안하고 읽어온 하나에 값에 대한 하나의 동작만이 필요하기 때문에 선택문으로 전부 연결한 것이죠.

if문은 상황에 따라 맞게 의미를 이해하시고 사용하셔야 합니다.

전체적으로, 코딩을 하면 아래와 같습니다.

import processing.serial.*;

Serial myPort;

int x=0;
int y=0;

void setup() {

    println(Serial.list());
    myPort = new Serial(this, Serial.list()[0], 9600);
    myPort.bufferUntil('\n');
    noStroke();
    size(600,600,P3D);
    
} 

void draw() {  
    background(0); //배경색
    lights();  //조명

    pushMatrix();  //Start
    fill(0,255,0); //채우기
    translate(width/2,height/2,-100); //이동
    rotateX(radians(y)); //x축 회전
    rotateY(radians(x)); //y축 회전
    
    box(200,100,200); //상자
    popMatrix(); //End
}
 
void serialEvent(Serial p) { 
  String inString = myPort.readStringUntil('\n');
  char ch=inString.charAt(0);
    
  println(ch);
  
  if (ch == 'w') {
      y+=1;
  } 
  else if (ch == 's') {
     y-=1;
  } 
  else if (ch == 'a') {
     x-=1;
  } 
  else if (ch == 'd') {
     x+=1;
  }
}

3. 실행 과정


[회로도]


[1단계] : 아두이노 IDE에 방향키 소스를 복사해서 붙여 넣으시고 화살표에 업로드 버턴을 누르면 아래 정상적으로 처리되면 업로드 완료라고 뜹니다.


[2단계] : 시리얼모니터로 결과 검사



정상적으로 방향키값이 시리얼모니터로 출력되는 것을 확인 하실 수 있습니다.

[3단계] : processing에 오늘 수정한 코드를 복사해서 붙여 넣으시고 화살표 실행 버턴을 누르시면 아래 그림처럼 console창에 방향키값이 정상적으로 출력되는 것을 확인 하실 수 있을 겁니다.



[4단계] : processing에서 만든 윈도우창이 뜨면서 3D box가 보여지는데 조이스틱으로 조정을 하면 이 3D box가 움직이는 걸 확인 하실 수 있을 꺼에요.


4. 결과


processing에서 실제로 조이스틱을 조정하면 움직이는 이미지 입니다.


실제 폰으로 촬영한 영상으로 살펴 봅시다.


만약, 여러분들이 processing를 좀 더 깊게 공부하시면 아두이노와 processing를 연동한 재밌는 작품들을 만드실 수 있을 거라 생각됩니다.

마무리


이렇게 시리얼통신을 통해서 읽은 데이터 값으로 processing 제어하는 실험을 하였습니다. 예전에는 processing에서 아두이노를 제어 했다면 반대로 아두이노에서 processing를 제어한 실험인데 그렇게 어렵지 않죠. 여기서 부터 출발하여 좀 더 다양한 시각화 표현을 processing 에서 코딩하면 좀 더 멋진 작품으로 만들어 질 수 있습니다.

여러분들도 한번 다른 모형을 만들어서 제어를 꼭 해보셨으면 합니다. 최대한 의미 전달을 목적으로 코딩을 최소화 하기 위해서 box()함수로 상자도형을 간단히 제어를 했짐나 좀 더 멋진 모형을 만들어서 멋진 작품을 만드셨으면 하네요.

댓글()