Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.
Medicine Reminder: the finished build
UnoHome automation

Medicine reminder with a “taken” button (Arduino Uno + DS3231 + LCD)

Beeps at dose time until the “taken” button is pressed; shows what was missed.

Difficulty
Easy
Build time
1.5 h
Parts cost
₹1,115 · $13
Verified — core avr 1.8.8, RTClib 2.1.4, 3 Nov 2026Compile-only

Community draft — report a mistake

Parts list

PartQtyApprox.Buy
Arduino Uno R3 (CH340 clone)1₹450Buy Arduino Uno R3 (CH340 clone)
DS3231 RTC module + CR2032 cell1₹150Buy DS3231 RTC module + CR2032 cell
16×2 LCD with I2C backpack (PCF8574, 0x27)1₹180Buy 16×2 LCD with I2C backpack (PCF8574, 0x27)
Active buzzer module1₹25Buy Active buzzer module
Large push button (green)1₹40Buy Large push button (green)
5 V 1 A USB adapter + cable (or a power bank)1₹120Buy 5 V 1 A USB adapter + cable (or a power bank)
Box that fits next to the pill organiser1₹150Buy 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 R3Only the pins used are shown · 5 V logicDS3231 real-time clock (RTC) module16×2 LCD + I2C backpackActive buzzer module (high-level trigger)Push button (6 mm tactile switch)5VGNDD4D8SDA · 18SCL · 1932K (not connected)SQW (not connected)SCLSDAVCCGNDGNDVCCSDASCLI/OVCCGND12
Every connection
Arduino Uno R3Part · pinWireNotes
5VDS3231 real-time clock (RTC) module · VCCred
GNDDS3231 real-time clock (RTC) module · GNDblack
SDADS3231 real-time clock (RTC) module · SDAblue
SCLDS3231 real-time clock (RTC) module · SCLyellow
5V16×2 LCD + I2C backpack · VCCred
GND16×2 LCD + I2C backpack · GNDblack
SDA16×2 LCD + I2C backpack · SDAblue
SCL16×2 LCD + I2C backpack · SCLyellow
D8Active buzzer module (high-level trigger) · I/Oorange
5VActive buzzer module (high-level trigger) · VCCred
GNDActive buzzer module (high-level trigger) · GNDblack
D4Push button (6 mm tactile switch) · 1green“Taken” button, INPUT_PULLUP
GNDPush button (6 mm tactile switch) · 2black

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.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// 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.

↑↓ to move · Enter to open