Millis Problem

Aloah,

Mein Projekt ist “eigentlich” ganz simpel… (nur ich bekomme es nicht hin)

Ich möchte sobald ein Knopf losgelassen wird das nach einer Wartezeit von 40 Sek. ein Alarm los geht.
Wenn ich jetzt mit millis arbeite dann prüft er anfangs ob die Zeit vergangen ist und tut was er soll.
Aber: Drücke ich dann den Knopf für 20 Sek. und lasse ihn danach wieder los dann fängt er nicht von 40 an zu zählen sondern tickt die Restzeit runter was dann ca 18-20 Sek sind.
Die Logik meine ich auch zu verstehen. Meine Theorie: Millis zählt ja weiter und fängt nicht von vorne an.

Wie bekomme ich es hin das er nach jedem erneuten drücken wieder von 40 anfängt und nicht die Restzeit abtickt.

In meiner verzweiflung habe ich auch schweinereien wie Arduino Reset und Delay probiert… ist bei Delay aber genau das gleiche.

Im Beispielcode sind 5 Sekunden drin damit ich nicht lange warten muss für einen Test also nicht wundern.

Danke vorab!

//void(* resetFunc) (void) = 0; //declare reset function @ address 0
unsigned long lastMillis1;
 
void setup() {
  pinMode(A5, OUTPUT); //speaker pin
  pinMode(12,INPUT_PULLUP); //pushbutton
  lastMillis1 = millis();
}
    
void routine2() {
  if (digitalRead(12) == HIGH){
      tone(A5, 1000);
      delay(300); 
      tone(A5, 500);
      delay(300);
      noTone(A5);
      lastMillis1 = millis();
      }
      
  if (digitalRead(12) == LOW){
      noTone(A5);
   // resetFunc();  //call reset
   }
}
  
void loop(){
  if (digitalRead(12) == HIGH){
    if ((millis() - lastMillis1) >= 5000) {
          lastMillis1 = millis();
for (int i=0; i <= 255; i++){
  routine2();
      }
    }
  }
}

(deleted)

Danke für deine Antwort!
Wie bekomme ich es hin das er aufhört zu zählen und wieder von vorne anfängt.

LG

(deleted)

es gibt für dich keinen Stopp oder Reset der millis.

Millis ist wie deine immer laufende Uhrzeit.

Du merkst die Zeit wenn dein Ereignis auftritt: gemerkteMillis=millis().
Dann vergleichst du laufend gegen deinen Intervall.
Ist der Intervall überschritten (millis()-gemerkteMillis>intervall)
löst du dein Ende-Event aus.

so und wenn nun du wieder so einen lauf starten willst, na dann merkst dir wieder die millis()

ob die nun auf 10, 20, 100 oder 1000 sind ist egal. es geht immer um die Differenz zwischen gemerkt und aktuell.

So gesehen gibt es ja kein Endevent da der Alarm (also die beiden Töne) ja dauerhaft im Loop abgespielt werden, solange der Knopf nicht wieder gedrückt wird fürs deaktivieren. Und dann kommt es eben zum Problem wie oben beschrieben das wenn ich zb sage: Warte 5 Sek. und in der Zeit öffne ich für 4 und schließe wieder dann geht bei der nächsten öffnung direkt der alarm los.

Auch wenn das eher Sekundär ist aber gibt es am Code dinge die man besser machen kann?

LG

Ramset:
Mein Projekt ist “eigentlich” ganz simpel… (nur ich bekomme es nicht hin)

Ich möchte sobald ein Knopf losgelassen wird das nach einer Wartezeit von 40 Sek. ein Alarm los geht.
Wenn ich jetzt mit millis arbeite dann prüft er anfangs ob die Zeit vergangen ist und tut was er soll.
Aber: Drücke ich dann den Knopf für 20 Sek. und lasse ihn danach wieder los dann fängt er nicht von 40 an zu zählen sondern tickt die Restzeit runter was dann ca 18-20 Sek sind.
Die Logik meine ich auch zu verstehen. Meine Theorie: Millis zählt ja weiter und fängt nicht von vorne an.

Wie bekomme ich es hin das er nach jedem erneuten drücken wieder von 40 anfängt und nicht die Restzeit abtickt

Du musst Dir merken das Du die Taste losgelassen hast UND die Zeit mitnehmen.

