Toggle button press on and off to set alarm time and alarm on and off

Hi,

I have managed to code an alarm clock as attached below, however, I’m currently using three switches and two buttons. The three switches are for setting the alarm time, turning the alarm on and off and toggling the lcd backlight on and off.

However, I would like to change all the switches to buttons, and when I’m in the idle state, one button press to go to ‘alarm clock hours case’ and then I would press it again to go back to the idle state, instead of holding it down. The same with setting the alarm on and off, and lcd backlight on and off.

I have looked at lots of code online to do with toggling and de-bouncing but nothing seems to be working. Any help will be appreciated!

Thanks,

Steven

#include <DS3231.h>
#include <Wire.h>

#include <LiquidCrystal.h>
const int RS = 6, EN = 7, DB4 = 8, DB5 = 9, DB6 = 10, DB7 = 11;
LiquidCrystal lcd(6, 7, 8, 9, 10, 11);

DS3231 rtc(SDA, SCL);    // Initiate the DS3231 using the hardware interface

Time t;

const int lcdBacklight = A0;

const int backlightButton = 5;
const int alarmSetupButton = 3;
const int upButton = 1;
const int nextButton = 2;
const int alarmClockButton = 4;

const int speaker = 7;

int Hor, Min, Sec, tim, dat, h, m, s;

enum states {idle, alarmClockHoursCase, alarmClockMinutesCase, alarmBuzzCase};


/*---------------------------------------- Setup ---------------------------------------------------------------*/

void setup()
{
  Serial.begin(115200);

  Wire.begin();

  lcd.begin(16, 4);

  pinMode(backlightButton, INPUT_PULLUP);
  pinMode(alarmSetupButton, INPUT_PULLUP);
  pinMode(alarmClockButton, INPUT_PULLUP);
  pinMode(nextButton, INPUT_PULLUP);
  pinMode(upButton, INPUT_PULLUP);

  pinMode(speaker, OUTPUT);

  pinMode(lcdBacklight, OUTPUT);

  rtc.begin();
  //rtc.setTime(10, 25, 20);     // Set the time (24hr format)
  //rtc.setDate(05, 04, 2019);   // Set the date

}

/*---------------------------------------- Loop ----------------------------------------------------------------*/

void loop()
{
  if (digitalRead(backlightButton) == LOW)
  {
    digitalWrite(lcdBacklight, HIGH);
  }
  else
  {
    digitalWrite(lcdBacklight, LOW);
  }
  
  /*----- Case statements --------------------------------------*/

  static int state = idle;

  switch (state)
  {
    /*----- idle case --------------------------------*/

    case idle:

      if (digitalRead(alarmSetupButton) == HIGH)
      {
        homeDisplay();
      }

      if (digitalRead(alarmSetupButton) == LOW)
      {
        lcd.clear();
        state = alarmClockHoursCase;
      }

      if (Hor == h && Min == m && digitalRead(alarmClockButton) == LOW)  // If current time and alarm time match, alarm buzz!
      {
        lcd.clear();
        state = alarmBuzzCase;
      }
      break;

    /*------- alarmClockHoursCase case --------------------------------*/

    case alarmClockHoursCase:

      alarmClockHoursSetup();

      if (digitalRead(nextButton) == LOW)
      {
        lcd.clear();
        state = alarmClockMinutesCase;
      }

      if (digitalRead(alarmSetupButton) == HIGH)
      {
        lcd.clear();
        state = idle;
      }
      break;

    /*------- alarmClockMinutesCase case --------------------------------*/

    case alarmClockMinutesCase:

      alarmClockMinutesSetup();

      if (digitalRead(alarmSetupButton) == HIGH)
      {
        lcd.clear();
        state = idle;
      }
      break;

    /*------- alarmBuzz case -------------------------------------------*/

    case alarmBuzzCase:
      if (digitalRead(alarmClockButton) == LOW)
      {
        alarmBuzz();
      }
      if (digitalRead(alarmClockButton) == HIGH)
      {
        noTone(speaker);
        lcd.clear();
        state = idle;
      }

      break;

    default:
      state = idle;
      break;
  }
}

