Introduction: Build a Hydration Reminder Device

I was so focused on work, I forgot to drink water again."

I said this to myself too many times - until I decided to fix it with tech.

Why It Matters

We live in a world of hustle. Whether you're coding, designing, gaming, or just grinding away on work, it's easy to forget one of the most basic human needs: drinking water.

I used to skip drinking for hours without even realizing it. Over time, it took a toll-headaches, low energy, and trouble focusing. Not drinking enough water can lead to range of problems: fatigue, poor concentration, even long-term kidney issues.

Apps can remind you, but most people swipe them away. I wanted something more effective - something physical, subtle, and responsive.

Story Behind the Build

That's how I ended up designing this Smart Hydration Reminder - a compact, customizable gadget powered by Arduino, that tracks your water bottle and reminds you exactly when you need it.

It's more than just a time. It uses an IR sensor to detect if your bottle is placed, a rotary encoder to let you control the time interval, an OLED display to keep it visually intuitive, and a buzzer to alert you. All of it is beautifully enclosed in a 3D-printed case that you can customize.

Features That Make This Stand Out

  1. 4-line Large font OLED messages
  2. Encoder-controlled timer (1-20 minutes)
  3. Beep on every encoder value change
  4. Beep when bottle is placed
  5. 3 short beeps when it's time to drink
  6. Asks "why" if bottle is removed early
  7. Auto resume or restart timer based on your reply
  8. Auto restart timer after drinking - no encoder libraries required
  9. Custom designed 3D-printed enclosure via JUSTWAY

What I Learned

This project taught me more than just Arduino code. This taught me how habit-building can be engineered - literally. And that even a small, humble idea - like a "water reminder" - can create real lifestyle change

Finally Thoughts

This isn't a fancy small bottle. it won't tell you how alkaline your water is. It won't sync with 5 apps or offer gamification But what it will do is remind you to drink water, every time - consistency gently, and reliably.

Build habits. Build gadgets. Stay hydrated.


Supplies

Electronics:

  1. Arduino UNO R4 Wi-Fi x1: I used this for performance + Wi-Fi (for future upgrades). But you can absolutely use any Arduino Board (Uno, Nano, etc.)


  1. IR Proximity Sensor x1: Detects whether bottle is placed or removed


  1. Rotary Encoder with Push Button Module x1: To navigate in between menus and select the timer


  1. 0.96" OLED I2C Display x1: To show timer, prompts, and messages


  1. Jumper Wires (as needed): For connecting all components together


  1. Active Buzzer x1: Gives short beeps on time change, alerts and bottle placement

I Designed a 3D Case for It - and Got It Printed via JUSTWAY

I wanted this project to not just work but look great too. So I designed a Compact, minimal 3D enclosure to house the electronics securely.

To bring the design to life, I used JUSTWAY's 3D Printing Service, and I can confidently say - it exceeded expectations.

Why JUSTWAY?

Unlock the Future of Manufacturing with JUSTWAY's 3D Printing Service!

Whether you're making prototypes, functional enclosure, or final products - JUSTWAY delivers precision, speed, and professionalism.

  1. Supports Technologies like SLA, SLS, FDM, SLM, MJF, DLP
  2. Offers a wide range of materials: resins, nylons, metals
  3. Super easy to order: Upload Get Instant Quote Track Order
  4. Finishing options + CNC, injection molding, and even metal fabrication
  5. Great customer support & fast delivery

Step 1: How to Order Your Own Model Via JUSTWAY

It's super simple:

