Timerverzögerungsaktionsschaltung mit Poti und Button

Hallo zusammen,

Das Programm soll:

Nach Spannungszugabe am Arduino soll das eigentliche Programm (nach einstellen des Potis) durch einen Taster aktiviert werden können. Man sollte einen Zahlenwert über den Poti einlesen und im Programm an diesem Wert ein Aktion starten können. (Bisher mit Switch Case gelöst.)

Zudem soll der Compiler nach Bearbeitung eines Case Falls, in den nächst höheren Case springen und diesen abarbeiten bis zum letzten Case.
Beim letzten Case soll das Programm wieder in Ausgangslage, also vor die Poti-Eingabe gebracht werden.

Des weiteren wäre es vorteilhaft das Programm bzw. den Aktionsteil in den Switch Cases jederzeit über den (oder einen) Taster stoppen zu können um das System zur Poti Eingabe zurückzusetzen.

z.B. Stellt man den Poti auf 5 (Auf Display sichtbar), startet das Programm per Knopfdruck und case5 wird ausgeführt. Somit wird z.B. eine Methode aktiviert die einen Motor steuert. Dieser Arbeitet eine gewissen Zeit, trotzdem wird der nächst zahlenmäßig höhere Case (Im Beispiel also Case 6) genau nach z.B. 3 stunden angesteuert, ohne die Zeit die der Motor bei Case5 braucht mitzurechnen.

Das Programm "kann":
-Potiwert einlesen und Wertzittern entfernen.
-Hauptprogramm per Knopfdruck starten.

Problem:

-->Das Programm kann nicht richtig durch den Taster ausgeschaltet werden. Wie am besten die Switch Case und die For schleife durchbrechen?
-->Wie kann man die Cases um jeweils z.B. 3 h zeitversetzt ansteuern, gleichzeitig aber ein aktuator bzw. eine Methode schalten die, die Zeitmessung möglichst nicht beeinträchtigt?

Für Tipps/Tricks und sonstige Hilfen bin ich sehr dankbar. Ich erwarte keinen fertigen code. Ich bin mir nicht sicher ob die aktuelle Herangehensweise sinnvoll genug ist, und ob ich die gesamt Idee so zum laufen bekomme. Bisher habe ich schon einigen Content auf YT bezüglich blinkWithoutDelay, StateMachine, Multitasking etc. angeschaut, komme im Moment jedoch seit einigen Stunden nicht weiter. Stehe auf dem Schlauch.
Ich hoffe der code ist nicht zu vertüddelt da meine Kenntnisse & Syntax bisher noch auf eher Anfängerniveau sind. Habe den Ardi erst ein paar Tage. Vielen Dank für Rückmeldung.

Code.ino (3.08 KB)

->Das Programm kann nicht richtig durch den Taster ausgeschaltet werden. Wie am besten die Switch Case und die For schleife durchbrechen?

ich vermute du sollst deinen Button debouncen. Dein Taster wird "prellen" und/oder du lässt den Taster zu spät wieder los und durchläuft microsekunden später noch mal den gleichen Code und stellt deine Flags wiederum. Zum Debouncen und zum Tasten auswerten gibt es in der IDE Beispiele.

Auf alle Fälle kannst du auch mehr Serial.print Ausgaben ergänzen, damit du siehst warum dein Sketch nicht so reagiert wie du glaubst dass er das tun soll.

Zeitsteuerung sollte relativ einfach sein. Halte dich an "BlinkWithoutDelay".
--> wenn deine Aktion beginnt, merkst du dir die startZeit (=millis).
in deinem onOFF prüfst du dann ,ob die aktuelle Zeit (millis) - startZeit > 3 601000UL ist . Wenn ja - schaltest weiter.

