I've created a sunrise alarm clock that begins to brighten 20 minutes before the scheduled alarm time and then runs a bird chirping routine when the alarm time is reached. Ity uses an Arduino Uno R3 board; a DS3231 RTC board named "rtc"; a TM1637 LED panel called "display"; a 48 chip LED WS28125050 device called "ring" connected to pin 13; an LED called "ledAlarm" connected to pin 12; an LED called "ledPM" connected to pin 10; five ezbuttons, named "modeBtn" connected to pin 2, "hrBtn" connected to pin 3, 'minBtn" connected to pin 4, "alarmBtn" connectd to pin 5, and "lightBtn" connected to pin 6. There is also an active Piezeoelectric Buzzer named "buzzer" connected to pin 12. The rtc is configured for 12 hour display, and the ledPM light is lit at 40% brightness when the time is PM. The hardware is set up to be a sunrise alarm clock with the following functions:
-
The modeBtn button controls a variable named "setMode" which is initially set to 0. It's value can be 0, 1 or 2. Pressing the modeBtn increments the setMode value by one, and blinks the ledPM light setMode number of times; when setMode is 1, the clock is in time set mode; when setMode is 2 the clock is in alarm set mode; when setMode is 0 the clock is in normal mode. If setMode > 2 set it back to 0.
-
When setMode is 1 (time set mode), pressing the hrBtn increments the time by one hour, using a 12 hour clock. Pressing the minBtn increments the time by one minute, and if it passes 59 it reverts to 0. If the new value is past noon, turn on the ledPM at 40% brightness; if it's past midnight, turn ledPM off.
-
When setMode is 2 (alarm set mode), pressing the hrBtn increments the rtc alarm1 by one hour, using a 12 hour clock. Pressing the minBtn increments the rtc alarm1 time by one minute, and if it passes 59 it reverts to 0. If the new alarm1 time is past noon, turn on the ledPM at 40% brightness; if it's past midnight, turn ledPM off.
-
The alarmBtn controls a boolean variable named "alarmOn" that is initially set to false. When the alarmBtn is pressed and alarmOn is false the value of alarmOn is set to true and the ledAlarm is illuminated at 40% brightness. If alarmOn is true, pressing the alarmBtn will turn off the ledAlarm light and set alarmOn to false. It must also terminate the alarm routine if it is running.
-
The lightBtn allows the LED ring to function as a lamp. It controls a value named "lightLev" that is initially set to 0. Pressing the lightBtn increments lightLev by 1, but if lightLev is > 3 it is set back to 0. When lightLev is 1 the LED ring chips 40 to 47 are illuminated; if lightLev is 2 the LED ring chips 23 to 47 are illuminated; if lightLev is 3, all 48 led ring chips are lit. When lightLev is 0, no ring chips are lit. The ring should display a warm light at 60% brightness.
-
If alarmOn is true, 20 minutes before the rtc alarm1 time the sunrise routine begins. Over 20 minutes the led ring is gradually illuminated up to 60 percent of full power, changing over that time from dark orange to light yellow in color one led chip at a time. At any time during the execution of this routine, if the alarmBtn button is pressed, the routine terminates and the ring is turned off. Otherwise, when the routine finishes the ring stays on until pressing the alarmBtn turns it off.
-
When alarmOn is true, upon reaching the rtc alarm1 time, implement a bird chirping routine on the buzzer that will operate continuously until the alarmBtn is pressed to turn the alarm off.
I've written the following code for it:
#include <Wire.h>
#include <RTClib.h>
#include <TM1637Display.h>
#include <Adafruit_NeoPixel.h>
#include <ezButton.h>
ezButton modeBtn(2); // create ezButton object attached to pin 2; modeBtn changes the setMode value, which determines whether the time or alarm time is set with the hrBtn and minBtn buttons.
ezButton hrBtn(3); // create ezButton object attached to pin 3.
ezButton minBtn(4); // create ezButton object attached to pin 4.
ezButton alarmBtn(5); // create ezButton object attached to pin 5.
ezButton lightBtn(6); // create ezButton object attached to pin 6.
#define LED_PIN 13
#define LED_COUNT 48
Adafruit_NeoPixel ring(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
RTC_DS3231 rtc;
#define CLK 9
#define DIO 8
TM1637Display display(CLK, DIO);
const uint8_t blank[] = {0x00, 0x00, 0x00,0x00};
DateTime myNow;
DateTime myTime;
// These are output pins for single LED lights and the buzzer.
int buzzer = 12;
int ledAlarm = 11;
int ledPM = 10;
int count = 10;
int count2 = 5;
int i, t, h, m, a;
int red[20] = {128,134,140,147,153,159,166,172,178,185,191,197,204,210,216,223,229,235,242,248};
int green[20] = {64,73,83,92,102,111,121,130,140,149,159,169,178,188,197,207,216,226,235,245};
int blue[20] = {0,11,22,33,44,55,66,77,88,99,110,121,132,143,154,165,176,187,198,209};
int bright = 5; // This variable controls the brightness increments for the sunrise, and can be a value from 1 to 12.
int ringLeds[] = {48,40,24,0};
int alarm_time, sunrise_time;
int setMode = 0;
int lightLev = 0;
bool alarmOn = false;
//-------------------------------------------------------------------------------
void setup() {
Serial.begin(9600); // Begin serial communication at a baud rate of 9600:
modeBtn.setDebounceTime(50); // set debounce time to 50 milliseconds
hrBtn.setDebounceTime(50); // set debounce time to 50 milliseconds
minBtn.setDebounceTime(50); // set debounce time to 50 milliseconds
alarmBtn.setDebounceTime(50); // set debounce time to 50 milliseconds
lightBtn.setDebounceTime(50); // set debounce time to 50 milliseconds
delay(3000); // Wait for console opening:
if (! rtc.begin()) { // Check if RTC is connected correctly:
Serial.println("Couldn't find RTC");
while (1);
}
/*
// Set the clock once, then comment this block out, as the rtc chip has a battery...
if (rtc.lostPower()) { // Check if the RTC lost power and if so, set the time:
Serial.println("RTC lost power, lets set the time!");
// The following line sets the RTC to the date & time this sketch was compiled:
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
rtc.setAlarm1(DateTime(2025, 1, 1, 5, 15, 0), DS3231_A1_Hour);
rtc.setAlarm2(DateTime(2025, 1, 1, 4, 55, 0), DS3231_A2_Hour);
}
*/
Wire.begin();
pinMode(buzzer, OUTPUT);
pinMode(ledAlarm, OUTPUT);
pinMode(ledPM, OUTPUT);
myNow = rtc.now();
ring.begin();
ring.setBrightness(0);
ring.show();
lightLev = 0;
alarmOn = false;
display.setBrightness(2);
display.showNumberDecEx(1234, 0b01000000, true);
delay(1000);
}
//-------------------------------------------------------------------------------
void loop() {
// These .loop functions reset the buttons; they don't actually loop
modeBtn.loop(); // MUST call the loop() function first
hrBtn.loop(); // MUST call the loop() function first
minBtn.loop(); // MUST call the loop() function first
alarmBtn.loop(); // MUST call the loop() function first
lightBtn.loop(); // MUST call the loop() function first
showTime();
if(alarmOn==true) { isAlarmTime(); }
// ----------------------------------------------------------------------------
// Handle toggling the alarm and alarm LED on/off.
if (alarmBtn.isPressed()) {
if(alarmOn==false) { // if button is pressed and boolean is false
analogWrite(ledAlarm, 64); // turn LED on
alarmOn= true; // set boolean to true
} else if (alarmOn==true) { // if button is pressed and boolean is true
digitalWrite(ledAlarm, LOW); // turn LED off
alarmOn = false; // set boolean to false
}
}
// ----------------------------------------------------------------------------
// Set the default time and alarm.
if (hrBtn.isPressed() && minBtn.isPressed()) {
setDefaultAlarmTime();
showAlarmTime();
}
// The main light has three levels -- the three rings -- activated by pressing the lightBtn sequentially...
if(lightBtn.isPressed()){
ring.clear();
lightLev++;
if(lightLev > 3) { lightLev = 0; }
for(int i = ringLeds[lightLev]; i < ring.numPixels(); i++){ ring.setPixelColor(i, 64, 64, 32); }
ring.setBrightness(128);
ring.show();
}
// ----------------------------------------------------------------------------
// Change the setMode value if the button is pressed...
if(modeBtn.isPressed()){
analogWrite(ledPM,0);
setMode++;
for (int i = 1; i <= setMode; i++) {
delay(300);
analogWrite(ledPM,64);
delay(300);
analogWrite(ledPM,0);
}
if(setMode > 2) { setMode = 0; }
}
// ----------------------------------------------------------------------------
// Handle the Hour incrementing for time and alarm...
if(setMode>0) {
if (hrBtn.isPressed()) {
// setMode 1 is to set the current time...
if(setMode==1) {
myNow = rtc.now();
DateTime plusOne (myNow.unixtime() + 3600); // add one hour
myNow = rtc.now();
rtc.adjust(plusOne); // set to new time
} else {
// Adjust the alarm time
DateTime alarm = rtc.getAlarm1();
DateTime alarm1 (alarm + TimeSpan(0,1,0,0));
rtc.setAlarm1(alarm1, DS3231_A1_Hour);
DateTime alarm2 (alarm1 - TimeSpan(0,0,20,0));
rtc.setAlarm2(alarm2, DS3231_A2_Hour);
alarm_time = (alarm1.hour()*100)+alarm1.minute();
sunrise_time = alarm_time - 20;
}
showTime();
delay(300);
}
// ----------------------------------------------------------------------------
// Handle the Minute incrementing for time and alarm...
if (minBtn.isPressed()) {
// setMode 1 is to set the current time...
if(setMode==1) {
myNow = rtc.now();
DateTime plusOne (myNow.unixtime() + 60); // add one minute
myNow = rtc.now();
rtc.adjust(plusOne); // set to new time
} else {
// Adjust the alarm time
DateTime alarm = rtc.getAlarm1();
DateTime alarm1 (alarm + TimeSpan(0,0,1,0));
rtc.setAlarm1(alarm1, DS3231_A1_Hour);
DateTime alarm2 (alarm1 - TimeSpan(0,0,20,0));
rtc.setAlarm2(alarm2, DS3231_A2_Hour);
alarm_time = (alarm1.hour()*100)+alarm1.minute();
sunrise_time = alarm_time - 20;
}
showTime();
delay(300);
}
}
}
//-------------------------------------------------------------------------------
void setDefaultAlarmTime() {
rtc.disableAlarm(1);
rtc.disableAlarm(2);
rtc.clearAlarm(1);
rtc.clearAlarm(2);
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
rtc.setAlarm1(DateTime(2025, 1, 5, 5, 15, 0), DS3231_A1_Hour);
rtc.setAlarm2(DateTime(2025, 1, 5, 4, 55, 0), DS3231_A2_Hour);
}
//-------------------------------------------------------------------------------
void checkAlarmStatus() {
alarmBtn.loop(); // MUST call the loop() function first
if (alarmBtn.isPressed()) { // if button is pressed and boolean is false
if(alarmOn==false) {
analogWrite(ledAlarm, 64); // turn LED on
alarmOn= true; // set boolean to true
} else if (alarmOn==true) { // if button is pressed and boolean is true
digitalWrite(ledAlarm, LOW); // turn LED off
alarmOn = false; // set boolean to false
}
}
}
//-------------------------------------------------------------------------------
void isAlarmTime() {
//if (rtc.alarmFired(1) == true){
myNow = rtc.now();
t =(myNow.hour()* 100 )+ myNow.minute();
if(t == sunrise_time && myNow.second()==0){
sunrise();
chirp();
}
}
//-------------------------------------------------------------------------------
void showAlarmTime() {
myNow = rtc.getAlarm1();
t =(myNow.hour()* 100 )+ myNow.minute();
// Serial.println(t);
display.showNumberDec(t, false, 4, 0);
analogWrite(ledPM,0);
delay(500);
for (int i = 1; i <= 3; i++) {
delay(500);
analogWrite(ledPM,64);
delay(500);
analogWrite(ledPM,0);
}
}
//-------------------------------------------------------------------------------
void showTime() {
myNow = rtc.now();
a = myNow.second()%2;
if (setMode == 2) { myNow = rtc.getAlarm1(); } // If we're in alarm set mode, change the display time to the alarm time...
t =(myNow.hour()* 100 )+ myNow.minute();
if(t > 1200) {
analogWrite(ledPM,32);
if (t >= 1300) { t = t - 1200; }
} else {
analogWrite(ledPM,0);
}
// Serial.println(t);
if(a==0){display.showNumberDec(t, false, 4, 0);}
else{display.showNumberDecEx(t, 0b11100000, false, 4, 0);}
}
//-------------------------------------------------------------------------------
void alarmOff(){
alarmOn = false;
analogWrite(ledAlarm,0);
ring.clear();
ring.setBrightness(0);
ring.show();
digitalWrite(buzzer,LOW);
}
//-------------------------------------------------------------------------------
void sunrise() {
if(alarmOn==true) {
ring.clear();
lightLev = 0;
for (int j = 0; j <20; j++){
ring.setBrightness((bright+1)*j);
for(int i = 0; i < ring.numPixels(); i++){
ring.setPixelColor(i, red[j], green[j], blue[j]);
ring.show();
showTime();
checkAlarmStatus();
if(alarmOn==false) {return;}
delay(1250); // To make this loop last 20 minutes.
// delay(125); // For testing purposes.
}
checkAlarmStatus();
if(alarmOn==false) {return;}
}
}
}
//-------------------------------------------------------------------------------
void chirp() {
while (alarmOn==true) {
showTime();
while(count2 >0){
for(int i = 300; i>0; i--){
digitalWrite(buzzer,HIGH);
delayMicroseconds(i);
digitalWrite(buzzer,LOW);
delayMicroseconds(i);
}
delay(100);
count2 = count2 -1;
}
count2 = 5;
while(count >0){
for(int i = 0; i <300; i++){
digitalWrite(buzzer,HIGH);
delayMicroseconds(i);
digitalWrite(buzzer,LOW);
delayMicroseconds(i);
}
count = count -1;
}
count = 10;
for(int i2 = 300; i2>0; i2--){
digitalWrite(buzzer,HIGH);
delayMicroseconds(i2);
digitalWrite(buzzer,LOW);
delayMicroseconds(i2);
}
delay(400);
while(count2 >0){
for(int i = 300; i>0; i--){
digitalWrite(buzzer,HIGH);
delayMicroseconds(i);
digitalWrite(buzzer,LOW);
delayMicroseconds(i);
}
delay(400);
count2 = count2 -1;
}
count2 = 5;
while(count >0){
for(int i = 0; i <300; i++){
digitalWrite(buzzer,HIGH);
delayMicroseconds(i);
digitalWrite(buzzer,LOW);
delayMicroseconds(i);
}
delay(100);
count = count -1;
}
count = 10;
for(int i2 = 300; i2>0; i2--){
digitalWrite(buzzer,HIGH);
delayMicroseconds(i2);
digitalWrite(buzzer,LOW);
delayMicroseconds(i2);
}
delay(random(5000,15000));
checkAlarmStatus();
} // end while loop...
}
I want to allow the alarmBtn to turn off the alarm at any point in the alarm / chirp functions. But the button doesn't seem to let that happen. How would I use the alarmBtn ezButton to do this?
Thanks! -Steve

