{"id":280,"date":"2024-10-30T13:20:56","date_gmt":"2024-10-30T13:20:56","guid":{"rendered":"http:\/\/techblog.auchmonoabspielbar.de\/?p=280"},"modified":"2024-11-01T16:18:54","modified_gmt":"2024-11-01T16:18:54","slug":"arduino-delay-considered-harmful","status":"publish","type":"post","link":"http:\/\/techblog.auchmonoabspielbar.de\/?p=280","title":{"rendered":"Arduino delay() considered harmful"},"content":{"rendered":"\n<p>Das Arduino Blinky Programm ist das Hello World der embedded Anwender. Es wird auch von Entwicklern verwendet, die sich gut in C oder C++ auskennen &#8211; es dient als Test f\u00fcr die Entwicklungsumgebung und Anbindung an den jeweiligen Microcontroller. F\u00fcr Einsteiger tr\u00e4gt es aber ein hohes Risiko, dass diese fr\u00fchzeitig auf einen falschen Pfad geleitet werden.<\/p>\n\n\n\n<p>Das Blinky Programm enth\u00e4lt zwei delay() Aufrufe mit einer erheblichen Wartezeit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void loop() {\n  digitalWrite(LED_BUILTIN, true);\n  delay(500);\n  digitalWrite(LED_BUILTIN, false);\n  delay(500);\n}<\/code><\/pre>\n\n\n\n<p>In diesem einfachen Fall ist das noch nicht mal falsch oder problematisch. Aber es ist einer der wenigen Sonderf\u00e4lle in denen so ein delay() vern\u00fcnftig einsetzbar ist. Der Sonderfall hier: es gibt nur einen einzigen Handlungsstrang, jede Sekunde wird eine LED aus- und wieder eingeschaltet. Soweit so gut. Aber wie sieht es aus, wenn eine zweite LED mit halber Zeitverz\u00f6gerung geschaltet werden soll:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void loop() {\n  digitalWrite(LED_1, true);\n  digitalWrite(LED_2, true);\n  delay(250);\n  digitalWrite(LED_2, false);\n  delay(250);\n  digitalWrite(LED_1, false);\n  digitalWrite(LED_2, true);\n  delay(250);\n  digitalWrite(LED_2, false);\n  delay(250);\n  digitalWrite(LED_1, false);\n}<\/code><\/pre>\n\n\n\n<p>Und das ist noch einfach. Wie es aussieht, wenn die Zeit f\u00fcr die zweite LED 144 Millisekunden betr\u00e4gt, mag ich gar nicht hinschreiben. Man sieht, dass man mit delay() schnell in eine Sackgasse ger\u00e4t wenn es mehrere Handlungsstr\u00e4nge mit unterschiedlichen Zeitanforderungen gibt.<\/p>\n\n\n\n<p>Ein erster Gedanke w\u00e4re der Einsatz eines real time operating systems (RTOS) und mehreren Threads. Das ist aber ein gr\u00f6\u00dferes Unterfangen. Zum Einen muss man sich in das System einarbeiten. Zum Anderen belegt jeder Thread nicht unerhebliche Ressourcen. Insbesondere bei den kleinen Microcontrollern ist der RAM Bereich oft sehr begrenzt, sodass sich der Einsatz nicht unbedingt lohnt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Eine flexiblere L\u00f6sung in C<\/h2>\n\n\n\n<p>Eine naheliegende L\u00f6sung w\u00e4re z.B., dass man f\u00fcr jede Aktion eine Zeitvariable anlegt, die angibt, wann sie das n\u00e4chste Mal ausgef\u00fchrt werden soll.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void loop() {\n  unsigned long now = millis();\n\n  if (now &gt; nextLed1) {\n    nextLed1 = now + 500;\n    digitalWrite(LED_1, led1State);\n    led1State = !led1State;\n  }\n\n  if (now &gt; nextLed2) {\n    nextLed2 = now + 250;\n    digitalWrite(LED_2, led2State);\n    led2State = !led2State;\n  }\n}<\/code><\/pre>\n\n\n\n<p>Jetzt kann man leicht mehrere Handlungsstr\u00e4nge mit beliebigen Zeitverhalten ausf\u00fchren. Die delay() Funktion wird gar nicht mehr verwendet, jede Teilaktion ist also in sehr kurzer Zeit abgeschlossen und wird erst wieder nach Ablauf der angegeben Zeit wieder aktiv. Aus meiner Sicht ist das f\u00fcr kleinere Projekte und insbesondere f\u00fcr Anf\u00e4ngerprojekte durchaus eine sinnvolle Vorgehensweise. Wenn das Projekt w\u00e4chst, bekommt man im Laufe der Zeit aber eine unsch\u00f6ne Kette von if Abfragen in der loop Schleife.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Eine Variante f\u00fcr C++<\/h2>\n\n\n\n<p>Wenn man sich entschlossen hat, das Projekt nicht in C, sondern in C++ zu erstellen, bietet sich die M\u00f6glichkeit an, die Handlungsstr\u00e4nge jeweils in eine eigene Klasse zu packen. Der Konstruktor der Klasse meldet den Strang dann selbst\u00e4ndig in der loop Schleife an. Die Aktion wird in einer Methode hinterlegt. Ich habe einen Rahmen hierzu erstellt, welcher einfach als #include tmux.hpp in das Programm eingebunden werden kann. F\u00fcr jeden Handlungsstrang legt man dann eine Klasse an, die die jeweiligen Aktionen enth\u00e4lt.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Matthias-Thiele\/TMux\/tree\/main\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/Matthias-Thiele\/TMux\/tree\/main<\/a><\/p>\n\n\n\n<p>Der Rahmen f\u00fcr so einen Strang kann in der Entwicklungsumgebung als Template hinterlegt werden und somit leicht eingef\u00fcgt werden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class Handlungsstrang: public TMWorker {\n  private:\n\n  public:\n    void setup() {\n    }\n\n    void loop() {\n    }\n\n} strang1;<\/code><\/pre>\n\n\n\n<p>Die Klasse muss im Anschluss einen sinnvollen Namen bekommen und die setup Methode mit dem Code zur Initialisierung (analog zur Setup() Funktion im Arduino) gef\u00fcllt werden. In der loop() Methode werden dann die Befehle ausgef\u00fchrt, die zu dem gew\u00fcnschten Zeitpunkt ausgef\u00fchrt werden sollen. Der lokale Status wird im private: Abschnitt gespeichert. Blinky sieht dann so aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class LEDBlink: public TMWorker {\n  private:\n    bool m_LEDstate;\n\n  public:\n    void setup() {\n      setDelay(500);\n    }\n\n    void loop() {\n      digitalWrite(LED_BUILTIN, m_LEDstate);\n      m_LEDstate = !m_LEDstate;\n    }\n\n} ledBlink;\n<\/code><\/pre>\n\n\n\n<p>In der setup Methode wird die Wartezeit von 500 Millisekunden voreingestellt, die zwischen zwei Aufrufen eingehalten werden soll. In der loop Methode wird dann die LED ein- oder ausgeschaltet, der Zustand wird \u00fcber die Membervariable m_LEDstate bestimmt.<\/p>\n\n\n\n<p>Das ist erst mal deutlich mehr Code als in der einfachen Variante mit den Zeitvariablen. Der Vorteil dieser L\u00f6sung ist, dass die einzelnen Handlungsstr\u00e4nge besser voneinander entkoppelt sind und auf den ersten Blick sichtbar wird, was zum jeweiligen Handlungsstrang geh\u00f6rt. Und der gr\u00f6\u00dfte Teil des Codes kann durch ein Template automatisch eingef\u00fcgt werden. Bei einfachen Aktionen kann man die Klassen direkt in der main.cpp Datei unterbringen. Wenn die Aktionen komplexer werden und deutlich mehr Code ben\u00f6tigen, kann es sinnvoll sein, diese in jeweils eigene Dateien aufzuteilen. Diese Aufteilung sorgt dann ebenfalls dazu, dass schneller sichtbar wird, was zu der jeweiligen Aktion dazu geh\u00f6rt oder nicht.<\/p>\n\n\n\n<p>Die Basisklasse TMWorker k\u00fcmmert sich im Konstruktor darum, dass jede Instanz dieser Klasse in einer Liste gespeichert wird. Diese ist als Array und nicht als eine variabel lange Liste angelegt, da ich keinen Heap verwenden m\u00f6chte. Das Array enth\u00e4lt nur Zeiger auf die Instanzen, ist also nicht besonders gro\u00df und kann bei Bedarf verl\u00e4ngert werden. Die Liste wird in einer Variablen tmux gespeichert, welche automatisch durch das include der hpp Datei angelegt wird. Ein Verst\u00e4ndnis dieser Internas ist aber nicht notwendig zur Verwendung der Klasse.<\/p>\n\n\n\n<p>Auf der Arduino Seite muss in der loop Schleife nur noch die Methode tmux.loop() aufgerufen werden und in der Arduino setup Methode tmux.setup(). Die loop Methode durchl\u00e4uft die Liste der registrierten Handlungsstr\u00e4nge und f\u00fchrt diese nacheinander aus.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void loop() {\n  tmux.loop();\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Weitere Funktionen<\/h2>\n\n\n\n<p>Falls die Zeiten zwischen den Aufrufen nicht konstant sind, kann die n\u00e4chste Ausf\u00fchrung in der loop() Methode explizit eingestellt werden. Die ge\u00e4nderte Wartezeit gilt dann f\u00fcr alle nachfolgenden Aufrufe bis zur n\u00e4chsten \u00c4nderung.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class LEDBlink: public TMWorker {\n  private:\n    bool m_LEDstate;\n    int  m_actDelay = 250;\n\n  public:\n    void setup() {\n      setDelay(m_actDelay);\n    }\n\n    void loop() {\n      digitalWrite(LED_BUILTIN, m_LEDstate);\n      m_LEDstate = !m_LEDstate;\n\n      \/\/ Wartezeit bei jedem Durchlauf um 100 Millisekunden verl\u00e4ngern\n      m_actDelay += 100;\n      setDelay(m_actDelay);\n    }\n\n} ledBlink;<\/code><\/pre>\n\n\n\n<p>Ebenfalls kann man eine Startverz\u00f6gerung einstellen, die daf\u00fcr sorgt, dass der erste Aufruf der Aktion erst sp\u00e4ter stattfindet. Das kann z.B. sinnvoll sein, wenn sich der Zustand des Systems erst stabilisieren soll bevor eine Messung ausgef\u00fchrt wird. Hierf\u00fcr gibt es die Methode setStartupDelay():<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    void setup() {\n      setDelay(250);\n      setStartupDelay(3000);\n    }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Externe Ereignisse per Interrupt einbinden<\/h2>\n\n\n\n<p>Das Arduino Framework unterst\u00fctzt die Verwendung von Interrupts zur schnellen Reaktion auf externe Ereignisse. Die Interrupt-Routine sollte aber m\u00f6glichst kurz gehalten werden, andernfalls kann es St\u00f6rungen in anderen Teilen des Programms oder des Frameworks geben. Falls die Aktion etwas aufw\u00e4ndiger ist, kann es deshalb sinnvoll sein, die Aktion als eine TMWorker Klasse (genau genommen &#8211; von TMWorker abgeleitete Klasse) mit &#8218;unendlicher&#8216; Wartezeit anzulegen. In der Interrupt Routine wird dann nur die Ausf\u00fchrung getriggert, sie ist also in sehr kurzer Zeit wieder beendet. Die Kopplung erfolgt dabei \u00fcber die Methode attachWorker() welche das Worker Objekt mit einer Interrupt-Quelle verbindet.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class InterruptButton: public TMWorker {\n  public:\n    void setup() {\n      pinMode(PA8, INPUT_PULLUP);\n      attachWorker(0, PA8, CHANGE);\n    }\n\n    void loop() {\n      Serial1.println(\"Button 2 changed.\");\n    }\n} interruptButton;<\/code><\/pre>\n\n\n\n<p>In der setup Methode wird der Pin PA8 als Input mit pullup Widerstand definiert. Mittels attachWorker wird dieses Objekt als Interrupt-Quelle 0 zum Port PA8 angemeldet. CHANGE definiert, dass der Interrupt bei jedem Pegelwechsel ausgef\u00fchrt werden soll. Vordefiniert sind drei Interrupt-Quellen (0, 1 und 2), falls weitere ben\u00f6tigt werden, muss die hpp Datei angepasst werden. In der setup Methode sieht man keinen Aufruf von setDelay() &#8211; das f\u00fchrt dazu, dass die default Wartezeit &#8218;unendlich&#8216; verwendet wird. Die Aktion soll schlie\u00dflich nur beim Auftreten einer Port \u00c4nderung ausgef\u00fchrt werden.<\/p>\n\n\n\n<p>Damit die Aktion zu einem Interrupt m\u00f6glichst schnell ausgef\u00fchrt wird, speichert das tmux Objekt den Handlungsstrang mit dem letzten aktiven Interrupt und f\u00fchrt diesen beim n\u00e4chsten loop als erstes aus, auch wenn er in der normalen Reihenfolge noch nicht dran w\u00e4re.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verwendung einer State Machine<\/h2>\n\n\n\n<p>Im einfachsten Fall f\u00fchrt ein Handlungsstrang periodisch eine Aktion immer wieder aus. In der Praxis ist es aber oft komplizierter. In Abh\u00e4ngigkeit von der Vorgeschichte sollen unterschiedliche Aktionen ausgef\u00fchrt werden. Hierf\u00fcr werden gerne endliche Automaten (State Machine) eingesetzt. Sie sind einfach zu implementieren und k\u00f6nnen Handlungsketten gut und \u00fcbersichtlich abbilden. So eine State Machine setzt sich im Wesentlichen aus zwei Informationen zusammen: welche Zust\u00e4nde gibt es und welche \u00dcberg\u00e4nge zwischen den Zust\u00e4nden sind m\u00f6glich.<\/p>\n\n\n\n<p>Eine einfache M\u00f6glichkeit der Implementierung ist ein switch Statement \u00fcber alle Zust\u00e4nde. Dazu kann man zur besseren Lesbarkeit ein enum definieren, welche alle Zust\u00e4nde mit sprechenden Namen versieht. Im folgenden m\u00f6chte ich es anhand einer einfachen Ampelsteuerung zeigen. Das enum bildet die Zust\u00e4nde Warten, Bereit zum Fahren, Fahren, Halten erwarten ab. Zus\u00e4tzlich gibt es eine Initialisierung, dabei ist es eine Geschmacksfrage, ob man diesen Code in den Konstruktor legt oder in einen eigenen Zustand. Wenn es \u00dcberg\u00e4nge gibt, die eine erneute Initialisierung erfordern (z.B. ein Reset im Fehlerfall), dann w\u00fcrde ich die Initialisierung immer in die State Machine \u00fcbernehmen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>enum TrafficLightStates {\n  INIT,\n  STOP,\n  PREPARE_GO,\n  GO,\n  PREPARE_STOP\n};<\/code><\/pre>\n\n\n\n<p>In der Klasse TrafficLight wird eine lokale Member Variable angelegt welche den aktuellen Zustand speichert &#8211; oder genauer gesagt: den Zustand, der beim n\u00e4chsten Schritt ausgef\u00fchrt werden soll. Sie wird mit INIT belegt, damit wird die Ampel dann in den Grundzustand gesetzt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class TrafficLight: public TMWorker {\n  private:\n    TrafficLightStates nextState = INIT;\n<\/code><\/pre>\n\n\n\n<p>In der setup Methode werden die Microcontroller Ausg\u00e4nge f\u00fcr die LEDs gesetzt. An dieser Stelle soll die State Machine noch ohne Verz\u00f6gerung zum n\u00e4chsten Zustand \u00fcbergehen, setDelay(0) \u00fcbernimmt diese Aufgabe.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    void setup() {\n      pinMode(TLED_RED, OUTPUT);\n      pinMode(TLED_YELLOW, OUTPUT);\n      pinMode(TLED_GREEN, OUTPUT);\n      setDelay(0);\n    }<\/code><\/pre>\n\n\n\n<p>In der loop() Methode gibt es dann das switch Statement \u00fcber den nextState.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    void loop() {\n      switch(nextState) {<\/code><\/pre>\n\n\n\n<p>F\u00fcr jeden Zustand aus den TrafficLightStates gibt es dann ein case Label innerhalb des switch Statements. In dem INIT Zustand werden alle Lichter dunkel geschaltet und als n\u00e4chster Zustand wird Halten (STOP: Rot) gew\u00e4hlt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>        case INIT:\n          digitalWrite(TLED_RED, false);\n          digitalWrite(TLED_YELLOW, false);\n          digitalWrite(TLED_GREEN, false);\n          nextState = STOP;\n          break;<\/code><\/pre>\n\n\n\n<p>Im STOP Zustand wird die rote LED eingeschaltet. Da dieser State normalerweise aus der Gelb-Phase kommt, muss die gelbe LED noch abgeschaltet werden. Wenn man auf Nummer Sicher gehen will, kann man auch die gr\u00fcne LED noch abschalten, sie ist an dieser Stelle aber immer aus. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>        case STOP:\n          Serial1.println(\"RED\");\n          digitalWrite(TLED_RED, true);\n          digitalWrite(TLED_YELLOW, false);\n          setDelay(5000);\n          nextState = PREPARE_GO;\n          break;<\/code><\/pre>\n\n\n\n<p>Die Rot-Phase soll 5 Sekunden dauern, das wird mittels setDelay(5000) erreicht. Nach diesen 5 Sekunden soll die Ampel in die Rot-Gelb Phase \u00fcbergehen, der nextState wird deshalb auf PREPARE_GO gesetzt. <strong>Wichtig: nicht das break am Ende jedes Zustands vergessen!<\/strong><\/p>\n\n\n\n<p>So werden nach und nach alle Zust\u00e4nde implementiert. Die komplette State Machine k\u00f6nnte dann so aussehen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int TLED_RED = PA_10, TLED_YELLOW = PA_11, TLED_GREEN = PA_12;\n\nenum TrafficLightStates {\n  INIT,\n  STOP,\n  PREPARE_GO,\n  GO,\n  PREPARE_STOP\n};\n\nclass TrafficLight: public TMWorker {\n  private:\n    TrafficLightStates nextState = INIT;\n\n  public:\n    void setup() {\n      pinMode(TLED_RED, OUTPUT);\n      pinMode(TLED_YELLOW, OUTPUT);\n      pinMode(TLED_GREEN, OUTPUT);\n      setDelay(0);\n    }\n\n    void loop() {\n      switch(nextState) {\n        case INIT:\n          digitalWrite(TLED_RED, false);\n          digitalWrite(TLED_YELLOW, false);\n          digitalWrite(TLED_GREEN, false);\n          nextState = STOP;\n          break;\n\n        case STOP:\n          digitalWrite(TLED_RED, true);\n          digitalWrite(TLED_YELLOW, false);\n          setDelay(5000);\n          nextState = PREPARE_GO;\n          break;\n\n        case PREPARE_GO:\n          digitalWrite(TLED_YELLOW, true);\n          setDelay(1000);\n          nextState = GO;\n          break;\n\n        case GO:\n          digitalWrite(TLED_RED, false);\n          digitalWrite(TLED_YELLOW, false);\n          digitalWrite(TLED_GREEN, true);\n          setDelay(4000);\n          nextState = PREPARE_STOP;\n          break;\n\n        case PREPARE_STOP:\n          digitalWrite(TLED_YELLOW, true);\n          digitalWrite(TLED_GREEN, false);\n          setDelay(1000);\n          nextState = STOP;\n          break;\n      }\n    }\n} trafficLight;\n<\/code><\/pre>\n\n\n\n<p>Das Schreiben der Aktionen direkt in das switch Statement ist nur sinnvoll, wenn die Zahl der Zust\u00e4nde klein bleibt und die jeweiligen Aktionen \u00fcberschaubar sind. Andernfalls bekommt man ein langes switch Statement welches un\u00fcbersichtlich ist und schnell zu Fehlern f\u00fchrt. In diesem Fall sollte man jede Zustandsaktion in eine eigene Methode auslagern und aus dem switch Statement heraus nur die jeweilige Methode aufrufen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    void loop() {\n      switch(nextState) {\n        case INIT:\n          processInit();\n          break;\n\n        case STOP:\n          processStop();\n          break;\n\n        case PREPARE_GO:\n          processPrepareGo();\n          break;\n\n        case GO:\n          processGo();\n          break;\n\n        case PREPARE_STOP:\n          processPrepareStop();\n          break;\n      }\n    }\n<\/code><\/pre>\n\n\n\n<p>Jetzt hat man schon wieder zus\u00e4tzlichen Code geschrieben, daf\u00fcr aber den Vorteil, dass die Aktionen der Zust\u00e4nde deutlicher getrennt werden. Im richtigen Leben sollte man die einzelnen Teile nat\u00fcrlich auch mit Dokumentation versehen. Die komplette Ampelsteuerung sieht dann so aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int TLED_RED = PA_10, TLED_YELLOW = PA_11, TLED_GREEN = PA_12;\n\nenum TrafficLightStates {\n  INIT,\n  STOP,\n  PREPARE_GO,\n  GO,\n  PREPARE_STOP\n};\n\nclass TrafficLight: public TMWorker {\n  private:\n    TrafficLightStates nextState = INIT;\n\n    void processInit() {\n      digitalWrite(TLED_RED, false);\n      digitalWrite(TLED_YELLOW, false);\n      digitalWrite(TLED_GREEN, false);\n      nextState = STOP;\n    }\n\n    void processStop() {\n      digitalWrite(TLED_RED, true);\n      digitalWrite(TLED_YELLOW, false);\n      setDelay(5000);\n      nextState = PREPARE_GO;\n    }\n\n    void processPrepareGo() {\n      digitalWrite(TLED_YELLOW, true);\n      setDelay(1000);\n      nextState = GO;\n    }\n\n    void processGo() {\n      digitalWrite(TLED_RED, false);\n      digitalWrite(TLED_YELLOW, false);\n      digitalWrite(TLED_GREEN, true);\n      setDelay(4000);\n      nextState = PREPARE_STOP;\n    }\n\n    void processPrepareStop() {\n      digitalWrite(TLED_YELLOW, true);\n      digitalWrite(TLED_GREEN, false);\n      setDelay(1000);\n      nextState = STOP;\n    }\n    \n  public:\n    void setup() {\n      pinMode(TLED_RED, OUTPUT);\n      pinMode(TLED_YELLOW, OUTPUT);\n      pinMode(TLED_GREEN, OUTPUT);\n      setDelay(0);\n    }\n\n    void loop() {\n      switch(nextState) {\n        case INIT:\n          processInit();\n          break;\n\n        case STOP:\n          processStop();\n          break;\n\n        case PREPARE_GO:\n          processPrepareGo();\n          break;\n\n        case GO:\n          processGo();\n          break;\n\n        case PREPARE_STOP:\n          processPrepareStop();\n          break;\n      }\n    }\n} trafficLight;\n<\/code><\/pre>\n\n\n\n<p><strong>Hier befindet sich die GitHub Seite zu der hpp Datei:<\/strong><\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Matthias-Thiele\/TMux\/tree\/main\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/Matthias-Thiele\/TMux\/tree\/main<\/a><\/p>\n\n\n\n<p>F\u00fcr PlatformIO habe ich ein Snippet erstellt, welches man sich in der CPP Snippet Datei hinterlegen kann. Wenn man tmuxw (f\u00fcr TMux Worker) eintippt und Enter dr\u00fcckt, wird ein komplettes Worker Termplate angelegt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\t\"TMux Task\":{\n\t\t\"prefix\": \"tmuxw\",\n\t\t\"body\": &#91;\n\t\t\t\"class $1: public TMWorker {\",\n\t\t\t\"  private:\",\n\t\t\t\"\",\n\t\t\t\"  public:\",\n\t\t\t\"    void setup() {\",\n\t\t\t\"    }\",\n\t\t\t\"\",\n\t\t\t\"    void loop() {\",\n\t\t\t\"    }\",\n\t\t\t\"\",\n\t\t\t\"} $0story1;\",\n\t\t\t\"\"\n\t\t],\n\t\t\"description\": \"Task Multiplexer action template\"\n\t}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Das Arduino Blinky Programm ist das Hello World der embedded Anwender. Es wird auch von Entwicklern verwendet, die sich gut in C oder C++ auskennen &#8211; es dient als Test f\u00fcr die Entwicklungsumgebung und Anbindung an den jeweiligen Microcontroller. F\u00fcr Einsteiger tr\u00e4gt es aber ein hohes Risiko, dass diese fr\u00fchzeitig auf einen falschen Pfad geleitet [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-280","post","type-post","status-publish","format-standard","hentry","category-software"],"_links":{"self":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts\/280","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=280"}],"version-history":[{"count":13,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts\/280\/revisions"}],"predecessor-version":[{"id":295,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts\/280\/revisions\/295"}],"wp:attachment":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=280"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=280"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=280"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}