05 May 2026

Jasmine Darman Final

 This was a project with a large end goal and scope that wasn't able to be achieved in time. However, the progress made was still significant. This was the large schematic and is what I based the small design on: 


The current prototype can run a base LED script, RFID reader script, and locate button pushes with an array. 

The base neo LED script is from neo pixels known as Simple. 

The other scripting: 

//First check what the current saved UID is- depending on what it is will affect how things work
//next check where a button press is located
// if it is, check if it's being held down or if it's just a quick tap
// held down means it's saving the object location to a new location
// quick tap means it's checking for where this object can go in relation to others- light things up

//to check with the array:
//send power high through one of the yellow parts
//check if this power is given into one of the blue things



#include <MFRC522.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN        0 // On Trinket or Gemma, suggest changing this to 1

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 16 // Popular NeoPixel ring size
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 500 // Time (in milliseconds) to pause between pixels

//Info for Character tags
//A = Wall, red can not go

#define RST_PIN 9  
#define SS_PIN 6

MFRC522 reader(SS_PIN, RST_PIN);


//parts of the array yellow:

int yellowArray[] = {1, 2, 3};
int yellowArrayLength = 3;


//parts of the array blue:
int blueArray[] = {A0, A1, A2};
int blueArrayLength = 3;

//array that stores the location ...? bqasically which one went off for electricity and what was the result back
int hexArray[9];
int hexArrayLength = 9;

//Saved RFID type here
char tokenType;
int tokenLocation;




void setup() {
    // set all yellow pins to output - sending electricity out  
  for(int i = 0; i < yellowArrayLength; i ++ ){
    pinMode(yellowArray[i], OUTPUT);
  }
  //set all the blue pins to input- taking electricity in to read
    for(int i = 0; i < blueArrayLength; i ++ ){
    pinMode(blueArray[i], INPUT);
  }
  Serial.begin(9600);  // Initialize serial communications with the PC


//RFID Reader
  while (!Serial)
    ;                  
  SPI.begin();        
  reader.PCD_Init();  


//pixels
  #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
    clock_prescale_set(clock_div_1);
  #endif
    // END of Trinket-specific code.

    pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)

    Serial.println("Ready"); // test - confirm serial is working
}

void loop() {
 // char pieceLetter = ReadCard(); //always check if something is being saved in the rfid character - just using characters for this - this will get the ID type
  pixels.clear();
   pixels.show();
   
   
 CheckButtons();
HandleRFID();
   
 //LightLeds(tokenType, tokenLocation);
 //pixels.show();





 

}

void HandleRFID(){
  if (!reader.PICC_IsNewCardPresent()) {
    return;
  }

  // Select one of the cards
  if (!reader.PICC_ReadCardSerial()) {
    return;
  }



  if (reader.PICC_GetType(reader.uid.sak) == MFRC522::PICC_TYPE_MIFARE_UL) {
 
    tokenType = ReadCard();
    Serial.println("read card");
  } else {
    Serial.println(F("Unsupported card type for NTAG215 write."));
  }

  // End communication with this card before waiting for the next one.
  reader.PICC_HaltA();
  reader.PCD_StopCrypto1();
  delay(300); // delay should not be here if handling buttons - maybe aync it
 
}

void LightLeds(char pieceType, int pieceLocation){

//location not 0
if(pieceLocation != 0){
//character
  if(pieceType == 'A'){
   
    //peiceLocation is a wall   for(int i=0; i<NUMPIXELS; i++) {
    pixels.setPixelColor(pieceLocation -1 , pixels.Color(255, 0, 0));

    pixels.show();  
    delay(DELAYVAL);

   

  }


 
}
 

}
char ReadCard(){
  const byte page = 4; // MIFARE_Read returns 4 pages (16 bytes) starting here
  byte data[18];
  byte size = sizeof(data);
  MFRC522::StatusCode status = reader.MIFARE_Read(page, data, &size);

  if (status == MFRC522::STATUS_OK) {
    Serial.print(F("Read character: "));
    Serial.println((char)data[0]);
    return (char)data[0];
  }
  else{
    return 'Z';
  }
}




