예전에 학생시절에는 PSP를 참 갖고 싶어했습니다. 지하철에서 플레이 하는 사람들을 보면 참 부러웠었지요.

그리고 세월이 지나 지금. PSP는 과거의 유물이 되었고 지금은 다들 스마트폰을 들고 다닙니다. 그리고 PPSSPP란 에뮬레이터가 나와서 PSP게임을 굴릴 수 있게 되었습니다.


하지만 터치인터페이스는 무진장 게임하기 불편했습니다. 그래서 스마트폰용 조이패드를 차던중 이런 녀석이 있더군요.



로지텍 파워쉘입니다. 문제는 이놈은 아이폰 전용이었고 범용성은 내다 버린 것이 문제였습니다. 사실 전 이런놈이 있는 줄도 몰랐는데 누가 컨셉 겹친다고 해서 찾아봤더니 있더군요. 결국 덤핑했다는 후문이 있지만...



어쨌거나 저는 대충 연구실에서 시간 나는대로 게임용 컨트롤러를 만들기로 했습니다.


그래서 만든 것이 바로 위의 물건입니다. 아직 하우징은 만들어지지 않았고 동작만 되는 형편입니다.


일단 제작을 위한 준비물은...



일단 준비물은 아두이노를 썼으니 아두이노가 필요하겠지요. 크기가 크면 문제가 많으므로 작은 모델이 필요합니다. 그리고 조이패드로 인식 시켜야 했으므로 Leonardo와 호환되어야 했지요. 그래서 찾은 모델이 이 모델입니다. 위의 것은 SparkFun 정품이고 아래의 물건은 그것의 호환 제품입니다. 어떤 것을 써도 상관없습니다. 개인적으로 호환품도 크게 문제는 없었습니다.


(프로 마이크로 입니다. 프로 미니, 나노 아니에요!)




버튼도 SparkFun제품을 사용했습니다. 이런 식을 나온 버튼이 상당히 누르는 것이 좋더군요. 하우징 설계할 때도 편리합니다. 뭣하면 다른 스위치를 써도 그만입니다.


조이패드도 SparkFun 호환품으로 구입. 사실 아두이노 키트사면 들어있어요.



그리고 대망의 스마트폰 고정 가이드는 없는 것 빼고 다 판다는 Coms의 제품입니다. 진짜 이 회사의 내부가 궁금합니다. 매번 있을까? 라고 생각하면 있습니다. 우리나라에서 이런 회사가 있다는 것이 자랑스럽습니다.



그외에 준비물로는 부품 고정용 만능 기판 PCB(대충 사서 톱으로 썰어야 합니다.)와 USB OTG케이블, 그리고 USB micro B 케이블(스마트폰 데이터 케이블도 상관없습니다.) 다 Coms에서 팔아요....


음...배선을 깜빡했네요. 배선은.... 그냥 아두이노 책을 보시고 해주시길 바랍니다.(...)


