Week 10
Weekly tasks
The idea
My idea was based on so-called Stroop effect. Simply, for our brain it is hard to acknowledge the colour when the word that has this colour means different colour. I decided to combine Arduino with Processing.Arduino
After last week I returned to Arduino (noises of happiness). To the Arduino I connected 5 different buttons with different colours. After button is pushed, the "Serial.println("b");" writes the coresponding colour.
#define BUTTON_RED 3 // r
#define BUTTON_GREEN 4 // g
#define BUTTON_BLUE 5 // b
#define BUTTON_YELLOW 2 // y
#define BUTTON_WHITE 6 // w
void setup() {
Serial.begin(9600);
pinMode(BUTTON_RED, INPUT_PULLUP);
pinMode(BUTTON_GREEN, INPUT_PULLUP);
pinMode(BUTTON_BLUE, INPUT_PULLUP);
pinMode(BUTTON_YELLOW, INPUT_PULLUP);
pinMode(BUTTON_WHITE, INPUT_PULLUP);
}
void loop() {
if (digitalRead(BUTTON_RED) == LOW) {
Serial.println("r");
delay(200);
}
if (digitalRead(BUTTON_GREEN) == LOW) {
Serial.println("g");
delay(200);
}
if (digitalRead(BUTTON_BLUE) == LOW) {
Serial.println("b");
delay(200);
}
if (digitalRead(BUTTON_YELLOW) == LOW) {
Serial.println("y");
delay(200);
}
if (digitalRead(BUTTON_WHITE) == LOW) {
Serial.println("w");
delay(200);
}
}
Processing
When I was satisfied with my hardware part of this miniproject, the main focus redirected to software part of it. From the lecture I understood that the easiest way would be to do it in "Processing". The main issue was that it is Java... did I work with Java before? No. Fortunately for me, tutorials (and Chatgpt) exist and I was able to produce this script. It is simplified version where you answer with keyboard and have limited time to do it.
String[] colorWords = {"RED", "GREEN", "BLUE"};
color[] actualColors = {color(255,0,0), color(0,255,0), color(0,0,255)};
int wordIndex;
int displayedColorIndex;
int timeLimit = 5000;
int lastTime;
boolean waitingForInput = false;
int score = 0;
void setup() {
size(600, 400);
textAlign(CENTER, CENTER);
textSize(64);
newRound();
}
void draw() {
background(255);
// Zobraz slovo s barvou
fill(actualColors[displayedColorIndex]);
text(colorWords[wordIndex], width/2, height/2);
// Zobraz skóre
fill(0);
textSize(32);
textAlign(LEFT, TOP);
text("Score: " + score, 10, 10);
// Obnovit zarovnání pro další text
textAlign(CENTER, CENTER);
textSize(64);
int timeLeft = timeLimit - (millis() - lastTime);
if (waitingForInput && timeLeft <= 0) {
println("Timeout!");
newRound();
}
}
void keyPressed() {
if (!waitingForInput) return;
int pressedIndex = -1;
if (key == 'r') pressedIndex = 0;
if (key == 'g') pressedIndex = 1;
if (key == 'b') pressedIndex = 2;
if (pressedIndex == displayedColorIndex) {
println("Correct!");
score++;
timeLimit = max(1000, timeLimit - 200);
} else {
println("Wrong!");
score = 0;
timeLimit = 5000;
}
newRound();
}
void newRound() {
wordIndex = int(random(3));
do {
displayedColorIndex = int(random(3));
} while (displayedColorIndex == wordIndex);
lastTime = millis();
waitingForInput = true;
}
Final result
When both parts were finished, it was time to befriend Arduino with Processing. The main role play first three lines of the script in Processing. The Arduino IDE code I did not change - but here was essential for some reason to close "Serial Monitor" (otherwise the Processing code did not work). The idea behind this code: it writes you name of some colour and it is in different colour. Then you have to push some button - the Arduino then sends the letter (each button is somehow labeled) and Processing analyses if you messed up or not. In the end you also get analysis of how fast was your reaction.
import processing.serial.*;
Serial myPort; // Create object from Serial class
String val; // Data received from the serial port
ArrayList reactionTimes = new ArrayList();
int roundStartTime;
boolean showFinalGraph = false;
String[] colorWords = {"červená", "zelená", "modrá", "žlutá", "bílá"};
color[] actualColors = {
color(255, 0, 0), // červená
color(0, 255, 0), // zelená
color(0, 0, 255), // modrá
color(255, 255, 0), // žlutá
color(255, 255, 255) // bílá
};
int wordIndex;
int displayedColorIndex;
int timeLimit = 5000;
int lastTime;
boolean waitingForInput = false;
boolean gameStarted = false;
int score = 0;
void setup() {
size(600, 400);
textAlign(CENTER, CENTER);
textSize(64);
// Initialize serial
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
myPort.clear();
}
void draw() {
background(0);
if (showFinalGraph) {
drawFinalGraph();
return;
}
if (!gameStarted) {
fill(255);
textSize(36);
text("Jsi připraven/a se otestovat?", width/2, height/2 - 40);
textSize(24);
text("Stiskni ENTER pro start", width/2, height/2 + 20);
return;
}
fill(actualColors[displayedColorIndex]);
textSize(64);
text(colorWords[wordIndex], width/2, height/2);
fill(255);
textSize(32);
textAlign(LEFT, TOP);
text("Skóre: " + score, 10, 10);
textAlign(CENTER, CENTER);
textSize(64);
}
void drawGraph() {
stroke(255);
noFill();
beginShape();
for (int i = 0; i < reactionTimes.size(); i++) {
float x = map(i, 0, max(10, reactionTimes.size()), 0, width);
float y = map(reactionTimes.get(i), 0, timeLimit, height, height - 100); // invert Y
vertex(x, y);
}
endShape();
}
void serialEvent(Serial myPort) {
String inString = myPort.readStringUntil('\n');
if (inString != null) {
inString = trim(inString);
if (inString.length() > 0) {
// Simulate keyPressed with character from Arduino
println("From Arduino: " + inString);
key = inString.charAt(0);
keyPressed(); // manually trigger keyPressed()
}
}
}
void keyPressed() {
if (!gameStarted && key == ENTER) {
gameStarted = true;
score = 0;
reactionTimes.clear();
newRound();
return;
}
if (!waitingForInput) return;
int pressedIndex = -1;
if (key == 'r') pressedIndex = 0;
if (key == 'g') pressedIndex = 1;
if (key == 'b') pressedIndex = 2;
if (key == 'y') pressedIndex = 3;
if (key == 'w') pressedIndex = 4;
if (pressedIndex == displayedColorIndex) {
println("Correct!");
score++;
int reactionTime = millis() - roundStartTime;
reactionTimes.add(reactionTime);
println("Reaction Time: " + reactionTime + " ms");
newRound();
} else {
println("Wrong! Game Over.");
gameStarted = false; // stops the game
showFinalGraph = true; // shows the graph
}
}
void newRound() {
wordIndex = int(random(5));
do {
displayedColorIndex = int(random(5));
} while (displayedColorIndex == wordIndex);
roundStartTime = millis();
waitingForInput = true;
}
void drawFinalGraph() {
background(0);
stroke(255);
fill(255);
textSize(20);
textAlign(CENTER, CENTER);
text("Reakční časy", width/2, 30);
// Axes
stroke(150);
line(60, height - 60, width - 40, height - 60); // X-axis
line(60, height - 60, 60, 60); // Y-axis
// Y-axis labels (reaction time in ms)
textSize(12);
int[] yTicks = {0, 500, 1000, 1500, 2000, 2500, 3000};
for (int i = 0; i < yTicks.length; i++) {
float y = map(yTicks[i], 0, 3000, height - 60, 60);
fill(200);
noStroke();
text(yTicks[i] + " ms", 35, y);
stroke(50);
line(60, y, width - 40, y); // optional horizontal grid lines
}
// X-axis labels (round numbers)
int maxX = reactionTimes.size();
for (int i = 0; i < maxX; i++) {
float x = map(i, 0, max(1, maxX - 1), 60, width - 40);
fill(200);
noStroke();
text(i + 1, x, height - 40);
}
// Axis units text
fill(255);
text("Číslo odpovědi", width / 2, height - 20);
pushMatrix();
translate(20, height / 2);
rotate(-HALF_PI);
text("Reakční čas (ms)", 0, 0);
popMatrix();
// Draw reaction time graph
noFill();
stroke(0, 255, 0);
beginShape();
for (int i = 0; i < reactionTimes.size(); i++) {
float x = map(i, 0, max(1, maxX - 1), 60, width - 40);
float y = map(reactionTimes.get(i), 0, 3000, height - 60, 60);
vertex(x, y);
}
endShape();
// Draw points
for (int i = 0; i < reactionTimes.size(); i++) {
float x = map(i, 0, max(1, maxX - 1), 60, width - 40);
float y = map(reactionTimes.get(i), 0, 3000, height - 60, 60);
fill(255, 0, 0);
noStroke();
ellipse(x, y, 6, 6);
}
}