void CheckButtons(){
 
  for( int y = 0; y < yellowArrayLength; y++){
    //turn it all off
    digitalWrite(yellowArray[y], LOW);
  }
  for( int y = 0; y < yellowArrayLength; y++){
    //loop through yellow
    digitalWrite(yellowArray[y], HIGH);
    for (int b = 0; b <blueArrayLength; b++){
      //loop through blue
      if(analogRead(blueArray[b])> 800){
      //  Serial.println(analogRead(blueArray[b]));
        Serial.println(y);
        Serial.print("\t");
        int val = (y*2)+b+1;
        tokenLocation = val;
        if(val != 0 ){
          Serial.println(val);
          return;
        }
       
        // Serial.println(val);
      }
     

    }
     
  }

}
Here are images of the work:




Here is an image of a multiplexer I couldn't get to use:


Here is the video:
https://youtu.be/bsAVMdYXNWQ

TinkerCad Link:
https://www.tinkercad.com/things/k8BHwkSJki4-magnificent-turing?sharecode=sF5BDK-GbTyRxx2Q33Wk7OspcDOm5Wo95juOC4Vkp1c









04 May 2026

Team 3 Final Project

Minecraft Controller 

My controller is designed for the game Minecraft. It is in the shape of a pickaxe for thematic purposes.

The pickaxe is the main tool you will use in Minecraft, as it involves mining. I decided to make the controller

a diamond pickaxe. To input your button presses, you must tap the head of the pickaxe on the table while pressing

a button. This is to signify doing an action in the game, similar to doing a similar action in real life. It also has

a joystick on the back to allow movement. The feedback on the controller is when you strike the table to cause

an action. The pickaxe has an end that naturally draws players to use it like a pickaxe. Tilting the head of the

pickaxe in any direction will cause the player to look around the screen. To achieve this, we used the

accelerometer in the Circuit Playground Express. This is intuitive and adds more action to the controller than

a mouse would. I wanted to make every button and analog fit on the handle of the pickaxe to be easy to reach

everything without adjusting, and while moving. I also planned to put lights on the pickaxe to add more feedback.

I may do this in the future.

 

Photo:




Code:

#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>
#include <Keyboard.h>
#include <Mouse.h>

int i = 0;
//Button Prompts
const int ButtonPin1 = A1;
const int ButtonPin2 = A2;
const int ButtonPin3 = A3;
const int ButtonPin4 = A6;
//Joystick Variables
const int joyX = A5;
const int joyY = A4;
int center = 400;
int deadzoneJoy = 150;
int x = 512;
int y = 512;
//Mouse Variables
float deadzone = 3;
float sensitivity = 1;
float smoothX = 0;
float smoothY = 0;
float alpha = 0.2;  // smoothing factor

//Sets up button pins, analog, and mouse and keyboard support
void setup() {
  Serial.begin(9600);
  CircuitPlayground.begin();
  pinMode(ButtonPin1, INPUT);
  pinMode(ButtonPin2, INPUT);
  pinMode(ButtonPin3, INPUT);
  pinMode(ButtonPin4, INPUT);
  pinMode(joyX, INPUT);
  pinMode(joyY, INPUT);
  randomSeed(micros());
  Mouse.begin();
  //Keyboard.begin();
  delay(2000);
}

void loop() {
  //Calls Joystick Function
  JoyStickControls();

  //Mouse Control
  float rawX = CircuitPlayground.motionX();
  float rawY = CircuitPlayground.motionY();

  // smoothing
  smoothX = alpha * rawX + (1 - alpha) * smoothX;
  smoothY = alpha * rawY + (1 - alpha) * smoothY;

  int moveX = 0;
  int moveY = 0;

  //if smoothX is more than the deadzone or less than the negative of the deadzone, make moveX = smoothX and adjust sensetivity
  if (abs(smoothX) > deadzone) {
    moveX = (int)(smoothX * sensitivity);
  }

  if (abs(smoothY) > deadzone) {
    moveY = (int)(smoothY * sensitivity);
  }

  //moves the mouse depending on which way the pickaxe is pointed using the accelorometer
  if (moveY > -5) {
    Mouse.move(moveX, -moveY, 0);
  }
  //if the fsr is pushed, then allow the buttons to be used
  if (digitalRead(ButtonPin4)) {
    float i = digitalRead(ButtonPin3);
    Serial.println(i);
    //button 1 holds down the mouse click and lets go when not pressed
    if (digitalRead(ButtonPin1)) {
      Serial.println("button 1 pressed");
      //Keyboard.write('w');
      Mouse.press();
      //delay(50);
    } else if (!digitalRead(ButtonPin1)) {
      Mouse.release();
    }
    //button 2 presses e once
    if (digitalRead(ButtonPin2)) {
      Serial.println("button 2 pressed");
      Keyboard.write('e');
      delay(200);
    }
    //button 3 holds down the mouse right click and lets go when not pressed
    if (digitalRead(ButtonPin3)) {
      Mouse.press(MOUSE_RIGHT);
    } else if (!digitalRead(ButtonPin3)) {
      Mouse.release(MOUSE_RIGHT);
    }
  }
}