Step1: Upload Your Design in Seconds: Head to the JUSTWAY website, log in (or sign up if you're new), and jump straight into the "Get Instant Quote" section.

Upload your STP, STEP, or other supported CAD file

Step 2: Choose Your Manufacturing Process

from resins, nylon, and PLA to metals and engineering grade polymers. Customize the finish and other specs - get your instant quote on the spot

Step 3: Preview and Inspect Your Model

Want to double-check how it'll turn out?

JUSTWAY lets you preview the uploaded model in 3D right in the browser before placing the order. Zoom, pan, rotate -inspect every detail

Step 4: Review Quote & Place Order

JUSTWAY will guarantee excellent production with stringent quality control inspections after your purchase is placed. you can anticipate receiving your 3D model withing the allotted lead time. Experience the ease and effectiveness of using JUSTWAY to bring your 3D creation to life!

They also offer:

  1. CNC machining
  2. Sheet metal fabrication
  3. Injection molding
  4. Surface finishing

Now let see How It Works (Step-by-Step Flow)

Step 5: How It Works: Power on & Animation

As soon as you power system via USB or 5V source, the OLED screen boots up with a custom splash screen of a Water drop appears.

Step 6: Waiting for Bottle Placement

The device now actively check the IR sensor if no bottle is detected the OLED displays:

This ensure the timer only starts after the user is ready.

Step 7: Bottle Detected Set Time

The moment you place the bottle, the IR sensor goes LOW (detection). The OLED now switches to time-setting mode using the rotary encoder

  1. Rotate encoder: Increase/decrease minutes (1-20 minutes)
  2. Press the encoder button to start

Step 8: Countdown Starts

After you press the encoder button, the timer starts counting down OLED shows something like this as shown above

Step 9: Countdown Ends

Reminder Triggered Once the countdown finishes, the system:

  1. Beeps 3 times
  2. OLED Shows the message "Drinking Time" as shown above

Step 10: Place the Bottle Again

Once the bottle is placed again, everything resumes smoothly - no need for manual resets. This step-based behavior makes it feel like a real assistant on you desk - one that understands and adapts to your routine.

Step 11: If Bottle Removed

If you remove the bottle in between the countdown, the system asks you a question:

  1. "Did the bottle fall?"
  2. "Or did you drink water?"

If you select "Drank Water" or "Bottle Fall" it will show same message "Resumes when bottle is placed"

But in case of "Drank water" the timer resets and starts from the original interval and in case of "bottle fell" the countdown resumes from where it paused.

Step 12: Wiring Connections

Here's how to connect each component to the Arduino UNO R4 Wi-Fi:

IR Sensor To Arduino Board

IR Sensor Pins → Arduino Board

GND → GND

VCC → 5v

OUT → D5

0.96" Inch Display To Arduino Board

OLED Display Pin → Arduino Board

GND → GND

VCC → 5v

SDA → SDA

SCL → SCL

Rotary Encoder To Arduino Board

Rotary Encoder Pin → Arduino Board

CLK → D2

DT → D3

SW → D4

GND → GND

VCC → 5v

Buzzer Pin To Arduino Board

Buzzer Pin → Arduino Board

Positive Pin → D6

Negative Pin → GND

These simple connections are enough to give life to your hydration reminder.

Step 13: Upload the Code

//included the neccessary libraries like Wir.h Adafruit GFX.h AdafruitSSD1306
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//Defined the size of the oled display
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Defined Pins
#define CLK 2
#define DT 3
#define SW 4
#define IR_SENSOR 5
#define BUZZER 6

int setTime = 1;
int lastEncoderState;
bool bottlePresent = false;
bool timerRunning = false;
bool inQuestion = false;
bool sessionCompleted = false;
bool waitingAfterDrink = false;

unsigned long timerStart = 0;
unsigned long timerDuration = 0;
unsigned long timeRemaining = 0;

int menuOption = 0;

// --- Paste your 128x64 bitmap below ---
const unsigned char myBitmap [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xfc, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xfc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xf8, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xe1, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

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

pinMode(CLK, INPUT);
pinMode(DT, INPUT);
pinMode(SW, INPUT_PULLUP);
pinMode(IR_SENSOR, INPUT);
pinMode(BUZZER, OUTPUT);
digitalWrite(BUZZER, LOW);

lastEncoderState = digitalRead(CLK);

if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED init failed"));
while (1);
}

display.clearDisplay();
display.drawBitmap(0, 0, myBitmap, 128, 64, SSD1306_WHITE);
display.display();
delay(2000);

displayMultiLine("Please", "place your", "water", "bottle");
}

void loop() {
int irStatus = digitalRead(IR_SENSOR);

if (inQuestion) {
handleQuestionMenu();
return;
}

if (irStatus == LOW) { // Bottle placed
if (!bottlePresent) {
bottlePresent = true;
beep(); // ✅ Beep on bottle placed
Serial.println("[INFO] Bottle placed.");

if (!timerRunning && timeRemaining > 0) {
Serial.println("[INFO] Resuming paused timer...");
timerStart = millis();
timerDuration = timeRemaining;
timerRunning = true;
timeRemaining = 0;
} else if (!timerRunning && timeRemaining == 0) {
if (waitingAfterDrink) {
Serial.println("[INFO] Restarting timer after drinking...");
timerDuration = setTime * 60 * 1000UL;
timerStart = millis();
timerRunning = true;
waitingAfterDrink = false;
sessionCompleted = false;
} else if (!sessionCompleted) {
displayTimer(setTime);
}
}
}

if (!timerRunning) {
handleEncoder();

if (digitalRead(SW) == LOW) {
timerDuration = setTime * 60 * 1000UL;
timerStart = millis();
timerRunning = true;
sessionCompleted = false;
Serial.print("[INFO] Timer started: ");
Serial.print(setTime);
Serial.println(" minutes");
delay(500);
}

} else {
unsigned long elapsed = millis() - timerStart;

if (elapsed >= timerDuration) {
Serial.println("[INFO] Drinking Time!");

for (int i = 0; i < 3; i++) {
beep();
delay(200);
}

displayMessage("Drinking\nTime ", 2);

while (digitalRead(IR_SENSOR) == LOW) {
delay(100);
}

timerRunning = false;
sessionCompleted = true;
waitingAfterDrink = true;
displayMultiLine("Please", "place your", "water", "bottle");
bottlePresent = false;
} else {
unsigned long remaining = (timerDuration - elapsed) / 1000;
int min = remaining / 60;
int sec = remaining % 60;
char buf[20];
sprintf(buf, "%02d:%02d", min, sec);
displayMessage(String("Next sip in:\n") + buf, 2);
Serial.print("[INFO] Time left: ");
Serial.println(buf);
delay(1000);
}
}

} else { // Bottle removed
if (bottlePresent && timerRunning) {
timeRemaining = timerDuration - (millis() - timerStart);
timerRunning = false;
inQuestion = true;
menuOption = 0;
displayQuestionMenu();
Serial.println("[INFO] Bottle removed during timer. Asking reason...");
}

if (!bottlePresent) {
displayMultiLine("Please", "place your", "water", "bottle");
}

bottlePresent = false;
}
}

