Using millis command to give 2 seconds to input value

Hey guys,

I'm writing a code to programme a game on a joystick potentiometer. When a button is pressed, one of four LEDs randomly lights up. The player must then press the joystick in the given direction. If they get it right, they get "Well done!". If they don't press the button quick enough, they get "Too slow". If they get it wrong, they get "Incorrect".

I have it programmed such that the code will wait for 2 seconds before giving the "Too slow" command. However, when I look to reset this 2 second millis() command it doesn't seem to work, i.e. when I press the push button to start, I immediately get the "Too slow" output unless I'm already holding the potentiometer in the correct direction. I can't figure out what's wrong with my code, can anyone guide me? Any help is greatly appreciated!

Full code:

int choice;
boolean lastButton = LOW;
unsigned long currentMillis;
unsigned long startMillis;

const unsigned long delayCount1 = 2000; // Delay for game
const unsigned long delayCount2 = 1000; // Delay for wait at end

const int pinNorthLED = 2;
const int pinEastLED = 3;
const int pinSouthLED = 4;
const int pinWestLED = 5;
const int pinStartButton = 6;
const int pinNorthSouthPot = A0;
const int pinEastWestPot = A1;

bool isRunning = false; // true = running, false = waiting to start

void setup() {
  Serial.begin(9600);
  pinMode(pinNorthLED, OUTPUT); // LED N
  pinMode(pinEastLED, OUTPUT); // LED E
  pinMode(pinSouthLED, OUTPUT); // LED S
  pinMode(pinWestLED, OUTPUT); // LED W
  pinMode(pinStartButton, INPUT_PULLUP); // Push to start
}

void all_LED_off() {
  digitalWrite(pinNorthLED, LOW);
  digitalWrite(pinEastLED, LOW);
  digitalWrite(pinSouthLED, LOW);
  digitalWrite(pinWestLED, LOW);
}

void loop() {
  currentMillis = millis();
  int currentButton = digitalRead(pinStartButton);

  if (lastButton != currentButton) {
    // button has changed state

    if (currentButton == LOW) {
      // button was pressed, either we are starting or stopping
      if ( isRunning == false ) {
        // begin running
        startGame();
      }
    }
    lastButton = currentButton;
  }

  if ( isRunning ) {
    // check for success or too slow
    checkGame();
  }
}

void startGame() {
  choice = random(0, 4); // Random number: 0, 1, 2, or 3

  all_LED_off();
  switch (choice) {
    case 0: digitalWrite(pinNorthLED, HIGH); break;
    case 1: digitalWrite(pinEastLED, HIGH); break;
    case 2: digitalWrite(pinSouthLED, HIGH); break;
    case 3: digitalWrite(pinWestLED, HIGH); break;
  }
  startMillis = currentMillis;
  isRunning = true;
}

void checkGame() {

  int potInput0 = analogRead(pinNorthSouthPot);
  int potInput1 = analogRead(pinEastWestPot);
  int response = -1;

  // figure out the joystick movement
  if (potInput0 > 800 && 450 < potInput1 && potInput1 < 650) {
    response = 0; // North
  }
  else if (potInput1 < 100 && 450 < potInput0 && potInput0 < 650) {
    response = 1; // East
  }
  else if (potInput0 < 100 && 450 < potInput1 && potInput1 < 650) {
    response = 2; // South
  }
  else if (potInput1 > 800 && 450 < potInput0 && potInput0 < 650) {
    response = 3; // West
  }

  // check elapsed time
  if ( currentMillis - startMillis >= delayCount1 ) {
    reportLight();
    Serial.println("Too slow");
    all_LED_off();
    delay(delayCount2);
    isRunning = false;  // reset for next time
    return;
  }

  // see if the movement is correct
  if ( response == choice ) {
    reportLight();
    Serial.println("Well done!");
    Serial.print("previousMillis: ");
    Serial.println(startMillis);
    Serial.print("currentMillis: ");
    Serial.println(currentMillis);
    all_LED_off();
    delay(delayCount2);
    isRunning = false;  // reset for next time
    return;
  }

  // if there was a response at this point, it is wrong
  if ( response >= 0 ) {
    reportLight();
    Serial.println("Incorrect");
    delay(delayCount2);
    isRunning = false;  // reset for next time
  }
}

void reportLight() {
  Serial.print("The light is ");
  switch ( choice ) {
  case 0: Serial.println("UP"); break;
  case 1: Serial.println("RIGHT"); break;
  case 2: Serial.println("DOWN"); break;
  case 3: Serial.println("LEFT"); break;
  }
}

These lines should be in setup(), not loop()

  pinMode(6, INPUT_PULLUP); // Push to start
  
  pinMode(2, OUTPUT); // LED N
  pinMode(3, OUTPUT); // LED E
  pinMode(4, OUTPUT); // LED S
  pinMode(5, OUTPUT); // LED W

This should a be global variable

    unsigned long past = 0;

And there are many more errors.

You seem to be at an early stage of learning to code. You are copying & pasting lines of code from other sketches without understand what each line means or what it is for. My advice would be to take some basic code tutorials. I'm sorry I can't recommend any tutorials. When I learned to code, the internet did not exist yet.

Hello
As already mentioned by PaulRB your sketch is spaghetti coded.
At first donĀ“t use magic number in the code. All code repeation shall be covered by using strutures, arrays and methodes.
Before start with coding the study of the IPO model is very useful.

My issue is the millis() command; I appreciate my code is bloated (as I wrote it myself), but it actually works which is the main thing, I just want to be able to fix the timing structure so that I have 2 seconds to input the correct value (currently I immediately get "Too slow", presumably due to 'past' not resetting properly)

Hi mecheng,

