Jogo de Tênis com Arduino – Experiência de Raquete Virtual com NeoPixel, Sensores e Bluetooth
Componentes e Materiais
| Arduino Nano R3 | × | 2 |
| SparkFun 9DoF Sensor Stick | × | 1 |
| Fios jumpers (genéricos) | × | 1 |
| Tira NeoPixel | × | 1 |
| Módulo Bluetooth HC-05 | × | 1 |
| Bateria 9V (genérica) | × | 1 |
Ferramentas e Máquinas Necessárias
- Ferro de solda (genérico)
Aplicativos e Serviços Online
- Arduino IDE
Sobre este Projeto
Este projeto tem 3 partes: o tabuleiro/display do jogo, o controle, e o roteador mestre (script Python).
Parte 1 - O Display
O display é uma matriz NeoPixel 15x10 que roda em um Arduino Nano. Ele lida com a lógica do jogo e entrada do Script Python rodando em um computador host. Exibe uma bola que cresce e se move mais para baixo na matriz - dando uma ilusão de profundidade e a bola vindo em sua direção. Se detectar que a bola foi rebatida, a bola será redesenhada indo "para longe" de você.

Pontuação
A direção da bola (esquerda ou direita) muda a cada rebatida. Se você perder a bola, o COM ganha um ponto, mas se o COM perder (25% de chance), você ganha o ponto. Este design de jogo é similar à versão de tênis do Wii Sports. Os pontos são exibidos sempre que o COM marca contra o jogador.

Recorde
Há um recorde em execução armazenado em uma EEPROM I2C, no endereço 0x04. Ele mantém um byte que guarda o recorde. Se você pontuar mais que o recorde anterior, ele sobrescreve o endereço. Se o COM chegar a 10 pontos, é game over... Se isso for confuso, fiz um fluxograma lógico:

Faça a Matriz
Fazer a matriz levará algum tempo, então siga o guia que fiz.


Parte 2 - O Controle
O controle é outro Arduino Nano que tem um stick 9DoF (apenas 3,3V!) e um módulo HC-05 BLE anexado. Se a aceleração do eixo X ou Y for maior que um limite especificado, ele envia um '1' via serial para o PC host, onde é repassado para o display. O Nano faz uma leitura do acelerômetro X e Y em um intervalo definido e verifica se os valores estão acima ou abaixo de 1,5g. Para acessar o stick I2C 9 DoF, você deve instalar a biblioteca Sparkfun que pode ser baixada aqui. Há também um tutorial sobre como conectar o sensor e rodar programas básicos aqui.
Para construir a raquete, usei uma caixa e cortei um formato de raquete em um pedaço de papelão. A partir daí foi fácil: coloquei um papel branco sobre o recorte de papelão e desenhei um design sobre o papel. Usei uma bateria 9V para alimentar o setup, apenas certifique-se de passar a bateria pelo pino Vin no Nano, senão será 9V não regulado! Aqui está uma foto dos componentes internos dentro da raquete:

Comunicação via Bluetooth
Agora você pode estar se perguntando: o que controla esta operação complexa? Como a raquete fala com o display? Aqui está um segredo: eles não falam entre si! Há um script Python simples que usa a porta COM Bluetooth de entrada e a porta COM da matriz para retransmitir informações entre eles. Também ajuda a sincronizar os dois sistemas, pois ambos resetam quando as portas seriais inicializam. A raquete envia um '1' para o script Python via Bluetooth, e o script Python envia um '1' para a matriz. Há um debounce de 1 segundo para que os buffers seriais não transbordem (eles só têm buffers de 32 bits).
A Raquete
Clique nas imagens para ver o passo a passo de como construí a raquete:






Divirta-se com seu novo jogo de tênis!
Um vídeo do tênis sendo jogado:
Código
Código da Matriz
#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();
}
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;
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;
}
Código da Raquete
#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);
}
}
Código Python
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)
Esquemáticos


Processo de manufatura
- Detector de Idiomas TinyML com Edge Impulse no Arduino Nano 33 BLE Sense
- Construa um Jogo em Tempo Real com Giroscópio: Arduino Nano + Sensor MPU-6050
- Dados Digital com Arduino: Gere Números Aleatórios 1-6 em LCD 16x2
- Construa um Homem de Ferro com Arduino: Componentes, Sensores e Guia Passo a Passo
- Find Me: Localizador Inteligente de Objetos com Arduino e Bluetooth
- Controlador de Umidificador DIY com Arduino e Relé – Configuração Segura de Alta Tensão
- Medição de Distância em Tempo Real com Sonar Arduino e Visualização no Processing
- MobBob: Construa Seu Próprio Robô Arduino, Controlado via Smartphone Android
- Lâmpada de Mesa Reativa a Áudio com Arduino Nano
- Jogo Pong Neon em Matriz NeoPixel com Arduino Nano