Project tutorial

Arduino Casino © GPL3+

Learn how to do animations on 1602 LCD displays and ultimately create your very own casino.

  • 499 views
  • 0 comments
  • 1 respect

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)
Only required if you are not using an off-the-self case

Apps and online services

About this project

In this tutorial, you will learn how to not only create custom characters for a LCD 1602 display but also how to turn these into complete animations.

Custom characters

1602 LCD displays (16 characters x 2 lines) use a HD44780 driver chip. This chip has a 5 x 8 pixel ASCII character set built in. It also allows you to add up to 8 custom characters to that character set.

A custom character is defined as a 8 byte array. Only the lower 5 bits of each byte are used to make up the 5 x 8 custom character. Below is the definition of a Heart character.

const byte heart[8] =
{
B01010,
B11111,
B11111,
B11111,
B01110,
B00100,
B00000,
B00000
};

To store this custom character in the LCD display, you need to pass a value between 0 and 7 that identifies which custom character slot you wish to set and also the array of bytes that define the character.

#define HEART 0
lcd.createChar(HEART, heart);

To display the custom character, you use the write function and pass it the custom character identifier.

lcd.write(HEART);

Animating characters

The custom characters can be initialized or changed at anytime before they are used. The restriction is that only the eight custom characters can be displayed at the same time.

That is you can't do something like this:

lcd.createChar(0, heart);
lcd.setCursor(0, 0);
lcd.write(0);
lcd.createChar(0, club);
lcd.setCursor(1, 0);
lcd.write(0);

The above example shows both the heart and club symbols trying to be displayed on the screen at the same time in different locations but both are using the same custom character "slot". This will not work.

However you can overwrite a character provided the one being overwritten is not used somewhere else on the screen.

lcd.createChar(0, heart);
lcd.setCursor(0, 0);
lcd.write(0);
delay(1000);
lcd.createChar(0, club);
lcd.setCursor(0, 0);
lcd.write(0);

The above example shows the heart character for 1 second and then replaces it with the club character.

In the slot machine, each "reel" contains 10 symbols.

#define REEL_SYMBOLS 10
#define TOTAL_REEL_SYMBOL_ROWS (REEL_SYMBOLS * 8)
const byte reel[TOTAL_REEL_SYMBOL_ROWS] =
{
//Heart
B00000,
B01010,
B11111,
B11111,
B11111,
B01110,
B00100,
B00000,

//Diamond
B00000,
B00100,
B01110,
B11111,
B11111,
B01110,
B00100,
B00000,

:

//Face
B00000,
B11011,
B11011,
B00000,
B10001,
B01110,
B00000,
B00000,
};

Each wheel has its own 8 byte memory buffer in which the reel data will be copied to.

byte wheel[8];

By keeping a index into the reel array, 8 bytes are copied to the wheel array and then set as a custom character and displayed. The index is incremented and after a short delay, the process is repeated.

Here is the code that populates the wheel character from the reel data. The row variable is a global variable that points to the next row to copy. After the newly formed character is displayed, the row variable is incremented. Once at the end of the list, it wraps to the start again.

#define SLOT 0
#define CURSOR_X 3
#define CURSOR_Y 0

int row = 0;

//Copy eight bytes into a character RAM from the reel array
// - global variable "row" points to top row to display
// - if "row + i" exceeds the array size, wrap round to the start
while (true)
{
for (int i = 0; i < 8; i++)
{
wheel[i] = reel[(row + i) % TOTAL_REEL_SYMBOL_ROWS];
}

//This example uses custom character slot 0. If there is more than one wheel to
//spin, each wheel will need its own unique custom character slot and cursor
//position.
lcd.createChar(SLOT, wheel);
lcd.setCursor(CURSOR_X, CURSOR_Y);
lcd.write(SLOT);

//Increase row for next animation frame. Wrap round to start if it gets past
//the end of the reel rows.
row = (row + 1) % TOTAL_REEL_SYMBOL_ROWS;

delay(100);
}

Breadboard Casino

The actual electronics is fairly simple. The parts required are:

  • Arduino UNO, Nano, Pro Mini or Mega
  • 1602 LCD screen
  • 220 ohm resistor for the LCD backlight
  • 10K trim pot for the LCD contrast adjustment
  • 4 tactile push buttons
  • 1 piezo electric passive buzzer

If you are wondering why the Arduino pin connections were chosen as they are, it was to simply the PCB layout.

Once you have wired up the Arduino, load the sketch provided into the Arduino IDE and program your Arduino board.

Playing the games

There are four buttons. From the left, they are HIT, STAND, LESS and MORE.

HIT - Whenever you can select your bet, this button will take you to the top level menu allowing you select a different game. In Blackjack, it is also used to signify the player wants another card.

STAND - In general, this starts a new game. In Slots, it spins the wheels, in Blackjack it starts a new hand and is also used to signify that the player is finished drawing cards and wants the dealer to play and in Craps, it is used to throw the dice.

LESS - Reduces the amount you want to bet. On the top level menu, it selects the previous menu item.

MORE - Increases the amount you want to bet. On the top level menu, it selects the next menu item.

Demonstration Video

Demonstration of Arduino Casino

Casino Console

Taking your build from the breadboard to a fully finished console will require a printed circuit board and some extra components. The Eagle files are attached should you wish to have the PCB commercially made or do as I did and make it yourself. I used the Toner method.

Using parts that I had around the workshop, the Arduino UNO was replaced with a ATMega328 DIL chip and mounted on the back of the board by using a DIL 28 pin IC socket and flattening out its pins. All capacitors are 0805 SMD variants with the exception of the 47uF/16V tantalum 3528 capacitor and the 1206 10uF ceramic capacitor. The crystal is a 16Mhz through hole variant also mounted on the back. The tactile switches are 12x12mm with round button caps.

The case is 3D printed using a 0.2mm layer height and no supports. Drill out the PCB mount holes with a 2.5mm drill and create a thread using a 3mm tap. Use M3 6mm screws to secure the board in place. I also drilled out the four mount holes on the PCB to 4mm to allow for any adjustment required to stop the button tops from sticking on the case when securing the board.

If the ATMega328 chip doesn't have an Arduino boot loader, you need to add one first. I used my AVR ISP Programmer to add the boot loader and then uploaded the sketch using a FTDI programmer.

Conclusion

The slot machine animations worked out really well. Hopefully you can use the animation techniques described here in one of your future projects.

Code

Lcd1602CasinoV1.inoC/C++
/*
 Arduino 1602 Casino
 by John Bradnam (jbrad2089@gmail.com)
 
*/

#include "Hardware.h"
#include "Game.h"
#include "SlotMachine.h"
#include "Blackjack.h"
#include "Craps.h"

enum gameModeEnum { GAME_SLOT, GAME_BLACKJACK, GAME_CRAPS };
gameModeEnum gameMode;
bool inMenu = true;
#define MENU_FLASH_RATE 200;
unsigned long menuTimeout;
bool menuState = false;

//--------------------------------------------------------------------------------------------

//Initialisation
void setup() 
{
  Serial.begin(115200);

  //Setup hardware and LCD
  hardwareSetup();

  creditBalance = STARTING_CREDIT_BALANCE;

  menuSetup();
}

//--------------------------------------------------------------------------------------------