//Joystick Logic
void JoyStickControls() {
  y = analogRead(joyX);
  x = analogRead(joyY);

  //joystick Y logic
  if (y < center - deadzoneJoy) {
    Keyboard.press('w');
  } else if (y > center + deadzoneJoy) {
    Keyboard.press('s');
  } else if (y > center - deadzoneJoy && y < center + deadzoneJoy) {
    Keyboard.release('s');
    Keyboard.release('w');
  }

  //joystick x logic
  if (x < center - deadzoneJoy) {
    Keyboard.press('d');
  } else if (x > center + deadzoneJoy) {
    Keyboard.press('a');
  } else if (x > center - deadzoneJoy && x < center + deadzoneJoy) {
    Keyboard.release('a');
    Keyboard.release('d');
  }

  Serial.print("X: ");
  Serial.print(x);

  Serial.print(" | Y: ");
  Serial.println(y);
}

Schematic:





03 May 2026

I Vergara - Final Project

 Productivity Pal

Productivity Pal is a small offline productivity device designed to help users stay focused without relying on apps, websites, or notifications. It includes three main features: a Pomodoro focus timer, an active break timer, and a simple to-do mode based on common tasks and activities.

The goal of the project is to create a distraction free tool for managing productivity. While there are many apps and websites that offer similar features, they often come with distractions of their own, such as animations, notifications, ads, or extra features competing for the user’s attention. Productivity Pal keeps the experience simple and physical, using RFID tags and a small screen to access each mode without needing to connect to the web.


 

 Image Gallery - Final product






 Image Gallery - Initial Sketches and prototypes

 




 


Code

#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <MFRC522.h>

#define TFT_CS    10
#define TFT_DC    8
#define TFT_RST   9

#define RFID_SS   7
#define RFID_RST  2

const int pinA = 3;
const int pinB = 4;
const int pushPin = 5;
const int k0Pin = 6;

int currentMode = 0;  

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
MFRC522 rfid(RFID_SS, RFID_RST);

String lastUID = "";


int lastAState;
bool lastButtonState = HIGH;

// Work mode options
int timerOptions[3] = {5, 10, 20};
int selectedIndex = 0;

bool workTimerStarted = false;
unsigned long workStartTime = 0;
unsigned long workDuration = 0;
int lastWorkSecond = -1;

// Break Mode options
String breakOptions[3] = {"Stretch", "Jumping Jacks", "Pushups"};
int breakSelectedIndex = 0;

bool breakTimerStarted = false;
unsigned long breakStartTime = 0;
const unsigned long breakDuration = 2UL * 60UL * 1000UL;
int lastBreakSecond = -1;

// Todo Mode options
const int TODO_COUNT = 5;
const char* todoOptions[TODO_COUNT] = {"Homework", "Work", "Clean", "Chores", "Groceries"};
bool todoSelected[TODO_COUNT] = {false, false, false, false, false};
bool todoCompleted[TODO_COUNT] = {false, false, false, false, false};
int todoSelectedIndex = 0;
bool todoListStarted = false;

// Home screen
void runHomeMode() {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_GREEN);
  tft.setCursor(20, 20);
  tft.println("Home");

  tft.setTextSize(1);
  tft.setCursor(20, 60);
  tft.println("Tap an RFID tag");
  tft.setCursor(20, 80);
  tft.println("Blue tag -> Work");
  tft.setCursor(20, 95);
  tft.println("Card -> Break");
  tft.setCursor(20, 110);
  tft.println("White tag -> Todo");
}

// Work screen
void drawWorkSelectionScreen() {
  tft.fillScreen(ST77XX_BLACK);

  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(20, 20);
  tft.println("Work Mode");

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(20, 50);
  tft.println("Choose timer:");

  for (int i = 0; i < 3; i++) {
    int y = 80 + i * 25;

    if (i == selectedIndex) {
      tft.fillRect(20, y - 2, 140, 18, ST77XX_YELLOW);
      tft.setTextColor(ST77XX_BLACK);
    } else {
      tft.fillRect(20, y - 2, 140, 18, ST77XX_BLACK);
      tft.setTextColor(ST77XX_WHITE);
    }

    tft.setTextSize(2);
    tft.setCursor(30, y);
    tft.print(timerOptions[i]);
    tft.print(" min");
  }

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_CYAN);
  tft.setCursor(20, 170);
  tft.println("Press knob to start");
}

