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.
