Arduino Forum

International => Deutsch => Topic started by: combie on Jul 28, 2016, 11:53 am

Title: [Projekt] Multitasking
Post by: combie on Jul 28, 2016, 11:53 am
Hi.

Das Thema (kooperatives) Multitasking und endliche Automaten wird immer wieder gerne genommen. Das ist auch nicht wirklich ein großes Wunder, denn viele, ja fast alle, Arduino Programme lassen sich auf diese "Software Design Pattern" runter brechen.

Die Stichworte mal als Sammlung:
Endlicher Automat, State Machine, Multitasking,
Coroutinen, Ablaufsteuerung, Schrittkette,
BlinkWithoutDelay, Ersetzen von delay(),
Unterbrechen von Schleifen

Als Wunderwaffe gegen alle diese Sorgen möchte ich euch meine TaskMacros
vorstellen. Siehe, die Arduino Library  im Anhang.
OK, ok, auch Wunderwaffen können nicht alles, bzw. haben ihre Einschränkungen...


Der Werkzeugkasten:

Als erstes mal die Umgrenzung einer Task:
taskBegin() Damit wird ein Task Block eingeleitet
taskEnd()   Das Ende eines Taskblockes

Taskkontrolle:
taskSwitch() Gibt die Rechenzeit an andere Tasks ab. Es wird im nächsten Durchlauf an dieser Stelle weiter gearbeitet.
taskPause(interval) Diese Task pausiert, bis interval abgelaufen.
taskWaitFor(condition)  Diese Task pausiert, bis condition true wird.

Genug der Theorie!
Hier mal ein Beispiel für BlinkMitDelay:
Code: [Select]

void blink()
{
   static byte count = 0;
   while(1)   // Endlosschleife, totale Blockade
   {
      count++;
      digitalWrite(13,count & 1);
      delay(500);  // blockiert für 500 ms
   }   
}

void setup()
{
  pinMode(13, OUTPUT);
}

void loop
{
  blink();
}

Das funktioniert!

Die Nachteile dieses Vorgehens sind natürlich offensichtlich.
Sowohl die while Schleife, als auch das Delay, blockieren.
Möchte man noch zusätzlichen Kram in loop() unter bringen, wird man daran versagen.

Der Umbau auf BlinkOhneDelay:
Code: [Select]

#include <TaskMacro.h>

void blink()
{
   static byte count = 0;
   
   taskBegin();
   while(1)   // blockiert dank der TaskPause nicht
   {
      count++;
      digitalWrite(13,count & 1);
      taskPause(500);   // gibt Rechenzeit ab
   }
   taskEnd();   
}

void setup()
{
  pinMode(13, OUTPUT);
}

void loop
{
  blink();
}



Noch ein Beispiel....
Blink so  modifiziert, dass man eine kurze Leuchtdauer (100ms) und eine lange Dunkelphase (700ms) bekommt
Code: [Select]

void blink()
{
   taskBegin();
   while(1)   // blockiert dank der TaskPause nicht
   {
      // Schritt 1
      digitalWrite(13,1);  // LED ein
      taskPause(100);   // gibt Rechenzeit ab
     
       // Schritt 2
      digitalWrite(13,0);   // LED aus
      taskPause(700);   // gibt Rechenzeit ab
     
      // gehe zu Schritt 1
   }
   taskEnd();   
}

Eine einfache Form der Schrittkette.


In den Beispielen finden sich noch ein paar Varianten. Z.B. wie man For Schleifen unterbrechbar macht. Und das Warten auf Flags.


Noch was erwähnenswertes? .....
Klar!

1:
Man sollte bedenken, dass lokale Variablen immer nur einen Durchlauf überleben. Man wird in solchen Tasks also eher statische, oder globale, Variablen verwenden.

2:
Dinge, welche bei jedem Aufruf erledigt werden wollen, haben sich vor  taskBegin(); einzufinden.

3:
Switch/Case sind nicht in den Tasks verwendbar, da ich diese Kontrollstruktur darin für die Steuerung verwende.

Ansonsten:
Viel Spaß mit diesen Macros....!

Verbesserungen?
Vorschläge?


PS:
An dieser Stelle möchte ich mich insbesondere bei "Adam Dunkel" und  "Donald E. Knuth" bedanken.





Edit:
Neue Version hochgeladen
(Die alte Version wurde 15 mal runter geladen)

Schritte können jetzt benannt und angesprungen werden. Das hilft deutlich beim Schrittkettenbau. Zu finden im Schrittketten Beispiel.
Dieses Beispiel besteht aus 3 Tasks:
1. Taste entprellen
2. Blinken in 2 Geschwindigkeiten, per Taste umschaltbar
3. Zeigen der Loopdurchläufe pro Sekunde (ca 62000 auf einem Uno)
Sicherlich könnte man dieses Beispiel noch etwas mehr straffen/optimieren....

Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Jul 28, 2016, 04:18 pm
Hast Du die Makros schon mal ausprobiert?
Was soll das do - while(0) in taskSwitch?

Wenn taskSwitch() mark=__LINE__ setzt, und case __LINE__ ans Ende der Zeile, wie  soll dann der Rest von taskPause (vor taskSwitch) mehr als einmal durchlaufen werden?
IMO sollte taskPause und taskWaitFor eher so aussehen:
Code: [Select]
#define taskPause(interval) timeStamp = millis(); taskSwitch(); if ((millis() - timeStamp) < (interval)) return;
#define taskWaitFor(condition)  taskSwitch(); if (!(condition)) return;

Ich vermute außerdem, daß jede Task in genau 1 Funktion liegen muß, in der mark und der switch definiert ist. Aufrufe der Makros in anderen Funktionen dürften nicht compilieren.
Title: Re: [Projekt] Multitasking
Post by: combie on Jul 28, 2016, 04:29 pm
Quote
Ich vermute außerdem, daß jede Task in genau 1 Funktion liegen muß, in der mark und der switch definiert ist.
Eine Task befindet sich in einer Funktion
Eingerahmt von taskBegin() und taskEnd()
Nur der Speicherverbrauch setzt der Anzahl Funktionen/Tasks Grenzen.
Und, keine Datei, welche eine (oder mehrere) Task beinhaltet, darf mehr als 32kZeilen haben.
(könnte man mit unsigned int auf 64kZeilen erhöhen.)

Quote
Aufrufe der Makros in anderen Funktionen dürften nicht compilieren.
Wenn in der Funktion kein  taskBegin() und taskEnd() in der richtigen Reihenfolge steckt, dann machen die TaskKontroll Dinger keinen Sinn, und werden Errors werfen.

Quote
Hast Du die Makros schon mal ausprobiert?
Natürlich!
;-) Du auch?  ;-)

Quote
Was soll das do - while(0) in taskSwitch?
Weil man sonst nicht den Einsprungspunkt für den nächsten Durchlauf hin bekommt.
(zumindest ich nicht, bisher)
Dieser Einsprungspunkt ist für jeden Taskwechsel unabdingbar nötig.
Der Compiler optimiert die tote Schleife weg.
Sie dient also nur dazu Syntaxfehler zu vermeiden.


Quote
IMO sollte taskPause und taskWaitFor eher so aussehen:
Das wird nicht funktionieren.
Denn dann wird kein Wiedereintrittspunkt für die Prüfung generiert. Die Prüfung muss ja öfter wiederholt werden, da bietet sich eine Schleife an. Ein if würde ja nur einmal prüfen.



Quote
Wenn taskSwitch() mark=__LINE__ setzt, und case __LINE__ ans Ende der Zeile, wie  soll dann der Rest von taskPause (vor taskSwitch) mehr als einmal durchlaufen werden?
Die Frage verstehe ich nicht.....
Eigentlich kann ich dazu nur sagen: Es funktioniert!


PS:
Und Danke für den Kommentar/Nachfrage!


Und ja!
Ich treibe da bösen Schindluder mit dem switch/case Konstrukt.
Sonst bekommt man keine Schleifen aufgebrochen, ohne jeder Task einen eigenen Stack zu spendieren. Und das, das kann z.B. RTOS viel besser als ich.

Die Funktion der ProtoThreads ist vielleicht noch am ehesten mit diesen TaskMacros zu vergleichen.






