분명 고3일 때 하라는 공부는 안 하고 블로그에 글을 쓰고 있었는데, 정신차려보니 어느덧 군 복무도 끝나가고 있습니다.
    새로운 마음으로 공부하고 작업한 내용을 기록하고자 새 Jekyll 블로그를 만들었습니다.

    댓글을 쓰셔도 보기가 어려우니, 불편하시더라도 궁금한 점은 옮긴 새 블로그에 댓글로 달아 주시면 성심성의껏 답변해 드리겠습니다. 감사합니다.
    2021년 2월 14일

    새 블로그 주소는 https://luftaquila.io입니다.
     

    LUFT - AQUILA

    A sky sailing Electron.

    luftaquila.io




    블로그를 이전하면서 글도 같이 옮기고 있습니다.

    블로그 이전 공지 : https://luftaquila.tistory.com/56

     

    이 글은

    https://luftaquila.io/blog/diy/arduino-hid-control-3/

     

    아두이노로 마우스 & 키보드 입력 제어 (3)

    조이스틱 제작

    luftaquila.io

     

    로 이동되었습니다!


    댓글을 쓰셔도 보기가 어려우니, 불편하시더라도 궁금한 점은 옮긴 새 블로그에 댓글로 달아 주시면 성심성의껏 답변해 드리겠습니다.

     

     

    원래 글을 보려면 더 보기를 누르세요

    더보기

    휴 드디어, 길고 험난하고 고달프고 배고프고 피곤하고 골때리는 여정 끝에 본 작업입니다.

     

    전 편 보기 : 아두이노로 마우스 / 키보드 입력 제어 (2) - HoodLoader2

     

    모든 편 링크 :

    아두이노로 마우스 / 키보드 입력 제어 (1) - HID 장치로 만들기

    아두이노로 마우스 / 키보드 입력 제어 (2) - HoodLoader2

    아두이노로 마우스 / 키보드 입력 제어 (3) - 조이스틱 제작

     

    --------------------------------------------------------------------------------------

     

    HoodLoader가 업로드된 보드는 누차 말씀드렸다시피 MCU가 두 개인 것처럼 동작합니다.

    아두이노와 컴퓨터를 USB로 연결하면 기본적으로 USB MCU인 16u2로 인식을 합니다.

    이걸 I/O MCU로 인식되도록 바꿔주려면,

    사진에 보이는 저 두 개의 핀을 빠르게 두 번 합선시켜 주시면 됩니다.

     

    일자드라이버나 점퍼선 MALE쪽으로 하셔도 되는데 불편하니 택트 스위치같은 거 하나 끼워서 하세요.

     

    보시다시피, 한 번만 합선시키면 16u2로 인식되고 두 번 빠르게 합선시키면 328, 즉 I/O MCU로 인식합니다.

    이렇게 MCU를 갈아타면서 두 MCU 모두 코딩을 하시면 됩니다.

     

     

    왼쪽이 스위치 한 번 눌러서 USB MCU로 인식시켰을 때, 오른쪽이 스위치 두 번 눌러서 I/O MCU로 인식됐을 때 보드 설정입니다.

    각각 코드 파일 업로드하실 때 이렇게 설정하고 올리셔야 합니다.

     

    -------------------------------------------------------------------------------------------

     

    그럼 우리는 다시 본래의 목적을 찾아서 HID 라이브러리를 설치해 보도록 하겠습니다.

    아두이노 IDE에 기본으로 깔려있는 HID 대신에 커스텀 HID 라이브러리를 사용할 건데요,

    기능도 더 다양하고 무엇보다도 개발자가 같은 사람이라 HoodLoader2가 올라간 보드들에서 사용할 수 있습니다.

     

    https://github.com/NicoHood/HID에서 다운로드 버튼 누르셔도 되고,

    https://github.com/NicoHood/HID/archive/master.zip 가 바로 다운로드되는 다이렉트 링크입니다.

     

    라이브러리 설치하시는 방법을 모르시면 아두이노 라이브러리 컴파일 에러 때려잡기 글을 참고해 주세요.

     

    이제 모든 준비가 끝났습니다! 아.. 진짜 여기까지 오느라 정말 힘들었어요...

    이제 본격적으로 조이스틱 제작을 시작해 보겠습니다. 근데 이것도 골때려요..

     

    광고를 보면서 머리를 식히고 시작해 보도록 하겠습니다! 와!! <퍽

    가난한 고딩 필자가 학교앞 문구점에서 100원짜리 불량식품이라도 사먹을 수 있게 광고 한 번만 클릭해 주세요오오오 (__)

     

     

     

     

    일단 아두이노 조이스틱 모듈로 아날로그 입력값을 받는 게 기본 동작이고, 따라서 I/O MCU 코드는 공통입니다.

    이 입력을 컴퓨터에 키보드 키 입력으로 줄 것인가, 마우스 조작으로 보낼 것인가에 따라 USB MCU에 업로드할 코드가 다릅니다.

     

     

    하나하나 상세하게 설명드리기는 힘들어 코드 옆에 주석처리를 해 놨으니, 추가적인 질문이 있으시면 언제든 댓글 달아 주세요.

     

    우선 조이스틱 모듈로부터 값을 읽어와 USB MCU에 전송하는, I/O MCU에 업로드할 코드입니다.

    이건 꼭 스위치 두 번 눌러서 I/O MCU로 인식시키고 업로드하세요.

    /* Joystick Control Program. for I/O MCU Coded By Luft Aquila, v1.1.0 at 2017.08.24 03:35 AM Contact : http://luftaquila.tistory.com / luftaquila@protonmail.ch */ const int xAxis = A0; // 조이스틱 모듈 X축 입력 핀 const int yAxis = A1; // 조이스틱 모듈 Y축 입력 핀 // 조이스틱 값 받아올 때 사용할 상수값들 int range = 12; // X축, Y축 동작 범위 int responseDelay = 5; // 응답 딜레이 5ms 지정 int threshold = range / 4; // 리셋 위치 지정 int center = range / 2; // 중심 위치 지정 void setup() { Serial.begin(115200); // 115200 보드 레이트로 시리얼 통신 개시. 아무래도 빠를수록 좋습니다. } void loop() { int xReading = readAxis(A0); // 조이스틱 모듈에서 X, Y축 아날로그 값 받아오기 int yReading = readAxis(A1); /* 이 부분이 핵심입니다. 시리얼 통신으로 USB MCU 쪽에 데이터를 보내는 송신 모듈입니다. X, Y축 데이터를 따로따로 보내면 어느 축 데이터인지 구분이 불가하므로 한 패킷에 넣어 보낼 겁니다. 받는 USB MCU쪽에서 이걸 다시 파싱해 컴퓨터에 입력하는 구조입니다. */ String SeparatorMiddle = ","; // X축과 Y축 데이터를 구분 분리해 줄 문자입니다. String SeparatorEND = "|"; // 한 패킷의 끝을 알리는 구분 문자입니다. 쉬프트 + \ 누르면 나오는 파이프라인입니다. String Data = xReading + SeparatorMiddle + yReading + SeparatorEND; /* 패킷 하나의 구성입니다. X축 값 + 축 분리 문자 + Y축 값 + 패킷 분리 문자 구조입니다. 예를 들어 X, Y축 값이 각각 3, 5이면 패킷은 3,5| 가 됩니다. 연속적으로 보내면 2,4|1,3|4,5|2,6| 이런 식으로 가죠. */ char Packet[20]; // 패킷을 담을 문자열 배열을 하나 선언해줍니다. Data.toCharArray(Packet, 20); // 패킷을 문자열 배열에 담아서 Serial.write(Packet); // 시리얼 모니터로 쏴줍니다. delay(responseDelay); // 너무 데이터를 많이 보내면 받는 USB MCU 쪽에서 값이 꼬입니다. 딜레이 살짝~ } // 조이스틱 모듈에서 아날로그 값을 받아오는 함수입니다. int readAxis(int thisAxis) { int reading = analogRead(thisAxis); reading = map(reading, 0, 1023, 0, range); int distance = reading - center; if (abs(distance) < threshold) { distance = 0; } return distance; }

    이제 USB MCU에 업로드할, 조이스틱-키보드 제어 코드입니다.

     

    /* Keyboard Joystick Control Program. for USB MCU Coded By Luft Aquila, v1.1.0 at 2017.08.24 03:35 AM Contact : http://luftaquila.tistory.com / luftaquila@protonmail.ch */ #include <keyboard.h> #include <hid.h> void setup() { Serial1.begin(115200); // I/O MCU와 연결된 하드웨어 시리얼입니다. 이걸로 값을 받아옵니다. Keyboard.begin(); // 키보드 제어 개시 } int xValue; int yValue; void loop() { String receive = Serial1.readStringUntil('|'); //한 패킷의 끝을 나타내는 | 문자가 나올 때까지만 값을 받아옵니다. int sub = receive.indexOf(','); // X, Y축 데이터를 분리하는 , 문자의 위치를 저장합니다. yValue = receive.substring(sub+1).toInt(); // , 다음 문자부터 끝까지 값을 정수형으로 변환해 Y축 값으로 저장합니다. receive.remove(sub); // ,부터 문자열 끝까지 값을 지웁니다. xValue = receive.toInt(); // 남은 값을 X축 값으로 저장합니다. // 아래는 입력받은 값을 바탕으로 키보드를 조작하는 코드입니다. if(xValue > 0) Keyboard.write(KEY_RIGHT_ARROW); if(xValue < 0) Keyboard.write(KEY_LEFT_ARROW); if(yValue < 0) Keyboard.write(KEY_UP_ARROW); if(yValue > 0) Keyboard.write(KEY_DOWN_ARROW); }

    이 상하좌우 키 말고 다른 키 입력을 원하시면 아래 링크에서 원하는 키 코드 값을 찾으시면 됩니다.

    https://www.arduino.cc/en/Reference/KeyboardModifiers

     

     

    다음으로 조이스틱 - 마우스 제어 버전 코드입니다. 마찬가지로 USB MCU에 업로드하세요.

     

     

    /* Mouse Joystick Control Program. for USB MCU Coded By Luft Aquila, v1.0.5 at 2017.08.23 01:44 PM Contact : http://luftaquila.tistory.com / luftaquila@protonmail.ch */ #include <Mouse.h> #include <HID.h> void setup() { Serial1.begin(115200); Mouse.begin(); // 마우스 입력 제어 개시 } int xValue; int yValue; void loop() { String receive = Serial1.readStringUntil('|'); int sub = receive.indexOf(','); yValue = receive.substring(sub+1).toInt(); receive.remove(sub); xValue = receive.toInt(); /* 여기까진 키보드 쪽 코드와 완전 똑같습니다. 그냥 입력값을 마우스 조작 코드로만 주면 돼요. 아래 한 줄이면 끝입니다. */ Mouse.move(xValue, yValue, 0); }

    마우스로 입력하실 때 조이스틱 핀이 제대로 연결이 안 되어 있으면 마우스가 혼자 제멋대로 움직입니다.

    미리 대비책같은 걸 세워두세요. 물론 뭐 이렇게 제가 말씀드리는 것보다 한 번 직접 겪어 보시는게 확실할 겁니다. 후후후

    한 번 제멋대로 움직이기 시작하면 골때리거든요.

     

    그렇게 해서, 모든 작업이 끝났습니다! 이제 조이스틱 움직이면 아마 키보드나 마우스가 그대로 따라 움직이실 겁니다.

    아.. 정말 힘들었습니다. 조이스틱으로 게임해봐야지! 했을 땐 이렇게 삽질하게 될 줄 몰랐어요.......

     

     

    완성된 조이스틱입니다. 조이스틱이 너무 짧아서 조종하는 맛이 안 나길래 고장난 분광기 경통을 위에다 끼웠어요.

    어쩜 사이즈가 완전 더도말고 덜도말고 딱맞게 끼더라고요. 크-

    밑 받침대는 분해해놨던 어항에 뽀글뽀글 기포 만드는 기포발생기입니다. 다 이렇게 짜집기해서 만드는거죠 뭐

     

     

    아무튼 이렇게 쌩노가다 해서 만들어낸 조이스틱으로 그렇게 바라던 게임하는 영상입니다.

    뭐, 잘 동작하면 된거죠. 하하하

     

     

     

    ------------------------------------------------------------------------------------------------

     

    + 키보드와 마우스 제어 모드를 왔다갔다 할 때마다 코드를 새로 업로드하는게 귀찮아서 둘을 통합시키고,

    조이스틱에 있는 버튼 입력으로 마우스 - 키보드 모드 전환을 하는 통합 코드를 작성했습니다.

    근데 글쎄 전환이 잘 되는 것 같기도 하고 안 되는 것 같기도 하고....

    아무튼 올려두었으니 수정하셔서 사용하실 능력자분들은 이거 쓰세요.

     

    근데 사실 수정 안 해도 어찌어찌 동작은 합니다. 먼저 I/O MCU용 코드입니다.

    주석 없으니 혹시 이해가 안 되거나 궁금한 부분 있으시면 댓글 주세요!

    /* Mouse - Keyboard Integrated Joystick Control Program. for I/O MCU Coded By Luft Aquila, v1.0.0 at 2017.08.24 05:58 PM Contact : http://luftaquila.tistory.com / luftaquila@protonmail.ch */ const int xAxis = A0; const int yAxis = A1 boolean Mode = true; // parameters for reading the joystick: int range = 12; int threshold = range / 4; int center = range / 2; void setup() { Serial.begin(115200); pinMode(2, INPUT_PULLUP); pinMode(13, OUTPUT); } void loop() { int xReading = readAxis(A0); int yReading = readAxis(A1); String SeparatorMiddle = ","; String SeparatorMiddle2 = "@"; String SeparatorEnd = "|" String SendData = xReading + SeparatorMiddle + yReading + SeparatorMiddle2 + digitalRead(2) + SeparatorEnd; char transmitter[20]; SendData.toCharArray(transmitter, 20); Serial.write(transmitter); if(Mode == true) digitalWrite(13, HIGH); else if(Mode == false) digitalWrite(13, LOW); delay(5); } int readAxis(int thisAxis) { // read the analog input: int reading = analogRead(thisAxis); // map the reading from the analog input range to the output range: reading = map(reading, 0, 1023, 0, range); // if the output reading is outside from the // rest position threshold, use it: int distance = reading - center; if (abs(distance) < threshold) { distance = 0; } // return the distance for this axis: return distance; }

    그리고 USB MCU에 업로드할 코드입니다.

    /* Mouse - Keyboard Integrated Joystick Control Program. for I/O MCU Coded By Luft Aquila, v1.0.0 at 2017.08.24 05:58 PM Contact : http://luftaquila.tistory.com / luftaquila@protonmail.ch */ #include <Keyboard.h> #include <Mouse.h> #include <HID.h> boolean Mode = true; int xValue; int yValue; int LastButtonReceive = 1; unsigned long int time1 = 0; void setup() { Serial1.begin(115200); Keyboard.begin(); Mouse.begin(); } void loop() { String receive = Serial1.readStringUntil('|'); Serial.println(receive); int SeparatorMiddle = receive.indexOf(','); int SeparatorMiddle2 = receive.indexOf('@'); int ButtonReceive = receive.substring(SeparatorMiddle2 + 1).toInt(); receive.remove(SeparatorMiddle2); yValue = receive.substring(SeparatorMiddle + 1).toInt() receive.remove(SeparatorMiddle); xValue = receive.toInt(); if(LastButtonReceive == 0 && LastButtonReceive != ButtonReceive) // Push time1 = millis(); if(LastButtonReceive == 1 && LastButtonReceive != ButtonReceive) // Release { if(millis() - time1 > 1000) Mode = !Mode; else Keyboard.write(KEY_RETURN); } LastButtonReceive = ButtonReceive; if(Mode == true) Mouse.move(xValue, yValue, 0); else if(Mode == false) { if(xValue > 0) Keyboard.write(KEY_RIGHT_ARROW); if(xValue < 0) Keyboard.write(KEY_LEFT_ARROW); if(yValue < 0) Keyboard.write(KEY_UP_ARROW); if(yValue > 0) Keyboard.write(KEY_DOWN_ARROW); } }

    아무튼, 삽질로만 따지면 역대 프로젝트 했던 것들 중 단연 최고였던 조이스틱 프로젝트가 끝났습니다.

    휴... 힘드네요.. 가난한 고딩 필자가 학교앞 문구점에서 100원짜리 불량식품이라도 사먹을 수 있게 광고 한 번만 클릭해 주세요... ㅠㅠ

     

    Posted by LUFT - AQUILA
    • 이전 댓글 더보기
    • 아두이노 초보
      2017.12.27 11:31

      감사합니다 ^^

    • ㅇㅇ
      2017.12.27 16:09

      고등학생이신것 같은데 열정이 대단하시네요! 많이 배우고 갑니다.
      물론 광고도 열심히 눌렀습니다. :)

      • BlogIcon LUFT - AQUILA
        2017.12.27 20:32 신고

        와 이런 말씀 해주실 때마다 정말 큰 힘이 됩니다! 감사합니다!!!

    • 아두이노 초보
      2017.12.28 01:57

      죄송한데 혹시 위에 두 개의 핀을 빠르게 두 번 합선시키라고 하셨는데 어떻게 하는지 자세히 알려주실 수 있으신가요..? 합선 자체를 어떻게 하는지 잘 몰라서 ㅠㅠ 부탁드립니당...

      • BlogIcon LUFT - AQUILA
        2017.12.28 02:14 신고

        그냥 두 핀 사이에 전기가 통하게 만드시면 됩니다. 점퍼선으로 연결하는 것처럼요

    • 아두이노 초보
      2017.12.28 02:19

      점퍼선으로 두 핀을 연결하면 되는건가요!? 그런데 자꾸 보드랑 포트 정보가 원래 상태랑 똑같이 나옵니다 ㅠㅠ 점퍼선 연결하면 심지어 포트 정보까지 안 뜹니다..

      • BlogIcon LUFT - AQUILA
        2017.12.28 02:20 신고

        연결해 두는게 아니라 택트스위치 같은 걸로 빠르게 두 번 연결시켰다 뗐다 해야 해요. 보드 정보는 직접 맞춰 주셔야 합니다.

    • 아두이노 초보
      2017.12.28 02:42

      아 그렇군요 택트스위치가 없어 핀셋으로도 해보고 점퍼선으로도 해봤는데 자꾸 안 되네요...

    • 아두이노 초보
      2018.01.03 01:20

      아~ 소리 켜서 들어보니 소리가 나네요! 감사합니다 ^^

    • BlogIcon whale
      2018.01.14 17:51

      잘 읽었습니다.
      아두이노 우노를 이용하여 레오나르도 처럼 쓸수도 있었군요.
      그럼 나노를 이용하여 더욱 작게 제작하는 것도 가능해 보입니다.
      정말 좋은글 잘 읽었습니다.
      위 글과는 관계는 없지만 하나 여쭤봅니다.
      그 노트북으로 하시던 게임 이름이 뭔가요?
      몹시 재밌어 보이는 군요 ㅎㅎ

    • 아두이노 초보
      2018.01.21 23:37

      마우스 클릭은 불가능한가요?

      • BlogIcon LUFT - AQUILA
        2018.01.21 23:42 신고

        Mouse.click()으로 가능합니다. 자세한 건 아두이노 Mouse.h 라이브러리 찾아보시면 나올 거에요

    • 아두이노 초보
      2018.01.22 00:47

      감사합니다. 혹시 마지막 통합 코드에서 키보드 이동 속도가 너무 빨라서 그런데 속도 줄이는 법은 없나요?

      • BlogIcon LUFT - AQUILA
        2018.01.24 14:53 신고

        딜레이 몇~몇십ms 정도만 넣어주시면 될 것 같습니다.

    • rail1217
      2018.02.06 18:07

      속성에 들어가서 잘 되었는지도 확인했고
      teency driver랑 보드 hoodloader2까지 설치했습니다.
      그런데 합선 1번이든 2번이든 arduino uno가 변화가 없네요 ㅠㅠ
      계속 변화없이 arduino uno 포트5 입니다.
      teency driver를 설치할 때 "이 프로그램이 제대로 설치되지 않았을 수 있습니다"가 뜨긴 했는데
      이때문일까요? ㅠㅠ

    • ohrum
      2018.04.16 17:09

      레오나르도보드라면 코드를 어떻게 업로드 해야할까요?

    • waitle
      2018.05.06 12:38

      스틱에서 값을 받아올때 아날로그로 받아왔다면 컴퓨터로 값을 던질때 기존에 있는 키가 아닌 조이스틱으로써 아날로그값을 던져줄순 없는건가요?
      그러려면 위의 키보드,마우스 헤더가 아닌 다른 전달방식이 있어야 될텐데 조금더 세밀한 컨트롤을 해줄 수 있게 만들어 줄 방법이 궁금합니다

    • waitle
      2018.05.06 12:41

      아! 그리고 위 댓글달면서 생각난건데요 소스의 로직을 약간만 바꾸고 제가 위에서 질문한 방식이 가능하다면 가변저항으로 스티어링휠을 구현하는것도 가능하지 않을까요? 회전각은 1바퀴 채 안되겠지만요

      • BlogIcon LUFT - AQUILA
        2018.05.06 14:20 신고

        키보드를 제어할 땐 그냥 방향에 따라서 해당 방향키를 눌러주는 방식밖에 안 되지만 마우스 커서를 제어할 때는 좀 더 세밀한 컨트롤도 가능해요. 아래가 그 코드입니다.

        int set_X = (analogRead(JOY_X) - 510) / scale;
        int set_Y = ((analogRead(JOY_Y) - 510) / scale) * -1;
        Mouse.move(set_X, set_Y, 0);
        delay(5);
        여기서 scale 변수값을 조정하시면 마우스 감도도 조정하실 수 있어요. 제 경험상 100~200 사이 값으로 해주면 적당하더라고요.

        무한회전 가능한 스티어링 휠도 구현할 수 있습니다! 가변저항 대신 무한회전 로터리 엔코더 사시면 회전각 360도짜리도 만드실 수 있어요. 대신 진짜 핸들처럼 두바퀴정도 돌아가고 걸리게는 힘들겠지만... 코드는 시중에서 파는 시뮬레이터 스티어링 휠이 조작 입력을 어떤 식으로 하는지 몰라서 잘 모르겠네요. 아마 찾아보면 나오지 않을까 합니다.

    • 작성글
      2018.06.18 08:47

      고생많으셨어요 소중하게 작성하신 글 잘 봤습니다. 다만 보고 따라하는것보다는 알리익스프레스에서 이러한 기능이 되는 호환보드를 사는게 편리할 듯 합니다. 저두 레오나르도 보드를 구해서 이것 저것 하고 있는데요. 이 부분에 대해서 어떻게 할 수 있나 생각해봤지만 쉽지 않은 여정인듯 합니다. ^^

      • BlogIcon LUFT - AQUILA
        2018.06.19 13:22 신고

        그렇죠 일단 레오나르도가 있으면 레오나르도 가지고 하는게 무조건 이득입니다. ㅋㅋㅋ 다만 저처럼 여건이 안 될 때 이런 방법도 있더라 하는 거죠.

    • 방문자
      2018.08.25 17:42

      글 잘보고 따라하고있습니다.~ 혹시 keyboard.h 헤더파일이 업로드할때 인식이 안되는데 이부분을 어떻게 해줘야 인식이되는건가요?

    • BlogIcon 냥캣
      2018.11.26 21:27

      혹시 레오나르도에 맞춰서 나온 코드를 여따가 올릴순 없는건가요? 안될 것 같기는 합니다만...

    • BlogIcon 공돌이pooh
      2020.02.26 20:38 신고

      좋은 글 재밌게 잘 보고 갑니다.

    • ㅇㅇ
      2020.05.08 08:24

      hoodLoader 삭제방법은 없나요? 아무리 해도 장치관리자에 Hid인식이 안되네요
      hoodloader 날리고 싶은데 어떻게 해야되나요 ㅠㅠ

    • 박주
      2020.08.01 22:56

      능력자시네요

    • 초짜
      2020.09.08 11:19

      스케치 업로드는 일반 hid스케치는 어디에 업로드하나요? 왜 두 곳에 스케치 입력하는가 모르겠네요 일반 hid스케치는 한곳에만 넣어도 되나요?