Aus Unterprogramm mit Button herauskommen

Hallo zusammen,

ich bin dabei eine RGB Lampe mit Arduino zu bauen und habe dabei ein Problem. Ich kann in meinem Programm über ein Menü Unterprogramme auswählen um den LED's zu sagen was sie machen sollen. Mir geht es darum die PWM's mit Potis einzustellen was bereits funktioniert. Das Unterprogramm wird durch den IF Befehl andauernd wiederholt da die Bedingung esc_val=0 erfüllt ist wie bekomme ich es hin das ich in das Hauptprogramm zurück komme.

void analog_p(){

  int esc_val =0;



  red_pot_val = analogRead(red_pot)/4;
  green_pot_val = analogRead(green_pot)/4;
  blue_pot_val = analogRead(blue_pot)/4;

  analogWrite(green,green_pot_val);
  analogWrite(red,red_pot_val);
  analogWrite(blue,blue_pot_val);
  lcd.clear();
  lcd.print("ANALOG");
  lcd.setCursor(0,1);
  lcd.print("R");
  lcd.print(red_pot_val);
  lcd.setCursor(5,1);
  lcd.print("G");
  lcd.print(green_pot_val);
  lcd.setCursor(10,1);
  lcd.print("B");
  lcd.print(blue_pot_val);
  delay(500);


  
  if(esc_val == 0){

    analog_p();
  }
  else{

    loop();

  }  
  
}

hier ist die Bedingung um esc_val zu erhöhen aus dem hauptteil

if(digitalRead(esc))
  {
    esc_val++;
    delay(100);

  }

Was muss ich jetzt alles um Unterprogramm einfügen sodass es funktioniert?

Vielen dank schon mal für eure Antworten :slight_smile:

Eine Methode kannst du abbrechen in dem du "return" schreibst. Da der Rückgabe-Wert void (als nichts) ist, muss kein Wert zurückgegeben werden.

Also in Pseudocode:

if(Bedingung == true)
{
return;
}

Das Programm macht dann mit der nächsten Zeile nach dem ursprünglichen Methoden-Aufruf weiter.

else
{
    loop();
}

Sowas darfst du nicht machen! Der Controller merkt sich schon die Rücksprung-Adresse wenn die Methode aufgerufen wurde. Wenn du dann so per Hand zurückspringst wird diese nicht vom Stack entfernt. Oder er kehrt nach Beenden der aufgerufenen Methode in die Unter-Methode zurück. Nach ein paar mal hin und her stimmt dann unter Umständen der Programmfluss wieder, aber es ist sehr unsicher.

Eine Methode sich selbst aufrufen zu lassen ist aus dem gleichen Grund keine gute Idee. Das nennt sich Rekursion und hat legitime Anwendungen, aber es ist durch das Speichern der Rücksprung-Adresse sehr Speicher-intensiv. Hier macht es einfach keinen Sinn.

Wenn du den Code in der Methode immer wieder aufrufen willst, dann mach eine while-Schleife herum:

while(Bedingung == false)
{
     //Code hier
}

Dann führt er das solange aus bis die Bedingung eintritt (z.B. Knopf drücken).

Vielen dank ich werde es mal ich werde mich mal dran versuchen :wink:

Hier ist jetzt das komplette Programm wie komme ich jetzt zurück ich bekomme irgend wie esc_val im unterprogrmm analog_p (was ganz unten ist) nicht auf den wert 1 oder so das er zurück ins Hauptprogramm kommt.

rgb_progrmm_v1_1.ino (5.77 KB)

Die Abfrage muss in die while-Schleife damit sie immer wieder abgerufen wird. Sonst ändert sich der Wert der Variablen nie und die steckst für immer in der Schleife.

int esc_val = 0;

while(true)
{
     //Blah

      if(digitalRead(esc))
      {
         esc_val++;
         delay(100);

       }
       
       if(esc_val > 0)
          return;
  
}  //end while

Theoretisch könnte die Abfrage des Tasters auch direkt in die while-Schleife (wo die Bedingung abgefragt wird), aber durch das Entprellen ist das so besser :slight_smile:

esc_val muss auch nicht global sein

Alternative:

int esc_val = 0;

while(esc_val == 0)
{
     //Blah

      if(digitalRead(esc))
      {
         esc_val++;
         delay(100);

      }
  
}  //end while

Da kannst du dir das return ganz sparen. Die while-Schleife wird beendet und danach ist gleich die Methode zu Ende.

Jetzt klappt es vielen Dank für die Hilfe :slight_smile:

:slight_smile:

Habe nochmal ne kürzere Alternative oben hingeschrieben, mit der du dir die if-Abfrage auch sparen kannst. Schadet aber nichts wenn du dir über die Optionen im Klaren bist, da es für beide Fälle Anwendungen gibt. z.B. wenn die Bedingung mehrere Werte annehmen kann und man nur ein einem Fall zurückkehren will und sonst etwas anderes machen will.

While-Schleifen kann man auch mit "break" beenden. Daher würde das hier auch funktionieren und ist in diesem Fall wahrscheinlich das sauberste:

while(true)
{
     //Blah

      if(digitalRead(esc))
      {
          delay(100);
          break;
       }
        
}  //end while

Wie gesagt, eben weil nach while-Schleife die Methode von selbst aufhört.

Bei allen außer einem Unterprogramm funktioniert es. Kann mir jemand sagen warum es nicht funktioniert?

void fade(){



  int esc_val = 0;


  while(esc_val == 0){

    int val_red = 255;
    int val_green = 0;
    int val_blue = 0;

    lcd.clear();
    lcd.print("->Durchlauf");
    
    red_pot_val = analogRead(red_pot);


      while (val_red != 0)
      {
        analogWrite(red,val_red);
        analogWrite(green,val_green);
        red_pot_val = analogRead(red_pot);


        val_red--;
        val_green++;

        delay(red_pot_val);

      }


    while (val_green != 0)
    {
      analogWrite(green,val_green);
      analogWrite(blue,val_blue);
      red_pot_val = analogRead(red_pot);


      val_green--;
      val_blue++;

      delay(red_pot_val);

    }  


    while (val_blue != 0)
    {
      analogWrite(blue,val_blue);
      analogWrite(red,val_red);
      red_pot_val = analogRead(red_pot);


     val_blue--;
     val_red++;

     delay(red_pot_val);

    }  
    if(digitalRead(esc))
    {
      esc_val++;
      delay(100);

    }

    if(esc_val > 0){
      return;
    }
  }
}

Du hast da zwei Versionen kombiniert. Da habe ich dich wahrscheinlich zu sehr verwirrt :slight_smile:

Wenn du esc_val in der while-Schleife abfragst brauchst da das manuelle Return nicht. Die while-Schleife beendet sich automatisch wenn die Bedingung false ist. Und danach beendet sich gleich die Methode, da du am Ende bist.

Die Variable esc_val brauchst du auch überhaupt nicht. Ich bin da halt am Anfang erst bei deinem Code geblieben. Hätte ich vielleicht gleich verwerfen sollen. Schau dir mal mein vorheriges Post an. While(true){} und dann einfach "break" wenn die Bedingung zutrifft. Das ist in diesem Fall am kürzesten.

Kann es sein, dass du irgendwie noch in den inneren while-Schleifen drinnen hängst und daher der Taster nie abgefragt wird? Generell wird der Taster auch nur am Ende kurz abgefragt und dann macht er 3 while-Schleifen. Je nachdem wie lange die dauern bekommst du die Aktion gar nicht mit.

2 Grundsätzliche Dinge:

  • Code bitte mit CODE-Tags einschließen
  • In C gibt es keine "Unterprogramme" sondern Funktionen.

Zu deinem Problem:

loop()
{
  if( Taster nicht gedrückt)
    {
      Funktion aufrufen
    }
}

Dazu mußt Du die Funktion fade umschreiben.
So wie Du sie geschrieben hast mußt Du oft länger als 3 Sekunden warten bis der AUS-Taster gelesen wird. Machs mit millis und globalen Variablen welche Farbe gerade goch oder runtergezählt wird. Außerdem würde ich auch kontrollieren ob die min bzw maximalwerte des PWM erreicht sind damit kein Überlauf und damit die Farbe plötzlich hell ist.

Grüße Uwe

Ich habe mir das gerade mal geschaut. Das Beenden der Funktion funktioniert nur beim wechsel in die nächste while Schleife. Die maximal und die minimal PWM Werte werden jeweils erreicht. Muss ich jetzt das rücksetzen in jede while schleife einzeln setzen?

Du musst nur vor dem dekrementieren oder inkrementieren abfragen ob du schon am Minimum oder Maximum bist:

if(val_red > 0)
val-red--;

Obwohl das glaube ich nicht unbedingt nötig ist. val_red wird ja schon durch die Abfrage while(val_red !=0) begrenzt. Wenn er bei 0 ist hört es auf.

Da du hier nur Werte zwischen 0 und 255 willst, nimm am besten gleich byte als Datentyp und nicht int

Du kannst auch erst mal nur für rot machen und dann die anderen Farben hinten dran hängen wenn das geht.

void fadeRed()
{
 while(true)
 {      
      long previousMillis = 0;
      red_pot_val = analogRead(red_pot);

      while (val_red != 0)
      {
        unsigned long currentMillis = millis();
        if(currentMillis - previousMillis > red_pot_val) 
        {
             previousMillis = currentMillis; 

             analogWrite(red,val_red);
             analogWrite(green,val_green);
             red_pot_val = analogRead(red_pot);

             if(val_red > 0)
                 valred--;
       
              if(val_green < 255)
                 val_green++;
        } //end if

        if(digitalRead(esc))
        {
            delay(100);
            return;
        }
      } //end while


        /* Hier die anderen Farben wie oben */

  } //end while
}

Damit macht er das erst mal mit rot und fragt ständig den Taster ab. Das Delay blockiert hierbei nicht.

P.S.: Der Code ist nicht unbedingt 100%ig korrekt und wurde nicht getestet