Subroutine beenden

Hallo zusammen!

Für meinen Sketch brauche ich eine Subroutine außerhalb des Loops.

void loop() {
  input = "";

  while (Serial.available() > 0) {
    input += (char) Serial.read();
    delay(5);

    if (input == "On") {
      digitalWrite(13, HIGH);
    }
    if (input == "Off") {
      digitalWrite(13, LOW);
    }
    if (input == "Blink") {
      Blink();
    }
  }
}
void Blink() {
  Start:
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
  goto Start; 
}

wie man sieht wird die Led mit "On" eingeschaltet, mit "Off" aus und mit "Blink soll sie blinken, mein Problem ist jetzt wie kann ich die Subroutine wieder beenden?

Hoffe jemand weis Rat, danke im Voraus.

mfg

s-fr:
...
wie man sieht wird die Led mit "On" eingeschaltet, mit "Off" aus und mit "Blink soll sie blinken, mein Problem ist jetzt wie kann ich die Subroutine wieder beenden?

Du könntest beim Aufruf der blink-Funktion übergeben, wie oft oder wie lange geblinkt werden soll. In der Funktion prüfst Du dann, ob oft oder lange genug geblinkt wurde und kehrst mit return; zum aufrufenden Programm zurück.

Oder Du programmierst eine Schleife, anstatt goto zu verwenden. Dann wird entsprechend oft geblinkt und wenn die Schleife beendet ist, wird im Hauptprogramm weitergemacht. Goto ist eh verpönt.

Gruß

Gregor

Die Subroutine, wie du sie nennst, heißt "Funktion" und wird, wenn keine blockierende Schleife oder was ähnliches eingebaut ist, automatisch verlassen.
Das "goto Start" kannst du dir sparen.

HotSystems:
Das “goto Start” kannst du dir sparen.

Wie ich das verstehe, springt das goto zur Marke am Anfang der Funktion, weshalb die Funktion eben endlos ausgeführt wird.

Gruß

Gregor

Die Funktion sollte endlos ausgeführt werden, bis man sie eben beendet. Gibt es keine einfache Methode eine Funktion zu schließen?

Die Funktion sollte endlos ausgeführt werden, bis man sie eben beendet.

Dieses Vorgehen solltest du dir gar nicht erst angewöhnen. Du hast in loop() schon eine Schleife. In der kannst du eine Funktion ständig aufrufen und wenn nichts zu tun ist wird sie sofort beendet und kehrt nach loop() zurück. So kann man dort noch andere Dinge tun. Wenn das schnell geschieht (also keine Verzögerungen in der Funktion sind) kann so mehrere Dinge quasi-gleichzeitig erledigen.

In deinem Fall willst du wie so oft einen Zustandsautomaten. Du fragst also die serielle Schnittelle ab und je nach Eingabe wird eine Zustandsvariable gesetzt. Die sagt dir was in diesem loop() Durchlauf zu tun ist, z.B. Blinken. Und je nach dem wird eine andere Funktion aufgerufen.
So kannst du dann neben dem Blinken noch die serielle Schnittstelle ständig abfragen. Wenn du das nicht tust bist du in der Funktion gefangen und kannst den Zustand nicht mehr ändern

Gibt es keine einfache Methode eine Funktion zu schließen?

return

wie kann ich die Subroutine wieder beenden?

Um Funktionen zu beenden, wurde extra ein Schlüsselwort erfunden: return

Das ist aber immer noch nicht, was der TO will.

Er hat eigentlich die falsche Frage gestellt.

ElEspanol:
Das ist aber immer noch nicht, was der TO will.

Er hat eigentlich die falsche Frage gestellt.

Ja, das sehe ich jetzt genauso.
In dieser einfachen Form wurde ich die "Blink-Funktion" immer aus der Loop aufrufen und den Aufruf über eine if-Abfrage einer Status Variable steuern.

Und wenn der TO dann noch die delays durch eine Funktion mit millis ersetzt (Beispiel "BlinkWithoutDelay"), dann wird auch der Sketch nicht blockiert.

