[SOLVED] Schrodinger's code (two states happening at the same time?)

So, in this program, at setup you get asked what kind of delay you want for a reaction time “game” is it going to be a RANDOM delay time or a FIXED one?
After you input that answer the game begins. If you choose FIXED all things are nice, but when you choose RANDOM the delay times get weird.

This block of code sets the delayVal in each case:

switch (gamemode) {
        case (FIXED_TIME):
          delayVal = delayMillis;
          break;

        case (RANDOM_TIME):
          delayVal = random(2, 8) * 1000; // generates a random number of milliseconds between 2 and 8
          break;
      }

when the button is pressed correctly, the following code is executed:

else if (gameState == GOOD_PRESS) {
      
      long reactionTime = (startTime - endTime - delayVal);
      
      gameState = STAND_BY;
    }

What actually happens is that when choosing random delays, the substraction is not done the way is supposed to happen. delayVal is fixed at 2000, when it should take the appropriate random value.

Here’s a serial output for both modes, with some debug info:

Enter Test mode:
Press 1 for random mode
Press 2 for fixed time mode
I received: 2
Game mode selected = Fixed time mode
9090
startTime:25626
endTime:23419
Delayval:2000
207
9090
startTime:30316
endTime:28125
Delayval:2000
191
9090
startTime:35192
endTime:32693
Delayval:2000
499
Enter Test mode:
Press 1 for random mode
Press 2 for fixed time mode
I received: 1
Game mode selected = Random mode
9090
debugtime206
startTime:12904
endTime:10698
Delayval:4000
-1794
9090
debugtime146
startTime:17670
endTime:15524
Delayval:2000
146
9090
debugtime150
startTime:23101
endTime:20951
Delayval:4000
-1850

Here ´´debugtime´´ is defined as ´´startTime - endTime - 2000´´ and this should be the actual value. Why is this happening? Where does this 2000 value come from? If all the rest is equal in the code, only the delayVal is calculated differently!

Thanks

The complete sketch:

/* COGNITIVE BIAS TEST 0.1 beta
    Luis Andrés Gonzalez, January 2016

*/

// declare constants:
const byte BUTTON_PIN = 6;    // pin where the button will be connected
const byte LED1_PIN   = 3 ;   // Left LED
const byte ledPin2    = 8 ;   // Middle LED
const byte ledPin3    = 11;   // Right LED
#define TIMEOUT_MILLISECONDS  7000UL  // maximum time for the animal to respond to the test, in milliseconds. Default is 60 seconds.

// declare some variables:
boolean lastButton = LOW;       // previous loop cycle button status
boolean pressed = LOW;          // button pressed flag
unsigned long lastMillis = 0;   // milliseconds passed since the last loop cycle
unsigned long elapsedMillis;    // total session time marker value
long startTime;                 // time since light turned on
long endTime;                   // time for response to stimulus
int randomMillis;               // randomMilliseconds
float elapsedTime;              // duration for ACTIVE_GAME
int fadeAmount = 5;    // how many points to fade the LED by
int brightness = 0;    // how bright the LED is
long delayMillis = 2000;
long delayVal;
bool answered = false;

// array for state text
const char* const stateText[] = { "Stand By", "Active Game", "Early Press", "Good Press", "Late Press"};

enum GameState {
  STAND_BY,
  ACTIVE_GAME,
  EARLY_PRESS,
  GOOD_PRESS,
  LATE_PRESS,
};

GameState gameState = STAND_BY;
GameState lastState;

const char* const modeText[] = {"Random mode", "Fixed time mode"};
enum GameMode {
  RANDOM_TIME,
  FIXED_TIME,
};

GameMode gamemode;

void setup() {
  // Setup button and LEDs:
  pinMode(BUTTON_PIN, INPUT);
  pinMode(LED1_PIN, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);

  // Begin serial communication
  Serial.begin(9600);
  Serial.println("Enter Test mode:");
  Serial.println("Press 1 for random mode");
  Serial.println("Press 2 for fixed time mode");

}