버튼들은 GND와 각 디지털 핀.(Tact스위치는 4개의 다리 중 대각선으로 해주시는 것이 정신건강에 좋습니다. 2개씩은 사실 하나의 다리라 헷갈리면 X되요.


조이패드는 핀배열에 맞게 해주시면 됩니다.


5V-5V

GND-GND

VRx-A0

VRy-A1

SW - 디지털핀 아무거나


그리고 프로그램은 우선 라이브러리를 설치해야 하는데

https://github.com/MHeironimus/ArduinoJoystickLibrary


이것을 사용합니다.

파일 다운로드는 https://github.com/MHeironimus/ArduinoJoystickLibrary/archive/master.zip


위 파일을 다운로드 받은다음


아두이노 IDE에서

스케치- 라이브러리 가져오기-Add Library를 선택하고

해당 파일을 선택합니다.


그리고 아두이노에 넣을 스케치는 다음과 같습니다.


 // Simple example application that shows how to read four Arduino
// digital pins and map them to the USB Joystick library.
//
// The digital pins 9, 10, 11, and 12 are grounded when they are pressed.
//
// NOTE: This sketch file is for use with Arduino Leonardo and
//       Arduino Micro only.
//
// by Matthew Heironimus
// 2015-11-20
//--------------------------------------------------------------------

#include <Joystick.h>

void setup() {
  // Initialize Button Pins
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
  pinMode(15, INPUT_PULLUP);
  pinMode(16, INPUT_PULLUP);
  // Initialize Joystick Library
  Joystick.begin();
}

// Constant that maps the phyical pin to the joystick button.
const int pinToButtonMap = 0;
const int deadZone = 30;

// Last state of the button
int lastButtonState[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
//int lastButtonState[4] = {0,0,0,0};
int X=0;
int Y=0;

void loop() {

  // Read pin values
  for (int index = 0; index < 17; index++)
  {
    int currentButtonState = !digitalRead(index + pinToButtonMap);
    if (currentButtonState != lastButtonState[index])
    {
      Joystick.setButton(index, currentButtonState);
      lastButtonState[index] = currentButtonState;
    }
  }
  X=analogRead(A0);
  Y=analogRead(A1);
  X=map(X,0,1023,127,-127);
  Y=map(Y,0,1023,-127,127);
  Joystick.setXAxis(X);
  Joystick.setYAxis(Y);

  delay(50);
}


간단하죠? 좀 반응이 느리다 싶으시면 delay를 20정도로 줄여주시면 됩니다. 저는 가끔 느린 것을 느끼기는 하는데 그냥 냅두고 있습니다.


그렇게해서 만들어진 대망의 물건



저는 실력이 미천하여 이정도밖에 못했지만 실력 좋으신 분들은 훨씬 더 좋은 물건을 만들 수 있을 거라고 봅니다.




==================2017. 5. 25======================


아두이노용 조이스틱 라이브러리의 버전이 업그레이드 되었습니다.

https://github.com/MHeironimus/ArduinoJoystickLibrary


이전과 동일한 곳이지만 그전에는

X,Y 축과 버튼까지만 지원했지만 이제는 거의 표준이라 할 수 있는

아날로그 스틱

HAT 방향키

트리거

버튼


이렇게 지원합니다. 즉, 엑스박스 패드의 형식을 그대로 지원하고 엑스박스 패드를 지원하는 게임에서 정상적으로 작동되게 할 수 있습니다.


제가 만든 구조는


왼쪽 4개의 버튼은 방향키로

아래의 아날로그는 HAT으로

오른쪽은 버튼으로 인식되게 했습니다.


하지만 마음을 먹으면 아날로그 스틱을 2개 달고 엑스박스 패드처럼 만들 수도 있는 것이지요.


그래서 이번에 수정한 소스를 공개합니다.


#include <Joystick.h>

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD,
  5, 1,                  // Button Count, Hat Switch Count
  true, true, false,     // X and Y, but no Z Axis
  false, false, false,   // No Rx, Ry, or Rz
  false, false,          // No rudder or throttle
  false, false, false);  // No accelerator, brake, or steering


int lastDpadState[]={0,0,0,0};
int lastButtonState[]={0,0,0,0,0,0,0};
int lastSWState=0;

void setup() {
  // Initialize Button Pins
  pinMode(10, INPUT_PULLUP);//A
  pinMode(14, INPUT_PULLUP);//B
  pinMode(15, INPUT_PULLUP);//X
  pinMode(16, INPUT_PULLUP);//Y
 
  pinMode(6, INPUT_PULLUP);//Up
  pinMode(8, INPUT_PULLUP);//down
  pinMode(7, INPUT_PULLUP);//left
  pinMode(9, INPUT_PULLUP);//right
 
  pinMode(2, INPUT_PULLUP);//HAT_SW

  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);
 /*not using pin Num
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  digitalWrite(11,LOW);
  digitalWrite(12,LOW);
  digitalWrite(13,LOW);  
  */
  //A0=X A1=Y

  // Initialize Joystick Library
  Joystick.begin();
  Joystick.setXAxisRange(-1023, 0);
  Joystick.setYAxisRange(-1023, 0);

  Joystick.setRxAxisRange(-1, 1);
  Joystick.setRyAxisRange(-1, 1);
}