//Main program loop
void loop() 
{
  if (inMenu)
  {
    lastButtonPressed = processMenu();
    if (lastButtonPressed == BTN_HIT || lastButtonPressed == BTN_STAND)
    {
      lastButtonPressed = BTN_NONE;
      switch(gameMode)
      {
        case GAME_SLOT: slotMachineSetup(); break;
        case GAME_BLACKJACK: blackjackSetup(); break;
        case GAME_CRAPS: crapsSetup(); break;
      }
      inMenu = false;
    }
  }
  else
  {
    bool exitGame = false;
    switch(gameMode)
    {
      case GAME_SLOT: exitGame = slotMachineLoop(); break;
      case GAME_BLACKJACK: exitGame = blackjackLoop(); break;
      case GAME_CRAPS: exitGame = crapsLoop(); break;
    }
    if (exitGame)
    {
      menuSetup();
    }
  }
}

//--------------------------------------------------------------------------------------------

//Menu setup
void menuSetup()
{
  lcd.clear();
  lcd.print(" Arduino Casino");
  
  //Slot machine 7
  for (int y = 0; y < 8; y++)
  {
    reels[0][y] = getReelRow((SEVEN << 3) + y);
  }
  lcd.createChar(0, &reels[0][0]);
  //Craps 3 & 4
  lcd.createChar(3, &dice[THREE][0]);
  lcd.createChar(4, &dice[FOUR][0]);
  //Black jack
  lcd.createChar(DIAMOND, &cards[DIAMOND][0]);  //1
  lcd.createChar(SPADE, &cards[SPADE][0]);      //2
  
  inMenu = true;
  displayMenu(true);
}

//--------------------------------------------------------------------------------------------

//Display menu
// Used in switch callback to flash active menu item while waiting for input
void displayMenu()
{
  displayMenu(false);
}

//--------------------------------------------------------------------------------------------

//Display menu
// force - true = show selected menu, false hide selected menu
void displayMenu(bool force)
{
  menuState = menuState | force;
  force = force || (millis() > menuTimeout);
  if (force) 
  {
    menuTimeout = millis() + MENU_FLASH_RATE;
    bool on = menuState;
    menuState = !menuState;

    lcd.setCursor(0, 1);
    if (!on || gameMode != GAME_SLOT)
    {
      //3 sevens
      lcd.write((byte)0);
      lcd.write((byte)0);
      lcd.write((byte)0);
    }
    else
    {
      lcd.print("   ");
    }
    
    lcd.setCursor(6, 1);
    if (!on || gameMode != GAME_BLACKJACK)
    {
      //Blackjack
      lcd.print("J");
      lcd.write(DIAMOND);
      lcd.print("A");
      lcd.write(SPADE);
    }
    else
    {
      lcd.print("    ");
    }
    
    lcd.setCursor(13, 1);
    if (!on || gameMode != GAME_CRAPS)
    {
      //Craps
      lcd.write((byte)4);
      lcd.print(" ");
      lcd.write((byte)3);
    }
    else
    {
      lcd.print("   ");
    }
  }
}

//-----------------------------------------------------------------

//Invoked by key press in MODE_GETBET mode
buttonEnum processMenu() 
{
  // Prompt user.
  bool force = false;
  lastButtonPressed = buttonPressed(displayMenu);
  switch (lastButtonPressed) 
  {
    case BTN_MORE:
      gameMode = (gameMode == GAME_CRAPS) ? GAME_SLOT : (gameModeEnum)((int)gameMode + 1);
      force = true;
      break;

    case BTN_LESS:
      gameMode = (gameMode == GAME_SLOT) ? GAME_CRAPS : (gameModeEnum)((int)gameMode - 1);
      force = true;
      break;
  }
  displayMenu(force);
  return lastButtonPressed;
};
SlotMachine.hC/C++
/*
 Arduino 1602 Casino
 by John Bradnam (jbrad2089@gmail.com)

 SlotMachine.h
 Routines for 1602 slot machine
  
*/

#include "Hardware.h"
#include "Game.h"
#include "Symbols.h"

enum slotPlayModeEnum { SLOT_NOINPUT, SLOT_GETBET, SLOT_SPIN };

#define WHEELS 3

/* Timing constants that ontrol how the reels spin */
#define START_DELAY_TIME 10
#define INCREMENT_DELAY_TIME 5
#define PAUSE_TIME 1000
#define MAX_DELAY_BEFORE_STOP 100
#define MIN_SPIN_TIME 1000
#define MAX_SPIN_TIME 3000
#define FLASH_REPEAT 10
#define FLASH_TIME 150

/* spinDigit holds the information for each wheel */
struct spinDigit 
{
  unsigned long delayTime;
  unsigned long spinTime;
  unsigned long frameTime;
  uint8_t row;
  uint8_t symbol;
  bool stopped;
  int stopDigit;
};

spinDigit spin[WHEELS]; 
//This array is used to create the unique wheel characters for the 1602 display
byte reels[WHEELS][8];

//Smiley faces are wild cards
//Payouts are halved if they contain one or more smiley faces
//Three smiley faces are treated as THREE_SYMBOL_PAYOUT
#define THREE_SEVEN_PAYOUT  600
#define THREE_SYMBOL_PAYOUT 122
#define TWO_SEVEN_PAYOUT    50
#define ONE_SEVEN_PAYOUT    3
#define TWO_SYMBOL_PAYOUT   2

slotPlayModeEnum slotPlayMode = SLOT_GETBET;

//-------------------------------- FUNCTIONS -------------------------------------------------

void slotMachineSetup(); 
bool slotMachineLoop(); 
void processSpin();
unsigned long spinTheWheels();
unsigned long highlightWinAndCalculatePayout();
void flashSymbol(uint8_t symbol);
void displayWheelSymbol(int wheel);
uint8_t getReelRow(uint8_t row);

//--------------------------------------------------------------------------------------------

//Initialisation
void slotMachineSetup() 
{
  //Set up each wheel. 
  for (uint8_t j = 0; j < WHEELS; j++)
  {
    spin[j].row = random(0, REEL_SYMBOLS) << 3;  //Start each wheel on a random symbol
  }

  // Print slot machine screen
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("[ ? ][ ? ][ ? ]");

  //Animate balance upto current balance
  Serial.println("creditBalance = " + String(creditBalance));
  animateBank(0, creditBalance, DIGIT_DELAY_TIME, 50);
  Serial.println("creditBalance = " + String(creditBalance));
 
  //Show the openning set of wheels
  for (uint8_t j = 0; j < WHEELS; j++)
  {
    displayWheelSymbol(j);
  }
  
  slotPlayMode = SLOT_GETBET;
  bet = BET_MIN;
  displayBet(bet);
}

//--------------------------------------------------------------------------------------------

//Main program loop
// Returns true to exit game
bool slotMachineLoop() 
{
  //Get button pressed
  lastButtonPressed = buttonPressed(NULL);
  while (lastButtonPressed != BTN_HIT && lastButtonPressed != BTN_NONE)
  {
    switch (slotPlayMode) 
    {
      case SLOT_GETBET: 
        if (processGetBet() == BTN_STAND) 
        {
          processSpin(); 
        }
        break;
        
      case SLOT_SPIN: 
        processSpin(); 
        break;
    }
    lastButtonPressed = buttonPressed(NULL);
  }
  return (lastButtonPressed == BTN_HIT);
}

//--------------------------------------------------------------------------------------------

//Spin the wheels and update bank with any payout of loss
void processSpin()
{
  unsigned long payout = spinTheWheels();
  long newBalance = min(creditBalance + (payout * bet) - bet, 99999);
  Serial.println("creditBalance = " + String(creditBalance) + ", payout = " + String(payout) + ", newBalance = " + String(newBalance));
  creditBalance = animateBank(creditBalance, newBalance, DIGIT_DELAY_TIME, (abs(creditBalance - newBalance) > 30) ? bet : 1);
  if (creditBalance < 0)
  {
    playLoseSound();
    creditBalance = animateBank(creditBalance, STARTING_CREDIT_BALANCE, DIGIT_DELAY_TIME, 10);
  }
  slotPlayMode = SLOT_GETBET;
}

