MBED Slot Machine Game using uLCD, Speaker, SD Card Reader, and User Input Hardware

Overview

This project creates a simple slot machine game using the uLCD-144-G2, a speaker with a Class D audio amplifier, an SD card reader, a pushbutton input, and a number keypad.

How the Game Works

The game begins with a start-up screen. A slot machine image is loaded to the uLCD screen and start-up music is played on the speaker. Next, the player is given 50 chips and is prompted for a bet from 1 to 3. After the player inputs his or her bet using the number keypad, the slot machine begins running. Each column stops moving and remains stationary as the pushbutton is pressed. After the push button is pressed three times, all three columns are stopped. The player wins if he or she has managed to get three of the same color circle in the middle row of the 3 x 3 grid. The game continues in this way until the player runs out of chips. When the player runs out of chips, a ‘Game Over’ image is loaded to the uLCD screen.

/media/uploads/ehopf3/start_up_screen.jpg

/media/uploads/ehopf3/game_over.jpg

Pinout

Below are the pin connections for the uLCD, the Micro SD Card Reader the MPR121 keypad, and the speaker and Class D Audio Amplifier.

MBEDuLCD
VU5V
GNDGND
P27TX
P28RX
P30RESET
MBEDMicroSD
P8CS
P5DI
VOUTVCC
P7SCK
GNDGND
P6DO
MBEDMPR121
GNDGND
P9SDA
P10SCL
P26IRQ
VOUTVCC
MBEDTPA2005D1Speaker
GNDPWR-, IN-
VOUTPWR+
P26IN+
OUT++
OUT--

Code

The slot machine was created by coding three threads that create three different-colored filled circles in each column of the 3 x 3 grid and quickly move the circles down across the grid. Various sound effects accompany different parts of the game, including a sound effect for when the push button is pressed and when the player wins or loses. The player wins 50 times their bet for 3 red circles in the middle row, 25 times their bet for 3 blue circles in the middle row, and 10 times their bet for 3 green circles in the middle row. The chip count is updated after each round and displayed on the screen for the player to see. The code is shown below. The MBED libraries for the SD Card Reader, Wave Player, RTOS, Pin Detect, 4DGL-uLCD-SE, and the MPR121 Keypad are also required.

main.cpp

#include "mbed.h"
#include "uLCD_4DGL.h"
#include "rtos.h"
#include "PinDetect.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include <mpr121.h>

// uses pushbutton interrupt and debouncing to stop each column and now 
// includes keypad

// Initialize uLCD (serial tx, serial rx, reset pin)
uLCD_4DGL uLCD(p28,p27,p30);

// LCD Mutex to make the lcd lib thread safe
Mutex ulcd_mutex;

// initialize sd card
SDFileSystem sd(p5, p6, p7, p8, "sd");

// initialize analog out
AnalogOut DACout(p18);

// initialize wave player
wave_player waver(&DACout);

// Initialize pushbutton
PinDetect pb1(p14);

// Create the interrupt receiver object on pin 26
InterruptIn interrupt(p26);

// Setup the i2c bus on pins 9 and 10
I2C i2c(p9, p10);

// Setup the Mpr121:
// constructor(i2c object, i2c address of the mpr121)
Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);

// create push button count int
volatile int count=0;

// create chip count
volatile int chips=50;

// scroll forward through songs
void pb1_hit_callback (void)
{
    count++;
}

// Function to create checkered board slot machine
void createBoard (void)
{
    ulcd_mutex.lock();
    
    uLCD.cls();
    uLCD.background_color(BLACK);
    
    // create borders for slot machine
    uLCD.line(22, 0, 22, 84, WHITE); // left border
    uLCD.line(106, 0, 106, 84, WHITE); // right border
    uLCD.line(22, 0, 106, 0, WHITE); // top border
    uLCD.line(22, 84, 106, 84, WHITE); // bottom border
    
    // create checkered pattern
    uLCD.line(50, 0, 50, 84, WHITE); // left vertical checkered
    uLCD.line(78, 0, 78, 84, WHITE); // right vertical checkered
    uLCD.line(22, 28, 106, 28, WHITE); // top horizontal checkered
    uLCD.line(22, 56, 106, 56, WHITE); // bottom horizontal checkered
    
    ulcd_mutex.unlock();       
}

