# Project export: PillWatch

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: CruzHacks 2025
- Tagline: A 3D-printed, caregiver-controlled dispenser that safely sorts and dispenses the right pills at the right time—ideal for seniors and those with dementia.
- Devpost: https://devpost.com/software/pillpass-v037sc
- GitHub: https://github.com/drooooooo/pill-dispenser-webapp
- Result: winner (Best Hardware Hack)
- Team: 1 GitHub contributor(s) — MindMecs4U (9 commits)

## Devpost submission (written by the team)

### Inspiration

We were inspired to create PillWatch after witnessing how difficult medication management can be for seniors and individuals with dementia. The risk of overdosing or taking the wrong medication is high when pills are taken manually, especially when someone has memory challenges or multiple prescriptions. We wanted to create a solution that ensures safety, reduces caregiver stress, and promotes independence for elderly users.

### What it does

PillWatch is a smart, 3D-printed pill dispenser that accurately sorts and dispenses the right type and amount of medication at the correct time. It is personalized for multiple users and fully controlled through a caregiver-facing web app. Caregivers input medication schedules and dosages, and the dispenser automatically handles the rest—preventing overdoses, missed doses, or pill mix-ups.

### How we built it

We started by designing a compact, 3D-printed frame to house our internal mechanisms. Inside, we built a mechanical pinwheel system powered by two servos that rotates and sorts pills based on the schedule. We programmed a web application that allows caregivers to manage user profiles, pill types, quantities, and timing. The system integrates seamlessly with the dispenser via a microcontroller, coordinating servo movement and scheduling logic in real-time.

### Challenges we ran into

One of the main challenges was aligning the pinwheel mechanism so that pills would dispense accurately without jamming. Calibrating the servos to sort different pill types required a lot of testing. On the software side, syncing the web app with the hardware to ensure perfect timing and reliability was tricky, especially when managing multiple user profiles.

### Accomplishments we're proud of

We’re proud of creating a fully functional prototype with a custom 3D-printed design that integrates both mechanical and software components. We also successfully built a user-friendly web interface for caregivers that adds real-world usability to the device. The fact that PillWatch can help prevent serious medical issues makes it a project we’re especially proud of.

### What we learned

We learned how to merge mechanical design with real-time software control, which involved both hardware debugging and backend development. We also gained experience in user-focused design, especially thinking from the perspective of both seniors and caregivers. Understanding how to build a reliable, real-world healthcare tool pushed our problem-solving and engineering skills to the next level.

### What's next

Next, we want to improve the precision of the dispensing system to handle a wider variety of pill shapes and sizes. We're also planning to add SMS or app notifications for caregivers and possibly integrate voice alerts for users. In the future, we'd love to partner with healthcare facilities or senior homes to test PillWatch in real-world environments and refine it even further.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 4 recognized source files, 64 KB.
- C++ (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code

## Codebase structure (from repository index)

### Files (25 of 25)

```
.DS_Store
app.py
bin/activate
bin/activate.csh
bin/activate.fish
bin/Activate.ps1
bin/flask
bin/pip
bin/pip3
bin/pip3.12
bin/pyserial-miniterm
bin/pyserial-ports
cruzhacks_arduino.ino
esp32_lcd_cruzhacks/include/README
esp32_lcd_cruzhacks/lib/README
esp32_lcd_cruzhacks/platformio.ini
esp32_lcd_cruzhacks/src/main.cpp
esp32_lcd_cruzhacks/test/README
package.json
pill_dispenser_webapp.code-workspace
public/index.html
public/models/face_landmark_68_model-weights_manifest.json
public/models/face_recognition_model-weights_manifest.json
public/models/tiny_face_detector_model-weights_manifest.json
server.js
```

### Dependencies

- package.json: express@^5.1.0, multer@^1.4.5-lts.2

### Recent commits (newest first)

- Update app.py
- Update cruzhacks_arduino.ino
- Add files via upload
- Update cruzhacks_arduino.ino
- Update cruzhacks_arduino.ino
- Add files via upload
- Add files via upload
- adding ds
- adding all files
- adding public folder

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "pill-dispenser-webapp",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^5.1.0",
    "multer": "^1.4.5-lts.2"
  }
}