Title: Re: [Projekt] Multitasking
Post by: michael_x on Jul 28, 2016, 06:20 pm
Sehe ich vermutlich richtig:  taskBegin und taskEnd allein nutzt nix:
Code: [Select]
void blink() {
   taskBegin();
   while(1)   
   {
      digitalWrite(13,!digitalRead(13));
      delay(100);
   }   
   Serial.println("Hier hin oder gar aus blink wieder raus kommt er nie"); 
   taskEnd();


Wenn man es richtig verwendet, funktioniert es wohl.
- geht nur innerhalb einer void Funktion
- taskBegin und taskEnd dürfen je Funktion nur einmal in richtiger Reihenfolge als Paar auftreten
- dazwischen dürfen taskPause taskWaitFor taskSwitch stehen (auf jeweils eigenen Zeilen in der gleichen Funktion)
- diese führen gegebenenfalls ein return aus. Es geht also nicht nach taskEnd(); weiter

Wir haben mal vor Urzeiten c-Makros geschrieben, die helfen sollten, etwas zu programmieren das wie FORTRAN aussah ( #define END } und sowas )

Ich mags nicht
- weil es was anderes macht als wie es aussieht
- weil Fehlermeldungen nur verständlich sind, wenn man die Makrodefinition kennt und versteht

Aber hübsch ist es schon, jedenfalls für Leute die auch gut ohne diese Makros auskämen ;)
Title: Re: [Projekt] Multitasking
Post by: combie on Jul 28, 2016, 06:39 pm
Quote
Sehe ich vermutlich richtig:  taskBegin und taskEnd allein nutzt nix:
Richtig!
Sonst bräuchte jede Task ihren eigenen Stack.
Preemptives Multitasking, wäre das dann.

Quote
Wenn man es richtig verwendet, funktioniert es wohl.
Du hast die Bedingungen korrekt erkannt.
Ich werde diese als Anregung für die Erweiterung der Doku nehmen.

Quote
- geht nur innerhalb einer void Funktion
Da habe ich lange dran geknackst...
Aber keinen eleganten Weg gefunden.

Quote
Aber hübsch ist es schon, jedenfalls für Leute die auch gut ohne diese Makros auskämen
Danke, das geht runter, wie Honig!


Quote
Es geht also nicht nach taskEnd(); weiter
Doch schon...
Unter Umständen...
z.B. wenn sich in der Task keine Endlosschleife befindet.

Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Jul 28, 2016, 07:29 pm
Erst mal: ich habe das Ampel-Beispiel jetzt ausprobiert, und es funktioniert tatsächlich. Bis hierhin sieht die Bibliothek wirklich brauchbar aus :-)

Mich hätte jetzt aber brennend interessiert, was der Präprozessor draus macht - wie komme ich da dran? Vielleicht macht der ja noch mehr unentdeckte Schweinereien?

Es sieht tatsächlich so aus, als ob der Compiler ohne Schleife den case __LINE__ wegoptimiert. Igitt igitt, aber gerade noch erklärbar.

Mein Vorschlag für taskPause funktioniert durchaus, da dürften beide Varianten den gleichen Code erzeugen.

Bei taskWaitFor hingegen tritt das Problem auf, unter welchen Umständen der nachfolgende Code ausgeführt wird. Folgt darauf kein weiterer taskSwitch, wird die Bedingung bei jedem Durchlauf erneut geprüft und der nachfolgende Code so lange ausgeführt, als die Bedingung wahr ist. Wird die Bedingung absichtlich oder versehentlich false, bleibt die Task hängen. Deshalb würde ich sicherheitshalber ein weiteres taskSwitch einfügen, wenn die Bedingung das erste mal true war, damit sie nicht weiterhin geprüft wird. Leider kriege ich das per Makro nicht hin, da case __LINE__ ja schon vergeben ist :-(

Ähnlich würde ich auch bei taskEnd ein taskSwitch einfügen, damit das Ende des switch (ab dem letzten taskSwitch) nicht endlos wiederholt wird. Das dürfte dann auch das Problem mit taskWaitFor beheben. Oder wait=0 setzen, damit es bei taskBegin weitergeht - dann kann man sogar die while(1) Schleife zwischen taskBegin und taskEnd einsparen.

Damit reduzieren sich meine Änderungsvorschläge hierauf:
Code: [Select]
#define taskEnd() wait = 0; }



Was die void Funktion betrifft, die könnte man mit taskBegin kombinieren, ähnlich ISR:
Code: [Select]
TASK() //incl. taskBegin
...
ENDTASK() //incl. taskEnd
Geschweifte Klammern je nach Geschmack hinzufügen.
Title: Re: [Projekt] Multitasking
Post by: combie on Jul 28, 2016, 08:09 pm
Quote
Mich hätte jetzt aber brennend interessiert, was der Präprozessor draus macht - wie komme ich da dran?
Kein Problem... ;-)
Build Meldungen aktivieren.
Dann zeigts dir das  Build Verzeichnis.
Im Build Verzeichnis findet sich ein Ordner namens preproc.
Da drin findest du es aufgedröselt.
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Jul 28, 2016, 08:26 pm
Man könnte ja auch noch in beginTask ein neues delay() Makro hinzufügen bzw. aktivieren, und in endTask wieder löschen. Aber bei dem dafür notwendigen Aufwand erscheint mir taskPause dann doch eleganter...
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Jul 28, 2016, 09:04 pm
Build Meldungen aktivieren.
Im Build Verzeichnis findet sich ein Ordner preproc
Da findest du es aufgedröselt.
Danke für dieses Puzzle, aber ich hab's noch geschafft ;-)

Seltsamerweise funktioniert es jetzt auch ohne die Schleife in beginTask. Sehr undurchsichtig :-(
Title: Re: [Projekt] Multitasking
Post by: Doc_Arduino on Jul 29, 2016, 12:24 pm
Hallo combie,

schön für die Mühen, aber ist das nicht ein umgedrehtes Intervall?
Sieht alles nach negativer Logik aus.
Und sollten Multitasking Aufgaben nicht eher vom OS verwaltet und ausgeführt werden statt vom Userprogramm?

Kannst aber gern weitermachen. Bin gespannt was noch bei rauskommt.  :)
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Jul 29, 2016, 02:02 pm
Je länger ich mich mit den Makros befasse, desto mehr bin ich davon begeistert :-)

Vor allem Anfänger dürften davon profitieren, die von den komplizierten Lösungen wie BlinkWithoutDelay abgeschreckt sind. Aber auch Fortgeschrittene können umfangreicheren Code damit sehr übersichtlich hinschreiben, besser als mit verschachtelten oder scheinbar unmotiviert aufeinanderfolgenden umfangreichen Abfragen. Die Effizienz könnte sogar höher sein als mit der sonst üblichen Technik, zumindest habe ich in dieser Richtung keine ernsthaften Bedenken.

Mich persönlich stört die while-Schleife zwischen taskBegin und taskEnd, und die läßt sich einfach vermeiden, wenn man in taskEnd mark=0 setzt. Eine Frage ist dabei, ob diese Schleife ein besseres Gefühl dafür vermittelt, wie der Code tatsächlich abläuft. Läßt man die Schleife weg, weil die Task nur einmal ausgeführt werden soll, dann hört sie tatsächlich garnicht auf, sondern wiederholt den letzten Teil (ab dem letzten case label) endlos. Dieses Verhalten empfinde ich jedenfalls wenig intuitiv.

Weitere Verbesserungsmöglichkeiten sind mir schon eingefallen, allerdings noch keine Lösungen dazu. Ich sollte die wohl einzeln zur Debatte stellen.
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Jul 29, 2016, 02:12 pm
Das Timing mit taskPause ist etwas unpräzise, wie man am Ampel Beispiel sehen kann. Ersetzt man dort in piepser() den letzten taskSwitch durch TaskPause(500), dann müßte der letzte Piep eigentlich nach dem Umschalten der Ampel (alle 2000ms) erfolgen - tut er aber meist nicht! Für zeitgenaues Loggen oder die Erzeugung von Impulsen ist das eher ungeeignet.

Vielleicht könnten dafür zwei Makros verwendet werden, mit taskPause oder taskDelay für unexaktes Warten als direkter Ersatz für delay(), und taskResume o.ä. das relativ zum letzten timeStamp wartet? Die Implementierung wäre relativ simpel
Code: [Select]
#define taskResume(interval) timestamp+=interval; while (millis()-timestamp < (interval)) taskSwitch();
Nur der Name gefällt mir noch nicht.
Title: Re: [Projekt] Multitasking
Post by: Jomelo on Jul 29, 2016, 03:28 pm
Nett das du es mal wieder versuchst mit Tasks.

Ich hab es damals mal versucht, es ist aber leider untergegangen:

http://forum.arduino.cc/index.php?topic=165552.0 (http://forum.arduino.cc/index.php?topic=165552.0)

Das Projekt habe ich seit dem nicht weiter verfolgt. Die Erkenntnisse sind dann mit in die LCDMenuLib geflossen.
Title: Re: [Projekt] Multitasking
Post by: combie on Jul 29, 2016, 04:02 pm
Nett das du es mal wieder versuchst mit Tasks.

Ich hab es damals mal versucht, es ist aber leider untergegangen:

http://forum.arduino.cc/index.php?topic=165552.0 (http://forum.arduino.cc/index.php?topic=165552.0)

