Introduction: Smart Book Return Reminder With Arduino UNO R4 Wi-Fi

We've all been there. You're deep into a good book - eyes wide, plot thickening - when suddenly, Mom yells:

Dinner's ready it's gone.

You put the book down just for a minute - maybe on the bed on the chair - and the next day, it's gone.

Lost under the pillow, trapped between blankets, or worse, mistaken for a plate coaster.

So, instead of accepting defeat (or another lecture), we turn to technology. This project - the Smart Book Return Reminder - ensures you never forget to put your books back.

Powered by the Arduino UNO R4 Wi-Fi, it uses 3 IR sensors and a buzzer to track when a book is removed and reminds you - loudly - if you don't return it on time.

it's smart. it's simple. It's slightly passive aggressive.

And it's your new best friend against procrastination.

How It Works

  1. Each of the 3 IR sensors monitors one section of your shelf.
  2. When any sensor goes CLEAR (book removed) the Arduino immediately asks you (via Serial Monitor):
Enter return time (HH:MM, 24-hour)
  1. At the specified time, the built-in RTC alarm checks if that section's book is still missing.
  2. If yes - buzzer activates and screams continuously until you return the book.
  3. Once all books that were removed are back - Buzzer stops automatically.

Step-By-Step Build Guide

Gather Everything


Supplies

Make sure you have:

  1. Arduino UNO R4 Wi-Fi
  2. 3 IR Sensors
  3. 1 Buzzer
  4. Jumper Wires

Keep a USB cable handy for programming the board.

Step 1: Wiring the Circuit

  1. Connect sensor 1,2, and 3 outputs to Arduino pins 2,3, and 4
  2. Connect all sensor VCC to 5v and all GNDs to GND
  3. Connect the Buzzer +ve to pin 11 and -ve to GND
  4. Ensure all grounds are common (Arduino, sensors, buzzer).

Step 2: Set Up the Arduino IDE

  1. Open the Arduino IDE
  2. Install the UNO R4 Wi-Fi Core (Board Manager - Search "UNO R4 Wi-Fi").
  3. Select Board: Arduino UNO R4 Wi-Fi
  4. Select the correct COM Port
  5. Open a new sketch and paste your provided code.

Step 3: Upload the Code

  1. Click Upload (Right arrow icon)
  2. Once uploaded, open Serial Monitor at 115200 baud.
  3. You'll see:
UNO R4 WiFi - Continuous buzzer until IR returns to DETECTED
Sensor polarity: ACTIVE LOW (LOW = detected)
When any sensor is CLEAR you'll be asked to enter HH:MM.

If you see RTC.begin() failed, make sure your board drivers and core are properly installed.

Code:

// UNO R4 WiFi — Continuous buzzer until previously-clear IR sensors return to DETECTED
// - IR sensors on pins 2,3,4
// - Buzzer on pin 11 (continuous tone while active)
// - Immediately prompt for HH:MM when any sensor is CLEAR; remember which sensors were clear
// - At alarm time, if any of those sensors are still CLEAR, start continuous buzzer
// - Buzzer stays ON until all those sensors become DETECTED again

#include <Arduino.h>
#include <RTC.h>

const int sensorPins[3] = {2, 3, 4};
const int buzzerPin = 11;

// true if IR module outputs LOW when an object is detected (common)
const bool SENSOR_ACTIVE_LOW = true;

// debounce for immediate prompt (ms)
const unsigned long CLEAR_DEBOUNCE_MS = 50UL;
// time print interval (ms)
const unsigned long TIME_PRINT_MS = 1000UL;

volatile bool rtcAlarmFired = false;

unsigned long lastTimePrintMs = 0;
unsigned long clearStartMs = 0;
bool waitingForTarget = false;
int targetHour = -1;
int targetMinute = -1;

// mask of sensors that were CLEAR at prompt time (bit0 = sensor1)
uint8_t clearMaskAtPrompt = 0;

// buzzer active state: when true, continuous tone is ON
bool buzzerActive = false;

// minimal alarm callback
void alarm_cbk() {
rtcAlarmFired = true;
}