Ausm Kopf - es kompiliert / Ich hab ein paar Kommentare mit eingebaut, damit Du siehst, was da passieren soll und was noch gehen kann.

//void(* resetFunc) (void) = 0; //declare reset function @ address 0
unsigned long lastmillis;
bool Status = LOW;

void setup()
{
  pinMode (A5, OUTPUT); //speaker pin
  pinMode (12, INPUT_PULLUP); //pushbutton
  lastmillis = millis();
}

void routine2 ()
{
  if (Status == HIGH)
  {
    tone (A5, 1000);
    delay (300);
    tone (A5, 500);
    delay (300);
    noTone (A5);
  }
  if (Status == LOW)
  {
    noTone (A5);
    // resetFunc();  //call reset
  }
}
void loop()
{
  if (digitalRead (12) == HIGH)  // Taste losgelassen
  {
    if (Status = LOW)            // Merker noch nicht gesetzt?
    {
      Status = HIGH;             // Merker "Taste losgelassen"
      lastmillis = millis();     // millis merken
    }
  }
  /*
    else if (digitalRead (12) == LOW) // Block auskommentieren für Restart vor Ablauf
    {
    Status=LOW;
    }
  */
  if (((millis() - lastmillis) >= 5000)
      && (Status == HIGH)) // Zeit abgelaufen UND Merker gesetzt?
  {
    for (int i = 0; i <= 255; i++)
    {
      routine2 ();
      // if (i==255) {Status=LOW;}  // Alternative zur nächsten Zeile
    }
    Status = LOW;          // setzt den Merker zurück, wenn for durchgelaufen ist
  }
}

PS: Ist das gewollt? i <= 255; i++ - willst Du den Tone sooft durchlaufen lassen?

my_xy_projekt:
Du musst Dir merken das Du die Taste losgelassen hast UND die Zeit mitnehmen.

Ausm Kopf - es kompiliert / Ich hab ein paar Kommentare mit eingebaut, damit Du siehst, was da passieren soll und was noch gehen kann.

//void(* resetFunc) (void) = 0; //declare reset function @ address 0

unsigned long lastmillis;
bool Status = LOW;

void setup()
{
  pinMode (A5, OUTPUT); //speaker pin
  pinMode (12, INPUT_PULLUP); //pushbutton
  lastmillis = millis();
}

void routine2 ()
{
  if (Status == HIGH)
  {
    tone (A5, 1000);
    delay (300);
    tone (A5, 500);
    delay (300);
    noTone (A5);
  }
  if (Status == LOW)
  {
    noTone (A5);
    // resetFunc();  //call reset
  }
}
void loop()
{
  if (digitalRead (12) == HIGH)  // Taste losgelassen
  {
    if (Status = LOW)            // Merker noch nicht gesetzt?
    {
      Status = HIGH;            // Merker “Taste losgelassen”
      lastmillis = millis();    // millis merken
    }
  }
  /*
    else if (digitalRead (12) == LOW) // Block auskommentieren für Restart vor Ablauf
    {
    Status=LOW;
    }
  */
  if (((millis() - lastmillis) >= 5000)
      && (Status == HIGH)) // Zeit abgelaufen UND Merker gesetzt?
  {
    for (int i = 0; i <= 255; i++)
    {
      routine2 ();
      // if (i==255) {Status=LOW;}  // Alternative zur nächsten Zeile
    }
    Status = LOW;          // setzt den Merker zurück, wenn for durchgelaufen ist
  }
}




PS: Ist das gewollt? **i <= 255; i++** - willst Du den Tone sooft durchlaufen lassen?

Danke für deine Antwort! Also den Alarm 255 läuten zu lassen war mehr oder weniger gewollt er soll eben eine ganz Zeit lang aktiv sein. Von mir aus kann er auch 24/7 dudeln.

Aus deinen ergänzungen lese ich das so als das ich dort noch “merker” setzen muss.
Lade ich den Sketch so hoch tut sich jedenfalls nichts.

Ramset:
Aus deinen ergänzungen lese ich das so als das ich dort noch "merker" setzen muss.
Lade ich den Sketch so hoch tut sich jedenfalls nichts.

Hi,
Sorry da ist eine Zeile falsch.
if (Status = LOW) // Merker noch nicht gesetzt?

muss heissen:
if (Status == LOW) // Merker noch nicht gesetzt?