// ------------------ Question Menu ------------------
void handleQuestionMenu() {
int currentState = digitalRead(CLK);
if (currentState != lastEncoderState) {
if (digitalRead(DT) != currentState) {
menuOption++;
} else {
menuOption--;
}
menuOption = constrain(menuOption, 0, 1);
displayQuestionMenu();
delay(100);
}
lastEncoderState = currentState;

if (digitalRead(SW) == LOW) {
delay(300);
Serial.println(menuOption == 0 ? "[SELECTED] Bottle Fell" : "[SELECTED] Drank Water");
displayMultiLine("Resume", "when", "bottle is", "placed");
waitForBottleReplacement(menuOption == 0);
inQuestion = false;
}
}

void displayQuestionMenu() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Why removed?");
display.setCursor(0, 20);
if (menuOption == 0) display.print("> ");
else display.print(" ");
display.println("Bottle fell");
display.setCursor(0, 35);
if (menuOption == 1) display.print("> ");
else display.print(" ");
display.println("I drank water");
display.display();
}

// ------------------ Helper Functions ------------------
void handleEncoder() {
int currentState = digitalRead(CLK);
if (currentState != lastEncoderState) {
if (digitalRead(DT) != currentState) {
setTime++;
} else {
setTime--;
}
setTime = constrain(setTime, 1, 20);
beep();
displayTimer(setTime);
delay(100);
}
lastEncoderState = currentState;
}

void beep() {
digitalWrite(BUZZER, HIGH);
delay(50);
digitalWrite(BUZZER, LOW);
}

void displayMessage(String msg, int textSize) {
display.clearDisplay();
display.setTextSize(textSize);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(msg);
display.display();
}

void displayTimer(int time) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Set Time:");
display.setTextSize(3);
display.setCursor(20, 30);
display.print(time);
display.println(" min");
display.display();
}

void displayMultiLine(String l1, String l2, String l3, String l4) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(l1);
display.println(l2);
display.println(l3);
display.println(l4);
display.display();
}

void waitForBottleRemoval() {
while (digitalRead(IR_SENSOR) == LOW) delay(100);
while (digitalRead(IR_SENSOR) == HIGH) delay(100);
}

void waitForBottleReplacement(bool resumePrevious) {
while (digitalRead(IR_SENSOR) == HIGH) delay(100);

if (resumePrevious) {
timerStart = millis();
timerDuration = timeRemaining;
Serial.print("[INFO] Resuming from: ");
Serial.print(timerDuration / 1000);
Serial.println(" seconds");
} else {
timerStart = millis();
timerDuration = setTime * 60 * 1000UL;
Serial.println("[INFO] Starting new timer.");
}

timerRunning = true;
timeRemaining = 0;
}

void loop(){
digitalWrite(10,HIGH);
dleay(1000);
}

The core functions include:

  1. IR Sensor check for bottle presence
  2. Rotary encoder sets time (1-20 minutes)
  3. OLED displays interval and countdown
  4. Buzzer gives single or repeated alerts
  5. Logic branches based on your response (Did bottle fall ? Or did you drink?)


Step 14: Video Demonstration

Want to see it working in real life? Here's a complete video demo showing very interaction. In just 5 minutes, you'll understand everything about how the project works

A small request to all of you: if you find this project helpful or inspiring, please subscribe to my YouTube channel and share the video. That would be a huge support from your side and will help me keep building and sharing more!