void enterWorkMode() {
  workTimerStarted = false;
  selectedIndex = 0;
  lastWorkSecond = -1;
  drawWorkSelectionScreen();
}

void drawWorkTimerScreen() {
  tft.fillScreen(ST77XX_BLACK);

  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(20, 20);
  tft.println("Work Mode");

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(20, 50);
  tft.print("Pomodoro ");
  tft.print(timerOptions[selectedIndex]);
  tft.println(" min");
}

void startWorkTimer() {
  workTimerStarted = true;
  workStartTime = millis();
  workDuration = (unsigned long)timerOptions[selectedIndex] * 60UL * 1000UL;
  lastWorkSecond = -1;
  drawWorkTimerScreen();
}

void runWorkMode() {
  if (!workTimerStarted) {
    int currentAState = digitalRead(pinA);

    if (currentAState != lastAState) {
      if (digitalRead(pinB) != currentAState) {
        selectedIndex++;
      } else {
        selectedIndex--;
      }

      if (selectedIndex > 2) selectedIndex = 0;
      if (selectedIndex < 0) selectedIndex = 2;

      drawWorkSelectionScreen();
      delay(5);
    }

    lastAState = currentAState;

    bool currentButtonState = digitalRead(pushPin);

    if (currentButtonState == LOW && lastButtonState == HIGH) {
      startWorkTimer();
      delay(200);
    }

    lastButtonState = currentButtonState;
  } else {
    unsigned long elapsed = millis() - workStartTime;
    if (elapsed > workDuration) {
      elapsed = workDuration;
    }

    int remainingSeconds = (workDuration - elapsed) / 1000;
    int minutes = remainingSeconds / 60;
    int seconds = remainingSeconds % 60;

    if (remainingSeconds != lastWorkSecond) {
      lastWorkSecond = remainingSeconds;

      char timeText[6];
      sprintf(timeText, "%02d:%02d", minutes, seconds);

      tft.fillRect(20, 80, 160, 40, ST77XX_BLACK);
      tft.setTextSize(3);
      tft.setTextColor(ST77XX_RED);
      tft.setCursor(20, 80);
      tft.print(timeText);

      if (remainingSeconds == 0) {
        tft.fillRect(20, 140, 140, 30, ST77XX_BLACK);
        tft.setTextSize(2);
        tft.setTextColor(ST77XX_GREEN);
        tft.setCursor(20, 140);
        tft.println("Done");
      }
    }
  }
}

// Break screen
void drawBreakSelectionScreen() {
  tft.fillScreen(ST77XX_BLACK);

  tft.setTextSize(2);
  tft.setTextColor(ST77XX_CYAN);
  tft.setCursor(20, 20);
  tft.println("Break Mode");

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(20, 50);
  tft.println("Choose exercise:");

  for (int i = 0; i < 3; i++) {
    int y = 80 + i * 25;

    if (i == breakSelectedIndex) {
      tft.fillRect(20, y - 2, 180, 18, ST77XX_CYAN);
      tft.setTextColor(ST77XX_BLACK);
    } else {
      tft.fillRect(20, y - 2, 180, 18, ST77XX_BLACK);
      tft.setTextColor(ST77XX_WHITE);
    }

    tft.setTextSize(1);
    tft.setCursor(30, y);
    tft.print(breakOptions[i]);
  }

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(20, 170);
  tft.println("Press knob to start");
}

void enterBreakMode() {
  breakTimerStarted = false;
  breakSelectedIndex = 0;
  lastBreakSecond = -1;
  drawBreakSelectionScreen();
}

void drawBreakTimerScreen() {
  tft.fillScreen(ST77XX_BLACK);

  tft.setTextSize(2);
  tft.setTextColor(ST77XX_CYAN);
  tft.setCursor(20, 20);
  tft.println("Break Mode");

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(20, 50);
  tft.print("Exercise: ");
  tft.println(breakOptions[breakSelectedIndex]);
}

void startBreakTimer() {
  breakTimerStarted = true;
  breakStartTime = millis();
  lastBreakSecond = -1;
  drawBreakTimerScreen();
}