void Analog()
{
  int x = analogRead(A0);
  int y = analogRead(A1);
 
  Joystick.setXAxis(-x);
  Joystick.setYAxis(-y);
}

void Buttons()
{
  for(int index=0; index < 7 ;index++)
  {
    int currentButtonState = !digitalRead(index+10);
    if (currentButtonState !=lastButtonState[index])
    {
      //Joystick.setButton(index, currentButtonState);
      switch (index)
      {
        case 0://A
          Joystick.setButton(0, currentButtonState);
          break;

        case 4://B  
        case 5://X
        case 6://Y
          Joystick.setButton(index-3,currentButtonState);
          break;
          
        default:
          break;
      }
      
      lastButtonState[index]=currentButtonState;
    }
  }
 
}


void Dpad()
{
  for (int index = 0; index < 4; index++)
  {
      int currentDpadState = !digitalRead(index+6);
      if (currentDpadState != lastDpadState[index])
      {
        switch(index){
          case 0://UP
            if (currentDpadState == 1) {
              Joystick.setRyAxis(1);
            } else {
              Joystick.setRyAxis(0);
            }
            break;
          
          case 1://left
          if (currentDpadState == 1) {
            Joystick.setRxAxis(-1);
          } else {
            Joystick.setRxAxis(0);
          }
          break;
            
          case 2://down
          if (currentDpadState == 1) {
            Joystick.setRyAxis(-1);
          } else {
            Joystick.setRyAxis(0);
          }
          break;

          case 3://right
          if (currentDpadState == 1) {
            Joystick.setRxAxis(1);
          } else {
            Joystick.setRxAxis(0);
          }
          break;
            
        }
        lastDpadState[index] = currentDpadState;
      }
  }
}


void HAT()
{
  bool valueChanged =false;
  for (int index = 0; index < 4; index++)
  {
      int currentDpadState = !digitalRead(index+6);
      if (currentDpadState != lastDpadState[index])
      {
        valueChanged =true;
        lastDpadState[index] = currentDpadState;
      }    
  }

 
  if (valueChanged) {
      
    if ((lastDpadState[0] == 0)
      && (lastDpadState[1] == 0)
      && (lastDpadState[2] == 0)
      && (lastDpadState[3] == 0)) {
        Joystick.setHatSwitch(0, -1);//HatSwitch0 is not changed
    }
    if (lastDpadState[0] == 1) {
      Joystick.setHatSwitch(0, 0);
    }
    if (lastDpadState[1] == 1) {
      Joystick.setHatSwitch(0, 90);
    }
    if (lastDpadState[2] == 1) {
      Joystick.setHatSwitch(0, 180);
    }
    if (lastDpadState[3] == 1) {
      Joystick.setHatSwitch(0, 270);
    }
  }//value Changed()
}
void SW()
{
  int currentSWState = !digitalRead(2);
  if(lastSWState != currentSWState)
  {
    Joystick.setButton(4, currentSWState);
    lastSWState=currentSWState;
  }
}

void loop()
{
  Analog();
  //Dpad();
  HAT();
  Buttons();
  SW();
  delay(2);
}


회로 연결은


0번 버튼은 10번핀에

1번 버튼은 14번핀에

2번 버튼은 15번핀에

3번 버튼은 16번핀에


방향버튼(D-PAD)는

위버튼은 6번핀

왼쪽버튼은 7번핀

아래버튼은 8번픈

오른쪽 버튼은 9번핀 으로 연결합니다.