/*---------------------------------------- Home Display Method -------------------------------------------------*/

void homeDisplay()
{
  t = rtc.getTime();
  Hor = t.hour;
  Min = t.min;
  Sec = t.sec;
  tim = rtc.getTimeStr();
  dat = rtc.getDateStr();

  lcd.setCursor(0, 0);
  lcd.print("Time :");
  lcd.setCursor(6, 0);
  lcd.print(rtc.getTimeStr());    // Time

  lcd.setCursor(0, 1);
  lcd.print("Date :");
  lcd.setCursor(6, 1);
  lcd.print(rtc.getDateStr());    // Date

  lcd.setCursor(0, 2);
  lcd.print("Alarm:");

  if (digitalRead(alarmClockButton) == LOW)
  {
    lcd.setCursor(6, 2);
    lcd.print("   ");
    lcd.setCursor(8, 2);
    lcd.print(":");

    if (h <= 9)
    {
      lcd.setCursor(6, 2);
      lcd.print("0");
      lcd.setCursor(7, 2);
      lcd.print(h);
    }
    if (h > 9)
    {
      lcd.setCursor(6, 2);
      lcd.print(h);
    }

    if (m <= 9)
    {
      lcd.setCursor(9, 2);
      lcd.print("0");
      lcd.setCursor(10, 2);
      lcd.print(m);
    }
    if (m > 9)
    {
      lcd.setCursor(9, 2);
      lcd.print(m);
    }
  }
  else
  {
    lcd.setCursor(6, 2);
    lcd.print("     ");
    lcd.setCursor(6, 2);
    lcd.print("OFF");
  }

  lcd.setCursor(0, 3);
  lcd.print("Temp :");
  lcd.setCursor(6, 3);
  lcd.print(rtc.getTemp());       // Temperature
  lcd.setCursor(11,3);
  lcd.print((char)223);           // Degree symbol;
  lcd.setCursor(12, 3);
  lcd.print("C");
}

/*------------------------------------- alarmClockHoursSetup Method --------------------------------------------*/

void alarmClockHoursSetup()
{
  if (digitalRead(upButton) == LOW)
  {
    if (h >= 23)
    {
      h = 0;
      lcd.setCursor(5, 1);
      lcd.print("  ");
    }
    else
    {
      h = h + 1;
      delay(150);
    }
  }

  if (h <= 9)
  {
    lcd.setCursor(5, 1);
    lcd.print("0");
    lcd.setCursor(6, 1);
    lcd.print(h);
  }
  if (h > 9)
  {
    lcd.setCursor(5, 1);
    lcd.print(h);
  }

  lcd.setCursor(0, 0);
  lcd.print("Alarm Hours");
  lcd.setCursor(7, 1);
  lcd.print(":");

  if (m <= 9)
  {
    lcd.setCursor(8, 1);
    lcd.print("0");
    lcd.setCursor(9, 1);
    lcd.print(m);
  }
  if (m > 9)
  {
    lcd.setCursor(8, 1);
    lcd.print(m);
  }
}

/*------------------------------------- alarmClockMinutesSetup Method --------------------------------------------*/

void alarmClockMinutesSetup()
{
  if (digitalRead(upButton) == LOW)
  {
    if (m >= 59)
    {
      m = 0;
      lcd.setCursor(8, 1);
      lcd.print("  ");
    }
    else
    {
      m = m + 1;
      delay(150);
    }
  }
  if (h <= 9)
  {
    lcd.setCursor(5, 1);
    lcd.print("0");
    lcd.setCursor(6, 1);
    lcd.print(h);
  }
  if (h > 9)
  {
    lcd.setCursor(5, 1);
    lcd.print(h);
  }

  lcd.setCursor(0, 0);
  lcd.print("Alarm Minutes");
  lcd.setCursor(7, 1);
  lcd.print(":");

  if (m <= 9)
  {
    lcd.setCursor(8, 1);
    lcd.print("0");
    lcd.setCursor(9, 1);
    lcd.print(m);
  }
  if (m > 9)
  {
    lcd.setCursor(8, 1);
    lcd.print(m);
  }
}