//--------------------------------------------------------------------------------------------

//Spins all the wheels and returns the payout
unsigned long spinTheWheels()
{
  unsigned long payout = 0;
  //Reset wheels for the spin
  unsigned long totalTime = millis();
  for (uint8_t j = 0; j < WHEELS; j++)
  {
    totalTime = totalTime + random(MIN_SPIN_TIME, MAX_SPIN_TIME);
    spin[j].delayTime = START_DELAY_TIME;
    spin[j].spinTime = totalTime;
    spin[j].frameTime = millis() + spin[j].delayTime;
    spin[j].stopped = false;
  }
  
  bool allStopped = false;
  while (!allStopped)
  {
    //Scroll each symbol up
    for (uint8_t j = 0; j < WHEELS; j++)
    {
      if (!spin[j].stopped && millis() > spin[j].frameTime)
      {
        spin[j].frameTime = millis() + spin[j].delayTime;

        displayWheelSymbol(j);
        spin[j].row = (spin[j].row + 1) % TOTAL_REEL_SYMBOL_ROWS;

        beepWheel();
        
        if (millis() > spin[j].spinTime)
        {
          //Stop if delayTime exceeds MAX_DELAY_BEFORE_STOP
          //Only stop on complete symbol
          if (spin[j].delayTime > MAX_DELAY_BEFORE_STOP && (spin[j].row % 8) == 1)
          {
            spin[j].stopped = true;
            spin[j].symbol = spin[j].row >> 3;
            if (j == (WHEELS - 1))
            {
              //All wheels are now stopped
              allStopped = true;
              payout = highlightWinAndCalculatePayout();
            }
          }
          else if (spin[j].delayTime <= MAX_DELAY_BEFORE_STOP)
          {
            spin[j].delayTime = spin[j].delayTime + INCREMENT_DELAY_TIME;
          }
        }
      }
    }
    yield();
  }
  return payout;
}

//--------------------------------------------------------------------------------------------

//Work out if the player has one anything
//If they have, flash winning sequence and return the payout multiplier
unsigned long highlightWinAndCalculatePayout()
{
  unsigned long payout = 0;
  uint8_t matches = 0;
  uint8_t symbol = 255;
  uint8_t sevens = 0;
  uint8_t faces = 0;
  uint8_t bestMatch = 0;
  for (uint8_t y = 0; y < WHEELS; y++)
  {
    matches = 1;
    if (spin[y].symbol == FACE)
    {
      faces++;
      if (symbol == 255)
      {
        symbol = FACE;
      }
    }
    else if (spin[y].symbol == SEVEN)
    {
      sevens++;
      matches = sevens;
      symbol = SEVEN;
    }
    else if (sevens == 0)
    {
      for (uint8_t x = 0; x < WHEELS; x++)
      {
        if (spin[y].symbol == spin[x].symbol && y != x)
        {
          matches++;
          symbol = spin[y].symbol;
        }
      }
      if (symbol == 255 || symbol == FACE)
      {
        symbol = spin[y].symbol;
      }
    }
    if (matches > bestMatch)
    {
      bestMatch = matches;
    }
  }
  Serial.println("Matches " + String(bestMatch) + ", Sevens " + String(sevens) + ", Faces " + String(faces) + ", Symbol " + String(symbol));
  if (bestMatch == 0 && faces > 0)
  {
    bestMatch = 1;
  }
  if (sevens > 0)
  {
    sevens += faces;
  }
  bestMatch += faces;
  if (bestMatch > 0)
  {
    switch (sevens)
    {
      case 3: payout = THREE_SEVEN_PAYOUT; playWinSoundLong(5); break;
      case 2: payout = TWO_SEVEN_PAYOUT; playWinSoundLong(3); break;
      case 1: payout = ONE_SEVEN_PAYOUT; playWinSoundLong(2); break;
      default:
        switch (bestMatch)
        {
          case 3: payout = THREE_SYMBOL_PAYOUT; playWinSoundLong(4); break;
          case 2: payout = TWO_SYMBOL_PAYOUT; playWinSoundLong(1); break;
        }
        break;
    }
    if (faces > 0 && faces < 3)
    {
      payout = payout >> 1;
    }
    if (payout > 0)
    {
      flashSymbol(symbol);
    }
  }
  return payout;
}

//---------------------------------------------------------------------------

//Flashes any wheel that is showing the specified symbol or face symbol
void flashSymbol(uint8_t symbol)
{
  Serial.println("Flashing " + String(symbol));
  bool on = true;
  uint8_t row = symbol << 3;
  for (uint8_t r = 0; r < FLASH_REPEAT; r++)
  {
    for (uint8_t j = 0; j < WHEELS; j++)
    {
      if (spin[j].symbol == symbol)
      {
        for (int8_t i = 7; i >= 0; i--)
        {
          reels[j][i] = (on) ? 0 : getReelRow((row + i) % TOTAL_REEL_SYMBOL_ROWS);
        }
        lcd.createChar(j, &reels[j][0]);
        lcd.setCursor(j * 5 + 2, 0);
        lcd.write(j);
      }
      else if (spin[j].symbol == FACE)
      {
        for (int8_t i = 7; i >= 0; i--)
        {
          reels[j][i] = (on) ? 0 : getReelRow(((FACE << 3) + i) % TOTAL_REEL_SYMBOL_ROWS);
        }
        lcd.createChar(j, &reels[j][0]);
        lcd.setCursor(j * 5 + 2, 0);
        lcd.write(j);
      }
    }
    on = !on;
    delay(FLASH_TIME);
  }
}

//--------------------------------------------------------------------------------------------

//Display the current symbol of the specified wheel
void displayWheelSymbol(int wheel)
{
  for (int8_t i = 7; i >= 0; i--)
  {
    reels[wheel][i] = getReelRow((spin[wheel].row + i) % TOTAL_REEL_SYMBOL_ROWS);
  }
  lcd.createChar(wheel, &reels[wheel][0]);
  lcd.setCursor(wheel * 5 + 2, 0);
  lcd.write(wheel);
}

//-----------------------------------------------------------------------------------

//Read a row from the reels either from FLASH memory or RAM
uint8_t getReelRow(uint8_t row)
{
  #ifdef REELS_IN_FLASH
    return pgm_read_byte(reel + row);
  #else
    return reel[row];
  #endif
}
Blackjack.hC/C++
/*
 Arduino 1602 Casino
 by John Bradnam (jbrad2089@gmail.com)

 Blackjack.h
 Routines for 1602 blackjack
  
*/

#include "Hardware.h"
#include "Game.h"
#include "Symbols.h"

char strBlackjack[] = "Blackjack";
char strHitOrStand[] = "H or S?";
char strPlayer[] = "Player ";
char strDealer[] = "Dealer ";
char strWins[] = "Wins";
char strLoses[] = "Loses";
char strBusts[] = "Busts";
char strDraw[] = "Draw";
char strYouWin[] = "You Win ";
char strYouLose[] = "You Lose ";
char strTappedOut[] = "Tapped out!";
char strYourBet[] = "Your Bet? ";
char strShuffling[] = "Shuffling";

enum bjPlayModeEnum { BJ_NOINPUT, BJ_GETBET, BJ_HITORSTAND, BJ_STARTOVER };
enum bjDealModeEnum { DEALER_FIRST, PLAYER_FIRST, DEALER_SECOND, PLAYER_SECOND, PLAYER_NEXT, DEALER_NEXT };