by the way...
warum du in deinem runningProgram() den switch case 10 mal durchführst ... finde ich eigenartig:
for (int i = comparator ; i <= 10 ; i++ ) {

aber wenn es wirklich das ist was du willst ... auch recht.

Danke für den Tipp, die for Schleife war sinnlos. Habe auch noch ein paar Sachen angepasst. Jetzt kann ich zumindest nach dem Start die case´s abarbeiten lassen, und danach auf OFF schalten. Werde noch einen externen Reset Knopf einbauen.

Jetzt stellt sich mir nurnoch die Frage wie ich nach z.B. Case5 24 stunden später Case6 aktivieren kann im BlinkWithoutDelay style. Werde mir das jetzt nochmal genauer anschauen.

Vielen Dank für die Hilfe !

Code_2.ino (2.87 KB)

Hallo,

in deinem switch case fehlt die brea****k Anweisung. Derzeit werden alle case in einem Rutsch abgearbeitet, Stichwort "Falltrough". Desweiteren fehlt dir eine default Auswertung. Schaue dir den Aufbau von switch case nochmal genauer an.

In der IDE alle Ausgaben einschalten.
Datei > Voreinstellungen >

  • Ausführliche Ausgabe während > beide Haken rein
  • Compilerwarnungen > "ALLE"
    Zeilennummern und Codefaltung sind auch hilfreich.
    Speichern beim Überprüfen und Hochladen würde ich abschalten.

Eine einfache Tasterentprellung wäre, dabei aufpassen ob INPUT oder INPUT_PULLUP und dessen negierte Logik.
Oder du nimmst die GitHub - thomasfredericks/Bounce2: Debouncing library for Arduino and Wiring Lib.

const byte pinTaster {2};

void setup()
{
  pinMode(pinTaster, INPUT_PULLUP);    
  Serial.begin(115200);
  Serial.println("\nStart");
}


void loop()
{
  const bool state = updateTaster(pinTaster, 30);
  if (state)
  {
    Serial.println(F("Taster wird gedrueckt"));
  }
}


bool updateTaster (const byte pin, const unsigned long interval)
{
  static unsigned long lastMillis {0};
  static bool state {false};

  if (millis() - lastMillis >= interval)
  {
    lastMillis = lastMillis + interval;
    state = !digitalRead(pin);
  }

  return state;
}

Zudem soll der Compiler nach Bearbeitung eines Case Falls, in den nächst höheren Case springen und diesen abarbeiten bis zum letzten Case.

In diesem Fall braucht es keinen Break bzw Du realisierst es durch ändern der Switch-Variable für den nächsten Durchlauf.

Der Compiler führt gar nichts aus. Der Compiler mit dem Linker und Preprozessor (hab ich da noch was vergessen) übersetzen den C-Code in Maschinencode. Dieser wird dann auf den Arduino übertragen und vom Controller auf dem Arduino sobald er resetiert wird oder Spannungsversorgung eingeschaltet wird ausgeführt.

Grüße Uwe

uwefed:
In diesem Fall braucht es keinen Break

Wenn er immer alle case-Fälle in einem Rutsch abarbeiten will, braucht es auch kein switch/case.

Ich denke aber er meint es so, dass beim nächsten Durchlauf der nächste case erfolgen soll. Dann braucht es aber break.

Gruß Tommy

Wenn er immer alle case-Fälle in einem Rutsch abarbeiten will, braucht es auch kein switch/case.

Wenn alle dann schon; aber wenn er verschiedene Einspringpunkte wählt dann schon.
Grüße Uwe

Richtig, wobei ich das auch noch nicht sehe.

Gruß Tommy

Also ich habe den Code2 im 3. Post eingefügt. Vielen Dank für die Rückmeldung!!

Falls jemand weiß wie ich die Aktionen in den Cases um jeweils z. B. 24 Stunden verzögern kann, bescheid geben.
Habe mir überlegt das evtl. doch in einer For Schleife zu machen, und in die if Bedingung dann Millis und timestamp einzubauen. Habe aber noch kein konkreten Entwurf.

Was soll Dir eine for-Schleife bei der Bestimmung von 24 Stunden helfen?

const uint32_t t24 = 24UL * 3600 * 1000;

In der vorhergehenden Aktion den Startwert setzen und dann die normale millis-Abfrage.

Gruß Tommy

Hallo,

zur Ergänzung. Lies mal hier millis() - Arduino Reference. Der Wertebereich vom Rückgabewert hat den Datentyp unsigned long. Der Maximalwert davon in Millisekunden betrachtet ergibt welche maximale Zeit die du intervallmäßig abfragen kannst? Dann musste nur noch das funktionelle Prinzip der Intervall Abfragen verinnerlicht haben, dann kannste damit sehr viel machen.

Theseus erklärt millis()

GuntherB - BlinkwithoutDelay - Die Nachtwächtererklärung

Denke dabei an deine Armbanduhr o.ä. mit der du sicherlich unbewusst jeden Tag irgendwelche Zeitdifferenzen bildest.

Habe es mir nochmal genau angeschaut, aber komme einfach nicht auf den Trichter.
Mehrere Versuche bisher gescheitert. Habe mir den BlinkWithoutDelay und den Nachtwächter angeschaut.
Kann den Syntax einfach nicht auf meine Anwendung übertragen.

Wenn ich es so versuche:

int runningProgram() {
comparator = smoothedPotiVal ();
timeStampOne = millis(); //Vergangene Zeit bis zum betätigen des Programmstartknopfs

if ( millis() - timeStampOne == 86 400 000 && comparator == 1 ) { //86 400 000 ms = 1 Tag
AktionTag2();
comparator++;
}
if ( millis() - timeStampOne == 86 400 000 * 2 && comparator == 2) {
AktionTag3();
comparator++;
}

if ( millis() - timeStampOne == (86 400 000 * 3) && comparator == 3) {
AktionTag4();
comparator++;
}

Komme ich einfach nicht auf den Trichter.
Wie ich ab dem Potiwert starten kann, und die aktionen nacheinander, mit 24h verzögerung starte.....
Die Aktionen die gestartet werden sollen, werden dann auch durch eine eigene millis() Methode angesteuert nehme ich an.

expected ')' before numeric constant muss auch noch beseitigt werden. Oder ist das Syntax problem?

Code_3.ino (3.93 KB)

Zahlen dürfen keine Leerzeichen enthalten.
86 400 000 funktioniert also nicht
86400000 würde prinzipiell funktionieren.

Allerdings...
int timeStampOne;
und später
timeStampOne = millis();
Ein int kann Werte zwischen -32,768 und 32,767 annehmen (zumindest auf einem UNO), da ist ein Überlauf schnell passiert. Sollte besser unsigned long sein.

Außerdem:
timeStampOne = millis();
if ( millis() - timeStampOne == 86400000 ...
Diese Bedingung wird höchstwarhscheinlich nie erfüllt sein. Ich schätze
millis() - timeStampOne
wird in den allermeisten Fällen wohl 0 sein,
in seltenen Fällen vielleicht 1. :slight_smile:

Hallo,

warum multiplizierst du den Wert vom Intervall? Das Intervall bleibt doch immer gleich, nämlich genau aller 24h. Mit Multiplikation wäre das nachfolgende Intervall dann 48h und das nächste 96h usw. Irgendwann sprengst du den Wertebereich.

Das Prinzip ist, es wird verglichen ob die Differenzzeit (Intervall) erreicht wurde oder noch nicht. Erst wenn es erreicht wurde, wir die aktuelle Zeit (millis) abgespeichert für den nächsten Vergleich.

Wenn du vorm Vergleich das hier machst

comparator = smoothedPotiVal ();
timeStampOne = millis();

dann hebelst du das gesamte Prinzip aus. Deine Comparatoraddition ist futsch bzw. überflüssig und deine Vergleiche werden nie erfüllt. Laut meiner Meinung benötigst du nur diesen einen Vergleich der ständig aufgerufen wird. Aller 24h wird comparator um eins erhöht.

if ( millis() - timeStampOne >= 86400000)	// 86400000 ms = 1 Tag
{              
   timeStampOne += 86400000;
   comparator++;
}

Danach folgt meinetwegen die Auswertung vom comparator Wert in deinem switch case mit break und default Anweisung.

Wegen der Lückenhaften Schreibweise. Du kannst 8'6400'000 schreiben. Desweiteren solltest du dir angewöhnen im Forum Code in Code Tags einzubetten, dann ist das besser lesbar und im dümmsten Fall werden keine Zeichen verstümmelt.

Und baue dir Funktionen, sonst blickst du irgendwann im Wirrwarr von Tausenden globaler Variablen nicht mehr durch. Wo möglich lokale Variablen verwenden. Bsp.:

byte updateVergleich (const unsigned long interval)
{
  static unsigned long lastMillis {0};
  static byte comparator {0};

  if (millis() - lastMillis >= interval)
  {
    lastMillis += interval;
    comparator++;
    if(comparator >=10)		// zählt von 0...9
    {
      comparator = 0;
    }
  }

  return comparator;
}

Nutzung:

byte comp = updateVergleich(8'6400'000);     // 24h Abfrage

Die Variable comp übergibts du dann deinem switch case.

Nochwas. Der Vergleich hier mit millis wird immer >= gemacht, nie ==.
Falls der Vergleich eine ms später erfolgt, durch Programmablaufverzögerungen, würde der Vergleich verpasst und nie gültig. Verstehste? Wenn das alles verstanden ist, kannste das nach Lust und Laune passend abändern oder auch nicht ...

Bei 86400000UL wäre mir wohler...

Hallo,

bei seinem Code ja. Bei meinem Code ist 8'6400'000 ist kein nacktes Literal.

Habe versucht das gennannte umzusetzen, jetzt werden die Case´s nach dem intervall hochgeschalten, aber ständig gestartet...
Ziel ist das die aktionen an z.B. Case 1 nur einmal gestartet wird, und dann nach dem Intervall die 2. aktion durch Case2....

Habe jetzt die Hauptfunktion runningProgramm ausgelagert und versuche erstmal das richtig in gang zu bekommen.

int on_off_LED_PIN = 13;

int comparator;
int timeStampOne;

unsigned long  comp;
unsigned long interval = 10000;

void setup() {
  // put your setup code here, to run once:
Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:


  if ( millis() - timeStampOne >= 10000) {                         //Auf 10000 geändert zum testen (86400000)
    timeStampOne += 10000;
    comparator++;
    comp = updateVergleich(interval);
  }

  switch (comp) {
    case 1: Serial.println(" case 1  ");
    day1();
    break;
    case 2: Serial.println(" case 2  ");
    day2();
    break;
    case 3: Serial.println(" case 3  "); 
    day3();
    break;
    case 4: Serial.println(" case 4  "); break;
    case 5: Serial.println(" case 5  "); break;
    case 6: Serial.println(" case 6  "); break;
    case 7: Serial.println(" case 7  "); break;
    case 8: Serial.println(" case 8  "); break;
    case 9: Serial.println(" case 9  "); break;
    case 10: Serial.println(" case 10"); break;
      //default:
      comparator++;
  }


}


void day1(){
  Serial.print(" action1 ");
  }
void day2(){
  Serial.print(" action2 ");
  }
void day3(){
  Serial.print(" action3 ");
  }




int updateVergleich (unsigned long ) {        //int updateVergleich (const unsigned long interval)
  static unsigned long lastMillis;
  static byte comparator;

  if (millis() - lastMillis >= interval)
  {
    lastMillis += interval;
    comparator++;
    if (comparator >= 10) { // zählt von 0...9
      comparator = 0;
    }
  }
  return comparator;
}

Weiß jemand wie man ein Relais im Programm für eine gewisse Zeit ansteuern kann ohne delay. Die MobaTools bib bekomme ich bisher nicht zum laufen. Jemand eine Idee ???Blinkwithout delay blinkt ja ständig, das relais soll nur für eine bestimmte zeit anziehen.

Oder ein link zu einer Erklärung bezüglich MoBaTools oder ähnliche?

siehe day1() {}

//blinkWithoutDelay
const int standBy_LED_PIN = 12;
int ledState = LOW;
unsigned long previousMillis = 0;
const long intervalLED = 1000;



//MAIN
int comparator ;                                        //NEU
unsigned long timeStampOne = 10000;                    //NEU=10000 zu Testzwecken anstatt 24h
unsigned long  comp;
unsigned long interval = 10000;

//On OFF BUTTON
const int on_off_LED_PIN = 13;
int button_PIN = 10;
int buttonStatus = 0;
int buttonCounter = 0;
int lastStatus = 0;

//potiVal
int potiValueRaw;
int smoothValue;
int mappedValue;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(on_off_LED_PIN, OUTPUT);//ON OFF LED
  pinMode(button_PIN, INPUT);//ON OFF BUTTON

  pinMode(standBy_LED_PIN, OUTPUT);

  //digitalWrite(standyBy_LED_PIN, HIGH);

}




void loop() {

  show_poti_on_lcd();
  standBy_LED();
  onOffMain();

}







int onOffMain() {
  buttonStatus = digitalRead(button_PIN);
  if (buttonStatus != lastStatus) {
    if (buttonStatus == 1) {
      buttonCounter++;
    }
    else {
      digitalWrite(on_off_LED_PIN, HIGH); //Folgenden 3 HIGH/LOW Signale reverse--> LED AN
    }
  }
  if (buttonCounter % 2 == 0) {
    digitalWrite(on_off_LED_PIN, LOW);
    buttonCounter = 0;
  }
  else {
    digitalWrite(on_off_LED_PIN, HIGH);
  }
  lastStatus = buttonStatus;
  delay(10);

  if (buttonCounter == 1) {//Hier Programm das geschalten werden soll eintragen
    mainProg();

  }
  else {
    Serial.print("  OFF  ");
    Serial.println("\t");

  }
}








void mainProg() {
  // put your main code here, to run repeatedly:


  if ( millis() - timeStampOne >= 10000) {                         //Auf 10000 geändert zum testen (86400000)
    timeStampOne = timeStampOne + 10000;                                         //NEU
    comparator++;
    comp = updateVergleich(interval);

  }

  switch (comp) {
    case 1:
      day1();
      break;
    case 2:
      day2();
      break;
    case 3:
      day3();
      break;
    case 4:
      day4();
      break;

    case 5:  break;
    case 6:  break;
    case 7:  break;
    case 8:  break;
    case 9:  break;
    case 10: break;
      //default:Serial.println(" default");
      comparator++;
  }
}







void day1() {
  Serial.println("action 1");
  //Serial.print("\t");

  /*if (digitalRead(on_off_LED_PIN) == HIGH   ) {

  }*/



}
void day2() {
  Serial.println("action 2");
  // Serial.print("\t");

}
void day3() {
  Serial.println("action 3");
  //Serial.print("\t");

}

void day4() {
  Serial.println("action 4");
  //Serial.print("\t");

}




void show_poti_on_lcd() {

  Serial.print("Poti: ");
  Serial.print(smoothedPotiVal ());
  Serial.print("\t");

}





int updateVergleich (unsigned long ) {        //int updateVergleich (const unsigned long interval)
  static unsigned long lastMillis;
  static byte comparator;

  if (millis() - lastMillis >= interval)
  {
    lastMillis = lastMillis + interval;
    comparator++;

    //smoothedPotiVal ();

    if (comparator >= 60) { // zählt von 0...9
      comparator = 0;
    }
  }
  return comparator + smoothedPotiVal ();
}






int smoothedPotiVal () {                                                //gibt den gesmoothten und angepassten Wert an aufrufende Methode zurück
  potiValueRaw = analogRead(A0);
  smoothValue = 0.7 * smoothValue + 0.3 * analogRead(A0);
  mappedValue = smoothValue / 17;
  return mappedValue;                                                   //Gibt den gesmoothten Potiwert  an die Aufrufende methode zurück
}


void standBy_LED() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= intervalLED) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    digitalWrite(standBy_LED_PIN, ledState);
  }
}

engineeeer:
Blinkwithout delay blinkt ja ständig, das relais soll nur für eine bestimmte zeit anziehen.

Dann hast Du BlinkWithoutDelay nicht richtig verstanden. Das kann auch das. Du brauchst halt noch eine Statusvariable, ob Du das einmalige Anschalten schon durch hast.

Gruß Tommy

Hallo,

mit Verlaub, wenn ich das in #16 und #17 sehe, dann empfehle ich dir dringend dich mit den Grundlagen zubeschäftigen. Ansonsten eierst du nur rum. Allein schon Datentypen und Parameter.