Ja, das sehe ich jetzt genauso.

Ich auch.

Aber ich kann nur die gestellte Frage beantworten.

Je ungestellter die Frage, desto wahrscheinlicher, dass ich mit der Antwort daneben liege.
Leider ist meine Kristallkugel gerade in Urlaub.
(hat sie auch dringend nötig)

OK, ich kann vermuten....

Dann:
s-fr möchte eine, auf kooperativem Multitasking beruhende, Ablaufsteuerung bauen.

Tipp:
Return ist dabei sehr hilfreich!
Egal ob implizit, oder explizit.

Also ich, mit meinem bescheidenen Wissen, würde sagen,

schmeiß die Zeilen

  • Start;
    und
  • goto Start;
    raus und schon müsste es funktionieren.

Gruß

MiReu

Genau genommen will er ja über Serial steuern, ob die LED den Zustand On, OFF, oder Blinken haben soll. Die LED soll also wohl solange blinken, bis ain anderes Kommando eintrifft.
Somit braucht er BlinkWithoutDelay und dabei jeden Blink-Zyklus von der Loop aus aufgerufen.

LG Stefan

ElEspanol:
Das ist aber immer noch nicht, was der TO will.

Ja, aber was er will ist Murks. Das mag vielleicht funktionieren wenn man die richtige Syntax verwendet ist aber vom Design her keine vernünftige Lösung.

Außerdem blinkt es dann vielleicht, aber die Abfrage der seriellen Schnittstelle geht nicht flüssig

Wenn die Funktion Blink() wenig Zeit braucht, wird es ganz einfach.
(Wenn Blink gar keine Zeit braucht, wäre es noch eleganter, aber das lassen wir vorerst mal weg).

Wenn das Kommando über Serial einfacher ist, wird es noch einfacher.
Ich schlage mal vor, der Sketch reagiert auf die Zeichen { '0' , '1' , 'B' } und ignoriert alles andere.

const int INTERVALL = 500; // Blinkfrequenz
byte ledStatus; // 0 = aus, 1 = an, 2 = blinkend an, 3 = blinkend aus

void loop() {
  while (Serial.available()) {
   int c = Serial.read();
   switch (c) {
    case '0': ledStatus = 0; break;
    case '1': ledStatus = 1; break;
    case 'B': 
    case 'b': ledStatus = 2; break;
    default: break;  // alles andere auch ignorieren
   }
  }

  if (ledStaus > 1) blink();
  else digitalWrite(13, ledStatus);  // AN oder AUS Dauerlicht
}

void blink() {
// Primitiv-Version: kehrt erst nach Ablauf einer Blink-Phase zurück
// Experten verstehen BlinkWithoutDelay

   if ( ledStatus == 2) {
     digitalWrite(13, HIGH);
     ledStatus = 3;
   } else {
     digitalWrite(13, LOW);
     ledStatus = 2;
   }
   delay (INTERVALL);
}

@s-fr: Du hattest wohl noch eine falsche Vorstellung des Begriffs "Programm".
Das Programm eines Controllers läuft nach dem Start "ewig".
Und wofür die Funktion loop() gedacht ist. (loop() ist nicht main() und sollte nicht ewig laufen, sondern optimalerweise gar keine Zeit brauchen)

Bzw. wie andere eigene Funktionen dabei mitspielen.
Die auch wenig (bis optimalerweise gar keine) Zeit brauchen.

Hi s-fr,
der code von michael_x funktioniert hervorragend.
Ich habe Dir hier noch den Code, den Du an Anfang gepostet hast angepasst und ergänzt. Dieser basiert
weiterhin auf den Befehlen 'On', 'Off' und 'Blink'. Ausserdem hab ich das void Blink() ohne delay gemacht,
wodurch das Programm schneller auf Befehlsänderung während dem Blinken reagiert. Ist wichtig bei langem Interval = niedrigen Blinkfrequenz.