// Initialize left column shape y positions
volatile int red_left_y, green1_left_y, blue1_left_y,
    green2_left_y, blue2_left_y;

// Initialize center column shape y positions
volatile int blue1_center_y, red_center_y, green1_center_y, 
    blue2_center_y, green2_center_y;
   
// Initialize right column shape y positions
volatile int blue_right_y, green1_right_y, red_right_y, 
    green2_right_y;
    
// Initialize shape radius
volatile int radius = 10;

// Thread 1: Controls left column of slot machine
void thread1(void const *args)
{
    // Initialize left column shape y positions
    red_left_y = 14;
    green1_left_y = 42;
    blue1_left_y = 70;
    green2_left_y = 150;
    blue2_left_y = 150;
    
    // Initialize left column shape x position
    int left_x = 36;
    
    // Create left column pause int
    int left_pause = rand() % 75 + 75;
    
    // Create left column waits
    bool red_left_wait = false;
    bool green1_left_wait = false;
    bool blue1_left_wait = false;
    bool green2_left_wait = false;
    bool blue2_left_wait = true;
    
    while(1) {
        ulcd_mutex.lock();
        
        // left column shapes
        uLCD.filled_circle(left_x, red_left_y, radius, RED);
        uLCD.filled_circle(left_x, green1_left_y, radius, GREEN);
        uLCD.filled_circle(left_x, blue1_left_y, radius, BLUE);
        uLCD.filled_circle(left_x, green2_left_y, radius, GREEN);
        uLCD.filled_circle(left_x, blue2_left_y, radius, BLUE);
        
        ulcd_mutex.unlock();
        
        while (count >= 1) { 
        }
        
        // move left column shape positions
        // red_left move
        if ((red_left_y == 14) || (red_left_y == 42)) {
            red_left_y = red_left_y + 28;
        } 
        else if (red_left_y == 70) {
            red_left_y = 150;
        }
        else if ((red_left_y == 150) && (red_left_wait)) {
            red_left_y = 14;
            red_left_wait = false;
        }
        else {
            red_left_y = 150;
            red_left_wait = true;
        }
        
        // green1_left move
        if ((green1_left_y == 14) || (green1_left_y == 42)) {
            green1_left_y = green1_left_y + 28;
        } 
        else if (green1_left_y == 70) {
            green1_left_y = 150;
        }
        else if ((green1_left_y == 150) && (green1_left_wait)) {
            green1_left_y = 14;
            green1_left_wait = false;
        }
        else {
            green1_left_y = 150;
            green1_left_wait = true;
        }
        
        // blue1_left move
        if ((blue1_left_y == 14) || (blue1_left_y == 42)) {
            blue1_left_y = blue1_left_y + 28;
        } 
        else if (blue1_left_y == 70) {
            blue1_left_y = 150;
        }
        else if ((blue1_left_y == 150) && (blue1_left_wait)) {
            blue1_left_y = 14;
            blue1_left_wait = false;
        }
        else {
            blue1_left_y = 150;
            blue1_left_wait = true;
        }
        
        // green2_left move
        if ((green2_left_y == 14) || (green2_left_y == 42)) {
            green2_left_y = green2_left_y + 28;
        } 
        else if (green2_left_y == 70) {
            green2_left_y = 150;
        }
        else if ((green2_left_y == 150) && (green2_left_wait)) {
            green2_left_y = 14;
            green2_left_wait = false;
        }
        else {
            green2_left_y = 150;
            green2_left_wait = true;
        }
        
        //blue2_left move
        if ((blue2_left_y == 14) || (blue2_left_y == 42)) {
            blue2_left_y = blue2_left_y + 28;
        } 
        else if (blue2_left_y == 70) {
            blue2_left_y = 150;
        }
        else if ((blue2_left_y == 150) && (blue2_left_wait)) {
            blue2_left_y = 14;
            blue2_left_wait = false;
        }
        else {
            blue2_left_y = 150;
            blue2_left_wait = true;
        }
        Thread::wait(left_pause);
    }
}