D-PAD는 HAT0로 인식되게 됩니다.


그리고 아날로그는 X축은 A0 Y축은 A1로 연결합니다.

만약 방향이 반대로 인식될 경우에는 위 소스에서 Analog()함수를 찾으신 다음


  Joystick.setXAxis(-x);
  Joystick.setYAxis(-y);


이 둘에서 -를 지워주시고 Seup()안의


  Joystick.setXAxisRange(-1023, 0);
  Joystick.setYAxisRange(-1023, 0);


이 두줄을 삭제해 주시면 됩니다.


아날로그 스틱을 눌렀을 때 스위치는 2번 핀에 연결했습니다.

제가 사용한 Arduino Pro Micro의 구조상 이 구조가 가장 간단해서 이렇게 만들었습니다.


코드나 회로에 자신있으신 분들은 제대로 만드실 수 있을 겁니다.

,

Avidemux는 옛날 VirtualDub을 대체하는 대표적인 오픈소스 프로그램입니다. 실제로 성공적으로 안착했고 지금은 VirtualDub이 뭐였는지도 모르는 사람이 많을 정도입니다.


그런데 그동안 우분투에서 공식적으로 제공해주던 Avidemux가 16.04에서 제공이 되지 않습니다. 새로바뀐 libx264때문이라는데요. 호환문제가 있다면 어딘가에서는 호환문제를 해결했을 겁니다.


역시나 호환문제가 해결된 PPA가 있었습니다.


설정-소프트웨어 & 업데이트를 선택합니다.

기타소프트웨어 탭


추가버튼을 누르고


deb http://archive.getdeb.net/ubuntu xenial-getdeb apps  


위 내용을 추가합니다.

그리고 확인 후 터미널을 열고

wget -q -O- http://archive.getdeb.net/getdeb-archive.key | sudo apt-key add -

위 명령을 칩니다.


그 다음


sudo apt-get update

sudo apt-get install avidemux2.6-qt


이제 Avidemux를 설치할 수 있게 되었습니다.


우분투 16.04에서 Avidemux를 쓸 수 있다.


,

어쩌면 황당할 수도 있고 어쩌면 당연할 수도 있지만 제 블로그 방문객들의 정보를 조금 살펴봤습니다.


제 블로그에는 구글의 애널리틱스가 적용되어 있습니다. 제 블로그에 들어오신 분들의 운영체제와 브라우저를 확인하고 약간의 연구를 위해서 6월 한 달 동안 방문객 상황을 확인을 해봤습니다.

우선 브라우저 통계입니다.


브라우저는 크롬이 제일 많은 것으로 나왔습니다. 그도 그럴 것이 전 세계적으로 크롬의 점유율이 60%가 넘었다는 것은 모두가 알고 있는 사실입니다. 그래서 마이크로소프트가 Edge를 만들 때 크롬을 벤치마크 했다는 것은 알게모르게 유명한 이야기입니다. 16%의 IE가 보이는데 제 블로그는 성향상 당연하게도 IE를 싫어합니다. 그래도 의외로 선전하고 있네요. Firefox가 그 뒤를 바로 뒤쫓고 있습니다. 저는 Firefox의 변종인 PaleMoon을 쓰는데 제가 접속을 하면 Firefox로 잡히게 됩니다. 따라서 저 Firefox의 세션 수에서 100~200정도를 빼야 진짜 Firefox로 접속한 방문객 수가 나오게 될 겁니다.


그런데 이상하다는 생각이 드는 것이 이 블로그는 우분투 블로그입니다. 우분투관련 자료를 찾으러 혹은 정보를 찾으러 오시는 분들이 많을텐데 우분투의 기본브라우저인 Firefox가 Windows의 브라우저인 IE보다 적습니다. 이 의문은 운영체제 분석을 보니 바로 알 수가 있었습니다.