```

### server.js

```javascript
// server.js - Updated for Raspberry Pi Integration

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const port = 3000;

// Create data directory if it doesn't exist
const dataDir = path.join(__dirname, 'data');
if (!fs.existsSync(dataDir)) {
    fs.mkdirSync(dataDir, { recursive: true });
}

// Create models directory for face-api.js models
const modelsDir = path.join(__dirname, 'public', 'models');
if (!fs.existsSync(modelsDir)) {
    fs.mkdirSync(modelsDir, { recursive: true });
}

// Middleware to parse JSON and serve static files
app.use(express.static('public'));
app.use(express.json({ limit: '50mb' })); // Increased limit for face descriptors
app.use(express.urlencoded({ extended: true }));

// Serve index.html from the public directory
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// POST /register — Save user data to /data folder and send to Raspberry Pi if configured
app.post('/register', (req, res) => {
    try {
        const userData = req.body;

        console.log(`✅ Received registration data for: ${userData.name}`);
        console.log(`📋 Schedule contains ${userData.schedules.length} medication times`);

        // Create a sanitized filename from the user's name
        const sanitizedName = userData.name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
        const filename = `data/${sanitizedName}.json`;

        // Format data specifically for Raspberry Pi if needed
        const raspberryPiData = {
            user: {
                name: userData.name,
                age: userData.age
            },
            schedules: userData.schedules.map(schedule => ({
                time: schedule.time,
                pillA: schedule.pillA,
                pillB: schedule.pillB
            })),
            faceDescriptors: userData.faceDescriptors
        };

        // Write the user data to a file
        fs.writeFileSync(filename, JSON.stringify(raspberryPiData, null, 2));

        // Here you would add code to send data to the Raspberry Pi
        // This could be via HTTP, MQTT, WebSockets, or other protocols
        console.log(`✅ Data saved locally to ${filename}`);
        console.log(`🍓 Ready to send to Raspberry Pi`);

        // Send a success response
        res.json({ status: 'success', savedTo: filename });
    } catch (error) {
        console.error('❌ Error saving registration:', error);
        res.status(500).json({ status: 'error', message: error.message });
    }
});

// Start the server
app.listen(port, () => {
    console.log(`✅ Web server running at http://localhost:${port}`);
    console.log(`💊 Pill Dispenser registration app ready`);
});
```

### app.py

```python
from flask import Flask, request, redirect, url_for, flash, get_flashed_messages, render_template_string
import serial
import threading
import time
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

app = Flask(__name__)
app.secret_key = 'your_secret_key_here'  # Replace with a strong secret key

# === Configure Your Serial Ports ===
try:
    arduino_port = '/dev/tty.usbmodem1423201'  # Update with your Arduino port
    esp32_port = '/dev/tty.usbserial-14210'    # Update with your ESP32 port
    arduino = serial.Serial(arduino_port, 9600, timeout=1)
    esp32 = serial.Serial(esp32_port, 115200, timeout=1)
except Exception as e:
    print("Error opening serial ports:", e)
    arduino = None
    esp32 = None

# Global list to store registered users (each a dict with registration details)
registered_users = []
# Global dictionary to store dosage status for each user.
# Key: username; Value: a list of three booleans for the three scheduled times.
taken_status = {}
# Last dispensed time and user - to track which button was most recently pressed
last_dispensed = {"user": None, "time_index": None, "timestamp": None}

