공중에서 사용할 수 있는 에어마우스를 일상 생활에서 쓸 일은 그리 많지는 않다. 최근들어 다양한 기능을 지원하는 스마트 티비의 개발과 함께 좀 더 다양한 동작을 할 수있는 에어마우스 형태의 등장했긴 했으나 아직까지 컴퓨터를 사용하면서 에어마우스를 쓸 일은 거의 없는것 같다.
하지만 게임이라면 이야기가 달라진다. FPS 게임의 경우 총의 조준을 마우스로 하지만 실감나는 게임을 위해 실제 총과 비슷한 콘솔을 이용하기도 한다. (Wii의 눈차크, 플레이스테이션등) 그래서 이번에는 FPS 게임을 위한 FPS 게임 컨트롤러 프로토타입을 제작해 볼 것이다.
우선은 MPU6050과 NRF24L01 모듈이 필요하다. 무선으로 제작할 계획이기 때문에 NRF24L01 모듈을 이용했으며 사용법은 하단의 링크를 참고하자
http://diy-project.tistory.com/2?category=710046
무선이기 때문에 송신부와 수신부를 각각 제작해야한다. 각각의 모습은 아래와 같다.
송신부
수신부
출처 : https://microcontrollerelectronics.com/using-an-nrf24l01-module-to-scan-the-2-4ghz-frequency-range/
처음에는 MPU6050의 기본 예제를 이용해서 에어마우스를 구연하려고 했으나 어마어마한 딜레이로 마우스가 절대 부드럽게 움직이지 않았다. 수많은 검색 끝에 국내에서 에어마우스에 관해 쓸만한 자료 두 곳을 찾았다. 소스코드는 두 곳을 모두 참고해 작성했다.
1. https://kocoafab.cc/make/view/445
2. http://cafe.naver.com/arduinostory/18039
첫번째 참고 사이트에서 국내의 거의 유일한 에어마우스의 완성도 높은 코드를 구할 수 있다. 하지만 많은 사람들이 I2C 통신 오류로 실패를 한 코드 이기도 하다. 필자 또한 스케치에 이 코드를 업로드 했을때 I2C관련 오류로 삽질을 좀 했다. 결국 두번째 사이트에서 해답을 찾을 수 있었는데, 첫번째 사이트의 코드가 완전한 코드가 아니었기 때문에 오류가 발생 한 것이다. 두 곳의 코드를 합쳐 수정한 코드는 아래와 같다.
송신부
#include <SPI.h>
#include "RF24.h"
#include <Wire.h>
#include "Kalman.h"
int msg[2]={0,0};
int msg1 = 0;
int msg2 = 0;
byte address[6] = "1Node";
RF24 radio(7,8); // CE, CSN
int16_t gyroX, gyroZ;
int Sensitivity = 600;
int delayi = 3;
uint32_t timer;
uint8_t i2cData[14]; // Buffer for I2C data
const uint8_t IMUAddress = 0x68; // AD0 is logic low on the PCB
const uint16_t I2C_TIMEOUT = 1000; // Used to check for errors in I2C communication
uint8_t i2cWrite(uint8_t registerAddress, uint8_t data, bool sendStop) {
return i2cWrite(registerAddress,&data,1,sendStop); // Returns 0 on success
}
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 i2cRead(uint8_t registerAddress, uint8_t* data, uint8_t nbytes) {
uint32_t timeOutTimer;
Wire.beginTransmission(IMUAddress);
Wire.write(registerAddress);
if(Wire.endTransmission(false)) // Don't release the bus
return 1; // Error in communication
Wire.requestFrom(IMUAddress, nbytes,(uint8_t)true); // Send a repeated start and then release the bus after reading
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; // Error in communication
}
}
return 0; // Success
}
void setup() {
Serial.begin(9600);
radio.begin();
radio.openWritingPipe(address);
radio.stopListening();
Wire.begin();
i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to ±2g
while(i2cWrite(0x19,i2cData,4,false)); // Write to all four registers at once
while(i2cWrite(0x6B,0x01,true)); // PLL with X axis gyroscope reference and disable sleep mode
while(i2cRead(0x75,i2cData,1));
if(i2cData[0] != 0x68) { // Read "WHO_AM_I" register
Serial.print(F("Error reading sensor"));
while(1);
}
delay(100); // Wait for sensor to stabilize
/* Set kalman and gyro starting angle */
while(i2cRead(0x3B,i2cData,6));
timer = micros();
// Mouse.begin();
}
void loop() {
/* Update all the values */
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;
Serial.print("\t");
Serial.print(gyroX);
Serial.print(gyroZ);
msg1=gyroX;
msg[0]=msg1;
msg2=gyroZ;
msg[1]=msg2;
radio.write(&msg, sizeof(msg));
Serial.print("\r\n");
delay(delayi);
}
수신부
#include <SPI.h>
#include <Mouse.h>
#include "RF24.h"
int msg[2];
int msg1;
int msg2;
byte address[6] = "1Node";
RF24 radio(7,8); // CE, CSN
void setup(void) {
Serial.begin(9600);
radio.begin();
Mouse.begin();
radio.openReadingPipe(1, address);
radio.startListening();
}
void loop(void) {
if(radio.available()) {
radio.read(&msg, sizeof(msg));
Serial.print("Meassage (RX) = ");
Serial.print(msg[0]);
Serial.println(msg[1]);
Mouse.move(msg[1], -msg[0]);
}
}
사용된 라이브러리는 칼만필터 (센서값의 노이즈를 잡아주는 아주 유명한 프로그램이다.), MPU6050, NRF24L01이다. 라이브러리는 아래 파일로 다운이 가능하다.
아래는 테스트 영상이다. 이 글이 에어마우스를 시도하는 많은 사람에게 도움이 되길 바란다.
[수정]
아래의 글들이 도움이 될 수있다.
2018/01/12 - [아두이노/프로젝트] - 아두이노 FPS 게임 컨트롤러 (오버워치, 더 하우스 오브 더 데드)
2018/05/20 - [아두이노/프로젝트] - 아두이노 FPS 게임 컨트롤러 2.0
2018/09/22 - [아두이노/프로젝트] - 아두이노 FPS 게임 컨트롤러 수신부 업그레이드 중
2018/09/25 - [아두이노/프로젝트] - 모바일 배틀그라운드 컨트롤러 만들기 [아두이노 FPS 게임컨트롤러 활용]
2018/10/04 - [아두이노/프로젝트] - FPS 게임 컨트롤러 수신기 PCB 발주
2018/10/07 - [분류 전체보기] - 아두이노 FPS 게임 컨트롤러 2.0 게임 테스트
'프로젝트 > FPS 게임 컨트롤러' 카테고리의 다른 글
FPS 게임 컨트롤러 수신기 PCB 발주 (0) | 2018.10.04 |
---|---|
모바일 배틀그라운드 컨트롤러 만들기 [아두이노 FPS 게임컨트롤러 활용] (4) | 2018.09.25 |
아두이노 FPS 게임 컨트롤러 수신부 업그레이드 중 (0) | 2018.09.22 |
아두이노 FPS 게임 컨트롤러 2.0 (2) | 2018.05.20 |
아두이노 FPS 게임 컨트롤러 (오버워치, 더 하우스 오브 더 데드) (50) | 2018.01.12 |