Arduino 控制弹球机
组件和用品
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
关于这个项目
Arduino 控制弹球机
构建 Arduino 控制的弹球机需要结合木工、机械、电子和编程技能。花了大约 6 个月的时间来创建它,这是我构建过的最复杂的东西。这主要是因为我无法找到有关如何构建弹球机的大量信息。
本文档将介绍该机器的制造方式。我会尽量提供足够的参考资料,这样如果你决定自己构建,你应该比我更轻松。与此同时,我不能过分强调测试一切的必要性。在安装到实际机器中之前,我使用了一块备用胶合板来制作每个组件的原型。从长远来看,这将为您省去很多悲伤。

从图片中可以看出,该机采用木柜,并使用市售弹球机更换零件。该机器由单个 Arduino Mega 2560 板控制。如果您想查看机器的运行情况,请转到此链接,或在此处观看:
案例
表壳由在家庭商店购买的橡木面胶合板制成。本文档中的所有尺寸均以英寸为单位。前后面板为 20H x 23W。侧板为 20H x 47L。这些碎片用斜切边缘切割,并使用饼干细木工和木胶连接。袖珍螺丝和胶水也可以。这台机器会受到很多虐待,所以它需要很强大。在将它连接在一起之前,我还从底部 1 英寸处切下了一个 ¾” 的墙裙。不要粘在架子上。它需要浮动以允许膨胀和收缩。搁板用于固定主电源和安装在盖子下的装饰性 LED 灯的电源。比赛场地为 22w x 42L。请注意,它明显比机柜短。这给了我前面大约 3 英寸的空间,可以将电线连接到鳍状肢按钮和 LED 灯。它还为球柱塞机构提供了空间。比赛台面简单地放置在一些小木块上,这些木块用螺丝固定在箱子顶部下方约 4 英寸的侧面。为了获得机器的内脏,我只需打开盖子,取出运动场并将其翻转过来。

表壳和比赛场地都是直角建造的。如该侧视图所示,比赛的球场由腿提供。弹球机的角度可以从 1 度到 7 度不等。角度越高,游戏速度越快。由于我不确定我需要什么角度,我将腿设计为可调节的。腿本身是木制的,在大多数家庭商店都有售。然后我使用螺旋钻在每条腿的末端钻了一个 12 英寸深的孔。在底部,我习惯于使用双组分环氧树脂插入 3/8 英寸的三通螺母。小心不要让任何胶水粘在螺纹上或它会堵塞你钻的孔的地方。然后我将一个 12 英寸 3/8 英寸的螺纹杆插入腿中,并将一个螺母拧到杆上。然后我在末端连接了一个调平脚。我在下面的链接中获得了调平脚和三通螺母。
http://www.mscdirect.com/product/details/40610032
http://www.mscdirect.com/product/details/67300962
桌腿安装板简单地拧到底部架子上,然后拧入桌腿。要调整机器的高度,只需松开螺母,然后将螺纹杆拧入或拧出即可。然后重新拧紧螺母以确保桌子保持牢固。
在图片中,您会注意到一根电线和 USB 电缆。电源线来自我连接到机器背面的电源板。所有东西都插在那里,这样一个开关就可以打开或关闭所有东西。 USB 电缆在那里,这样我就可以重新编程 Arduino 板,而不必移除运动场。
盖子
盖子框架由橡木制成,与橱柜的外形尺寸相同。顶部和侧面宽 1.5 英寸,底部宽 5 英寸。底部的额外宽度用于隐藏比赛场地和机柜之间的间隙。一个墙裙沿着框架件的内边缘运行以固定有机玻璃盖。我用有机玻璃代替玻璃,因为它更轻、更安全、更容易切割。盖子是用袖珍螺丝钉在一起的。我这样做是为了在有机玻璃需要更换的情况下将其拆开。我还在盖子下面安装了一条彩色 LED 灯,用于装饰。最后,我使用钢琴铰链将盖子安装到外壳上,我将其嵌入盖子和外壳中。