/*---------------------------------------- alarmBuzz Method ----------------------------------------------------*/

void alarmBuzz()
{
  lcd.setCursor(0, 0);
  lcd.print("Alarm!");
  lcd.setCursor(0, 1);
  lcd.print("Turn off alarm!");

  tone(speaker, 1000);
}

I have looked at lots of code online to do with toggling

Did you look at the StateChangeDetection example in the IDE ?

UKHeliBob:
Did you look at the StateChangeDetection example in the IDE ?

Yes I've looked at that, but that only changed it for the split second that the button was being pressed or not pressed, and didn't latch it into the next state until it pressed again.

I've looked at that, but that only changed it for the split second that the button was being pressed or not pressed, and didn't latch it into the next state until it pressed again.

Then either you did something wrong or the switch contacts are bouncing.

UKHeliBob:
Then either you did something wrong or the switch contacts are bouncing.

Fair enough haha, I'll have another crack at it! And I'll make sure to add a delay to stop the mechanical bouncing.

If anyone happens to read this post, with help from a friend I’ve managed to get it working how I wanted.

Instead of using a case statments, we changed it to if statements (because that’s basically all the case statment was) and used do while loops along with boolean true or false variables to lock it into place when a button was pressed.

I’m very happy it’s working! Still got a bit of editing to do but at least the button presses are latching now.

#include <SoftwareSerial.h>
SoftwareSerial port(12, 13);

#include <EasyVR.h>

#pragma once // Stops multiple opening of same .h files

EasyVR easyvr(port);

#include <DS3231.h>
#include <Wire.h>

#include <LiquidCrystal.h>
const int RS = 6, EN = 7, DB4 = 8, DB5 = 9, DB6 = 10, DB7 = 11;
LiquidCrystal lcd(6, 7, 8, 9, 10, 11);

DS3231 rtc(SDA, SCL);

Time t;

const int lcdBacklight = A0;
const int backlightButton = 5;
const int alarmSetupButton = 3;
const int downButton = 1;
const int upButton = 0;
const int nextButton = 2;
const int alarmClockButton = 4;

boolean alarmSetupButtonPressed = false;
boolean minutesNextButtonPressed = false;
boolean alarm = false;

int Hor, Min, Sec, tim, dat, h, m, s;

enum states {idle, alarmClockHoursCase, alarmClockMinutesCase, alarmBuzzCase};

//Groups and Commands
enum Groups {
  GROUP_1  = 1,
  GROUP_2  = 2,
  GROUP_3  = 3,
  GROUP_4  = 4,
  GROUP_5  = 5,
  GROUP_6  = 6,
  GROUP_7  = 7,
  GROUP_8  = 8,
  GROUP_9  = 9,
  GROUP_10 = 10,
  GROUP_11 = 11,
};

enum Group1 {
  G1_1 = 0,
  G1_2 = 1,
  G1_3 = 2,
  G1_4 = 3,
  G1_5 = 4,
  G1_6 = 5,
  G1_7 = 6,
  G1_8 = 7,
  G1_9 = 8,
  G1_10 = 9,
};

enum Group2 {
  G2_5_PLUS_10_IS_15 = 0,
  G2_REPEAT_QUESTION = 1,
};

enum Group3 {
  G3_6_PLUS_8_IS_14 = 0,
  G3_REPEAT_QUESTION = 1,
};

enum Group4 {
  G4_20_MINUS_18_IS_2 = 0,
  G4_REPEAT_QUESTION = 1,
};

enum Group5 {
  G5_6_TIMES_8_IS_48 = 0,
  G5_REPEAT_QUESTION = 1,
};

