아두이노 > Wii 눈차크를 이용하여 구글 어스 비행 시뮬레이터를 제어해봅시다.

TODAY1,052 TOTAL679,975
사이트 이용안내
Login▼/회원가입
최신글보기 질문게시판 기술자료 동영상강좌

아두이노 센서 ATMEGA128 PWM LED 초음파 AVR 블루투스 LCD UART 모터 적외선


BASIC4MCU | 아두이노 | 아두이노 | Wii 눈차크를 이용하여 구글 어스 비행 시뮬레이터를 제어해봅시다.

페이지 정보

작성자 키트 작성일2017-09-11 16:50 조회871회 댓글0건

본문

 

Wii 눈차크를 이용하여 구글 어스 비행 시뮬레이터를 제어해봅시다.

2093095301_ATjtMmpz_tutorial_bar.png2014-09-01 15:53:39

개요

구글 어스에는 비행 시뮬레이터라는 기능이 있습니다. 

이 기능을 사용하게되면 마우스와 키보드를 이용하여 멋진 자연 경관이나 내가 사는 곳을 비행기 조종사가 되어 날아 볼 수 있습니다.

2093095301_tFTqIj4W_140901052011.png
2093095301_6TGlCK7q_140901051946.png
이 기능을 마우스, 키보드가 아닌 Wii 눈차크(Nunchuck)를 이용하여 더 실감 나게 비행기를 조정해 봅시다.
(정밀한 조정은 불가능하나 비행기 게임을 하는 것 같이 조정 해 봅시다.)
2093095301_KmRA2uw4_140901052212.jpg

미리 보기 동영상


시작 전 개념 이해하기

 - Wii 눈차크 가속도계 활용하기
 - 구글 어스 비행 시뮬레이터
         

부품 목록

NO부품명수량상세설명
1아두이노 우노 R31아두이노
2눈차크1눈차크
3눈차크 어뎁터1눈차크 어뎁터

부품명아두이노 우노 R3눈차크눈차크 어뎁터
파트2093095301_zAHaoM48_140901030829.jpg2093095301_AJyHbCBg_140901030842.jpg2093095301_aty5YxOT_140901030849.jpg


하드웨어 Making

회로도


2093095301_BFlcs61z_140901031009.png

브레드보드 레이아웃

2093095301_onTqpdxE_140901031023.png




2093095301_pBaD1UmY_140901031050.jpg

* 눈차크 어뎁터를 사용할 경우 아두이노와 쉽게 연결 할 수 있습니다.(밑의 사진은 아날로그 A2~A5 핀과 연결)

2093095301_b2sEu97i_140901033557.jpg

소프트웨어 Coding

* 구글 어스 설치 및 비행기 시뮬레이터 사용 방법
구글 어스(Google earth)는 http://www.google.com/earth 에서 무료로 다운로드를 할 수 있습니다.

구글 어스를 다운로드 후 설치를 하면 시작 도움말과 함께 지구가 화면에 나옵니다.(여기서 자신이 보고자 하는 곳을 보시면 됩니다.)
2093095301_9Ws6yXED_140901034158.png
2093095301_oTgNh2yC_140901034251.png

비행 시뮬레이션을 사용할려면 도구 -> 비행 시뮬레이터 시작 을 누르시면 됩니다.(단축키 : Ctrl + Alt + A)2093095301_DMzkZ7dw_140901034340.png

비행 시뮬레이터를 실행하면 밑의 사진처럼 창이 뜨는데 항공기를 선택하고(처음이시면 SR22을 추천합니다.), 시작 위치를 정한 뒤 비행 시작 버튼을 누르면 시작합니다.
2093095301_VHcQSuvd_140901034417.png
2093095301_HNMdXlZe_140901034533.png
2093095301_p7GDtLPc_140901044028.png


비행 시뮬레이터 키보드 단축키(프로세싱에서 눈차크 값에 따른 다른 명령을 주고자 하면, 밑의 단축키를 보시고 설정해 주시면 됩니다.)
2093095301_woeHQS8g_140901043552.png
2093095301_FIw5ZoXW_140901043559.png