Das Projekt habe ich seit dem nicht weiter verfolgt. Die Erkenntnisse sind dann mit in die LCDMenuLib geflossen.
Hi..

Hmm.. das hatte ich bisher nicht gefunden....
Das war wohl vor meiner Zeit hier ;-)


Dein TaskDingen verfolgt ja einen ganz anderen Ansatz. Den werde ich mir auch mal genau anschauen...


Mir drehte es sich eher darum, einen 1:1 DropIn für delay() und nebenläufige Fäden trotz lang laufender Schleifen zu bekommen.

Lange habe ich darüber gegrübelt,  sowas wie einen TaskKontrollBlock einzuführen, aber dann verworfen. Das hätte mehr Möglichkeiten geschaffen, wäre aber auch um Größenordnungen komplexer geworden. Auch für den Anwender, und genau das wollte ich nicht.


Quote
Das Timing mit taskPause ist etwas unpräzise,
Ja ...
Dass das Ganze kein Echtzeitbetriebsystem wird, dank des kooperativen Charakters auch nie werden können wird, darf einen nicht verwundern.
Will man es exakter, wird man die Hardwaretimer bzw. ein RTOS bemühen müssen.






Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Jul 29, 2016, 04:14 pm
Im Ampel-Test war noch ein Denkfehler drin. Bei 5 Pieps im Abstand von 500ms kommt der letzte ja bereits nach 2000ms, also etwa zeitgleich mit der Weiterschaltung der Ampel, und nicht erst nach 2500ms. Tatsächlich kommt der letzte Piep sauber nach dem letzten Ampel-Zustand, wenn das Intervall auf 501ms hochgesetzt wird. So schlimm ist es mit dem Zeitversatz also doch nicht :-)

Bei meinen angedachten Erweiterungen geht es nicht um hochkomplizierte Funktionen aus Echtzeit-Systemen, sondern was sich mit vergleichbar einfachen Mitteln an Standard-Aufgaben sonst noch erledigen lassen kann.
Title: Re: [Projekt] Multitasking
Post by: michael_x on Jul 29, 2016, 05:00 pm
Quote
Anfänger dürften davon profitieren, die von den komplizierten Lösungen wie BlinkWithoutDelay abgeschreckt sind
Das Problem bei Makros ist, dass der Compiler was anderes sieht als was der Anfänger geschrieben hat, und eventuelle Fehlermeldungen dadurch noch viel unverständlicher werden als sie für Anfänger so schon sind.

Wem BlinkWithoutDelay zu komplex ist, dem ist nicht zu helfen  der wird auch mit den Multitasking Makros Probleme haben  :smiley-confuse:

Wenn man weiss, dass diese Makros dazu dienen, heimlich aus der umgebenden Funktion zu verschwinden um beim nächsten Mal genau dort wieder weiter zu machen, und man damit gedanklich zurecht kommt, schön. Damit kann man sicher übersichtlich AblaufSteuerungen definieren.
Title: Re: [Projekt] Multitasking
Post by: combie on Jul 29, 2016, 05:32 pm
Quote
Wenn man weiss, dass diese Makros dazu dienen, heimlich aus der umgebenden Funktion zu verschwinden um beim nächsten Mal genau dort wieder weiter zu machen,
Das hast du schön formuliert.
.... "heimlich" .... "verschwinden" ....
;-)





Title: Re: [Projekt] Multitasking
Post by: skorpi080 on Jul 29, 2016, 06:31 pm
Was gefällt euch an Jomelos simpleThread (https://github.com/Jomelo/simpleThread) nicht?
Title: Re: [Projekt] Multitasking
Post by: Doc_Arduino on Jul 29, 2016, 08:46 pm
Hallo,

ich hatte ja schon einmal versucht zu fragen, bekam aber keine Antwort. Im Grunde ist das ja die negative Logik von deinem Intervall Projekt. Womit ich nicht klar komme vom Verständnis her ist folgendes.
Man möchte doch das die loop so schnell wie möglich durchlaufen wird.
Da widerspricht es doch das man hier feste Zeiten für andere Dinge frei gibt.
Statt so schnell wie möglich sein Ding fertig zu machen und dann kann das nächste dran kommen.

Title: Re: [Projekt] Multitasking
Post by: combie on Jul 29, 2016, 09:16 pm
Quote
ich hatte ja schon einmal versucht zu fragen, bekam aber keine Antwort.
Sorry, ich hatte deine Frage nicht verstanden.
Auch jetzt noch nicht wirklich.

Aber, ich versuchs mal....

Quote
Man möchte doch das die loop so schnell wie möglich durchlaufen wird.
Das stimmt!
Findet bei den ganzen Beispielen ja auch mehr als 100.000 mal pro Sekunde statt.

Quote
Da widerspricht es doch das man hier feste Zeiten für andere Dinge frei gibt.
Nöö...
Denn die "freie" Zeit geht ja an loop(), im Grunde so, wie es das Interval Macro auch macht.

Ich halte es also nicht für inverse Logik, sondern eher für die gleiche.


Hmmm.
War es das, was du wissen wolltest?

Nachtrag:
Ah....
Ich glaube, jetzt... kommts mir...

Zu Anfang sagte ich:
Quote
taskSwitch() Gibt die Rechenzeit an andere Tasks ab. Es wird im nächsten Durchlauf an dieser Stelle weiter gearbeitet.
Der erste Teil ist wohl missverständlich.
Die Rechenzeit wird nicht direkt an andere Tasks abgegeben, sondern der Programmfluss wird per Return an die aufrufende Funktion übergeben, und loop() kann dann andere Tasks aufrufen, bzw. zusätzliche Aufgaben erledigen. Das gilt natürlich auch für die anderen Taskkontrol Dinger..
Title: Re: [Projekt] Multitasking
Post by: combie on Jul 29, 2016, 10:32 pm
Was gefällt euch an Jomelos simpleThread (https://github.com/Jomelo/simpleThread) nicht?
Das kannte ich vorher noch nicht.

Auch sind meine taskMacros vermutlich schon einiges älter.
Hatte sie nur noch nicht aufs Arduino Umfeld portiert.


Title: Re: [Projekt] Multitasking
Post by: Doc_Arduino on Jul 30, 2016, 03:54 am
Hallo,

genau jetzt gehts in die richtige Richtung. Ich hatte noch keine Zeit mir das näher anzuschauen, habe nur deine Texte hier dazu mehrfach gelesen. Also macht der Task sein Ding und wenn er fertig ist gehts in der loop normal weiter. Und wenn der Task selbst aktuell nichts zu tun hat blockiert er auch nicht sondern gibt sich sofort wieder frei zurück an loop.
Okay, dass verstehe ich. Wie dein Intervall nur eben anders verpackt. Sehr schön.  :)   Werde das sicherlich bald selbst testen.

Title: Re: [Projekt] Multitasking
Post by: SebastianHeiden on Aug 04, 2016, 08:25 pm
Was mache ich falsch  :smiley-confuse:

kurze Einleitung:

ich möchte einen Servo ansteuern und nebenbei andere dinge erledigen
doch ich bekomme diese Fehlermeldung und habe keinen schimmer was falsch ist

Bitte um richtigstellung !!

Code: [Select]
#include <TaskMacro.h>
#include <Servo.h>
Servo servo1;

void setup() {
 
  Serial.begin(38400);     
  delay(500);
  servo1.attach(9);

}

void loop() {

 bewegung();
 
}

void bewegung() {
 
 taskBegin();

  int position;


  for(position = 20; position < 180; position += 2)   
  {
    servo1.write(position); 
    taskPause(20);               
  }

 
  for(position = 180; position >= 20; position -= 2)
  {                               
    servo1.write(position); 
    taskPause(20);     
   
  }
 
}




Fehlermeldung:

Code: [Select]


 In function 'void bewegung()':

sketch_aug04a:40: error: expected '}' at end of input

 };

  ^

exit status 1
expected '}' at end of input

 
Title: Re: [Projekt] Multitasking
Post by: combie on Aug 04, 2016, 08:38 pm
Code: [Select]
#include <TaskMacro.h>
#include <Servo.h>
Servo servo1;

void setup() {
 
  Serial.begin(38400);     
  delay(500);
  servo1.attach(9);

}

void loop() {

 bewegung();
 
}

void bewegung() { 
  static int position;
  taskBegin();

  for(position = 20; position < 180; position += 2)   
  {
    servo1.write(position); 
    taskPause(20);               
  }

 
  for(position = 180; position >= 20; position -= 2)
  {                               
    servo1.write(position); 
    taskPause(20);     
   
  }
  taskEnd();
 
}


Die Meldung sagt:  taskEnd(); vergessen

 int position muss static werden, damit der Wert über mehrere Taskdurchläufe erhalten bleibt.
Die Deklaration sollte vor taskBegin stattfinden.