void runBreakMode() {
  if (!breakTimerStarted) {
    int currentAState = digitalRead(pinA);

    if (currentAState != lastAState) {
      if (digitalRead(pinB) != currentAState) {
        breakSelectedIndex++;
      } else {
        breakSelectedIndex--;
      }

      if (breakSelectedIndex > 2) breakSelectedIndex = 0;
      if (breakSelectedIndex < 0) breakSelectedIndex = 2;

      drawBreakSelectionScreen();
      delay(5);
    }

    lastAState = currentAState;

    bool currentButtonState = digitalRead(pushPin);

    if (currentButtonState == LOW && lastButtonState == HIGH) {
      startBreakTimer();
      delay(200);
    }

    lastButtonState = currentButtonState;
  } else {
    unsigned long elapsed = millis() - breakStartTime;
    if (elapsed > breakDuration) {
      elapsed = breakDuration;
    }

    int remainingSeconds = (breakDuration - elapsed) / 1000;
    int minutes = remainingSeconds / 60;
    int seconds = remainingSeconds % 60;

    if (remainingSeconds != lastBreakSecond) {
      lastBreakSecond = remainingSeconds;

      char timeText[6];
      sprintf(timeText, "%02d:%02d", minutes, seconds);

      tft.fillRect(20, 85, 160, 40, ST77XX_BLACK);
      tft.setTextSize(3);
      tft.setTextColor(ST77XX_GREEN);
      tft.setCursor(20, 85);
      tft.print(timeText);

      if (remainingSeconds == 0) {
        tft.fillRect(20, 140, 220, 50, ST77XX_BLACK);
        tft.setTextSize(2);
        tft.setTextColor(ST77XX_YELLOW);
        tft.setCursor(20, 140);
        tft.println("Congrats on");
        tft.setCursor(20, 165);
        tft.println("staying active");
      }
    }
  }
}

// Todo screen
int getTodoChosenCount() {
  int count = 0;

  for (int i = 0; i < TODO_COUNT; i++) {
    if (todoSelected[i]) {
      count++;
    }
  }

  return count;
}

void drawTodoSelectionScreen() {
  tft.fillScreen(ST77XX_BLACK);

  tft.setTextSize(2);
  tft.setTextColor(ST77XX_MAGENTA);
  tft.setCursor(20, 15);
  tft.println("Todo Mode");

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(20, 45);
  tft.println("Press knob to select");

  for (int i = 0; i < TODO_COUNT; i++) {
    int y = 70 + i * 25;

    if (i == todoSelectedIndex) {
      tft.fillRect(20, y - 3, 200, 19, ST77XX_MAGENTA);
      tft.setTextColor(ST77XX_BLACK);
    } else {
      tft.fillRect(20, y - 3, 200, 19, ST77XX_BLACK);
      tft.setTextColor(todoSelected[i] ? ST77XX_GREEN : ST77XX_WHITE);
    }

    tft.setTextSize(1);
    tft.setCursor(30, y);
    tft.print(todoSelected[i] ? "[x] " : "[ ] ");
    tft.print(todoOptions[i]);
  }

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(20, 210);
  tft.println("Side button starts list");
}

void drawTodoListScreen() {
  tft.fillScreen(ST77XX_BLACK);

  tft.setTextSize(2);
  tft.setTextColor(ST77XX_MAGENTA);
  tft.setCursor(20, 15);
  tft.println("Today's List");

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(20, 45);
  tft.println("Press knob when done");

  int shown = 0;

  for (int i = 0; i < TODO_COUNT; i++) {
    if (!todoSelected[i]) {
      continue;
    }

    int y = 75 + shown * 28;

    if (todoCompleted[i]) {
      tft.fillRect(20, y - 4, 220, 21, ST77XX_GREEN);
      tft.setTextColor(ST77XX_BLACK);
    } else if (i == todoSelectedIndex) {
      tft.fillRect(20, y - 4, 220, 21, ST77XX_MAGENTA);
      tft.setTextColor(ST77XX_BLACK);
    } else {
      tft.fillRect(20, y - 4, 220, 21, ST77XX_BLACK);
      tft.setTextColor(ST77XX_WHITE);
    }

    tft.setTextSize(2);
    tft.setCursor(30, y);
    tft.print(todoOptions[i]);
    shown++;
  }

  if (shown == 0) {
    tft.setTextSize(2);
    tft.setTextColor(ST77XX_YELLOW);
    tft.setCursor(20, 95);
    tft.println("No tasks");
    tft.setTextSize(1);
    tft.setCursor(20, 125);
    tft.println("Use side button to go back");
  }
}

