久久ER99热精品一区二区-久久精品99国产精品日本-久久精品免费一区二区三区-久久综合九色综合欧美狠狠

新聞中心

EEPW首頁 > 嵌入式系統 > 設計應用 > 基于 Arduino Mega 2560 的全尺寸電動彈珠機設計與實現

基于 Arduino Mega 2560 的全尺寸電動彈珠機設計與實現

作者: 時間:2025-11-21 來源: 收藏

Arduino Controlled Pinball Machine

摘要

本文介紹了一臺以 Arduino Mega 2560 為核心控制器的全尺寸電動彈珠機(Pinball Machine)。整機采用木制機柜結構,配合市售彈珠機標準配件(彈板、彈簧、擋片、Pop Bumper 等),通過 Arduino 實現 得分邏輯、燈光控制、目標判定以及游戲流程管理。項目從機械結構、電子系統到軟件邏輯均由作者自行設計與實現,歷時約六個月間斷完成,是一個集木工、機械設計、電力電子與嵌入式編程于一體的復雜系統工程。

文中詳細說明機柜結構尺寸、可調節機腳設計、彈珠臺傾角控制、24V 電源與 5V 邏輯電路隔離、Pop Bumper 高壓檢測的電壓分壓方案,以及基于 Arduino Mega 的得分與游戲邏輯實現方法,可為有意自制彈珠機的工程愛好者提供系統參考。


1 項目概述

作者在項目開始時,幾乎找不到完整的“如何從零自制彈珠機”的資料,因此采用了如下策略:

  • 使用 備用膠合板 作為原型試驗平臺:
    每個機構(Flipper、Slingshot、Pop Bumper、目標靶等)都先在測試板上安裝與調試,待可靠后才移植到正式機臺。

  • 采用 標準彈珠機配件 + 自制木柜 的混合方式:
    機柜、上蓋、內部支撐結構自己木工制作;彈板、Pop Bumper、目標靶等則盡量使用成熟的替換件。

  • 邏輯控制全部交給 Arduino Mega 2560
    使用其豐富的數字與模擬 I/O,既負責采集各類開關信號,又負責控制燈光與得分顯示。

結果是一臺結構接近商用尺寸的彈珠機,但游戲規則與燈光邏輯完全可編程。


2 機械結構設計

2.1 機柜結構

機柜采用 橡木貼面膠合板 制作,基本尺寸(單位:英寸)如下:

  • 前后面板:20 高 × 23 寬

  • 側板:20 高 × 47 長

  • 臺面(Playfield):22 寬 × 42 長

設計要點:

  1. 結構連接方式

    • 板材采用斜接(miter)切割,使用餅干榫(biscuit joiner)與木膠拼接;

    • 也可用暗扣螺絲 + 木膠組合,保證強度——彈珠機在游戲中會承受頻繁沖擊。

  2. 底部擱板與電源布置

    • 不與側板膠合,以適應木材膨脹收縮;

    • 用于放置主電源、LED 燈電源等模塊。

    • 在底部上方 1 英寸位置開 ? 英寸寬的榫槽(dado),做一個“浮動”擱板:

  3. 臺面與前部空腔

    • 前方預留約 3 英寸空間,用于布線、安裝彈簧拉桿(Plunger)和前部燈光等;

    • Playfield 長度 42 英寸,明顯短于機柜 47 英寸:

    • 臺面通過側板上的小木塊支撐,距離上沿約 4 英寸;

    • 維護時,只需打開上蓋,將臺面整體抬出并翻轉即可檢修底面機構與線束。

2.2 傾角與機腳設計

彈珠機的“節奏”很大程度取決于臺面的傾角,通常在 1°–7° 之間。角度越大,球速越快,游戲越刺激。

為方便調節傾角,作者自制了可調機構的木質機腳

  • 使用市售木質桌腿,底部鉆入約 12 英寸深孔;

  • 在孔底用雙組份環氧膠固定 3/8 英寸 Tee Nut(內螺母),注意避免膠體污染螺紋;

  • 將 12 英寸長的 3/8 英寸螺紋桿旋入腿內,末端安裝可調水平腳(Leveling Foot),中間加鎖緊螺母;

  • 櫥柜底部通過金屬腿板(Table Leg Mounting Plate)與木腿連接。