Evtl fehlt da noch eine Schleife, damit sich das Gezappel wiederholt.
Title: Re: [Projekt] Multitasking
Post by: SebastianHeiden on Aug 04, 2016, 09:03 pm
Danke für die Rückmeldung
nach deinen Verbesserungen klappte es wie gewollt!
Hoffe das es dabei bleibt!!
Title: Re: [Projekt] Multitasking
Post by: combie on Aug 04, 2016, 09:09 pm
Quote
Hoffe das es dabei bleibt!!
Ach, sicher..... ich habe da volles Vertrauen.

Quote
Danke für die Rückmeldung
Und, dir Danke für den Test!
Title: Re: [Projekt] Multitasking
Post by: SebastianHeiden on Aug 04, 2016, 09:25 pm
was mach ich jetzt falsch   :(

Code: [Select]
#include <TaskMacro.h>
#include <Servo.h>
Servo servo1;

void setup() {
 
  Serial.begin(38400);     
  delay(500);
  servo1.attach(9);

}



void bewegung() {

if(Serial.available())
{
  if(Serial.read() == "1")
  {

 
 taskBegin();

 static int position;

  while(1){
 
  for(position = 20; position < 180; position += 2)   
  {
    servo1.write(position); 
    taskPause(20);               
  }

 
  for(position = 180; position >= 20; position -= 2)
  {                               
    servo1.write(position); 
    taskPause(20);     
   
  }
  }
  }
  taskEnd();
}


void count() {

  taskBegin();

  static int zahl = 0;

  while(1) {

    Serial.println(zahl);
    zahl++;
    taskPause(100);
  }
  taskEnd();
}

void loop() {

 bewegung();
 count();
}

Fehlermeldung:
Code: [Select]

In function 'void bewegung()':

sketch_aug04a:19: error: ISO C++ forbids comparison between pointer and integer [-fpermissive]

   if(Serial.read() == "1")

                       ^

sketch_aug04a:48: error: a function-definition is not allowed here before '{' token

 void count() {

              ^

sketch_aug04a:67: error: expected '}' at end of input

 }

 ^


exit status 1
ISO C++ forbids comparison between pointer and integer [-fpermissive]
Title: Re: [Projekt] Multitasking
Post by: Serenifly on Aug 04, 2016, 09:28 pm
Der Fehler ist genau was da steht. Du kannst C Strings (d.h. Null-terminiere char Arrays) nicht mit == vergleichen. Und C Strings und Integer vergleichen geht erst recht nicht. Wir sind hier nicht in Visual Basic. C++ ist zwar nur schwach typisiert und macht einige implizierte Konvertierungen, aber du musst schon etwas auf die Datentypen achten.
Da steht was von Zeigern weil Arrays in Zeiger auf das erste Element zerfallen.

Das willst du allerdings auch nicht. 1 als ein ASCII Zeichen ist '1' (ASCII Code 49 und ein Integer) und nicht "1"
Title: Re: [Projekt] Multitasking
Post by: Jomelo on Aug 04, 2016, 09:33 pm
Fehlt da nicht noch eine Klammer beim schließen. Ich zähl 5 die du öffnest, aber nur vier die du schließst.

void bewegung() {

...

Wo wird die While Schleife geschlossen ?
Title: Re: [Projekt] Multitasking
Post by: combie on Aug 04, 2016, 10:01 pm
Richtig!

Am Besten ordentlich einrücken, dann sieht man auch was zusammen gehört.
Und nochmal: Statische Variablen gehören an den Anfang der Funktion.(und sei es nur für die Übersichtlichkeit

Und auch das  taskEnd(); sollte an die richtige Stelle. Um diese zu finden hilft das Einrücken.

Code: [Select]
#include <TaskMacro.h>
#include <Servo.h>
Servo servo1;

void setup() {
 
  Serial.begin(38400);     
  delay(500);
  servo1.attach(9);

}



void bewegung()
{
  static int position;

  if(Serial.available())
  {
    if(Serial.read() == '1')
    {
       taskBegin();
     
        while(1)
        {
       
            for(position = 20; position < 180; position += 2)   
            {
              servo1.write(position); 
              taskPause(20);               
            }
         
           
            for(position = 180; position >= 20; position -= 2)
            {                               
              servo1.write(position); 
              taskPause(20);     
             
            }
        }
        taskEnd();   
    }
  }
 
}


void count()
{
  static int zahl = 0;
  taskBegin();


  while(1)
  {
    Serial.println(zahl);
    zahl++;
    taskPause(100);
  }
  taskEnd();
}

void loop() {

 bewegung();
 count();
}
)
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Aug 07, 2016, 03:02 pm
Ich denke, daß ich das Problem mit der überflüssigen Schleife in taskSwitch() gelöst habe. Wichtig ist alleine der Block, der den Code zusammenhält, die Schleife drumrum ist überflüssig.
Code: [Select]
#define taskSwitch() { mark=__LINE__; return; case __LINE__: ; }
Ohne die Klammern kann es vorkommen, daß nur das erste Statement zu einem vorhergehenden if oder while gehört, der Rest danach aber immer ausgeführt wird.

Jetzt scheint in meiner ALib0 alles zu funktionieren, und ich kann die erste Version nach github (https://github.com/DrDiettrich/ALib0) hochladen. In dieser Bibliothek liegt neben den Task Makros noch eine AButton Klasse, mit der sich die häufigsten Anfängerprobleme (Debounce, StateChange) einfach erschlagen lassen.

Ich hatte diverse Probleme mit dem Upload, kann jemand die Beispiele testen?
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Aug 08, 2016, 03:31 pm
Inzwischen ist mir noch ein schnuckeliges Makro für die regelmäßige Ausführung eingefallen. Das soll allerdings in loop() verwendet werden, um beliebige Abläufe in regelmäßigem Abstand auszuführen.
Code: [Select]
//do something at regular intervals. This is designed for use in loop(), not within a task
#define every(ms) for (static millis_t _t=0; millis()-_t>=ms; _t+=ms)

Hier wird for für das Verstecken lokaler Variablen mißbraucht, funktioniert prima :-)

Für das sperrige "unsigned long" bei millis() habe ich dazu noch die Abkürzung millis_t eingeführt.
Title: Re: [Projekt] Multitasking
Post by: michael_x on Aug 08, 2016, 04:00 pm
Quote
funktioniert prima :-)
kann man es schachteln ?  ( nicht unbedingt nötig, aber wenn schon, denn schon )

Code: [Select]
#define every(ms) for (static millis_t _t=0; millis()-_t>=ms; _t+=ms)
typedef unsigned long millis_t;
#define LED 13

void setup() {
  Serial.begin(9600);
  pinMode(LED,OUTPUT);
}
void loop() {
 
 static bool status;
 every(500) {
   status = !status;
   digitalWrite(LED, status);
   every (10000) Serial.write('.'); 
 }
}
compiliert auf jeden Fall schonmal
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Aug 08, 2016, 06:11 pm
Läuft Dein Testprogramm nicht?

Normalerweise schreibt man solche Aktionen hintereinander:
Code: [Select]
every(500) dies;
every(10000) das;
Bei meinem Testprogramm in der ALib0 loggen 3 Tasks etwas parallel, nach verschiedenen Verfahren und mit unterschiedlichen Intervallen.
Title: Re: [Projekt] Multitasking
Post by: michael_x on Aug 08, 2016, 06:24 pm
Hab hier grade keinen Arduino zur Hand, kann es daher nur compilieren.
Gefällt mir aber schonmal gut.

Quote
Normalerweise schreibt man solche Aktionen hintereinander
Ja klar.
War nur neugierig, was passiert, wenn man sowas machen würde.

Oder sowas:

Code: [Select]
void loop() {
   every(1000) sekundenZyklus();
}

void sekundenZyklus() {
   every(60000UL) minutenZyklus();
}

void minutenZyklus() {}


Das geht "noch sicherer", da sich die Variablen erst recht nicht ins Gehege kommen können.
Auch wenn es -normalerweise- nicht so nicht nötig wäre.
Title: Re: [Projekt] Multitasking
Post by: combie on Apr 14, 2017, 12:01 pm
Hi!

Bin gerade dabei die TaskMakros zu überarbeiten.

Hier möchte ich euch schon mal ein Halbzeug vorstellen.
Ein einfacher Scheduler.

Als Vorlage habe ich das original Blink Beispiel verwendet.
Die delay() sind erhalten geblieben.

Nur den Scheduler und 2 UserTasks hinzugefügt.
Um ein wenig Demo Kram da rein zu bekommen.


In Tasks sind delay() weiterhin blockierend. Wirken sich aber nicht fatal aus, denn
in yield() wird eine Rekursion erkannt und verhindert.

Code: [Select]
#include <TaskMacro.h>
// siehe: https://forum.arduino.cc/index.php?topic=415229.0

unsigned long loopcount  = 0;
unsigned long yieldcount = 0;


void printCount()  // User Task
{
  taskBegin();
  while(1)
  {
    taskPause(1000); // jede Sekunde 
    Serial.print("Loop: ");   
    Serial.print(loopcount);   
    Serial.print("    Yield: ");   
    Serial.print(yieldcount);   
    Serial.println();
  } 
  taskEnd();
}

void printA() // User Task
{
  taskBegin();
  while(1)
  {
    taskPause(5000); // alle 5 Sekunden   
    Serial.println("A");
  } 
  taskEnd();
}



// hier alle Tasks, gefolgt von einem taskSwitch() eintragen
// alternativ, ein Array verwenden
void scheduler() //  Scheduler
{
  taskBegin();
  while(1)
  {
    printCount();
    taskSwitch();
    printA();
    taskSwitch();
  } 
  taskEnd();
}

void yield() // wird im Delay aufgerufen
{
    static bool reentranceFlag = true;
    yieldcount++;
    if(reentranceFlag) 
    {
      reentranceFlag = false; // zirkulaere Aufrufe unterdruecken
      scheduler();   
      reentranceFlag = true;
    }
}

void setup()
{
  Serial.begin(9600);
  Serial.println();
  Serial.println("Start");
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  loopcount++;
  digitalWrite(LED_BUILTIN, HIGH);   
  delay(1000);                     
  digitalWrite(LED_BUILTIN, LOW);   
  delay(1000);                       
}

 

Title: Re: [Projekt] Multitasking
Post by: epunkt on Oct 13, 2019, 07:13 pm
Hi, hoffentlch liest hier noch jemand....

Code schicke ich nach, aber die Frage scheint mir ganz einfach: Bei mir wartet taskWaitFor(condition) bis zum ersten true und dann nicht mehr. Die Task wird ganz normal beendet (wird sie das?) und später wiederholt aufgerufen, aber da wird nicht mehr gewartet, obwohl condition gar nicht true ist. Oder hab ich da wie ich vermute ein Verständnisproblem?

--
Servus, Elmar
Title: Re: [Projekt] Multitasking
Post by: combie on Oct 13, 2019, 07:29 pm
Du machst was falsch!
Das kann ich dir so schon sagen.

Ist die Frage damit beantwortet?
Title: Re: [Projekt] Multitasking
Post by: epunkt on Oct 13, 2019, 07:43 pm
Okay, Super das Du hier noch liest :))

