Rohan Barnwal
Published

Never Forget to Drink Water Again - Build This Gadget!

Build a smart DIY device that reminds you to drink water using Arduino, IR Sensor, OLED & a #d-Printed case. Stay healthy, stay hydrated

BeginnerFull instructions provided221
Never Forget to Drink Water Again - Build This Gadget!

Things used in this project

Hardware components

Arduino UNO R4 WiFi
Arduino UNO R4 WiFi
×1
0.96 OLED Display
×1
IR Sensor
×1
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Buzzer, Piezo
Buzzer, Piezo
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Connections

IR Sensor OUT → D5

IR Sensor VCC → 5V

IR Sensor GND → GND

Rotary Encoder CLK → D2

Rotary Encoder DT → D3

Rotary Encoder SW → D4

Encoder VCC → 5V

Encoder GND → GND

OLED SDA → A4

OLED SCL → A5

OLED VCC → 3.3V or 5V (based on module)

OLED GND → GND

Buzzer + → D6

Buzzer − → GND

Code

Code

Arduino
//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);
}

Credits

Rohan Barnwal
40 projects • 38 followers
Rohan Barnwal - maker, hacker, tech enthusiast. I explore new tech & find innovative solutions. See my projects on hackster.io!

Comments