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.
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.
MBED | uLCD |
---|---|
VU | 5V |
GND | GND |
P27 | TX |
P28 | RX |
P30 | RESET |
MBED | MicroSD |
---|---|
P8 | CS |
P5 | DI |
VOUT | VCC |
P7 | SCK |
GND | GND |
P6 | DO |
MBED | MPR121 |
---|---|
GND | GND |
P9 | SDA |
P10 | SCL |
P26 | IRQ |
VOUT | VCC |
MBED | TPA2005D1 | Speaker |
---|---|---|
GND | PWR-, IN- | |
VOUT | PWR+ | |
P26 | IN+ | |
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.