Problemas con millis()

Buenas, llevo unos dias intentando arreglar el problema de mi funcion “cuenta_atras”, en la que incluye la funcion millis().
El problema es que cuando el loop de arduino vuelve a ejecutar el código, se salta esa cuenta atrás, no entra en el while de millis() - start < e
He estado buscando y algunos dicen de resetear el millis() pero no se como se hace y creo que si lo hiciera afectaría al resto de funciones.
Ésta es la parte del código a la que me refiero:

void cuenta_atras() {
  tone(speakerPin, tones[10], 50);
  int e = 800;
  tecla = NO_KEY;
  while (e > 0 && tecla == NO_KEY) {
    tone(speakerPin, tones[9], 180);
    unsigned start = millis();
    while (millis() - start < e) { #En la segunda vuelta del loop, no entra en este while.
    Serial.print("Start : ");
    Serial.println(millis() - start);
      tecla = teclado.getKey();
      if (tecla != NO_KEY) {
        numero = numero + tecla;
        tone(speakerPin, tones[4], 200);
        Serial.println(numero);
      }
      if (tecla == '#') {
        if (check_cancel(numero) == true) {
          cuenta_terminada = false;
          numero = "";
          break;
        }
        else{
          sonido_incorrecta();
          numero = "";
          tecla = NO_KEY;
          }
      }
    }
    e = e - 25;
  }
 if (e == 0) cuenta_terminada = true;
}

Las respuestas a veces son simples y a veces no tanto.

tone(pin, frequency, duration)

Parameters

pin: the pin on which to generate the tone

frequency: the frequency of the tone in hertz - unsigned int

duration: the duration of the tone in milliseconds (optional) - unsigned long

Esto dice que tu instrucción tone cada vez que presiones una tecla demorará 200 mseg que es una eternitdad

if (tecla != NO_KEY) {
        numero = numero + tecla;
        tone(speakerPin, tones[4], 200);
        Serial.println(numero);
      }

Tu código anterior dice que el while se ejecute durante 800 mseg o sea que te da 4 pasadas o mejor dicho 3 porque estas justo para 4 ya que 200 x 4 = 800 y debo sumar los tiempos del resto del código.

Por otro lado por algo siempre se pide que se publique el código completo Que es esto

sonido_incorrecta();

Cuanto demora?

La funcion sonido_incorrecta() es esta:

void sonido_incorrecta() {
  Serial.println("Clave de activacion incorrecta!");
  for (int i = 0; i <= 2; i++) {
    tone(speakerPin, tones[8]);
    delay(100);
    noTone(speakerPin);
    delay(100);
    tone(speakerPin, tones[8]);
    delay(100);
    noTone(speakerPin);
    delay(100);
    tone(speakerPin, tones[8]);
    delay(100);
    noTone(speakerPin);
    delay(200);
  }
}

Demora 700ms y sirve para que cuando se envìe una clave erronea suene ese sonido, pero la cuenta atrás sigue.

No se si me entiendes lo que quiero hacer, es una cuenta atrás, con un teclado, antes de la cuenta atrás hay esta funcion:

void lectura_codigo() {
  Serial.println("Pulse la tecla para activar");
  while (tecla != '*') {
    tecla = teclado.getKey();
    if (tecla != NO_KEY) {
      tone(speakerPin, tones[10]);
      delay(100);
      noTone(speakerPin);
      Serial.println(tecla);
    }

  }
  cuenta_atras();
}

Todo eso está dentro del loop(); , y el problema me lo da cuando se vuelve a repetir el mismo código de nuevo. No se si es posible hacerlo con tone(); , osea, ejecutar tone() , el cual requiere tiempos de pausa para cada tono y la funcion millis(); para controlar el tiempo de la cuenta_atras(); y entre tono y tono cada vez el tiempo de duracion es menor hasta que “e == 0”

definiste a e = 800 o sea 800 mseg y me dices que si alguien presiona una tecla mal consumes 700 mseg y aun preguntas porque no funciona tu código?

No es que dure 800ms, cada iteración se le decrementa a 'e' 25. El problema que tengo es al volver ejecutar el código ya que está en el loop.

Bien, veo que he malinterpretado como funcionaba la rutina. Ahora lo veo mejor toma el tiempo que demora

tecla = teclado.getKey();

me preocupa que el problema este por ahi

Mejor os subo todo el código, os explico lo que quiero hacer exactamente, el programa empieza con una funcion que se llama activar(); , la cual espera a que se pulse la tecla ‘*’ del keypad, a continuación una vez pulsada esa tecla, se comprueba si la funcion cuenta_atras() devuelve true o false, en esa funcion se empieza una cuenta atrás con tonos cada vez más rápidos en reproducirse según pasa el tiempo, tambien, se lee el código de desactivación de la alarma en el caso en el que se quiera desactivar la alarma en la cuenta atrás, ese es el mayor problema que tengo, estar reproduciendo unos tonos y a la vez esperar a las entradas del teclado todo el tiempo, hace un tiempo me puse un post de éste programa, en el que tenía ese problema, uno de vosotros me dijisteis que usara la función millis() , la cual es la que tengo ahora mismo y todo va bien, todo va bien hasta que el programa se ejecuta varias veces, ya que está en el loop(); de arduino , algunas veces cuanto mas veces se itera el loop(); se salta la cuenta atrás.

#include <Keypad.h>

const byte filas = 4;
const byte columnas = 3;
byte pinsFilas[filas] = {8, 7, 6, 5};
byte pinsColumnas[columnas] = {4, 3, 2};

char teclas[filas][columnas] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

Keypad teclado = Keypad(makeKeymap(teclas), pinsFilas, pinsColumnas, filas, columnas);


String punto;
char tecla;
String numero;
char clave;
int reestablecer_alarma = 0;
int reestablecer_cancelacion = 0;
int sensor = 9;
int intentos = 3;
int intentos_cancelacion = 3;
int intentos_activacion = 3;
// Introducimos la variable por donde saldrá nuestra señal digital hasta el zumbador
int speakerPin = 12;
// Definimos una variable con el número de tonos que va a reproducir
int numTones = 10;
int tones[] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440, 600};
//                  mid C  C#   D     D#    E     F     F#    G     G#   A
// Arriba se muestran las equivalencias entre frecuencias y Notas de la escala natural, no están todas declaradas pero existen.

void setup() {
  Serial.begin(9600);
  pinMode(9, INPUT);
  pinMode(12, OUTPUT);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
}

void loop() {
  activar();
  if(cuenta_atras() == false){
    alarma_activada();
    }
}

bool check_cancel(String cancel_pw) {
  if (cancel_pw == "1111#") {
    return true;
  }
  else {
    return false;
  }
}

void activar() {
  Serial.println("Pulse la tecla para activar");
  while (tecla != '*') {
    tecla = teclado.getKey();
    if (tecla != NO_KEY) {
      tone(speakerPin, tones[10]);
      delay(100);
      noTone(speakerPin);
      Serial.println(tecla);
    }

  }
  tecla = NO_KEY;
  Serial.println("Clave correcta");
}
void sonido_incorrecta() {
  Serial.println("Clave de activacion incorrecta!");
  for (int i = 0; i <= 2; i++) {
    tone(speakerPin, tones[8]);
    delay(100);
    noTone(speakerPin);
    delay(100);
    tone(speakerPin, tones[8]);
    delay(100);
    noTone(speakerPin);
    delay(100);
    tone(speakerPin, tones[8]);
    delay(100);
    noTone(speakerPin);
    delay(200);
  }
}
bool cuenta_atras() {
  Serial.println("Empezando cuenta atras");
  tone(speakerPin, tones[10], 50);
  int e = 800;
  while (e > 0 && tecla == NO_KEY) {
    tone(speakerPin, tones[9], 180);
    unsigned start = millis();
    while (millis() - start < e) {
      tecla = teclado.getKey();
      if (tecla != NO_KEY) {
        numero = numero + tecla;
        tone(speakerPin, tones[4], 200);
        Serial.println(numero);
      }
      if (tecla == '#') {
        if (check_cancel(numero) == true) {
          return true;
          break;
        }
        else{
          sonido_incorrecta();
          numero = "";
          tecla = NO_KEY;
          }
      }
    }
    e = e - 25;
  }
  return false;
}

void alarma_activada() {
  Serial.println("Alarma activada!");
  int estado_sensor = LOW;
  delay(100);
  while (estado_sensor != HIGH) {
    estado_sensor = digitalRead(sensor);
  }
  Serial.println("Intruso");
  tone(speakerPin, 300);
  Serial.println("Introduzca clave para desactivar la alarma");
  String clave_desactivar = "";
  while (clave_desactivar != "0000") {
    if (tecla == '#') {
      tone(speakerPin, 500);
      Serial.println("Clave de desactivacion incorrecta!");
      delay(2000);
      noTone(speakerPin);
      intentos -= 1;
      clave_desactivar = "";
    }
    if (intentos == 0) {
      Serial.println("Has agotado el numero de intentos");
      Serial.println("Espere para reestablecer la alarma...");
      while (reestablecer_alarma != 1) {
        punto = punto + ".";
        Serial.print(punto);
        delay(500);
      }
      intentos = 3;
      clave_desactivar = "";
    }
    tecla = teclado.getKey();
    if (tecla != NO_KEY) {
      clave_desactivar = clave_desactivar + tecla;
      tone(speakerPin, tones[10]);
      delay(100);
      noTone(speakerPin);
      Serial.println(clave_desactivar);
    }
  }
  Serial.println("Alarma desactivada!");
}

En el último paso que es el de detectar movimiento con el sensor PIR , algunas veces cuando itera el loop(); una vez o más veces, estando en la primera funcion “activar();” , me salta a la función alarma_activada(); diciendome por serial como tengo puesto, que ha detectado un instruso, no entiendo porque hace eso si supuestamente al estar en la primera funcion activar(); no tiene por que ir comprobando cosas de otra funcion que aún no ha ido, bueno esos son los dos problemas que tengo, espero que me podáis ayudar, éste es el único foro que veo que es útil y que es activo, muchas gracias a todos por vuestro tiempo un saludo :slight_smile:

No creo que la única opción para hacer lo que yo quiero es separar la parte de la cuenta atrás de arduino, que se ejecute en otra placa tipo arduino y que vaya recogiendo datos por serial para saber si la cuenta atrás ha terminado o no, ya que arduino no puede hacer dos cosas a la vez como quiero que haga.

Creo que acabo de arreglar el problema, al menos temporalmente, me gustaría que viéseis el código y me dijérais si cambiaríais algo, el primer futuro problema que veo es que al utilizar la funcion millis(); , la meto en una variable de tipo “long” , ese tipo de dato numérico tiene un límite como todos, haciendo cálculos, te da, si metes milisegundos en ese dato, para 24 dias, osea que cuando pasen esos dias y mi arduino aún esté ejecutando código, la función millis(); para el uso que le doy en el programa deja de funcionar, ya que no puede almacenar mas milisegundos en la variable.

#include <Tone.h>
#include <Keypad.h>


const byte filas = 4;
const byte columnas = 3;
byte pinsFilas[filas] = {8, 7, 6, 5};
byte pinsColumnas[columnas] = {4, 3, 2};

char teclas[filas][columnas] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

Keypad teclado = Keypad(makeKeymap(teclas), pinsFilas, pinsColumnas, filas, columnas);


String punto;
char tecla;
String numero;
char clave;
int reestablecer_alarma = 0;
int reestablecer_cancelacion = 0;
int sensor = 9;
int intentos = 3;
int intentos_cancelacion = 3;
int intentos_activacion = 3;
// Introducimos la variable por donde saldrá nuestra señal digital hasta el zumbador
int altavoz_teclas = 12;
int altavoz_alarma = 10;
// Definimos una variable con el número de tonos que va a reproducir
int numTones = 10;
int tones[] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440, 600};
//                  mid C  C#   D     D#    E     F     F#    G     G#   A
// Arriba se muestran las equivalencias entre frecuencias y Notas de la escala natural, no están todas declaradas pero existen.
Tone tone1;
Tone tone2;

void setup() {
  Serial.begin(9600);
  pinMode(9, INPUT);
  pinMode(12, OUTPUT);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  tone1.begin(altavoz_teclas);
  tone2.begin(altavoz_alarma);
}

void loop() {
  activar();
  if(cuenta_atras() == false){
    alarma_activada();
    }
}

bool check_cancel(String cancel_pw) {
  if (cancel_pw == "1111#") {
    return true;
  }
  else {
    return false;
  }
}

bool check_disable(String disable_pw){
  if (disable_pw == "0000#") return true;
  else return false;
  }

void activar() {
  Serial.println("Pulse la tecla para activar");
  while (tecla != '*') {
    tecla = teclado.getKey();
    if (tecla != NO_KEY) {
      tone1.play(tones[10], 200);
      delay(100);
      tone1.stop();
      Serial.println(tecla);
    }

  }
  tecla = NO_KEY;
  Serial.println("Clave correcta");
}
void sonido_incorrecta() {
    tone2.stop();
    tone1.play(tones[4], 200);
    delay(300);
    tone1.stop();
    tone1.play(tones[4], 200);
    delay(300);
    tone1.stop();
  }
bool cuenta_atras() {
  Serial.println("Empezando cuenta atras");
  tone1.play(tones[10], 50);
  int e = 800;
  while (e > 0 && tecla == NO_KEY) {
    tone1.play(tones[9], 200);
    unsigned long start = millis();
    while (millis() - start < e) {
      tecla = teclado.getKey();
      if (tecla != NO_KEY) {
        numero = numero + tecla;
        tone1.play(tones[4], 150);
        Serial.println(numero);
      }
      if (tecla == '#') {
        if (check_cancel(numero) == true) {
          return true;
          break;
        }
        else{
          Serial.println("Clave incorrecta!");
          sonido_incorrecta();
          numero = "";
          tecla = NO_KEY;
          }
      }
    }
    e = e - 25;
  }
  numero = "";
  return false;
}

void alarma_activada() {
  Serial.println("Alarma activada!");
  int estado_sensor = LOW;
  delayMicroseconds(1000); 
  while (estado_sensor != HIGH) {
    Serial.println(estado_sensor);
    estado_sensor = digitalRead(sensor);
  }
  Serial.println("Intruso");
  Serial.println("Introduzca clave para desactivar la alarma");
  String clave_desactivar = "";
  while (true) {
  tone2.play(300);
    tecla = teclado.getKey();
    if (tecla != NO_KEY) {
      clave_desactivar = clave_desactivar + tecla;
      tone1.play(tones[10], 200);
      Serial.println(clave_desactivar);
    }
    
    if (tecla == '#'){
      if(check_disable(clave_desactivar) == false){
        Serial.println("Clave incorrecta!");
        tone1.stop();
        sonido_incorrecta();
        intentos -= 1;
        clave_desactivar = "";
        }
      else{
        Serial.println("Clave correcta!");
        Serial.println("Alarma desactivada!");
        clave_desactivar = "";
        tone2.stop();
        intentos = 3;
        break;
        }
      }
    if (intentos == 0) {
      Serial.println("Has agotado el numero de intentos");
      Serial.println("Espere para reestablecer la alarma...");
      while (reestablecer_alarma != 1) {
        punto = punto + ".";
        Serial.print(punto);
        delay(500);
      }
      intentos = 3;
      clave_desactivar = "";
    }   
  }
}

Un saludo.

millis() es del tipo unsigned long asi que nunca definas la variable que toma el estado temporal o inicial con nada que no sea lo mismo.

No había visto tu código completo y esto ya de por sí es inusual, no se como te compiló

unsigned start = millis();

unsigned solo nunca lo usé. Evidentemente a ti te ha compilado y el compilador lo tomó como unsigned int o sea 0 a 2^16-1 = 65535

Pero como dije debe ser unsigned long. 0 a 2^32 -1 = 4294967295 mseg = 49 dias 17 hs 2 min 47.295 seg

millis() es del tipo unsigned long asi que nunca definas la variable que toma el estado temporal o inicial con nada que no sea lo mismo.

No había visto tu código completo y esto ya de por sí es inusual, no se como te compiló

unsigned start = millis();

unsigned solo nunca lo usé. Evidentemente a ti te ha compilado y el compilador lo tomó como unsigned int o sea 0 a 2^16-1 = 65535

Pero como dije debe ser unsigned long. 0 a 2^32 -1 = 4294967295 mseg = 49 dias 17 hs 2 min 47.295 seg

Para mi usar delay y millis() es un contrasentido pero si te funciona bien. Son funciones antagónicas, porque delay detiene la ejecución del programa y millis() espera que el programa fluya y sigue contabilizando tiempo. Tu rutina tiene tone que detiene la ejecución y delays que también lo hace.

Pero como dije, si funciona... sigue.

Estoy por hacer que el trabajo de la cuenta atrás y sonidos más o menos complejos en general lo lleve la raspberry, y que se comuniquen por serial si la cuenta atrás ha finalizado o si han cancelado la cuenta atrás, la cosa es quitar de arduino millis() ; y dejarlo solo con el keypad y el sensor PIR