#define TOTAL_DECK_CARDS 52
#define TOTAL_HAND_CARDS 7

byte deck[TOTAL_DECK_CARDS];    //Holds current pack
byte nextCardToDraw = 0;        //Next card to draw from pack
byte player[TOTAL_HAND_CARDS];  //Player cards
byte nextPlayerCard = 0;        //Next card player will be dealt
byte dealer[TOTAL_HAND_CARDS];  //Dealer cards
byte nextDealerCard = 0;        //Next card dealer will be dealt
bool hideDealerCard = true;     //Whether dealers first card is hidden
bjDealModeEnum bjDealMode;      //Current card being dealt
bjPlayModeEnum bjPlayMode;      //Current mode of play

//-------------------------------- FUNCTIONS -------------------------------------------------

void blackjackSetup();
bool blackjackLoop();
void startGame();
void processHitStand();
void processPlayerHit(byte card);
void dealerHit(); 
void processDealerHit(byte card);
void dealInitialCards(byte card);
void dealCard();
void displayHands();
void displayHand(byte* hand, byte nextCard, bool hideFirstCard);
byte countHand(byte* hand, byte nextCard);
void processTestCards();
void playerBlackjack();
void playerWin(); 
void playerBust(); 
void dealerBlackjack();
void dealerWin();
void dealerBust(); 
void playerDraw(); 
void shuffleDeck();
void showBlackjackSplashScreen();

//--------------------------------------------------------------------------------------------

//Setup the game
void blackjackSetup()
{
  //Define all the new card characters
  lcd.createChar(HEART, &cards[HEART][0]);
  lcd.createChar(DIAMOND, &cards[DIAMOND][0]);
  lcd.createChar(SPADE, &cards[SPADE][0]);
  lcd.createChar(CLUB, &cards[CLUB][0]);
  lcd.createChar(BACK_1, &cards[BACK_1][0]);
  lcd.createChar(BACK_2, &cards[BACK_2][0]);
  lcd.createChar(TEN, &cards[TEN][0]);

  //Initialise deck of cards
  for (byte i = 0; i < TOTAL_DECK_CARDS; i++)
  {
    deck[i] = i;
  }

  showBlackjackSplashScreen();
  randomSeed(millis());
  shuffleDeck();
  startGame();
}

//-----------------------------------------------------------------

//Main program loop
// Returns true to exit game
bool blackjackLoop()
{
  //Get button pressed
  lastButtonPressed = buttonPressed(NULL);
  while ((lastButtonPressed != BTN_HIT || bjPlayMode != BJ_GETBET) && lastButtonPressed != BTN_NONE)
  {
    Serial.println("bjPlayMode=" + String(bjPlayMode) + ", lastButtonPressed=" + String(lastButtonPressed));
    switch (bjPlayMode) 
    {
      case BJ_GETBET: 
        if (processGetBet() == BTN_STAND)
        {
          bjPlayMode == BJ_NOINPUT;
          // Deal initial hand.
          bjDealMode = DEALER_FIRST;
          dealCard();
        }
        break;
        
      case BJ_HITORSTAND: processHitStand(); break;
      case BJ_STARTOVER: startGame(); break;
    }
    lastButtonPressed = buttonPressed(NULL);
  }
  return (lastButtonPressed == BTN_HIT);
}
  
//-----------------------------------------------------------------

//Start a new game
void startGame()
{
  nextDealerCard = 0;
  nextPlayerCard = 0;
  lastButtonPressed = BTN_NONE;
  lcd.clear();
  if (creditBalance < BET_MIN) 
  {
    // Yes, Hope you have more in the ATM.
    lcd.clear();
    lcd.print(strTappedOut);
    creditBalance = animateBank(creditBalance, STARTING_CREDIT_BALANCE, DIGIT_DELAY_TIME, 50);
  }
  creditBalance = min(creditBalance, 99999);
  lcd.clear();
  lcd.print(strYourBet);
  animateBank(creditBalance, creditBalance, DIGIT_DELAY_TIME, 50);
  displayBet(bet);
  bjPlayMode = BJ_GETBET;
}

//-----------------------------------------------------------------

//Invoked by key press in BJ_HITORSTAND mode
void processHitStand() 
{
  switch (lastButtonPressed) 
  {
    case BTN_HIT:
      bjDealMode = PLAYER_NEXT;
      dealCard();
      break;

    case BTN_STAND:
      bjPlayMode = BJ_NOINPUT;
      hideDealerCard = false;
      dealerHit();
      break;

    case BTN_LESS:
    case BTN_MORE:
      playBadKeySound();
      break;
  }
}

//-----------------------------------------------------------------

//Player has requested another card
void processPlayerHit(byte card) 
{
  player[nextPlayerCard] = card;
  nextPlayerCard++;
  displayHands();
  lcd.setCursor(9, 0);
  lcd.print(strHitOrStand);
  int playerCount = countHand(player, nextPlayerCard);
  if (playerCount > 21) 
  {
    hideDealerCard = false;
    displayHands();
    int dealerCount = countHand(dealer, nextDealerCard);
    displayCount(dealerCount, 0);
    displayCount(playerCount, 1);
    bjPlayMode = BJ_NOINPUT;
    delay(2000);
    playerBust();
  }
}

//-----------------------------------------------------------------

//Player has stood, start playing dealers hand
void dealerHit() 
{
  //Play out dealers hand - dealer must draw on 16 or less
  int dealerCount = countHand(dealer, nextDealerCard);
  int playerCount = countHand(player, nextPlayerCard);
  if (dealerCount < 17) 
  {
    bjDealMode = DEALER_NEXT;
    dealCard();
  }
  else 
  {
    displayHands();
    displayCount(dealerCount, 0);
    displayCount(playerCount, 1);
    delay(2000);
    if (dealerCount > 21)
      dealerBust();
    else if (dealerCount > playerCount)
      dealerWin();
    else if (dealerCount < playerCount)
      playerWin();
    else
      playerDraw();
  }
}

//-----------------------------------------------------------------

//Dealer was forced to requested another card
void processDealerHit(byte card) 
{
  dealer[nextDealerCard] = card;
  nextDealerCard++;
  displayHands();
  delay(1000);
  dealerHit();
}

//-----------------------------------------------------------------

//Deals dealers and players first two cards
void dealInitialCards(byte card) 
{
  // Deal card.
  if (bjDealMode == DEALER_FIRST)
  {
    hideDealerCard = true;
  }
  if (((int)bjDealMode & 1) == 0) 
  {
    dealer[nextDealerCard] = card;
    nextDealerCard++;
  }
  else {
    player[nextPlayerCard] = card;
    nextPlayerCard++;
  }
  displayHands();
  bjDealMode = (bjDealModeEnum)((int)bjDealMode + 1);
  
  if (bjDealMode == PLAYER_NEXT)
    processTestCards();
  else
    dealCard();
};

//-----------------------------------------------------------------

//Gets next card from deck and shuffles if necessaty.
//bjDealMode controls where this function returns to.
void dealCard() 
{
  byte card = deck[nextCardToDraw];
  nextCardToDraw++;
  if (nextCardToDraw >= TOTAL_DECK_CARDS) 
    shuffleDeck();
  if (bjDealMode == PLAYER_NEXT)
    processPlayerHit(card);
  else if (bjDealMode == DEALER_NEXT)
    processDealerHit(card);
  else
    dealInitialCards(card);
};