// Thread 2: Controls center column of slot machine
void thread2(void const *args)
{
    // Initialize center column shape y positions
    blue1_center_y = 14;
    red_center_y = 42;
    green1_center_y = 70;
    blue2_center_y = 150;
    green2_center_y = 150;
    
    // Initialize center column shape x position
    int center_x = 64;
    
    // Create center column pause int
    int center_pause = rand() % 30 + 20;
    
    // Create center column waits
    bool blue1_center_wait = false;
    bool red_center_wait = false;
    bool green1_center_wait = false;
    bool blue2_center_wait = false;
    bool green2_center_wait = true;
    
    while(1) {
        ulcd_mutex.lock();
        
        // center column shapes
        uLCD.filled_circle(center_x, blue1_center_y, radius, BLUE);
        uLCD.filled_circle(center_x, red_center_y, radius, RED);
        uLCD.filled_circle(center_x, green1_center_y, radius, GREEN);
        uLCD.filled_circle(center_x, blue2_center_y, radius, BLUE);
        uLCD.filled_circle(center_x, green2_center_y, radius, GREEN);
        
        ulcd_mutex.unlock();
        
        while (count >= 2) {
        }
        
        // move center column shape positions
        // red_center move
        if ((red_center_y == 14) || (red_center_y == 42)) {
            red_center_y = red_center_y + 28;
        } 
        else if (red_center_y == 70) {
            red_center_y = 150;
        }
        else if ((red_center_y == 150) && (red_center_wait)) {
            red_center_y = 14;
            red_center_wait = false;
        }
        else {
            red_center_y = 150;
            red_center_wait = true;
        }
        
        // green1_center move
        if ((green1_center_y == 14) || (green1_center_y == 42)) {
            green1_center_y = green1_center_y + 28;
        } 
        else if (green1_center_y == 70) {
            green1_center_y = 150;
        }
        else if ((green1_center_y == 150) && (green1_center_wait)) {
            green1_center_y = 14;
            green1_center_wait = false;
        }
        else {
            green1_center_y = 150;
            green1_center_wait = true;
        }
        
        // blue1_center move
        if ((blue1_center_y == 14) || (blue1_center_y == 42)) {
            blue1_center_y = blue1_center_y + 28;
        } 
        else if (blue1_center_y == 70) {
            blue1_center_y = 150;
        }
        else if ((blue1_center_y == 150) && (blue1_center_wait)) {
            blue1_center_y = 14;
            blue1_center_wait = false;
        }
        else {
            blue1_center_y = 150;
            blue1_center_wait = true;
        }
        
        // green2_center move
        if ((green2_center_y == 14) || (green2_center_y == 42)) {
            green2_center_y = green2_center_y + 28;
        } 
        else if (green2_center_y == 70) {
            green2_center_y = 150;
        }
        else if ((green2_center_y == 150) && (green2_center_wait)) {
            green2_center_y = 14;
            green2_center_wait = false;
        }
        else {
            green2_center_y = 150;
            green2_center_wait = true;
        }
        
        //blue2_center move
        if ((blue2_center_y == 14) || (blue2_center_y == 42)) {
            blue2_center_y = blue2_center_y + 28;
        } 
        else if (blue2_center_y == 70) {
            blue2_center_y = 150;
        }
        else if ((blue2_center_y == 150) && (blue2_center_wait)) {
            blue2_center_y = 14;
            blue2_center_wait = false;
        }
        else {
            blue2_center_y = 150;
            blue2_center_wait = true;
        }
        Thread::wait(center_pause);
    }
}

