#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;
// 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");
}
// 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");
}
}
}
}
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(1);
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 == "13 EA 00 2D") {
currentMode = 1;
lastAState = digitalRead(pinA);
lastButtonState = HIGH;
enterWorkMode();
}
else if (lastUID == "8C 50 FA 03") {
currentMode = 2;
lastAState = digitalRead(pinA);
lastButtonState = HIGH;
enterBreakMode();
}
else {
currentMode = 0;
runHomeMode();
}
}
if (digitalRead(k0Pin) == LOW) {
currentMode = 0;
runHomeMode();
delay(200);
}
if (currentMode == 1) {
runWorkMode();
}
else if (currentMode == 2) {
runBreakMode();
}
delay(50);
}