void loop() {

  getTestMode();

  if (answered) {
    startTime = millis ();                  //

    pressed = checkButton();               // read the pushbutton input pin in the current loop

    if (gameState == STAND_BY) {
      Light(LED1_PIN, "off");
      Fade();

      if (pressed) {                        // while on STAND_BY, if the start button is pressed then:
        Light(LED1_PIN, "off");              // reset ledPin1
        Light(ledPin3, "off");              // reset ledPin2

        endTime = millis();                 //
        Serial.println("9090");             // signal code for start sound

        Blink(1);
        gameState = ACTIVE_GAME;
      } // end if pressed

    }   // end STAND_BY


    else if (gameState == ACTIVE_GAME) {

      elapsedMillis = millis() - lastMillis;
      switch (gamemode) {
        case (FIXED_TIME):
          delayVal = delayMillis;
          break;

        case (RANDOM_TIME):
          delayVal = random(2, 8) * 1000; // generates a random number of milliseconds between 2 and 8
          break;
      }

      if (elapsedMillis >= delayVal) {
        if (elapsedMillis < delayVal + TIMEOUT_MILLISECONDS) {
          Light(LED1_PIN, "on");
        }
        else if (elapsedMillis > delayVal + TIMEOUT_MILLISECONDS) {
          gameState = LATE_PRESS;
        }

      }

      handleButton(); // checks if the button is pressed GOOD or EARLY


    } // end ACTIVE_GAME

    else if (gameState == EARLY_PRESS) {

      Serial.println("1010"); // signal code for early response
      Blink(3);
      gameState = STAND_BY;

    }
    else if (gameState == GOOD_PRESS) {
      if (gamemode == RANDOM_TIME) {
        long debugTime = (startTime - endTime - 2000);
        Serial.print("debugtime");
        Serial.println(debugTime);
      }

      long reactionTime = (startTime - endTime - delayVal);
      //DEBUG

      Serial.print("startTime:");
      Serial.println(startTime);
      Serial.print("endTime:");
      Serial.println(endTime);
      Serial.print("Delayval:");
      Serial.println(delayVal);
      Serial.println(reactionTime);
      gameState = STAND_BY;
    }

    else if (gameState == LATE_PRESS) {
      Serial.println("7777");
      Light(LED1_PIN, "off");
      Blink(4);
      gameState = STAND_BY;
    }

  } // end if answered

}// end loop

//=================================================================================

bool checkButton() {
  static byte lastButtonState = 1;
  byte buttonState = digitalRead(BUTTON_PIN);
  byte bounceDelay = 5UL;
  if (buttonState == 0 && lastButtonState == 1 && millis() - lastMillis > bounceDelay) {
    lastMillis = millis();
    //Serial.println("!");
    return true;
  }
  lastButtonState = buttonState;
  return false;
}

void handleButton() {
  if (pressed) {                 // checks if the button is pressed while the light is still off
    if (digitalRead(LED1_PIN)) {
      gameState = GOOD_PRESS;
    }
    else  {
      gameState = EARLY_PRESS;
    }
  }
}

void Light(int lednumber, String onOff) {
  if (onOff == "on") {
    digitalWrite(lednumber, HIGH);

  } else if (onOff == "off") {
    digitalWrite(lednumber, LOW);
  }
}
void Blink(int repetitions) {
  for (int i = 0; i < repetitions; i++) {
    digitalWrite(ledPin2, HIGH);
    delay(100);
    digitalWrite(ledPin2, LOW);
    delay(100);
  }
}

void Fade() {

  analogWrite(ledPin3, brightness);

  brightness = brightness + fadeAmount;
  if (brightness == 0 || brightness == 255) {
    fadeAmount = -fadeAmount ;
  }
  delay(30);
}

int getTestMode() {
  if (!answered) {
    if (Serial.available()) {
      char ch = Serial.read();
      Serial.print("I received: ");
      Serial.println(ch);

      if (ch >= '1' && ch <= '2') {
        if (ch == '1') {
          gamemode = RANDOM_TIME;
        } else {
          gamemode = FIXED_TIME;
        }
        Serial.print("Game mode selected = ");
        Serial.println(modeText[gamemode]);
        answered = true;
      } else {
        Serial.print("Invalid option");
      }
    }
  }
}
delayVal = random(2, 8) * 1000; // generates a random number of milliseconds between 2 and 8

nope... it generates a random number of milliseconds between 2000 and 7000, but in increments of 1000.

consider:

delayVal = random(2000, 8000); // generates a random number of milliseconds between 2000 and 8000

to accurately reflect your apparent goal

I'm not sure if that even affects your problem, however.

Edit: learn about random() here.

I'm almost certain that this doesn't affects the weird value. In this case is just the comment that is wrong.

If you see the serial output, despite having different random delayVals, there are 2000 millis that are coming from god-knows where in all of the values.

try eliminating the parenthesis around the case options:

      elapsedMillis = millis() - lastMillis;
      switch (gamemode) {
        case (FIXED_TIME):
          delayVal = delayMillis;
          break;

        case (RANDOM_TIME):
          delayVal = random(2, 8) * 1000; // generates a random number of milliseconds between 2 and 8
          break;
      }