void setup() {
Serial.begin(115200);
while (!Serial) { /* wait for serial */ }

Serial.println();
Serial.println("UNO R4 WiFi - Continuous buzzer until IR returns to DETECTED");
Serial.print("Sensor polarity: ");
Serial.println(SENSOR_ACTIVE_LOW ? "ACTIVE LOW (LOW = detected)" : "ACTIVE HIGH (HIGH = detected)");
Serial.println("When any sensor is CLEAR you'll be asked to enter HH:MM.");
Serial.println();

if (!RTC.begin()) {
Serial.println("WARNING: RTC.begin() failed. Make sure UNO R4 WiFi core is installed.");
} else {
Serial.println("RTC initialized (RTC.begin OK).");
// Optionally set RTC here if needed.
}

for (int i = 0; i < 3; ++i) pinMode(sensorPins[i], INPUT);
pinMode(buzzerPin, OUTPUT);
noTone(buzzerPin); // ensure buzzer off

// initial print
printCurrentTimeAndStatus();
lastTimePrintMs = millis();
}

void loop() {
unsigned long nowMs = millis();

// A) print current time + status each second
if (nowMs - lastTimePrintMs >= TIME_PRINT_MS) {
printCurrentTimeAndStatus();
lastTimePrintMs = nowMs;
}

// B) check current sensor clear status for immediate prompt
bool curClear[3];
bool anyClear = false;
for (int i = 0; i < 3; ++i) {
curClear[i] = !readSensor(i); // readSensor: true=DETECTED -> invert => CLEAR
if (curClear[i]) anyClear = true;
}

if (anyClear) {
if (clearStartMs == 0) clearStartMs = nowMs;
else if (!waitingForTarget && (nowMs - clearStartMs >= CLEAR_DEBOUNCE_MS)) {
// build mask
clearMaskAtPrompt = 0;
for (int i = 0; i < 3; ++i) if (curClear[i]) clearMaskAtPrompt |= (1 << i);

Serial.println();
Serial.print("Sensors clear now: ");
for (int i = 0; i < 3; ++i) {
Serial.print("S"); Serial.print(i+1); Serial.print(":"); Serial.print(curClear[i] ? "CLEAR" : "DETECTED");
if (i < 2) Serial.print(" ");
}
Serial.println();
Serial.println("Enter target time (HH:MM, 24-hour) at which I will check these sensor(s):");
Serial.print("Time> ");
waitingForTarget = true;
// keep clearStartMs so user can cancel with empty input if desired
}
} else {
clearStartMs = 0;
}

// C) accept Serial time if waiting
if (waitingForTarget && Serial.available()) {
String line = Serial.readStringUntil('\n');
line.trim();
if (line.length() == 0) {
Serial.println("Cancelled (empty).");
waitingForTarget = false;
clearMaskAtPrompt = 0;
} else {
int h, m;
if (parseTimeString(line, h, m)) {
targetHour = h; targetMinute = m;
Serial.print("Target time set: ");
printTwoDigits(targetHour); Serial.print(":"); printTwoDigits(targetMinute); Serial.println();

// prepare alarm time + match
RTCTime alarmTime;
alarmTime.setHour(targetHour);
alarmTime.setMinute(targetMinute);
alarmTime.setSecond(0);

AlarmMatch match;
match.addMatchHour();
match.addMatchMinute();

if (RTC.setAlarmCallback(alarm_cbk, alarmTime, match)) {
Serial.print("Alarm registered for ");
printTwoDigits(targetHour); Serial.print(":"); printTwoDigits(targetMinute);
Serial.print(" checking mask 0b");
for (int b = 2; b >= 0; --b) Serial.print((clearMaskAtPrompt >> b) & 1);
Serial.println();
} else {
Serial.println("ERROR: Failed to set RTC alarm. Ensure RTC time is set and RTC.begin() succeeded.");
clearMaskAtPrompt = 0;
}
waitingForTarget = false;
} else {
Serial.println("Invalid format. Use HH:MM (24-hour). Example: 01:28 or 13:28");
Serial.print("Time> ");
}
}
}

// D) Handle RTC alarm when fired
if (rtcAlarmFired) {
noInterrupts();
rtcAlarmFired = false;
interrupts();

RTCTime nowt;
RTC.getTime(nowt);
int hh = nowt.getHour();
int mm = nowt.getMinutes();
int ss = nowt.getSeconds();
Serial.print("\nRTC alarm fired at ");
printTwoDigits(hh); Serial.print(":"); printTwoDigits(mm); Serial.print(":"); printTwoDigits(ss); Serial.println();

// If there is no mask, nothing to check
if (clearMaskAtPrompt == 0) {
Serial.println("No sensors were recorded CLEAR at prompt time — nothing to check.");
} else {
// check only sensors in mask
bool anyStillClear = false;
for (int i = 0; i < 3; ++i) {
if (clearMaskAtPrompt & (1 << i)) {
bool detectedNow = readSensor(i); // true=DETECTED
Serial.print("Sensor"); Serial.print(i+1); Serial.print(" (was clear): ");
Serial.println(detectedNow ? "DETECTED now" : "STILL CLEAR");
if (!detectedNow) anyStillClear = true;
}
}

if (anyStillClear) {
Serial.println("One or more sensors that were CLEAR are STILL CLEAR -> starting continuous buzzer!");
startContinuousBuzzer(); // starts tone and sets buzzerActive
} else {
Serial.println("All sensors that were CLEAR are now DETECTED -> no buzzer.");
}
}

Serial.println("------------------------------------");
}

// E) If buzzerActive, monitor the same mask and stop buzzer only when all those sensors become DETECTED
if (buzzerActive) {
bool allDetectedNow = true;
for (int i = 0; i < 3; ++i) {
if (clearMaskAtPrompt & (1 << i)) {
if (!readSensor(i)) { // still CLEAR
allDetectedNow = false;
break;
}
}
}
if (allDetectedNow) {
Serial.println("All previously-clear sensors are now DETECTED -> stopping buzzer.");
stopContinuousBuzzer();
// clear mask as handled
clearMaskAtPrompt = 0;
}
// otherwise keep buzzing; we check again next loop iteration
}

delay(10);
}

