Snake and Physics based jumping game with high scores and different difficulties.
Dependencies: N5110 SDFileSystem mbed
main.cpp
- Committer:
- el14jw
- Date:
- 2016-05-04
- Revision:
- 5:ae641b1d04fa
- Parent:
- 4:c2d920b17b14
File content as of revision 5:ae641b1d04fa:
/** 2645 Project Games @file main.cpp @brief Program Implementation @brief Revision 1.1 @author Joel W. Webb */ #include "main.h" // Joystick Polling code taken from joystick example code /// Enumerated type to store joystick direction enum DirectionName { UP, DOWN, LEFT, RIGHT, CENTRE, UNKNOWN }; /// Struct for storing data about default Joystick position and Current position struct JoyStick { float x; // current x value float x0; // 'centred' x value float y; // current y value float y0; // 'centred' y value DirectionName direction; // current direction }; typedef struct JoyStick Joystick; /// Global Joystick struct for storing data about default Joystick position and Current position Joystick joystick; /// Ticker interrput for polling the joystick Ticker pollJoystick; /// Ticker for controlling refresh rate of games Ticker gametick; /// Ticker for sctrolling the refresh rate of the display Ticker screentick; /// File pointer for accessing SD card locations FILE *fp = NULL; int main() { initInputs(); playSound(tune_intro); int snakeDifficulty = 1; int plinkDifficulty = 0; while (true) { // Main menu screen int select = menu(menuList,3); // Selected Snake if (select == 0) { // If select = 3 or -1 then user has chosen 'Back' button while( !(select == 3 || select ==-1) ) { // Snake selection screen select = menu(snakeList,5); if (select == 0) { // Play Snake game snake(snakeDifficulty); } // Selected Difficulty else if (select == 1) { // Difficulty selection screen int tempdiff = snakeDifficulty; snakeDifficulty = menu(difficultyList,4); if (snakeDifficulty == -1) { snakeDifficulty = tempdiff; } } // Highscore else if (select == 2) { fp = fopen("/sd/snakehighscore.txt", "r"); wait(0.05); int stored_top_score = -1; // -1 to demonstrate it has changed after reading lcd.clear(); if (fp == NULL) { // if it can't open the file then print error message lcd.printString("No Results",0,0); wait(2.0); } else { // opened file so can write while ( ! (g_buttonA_flag | g_buttonjoy_flag)) { fscanf(fp,"%d",&stored_top_score); // ensure data type matches - note address operator (&) fclose(fp); // ensure you close the file after reading lcd.printString("Highscore:",10,0); if (stored_top_score != -2) { char buffer[14]; sprintf(buffer,"%d",stored_top_score); lcd.printString(buffer,0,1); } else { lcd.printString("No Saved Score",0,1); } lcd.printString(">> Back",0,2); sleep(); } g_buttonjoy_flag = 0; g_buttonA_flag = 0; } } } } // Selected Plink else if (select == 1) { // If select = 3 or -1 then user has chosen 'Back' button while( !(select == 3 || select ==-1) ) { select = menu(plinkList,5); // Start Plink if (select == 0) { playSound(tune_intro); plink(plinkDifficulty); } // Difficulty selection else if (select == 1) { int tempdiff = plinkDifficulty; plinkDifficulty = menu(difficultyList,4); if (plinkDifficulty == -1) { plinkDifficulty = tempdiff; } } // Highscore else if (select == 2) { fp = fopen("/sd/plinkhighscore.txt", "r"); wait(0.05); int stored_top_score = -2; // -2 to demonstrate it has changed after reading lcd.clear(); if (fp == NULL) { // if it can't open the file then print error message lcd.printString("No Results",0,0); wait(2.0); } else { // opened file so can read while ( ! (g_buttonA_flag || g_buttonjoy_flag)) { fscanf(fp,"%d",&stored_top_score); // ensure data type matches - note address operator (&) fclose(fp); // ensure you close the file after reading lcd.printString("Highscore",10,0); if (stored_top_score !=-2) { char buffer[14]; sprintf(buffer,"%d",stored_top_score); lcd.printString(buffer,0,1); } else { lcd.printString("No Saved Score",0,1); } lcd.printString(">> Back",0,2); sleep(); } g_buttonjoy_flag = 0; g_buttonA_flag = 0; } } } } } } // SNAKE void snake(int difficulty) { // Play the snake intro tune playSound(tune_snakeintro); // Creates an array for storing the snake body x and y data. The snake can be up to 880 cells long cell snake[880]; // Clears data from previous games for (int i=0; i<880; i++) { snake[i].x = 0; snake[i].y = 0; } cell food; // Starting length is 5 cells int length = 5; // Starting 5 cells are x = 20 and y = 11,12,13,14 and 15 for (int i=0; i<length; i++) { snake[i].x = 20; snake[i].y = 11+i; } // Creates timer based on how hard you set the difficulty (default Medium) gametick.attach(&gametick_isr,(0.4-(difficulty*0.15))); // Initialise the snake_direction parameter as UP DirectionName snake_direction = UP; // Set the game over condition before while loop int gameOver = 0; // Create the random food location flag (0 if food needs to be randomised and 1 if food still exists) int rand_food = 0; while(!gameOver) { // When the game ticker updates if (g_gametick_flag) { // Reset the ticker flag g_gametick_flag = 0; // If the current snake_direction is the opposite of the joystick direction or the joystick direction is centerd or unknown then keep current direction if ( !( (snake_direction == UP && joystick.direction == DOWN) || (snake_direction == DOWN && joystick.direction == UP) || (snake_direction == LEFT && joystick.direction == RIGHT) || (snake_direction == RIGHT && joystick.direction == LEFT) || (joystick.direction == CENTRE || joystick.direction == UNKNOWN) )) { snake_direction = joystick.direction; } // Iterate through array and move each cell backwards as far as the length of the snake for (int i=(length-1); i>0; i--) { snake[i] = snake[i-1]; } // Then change the x or y coordinate of the first cell of the snake depending on which direction it is moving in switch(snake_direction) { case UP: snake[0].y = snake[0].y - 1; break; case DOWN: snake[0].y = snake[0].y + 1; break; case LEFT: snake[0].x = snake[0].x - 1; break; case RIGHT: snake[0].x = snake[0].x + 1; break; default: error(); } // If snake hits wall then game over if ( (snake[0].x == 41) || (snake[0].x == 0) || (snake[0].y == 23) || (snake[0].y == 0 ) ) { gameOver = 1; } // If snake hits self then game over for (int i=1; i<length; i++) { if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) { gameOver = 1; } } // If the snake head is at the same value of x and y as the food then play tune and increase length by 2 if (food.x == snake[0].x && food.y == snake[0].y) { playSound(tune_snakeEat); length += 2; // Also set new random food to be generated rand_food = 0; } // Create a random location for the food that is not the same as a snake location while(rand_food == 0) { rand_food = 1; food.x = rand(); food.x = food.x %40 +1; food.y = rand(); food.y = food.y %22 +1; for (int i=0; i<length; i++) { if(food.x == snake[i].x && food.y == snake[i].y) { rand_food = 0; } } } // Drawing Display lcd.clear(); // Draws Boundary 2 thick lcd.drawRect(0,0,83,47,0); lcd.drawRect(1,1,81,45,0); // Draws snake for (int i=0; i<length; i++) { lcd.drawRect(snake[i].x*2,snake[i].y*2,1,1,1); } // Draws food lcd.drawRect(food.x*2,food.y*2,1,1,1); lcd.refresh(); } sleep(); } // End game ticker gametick.detach(); int score = length -5; // length -5 because starting length is 5 and a score without collecting anything should be 0 lcd.clear(); // Read previous highscore if one exists fp = fopen("/sd/snakehighscore.txt", "r"); int current_high_score = -1; // -1 to demonstrate it has changed after reading if (fp == NULL) { // if it can't open the file then print error message lcd.printString("Failed Read",0,5); } else { // opened file so can write fscanf(fp,"%d",¤t_high_score); // ensure data type matches - note address operator (&) fclose(fp); // ensure you close the file after reading } // Writing score to sd card if new high score if (current_high_score < score && current_high_score != -1) { fp = fopen("/sd/snakehighscore.txt", "w"); if (fp == NULL) { // if it can't open the file then print error message lcd.printString("No SD Card",0,5); } else { // opened file so can write fprintf(fp,"%d",score); // ensure data type matches fclose(fp); // ensure you close the file after writing } } // Appropriate strings printed to LCD lcd.printString("Game Over",20,0); if (current_high_score == -2) { lcd.printString("New High Score:",0,1); } else if (current_high_score != -1 && current_high_score < score) { lcd.printString("New High Score",0,1); lcd.printString("Previously",0,3); } else if (current_high_score != -1 && current_high_score >= score) { lcd.printString("Score",0,1); lcd.printString("High Score",0,3); } else { lcd.printString("Score:",0,1); lcd.printString("No Previous",0,3); } // Print score char buffer[14]; sprintf(buffer,"%d",score); lcd.printString(buffer,20,2); // Print previous high-score if (current_high_score != -1 && current_high_score != -2) { char str[14]; sprintf(str,"%d",current_high_score); lcd.printString(str,20,4); } playSound(tune_gameOver); wait(3.0); g_buttonA_flag = 0; g_buttonB_flag = 0; g_buttonjoy_flag = 0; } // PLINK void plink(int difficulty) { playSound(tune_plinkintro); lcd.clear(); // Instead of damaging the program flow with global variables I pass a struct full of variables from one plink function to another. plinkvar pvar; pvar.difficulty = difficulty; // Initialise plink variables pvar = initpvar(pvar); // Game ticker int refreshRate = 100; float tickerDelay = 1.0/refreshRate; pvar.tickerDelay = tickerDelay; gametick.attach(&gametick_isr,tickerDelay); // Update screen 30 fps tickerDelay = 1.0/30.0; screentick.attach(&screentick_isr,tickerDelay); wait(0.25); while(!pvar.gameOver) { if (g_gametick_flag) { g_gametick_flag = 0; // Screen scrolling pvar = plinkScroll(pvar); // Platform generation pvar = plinkPlatGen(pvar); // Collisions pvar = plinkCollisions(pvar); // Physics Engine pvar = plinkPhysicsEngine(pvar); // LCD drawing pvar = plinkDrawScreen(pvar); } // Sleep to conserve power until next interrupt sleep(); } gametick.detach(); lcd.clear(); // Handle high scores and printing game over messages to LCD pvar = plinkGameOver(pvar); } plinkvar initpvar(plinkvar pvar) { // Initial physics conditions vector pos = {41,47}; vector vel = {0,-25}; vector acc = {0,10}; pvar.pos = pos; pvar.vel = vel; pvar.acc = acc; // Initialising game parameters pvar.height = 0; // This is the number of pixels that the lcd has 'scrolled' by pvar.gameOver = 0; // When something consitutes a game over parameter this is set to 1 and the while loop is broken out of pvar.platformWidth = 7 - (pvar.difficulty * 2); // Platform width is based on difficulty and also changed by power ups cell powerUp = {42,-100}; // The first power up can be found at these coordinates pvar.powerUp = powerUp; pvar.powerUpHeight = 0; // This parameter keeps tabs on the number of pixels scrolled while a power up is active (after 100 picels the power up wears off) pvar.powerUpRadius = 5; // Helpful parameter for detecting collisions pvar.ballRadius = 2; // Also helpful parameter for detecting collisions and is changed in 2 power up cases // Initialising platforms pvar.platforms[0].x = 42; pvar.platforms[0].y = 46; for (int i=1; i<20; i++) { pvar.platforms[i].x = rand()%(84-pvar.platformWidth) + (pvar.platformWidth-1)/2; pvar.platforms[i].y = pvar.platforms[i-1].y - (rand()%10 + 5); } return pvar; } plinkvar plinkScroll(plinkvar pvar) { // Scrolling Block while (pvar.pos.y < 14) { pvar.pos.y++; pvar.height++; pvar.powerUpHeight++; for (int i=0; i<20; i++) { pvar.platforms[i].y++; } pvar.powerUp.y++; } return pvar; } plinkvar plinkPlatGen(plinkvar pvar) { // Platform generation for (int i=0; i<20; i++) { if (pvar.platforms[i].y > 47) { // Stage 1 if (pvar.height <= 500) { if (i == 0) { pvar.platforms[0].x = rand()%(84-pvar.platformWidth) + (pvar.platformWidth-1)/2; pvar.platforms[0].y = pvar.platforms[19].y - (rand()%10 + 5); } else { pvar.platforms[i].x = rand()%(84-pvar.platformWidth) + (pvar.platformWidth-1)/2; pvar.platforms[i].y = pvar.platforms[i-1].y - (rand()%10 + 5); } } // Stage 2 else if (500 < pvar.height <= 1000) { if (i == 0) { pvar.platforms[0].x = rand()%(84-pvar.platformWidth) + (pvar.platformWidth-1)/2; pvar.platforms[0].y = pvar.platforms[19].y - (rand()%10 + 10); } else { pvar.platforms[i].x = rand()%(84-pvar.platformWidth) + (pvar.platformWidth-1)/2; pvar.platforms[i].y = pvar.platforms[i-1].y - (rand()%10 + 10); } } //Stage 3 else if (pvar.height > 1000) { if (i == 0) { pvar.platforms[0].x = rand()%(84-pvar.platformWidth) + (pvar.platformWidth-1)/2; pvar.platforms[0].y = pvar.platforms[19].y - (rand()%10 + 15); } else { pvar.platforms[i].x = rand()%(84-pvar.platformWidth) + (pvar.platformWidth-1)/2; pvar.platforms[i].y = pvar.platforms[i-1].y - (rand()%10 + 15); } } } } return pvar; } plinkvar plinkCollisions(plinkvar pvar) { // Collisions // Platforms for (int i=0; i<20; i++) { if ( (int)pvar.pos.y == (pvar.platforms[i].y - 1) && (int)pvar.pos.x > pvar.platforms[i].x-pvar.platformWidth-2 && (int)pvar.pos.x < pvar.platforms[i].x+pvar.platformWidth+2 ) { pvar.vel.y = -25; // Play appropriate sound when bouncing switch (pvar.ballRadius) { case 1: playSound(tune_plinkjump_small); break; case 2: playSound(tune_plinkjump_normal); break; case 4: playSound(tune_plinkjump_big); break; } } } // Walls if (pvar.pos.x <= pvar.ballRadius) { pvar.vel.x = abs(pvar.vel.x); } else if (pvar.pos.x >= 83 - pvar.ballRadius) { pvar.vel.x = -abs(pvar.vel.x); } // Power Up if ((int)pvar.pos.x+pvar.ballRadius >= (pvar.powerUp.x-pvar.powerUpRadius) && (int)pvar.pos.x-pvar.ballRadius <= (pvar.powerUp.x+pvar.powerUpRadius) && (int)pvar.pos.y+pvar.ballRadius >= (pvar.powerUp.y-pvar.powerUpRadius) && (int)pvar.pos.y-pvar.ballRadius <= (pvar.powerUp.y+pvar.powerUpRadius)) { // Create new powerUp box at the next 100 mark pvar.powerUp.x = rand()%(84-1-(pvar.powerUpRadius*2)) + pvar.powerUpRadius; pvar.powerUp.y = -100; //reset power up height pvar.powerUpHeight = 0; // Choose new power up randomly switch (rand()%4) { case 0: pvar.ballRadius = 4; break; case 1: pvar.ballRadius = 1; break; case 2: pvar.platformWidth = 11; break; case 3: pvar.platformWidth = 5; break; default: error(); } } // If the powerup has been missed then generate new one else if (pvar.powerUp.y > 47+pvar.powerUpRadius) { pvar.powerUp.x = rand()%(84-1-(pvar.powerUpRadius*2)) + pvar.powerUpRadius; pvar.powerUp.y = -150; } // After certain height achieved then cancel power up if (pvar.powerUpHeight > 100) { pvar.ballRadius = 2; pvar.platformWidth = 7 - (pvar.difficulty * 2); } return pvar; } plinkvar plinkPhysicsEngine(plinkvar pvar) { // Physics engine // Acceleration due to joystick if (g_joystick_flag) { if (joystick.direction == LEFT) { pvar.acc.x = -15; } else if (joystick.direction == RIGHT) { pvar.acc.x = 15; } else { pvar.acc.x = 0; } } // physics iteration pvar.vel.y = pvar.vel.y + (pvar.acc.y * pvar.tickerDelay * 4); pvar.vel.x = 0.97F*(pvar.vel.x + (pvar.acc.x * pvar.tickerDelay)*4); // 0.97 adds slight damping factor to horizontal movement pvar.pos.y = pvar.pos.y + (pvar.vel.y * pvar.tickerDelay * 4); pvar.pos.x = pvar.pos.x + (pvar.vel.x * pvar.tickerDelay * 4); // Boundaries of physics engine if (pvar.pos.y > 48) { pvar.gameOver = 1; } // If ball is out of boundaries then place it back in boundaries if (pvar.pos.x - pvar.ballRadius < 0) { pvar.pos.x = pvar.ballRadius; } else if (pvar.pos.x + pvar.ballRadius > 83) { pvar.pos.x = 83-pvar.ballRadius; } return pvar; } plinkvar plinkDrawScreen(plinkvar pvar) { if (g_screentick_flag) { g_screentick_flag = 0; // Drawing on lcd lcd.clear(); // Print Current Score char buffer[14]; sprintf(buffer,"Score: %d",pvar.height); lcd.printString(buffer,0,0); // Drawing ball/blob // If second blob stage int noPlatform = 1; if (pvar.ballRadius == 2) { for (int i=0; i<20; i++) { if ( (int)pvar.pos.y == (pvar.platforms[i].y - 2) && (int)pvar.pos.x > pvar.platforms[i].x-pvar.platformWidth-2 && (int)pvar.pos.x < pvar.platforms[i].x+pvar.platformWidth+2 ) { noPlatform = 0; lcd.drawRect(pvar.pos.x-2,pvar.pos.y-1,4,2,1); // X, Y, Width, Height, Fill lcd.setPixel(pvar.pos.x-1,pvar.pos.y-2); lcd.setPixel(pvar.pos.x,pvar.pos.y-2); lcd.setPixel(pvar.pos.x+1,pvar.pos.y-2); lcd.setPixel(pvar.pos.x-3,pvar.pos.y+1); lcd.setPixel(pvar.pos.x+3,pvar.pos.y+1); } // If third blob stage else if ( (int)pvar.pos.y == (pvar.platforms[i].y - 1) && (int)pvar.pos.x > pvar.platforms[i].x-pvar.platformWidth-2 && (int)pvar.pos.x < pvar.platforms[i].x+pvar.platformWidth+2 ) { noPlatform = 0; lcd.drawRect(pvar.pos.x-3,pvar.pos.y-1,6,1,1); // X, Y, Width, Height, Fill lcd.setPixel(pvar.pos.x-4,pvar.pos.y); lcd.setPixel(pvar.pos.x+4,pvar.pos.y); lcd.setPixel(pvar.pos.x-2,pvar.pos.y-2); lcd.setPixel(pvar.pos.x-1,pvar.pos.y-2); lcd.setPixel(pvar.pos.x,pvar.pos.y-2); lcd.setPixel(pvar.pos.x+1,pvar.pos.y-2); lcd.setPixel(pvar.pos.x+2,pvar.pos.y-2); } } } // Else default blob stage if (noPlatform == 1) { lcd.drawCircle((int)pvar.pos.x,(int)pvar.pos.y,pvar.ballRadius,1); // X, Y, Radius, Fill lcd.refresh(); } // Platforms and boundaries drawing for (int i=0; i<20; i++) { if (pvar.platforms[i].y > 0) { lcd.drawRect( pvar.platforms[i].x-pvar.platformWidth ,pvar.platforms[i].y,pvar.platformWidth*2,1,1); // X, Y, Width, Height, Fill } } lcd.drawLine(0,0,0,47,1); // x0,y0,x1,y1,type lcd.drawLine(83,0,83,47,1); // PowerUp drawing if (pvar.powerUp.y > 0-pvar.powerUpRadius && pvar.powerUp.y < 47+pvar.powerUpRadius) { lcd.drawRect(pvar.powerUp.x-pvar.powerUpRadius,pvar.powerUp.y-pvar.powerUpRadius,10,10,0); // X, Y, Width, Height, Fill lcd.setPixel(pvar.powerUp.x,pvar.powerUp.y+3); lcd.setPixel(pvar.powerUp.x,pvar.powerUp.y+1); lcd.setPixel(pvar.powerUp.x+1,pvar.powerUp.y); lcd.setPixel(pvar.powerUp.x+2,pvar.powerUp.y-1); lcd.setPixel(pvar.powerUp.x+2,pvar.powerUp.y-2); lcd.setPixel(pvar.powerUp.x+1,pvar.powerUp.y-3); lcd.setPixel(pvar.powerUp.x,pvar.powerUp.y-3); lcd.setPixel(pvar.powerUp.x-1,pvar.powerUp.y-3); lcd.setPixel(pvar.powerUp.x-2,pvar.powerUp.y-2); } lcd.refresh(); // Looks more pleasing when the ball pauses to bounce if (noPlatform == 0) { wait(0.03); } } return pvar; } plinkvar plinkGameOver(plinkvar pvar) { // Handle saving high scores to SD card // Read previous highscore if one exists fp = fopen("/sd/plinkhighscore.txt", "r"); int current_high_score = -1; // -1 to demonstrate it has changed after reading if (fp == NULL) { // if it can't open the file then print error message lcd.printString("Failed Read",0,5); } else { // opened file so can write fscanf(fp,"%d",¤t_high_score); // ensure data type matches - note address operator (&) fclose(fp); // ensure you close the file after reading } // Writing score to sd card if new high score if (current_high_score < pvar.height && current_high_score != -1) { fp = fopen("/sd/plinkhighscore.txt", "w"); if (fp == NULL) { // if it can't open the file then print error message lcd.printString("No SD Card ",0,5); } else { // opened file so can write fprintf(fp,"%d",pvar.height); // ensure data type matches fclose(fp); // ensure you close the file after writing } } // Appropriate strings printed to LCD lcd.printString("Game Over",20,0); if (current_high_score == -2) { lcd.printString("New High Score:",0,1); } else if (current_high_score != -1 && current_high_score < pvar.height) { lcd.printString("New High Score",0,1); lcd.printString("Previously",0,3); } else if (current_high_score != -1 && current_high_score >= pvar.height) { lcd.printString("Score:",0,1); lcd.printString("High Score",0,3); } else { lcd.printString("Score:",0,1); lcd.printString("No Previous",0,3); } // Print score char buffer[14]; sprintf(buffer,"%d",pvar.height); lcd.printString(buffer,20,2); // Print previous high-score if (current_high_score != -1 && current_high_score != -2) { char str[14]; sprintf(str,"%d",current_high_score); lcd.printString(str,20,4); } playSound(tune_gameOver); lcd.refresh(); wait(3.0); // Reset all flags so no unexpected behaviour occurs g_buttonA_flag = 0; g_buttonB_flag = 0; g_buttonjoy_flag = 0; return pvar; } // ISR's void buttonA_isr() { g_buttonA_flag = 1; } void buttonB_isr() { g_buttonB_flag = 1; } void buttonjoy_isr() { g_buttonjoy_flag = 1; } void gametick_isr() { g_gametick_flag = 1; } void screentick_isr() { g_screentick_flag = 1; } // Initialises Inputs void initInputs() { errorLED = 1; /// Setting up Interrupt service routines for input buttons /// PullDown mode is assumed buttonA.rise(&buttonA_isr); buttonB.rise(&buttonB_isr); buttonjoy.rise(&buttonjoy_isr); buttonA.mode(PullDown); buttonB.mode(PullDown); buttonjoy.mode(PullDown); /// Testing LCD display works (splash screen) lcd.init(); wait(0.5); lcd.setBrightness(0.5); // Brightness set to maximum duty cycle so it isn't affected by changing frequency of buzzer lcd.printString("Calibrating",8,0); lcd.printString("Do not move",8,1); lcd.printString("Joystick",18,2); lcd.refresh(); /// Calibrating the Joystick on startup calibrateJoystick(); /// read joystick 10 times per second pollJoystick.attach(&updateJoystick,1.0/10.0); // Patch for sd card access not working first time fp = fopen("/sd/test.txt", "w"); if (fp != NULL) { fclose(fp); } fp = fopen("/sd/test.txt", "w"); if (fp != NULL) { fclose(fp); // ensure you close the file after writing } // If file does not exist then create it fp = fopen("/sd/plinkhighscore.txt","r"); if (fp == NULL) { fp = fopen("/sd/plinkhighscore.txt","w"); if (fp != NULL) { fprintf(fp,"%d",-2); fclose(fp); } } else { fclose(fp); } fp = fopen("/sd/snakehighscore.txt","r"); if (fp == NULL) { fp = fopen("/sd/snakehighscore.txt","w"); if (fp != NULL) { fprintf(fp,"%d",-2); fclose(fp); } } else { fclose(fp); } wait(1.0); } // Hangs on an error void error() { while(1) { errorLED = !errorLED; wait(0.2); } } // Menu function for handling the printing and selecting of menu screens int menu(const stringList* menuList,int lines) { int select = 0; // While either joystick button or button A or button B are not on then continue selection process // Once either button is on the the selected value is returned while(g_buttonjoy_flag == 0 && g_buttonA_flag == 0 && g_buttonB_flag == 0) { // Changes the value of variable select determined by what the joystick value is if (g_joystick_flag) { if (joystick.direction == UP) { if (select == 0) { select = lines-2; } else { select--; } } else if(joystick.direction == DOWN) { if (select == lines-2) { select = 0; } else { select++; } } } // Prints the correct strings drawStrings(menuList,lines); int line = select + 1; char buffer[14]; sprintf(buffer,">> %s",menuList[line].str); lcd.printString(buffer,menuList[line].offset,line); if (g_joystick_flag) { g_joystick_flag = 0; // Adds acceptable scrolling delay wait(0.2); } sleep(); } // Debouncing wait(0.2); g_buttonjoy_flag = 0; g_buttonA_flag = 0; // Returns -1 if B is pressed to automatically go back a menu if (g_buttonB_flag) { g_buttonB_flag = 0; select = -1; } return select; } // Draw Strings function used for plotting an array of stringList to the LCD void drawStrings(const stringList* list,int lines) { lcd.clear(); for (int i=0; i<lines; i++) { lcd.printString(list[i].str,list[i].offset,i); } } /// Read default positions of the joystick to calibrate later readings void calibrateJoystick() { // must not move during calibration joystick.x0 = xPot; /// initial positions in the range 0.0 to 1.0 (0.5 if centred exactly) joystick.y0 = yPot; } void updateJoystick() { /// read current joystick values relative to calibrated values (in range -0.5 to 0.5, 0.0 is centred) joystick.x = xPot - joystick.x0; joystick.y = yPot - joystick.y0; // calculate direction depending on x,y values // tolerance allows a little lee-way in case joystick not exactly in the stated direction if ( fabs(joystick.y) < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { joystick.direction = CENTRE; } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { joystick.direction = UP; } else if ( joystick.y > DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { joystick.direction = DOWN; } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { joystick.direction = RIGHT; } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { joystick.direction = LEFT; } else { joystick.direction = UNKNOWN; } // set flag for printing g_joystick_flag = 1; }