Project tutorial
Pinball ARDUINO Conversion (another one)

Pinball ARDUINO Conversion (another one) © GPL3+

A KISS solution maximising OTS components minimising wiring, designing and making one off PCB, all parts available from multiple suppliers.

  • 578 views
  • 4 comments
  • 1 respect

Components and supplies

WS2812 strip leds
12 volt version
×1
Ph a000067 iso (1) wzec989qrf
Arduino Mega 2560
×1
Ph a000066 iso (1) ztbmubhmho
Arduino UNO
×2
fanless pc
×1
mosfet shield
Freetronics
×2
PC and LED Tv
×1
Pinball playfield
×1
PSU's
Switching PSU 150 watts
×2
Arduino screw shield
×2

About this project

This a followon project from one I did to convert a bally bingo to an arduino solution. The basic idea was to have a single solution and be able to use 3 pinball playfields due to lack of space and did not fancy having AC voltages flying around. I replaced all the electro mechanical relays with a number of arduinos thus low voltage and low current.

Graphics Display

First I created the backglass in high resolution(360 dpi) from stuff I found on the WEB, had it printed professionally so that I could do the backlighting ( still to be completed) and also later to be able to build a backbox cabinet.

The 40" LED TV installed in cabinet (Home Built) displaying my first GOTTLIEB game Flying Carpet.

I used the high speed version sometimes referred to as UF1N4004, these were used because using ARDUINO PWM switching speeds. Did have a few MOSFETs blow with 1N4004 and a home brew driver Board.

This little shield is a key part in my design and is controlled by the ARDUINO. The screw terminals provide 6 outlets for the “Drain” connections of the MOSFETs. Each channel is controlled by a single digital pin (3, 5, 6, 9, 10, 11) and is marked on the shield. NOTE does not include FLYBACK protection unfortunately so they needs to be inserted across the coils and solenoids. See above how I used the JONES Plugs to achieve this neatly.

Code

