// using https://www.arduino.cc/reference/en/libraries/hd44780 for the LCD
#include <Wire.h>
#include <hd44780.h> // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
//using https://github.com/Dlloydev/Toggle for the button
#include <Toggle.h>
hd44780_I2Cexp lcd;
const int LCD_COLS = 16;
const int LCD_ROWS = 2;
const byte buttonPin = 2;
Toggle button;
unsigned long lastTick;
const unsigned long tickPeriod = 100; // ms
enum {PAUSED, RUNNING, ASKING} state = PAUSED;
class Led {
const char ** questions;
const size_t questionCount;
const byte ledPin;
public:
Led (const byte pin, const char ** q, const size_t n) :
ledPin(pin), questions(q), questionCount(n) {}
void begin() {
pinMode(ledPin, OUTPUT);
}
void flip() {
digitalWrite(ledPin, digitalRead(ledPin) == LOW ? HIGH : LOW);
}
void on() {
digitalWrite(ledPin, HIGH);
}
void off() {
digitalWrite(ledPin, LOW);
}
const char* getRandomQuestion() {
if (questions == nullptr || questionCount == 0) return nullptr;
return questions[random(questionCount)];
}
};
// the questions
const char * QLed0[] = {"led0 Q0", "led0 Q1", "led0 Q2", "led0 Q3", "led0 Q4"};
const char * QLed1[] = {"led1 Q0", "led1 Q1", "led1 Q2", "led1 Q3"};
const char * QLed2[] = {"led2 Q0", "led2 Q1", "led2 Q2"};
const char * QLed3[] = {"led3 Q0", "led3 Q1"};
const char * QLed4[] = {"led4 Q0", "led4 Q1", "led4 Q2", "led4 Q3", "led4 Q4"};
Led leds[] = {
{ 9, QLed0, sizeof QLed0 / sizeof * QLed0 },
{10, QLed1, sizeof QLed1 / sizeof * QLed1 },
{11, QLed2, sizeof QLed2 / sizeof * QLed2},
{12, QLed3, sizeof QLed3 / sizeof * QLed3 },
{13, QLed4, sizeof QLed4 / sizeof * QLed4 },
};
const size_t ledsCount = sizeof leds / sizeof * leds;
size_t currentLed;
void setup() {
button.begin(buttonPin);
for (auto&l : leds) l.begin();
int result = lcd.begin(LCD_COLS, LCD_ROWS);
if (result) {
Serial.print("LCD initialization failed: ");
Serial.println(result);
hd44780::fatalError(result);
}
randomSeed(analogRead(A0)); // randomize
lcd.clear();
lcd.print("PRESS TO START");
}
void loop() {
switch (state) {
case PAUSED: // a click on the button starts the led danse
button.poll();
if (button.onPress()) {
lcd.clear();
lcd.print("Game on!");
lastTick = millis();
// turn off all leds
for (auto&l : leds) l.off();
// turn on first led
currentLed = 0;
leds[currentLed].on();
state = RUNNING;
}
break;
case RUNNING:
button.poll();
if (button.onPress()) {
// we stop the led danse and ask a question from the current Led
lcd.clear();
lcd.print(leds[currentLed].getRandomQuestion());
state = ASKING;
} else {
// check if it's time to go to next led, do that
if (millis() - lastTick >= tickPeriod) {
leds[currentLed++].off();
if (currentLed >= ledsCount) currentLed = 0;
leds[currentLed].on();
lastTick = millis();
}
}
break;
case ASKING:
// a click on the button starts the led danse again
button.poll();
if (button.onPress()) {
// turn off all leds (this is not necessary unless you messed up the leds in other part of the code, whilst asking the question)
for (auto&l : leds) l.off();
// turn on the current led
leds[currentLed].on();
lcd.clear();
lcd.print("Try again!");
state = RUNNING;
}
break;
}
}