阿杜诺
Arduino Mega 2560 基本上做了四件事。它确定何时激活开关,相应地打开或关闭灯,跟踪得分,并在小 LCD 上显示得分和球数。大多数接线非常简单。例如,目标连接到 Arduino 的数字输入之一并接地。使用如下所示的语句配置输入。
pinMode(Pin_Number,INPUT_PULLUP)
当代码检测到输入从高变为低时,它知道目标已被击中,然后打开相应的 LED 弹球灯。
唯一的并发症来自流行保险杠。此游戏中使用的翻转开关和目标均来自 Arduino。 Pop Bumpers 使用单独的 25 伏电源供电,因为它们需要比 Arduino 提供的更多的果汁。 Pop Bumpers 确实有一个开关可以激活它们,但由于电压高,我无法直接读取它。因此,我首先将 Pop Bumper 开关的输出发送到分压器。可以在下面的链接中找到分压器的示例。
分压器
一旦通过分压器,电压就会降低到 5 伏,Arduino 可以处理它。我发现如果我使用模拟输入,我得到的错误读数会减少,因为分压器似乎总是泄漏一些电流。使用模拟输入,我可以检查是否超过了阈值,以便我可以轻松地将信号与噪声分开。
在游戏过程中,如果所有某种类型的设备(目标、防撞杆或翻车)都被激活,我对 Arduino 进行编程,让这些设备的所有灯闪烁,然后增加它们的得分。
我还使用了一个小型力传感器来判断球何时丢失。这使我能够跟踪打出的球数并确定游戏何时结束。游戏结束后,所有灯依次闪烁。
最后用Arduino来显示比分和球数。
随附的接线图显示了每个主要组件中的一个是如何接线的。图表如果都显示的话会很混乱。
电源
为了给弹球组件供电,我使用了 24 伏电源。这个单位为脚蹼和搭档提供了充足的动力。流行保险杠有点弱。您可能希望为这些使用更强的电源。如果你这样做,你将不得不制作自己的分压器,因为我只能找到最大为 25 伏的分压器。可以在此处找到有关如何制作分压器的信息。
我没有将所有东西直接连接到电源,而是将电线从它连接到一对母线,而不是安装到运动场的底部。这使得连接不同的组件变得更加容易。您可以在 Slingshot 部分的第二张图片中看到其中一个母线。它们可以在电器部的家居商店买到。
球射手或柱塞
球射手或柱塞是通过以下链接购买的
http://www.marcospecialties.com/pinball-parts/B-12445
需要在外壳的前部钻一个孔,柱塞才能穿过。然后将其拧入外壳内部。必须非常小心以确保柱塞与比赛场地正确对齐。将直尺放在比赛场地上,并在它碰到箱子内侧的地方做标记。然后上升到足以使柱塞中间的高度等于弹球高度的一半。 (弹球的尺寸为 1 又 1/16 英寸,可在 Pinball 购买。)
比赛场地
运动场由 ¼ 英寸桦木饰面胶合板制成。为了让游戏更具装饰性,我从下面的美国宇航局链接购买了一张图片。
http://hubblesite.org/gallery/printshop/ps35/
我首先用接触粘合剂喷涂胶合板,然后将海报层压在上面。然后我把它翻过来剪成合适的大小。
脚蹼
鳍状肢是通过以下链接购买的。
http://www.pinballlife.com/index.php?p=product&id=172
当您购买大多数弹球组件时,它们往往没有有用的说明。下图所示的鳍状肢接线方式。它来自这个链接。

除了鳍状肢机构,您还需要鳍状肢球棒、鳍状肢开关和按钮。
弹弓
可以在此处找到完整的弹弓组件。您还需要别针和橡皮筋。您可以在同一位置找到它们。还显示了安装在下方的弹弓图片。我应该指出,16 号线用于连接到 24 伏电源的所有组件,22 号线用于连接到 Arduino 板。
第一张图片还显示了鳍状蝙蝠旁边的一些金属导轨。我找不到买导轨的地方,所以我做了它们。我使用了一些在家庭商店中找到的铬钢线。当草坪喷洒杀虫剂时,它被用来标记草坪。我剪断并弯曲了电线。然后我在运动场上钻孔并用环氧树脂将导轨固定到位。


流行保险杠
可在此处购买流行保险杠组件。您还需要开关和盖子。防撞杆通过钻三个孔安装,两个用于金属支撑杆,一个用于触发开关的轴。当球滚入俯视图中的白色圆盘时,它会将轴向下推。这会触发叶片开关,然后激活螺线管。然后螺线管拉下一个金属环,将球向相反的方向发送。