// ---------------- Helper functions ----------------

bool readSensor(int idx) {
int raw = digitalRead(sensorPins[idx]);
if (SENSOR_ACTIVE_LOW) return (raw == LOW);
else return (raw == HIGH);
}

void printCurrentTimeAndStatus() {
RTCTime t;
RTC.getTime(t);
int hh = t.getHour();
int mm = t.getMinutes();
int ss = t.getSeconds();

int disp12 = hh % 12; if (disp12 == 0) disp12 = 12;
String ampm = (hh < 12) ? "AM" : "PM";

Serial.print("Time: ");
printTwoDigits(hh); Serial.print(":"); printTwoDigits(mm); Serial.print(":"); printTwoDigits(ss);
Serial.print(" (");
if (disp12 < 10) Serial.print('0');
Serial.print(disp12); Serial.print(":"); printTwoDigits(mm); Serial.print(" "); Serial.print(ampm);
Serial.print(") ");

for (int i = 0; i < 3; ++i) {
bool s = readSensor(i);
Serial.print("S"); Serial.print(i+1); Serial.print(":");
Serial.print(s ? "DETECTED" : "CLEAR");
if (i < 2) Serial.print(" ");
}
Serial.println();
}

bool parseTimeString(const String &in, int &outH, int &outM) {
int colon = in.indexOf(':');
if (colon <= 0) return false;
String sh = in.substring(0, colon);
String sm = in.substring(colon + 1);
sh.trim(); sm.trim();
if (sh.length() == 0 || sm.length() == 0) return false;
int h = sh.toInt();
int m = sm.toInt();
if (h < 0 || h > 23 || m < 0 || m > 59) return false;
outH = h; outM = m;
return true;
}

// Start continuous buzzer: set tone once and set buzzerActive flag
void startContinuousBuzzer() {
if (!buzzerActive) {
tone(buzzerPin, 2000); // continuous 2kHz tone (stays until noTone)
buzzerActive = true;
}
}

// Stop continuous buzzer: call noTone() and clear flag
void stopContinuousBuzzer() {
if (buzzerActive) {
noTone(buzzerPin);
buzzerActive = false;
}
}

// print number with two digits
void printTwoDigits(int x) {
if (x < 10) Serial.print('0');
Serial.print(x);
}

Step 4: Mount the IR Sensors

Mount one IR sensor per section on bookshelf:

  1. The sensor should pint directly at the book's spine or edge.
  2. When a book is there - sensor reads DETECTED (LOW).
  3. When removed - sensor reads CLEAR (HIGH).

Test each one using the Serial Monitor - it prints sensor status every second.

Step 5: Remove a Book

Now test it:

  1. All books in place - all sensors show DETECTED.
  2. Remove a book - you'll see something like:
Sensors clear now: S1:CLEAR S2:DETECTED S3:DETECTED
Enter target time (HH:MM, 24-hour):
Time>
  1. Type a time, e.g. 18:45, then press Enter.

That's the time by which you must return your book.

Step 6: RTC Alarm in Action

When the clock reaches your target time:

  1. The RTC alarm triggers
  2. Arduino checks if that book's sensor still reads CLEAR
  3. If yes The buzzer starts buzzing continuously:
One or more sensors that were CLEAR are STILL CLEAR -> starting continuous buzzer!

The buzzer will not stop until all those sensors detect their books again

Step 7: Returning the Book

Put the book back in its place.

Once the IR sensor detects it, you'll see:

All previously-clear sensors are now DETECTED -> stopping buzzer.

And the sound stops automatically.

Step 8: Try Multiple Sensors

You can remove multiple books before setting the alarm.

The Arduino keeps track of which sensors were CLEAR when you entered the time - and checks all of them at the alarm moment.

So if you removed two books, both must be returned before the buzzer stops. No shortcuts.

Step 9: Cancel a Prompt

If you changed your mind while setting a time, just press Enter without typing anything Arduino will respond:

Cancelled (empty).

and reset the waiting state.

Step 10: Code Behavior Overview

  1. readSensor(i): Reads whether the IR sensor i detects a book
  2. printCurrentTimeAndStatus(): Displays time + sensor state every seconds
  3. parseTimeString(): Validates and extracts HH:MM input
  4. startContinuousBuzzer(): Turns buzzer ON until manually stopped
  5. stopContinuousBuzzer(): Turns buzzer OFF
  6. alarm_cbk(): calls automatically when RTC alarm time hits
  7. loop(): Main logic loop controlling all the above

Step 11: Troubleshooting

  1. RTC not starting: Ensure UNO R4 Wi-Fi core installed; reconnect board
  2. Sensors reversed: Change SENSOR_ACTIVE_LOW to false
  3. Buzzer silent: Test buzzer with simple tone(11,2000) sketch
  4. No Serial Output: Check baud rate (115200) and COM port
  5. Buzzer never stops: Verify sensors are aligned and working correclty


Step 12: Optional Improvements

  1. Add LEDs: Green when book present, Red when missing
  2. Wi-Fi Alerts: Send phone notification using UNO R4 Wi-Fi features
  3. Voice Module: Use player module to say "Put your book back!"
  4. Logging: Store missed returns in EEPROM or SD Card.
  5. Physical Input: Add keypad or buttons for entering time directly.

Taking It to the Next Level - With JUSTWAY

Your breadboard prototype may work perfectly, but looks like a tech spaghetti bowl.

When you want to impress at a science fair, competition, or investor meeting presentation is everything.

That's where JUSTWAY comes in.

JUSTWAY helps you transform your DIY project into a professional grade prototype, complete with a custom enclosure with a custom enclosure, metal finish, or injection-molded body - ready for the world to see.

Why JUSTWAY Is the Perfect Partner

Rapid Prototyping: 24-hour turnaround, real time order tracking

CNC Machining: Aluminum 6061 or Stainless Steel 304 - strong, premium enclosures

Sheet Metal Fabrication: Laser-cut, CNC-bent, and powder-coated finishes

Injection Molding: Ideal for moving from prototype to mass production

Urethane Casting: Perfect for small batches or display models

3D Printing(SLA / HP-PA12): SLA resin for clear aesthetic display, HP-PA12 nylon for durable, matte finish

Pro Tip: Want your circuit to look futuristic?

  1. Use transparent SLA resin to show off your Arduino and LEDs
  2. Or go matte black HP-PA12 for a stealthy, modern vibe.

How To Order in 4 Easy Steps

Step 1: Upload You CAD Files at JUSTWAY.com

Step 2: Select Material & Finish - plastics, resins, or metals

Step 3: Preview Your Model in 3D - Check fit and look before confirming

Step 4: Place Your Order - Fast delivery, Transparent Pricing, Zero Hidden Costs


Future Enhancement

  1. Wi-Fi notification directly to your smartphone
  2. Book databases with names and return logs
  3. OLED display showing real-time status of all shelves
  4. RGB lights indicating which section is overdue
  5. Mobile App Integration for remote tracking

Conclusion

You just built something truly useful - and a little bit hilarious.

Your Smart Book Return Reminder uses Arduino UNO R4 WiFi's RTC, IR Sensors, and a relentless buzzer to keep your shelves organizes (and your mom happy).

It's practical, fun, and a great example of combining sensors, real-time logic, and creativity. And when you're ready to make it look as good as it works, remember...

JUSTWAY - Turning Your Ideas Into Real Products

From DIY to Display-Ready, JUSTWAY is your best ally in transforming prototypes into professional masterpieces.

"Return your books. Respect your shelves
Save your sanity - with Arduino and JUSTWAY.