Parts list
| Part | Qty | Approx. | Buy |
|---|---|---|---|
| Arduino Uno R3 (CH340 clone) | 1 | ₹450 | Buy Arduino Uno R3 (CH340 clone) |
| DS3231 RTC module + CR2032 cell | 1 | ₹150 | Buy DS3231 RTC module + CR2032 cell |
| 16×2 LCD with I2C backpack (PCF8574, 0x27) | 1 | ₹180 | Buy 16×2 LCD with I2C backpack (PCF8574, 0x27) |
| Active buzzer module | 1 | ₹25 | Buy Active buzzer module |
| Large push button (green) | 1 | ₹40 | Buy Large push button (green) |
| 5 V 1 A USB adapter + cable (or a power bank) | 1 | ₹120 | Buy 5 V 1 A USB adapter + cable (or a power bank) |
| Box that fits next to the pill organiser | 1 | ₹150 | Buy Box that fits next to the pill organiser |
| Total | ₹1,115 |
Affiliate disclosure: “Buy” links may earn FlashReady a small commission at no extra cost to you. Prices are indicative.
Wiring
| Arduino Uno R3 | Part · pin | Wire | Notes |
|---|---|---|---|
| 5V | DS3231 real-time clock (RTC) module · VCC | red | |
| GND | DS3231 real-time clock (RTC) module · GND | black | |
| SDA | DS3231 real-time clock (RTC) module · SDA | blue | |
| SCL | DS3231 real-time clock (RTC) module · SCL | yellow | |
| 5V | 16×2 LCD + I2C backpack · VCC | red | |
| GND | 16×2 LCD + I2C backpack · GND | black | |
| SDA | 16×2 LCD + I2C backpack · SDA | blue | |
| SCL | 16×2 LCD + I2C backpack · SCL | yellow | |
| D8 | Active buzzer module (high-level trigger) · I/O | orange | |
| 5V | Active buzzer module (high-level trigger) · VCC | red | |
| GND | Active buzzer module (high-level trigger) · GND | black | |
| D4 | Push button (6 mm tactile switch) · 1 | green | “Taken” button, INPUT_PULLUP |
| GND | Push button (6 mm tactile switch) · 2 | black |
Board quirks: Set the clock once with the Serial Monitor at 115200 baud: <code>SET 2026 06 15 07 59 30</code>. The DS3231’s coin cell keeps it through power cuts. Edit the <code>DOSES</code> list with the times the doctor prescribed, in 24-hour time, in order through the day. The labels are what the LCD shows (“TAKE:Morning”), so use words your parent recognises. The reminder only knows the button was pressed, not that the right tablet was swallowed. Pair it with a 7-day pill organiser filled by a family member. This is a memory aid, not medical advice. Never change doses or times without the doctor.
Code
// Medicine reminder with "taken" button — Arduino Uno + DS3231 RTC + 16x2 I2C LCD + buzzer module
// DS3231 and LCD share I2C (SDA/SCL). Buzzer module on D8. "Taken" push button on D4 to GND.
// At each dose time it beeps every 5 s until the button is pressed; after 30 minutes it shows MISSED.
// Set the clock once: send "SET 2026 06 15 07 59 30" in the Serial Monitor (115200 baud).
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
constexpr uint8_t BUZZER_PIN = 8;
constexpr uint8_t BUTTON_PIN = 4;
constexpr unsigned long MISSED_AFTER_MS = 30UL * 60UL * 1000UL;
struct Dose { uint8_t h, m; const char* label; };
// Edit to the doctor's schedule (24-hour clock). Labels: up to 11 characters.
const Dose DOSES[] = {{8, 0, "Morning"}, {14, 0, "After lunch"}, {21, 0, "Night"}};
constexpr uint8_t N_DOSES = sizeof(DOSES) / sizeof(DOSES[0]);
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);
int due = -1; // index of the dose waiting to be taken
unsigned long dueSince = 0;
int lastStartedMinute = -1;
char lastNote[17] = ""; // e.g. "Taken 08:03" or "MISSED Night"
void serialCommands() {
if (!Serial.available()) return;
String cmd = Serial.readStringUntil('\n');
int y, mo, d, h, mi, s;
if (sscanf(cmd.c_str(), "SET %d %d %d %d %d %d", &y, &mo, &d, &h, &mi, &s) == 6) {
rtc.adjust(DateTime(y, mo, d, h, mi, s));
Serial.println(F("Clock set"));
}
}
void logLine(const DateTime& t, const char* label, const __FlashStringHelper* what) {
char buf[24];
snprintf(buf, sizeof buf, "%04d-%02d-%02d %02d:%02d ", t.year(), t.month(), t.day(), t.hour(), t.minute());
Serial.print(buf);
Serial.print(label);
Serial.print(' ');
Serial.println(what);
}
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(115200);
lcd.init();
lcd.backlight();
if (!rtc.begin()) {
lcd.print(F("RTC not found"));
Serial.println(F("DS3231 not found - check SDA/SCL and power"));
for (;;) delay(1000);
}
if (rtc.lostPower()) Serial.println(F("RTC lost power - set the time with SET YYYY MM DD hh mm ss"));
}
void loop() {
serialCommands();
DateTime now = rtc.now();
bool lost = rtc.lostPower();
int minuteOfDay = now.hour() * 60 + now.minute();
unsigned long ms = millis();
// A dose becomes due at second 0 of its minute.
if (!lost && due < 0 && minuteOfDay != lastStartedMinute) {
for (uint8_t i = 0; i < N_DOSES; i++) {
if (DOSES[i].h == now.hour() && DOSES[i].m == now.minute()) {
due = i;
dueSince = ms;
lastStartedMinute = minuteOfDay;
logLine(now, DOSES[i].label, F("due"));
}
}
}
bool button = digitalRead(BUTTON_PIN) == LOW;
if (due >= 0) {
if (button) {
snprintf(lastNote, sizeof lastNote, "Taken %02d:%02d", now.hour(), now.minute());
logLine(now, DOSES[due].label, F("taken"));
due = -1;
digitalWrite(BUZZER_PIN, LOW);
while (digitalRead(BUTTON_PIN) == LOW) delay(10);
} else if (ms - dueSince > MISSED_AFTER_MS) {
snprintf(lastNote, sizeof lastNote, "MISSED %s", DOSES[due].label);
logLine(now, DOSES[due].label, F("MISSED"));
due = -1;
digitalWrite(BUZZER_PIN, LOW);
} else {
// beep-beep every 5 seconds
unsigned long p = (ms - dueSince) % 5000;
digitalWrite(BUZZER_PIN, p < 150 || (p > 300 && p < 450));
}
}
char line[17];
snprintf(line, sizeof line, "%02d:%02d:%02d %02d/%02d ", now.hour(), now.minute(), now.second(), now.day(), now.month());
lcd.setCursor(0, 0);
lcd.print(line);
if (lost) {
snprintf(line, sizeof line, "Set the clock! ");
} else if (due >= 0) {
snprintf(line, sizeof line, "TAKE:%-11.11s", DOSES[due].label);
} else if (lastNote[0] && ms / 4000 % 2) {
snprintf(line, sizeof line, "%-16.16s", lastNote); // alternate with the next dose
} else {
const Dose* next = &DOSES[0];
for (uint8_t i = 0; i < N_DOSES; i++)
if (DOSES[i].h * 60 + DOSES[i].m > minuteOfDay) { next = &DOSES[i]; break; }
snprintf(line, sizeof line, "%02d:%02d %-10.10s", next->h, next->m, next->label);
}
lcd.setCursor(0, 1);
lcd.print(line);
delay(100);
}// Medicine reminder with "taken" button — Arduino Uno + DS3231 RTC + 16x2 I2C LCD + buzzer module
// DS3231 and LCD share I2C (SDA/SCL). Buzzer module on D8. "Taken" push button on D4 to GND.
// At each dose time it beeps every 5 s until the button is pressed; after 30 minutes it shows MISSED.
// Set the clock once: send "SET 2026 06 15 07 59 30" in the Serial Monitor (115200 baud).
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
constexpr uint8_t BUZZER_PIN = 8;
constexpr uint8_t BUTTON_PIN = 4;
constexpr unsigned long MISSED_AFTER_MS = 30UL * 60UL * 1000UL;
struct Dose { uint8_t h, m; const char* label; };
// Edit to the doctor's schedule (24-hour clock). Labels: up to 11 characters.
const Dose DOSES[] = {{8, 0, "Morning"}, {14, 0, "After lunch"}, {21, 0, "Night"}};
constexpr uint8_t N_DOSES = sizeof(DOSES) / sizeof(DOSES[0]);
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);
int due = -1; // index of the dose waiting to be taken
unsigned long dueSince = 0;
int lastStartedMinute = -1;
char lastNote[17] = ""; // e.g. "Taken 08:03" or "MISSED Night"
void serialCommands() {
if (!Serial.available()) return;
String cmd = Serial.readStringUntil('\n');
int y, mo, d, h, mi, s;
if (sscanf(cmd.c_str(), "SET %d %d %d %d %d %d", &y, &mo, &d, &h, &mi, &s) == 6) {
rtc.adjust(DateTime(y, mo, d, h, mi, s));
Serial.println(F("Clock set"));
}
}
void logLine(const DateTime& t, const char* label, const __FlashStringHelper* what) {
char buf[24];
snprintf(buf, sizeof buf, "%04d-%02d-%02d %02d:%02d ", t.year(), t.month(), t.day(), t.hour(), t.minute());
Serial.print(buf);
Serial.print(label);
Serial.print(' ');
Serial.println(what);
}
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(115200);
lcd.init();
lcd.backlight();
if (!rtc.begin()) {
lcd.print(F("RTC not found"));
Serial.println(F("DS3231 not found - check SDA/SCL and power"));
for (;;) delay(1000);
}
if (rtc.lostPower()) Serial.println(F("RTC lost power - set the time with SET YYYY MM DD hh mm ss"));
}
void loop() {
serialCommands();
DateTime now = rtc.now();
bool lost = rtc.lostPower();
int minuteOfDay = now.hour() * 60 + now.minute();
unsigned long ms = millis();
// A dose becomes due at second 0 of its minute.
if (!lost && due < 0 && minuteOfDay != lastStartedMinute) {
for (uint8_t i = 0; i < N_DOSES; i++) {
if (DOSES[i].h == now.hour() && DOSES[i].m == now.minute()) {
due = i;
dueSince = ms;
lastStartedMinute = minuteOfDay;
logLine(now, DOSES[i].label, F("due"));
}
}
}
bool button = digitalRead(BUTTON_PIN) == LOW;
if (due >= 0) {
if (button) {
snprintf(lastNote, sizeof lastNote, "Taken %02d:%02d", now.hour(), now.minute());
logLine(now, DOSES[due].label, F("taken"));
due = -1;
digitalWrite(BUZZER_PIN, LOW);
while (digitalRead(BUTTON_PIN) == LOW) delay(10);
} else if (ms - dueSince > MISSED_AFTER_MS) {
snprintf(lastNote, sizeof lastNote, "MISSED %s", DOSES[due].label);
logLine(now, DOSES[due].label, F("MISSED"));
due = -1;
digitalWrite(BUZZER_PIN, LOW);
} else {
// beep-beep every 5 seconds
unsigned long p = (ms - dueSince) % 5000;
digitalWrite(BUZZER_PIN, p < 150 || (p > 300 && p < 450));
}
}
char line[17];
snprintf(line, sizeof line, "%02d:%02d:%02d %02d/%02d ", now.hour(), now.minute(), now.second(), now.day(), now.month());
lcd.setCursor(0, 0);
lcd.print(line);
if (lost) {
snprintf(line, sizeof line, "Set the clock! ");
} else if (due >= 0) {
snprintf(line, sizeof line, "TAKE:%-11.11s", DOSES[due].label);
} else if (lastNote[0] && ms / 4000 % 2) {
snprintf(line, sizeof line, "%-16.16s", lastNote); // alternate with the next dose
} else {
const Dose* next = &DOSES[0];
for (uint8_t i = 0; i < N_DOSES; i++)
if (DOSES[i].h * 60 + DOSES[i].m > minuteOfDay) { next = &DOSES[i]; break; }
snprintf(line, sizeof line, "%02d:%02d %-10.10s", next->h, next->m, next->label);
}
lcd.setCursor(0, 1);
lcd.print(line);
delay(100);
}Compiled for uno on avr 1.8.8 with RTClib 2.1.4 · 3 Nov 2026
How it works
Elderly parents on BP, diabetes or heart medicines often take three or four doses a day, and both forgetting a dose and taking it twice are common. Phone alarms get dismissed and forgotten. This reminder keeps beeping until a big Taken button is pressed, and shows clearly when a dose was missed.
How it works
The DS3231 real-time clock keeps the time on its own coin cell. The Uno checks it ten times a second and shows the time and date on the 16×2 LCD.
When a dose time in the DOSES list arrives, that dose becomes due: the LCD shows “TAKE:Morning” and the buzzer beeps twice every 5 seconds. Pressing the button marks it taken — the LCD then alternates “Taken 08:03” with the next dose time. If nobody presses it within 30 minutes, it shows “MISSED Morning” instead and stops beeping, so the family can see at a glance what happened.
Every event is also printed on the Serial Monitor with the date and time (2026-06-15 08:03 Morning taken) — leave a laptop or an ESP32 logger on it if you want a record.
Why these parts
- DS3231 — accurate and battery-backed, so a power cut at night doesn’t shift the 8 a.m. dose.
- 16×2 LCD — big, simple characters that are easy to read without glasses (choose a blue or green backlight version).
- One big button — nothing to navigate.
India notes
Keep the reminder where the tablets are, usually near the dining table or the bed, and put it on the inverter or a power bank so it doesn’t go dark in a power cut. Doses with food (“after lunch”) depend on mealtimes; set them to your family’s usual lunch time rather than a fixed 1 p.m.
Troubleshooting
It beeps at the wrong time
Check the time on the top line of the LCD and send SET again. The DS3231 drifts about a minute a year; a big jump means the coin cell is flat.
The LCD shows blocks or nothing
Turn the contrast trimmer on the I2C backpack. If still blank, run the I2C scanner — some backpacks answer at 0x3F.
The beep is too quiet for my parent
Use a louder 5 V buzzer through a transistor on D8, or a buzzer with a lower pitch (easier for older ears to hear). You can also blink the LCD backlight while a dose is due.
Upgrades
- Send the “taken / MISSED” log to a family member’s phone with an ESP32 and ntfy.sh.
- Add one LED per dose on the pill organiser compartment so it’s clear which one to open.
- Record a voice reminder in your parent’s language on a DFPlayer Mini.