//-----------------------------------------------------------------

// Display both hands.
void displayHands() 
{
  // Display dealer's hand.
  lcd.clear();
  lcd.print("D:");
  displayHand(dealer, nextDealerCard, hideDealerCard);

  // Display player's hand.
  lcd.setCursor(0, 1);
  lcd.print("P:");
  displayHand(player, nextPlayerCard, false);
}

//-----------------------------------------------------------------

// Display hand.
// hand - pointer to player or dealer hand array
// nextCard - position to store next card in above array
// hideFirstCard - true if the first card is hidden
void displayHand(byte* hand, byte nextCard, bool hideFirstCard) 
{
  // Display cards.
  for (byte card = 0; card < nextCard; card++) 
  {
    if (hideFirstCard && card == 0) 
    {
      //Show back of first card.
      lcd.write(BACK_1);
      lcd.write(BACK_2);
    }
    else 
    {
      byte suit = hand[card] / 13;
      byte face = hand[card] % 13;
      // No, display card rank.
      switch (face)
      {
        case 0: lcd.print("A"); break;
        case 9: lcd.write(TEN); break;
        case 10: lcd.print("J"); break;
        case 11: lcd.print("Q"); break;
        case 12: lcd.print("K"); break;
        default: lcd.print(face + 1);
      }
      // Display card suit.  
      lcd.write(suit);
    }
  }
}

//-----------------------------------------------------------------

// Count the given hand.
// hand - pointer to player or dealer hand array
// nextCard - position to store next card in above array
// returns maximum value of a valid hand;
byte countHand(byte* hand, byte nextCard) 
{
  byte score = 0;
  byte aces = 0;
  
  //Sum all the cards counting aces as 11 and keeping track of how many are present
  for (byte card = 0; card < nextCard; card++) 
  {
    byte face = hand[card] % 13;
    if (face == 0)
    {
      aces++;
    }
    score += (face == 0) ? 11 : (face > 9) ? 10 : (face + 1);
  }

  //If over 21 and we have aces, start treating the ace value as 1 and see if we can
  //get under 21;
  while (score > 21 && aces > 0)
  {
    score = score - 10;
    aces--;
  }
  return score;
}

//-----------------------------------------------------------------

void processTestCards () 
{
  if (nextDealerCard == 2 && countHand(dealer, nextDealerCard) == 21) 
  {
    //Dealer has black jack
    hideDealerCard = false;
    displayHands();
    lcd.setCursor(7, 0);
    lcd.print(strBlackjack);
    // Give player the bad news.
    delay(2000);
    dealerBlackjack();
  }
  else if (nextPlayerCard == 2 && countHand(player, nextPlayerCard) == 21) 
  {
    hideDealerCard = false;
    displayHands();
    lcd.setCursor(7, 1);
    lcd.print(strBlackjack);
    delay(2000);
    playerBlackjack();
  }
  else {
    //Accept hits until player stands or busts
    lcd.setCursor(9, 0);
    lcd.print(strHitOrStand);
    bjPlayMode = BJ_HITORSTAND;
  }
}

//-----------------------------------------------------------------

//Player has blackjack
void playerBlackjack() 
{
  lcd.clear();
  lcd.print(strPlayer);
  lcd.print(strBlackjack);
  displayBankAndBet(creditBalance, bet);
  creditBalance = animateBank(creditBalance, creditBalance - bet * 1.5, DIGIT_DELAY_TIME, 1);
  playWinSoundShort();
  lastButtonPressed = BTN_NONE;
  bjPlayMode = BJ_STARTOVER;
};

//-----------------------------------------------------------------

//Player wins
void playerWin() 
{
  lcd.clear();
  lcd.print(strPlayer);
  lcd.print(strWins);
  displayBankAndBet(creditBalance, bet);
  creditBalance = animateBank(creditBalance, creditBalance + bet, DIGIT_DELAY_TIME, 1);
  playWinSoundShort();
  lastButtonPressed = BTN_NONE;
  bjPlayMode = BJ_STARTOVER;
};

//-----------------------------------------------------------------

//Player busts
void playerBust() 
{
  lcd.clear();
  lcd.print(strPlayer);
  lcd.print(strBusts);
  displayBankAndBet(creditBalance, bet);
  creditBalance = animateBank(creditBalance, creditBalance - bet, DIGIT_DELAY_TIME, 1);
  playLoseSound();
  lastButtonPressed = BTN_NONE;
  bjPlayMode = BJ_STARTOVER;
};

//-----------------------------------------------------------------

//Dealer has blackjack
void dealerBlackjack() 
{
  // Player Blackjack?
  lcd.setCursor(7, 1);
  if ((nextPlayerCard == 2) && (countHand(player, nextPlayerCard) == 21)) {
    // Yes, Push.
    lcd.print(strDraw);
  }
  else {
    // No, Loser.
    lcd.print(strYouLose);
    creditBalance -= bet;
    playLoseSound();
  }
  lastButtonPressed = BTN_NONE;
  bjPlayMode = BJ_STARTOVER;
};

//-----------------------------------------------------------------

//Dealer wins
void dealerWin() 
{
  // Dealer wins.
  lcd.clear();
  lcd.print(strDealer);
  lcd.print(strWins);
  displayBankAndBet(creditBalance, bet);
  creditBalance = animateBank(creditBalance, creditBalance - bet, DIGIT_DELAY_TIME, 1);
  playLoseSound();
  lastButtonPressed = BTN_NONE;
  bjPlayMode = BJ_STARTOVER;
};

//-----------------------------------------------------------------

//Dealer busts
void dealerBust() 
{
  lcd.clear();
  lcd.print(strDealer);
  lcd.print(strBusts);
  displayBankAndBet(creditBalance, bet);
  creditBalance = animateBank(creditBalance, creditBalance + bet, DIGIT_DELAY_TIME, 1);
  playWinSoundShort();
  lastButtonPressed = BTN_NONE;
  bjPlayMode = BJ_STARTOVER;
};

//-----------------------------------------------------------------

//Draw
void playerDraw() 
{
  lcd.clear();
  lcd.print(strDraw);
  displayBankAndBet(creditBalance, bet);
  lastButtonPressed = BTN_NONE;
  bjPlayMode = BJ_STARTOVER;
};

//-----------------------------------------------------------------

//Show the startup screen
void shuffleDeck()
{
  lcd.clear();
  lcd.print(strShuffling);
  // Swap each card in the deck with a random card.
  for (byte cardShuffle = 0; cardShuffle < TOTAL_DECK_CARDS ; cardShuffle++) 
  {
    // Init random card to swap (could be the same card).
    byte cardRandom = random(0, TOTAL_DECK_CARDS);
    byte t =  deck[cardShuffle];
    deck[cardShuffle] = deck[cardRandom];
    deck[cardRandom] = t;
  }
  nextCardToDraw = 0;
  // Slow humans.
  delay(2000);
  // Display hands (to get rid of "Shuffling" message).
  displayHands();
}

//-----------------------------------------------------------------

