rhandycan1

Simple Alarm Clock | RTC DS1302 + LCD + pi Pico

Oct 11th, 2025 (edited)
214
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.12 KB | Source Code | 0 0
  1. //pi pico version//
  2. //PROGRAM BY: RANDY N. CAÑADA
  3. /*SUBSCRIBE: FB PAGES:
  4. Playlist.ph:
  5. https://www.facebook.com/profile.php?id=61573177858722
  6.  
  7. Arduino:
  8. https://www.facebook.com/rhacan2?__tn__=R
  9.  
  10. YOUTUBE:
  11. PlaylistPH
  12. https://www.youtube.com/@PlaylistPh-h2v
  13.  
  14. ElectroCrafters:
  15. https://www.youtube.com/channel/UCu1aqzUdTqSnTN5BHNlbVgA */
  16.  
  17.  
  18. #include <LiquidCrystal_I2C.h>
  19. #include <ThreeWire.h>
  20. #include <RtcDS1302.h>
  21.  
  22. LiquidCrystal_I2C lcd(0x27, 16, 2);//sda=4,sda=5 //use your own LCD address. Use I2c scanner
  23. ThreeWire myWire(10, 11, 12); // DAT=GP10, CLK=GP11, RST=GP12
  24. RtcDS1302<ThreeWire> Rtc(myWire);
  25.  
  26. const int pinCLK = 14;
  27. const int pinDT  = 15;
  28. const int pinSW  = 16;
  29. const int pinBuzzer = 17;
  30.  
  31. enum TimeField {
  32.   NONE, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND,
  33.   ALARM_HOUR, ALARM_MINUTE, DONE
  34. };
  35. enum SetupMode { MODE_NONE, MODE_TIME, MODE_ALARM, MODE_MENU };
  36. SetupMode setupMode = MODE_NONE;
  37. TimeField currentField = NONE;
  38.  
  39. int year, month, day, hour, minute, second;
  40. int alarmHour = 6;
  41. int alarmMinute = 0;
  42. bool settingMode = false;
  43. bool alarmActive = false;
  44. bool buzzerOn = false;
  45. bool alarmDismissed = false;
  46.  
  47. unsigned long buzzerStartTime = 0;
  48. const unsigned long buzzerDuration = 10000;
  49.  
  50. int lastEncoded = 0;
  51. unsigned long lastRotateTime = 0;
  52. const unsigned long rotateDebounce = 170;
  53.  
  54. bool swHeld = false;
  55. bool longPressTriggered = false;
  56. unsigned long swPressStart = 0;
  57. const unsigned long longPressDuration = 1000;
  58.  
  59. int lastSecond = -1;
  60. TimeField lastField = NONE;
  61. int lastValue = -1;
  62.  
  63. int menuIndex = 0; // 0 = Set Time, 1 = Set Alarm
  64.  
  65. void setup() {
  66.   Wire.setSDA(4);
  67.   Wire.setSCL(5);
  68.   lcd.init();
  69.   lcd.backlight();
  70.   Rtc.Begin();
  71.  
  72.   // Load RTC values into vars
  73.   RtcDateTime now = Rtc.GetDateTime();
  74.   year   = now.Year();
  75.   month  = now.Month();
  76.   day    = now.Day();
  77.   hour   = now.Hour();
  78.   minute = now.Minute();
  79.   second = now.Second();
  80.  
  81.   pinMode(pinCLK, INPUT_PULLUP);
  82.   pinMode(pinDT, INPUT_PULLUP);
  83.   pinMode(pinSW, INPUT_PULLUP);
  84.   pinMode(pinBuzzer, OUTPUT);
  85.   digitalWrite(pinBuzzer, LOW);
  86.  
  87.   Serial.begin(9600);
  88. }
  89.  
  90. void loop() {
  91.   RtcDateTime now = Rtc.GetDateTime();
  92.  
  93.   // Reset alarmDismissed when minute changes
  94.   static int lastAlarmMinute = -1;
  95.   if (now.Minute() != lastAlarmMinute) {
  96.     alarmDismissed = false;
  97.     lastAlarmMinute = now.Minute();
  98.   }
  99.  
  100.   // Trigger alarm
  101.   if (alarmActive && !alarmDismissed && now.Hour() == alarmHour && now.Minute() == alarmMinute && !buzzerOn) {
  102.     buzzerOn = true;
  103.     buzzerStartTime = millis();
  104.     digitalWrite(pinBuzzer,HIGH);
  105.     lcd.clear();
  106.     lcd.setCursor(0, 0);
  107.     lcd.print("Alarm Ringing!");
  108.   }
  109.  
  110.   // Auto-silence
  111.   if (buzzerOn && millis() - buzzerStartTime > buzzerDuration) {
  112.     buzzerOn = false;
  113.     alarmDismissed = true;
  114.     digitalWrite(pinBuzzer, LOW);
  115.     lcd.clear();
  116.     lcd.setCursor(0, 0);
  117.     lcd.print("Alarm Stopped");
  118.   }
  119.  
  120.   // Manual silence
  121.   if (buzzerOn && digitalRead(pinSW) == LOW) {
  122.     buzzerOn = false;
  123.     alarmDismissed = true;
  124.     digitalWrite(pinBuzzer, LOW);
  125.     lcd.clear();
  126.     lcd.setCursor(0, 0);
  127.     lcd.print("Alarm Stopped");
  128.     delay(1000);
  129.   }
  130.  
  131.   // ---------------- Idle mode ----------------
  132.   if (!settingMode) {
  133.     if (now.Second() != lastSecond) {
  134.       lastSecond = now.Second();
  135.       char dateBuffer[11];
  136.       sprintf(dateBuffer, "%02d/%02d/%04d", now.Day(), now.Month(), now.Year());
  137.       char timeBuffer[9];
  138.       sprintf(timeBuffer, "%02d:%02d:%02d", now.Hour(), now.Minute(), now.Second());
  139.       lcd.setCursor(0, 0);
  140.       lcd.print("Date: "); lcd.print(dateBuffer); lcd.print("   ");
  141.       lcd.setCursor(0, 1);
  142.       lcd.print("Time: "); lcd.print(timeBuffer); lcd.print("   ");
  143.     }
  144.  
  145.     // Long press → enter menu
  146.     if (digitalRead(pinSW) == LOW) {
  147.       if (!swHeld) {
  148.         swPressStart = millis();
  149.         swHeld = true;
  150.         longPressTriggered = false;
  151.       } else if (!longPressTriggered && millis() - swPressStart > longPressDuration) {
  152.         settingMode = true;
  153.         longPressTriggered = true;
  154.         setupMode = MODE_MENU;
  155.         menuIndex = 0;
  156.         showMenu();
  157.         delay(500);
  158.       }
  159.     } else {
  160.       swHeld = false;
  161.     }
  162.     return;
  163.   }
  164.  
  165.   // ---------------- Menu ----------------
  166.   if (setupMode == MODE_MENU) {
  167.     handleMenu();
  168.   } else {
  169.     handleEditing();
  170.   }
  171. }
  172.  
  173. // ---------------- Menu Functions ----------------
  174. void showMenu() {
  175.   lcd.setCursor(0, 0);
  176.   lcd.print(menuIndex == 0 ? "> Set Time      " : "  Set Time     "); // add spaces to clear leftover
  177.   lcd.setCursor(0, 1);
  178.   lcd.print(menuIndex == 1 ? "> Set Alarm    " : "  Set Alarm    "); // add spaces
  179. }
  180.  
  181.  
  182. void handleMenu() {
  183.   // Rotary encoder
  184.   int MSB = digitalRead(pinCLK);
  185.   int LSB = digitalRead(pinDT);
  186.   int encoded = (MSB << 1) | LSB;
  187.   int sum = (lastEncoded << 2) | encoded;
  188.  
  189.   if (millis() - lastRotateTime > rotateDebounce) {
  190.     if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) {
  191.       menuIndex = (menuIndex + 1) % 2;
  192.       lastRotateTime = millis();
  193.     } else if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) {
  194.       menuIndex = (menuIndex == 0) ? 1 : 0;
  195.       lastRotateTime = millis();
  196.     }
  197.     showMenu();
  198.  
  199.   }
  200.   lastEncoded = encoded;
  201.  
  202.   // Short press → select
  203.   if (digitalRead(pinSW) == LOW && !swHeld && !longPressTriggered) {
  204.     swHeld = true;
  205.     if (menuIndex == 0) { // Set Time
  206.       setupMode = MODE_TIME;
  207.       currentField = YEAR;
  208.       lcd.clear();
  209.       lcd.setCursor(0, 0); lcd.print("Set Time");
  210.     } else { // Set Alarm
  211.       setupMode = MODE_ALARM;
  212.       currentField = ALARM_HOUR;
  213.       lcd.clear();
  214.       lcd.setCursor(0, 0); lcd.print("Set Alarm");
  215.     }
  216.     delay(500);
  217.     lastField = NONE; // force LCD update
  218.     lastValue = -1;
  219.   } else if (digitalRead(pinSW) == HIGH) {
  220.     swHeld = false;
  221.     longPressTriggered = false;
  222.   }
  223. }
  224.  
  225. // ---------------- Editing Functions ----------------
  226. void handleEditing() {
  227.   int MSB = digitalRead(pinCLK);
  228.   int LSB = digitalRead(pinDT);
  229.   int encoded = (MSB << 1) | LSB;
  230.   int sum = (lastEncoded << 2) | encoded;
  231.  
  232.   if (millis() - lastRotateTime > rotateDebounce) {
  233.     if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) { incrementField(currentField); lastRotateTime = millis();}
  234.     if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) { decrementField(currentField); lastRotateTime = millis();}
  235.   }
  236.   lastEncoded = encoded;
  237.  
  238.   // LCD update
  239.   int currentValue = getFieldValue(currentField);
  240.   if (currentField != lastField || currentValue != lastValue) {
  241.     lastField = currentField;
  242.     lastValue = currentValue;
  243.  
  244.     lcd.setCursor(0, 0);
  245.     if (currentField == DONE) {
  246.       lcd.print("Press to Save   ");
  247.       lcd.setCursor(0, 1);
  248.       lcd.print("                ");
  249.     } else {
  250.       lcd.print("Set "); lcd.print(fieldName(currentField)); lcd.print("        ");
  251.       lcd.setCursor(0, 1);
  252.       lcd.print("Value: "); lcd.print(paddedValue(currentField)); lcd.print("     ");
  253.     }
  254.   }
  255.  
  256.   // Short press confirm
  257.   if (digitalRead(pinSW) == LOW && !swHeld && !longPressTriggered) {
  258.     swHeld = true;
  259.  
  260.     if (setupMode == MODE_TIME) {
  261.       if (currentField == SECOND) currentField = DONE;
  262.       else currentField = static_cast<TimeField>(currentField + 1);
  263.     } else if (setupMode == MODE_ALARM) {
  264.       if (currentField == ALARM_MINUTE) currentField = DONE;
  265.       else currentField = static_cast<TimeField>(currentField + 1);
  266.     }
  267.  
  268.     lastField = NONE;
  269.  
  270.     if (currentField == DONE) {
  271.       if (setupMode == MODE_TIME) {
  272.         RtcDateTime newTime(year, month, day, hour, minute, second);
  273.         Rtc.SetDateTime(newTime);
  274.         lcd.clear(); lcd.print("Time Saved!");
  275.       } else if (setupMode == MODE_ALARM) {
  276.         alarmActive = true;
  277.         lcd.clear(); lcd.print("Alarm Saved!");
  278.       }
  279.       delay(1000);
  280.       setupMode = MODE_NONE;
  281.       settingMode = false;
  282.       currentField = NONE;
  283.       lastSecond = -1;
  284.     }
  285.     delay(300);
  286.   } else if (digitalRead(pinSW) == HIGH) {
  287.     swHeld = false; longPressTriggered = false;
  288.   }
  289. }
  290.  
  291. // ---------------- Utility Functions ----------------
  292. String fieldName(TimeField field) {
  293.   switch (field) {
  294.     case YEAR: return "Year"; case MONTH: return "Month"; case DAY: return "Day";
  295.     case HOUR: return "Hour"; case MINUTE: return "Minute"; case SECOND: return "Second";
  296.     case ALARM_HOUR: return "AlarmHr"; case ALARM_MINUTE: return "AlarmMin";
  297.     default: return "";
  298.   }
  299. }
  300.  
  301. String paddedValue(TimeField field) {
  302.   char buffer[5];
  303.   int value = getFieldValue(field);
  304.   if (field == YEAR) sprintf(buffer, "%04d", value);
  305.   else sprintf(buffer, "%02d", value);
  306.   return String(buffer);
  307. }
  308.  
  309. int getFieldValue(TimeField field) {
  310.   switch (field) {
  311.     case YEAR: return year; case MONTH: return month; case DAY: return day;
  312.     case HOUR: return hour; case MINUTE: return minute; case SECOND: return second;
  313.     case ALARM_HOUR: return alarmHour; case ALARM_MINUTE: return alarmMinute;
  314.     default: return 0;
  315.   }
  316. }
  317.  
  318. void incrementField(TimeField field) {
  319.   switch (field) {
  320.     case YEAR: year++; break;
  321.     case MONTH: month = (month % 12) + 1; break;
  322.     case DAY: day = (day % 31) + 1; break;
  323.     case HOUR: hour = (hour + 1) % 24; break;
  324.     case MINUTE: minute = (minute + 1) % 60; break;
  325.     case SECOND: second = (second + 1) % 60; break;
  326.     case ALARM_HOUR: alarmHour = (alarmHour + 1) % 24; break;
  327.     case ALARM_MINUTE: alarmMinute = (alarmMinute + 1) % 60; break;
  328.     default: break;
  329.   }
  330. }
  331.  
  332. void decrementField(TimeField field) {
  333.   switch (field) {
  334.     case YEAR: year = max(2000, year - 1); break;
  335.     case MONTH: month = (month == 1) ? 12 : month - 1; break;
  336.     case DAY: day = (day == 1) ? 31 : day - 1; break;
  337.     case HOUR: hour = (hour == 0) ? 23 : hour - 1; break;
  338.     case MINUTE: minute = (minute == 0) ? 59 : minute - 1; break;
  339.     case SECOND: second = (second == 0) ? 59 : second - 1; break;
  340.     case ALARM_HOUR: alarmHour = (alarmHour == 0) ? 23 : alarmHour - 1; break;
  341.     case ALARM_MINUTE: alarmMinute = (alarmMinute == 0) ? 59 : alarmMinute - 1; break;
  342.     default: break;
  343.   }
  344. }
  345.  
Advertisement
Add Comment
Please, Sign In to add comment