Main MEGA processorArduino
Arduino Pinball machine
#include <FastLED.h>
// GOTTLIEB EM Pinball with a ARDUINO MEGA
// replacing the EM Stuff and LEDS replacing bulbs
// Written by B J Mclaren
// based on experience of building an ARDUINO based Bally Bingo
// This is currenty for FLYING CARPET but will be able to
// play DUOTRON in future (Next release).
// Special thanks to all involved in ARDUINO Pinballs.
// Most of the code is original except some Bumper stuff 
// This solution used distributed processing with a MEGA
// as the Master controller
// UNO's as Auxillary Controllers for Chimes etc
// an extra UNO when more than 12 solenoids are required (DUOTRON)
// Processing 3(see processing.org)for Backglass (+ Sound if no Chime Unit)
// A PC, (a Fanless PC is powerfull enough) to run the
// software to drive a 40" LCD TV Backglass.
//
// There are 6 channels on each MOSFET Board.
// PWM Pins used by the driver are 3,5,6,9,10,11
// Board 1 ( MASTER ) mounted on MEGA is for 3 POP Bumper
// and Ball delivery + 2 Spares
// a single MOSFET Driver is used for the 100 points LED
// There are 6 channels on MOSFET Board 2
// Board 2 ( SLAVE 1 )mounted on UNO for chimes, Knocker and flippers
// Board 3 ( SLAVE 2 )future expansion eg DUOTRON
// Board 1 utilises a 12 Volt PSU
// Board 2 utilises a 15 Volt PSU
// A total of 18 solenoid Drivers can be provided
// Comms to Slave 1 and Slave 2 via RS232
// later versions may use I2C (so reserve A4 and A5)
//
// Switch and Targets ARDUINO Pins
#define Fswitch 36
#define Lswitch 37
#define Yswitch 38
#define Iswitch 39
#define Nswitch 40
#define Gswitch 41
//
#define Cswitch 42
#define Aswitch 43
#define Rswitch 44
#define Pswitch 45
#define Eswitch 46
#define Tswitch 47
// Misc Switches and Buttons
#define Redbutton 27
#define Yellbutton 28
#define Tilt 34
#define Slingshot 35              //ALL score slingshots are in parallel
//
#define B1_Bumper_Switch 29
#define B1_MOSFET_Driver 5
//
#define B2_Bumper_Switch 31
#define B2_MOSFET_Driver 3
//
#define B3_Bumper_Switch 30
#define B3_MOSFET_Driver 6
//
#define Deliver_Ball_MOSFET_Driver 9
//
#define BlueLed_MOSFET_Driver 4     // Lit when 100 points
#define Spare1_MOSFET_Driver 10
#define Spare2_MOSFET_Driver 11
//
// Array for LEDs for playfield
#define NUM_LEDS 70               //Space for up to 70 LEDS 
#define DATA_PIN A0
//
#define Ball_Lights 41            // Offset for Balls played LEDS
#define On_Time 40                // Solenoids ON Time in ms
//
CRGB leds[NUM_LEDS];
// This section defined the index into the LED arrays
// LED's for "FLYING CARPET" in centre of playfield
int FCLamps [13] =
{
  21, 22, 24, 25, 27, 28, 34, 33, 32, 31, 30, 29, 0
};
// switch LED's on Playfield
int FCswitchLamps [13] =
{
  17, 8, 57, 5, 35, 50, 54, 6, 13, 7, 49, 36, 0
};
// Target LED's on Playfield
int FCTargetLamps [13] =
{
  59, 20, 9, 51, 11, 60, 12, 52, 14, 19, 61, 10, 0
};
// Index for special targets
#define SR1 16                     //Special R Target 1
#define SY1 23                     //Special Y Target 1
#define SR2 26                     //Special R Target 2
#define SY2 56                     //Special Y Target 2
//
// 12 slots to indicate "FLYING CARPET" Targets/Rollovers hit
int FC[13];                     // slots 0 and 13 not used
int Balls = 0;
int RandNumber = 0;             // Lucky Matching number
int BallsPerGame = 5;
int GameOver = 1;
int FCcount = 0;                // count of numbers of FC lights ON
int SpecialON = 0;              // Special ON
int BlueBumper = 0;             // Blue Score 100 if set to 1
int Phit = 0;                   // set if P rollover or target hit
int Ahit = 0;                   // set if A rollover or target hit
// if both hit Blue bumper score 100
int Yspecial = 0;
int Rspecial = 0;
int replays = 0;
int units = 0;                  // keep track of units for Lucky Number
int switch_timer = 0;           // determine if multi reports of a target hit
int debounce = 50;              // switch debounce in mills
// Variables for target handling
unsigned long System_Base_Time = 0;
unsigned long Target_Hit_Time = 0;
unsigned long Old_Target_Hit_Time = 0;
unsigned long diff = 0;
// Bumper timing stuff
unsigned long B1_previousMillis = 0;
unsigned long B1_currentMillis = 0;
unsigned long B1_ON_Time = 0;
//
int B1_Coil_Trigger = 0;
int B1_Coil_State_last = LOW;
int B1_Coil_State;
int B1_difference;
//
unsigned long B2_previousMillis = 0;
unsigned long B2_currentMillis = 0;
unsigned long B2_ON_Time = 0;
//
int B2_Coil_Trigger = 0;
int B2_Coil_State_last = LOW;
int B2_Coil_State;
int B2_difference;
//
unsigned long B3_previousMillis = 0;
unsigned long B3_currentMillis = 0;
unsigned long B3_ON_Time = 0;
//
int B3_Coil_Trigger = 0;
int B3_Coil_State_last = LOW;
int B3_Coil_State;
int B3_difference;
//
unsigned long end_time = 0;             // used for timing
unsigned long total_time = 0;
// Max time before switching off bumpers to stop coil burnup
int Max_On_Time = 1000;                 //
void setup()
{
  pinMode(Deliver_Ball_MOSFET_Driver, OUTPUT);
  analogWrite(Deliver_Ball_MOSFET_Driver, 0);
  //
  pinMode(Spare1_MOSFET_Driver, OUTPUT);
  analogWrite(Spare1_MOSFET_Driver, 0);
  //
  pinMode(Spare2_MOSFET_Driver, OUTPUT);
  analogWrite(Spare2_MOSFET_Driver, 0);
  //
  pinMode(BlueLed_MOSFET_Driver, OUTPUT);
  digitalWrite (BlueLed_MOSFET_Driver, 0);
  // Pop Bumpers
  pinMode(B1_Bumper_Switch, INPUT_PULLUP);
  pinMode(B1_MOSFET_Driver, OUTPUT);
  analogWrite(B1_MOSFET_Driver, 0);
  //
  pinMode(B2_Bumper_Switch, INPUT_PULLUP);
  pinMode(B2_MOSFET_Driver, OUTPUT);
  analogWrite(B2_MOSFET_Driver, 0);
  //
  pinMode(B3_Bumper_Switch, INPUT_PULLUP);
  pinMode(B3_MOSFET_Driver, OUTPUT);
  analogWrite(B3_MOSFET_Driver, 0);
  // LEDS
  FastLED.addLeds<WS2811, DATA_PIN,  BRG>(leds, NUM_LEDS);
  FastLED.setBrightness(100);
  FastLED.show();
  // Switches and Targets
  pinMode(Fswitch, INPUT_PULLUP);
  pinMode(Lswitch, INPUT_PULLUP);
  pinMode(Yswitch, INPUT_PULLUP);
  pinMode(Iswitch, INPUT_PULLUP);
  pinMode(Nswitch, INPUT_PULLUP);
  pinMode(Gswitch, INPUT_PULLUP);
  //
  pinMode(Cswitch, INPUT_PULLUP);
  pinMode(Aswitch, INPUT_PULLUP);
  pinMode(Rswitch, INPUT_PULLUP);
  pinMode(Pswitch, INPUT_PULLUP);
  pinMode(Eswitch, INPUT_PULLUP);
  pinMode(Tswitch, INPUT_PULLUP);
  //
  pinMode(Slingshot, INPUT_PULLUP);
  pinMode(Tilt, INPUT_PULLUP);
  // Game Start Stuff
  pinMode(Redbutton, INPUT_PULLUP);
  pinMode(Yellbutton, INPUT_PULLUP);
  //
  Serial.begin   (9600);      // -> Tv Monitor via Processing I3 on PC or PI
  Serial1.begin  (9600);      // chime unit, ball delivery anf flippers
}
//
void Lucky()
// Are we Lucky today?
// match number for replay
// For Fun Only as we are a Free Play Machine
{
  int u = units - (int(units / 10) * 10);
  int r = random (0, 9);
  if (r == u)
  {
    replay();
  }
}
//
void timer ()
// Used to determine if a Target is bouncing causing
// multiple reporting of a hit
// this routine determines the time in milli seconds
// since the last target hit
// Currenty set to 200 ms - unlikely 2 targets can be
// hit within 200ms
{
  Target_Hit_Time = millis();
  diff = Target_Hit_Time - Old_Target_Hit_Time;
  Old_Target_Hit_Time = Target_Hit_Time;
  if (diff < 200 )switch_timer = 1;  // adjust time to suit machine
}
//
void waiton (int pin)
{
  delay (debounce);                 // let switch settle
  while (digitalRead(pin) == 0 )
  {
    delay (debounce);
  }
}// end of waiton
//
void TopRipple(int score)
// routine to light top 5 lights in sequence
// if score then send add 10 command to score
// and chime unit
{
  for (int i = 0; i < 5; i = i + 1)
  {
    if (score == 1 )
    {
      Serial.print('b');
      Serial1.print ('b');
    }
    leds [ i ] = CRGB::Black;
    FastLED.show();
    delay (50);
    //
    leds [ i ] = CRGB::White;
    FastLED.show();
  }
}
//
void replay()
{
  replays++;
  Serial.print (replays);
  Serial1.print ('K');                // activate knocker
}
void reset()
{
  // Light all the LEDS
  for (int i = 0; i < 70; i = i + 1)
  {
    leds[i] = CRGB::White;
  }
  FastLED.show();
  // switch off LEDs that are spare underneath playfield
  leds [58] = CRGB::Black;
  leds [9] =  CRGB::Black;
  //
  FastLED.show();
  // switch of 4 special lights
  leds [SY1] = CRGB::Black;
  leds [SY2] = CRGB::Black;
  leds [SR1] = CRGB::Black;
  leds [SR2] = CRGB::Black;
  // now switch OFF central FLYING CARPET Lights
  for (int i = 0; i < 13; i = i + 1)
  {
    int FC_Lights = FCLamps[i];
    leds[FC_Lights] = CRGB::Black;
  }
  // Switch off Ball in play and Game Over LEDS
  for (int i = 0; i < 6; i = i + 1)
  {
    leds[i + Ball_Lights] = CRGB::Black;
  }
  FastLED.show();
  digitalWrite(BlueLed_MOSFET_Driver, 0);
  BallsPerGame = 5;
  Balls = 0;
  units = 0;
  FCcount = 0;
  Phit = 0;
  Ahit = 0;
  Yspecial = 0;
  Rspecial = 0;
  GameOver = 0;
  SpecialON = 0;               // = 1 if 'Y' target and -1 if R target
  BlueBumper = 0;              // = 1 to light; set to 2 when alight
  for (int i = 0; i < 12; i++)
  {
    FC[i] = 0;
  }
  timer();                     // intialise timer
}
void diag()
// Diagnostic routine to determine if switch stuck
// and all lights are working
{
  // Light all the LEDS
  for (int i = 0; i < 70; i = i + 1)
  {
    leds[i] = CRGB::White;
  }
  FastLED.show();
  delay (2000);                 // all LEDs ON
  // all LEDS OFF
  for (int i = 0; i < 70; i = i + 1)
  {
    leds [ i ] = CRGB::Black;
  }
  FastLED.show();
  delay (1500);
  // Check coil switch state
  // Normal start state for Coil Switch is HIGH
  // Error codes are 1, 10, 100 for coil faults
  if (digitalRead(B2_Bumper_Switch) == 0)
  {
    Serial.print ('a');                 // indicate 1
    GameOver = 1;
  }
  if (digitalRead(B1_Bumper_Switch) == 0)
  {
    Serial.print ('b');                 // indicate 10
    GameOver = 1;
  }
  if (digitalRead(B3_Bumper_Switch)  == 0)
  {
    Serial.print ('c');                 // indicate 100
    GameOver = 1;
  }
  // check if any switches or targets are closed
  // inputs on pins 36 to 47
  for (int i = 36; i >= 47; i = i - 1)
  {
    int Switch = digitalRead(i);
    if (Switch == 0 )
    {
      // light faulty rollover or target letter in playfield centre
      leds[i - 15] = CRGB::White;
      GameOver = 1;
    }
  }
}
//
void check_for_special(int letter)
// completing F-L-Y-I-N-G C-A-R-P-E-T sequence lights
// 'Y' Target and switch
// alternatively
// 'R' Target and switch
{
  FC [letter] = 1;
  int Lmp = FCLamps[letter];
  leds[Lmp] = CRGB::White;
  int Tgt = FCTargetLamps[letter];
  leds[Tgt] = CRGB::Black;
  int roll = FCswitchLamps[letter];
  leds[roll] = CRGB::Black;
  FastLED.show();
  //check if special to be switched ON
  FCcount = 0;
  for (int i = 0; i < 12; i++)
  {
    if (FC[i] == 1)
    {
      FCcount = FCcount + 1;
    }
  }
  // check for specials in LED Array
  // and light special if 'Flying Carpet' fully lit
  if (FCcount == 12)
  {
    SpecialON = !SpecialON;
    if (SpecialON == 0 )
    {
      Rspecial = 1;
      Yspecial = 0;
      leds[SY1] = CRGB::White;
      leds[SY2] = CRGB::White;
      leds[SR1] = CRGB::Black;
      leds[SR2] = CRGB::Black;
      FastLED.show();
    }
    if (SpecialON == 1 )
    {
      Yspecial = 1;
      Rspecial = 0;
      leds[SR1] = CRGB::White;
      leds[SR2] = CRGB::White;
      leds[SY1] = CRGB::Black;
      leds[SY2] = CRGB::Black;
      FastLED.show();
    }
  }
}
//
void Check_Bumpers()
{
  GameOver = 0;
  if (( B1_Coil_Trigger == LOW) && (B1_Coil_State_last == HIGH))
  {
    B1_difference =  B1_currentMillis - B1_ON_Time;
    if (B1_difference >= Max_On_Time)GameOver = 1;
  }
  //
  if (( B2_Coil_Trigger == LOW) && (B2_Coil_State_last == HIGH))
  {
    B2_difference =  B2_currentMillis - B2_ON_Time;
    if (B2_difference >= Max_On_Time)GameOver = 1;
  }
  if (( B3_Coil_Trigger == LOW) && (B3_Coil_State_last == HIGH))
  {
    B3_difference =  B3_currentMillis - B3_ON_Time;
    if (B3_difference >= Max_On_Time)GameOver = 1;
  }
}
//
void B1_Bumper_Hit()
{
  B1_currentMillis = System_Base_Time;     //Start Time
  B1_Coil_State = digitalRead(B1_Bumper_Switch);  //Variable for Coil Switch
  if ((B1_Coil_State == LOW)  && (B1_Coil_State_last == LOW))
  {
    //Coil has been triggered
    B1_ON_Time =  B1_currentMillis;
    B1_Coil_State_last = HIGH;
    B1_previousMillis = millis();
   analogWrite(B1_MOSFET_Driver, 200);   //Coil Solenoid activated !
    B1_Coil_Trigger = 1 ;
  }
  else if ((B1_Coil_State == HIGH) && (B1_Coil_State_last  == HIGH))
  {
    //Coil released
    B1_Coil_State_last = LOW;
  }
  B1_difference = B1_currentMillis - B1_previousMillis;
  if ((B1_difference >= debounce) && (B1_Coil_Trigger == 1))
  {
    B1_previousMillis = B1_currentMillis;
    B1_Coil_Trigger = 0;
    analogWrite(B1_MOSFET_Driver, 0);  //Coil Solenoid deactivated
    if (BlueBumper > 0 )
    {
      Serial.print ('c');
      Serial1.print ('c');          // to Chime 100
    }
    else
    {
      Serial.print ('b');
      Serial1.print ('b');          // to Slave Chime 10
    }
  }
}
//
void B2_Bumper_Hit()
{
  B2_currentMillis = System_Base_Time;    //Start Time
  B2_Coil_State = digitalRead(B2_Bumper_Switch);  //Variable for Coil Switch
  if ((B2_Coil_State == LOW)  && (B2_Coil_State_last == LOW))
  {
    //Coil has been triggered
    B2_ON_Time =  B2_currentMillis;
    B2_Coil_State_last = HIGH;
    B2_previousMillis = millis();
     analogWrite(B2_MOSFET_Driver, 200);   //Coil Solenoid activated !
    B2_Coil_Trigger = 1 ;

  }
  else if ((B2_Coil_State == HIGH) && (B2_Coil_State_last  == HIGH))
  {
    //Coil released
    B2_Coil_State_last = LOW;
  }
  B2_difference = B2_currentMillis - B2_previousMillis;
  if ((B2_difference >= debounce) && (B2_Coil_Trigger == 1))
  {
    B2_previousMillis = B2_currentMillis;
    B2_Coil_Trigger = 0;
    analogWrite(B2_MOSFET_Driver, 0);  //Coil Solenoid deactivate
    if (BlueBumper > 0 )
    {
      Serial.print ('c');
      Serial1.print ('c');          // to Chime 100
    }
    else
    {
      Serial.print ('b');
      Serial1.print ('b');          // to Slave Chime 10
    }
  }
}
//
void B3_Bumper_Hit()
{
  B3_currentMillis = System_Base_Time;    //Start Time
  B3_Coil_State = digitalRead(B3_Bumper_Switch);  //Variable for Coil Switch
  if ((B3_Coil_State == LOW)  && (B3_Coil_State_last == LOW))
  {
    //Coil has been triggered
    B3_ON_Time =  B3_currentMillis;
    B3_Coil_State_last = HIGH;
    B3_previousMillis = millis();
     analogWrite(B3_MOSFET_Driver, 200);   //Coil Solenoid activated !
    B3_Coil_Trigger = 1 ;
  }
  else if ((B3_Coil_State == HIGH) && (B3_Coil_State_last  == HIGH))
  {
    //Coil released
    B3_Coil_State_last = LOW;
  }
  B3_difference = B3_currentMillis - B3_previousMillis;
  if ((B3_difference >= debounce) && (B3_Coil_Trigger == 1))
  {
    B3_previousMillis = B3_currentMillis;
    B3_Coil_Trigger = 0;
    analogWrite(B3_MOSFET_Driver, 0);  //Coil Solenoid deactivated
    Serial.print ('c');
    Serial1.print ('c');
  }
}
//
void loop()
{
  System_Base_Time = millis();
  if (GameOver == 1)
    // Game Over light ON all others OFF
    // set Gameover to 2 since we only want it to
    // set the lights once
  {
    for (int i = 0; i < 70; i = i + 1)
    {
      leds [ i ] = CRGB::Black;
    }
    leds [ 41 ] = CRGB::White;
    FastLED.show();
    GameOver = 2;
    Serial1.print ('D');            // switch off aux processor

  }
  // timings are as follows:
  // NOTE Targets can bounce for up to 200 milliseconds
  // around 'loop'; approx 2 milli second for approx 20
  // switch / target reads
  // Score output 2 milliseconds (longer for 50 score)
  // Pop Bumpers approx 2 milliseconds
  // FastLED.show takes approx 2 to 3 milli seconds
  // Flipper not applicable since not controlled by the MEGA board
  // only commands to switch ON or OFF at start and end of game
  // sent to Slave 1 UNO.
  //
  if (digitalRead(Redbutton) == 0)
    // used to start game if no replays left
    // add 1 to get a free game
  {
    waiton (Redbutton);
    diag();                     // error codes displayed on backglass
    if (replays == 0)
    {
      replay();                 // give a free replay
      Serial1.print ('K');      // to AUX 1 GameOver
    }                           // activate Knocker
    replays = replays - 1;      // reduce replays by 1
    Serial.print (replays);     // update backglass
    Serial.print ('g');         // reset backglass score to "0000"
    Serial1.print ('D');        // to AUX 1 GameOver
    delay(1000);                // allow a reset on slave processors
    Serial1.print ('E');        // -> AUX 1 New Game
    reset();
    // Deliver 1st ball ready for new game start
    Balls = 1;
    leds[46] = CRGB::White;
    // activate solonoid to kick ball into shooter lane
    analogWrite(Deliver_Ball_MOSFET_Driver, 200);
    // give the right amount of kick,delay here no problem
    // since nothing else happening
    delay (50);                 
    analogWrite(Deliver_Ball_MOSFET_Driver, 0);
    TopRipple(0);
  }
  // Everything OK; so Game ON
  if (GameOver == 0)
  {
    B1_Bumper_Hit();
    B2_Bumper_Hit();
    B3_Bumper_Hit();
    Check_Bumpers();
    switch_timer = 0;
    if (digitalRead(Yellbutton) == 0)
    {
      waiton(Yellbutton);
      leds[47 - Balls] = CRGB::Black;
      Balls++;
      leds[47 - Balls] = CRGB::White;
      // If Game Over so tell backglass
      if (Balls > BallsPerGame)
      {
        Serial.print ('x');
        Serial1.print ('X');        // to Slave 1 GameOver
        GameOver = 1;
        Balls = 0;                  // stops multiple Game Over
      }
      else
      {
        TopRipple(0);
        // activate solonoid to kick ball into shooter lane
        analogWrite(Deliver_Ball_MOSFET_Driver, 200);
        // give the right amount of kick
        delay (50);
        analogWrite(Deliver_Ball_MOSFET_Driver, 0);
      }
    }
    //
    if (digitalRead(Tilt) == 0)
    {
      //light 'TILT' Lamp
      waiton (Tilt);             // wait for TILT sensor to stabalize
      Serial.print ('t');        // inform backglass software
      GameOver = 1;
    }
    //
    if ((Phit == 1) && (Ahit == 1))
    {
      if (BlueBumper == 0)BlueBumper = 1;
    }
    //
    // Sling Shots
    if (digitalRead(Slingshot) == 0)
    {
      waiton (Slingshot);         // wait for button release
      Serial.print ('a');
      Serial1.print ('a');        //to Chime 1
      units++;
    }
    // switchs and Targets
    // These are connected in parallel so no need for seperate code
    // NOTE LEDS Lights for FLYING CARPET are numbered from 0 to 11
    if (digitalRead(Fswitch) == 0)
    {
      waiton (Fswitch);         // wait for button release
      TopRipple(1);
      // light appropriate light
      check_for_special(0);
    }
    //
    if (digitalRead(Lswitch) == 0)
    {
      waiton (Lswitch);         // wait for button release
      TopRipple(1);
      // light appropriate light
      check_for_special(1);
    }
    //
    if (digitalRead(Yswitch) == 0)
    {
      waiton (Yswitch);         // wait for button release
      TopRipple(1);
      // light appropriate light
      check_for_special(2);
      if (Yspecial == 1 )
      {
        // although Replays are awarded, they are for
        //              AMUSEMENT ONLY
        // The display software will ingores replays above 9
        replays++;
        Serial.print (replays);
        Serial1.print ('R');
      }
    }
    //
    if (digitalRead(Iswitch) == 0)
    {
      waiton (Iswitch);         // wait for button release
      TopRipple(1);
      //light appropriate light
      check_for_special(3);
    }
    //
    if (digitalRead(Nswitch) == 0)
    {
      waiton (Nswitch);         // wait for button release
      TopRipple(1);
      //light appropriate light
      check_for_special(4);
    }
    //
    if (digitalRead(Gswitch) == 0)
    {
      waiton (Gswitch);         // wait for button release
      TopRipple(1);
      //light appropriate light
      check_for_special(5);
    }
    if (digitalRead(Cswitch) == 0)
    {
      waiton (Cswitch);        // wait for button release
      timer();
      if (switch_timer == 0)
      {
        Serial.print ('c');
        Serial1.print ('c');
        //light appropriate light
        check_for_special(6);
      }
    }
    //
    if (digitalRead(Aswitch) == 0)
    {
      waiton (Aswitch);         // wait for button release
      timer();
      if (switch_timer == 0)
      {
        Serial.print ('c');
        Serial1.print ('c');
        Ahit = 1;
        //light appropriate light
        check_for_special(7);
      }
    }
    //
    if (digitalRead(Rswitch) == 0)
    {
      waiton (Rswitch);         // wait for button release
      timer();
      if (switch_timer == 0)
      {
        Serial.print ('c');
        Serial1.print ('c');
        //light appropriate light
        check_for_special(8);
        if (Rspecial == 1 )
        {
          // although Replays are awarded, they are for
          //              AMUSEMENT ONLY
          replay();
        }
      }
    }
    //
    if (digitalRead(Pswitch) == 0)
    {
      waiton (Pswitch);        // wait for button release
      timer();
      if (switch_timer == 0)
      {
        Serial.print ('c');
        Serial1.print ('c');
        //light appropriate light
        check_for_special(9);
        Phit = 1;
      }
    }
    //
    if (digitalRead(Eswitch) == 0)
    {
      waiton (Eswitch);        // wait for button release
      timer();
      if (switch_timer == 0)
      {
        Serial.print ('c');
        Serial1.print ('c');
        //light appropriate light
        check_for_special(10);
      }
    }
    //
    if (digitalRead(Tswitch) == 0)
    {
      waiton (Tswitch);         // wait for button release
      timer();
      {
        if (switch_timer == 0)
        {
          Serial.print ('c');
          Serial1.print ('c');
        }
        //light appropriate light
        check_for_special(11);
      }
    }
  }
}
UNO for Flippers, Knocker and ChimesArduino
// Version 2.0 13/11/20
// GOTTLIEB Flying Carpet EM Pinball with a ARDUINO's
// replacing the EM Stuff and LEDS replacing bulbs.
// Written by B J Mclaren based on experience of doing
// an ARDUINO based Bally Bingo
// This solution uses:
// A MEGA as the Master controller for the Playfield stuff
// AUX1 (UNO) has 6 pwm channels for Chime Unit, Flippers
// and Knocker
// This is the AUX1 code for an UNO and a FREETRONICS 6 Channel
// MOSFET Shield
//
// Valid Commands are
// 'a' = 1st score chime
// 'b' = 2nd score chime
// 'c' = 3rd score chime
// 'K' = Knocker
// 'E' = Enable AUX processor
// 'D' = Disable AUX processor (eg Game Over / Tilt)
//
#define Right_Flipper_Switch A0
#define Left_Flipper_Switch  A1
//
#define Chime1_Driver  3
#define Chime2_Driver  5
#define Chime3_Driver  6
#define Knocker_Driver 9
#define Left_Flipper_Driver  10
#define Right_Flipper_Driver 11
//
#define On_Time 50               // Chime ON Time in ms
#define LED 13
char ch;
int GameON;
int LFlipperON = 0;
int RFlipperON = 0;
int Chime1ON = 0;
int Chime2ON = 0;
int Chime3ON = 0;
int KnockerON = 0;
int LPWM = 0;
int RPWM = 0;
//
unsigned long System_Time = 0;
unsigned long Chime1ON_Time = 0;
unsigned long Chime2ON_Time = 0;
unsigned long Chime3ON_Time = 0;
unsigned long KnockerON_Time = 0;
unsigned long LFlipperON_Time = 0;
unsigned long RFlipperON_Time = 0;
unsigned long diff = 0;
//
void setup()
{
  Serial.begin   (9600);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  //
  pinMode(Chime1_Driver, OUTPUT);
  digitalWrite(Chime1_Driver, LOW);
  pinMode(Chime2_Driver, OUTPUT);
  digitalWrite(Chime2_Driver, LOW);
  pinMode(Chime3_Driver, OUTPUT);
  digitalWrite(Chime3_Driver, LOW);
  //
  pinMode(Knocker_Driver, OUTPUT);
  digitalWrite(Knocker_Driver, LOW);
  //
  pinMode(Right_Flipper_Switch, INPUT_PULLUP);
  analogWrite(Right_Flipper_Driver, 0);
  //
  pinMode(Left_Flipper_Switch, INPUT_PULLUP);
  analogWrite(Left_Flipper_Driver, 0);
  // Setup Variables
  GameON = 0;
  LFlipperON = 0;
  RFlipperON = 0;
  System_Time = 0;
  Chime1ON = 0;               // Flag to indicate chime is ctive
  Chime2ON = 0;
  Chime3ON = 0;
  KnockerON = 0;
  System_Time = 0;            // base time for timing operations
  Chime1ON_Time = 0;          // time chime became active
  Chime2ON_Time = 0;
  Chime3ON_Time = 0;
  LFlipperON_Time = 0;
  RFlipperON_Time = 0;
  diff = 0;
  //
}
//
void loop()
{
  System_Time = millis();
  //
  if (RFlipperON == 1 )
  {
    diff =  System_Time - RFlipperON_Time;
    // if flipper held for 1 seconds reduce power
    if (diff > 1000)
    {
      RPWM = 190;
    }
  }
  if (LFlipperON == 1 )
  {
    diff =  System_Time - LFlipperON_Time;
    // if flipper held for 1 seconds reduce power
    if (diff > 1000)
    {
      LPWM = 190;
    }
  }
  //
  if (KnockerON == 1 )
  {
    diff =  System_Time - KnockerON_Time;
    if (diff > On_Time )
    {
      KnockerON = 0;
      digitalWrite(Knocker_Driver, LOW);
    }
  }
  if (Chime1ON == 1 )
  {
    diff =  System_Time - Chime1ON_Time;
    if (diff > On_Time )
    {
      Chime1ON = 0;
      digitalWrite(Chime1_Driver, LOW);
    }
  }
  if (Chime2ON == 1 )
  {
    diff =  System_Time - Chime2ON_Time;
    if (diff > On_Time )
    {
      Chime2ON = 0;
      digitalWrite(Chime2_Driver, LOW);
    }
  }
  if (Chime3ON == 1 )
  {
    diff =  System_Time - Chime3ON_Time;
    if (diff > On_Time )
    {
      Chime3ON = 0;
      digitalWrite(Chime3_Driver, LOW);
    }
  }
  //
  if (Serial.available() > 0)
  { 
    // Read input from ARDUINO MEGA
    ch = Serial.read();
    if (ch == 'E')
    { // enable processor eg New Game
      GameON = 1;
      digitalWrite(LED, HIGH);
      System_Time = 0;
      Chime1ON = 0;
      Chime2ON = 0;
      Chime3ON = 0;
      KnockerON = 0;
      LFlipperON = 0;
      RFlipperON = 0;
    }
    if (ch == 'D')
    { // disable processor Game Over / Tilt
      GameON = 0;
      digitalWrite(LED, LOW);
      analogWrite(Left_Flipper_Driver,  0);
      analogWrite(Right_Flipper_Driver,  0);
    }
    //
    if (ch == 'K')
    {
      // activate Knocker
      digitalWrite(Knocker_Driver, HIGH);
      KnockerON = 1;
      KnockerON_Time = millis();
    }
    if (ch == 'a')
    { // activate 1st Chime
      digitalWrite(Chime1_Driver, HIGH);
      Chime1ON = 1;
      Chime1ON_Time = millis();
    }
    if (ch == 'b')
    { // activate 2nd Chime
      digitalWrite(Chime2_Driver, HIGH);
      Chime2ON = 1;
      Chime2ON_Time = millis();
    }
    if (ch == 'c')
    { // activate 3rd Chime
      digitalWrite(Chime3_Driver, HIGH);
      Chime3ON = 1;
      Chime3ON_Time = millis();
    }
  }
  // Switch Off Flipper
  if (digitalRead(Right_Flipper_Switch) == HIGH)
  { //Flipper OFF
    analogWrite(Right_Flipper_Driver, 0);
    RFlipperON = 0;
    RPWM = 255;
  }
  if (digitalRead(Left_Flipper_Switch) == HIGH)
  { //Flipper OFF
    analogWrite(Left_Flipper_Driver,  0);
    LFlipperON = 0;
    LPWM = 255;
  }
  if (GameON == 1)
  { // Flipper active only when Game is on
    if (digitalRead(Right_Flipper_Switch) == LOW)
    {
      analogWrite(Right_Flipper_Driver, RPWM );
      if ( RFlipperON == 0)
      {
        RFlipperON_Time = millis();
      }
      RFlipperON = 1;
    }
    if (digitalRead(Left_Flipper_Switch) == LOW)
    { // Left Flipper ON
      analogWrite(Left_Flipper_Driver, LPWM );
      if ( LFlipperON == 0)
      {
        LFlipperON_Time = millis();
      }
      LFlipperON = 1;
    }
  }
}
Processing P3 for backglass PCProcessing
Used to display score and backglass graphics
import processing.serial.*;
import ddf.minim.*;
Minim minim;
AudioPlayer base;
AudioPlayer chime100;
AudioPlayer chime10;
AudioPlayer chime1;
AudioPlayer reel;
//
Serial myPort;  // The serial port:
PImage bg;
PImage img [];
int y=205;
int x0=639;
int x1=737;
int x2=835;
int x3=933;
//
// Highest Score Variable
int highscore;
int highscoreones;
int highscoretens;
int highscorehundreds;
int highscorethous;
//
int d;
int thous;
int hundreds;
int tens;
int ones; 
int oldthous;
int oldhundreds;
int oldtens;
int oldones; 
int oldscore;
int newscore;
int add;
int reset;
//
//
void setup()
{
  minim = new Minim(this);
  base = minim.loadFile("base.wav");
  chime100 = minim.loadFile("chime100.wav");
  chime10 = minim.loadFile("chime10.wav");
  chime1 = minim.loadFile("chime1.wav");
  reel=minim.loadFile("reel.wav");
  //
  myPort = new Serial(this, Serial.list()[0], 9600);
  //size(978,1080);
  //bg =   loadImage("FC1.png");
   size(1080,1193);
   bg =   loadImage("FC.png");
  newscore = 0;
  int nPics = 10;
  img = new PImage [nPics];
  for (int i = 0; i <nPics; i++) 
  {
    img[i] = loadImage(+i+".jpg");
  }
  background(bg);
//
}
//
void display()
{
   oldthous = thous;
   oldhundreds = hundreds;
   oldtens = tens;
   oldones = ones;    
   oldscore = newscore;
   if(oldscore>9999)oldscore=0;
   thous = int(oldscore/1000); 
   hundreds = int(oldscore/100);
   hundreds = hundreds-(int(hundreds/10)*10);
   tens = int(oldscore/10);
   tens = tens-(int(tens/10)*10);
   ones = oldscore-(int(oldscore/10)*10);
   // 
   image(img[ones], x3, y);
   image(img[tens], x2, y);
   image(img[hundreds], x1, y);
   image(img[thous], x0, y);
   delay(20);
}
//  
void draw() 
  {
    if (reset==1)
    {
      int total=ones+(10*tens)+(100*hundreds)+(1000*thous);
      if (total == 0) 
      {
        reset=0;
        ones=0;
        tens=0;
        hundreds=0;
        thous=0;
        newscore=0;
        oldscore=0;
      }
//      
      if (ones != 0) 
      {
        ones=ones+1;
        if (ones==10)ones=0;
        image(img[ones], x3, y);
        reel.rewind();
        reel.play();
        delay(75);
      }
//      
      if (tens != 0) 
      {
        tens=tens+1;
        if (tens==10)tens=0;
        image(img[tens], x2, y);
        reel.rewind();
        reel.play();
        delay(75); 
      }
//     
      if (hundreds != 0) 
      {
        hundreds=hundreds+1;
        if (hundreds==10)hundreds=0;
        image(img[hundreds], x1, y);
        reel.rewind();
        reel.play();
        delay(75);  
      }
      if (thous != 0) 
      {
        thous=thous+1;
        if (thous==10)thous=0;
        image(img[thous], x0, y);
        reel.rewind();
        reel.play();
        delay(75);  
      }
  }
//  Process any inputs
   while (myPort.available() > 0) 
   {
    char inByte = myPort.readChar();
//  
    if (inByte == 'x') 
    {
      base.rewind();
      base.play();
    }
    if (inByte == 'g') 
    {
     reset = 1;
     base.rewind();
     base.play();
     add=0;
    }
  if (inByte == 'a')
  {
    add = 1;
    chime1.rewind();
    chime1.play();
  }
  if (inByte == 'b')
  {
    add = 10;
    chime10.rewind();
    chime10.play(); 
  }
  if (inByte == 'c')
  {
   add = 100; 
   chime100.rewind();
   chime100.play();
  }
  if (add>0)
   {
     oldscore = newscore; 
     newscore = newscore+add;
     add=0;
   }
   display();
  }
}

Schematics

UNO Schematic
Flippers, Chimes and knocker drivers
Aux schematic aexiew0jdh
Main MEGA processor
Controls the game
Master schematic qygeahmicz

Comments

Similar projects you might like

Arduino Conversion of an EM Pinball Machine

Project showcase by Frogger1108

  • 10,954 views
  • 16 comments
  • 34 respects

Arduino Ping Pong Pinball

Project tutorial by joesinstructables

  • 8,010 views
  • 4 comments
  • 12 respects

Homemade Arduino Pinball Machine

Project showcase by Frogger1108

  • 25,855 views
  • 22 comments
  • 75 respects

Build a Pinball Machine

Project showcase by grahamasker

  • 6,703 views
  • 17 comments
  • 16 respects

Arduino Controlled Pinball Machine

Project tutorial by Bob Blomquist

  • 55,712 views
  • 50 comments
  • 140 respects

Pinball Sounds and Music

Project tutorial by Bob Blomquist

  • 9,656 views
  • 4 comments
  • 23 respects
Add projectSign up / Login