調節方法:

  • 松開鎖緊螺母 → 旋入 / 旋出螺桿以升降臺面 → 鎖緊螺母固定。

2.3 上蓋與透明面板

上蓋(Lid)與機柜同外形尺寸,材質為橡木框架:

  • 上側與兩邊框寬約 1.5 英寸;

  • 底邊加寬至 5 英寸,用于遮擋臺面與機柜間的間隙;

  • 內側開槽嵌入 亞克力板(Plexiglas)

    • 相比玻璃更輕、更安全,也更易切割;

  • 采用暗扣螺絲(Pocket Screw)裝配,便于今后更換亞克力板;

  • 內側安裝一圈彩色 LED 燈帶,用于裝飾;

  • 上蓋與機柜通過 鋼琴合頁(Piano Hinge) 鉸接,并在兩側開槽嵌入,提升外觀與強度。

機柜背面還固定一條 電源插排,所有電源統一插入,便于通過一個總開關控制整機上電;同時預留一條 USB 延長線,方便在不拆臺面的前提下更新 Arduino 程序。


3 控制與電子系統

3.1 Arduino 控制邏輯概述

整機由一塊 Arduino Mega 2560 控制,主要承擔四大任務:

  1. 采集開關與傳感器狀態

    • 目標靶(Target Switch)

    • 滾輪開關(Rollover Switch)

    • Pop Bumper 開關(通過電壓分壓)

    • 彈球發射道開關

    • 壓力傳感器(用于監測丟球)

  2. 控制燈光與效果

    • 目標靶對應燈

    • 滾輪燈

    • Pop Bumper 燈

    • Game Over 時全場閃爍效果

  3. 得分與規則管理

    • 各類元件命中得分;

    • 所有同類元件被依次命中后,觸發燈光全閃與分值提升(倍乘);

    • 記錄當前球數、是否 Game Over 等。

  4. LCD 顯示

    • 當前得分(Score)

    • 當前球號(Ball)

    • 使用一個簡單串口 LCD 顯示:

3.2 開關輸入與上拉配置

大部分目標開關采用“輸入 + 內部上拉”的方式接入 Arduino:

pinMode(pinNumber, INPUT_PULLUP);

接線方式:

  • 一端接 Arduino 數字引腳;

  • 另一端接 GND;

  • 開關未觸發時引腳為 HIGH(被上拉);

  • 開關閉合(被彈珠撞擊)時,引腳被拉低為 LOW。

在代碼中,檢測到輸入由 HIGH → LOW,即認為該目標被擊中。

3.3 Pop Bumper 與高壓檢測:電壓分壓

與普通目標與滾輪使用 Arduino 5V 供電不同,Pop Bumper 需要強勁的沖擊力,因此采用獨立的 25V–24V 電源 驅動線圈。Pop Bumper 的“觸發開關”處于高壓側,不能直接接入 Arduino。

解決方案:

  • 將 Pop Bumper 開關輸出接入 電壓分壓電路

  • 通過適當的電阻比,將高壓側信號衰減到不超過 5V;

  • 分壓后的信號接入 Arduino 的 模擬輸入端口(Analog Input)

原因:

  • 即使在未觸發情況下,分壓器也會漏少量電流,導致信號有一定電壓噪聲;

  • 使用模擬口可以設置閾值,如 analogRead(i) > 500,更容易區分“真觸發”與“背景泄漏”。

3.4 球的丟失檢測:力傳感器

系統還使用一個小型 力/壓力傳感器(Force Sensor),通常布置在“落球區”或回收通道。當球掉回底部時,會壓到傳感器:

  • 用力傳感器讀數作為“丟球”判定依據;

  • 同時配合發射道開關(上、下發射口),區分“球剛被射出”與“球已丟失”;

  • 每次檢測到丟球,球數(Ball)+1;

  • 當 Ball 達到最大值(如 5 球),觸發 Game Over 流程。

3.5 分數與效果邏輯(來自示例代碼)