//Show the startup screen
void showBlackjackSplashScreen()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("** ");
  lcd.print(strBlackjack);
  lcd.print(" **");
  lcd.setCursor(5, 1);
  lcd.write(BACK_1);
  lcd.write(BACK_2);
  delay(1000);
  
  lcd.setCursor(9, 1);
  lcd.write(BACK_1);
  lcd.write(BACK_2);
  delay(1000);
  
  lcd.setCursor(5, 1);
  lcd.print("J");
  lcd.write(DIAMOND);
  delay(1000);
  
  lcd.setCursor(9, 1);
  lcd.print("A");
  lcd.write(SPADE);
  delay(1000);
  
  lcd.setCursor(0, 0);
  lcd.print("   ");
  lcd.print(strBlackjack);
  lcd.print("!   ");
  
  int animDelay = 10;
  while (animDelay < 31)
  {
    animDelay = animDelay + 1;
    if ((animDelay % 2) > 0)
      lcd.display();
    else
      lcd.noDisplay();
    delay((31 - animDelay) * (31 - animDelay));
  };
  delay(1000);
};
Craps.hC/C++
/*
 Arduino 1602 Casino
 by John Bradnam (jbrad2089@gmail.com)

 Craps.h
 Routines for 1602 craps
  
 Throws first TWO dice
 7 or 11 - Automatic WIN
 2 or 3 or 12 - Automatic LOSS
 4, 5, 6, 8, 9, and 10 - Throws Last two dice
   - If match player winds
   - If 7, player losses
   - Otherwise player throws last two dice again
*/

#include "Hardware.h"
#include "Game.h"
#include "Symbols.h"

enum crapsPlayModeEnum { CRAPS_NOINPUT, CRAPS_GETBET, CRAPS_THROW };

crapsPlayModeEnum crapsPlayMode = CRAPS_GETBET;
int firstSum;

//-------------------------------- FUNCTIONS -------------------------------------------------

void crapsSetup();
bool crapsLoop();
void processThrow(bool first);
int newRoll(int lastRoll);
int roll(int x1, int x2, int y);

//--------------------------------------------------------------------------------------------

//Setup the game
void crapsSetup()
{
  //Define all the new card characters
  lcd.createChar(ONE, &dice[ONE][0]);
  lcd.createChar(TWO, &dice[TWO][0]);
  lcd.createChar(THREE, &dice[THREE][0]);
  lcd.createChar(FOUR, &dice[FOUR][0]);
  lcd.createChar(FIVE, &dice[FIVE][0]);
  lcd.createChar(SIX, &dice[SIX][0]);

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("[?] [?]  [ ] [ ]");

  //Animate balance upto current balance
  animateBank(0, creditBalance, DIGIT_DELAY_TIME, 50);
 
  crapsPlayMode = CRAPS_GETBET;
  bet = BET_MIN;
  displayBet(bet);
}

//-----------------------------------------------------------------

//Main program loop
// Returns true to exit game
bool crapsLoop()
{
  //Get button pressed
  lastButtonPressed = buttonPressed(NULL);
  while (lastButtonPressed != BTN_HIT && lastButtonPressed != BTN_NONE)
  {
    switch (crapsPlayMode) 
    {
      case CRAPS_GETBET: 
        if (processGetBet() == BTN_STAND) 
        {
          processThrow(true);
        }
        break;
        
      case CRAPS_THROW: 
        processThrow(false);
        break;
    }
    lastButtonPressed = buttonPressed(NULL);
  }
  return (lastButtonPressed == BTN_HIT);
}
  
//-----------------------------------------------------------------

//Throw a pair of dice
//  first = true for first set of dice, false for second set
void processThrow(bool first)
{
  int payout = 0;
  if (crapsPlayMode == CRAPS_THROW)
  {
    int secondSum = roll(10, 14, 0);
    if (secondSum == firstSum)
    {
      //Player wins
      payout = bet;
      playWinSoundShort();
    }
    else if (secondSum == 7)
    {
      //Player loses
      payout = -bet;
      playLoseSound();
    }
  }
  else
  {
    //Clear second dice
    lcd.setCursor(10, 0);
    lcd.print(" ");
    lcd.setCursor(14, 0);
    lcd.print(" ");
    
    firstSum = roll(1, 5, 0);
    if (firstSum == 7 || firstSum == 11)
    {
      //Automatic Win
      payout = bet;
      playWinSoundShort();
    }
    else if (firstSum == 2 || firstSum == 3 || firstSum == 12)
    {
      //Automatic Loss
      payout = -bet;
      playLoseSound();
    }
    else
    {
      //Throw second pair
      crapsPlayMode = CRAPS_THROW;
    }
  }
  if (payout != 0)
  {
    long newBalance = min(creditBalance + payout, 99999);
    Serial.println("creditBalance = " + String(creditBalance) + ", payout = " + String(payout) + ", newBalance = " + String(newBalance));
    creditBalance = animateBank(creditBalance, newBalance, DIGIT_DELAY_TIME, 1);
    if (creditBalance < 0)
    {
      playLoseSound();
      creditBalance = animateBank(creditBalance, STARTING_CREDIT_BALANCE, DIGIT_DELAY_TIME, 10);
    }
    crapsPlayMode = CRAPS_GETBET;
  }
}

//-----------------------------------------------------------------

//Get a random roll that isn't the same as the last value
//lastRoll is current dice value
//returns new roll
int newRoll(int lastRoll)
{
  int roll = lastRoll;
  while (roll == lastRoll)
  {
    roll = random(1, 7);
  }
  return roll;
}

//-----------------------------------------------------------------

//Roll the dice
//diceMask is one or more DICE_X constants
int roll(int x1, int x2, int y)
{
  int d = 0;
  int dice1 = -1;
  int dice2 = -1;
  for (int i = 0; i < (random(7, 30)); i++) 
  {
    dice1 = newRoll(dice1);
    lcd.setCursor(x1, y);
    lcd.write(dice1 - 1);
    dice2 = newRoll(dice2);
    lcd.setCursor(x2, y);
    lcd.write(dice2 - 1);
    playRollTone();
    delay(d);
    d = d + 50;
  }
  return dice1 + dice2;
}
Game.hC/C++
/*
 Arduino 1602 Casino
 by John Bradnam (jbrad2089@gmail.com)

 Game.h
 Common Game and functions
*/

#pragma once

#define BET_INC 2
#define BET_MIN 2
#define BET_MAX 20
int bet = BET_MIN;              //Amount been wagered

#define DIGIT_DELAY_TIME 50

#define STARTING_CREDIT_BALANCE 1000    // Number of credits you have at "factory reset".
long creditBalance = 0;                 // Current balance
buttonEnum lastButtonPressed;   //Stores the last button pressed


//-------------------------------- FUNCTIONS -------------------------------------------------

buttonEnum processGetBet(); 
void displayBankAndBet(long bank, int bet);
long animateBank(long oldBalance, long newBalance, long delayTime, int rate);
void displayBet(int bet);
void displayCount(int count, int line);
void displayNumber(int x, int y, long number, int padding, bool leadingZeros);

//-----------------------------------------------------------------

//Invoked by key press in MODE_GETBET mode
buttonEnum processGetBet() 
{
  // Prompt user.
  switch (lastButtonPressed) 
  {
    case BTN_MORE:
      if ((bet < BET_MAX) && (bet < creditBalance))
        bet += BET_INC;
      else
        playBadKeySound();
      break;

    case BTN_LESS:
      if (bet > BET_MIN)
        bet -= BET_INC;
      else
        playBadKeySound();
      break;

    case BTN_STAND:
      if (bet > BET_MAX || bet > creditBalance) 
      {
        lastButtonPressed = BTN_NONE;
        playBadKeySound();
      }
      break;
  }
  displayBet(bet);
  return lastButtonPressed;
};

//-----------------------------------------------------------------

//Display the current bank balance and the current bet
void displayBankAndBet(long bank, int bet)
{
  lcdClearRow(1);
  animateBank(bank, bank, DIGIT_DELAY_TIME, 50);
  displayBet(bet);
};

//-----------------------------------------------------------------------------------

