虚拟 Arduino 网球
组件和用品
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
必要的工具和机器
|
应用和在线服务
|
关于这个项目
这个项目有 3 个部分:游戏板/显示器、控制器和主路由器(Python 脚本)。
第 1 部分 - 显示
显示器是一个 15x10 的新像素矩阵,由 Arduino Nano 驱动。它处理游戏逻辑和来自主机运行的 Python 脚本的输入。它显示一个球,该球在矩阵中进一步向下生长和移动 - 给人一种深度和球向您袭来的错觉。如果它检测到球被击中,球将被重新绘制,远离您。
积分
每次击球时,球的方向(向左或向右)都会改变。如果您错过了球,COM 会得到一分,但是如果 COM 未命中(25% 的机会),您就会得到一分。这个游戏设计类似于Wii Sports版的网球。每当 COM 对玩家得分时,就会显示分数。
高分
在 I2C EEPROM 中的 0x04 地址中存储了一个正在运行的高分。它保留一个保存高分的字节。如果您的得分高于之前的高分,它会覆盖该地址。如果 COM 得到 10 分,则游戏结束……如果这令人困惑,我制作了一个逻辑图:
制作矩阵
制作矩阵需要一些时间,所以请按照我在这里制作的指南进行操作。
图>
第 2 部分 - 控制器
控制器是另一个 Arduino Nano,它有一个 9DoF 棒(仅限 3v3!)和一个 HC-05 BLE 模块连接到它。如果 x 或 y 轴的加速度大于指定的阈值,它会通过串行向主机 PC 发送“1”,然后将其中继到显示器。 Nano 以设定的时间间隔读取 x 和 y 加速度计的读数,然后检查值是否高于或低于 1.5 克。为了访问 I2C 9 DoF 棒,您必须安装可在此处下载的 Sparkfun 库。这里还有一个关于如何连接传感器并使用它运行基本程序的教程。
为了制作球拍,我使用了一个盒子,然后在一块纸板上剪出一个球拍形状。从那里开始很容易 - 只需在纸板切口上放一张白纸,然后在纸上画一个设计。我使用 9v 电池为设置供电,只需确保通过 Nano 上的 Vin 引脚运行电池,否则它将是一个未调节的 9v!这是球拍内部的图片:
通过蓝牙通信
所以现在您可能想知道,是什么控制了这个复杂的操作?球拍如何与显示器对话?这是一个秘密:他们不互相交谈!有一个简单的 Python 脚本,它使用传入的蓝牙 COM 端口和矩阵 COM 端口在它们之间中继信息。它还有助于同步两个系统,因为它们都在串行端口初始化时重置。球拍通过蓝牙向 Python 脚本发送“1”,然后 Python 脚本向矩阵发送“1”。有 1 秒的去抖动,因此串行端口不会使其缓冲区溢出(它们只有 32 位缓冲区)。
球拍
点击图片了解我是如何制作球拍的:
图> 图> 图> 图> 图>玩你的新网球游戏!
正在打网球的视频:
代码
- 矩阵码
- 球拍代码
- Python 代码
矩阵代码C/C++
继续连接到矩阵的 Arduino。#include#include #include #include #include #define PIN 6 //矩阵数据引脚#define EEPROM_ADR 0x50 //EEPROM的I2C地址#define HS_ADR 0x02 //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; //球的半径int r_incr =1; //bool 增加多少半径 isSwung =false; //球拍是不是球拍ball_x_dir =1; //球的X方向int ball_y_dir =1; //球的Y方向bool isDirRight =true;int score =0; //当前游戏的得分int COM_score =0;int highscore =0; //所有游戏的最高分int framerate =50; //每帧之间有多少毫秒int serial_data;String score_string ="";uint16_t colours[] ={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);矩阵开始();矩阵.fillScreen(0);矩阵.setTextColor(颜色[1]);随机种子(模拟读取(A2)); display_scores();高分 =read_HS(); Serial.println(highscore,DEC);矩阵.setCursor(0,1); matrix.print("HS:");矩阵.show();延迟(1000);矩阵.fillScreen(0);矩阵.setCursor(0,1); matrix.print(highscore,DEC);矩阵.show();延迟(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 =随机(4); Serial.println(randNum); if(randNum ==2){ //25% 的 COM 缺失分数 +=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;矩阵.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;球_x =7;球_y =2;半径 =1; display_scores();矩阵.fillScreen(0); matrix.fillCircle(ball_x,ball_y,radius,colors[2]);矩阵.show(); }void end_game(){ matrix.fillScreen(0);矩阵.setCursor(0,1);矩阵.setTextColor(颜色[0]);矩阵.drawLine(3,0,12,9,colors[0]);矩阵.drawLine(11,0,2,9,colors[0]);矩阵.show();延迟(500); while(1){ }}void display_scores(){ matrix.fillScreen(0);矩阵.setTextColor(颜色[1]);矩阵.setCursor(0,1); score_string =String(score) + "-" + String(COM_score);滚动文本(分数字符串);矩阵.fillScreen(0);延迟(2000); matrix.show();}void scrollText(String text){ int pass =0; int x =matrix.width(); for(int i=0;i<24;i++){ matrix.fillScreen(0);矩阵.setCursor(x,2);矩阵打印(文本); x -=1;矩阵.show();延迟(150); }}void write_HS(){ EEPROM.write(0x04,int(score));}int read_HS(){ byte HS =EEPROM.read(0x04); //从地址4读取返回HS;}
球拍代码C/C++
#include#include #include LSM9DS1 imu;#define LSM9DS1_M 0x1E // 如果 SDO_M 为低,则为 0x1C #define LSM9DS1_AG 0x6B // 如果为 0x6A SDO_AG 是 LOW#define PRINT_SPEED 10 // 10 ms 检查静态 unsigned long lastPrint =0; // 跟踪打印时间float accelx =0;float accelx =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) =1.5){ Serial.print(1);延迟(600); } else if(accely <=-1.5 || accely>=1.5){ Serial.print(1);延迟(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)
示意图
制造工艺