バーチャルArduinoテニス
コンポーネントと消耗品
> |
| × | 2 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
必要なツールとマシン
> |
|
アプリとオンラインサービス
> |
|
このプロジェクトについて
このプロジェクトには、ゲームボード/ディスプレイ、コントローラー、マスタールーター(Pythonスクリプト)の3つの部分があります。
パート1-ディスプレイ
ディスプレイは、ArduinoNanoから実行される15x10ネオピクセルマトリックスです。ゲームロジックと、ホストコンピューターで実行されているPythonスクリプトからの入力を処理します。成長してマトリックスをさらに下に移動するボールが表示され、奥行きとボールがあなたに向かってくるような錯覚を与えます。ボールが打たれたことを検出した場合、ボールはあなたから「離れて」再描画されます。
<図>
ポイント
ボールの方向(左または右)は、ヒットするたびに変わります。ボールを逃した場合、COMはポイントを獲得しますが、COMがミスした場合(25%の確率)、あなたはポイントを獲得します。このゲームデザインは、WiiSportsバージョンのテニスに似ています。ポイントは、COMがプレーヤーに対してスコアを付けるたびに表示されます。
<図>
ハイスコア
0x04アドレスのI2CEEPROMに保存されている実行中のハイスコアがあります。ハイスコアを保持するバイトを保持します。以前のハイスコアよりも高いスコアを獲得すると、アドレスが上書きされます。 COMのスコアが10の場合、ゲームオーバーです...それが紛らわしい場合は、論理チャートを作成しました:
<図>
マトリックスを作成する
マトリックスの作成には時間がかかるので、ここで作成したガイドに従ってください。
<図> <図>
パート2-コントローラー
コントローラは、9DoFスティック(3v3のみ!)とHC-05BLEモジュールが接続された別のArduinoNanoです。 x軸またはy軸のいずれかの加速度が指定されたしきい値よりも大きい場合、ホストPCにシリアル経由で「1」を送信し、そこでディスプレイに中継されます。 Nanoは、設定された間隔でxおよびy加速度計を読み取り、値が1.5グラムを上回っているか下回っているかをチェックします。 I2C 9 DoFスティックにアクセスするには、ここからダウンロードできるSparkfunライブラリをインストールする必要があります。ここには、センサーを接続して基本的なプログラムを実行する方法に関するチュートリアルもあります。
ラケットを作るために、私は箱を使って、それから段ボールにラケットの形を切り取りました。そこからは簡単でした。段ボールの切り欠きの上に白い紙を置き、その紙の上にデザインを描くだけです。セットアップに電力を供給するために9Vバッテリーを使用しました。必ず、NanoのVinピンを介してバッテリーを実行してください。そうしないと、調整されていない9Vになります。これがラケット内部の内部の写真です:
<図>
Bluetooth経由で通信する
では、この複雑な操作を何が制御しているのか疑問に思われるかもしれません。ラケットはどのようにディスプレイに話しかけますか?ここに秘密があります:彼らはお互いに話しません!着信BluetoothCOMポートとマトリックスCOMポートを使用してそれらの間で情報を中継する単純なPythonスクリプトがあります。また、シリアルポートが初期化されると両方ともリセットされるため、2つのシステムを同期するのにも役立ちます。ラケットはBluetooth経由でPythonスクリプトに「1」を送信し、次にPythonスクリプトはマトリックスに「1」を送信します。 1秒のデバウンスがあるため、シリアルポートはバッファをオーバーフローさせません(32ビットバッファしかない)。
ラケット
写真をクリックして、ラケットの作り方を説明します。
<図> <図> <図> <図> <図> <図>新しいテニスゲームを楽しんでください!
テニスが行われているビデオ:
コード
- マトリックスコード
- ラケットコード
- Pythonコード
マトリックスコード C / C ++
マトリックスに接続されたArduinoに移動します。#include#include #include #include #include #define PIN 6 // matrix#define EEPROM_ADR 0x50のデータピン// EEPROM#define HS_ADR 0x02のI2Cアドレス// EEPROM#defineのハイスコアバイトのアドレスNOTE_C1 33#define SPKR_PIN 3Adafruit_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; //ボールのX座標ball_y =2; //ボールのY座標半径=1; //ボールのradiusintr_incr =1; //半径をどれだけ増やすかbyboolisSwung =false; //ラケットはスイングイントですball_x_dir =1; //ボールのX方向intball_y_dir =1; //ボールのY方向boolisDirRight =true; int score =0; //現在のゲームのスコアintCOM_score =0; int highscore =0; //すべてのgamesintフレームレートのハイスコア=50; //各フレーム間のミリ秒数intserial_data; String score_string =""; uint16_tcolors [] ={matrix.Color(255,0,0)、matrix.Color(0,255,0)、matrix.Color(150,200,0) }; int melody [] ={0}; int tempo [] ={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(); Serial.println(highscore、DEC); matrix.setCursor(0,1); matrix.print( "HS:"); matrix.show(); delay(1000); matrix.fillScreen(0); matrix.setCursor(0,1); matrix.print(highscore、DEC); matrix.show(); delay(1000); score_string ="";} void loop(){if((lastFrame + framerate) =5 &&ball_y <7 &&isSwung){if(isDirRight){ball_x_dir =-1; ball_y_dir =-1; } else if(!isDirRight){ball_x_dir =1; ball_y_dir =-1; } r_incr =-1; } else if(ball_y> =8){COM_score + =1; end_round(); } else if(ball_y <=2){isDirRight =!isDirRight; int randNum =random(4); Serial.println(randNum); if(randNum ==2){// COMがスコアを失う可能性は25%+ =1; if(score> highscore){write_HS(); } end_round; } else {if(isDirRight){ball_x_dir =1; ball_y_dir =1; } else if(!isDirRight){ball_x_dir =-1; ball_y_dir =1; } r_incr =1; }} ball_x + =ball_x_dir; ball_y + =ball_y_dir;半径+ =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;半径=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 pass =0; int x =matrix.width(); for(int i =0; i <24; i ++){matrix.fillScreen(0); matrix.setCursor(x、2); matrix.print(text); x- =1; matrix.show(); delay(150); }} void write_HS(){EEPROM.write(0x04、int(score));} int read_HS(){byte HS =EEPROM.read(0x04); //アドレス4から読み取るreturnHS;}
ラケットコード C / C ++
#include#include #include LSM9DS1 imu; #define LSM9DS1_M 0x1E // SDO_MがLOWの場合は0x1Cになります#defineLSM9DS1_AG 0x6B //の場合は0x6AになりますSDO_AGはLOW#define PRINT_SPEED 10 //チェック間の10ミリ秒staticunsigned long lastPrint =0; //印刷時間を追跡しますfloataccelx =0; float accely =0; void setup(){//セットアップコードをここに配置して1回実行します: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()){//(1)中に失敗しました; }} void loop(){if((lastPrint + PRINT_SPEED) =1.5){Serial.print(1); delay(600); } else if(accely <=-1.5 || accely> =1.5){Serial.print(1); delay(600);}}
Pythonコード Python
import serialimport timematrix_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( )データを出力するif data =="1":print "hit" matrix.write( "1")time.sleep(1)time.sleep(.05)
回路図
製造プロセス