Baby Feeding & Nap Timer
A two-button tracking device that monitors elapsed time since baby's last feeding and nap using a real-time clock and LCD display. Data persists through power loss via EEPROM storage.
Key Features
- Dual countdown timers on 16×2 LCD
- Real-time clock for power outages
- Fast-forward by holding buttons
- Non-volatile memory
- Two pushbuttons interface
Hardware Components
- Arduino Uno
- DS1302 RTC module with CR2032 battery
- 16×2 LCD (HD44780)
- 10kΩ potentiometer
- 2 pushbuttons
- Breadboard
Wiring Details
LCD: RS→Pin 12, E→Pin 11, D4-7→Pins 10,9,8,13
Buttons: Feed→Pin 2, Nap→Pin 3
RTC: CLK→Pin 6, DAT→Pin 5, RST→Pin 4
Operation
Display shows Feed: HH:MM:SS / Nap: HH:MM:SS
Quick press: Resets timer
Hold 0.5s: Fast-forward mode
View Complete User Manual
Basic Operation
The screen shows two timers counting upward representing time since last feeding and time since last nap:
Feed: HH:MM:SS Nap: HH:MM:SS
Resetting a Timer
- Feeding Button (D2): Press once to reset feed timer to 00:00:00. Hold 0.5+ seconds for fast-forward mode.
- Nap Button (D3): Press once to reset nap timer to 00:00:00. Hold 0.5+ seconds for fast-forward mode.
Fast-Forward Mode
If you forgot to press the button earlier, the device can catch up:
- Holding a button for 0.5 seconds activates fast-forward mode
- Timer advances by 1 minute every 0.2 seconds (display updates in real-time)
- Release the button to stop fast-forward
Power Loss Behavior
The DS1302 RTC stores time using its own battery. The Arduino saves timestamps to EEPROM. You can unplug the device, move it, and plug it back in - both timers resume correctly based on real elapsed time. No need to set the clock again.
Troubleshooting
- LCD shows blocks or very faint: Adjust the 10kΩ potentiometer
- Timers reset after power loss: CR2032 battery missing/dead, check RTC wiring
- Buttons don't register: Check GND connection, shorten jumper wires
- RTC time wrong: Upload sketch with rtc.time() setter, then remove it
View Complete Arduino Code
#include <LiquidCrystal.h>
#include <DS1302.h>
#include <EEPROM.h>
// LCD pins: RS, E, D4, D5, D6, D7
LiquidCrystal lcd(12, 11, 10, 9, 8, 13);
// DS1302 pins: CE, IO, SCLK
DS1302 rtc(4, 5, 6);
// Button pins
const int feedingButtonPin = 2;
const int napButtonPin = 3;
// States
bool lastFeedingState = HIGH;
bool lastNapState = HIGH;
// Times (unix timestamps)
unsigned long feedingStart = 0;
unsigned long napStart = 0;
// Hold tracking
unsigned long feedPressedTime = 0;
unsigned long napPressedTime = 0;
bool feedingHeld = false;
bool napHeld = false;
unsigned long lastFeedAddTime = 0;
unsigned long lastNapAddTime = 0;
// Constants
const unsigned long holdThreshold = 500; // ms
const unsigned long incrementInterval = 200; // ms
const unsigned long oneMinute = 60; // seconds
// EEPROM addresses (4 bytes each)
const int EEPROM_FEEDING_ADDR = 0;
const int EEPROM_NAP_ADDR = 4;
void setup() {
pinMode(feedingButtonPin, INPUT_PULLUP);
pinMode(napButtonPin, INPUT_PULLUP);
lcd.begin(16, 2);
Serial.begin(9600);
rtc.halt(false);
rtc.writeProtect(false);
// Read stored timestamps
EEPROM.get(EEPROM_FEEDING_ADDR, feedingStart);
EEPROM.get(EEPROM_NAP_ADDR, napStart);
// Validate EEPROM values; if invalid, initialize to current RTC time
Time nowTime = rtc.time();
unsigned long now = nowTime.unixtime();
if (feedingStart == 0xFFFFFFFF || feedingStart == 0) feedingStart = now;
if (napStart == 0xFFFFFFFF || napStart == 0) napStart = now;
}
void loop() {
Time nowTime = rtc.time();
unsigned long currentTime = nowTime.unixtime();
// Feeding button logic
bool currentFeedingState = digitalRead(feedingButtonPin);
if (lastFeedingState == HIGH && currentFeedingState == LOW) {
feedPressedTime = millis();
feedingHeld = false;
} else if (lastFeedingState == LOW && currentFeedingState == LOW) {
if (!feedingHeld && (millis() - feedPressedTime >= holdThreshold)) {
feedingHeld = true;
lastFeedAddTime = millis();
}
if (feedingHeld && (millis() - lastFeedAddTime >= incrementInterval)) {
feedingStart -= oneMinute;
lastFeedAddTime = millis();
EEPROM.put(EEPROM_FEEDING_ADDR, feedingStart);
}
} else if (lastFeedingState == LOW && currentFeedingState == HIGH) {
if (!feedingHeld) {
feedingStart = currentTime;
EEPROM.put(EEPROM_FEEDING_ADDR, feedingStart);
}
}
lastFeedingState = currentFeedingState;
// Nap button logic
bool currentNapState = digitalRead(napButtonPin);
if (lastNapState == HIGH && currentNapState == LOW) {
napPressedTime = millis();
napHeld = false;
} else if (lastNapState == LOW && currentNapState == LOW) {
if (!napHeld && (millis() - napPressedTime >= holdThreshold)) {
napHeld = true;
lastNapAddTime = millis();
}
if (napHeld && (millis() - lastNapAddTime >= incrementInterval)) {
napStart -= oneMinute;
lastNapAddTime = millis();
EEPROM.put(EEPROM_NAP_ADDR, napStart);
}
} else if (lastNapState == LOW && currentNapState == HIGH) {
if (!napHeld) {
napStart = currentTime;
EEPROM.put(EEPROM_NAP_ADDR, napStart);
}
}
lastNapState = currentNapState;
// Calculate elapsed times
unsigned long feedElapsed = currentTime - feedingStart;
unsigned long napElapsed = currentTime - napStart;
// Display on LCD
displayElapsedTime(0, "Feed: ", feedElapsed);
displayElapsedTime(1, "Nap: ", napElapsed);
delay(50);
}
void displayElapsedTime(int row, const char* label, unsigned long elapsedSeconds) {
unsigned long hours = elapsedSeconds / 3600;
unsigned long minutes = (elapsedSeconds % 3600) / 60;
unsigned long seconds = elapsedSeconds % 60;
lcd.setCursor(0, row);
lcd.print(label);
if (hours < 10) lcd.print('0');
lcd.print(hours);
lcd.print(':');
if (minutes < 10) lcd.print('0');
lcd.print(minutes);
lcd.print(':');
if (seconds < 10) lcd.print('0');
lcd.print(seconds);
lcd.print(" ");
}