原文給出了完整的 Arduino 控制代碼,核心思想如下:

  1. 變量設計

    • Score:當前得分

    • Target, Pop, Roll:三類目標的基礎得分值

    • Targets[8], Rolls[3], Pops[4]:記錄每個目標是否被命中

    • Ball:當前球號(1–5)

    • Flash:燈光閃爍延時

    • Pressure:力傳感器觸發閾值

  2. 目標靶(Targets)處理流程

    • 所有目標燈閃爍若干次;

    • 燈全部熄滅;

    • 增加 Target 的分值(Target = Target * 5),提高之后的得分獎勵。

    • 循環掃描 8 個目標輸入(數字引腳 2–9);

    • 某個為 LOW → 標記該 Target 已命中、增加 Score += Target、點亮對應燈;

    • 若 8 個目標全部命中(Sum == 8):

  3. Rollovers 處理

    • 對應燈全閃;

    • 分值倍乘并清零標記(代碼中示例為 Score = Score * 2, Roll = Roll * 10)。

    • 類似邏輯,掃描 3 個 Rollover 開關;

    • 全部命中后:

  4. Pop Bumper 處理

    • 所有 Pop 燈閃爍;

    • 分值提升(Pop = Pop * 2),形成累進獎勵。

    • 標記該 Pop;

    • 增加 Score += Pop

    • 點亮對應燈;

    • 使用 analogRead(i) 讀取 4 個模擬輸入;

    • 若某一路超過閾值(>500),認為 Pop Bumper 被觸發:

    • 當 4 個 Pop 全部被觸發后:

  5. 球數與 Game Over 判定

    • 若在某次檢測中讀數持續高于 PressureBall == 5,則:

    • 依次閃爍 Rollovers、Pop Bumpers 與 Targets 所有燈;

    • 在 LCD 顯示“Game Over!!!”。

    • 若這是本球首次檢測到,Ball++,并設置 Shot 標記;

    • 超出設定最大球數(如 Ball == 6)時,清零分數與倍率參數重新開始。

    • 通過發射通道下方兩個開關(下、上射出),當球通過時:

    • 通過力傳感器讀數判斷球是否落入最終回收區:

  6. LCD 顯示輸出

    • 清屏 → 打印當前得分 → 換行顯示 Ball = x

    • 通過 SoftwareSerial 驅動串口 LCD;

    • Score 發生變化時:

    • TxPin 引腳用于向 LCD 發送串口數據。


4 電源與配線

4.1 24V 電源系統

彈珠機中的電磁組件(彈板、Slingshot、Pop Bumper 等)需較大的瞬時電流,故使用 24V 開關電源

  • 24V 電源主要供給:

    • 彈板線圈(Flipper Assembly)

    • Slingshot 組件

    • Pop Bumper 線圈

  • 24V 輸出先接至安裝在臺面底部的 配電銅排(Bus Bar),再由銅排為各組件分配電源,這樣布線更整齊、維護方便。

對于 Pop Bumper 等高壓部分,需要特別注意:

  • 若改用更高電壓電源(>25V),則現成的電壓分壓模塊可能不適用,需自行設計分壓電路,確保 Arduino 端電壓不超過 5V。

4.2 線徑選擇

根據原文經驗:

  • 接 24V 高壓線圈類負載:約 16 AWG 線材(粗線,承載大電流);

  • 接 Arduino I/O 與傳感器信號:約 22 AWG 細線即可。


5 關鍵彈珠機構與配件

5.1 彈簧拉桿(Plunger)

Plunger 組件為標準件,從專業彈珠配件商處購入。安裝流程:

  • 在機柜前板上鉆孔,使 Plunger 穿出前面板;

  • 內部用螺絲固定;

  • 高度對齊:

    • 將直尺沿臺面延伸至前內側板;

    • 在該點處標記 Plunger 的中心位置;

    • 垂直方向上使 Plunger 中心略高于彈珠直徑的一半(標準彈珠為 1 1/16 英寸)。

5.2 臺面與裝飾

臺面使用 1/4 英寸的樺木貼面膠合板。為提升視覺效果,作者選取了一張 NASA 的太空照片作為背景:

  1. 在板面噴涂噴膠(Contact Adhesive);

  2. 將海報覆于板面,刮平氣泡;

  3. 背面修剪多余邊緣,使整體尺寸精確符合 22W × 42L。

