diff --git a/toothbrush-timer.ino b/toothbrush-timer.ino new file mode 100644 index 0000000..1216a1d --- /dev/null +++ b/toothbrush-timer.ino @@ -0,0 +1,199 @@ +#include +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 32 +#define OLED_RESET -1 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +// Button and buzzer pins +const int buttonPin = 7; +const int buzzerPin = 8; + +unsigned long lastUpdate = 0; +int remaining = 0; +bool running = false; + +// ------------------------------------------------------------ +// Draw a star-shaped sparkle +// ------------------------------------------------------------ +void drawStar(int x, int y) { + display.drawPixel(x, y, SSD1306_WHITE); + display.drawPixel(x - 1, y, SSD1306_WHITE); + display.drawPixel(x + 1, y, SSD1306_WHITE); + display.drawPixel(x, y - 1, SSD1306_WHITE); + display.drawPixel(x, y + 1, SSD1306_WHITE); + + display.drawPixel(x - 1, y - 1, SSD1306_WHITE); + display.drawPixel(x + 1, y - 1, SSD1306_WHITE); + display.drawPixel(x - 1, y + 1, SSD1306_WHITE); + display.drawPixel(x + 1, y + 1, SSD1306_WHITE); +} + +// ------------------------------------------------------------ +// Celebration animation with glow + sparkles +// ------------------------------------------------------------ +void showSmiley() { + int cx = 64; + int cy = 16; + + unsigned long startTime = millis(); + + // --- 1. Sparkle burst at the start --- + for (int burst = 0; burst < 12; burst++) { + display.clearDisplay(); + + int sx, sy, dx, dy, dist; + + // Keep sparkles OUTSIDE the face radius + do { + sx = random(0, 128); + sy = random(0, 32); + + dx = sx - cx; + dy = sy - cy; + dist = sqrt(dx * dx + dy * dy); + + } while (dist < 22); + + drawStar(sx, sy); + + display.display(); + delay(60); + } + + // --- 2. Main 15-second animation --- + while (millis() - startTime < 15000) { + display.clearDisplay(); + + // Pulsing glow radius + float t = (millis() - startTime) * 0.008; + int pulse = 2 + (sin(t) * 3); + + // Glow ring + display.drawCircle(cx, cy, 14 + pulse, SSD1306_WHITE); + + // Face outline + display.drawCircle(cx, cy, 14, SSD1306_WHITE); + + // Eyes + display.fillCircle(cx - 6, cy - 4, 2, SSD1306_WHITE); + display.fillCircle(cx + 6, cy - 4, 2, SSD1306_WHITE); + + // Smile (correct orientation) + for (int angle = 20; angle <= 160; angle += 5) { + float rad = angle * 0.0174533; + int x = cx + cos(rad) * 10; + int y = cy + sin(rad) * 10; + display.drawPixel(x, y, SSD1306_WHITE); + } + + // --- Twinkling sparkles (outside the face) --- + for (int i = 0; i < 5; i++) { + int sx, sy, dx, dy, dist; + + do { + sx = random(0, 128); + sy = random(0, 32); + + dx = sx - cx; + dy = sy - cy; + dist = sqrt(dx * dx + dy * dy); + + } while (dist < 22); // keep sparkles OUTSIDE the face + glow + + drawStar(sx, sy); + } + + display.display(); + delay(120); + } + + // Turn off the display after animation + display.clearDisplay(); + display.display(); + display.ssd1306_command(SSD1306_DISPLAYOFF); +} + +// ------------------------------------------------------------ +// Draw the horizontal countdown bar + digits +// ------------------------------------------------------------ +void drawTimer() { + display.clearDisplay(); + + // Bar dimensions + int barHeight = 32; + int barWidth = 96; + + // Map remaining seconds to bar width + int currentWidth = map(remaining, 0, 90, 0, barWidth); + + // Draw bar left → right + display.fillRect(0, 0, currentWidth, barHeight, SSD1306_WHITE); + + // Digits on the right + display.setTextSize(2); + display.setTextColor(SSD1306_WHITE); + + int digitX = 100; + int digitY = 10; + + display.setCursor(digitX, digitY); + display.print(remaining); + + display.display(); +} + +// ------------------------------------------------------------ +// Setup +// ------------------------------------------------------------ +void setup() { + pinMode(buttonPin, INPUT_PULLUP); + pinMode(buzzerPin, OUTPUT); + + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + display.clearDisplay(); + display.setRotation(0); // horizontal + display.display(); +} + +// ------------------------------------------------------------ +// Main loop +// ------------------------------------------------------------ +void loop() { + // Start timer on button press + if (!running && digitalRead(buttonPin) == LOW) { + remaining = 90; + running = true; + display.ssd1306_command(SSD1306_DISPLAYON); + + delay(200); // debounce + } + + if (running) { + unsigned long now = millis(); + + // Update once per second + if (now - lastUpdate >= 1000) { + lastUpdate = now; + remaining--; + + // Final 5-second countdown beeps (higher pitch) + if (remaining > 0 && remaining <= 5) { + tone(buzzerPin, 3000, 150); // 3 kHz beep + } + // Regular 10-second interval beeps + else if (remaining > 0 && remaining % 10 == 0) { + tone(buzzerPin, 2000, 150); // 2 kHz beep + } + + if (remaining <= 0) { + running = false; + showSmiley(); + } + } + + drawTimer(); + } +}