# === Email Configuration ===
EMAIL_ENABLED = True  # Set to False to disable email notifications during testing
EMAIL_SENDER = "medication.dispenser@gmail.com"  # Update with your sender email
EMAIL_PASSWORD = "eqym bmbm cqzb nqfx"  # Update with your email password
EMAIL_RECIPIENT = "da.roof928@gmail.com"  # Update with caregiver's email
SMTP_SERVER = "smtp.example.com"  # Update with your SMTP server
SMTP_PORT = 587  # Update with your SMTP port (typically 587 for TLS)


def send_email_notification(patient_name, medication_time, pill_a_count, pill_b_count):
    """
    Send an email notification to the caregiver about a dispensed medication.

    Args:
        patient_name: Name of the patient who received medication
        medication_time: Time period (Morning, Afternoon, Evening)
        pill_a_count: Number of Pill A dispensed
        pill_b_count: Number of Pill B dispensed
    """
    if not EMAIL_ENABLED:
        print("Email notifications disabled.")
        return

    try:
        # Create email message
        msg = MIMEMultipart()
        msg['From'] = EMAIL_SENDER
        msg['To'] = EMAIL_RECIPIENT
        msg['Subject'] = f"Medication Alert: {patient_name} - {medication_time} Dose"

        # Format timestamp
        current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")

        # Email body
        email_body = f"""
        <html>
        <body style="font-family: Arial, sans-serif; line-height: 1.6;">
            <h2>Medication Dispenser Notification</h2>
            <p>This is an automated notification from the Medication Dispenser System.</p>
            
            <div style="background-color: #f0f0f0; padding: 15px; border-radius: 5px; margin: 15px 0;">
                <p><strong>Patient:</strong> {patient_name}</p>
                <p><strong>Medication Time:</strong> {medication_time}</p>
                <p><strong>Dispensed:</strong> {current_time}</p>
                <p><strong>Medication Dispensed:</strong></p>
                <ul>
                    <li>Pill A: {pill_a_count}</li>
                    <li>Pill B: {pill_b_count}</li>
                </ul>
            </div>
            
            <p>Please contact the patient to ensure medication was taken as prescribed.</p>
            <p>This is an automated message. Please do not reply.</p>
        </body>
        </html>
        """

        msg.attach(MIMEText(email_body, 'html'))

        # Connect to SMTP server and send email
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.starttls()  # Encrypt the connection
            server.login(EMAIL_SENDER, EMAIL_PASSWORD)
            server.send_message(msg)

        print(
            f"Email notification sent for {patient_name}'s {medication_time} medication")

    except Exception as e:
        print(f"Failed to send email notification: {e}")


def calculate_commands_for_choice(record, time_choice):
    """
    Given a registration record and a chosen time index (0, 1, or 2), compute the command strings.
    The Arduino command uses the pill values for the chosen time.
    The ESP32 command sends the full schedule.
    """
    user_name = record['user_name']
    time1 = record['time1']
    time2 = record['time2']
    time3 = record['time3']
    pill_a_values = [record['pill_a1'], record['pill_a2'], record['pill_a3']]
    pill_b_values = [record['pill_b1'], record['pill_b2'], record['pill_b3']]

    selected_a = pill_a_values[time_choice]
    selected_b = pill_b_values[time_choice]
    # Arduino command: dispense:[user]:[Pill A #]:[Pill B #]
    arduino_command = f"dispense:{user_name}:{selected_a}:{selected_b}"
    # ESP32 command (full schedule):
    esp32_command = (
        f"NAME:{user_name};"
        f"TIME1:{time1};TIME2:{time2};TIME3:{time3};"
        f"PILL1:[{record['pill_a1']},{record['pill_a2']},{record['pill_a3']}];"
        f"PILL2:[{record['pill_b1']},{record['pill_b2']},{record['pill_b3']}]"
    )
    return arduino_command, esp32_command


# HTML Template
HTML_TEMPLATE = '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Medication Dispenser System</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f7f9fc;
        }
        h1, h2, h3, h4 {
            color: #2c3e50;
        }
        h1 {
            text-align: center;
            margin-bottom: 30px;
            color: #3498db;
            border-bottom: 2px solid #3498db;
            padding-bottom: 10px;
        }
        .c