//animate the change in credit balance
// oldBalance - current bank balance
// newBalance - new bank balance
// delayTime - time between screen updates
// rate - rate of change from old balance to new balance
long animateBank(long oldBalance, long newBalance, long delayTime, int rate)
{
  unsigned int difference;
  int8_t direction;
  if (oldBalance != newBalance)
  {
    if (oldBalance > newBalance)
    {
      difference = oldBalance - newBalance;
      direction = -1;
    }
    else
    {
      difference = newBalance - oldBalance;
      direction = 1;
    }
    
    for (unsigned int i = 0; i < difference; i += rate)
    {
      oldBalance = oldBalance + direction * rate;
      lcd.setCursor(0, 1);
      lcd.print((oldBalance >= 0) ? " " : "-");
      lcd.print("$     ");
      displayNumber(2, 1, abs(oldBalance), 0, false);
      beepDigit();
      delay(delayTime);
    }
  }
  lcd.setCursor(0, 1);
  lcd.print((newBalance >= 0) ? " " : "-");
  lcd.print("$     ");
  displayNumber(2, 1, abs(newBalance), 0, false);
  return newBalance;
}

//-----------------------------------------------------------------------------------

//Display a the bet
void displayBet(int bet)
{
  lcd.setCursor(9,1);
  lcd.print("Bet:$  ");
  lcd.setCursor(14,1);
  lcd.print(bet);
}

//-----------------------------------------------------------------
//Displays a number on the far right of a line

void displayCount(int count, int line) 
{
  if (count > 9)
    lcd.setCursor(14, line);
  else
    lcd.setCursor(15, line);
  lcd.print(count);
};

//-----------------------------------------------------------------------------------

//Display a number on the LCD screen
// x = X position (0..15)
// y = Y position (0..1)
// number = Number to display
// padding = Fixed length of number or 0 for no padding (Doesn't include negative sign if any)
// leadingZeros = true - pad with zeros otherwise pad with spaces
void displayNumber(int x, int y, long number, int padding, bool leadingZeros)
{
  bool negative = (number < 0);
  number = abs(number);
  if (padding == 0)
  {
    padding = (number > 0) ? floor(log10(number)) + 1 : 1;
  }
  x = x + padding + ((negative) ? 1 : 0);
  bool first = true;
  for (int i = 0; i < padding; i++)
  {
    x--;
    lcd.setCursor(x, y);
    if (number != 0 || first)
    {
      lcd.print(number % 10);
      number = number / 10;
      first = false;
    }
    else if (negative && !leadingZeros)
    {
      lcd.print("-");
      negative = false;
    }
  else
    {
      lcd.print((leadingZeros) ? "0" : " ");
    }
  }
  if (negative && leadingZeros)
  {
    x--;
    lcd.setCursor(x, y);
    lcd.print("-");
  }
}
Hardware.hC/C++
/*
 Arduino 1602 Casino
 by John Bradnam (jbrad2089@gmail.com)

 Hardware.h
 Hardware definitions
 Print functions
 Button functions
 Sound functions
*/

#pragma once

#include <LiquidCrystal.h>
#include <TimerFreeTone.h>  // https://bitbucket.org/teckel12/arduino-timer-free-tone/wiki/Home

//LCD Screen
#define LCD_RS 9
#define LCD_EN 10
#define LCD_D4 12
#define LCD_D5 13
#define LCD_D6 A1
#define LCD_D7 A2

#define HIT_PIN 5
#define STAND_PIN 4
#define LESS_PIN 3
#define MORE_PIN 2

#define TONE_PIN 11
#define BUZZER_DDR  DDRB
#define BUZZER_PORT PORTB
#define BUZZER_PIN  DDB3

enum buttonEnum { BTN_NONE, BTN_HIT, BTN_STAND, BTN_LESS, BTN_MORE };

//Initialize the LCD
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

//--------------------------- FUNCTIONS ------------------------------------------------------

void hardwareSetup();
void lcdClearRow(int row);
buttonEnum buttonPressed(void (*pBackgroundFunction)());
bool IsSinglePress(int pin, void (*pBackgroundFunction)());
void beepWheel();
void beepDigit(); 
void playSiren();
void playRollTone();
void playRolledTone();
void playBadKeySound();
void playWinSoundLong(uint8_t repeat);
void playWinSoundShort();
void playLoseSound();

//--------------------------------------------------------------------------------------------

//Setup hardware pins
void hardwareSetup()
{
  Serial.begin(115200);
  
  pinMode(TONE_PIN, OUTPUT);
  pinMode(HIT_PIN, INPUT_PULLUP);
  pinMode(STAND_PIN, INPUT_PULLUP);
  pinMode(LESS_PIN, INPUT_PULLUP);
  pinMode(MORE_PIN, INPUT_PULLUP);

  //Setup random seed
  randomSeed(analogRead(A0));
  
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.clear();
}

//-----------------------------------------------------------------

// Clear LCD single row.
// Leaves cursor at the beginning of the cleared row.
void lcdClearRow(int row) 
{
  if (row >= 0 && row < 2) 
  {
    lcd.setCursor(0, row);
    for (int x = 0; x < 16; x++) 
    {
      lcd.print(" ");
    }
    lcd.setCursor(0, row);
  }
};

//------------------------------------------------------------------

//Test all buttons to see if pressed
buttonEnum buttonPressed(void (*pBackgroundFunction)())
{
  if (IsSinglePress(HIT_PIN, pBackgroundFunction))
    return BTN_HIT;
  else if (IsSinglePress(STAND_PIN, pBackgroundFunction))
    return BTN_STAND;
  else if (IsSinglePress(MORE_PIN, pBackgroundFunction))
    return BTN_MORE;
  else if (IsSinglePress(LESS_PIN, pBackgroundFunction))
    return BTN_LESS;
  else
    return BTN_NONE;
}

//------------------------------------------------------------------

//Tests if a button is pressed and released
//  pin - pin of the button to test
//  returns true if the button was pressed and released
bool IsSinglePress(int pin, void (*pBackgroundFunction)())
{
  bool pressed = false;
  if (digitalRead(pin) == LOW)
  {
    delay(10);
    if (digitalRead(pin) == LOW)
    {
      while (digitalRead(pin) == LOW)
      {
        if (pBackgroundFunction != NULL)
        {
          pBackgroundFunction();
        }
      }
      pressed = true;
    }
  }
  return pressed;
}

//-----------------------------------------------------------------------------------

//Turn on and off buzzer quickly
void beepWheel() 
{                                     // Beep and flash LED green unless STATE_AUTO
  BUZZER_PORT |= (1 << BUZZER_PIN);   // turn on buzzer
  delay(20);
  BUZZER_PORT &= ~(1 << BUZZER_PIN);  // turn off the buzzer
}

//-----------------------------------------------------------------------------------

//Turn on and off buzzer quickly
void beepDigit() 
{                                     // Beep and flash LED green unless STATE_AUTO
  BUZZER_PORT |= (1 << BUZZER_PIN);   // turn on buzzer
  delay(5);
  BUZZER_PORT &= ~(1 << BUZZER_PIN);  // turn off the buzzer
}

//-----------------------------------------------------------------------------------

//Play the siren sound
void playSiren() 
{
  #define MAX_NOTE 4978               // Maximum high tone in hertz. Used for siren.
  #define MIN_NOTE 31                 // Minimum low tone in hertz. Used for siren.
  
  for (int note = MIN_NOTE; note <= MAX_NOTE; note += 5)
  {                       
    TimerFreeTone(TONE_PIN, note, 1);
  }
}

//------------------------------------------------------------------

