Hallo Gemeinde. Ich verwende derzeit einen ESP8266 und programmiere diesen unter der weit verbreiteten Arduino IDE. Mal ganz kurz ne simple Frage: Angenommen ich habe an drei Pins jeweils eine LED angeschlossen und möchte diese in unterschiedlichen Intervallen blinken lassen: z.B. LED an Pin D1 100ms an/aus LED an Pin D2 200ms an/aus LED an Pin D3 300ms an/aus Diese Zeiten müssen exakt eingehalten werden. (Ich möchte diese am Oszi nachmessen können.) Parallel möchte ich natürlich mit dem ESP8266-Modul via WLAN kommunizieren. Ich sehe da bisher nur blockierende Funktionen für den Empfang von Daten. Also wie um Himmelswillen, sollen da bitteschön noch die Zeiten der LEDs eingehalten werden? Einen Task/Thread kann ich scheinbar nicht aufziehen... Ich habe nur meine Setup()- und Loop()-Funktion. Vielleicht kann mir jemand grundsätzlich erläutern, wie man mit "pseudo-parallelen" Aufgaben umgeht? Im WWW finde ich überall nur sehr sehr einfach gehaltene Beispiele. Entweder ein reines Webserver-Beispiel oder das Blinken von LEDs. Herzlichen Dank.
Hallo, es gibt inzwischen auch fast alles asyncron, AsyncHTTP, AsyncMQTT usw. Die LEDs kann man aber auch per Timer-Interrupt blinken lassen. Aber: was ist "exakt"? Der Jitter, der durch die WLAN-Routinen des SDK erzeugt wird, ist bemerkbar, Multiplex-Routinen z.B. für 7-Segmet/8 Stellen können durchaus mal erkennbar "zucken" wenn WLAN ungünstig dazwischen gerät. Gruß aus Berlin Michael
Wenn Du das per Timer-Interrupt machst, dann sollte das klappen ohne das der Webserver dazwischen haut. Ist sicher von der Priorität der Interrupts her abbildbar. (genau weiß ich es in dem konkreten Fall nicht, aber alle gängigen Architekturen können es mehr oder weniger fein granuliert)
Ich würde viel lieber mit "richtigen" Tasks arbeiten... Gibt es da ne Lösung?
Stefan S. schrieb: > Ich würde viel lieber mit "richtigen" Tasks arbeiten... > Gibt es da ne Lösung? Ja, das Zauberwort heisst "Betriebssystem". Aber unter Arduino IDE, neeeee neeeee ..... Uwe D. schrieb: > Ist sicher von der Priorität der Interrupts her abbildbar. Aber unter Arduino IDE, neeeee neeeee .....
Du hast das Problem korrekt erkannt. Einige API Aufrufe sind blockierend, manche sogar mehrere Sekunden. Selbst bei den nicht blockierenden werden Dir Interrupts dazwischen kommen. > Ich würde viel lieber mit "richtigen" Tasks arbeiten Das würde dein Problem nicht lösen, denn wenn ein Single-Core Prozessor mehr als einen Task ausführt, muss das Betriebsystem zwischen den Tasks umschalten und das wird auch nicht so 100% genau sein, wie du das gerne hättest. Bestes beispiel ist Windows. Toggel doch mal in Windows einen Pin von parallel-Port und staune, wie unregelmäßig das abläuft. Langer Rede kurzer Sinn: Ich denke es geht nicht. Du brauchst einen zweiten Mikrocontroller dessen Firmware Echtzeit-tauglich ist. Am einfachsten, indem du Timer und ihre Output-Compare Ausgänge verwendest. > Aber unter Arduino IDE, neeeee neeeee ..... In diesem Fall ist ausnahmsweise weder die Arduino IDE noch das Arduino Framework der Knackpunkt, sondern die Firmware von Espressif.
Auf dem ESP gibt es den Befehl 'ticker' https://github.com/esp8266/Arduino/tree/master/libraries/Ticker Ob dir die Genauigkeit reicht musst du checken. Tipp: WLAN nicht einschalten oder auf esp32 wechseln. EDIT: Einfach mal alle Beispiele die mit dem esp Package kamen durcharbeiten und die Doku auf Github lesen (ist recht kurz)https://github.com/esp8266/Arduino
:
Bearbeitet durch User
Stefan S. schrieb: > Ich verwende derzeit einen ESP8266 und programmiere diesen unter der > weit verbreiteten Arduino IDE. > [...] > z.B. > LED an Pin D1 100ms an/aus > LED an Pin D2 200ms an/aus > LED an Pin D3 300ms an/aus > > Diese Zeiten müssen exakt eingehalten werden. (Ich möchte diese am Oszi > nachmessen können.) > [...] > Ich würde viel lieber mit "richtigen" Tasks arbeiten... > Gibt es da ne Lösung? Leider nicht. Kurz zusammengefaßt: der ESP hat nur einen Kern und kann damit zu jedem gegebenen Zeitpunkt nur genau einen einzigen Prozeß ausführen. Damit ist präemptives Multitasking unmöglich. Es geht nur kooperatives Multitasking, welches sich aber nicht mit blocking Calls vereinbaren läßt, sofern harte Echtzeitvorgaben einzuhalten sind. Du könntest Deine LEDs aber an einen Arduino und diesen wiederum an den ESP hängen. Der ESP würde sich dann um die Kommunikation, und der Arduino um die Blinkerei kümmern. Damit hättest Du im Prinzip eine Art asymmetrisches Multiprocessing, das (vage) einer big.LITTLE-Architektur wie etwa im Exynos 5 Octa 5422 (Odroid-XU4) ähnelt. YMMV.
öhm blöde frage: den teil der zeitkritisch ist, mit externen hardware timern lösen? da muss sich doch auch eine schaltung aufbauen lassen, dass die zeiten per software einstellbar sind. könnte einfacher/billiger und vor allem genauer sein als ein zweiter mikrocontroller oder ein esp32.
Sheeva P. schrieb: > hat nur einen Kern und kann damit zu jedem > gegebenen Zeitpunkt nur genau einen einzigen Prozeß ausführen. Damit ist > präemptives Multitasking unmöglich. Es geht nur kooperatives > Multitasking Also kein Linux auf z.B. 486er möglich? ;) Stichwort Interrupt und Scheduler. Im Falle des ESP hast du natürlich recht aber das liegt nicht an dem einen Kern. Auch wenn ich mich wiederhole: 'Ticker' ist der gesuchte "Taskmaker"
Wie STM Apprentice (Gast) schon schrieb, ginge das nur mit einer Interrupt-Server-Routine. Ob das auf der Arduino SDK geht, oder überhaupt ein unbenutzter Timer-Interrupt (des ESP) mit höherer Priorität als die SDK zur Verfügung steht weiß ich aber auch nicht. Wenn das aber der Fall ist, müsste nur eine entsprechende ISR geschrieben und mit eingebunden werden und beim StartUp entsprechend scharf gemacht werden. Da muss man aber schon recht tief einsteigen.
Chr. M. schrieb: > Also kein Linux auf z.B. 486er möglich? ;) Stimmt, aber konkurrierende harte Echtzeit mit blocking Calls? Klar, kann man machen, aber bitte sei so nett und erklär' Du das dem TO. ;-)
Stefan S. schrieb: > Diese Zeiten müssen exakt eingehalten werden. (Ich möchte diese am Oszi > nachmessen können.) Was bedeutet das genau? Was verstehst Du unter "exakt", 1us oder 1ms erlaubte Abweichung? Wenn es Dir nicht um us-Genauigkeit geht, dann kann das entweder mit einem Multitask-System (z.B. freeRTOS) oder über einen Timer-ISR gelöst werden. Den/die Tasks für die Leds priorisierst Du etwas höher als den Task für die WLAN-Kommunikation. Übrigens, der ESP32 hat ein "Remote Controller Peripheral", über das kannst Du auf bis zu 8 Kanälen sehr flexible Ausgangspattern erzeugen, ganz ohne Eingriff der CPU und mit der Genauigkeit der APB_Clock. Viele Grüße, Stefan
> da muss sich doch auch eine schaltung aufbauen lassen, dass die zeiten > per software einstellbar sind. Wie du selbst erkannt hast, brauchst du dazu programmiere, also digital konfigurierbare Timer. Und die bekommt man mit ESP kompatibler Schnittstelle am einfachsten und billigsten in Form eines Mikrocontrollers. AVR wäre hier zu bevorzugen, weil deine Arduino IDE diese unterstützt. Das muss ja kein großes Teil sein, vermutlich genügt Dir schon ein ATtiny85.
Stefan U. schrieb: >> da muss sich doch auch eine schaltung aufbauen lassen, dass die zeiten >> per software einstellbar sind. > > Wie du selbst erkannt hast, brauchst du dazu programmiere, also digital > konfigurierbare Timer. Und die bekommt man mit ESP kompatibler > Schnittstelle am einfachsten und billigsten in Form eines > Mikrocontrollers. AVR wäre hier zu bevorzugen, weil deine Arduino IDE > diese unterstützt. > > Das muss ja kein großes Teil sein, vermutlich genügt Dir schon ein > ATtiny85. jo, wenn die zeiten wirklich per software einstellbar sein müssen dann mag das so sein. das wusste ich nicht, habe nicht so einen bauteilüberblick. wobei so ein attiny85 auch nicht so teuer ist, oder? wenn fixe zeiten reichen, würde sich ein NE555 anbieten?!
Wenn Du auf die Arduino IDE verzichtest und auf den ESP8266 LUA (nodemcu) aufspielst, ist das ein Thema von ca. 10 Zeilen code. https://nodemcu.readthedocs.io/en/master/ Nodemcu bietet mehrere Timer an, in denen man eine LED togglen kann: https://nodemcu.readthedocs.io/en/master/en/modules/tmr/ Der Timer ist dann ein Einzeiler: tmr.alarm(0,300,1, function() toggle_led() end) Und ja, paralleler Webserver geht auch. Ungenauigkeiten von einigen Mikrosekunden sind natürlich möglich, kann Dein Auge aber nicht erfassen. Die Theorie von Echtzeitbetriebssystemen wurde ja oben schon behandelt.
:
Bearbeitet durch User
ich benutze dafür nicht blockierende makros #ifndef PERIODIC_H #define PERIODIC_H //usage: // PERIODIC(100) //{ // digitalWrite(PIN, HIGH/LOW) // } //create an unique name from given parameter and line number #define UNIQUE(name) __CONCAT(name,__LINE__) //creates an unique timer for each instance, ms period in milli-seconds #define WAITBLOCK(ms) \ static uint32_t UNIQUE(timer) = millis(); \ if (millis() - UNIQUE(timer) >= ms) { //creates an unique timer for each instance, ms period in milli-seconds #define PERIODIC(ms) \ static uint32_t UNIQUE(timer) = millis(); \ if (millis() - UNIQUE(timer) >= ms) { \ UNIQUE(timer) = millis(); //XPERIODIC(ms,1) = WAITBLOCK but takes more program code space! //XPERIODIC(ms,0) = PERIODIC //creates an unique timer for each instance, x: repeats 0-255, 0=endless #define XPERIODIC(ms, x) \ static uint32_t UNIQUE(timer) = millis(); \ static uint8_t UNIQUE(xRepeats) = x; \ static uint8_t UNIQUE(lock); \ if (millis() - UNIQUE(timer) >= ms) { \ if (!UNIQUE(lock)) { \ if ((UNIQUE(xRepeats)==0) || (--UNIQUE(xRepeats))) \ UNIQUE(timer) = millis(); \ else UNIQUE(lock)=1; \ } #endif //PERIODIC_H
oder ich benutze meine timed action queue ... #ifndef TIMEDQACTION_H #define TIMEDQACTION_H //usage: //timedQAction led/buzzer(functionOn, functionOff); //buzzer.addQAction(500, 500, 2 ); //action two times with 500ms on/off //buzzer.updateQAction(); //check and call action class timedQAction { public: //---------------------------------------------------------------------- ------------------- #define QUEUE_SIZE 8 //TODO: remove to mytypdefs.h #define SIZE_T uint8_t typedef void (*fptr_t)(void); //********************************************************************** ***************** timedQAction(fptr_t callBackOn, fptr_t callBackOff) { //constructor to create action object //********************************************************************** ***************** callActionOn = callBackOn; callActionOff = callBackOff; } //********************************************************************** ***************** ~timedQAction() {}; //destructor to delete action object, also if ...out of scope //********************************************************************** ***************** //********************************************************************** ***************** inline void updateQAction(void) { //********************************************************************** ***************** if (actionInQueue) { if (!actionFlag && ((millis()-offTime) >= fifo[readIdx][OFFDELAY])) actionOn(); //turn on the next action if (actionFlag && ((millis()-onTime) >= fifo[readIdx][ONDELAY])) actionOff(); } } //********************************************************************** ***************** inline void clearQAction(void) { //********************************************************************** ***************** actionInQueue = 0; readIdx = writeIdx = 0; } //********************************************************************** ***************** bool addQAction(uint16_t onDelay=200, uint16_t offDelay=200, uint8_t numRepeats=1) { //********************************************************************** ***************** if (qSize > actionInQueue) { //if not full write parameters of action to the queue buffer fifo[writeIdx][ONDELAY] = onDelay; fifo[writeIdx][OFFDELAY] = offDelay; fifo[writeIdx][NUMREPEATS] = numRepeats; actionInQueue++; writeIdx = (writeIdx + 1) % qSize; return true; } else return false; } private: //---------------------------------------------------------------------- ------------------- enum { ONDELAY, //action time to stay "on" OFFDELAY, //action time to stay "off" NUMREPEATS, //number of on/off-action repeats }; const static size_t qSize = QUEUE_SIZE; //number of actions that can be queued uint16_t fifo[qSize][3]; //buffer to save queued action unsigned long onTime = 0; //time stamp of action that was turned "on" unsigned long offTime = 0; //time stamp of action that was turned "off" SIZE_T readIdx = 0; //read index of active timed action SIZE_T writeIdx = 0; //write index to add next timed action entry SIZE_T actionInQueue = 0; //number of queued actions bool actionFlag = false; //on/off action state fptr_t callActionOn = NULL; //action-on call back function fptr_t callActionOff = NULL; //action-off call back function //********************************************************************** ***************** inline void actionOn() { //********************************************************************** ***************** if (callActionOn) callActionOn(); //if not NULL call back on-action function actionFlag = true; onTime = millis(); } //********************************************************************** ***************** inline void actionOff() { //********************************************************************** ***************** if (callActionOff) callActionOff(); //if not NULL call back off-action function actionFlag = false; offTime = millis(); if (!fifo[readIdx][NUMREPEATS]) return; //if null than repeat endless else if (!--fifo[readIdx][NUMREPEATS]) { readIdx = (readIdx+1) % qSize; //increment index or wrap around if buf maximum is reached actionInQueue--; //action sequence is done, therefore take it out from queue } } }; #endif //TIMEDQACTION_H
Bleibt also noch zu klären, wie genau die Zeiten eingehalten werden müssen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.