[truncated — 18378 more characters]
```

### public/index.html

```html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Smart Pill Dispenser</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/face-api.js/0.22.2/face-api.min.js"></script>
    <style>
        .step {
            display: none;
        }

        .step.active {
            display: block;
        }

        #video,
        #canvas {
            width: 100%;
            max-width: 500px;
            border-radius: 0.5rem;
        }

        .pill-time {
            background-color: #f3f4f6;
            border-radius: 0.5rem;
            padding: 0.75rem;
            margin-bottom: 0.5rem;
        }
    </style>
</head>

<body class="bg-gray-100 min-h-screen">
    <div class="container mx-auto px-4 py-8 max-w-3xl">
        <div class="bg-white shadow-lg rounded-lg p-6 mb-8">
            <div class="flex items-center justify-between mb-6">
                <h1 class="text-2xl font-bold text-indigo-700">Smart Pill Dispenser</h1>
                <div class="bg-indigo-100 text-indigo-800 px-3 py-1 rounded-full text-sm">Registration</div>
            </div>

            <div class="mb-4">
                <div class="flex mb-6">
                    <div class="step-indicator flex-1 flex flex-col items-center">
                        <div
                            class="w-8 h-8 rounded-full flex items-center justify-center step-circle bg-indigo-600 text-white">
                            1</div>
                        <div class="text-xs mt-1">Personal Info</div>
                    </div>
                    <div class="h-0.5 bg-gray-300 flex-1 self-center mx-2 step-line"></div>
                    <div class="step-indicator flex-1 flex flex-col items-center">
                        <div
                            class="w-8 h-8 rounded-full flex items-center justify-center step-circle bg-gray-300 text-gray-600">
                            2</div>
                        <div class="text-xs mt-1">Medication</div>
                    </div>
                    <div class="h-0.5 bg-gray-300 flex-1 self-center mx-2 step-line"></div>
                    <div class="step-indicator flex-1 flex flex-col items-center">
                        <div
                            class="w-8 h-8 rounded-full flex items-center justify-center step-circle bg-gray-300 text-gray-600">
                            3</div>
                        <div class="text-xs mt-1">Face Scan</div>
                    </div>
                </div>

                <div id="step1" class="step active">
                    <h2 class="text-xl font-semibold mb-4">Personal Information</h2>
                    <div class="mb-4">
                        <label for="name" class="block text-gray-700 mb-2">Full Name</label>
                        <input type="text" id="name" required
                            class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
                    </div>
                    <div class="mb-4">
                        <label for="age" class="block text-gray-700 mb-2">Age</label>
                        <input type="number" id="age" required
                            class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
                    </div>
                    <div class="text-right">
                        <button id="nextToStep2"
                            class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition">Next</button>
                    </div>
                </div>

                <div id="step2" class="step">
                    <h2 class="text-xl font-semibold mb-4">Medication Schedule</h2>
                    <p class="text-gray-600 mb-4">Add your medication schedule with quantities of Pill A and Pill B</p>

                    <div id="scheduleContainer" class="mb-4">
                        <!-- Pill schedules will be added here -->
                    </div>

                    <div class="mb-4">
                        <div class="flex mb-2">
                            <input type="time" id="newTime"
                                class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
                        </div>
                        <div class="flex space-x-2">
                            <div class="flex-1">
                                <label for="pillAQty" class="block text-gray-700 text-sm mb-1">Pill A Quantity</label>
                                <input type="number" id="pillAQty" min="0" value="0"
                                    class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
                            </div>
                            <div class="flex-1">
                                <label for="pillBQty" class="block text-gray-700 text-sm mb-1">Pill B Quantity</label>
                                <input type="number" id="pillBQty" min="0" value="0"
                                    class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
                            </div>
                        </div>
                    </div>

                    <button id="addPill"
                        class="mb-6 w-full bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition">+
                        Add Medication Time</button>

                    <div class="flex justify-between">
                        <button id="backToStep1"
                            class="bg-gray-200 text-gray-700 px-6 py-2 rounded-lg hover:bg-gray-300 transition">Back</button>
                    