void moveTodoSelection(int direction) {
  if (!todoListStarted) {
    todoSelectedIndex += direction;

    if (todoSelectedIndex >= TODO_COUNT) todoSelectedIndex = 0;
    if (todoSelectedIndex < 0) todoSelectedIndex = TODO_COUNT - 1;
    drawTodoSelectionScreen();
    return;
  }

  if (getTodoChosenCount() == 0) {
    return;
  }

  int nextIndex = todoSelectedIndex;

  do {
    nextIndex += direction;

    if (nextIndex >= TODO_COUNT) nextIndex = 0;
    if (nextIndex < 0) nextIndex = TODO_COUNT - 1;
  } while (!todoSelected[nextIndex]);

  todoSelectedIndex = nextIndex;
  drawTodoListScreen();
}

void enterTodoMode() {
  todoListStarted = false;
  todoSelectedIndex = 0;

  for (int i = 0; i < TODO_COUNT; i++) {
    todoSelected[i] = false;
    todoCompleted[i] = false;
  }

  drawTodoSelectionScreen();
}

void startTodoList() {
  todoListStarted = true;

  if (!todoSelected[todoSelectedIndex]) {
    for (int i = 0; i < TODO_COUNT; i++) {
      if (todoSelected[i]) {
        todoSelectedIndex = i;
        break;
      }
    }
  }

  drawTodoListScreen();
}

void handleTodoSideButton() {
  if (!todoListStarted) {
    startTodoList();
  } else {
    currentMode = 0;
    runHomeMode();
  }
}

void runTodoMode() {
  int currentAState = digitalRead(pinA);

  if (currentAState != lastAState) {
    if (digitalRead(pinB) != currentAState) {
      moveTodoSelection(1);
    } else {
      moveTodoSelection(-1);
    }

    delay(5);
  }

  lastAState = currentAState;

  bool currentButtonState = digitalRead(pushPin);

  if (currentButtonState == LOW && lastButtonState == HIGH) {
    if (!todoListStarted) {
      todoSelected[todoSelectedIndex] = !todoSelected[todoSelectedIndex];
      drawTodoSelectionScreen();
    } else if (todoSelected[todoSelectedIndex]) {
      todoCompleted[todoSelectedIndex] = !todoCompleted[todoSelectedIndex];
      drawTodoListScreen();
    }

    delay(200);
  }

  lastButtonState = currentButtonState;
}

void setup() {
  Serial.begin(9600);
  delay(2000);

  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  pinMode(pushPin, INPUT_PULLUP);
  pinMode(k0Pin, INPUT_PULLUP);

  SPI.begin();

  tft.init(240, 320);
  tft.invertDisplay(false);
  tft.setRotation(3);

  rfid.PCD_Init();

  lastAState = digitalRead(pinA);
  runHomeMode();
}

void loop() {
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
    String uidString = "";

    for (byte i = 0; i < rfid.uid.size; i++) {
      if (rfid.uid.uidByte[i] < 0x10) {
        uidString += "0";
      }
      uidString += String(rfid.uid.uidByte[i], HEX);
      if (i < rfid.uid.size - 1) {
        uidString += " ";
      }
    }

    uidString.toUpperCase();
    lastUID = uidString;

    Serial.print("UID: ");
    Serial.println(lastUID);

    rfid.PICC_HaltA();
    rfid.PCD_StopCrypto1();

    if (lastUID == "04 88 F0 32 DA 61 80") {
      currentMode = 1;
      lastAState = digitalRead(pinA);
      lastButtonState = HIGH;
      enterWorkMode();
    }
    else if (lastUID == "04 16 36 3A DA 61 81") {
      currentMode = 2;
      lastAState = digitalRead(pinA);
      lastButtonState = HIGH;
      enterBreakMode();
    }
    else if (lastUID == "04 4F E1 7A 2A 62 81") {
      currentMode = 3;
      lastAState = digitalRead(pinA);
      lastButtonState = HIGH;
      enterTodoMode();
    }
    else {
      currentMode = 0;
      runHomeMode();
    }
  }

  if (digitalRead(k0Pin) == LOW) {
    if (currentMode == 3) {
      handleTodoSideButton();
    } else {
      currentMode = 0;
      runHomeMode();
    }

    delay(200);
  }

  if (currentMode == 1) {
    runWorkMode();
  }
  else if (currentMode == 2) {
    runBreakMode();
  }
  else if (currentMode == 3) {
    runTodoMode();
  }

  delay(50);
}