工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験

プロジェクト概要

15×10のNeoPixelマトリクスを画面とし、9DoFセンサー(LSM9DS1)内蔵の自作ラケットでプレイするテニスゲームです。Wii Sportsテニスのような「奥行き感」をLEDの大きさと位置で表現し、Bluetooth経由でラケットのスイングを検知してラリーを楽しめます。Pythonスクリプトがブリッジ役を務め、2台のArduino Nanoを連携させます。

システム構成

パートハードウェア役割
ディスプレイArduino Nano + 15×10 NeoPixelマトリクスゲームロジック・描画・スコア管理・EEPROMハイスコア保存
コントローラArduino Nano + SparkFun 9DoF Sensor Stick + HC-05 Bluetooth加速度検知(±1.5G閾値)でスイング判定、'1'を送信
ブリッジPC上のPythonスクリプトBluetooth COMポート↔マトリクスCOMポートを中継、1秒デバウンス

パート1:ディスプレイ(ゲームボード)

Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験

ゲームルール

Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
ゲームロジックフロー

マトリクス製作

150個のNeoPixelをジグザグ配線で15列×10行に配置。詳細な組み立てガイドはこちら参照。

Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験

パート2:コントローラ(ラケット)

Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験

ハードウェア

スイング検知ロジック

10ms間隔でX/Y軸加速度を読み、絶対値が1.5Gを超えたらスイングと判定、シリアルで 1 を送信。600msのクールダウンで連打防止。

Bluetooth通信とPythonブリッジ

ラケットとディスプレイは直接通信しません。PC上のPythonが両シリアルポートを開き、ラケットからの 1 を受信したらディスプレイ側へ 1 を転送。両Arduinoのリセットタイミング同期も兼ねます。

import serial
import time

matrix_port = "COM3"      # ディスプレイ側Arduino
raquet_port = "COM9"      # ラケット側Bluetooth

matrix = serial.Serial(matrix_port, 9600)
racquet = serial.Serial(raquet_port, 9600)

time.sleep(10)  # 両Arduinoの起動待ち

while True:
    data = racquet.read()
    print(data)
    if data == "1":
        print("hit")
        matrix.write("1")
        time.sleep(1)   # デバウンス
    time.sleep(0.05)

ラケット製作フォトガイド

Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験

ソースコード

Matrix Code(ディスプレイ側Arduino Nano)

#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 0x02
#define NOTE_C1 33
#define SPKR_PIN 3

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;
int serial_data;
String score_string = "";
uint16_t colors[] = {
  matrix.Color(255,0,0), matrix.Color(0,255,0), matrix.Color(150,200,0)
};
static unsigned long lastFrame = 0;

void setup() {
  Serial.begin(9600);
  matrix.begin();
  matrix.fillScreen(0);
  matrix.setTextColor(colors[1]);
  randomSeed(analogRead(A2));
  display_scores();
  highscore = read_HS();
  matrix.setCursor(0,1); matrix.print("HS: "); matrix.show(); delay(1000);
  matrix.fillScreen(0); matrix.setCursor(0,1); matrix.print(highscore); matrix.show(); delay(1000);
  score_string = "";
}

void loop() {
  if ((lastFrame + framerate) < millis()) {
    update_frame();
    lastFrame = millis();
  }
}

void update_frame() {
  serial_data = Serial.parseInt();
  isSwung = (serial_data != 0);

  if (ball_y >= 5 && ball_y < 7 && isSwung) {
    if (isDirRight) { ball_x_dir = -1; ball_y_dir = -1; }
    else { ball_x_dir = 1; ball_y_dir = -1; }
    r_incr = -1;
  }
  else if (ball_y >= 8) {
    COM_score++; end_round();
  }
  else if (ball_y <= 2) {
    isDirRight = !isDirRight;
    int randNum = random(4);
    if (randNum == 2) { // 25% COMミス
      score++;
      if (score > highscore) write_HS();
      end_round();
    } else {
      if (isDirRight) { ball_x_dir = 1; ball_y_dir = 1; }
      else { ball_x_dir = -1; ball_y_dir = 1; }
      r_incr = 1;
    }
  }
  ball_x += ball_x_dir;
  ball_y += ball_y_dir;
  radius += r_incr;
  matrix.fillScreen(0);
  matrix.fillCircle(ball_x, ball_y, radius, colors[2]);
  matrix.show();
}