Wie füge ich Code ein?

Code: [Select]

#include <TaskMacro.h>
#include <millisDelay.h>

int t1Power = 13;
int t2Power = 2;
int BHR = 3; //Bahnhofsreed
//int GBM1 = 4;  //Gleisbesetztmelder Bahnhof außen
int SignalBahnhof = 10;
int speed = 110;  // Geschwindigkeitsvektor, startet bei Programmstart mit Normalgeschwindigkeit 0-255
int smax = 240;    // Maximalgeschwindigkeit
int norm = speed;   // Normalgeschwindigkeit
int smin = 180;      // Minimalgeschwindigkeit
int t1stop,t1start,t2stop,t2start = 0;
int t1stopzeit,t2stopzeit=4000;
int t1haltezeit,t2haltezeit=15000;
int t1halt,t2halt=0;

int t1speed=0;
int t2speed=0;
int t1norm=220;
int pstart=1;
int t1direction=0; //0-right, 1-left
int W1rechts=8;
int m1=30;
int m2=31;
int mo1=32;
int mo2=33;
long delay1 = 180000;
long delay2 = 60000;
long delay3 = 120000;
long delay0 = 5000;
int GBM1 =7, GBM2=6, GBM3=5, GBM4=4;

unsigned long ankunft = 0;
unsigned long zeit = 0;
const long takt = 12000;

static uint8_t tog,tog1 =0;

// static uint32_t ledtime=millis(),s1=ledtime+66,s2=s1+90,s3=s1+187,s4=s1+231;
static uint32_t s1= millis(),s2=s1+20000,s3=s1+50000,s4=s1;

// the setup routine runs once when you press reset:
void setup() {
  randomSeed(100);
  pinMode(BHR, INPUT);
  pinMode(GBM1, INPUT_PULLUP); //äußeres Gleis links innen
  pinMode(GBM2, INPUT_PULLUP); //äußeres Gleis links außen
  pinMode(GBM3, INPUT_PULLUP); //äußeres Gleis Bahnhof
  pinMode(GBM4, INPUT_PULLUP); //äußeres Gleis rechts
 
  pinMode(t1Power, OUTPUT);
  pinMode(t2Power, OUTPUT);
 
  pinMode(m1,OUTPUT);
  pinMode(m2,OUTPUT);
  pinMode(mo1,OUTPUT);
  pinMode(mo2,OUTPUT);
Serial.begin(9600);

//m1 HIGH, m2 LOW --> Zug nach links
//m1 low  m2 HIGH --> Zug nach rechts
//ELok t1norm guter Speed
          digitalWrite(m1,LOW);
          digitalWrite(m2,HIGH);
  //        analogWrite(t1Power, t1speed);
          analogWrite(t1Power, 220);
          delay(500);
          //digitalWrite(t1Power, 0);
}

void rechts() {
  digitalWrite(m1,LOW);
  digitalWrite(m2,HIGH);
  t1direction=0;
  Serial.println("Rechts-----------------------------");
}
void links() {
  digitalWrite(m2,LOW);
  digitalWrite(m1,HIGH);
  t1direction=1;
  Serial.println("Links-----------------------------");
}



void t1anhalten(){
taskBegin();

while (t1speed>0){
  Serial.println("anhalten-----------------------------");
  t1speed=t1speed-20; analogWrite(t1Power, t1speed);
  taskPause(50);
  ankunft=millis();
if (t1speed<=20) {
  t1speed=0;analogWrite(t1Power, t1speed);

}

}

taskEnd();
t1halt=2;
}

void t1losfahren(){
taskBegin();
taskWaitFor((zeit-ankunft>=takt) && (zeit-ankunft<=200000)); //??????????????????????????????????????????????????

while (t1speed<t1norm){
//taskSwitch();
  Serial.println("beschleunigen-----------------------------");
  t1speed=t1speed+15;analogWrite(t1Power, t1speed);
  taskPause(10);
if (t1speed>155) {
  t1speed=t1norm;analogWrite(t1Power, t1speed);
 
}
}

t1halt=0;
taskEnd();
}

// the loop routine runs over and over again forever:
void loop() {
 
delay(500); 
//analogWrite(t1Power, t1speed);
//taskBegin();   
 
// wenn GBM1 == 0 und m1==HIGH bremsen (while t1speed>0 reduce)
// warten Richtungswechsel (m1 LOW) beschleunigen (while t1speed<t1norm increase)
// wenn GBM4 == 0 und m1=LOW bremsen (while t1speed>0 reduce)
// warten Richtungswechsel (m1 HIGH) beschleunigen (while t1speed<t1norm increase)

if (pstart<10){
  if (digitalRead(GBM1)==0){
    rechts();
    t1losfahren();
  }
  if (digitalRead(GBM4)==0){
    links();
    t1losfahren();
  }
   
  pstart++;
}
//analogWrite(t1Power, t1speed);
//delay(100);
//analogWrite(t1Power, t1speed);
Serial.println("loop-----------------------------");

if (((digitalRead(GBM1)==1)) &&  ((digitalRead(GBM4)==1)))
{
   t1speed=220;analogWrite(t1Power, t1speed); //--------------------------------------------------------------
}

if ((digitalRead(GBM1)==0) && (t1direction==1))
{
 
  t1anhalten();
  //delay(44000);
}

if ((digitalRead(GBM1)==0) && (t1halt==2)) //Bahnhof links

 
  rechts();
  delay(50);
  Serial.println("beschleunigen aufrufen-----------------------------");
  t1losfahren();
 //taskPause(84000);
}


if ((digitalRead(GBM4)==0) && (t1direction==0))
{
  t1anhalten();

  //delay(4000);

if ((digitalRead(GBM4)==0) && (t1halt==2)) {  //Bahnhof rechts
   // taskPause(54000);
    links();
    delay(50);
    Serial.println("beschleunigen aufrufen -----------------------------");
    t1losfahren();
  }

Serial.print(t1direction);
Serial.print("-");
Serial.print(digitalRead(GBM1));
Serial.print("-");
Serial.print(digitalRead(GBM2));   
Serial.print("-");
Serial.print(digitalRead(GBM3));
Serial.print("-");
Serial.print(digitalRead(GBM4));
Serial.print("-");
Serial.print(t1speed);
Serial.print("--");
Serial.print(t1halt);
Serial.print("--");
Serial.print(zeit-ankunft);
Serial.println("");
zeit=millis();
//taskEnd();
}
Title: Re: [Projekt] Multitasking
Post by: Tommy56 on Oct 13, 2019, 07:45 pm
Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [*code] davor und [*/code] dahinter ohne *).
So ist er auch auf portablen Geräten lesbar. Das kannst Du auch noch nachträglich ändern.