you are trying to be fast and you have reached 80% functionality in 20% of the time.
Now you are at the point to invest either
800% (this is not a typo I mean eight hundred percent) time of time fiddling around with your code trying to modify it

or just investing 80% time (which is 10x less than 800%)
by doing two things:

  • learning some more basics how to code especially how to use millis()
  • modifying your code to consequently use constants for IO-Pins and other constants to keep the code consistant and to make your code much less error-prone.

But maybe coding this way is just too boring for you.
If you enjoy running in circles without moving forward, tinkering around, trying this, trying that in a quick manner because it makes you feel "busy" go ahead.

best regards Stefan

I've added a function as well as some variable names for the delays. My issue is still with the millis() command; when troubleshooting, I can't find the optimum location to position currentMillis and previousMillis to give me 2 seconds on the button. Where should I be positioning them?

could you please RE-edit your posting above and delete the too much empty lines?

as a quick-tip replace

ALL

delays be non-blocking timing

you are a fast copy&paster without understanding what you are doing
if you declare variables inside an if-condition the scope of these variables is limited to this if-condition.

I guess you have to read what "declaring a variable" and what "scope" means.
I will not do remotely instructed keyboard-typing. Or writing code for you.

read here
https://www.arduino.cc/en/Reference/VariableDeclaration
(but I guess you keep on trying this, trying that the "quick" way trying to get it to work)
[it is not quick it is timewasting]

best regards Stefan

You need to record the time you start... something like this [with a few variable names that make more sense] and some removal of duplicate code

int choice;
boolean lastButton = LOW;
unsigned long currentMillis;
unsigned long startMillis;

const unsigned long delayCount1 = 2000; // Delay for game
const unsigned long delayCount2 = 1000; // Delay for wait at end

const int pinNorthLED = 2;
const int pinEastLED = 3;
const int pinSouthLED = 4;
const int pinWestLED = 5;
const int pinStartButton = 6;
const int pinNorthSouthPot = A0;
const int pinEastWestPot = A1;

bool isRunning = false; // true = running, false = waiting to start

void setup() {
  Serial.begin(9600);
  pinMode(pinNorthLED, OUTPUT); // LED N
  pinMode(pinEastLED, OUTPUT); // LED E
  pinMode(pinSouthLED, OUTPUT); // LED S
  pinMode(pinWestLED, OUTPUT); // LED W
  pinMode(pinStartButton, INPUT_PULLUP); // Push to start
}

void all_LED_off() {
  digitalWrite(pinNorthLED, LOW);
  digitalWrite(pinEastLED, LOW);
  digitalWrite(pinSouthLED, LOW);
  digitalWrite(pinWestLED, LOW);
}

void loop() {
  currentMillis = millis();
  int currentButton = digitalRead(pinStartButton);

  if (lastButton != currentButton) {
    // button has changed state

    if (currentButton == LOW) {
      // button was pressed, either we are starting or stopping
      if ( isRunning == false ) {
        // begin running
        startGame();
      }
    }
    lastButton = currentButton;
  }

  if ( isRunning ) {
    // check for success or too slow
    checkGame();
  }
}

void startGame() {
  choice = random(0, 4); // Random number: 0, 1, 2, or 3

  all_LED_off();
  switch (choice) {
    case 0: digitalWrite(pinNorthLED, HIGH); break;
    case 1: digitalWrite(pinEastLED, HIGH); break;
    case 2: digitalWrite(pinSouthLED, HIGH); break;
    case 3: digitalWrite(pinWestLED, HIGH); break;
  }
  startMillis = currentMillis;
  isRunning = true;
}

void checkGame() {

  int potInput0 = analogRead(pinNorthSouthPot);
  int potInput1 = analogRead(pinEastWestPot);
  int response = -1;

  // figure out the joystick movement
  if (potInput0 > 800 && 450 < potInput1 && potInput1 < 650) {
    response = 0; // North
  }
  else if (potInput1 < 100 && 450 < potInput0 && potInput0 < 650) {
    response = 1; // East
  }
  else if (potInput0 < 100 && 450 < potInput1 && potInput1 < 650) {
    response = 2; // South
  }
  else if (potInput1 > 800 && 450 < potInput0 && potInput0 < 650) {
    response = 3; // West
  }

  // check elapsed time
  if ( currentMillis - startMillis >= delayCount1 ) {
    reportLight();
    Serial.println("Too slow");
    all_LED_off();
    delay(delayCount2);
    isRunning = false;  // reset for next time
    return;
  }

  // see if the movement is correct
  if ( response == choice ) {
    reportLight();
    Serial.println("Well done!");
    Serial.print("previousMillis: ");
    Serial.println(startMillis);
    Serial.print("currentMillis: ");
    Serial.println(currentMillis);
    all_LED_off();
    delay(delayCount2);
    isRunning = false;  // reset for next time
    return;
  }

  // if there was a response at this point, it is wrong
  if ( response >= 0 ) {
    reportLight();
    Serial.println("Incorrect");
    delay(delayCount2);
    isRunning = false;  // reset for next time
  }
}

void reportLight() {
  Serial.print("The light is ");
  switch ( choice ) {
  case 0: Serial.println("UP"); break;
  case 1: Serial.println("RIGHT"); break;
  case 2: Serial.println("DOWN"); break;
  case 3: Serial.println("LEFT"); break;
  }
}

Thanks for the minor edit - I actually managed to get it working correctly with a

while(millis()-currentMillis < delayCount)

command

this line of code:

again shows:

using a while loop is blocking again and kills the advantages of non-blocking coding.
You haven't posted your full sketch. It might be that this while-loop embraces enough-code to make it work

calculating the timedifference this way will fail in case of a rollover of millis()
well if your satisfied with an eventual failure - all is fine
best regards Stefan