void end_round() {
  if (COM_score >= 10) end_game();
  isDirRight = true; r_incr = 1;
  ball_x_dir = 1; ball_y_dir = 1;
  ball_x = 7; ball_y = 2; radius = 1;
  display_scores();
  matrix.fillScreen(0);
  matrix.fillCircle(ball_x, ball_y, radius, colors[2]);
  matrix.show();
}

void end_game() {
  matrix.fillScreen(0);
  matrix.setCursor(0,1);
  matrix.setTextColor(colors[0]);
  matrix.drawLine(3,0,12,9,colors[0]);
  matrix.drawLine(11,0,2,9,colors[0]);
  matrix.show();
  delay(500);
  while(1); // 停止
}

void display_scores() {
  matrix.fillScreen(0);
  matrix.setTextColor(colors[1]);
  matrix.setCursor(0,1);
  score_string = String(score) + "-" + String(COM_score);
  scrollText(score_string);
  matrix.fillScreen(0);
  delay(2000);
  matrix.show();
}

void scrollText(String text) {
  int x = matrix.width();
  for (int i=0; i<24; i++) {
    matrix.fillScreen(0);
    matrix.setCursor(x,2);
    matrix.print(text);
    x--;
    matrix.show();
    delay(150);
  }
}

void write_HS() { EEPROM.write(0x04, (int)score); }
int read_HS() { return EEPROM.read(0x04); }

Racquet Code(ラケット側Arduino Nano)

#include <Wire.h>
#include <SPI.h>
#include <SparkFunLSM9DS1.h>

LSM9DS1 imu;
#define LSM9DS1_M 0x1E
#define LSM9DS1_AG 0x6B
#define PRINT_SPEED 10

static 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 = LSM9DS1_M;
  imu.settings.device.agAddress = LSM9DS1_AG;
  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) {
    Serial.print(1); delay(600);
  } else if (accely <= -1.5 || accely >= 1.5) {
    Serial.print(1); delay(600);
  }
}

Python Bridge Code

import serial, time
matrix_port = "COM3"
raquet_port = "COM9"
matrix = serial.Serial(matrix_port, 9600)
racquet = serial.Serial(raquet_port, 9600)
time.sleep(10)
while 1:
    data = racquet.read()
    print(data)
    if data == "1":
        print("hit")
        matrix.write("1")
        time.sleep(1)
    time.sleep(.05)

回路図

Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験
Arduinoテニスゲーム:NeoPixelマトリクス・9DoFセンサー・Bluetoothで作るバーチャルラケット体験

まとめ

NeoPixelマトリクスとIMUセンサー、Bluetoothを組み合わせたレトロフューチャーなテニスゲーム。ハードウェア製作(マトリクス配線・ラケット工作)に手間はかかりますが、完成時の達成感と遊びごたえは格別です。Pythonブリッジを介した2マイコン協調動作の勉強にもなります。ぜひ挑戦してみてください!

製造プロセス

  1. TinyML言語検出器:Edge ImpulseとArduino Nano 33 BLE Senseで「はい」を多言語判別
  2. Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
  3. Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
  4. Arduinoでアイアンマンを自作:部品・センサー・手順ガイド
  5. ArduinoとBluetoothで作るスマート紛失防止タグ「Find Me」
  6. DIY Arduino加湿器コントローラー:リレーを用いた安全な高電圧制御
  7. Arduino超音波センサーとProcessingで作るリアルタイム距離測定ソナー
  8. MobBob:Arduino製ロボットをAndroidスマホでシームレスに制御
  9. Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション
  10. Arduino Nanoで作るNeoPixelマトリックス版Pongゲーム
  11. ゲーム用カスタムArduinoジョイスティックハンドルの製作