Schön wäre auch, wenn Du unnötige Leerzeilen löschst.

Gruß Tommy
Title: Re: [Projekt] Multitasking
Post by: epunkt on Oct 13, 2019, 07:53 pm
Okay, hab ich gemacht. Entschuldigung bitte.
Title: Re: [Projekt] Multitasking
Post by: combie on Oct 13, 2019, 10:02 pm
Okay, Super das Du hier noch liest :))
Warum auch nicht.....

Mir fehlen die Enlosschleifen in deinen Tasks!
Von daher darfst du dich nicht wundern, dass es nur eine Runde läuft.

Endlosschleifen sind in dem Zusammenhang was ganz normales.
Jede Windows/Linux/FreeRtos usw. tut sowas, oder so ähnlich.
Darum findest du dieses auch in jedem meiner Beispiele.

Ansonsten, keine Ahnung, was dein Programm tun soll.
Und Formatiert ist es auch gruselig.
Ein STRG-T würde schon helfen..



Title: Re: [Projekt] Multitasking
Post by: epunkt on Oct 13, 2019, 11:56 pm
Dankeschön für die Antwort. Strg T hab ich grade gemacht.....

Ich bin vielleicht etwas unbeholfen, nur zur Erklärung, nicht Entschuldigung, ich bin 62 und wollte mal etwas neues probieren. Arduino Mega + diese http://wiki.seeedstudio.com/Motor_Shield_V1.0/ H-Brücke  sind die verbauten Komponenten.

Das Programm soll die einfachste Form einer Pendelzugsteuerung sein. Fahre nach rechts, bis der rechte Gleisbesetztmelder (GBM4) belegt meldet (digitalread(GBM4==0)) , dann bremse ab bis null, warte die Zeit die in takt festgelegt ist, beschleunige wieder nach links bis Zug am linken Bahnhof (GBM1) ist, bremse ab, warte die Zeit die in takt festgelegt ist usw.
Der Rest im Programm ist nur, damit der Zug auch losfährt, wenn er bei Programmstart irgendwo, jedenfalls nicht im rechten oder linken Bahnhof auf einem Gleisbesetztmelder steht und schlampige Rudimente aus Versuchen.

Das alles _funktioniert_ bis auf das Warten.

Der Zug bremst, wartet beim _ersten_ Durchlauf den takt ab, beschleunigt, bremst wieder am Gegenbahnhof, wartet dann aber nicht mehr, sondern fängt sofort wieder mit Beschleunigen in die Gegenrichtung an.

Irgendwas mach ich da falsch, und vielleicht kannst Du mir helfen. Das Problem beim Nachstellen ist, das der Code bei Dir sicher compiliert aber für den Ablauf eine fahrende Lok und die Gleisbesetzmelder braucht..

Danke und viele Grüße

Elmar
Title: Re: [Projekt] Multitasking
Post by: combie on Oct 14, 2019, 12:30 am
Dieses Multitasking System ist dafür gedacht, dass mehre Abläufe quasi gleichzeitig ablaufen können.
Wenn du es verwenden möchtest, wirst du dein Programm in Nebenläufigkeiten aufteilen müssen.

Im Moment, sehe ich noch nicht, wie man das tun könnte...

Title: Re: [Projekt] Multitasking
Post by: epunkt on Oct 14, 2019, 05:02 am
Das quasi gleichzeitig wird sofort benötigt wenn die zweite Strecke und der zweite Zug dazukommt. Mit einer Ausweichstelle in der Mitte der Strecke gehen dann sogar 3 Züge gleichzeitig. (Auf der Platte schon vorhanden)

Geht ja auch alles wie gewünscht, nur das Warten an den Bahnhöfen nicht. Wie kann ich das Lösn? Schau doch mal bitte....

--
Elmar
Title: Re: [Projekt] Multitasking
Post by: Doc_Arduino on Oct 14, 2019, 08:22 am
Hallo,

Edit:  Bevor ich Ärger bekomme, gelöscht. In deinem eigenen Thread sehen wir uns vielleicht wieder.
Title: Re: [Projekt] Multitasking
Post by: combie on Oct 14, 2019, 09:36 am
Schau doch mal bitte....
Schade, dass die Kommunikation zwischen uns beiden nicht so richtig funktioniert.

Ein paar Probleme gibt es hier:
Du kaperst meinen Taskmakro Vorstellungsthread, anstatt einen eigenen aufzumachen.
Die Salamitaktik, die geht mir quer. (erst kein Code, dann eine unformatierte Wurst, dann 3 Züge)
Sagst mir, meine Makros funktionieren nicht. Bist aber nicht bereit dich den Bedingungen zu stellen (Endlosschleife einzubauen)
Und dann die dreiste Aufforderung: Mach du mal für mich....

Sorry, dass ich das etwas grob formuliert habe, aber, du siehst die Diskrepanz, zwischen deiner und meiner Erwartungshaltung, oder?

---

Meine Makros können dir den Bau von endlichen Automaten, Schrittketten und Ablaufsteuerungen vereinfachen.
Aber sie können nicht dein Denken ändern.
Das kannst nur du.

Geht ja auch alles wie gewünscht, nur das Warten an den Bahnhöfen nicht. Wie kann ich das Lösn?
Denke in Nebenläufigkeiten.

Und wenn es um 3 Züge geht, welche sich gleich verhalten sollen, dann wären eher die Taskobjekte (https://forum.arduino.cc/index.php?topic=596279.msg4051105#msg4051105) für das Problem geeignet, als dieser eher primitive Vorläufer davon.
Auch ist da die Endlosschleife schon fest eingebaut.
Da gibts dann kein Weg drumrum.

Tipp:
Male Ablaufdiagramme, so wie es dir auf der Wikipedia Seite zu endlichen Automaten vorgemacht wird (https://de.wikipedia.org/wiki/Endlicher_Automat)
Vielleicht verstehe ich dich ja dann.....
Title: Re: [Projekt] Multitasking
Post by: epunkt on Oct 15, 2019, 12:44 pm
Servus, ich noch mal kurz.....

Keinesfalls wollte ich sagen, das die Makros nicht funktionieren.
Ich schrieb, das ich irgendwas nicht gerafft habe, weil taskWaitFor(condition) nur bei mir nur einmal
greift, und dann nie wieder.

Ist es so, das eine einmal gestartete Task eigentlich immer aktiv ist und nie endet?

Ich bin Deinem Rat gefolgt und hab mal überlegt wie Züge in der Wirklichkeit fahren (sollten :))
Da gibt es einen Fahrplan mit Abfahrtszeiten an den Bahnhöfen. Ab der Erkenntnis war alles ganz einfach, ein Array mit Abfahrtsminuten, also:
Code: [Select]

int BlinksAbfahrt[] = {2, 7, 12, 21, 26, 32, 36, 45, 49, 54, 58};
int BrechtsAbfahrt[] = {5, 10, 15, 24, 29, 34, 39, 48, 51, 56};
int BmitteAbfahrt[] = {3, 6, 8, 11, 13, 16, 22, 25, 27, 30, 33, 35, 37, 40, 46, 49, 50, 52, 55, 57, 59};

hat das Ganze einfach und flexibel gelöst. Läuft so natürlich auch für beliebig viele Züge, so sie unterschiedliche Gleise und Stromkreise haben und sieht auf der Platte ausreichend echt aus:)

die Makros für das beschleunigen und abbremsen sind nach wie vor drin und tun genau was sie sollen, nämlich das wenn grade gebremst wird bekommt der Prozessor trotzdem sofort mit wenn ein anderer Zug den Streckenabschnitt wechselt, was bei delay() definitiv nicht der Fall wäre.

--
     Cool .signatures are so 90s...
Title: Re: [Projekt] Multitasking
Post by: combie on Oct 15, 2019, 02:28 pm
Erstmal: Schön, dass es funktioniert.

Quote
Ist es so, das eine einmal gestartete Task eigentlich immer aktiv ist und nie endet?
Wenn sie die Gelegenheit bekommt, dann ja.


Code: [Select]


// leicht witzig, aber recht realistisch.


   const bool stromausfall = false;


   while(not stromausfall)
   {
     taskA();
     taskB();
   }