[truncated — 16973 more characters]
```

### esp32_lcd_cruzhacks/src/main.cpp

```c++
/*******************************************************************
    ESP32 Cheap Yellow Display - User Greeting Display with Medication Schedule

    Displays "SCAN HERE:" and shows a personalized greeting when
    receiving a user name via serial. Includes a button to view medication schedule.
    Automatically returns to scan screen after 7 seconds.
 *******************************************************************/

#include <SPI.h>
#include <XPT2046_Touchscreen.h>
#include <TFT_eSPI.h>

// Touch Screen pins
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33

// Border properties
#define BORDER_WIDTH 10
#define BORDER_COLOR TFT_BLUE
#define BACKGROUND_COLOR TFT_WHITE
#define TEXT_COLOR TFT_BLACK
#define GREETING_COLOR TFT_RED
#define BUTTON_COLOR TFT_GREEN
#define BUTTON_TEXT_COLOR TFT_WHITE
#define MEDICATION_TIME_COLOR TFT_BLUE
#define MEDICATION_PILL_COLOR TFT_DARKGREY

// Serial communication
#define SERIAL_TIMEOUT 100
#define MAX_NAME_LENGTH 32 // Maximum length for user name

// Screen modes
#define MODE_SCAN 0
#define MODE_GREETING 1
#define MODE_SCHEDULE 2

SPIClass touchSpi = SPIClass(VSPI);
XPT2046_Touchscreen ts(XPT2046_CS, XPT2046_IRQ);

TFT_eSPI tft = TFT_eSPI();

// Forward declarations
void drawBorder();
void displayScanText();
void displayGreeting(const String &userName);
void displayMedicationSchedule(const String &userName);
void checkSerialForUserData();
void checkForTouchEvents();
void drawPillIcon(int x, int y, uint16_t color, String letter);
bool isTouchInsideButton(int x, int y);
void checkInactivityTimer();

// State management
int currentMode = MODE_SCAN;
String currentUserName = "";
unsigned long greetingStartTime = 0;
unsigned long lastUserActionTime = 0;
const unsigned long GREETING_DURATION = 5000;    // Show greeting for 5 seconds
const unsigned long MAX_INACTIVITY_TIME = 10000; // Return to scan after 7 seconds

// Button coordinates (will be set in displayGreeting)
int buttonX, buttonY, buttonW, buttonH;

void setup()
{
  Serial.begin(115200);
  delay(1000); // Short delay to allow serial to initialize fully

  Serial.println("\n\n*** ESP32 Display with User Greeting and Medication Schedule ***");
  Serial.println("Test by sending 'NAME:YourName' in the Serial Monitor");

  // Start the SPI for the touch screen and init the TS library
  touchSpi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  ts.begin(touchSpi);
  ts.setRotation(0);

  // Start the TFT display
  tft.init();
  tft.setRotation(0); // Portrait mode

  // Fill the screen with white color
  tft.fillScreen(BACKGROUND_COLOR);

  // Draw a decorative border
  drawBorder();

  // Display "SCAN HERE:" text with a cool font
  displayScanText();

  Serial.println("Display initialized. Waiting for user data...");
  Serial.setTimeout(SERIAL_TIMEOUT);
}

