Simon Game
Simon Game
This mbed application will utilize multiple SPI port expanders in order to create a Simon game. The game will have increasing levels of difficulty, which will be selectable in menu navigation. It will use the mbed's random number generator in it's highest difficulty. Similar to the game Simon, the board will light up the LEDs in a specific order. It will be the user's task to push the corresponding pushbuttons in the correct order within a certain time interval. If the user succeeds, another LED is added to the sequence. If the user fails, it's game over. The board will also generate sound cues for correct sequences and incorrect sequences, if the optional speaker is attached.
Hardware
9 LEDs (different colors if available)
9 Pushbuttons
3 SPI port expanders
1 Text LCD
1 Rotary Pulse Generator for menu navigation
1 Speaker
1 Transistor
SD card with breakout board
9 10k Ohm Resistors for pushbuttons
9 100 Ohm Resistors for LEDs
1 1 kOhm resistor for speaker data line
Picture of Setup
Schematic
Software
Cookbook Pages (for reference)
The MCP23S17 library is used for the SPI port expanders
The Text LCD protocol is used for menu navigation and program-user communication
Import library
Public Types |
|
enum | LCDType { LCD16x2 , LCD16x2B , LCD20x2 , LCD20x4 } |
LCD panel format. More... |
|
Public Member Functions |
|
TextLCD (PinName rs, PinName e, PinName d4, PinName d5, PinName d6, PinName d7, LCDType type=LCD16x2) | |
Create a
TextLCD
interface.
|
|
int | putc (int c) |
Write a character to the LCD.
|
|
int | printf (const char *format,...) |
Write a formated string to the LCD.
|
|
void | locate (int column, int row) |
Locate to a screen column and row.
|
|
void | cls () |
Clear the screen and locate to 0,0.
|
The rotary pulse generator library is used to navigate the LCD screen
Import libraryRPG
Simple RPG Library
The Wave Player library is used to play the .wav files for the SD card
Import librarywave_player
Wave playing code, based on Big Mouth Billy Bass, but cleaned up and capable of playing more bitrates and sample sizes.
The SD Card FileSystem is used to store the wav files for keypresses and the winning and losing sound cues as well
Import librarySDFileSystem
SDFileSystem
Program
Download
Import programSimonGame
Simple Simon game implemented on the mbed using SPI port expanders, an SD card file system, a Text LCD, LEDs, Push buttons and a speaker.
Video Demonstrations
Board Overview
Menu Navigation
Hard Mode and Defeat Sound Cue
Easy Mode and Victory Sound Cue
Code
main.cpp
// See http://mbed.org/users/romilly/notebook/mcp23s17-addressable-16-bit-io-expander-with-spi/ // MCP23S17 datasheet http://ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf // uses MCP23S17 library version 0.4 #include "mbed.h" #include "MCP23S17.h" #include "TextLCD.h" #include "RPG.h" #include "wave_player.h" #include "SDFileSystem.h" #define S_BEGIN 0 #define S_PLAY 1 #define S_VICTORY 2 #define S_DEFEAT 3 // Creates an initializes all necessary objects void init(); // Obstains menu input and updates LCD text accordingly int handleMenu(); // Plays the current sequence so far using timing appropriate to the chosen difficulty void playSequence(); // Listens for the player move and returns a value indicating success or failure int listenSequence(); // Re-initializes the game void restart(); // Create SPI bus SPI spi(p5, p6, p7); // Create Text LCD Controller TextLCD lcd(p21, p22, p23, p24, p25, p26); // Create RPG interface RPG rpg(p16,p17,p20); // Create SD interface SDFileSystem sd(p11, p12, p13, p14, "sd"); // Sound output AnalogOut DACout(p18); // Wave file player wave_player waver(&DACout); // Sound files FILE *Sounds[11]; // Wiring Connections: // mbed p5,p6,p7 are tied to MCP23S17 SI, SO, SCK pins // mbed p8 to MCP23S17 CS // MCP23S17 reset pin pulled high // Chip addresses (opcodes) char opcodes[3]; // Chip objects MCP23S17* chips[3]; // Menu variables char* menuOptions[] = { " Easy >", "< Medium >", "< Hard " }; short menuState = 0; short oldMenuState = -1; short menuBuffer = 0; // Difficulty settings double timeoutVals[3] = { 2, 1.5, 1 }; double delayVals[3] = { 0.5, 0.3, 0.1}; short victoryVals[3] = {4 , 6, 8}; // Game variables short sequence[15]; short current = 0; short gameState = 0; short victory; double timeout; double delay; char input = 0; // Push button input int main() { init(); while (1) { int menuSelection; switch(gameState) { case S_BEGIN: menuSelection = handleMenu(); if(menuSelection > -1) { gameState = S_PLAY; timeout = timeoutVals[menuSelection]; delay = delayVals[menuSelection]; victory = victoryVals[menuSelection]; } break; case S_PLAY: playSequence(); if(!listenSequence()) gameState = S_DEFEAT; else if(current == victory) gameState = S_VICTORY; break; case S_VICTORY: lcd.cls(); lcd.locate(0,0); lcd.printf(" YOU WON :D "); lcd.printf(" <( . _ . )> "); waver.play(Sounds[10]); rewind(Sounds[10]); wait(5); gameState = S_BEGIN; restart(); break; case S_DEFEAT: lcd.cls(); lcd.locate(0,0); lcd.printf(" DEFEAT "); lcd.printf(" TRY HARDER "); waver.play(Sounds[9]); rewind(Sounds[9]); wait(5); gameState = S_BEGIN; restart(); break; default: lcd.printf("Invalid State"); } handleMenu(); } } void init() { // Set chips opcodes (Dependent on hardware connections) opcodes[0] = 0x40; opcodes[1] = 0x42; opcodes[2] = 0x44; /* Create IO Expander objects with their respective opcodes * and set port B of each chip for input and port A of each chip * for output. */ for(int i = 0; i <3 ; i++) { chips[i] = new MCP23S17(spi, p8, opcodes[i]); // Set all 8 Port A bits to output direction chips[i]->direction(PORT_A, 0x00); // Set all 8 Port B bits to input direction chips[i]->direction(PORT_B, 0xFF); // Turn off all LEDs chips[i]->write(PORT_A, 0); } // Clear sequence for(int i = 0; i < 15; i++) sequence[i] = 0 ; set_time(1256729737); // Set RTC time to Wed, 28 Oct 2009 11:35:37 // Initialize sounds and file system char ftotal[] = "/sd/sounds/x.wav"; for(int i = 0;i<9;i++) { ftotal[11] = i + '0'; Sounds[i] = fopen(ftotal, "r"); if(Sounds[i] == NULL) lcd.printf("SOUND ERROR"); } Sounds[9] = fopen("/sd/sounds/lose.wav", "r"); Sounds[10] = fopen("/sd/sounds/win.wav" , "r"); } int handleMenu() { menuBuffer +=rpg.dir(); if(menuBuffer == -15) { menuState++; menuBuffer = 0; } else if (menuBuffer == 15) { menuState--; menuBuffer = 0; } if(menuState < 0) menuState = 0; else if(menuState > 2) menuState = 2; if( oldMenuState != menuState) { lcd.cls(); lcd.printf("Difficulty:"); lcd.locate(0,1); lcd.printf("%s", menuOptions[menuState]); } oldMenuState = menuState; if(rpg.pb()) return menuState; else return -1; } void playSequence() { lcd.cls(); lcd.locate(0,0); lcd.printf("Sequence Playing"); lcd.printf("..."); wait(delay + 0.5); sequence[current] = time(NULL)%9 ; current++; for(int i = 0; i < current; i++) { int chipNum = sequence[i]/3; int ledNum = sequence[i]%3; chips[chipNum]->write(PORT_A, (1 << ledNum) ); waver.play(Sounds[sequence[i]]); rewind(Sounds[sequence[i]]); wait(delay); chips[chipNum]->write(PORT_A, 0); wait(delay); } lcd.cls(); lcd.locate(0,0); lcd.printf("Get ready..."); wait(timeout + 0.5 ); lcd.cls(); lcd.locate(0,0); lcd.printf("GO!!!"); wait(delay + 0.3); } int listenSequence() { time_t startTime = time(NULL); time_t currentTime = startTime; time_t timeDiff = 0; int totalTime = timeout * current; int currentCheck = 0; bool correct = false; int input; int oldInput[3] = {0, 0, 0}; lcd.cls(); while(timeDiff <= totalTime) { lcd.locate(0,0); lcd.printf("Time: %d", totalTime - timeDiff); if(totalTime - timeDiff < 10) lcd.printf(" "); lcd.locate(0,1); lcd.printf("Round: %d",current); currentTime = time(NULL); timeDiff = currentTime - startTime; for(int i = 0; i < 3; i++) { // Read lower 3 bits input = chips[i]->read(PORT_B) & 7; if(input != 0 && oldInput[i] == 0) { chips[i]->write(PORT_A, input); wait(0.3); chips[i]->write(PORT_A, 0); if(correct) return 0; int rest; if(input == 1) rest = 0; else if(input == 2) rest = 1; else if(input == 4) rest = 2; else rest = 3; if( i * 3 + rest == sequence[currentCheck]) { waver.play(Sounds[sequence[currentCheck]]); rewind(Sounds[sequence[currentCheck]]); currentCheck++; correct = true; } else { lcd.cls(); lcd.locate(0,0); lcd.printf("You pressed: %d", i * 3 + rest); lcd.locate(0,1); lcd.printf("Next Was: %d",sequence[currentCheck]); wait(6); return 0; } } oldInput[i] = input; } correct = false; if(currentCheck == current) break; } wait(delay + 0.5); if(currentCheck == current) return 1; else return 0; } void restart() { current = 0; menuState = 0 ; oldMenuState = -1; }
Please log in to post comments.