Hier Lesestoff (https://de.wikipedia.org/wiki/Prozess_(Informatik)) wird das Prinzip beschreiben. Natürlich kann ein kleiner AVR das nicht alles, darum auch meine gnadenlos abgespeckte Version.
Title: Re: [Projekt] Multitasking
Post by: heweb on Dec 04, 2019, 07:56 am
Schon vor Jahren habe ich mit dieser Konstruktion experimentiert.
Hier mein Link zu meinem Tutorial in 8 Lektionen mit 60 Seiten Dokumentation dazu und Sourcecn.
Vom einfachsten Multitasking bis zum Scheduler, Taskstart in Interrupts etc.
Für alle Prozessoren geeignet. In Deutsch.

https://github.com/MacLeod-D/CoopOS-Kurs-Deutsch
Title: Re: [Projekt] Multitasking
Post by: combie on Dec 04, 2019, 08:08 am
@heweb
Schämst du dich gar nicht?
Title: Re: [Projekt] Multitasking
Post by: DrDiettrich on Dec 04, 2019, 12:31 pm
Wer sich McLeod nennt, der glaubt wohl auch an seine Einzigartigkeit :-(

Ich habe mir sein Tutorial nicht angeschaut - lohnt sich das denn?
Title: Re: [Projekt] Multitasking
Post by: TomLA on Jan 18, 2020, 07:51 pm
Hallo, ich bin neu in diesem Forum und hoffe, das hier in diesen Threat noch jemand mitliest.
Ich benutze die Taskmacros von Combie. Die funktionieren soweit auch recht gut. Nun habe ich aber ein Problem, bei dem ich denke, das ich ohne eure Hilfe nicht mehr weiterkomme.
Das Problem ist folgendes:
Ich bzw. der Arduino arbeitet gerade in einem Task, aus dem ruft er eine andere Funktion auf. Nach Beendigung der anderen Funktion soll aber der ursprünglich Task nicht mehr weiter aufgerufen bzw. ausgeführt werden. Ich vermute, es liegt an der darin vorkommenden taskPause() oder er akzeptiert das return (um den Task zu verlassen) nicht und arbeitet stur nach dem Rücksprung in die Task die nächste Zeile (nach dem return) weiter.
Vielleicht kann mir da jemand auf die Sprünge helfen ??
Falls der Code (ewig lang und etwas unschön, weil ich ein alter Spagetti-Code-Programmierer bin) oder andere Angaben benötigt werden, kann ich das gerne machen, aber ich denke ihr stellt die Fragen, und ich liefere was nötig ist.
Vielen Dank im Voraus
Tom
Title: Re: [Projekt] Multitasking
Post by: michael_x on Jan 19, 2020, 12:28 am
Quote
Ich bzw. der Arduino arbeitet gerade in einem Task, aus dem ruft er eine andere Funktion auf. Nach Beendigung der anderen Funktion soll aber der ursprünglich Task nicht mehr weiter aufgerufen bzw. ausgeführt werden.
Könnte sein, dass das Konzept "Task" gar nicht passt, und du eher eine "State Machine" beschreibst.
?
Title: Re: [Projekt] Multitasking
Post by: combie on Jan 19, 2020, 03:14 pm
Ich verstehe dein Problem auch nicht.

Das Konzept "Task anhalten", von Außen, ist bei den TaskMacros nicht vorgesehen.
Kooperation, ist das primäre Verhaltensmuster.

Möglichkeiten:



Title: Re: [Projekt] Multitasking
Post by: TomLA on Jan 19, 2020, 07:06 pm
Hallo erstmal, vielen Dank, dass ihr euch mein Problem mal angesehen habt.
Zur Erklärung bzw. Verdeutlichung, warum ich glaube, einen Task zu benötigen:
Eigentlich war die ursprüngliche Funktion kein Task. In der Funktion wurde, je nach Zustand der (Alarm-)Anlage, eine Taste auf einem TFT angezeigt. Das hat auch sehr gut funktioniert. Dann kam mir die Idee, die angezeigte Taste (rot) könnte nach Betätigung einen Countdown von 15 sekunden anzeigen. Während dieses Countdowns muss vom Benutzer eine Chipcard vor die Anlage gehalten werden. Wenn er das macht, springt die Taste wieder auf ihren Normalzustand um (grün).
Das hat aber nur mit einem Task funktioniert (zumindest ist mir keine andere Lösung eingefallen, um den Countdown darzustellen, ohne delay zu benutzen).
MIt dem Task läuft es auch so wie ich es mir vorstellte, aber sobald die ChipKarte an die Anlage gehalten wird, spring der Arduino von der Funktion (Alarm aus) wieder in den Task (Alarm an mit Countdown der Taste) und startet den Countdown neu.
Mein serieller Monitor zeigt mir das auch so an, das der Zustand von dem Task wieder geändert wird.
Ich weiss, das ist für Aussenstehende schwer zu verstehen, bzw. ich weiss nicht, wie ich es erklären soll.
Ich stelle mal einen Code-Abschnitt und einen Teil der Ausgabe am seriellen Monitor (mit jeweils ein paar Kommentaren meinerseits) zum hoffentlich besseren Verständnis mit ein.

Der Ausschnitt aus dem Programmcode

Code: [Select]

void alarm_an(unsigned char zimmer)
{
  Serial.println("*** alarm_an ***");
  /*
    Serial.print("alarm_zimmer_1: ");
    Serial.println(alarm_zimmer_1);
    Serial.print("alarm_zimmer_2: ");
    Serial.println(alarm_zimmer_2);
  */
  if (zimmer == Zimmer_1)
  {
    //    zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "Alarm 1 aus", 2);
    //    zimmer_1_btn.drawButton(false);
    alarm_zimmer_1 = ON;
    alarm_tft_anzeige(Zimmer_1, "alarm1");
  }
  else if (zimmer == Zimmer_2)
  {
    zimmer_2_btn.initButton(&tft, 175, 130, 300, 40, WHITE, RED, WHITE, "Alarm 2 aus", 2);
    zimmer_2_btn.drawButton(false);
    alarm_zimmer_2 = ON;
  }
  alarm_status = 1;
  Wire.beginTransmission(8);
  Wire.write(alarm_status);
  Wire.endTransmission();
  relay_SetStatus(zimmer, ON);         // Relais für alarmauslösendes Zimmer an
}

Task alarm_tft_anzeige(unsigned char zimmer, char alarm_taste_status[])
{
  Serial.println("*** alarm_tft_anzeige ***");
  Serial.print ("zimmer: ");
  Serial.println (zimmer);
  Serial.print ("alarm_taste_status: ");
  Serial.println (alarm_taste_status);
  Serial.print ("alarm_taste_tft_has_to_be_changed: ");
  Serial.println (alarm_taste_tft_has_to_be_changed);
  Serial.print ("countdown: ");
  Serial.println (countdown);
  taskBegin();
  while (1)
  {
    if (zimmer == Zimmer_1)
    {
      if (alarm_taste_status == "ok")
      {
        if (alarm_taste_tft_has_to_be_changed == true)
        {
          zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, GREEN, BLACK, "Zimmer 1 OK", 2);
          zimmer_1_btn.drawButton(false);
          alarm_taste_tft_has_to_be_changed = false;
          return;
        }
      }
      else if (alarm_taste_status == "alarm1")
      {
        if (alarm_taste_tft_has_to_be_changed == true)
        {
          zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "Alarm 1 aus", 2);
          zimmer_1_btn.drawButton(false);
          alarm_taste_tft_has_to_be_changed = false;
          return;
        }
      }
      else if (alarm_taste_status == "alarm2")
      {
        if (alarm_taste_tft_has_to_be_changed == true)
        {
          zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "Alarm 1", 2);
          zimmer_1_btn.drawButton(false);
          alarm_taste_tft_has_to_be_changed = false;
          return;
        }
      }
      else if (alarm_taste_status == "warten")
      {
        if (countdown > 14)
        {
          alarm_taste_tft_has_to_be_changed = true;
          // alarm_taste_status = "alarm2";
          alarm_tft_anzeige(Zimmer_1, "alarm2");
          return;
        }
        if (keycard_status == true)
        {
          keycard_check = false;
          alarm_aus(Zimmer_1);                              //<-- hier springt er zu der Funktion, welche ich im ersten Posting gemeint habe
          /*          alarm_taste_status = "ok";
                    alarm_taste_tft_has_to_be_changed = true;
                    alarm_tft_anzeige(Zimmer_1, "ok");*/
          return;                                                  //<-- hier sollte er den Task dann verlassen und gut is, aber er überspringt diese Anweisung bzw. er sollte nie hier landen, da der alarm_taste_status eigentlich "ok" sein sollte
        }
        zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, BLACK, text[countdown], 2);
        zimmer_1_btn.drawButton(true);

        countdown++;
        taskPause(1000);
      }
      else
      {
        zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "ERROR", 2);
        zimmer_1_btn.drawButton(false);
      }
    }
    else if (zimmer == Zimmer_2)
    {
      zimmer_2_btn.initButton(&tft, 175, 130, 300, 40, WHITE, RED, WHITE, "Alarm 2 aus", 2);
      zimmer_2_btn.drawButton(false);
      alarm_zimmer_2 = ON;
    }
  }
  taskEnd();
}