5.3 Flippers(彈板)

彈板組件包括:

  • Flipper 機械總成

  • Flipper Bat(塑料擋板)

  • Flipper Switch(高壓開關)

  • 側面按鈕

購買后通常沒有任何接線說明,需要參考彈珠維修資料中的典型接線方式(原文鏈接中給出示意圖)。核心要點:

  • 線圈通常包含高阻/低阻兩組繞組,用于啟動與保持;

  • 開關結構在按鈕與線圈之間,配合機械角度限制,實現可靠回彈。

5.4 Slingshots(側擊機構)

Slingshot 組件為彈珠經過時會被“側推”的機構:

  • 需要完整的 Slingshot Assembly、支撐銷與專用橡膠圈;

  • 安裝在 Flipper 上方兩側,形成典型的下方區域拱形布局;

  • 底部布線同樣接至 24V 總線與 Arduino 觸發信號。

作者還自己制作了 金屬防護欄(Rails)

  • 使用家居店購買的鍍鉻鋼絲(原用途為草坪標記線);

  • 按需切割、彎折成形;

  • 在臺面鉆孔后,用環氧膠固定。

5.5 Pop Bumpers

Pop Bumper 由底座、線圈、頂蓋、支撐桿與觸發軸組成:

  • 從上方看為一個白色圓盤;

  • 當球撞入圓盤時,盤面被壓下,帶動中心軸下移;

  • 軸底部觸發葉片開關,進而驅動線圈通電;

  • 線圈帶動金屬環向下拉,向上反彈時將球彈回。

安裝時需在臺面鉆三孔:

  • 兩個用于支撐桿固定;

  • 一個用于中心軸通過與觸發葉片開關。

5.6 目標靶(Targets)、Rollovers 與燈具

  • 目標靶:立式靶板,球撞擊后觸發后方微動開關;

  • Rollover Switch:嵌在臺面上的小金屬片或塑料結構,球滾過即觸發;

  • LED Lamp:用于標記目標狀態與裝飾。

接線方式統一:

  • 一端接 Arduino 輸入(或輸出控制燈);

  • 一端接地或電源;

  • 通過 Mega 2560 的數字口進行采集與控制。


6 總結與展望

本文系統介紹了一臺 Arduino 控制的自制全尺寸彈珠機 的設計實現過程。項目的特點在于:

  • 結構工程

    • 采用標準尺寸木柜結構,使用可調機腳控制傾角;

    • 臺面可整體抬出翻轉,便于維護與調試。

  • 電子與電源架構

    • 使用 24V 高壓系統驅動線圈部件;

    • 使用電壓分壓將 Pop Bumper 的高壓信號安全地引入 Arduino;

    • 將高壓總線分發至配電銅排,提高布線整潔性。

  • 邏輯控制與玩法擴展

    • 基于 Mega 2560 的多路數字與模擬輸入輸出,實現完整得分、倍分、全亮閃爍、Game Over 等玩法;

    • 利用力傳感器判斷丟球,使游戲流程自動化;

    • LCD 顯示當前得分與球號,增強交互性。

作者也指出,一旦開始搭建,你很可能會不斷添加新元素(聲效、更多燈效、獎勵關卡等),但有了本文所述的結構與控制架構,后續擴展將更加容易。

代碼:

  const int TxPin = 17;

  long Score = 0;

  long OldScore = 0;

  long Target = 1;

  long Pop = 1;

  long Roll = 10;

  int Targets[8];

  int Rolls[3];

  int Pops[4];

  int Milli = 10;

  int Sum = 0;

  int Flash = 100;

  int Ball = 0;

  int i=0;

  int Shot = 0;

  int Lost = 0;

  int Pressure = 1024;

  

#include <SoftwareSerial.h>;

SoftwareSerial mySerial = SoftwareSerial(255, TxPin);



