// Download this in a .zip file.
/* Test the freetronics 16x2 LCD shield http://www.freetronics.com/pages/16x2-lcd-shield-quickstart-guide#.UBKMm7T9OK9 MTG Life Counter w/ hidden poison counter by eturnerx@gmail.com 201208 */ /*-------------------------------------------------------------------------------------- Includes --------------------------------------------------------------------------------------*/ #include// include LCD library /*-------------------------------------------------------------------------------------- Defines --------------------------------------------------------------------------------------*/ // Pins in use #define BUTTON_ADC_PIN A0 // A0 is the button ADC input #define LCD_BACKLIGHT_PIN 3 // D3 controls LCD backlight // ADC readings expected for the 5 buttons on the ADC input #define RIGHT_10BIT_ADC 0 // right #define UP_10BIT_ADC 145 // up #define DOWN_10BIT_ADC 329 // down #define LEFT_10BIT_ADC 505 // left #define SELECT_10BIT_ADC 741 // right #define BUTTONHYSTERESIS 10 // hysteresis for valid button sensing window //return values for ReadButtons() #define BUTTON_NONE 0 // #define BUTTON_RIGHT 1 // #define BUTTON_UP 2 // #define BUTTON_DOWN 3 // #define BUTTON_LEFT 4 // #define BUTTON_SELECT 5 // // Define the state: it makes the code easier to read #define STATE_LIFE1 0 #define STATE_LIFE2 1 #define STATE_POISON1 2 #define STATE_POISON2 3 /*-------------------------------------------------------------------------------------- Variables --------------------------------------------------------------------------------------*/ byte buttonJustPressed = false; //this will be true after a ReadButtons() call if triggered byte buttonJustReleased = false; //this will be true after a ReadButtons() call if triggered byte buttonWas = BUTTON_NONE; //used by ReadButtons() for detection of button events // Setup the variables that will be used int life1 = 20; int life2 = 20; byte poison1 = 0; byte poison2 = 0; byte resetcounter = 0; byte state = 0; /*-------------------------------------------------------------------------------------- Init the LCD library with the LCD pins to be used --------------------------------------------------------------------------------------*/ LiquidCrystal lcd( 8, 9, 4, 5, 6, 7 ); //Pins for the freetronics 16x2 LCD shield. LCD: ( RS, E, LCD-D4, LCD-D5, LCD-D6, LCD-D7 ) /*-------------------------------------------------------------------------------------- setup() Called by the Arduino framework once, before the main loop begins --------------------------------------------------------------------------------------*/ void setup() { // Setup pins. pinMode(BUTTON_ADC_PIN, INPUT ); // Analogue input for the LCD's buttons digitalWrite(BUTTON_ADC_PIN, LOW ); pinMode(LCD_BACKLIGHT_PIN, OUTPUT ); // Backlight control (off/on) digitalWrite(LCD_BACKLIGHT_PIN, HIGH ); // Turn the backlight on lcd.begin( 16, 2 ); // Tell LCD library this is 16x2 display lcd.setCursor( 0, 0 ); // 1234567890123456 lcd.print( "The Life Counter" ); updateLifeCounts(); digitalWrite(LCD_BACKLIGHT_PIN, LOW); delay(150); //PULSE the backlight a few times digitalWrite(LCD_BACKLIGHT_PIN, HIGH); delay(150); digitalWrite(LCD_BACKLIGHT_PIN, LOW); delay(150); digitalWrite(LCD_BACKLIGHT_PIN, HIGH); delay(150); digitalWrite(LCD_BACKLIGHT_PIN, LOW); delay(150); digitalWrite(LCD_BACKLIGHT_PIN, HIGH); } /*-------------------------------------------------------------------------------------- loop() Arduino main loop --------------------------------------------------------------------------------------*/ void loop() { byte button; button = ReadButtons(); if(button == BUTTON_NONE) { // If no button is pressed then resetcounter = 0; // reset timer for the rest function } else if(buttonJustPressed) { // Otherwise, if a button has just been pressed switch(state) { // Do different things depending on "state" case STATE_LIFE1: do_state_life1(button); break; case STATE_LIFE2: do_state_life2(button); break; case STATE_POISON1: do_state_poison1(button); break; case STATE_POISON2: do_state_poison2(button); break; default: break; } updateLifeCounts(); // Something was pressed so update the display } else if(button == BUTTON_SELECT) { // BUTTON_SELECT is pressed so do the reset sequence digitalWrite(LCD_BACKLIGHT_PIN, LOW ); delay( 150 ); // Backlight off digitalWrite(LCD_BACKLIGHT_PIN, HIGH ); delay( 150 ); // Backlight on resetcounter++; if(resetcounter > 10) { // Life/Poison is reset after button is held for a time resetcounter = 0; life1 = 20; life2 = 20; poison1 = 0; poison2 = 0; state = 0; updateLifeCounts(); } } //clear the buttonJustPressed & buttonJustReleased flags; they've already done their job if(buttonJustPressed) buttonJustPressed = false; if(buttonJustReleased) buttonJustReleased = false; delay(25); // Delay to stop buttons "jittering" false presses } void do_state_life1(byte button) { switch(button) { case BUTTON_LEFT: state = STATE_POISON1; break; case BUTTON_RIGHT: state = STATE_LIFE2; break; case BUTTON_UP: life1++; break; case BUTTON_DOWN: life1--; break; default: break; } } void do_state_life2(byte button) { switch(button) { case BUTTON_LEFT: state = STATE_LIFE1; break; case BUTTON_RIGHT: state = STATE_POISON2; break; case BUTTON_UP: life2++; break; case BUTTON_DOWN: life2--; break; default: break; } } void do_state_poison1(byte button) { switch(button) { case BUTTON_LEFT: state = STATE_LIFE1; break; case BUTTON_RIGHT: state = STATE_LIFE1; break; case BUTTON_UP: poison1++; break; case BUTTON_DOWN: if(poison1 > 0) poison1--; break; default: break; } } void do_state_poison2(byte button) { switch(button) { case BUTTON_LEFT: state = STATE_LIFE2; break; case BUTTON_RIGHT: state = STATE_LIFE2; break; case BUTTON_UP: poison2++; break; case BUTTON_DOWN: if(poison2 > 0) poison2--; break; default: break; } } // 1234567890123456 // The Life Counter // LLssP PssLL // 1234567890123456 void updateLifeCounts() { lcd.setCursor( 0, 1 ); if(life1 <= 0 || poison1 >= 10) lcd.print("XXXXX "); else { if(life1 > 99) lcd.print("99"); else { if(life1 < 10) lcd.print(' '); lcd.print(life1); } if(state == STATE_LIFE1) lcd.print("* "); else if(state == STATE_POISON1) lcd.print(" ^"); else lcd.print(" "); if(state == STATE_POISON1 || poison1 > 0) lcd.print(poison1); else lcd.print(' '); lcd.print(" "); } if(life2 <= 0 || poison2 >= 10) lcd.print("XXXXX"); else { if(state == STATE_POISON2 || poison2 > 0) lcd.print(poison2); else lcd.print(' '); if(state == STATE_LIFE2) lcd.print(" *"); else if(state == STATE_POISON2) lcd.print("^ "); else lcd.print(" "); if(life1 > 99) lcd.print("99"); else { if(life2 < 10) lcd.print(' '); lcd.print(life2); } } } /*-------------------------------------------------------------------------------------- ReadButtons() Detect the button pressed and return the value Uses global values buttonWas, buttonJustPressed, buttonJustReleased. --------------------------------------------------------------------------------------*/ byte ReadButtons() { unsigned int buttonVoltage; byte button = BUTTON_NONE; // return no button pressed if the below checks don't write to btn //read the button ADC pin voltage buttonVoltage = analogRead( BUTTON_ADC_PIN ); //sense if the voltage falls within valid voltage windows if( buttonVoltage < ( RIGHT_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_RIGHT; } else if( buttonVoltage >= ( UP_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( UP_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_UP; } else if( buttonVoltage >= ( DOWN_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( DOWN_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_DOWN; } else if( buttonVoltage >= ( LEFT_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( LEFT_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_LEFT; } else if( buttonVoltage >= ( SELECT_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( SELECT_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_SELECT; } //handle button flags for just pressed and just released events if( ( buttonWas == BUTTON_NONE ) && ( button != BUTTON_NONE ) ) { //the button was just pressed, set buttonJustPressed, this can optionally be used to trigger a once-off action for a button press event //it's the duty of the receiver to clear these flags if it wants to detect a new button change event buttonJustPressed = true; buttonJustReleased = false; } if( ( buttonWas != BUTTON_NONE ) && ( button == BUTTON_NONE ) ) { buttonJustPressed = false; buttonJustReleased = true; } //save the latest button value, for change event detection next time round buttonWas = button; return( button ); }