like this:

      elapsedMillis = millis() - lastMillis;
      switch (gamemode) {
        case FIXED_TIME:
          delayVal = delayMillis;
          break;

        case RANDOM_TIME:
          delayVal = random(2, 8) * 1000; // generates a random number of milliseconds between 2 and 8
          break;
      }

Can you explain what 2000ms you're talking about? I'm not seeing the 2000ms except for DelayVal and that's pretty explainable.

Delta_G:
Can you explain what 2000ms you’re talking about? I’m not seeing the 2000ms except for DelayVal and that’s pretty explainable.

Check the Serial output:

Game mode selected = Random mode

9090 <-a serial code to send to processing

debugtime206 <-what the real reaction time supposed to be, in ms

startTime:12904

endTime:10698

Delayval:4000 <-the delayval for this loop (which is executed correctly in the electronics).

-1794 ← what is shown as the actual reaction time. If you add 2000 to this you get 206.

Well you said that reaction time should be:

long reactionTime = (startTime - endTime - delayVal);

12904 - 10698 - 4000 == -1794

So it's printing what you told it to with that value of delayVal.

debugTime is calculated as:

long debugTime = (startTime - endTime - 2000);

Which looks to me like it should come out 2000 more than reactionTime if delayVal is 4000.

In the case in the middle in your output where delayVal is 2000 reactionTime and debugTime come out the same like they should since they're calculated the same.

Delta_G: Well you said that reaction time should be:

long reactionTime = (startTime - endTime - delayVal);

12904 - 10698 - 4000 == -1794

So it's printing what you told it to with that value of delayVal.

debugTime is calculated as:

long debugTime = (startTime - endTime - 2000);

Which looks to me like it should come out 2000 more than reactionTime if delayVal is 4000.

In the case in the middle in your output where delayVal is 2000 reactionTime and debugTime come out the same like they should since they're calculated the same.

My idea for a reaction time is like this

reaction time = total time - delayTime

and total time (the delay plus the reaction till pressing the button) here is calculated like this

total time = timestamp at the end - timestamp at start

