hlemont
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    1
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: G-건(총 모양 마우스) 만들기 프로젝트 description: 센서 및 버튼 입력을 이용해 마우스의 기능을 하는 총 모양 장치를 개발하는 프로젝트 image: https://i.imgur.com/uIefbOj.png --- &nbsp; # G-건(총 모양 마우스) 만들기 프로젝트 ![title](https://i.imgur.com/uIefbOj.png "베인인이(출처: 똘게더)") 센서 및 버튼 입력을 이용해 마우스의 기능을 하는 총 모양 장치를 개발하는 프로젝트. **현재 이 프로젝트의 내용은 최신화가 되어 있지 않습니다. 따라서 프로젝트 내용 그대로 만들시 작동이 보장되지 않습니다. 기본적인 설계의 흐름만 참조하는 용도로 사용하시기 바랍니다.** [Github](https://github.com/Taejunejung/Ggun) &nbsp; ### 타임라인 > [color=#467faa] 1. 똘삼이 본격적으로 프로젝트를 시작함. [time=2021, July 3rd, Saturday] > 2. 똘삼이 프로젝트명으로 G-건(GTA Gun)을 제안함. [time=2021, July 4th, Sunday 11:32 PM] > 3. 처음으로 버튼 입력 기능이 동작하는 수신기 프로토타입을 제작함. [time=2021, July 5th, Monday 1:39 AM] > 4. 수신기 프로토타입이 아날로그 입력 기능이 동작하도록 개선함. [time=2021, July 5th, Monday 10:54 PM] > 5. Orientation 센서에서 지역 가속도를 전역 가속도로 변환하는 방법을 찾는 과정에서 난관에 봉착함. [time=2021, July 6th, Tuesday 12:28 AM] > 6. 방향을 전환해, 리얼 임팩트 건과 같이 지역 가속도만을 이용해 마우스를 움직이는 방법을 먼저 시도해 보기로 함. [time=2021, July 6th, Tuesday 6:40 PM] > 7. 가속도 방식을 철저히 포기하고, 각속도를 이용하기로 함. [time=2021, July 6th, Tuesday 10:37 PM] > 8. 기적적으로, 시청자가 가져온 코드가 자이로를 이용하여 기울어진 상태에서도 상당히 잘 작동하는 것을 확인함. [time=2021, July 6th, Tuesday 11:59 PM] > 9. 이동 평균 필터를 적용하는 것을 마지막으로, 마우스 커서 변위값을 도출해내는 방식을 확정함. [time=2021, July 7th, Thursday] > 10. 블루투스 통신을 통해 센서 및 장치 등의 값을 전송하여 컴퓨터를 조작하는 시도를 시작함. [time=2021, July 11th, Sunday] > 11. 두 장치를 직접 연결하여 조작하는 데에는 성공하였으나, 블루투스 모듈을 이용하였을때에는 레이턴시가 크게 나타남. [time=2021, July 11th, Sunday] > 12. 블루투스 버전 4.0을 지원하는 다른 모듈을 이용해서 낮은 레이턴시로 작동시키는 데에 성공함. [time=2021, July 12th, Monday 2:02 AM] > 13. 프로토타입을 장난감 총 내부에 집어넣기 위한 작업에 돌입함. [time=2021, July 12th, Monday] > 14. 프로토타입을 총 내부에 집어넣은 제품을 완성하였으나, 기울기를 반영한 동작이 제대로 동작하지 않음. 이 부분을 보완하려면 돌아가야 할 것들이 많아, 프로젝트를 여기서 마무리지으려고 함. [time=2021, July 18th, Sunday] > 15. 인줄 알았으나, 센서 모듈을 짭 모듈로 변경하여 생긴 차이에 의한 오류였던 것으로 판명됨. 즉, 기울기를 정상적으로 반영하는 장난감 총을 완성하는 데에 성공한 것이었고, 프로젝트는 성공적이었음. [time=2021, July 18th, Sunday 1:49 AM] > *프로젝트 완결* (휴식 기간 포함 약 15일 소요됨) &nbsp; &nbsp; ## 요구 사항 - [x] 기울기 반영 > 아두이노를 사용한 기존의 에어마우스형 컨트롤러는 기울인 뒤 이동시 움직임이 부정확해지는 부분이 있음. 이를 LG의 매직 리모컨과 유사한 형태로 어떤 그립상태에서도 사용자가 원하는 움직임이 가능하도록 제작한다. - [x] 저지연 무선 > 블루투스 or wifi등을 이용하여 최소의 레이턴시로 움직임을 반영하도록 한다. - [x] 플러그 앤 플레이 > 별도 프로그램 및 드라이버 설치 없이 연결하는 순간 바로 작동하도록 - [x] 총 상호작용 구현 > 진동 모터를 이용한 격발 체감 효과, 일반 모터 혹은 리니어 모터를 이용한 상부 블로우백 효과 등 가능한 한 실총과 유사한 경험을 제공하도록 &nbsp; &nbsp; # 구상 및 자료 조사 &nbsp; ## 구상도 ### 초기 ```mermaid graph TD subgraph "장치 내부" %% nodes ARD[(아두이노 나노)] BTN1[[버튼 1: 좌클릭]] BTN2[[버튼 2: 우클릭]] BTN3[[버튼 3: 재장전]] BTN4[[버튼 4: Reset]] ANALOG((아날로그 스틱)) BNO[다중 기능 센서] BLT[블루투스 모듈] %% BTN1 --- ARD BTN2 --- ARD BTN3 --- ARD BTN4 --- ARD ARD --- ANALOG ARD --- BNO ARD --- BLT end subgraph PC[컴퓨터] BLT -.-|블루투스 시리얼 통신| PROGRAM[대충 드라이버 프로그램] end %% classDef global font-size:15px %% class ARD,BTN1,BTN2,BTN3,BTN4,BNO,BLT, global ``` &nbsp; ### 개선안 1. 수신기 추가(채택) ```mermaid graph TD subgraph gun[장치] %% nodes ARD1[(아두이노 나노)] BTN1[[버튼 1: 좌클릭]] BTN2[[버튼 2: 우클릭]] BTN3[[버튼 3: 재장전]] BTN4[[버튼 4: Reset]] ANALOG((아날로그 스틱)) BNO[다중 기능 센서] BLT1[블루투스 모듈] %% links BTN1 --- ARD1 BTN2 --- ARD1 BTN3 --- ARD1 BTN4 --- ARD1 ARD1 --- ANALOG ARD1 --- BNO ARD1 --- BLT1 end subgraph receiver[수신기] %% nodes BLT2[블루투스 모듈] ARD2[(아두이노 레오나르도)] %% links BLT1 -.-|블루투스 마스터/슬레이브 통신| BLT2 BLT2 --- ARD2 end PC[컴퓨터] ----|USB| ARD2 %% classDef global fill:#fffffff0,stroke:#a0a0a0,stroke-width:1.5px %% class ARD,BTN1,BTN2,BTN3,BTN4,BNO,BLT, global ``` > 블루투스로 PC와 시리얼 통신해 PC에서 파이썬 등으로 컨트롤할 수 있으나 게임에 따라 사용이 불가능할 수 있어(핵으로 인식) 키보드 마우스로 작동하는 아두이노를 추가하는 방식으로 구상도를 그려봤습니다. > [name=sko153] > [출처](https://tgd.kr/s/jungtaejune/55583245#comment-55584694) &nbsp; > 경험상 블루투스 모듈 연결이 문제를 일으키는 경우가 많으니 좀더 간단하고 싼? RF 모듈을 쓰는 것도 좋을 것 같습니다. [name=Gang V] > [구매처](https://www.aliexpress.com/item/4000018571977.html) &nbsp; ### 개선안 2. ESP32 보드 사용 ```mermaid graph TD subgraph "장치 내부" %% nodes ARD[(ESP32)] BTN1[[버튼 1: 좌클릭]] BTN2[[버튼 2: 우클릭]] BTN3[[버튼 3: 재장전]] BTN4[[버튼 4: Reset]] ANALOG((아날로그 스틱)) BNO[다중 기능 센서] %% BTN1 --- ARD BTN2 --- ARD BTN3 --- ARD BTN4 --- ARD ARD --- ANALOG ARD --- BNO end PC[컴퓨터] ARD -..-|"블루투스(입력 장치로 인식)"| PC ``` > ESP32(ESP32 D1)라는 보드를 사용하면 내장 블루투스를 사용해 블루투스 키보드, 마우스 장치로 인식이 가능합니다. (일반적인 아두이노 보드가 아니어서 어려울 수 있습니다.) > [name=sko153] > [의견 출처](https://tgd.kr/s/jungtaejune/55583245#comment-55584694), [관련 자료](https://cdn-learn.adafruit.com/assets/assets/000/092/545/medium800/sensors_BNO055_STEMMA_fab_print.png?1593120289) &nbsp; ## 자료 <details><summary>Arduino Nano</summary> <blockquote> <p><img src="https://www.etechnophiles.com/wp-content/uploads/2021/05/Arduino-Every-Pinout-in-detail.png" alt="" title="Arduino Nano pinout"></p> <ul> <li>Arduino Nano pinout</li> </ul> </blockquote> </details> <details><summary>Arduino Micro</summary> <blockquote> <p><img src="https://i.imgur.com/gHb5fHS.png" alt="" title="Arduino Micro pinout"></p> <ul> <li>Arduino Micro pinout</li> </ul> </blockquote> </details> <details><summary>Arduino Pro Micro</summary> <blockquote> <p><img src="https://cdn.sparkfun.com/assets/9/c/3/c/4/523a1765757b7f5c6e8b4567.png" alt="" title="Arduino Pro Micro"></p> <ul> <li>Arduino Pro Micro pinout</li> </ul> </blockquote> </details> <details><summary>BNO055</summary> <blockquote> <p><img src="https://cdn-learn.adafruit.com/assets/assets/000/092/545/medium800/sensors_BNO055_STEMMA_fab_print.png?1593120289" alt="bno055_pinout"></p> <ul> <li><p><a href="https://learn.adafruit.com/adafruit-bno055-absolute-orientation-sensor/pinouts">BNO055 Pinout</a> </p> </li> <li><p><a href="https://cdn-shop.adafruit.com/datasheets/BST_BNO055_DS000_12.pdf">BNO055 9DOF 센서 데이터시트</a></p> </li> <li><a href="https://learn.adafruit.com/adafruit-bno055-absolute-orientation-sensor/device-calibration">캘리브레이션</a> <a href="https://www.mathworks.com/help/supportpkg/arduinoio/ug/calibrate-sensors.html">추가</a><ul> <li><a href="https://learn.adafruit.com/adafruit-bno055-absolute-orientation-sensor/arduino-code">예제</a>, <a href="https://github.com/adafruit/Adafruit_BNO055/blob/master/examples/rawdata/rawdata.ino">예시 코드 1</a></li> </ul> </li> </ul> </blockquote> </details> <details><summary>HC-05 블루투스 모듈</summary> <blockquote> <ul> <li><a href="https://components101.com/wireless/hc-05-bluetooth-module">정보</a></li> </ul> </blockquote> </details>1593120289 &nbsp; ### 코드공유소 *성황리에 폐쇄* &nbsp; &nbsp; # 회로 설계 &nbsp; ## 부분 동작 시연용 회로 ![circuit_prototype_1](https://i.imgur.com/gnfQoyh.png) 아두이노 프로 마이크로에 버튼, BNO055, 아날로그 스틱을 연결한 모습. > [회로 참고용 영상](https://clips.twitch.tv/TangibleJoyousMilkBleedPurple-KDsa6fwgXdHk1IJM) &nbsp; ## 최종 회로 ![circuit_fritzing](https://i.imgur.com/gONjCeQ.jpeg) &nbsp; &nbsp; # 프로그램 설계 및 개발 &nbsp; ## 수신기 부분 동작 시연 코드 ```c= #include <Keyboard.h> #include <Mouse.h> const byte Pin_Mouse_Left = 7; const byte Pin_Mouse_Right = 8; const byte Pin_Reload = 9; const byte Pin_Recenter = 10; const byte Pin_Joybutton = 4; boolean state = true; void setup() { Serial.begin(9600); Keyboard.begin(); Mouse.begin(); pinMode(Pin_Mouse_Left, INPUT_PULLUP); pinMode(Pin_Mouse_Right, INPUT_PULLUP); pinMode(Pin_Reload, INPUT_PULLUP); pinMode(Pin_Recenter, INPUT_PULLUP); pinMode(Pin_Joybutton, INPUT_PULLUP); } void loop() { int sensorValueY = analogRead(A1); int sensorValueX = analogRead(A0); if (digitalRead(Pin_Mouse_Left) == LOW){ Mouse.press(MOUSE_LEFT); Serial.println("press mouse left"); } else { Mouse.release(MOUSE_LEFT); } if (digitalRead(Pin_Mouse_Right) == LOW){ Mouse.press(MOUSE_RIGHT); Serial.println("press mouse right"); } else { Mouse.release(MOUSE_RIGHT); } if (digitalRead(Pin_Reload) == LOW){ Keyboard.press('r'); Serial.println("press Reload"); } else { Keyboard.release('r'); } if (digitalRead(Pin_Recenter) == LOW){ Keyboard.press(KEY_BACKSPACE); Serial.println("press Recenter"); } else { Keyboard.release(KEY_BACKSPACE); } if (digitalRead(Pin_Joybutton) == LOW){ Keyboard.press('p'); Serial.println("press Joybutton"); } else { Keyboard.release('p'); } // Y+ if (sensorValueY < 300) { Keyboard.press('w'); } else { Keyboard.release('w'); } //Y- if (sensorValueY > 700) { Keyboard.press('s'); } else { Keyboard.release('s'); } //X if (sensorValueX < 300) { Keyboard.press('a'); } else { Keyboard.release('a'); } //X- if (sensorValueX > 700) { Keyboard.press('d'); } else { Keyboard.release('d'); } delay (20); } ``` 수신기가 키보드 및 마우스로서 동작하는 모습을 체크하고 디버깅 하기 위한 코드. 버튼 및 아날로그 스틱을 수신기에 연결하여 독립적으로 테스트함. &nbsp; ## 센서 동작 시연 코드 ```c= #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BNO055.h> #include <utility/imumaths.h> // 마우스 제어용 헤더 #include <Mouse.h> #include <Keyboard.h> /* This driver reads raw data from the BNO055 Connections =========== Connect SCL to analog 5 Connect SDA to analog 4 Connect VDD to 3.3V DC Connect GROUND to common ground History ======= 2015/MAR/03 - First release (KTOWN) */ /* Set the delay between fresh samples */ #define BNO055_SAMPLERATE_DELAY_MS (10) // ===================== 코드에서 사용할 전역 변수 값들 ===== // 가속도의 무시를 위한 데드존 const float DEADZONE = 0.01f; // ======================================================== // Check I2C device address and correct line below (by default address is 0x29 or 0x28) // id, address Adafruit_BNO055 bno = Adafruit_BNO055(-1, 0x29); void setup(void) { Serial.begin(115200); Serial.println("Orientation Sensor Raw Data Test"); Serial.println(""); /* Initialise the sensor */ if(!bno.begin()) { /* There was a problem detecting the BNO055 ... check your connections */ Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!"); while(1); } delay(1000); /* Display the current temperature */ int8_t temp = bno.getTemp(); Serial.print("Current Temperature: "); Serial.print(temp); Serial.println(" C"); Serial.println(""); bno.setExtCrystalUse(true); Serial.println("Calibration status values: 0=uncalibrated, 3=fully calibrated"); // 마우스 초기화 Mouse.begin(); } void loop(void) { // Possible vector values can be: // - VECTOR_ACCELEROMETER - m/s^2 // - VECTOR_MAGNETOMETER - uT // - VECTOR_GYROSCOPE - rad/s // - VECTOR_EULER - degrees // - VECTOR_LINEARACCEL - m/s^2 // - VECTOR_GRAVITY - m/s^2 imu::Vector<3> euler = bno.getVector(Adafruit_BNO055::VECTOR_EULER); /* Display calibration status for each sensor. */ uint8_t system, gyro, accel, mag = 0; bno.getCalibration(&system, &gyro, &accel, &mag); // 각속도 얻어오는 부분 imu::Vector<3> Gyro = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE); float gyroX = -Gyro.x(); float gyroY = Gyro.y(); float c = cosf(euler.z() / 180.0f * 3.141592f); float s = sinf(euler.z() / 180.0f * 3.141592f); float newGyroX = gyroX * c + gyroY * s; float newGyroY = gyroX * s * -1 + gyroY * c; // 각속도 테스트용 출력 부분 Serial.print("newGyroX:"); Serial.print(newGyroX); Serial.print(" newGyroY:"); Serial.print(newGyroY); Mouse.move(newGyroX, newGyroY, 0); delay(BNO055_SAMPLERATE_DELAY_MS); } ``` 자이로 측정값과 오일러 각도를 이용하여, 장치의 기울기에 대응하여 마우스 커서가 적절한 방향으로 이동하게 만듦. 완벽하지는 않지만 상당히 높은 수준의 정확도를 보임. > [name=햙쨕] [두 시연 코드를 합쳐놓은 코드](https://tgd.kr/s/jungtaejune/55655687) &nbsp; ## 최종 코드 수신기와 송신기 기능을 각각 구현한 최종 코드다. 송신기에는 센서와 각종 입력 핀들이 구성되어 있고, 수신기에는 간단하게 블루투스 모듈만 구성되어 있다. &nbsp; ### transmitter.ino ```c= //************************************************************************************* // G-gun 송신기용 코드 (총) //************************************************************************************* #include <Wire.h> //IMU 센서 제어용 헤더 #include <Adafruit_Sensor.h> #include <Adafruit_BNO055.h> #include <utility/imumaths.h> #include <math.h> // boolean 타입 사용을 위한 헤더 #include <stdbool.h> //폴링 딜레이 설정. #define BNO055_SAMPLERATE_DELAY_MS (1) #define STARTUP_DELAY_MS (1000) #define RAD_TO_DEG 57.295779513082320876798154814105 #define DEG_TO_RAD 0.01745329251994329576923690768489 //필요 #define FRAME_START 0xAA #define FRAME_END 0xDD #define FRAME_MAX_SIZE 13 //필요 typedef struct _data { byte pin; uint8_t joystick_x; uint8_t joystick_y; float mouse_x; float mouse_y; } DATA; // ===================== 코드에서 사용할 전역 변수 값들 ===== // 가속도의 무시를 위한 데드존 const float DEADZONE = 0.01f; bool bHold = false; // recenter 용도 const byte Pin_Joybutton = 4; // 전송될 핀들 const byte Pin_Mouse_Left = 7; const byte Pin_Mouse_Right = 8; const byte Pin_Reload = 9; const byte Pin_Jump = 10; const byte Pin_Use = A3; const byte Pin_Skill = A2; const byte Pin_Weapon = 5; const byte Pin_JoyUp = A0; const byte Pin_JoyDown = A1; const byte Pin_JoyLeft = 15; const byte Pin_JoyRight = 14; double filteredX, filteredY; // set alpha 0.0f to turn off ema filter const float alpha = 0.8f; // 인코딩 및 전송 관련 DATA data; byte buffer[FRAME_MAX_SIZE]; void write_bytes(byte *buffer, DATA *data) { buffer[0] = FRAME_START; buffer[1] = (byte)(data->pin); buffer[2] = (byte)(data->joystick_x); buffer[3] = (byte)(data->joystick_y); // mouse_x memcpy(buffer + 4, &data->mouse_x, 4); // *((float *)(buffer + 4)) = data->mouse_x; // mouse_y memcpy(buffer + 8, &data->mouse_y, 4); // *((float *)(buffer + 8)) = data->mouse_y; buffer[12] = FRAME_END; } // 핀 데이터를 읽어서 각 bit에 저장함 byte get_pin_data() { byte pin_data = 0; pin_data |= (byte)(digitalRead(Pin_Mouse_Left) == LOW) << 0; pin_data |= (byte)(digitalRead(Pin_Mouse_Right) == LOW) << 1; pin_data |= (byte)(digitalRead(Pin_Reload) == LOW) << 2; pin_data |= (byte)(digitalRead(Pin_Jump) == LOW) << 3; pin_data |= (byte)(digitalRead(Pin_Use) == LOW) << 4; pin_data |= (byte)(digitalRead(Pin_Skill) == LOW) << 5; pin_data |= (byte)(digitalRead(Pin_Weapon) == LOW) << 6; return pin_data; } // 조이스틱 핀 데이터를 읽어서 상태를 -1, 0, 1로 저장함. uint8_t get_joystick_data_x() { bool xPlus = digitalRead(Pin_JoyRight) == LOW; bool xMinus = digitalRead(Pin_JoyLeft) == LOW; uint8_t status; // will be translated to -1, 1, 0 if (xMinus) { status = 0; } else if (xPlus) { status = 2; } else { status = 1; } return status; } // 조이스틱 핀 데이터를 읽어서 상태를 -1, 0, 1로 저장함. uint8_t get_joystick_data_y() { bool yPlus = digitalRead(Pin_JoyDown) == LOW; bool yMinus = digitalRead(Pin_JoyUp) == LOW; uint8_t status; // will be translated to -1, 1, 0 if (yMinus) { status = 0; } else if (yPlus) { status = 2; } else { status = 1; } return status; } // ======================================================== // IMU 센서 I2C 주소 설정 (-1, 0x29) Adafruit_BNO055 bno = Adafruit_BNO055(-1, 0x29); /**************************************************************************/ /* Arduino setup function (automatically called at startup) */ /**************************************************************************/ void setup(void) { Serial.begin(9600); Serial1.begin(9600); //HC 05 블루투스 시리얼 Serial.println("Orientation Sensor Raw Data Test\n"); /* 센서 연결 */ if (!bno.begin()) { /* 센서가 연결되지 않을 경우 오류 메시지 출력 */ Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!"); while (1) ; } bno.setExtCrystalUse(true); // 핀 설정 pinMode(A0, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(A1, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(A2, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(A3, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(Pin_Mouse_Left, INPUT_PULLUP); pinMode(Pin_Mouse_Right, INPUT_PULLUP); pinMode(Pin_Reload, INPUT_PULLUP); pinMode(Pin_Jump, INPUT_PULLUP); pinMode(Pin_Joybutton, INPUT_PULLUP); pinMode(Pin_JoyUp, INPUT_PULLUP); pinMode(Pin_JoyDown, INPUT_PULLUP); pinMode(Pin_JoyLeft, INPUT_PULLUP); pinMode(Pin_JoyRight, INPUT_PULLUP); pinMode(Pin_Use, INPUT_PULLUP); pinMode(Pin_Skill, INPUT_PULLUP); pinMode(Pin_Weapon, INPUT_PULLUP); delay(STARTUP_DELAY_MS); } void loop(void) { bHold = digitalRead(Pin_Joybutton) == LOW; // 조이스틱 중앙버튼 if (!bHold) // 리센터 버튼을 누를경우 마우스 움직임 처리를 무시함. { imu::Vector<3> eulerVector = bno.getVector(Adafruit_BNO055::VECTOR_EULER); imu::Vector<3> gyroVector = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE); /* Display calibration status for each sensor. */ uint8_t system, gyro, accel, mag = 0; bno.getCalibration(&system, &gyro, &accel, &mag); // z축 회전값으로 코사인 사인 함수값 계산 double c = cos(eulerVector.z() * DEG_TO_RAD); double s = sin(eulerVector.z() * DEG_TO_RAD); // DEADZONE 이상의 수치를 보여주는 각속도 값 읽어오기 double gyroX = fabs(gyroVector.x()) < DEADZONE ? 0 : -gyroVector.x(); double gyroY = fabs(gyroVector.y()) < DEADZONE ? 0 : gyroVector.y(); // 해당 각속도를 회전변환을 통해 중력과 평행하는 각속도로 변환 double moveX = gyroX * c + gyroY * s; double moveY = gyroX * -s + gyroY * c; //전역으로 안되어있길래 수정함 // 이동 평균 필터링으로 각속도를 수정 filteredX = filteredX * alpha + moveX * (1 - alpha); filteredY = filteredY * alpha + moveY * (1 - alpha); data.mouse_x = filteredX; data.mouse_y = filteredY; } else { // recenter 상태인 경우 0.0f을 전송 data.mouse_x = 0.0f; data.mouse_y = 0.0f; } data.pin = get_pin_data(); data.joystick_x = get_joystick_data_x(); data.joystick_y = get_joystick_data_y(); // data를 인코드해서 buffer에 저장함 write_bytes(buffer, &data); // buffer를 블루투스 시리얼로 전송함 Serial1.write(buffer, FRAME_MAX_SIZE); Serial1.flush(); // 디버그 용 시리얼 출력 // Serial.print(",mouse_x:"); // Serial.print(data.mouse_x); // Serial.print(",mouse_y:"); // Serial.println(data.mouse_y); // Serial.print("pin:"); // Serial.print(data.pin, BIN); // Serial.print(",joy_x:"); // Serial.print(data.joystick_x); // Serial.print(",joy_y:"); // Serial.println(data.joystick_y); delay(BNO055_SAMPLERATE_DELAY_MS); } ``` &nbsp; ### transmitter_final.ino ```c= //************************************************************************************* // G-gun 송신기용 코드 (총) //************************************************************************************* #include <Wire.h> //IMU 센서 제어용 헤더 #include <Adafruit_Sensor.h> #include <Adafruit_BNO055.h> #include <utility/imumaths.h> #include <math.h> // boolean 타입 사용을 위한 헤더 #include <stdbool.h> //폴링 딜레이 설정. #define BNO055_SAMPLERATE_DELAY_MS (1) #define STARTUP_DELAY_MS (1000) #define RAD_TO_DEG 57.295779513082320876798154814105 #define DEG_TO_RAD 0.01745329251994329576923690768489 //필요 #define FRAME_START 0xAA #define FRAME_END 0xDD #define FRAME_MAX_SIZE 13 //필요 typedef struct _data { byte pin; uint8_t joystick_x; uint8_t joystick_y; float mouse_x; float mouse_y; } DATA; // ===================== 코드에서 사용할 전역 변수 값들 ===== // 가속도의 무시를 위한 데드존 const float DEADZONE = 0.01f; bool bHold = false; // recenter 용도 const byte Pin_Joybutton = 6; // 전송될 핀들 (7월 18일 핀배열 전부 새로함) const byte Pin_Mouse_Left = 16; const byte Pin_Mouse_Right = 14; const byte Pin_Reload = 15; const byte Pin_Jump = A0; const byte Pin_Use = 4; const byte Pin_Skill = A2; const byte Pin_Weapon = 5; const byte Pin_JoyUp = 8; const byte Pin_JoyDown = 7; const byte Pin_JoyLeft = 9; const byte Pin_JoyRight = 10; double filteredX, filteredY; const float alpha = 0.0f; // 인코딩 및 전송 관련 DATA data; byte buffer[FRAME_MAX_SIZE]; void write_bytes(byte *buffer, DATA *data) { buffer[0] = FRAME_START; buffer[1] = (byte)(data->pin); buffer[2] = (byte)(data->joystick_x); buffer[3] = (byte)(data->joystick_y); // mouse_x memcpy(buffer + 4, &data->mouse_x, 4); // *((float *)(buffer + 4)) = data->mouse_x; // mouse_y memcpy(buffer + 8, &data->mouse_y, 4); // *((float *)(buffer + 8)) = data->mouse_y; buffer[12] = FRAME_END; } // 핀 데이터를 읽어서 각 bit에 저장함 byte get_pin_data() { byte pin_data = 0; pin_data |= (byte)(digitalRead(Pin_Mouse_Left) == LOW) << 0; pin_data |= (byte)(digitalRead(Pin_Mouse_Right) == LOW) << 1; pin_data |= (byte)(digitalRead(Pin_Reload) == LOW) << 2; pin_data |= (byte)(digitalRead(Pin_Jump) == LOW) << 3; pin_data |= (byte)(digitalRead(Pin_Use) == LOW) << 4; pin_data |= (byte)(digitalRead(Pin_Skill) == LOW) << 5; pin_data |= (byte)(digitalRead(Pin_Weapon) == LOW) << 6; return pin_data; } // 조이스틱 핀 데이터를 읽어서 상태를 -1, 0, 1로 저장함. uint8_t get_joystick_data_x() { bool xPlus = digitalRead(Pin_JoyRight) == LOW; bool xMinus = digitalRead(Pin_JoyLeft) == LOW; uint8_t status; // will be translated to -1, 1, 0 if (xMinus) { status = 0; } else if (xPlus) { status = 2; } else { status = 1; } return status; } // 조이스틱 핀 데이터를 읽어서 상태를 -1, 0, 1로 저장함. uint8_t get_joystick_data_y() { bool yPlus = digitalRead(Pin_JoyDown) == LOW; bool yMinus = digitalRead(Pin_JoyUp) == LOW; uint8_t status; // will be translated to -1, 1, 0 if (yMinus) { status = 0; } else if (yPlus) { status = 2; } else { status = 1; } return status; } // ======================================================== // IMU 센서 I2C 주소 설정 (-1, 0x29) Adafruit_BNO055 bno = Adafruit_BNO055(-1, 0x28); //짭센서 0x29 정품센서 0x28 /**************************************************************************/ /* Arduino setup function (automatically called at startup) */ /**************************************************************************/ void setup(void) { Serial.begin(9600); Serial1.begin(9600); //HC 05 블루투스 시리얼 Serial.println("Orientation Sensor Raw Data Test\n"); /* 센서 연결 */ if (!bno.begin()) { /* 센서가 연결되지 않을 경우 오류 메시지 출력 */ Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!"); while (1) ; } bno.setExtCrystalUse(true); // 핀 설정 pinMode(A0, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(A1, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(A2, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(A3, INPUT); //아날로그 핀을 디지털 핀으로 교체 pinMode(Pin_Mouse_Left, INPUT_PULLUP); pinMode(Pin_Mouse_Right, INPUT_PULLUP); pinMode(Pin_Reload, INPUT_PULLUP); pinMode(Pin_Jump, INPUT_PULLUP); pinMode(Pin_Joybutton, INPUT_PULLUP); pinMode(Pin_JoyUp, INPUT_PULLUP); pinMode(Pin_JoyDown, INPUT_PULLUP); pinMode(Pin_JoyLeft, INPUT_PULLUP); pinMode(Pin_JoyRight, INPUT_PULLUP); pinMode(Pin_Use, INPUT_PULLUP); pinMode(Pin_Skill, INPUT_PULLUP); pinMode(Pin_Weapon, INPUT_PULLUP); delay(STARTUP_DELAY_MS); } void loop(void) { bHold = digitalRead(Pin_Joybutton) == LOW; // 조이스틱 중앙버튼 if (!bHold) // 리센터 버튼을 누를경우 마우스 움직임 처리를 무시함. { imu::Vector<3> eulerVector = bno.getVector(Adafruit_BNO055::VECTOR_EULER); imu::Vector<3> gyroVector = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE); /* Display calibration status for each sensor. */ uint8_t system, gyro, accel, mag = 0; bno.getCalibration(&system, &gyro, &accel, &mag); // z축 회전값으로 코사인 사인 함수값 계산 double c = cosf(eulerVector.z() * DEG_TO_RAD); double s = sinf(eulerVector.z() * DEG_TO_RAD); // DEADZONE 이상의 수치를 보여주는 각속도 값 읽어오기 double gyroX = fabs(gyroVector.z()) < DEADZONE ? 0 : -gyroVector.z(); //정품센서와 짭센서가 X와 Z 오리엔테이션이 달라서 변경함. double gyroY = fabs(gyroVector.y()) < DEADZONE ? 0 : gyroVector.y(); // 해당 각속도를 회전변환을 통해 중력과 평행하는 각속도로 변환 double moveX = gyroX * c + gyroY * s; double moveY = gyroX * -s + gyroY * c; //전역으로 안되어있길래 수정함 // 이동 평균 필터링으로 각속도를 수정 // filteredX = filteredX * alpha + moveX * (1 - alpha); // filteredY = filteredY * alpha + moveY * (1 - alpha); data.mouse_x = moveX; data.mouse_y = moveY; } else { // recenter 상태인 경우 0.0f을 전송 data.mouse_x = 0.0f; data.mouse_y = 0.0f; } data.pin = get_pin_data(); data.joystick_x = get_joystick_data_x(); data.joystick_y = get_joystick_data_y(); // data를 인코드해서 buffer에 저장함 write_bytes(buffer, &data); // buffer를 블루투스 시리얼로 전송함 Serial1.write(buffer, FRAME_MAX_SIZE); Serial1.flush(); // 디버그 용 시리얼 출력 // Serial.print(",mouse_x:"); // Serial.print(data.mouse_x); // Serial.print(",mouse_y:"); // Serial.println(data.mouse_y); // Serial.print("pin:"); // Serial.print(data.pin, BIN); // Serial.print(",joy_x:"); // Serial.print(data.joystick_x); // Serial.print(",joy_y:"); // Serial.println(data.joystick_y); delay(BNO055_SAMPLERATE_DELAY_MS); } ``` 입력 핀 배열 및 BNO055 센서 축 방향에 관해서 변경이 있었음. 정품 BNO055 센서를 이용했다면 마우스 변위 계산 부분에 이쪽 코드를 이용해야 할 것. &nbsp; ### receiver.ino ```c= //************************************************************************************* // G-gun 수신기용 코드 (동글) //************************************************************************************* #include <Wire.h> #include <Mouse.h> #include <stdbool.h> #include <Keyboard.h> #define FRAME_START 0xAA #define FRAME_END 0xDD #define FRAME_MAX_SIZE 13 bool mouse_left, mouse_right, reload, joybutton, jump, use, skill, weapon; short joystick_x, joystick_y; float mouse_x, mouse_y; const float MouseXScale = 0.3f; //마우스 감도 설정 const float MouseYScale = 0.2f; //마우스 감도 설정 byte buffer[FRAME_MAX_SIZE]; void setup() { Serial.begin(9600); Serial1.begin(9600); //HC 05 블루투스 시리얼 시작. Serial1.setTimeout(20); while (!Serial1) { } } void loop() { memset(buffer, 0, sizeof(buffer)); //FRAME을 한번에 읽는 버전 // 이후의 Serial을 Bluetooth를 이용하는 Serial로 바꿔주세요 if (Serial1.available() >= FRAME_MAX_SIZE) { Serial1.readBytes(buffer, 1); // Serial.print(buffer[0], HEX); // 프레임이 미리 지정된 FRAME_START로 시작하지 않으면 건너뜀 if (buffer[0] != FRAME_START) { // Serial.println(""); return; } Serial1.readBytes(buffer, FRAME_MAX_SIZE - 1); // for(int i=0; i< FRAME_MAX_SIZE - 1; i++) { // Serial.print(":"); // Serial.print(buffer[i], HEX); // } // Serial.println(""); // boolean values mouse_left = buffer[0] & 0x01; mouse_right = buffer[0] >> 1 & 0x01; reload = buffer[0] >> 2 & 0x01; jump = buffer[0] >> 3 & 0x01; use = buffer[0] >> 4 & 0x01; skill = buffer[0] >> 5 & 0x01; weapon = buffer[0] >> 6 & 0x01; // joystick values // -1 means negative, 0 means neutral, and 1 means positive joystick_x = (short)buffer[1] - 1; joystick_y = (short)buffer[2] - 1; // joystick_x = (signed short)(uint8_t)buffer[1] - 1; // joystick_y = (signed short)(uint8_t)buffer[2] - 1; // read mouse_x as float memcpy(&mouse_x, buffer + 3, 4); // read mouse_y as float memcpy(&mouse_y, buffer + 7, 4); // 역시 프레임이 FRAME_END로 끝나지 않으면 건너뜀 if (buffer[11] != FRAME_END) return; } else { return; } // 처리 if (mouse_left) //마우스 왼클릭 { Mouse.press(MOUSE_LEFT); // Serial.println("press mouse left"); } else { Mouse.release(MOUSE_LEFT); } if (mouse_right) //마우스 오른클릭 { Mouse.press(MOUSE_RIGHT); // Serial.println("press mouse right"); } else { Mouse.release(MOUSE_RIGHT); } if (reload) //리로드 버튼 R { Keyboard.press('r'); // Serial.println("press Reload"); } else { Keyboard.release('r'); } if (jump) { Keyboard.press(0x20); // Serial.println("press Jump"); } else { Keyboard.release(0x20); } if (use) { Keyboard.press('f'); // Serial.println("press use"); } else { Keyboard.release('f'); } if (skill) { Keyboard.press(KEY_CAPS_LOCK); // Serial.println("press skill"); } else { Keyboard.release(KEY_CAPS_LOCK); } if (weapon) { Keyboard.press(KEY_TAB); // Serial.println("press weapon"); } else { Keyboard.release(KEY_TAB); } // ============= 5방향 스틱 (중앙 버튼은 송신기에서 리센터 버튼으로 사용) ============ // X+ : right // X- : left // Y+ : down // Y- : up // Y- if (joystick_y == -1) { Keyboard.press('w'); } else { Keyboard.release('w'); } //Y+ if (joystick_y == 1) { Keyboard.press('s'); } else { Keyboard.release('s'); } //X- if (joystick_x == -1) { Keyboard.press('a'); } else { Keyboard.release('a'); } //X+ if (joystick_x == 1) { Keyboard.press('d'); } else { Keyboard.release('d'); } //마우스 움직임 Mouse.move(mouse_x * MouseXScale, mouse_y * MouseYScale, 0); } ``` &nbsp; &nbsp; # 프로토타입 제작 및 테스트 &nbsp; ## 제작 과정 *>>> 똘튜브 영상 희망 <<<* &nbsp; ## 결과물 <iframe src="https://clips.twitch.tv/embed?clip=BumblingSuspiciousPuddingBabyRage-hkKhCtyJF6QSTtk7&parent=hackmd.io" frameborder="0" allowfullscreen="true" scrolling="no" height="378" width="620"></iframe> &nbsp; &nbsp; # 완성품 설계 및 제작 &nbsp; ## 제작 과정 *>>> 똘튜브 영상 희망 <<<* &nbsp; ## 결과물 ![g-gun_final](https://i.imgur.com/omPbekC.jpg) > G-건을 이용해서 Grand Theft Auto를 조작하고 있는 모습 &nbsp; <!-- <iframe src="https://clips.twitch.tv/embed?clip=NaiveArtisticManateeHassaanChop--uPVUImf80v8Ohxj&parent=hackmd.io" frameborder="0" allowfullscreen="true" scrolling="no" height="378" width="620"></iframe> --타사 제품에 대한 비방처럼 보일수 있어 주석 처리함(똘삼)> &nbsp; &nbsp; # 최종 결과물 *>>> 똘튜브 영상 희망 <<<* &nbsp; > [color=#ff1f1a] 영상 첨부하는 법: > 아래와 같이 적은 후 `-EGT7Mehn98` 부분을 원하는 영상의 ID로 교체 > [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/-EGT7Mehn98/0.jpg)](https://www.youtube.com/watch?v=-EGT7Mehn98) [^1]: 출처: https://duino4projects.com/arduino-circuit-design-program/

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully