¿Rebotes o código mal escrito? (SOLUCIONADO)

Buenas me llamo Diego y soy nuevo en el tema de programación (por no decir que se muy poco).
Si este post esta mal hecho sepan disculparme y lo borran.
El tema es el siguiente: Quiero hacer un proyecto de un reloj con alarma.
Por ahora solo me he dedicado a que suene con determinada secuencia. Lo que he logrado es que tome el valor del pin correspondiente cuando se presiona el botón y realice la secuencia que se le ordena. Después cuando se toca otra vez el mismo botón debería dejar de ejecutar el programa.
El problema pasa porque tengo que seguir presionando el botón para que corte el bucle y así terminar de ejecutarlo.
¿Sera problemas de rebote, el programa mal estructurado o escrito?
He visto también que usan secuencias “if” en vez de bucles no se si sera porque son mas efectivos para estos casos.

Mando el código que se ejecuta en un Arduino UNO

#include <Wire.h>

int PULSADOR = 10;
int PULSADOR2 = 12;
int BOCINA = 2;
 

void setup()
{
  Serial.begin(9600);
  pinMode(BOCINA,OUTPUT);
  pinMode(PULSADOR,INPUT);
 // pinMode(PULSADOR2,INPUT_PULLUP);
}
void loop()
{
int i,j;

while(digitalRead(PULSADOR) == LOW){
     
      }
                  
    do{
     for(i=0; i<3; i++){
          digitalWrite(BOCINA,HIGH);
          delay(50);
          digitalWrite(BOCINA,LOW);
          delay(50);
       }
       delay(750);
  }while(digitalRead(PULSADOR) == LOW );
  
  while(digitalRead(PULSADOR) == HIGH){
    
 }
}

Desde ya muchas gracias y disculpen las molestias

No es problema de rebote, es problema por usar delay()!!!

Cada vezque usas delay tu Arduino se queda de brazos cruzados esperando que termine.
Si presionas cuando esta en un delay() hará eso… cuando termina el delay prestará atención.
Mira lo que hace tu código

for(i=0; i<3; i++){
          digitalWrite(BOCINA,HIGH);
          delay(50);
          digitalWrite(BOCINA,LOW);
          delay(50);
       }
       delay(750);

100mseg x 3 = 300 mseg mas 750 = 1.050 mseg esperando a que reaccione a tu pulsado.

Solución, ve a documentación => Indice de temas tutorales => millis().

Muchas gracias por la rápida respuesta.
Voy a ver si me interiorizo con el tema de millis().
Gracias otra vez

Tambien lee máquiina de estados.
Presta atención a la explicación de PeterkanTropus

Buenas tardes.
He completado un poco mas el código y agregado funciones.
Cambie la función delay() por millis().
El problema que tengo ahora es que cuando suena el buzzer no respeta el tiempo que le doy con la función millis. Sea cual sea el tiempo que le varié, siempre ejecuta durante un segundo el buzzer.
¿Alguien podría saber por que sucede?

Es un reloj alarma y estoy utilizando el lcd I2C mas el modulo RTC.
Desde ya muchas gracias

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DS1307.h"


LiquidCrystal_I2C lcd(0x27, 20, 4);  


int Pre_Sec;
DS1307 clock;

int PULSADOR = 10;
int BOCINA = 2;
bool ESTADO = false;
int cont = 0;

unsigned long tiempo = 0;
unsigned long tiempo2 = 0;
unsigned long diferencia = 1000;

void setup()
{
  Serial.begin(9600);
  clock.begin();
  clock.fillByYMD(2019, 12, 27);//AUG 18,2017
  clock.fillByHMS(11, 00, 55);//12:00:00"
  clock.fillDayOfWeek(FRI);//Saturday
  clock.setTime();//write time to the RTC chip
  Pre_Sec = clock.second;

  pinMode(BOCINA, OUTPUT);
  pinMode(PULSADOR, INPUT);

}

void loop()
{
  lcd.init();   
  lcd.backlight();
  printTime();
  alarma();
}

void printTime()
{

  char hora[10];
  char fecha[20];

  clock.getTime();
  if (clock.second != Pre_Sec)
  {
    lcd.setCursor(3, 0);
    sprintf(hora, "%02d:%02d:%02d", clock.hour, clock.minute, clock.second);
    lcd.print(hora);
    lcd.setCursor(0, 2);
    sprintf(fecha, "%02d/%02d/%02d ", clock.dayOfMonth, clock.month, clock.year + 2000);
    lcd.print(fecha);

    switch (clock.dayOfWeek)// Friendly printout the weekday
    {
      case MON:
        lcd.print("LUNES");
        break;
      case TUE:
        lcd.print("MARTES");
        break;
      case WED:
        lcd.print("MIERCOLES");
        break;
      case THU:
        lcd.print("JUEVES");
        break;
      case FRI:
        lcd.print("VIERNES");
        break;
      case SAT:
        lcd.print("SABADO");
        break;
      case SUN:
        lcd.print("DOMINGO");
        break;
    }
    Pre_Sec = clock.second;
  }
}
void alarma() {

  int i;

  tiempo = millis();

  if (clock.minute == 1 && digitalRead(PULSADOR) == LOW) {
    if (tiempo >= (tiempo2 + diferencia)) {
      tiempo2 = tiempo;
      ESTADO = !ESTADO;
      digitalWrite(BOCINA, ESTADO);
    }
  } else {
    digitalWrite(BOCINA, LOW);
  }
  
}