void setup() {

  /* Words without an s are the value achieved by interacting with a device. 

   * Works with an s keep track of which individual ones were interacted with. 

   * The latter is needed to determine when all have been hit and the value needs upgrading

   * and the lights need turning off.

   */

  pinMode(TxPin, OUTPUT);

  digitalWrite(TxPin, HIGH);

  mySerial.begin(9600);

  mySerial.write(12);                 // Clear             

  mySerial.write(17);                 // Turn backlight on

  

  //target inputs

  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);

  //rollover inputs

  pinMode(10,INPUT_PULLUP);

  pinMode(11,INPUT_PULLUP);

  pinMode(12,INPUT_PULLUP);

  //lower ball shot switch

  pinMode(15,INPUT_PULLUP);

  //upper ball shot switch

  pinMode(16,INPUT_PULLUP);

  //lcd output

  pinMode(17,OUTPUT);

  //target lights, respective

  pinMode(32,OUTPUT);

  pinMode(33,OUTPUT);

  pinMode(34,OUTPUT);

  pinMode(35,OUTPUT);

  pinMode(36,OUTPUT);

  pinMode(37,OUTPUT);

  pinMode(38,OUTPUT);

  pinMode(39,OUTPUT);

  //rollover lights, respective

  pinMode(40,OUTPUT);

  pinMode(41,OUTPUT);

  pinMode(42,OUTPUT);

  //pop bumper lights

  pinMode(50,OUTPUT);

  pinMode(51,OUTPUT);

  pinMode(52,OUTPUT);

  pinMode(53,OUTPUT);

}


