Permainan Tenis Arduino – Buat Pengalaman Raket Virtual dengan NeoPixel, Sensor, dan Bluetooth
Komponen dan Perlengkapan
- Arduino Nano R3 — 2
- SparkFun 9DoF Sensor Stick — 1
- Kawat Jumper (Generic) — 1
- Strip NeoPixel — 1
- Modul Bluetooth HC-05 — 1
- Baterai 9V (generic) — 1
Alat dan Mesin yang Diperlukan
Aplikasi dan Layanan Online
Tentang Proyek Ini
Proyek ini memiliki 3 bagian: papan permainan/tampilan, pengontrol, dan router master (skrip Python).
Bagian 1 - Tampilan
Tampilan adalah matriks neopixel 15x10 yang berjalan di atas Arduino Nano. Ini menangani logika permainan dan masukan dari skrip Python yang berjalan di komputer host. Ini menampilkan bola yang tumbuh dan bergerak lebih jauh ke bawah matriks—memberikan ilusi kedalaman dan bola datang ke Anda. Jika terdeteksi bola dipukul, bola akan digambar kembali pergi 'dari' Anda.
Poin
Arah bola (baik kiri atau kanan) berubah dengan setiap pukulan. Jika Anda melewatkan bola, COM mendapat poin, tapi jika COM melewatkan (peluang 25%), Anda mendapat poin. Desain permainan ini mirip dengan versi tenis Wii Sports. Poin ditampilkan setiap kali COM mendapatkan poin terhadap pemain.
Skor Tertinggi
Ada skor tertinggi berjalan yang disimpan di EEPROM I2C, di alamat 0x04. Ini menyimpan byte yang menahan skor tertinggi. Jika Anda mendapatkan skor lebih tinggi dari skor tertinggi sebelumnya, itu menimpa alamat. Jika COM mendapatkan skor 10, game berakhir...
Buat Matriks
Membuat matriks akan memakan waktu, jadi ikuti panduan yang saya buat di sini.
Bagian 2 - Pengontrol
Pengontrol adalah Arduino Nano lain yang memiliki stik 9DoF (hanya 3v3!) dan modul BLE HC-05 yang dipasang padanya. Jika percepatan dari sumbu x atau y lebih besar dari ambang yang ditentukan, itu mengirim '1' melalui serial ke PC host, di mana itu diteruskan ke tampilan. Nano mengambil pembacaan accelerometer x dan y pada interval tertentu di mana itu memeriksa apakah nilai di atas atau di bawah 1,5 gram.
Komunikasi melalui Bluetooth
Jadi sekarang Anda mungkin bertanya, apa yang mengontrol operasi kompleks ini? Bagaimana raket berbicara dengan tampilan? Rahasia: mereka tidak berbicara satu sama lain! Ada skrip Python sederhana yang menggunakan port COM Bluetooth yang masuk dan port COM matriks untuk mentransfer informasi di antara mereka. Ini juga membantu mensinkronkan dua sistem, karena keduanya di-reset saat port serial diinisialisasi. Raket mengirim '1' ke skrip Python melalui Bluetooth, dan kemudian skrip Python mengirim '1' ke matriks. Ada debounce 1 detik sehingga port serial tidak membuffer overflow (mereka hanya memiliki buffer 32 bit).
Raket
Klik melalui gambar untuk melihat bagaimana saya membangun raket:
Bersenang-senanglah dengan permainan tenis baru Anda!
Code
- Kode Matriks
- Kode Raket
- Kode Python
Kode MatriksC/C++
#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;
int ball_y = 2;
int radius = 1;
int r_incr = 1;
bool isSwung = false;
int ball_x_dir = 1;
int ball_y_dir = 1;
bool isDirRight = true;
int score = 0;
int COM_score = 0;
int 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)};
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)< millis()){
update_frame();
lastFrame = millis();
}
}
void update_frame(){
serial_data = Serial.parseInt();
if(serial_data){ isSwung = true; }
else if(!serial_data){ isSwung = false; }
if(ball_y >= 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){
score += 1;
if(score > highscore){ write_HS(); }
} 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;
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 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);
return HS;
}
Kode RaketC/C++
#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;
float 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);
}
}
Kode PythonPython
import serial
import 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)
Skematika