Merke: EIN = setzt den Wert; ZWEI == vergleichen den Wert
Hier wird verglichen, ob der Wert in der Variablen Status aktuell LOW ist; Dann mache da weiter.

Probiers mal.

Danke habs korrigiert.
Also soweit läuft das Programm allerdings durchläuft er erst die for Schleife komplett bevor er auf LOW schaltet.
Da ich einen Daueralarm möchte müsste er prüfen ob der Knopf zwischenzeitlich wieder gedrückt wird.

Also das wie du es Programmiert hast das hätte ich NIEMALS!! hinbekommen.
Ich versuche wenigstens die Abfrage selbst hinzubekommen. Falls nicht werde ich hier nochmals um Rat fragen.

Ein dickes Danke!

Ramset:
Danke habs korrigiert.
Also soweit läuft das Programm allerdings durchläuft er erst die for Schleife komplett bevor er auf LOW schaltet.
Da ich einen Daueralarm möchte müsste er prüfen ob der Knopf zwischenzeitlich wieder gedrückt wird.

Dann musst Du innerhalb von for erneut auf digitalRead (12) == LOW prüfen.

Ginge z.B. if (digitalRead (12) == LOW) {i=255;}

Ich schau nachher nochmal drüber.

Hast Du probiert, was passiert wenn Du den Block /* [...] */ auskommentierst?

Hallo nochmal, also mein fertiger Code (Dank dir!!) sieht so aus:

unsigned long lastmillis;
bool Status = LOW;

void setup()
{
  pinMode (A5, OUTPUT); //speaker pin
  pinMode (12, INPUT_PULLUP); //pushbutton
  lastmillis = millis();
}

void routine2 ()
{
  if (Status == HIGH)
  {
    tone (A5, 1000);
    delay (300);
    tone (A5, 500);
    delay (300);
    noTone (A5);
  }
  if (Status == LOW)
  {
    noTone (A5);
    // resetFunc();  //call reset
  }
}
void loop()
{
  if (digitalRead (12) == HIGH)  // Taste losgelassen
  {
    if (Status == LOW)            // Merker noch nicht gesetzt?
    {
      Status = HIGH;             // Merker "Taste losgelassen"
      lastmillis = millis();     // millis merken
    }
  }
  /*
    else if (digitalRead (12) == LOW) // Block auskommentieren für Restart vor Ablauf
    {
    Status=LOW;
    }
  */
  if (((millis() - lastmillis) >= 40000)
      && (Status == HIGH)) // Zeit abgelaufen UND Merker gesetzt?
  {
    for (int i = 0; i <= 255; i++)
    {
  if (digitalRead (12) == LOW){
       break;
       }
      routine2 ();
      // if (i==255) {Status=LOW;}  // Alternative zur nächsten Zeile
    }
    Status = LOW;          // setzt den Merker zurück, wenn for durchgelaufen ist
  }
}

Habe noch einen Break rein. Als erstes wollte ich die Schleife bzw die Taste so prüfen:
if (Status == LOW)
break;
Habe mich dann gewundert warum es nicht geht. Ist sicher wieder eine Sache der Programmierlogik.
Das hat geholfen:
if (digitalRead (12) == LOW)
break;
Für was ist eigentlich das hier {i=255} hinter dem …== LOW) bzw. was macht das?

Nochmals danke!

Ramset:
Für was ist eigentlich das hier {i=255} hinter dem …== LOW) bzw. was macht das?

Das macht (fast) das, was Du mit dem break machst.

Wenn innerhalb von for der Wert erst bei 100 wäre, würde der Wert dann 255 sein und damit die Wiederholbedingung i<=255 erledigt.
Ich könnte dann noch bis zum Ende der Schleife weiter - im Gegensatz zum break, was zum sofortigen Abbruch führt.
Wenn Du das einsetzt und dann mit STRG-T formatierst, wirds vielleicht übersichtlicher :wink:

also so wie ich das verstanden habe setzt er den wert auf 255 womit die schleife beendet wird. Ist das nun besser oder schlechter als break? (nur für mich als Grundlage zum verstehen)

Ramset:
also so wie ich das verstanden habe setzt er den wert auf 255 womit die schleife beendet wird. Ist das nun besser oder schlechter als break? (nur für mich als Grundlage zum verstehen)

