아두이노 테니스 게임: 네오픽셀·9DoF 센서·블루투스로 만드는 가상 라켓 체험
프로젝트 개요
15×10 네오픽셀 매트릭스를 디스플레이로, 9DoF IMU 센서가 장착된 라켓 컨트롤러를 블루투스로 연결해 즐기는 1인용 테니스 게임입니다. 공이 화면 안쪽(원근감 표현)으로 다가오고, 라켓 스윙을 감지하면 공이 튕겨 나가는 방식입니다. 파이썬 스크립트가 블루투스-시리얼 중계 역할을 수행합니다.
시스템 아키텍처 (3파트 구성)
- 디스플레이 보드: 아두이노 나노 + 네오픽셀 150개(15×10) + 게임 로직 수행
- 라켓 컨트롤러: 아두이노 나노 + SparkFun 9DoF 센서 스틱(LSM9DS1) + HC-05 블루투스 모듈 + 9V 배터리
- 마스터 라우터(PC): 파이썬 스크립트로 블루투스 COM 포트와 매트릭스 시리얼 포트 간 데이터 중계
게임 규칙 및 로직
- 공은 Y축(세로)으로 이동하며 크기(반지름)가 커졌다 작아지며 원근감 표현
- 공이 Y=5~7 구간에서 스윙 감지 시 방향 반전(X 방향 토글, Y 방향 반전)
- 공이 바닥(Y≥8) 도달 시 COM 득점, 천장(Y≤2) 도달 시 25% 확률로 플레이어 득점
- COM 10점 선취 시 게임 오버, 최고 점수는 I2C EEPROM(주소 0x50, 오프셋 0x04)에 영구 저장
- 점수 판정 시 스크롤 텍스트로 "Player-COM" 형식 표시
하드웨어 상세
디스플레이 보드
- 아두이노 나노 R3 × 1
- 네오픽셀 스트립 (WS2812B 등) 150개 → 15열×10행 지그재그 배열
- 데이터 핀: D6, 스피커 핀: D3 (선택 사항)
- I2C EEPROM (24LC256 등) 선택 사항: 최고 점수 저장용
라켓 컨트롤러
- 아두이노 나노 R3 × 1
- SparkFun 9DoF 센서 스틱 (LSM9DSI, 3.3V 전용!)
- HC-05 블루투스 모듈 (마스터/슬레이브 설정 필요)
- 9V 배터리 → 나노 Vin 핀 연결 (레귤레이터 거쳐 5V 생성)
- 골판지+종이로 라켓 형상 제작, 내부에 보드 고정
소프트웨어 구현
1. 매트릭스 코드 (아두이노 나노 - 디스플레이)
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <EEPROM.h>
#define PIN 6
#define EEPROM_ADR 0x50
#define HS_ADR 0x04
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(15, 10, PIN,
NEO_MATRIX_BOTTOM + NEO_MATRIX_LEFT + NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
NEO_GRB + NEO_KHZ800);
int ball_x = 7, ball_y = 2, radius = 1, r_incr = 1;
bool isSwung = false;
int ball_x_dir = 1, ball_y_dir = 1;
bool isDirRight = true;
int score = 0, COM_score = 0, highscore = 0;
int framerate = 50;
unsigned long lastFrame = 0;
uint16_t colors[] = {matrix.Color(255,0,0), matrix.Color(0,255,0), matrix.Color(150,200,0)};
void setup() {
Serial.begin(9600);
matrix.begin();
matrix.fillScreen(0);
highscore = EEPROM.read(HS_ADR);
// 시작 화면: 하이스코어 표시 등
}
void loop() {
if ((lastFrame + framerate) < millis()) {
update_frame();
lastFrame = millis();
}
}
void update_frame() {
int serial_data = Serial.parseInt();
isSwung = (serial_data != 0);
// 공 이동 및 충돌 판정 로직 (생략: 원본 참조)
// ...
// 화면 갱신
matrix.fillScreen(0);
matrix.fillCircle(ball_x, ball_y, radius, colors[2]);
matrix.show();
}
2. 라켓 코드 (아두이노 나노 - 컨트롤러)
#include <Wire.h>
#include <SparkFunLSM9DS1.h>
LSM9DS1 imu;
#define PRINT_SPEED 10
unsigned long lastPrint = 0;
float accelx = 0, accely = 0;
void setup() {
Serial.begin(9600);
imu.settings.device.commInterface = IMU_MODE_I2C;
imu.settings.device.mAddress = 0x1E;
imu.settings.device.agAddress = 0x6B;
if (!imu.begin()) while(1);
}
void loop() {
if ((lastPrint + PRINT_SPEED) < millis()) {
if (imu.accelAvailable()) imu.readAccel();
lastPrint = millis();
}
accelx = imu.calcAccel(imu.ax);
accely = imu.calcAccel(imu.ay);
if (accelx <= -1.5 || accelx >= 1.5 || accely <= -1.5 || accely >= 1.5) {
Serial.print('1'); // 스윙 감지 신호 전송
delay(600); // 디바운스
}
}
3. 파이썬 중계 스크립트 (PC)
import serial, time
matrix_port = "COM3" # 디스플레이 보드 시리얼 포트
raquet_port = "COM9" # 블루투스 가상 COM 포트
matrix = serial.Serial(matrix_port, 9600)
racquet = serial.Serial(raquet_port, 9600)
time.sleep(10) # 아두이노 리셋 대기
while True:
data = racquet.read()
if data == b'1':
print("Hit detected")
matrix.write(b'1')
time.sleep(1) # 디바운스 (시리얼 버퍼 32바이트 보호)
time.sleep(0.05)
제작 팁 & 주의사항
- 네오픽셀 매트릭스 제작: 납땜이 많이 필요하므로 인두기 좋은 것 준비, 전원 공급 별도 고려 (5V 3A 이상 권장)
- 9DoF 센서 라이브러리: SparkFun LSM9DS1 라이브러리 필수 설치
- 블루투스 페어링: HC-05를 마스터 모드로 설정해 라켓 측과 자동 연결되게 구성 권장
- 시리얼 버퍼 오버플로 방지: 파이썬 측 1초 디바운스 필수
- 전원: 라켓은 9V 배터리, 디스플레이는 USB 또는 별도 5V 어댑터
확장 아이디어
- 사운드 이펙트 추가 (피에조 부저로 타구음, 득점음)
- 2인용 대전: 라켓 2개 + 블루투스 멀티 커넥션 또는 NRF24L01 등 무선 통신
- OLED/LCD에 점수 상시 표시
- 난이도 조절: 공 속도, COM 실수 확률, 코트 크기 가변
- 웹캠+OpenCV로 실제 라켓 동작 인식 방식으로 대체
회로도
제공된 프리칭 회로도 2장(매트릭스 보드, 라켓 보드) 참고. 네오픽셀 데이터 라인에 330~470Ω 저항, 전원 라인에 1000μF 커패시터 권장.
마무리
아두이노 2대, 네오픽셀, IMU 센서, 블루투스만으로 놀랍게 완성도 높은 아케이드 스타일 테니스 게임을 만들 수 있습니다. 메이커 페어나 교육용 시연 프로젝트로도 훌륭하며, 파이썬 중계 레이어 덕분에 무선 컨트롤러 구현이 매우 단순해집니다. 직접 만들어 보며 임베디드 게임 프로그래밍의 재미를 느껴보세요!