// Thread 3: Controls right column of slot machine
void thread3(void const *args)
{
    // Initialize right column shape y positions
    blue_right_y = 14;
    green1_right_y = 42;
    red_right_y = 70;
    green2_right_y = 150;
    
    // Initialize right column shape x position
    int right_x = 92;
    
    // Create right column pause int
    int right_pause = rand() % 20 + 10;
    
    while(1) {
        ulcd_mutex.lock();
        
        // right column shapes
        uLCD.filled_circle(right_x, blue_right_y, radius, BLUE);
        uLCD.filled_circle(right_x, green1_right_y, radius, GREEN);
        uLCD.filled_circle(right_x, red_right_y, radius, RED);
        uLCD.filled_circle(right_x, green2_right_y, radius, GREEN);
        
        ulcd_mutex.unlock();
        
        while (count >= 3) {
        }
        
        // move right column shape positions
        // blue_right move
        if ((blue_right_y == 14) || (blue_right_y == 42)) {
            blue_right_y = blue_right_y + 28;
        } 
        else if (blue_right_y == 70) {
            blue_right_y = 150;
        }
        else {
            blue_right_y = 14;
        }
        
        // green1_right move
        if ((green1_right_y == 14) || (green1_right_y == 42)) {
            green1_right_y = green1_right_y + 28;
        } 
        else if (green1_right_y == 70) {
            green1_right_y = 150;
        }
        else {
            green1_right_y = 14;
        }
        
        // red_right move
        if ((red_right_y == 14) || (red_right_y == 42)) {
            red_right_y = red_right_y + 28;
        } 
        else if (red_right_y == 70) {
            red_right_y = 150;
        }
        else {
            red_right_y = 14;
        }
        
        // green2_right move
        if ((green2_right_y == 14) || (green2_right_y == 42)) {
            green2_right_y = green2_right_y + 28;
        } 
        else if (green2_right_y == 70) {
            green2_right_y = 150;
        }
        else {
            green2_right_y = 14;
        }
        Thread::wait(right_pause);
    }
}

// sound effect for push button
void thread4(void const *args)
{
    int last = 0;
    FILE *wave_file;
    while (1) {
        wait(0.01);
        if (count != last) {
            wave_file=fopen("/sd/button_press.wav","r");
            waver.play(wave_file);
            fclose(wave_file);
        }
        last = count;
    }  
}

// sound effect for slot machines
/*
void thread5(void const *args)
{
    FILE *wave_file;
    while (count < 3) {
        wave_file=fopen("/sd/slotsrunning.wav","r");
        waver.play(wave_file);
        fclose(wave_file);
    }  
}
*/