Stell einem Juristen eine Frage. Die erste Antwort: “Es kommt darauf an”.
Wenn Du einen sofortigen Abbruch brauchst, dann ist break die bessere Variante.
Wenn Du aber noch weiter auswertest - ich hatte da z.B. noch if (i==255) {Status=LOW;} als Beispiel drin, dann macht break() das kaputt.
Ist hier nicht problematisch, weil nach Beendigung von for die Variable zurückgesetzt werden kann, ohne das es Auswirkungen auf den Programmablauf hat.

Ist also nur eine Frage der Überlegung, was passieren soll.

Ok danke für die Erläuterung!

Ein mechanischer Taster prellt, was dazu führt, daß die Zeit schon beim Drücken des Tasters losläuft. Damit dürfte der Alarm auch 40 Sekunden nach Drücken losgehen. Eigentlich soll die Zeit aber erst nach dem Loslassen starten. Hier sehe ich einen logischen Fehler.

Oder?

agmue:
Ein mechanischer Taster prellt, Eigentlich soll die Zeit aber erst nach dem Loslassen starten. Hier sehe ich einen logischen Fehler.
Oder?

Nein. [edit] Natürlich sollte es Ja heissen - aber eben ohne Folgen. Siehe unten [/edit]

Damit wäre die Frage beantwortet. :wink:

Der OP hat als Anforderung aber etwas, was eine Prüfung auf Prellen ausschliessen kann.

Ich möchte sobald ein Knopf losgelassen wird das nach einer Wartezeit von 40 Sek. ein Alarm los geht.

Das heisst, das erste Loslassen zählt.

Ich hatte mich dem bereits angenommen.
In meinem Code kann jederzeit - bis zum Start der for - der Status der Taste resettet werden.

  /*
    else if (digitalRead (12) == LOW) // Block auskommentieren für Restart vor Ablauf
    {
    Status=LOW;
    }
  */

Sobald die Taste irgendwann gedrückt ist, bevor der Alarm losgeht, oder prellt, ist das ganze wieder auf Null.

Ein Prellen der Taste dürfte bei einer "Wartezeit" von 40 Sekunden aber eher unter fernerliefen eingestuft sein.

Das heisst, das erste Loslassen zählt.

Das letzte Loslassen!
40 Sekunden nach dem letzten loslassen, geht der Alarm los.

#include <CombieTimer.h>
#include <CombiePin.h>
#include <CombieTypeMangling.h>

using namespace Combie::Pin;
using namespace Combie::Timer;
using namespace Combie::Millis;

struct 
{
  bool operator=(const bool value) const
  {
    return not value;
  }
} inverter;

bool  alarmAnforderung {false};
const byte alarmPin {A5};
TasterGND<12> totMannKnopf; // zwischen Pin und GND(invertierend)
FallingEdgeTimer toff {40_sec};  // abfallende Flanke wird verzoegert
 
void setup(void) 
{
  totMannKnopf.initPullup();
}

void yield() 
{
  alarmAnforderung  =  inverter = toff = totMannKnopf;
}

void loop() 
{
  delay(10_ms); // dem yield Zeit geben
  if(alarmAnforderung)
  {
      tone(alarmPin, 1000); // korrigiert
      delay(300_ms);
      tone(alarmPin, 500); // korrigiert
      delay(300_ms);
      noTone(alarmPin);
  }
}

Unter Beibehaltung der originalen Alarmeinrichtung.
Wenn man diese durch einen millis() gesteuerten Automaten ersetzt, kann man natürlich auch auf yield() verzichten

CombieLib.zip (81 KB)

combie:
Das letzte Loslassen!
40 Sekunden nach dem letzten loslassen, geht der Alarm los.

Nein.
Die Anforderung war:

Ich möchte sobald ein Knopf losgelassen wird

Mit dem ersten loslassen LOW->HIGH wird der Merker gesetzt

  if (digitalRead (12) == HIGH)  // Taste losgelassen
  {
    if (Status == LOW)            // Merker noch nicht gesetzt?
    {
      Status = HIGH;             // Merker "Taste losgelassen"
      lastmillis = millis();     // millis merken
    }
  }

Jeder weitere Tastendruck hat keinen Einfluss.
Erst wenn die Alarmierung beendet ist wird der Status LOW gesetzt.

Nur wenn man den auskommentierten Teil nutzt, wird der Merker zurückgesetzt, solange der Alarm noch nicht ausgelöst. Dann gilt der letzte Übergang LOW->HIGH als Startzeitpunkt, was einem debounce entsprechen würde.