#include <vector>
#include <WiFi.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <LiquidCrystal_I2C.h>
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define WIFI_CHANNEL 6
#define pinA 4
#define pinB 16
#define pinC 17
#define pinD 18
#define leftArrowPin 32
#define enterPin 33
#define rightArrowPin 25
#define rgbRedPin 0
#define rgbGreenPin 2
#define rgbBluePin 15
#define homePin 26
#define lcdWidth 20
#define lcdHeight 4
#define lcdAddress 0x27
#define numberOfButtons 4
#define numberOfHomeButtons 3
#define transitionMsDelay 500
#define reviewInterval 3000
#define mainLoopMsDelay 50
String title = "StudyBot";
int pinArray[numberOfButtons] = {pinA, pinB, pinC, pinD};
int homeWindowPins[numberOfHomeButtons] = {leftArrowPin, enterPin, rightArrowPin};
const int JSON_SIZE = 5000;
const String API_URL = "https://studybotapi.pythonanywhere.com/getUploadedQuestion";
const String QUESTION_LOG_URL = "https://studybotapi.pythonanywhere.com/uploadQuestionLog";
byte leftArrow[] = { B00001, B00010, B00100, B01000, B00100, B00010, B00001, B00000 };
byte rightArrow[] = { B10000, B01000, B00100, B00010, B00100, B01000, B10000, B00000 };
byte logo1x9[] = { B10010, B10010, B10000, B10000, B10000, B00000, B00000, B00000 };
byte logo0x6[] = { B00000, B00000, B00001, B00110, B11000, B00111, B00000, B00001 };
byte logo0x7[] = { B00000, B00111, B11000, B00000, B00110, B10000, B11111, B00000 };
byte logo0x8[] = { B11110, B00001, B00000, B01000, B00001, B00001, B11111, B00000 };
byte logo0x9[] = { B00000, B10000, B01100, B00011, B11100, B00100, B00100, B10010 };
byte logo1x6[] = { B00001, B00001, B00001, B00001, B00001, B00000, B00000, B00000 };
byte logo1x7[] = { B00110, B00100, B00000, B00100, B00011, B10000, B01111, B00000 };
byte logo1x8[] = { B01100, B01000, B00000, B00100, B11000, B00001, B11110, B00000 };
String choiceLetters[] = {"A", "B", "C", "D"};
class APIManager{
private:
String apiUrl;
int jsonSize;
String callAPI() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(this->apiUrl);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
String value = http.getString();
http.end();
return value;
}
else{
Serial.print("Api failed");
}
http.end();
} else {
Serial.println("WiFi not connected");
}
}
public:
APIManager(String apiUrl, int jsonSize){
this->apiUrl = apiUrl;
this->jsonSize = jsonSize;
}
JsonDocument getJson(){
DynamicJsonDocument jsonDoc(this->jsonSize);
String jsonString = this->callAPI();
DeserializationError error = deserializeJson(jsonDoc, jsonString);
if (error) {
Serial.print("JSON deserialization failed: ");
Serial.println(error.c_str());
}
return jsonDoc;
}
};
class DatabaseManager{
private:
String questionLogUrl;
public:
DatabaseManager(String questionLogUrl){
this->questionLogUrl = questionLogUrl;
}
void uploadQuestionLog(String title, String score) {
StaticJsonDocument<200> jsonDoc;
jsonDoc["title"] = title;
jsonDoc["score"] = score;
String jsonString;
serializeJson(jsonDoc, jsonString);
HTTPClient http;
http.begin(this->questionLogUrl);
http.addHeader("Content-Type", "application/json");
int httpResponseCode = http.POST(jsonString);
if (httpResponseCode > 0) {
String response = http.getString();
Serial.println("HTTP Response code: " + String(httpResponseCode));
Serial.println("Response: " + response);
} else {
Serial.println("Error on sending POST: " + String(httpResponseCode));
}
http.end();
}
};
class QuestionManager{
private:
JsonArray questionsArray;
String title;
size_t currentIndex = 0;
JsonObject getCurrentQuestion(){
return questionsArray[currentIndex].as<JsonObject>();
}
public:
QuestionManager(){
}
bool checkIfCorrectAnswer(String chosenAnswer){
String correctAnswer = this->getCurrentQuestion()["answer"].as<String>();
return chosenAnswer == correctAnswer;
}
String getAnswer(){
return this->getCurrentQuestion()["answer"].as<String>();
}
String* getChoices(){
JsonObject choicesJson = this->getCurrentQuestion()["choices"].as<JsonObject>();
static String newChoices[numberOfButtons];
int index = 0;
for (JsonPair keyValuePair : choicesJson) {
String currentChoice = keyValuePair.value().as<String>();
newChoices[index++] = currentChoice;
}
return newChoices;
}
void setQuestions(JsonArray questionsArray){
this->questionsArray = questionsArray;
}
void setTitle(String title){
this->title = title;
}
String getTitle(){
return this->title;
}
String getQuestion(){
return this->getCurrentQuestion()["question"].as<String>();
}
void resetIndex(){
this->currentIndex = 0;
}
int questionSize(){
return (int)this->questionsArray.size();
}
bool inLastQuestion(){
return this->currentIndex == this->questionsArray.size() - 1;
}
void nextQuestion(){
if(this->questionsArray.size() <= 0){
Serial.println("The question is empty!");
}
this->currentIndex++;
if(this->currentIndex >= questionsArray.size()){
this->currentIndex = 0;
}
}
void previousQuestion(){
if(this->questionsArray.size() <= 0){
Serial.println("The question is empty!");
}
if(this->currentIndex == 0){
this->currentIndex = questionsArray.size();
}
this->currentIndex--;
}
};
struct Button {
int pin;
String answer;
};
class ButtonManager{
private:
Button buttonList[numberOfButtons];
public:
ButtonManager(int buttonPins[]){
for(int index = 0; index < numberOfButtons; index++){
int buttonPin = buttonPins[index];
this->buttonList[index].pin = buttonPin;
pinMode(buttonPin, INPUT);
}
}
void setChoices(String* choices){
for(int index = 0; index < numberOfButtons; index++){
this->buttonList[index].answer = choices[index];
}
}
String checkIfPressedButton(){
for(Button& button : this->buttonList){
bool pressedButton = digitalRead(button.pin);
if(pressedButton){
return button.answer;
}
}
return "";
}
};
class LCDManager{
private:
LiquidCrystal_I2C& lcd;
public:
LCDManager(LiquidCrystal_I2C& lcdInput) : lcd(lcdInput) {
}
void show(int column, int line, String character){
this->lcd.setCursor(column, line);
this->lcd.print(character);
}
void clear(){
this->lcd.clear();
}
void addCharacter(int identifier, byte* character){
this->lcd.createChar(identifier, character);
}
void showCharacter(int column, int line, int identifier){
this->lcd.setCursor(column, line);
this->lcd.write(identifier);
}
};
class Window{
public:
virtual void run() = 0;
virtual String title() const = 0;
virtual void setup() = 0;
};
class WindowManager{
private:
std::vector<Window*> windowVector;
int currentWindowIndex = 0;
public:
void addWindow(Window* window){
this->windowVector.push_back(window);
}
void runCurrentWindow(){
if (!windowVector.empty()) {
this->windowVector[this->currentWindowIndex]->run();
}
}
void setupCurrentWindow(){
if (!windowVector.empty()) {
this->windowVector[this->currentWindowIndex]->setup();
}
}
String getCurrentWindowTitle(){
if (!windowVector.empty()) {
return this->windowVector[this->currentWindowIndex]->title();
}
}
void previousWindow(){
if(this->currentWindowIndex == 0){
this->currentWindowIndex = this->windowVector.size();
}
this->currentWindowIndex--;
}
void nextWindow(){
this->currentWindowIndex++;
if(this->currentWindowIndex == this->windowVector.size()){
this->currentWindowIndex = 0;
}
}
};
class HomeManager{
private:
bool runState = false;
WindowManager& windowManager;
LCDManager& lcdManager;
void printLeftArrow(){
lcdManager.showCharacter(0, 1, 0);
}
void printRightArrow(){
lcdManager.showCharacter(lcdWidth - 1, 1, 1);
}
void printArrows(){
printLeftArrow();
printRightArrow();
}
void updateView(){
lcdManager.clear();
printArrows();
printCurrentMode();
}
void printCurrentMode(){
String title = windowManager.getCurrentWindowTitle();
lcdManager.show(((lcdWidth / 2) - (title.length() / 2)), 1, title);
}
void enter(){
runState = false;
lcdManager.clear();
windowManager.setupCurrentWindow();
}
public:
HomeManager(LCDManager& lcdManagerInput, WindowManager& windowManager, int* homeWindowPins)
: lcdManager(lcdManagerInput), windowManager(windowManager) {
for(int index = 0; index < numberOfHomeButtons; index++){
pinMode(homeWindowPins[index], INPUT);
}
}
void run(){
bool pressedLeft = digitalRead(leftArrowPin);
bool pressedRight = digitalRead(rightArrowPin);
bool pressedEnter = digitalRead(enterPin);
if(pressedLeft){
Serial.println("Pressed Left");
this->windowManager.previousWindow();
this->updateView();
delay(100);
}
else if(pressedRight){
Serial.println("Pressed Right");
this->windowManager.nextWindow();
this->updateView();
delay(100);
}
else if(pressedEnter){
Serial.println("Pressed Enter");
this->enter();
delay(100);
}
}
void startHome(){
this->runState = true;
this->lcdManager.clear();
delay(transitionMsDelay);
this->updateView();
}
bool running(){
return this->runState;
}
};
class LCDQuestionManager{
private:
LCDManager& lcdManager;
void printQuestionFirstLine(String questionLine){
lcdManager.show(0, 0, questionLine);
}
void printQuestionSecondLine(String questionLine){
lcdManager.show(0, 1, questionLine);
}
void printLineCentered(int column, String line){
lcdManager.show((lcdWidth / 2) - (line.length() / 2), column, line);
}
void printChoiceA(String choice){
lcdManager.show(0, 2, choice);
}
void printChoiceB(String choice){
lcdManager.show(0, 3, choice);
}
void printChoiceC(String choice){
int edgePadding = 10 - choice.length(); // so that the choice is in the edge
lcdManager.show(10 + edgePadding, 2, choice);
}
void printChoiceD(String choice){
int edgePadding = 10 - choice.length(); // so that the choice is in the edge
lcdManager.show(10 + edgePadding, 3, choice);
}
std::vector<String> splitSentence(const String& sentence, size_t maxLength) {
std::vector<String> chunks;
String currentChunk = "";
String word;
int lastSpace = 0;
for (int index = 0; index <= sentence.length(); index++) {
char currentCharacter = sentence.charAt(index);
if (currentCharacter == ' ' || currentCharacter == '\0') {
word = sentence.substring(lastSpace, index);
lastSpace = index + 1;
if (currentChunk.length() + word.length() + 1 > maxLength) {
if (currentChunk.length() > 0) {
chunks.push_back(currentChunk);
}
currentChunk = word;
} else {
if (currentChunk.length() > 0) {
currentChunk += " ";
}
currentChunk += word;
}
}
}
if (currentChunk.length() > 0) {
chunks.push_back(currentChunk);
}
return chunks;
}
public:
LCDQuestionManager(LCDManager& lcdManagerInput) : lcdManager(lcdManagerInput) {
}
void show(String question, String* choices){
showQuestion(question);
this->printChoiceA(choices[0]);
this->printChoiceB(choices[1]);
this->printChoiceC(choices[2]);
this->printChoiceD(choices[3]);
}
void showQuestion(String question){
this->lcdManager.clear();
std::vector<String> questionVector = this->splitSentence(question, lcdWidth);
this->printQuestionFirstLine(questionVector[0]);
this->printQuestionSecondLine(questionVector[1]);
}
void showQuestionCentered(String question, int column = 1, bool clearLcd = true){
if(clearLcd){
this->lcdManager.clear();
}
std::vector<String> questionVector = this->splitSentence(question, lcdWidth);
this->printLineCentered(column, questionVector[0]);
this->printLineCentered(column + 1, questionVector[1]);
}
void showAnswer(String answer, int column = 1, bool clearLcd = true){
if(clearLcd){
this->lcdManager.clear();
}
this->lcdManager.show((lcdWidth / 2) - (answer.length() / 2), column, answer);
}
void clear(){
this->lcdManager.clear();
}
void showIncorrect(){
String label = "Incorrect!";
this->lcdManager.show((lcdWidth / 2) - (label.length() / 2), 1, label);
delay(transitionMsDelay * 2);
this->lcdManager.clear();
}
void showCorrect(){
String label = "Correct!";
this->lcdManager.show((lcdWidth / 2) - (label.length() / 2), 1, label);
delay(transitionMsDelay * 2);
this->lcdManager.clear();
}
void showEndScreen(String title, String score){
static String back = "Back";
this->lcdManager.show(0, 0, title);
this->lcdManager.show(0, 1, score);
this->lcdManager.showCharacter(0, 3, 0);
this->lcdManager.show(2, 3, back);
}
void showUploadingQuestionLog(){
this->lcdManager.clear();
String labelOne = "Uploading to";
String labelTwo = "Database...";
this->lcdManager.show((lcdWidth / 2) - (labelOne.length() / 2), 1, labelOne);
this->lcdManager.show((lcdWidth / 2) - (labelTwo.length() / 2), 2, labelTwo);
}
};
class ScoreManager{
private:
int totalQuestions = 0;
int correctAnswers = 0;
public:
void reset(){
this->totalQuestions = 0;
this->correctAnswers = 0;
}
void setQuestions(int questionAmount){
this->totalQuestions = questionAmount;
}
String getScore(){
String scoreString = (String)this->correctAnswers + "/" + (String)this->totalQuestions;
return scoreString;
}
void addCorrect(){
this->correctAnswers++;
}
};
class QuizModeWindow : public Window{
private:
LCDQuestionManager& lcdQuestionManager;
QuestionManager& questionManager;
ButtonManager& buttonManager;
HomeManager& homeManager;
ScoreManager& scoreManager;
DatabaseManager& databaseManager;
bool finished = false;
void nextQuestion(){
questionManager.nextQuestion();
delay(100);
}
void returnHome(){
questionManager.resetIndex();
finished = false;
colorRgbLed(false, false, false);
homeManager.startHome();
scoreManager.reset();
}
void showCurrentQuestion(){
String* choices = questionManager.getChoices();
String question = questionManager.getQuestion();
buttonManager.setChoices(choices);
lcdQuestionManager.show(question, choices);
}
void colorRgbLed(bool red, bool green, bool blue){
digitalWrite(rgbRedPin, red);
digitalWrite(rgbGreenPin, green);
digitalWrite(rgbBluePin, blue);
}
void showEndScreen(){
finished = true;
String title = questionManager.getTitle();
String score = scoreManager.getScore();
lcdQuestionManager.showUploadingQuestionLog();
databaseManager.uploadQuestionLog(title, score);
lcdQuestionManager.clear();
delay(transitionMsDelay);
lcdQuestionManager.showEndScreen(title, score);
}
public:
QuizModeWindow(LCDQuestionManager& lcdQuestionManager, QuestionManager& questionManager, ButtonManager& buttonManager, HomeManager& homeManager, ScoreManager& scoreManager, DatabaseManager& databaseManager)
:lcdQuestionManager(lcdQuestionManager), questionManager(questionManager), buttonManager(buttonManager), homeManager(homeManager), scoreManager(scoreManager), databaseManager(databaseManager){
}
void run() override{
if(this->finished){
bool pressedLeft = digitalRead(leftArrowPin);
bool pressedHome = digitalRead(homePin);
if(pressedLeft | pressedHome){
this->returnHome();
}
return;
}
String chosenAnswer = this->buttonManager.checkIfPressedButton();
bool pressedHome = digitalRead(homePin);
if(pressedHome){
this->returnHome();
}
if(chosenAnswer == ""){
return;
}
if(questionManager.checkIfCorrectAnswer(chosenAnswer)){
this->lcdQuestionManager.clear();
this->scoreManager.addCorrect();
delay(transitionMsDelay);
this->colorRgbLed(false, true, false);
this->lcdQuestionManager.showCorrect();
this->colorRgbLed(false, false, false);
delay(transitionMsDelay);
}
else{
this->lcdQuestionManager.clear();
delay(transitionMsDelay);
this->colorRgbLed(true, false, false);
this->lcdQuestionManager.showIncorrect();
this->colorRgbLed(false, false, false);
delay(transitionMsDelay);
}
if(this->questionManager.inLastQuestion()){
this->showEndScreen();
return;
}
this->nextQuestion();
this->showCurrentQuestion();
}
String title() const override{
return "Quiz Mode";
}
void setup() override{
this->lcdQuestionManager.clear();
delay(transitionMsDelay);
this->scoreManager.setQuestions(this->questionManager.questionSize());
this->showCurrentQuestion();
}
};
class StudyModeWindow : public Window{
private:
LCDQuestionManager& lcdQuestionManager;
QuestionManager& questionManager;
HomeManager& homeManager;
bool showingAnswer = false;
void showCurrentQuestion(){
String question = questionManager.getQuestion();
lcdQuestionManager.showQuestionCentered(question);
}
void showCurrentAnswer(){
String answer = questionManager.getAnswer();
lcdQuestionManager.showAnswer(answer);
}
void returnHome(){
this->questionManager.resetIndex();
homeManager.startHome();
}
public:
StudyModeWindow(LCDQuestionManager& lcdQuestionManager, QuestionManager& questionManager, HomeManager& homeManager)
:lcdQuestionManager(lcdQuestionManager), questionManager(questionManager), homeManager(homeManager){
}
void run() override{
bool pressedLeft = digitalRead(leftArrowPin);
bool pressedRight = digitalRead(rightArrowPin);
bool pressedEnter = digitalRead(enterPin);
bool pressedHome = digitalRead(homePin);
if(pressedHome){
this->showingAnswer = false;
this->returnHome();
}
if(pressedLeft){
Serial.println("Previous Question");
this->questionManager.previousQuestion();
this->showingAnswer = false;
this->showCurrentQuestion();
delay(100);
}
else if(pressedRight){
Serial.println("Next Question");
this->questionManager.nextQuestion();
this->showingAnswer = false;
this->showCurrentQuestion();
delay(100);
}
else if(pressedEnter){
if(this->showingAnswer){
this->showingAnswer = false;
this->showCurrentQuestion();
}
else{
this->showingAnswer = true;
this->showCurrentAnswer();
}
delay(100);
}
}
String title() const override{
return "Study Mode";
}
void setup() override{
this->lcdQuestionManager.clear();
delay(transitionMsDelay);
this->showCurrentQuestion();
}
};
class ReviewModeWindow : public Window{
private:
LCDQuestionManager& lcdQuestionManager;
QuestionManager& questionManager;
HomeManager& homeManager;
unsigned long previousMillis = 0;
long msInterval;
bool showingAnswer = false;
void showCurrentQuestion(bool clearLcd = false){
String question = questionManager.getQuestion();
lcdQuestionManager.showQuestionCentered(question, 0, clearLcd);
}
void showCurrentAnswer(){
String answer = questionManager.getAnswer();
lcdQuestionManager.showAnswer(answer, 3, false);
}
void returnHome(){
this->questionManager.resetIndex();
homeManager.startHome();
}
void checkIfFinishedInterval(){
unsigned long currentMillis = millis();
if (!(currentMillis - previousMillis >= msInterval)) {
return;
}
previousMillis = currentMillis;
if(!showingAnswer){
showCurrentAnswer();
showingAnswer = true;
}
else{
questionManager.nextQuestion();
showingAnswer = false;
showCurrentQuestion(true);
}
}
void reset(){
showingAnswer = false;
showCurrentQuestion(true);
previousMillis = millis();
}
public:
ReviewModeWindow(LCDQuestionManager& lcdQuestionManager, QuestionManager& questionManager, HomeManager& homeManager, long msInterval)
:lcdQuestionManager(lcdQuestionManager), questionManager(questionManager), homeManager(homeManager){
this->msInterval = msInterval;
}
void run() override{
bool pressedLeft = digitalRead(leftArrowPin);
bool pressedRight = digitalRead(rightArrowPin);
bool pressedHome = digitalRead(homePin);
if(pressedHome){
this->showingAnswer = false;
this->returnHome();
}
else if(pressedLeft){
Serial.println("Previous Question");
this->questionManager.previousQuestion();
this->reset();
delay(100);
}
else if(pressedRight){
Serial.println("Next Question");
this->questionManager.nextQuestion();
this->reset();
delay(100);
}
this->checkIfFinishedInterval();
}
String title() const override{
return "Review Mode";
}
void setup() override{
this->previousMillis = millis();
this->lcdQuestionManager.clear();
delay(transitionMsDelay);
this->showCurrentQuestion();
}
};
class MultiplayerModeWindow : public Window{
private:
LCDQuestionManager& lcdQuestionManager;
QuestionManager& questionManager;
public:
MultiplayerModeWindow(LCDQuestionManager& lcdQuestionManager, QuestionManager& questionManager)
:lcdQuestionManager(lcdQuestionManager), questionManager(questionManager){
}
void run() override{
}
String title() const override{
return "Multiplayer Mode";
}
void setup() override{}
};
void printChoices(String *choices){
for(int index = 0; index < numberOfButtons; index++){
Serial.println(choiceLetters[index] + ": " + choices[index]);
}
}
void printQuestion(String question){
Serial.println("\n" + question + "\n");
}
void startWifi(String wifiSSID, String wifiPassword, int wifiChannel){
WiFi.begin(wifiSSID, wifiPassword, wifiChannel);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println();
Serial.println("Connected to Wi-Fi");
}
void setupLcd(LiquidCrystal_I2C& lcd){
lcd.init();
lcd.backlight();
}
void setupPins(){
pinMode(rgbRedPin, OUTPUT);
pinMode(rgbGreenPin, OUTPUT);
pinMode(rgbBluePin, OUTPUT);
pinMode(homePin, INPUT);
}
void addIntroCharacters(LCDManager lcdManager) {
lcdManager.addCharacter(1, logo0x6);
lcdManager.addCharacter(2, logo0x7);
lcdManager.addCharacter(3, logo0x8);
lcdManager.addCharacter(4, logo0x9);
lcdManager.addCharacter(5, logo1x6);
lcdManager.addCharacter(6, logo1x7);
lcdManager.addCharacter(7, logo1x8);
lcdManager.addCharacter(8, logo1x9);
}
void addHomeLogo(LCDManager lcdManager) {
lcdManager.addCharacter(0, leftArrow);
lcdManager.addCharacter(1, rightArrow);
}
void showIntro(LCDManager lcdManager){
lcdManager.showCharacter(4, 1, 1);
lcdManager.showCharacter(5, 1, 2);
lcdManager.showCharacter(6, 1, 3);
lcdManager.showCharacter(7, 1, 4);
lcdManager.showCharacter(4, 2, 5);
lcdManager.showCharacter(5, 2, 6);
lcdManager.showCharacter(6, 2, 7);
lcdManager.showCharacter(7, 2, 8);
// lcdManager.showCharacter(8, 0, 1);
// lcdManager.showCharacter(9, 0, 2);
// lcdManager.showCharacter(10, 0, 3);
// lcdManager.showCharacter(11, 0, 4);
// lcdManager.showCharacter(8, 1, 5);
// lcdManager.showCharacter(9, 1, 6);
// lcdManager.showCharacter(10, 1, 7);
// lcdManager.showCharacter(11, 1, 8);
lcdManager.show(9, 1, title);
// lcdManager.show(((lcdWidth / 2) - (title.length() / 2)), 2, title);
}
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(lcdAddress, lcdWidth, lcdHeight);
LCDManager lcdManager(lcd);
LCDQuestionManager lcdQuestionManager(lcdManager);
DatabaseManager databaseManager(QUESTION_LOG_URL);
ButtonManager buttonManager(pinArray);
WindowManager windowManager = WindowManager();
QuestionManager questionManager = QuestionManager();
ScoreManager scoreManager = ScoreManager();
HomeManager homeManager(lcdManager, windowManager, homeWindowPins);
QuizModeWindow quizModeWindow(lcdQuestionManager, questionManager, buttonManager, homeManager, scoreManager, databaseManager);
StudyModeWindow studyModeWindow(lcdQuestionManager, questionManager, homeManager);
ReviewModeWindow reviewModeWindow(lcdQuestionManager, questionManager, homeManager, reviewInterval);
APIManager apiManager(API_URL, JSON_SIZE);
JsonDocument mainQuestionJson;
void setupWindow(){
windowManager.addWindow(&quizModeWindow);
windowManager.addWindow(&studyModeWindow);
windowManager.addWindow(&reviewModeWindow);
}
void setup() {
Serial.begin(115200);
setupLcd(lcd);
setupPins();
setupWindow();
addIntroCharacters(lcdManager);
delay(transitionMsDelay);
showIntro(lcdManager);
startWifi(WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
mainQuestionJson = apiManager.getJson();
questionManager.setTitle(mainQuestionJson["title"].as<String>());
questionManager.setQuestions(mainQuestionJson["questions"].as<JsonArray>());
lcdManager.clear();
addHomeLogo(lcdManager);
delay(transitionMsDelay);
homeManager.startHome();
}
void loop() {
if(homeManager.running()){
homeManager.run();
}
else{
windowManager.runCurrentWindow();
}
delay(mainLoopMsDelay);
}