Scheduled Appliance Controller
HARDWARE REQUIRED:
- PICUNO Microcontroller board
- 1 × 16x2 I2C LCD Display
- 1 × DS1302 Real Time Clock Module
- 1 × 5V Relay Module
- 1 × LED
- 1 × 220Ω resistor (Current-limiting resistor)
- Jumper wires
- USB cable
- 1 × 4xAA Battery pack (with fresh or rechargeable batteries)
DESCRIPTION:
This project creates a simple but effective smart timer, like those used for home appliances. The system uses a DS1302 Real-Time Clock (RTC) module to keep accurate track of the current time.
A user can define a specific "ON" time and "OFF" time directly in the code. The PicUNO continuously checks the time from the RTC and compares it to the schedule. When the current time matches the "ON" time, it activates a relay module. When the "OFF" time is reached, it deactivates the relay. The relay's switched terminals can control an external circuit, represented here by an LED. An I2C LCD provides a constant, real-time display of the current time and the relay's status (ON/OFF).
LIBRARIES REQUIRED:
For C / Arduino IDE:
Ds1302 by Rafa Couto: The driver library for the DS1302 RTC Module.
Wire.h: Manages I2C communication (usually included by default).
LiquidCrystal_I2C.h: The driver library for the I2C LCD module.
For Micropython / Thonny IDE:
ds1302.py: The custom library file for the RTC, saved to the PICUNO board.
i2c_lcd.py: The custom library file saved to the PICUNO board.
The code also uses the built-in machine and time modules.
A user can define a specific "ON" time and "OFF" time directly in the code. The PicUNO continuously checks the time from the RTC and compares it to the schedule. When the current time matches the "ON" time, it activates a relay module. When the "OFF" time is reached, it deactivates the relay. The relay's switched terminals can control an external circuit, represented here by an LED. An I2C LCD provides a constant, real-time display of the current time and the relay's status (ON/OFF).
LIBRARIES REQUIRED:
For C / Arduino IDE:
Ds1302 by Rafa Couto: The driver library for the DS1302 RTC Module.
Wire.h: Manages I2C communication (usually included by default).
LiquidCrystal_I2C.h: The driver library for the I2C LCD module.
For Micropython / Thonny IDE:
ds1302.py: The custom library file for the RTC, saved to the PICUNO board.
i2c_lcd.py: The custom library file saved to the PICUNO board.
The code also uses the built-in machine and time modules.
CIRCUIT DIAGRAM:
- Connect the LCD Module's GND pin to a GND pin on board.
- Connect the LCD Module's VCC pin to positive terminal (+) of the 4xAA Battery Pack.
- Connect the LCD Module's SDA pin GPIO 4 (SDA Pin on PICUNO).
- Connect the LCD Module's SCL pin GPIO 5 (SCL Pin on PICUNO).
- Connect the negative terminal (-) of the 4xAA Battery Pack to GND pin on PICUNO board to create common ground.
- Connect the VCC pin to 5V pin on the board.
- Connect the GND pin to GND pin on board.
- Connect the CLK pin to GPIO 8.
- Connect the DAT pin to GPIO 9.
- Connect the RST pin to GPIO 10.
- Connect the GND pin to a GND pin on PICUNO.
- Connect the VCC pin to the 5V pin on PICUNO.
- Connect the IN (Signal) pin to GPIO 7.
- Connect the COM (Common) Screw terminal on the relay to 3.3V.
- Connect the NO (Normally Open) Screw terminal on the relay to longer leg (anode) of the LED.
- Connect the shorter leg (cathode) of the LED to one end of 220Ω resistor.
- Connect the other end of the resistor to GND.
SCHEMATIC:
LCD VCC → 5V
LCD GND → GND
LCD SDA → GPIO 4 (Board SDA Pin)
LCD SCL → GPIO 5 (Board SCL Pin)
RTC VCC → 5V
RTC GND → GND
RTC CLK → GPIO 8
RTC DAT → GPIO 9
RTC RST → GPIO 10
Relay VCC → 5V
Relay GND → GND
Relay IN → GPIO 7
Relay COM → 3.3V
Relay NO → anode of LED
Cathode of LED → 220Ω resistor → GND
CODE -- C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <Ds1302.h> LiquidCrystal_I2C lcd(0x27, 16, 2); // Define pins for the DS1302 module const int RST_PIN = 10; const int DAT_PIN = 9; const int CLK_PIN = 8; Ds1302 rtc(RST_PIN, CLK_PIN, DAT_PIN); const int RELAY_PIN = 7; // --- Schedule Configuration --- const int ON_HOUR = 7, ON_MINUTE = 0, ON_SECOND = 0; const int OFF_HOUR = 7, OFF_MINUTE = 5, OFF_SECOND = 0; void setup() { // Initialize components lcd.init(); lcd.backlight(); rtc.init(); // Initialize the RTC pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, LOW); // Start with relay off if (rtc.isHalted()) { // Create a DateTime object with the current time Ds1302::DateTime dt = { .year = 25, // Year (00-99) -> 2025 .month = 8, // Month .day = 16, // Day .hour = 15, // Hour (24-hour format) .minute = 27, // Minute .second = 48, // Second .dow = 7 // Day of week (1=Sun, 2=Mon... 7=Sat) }; // Set the time on the RTC module rtc.setDateTime(&dt); } lcd.print("Scheduler Ready"); delay(2000); } void loop() { // 1. Get the current time from the RTC Ds1302::DateTime now; rtc.getDateTime(&now); // 2. Check against the schedule if (now.hour == ON_HOUR && now.minute == ON_MINUTE && now.second == ON_SECOND) { if (digitalRead(RELAY_PIN) == LOW) { // Check if not already on digitalWrite(RELAY_PIN, HIGH); } } if (now.hour == OFF_HOUR && now.minute == OFF_MINUTE && now.second == OFF_SECOND) { if (digitalRead(RELAY_PIN) == HIGH) { // Check if not already off digitalWrite(RELAY_PIN, LOW); } } // 3. Display status on the LCD lcd.setCursor(0, 0); char timeString[9]; sprintf(timeString, "%02d:%02d:%02d", now.hour, now.minute, now.second); lcd.print(timeString); lcd.setCursor(0, 1); lcd.print("Relay Status: "); lcd.print(digitalRead(RELAY_PIN) == HIGH ? "ON " : "OFF"); delay(500); }
Ds1302 rtc(RST_PIN, CLK_PIN, DAT_PIN); - This creates the RTC object. Note the specific pin order required by this library: Reset, Clock, Data.
if (rtc.isHalted()) - The code to set the time will only run if the RTC chip has lost all power and its internal clock has stopped. This prevents the time from being reset every time you power on the PicUNO.
rtc.setDateTime(&dt); - This is the command used to send the DateTime structure to the RTC module to set its time.
rtc.getDateTime(&now); - In the main loop, this command fetches the current time from the RTC and stores it in the now variable. You can then access the parts of the time with now.hour, now.minute, etc.
if (rtc.isHalted()) - The code to set the time will only run if the RTC chip has lost all power and its internal clock has stopped. This prevents the time from being reset every time you power on the PicUNO.
rtc.setDateTime(&dt); - This is the command used to send the DateTime structure to the RTC module to set its time.
rtc.getDateTime(&now); - In the main loop, this command fetches the current time from the RTC and stores it in the now variable. You can then access the parts of the time with now.hour, now.minute, etc.
CODE -- PYTHON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 from machine import Pin, I2C import time from i2c_lcd import I2cLcd from ds1302 import DS1302 # I2C LCD Display i2c = I2C(0, scl=Pin(5), sda=Pin(4)) lcd = I2cLcd(i2c, 0x27, 2, 16) # DS1302 RTC Module rtc = DS1302(Pin(8), Pin(9), Pin(10)) relay = Pin(7, Pin.OUT) ON_TIME = (7, 0, 0) # 7:00:00 AM OFF_TIME = (7, 5, 0) # 7:05:00 AM #Format: (Year, Month, Day, Weekday(1=Mon), Hour, Minute, Second) #rtc.date_time((2025, 8, 16, 6, 12, 35, 0)) # --- Main Program --- print("Scheduled Appliance Controller Initialized.") relay.low() lcd.clear() lcd.putstr("Scheduler Ready") time.sleep(2) while True: (year, month, day, weekday, hour, minute, second) = rtc.date_time() current_time_tuple = (hour, minute, second) # 2. Check against the schedule if current_time_tuple == ON_TIME: if relay.value() == 0: # Check if it's not already on print(f"ON Time Reached! Activating relay at {hour:02d}:{minute:02d}:{second:02d}") relay.high() if current_time_tuple == OFF_TIME: if relay.value() == 1: # Check if it's not already off print(f"OFF Time Reached! Deactivating relay at {hour:02d}:{minute:02d}:{second:02d}") relay.low() # 3. Display status on the LCD # Format the time and date strings with leading zeros time_string = f"{hour:02d}:{minute:02d}:{second:02d}" relay_status = "ON " if relay.value() == 1 else "OFF" # Display on LCD lcd.move_to(0, 0) lcd.putstr(time_string) lcd.move_to(0, 1) lcd.putstr(f"Relay Status: {relay_status}") time.sleep(0.5)
rtc.date_time((...)) - This is the one-time command used to set the clock. You should uncomment it, fill in the current time, run the script once to set the RTC, and then comment it out again.
if current_time_tuple == ON_TIME: - This is the core logic of the scheduler. It directly compares the current time with the target "ON" time. If they match exactly, it activates the relay.
f"{hour:02d}" - This is a Python f-string used for formatting. The :02d part tells it to format the number as a 2-digit decimal, adding a leading zero if necessary (e.g., 7 becomes 07).
if current_time_tuple == ON_TIME: - This is the core logic of the scheduler. It directly compares the current time with the target "ON" time. If they match exactly, it activates the relay.
f"{hour:02d}" - This is a Python f-string used for formatting. The :02d part tells it to format the number as a 2-digit decimal, adding a leading zero if necessary (e.g., 7 becomes 07).