눈차크로 구글 어스 비행 시뮬레이터 제어 하는 순서

 아두이노 스케치를 이용하여 아두이노에 업로드 -> 구글 어스 비행 시뮬레이터를 실행 -> 프로세싱 실행 -> 구글 어스 활성 -> 눈차크 Z버튼을 누른 후 제어


아두이노 소스
 * 본 소스는 스케치를 사용하여 작성 / 업로드 합니다. 스케치에 대한 사용법은 링크를 참고 하시기 바랍니다.
 * 밑의 프로세싱 소스를 작성하시기 전에 아두이노 소스를 작성, 업로드 하시기 바랍니다.
 * 아두이노에 소스를 업로드 시킨 후, 아두이노와 USB를 연결된 상태로 프로세싱를 작성, 업로드 하시기 바랍니다.
 * 본 소스를 수정 했을 경우, 밑의 프로세싱 소스도 수정해야 합니다. 수정 하실 경우 밑의 설명을 읽고 수정하시기 바랍니다.
 * 아두이노 소스는 Nunchuck 라이브러리를 사용합니다. 라이브러리는 링크에서 받으시면 됩니다.(라이브러리 사용법은 링크를 참고 하시기 바랍니다.)
#include #include "Nunchuck.h"int offsetX, offsetY, offsetZ;
// 마우스가 중앙에 있을 때 0을 가져오기 위해 센서에 추가 하는 값void setup(){ Serial.begin(57600); nunchuckSetPowerpins(); nunchuckInit(); // 초기화 핸드셰이크를 전송한다. nunchuckRead(); // 처음엔 무시한다. delay(50);}void loop(){ nunchuckRead(); delay(6); boolean btnC = nunchuckGetValue(wii_btnC); boolean btnZ = nunchuckGetValue(wii_btnZ); if(btnC) { offsetX = 127 - nunchuckGetValue(wii_accelX) ; offsetY = 127 - nunchuckGetValue(wii_accelY) ; } Serial.print("Data,"); printJoy(nunchuckGetValue(wii_joyX)); printJoy(nunchuckGetValue(wii_joyY)); printButton(nunchuckGetValue(wii_btnZ)); printAccel(nunchuckGetValue(wii_accelY), offsetY); printAccel(nunchuckGetValue(wii_accelX), offsetX); // 프로세싱에서 자료를 받아서 분석 하기 위해 "Data, 조이패드 X값, 조이패드 Y값, Z버튼, 가속도 Y값, 가속도 X값" 순서로 보낸다.
// 프로세싱에서는 아두이노에서 보낸 값을 " , " 로 나눠서 값을 저장한다.
Serial.println();} void printAccel(int value, int offset){ // 가속도 값을 출력 하는 함수 Serial.print(adjReading(value, 127-50, 127+50, offset)); Serial.print(",");}void printJoy(int value){ // 조이패드 값을 출력하는 함수 Serial.print(adjReading(value,0, 255, 0)); Serial.print(",");}void printButton(int value){ // 버튼 값을 출력하는 함수 if( value != 0) value = 127; Serial.print(value,DEC); Serial.print(",");}int adjReading( int value, int min, int max, int offset){ // 받은 값의 범위를 재설정 해주는 함수 value = constrain(value + offset, min, max);
// 범위 한정(min값 보다 낮을경우 min값으로, max값보다 클 경우 max값으로 바꿔준다.) value = map(value, min, max, -127, 127); // 값의 범위 재설정 return value; }

// 출처 : 레시피로 배우는 아두이노 쿡북(저자 : 마이클 마골리스)
// 아두이노 쿡북에 있는 소스를 참고하여 수정, 추가하였습니다.


프로세싱 코드
 * 밑의 소스는 프로세싱으로 작성 / 업로드 합니다. 프로세싱에 대한 사용법은 링크를 보고 참고 하시기 바랍니다.
import java.awt.AWTException;import java.awt.Robot;import java.awt.event.InputEvent;import java.awt.event.KeyEvent;import processing.serial.*;import java.awt.Dimension;Serial myPort;  // Serial 클래스의 오브젝트를 생성한다.arduMouse myMouse; // arduMouse 클래스의 오브젝트를 생성한다.String message = null;int maxDataFields = 7; // 최대 데이터 필드를 선언한다.(가속도계 3개, 버튼 2개, 조이스틱 축 2개)boolean isStarted = false;int accTemp, sightTemp, joyX, joyY, btnZ; // 아두이노에서 보낸 데이터 값이 여기에 저장이 된다.     void setup() {  size(260, 260);  PFont fontA = createFont("Arial.normal", 12);    textFont(fontA);  short portIndex = 0;  // com 포트를 선택한다.(0번은 연결된 첫번째 포트)  String portName = Serial.list()[portIndex];
// 연결된 포트 리스트가 프로세싱에 출력이 된다. 이것을 보고 위에서 포트를 선택해 주면 된다. println(Serial.list()); println(" Connecting to -> " + portName) ; myPort = new Serial(this, portName, 57600); myMouse = new arduMouse(); fill(0); text("Start Google FS in the center of your screen", 5, 40); text("Center the mouse pointer in Google earth", 10, 60); text("Press and release Nunchuck Z button to play", 10, 80); text("Press Z button again to pause mouse", 20, 100);}void draw() { processMessages(); if (isStarted == false) { if ( btnZ != 0) { println("Release button to start"); do{ processMessages();} while(btnZ != 0); myMouse.mousePress(InputEvent.BUTTON1_MASK); // 눈차크에 Z버튼을 눌렀을 경우 눈차크 인식을 시작한다. isStarted = true; } } else { if ( btnZ != 0) { isStarted = false; background(204); text("Release Z button to play", 20, 100); print("Stopped, "); } else{ myMouse.move(joyX, joyY); // 조이스틱의 X, Y의 값을 이용해 마우스를 조정한다. myMouse.speedCon(accTemp); // 눈차크 가속도의 Y축 값을 이용하여 시뮬레이터의 속도를 조정한다. myMouse.sight(sightTemp); // 눈차크 가속도의 X축 값을 이용하여 시뮬레이터의 방향을 조정한다. fill(0); stroke(255, 0, 0); background(#8CE7FC); ellipse(127+joyX, 127+joyY, 4, 4); } }}void processMessages() { while (myPort.available () > 0) { message = myPort.readStringUntil(10); if (message != null) { String [] data = message.split(","); // 콤마(,) 를 이용하여 받은 데이터를 자른다. if ( data[0].equals("Data"))// 자른 데이터중 제일 처음이 Data일 경우 { try { joyX = Integer.parseInt(data[1]); // 조이스틱의 X값 joyY = Integer.parseInt(data[2]); // 조이스틱의 Y값 btnZ = Integer.parseInt(data[3]); // Z버튼 accTemp = Integer.parseInt(data[4]); // 눈차크 가속도 Y축값 sightTemp = Integer.parseInt(data[5]); // 눈차크 가속도 X축값 } catch (Throwable t) { println("."); } } } }}class arduMouse { Robot myRobot; static final short rate = 4; int centerX, centerY; arduMouse() { try { myRobot = new Robot(); } catch (AWTException e) { e.printStackTrace(); } Dimension screen = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); centerY = (int)screen.getHeight() / 2 ; centerX = (int)screen.getWidth() / 2; } void move(int offsetX, int offsetY) { // 주어진 값으로 마우스를 이동한다.(조이스틱 x, y 값) myRobot.mouseMove(centerX + (rate* offsetX), centerY - (rate * offsetY)); } void mousePress( int button) { // Z버튼이 눌렸을 경우 마우스 클릭을 한다. myRobot.keyRelease(KeyEvent.VK_SHIFT); myRobot.mousePress(button) ; } void speedCon(int accTemp){ // 눈차크 가속도 Y축의 값을 기준으로 30이 넘어갈경우 가속, 0보다 낮을경우 감속을 한다.
// 가속일 경우 키보드 Page_UP키 감속일 경우 Page_Down키를 누르면 된다.
// keyPress함수를 사용할 경우 키가 계속 눌러진 상태로 있기 때문에 꼭 keyRelease함수를 사용해야한다. if(accTemp > 30){ myRobot.keyRelease(KeyEvent.VK_PAGE_DOWN); myRobot.keyPress(KeyEvent.VK_PAGE_UP); } else if(accTemp < 0){ myRobot.keyRelease(KeyEvent.VK_PAGE_UP); myRobot.keyPress(KeyEvent.VK_PAGE_DOWN); } } void sight(int sightTemp){ // 눈차크 가속도 X축의 값을 기준으로 100보다 크면 오른쪽으로, 100보다 작으면 왼쪽으로 이동한다.
// 키보드 입력은 쉬프트 + 오른쪽 화살표, 쉬프트 + 왼쪽 화살표 if(sightTemp >= 100){ myRobot.keyRelease(KeyEvent.VK_SHIFT); myRobot.keyRelease(KeyEvent.VK_LEFT); myRobot.keyPress(KeyEvent.VK_SHIFT); myRobot.keyPress(KeyEvent.VK_RIGHT); } else if(sightTemp <= -100){ myRobot.keyRelease(KeyEvent.VK_SHIFT); myRobot.keyRelease(KeyEvent.VK_RIGHT); myRobot.keyPress(KeyEvent.VK_SHIFT); myRobot.keyPress(KeyEvent.VK_LEFT); } else{ myRobot.keyRelease(KeyEvent.VK_SHIFT); myRobot.keyRelease(KeyEvent.VK_LEFT); myRobot.keyRelease(KeyEvent.VK_RIGHT); } }}
 

소프트웨어 설명

구글 어스에서 비행 시뮬레이터 조정은 마우스와 키보드를 이용합니다.

이것을 눈차크를 이용해 키보드와 마우스를 제어하여 구글 어스 비행 시뮬레이터를 조정하는 겁니다.

조이스틱 X, Y 값은 마우스의 이동을, 버튼 z는 마우스 클릭을, 눈차크 가속도 X, Y 값은 키보드를 제어합니다.

- 아두이노
아두이노 소스는 Nunchuck 라이브러리를 사용합니다. 아두이노 library 폴더 안에 Nunchuck 폴더를 만든 후, Nunchuck.h와 Nunchuck.cpp 파일을 만들어 넣으시면 됩니다.(링크 에 파일이 있으니 받으셔도 되고, 밑의 소스를 메모장에 옮겨서 위의 두 제목과 확장자로 저장하셔도 됩니다.)


Nunchuck.h(출처 : 아두이노 쿡북)
/* * Nunchuck.h * Arduino library to interface with wii Nunchuck */ #ifndef Nunchuck_included#define Nunchuck_included// identities for each field provided by the wii nunchuckenum nunchuckItems { wii_joyX, wii_joyY, wii_accelX, wii_accelY, wii_accelZ, wii_btnC, wii_btnZ, wii_ItemCount };// uses pins adjacent to I2C pins as power & ground for Nunchuckvoid nunchuckSetPowerpins(); // initialize the I2C interface for the nunchuckvoid nunchuckInit(); // Request data from the nunchuckvoid nunchuckRequest();// Receive data back from the nunchuck, // returns true if read successful, else falsebool nunchuckRead(); // Encode data to format that most wiimote drivers exceptchar nunchuckDecode (uint8_t x);  // return the value for the given itemint nunchuckGetValue(int item); #endif

Nunchuck.cpp(출처 : 아두이노 쿡북)
/* * Nunchuck.cpp * Arduino library to interface with wii Nunchuck */#include "Arduino.h"   // Arduino defines#include "Wire.h"      // Wire (I2C) defines#include "Nunchuck.h"  // Defines for this library// defines for standard Arduino board (use 19 and 18 for mega)const int vccPin = 17; // +v and gnd provided through these pinsconst int gndPin = 16;   const int dataLength = 6;          // number of bytes to requeststatic byte rawData[dataLength];   // array to store nunchuck data// uses pins adjacent to I2C pins as power & ground for Nunchuckvoid nunchuckSetPowerpins(){  pinMode(gndPin, OUTPUT); // set power pins to the correct state  pinMode(vccPin, OUTPUT);  digitalWrite(gndPin, LOW);  digitalWrite(vccPin, HIGH);  delay(100); // wait for power to stabilize} // initialize the I2C interface for the nunchuckvoid nunchuckInit(){  Wire.begin();                // join i2c bus as master  Wire.beginTransmission(0x52);// transmit to device 0x52  Wire.write((byte)0x40);      // sends memory address  Wire.write((byte)0x00);      // sends sent a zero.    Wire.endTransmission();      // stop transmitting}   // Request data from the nunchuckvoid nunchuckRequest(){  Wire.beginTransmission(0x52);// transmit to device 0x52  Wire.write((byte)0x00);// sends one byte  Wire.endTransmission();// stop transmitting}// Receive data back from the nunchuck, // returns true if read successful, else falsebool nunchuckRead(){  byte cnt=0;  Wire.requestFrom (0x52, dataLength);// request data from nunchuck  while (Wire.available ()) {    byte x = Wire.read();     rawData[cnt] = nunchuckDecode(x);    cnt++;  }  nunchuckRequest();  // send request for next data payload  if (cnt >= dataLength)     return true;   // success if all 6 bytes received  else    return false; //failure}// Encode data to format that most wiimote drivers exceptchar nunchuckDecode (byte x){   return (x ^ 0x17) + 0x17;}  // return the value for the given itemint nunchuckGetValue(int item){  if( item <= wii_accelZ)    return (int)rawData[item];   else if(item  == wii_btnZ)     return bitRead(rawData[5], 0) ? 0: 1;  else if(item  == wii_btnC)     return bitRead(rawData[5], 1) ? 0: 1;   }
 
아두이노 소스는 간단합니다. 눈차크 라이브러리를 사용해서 눈차크의 값을 받고, 그것을 사용할 수 있게 값을 변경 후 시리얼 통신으로 전송 합니다.
(가속도 값은 printAccel() 함수, 조이스틱 값은 printJoy() 함수, 버튼 값은 printButton()함수를 사용해서 전송 합니다.)

이 때 프로세싱에서 데이터를 받은 후 구분하는 방법이 " , " 를 사용하여 구분하기 때문에 각 값들 사이에 꼭 , 로 구분해서 보내야 합니다.
또한 한번에 엔터값 (" \n ") 까지 끊어서 받기 때문에 전송할 자료값이 남았는데 println() 을 써서 보낼 경우 뒤에 값이 짤리게 됩니다.


- 프로세싱
프로세싱을 실행 시키면 밑의 사진 처럼 화면이 뜹니다. 이 화면이 뜨면 구글 어스 비행 시뮬레이터를 활성 시킨 후 Z버튼을 누른 후 눈차크로 제어하시면 됩니다.
2093095301_GxgbDeyh_140901050244.png

  short portIndex = 0;  // com 포트를 선택한다.(0번은 연결된 첫번째 포트)  String portName = Serial.list()[portIndex];
// 연결된 포트 리스트가 프로세싱에 출력이 된다. 이것을 보고 위에서 포트를 선택해 주면 된다. println(Serial.list()); println(" Connecting to -> " + portName) ; myPort = new Serial(this, portName, 57600);
시리얼 통신을 위해 아두이노와 연결된 포트를 찾고 연결 해 주는 부분입니다.
우선 실행을 하시면 밑의 사진 처럼 연결된 포트번호가 나옵니다. 이중에 아두이노와 연결된 포트 번호를 찾아서, portIndex를 설정해 주시면 됩니다.
(제 경우 아두이노는 4번 포트에 연결되 있어서, portIndex를 0으로 설정해 주었습니다.)
2093095301_XQ0d46Gn_140901044442.png

 myMouse.move(joyX, joyY); // 조이스틱의 X, Y의 값을 이용해 마우스를 조정한다. myMouse.speedCon(accTemp); // 눈차크 가속도의 Y축 값을 이용하여 시뮬레이터의 속도를 조정한다. myMouse.sight(sightTemp); // 눈차크 가속도의 X축 값을 이용하여 시뮬레이터의 방향을 조정한다.
위의 3함수는 마우스, 키보드를 제어하는 함수 입니다. 아두이노에서 보낸 눈차크 값을 입력 받아 그에 맞는 마우스와 키보드의 동작을 제어합니다.

 while (myPort.available () > 0) {    message = myPort.readStringUntil(10);     if (message != null) {      String [] data  = message.split(","); // 콤마(,) 를 이용하여 받은 데이터를 자른다.       if ( data[0].equals("Data"))// 자른 데이터중 제일 처음이 Data일 경우      {        try {          joyX = Integer.parseInt(data[1]); // 조이스틱의 X값          joyY = Integer.parseInt(data[2]); // 조이스틱의 Y값          btnZ = Integer.parseInt(data[3]); // Z버튼          accTemp = Integer.parseInt(data[4]); // 눈차크 가속도 Y축값          sightTemp = Integer.parseInt(data[5]); // 눈차크 가속도 X축값        }        catch (Throwable t) {          println(".");         }      }    }  }
이 부분은 아두이노에서 보낸 값을 확인하고 분해 하는 부분입니다. 아두이노에서 보낸 데이터를 한 줄 단위로 끊어서 받은 다음, 그 안에서 "," 를 기준으로 데이터를 잘라서 각각 저장합니다.

아두이노에서 정상적으로 전송이 됬으면 총 6개의 값이 나뉘는데, 맨처음엔 "Data"라는 글이 저장되고, 그 다음 부터 순서대로 조이스틱 X값, 조이스틱 Y값, Z버튼, 눈차크 가속도 Y축 값, 눈차크 가속도 X축 값이 저장됩니다.

  void speedCon(int accTemp){ // 눈차크 가속도 Y축의 값을 기준으로 30이 넘어갈경우 가속, 0보다 낮을경우 감속을 한다.
// 가속일 경우 키보드 Page_UP키 감속일 경우 Page_Down키를 누르면 된다.
// keyPress함수를 사용할 경우 키가 계속 눌러진 상태로 있기 때문에 꼭 keyRelease함수를 사용해야한다. if(accTemp > 30){ myRobot.keyRelease(KeyEvent.VK_PAGE_DOWN); myRobot.keyPress(KeyEvent.VK_PAGE_UP); } else if(accTemp < 0){ myRobot.keyRelease(KeyEvent.VK_PAGE_UP); myRobot.keyPress(KeyEvent.VK_PAGE_DOWN); } } void sight(int sightTemp){ // 눈차크 가속도 X축의 값을 기준으로 100보다 크면 오른쪽으로, 100보다 작으면 왼쪽으로 이동한다.
// 키보드 입력은 쉬프트 + 오른쪽 화살표, 쉬프트 + 왼쪽 화살표 if(sightTemp >= 100){ myRobot.keyRelease(KeyEvent.VK_SHIFT); myRobot.keyRelease(KeyEvent.VK_LEFT); myRobot.keyPress(KeyEvent.VK_SHIFT); myRobot.keyPress(KeyEvent.VK_RIGHT); } else if(sightTemp <= -100){ myRobot.keyRelease(KeyEvent.VK_SHIFT); myRobot.keyRelease(KeyEvent.VK_RIGHT); myRobot.keyPress(KeyEvent.VK_SHIFT); myRobot.keyPress(KeyEvent.VK_LEFT); } else{ myRobot.keyRelease(KeyEvent.VK_SHIFT); myRobot.keyRelease(KeyEvent.VK_LEFT); myRobot.keyRelease(KeyEvent.VK_RIGHT); } }
위의 두 함수는 키보드를 제어 하는 함수 입니다. keyPress() 함수는 괄호 안에 맞는 키를 누르는 함수이고, keyRelease() 함수는 괄호 안의 맞는 키를 풀어주는 함수 입니다. keyPress()함수를 사용하면 계속 눌러진 상태로 있기 때문에, 그에 맞는 키를 꼭 keyRelase() 해주셔야 합니다.
(키보드에 맞는 KeyEvent는 링크에 나와 있습니다.)

http://kocoafab.cc/make/view/175 

댓글 0

조회수 871

등록된 댓글이 없습니다.

아두이노HOME > 아두이노 > 전체 목록

게시물 검색

2019년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
2018년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
Privacy Policy
MCU BASIC ⓒ 2017
모바일버전으로보기