https://diy-project.tistory.com/19
https://diy-project.tistory.com/57
어쩌다 보니 FPS 게임 컨트롤러 버전 3까지 만들게 되었다. 사실 버전 1과 버전 2는 구조적으로 큰 차이가 없었지만 버전 3은 상당한 변화가 생겼다.
첫 번째로 버전 1, 2와 달리 이제는 수신부를 필요로 하지 않고 기기와 블루투스로 연결이 가능하다. 덕분에 코드도 많이 간소화되었고 기존 nrf24l01의 고질적인 통신 문제도 해결되었다.
두 번째로 기존 조이스틱과 트리거 스위치를 제거하고 Wii의 눈차크를 사용한다. 조이스틱이랑 방아쇠 부분의 스위치는 연결 핀도 많아지고 회로가 복잡해지는 경향이 있는 듯하여 총과 가장 유사한 형태의 컨트롤러인 Wii 눈차크를 사용했다. 아두이노와 Wii 눈차크를 같이 사용하는 방법은 아래 글을 참고하자.
https://diy-project.tistory.com/113
한 가지 문제는 mpu6050 자이로 센서와 Wii 눈차크 모두 I2C 통신을 사용한다는 점인데, 덕분에 코드를 작성하는데 애를 좀 먹었다. 어찌어찌 동작은 한다.
아래는 필요한 부품 리스트들이다. 알다시피 대부분의 부품들은 알리익스프레스에서 구매한다.
1. ESP-32
2. mpu6050
3. Wii 눈차크
4. Wii 눈차크 커넥터 단자
위 핵심 부품들만 있어도 동작이 가능하다. 회로는 아래와 같이 구성했다.
연결 방법은 저번 블루투스 에어마우스랑 크게 다르지 않고 Wii 눈차크 연결만 추가되었다. 버전 2와 마찬가지로 PCB를 설계했다.
PCB 주문은 언제나 그랬듯 JLCPCB를 통해 주문했다.
위 링크는 현재 JLCPCB에서 한국어로 되어있는 랜딩 페이지이며 위 링크로 접속 시 새로운 한국 고객을 위해서 30달러의 쿠폰도 제공한다.
배송료를 제외하고 2달러 정도로 매우 저렴하게 구매가 가능하다. 필자가 설계한 파일은 아래 링크로 다운로드할 수 있다.
휴대용으로 제작했기 때문에 충전 회로와 배터리를 먼저 연결했다. 위 기판을 사용하는 경우 5V 전압을 출력하게 설계하면 된다. 필자는 간단하게 보조배터리 충방전 회로를 재사용했다.
그 위에 설계한 기판과 부품을 배치해 봤다. 연결 핀의 5V에 5V 입력을 주면 되고 SENS는 감도 조절용 가변 저항을 장착하면 된다. 나머지 2x6 핀들은 추가적인 버튼 착용 핀들이다. 필자는 아래 사진처럼 추가적인 버튼 3개를 연결했다. (기계식 키보드의 스위치를 사용했다.)
코드는 아래와 같다. 추가적인 핀을 사용하려면 몇 가지 수정을 해야 할 것 같다.
#include <Wire.h>
#include <SPI.h>
#include <SoftwareSerial.h>
#include <BleCombo.h>
//----------------------------------------------------//
#define ZERO_AX 530
#define ZERO_AY 530
#define ZERO_AZ 530
#define WII_NUNCHUCK_I2C_ADDRESS 0x52
int counter;
uint8_t data[6];
int16_t gyroX, gyroZ;
const int sensPin = 32;
int Sensitivity;
int delayi = 25;
uint32_t timer;
uint8_t i2cData[14];
const uint8_t IMUAddress = 0x68;
const uint16_t I2C_TIMEOUT = 1000;
uint8_t i2cWrite(uint8_t registerAddress, uint8_t* data, uint8_t length, bool sendStop) {
Wire.beginTransmission(IMUAddress);
Wire.write(registerAddress);
Wire.write(data, length);
return Wire.endTransmission(sendStop); // Returns 0 on success
}
uint8_t i2cWrite2(uint8_t registerAddress, uint8_t data, bool sendStop) {
return i2cWrite(registerAddress,&data,1,sendStop); // Returns 0 on success
}
uint8_t i2cRead(uint8_t registerAddress, uint8_t* data, uint8_t nbytes) {
uint32_t timeOutTimer;
Wire.beginTransmission(IMUAddress);
Wire.write(registerAddress);
if(Wire.endTransmission(false))
return 1;
Wire.requestFrom(IMUAddress, nbytes,(uint8_t)true);
for(uint8_t i = 0; i < nbytes; i++) {
if(Wire.available())
data[i] = Wire.read();
else {
timeOutTimer = micros();
while(((micros() - timeOutTimer) < I2C_TIMEOUT) && !Wire.available());
if(Wire.available())
data[i] = Wire.read();
else
return 2;
}
}
return 0;
}
//----------------------------------------------------//
void setup() {
Wire.begin();
Wire.beginTransmission(WII_NUNCHUCK_I2C_ADDRESS);
Wire.write(0xF0);
Wire.write(0x55);
Wire.endTransmission();
//----------------------------------------------------//
Wire.begin();
i2cData[0] = 7;
i2cData[1] = 0x00;
i2cData[3] = 0x00;
while(i2cWrite(0x19,i2cData,4,false));
while(i2cWrite2(0x6B,0x01,true));
while(i2cRead(0x75,i2cData,1));
while(i2cRead(0x3B,i2cData,6));
timer = micros();
Serial.begin(115200);
Keyboard.begin();
delay(100);
Mouse.begin();
delay(100);
}
void loop() {
Sensitivity = analogRead(sensPin);
while(i2cRead(0x3B,i2cData,14));
gyroX = ((i2cData[8] << 8) | i2cData[9]);
gyroZ = ((i2cData[12] << 8) | i2cData[13]);
gyroX = gyroX / Sensitivity / 1.1 * -1;
gyroZ = gyroZ / Sensitivity * -1;
Wire.requestFrom(WII_NUNCHUCK_I2C_ADDRESS, 6);
counter = 0;
while(Wire.available()){
data[counter++] = Wire.read();
}
Wire.beginTransmission(WII_NUNCHUCK_I2C_ADDRESS);
Wire.write(0x00);
Wire.endTransmission();
if(counter >= 5){
int SX = data[0];
int SY = data[1];
double AX = ((data[2] << 2) + ((data[5] >> 2) & 0x03) - ZERO_AX);
double AY = ((data[3] << 2) + ((data[5] >> 4) & 0x03) - ZERO_AY);
double AZ = ((data[4] << 2) + ((data[5] >> 6) & 0x03) - ZERO_AZ);
int BC =(data[5] >> 1) & 0x01;
int BZ = data[5] & 0x01;
Serial.print(BC);
Serial.print(" ");
Serial.print(BZ);
Serial.print(" ");
Serial.print(SX);
Serial.print(" ");
Serial.print(SY);
Serial.print(" ");
Serial.print(gyroX);
Serial.print(" ");
Serial.print(gyroZ);
Serial.print("\r\n");
if(Keyboard.isConnected()) {
Mouse.move(gyroZ, gyroX);
if (BC == 0) {
Mouse.press(MOUSE_LEFT);
}
else {
Mouse.release(MOUSE_LEFT);
}
if (BZ == 0) {
Mouse.press(MOUSE_RIGHT);
}
else {
Mouse.release(MOUSE_RIGHT);
}
if (SX > 170) {
Keyboard.press('d');
}
else if (SX < 70) {
Keyboard.press('a');
}
else if (SY > 170) {
Keyboard.press('w');
}
else if (SY < 70) {
Keyboard.press('s');
}
else {
Keyboard.releaseAll();
}
}
}
delay(delayi);
}
사용된 라이브러리는 블루투스 에어마우스랑 비슷한데 추가로 키보드, 마우스 콤보 라이브러리를 사용했다.
https://github.com/ServAlex/ESP32-BLE-Combo
아래는 작동 영상이다. 같이 비교해보면 좋을 것 같아 버전 1, 2의 영상도 넣었다.
[버전 1]
https://www.youtube.com/watch?v=dkw_3YOvp6A&ab_channel=WanderingDragon
[버전 2]
https://www.youtube.com/watch?v=aFOWWeR_Nek&ab_channel=WanderingDragon
[버전 3]
https://www.youtube.com/watch?v=tvRXFyJaSOE&ab_channel=WanderingDragon
오락실에서 자주 보이는 The House of The Dead 4를 플레이해봤다. 에뮬레이터 버전인데 설치 방법은 아래 글을 참고했다.
https://gall.dcinside.com/mgallery/board/view/?id=hotdm&no=354
'프로젝트 > FPS 게임 컨트롤러' 카테고리의 다른 글
아두이노 블루투스 에어마우스 만들기 (5) | 2022.06.19 |
---|---|
아두이노 FPS 게임 컨트롤러 2.0 게임 테스트 (5) | 2018.10.07 |
FPS 게임 컨트롤러 수신기 PCB 발주 (0) | 2018.10.04 |
모바일 배틀그라운드 컨트롤러 만들기 [아두이노 FPS 게임컨트롤러 활용] (4) | 2018.09.25 |
아두이노 FPS 게임 컨트롤러 수신부 업그레이드 중 (0) | 2018.09.22 |