void playRollTone()
{
  TimerFreeTone(TONE_PIN, 300,50); 
}

//------------------------------------------------------------------

void playRolledTone()
{
  TimerFreeTone(TONE_PIN, 600, 150);
}

//------------------------------------------------------------------

//Play a short note for wrong key press
void playBadKeySound()
{
  TimerFreeTone(TONE_PIN, 440,100);
}

//-----------------------------------------------------------------------------------

//Play the winning siren multiple times
void playWinSoundLong(uint8_t repeat)
{
  for (uint8_t i = 0; i < repeat; i++)
  {
    playSiren();
  }
}

//------------------------------------------------------------------

//Play a high note as a sign you lost
void playWinSoundShort()
{
  //TimerFreeTone(TONE_PIN,880,300);
  TimerFreeTone(TONE_PIN,880,100); //A5
  TimerFreeTone(TONE_PIN,988,100); //B5
  TimerFreeTone(TONE_PIN,523,100); //C5
  TimerFreeTone(TONE_PIN,988,100); //B5
  TimerFreeTone(TONE_PIN,523,100); //C5
  TimerFreeTone(TONE_PIN,587,100); //D5
  TimerFreeTone(TONE_PIN,523,100); //C5
  TimerFreeTone(TONE_PIN,587,100); //D5
  TimerFreeTone(TONE_PIN,659,100); //E5
  TimerFreeTone(TONE_PIN,587,100); //D5
  TimerFreeTone(TONE_PIN,659,100); //E5
  TimerFreeTone(TONE_PIN,659,100); //E5
  delay(250);
}

//------------------------------------------------------------------------------------------------------------------

//Play wah wah wah wahwahwahwahwahwah
void playLoseSound()
{
  delay(400);
  //wah wah wah wahwahwahwahwahwah
  for(double wah=0; wah<4; wah+=6.541)
  {
    TimerFreeTone(TONE_PIN, 440+wah, 50);
  }
  TimerFreeTone(TONE_PIN, 466.164, 100);
  delay(80);
  for(double wah=0; wah<5; wah+=4.939)
  {
    TimerFreeTone(TONE_PIN, 415.305+wah, 50);
  }
  TimerFreeTone(TONE_PIN, 440.000, 100);
  delay(80);
  for(double wah=0; wah<5; wah+=4.662)
  {
    TimerFreeTone(TONE_PIN, 391.995+wah, 50);
  }
  TimerFreeTone(TONE_PIN, 415.305, 100);
  delay(80);
  for(int j=0; j<7; j++)
  {
    TimerFreeTone(TONE_PIN, 391.995, 70);
    TimerFreeTone(TONE_PIN, 415.305, 70);
  }
  delay(400);
}
Symbols.hC/C++
/*
 Arduino 1602 Casino
 by John Bradnam (jbrad2089@gmail.com)

 Symbols.h
 Symbol definitions for LCD
*/

#pragma once

//------------------------------- SLOT MACINE ----------------------------

#define REELS_IN_FLASH    //Uncomment to put reels in RAM

#define REEL_SYMBOLS 10
#define TOTAL_REEL_SYMBOL_ROWS (REEL_SYMBOLS << 3)

#define SEVEN 4
#define FACE 9

#ifdef REELS_IN_FLASH
const byte reel[] PROGMEM =
#else
const byte reel[] =
#endif
{	
  //Heart
  B00000,
  B01010,
  B11111,
  B11111,
  B11111,
  B01110,
  B00100,
  B00000,

  //Diamond
  B00000,
  B00100,
  B01110,
  B11111,
  B11111,
  B01110,
  B00100,
  B00000,

  //Spade
  B00000,
  B00100,
  B01110,
  B11111,
  B11111,
  B00100,
  B01110,
  B00000,

  //Club
  B00000,
  B01110,
  B01110,
  B11111,
  B11111,
  B00100,
  B01110,
  B00000,

  //7
  B00000,
  B11111,
  B10001,
  B11101,
  B01010,
  B10100,
  B11100,
  B00000,
  
  //#
  B00000,
  B00000,
  B01010,
  B11111,
  B01010,
  B11111,
  B01010,
  B00000,
  
  //3 Bars
  B00000,
  B00000,
  B11111,
  B00000,
  B11111,
  B00000,
  B11111,
  B00000,
  
  //2 Bars
  B00000,
  B00000,
  B11111,
  B00000,
  B00000,
  B11111,
  B00000,
  B00000,

  //1 Bar
  B00000,
  B00000,
  B11111,
  B10001,
  B11111,
  B00000,
  B00000,
  B00000,
  
  //Face
  B00000,
  B11011,
  B11011,
  B00000,
  B10001,
  B01110,
  B00000,
  B00000,
};

//------------------------------- CRAPS ----------------------------

#define DICE_FACES 6

enum DICE_ENUM { ONE, TWO, THREE, FOUR, FIVE, SIX };
const byte dice[DICE_FACES][8] =
{  
  {B00000,B00000,B00000,B00100,B00000,B00000,B00000,B00000}, //One
  {B00000,B00001,B00000,B00000,B00000,B10000,B00000,B00000}, //Two
  {B00000,B10000,B00000,B00100,B00000,B00001,B00000,B00000}, //Three
  {B00000,B10001,B00000,B00000,B00000,B10001,B00000,B00000}, //Four
  {B00000,B10001,B00000,B00100,B00000,B10001,B00000,B00000}, //Five
  {B00000,B10001,B00000,B10001,B00000,B10001,B00000,B00000}  //Six 
};

//------------------------------ BLACKJACK ------------------------------

#define CARD_FACES 7

enum CARD_ENUM { HEART, DIAMOND, SPADE, CLUB, BACK_1, BACK_2, TEN };
const byte cards[CARD_FACES][8] =
{
  {B01010,B11111,B11111,B11111,B01110,B00100,B00000,B00000}, //Heart
  {B00100,B01110,B11111,B11111,B01110,B00100,B00000,B00000}, //Diamond
  {B00100,B01110,B11111,B11111,B00100,B01110,B00000,B00000}, //Spade
  {B01110,B01110,B11111,B11111,B00100,B01110,B00000,B00000}, //Club
  {B11111,B10110,B10101,B10110,B10101,B10110,B10000,B11111}, //Back 1
  {B11111,B00001,B01111,B00101,B00101,B10101,B01001,B11111}, //Back 2
  {B01000,B11000,B01000,B01010,B11101,B00101,B00101,B00010}  //Ten
};

Custom parts and enclosures

Case - Top
Case - Bottom

Schematics

Eagle Files
Schematic and PCB in Eagle format
eagle_files_oJI7qLqJwp.zip
Schematic
Schematic rhbodu1j7o

Comments

Similar projects you might like

Retro Gamer Clock

Project tutorial by John Bradnam

  • 1,051 views
  • 1 comment
  • 6 respects

Minesweeper Game with Arduino

Project tutorial by Lima Moreira

  • 2,182 views
  • 1 comment
  • 2 respects

Arduino LCD Video Game

Project tutorial by xXarduino_11Xx

  • 6,647 views
  • 4 comments
  • 21 respects

How to Make a Walking Robot

Project tutorial by MEGA DAS

  • 947 views
  • 0 comments
  • 7 respects

WW2 Tank Laser Tag Sherman & Panther

Project tutorial by Arduino “having11” Guy

  • 19,976 views
  • 1 comment
  • 60 respects

LCD Game

Project showcase by Team Iron_SalsaStudio

  • 17,862 views
  • 17 comments
  • 27 respects
Add projectSign up / Login