void alarm_aus(unsigned char zimmer)
{
  Serial.println("*** alarm_aus ***");
  Serial.print("alarm_zimmer_1: ");
  Serial.println(alarm_zimmer_1);
  Serial.print("alarm_zimmer_2: ");
  Serial.println(alarm_zimmer_2);
  if (zimmer == Zimmer_1)
  {
    if (alarm_zimmer_1 == ON)
    {
      alarm_zimmer_1 = OFF;
      if (menue_flag == true)
      {
        menue_flag = false;
        display_change = true;
      }
      else
      {
        alarm_taste_tft_has_to_be_changed = true;
        alarm_tft_anzeige(Zimmer_1, "ok");
        //        zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, GREEN, BLACK, "Zimmer 1 OK", 2);
        //        zimmer_1_btn.drawButton(false);
        display_change = false;
        //      alarm_taste_tft_has_to_be_changed=true;
      }
    }
  }
  else if (zimmer == Zimmer_2)
  {
    if (alarm_zimmer_2 == ON)
    {
      alarm_zimmer_2 = OFF;
      if (menue_flag == true)
      {
        menue_flag = false;
        display_change = true;
      }
      else
      {
        zimmer_2_btn.initButton(&tft, 175, 130, 300, 40, WHITE, GREEN, BLACK, "Zimmer 2 OK", 2);
        zimmer_2_btn.drawButton(false);
        display_change = false;
      }
    }
  }
  Serial.print("display_change: ");
  Serial.println(display_change);
  if (alarm_zimmer_1 == OFF && alarm_zimmer_2 == OFF)
  {
    alarm_status = 2;
    keycard_check = 0;
    Wire.beginTransmission(8);
    Wire.write(alarm_status);
    Wire.endTransmission();
    countdown = 0;
  }
  relay_SetStatus(zimmer, OFF);        // Relais für alarmauslösendes Zimmer aus
}



Und hier der Ausschnitt aus dem seriellen Monitor

Code: [Select]

*** alarm_tft_anzeige ***
zimmer: 24
alarm_taste_status: warten
alarm_taste_tft_has_to_be_changed: 1
countdown: 2
*** keycard_access ***
Authorize with access card or Chip
Authorized access chip
keycard_status: 1
keycard_card: 0
keycard_chip: 1
menue_flag: 0
alarm_status: 1
keycard_check: 0
 
*** keycard_alarm_aus ***
*** alarm_aus ***
alarm_zimmer_1: 1
alarm_zimmer_2: 0
*** alarm_tft_anzeige ***
zimmer: 24
alarm_taste_status: ok                           //<-- an der Stelle ist alles ok
alarm_taste_tft_has_to_be_changed: 1
countdown: 3
display_change: 0
*** alarm_aus ***
alarm_zimmer_1: 0
alarm_zimmer_2: 0
display_change: 0
*** alarm_tft_anzeige ***                    //<-- hier sieht man, das er in den Task zurückkehrt, also auch noch ok
zimmer: 24
alarm_taste_status: warten                   //<-- hier setzt er eigenwillig seinen alten Zustand (vor Verlassen des Task) wieder ein (So ein pösser Pursche ;-))
alarm_taste_tft_has_to_be_changed: 1
countdown: 0
*** keycard_access ***
Authorize with access card or Chip
*** alarm_tft_anzeige ***
zimmer: 24
alarm_taste_status: warten
alarm_taste_tft_has_to_be_changed: 1
countdown: 0



Hoffentlich habe ich jetzt endgültig alle Klarheiten beseitigt ;-)
Tom
Title: Re: [Projekt] Multitasking
Post by: Tommy56 on Jan 19, 2020, 07:21 pm
Dein Problem ist delay(), ohne brauchst Du keinen Tasks.

Schau Dir BlinkWithoutDelay in den Beispielen der IDE an und verstehe es.
Dabei kann Dir z.B. die Nachtwächtererklärung helfen.

Gruß Tommy
Title: Re: [Projekt] Multitasking
Post by: combie on Jan 19, 2020, 07:31 pm
Quote
if (alarm_taste_status == "alarm1")
So vergleicht man keine Zeichenketten, sondern Zeiger.
Und das willst du sicherlich nicht.
Title: Re: [Projekt] Multitasking
Post by: TomLA on Jan 19, 2020, 08:32 pm
@Tommy56
OK - Ich verstehe BlinkWithoutDelay und meine ich hätte das auch schon mal versucht. Aber ich werde es nochmal versuchen, es in meinen Fall einzubauen. Ich melde mich dann wieder, wenn ich ein Ergebnis habe (hoffentlich ein positives).

@combie
Sorry für die doofe Gegenfrage - wie würde es denn richtig gehen? Mit strcmp?

Danke schon mal im Voraus
Tom
Title: Re: [Projekt] Multitasking
Post by: TomLA on Jan 19, 2020, 09:09 pm
Juhu - es funktioniert. War total easy.
Manchmal braucht es eben doch den berühmten Schlag aud den Hinterkopf ;-)

Vielen, vielen Dank an Tommy56
Title: Re: [Projekt] Multitasking
Post by: TomLA on Jan 19, 2020, 09:14 pm
Manchmal braucht es eben doch den berühmten Schlag aud den Hinterkopf ;-)
Und der hat anscheinend auch bleibende Schäden verursacht  :smiley-grin:  :smiley-grin:
Title: Re: [Projekt] Multitasking
Post by: Tommy56 on Jan 19, 2020, 09:15 pm
Schön, wenn der leichte Schlag geholfen hat und danke für die Rückmeldung.

Gruß Tommy
Title: Re: [Projekt] Multitasking
Post by: Demokrit on Mar 08, 2020, 06:26 pm
Hallo combie,

ich nutze deinen TaskMakro gerne und oft - speziell, weil er mich zwingt, vermehrt in "Tasks" zu denken, und damit Spaghetticode zu vermeiden. Bislang waren dies eher einfachere Aufgaben, wie 2 Motoren, ein Display plus Rotary Encoder.

Jetzt stecke ich in einem Projekt mit deutlich höherer Komplexität (ESP32 mit I2C Port Expander, 5 (synchronisierte) Achsen, eine I2C WiiConsole, I2C Display und diverse Sensoren). Hier wird, zumindest nach aktuellem Konzept, die Zahl der Tasks stark ansteigen. Meine Frage betrifft den Loop, in dem ich bislang die Tasks einfach sequenziell aufgeführt habe.

So benötige ich z.B. eine Init-Task, die 5 Home und Limit Positionen ermittelt, und einige andere Dinge initialisiert. Dies tut sie i.d.R. nur 1x. Danach brauche ich sie nicht mehr. Würde hier ein konditionaler Aufruf (z.B. if (init)) im Loop Sinn machen, oder schieße ich mir damit ins Bein? Soll heißen: Gewinne ich Zeit mit einem "if" im Loop gegenüber "taskWaitFor(initMarker)". Es würde helfen, eine einmal abgearbeitet Task gar nicht mehr zu "betreten". Wenn dies etwas bringt, so könnte ich auch die InitTask weiter granulieren und abstrahieren.

Ich hoffe, ich habe mich verständlich ausgedrückt,

Beste Grüße,
Thomas
Title: Re: [Projekt] Multitasking
Post by: combie on Mar 08, 2020, 06:44 pm
Quote
Ich hoffe, ich habe mich verständlich ausgedrückt,
Durchaus!
Zumindest zum Teil.

Aber dennoch möchte ich an den Grundfesten der Annahme rütteln.

Auf dem ESP32 stehen dir 2 vollwertige Kerne und FreeRTOS zu Diensten.

Meine TaksMakros kannst du da(auf dem ESP32) auch einsetzen.
Aber viel Sinn macht das nicht unbedingt, da das "Bessere" schon da ist und nur genutzt werden will.



Quote
Gewinne ich Zeit mit einem "if" im Loop gegenüber "taskWaitFor(initMarker)"
Geringfügig.
Es spart einen Call und die goto oder switch Anweisung ein.

Hiermit  (https://forum.arduino.cc/index.php?topic=596279.msg4051105#msg4051105)kannst du Tasks dynamisch erzeugen und auch wieder verwerfen
Title: Re: [Projekt] Multitasking
Post by: Demokrit on Mar 08, 2020, 07:14 pm
Ich gebe zu, mich noch nicht wirklich mit FreeRTOS beschäftigt zu haben. Oberflächlich gelesen hatte ich mal, dass da der Einsatz von delay() statthaft ist, und zu einem Taskwechsel führt. Bislang war mir auch wichtig, wahlfrei zwischen Nanos und ESP32 wechseln zu können. Andererseits bin ich von dem ESP32 so begeistert, dass ich vielleicht nie mehr einen Nano anfassen werde.

Danke für den Hinweis zum FreeRTOS. Ich werde mich einlesen.