#define LEDpin 13                  // Pin andem die LED angeschlossen ist 
unsigned long interval = 500;      // interval gibt an, wie lange LED 'on' bzw. 'off' ist beim Blinken
unsigned long intervalStart = 0;   // IntervalStart merkt sich den Start des Intervals für Test ob Intervalzeit abgelaufen ist
bool blinken = false;              // Merker ob für Blinken aus/ein  (false = kein Blinken)
bool ledStatus = 0;                // Status der LED beim Binken (0 = LED ist aus) 

String input;                      // Nimmt das Komanndo über die Serial auf. 


void setup() {
  // put your setup code here, to run once:
pinMode(LEDpin, OUTPUT);          // LEDPin als Ausgang definieren    
Serial.begin(9600);               
}

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

  while (Serial.available() > 0) {
    input += (char) Serial.read();
    delay(5);

    if (input == "On") {
      digitalWrite(LEDpin, HIGH);  // LEDpin ist oben als Pin 13 definiert
      blinken = false;             // kein Blinken
    }
    if (input == "Off") {
      blinken = false;             // kein Blinken
      digitalWrite(LEDpin, LOW);   // LEDpin ist oben als Pin 13 definiert
    }
    if (input == "Blink") {
      blinken = true;              // Blinken einschalten
    }
  }
  
  if (blinken == true) {          // Blinken ist aktiv
    if (millis() - intervalStart > interval)  Blink();    // Wenn das Interval abgelaufen ist, gehe Blinken  
  }
}

void Blink() {
  ledStatus = !ledStatus;             // Wenn led vorher aus, dann led ein,   wenn led vorher ein, dann led aus
  digitalWrite(LEDpin, ledStatus);    // LED ein- oder ausschalten
  intervalStart = millis();           // Startzeit für nächstes Interval merken
}

Ist sicher nicht die aller eleganteste Lösung, ich wollte aber bewusst bei dem bleiben, was in Deinem Sketch bereits funktioniert hat. Lediglich die Definition des Pin's an dem die LED hängt, hab ich flexibler gestaltet.

LG Stefan

struct Led
{
  enum Auftrag {AUS,EIN,BLINK} auftrag;
  byte ledPin;
  bool status; // statusmerker blinkphase
  unsigned long interval;  
  unsigned long timestamp;

  Led(byte ledPin,unsigned long interval):ledPin(ledPin),interval(interval){}

  void nachricht(Auftrag nachricht)
  {
    auftrag = nachricht;
    switch(auftrag)
    {
      case AUS   : digitalWrite(ledPin,LOW); break;
      case BLINK : status = HIGH;
      case EIN   : digitalWrite(ledPin,HIGH); break;
    }
  }
  
  void begin()
  {
    pinMode(ledPin,OUTPUT);
    nachricht(AUS);
  }
  
  void run()
  {
    if(BLINK == auftrag)
    {
      if(millis()-timestamp>=interval)
      {
        timestamp = millis();
        status    = !status;
        digitalWrite(ledPin,status);
      }
    }else
    {
      timestamp = millis();
    }
  }
};


Led led(13,500);

void serialEvent()
{
   while(Serial.available()) 
   {
     switch((char)Serial.read())
     {
      case 'e' : led.nachricht(Led::EIN);   break;
      case 'a' : led.nachricht(Led::AUS);   break;
      case 'b' : led.nachricht(Led::BLINK); break;
     }
   }
}


void setup() 
{
  Serial.begin(9600);
  led.begin();
}

void loop() 
{
  led.run();
}

@combie
Hoffentlich kappiert der TO Deinen Code etwas schneller als ich. :slight_smile:

LG Stefan

Das hoffe ich auch!
:smiling_imp: :smiling_imp: :smiling_imp:

Deltaflyer:
@combie
Hoffentlich kappiert der TO Deinen Code etwas schneller als ich. :slight_smile:

Hoffentlich kapiert er ihn überhaupt, und lässt sich nicht verschrecken. Wäre sonst schade um das wirklich schöne Stück. 8)

Immerhin hat er jetzt genug Varianten.

Wäre sonst schade um das wirklich schöne Stück.

Danke für die Blumen!

verschrecken

:o Habe ich etwa etwas übertrieben? :o