void drawBorder()
{
  // Top border
  tft.fillRect(0, 0, tft.width(), BORDER_WIDTH, BORDER_COLOR);

  // Bottom border
  tft.fillRect(0, tft.height() - BORDER_WIDTH, tft.width(), BORDER_WIDTH, BORDER_COLOR);

  // Left border
  tft.fillRect(0, 0, BORDER_WIDTH, tft.height(), BORDER_COLOR);

  // Right border
  tft.fillRect(tft.width() - BORDER_WIDTH, 0, BORDER_WIDTH, tft.height(), BORDER_COLOR);

  // Add some corner decorations - rounded inner corners
  int cornerSize = BORDER_WIDTH * 2;

  // Inner corners with a different color for decoration
  tft.fillCircle(BORDER_WIDTH * 2, BORDER_WIDTH * 2, BORDER_WIDTH, TFT_RED);
  tft.fillCircle(tft.width() - BORDER_WIDTH * 2, BORDER_WIDTH * 2, BORDER_WIDTH, TFT_RED);
  tft.fillCircle(BORDER_WIDTH * 2, tft.height() - BORDER_WIDTH * 2, BORDER_WIDTH, TFT_RED);
  tft.fillCircle(tft.width() - BORDER_WIDTH * 2, tft.height() - BORDER_WIDTH * 2, BORDER_WIDTH, TFT_RED);
}

void displayScanText()
{
  // Clear the middle part of the screen (not the border)
  tft.fillRect(BORDER_WIDTH, BORDER_WIDTH,
               tft.width() - (BORDER_WIDTH * 2),
               tft.height() - (BORDER_WIDTH * 2),
               BACKGROUND_COLOR);

  currentMode = MODE_SCAN;

  // Using the large font
  tft.setTextColor(TEXT_COLOR);

  // First display using Font 4 (a large font available in TFT_eSPI)
  tft.setTextSize(2);
  tft.setTextFont(4); // Use font 4

  int x = tft.width() / 2;
  int y = tft.height() / 3;

  tft.setTextDatum(MC_DATUM); // Middle center
  tft.drawString("SCAN", x, y);

  y += 60; // Move down for the next line
  tft.drawString("HERE:", x, y);

  // Draw an arrow pointing down
  int arrowY = y + 70;
  int arrowWidth = 40;
  int arrowHeight = 50;

  // Arrow shaft
  tft.fillRect(x - 5, arrowY, 10, arrowHeight, TFT_RED);

  // Arrow head
  for (int i = 0; i < arrowWidth / 2; i++)
  {
    tft.drawLine(x - i, arrowY + arrowHeight - 2 * i, x + i, arrowY + arrowHeight - 2 * i, TFT_RED);
  }

  Serial.println("Scan screen displayed. Ready for new input.");
}

void drawScheduleButton()
{
  // Define button size and position
  buttonW = 160;
  buttonH = 40;
  buttonX = (tft.width() - buttonW) / 2;
  buttonY = tft.height() - BORDER_WIDTH - buttonH - 40;

  // Draw button
  tft.fillRoundRect(buttonX, buttonY, buttonW, buttonH, 8, BUTTON_COLOR);
  tft.drawRoundRect(buttonX, buttonY, buttonW, buttonH, 8, TFT_DARKGREY);

  // Add text to button - CHANGED: Text from "MEDICATION" to "SCHEDULE"
  tft.setTextColor(BUTTON_TEXT_COLOR);
  tft.setTextSize(1);
  tft.setTextFont(4);
  tft.setTextDatum(MC_DATUM);
  tft.drawString("SCHEDULE", buttonX + buttonW / 2, buttonY + buttonH / 2);
}

void displayGreeting(const String &userName)
{
  // Clear the middle part of the screen (not the border)
  tft.fillRect(BORDER_WIDTH, BORDER_WIDTH,
               tft.width() - (BORDER_WIDTH * 2),
               tft.height() - (BORDER_WIDTH * 2),
               BACKGROUND_COLOR);

  currentMode = MODE_GREETING;
  currentUserName = userName;
  greetingStartTime = millis();
  lastUserActionTime = millis(); // Reset inactivity timer

  // Set text properties
  tft.setTextCo
[truncated — 9118 more characters]
```