目标、翻转开关和灯
目标可在以下链接中找到。
http://www.marcospecialties.com/pinball-parts/A-14691-5
翻转开关和安装座可以在下面找到。
http://www.actionpinball.com/parts.php?item=A-12687
我使用的灯不再制造,但等效物如下所示。
http://www.actionpinball.com/parts.php?item=LED4555
您还需要如下所示的灯座。
http://www.actionpinball.com/parts.php?item=077-5030-00
所有这些的接线基本相同,并在 Arduino 部分中提到。
总结
一旦开始,您无疑会发现想要添加的其他花里胡哨,但这应该会让您开始。
代码
- Arduino 弹球机代码
Arduino弹球机代码Arduino
允许Arduino控制自制弹球机的游戏玩法const int TxPin =17;长分数 =0;长 OldScore =0;长目标 =1;长流行 =1;长卷 =10;整数目标[8];整数卷[3]; int Pops[4];整数米 =10;整数总和 =0; int Flash =100;整数球 =0;国际我=0;整数射击 =0; int 丢失 =0; int 压力 =1024; #include;SoftwareSerial mySerial =SoftwareSerial(255, TxPin);void setup() { /* 没有 s 的词是通过与设备交互获得的值。 * 使用 s 跟踪与哪些个人交互。 * 需要后者来确定何时全部被击中并且值需要升级 * 并且灯需要关闭。 */ pinMode(TxPin, OUTPUT);数字写入(TxPin,高); mySerial.begin(9600); mySerial.write(12); // 清除 mySerial.write(17); // 打开背光 // 目标输入 pinMode(2,INPUT_PULLUP); pinMode(3,INPUT_PULLUP); pinMode(4,INPUT_PULLUP); pinMode(5,INPUT_PULLUP); pinMode(6,INPUT_PULLUP); pinMode(7,INPUT_PULLUP); pinMode(8,INPUT_PULLUP); pinMode(9,INPUT_PULLUP); //翻转输入 pinMode(10,INPUT_PULLUP); pinMode(11,INPUT_PULLUP); pinMode(12,INPUT_PULLUP); //下球投篮开关 pinMode(15,INPUT_PULLUP); //上球投篮开关pinMode(16,INPUT_PULLUP); //液晶输出pinMode(17,OUTPUT); //目标灯,各自的pinMode(32,OUTPUT); pinMode(33,输出); pinMode(34,输出); pinMode(35,输出); pinMode(36,输出); pinMode(37,输出); pinMode(38,输出); pinMode(39,输出); //翻转灯,各自的pinMode(40,OUTPUT); pinMode(41,输出); pinMode(42,输出); //弹出保险杠灯 pinMode(50,OUTPUT); pinMode(51,输出); pinMode(52,输出); pinMode(53,OUTPUT);}void loop() { // 把你的主要代码放在这里,重复运行: //如果使用下拉电阻,输入引脚在开关打开时为低电平,当开关打开时为高电平开关关闭。 //检查是否命中目标//****** Targets ***** for (int i=0; i<8; i++){ if (digitalRead(i+2) ==LOW){ / /Target 已激活 Targets[i]=1;分数 =分数 + 目标; //打开目标灯 digitalWrite(i+32,HIGH); //延迟,以免一次命中延迟(Milli)获得多分;休息;总和 =0; for (int i=0; i<8; i++){ Sum =Sum + Targets[i]; } if (Sum ==8){ //所有目标都点亮,所以闪烁然后关闭。 for (int j=0; j<3; j++){ for (int i=0; i<8; i++){ digitalWrite(i+32, LOW);延迟(闪光); for (int i=0; i<8; i++){ digitalWrite(i+32, HIGH);延迟(闪光); } for (int i=0; i<8; i++){ digitalWrite(i+32, LOW);目标[i]=0;延迟(闪光); //将目标值乘以10 Target =Target * 5; //转到跳过; } // *********** 翻转 ********* for (int i=0; i<3; i++){ if (digitalRead(i+10) ==LOW) { //翻转激活Rolls[i]=1;分数 =分数 + 滚动; //打开翻转灯 digitalWrite(i+40,HIGH); //延迟,以免一次命中延迟(Milli)获得多分;休息;总和 =0; for (int i=0; i<3; i++){ Sum =Sum + Rolls[i]; } if (Sum ==3){ //所有翻转都点亮,所以闪烁然后关闭。 for (int j=0; j<3; j++){ for (int i=0; i<3; i++){ digitalWrite(i+40, LOW);延迟(闪光); for (int i=0; i<3; i++){ digitalWrite(i+40, HIGH);延迟(闪光); } for (int i=0; i<3; i++){ digitalWrite(i+40, LOW);卷[i]=0;延迟(闪光); //将分数乘以2 Score =Score * 2;滚动 =滚动 * 10; //转到跳过; } //********** 流行保险杠 ********** for (int i=0; i<4; i++){ if (analogRead(i)> 500){ / /pop 激活 Pops[i]=1;分数 =分数 + 流行音乐; //打开保险杠灯 digitalWrite(i+50,HIGH); //延迟以免一次命中获得多个点 //mySerial.print(analogRead(i)); //mySerial.print(" ");延迟(毫);休息;总和 =0; for (int i=0; i<4; i++){ Sum =Sum + Pops[i]; } if (Sum ==4){ //所有弹出式保险杠都点亮,所以闪烁然后关闭。 for (int j=0; j<3; j++){ for (int i=0; i<4; i++){ digitalWrite(i+50, LOW);延迟(闪光); for (int i=0; i<4; i++){ digitalWrite(i+50, HIGH);延迟(闪光); } for (int i=0; i<4; i++){ digitalWrite(i+50, LOW);流行音乐[i]=0;延迟(闪光); //将目标值乘以 10 Pop =Pop * 2; //转到跳过; }Skip://确定球数 if (digitalRead(15) ==LOW){ //球击中下通道开关 //如果还没有这样做,增加球 if (Shot ==0){ //Set Lost =0因为不在压力垫上 Lost =0;压力 =模拟读取(7)+ 20; //设置OldScore以便在LCD上重新打印球值 OldScore =-1;球 =球 + 1;如果(球 ==6){ 球 =1;分数 =0;目标 =1;滚动 =1;流行 =1;射击 =1; } } if (digitalRead(16) ==LOW){ //球击中小巷开关 //如果还没有这样做,增加球 if (Shot ==0){ //设置 Lost =0 因为不在压力垫上 Lost =0;压力 =模拟读取(7)+ 15; //设置OldScore以便在LCD上重新打印球值 OldScore =-1;球 =球 + 1;如果(球 ==6){ 球 =1;分数 =0;目标 =1;滚动 =1;流行 =1;射击 =1; } } if (analogRead(7)> Pressure){ //球在压力垫上 Shot =0; if (Lost ==0){ //mySerial.print(analogRead(7)); //分数 =分数 + 100;丢失 =1; if (Ball ==5){ //Game Over //快速翻转然后关闭。 for (int j=0; j<3; j++){ for (int i=0; i<3; i++){ digitalWrite(i+40, LOW);延迟(闪光); for (int i=0; i<3; i++){ digitalWrite(i+40, HIGH);延迟(闪光); } for (int i=0; i<3; i++){ digitalWrite(i+40, LOW);卷[i]=0; } // flash pop 保险杠然后关闭 for (int j=0; j<3; j++){ for (int i=0; i<4; i++){ digitalWrite(i+50, LOW);延迟(闪光); for (int i=0; i<4; i++){ digitalWrite(i+50, HIGH);延迟(闪光); } for (int i=0; i<4; i++){ digitalWrite(i+50, LOW);流行音乐[i]=0; } //Flash Targets 然后关闭。 for (int j=0; j<3; j++){ for (int i=0; i<8; i++){ digitalWrite(i+32, LOW);延迟(闪光); for (int i=0; i<8; i++){ digitalWrite(i+32, HIGH);延迟(闪光); } for (int i=0; i<8; i++){ digitalWrite(i+32, LOW);目标[i]=0; } mySerial.write(12); // 清除延迟(5); // 需要延迟 mySerial.print(Score); // 第一行 mySerial.write(13); // 表单输入 mySerial.print("Game Over!!!"); // 第二行 } } } //打印到 LCD if (Score !=OldScore){ mySerial.write(12); // 清除延迟(5); // 所需的延迟 //mySerial.print(analogRead(7)); mySerial.print(Score); // 第一行 mySerial.write(13); // 表单输入 mySerial.print("Ball ="); // 第二行 mySerial.print(Ball); OldScore =分数; }}
示意图

制造工艺