enum Group6 {
  G6_32_MINUS_16_IS_16 = 0,
  G6_REPEAT_QUESTION = 1,
};

enum Group7 {
  G7_50_BY_5_IS_10 = 0,
  G7_REPEAT_QUESTION = 1,
};

enum Group8 {
  G8_82_MINUS_37_IS_45 = 0,
  G8_REPEAT_QUESTION = 1,
};

enum Group9 {
  G9_122_TIMES_3_IS_366 = 0,
  G9_REPEAT_QUESTION = 1,
};

enum Group10 {
  G10_SQRT_64_IS_8 = 0,
  G10_REPEAT_QUESTION = 1,
};

enum Group11 {
  G11_56_BY_7_IS_8 = 0,
  G11_REPEAT_QUESTION = 1,
};

int8_t group, idx;

/*---------------------------------------- Setup ---------------------------------------------------------------*/

void setup() {
  Serial.begin(9600);
  port.begin(9600);

  easyvr.setDelay(0); // speed-up replies
  easyvr.setTimeout(20); // Set the number of seconds to listen for each command
  easyvr.setLanguage(0); //<-- same language set on EasyVR Commander when code was generated (English)

  Wire.begin();

  lcd.begin(16, 4);

  pinMode(backlightButton, INPUT_PULLUP);
  pinMode(alarmSetupButton, INPUT_PULLUP);
  pinMode(alarmClockButton, INPUT_PULLUP);
  pinMode(nextButton, INPUT_PULLUP);
  pinMode(upButton, INPUT_PULLUP);
  pinMode(downButton, INPUT_PULLUP);
  pinMode(lcdBacklight, OUTPUT);

  digitalWrite(lcdBacklight, HIGH);

  rtc.begin();
  //rtc.setTime(10, 25, 20);     // Set the time (24hr format)
  //rtc.setDate(05, 04, 2019);   // Set the date
}

/*---------------------------------------- Loop ----------------------------------------------------------------*/

void loop()
{
  homeDisplay();

  // check if lcdbacklightButton pressed
  if (digitalRead(backlightButton) == LOW) {
    delay(150);
    if (digitalRead(lcdBacklight) == LOW)
      digitalWrite(lcdBacklight, HIGH);
    else
      digitalWrite(lcdBacklight, LOW);
  }

  // check if alarm setup button pressed
  if (digitalRead(alarmSetupButton) == LOW) {
    delay(150);
    lcd.clear();
    alarmClockHoursSetup();
    alarmSetupButtonPressed = false;
    alarm = true;
  }

  if (Hor == h && Min == m && alarm == true) {
    // run some stuff
    alarm = false;
    easyvr.playSound(1, EasyVR::VOL_FULL);
    easyvr.playSound(2, EasyVR::VOL_FULL);
    easyvr.playSound(3, EasyVR::VOL_FULL);
    easyvr.playSound(4, EasyVR::VOL_FULL);
    group = GROUP_1; // Start group (customize)
    voiceRecognition();
  }

  if (digitalRead(alarmClockButton) == LOW) {
    delay(150);
    if (alarm == false)
      alarm = true;
    else
      alarm = false;
  }
}

/*---------------------------------------- Home Display Method -------------------------------------------------*/
// Print out main home display that will show by default.
// Will show time, date, alarm (if any set), and temperature.
void homeDisplay() {
  t = rtc.getTime();
  Hor = t.hour;
  Min = t.min;
  Sec = t.sec;
  tim = rtc.getTimeStr();
  dat = rtc.getDateStr();

  printToDisplay(0, 0, "Time :", rtc.getTimeStr());
  printToDisplay(0, 1, "Date :", rtc.getDateStr());
  printToDisplay(0, 2, "Alarm:", "");


  if (alarm == true) {
    printToDisplay(8, 2, ":", "");
    if (h <= 9)
      printToDisplay2(6, 2, "0", h);
    if (h > 9)
      printToDisplay2(6, 2, "", h);

    if (m <= 9)
      printToDisplay2(8, 2, ":0", m);
    if (m > 9)
      printToDisplay2(8, 2, ":", m);
  }
  else
    printToDisplay(6, 2, "OFF  ", "");


  printToDisplay(0, 3, "Temp :", (String)rtc.getTemp());
  lcd.setCursor(11, 3);
  lcd.print((char)223);           // Degree symbol;
  lcd.print("C");
}