우선 제 블로그에서 모바일파트를 확인했습니다. 우선 iOS가 220세션이라고 나옵니다. iOS에서 다른 브라우저는 쓸 것이 못 됩니다. 따라서 위의 브라우저 통계에서 iOS통계의 대부분은 위의 사파리 통계 안에 있다고 봐야 합니다. 실제로 둘이 비슷합니다. 1위는 당연하게도 안드로이드인데 한국에서 안드로이드 점유율이 70%를 돌파한지 오래이기 때문에 당연하다면 당연하다고 할 수 있습니다. 게다가 저는 안드로이드 관련 글도 적고 있습니다. 많이 들어올 만 합니다. 그러면 여기서 위의 크롬을 신경써야 하는 이유가 나오게 됩니다.


안드로이드의 4.0버전 이후 기본 브라우저는 크롬입니다. 실제로 User-Agent String을 보면 안드로이드에서 접속을 해도 크롬이라고 적혀있는 것을 알 수 있습니다. 그런데 781이면 좀 적군요. 즉 대부분 방문객은 데스크탑으로 들어오신 겁니다. 우분투 블로그니까 그러려니 했습니다. 그런데...

여느 블로그와 다를 것 없는 분포도!!!! Linux가 Mac보다 높은 것이 특징이라면 특징이지만...


Windows 접속량이 Linux의 2배를 넘습니다. IE가 접속량 2위인 의문이 풀리는 순간입니다. Windows로 제 블로그에 접속하는 분들이 의외로 많았군요. 제 블로그에서 딱히 긁어갈 정보는 적다고 생각했었는데 아마도 VirtualBox로 우분투를 설치해서 쓰시는 분들이거나(가상머신에서 인터넷하면 죽을맛이지요. 이해합니다.) 회사에서 우분투 사용 정보를 찾는데 사무실PC는 Windows이거나 한 경우인 듯 합니다. 우분투를 저처럼 취미 혹은 주력으로 쓰는 사람은 대한민국에서 꽤 적기 때문에 이런 결과가 나온 것이 아닐까 하는 생각이 드는데요. 그래도 Linux 20%가 Linux1%도 안 되는 나라에서 리눅스 관련 블로그임을 다행히 대변해 주는 듯 합니다.


미리 말씀드리지만 제 블로그에서 해외 접속 비율은 무시할 수 있는 수준입니다. 한국어로만 되어있어서 그렇기도 하고 제가 영어권 사람들을 전혀 배려하지 않았기 때문이기도 합니다. 뭣하면 구글링하면 다 나오니까 굳이 제 블로그에 해외 언어권에서 올 이유가 없습니다. (저부터 필요하면 영어로 구글링합니다. 그렇게 찾은 해답을 블로그에 적기도 하고요.) 결론적으로 말하면:

 제 블로그만 봤을 경우지만 "우리나라 사람들 중에서 우분투라는 것을 아는 사람들은 Windows를 설치한 후에 Chrome도 같이 깐다." 라는 결과가 나오는데요. 다른 사람들은 어떻게 나오는지 궁금하네요. 이런 것을 모으다보면 재미있는 통계학 논문 하나가 나올지도 모르겠습니다.


어쨌거나 흥미로운 결과가 나왔습니다. 저는 Firefox 신봉자이지만 Chrome이 정말 대중화가 잘 되었군요. IE쓰면 컴맹이라는 의견을 주신분도 있지만(사실 대다수 컴맹들이 IE만 쓰기는 합니다. 그렇다고 Chrome 쓰는 사람중에 컴맹이 없는 것은 아니지만)기본 브라우저라는 특성상 어쩔 수 없을 듯 합니다. (Chrome보다 화면 갱신이 빠르던 Edge는 어떻게 되었는가!!!)

일단 6월~현재까지의 결과가 이렇고 학생들의 방학이 시작되는 7월부터는 어떻게 되는지 한번 보도록 해보지요. 그래봐야 블로그 특성상 별 차이 없을 것 같지만(...)

,