hola dr4cu14, yo para reducir el rebote uso la libreria Bounce2.h GitHub - thomasfredericks/Bounce2: Debouncing library for Arduino and Wiring

No tiene rebote.
El dice que la alarma no responde al intervalo que le indica asi que eso no tiene nada que ver con rebotes.

No entiendo que hace el pulsasor aquí

if (clock.minute == 1 && digitalRead(PULSADOR) == LOW) {

La alarma debería invocarse de otro modo y el procedimiento de alarma funcionar independientemente del minuto del reloj.

A ver que fué lo que pensé el autor del hilo al respecto.

Hola buena tardes.
Gracias por las respuestas.
Solo aclaro que no soy programador. Lo hago como pasatiempo porque me gusto el mundo de la programación.
En realidad lo que programo son maquinas CNC.
volviendo al tema...
Lo que hace el botón seria cancelar la alarma en caso de que la persona así lo desee.
Imaginate si te despierta la alarma y no la podes cancelar hasta que se termine el tiempo de la misma :stuck_out_tongue_closed_eyes:
Tendria que ver como llamar a una alarma como dice surbyte.

Mira a ver como se comporta este código

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DS1307.h"


LiquidCrystal_I2C lcd(0x27, 20, 4); 


int Pre_Sec;
DS1307 clock;

int PULSADOR = 10;
int BOCINA = 2;
bool ESTADO = false;
int cont = 0;

unsigned long tiempo = 0;
unsigned long tiempo2 = 0;
unsigned long diferencia = 1000;
String diaSemana[] = {"Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"};
bool estado, estadoAnt = false;
bool flag = false;

void setup()
{
  Serial.begin(9600);
  clock.begin();
  clock.fillByYMD(2019, 12, 27);  // 27/12/19
  clock.fillByHMS(11, 00, 55);    // 12:00:00"
  clock.fillDayOfWeek(FRI);       // Saturday
  clock.setTime();                // write time to the RTC chip
 
  pinMode(BOCINA, OUTPUT);
  pinMode(PULSADOR, INPUT);

}

void loop() {
  lcd.init();   
  lcd.backlight();
  printTime();
  estado = digitalRead(PULSADOR);
  if (!estado && estadoAnt) {
      flag = !flag;
  }
  estadoAnt = estado;

  if (flag) alarma();
}

void printTime() {
  char hora[10];
  char fecha[20];

  clock.getTime();
  if (clock.second != Pre_Sec)  {
      lcd.setCursor(3, 0);
      sprintf(hora, "%02d:%02d:%02d", clock.hour, clock.minute, clock.second);
      lcd.print(hora);
      lcd.setCursor(0, 2);
      sprintf(fecha, "%02d/%02d/%02d ", clock.dayOfMonth, clock.month, clock.year + 2000);
      lcd.print(fecha);

      lcd.print(diaSemana[clock.dayOfWeek])  // Friendly printout the weekday
      Pre_Sec = clock.second;
  }
}
void alarma() {

  int i;
  tiempo = millis();

  if (tiempo >= (tiempo2 + diferencia)) {
      tiempo2 = tiempo;
      digitalWrite(BOCINA, HIGH);
  } else {
      digitalWrite(BOCINA, LOW);
  }
}

No entiendo la funcion alarma() según lo que pones cada vez que estas en el minuto =1 suena la alarma?

Ahora tal vez yo he confundido la idea porque hice que el pulsador active la alarma pero como vi que lo hacia por LOW puse todo para que una pulsación la ponga en marcha y una segunda la quite.

O sea tu alarma es que suene cuando el minuto == 1?

Lo puse en 1 minuto para hacer pruebas.
En realidad se activa con una hora elegida por el usuario y se desactiva con el tiempo transcurrido o presionando el botón.

Bien okay, entonces eso que has puesto sería como la programación de tu alarma y solo quieres desactivarla.

Entonces lo que deberías hacer es que al minuto se active el flag y tu puedas controlar con el pulsador ese flag al estado opuesto para que no suene mas.

Ha quedado algo rebuscado… y por alguna razón mi cerebro no sugiere otra forma, tal vez sea porque no quiere programar mas en 2019… no lo se
Prueba a ver como se comporta esto.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DS1307.h"


LiquidCrystal_I2C lcd(0x27, 20, 4);


int Pre_Sec;
DS1307 clock;

int PULSADOR = 10;
int BOCINA = 2;
bool ESTADO = false;
int cont = 0;

unsigned long tiempo = 0;
unsigned long tiempo2 = 0;
unsigned long diferencia = 1000;
String diaSemana[] = {"Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"};
bool estado, estadoAnt = false;
bool flag = false;

void setup()
{
  Serial.begin(9600);
  clock.begin();
  clock.fillByYMD(2019, 12, 27);  // 27/12/19
  clock.fillByHMS(11, 00, 55);    // 12:00:00"
  clock.fillDayOfWeek(FRI);       // Saturday
  clock.setTime();                // write time to the RTC chip
 
  pinMode(BOCINA, OUTPUT);
  pinMode(PULSADOR, INPUT);

}

void loop() {
  lcd.init();  
  lcd.backlight();
  printTime();
  chequeo_alarmas();

  estado = digitalRead(PULSADOR);
  if (!estado && estadoAnt) {
      if (flag) flag = false; // desactivo alarma
  }
  estadoAnt = estado;
  alarmas();
}

void printTime() {
  char hora[10];
  char fecha[20];

  clock.getTime();
  if (clock.second != Pre_Sec)  {
      lcd.setCursor(3, 0);
      sprintf(hora, "%02d:%02d:%02d", clock.hour, clock.minute, clock.second);
      lcd.print(hora);
      lcd.setCursor(0, 2);
      sprintf(fecha, "%02d/%02d/%02d ", clock.dayOfMonth, clock.month, clock.year + 2000);
      lcd.print(fecha);

      lcd.print(diaSemana[clock.dayOfWeek])  // Friendly printout the weekday
      chequeo_alarmas();

      Pre_Sec = clock.second;
  }
}
void  chequeo_alarmas() {
  if (clock.minute == 1 && clock.seconds == 0) {
      flag = true;
  }
}

void alarmas() {
  int i;
  tiempo = millis();
  if (flag)
      if (tiempo >= (tiempo2 + diferencia)) {
          tiempo2 = tiempo;
          digitalWrite(BOCINA, HIGH);
      } else {
          digitalWrite(BOCINA, LOW);
      }
}

Jaja me imagino que esa cabeza ya quiere un descanso.
Bueno muchas gracias y felicidades para todos.

Buenos días.
Pude solucionar el problema de que la bocina no respetara el tiempo que le había puesto con millis();.
El problema radicaba en el uso de lcd.init(); en la funcion loop. la cambie para el menu principal y ahora funciona a la perfección.
Sigo trabajando en el código para agregarle mas funciones y dolores de cabeza GG.
Gracias por los consejos dados.

A continuación el código:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DS1307.h"

LiquidCrystal_I2C lcd(0x27, 20, 4);

int Pre_Sec;
DS1307 clock;

int PULSADOR = 10;
int BOCINA = 2;
bool ESTADO = false;
int cont = 0;

unsigned long tiempo_inicial;
unsigned long tiempo_transcurrido ;
const unsigned long periodo = 100;


void setup()
{
  Serial.begin(9600);
  clock.begin();
  clock.fillByYMD(2019, 12, 27);//AUG 18,2017
  clock.fillByHMS(11, 00, 55);//12:00:00"
  clock.fillDayOfWeek(FRI);//Saturday
  clock.setTime();//write time to the RTC chip
  Pre_Sec = clock.second;

  lcd.init(); //ACA COLOQUE LA FUNCIÓN MENCIONADA
  lcd.backlight();

  pinMode(BOCINA, OUTPUT);
  pinMode(PULSADOR, INPUT);
  tiempo_inicial = millis();
}

void loop()
{
  printTime();
  alarma();
}

void printTime()
{

  char hora[10];
  char fecha[20];

  clock.getTime();
  if (clock.second != Pre_Sec)
  {
    lcd.setCursor(3, 0);
    sprintf(hora, "%02d:%02d:%02d", clock.hour, clock.minute, clock.second);
    lcd.print(hora);
    lcd.setCursor(0, 2);
    sprintf(fecha, "%02d/%02d/%02d ", clock.dayOfMonth, clock.month, clock.year + 2000);
    lcd.print(fecha);

    switch (clock.dayOfWeek)// Friendly printout the weekday
    {
      case MON:
        lcd.print("LUNES");
        break;
      case TUE:
        lcd.print("MARTES");
        break;
      case WED:
        lcd.print("MIERCOLES");
        break;
      case THU:
        lcd.print("JUEVES");
        break;
      case FRI:
        lcd.print("VIERNES");
        break;
      case SAT:
        lcd.print("SABADO");
        break;
      case SUN:
        lcd.print("DOMINGO");
        break;
    }
    Pre_Sec = clock.second;
  }
}
void alarma() {

  tiempo_transcurrido = millis();

  if (tiempo_transcurrido - tiempo_inicial >= periodo) {
    ESTADO = !ESTADO;
    digitalWrite(BOCINA, ESTADO);
    tiempo_inicial = tiempo_transcurrido;
  }

}

Uhhh perdona, no advertí que lcd.init() estaba en el loop igual que lcd.backlight() aunque esta ultima si puede formar parte de un menú.

La primera va en el setup() siempre.

Me alegro que lo hayas detectado.