/*------------------------------------- alarmClockHoursSetup Method --------------------------------------------*/
// When setting the alarm, this method allows the user to chose an hour for it go off
void alarmClockHoursSetup() {
  do {

    if (digitalRead(upButton) == LOW) {
      if (h >= 23) {
        h = 0;
        printToDisplay(5, 1, "  ", "");
      }
      else {
        h++;
        delay(150);
      }
    }

    if (digitalRead(downButton) == LOW) {
      if (h <= 0) {
        h = 23;
        printToDisplay(5, 1, "  ", "");
      }
      else {
        h--;
        delay(150);
      }
    }

    if (h <= 9)
      printToDisplay2(5, 1, "0", h);
    else
      printToDisplay2(5, 1, "", h);

    printToDisplay(0, 0, "Alarm Hours   ", "");
    if (m <= 9)
      printToDisplay2(7, 1, ":0", m);
    else
      printToDisplay2(7, 1, ":", m);

    if (digitalRead(nextButton) == LOW) {
      delay(150);
      alarmClockMinutesSetup();
      minutesNextButtonPressed = false;
    }

    if (digitalRead(alarmSetupButton) == LOW) {
      alarmSetupButtonPressed = true;
      delay(150);
    }

  } while (alarmSetupButtonPressed == false);
}

/*------------------------------------- alarmClockMinutesSetup Method --------------------------------------------*/
// When setting the alarm, this method allows the user to chose a minute for it go off
void alarmClockMinutesSetup() {
  do {

    if (digitalRead(upButton) == LOW) {
      if (m >= 59) {
        m = 0;
        printToDisplay(7, 1, "   ", "");
      }
      else {
        m++;
        delay(150);
      }
    }

    if (digitalRead(downButton) == LOW) {
      if (m < 1) {
        m = 59;
        printToDisplay(8, 1, "   ", "");
      }
      else {
        m--;
        delay(150);
      }
    }

    // print hours
    if (h <= 9)
      printToDisplay2(5, 1, "0", h);
    else
      printToDisplay2(5, 1, "", h);

    //print minutes
    printToDisplay(0, 0, "Alarm Minutes", "");
    if (m <= 9) {
      printToDisplay(10, 1, " ", "");   // To clear the '-1' that's printed
      printToDisplay2(7, 1, ":0", m);
    }
    else
      printToDisplay2(7, 1, ":", m);

    if (digitalRead(nextButton) == LOW) {
      delay(150);
      minutesNextButtonPressed = true;
    }

    if (digitalRead(alarmSetupButton) == LOW) {
      delay(150);
      alarmSetupButtonPressed = true;
      minutesNextButtonPressed = true;
    }

  } while (minutesNextButtonPressed == false);
}


/*-------------------------------------- print to display method -----------------------------------------------*/
// Print stuff to the display by passing in a location and message
void printToDisplay (int cursor1, int cursor2, String message1, String message2) {
  lcd.setCursor(cursor1, cursor2);
  lcd.print(message1);
  lcd.print(message2);
}

void printToDisplay2 (int cursor1, int cursor2, String message1, int message2) {
  lcd.setCursor(cursor1, cursor2);
  lcd.print(message1);
  lcd.print(message2);
}

/*---------------------------------------- voiceRecognition Method ----------------------------------------------------*/
// Voice recognition part of the system where user is asked a question
// when the alarm goes off. They need to get it right to disable the alarm.
void voiceRecognition() {
}