(now I realized the names of the variables endTime and startTime were reversed, now it's fixed.

delayVal is calculated differently depending on the gamemode value. In theory, once start and end time are calculated (total time), I must substract delayVal in order to obtain the real reaction time. In the case of FIXED_TIME, delayTime is constant; in the case of RANDOM_TIME i have to account this variable delayTime, so that's why I originally used that formula.

What am I thinking wrong in here?

Why so convoluted? Why not capture the value of millis right at the end of your delay time and again right when the player pushes the button. Then it is a simple subtraction of two numbers.

I was looking on how to do that, but I got lost

Any chance that you could show me where?

It’s not magic. Just look at the places in your code where things are happening. I don’t really understand the flow of your code, but I’m guessing that this:

if (elapsedMillis >= delayVal) {
        if (elapsedMillis < delayVal + TIMEOUT_MILLISECONDS) {
          Light(LED1_PIN, "on");
        }

Is were we start right? If the player were to push the button the instant that light came on he’d have a 0 reaction time right? So capture your start time there.

I don’t get the two separate functions for checkButton and handleButton. I don’t really see where you’re going with that. But at one of the calls to one of those functions is where you are saying the player has pressed the button and the game is over and you can calculate the reaction time. Capture your end time right there.

Maybe it’s right here where you declare it a GOOD_PRESS:

if (digitalRead(LED1_PIN)) {
      gameState = GOOD_PRESS;
    }

I think a little time with a flow chart and some rethinking and you could rearrange this code to make a lot more sense.

Standby state:
   if button is pressed start the game capture the start time and move to Light on state
Light on state:
   if button is pressed then it's an early press.  Print something and end the game (move to standby state?)
   if delay time has passed move to Light off state
Light off state: 
   if button is pressed calculate your reaction time and end the game print out reaction time (move to Standby state?)

You only need one function to handle telling you if your button is pressed or not. Everything else can happen inside a switch case statement in loop over those three states.

I’ve already tried putting those markers where you say. That’s why this is driving me nuts!
If I put it where you say, I only get 1’s or 0’s (WTF :o ?!) as a reaction time.
There is some problem with the scope, or where in the loop the things are placed. I really don’t know.

Standby state:
if button is pressed start the game capture the start time and move to Light on state

check. Is already there.

if (gameState == STAND_BY) {
      Light(LED1_PIN, "off");
      Fade();

      if (pressed) {                        // while on STAND_BY, if the start button is pressed then:
        Light(LED1_PIN, "off");              // reset ledPin1
        Light(ledPin3, "off");              // reset ledPin2
        Blink(1);
        Serial.println("9090");             // signal code for start sound
        gameState = ACTIVE_GAME;
        startTime = millis();
      } // end if pressed

    }   // end STAND_BY

Light on state:
if button is pressed then it’s an early press. Print something and end the game (move to standby state?)
if delay time has passed move to Light off state

Check. The problem here is that I need to set the delay depending on the game mode. That is why there is the `switch` there.

else if (gameState == ACTIVE_GAME) {

      switch (gamemode) {
        case (FIXED_TIME):
          delayVal = delayMillis;
          break;

        case (RANDOM_TIME):
          delayVal = random(2000, 8000); // generates a random number of milliseconds between 2 and 8
          break;
      }

In order to put in action the delay elapsedMillis keeps the record of the time of the actual loop. After the designated delay milliseconds have passed, the light turns on:

      elapsedMillis = millis() - lastMillis;
      if (elapsedMillis >= delayVal) {
        if (elapsedMillis < delayVal + TIMEOUT_MILLISECONDS) {
          Light(LED1_PIN, "on");
          
        }

If there is a timeout, respond accordingly.

        else if (elapsedMillis > delayVal + TIMEOUT_MILLISECONDS) {
          gameState = LATE_PRESS;
        }

      }

      handleButton(); // checks if the button is pressed GOOD or EARLY


    } // end ACTIVE_GAME

LATE_PRESS works fine.

Light off state:
if button is pressed calculate your reaction time and end the game print out reaction time (move to Standby state?)

This is why handle and check button are different functions. Checkbutton is for debouncing purposes and handleButton are the instructions on what to do if the button is pressed.

void handleButton() {
  if (pressed) {                 // checks if the button is pressed while the light is still off
    
    if (digitalRead(LED1_PIN)) {
      gameState = GOOD_PRESS;
      endTime = millis ();
    }
    else  {
      gameState = EARLY_PRESS;
    }
  }
}

.

BTW, you should call randomSeed() in your setup.

See:

https://www.arduino.cc/en/Reference/Random

switch (gamemode) {
        case (FIXED_TIME):
          delayVal = delayMillis;
          break;

        case (RANDOM_TIME):
          delayVal = random(2, 8) * 1000; // generates a random number of milliseconds between 2 and 8
          break;
      }

Does it make sense for this to happen on every pass of loop during active game mode? Get a new value for the delay time and then a different one and then a different one? Or would it make more sense to calculate the random delay time once per game? Maybe right when you get your start time and start the game?

Where does lastMillis get a value?

luispotro: So, in this program, at setup you get asked what kind of delay you want for a reaction time "game" is it going to be a RANDOM delay time or a FIXED one? After you input that answer the game begins. If you choose FIXED all things are nice, but when you choose RANDOM the delay times get weird.

What actually happens is that when choosing random delays, the substraction is not done the way is supposed to happen. delayVal is fixed at 2000, when it should take the appropriate random value.

Here's a serial output for both modes, with some debug info:

Enter Test mode:
Press 1 for random mode
Press 2 for fixed time mode
I received: 2
Game mode selected = Fixed time mode
9090
startTime:25626
endTime:23419
Delayval:2000
207
9090
startTime:30316
endTime:28125
Delayval:2000
191
9090
startTime:35192
endTime:32693
Delayval:2000
499
Enter Test mode:
Press 1 for random mode
Press 2 for fixed time mode
I received: 1
Game mode selected = Random mode
9090
debugtime206
startTime:12904
endTime:10698
Delayval:4000
-1794
9090
debugtime146
startTime:17670
endTime:15524
Delayval:2000
146
9090
debugtime150
startTime:23101
endTime:20951
Delayval:4000
-1850

Here ´´debugtime´´ is defined as ´´startTime - endTime - 2000´´ and this should be the actual value. Why is this happening? Where does this 2000 value come from? If all the rest is equal in the code, only the delayVal is calculated differently!

Thanks

I don't understand. Your output is exactly what I would expect it to be.

Here are lines 128 - 146:

        else if (gameState == GOOD_PRESS) {
            if (gamemode == RANDOM_TIME) {
                long debugTime = (startTime - endTime - 2000);
                Serial.print("debugtime");
                Serial.println(debugTime);
            }

            long reactionTime = (startTime - endTime - delayVal);
            //DEBUG

            Serial.print("startTime:");
            Serial.println(startTime);
            Serial.print("endTime:");
            Serial.println(endTime);
            Serial.print("Delayval:");
            Serial.println(delayVal);
            Serial.println(reactionTime);
            gameState = STAND_BY;
        }

Line 150 has a fixed value of 2000. So, yeah, your output is as expected.

delayVal on the other hand, is correctly sometimes 2000, sometimes 4000, etc., and the reactionTime is correctly, start - end - delay. So what do you expect to be different?

luispotro: check. Is already there.

No, it's in stand by mode when you turn on the light. Capture that time when you turn OFF the light to start timing the reaction. Then you don't have to subtract the delay time.

If I were writing a game like this it would go something like this:

#define TIMEOUT 7000ul

enum State {
  STANDBY , LIGHTON, LIGHTOFF};
State state;

const byte buttonPin = 6;
const byte ledPin = 3;
int delayValue;
unsigned long gameStart;
unsigned long reactionStart;
boolean lastButtonCheck = false;
boolean thisButtonCheck = false;


void setup(){
  Serial.begin(115200);

  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  state = STANDBY;

  Serial.println("Starting Game");
}

void loop(){
  // Some edge detection for the button
  boolean b = checkButton();
  if(b != lastButtonCheck){
    lastButtonCheck = b;
    thisButtonCheck = b;
  }
  else {
    thisButtonCheck = false;
  }
    
  switch(state){
  case STANDBY:
    if(thisButtonCheck){
      gameStart = millis();
      delayValue = random(2000, 8000);
      digitalWrite(ledPin, HIGH);  // turn led on 
      state = LIGHTON;   
    }  
    break;

  case LIGHTON:
    if(thisButtonCheck){
      Serial.println("You pressed too early");
      state = STANDBY;
    }
    else if(millis() - gameStart >= delayValue){
      state = LIGHTOFF;
      digitalWrite(ledPin, LOW);  // led off time to react.
      reactionStart = millis();
    }
    break;

  case LIGHTOFF:
    if(thisButtonCheck){
      int reactionTime = millis() - reactionStart;
      Serial.print("You took ");
      Serial.print(reactionTime);
      Serial.println(" milliseconds");
      state = STANDBY;
    }
    else if(millis() - reactionStart >= TIMEOUT){
      Serial.println("Timeout");
      state = STANDBY;
    }
    break;
  }  // switch(state)
}

boolean checkButton(){  
  static unsigned long lastChange = millis();
  static int lastState = HIGH;
  int currentState = digitalRead(buttonPin);
  if(currentState != lastState){
    lastState = currentState;
    lastChange = millis();
  }
  // 50ms debounce
  if((millis() - lastChange > 50) && currentState == LOW){
    return true;
  }
  return false; 
}

You can add the part to setup for two modes.

I'll chek it out tomorrow and I'll let you know if it works as intended. Thank you, Delta_G :) .

Regarding the other questions, I was writing this reply:

Delta_G: Does it make sense for this to happen on every pass of loop during active game mode? Get a new value for the delay time and then a different one and then a different one? Or would it make more sense to calculate the random delay time once per game? Maybe right when you get your start time and start the game?

Where does lastMillis get a value?

Allright! In what if loop should I put the value? listMillis is used in the debouncer function.

arduinodlb: I don't understand. Your output is exactly what I would expect it to be.

delayVal on the other hand, is correctly sometimes 2000, sometimes 4000, etc., and the reactionTime is correctly, start - end - delay. So what do you expect to be different?

i don't expect a permanent offset of 2000 when those 2000 shouldn't be there, outputting negative reactive values! Also, there are two game modes and delayVal is supposed to behave differently according to those modes.

Delta_G: No, it's in stand by mode when you turn on the light. Capture that time when you turn OFF the light to start timing the reaction. Then you don't have to subtract the delay time.

the IF block inside the STAND_BY segment of the code is there for that purpose. Why would I start timing when I turn off the light?

luispotro: Why would I start timing when I turn off the light?

Because the reaction time is the time from the time the light goes off until the player hits the button right?

The way I understand the game:

Player presses button to start Light comes on for some random time Light goes off and player should react by pressing button

So the reaction time is the time from when the light goes off until the button is pressed. Why would you start counting the reaction time before the player is supposed to react?

luispotro: i don't expect a permanent offset of 2000 when those 2000 shouldn't be there, outputting negative reactive values! Also, there are two game modes and delayVal is supposed to behave differently according to those modes.

You don't have a permanent offset of 2000. All the calculations are working as expected.

Here's one part of your output

debugtime150
startTime:23101
endTime:20951
Delayval:4000
-1850

and it is correct.

23101 - 20951 - 4000 (the random delay) = -1850, as expected.

So, I don't know what you expect it to do differently??