Smart Book Return Reminder With Arduino UNO R4 Wi-Fi

by rohanbarnwal in Circuits > Arduino

120 Views, 1 Favorites, 0 Comments

Smart Book Return Reminder With Arduino UNO R4 Wi-Fi

20251016_0853_Smart Workspace Alert_simple_compose_01k7ngm65rfvms841nykwzwhq2.png
Untitled design (13).png

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

IMG_7061.jpg

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.

Wiring the Circuit

connect RFID.jpg
  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).

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.

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

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.

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.

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

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.

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.

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.

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

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


Optional Improvements

11.png
22.png
44.png
download (10) - Copy.png
222.png
  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.