void loop() {

  // put your main code here, to run repeatedly:

  //If a pull-down resistor is used, the input pin will be LOW when the switch is open and HIGH when the switch is closed. 

  //check if a target was hit


//****** Targets *****


  for (int i=0; i<8; i++){

    if (digitalRead(i+2) == LOW){

      //Target activated

      Targets[i]=1;

      Score = Score + Target;

      //turn on Target light

      digitalWrite(i+32,HIGH);

      //delay so as not get multiple points for one hit

      delay(Milli);

      break;

    }

  }

  Sum = 0;  

  for (int i=0; i<8; i++){

    Sum = Sum + Targets[i];

  }

  if (Sum == 8){

    //all Targets lit, so flash and then turn off.

    for (int j=0; j<3; j++){

      for (int i=0; i<8; i++){

        digitalWrite(i+32, LOW);

      }

      delay(Flash);

      for (int i=0; i<8; i++){

        digitalWrite(i+32, HIGH);

      }

      delay(Flash);

    }

    for (int i=0; i<8; i++){

      digitalWrite(i+32, LOW);

      Targets[i]=0;

    } 

    delay(Flash);   

    //Multiply target value by 10

    Target = Target * 5;

    //goto Skip;  

  }

  


// ***********  Rollovers *********


  

   for (int i=0; i<3; i++){

    if (digitalRead(i+10) == LOW){

      //rollover activated

      Rolls[i]=1;

      Score = Score + Roll;

      //turn on rollover light

      digitalWrite(i+40,HIGH);

      //delay so as not get multiple points for one hit

      delay(Milli);

      break;

    }

  }

  Sum = 0;  

  for (int i=0; i<3; i++){

    Sum = Sum + Rolls[i];

  }

  if (Sum == 3){

    //all rollovers lit, so flash and then turn off.

    for (int j=0; j<3; j++){

      for (int i=0; i<3; i++){

        digitalWrite(i+40, LOW);

      }

      delay(Flash);

      for (int i=0; i<3; i++){

        digitalWrite(i+40, HIGH);

      }

      delay(Flash);

    }

    for (int i=0; i<3; i++){

      digitalWrite(i+40, LOW);

      Rolls[i]=0;

    } 

    delay(Flash);   

    //Multiply score by 2

    Score = Score * 2;

    Roll = Roll * 10;

    //goto Skip;  

  }

  

  //**********  Pop Bumpers **********

  

   for (int i=0; i<4; i++){

    if (analogRead(i) > 500){

      //pop activated

      Pops[i]=1;

      Score = Score + Pop;

      //turn on pop bumper light

      digitalWrite(i+50,HIGH);

      //delay so as not get multiple points for one hit

      //mySerial.print(analogRead(i));

      //mySerial.print(" ");

      delay(Milli);

      break;

    }

  }

  Sum = 0;  

  for (int i=0; i<4; i++){

    Sum = Sum + Pops[i];

  }

  if (Sum == 4){

    //all pop bumpers lit, so flash and then turn off.

    for (int j=0; j<3; j++){

      for (int i=0; i<4; i++){

        digitalWrite(i+50, LOW);

      }

      delay(Flash);

      for (int i=0; i<4; i++){

        digitalWrite(i+50, HIGH);

      }

      delay(Flash);

    }

    for (int i=0; i<4; i++){

      digitalWrite(i+50, LOW);

      Pops[i]=0;

    } 

    delay(Flash);   

    //Multiply target value by 10

    Pop = Pop * 2;

    //goto Skip;  

  }

Skip:

  

  //Determine ball number

  if (digitalRead(15) == LOW){

    //ball hit lower alley switch

    //if not already done so, increase Ball 

    if (Shot == 0){

      //Set Lost = 0 since not on pressure pad

      Lost = 0;

      Pressure = analogRead(7) + 20;

      //set OldScore so as to reprint ball value on LCD

      OldScore =-1;

      Ball = Ball + 1;

      if (Ball == 6){

        Ball = 1;

        Score = 0;

        Target = 1;

        Roll = 1;

        Pop = 1;

      }

      Shot = 1;

    }

  }

  if (digitalRead(16) == LOW){

    //ball hit lower alley switch

    //if not already done so, increase Ball

    if (Shot == 0){

      //Set Lost = 0 since not on pressure pad

      Lost = 0;

      Pressure = analogRead(7) + 15;  

      //set OldScore so as to reprint ball value on LCD

      OldScore =-1;

      Ball = Ball + 1;

      if (Ball == 6){

        Ball = 1;

        Score = 0;

        Target = 1;

        Roll = 1;

        Pop = 1;

      }

      Shot = 1;

    }

  }


  if (analogRead(7) > Pressure){

    //ball on pressure pad

    Shot = 0;

    if (Lost == 0){

      //mySerial.print(analogRead(7));

      //Score = Score + 100;

      Lost = 1;

      if (Ball == 5){

        //Game Over

        //flash rollovers and then turn off.

        for (int j=0; j<3; j++){

          for (int i=0; i<3; i++){

            digitalWrite(i+40, LOW);

          }

          delay(Flash);

          for (int i=0; i<3; i++){

            digitalWrite(i+40, HIGH);

          }

          delay(Flash);

        }

        for (int i=0; i<3; i++){

          digitalWrite(i+40, LOW);

          Rolls[i]=0;

        } 

        // flash pop bumpers and then turn off

        for (int j=0; j<3; j++){

          for (int i=0; i<4; i++){

            digitalWrite(i+50, LOW);

          }

          delay(Flash);

          for (int i=0; i<4; i++){

            digitalWrite(i+50, HIGH);

          }

          delay(Flash);

        }

        for (int i=0; i<4; i++){

          digitalWrite(i+50, LOW);

          Pops[i]=0;

        } 

        //Flash Targets and then turn off.

        for (int j=0; j<3; j++){

          for (int i=0; i<8; i++){

            digitalWrite(i+32, LOW);

          }

          delay(Flash);

          for (int i=0; i<8; i++){

            digitalWrite(i+32, HIGH);

          }

          delay(Flash);

        }

        for (int i=0; i<8; i++){

          digitalWrite(i+32, LOW);

          Targets[i]=0;

        } 

        mySerial.write(12);                 // Clear

        delay(5);

        // Required delay

        mySerial.print(Score);  // First line

        mySerial.write(13);                 // Form feed

        mySerial.print("Game Over!!!");   // Second line

      }

    }

  }

  //print to LCD

  if (Score != OldScore){ 

  mySerial.write(12);                 // Clear

  delay(5);                           // Required delay

  //mySerial.print(analogRead(7));

  mySerial.print(Score);  // First line

  mySerial.write(13);                 // Form feed

  mySerial.print("Ball = ");   // Second line

  mySerial.print(Ball);

  OldScore = Score;

  }

}



關鍵詞:

評論


相關推薦

技術專區

關閉