int main() {    
    // create int for keypad value and winnings
    int bet = 0;
    int winnings;
    
    // pull up push button
    pb1.mode(PullUp);
    wait(0.001);  
     
    // Setup Interrupt callback function for a pb hit
    pb1.attach_deasserted(&pb1_hit_callback);
    // Start sampling pb input using interrupts
    pb1.setSampleFrequency(); 
    
    // display start-up image
    uLCD.media_init();
    uLCD.set_sector_address(0x0000, 0x0000);
    uLCD.display_image(0,0);
    wait(0.5);
    uLCD.locate(0,12);
    uLCD.printf("Welcome to Slots!");
    
    // play start up sound
    FILE *wave_file1;
    wave_file1=fopen("/sd/startup.wav","r");
    waver.play(wave_file1);
    fclose(wave_file1);
    wait(1);  
     
    while(chips > 0)
    {        
        // enter bet
        ulcd_mutex.lock();
        uLCD.cls();
        uLCD.background_color(BLACK);
        uLCD.locate(0,0);
        uLCD.printf("Chip count: %d", chips);
        wait(1);
        uLCD.locate(0,2);
        uLCD.printf("Please enter your bet from 1 to 3.");
        ulcd_mutex.unlock();
        while (bet == 0 || bet > 8) {
            bet=mpr121.read(0x00);
            bet +=mpr121.read(0x01)<<8;  
        }
        if (bet == 2) {
            bet = 1;
            ulcd_mutex.lock();
            uLCD.locate(0,5);
            uLCD.printf("Your bet was 1.");
            ulcd_mutex.unlock();
        }
        if (bet == 4) {
            bet = 2;
            ulcd_mutex.lock();
            uLCD.locate(0,5);
            uLCD.printf("Your bet was 2.");
            ulcd_mutex.unlock();
        }
        if (bet == 8) {
            bet = 3;
            ulcd_mutex.lock();
            uLCD.locate(0,5);
            uLCD.printf("Your bet was 3.");
            ulcd_mutex.unlock();
        }
        wait(1);
        ulcd_mutex.lock();
        uLCD.locate(0,7);
        uLCD.printf("Let's play!");
        ulcd_mutex.unlock();
        wait(2);
        
        
        // start slot machine
        createBoard();
        Thread t1(thread1); //start thread1
        Thread t2(thread2); //start thread2
        Thread t3(thread3); //start thread3 
        Thread t4(thread4); //start thread 4
        //Thread t5(thread5); //start thread 5
        
        while(count < 3) {  
        }
        //t5.terminate();
        wait(2);
        t4.terminate();
        if ((red_left_y == 42) && (red_center_y == 42) &&
            (red_right_y == 42)) {
            winnings = bet * 50;
            chips = chips + winnings;
            FILE *wave_file2;
            wave_file2=fopen("/sd/winner.wav","r");
            waver.play(wave_file2);
            fclose(wave_file2);
            ulcd_mutex.lock();
            uLCD.line(22, 28, 106, 28, RED); // top horizontal checkered
            uLCD.line(22, 56, 106, 56, RED); // bottom horizontal checkered
            uLCD.line(22, 28, 22, 56, RED); // left border
            uLCD.line(106, 28, 106, 56, RED); // right border
            uLCD.locate(0,12);
            uLCD.printf("Bingo! You won %d",winnings);
            uLCD.printf("chips. You now \nhave %d ", chips);
            uLCD.printf("chips.\n");
            uLCD.printf("Play again!");
            ulcd_mutex.unlock();
            wait(2);       
        }
        else if ( ((blue1_left_y == 42) || (blue2_left_y == 42)) 
            && ((blue1_center_y == 42) || (blue2_center_y == 42))
            && (blue_right_y == 42)) {
            winnings = bet * 25;
            chips = chips + winnings;
            FILE *wave_file2;
            wave_file2=fopen("/sd/winner.wav","r");
            waver.play(wave_file2);
            fclose(wave_file2);
            ulcd_mutex.lock();
            uLCD.line(22, 28, 106, 28, RED); // top horizontal checkered
            uLCD.line(22, 56, 106, 56, RED); // bottom horizontal checkered
            uLCD.line(22, 28, 22, 56, RED); // left border
            uLCD.line(106, 28, 106, 56, RED); // right border
            uLCD.locate(0,12);
            uLCD.printf("Bingo! You won %d",winnings);
            uLCD.printf("chips. You now \nhave %d ", chips);
            uLCD.printf("chips.\n");
            uLCD.printf("Play again!");
            ulcd_mutex.unlock();
            wait(2);
        }
        else if ( ((green1_left_y == 42) || (green2_left_y == 42)) 
            && ((green1_center_y == 42) || (green2_center_y == 42))
            && ((green1_right_y == 42) || (green2_right_y == 42))) {
            winnings = bet * 10;
            chips = chips + winnings;
            FILE *wave_file2;
            wave_file2=fopen("/sd/winner.wav","r");
            waver.play(wave_file2);
            fclose(wave_file2);
            ulcd_mutex.lock();
            uLCD.line(22, 28, 106, 28, RED); // top horizontal checkered
            uLCD.line(22, 56, 106, 56, RED); // bottom horizontal checkered
            uLCD.line(22, 28, 22, 56, RED); // left border
            uLCD.line(106, 28, 106, 56, RED); // right border
            uLCD.locate(0,12);
            uLCD.printf("Bingo! You won %d ",winnings);
            uLCD.printf("chips. You now\nhave %d ", chips);
            uLCD.printf("chips.\n");
            uLCD.printf("Play again!");
            ulcd_mutex.unlock();
            wait(2);
        }
        else {
            chips = chips - bet;
            FILE *wave_file2;
            wave_file2=fopen("/sd/loser.wav","r");
            waver.play(wave_file2);
            fclose(wave_file2);
            ulcd_mutex.lock();
            uLCD.locate(0,12);
            uLCD.printf("So close! You lost%d ", bet);
            uLCD.printf("chips. You now \nhave %d ", chips);
            uLCD.printf("chips.\n");
            uLCD.printf("Let's play again!");
            ulcd_mutex.unlock();
            wait(2);
        }
        wait(2);     
        count = 0;
        bet = 0;
        t1.terminate();
        t2.terminate();
        t3.terminate();
    }
    
    // game over
    uLCD.cls();
    uLCD.media_init();
    uLCD.set_sector_address(0x0000, 0x0041);
    uLCD.display_image(0,0);
    
    FILE *wave_file3;
    wave_file3=fopen("/sd/gameover.wav","r");
    waver.play(wave_file3);
    fclose(wave_file3);    
}


Please log in to post comments.