We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I'm programming my arduino to make a reaction time game as a sketch for a more serious scientific study. It works like this
I've managed to work ok all steps from 1 through 5, but I cannot see how to get to work step 6. The way the code is written, after 5 it loops back to 1, infinitely. Any ideas on how to solve this?
/* REACTION TIME (with 2 leds) v2.1 beta
Luis Andrés Gonzalez
Reaction time original version from http://www.instructables.com/id/Arduino-Reaction-Time-Tester/?ALLSTEPS
Send data to processing via the Serial Port original from By Elaine Laguerta http://url/of/online/tutorial.cc
*/
const int switchPin = 6; // pin where the button will be connected
const int ledPin1 = 2 ; // Left LED
const int ledPin2 = 8 ; // Middle LED
const int ledPin3 = 12; // Right LED
// declare some variables:
boolean lastButton = LOW;
boolean currentButton = LOW;
boolean gameStarted = false; // true if game has started
boolean timerFlag = false; // true if timer is running
long startTime;
long endTime;
int randomSeconds;
long beginTime;
float elapsedTime;
int maxTimer = 5 * 1000;
float totalTime;
void setup() {
// Setup button and LEDs:
pinMode(switchPin, INPUT);
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
// Begin serial communication
Serial.begin(9600);
}
void loop() {
// Check https://www.arduino.cc/en/Tutorial/StateChangeDetection to understand the following code.
if ((millis() - beginTime) > (randomSeconds + maxTimer)) {
Stop();
//resets game:
gameStarted = false;
timerFlag = false;
currentButton = LOW;
lastButton = LOW;
Blink(2);
Serial.println("message");
}
// read the pushbutton input pin in the current loop:
currentButton = buttonPressed(lastButton);
// if there's a change from low to high (comparing last and current loop) then the following is true
if (currentButton == HIGH && lastButton == LOW) { // outer IF
if (gameStarted == false) { // middle IF. starts the game
gameStarted = true;
randomSeconds = random(2, 5) * 1000; // generates a random number of seconds between 3 and 8
Blink(1);
Serial.println("9090"); // signal code for start sound
beginTime = millis();
} else {
if ((millis() - beginTime) >= randomSeconds) {
Stop();
gameStarted = false;
timerFlag = false;
} else if ((millis() - beginTime) < randomSeconds ) {
gameStarted = false;
timerFlag = false;
Serial.println("1010"); // signal code for early response
Blink(3);
}
}
} // end outer if
// save the current state as the last state,
//for next time through the loop
lastButton = currentButton;
// If true, starts the response time timer and lights up the LED
if (gameStarted == true && (millis() - beginTime) >= randomSeconds && timerFlag == false) {
timerFlag = true;
Start();
}
} // end void loop
//===========================================================================================
boolean buttonPressed(boolean last) { //button debouncing function
boolean current = digitalRead(switchPin);
if (last != current) {
delay(5);
current = digitalRead(switchPin);
}
return current;
}
void Start() {
startTime = millis();
Light(ledPin1, "on");
}
void Stop() {
if ( (millis() - beginTime)
Answers
Somehow your code is not complete at the very end
Anyway. I suggest you work with states. Like states of a program
Google state within the forum.
You can use
if(state==stateWaitForRestart) {
etc.
When you define stateWaitForRestart as
int stateWaitForRestart=6;
Before setup()
make an if else if clause for each state
Nothing I repeat nothing is allowed outside the if ... else if {...} clause in draw()
but thus you get a very readable code and all things will fall into place
Just handle the states and the Transformation between them right
Thanks @Chrisir. I've followed your advice and from everyone else and I've revamped the code to deal with states. Now the problem is that I can't manage to make the reaction timer to work. It shows an ever-increasing ammount of time.
/* REACTION TIME (with 2 leds) v3.0 beta Luis Andrés Gonzalez Reaction time original version from http://www.instructables.com/id/Arduino-Reaction-Time-Tester/?ALLSTEPS Send data to processing via the Serial Port original from By Elaine Laguerta http://url/of/online/tutorial.cc */ #define TIMEOUT_MILLISECONDS 5000UL // not too tough; make smaller for a bigger challenge 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, }; enum Outcomes { GOOD, EARLY, LATE, }; const byte buttonPin = 6; // pin where the button will be connected const byte ledPin1 = 3 ; // Left LED const byte ledPin2 = 8 ; // Middle LED const byte ledPin3 = 12; // Right LED GameState gameState = STAND_BY; Outcomes outcome; GameState lastState; // declare some variables: boolean lastButton = LOW; boolean pressed = LOW; unsigned long lastMillis = 0; unsigned long elapsedMillis; boolean gameStarted = false; // true if game has started boolean timerFlag = false; // true if timer is running long startTime; long endTime; int randomSeconds; long beginTime; float elapsedTime; int maxTimer = 5 * 1000; float totalTime; int brightness = 0; // how bright the LED is int fadeAmount = 5; // how many points to fade the LED by void setup() { // Setup button and LEDs: pinMode(buttonPin, INPUT); pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); pinMode(ledPin3, OUTPUT); // Begin serial communication Serial.begin(9600); Serial.println(stateText[gameState]); } void loop() { // read the pushbutton input pin in the current loop: pressed = checkButton(); if (gameState == STAND_BY) { Fade(); if (pressed) { Light(ledPin1, "off"); randomSeconds = random(2, 5) * 1000; // generates a random number of seconds between 2 and 5 Blink(1); startTime = millis(); gameState = ACTIVE_GAME; Serial.println("9090"); // signal code for start sound Serial.println(stateText[gameState]); } } else if (gameState == ACTIVE_GAME) { elapsedMillis = millis() - lastMillis; if (elapsedMillis >= randomSeconds) { if (elapsedMillis < randomSeconds + TIMEOUT_MILLISECONDS) { Light(ledPin1, "on"); } else if (elapsedMillis > randomSeconds + TIMEOUT_MILLISECONDS) { Serial.println("TimeOut!!"); Light(ledPin1, "off"); gameState = LATE_PRESS; } } else if (pressed) { if (digitalRead(ledPin1)) { long myTime = millis() - startTime; Serial.println(float (myTime/1000)); gameState = GOOD_PRESS; Serial.println(stateText[gameState]); } else { gameState = EARLY_PRESS; } } } else if (gameState == EARLY_PRESS) { Serial.println("1010"); // signal code for early response Blink(3); gameState = STAND_BY; Serial.println("Standing by..."); delay(1000); } else if (gameState == GOOD_PRESS) { gameState = STAND_BY; Serial.println("Standing by..."); delay(1000); if (pressed) { } } else if (gameState == LATE_PRESS) { gameState = STAND_BY; Serial.println("Standing by..."); delay(1000); } } bool checkButton() { static byte lastButtonState = 1; byte buttonState = digitalRead(buttonPin); if (buttonState == 0 && lastButtonState == 1 && millis() - lastMillis > 75UL) //In line 100 the else makes no sense.
I feel the version is still too complicated
You need more states: activegame_begin // led off
activegame_waiting // led on
Also you still have code outside if state== etc.
Also you have delay (1000) you shouldn't..... handle this with new states
The main goal is to make things work the way it was sketched. This program is never intended to work this way, so I think adding more states is an extra work for no purpose.
The
ifaround theelsein line 100 is the one that checks if the button is pressed before the light is turned on. Is not as elegant as declaring a state likeLIGHT_ONbut I like the fact that relies directly on the fact that the light is actually turned on.Anyways, you're right the code is messy. I solved the issue around the timer and remove some clutter. Here's the last code that does the trick.
/* REACTION TIME (with 2 leds) v3.0 Luis Andrés Gonzalez Reaction time original version from http://www.instructables.com/id/Arduino-Reaction-Time-Tester/?ALLSTEPS Send data to processing via the Serial Port original from By Elaine Laguerta http://url/of/online/tutorial.cc */ #define TIMEOUT_MILLISECONDS 5000UL // not too tough; make smaller for a bigger challenge 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, }; const byte buttonPin = 6; // pin where the button will be connected const byte ledPin1 = 3 ; // Left LED const byte ledPin2 = 8 ; // Middle LED const byte ledPin3 = 11; // Right LED GameState gameState = STAND_BY; GameState lastState; // declare some variables: boolean lastButton = LOW; boolean pressed = LOW; unsigned long lastMillis = 0; unsigned long elapsedMillis; long startTime; long endTime; int randomMillis; long beginTime; float elapsedTime; float totalTime; int brightness = 0; // how bright the LED is int fadeAmount = 5; // how many points to fade the LED by void setup() { // Setup button and LEDs: pinMode(buttonPin, INPUT); pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); pinMode(ledPin3, OUTPUT); // Begin serial communication Serial.begin(9600); } void loop() { startTime = millis (); // read the pushbutton input pin in the current loop: pressed = checkButton(); if (gameState == STAND_BY) { Fade(); Light(ledPin1,"off"); if (pressed) { endTime = millis(); Light(ledPin1, "off"); randomMillis = random(2, 8) * 1000; // generates a random number of milliseconds between 2 and 5 Blink(1); gameState = ACTIVE_GAME; Light(ledPin3,"off"); Serial.println("9090"); // signal code for start sound beginTime = millis() - lastMillis; } } else if (gameState == ACTIVE_GAME) { elapsedMillis = millis() - lastMillis; if (elapsedMillis >= randomMillis) { if (elapsedMillis < randomMillis + TIMEOUT_MILLISECONDS) { Light(ledPin1, "on"); } else if (elapsedMillis > randomMillis + TIMEOUT_MILLISECONDS) { Serial.println("7777"); delay(1000); Light(ledPin1, "off"); gameState = LATE_PRESS; } } else if (pressed) { if (digitalRead(ledPin1)) { gameState = GOOD_PRESS; } else { gameState = EARLY_PRESS; } } } else if (gameState == EARLY_PRESS) { Serial.println("1010"); // signal code for early response Blink(3); gameState = STAND_BY; delay(1000); } else if (gameState == GOOD_PRESS) { gameState = STAND_BY; long reactionTime = (startTime - endTime - randomMillis); Serial.println(reactionTime); } else if (gameState == LATE_PRESS) { gameState = STAND_BY; delay(1000); } } bool checkButton() { static byte lastButtonState = 1; byte buttonState = digitalRead(buttonPin); if (buttonState == 0 && lastButtonState == 1 && millis() - lastMillis > 5UL) //Great that you accomplished it!
I really think states help to make a program better. You can hunt errors easier, you got better compartmentalization / sections ...
It's better readable, better maintainable
States are not a goal i itself but only a tool. A powerful tool.
You can auto-format your code with ctrl-t btw (iirc for arduino as well)
Since those above are also constants, just like TIMEOUT_MILLISECONDS, STAND_BY, OUTPUT, etc., the convention dictates they should be entirely capitalized too! Either as
constor#define: ;;)