Sunday, February 10, 2013

LCD Game Part 3 - Building the User Interface

Hey, Digisparklers, we're back. In part 2 we learned how to select true random numbers by having the user press a button to start our game. In this part we will start to prepare a way for the user to select how many digits he or she wants to flip. When I wrote this project for the Arduino, I had the luxury of using a 20 column by 4 row display. This time I am limited to only 16 by 2 because that is the only I2C LCD I have at this time. I think I have come up with a clever work-around for this limitation.

When I had more rows to work with I was able to design a cursor that sat at the top of the row under the digits. I still had plenty of room on the screen to display both the current score and a high score for the game. I would like to include both of those scoring features, but that leaves no place for a cursor.

As the video above shows, there is still a way to do both. One at a time, I am sifting the characters in my sting of digits one place to the left. Currently I have set it up to do that at the rate of one per second. Later when we start to build skill levels into the game we will have a way to speed that up to make the puzzle more challenging.

Just for demonstration purposes, the video and the code that we will discuss here, repeats the randomization of the digits and walks through the shifting process. Once the user presses the button to start the game, the button is no longer read by the program so it does nothing but light the LED on the board. We will put it to work in part 4.

So let's look at what makes all this work. The key to digit shifting routine is the command

    lcd.setCursor(tick + 2,0);   

This line determines where the next character we print on the LCD is going to appear.
lcd.setCursor(x,y) requires two values. The first is which column: 0 - 15, and the second is which row: 0-1. With those two items you can print anywhere on the display. Think of it as a starting point. First you print a letter, or a word, here and the next time you print, your characters will be automatically appear in the next position. tick starts out at zero and we increment it each time we print a digit until every digit has been printed again in its new spot. We also use tick to determine which character in the string to print:

    lcd.print(workingString[tick]);

That gives us two copies of the same digit, so we simply follow up by printing a space which erases the unwanted image. We don't need to fuss over where, because we know that it will be the next character after the last one we printed. I have packaged all of this into the following function.


void shiftLeft(){
  if (tick < 10){
      delay(1000);
      lcd.setCursor(tick + 2,0);   
      lcd.print(workingString[tick]);
      lcd.print(' ');
      tick++;
    
    }else{
      delay(1500);
      lcd.clear();
      delay(1500);
      scrambleString();
      displayWorkingString();
      tick = 0;
      
  }
}

The 'else' part of the function is just some temporary code to let us see how this is going to play on our display. To make this all work, we need to add the following highlight line to our loop() function.

void loop()  {
  btnPress = digitalRead(myButton);
  if (btnPress==1){
    digitalWrite(1,1);
  }else{
    digitalWrite(1,0);
  }
  shiftLeft();
}

That let's us test our procedures, we will move the line to a new location later. 

So what's next? We will need to check Sparky's button while the numbers are shifting left. Stop that nonsense in its tracks and flip a batch of digits around. Then we do it all again. As before, you can copy the code we've written so far below and try it yourself. C U next time.















/* ATtiny85 as an I2C Master   Ex2        BroHogan                           1/21/11
 * Modified for Digistump - Digispark LCD Shield by Erik Kettenburg 11/2012
 * SETUP:
 * ATtiny Pin 1 = (RESET) N/U                      ATtiny Pin 2 = (D3) N/U
 * ATtiny Pin 3 = (D4) to LED1                     ATtiny Pin 4 = GND
 * ATtiny Pin 5 = SDA on DS1621  & GPIO            ATtiny Pin 6 = (D1) to LED2
 * ATtiny Pin 7 = SCK on DS1621  & GPIO            ATtiny Pin 8 = VCC (2.7-5.5V)
 * NOTE! - It's very important to use pullups on the SDA & SCL lines!
 * PCA8574A GPIO was used wired per instructions in "info" folder in the LiquidCrystal_I2C lib.
 * This ex assumes A0-A2 are set HIGH for an addeess of 0x3F
 * LiquidCrystal_I2C lib was modified for ATtiny - on Playground with TinyWireM lib.
 * TinyWireM USAGE & CREDITS: - see TinyWireM.h
 */

//#define DEBUG
#include <TinyWireM.h>                  // I2C Master lib for ATTinys which use USI - comment this out to use with standard arduinos
#include <LiquidCrystal_I2C.h>          // for LCD w/ GPIO MODIFIED for the ATtiny85

#define GPIO_ADDR     0x27             // (PCA8574A A0-A2 @5V) typ. A0-A3 Gnd 0x20 / 0x38 for A - 0x27 is the address of the Digispark LCD modules.


LiquidCrystal_I2C lcd(GPIO_ADDR,16,2);  // set address & 16 chars / 2 lines

String winner ="0123456789";            // a test string to easily see game is over
String workingString = "0123456789";    // all the digits that we will scramble at the beginning of the game
int rnd;     // will hold a random number for us.
int myButton = 5; // our button hooks up here
int btnPress;     // is it down or not
byte tick = 0;

void setup(){
  TinyWireM.begin();                    // initialize I2C lib - comment this out to use with standard arduinos
  lcd.init();                           // initialize the lcd 
  lcd.clear();
  lcd.backlight();
  pinMode (myButton, INPUT);
  pinMode(1, OUTPUT); //LED on Model A
  
  scramblePrompt();
  scrambleString();
  displayWorkingString();
  delay(3000);
}

void loop()  {
  btnPress = digitalRead(myButton);
  if (btnPress==1){
    digitalWrite(1,1);
  }else{
    digitalWrite(1,0);
  }
  shiftLeft();
}

void shiftLeft(){
if (tick < 10){
    delay(1000);
    lcd.setCursor(tick + 2,0);   
    lcd.print(workingString[tick]);
    lcd.print(' ');
    tick++;
    
  }else{
    delay(1500);
    lcd.clear();
    delay(1500);
    scrambleString();
    displayWorkingString();
    tick = 0;
    
  }
}

void scramblePrompt(){
  lcd.noCursor();
  lcd.print("NUMBER FLIP-FLOP");
  lcd.setCursor(0,1);
  lcd.print("Press Button Now");
  
  do {
    rnd=random(9);
  } while (!digitalRead(myButton));
 }



void scrambleString() {
  lcd.clear();
  lcd.print("NUMBER FLIP-FLOP");
  
  for (int i=0; i<10; i++){
    rnd=random(9);
    swap(i,rnd);
  }
  delay(2000);
  lcd.clear();
   
}

void swap(int x, int y){
  byte hold=workingString[x];
  workingString[x]=workingString[y];
  workingString[y]=hold;
}
  

void displayWorkingString(){
  lcd.setCursor(3,0);
  lcd.print(workingString);

  lcd.noCursor();
  
}


No comments:

Post a Comment