Funktion in eigenem Thread laufen lassen

Hallo zusammen,
ist es mit dem NodeMCU ESP8266 12E möglich eine Funktion in einem eigenen, zusätzlichen Thread laufen zu lassen?
Hintergrund:
Ich möchte in meinem Programmablauf die Ansteuerung des LED-Strip (Adafruit_NeoPixel) mit einem Effekt in einem eigenen Thread laufen lassen, so dass diese den weiteren Programmablauf nicht stört/blockiert.
Aktuell ist das Programm so lange unterbrochen, bis der LED-Effekt beendet ist.

Vielen Dank schon mal im Voraus für eure Mithilfe.

Gruß
Frank

1 Like

Dann hast Du was falsch gemacht.
Zeig mal den Code.
Sowas ähnliches hatten wir gerade die Tage gelöst.

Ich habe als Basis zum Testen das Beispiel Adafruit StrandtestWheel genommen.
Die FastLED Lib hatte ich zuerst, allerdings gibt´s da Probleme wenn WiFi aktiv ist. Daher jetzt mit NeoPixel.

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif


#define NUM_LEDS 12      /* The amount of pixels/leds you have */
#define DATA_PIN D1       /* The pin your data line is connected to */
#define LED_TYPE WS2812B /* I assume you have WS2812B leds, if not just change it to whatever you have */
#define BRIGHTNESS 50   /* Control the brightness of your leds */
#define SATURATION 127   /* Control the saturation of your leds */
#define FRAMES_PER_SECOND 120

#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.setBrightness(50);
  strip.show(); // Initialize all pixels to 'off'
  Serial.begin(115200);
  
}

void loop() {

  Serial.println("Rainbow");
  rainbow(20);
  Serial.println("Rainbow Cycle");
  rainbowCycle(20);
  Serial.println("theaterChaseRainbow");
  theaterChaseRainbow(25);
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

Die Effekte werden nacheinander ausgeführt. Einer nach dem anderen...

:slight_smile: Wie immer... Ich habs geahnt.
delay().

Raus damit und alles schick.
Mal sehen...

Dann wird der Effekt aber extrem kurz :rofl:
Wenn ich z.B. theaterChaseRainbow() verwende sehe ich es nur kurz Aufblitzen, das war nicht der Paln :grin:

Sofern also keine separaten Threads möglich sind, müsste ich die Effekte Äquidistant zu bestimmten Zeiten aufrufen usw. Den Verwaltungsaufwand wollte ich mir sparen :roll_eyes:, aber darauf läuft es dann wahrscheinlich doch raus.

Hat jemand noch weitere Vorschläge die mir das Leben leichter machen könnten :sunglasses:

Warte doch mal ab...
Du bist zu aufgeregt.

Meine TaskMacros

Wo ist das Problem?

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif


#define NUM_LEDS 12      /* The amount of pixels/leds you have */
#define DATA_PIN D1       /* The pin your data line is connected to */
#define LED_TYPE WS2812B /* I assume you have WS2812B leds, if not just change it to whatever you have */
#define BRIGHTNESS 50   /* Control the brightness of your leds */
#define SATURATION 127   /* Control the saturation of your leds */
#define FRAMES_PER_SECOND 120

#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);

uint32_t lastMillis = 0;


void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  strip.begin();
  strip.setBrightness(50);
  strip.show(); // Initialize all pixels to 'off'
  Serial.begin(115200);
}

void loop()
{
  effekte();
}


void effekte()
{
  enum {rainBowStart, rainBow, cycleStart, cycle, chaseStart, chase};
  static byte effekt = rainBowStart;
  switch (effekt)
  {
    case rainBowStart:
      Serial.println("Rainbow");
      effekt = rainBow;
      break;
    case rainBow:
      if (rainbow(20))
        effekt = cycleStart;
      break;
    case cycleStart:
      Serial.println("Rainbow Cycle");
      effekt = cycle;
      break;
    case cycle:
      if (rainbowCycle(20))
        effekt = chaseStart;
      break;
    case chaseStart:
      Serial.println("theaterChaseRainbow");
      effekt = chase;
      break;
    case chase:
      if (theaterChaseRainbow(25))
        effekt = rainBowStart;
      break;
  }
}


bool rainbow(uint8_t wait)
{
  bool myStatus = false;
  static uint16_t j = 0;
  if (millis() - lastMillis >= wait)
  {
    lastMillis += wait;
    if (j < 256)
    {
      j++;
      for (byte i = 0; i < strip.numPixels(); i++)
      {
        strip.setPixelColor(i, Wheel((i + j) & 255));
      }
      strip.show();
    }
    else
    {
      j = 0;
      myStatus = true;
    }
  }
  return myStatus;
}
bool rainbowCycle(uint8_t wait)
{
  delay(200);
  Serial.println("Nach dem Muster rainbow bauen!");
  return true;
}
//Theatre-style crawling lights with rainbow effect
bool theaterChaseRainbow(uint8_t wait)
{
  delay(200);
  Serial.println("Nach dem Muster rainbow bauen!");
  return true;
}
uint32_t Wheel(byte WheelPos)
{
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85)
  {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170)
  {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

Auch der ESP8266 sollte eigentlich das hier (FreeRTOS) können:

  xTaskCreate(    myTaskFunction,       /* Task function. */
                  "TaskName",           /* String with name of task. */
                  8192,                 /* Stack size in bytes. */
                  (void*)this,          /* "this" ist nur ein Beipiel. NULL bei nix. */
                                        /* Parameter passed as input of the task */
                  1,                    /* Priority of the task. */
                  &myTaskHandle);       /* Task handle; oder NULL falls nicht benötigt */

Das Gerüst einer Taskfunktion wäre dieses:

void myTaskFunction(void* parameters)
{
  // Das ist schon sehr C-lastig: 
  // Hinter dem void Pointer kann sich alles mögliche verstecken 
  // - man muss es halt casten.
  // Datentyp* data = (Datentyp*)parameters;

  while (1)
  {
    // hier kann die Taks ihre Arbeit erledigen
  }

  // Die Task darf nie (wirklich nie!) zurückkehren (return),
  // Deshalb löschen wir uns im Bedarfsfall lieber selbst bevor das passiert
  vTaskDelete(NULL);
}

Du meinst, wer delay() nicht gegen millis() tauschen kann, hat RTOS verinnerlicht? :wink:
Na denn...

Warum soll man sich mit dem millis() Hobby Gestümpere rum schlagen, wenn es doch so ein feines FreeRTOS gibt.

Danke, das schaue ich mir mal an.

Satt eines unnötigen Spruches könntest du einfach meine Frage beantworten! Die Steht übrigens gaanz oben in diesem Beitrag ;-).
Für dich wiederhole ich die Frage aber gerne noch mal:
Ist es mit dem NodeMCU ESP8266 12E möglich eine Funktion in einem eigenen, usätzlichen Thread laufen zu lassen?

Ich hoffe wir können jetzt technisch weiter diskutieren. Dankeschön.

Und übrigens, Danke für dein Code-Beispiel. Auch wenn das leider nicht meine Frage beantwortet.

Hi, danke für die Info. Prinzipiell gibt's also Möglichkeiten für mehrer Tasks :slight_smile:
Welche Bibliothek muss ich für das Board NodeMCU ESP8266 12E verwenden?
Ich habe das FreeRTOS von Philip Stevens verwendet:

Den ersten Test habe ich mit dem Beispiel FreeRTOS -> Notifications versucht, das liefert mir aber folgende Fehlermeldung:
"Fehler beim Kompilieren für das Board NodeMCU 1.0 (ESP-12E Module)."

Sehe ich dass richtig, dass FreeRTOS doch nicht mit dem ESP8266 funktioniert? Oder sind weitere Einstellungen notwendig?

Das vom Philip brauchst Du gar nicht (das ist auch für ATmega-CPUs, nicht für die ESPs).
Die FreeRTOS-Implementierung steckt schon im Board-Support für die ESPs mit drin.
Ich denke, ein simples `#include <Arduino.h>" müsste reichen (damit der Compiler weiß, wo Du unterwegs bist - eben nicht in der Espressif IDF-Welt).

Habe leider gerade keinen ESP8266 aufgebaut, aber beim ESP32 ist das so.
Irgendwer hat hier auch mal eine Einführung "using FreeRTOS auf..." geschrieben (ich muss mir sowas bookmarken :frowning: ); kann aber sein, dass das für den 32er war.


Gruß Walter

Es würde funktionieren, wenn es einer implementiert hätte. (das könnte deine Rolle werden)

Zudem gibt es ein ESP8266 RTOS SDK, aber leider nicht für Arduino.

Die Antwort lautet also: Nein.

Tu du das.

Danke - habe ich was gelernt und ziehe meinen Vorschlag zurück.

Bitte die gestiftete Verwirrung zu entschuldigen.

Mit einem ESP32 als Board in der IDE ausgewählt lässt es sich zumindest mal compilieren. Ein funktionierendes Bord habe ich nicht leider zur Verfügung.
Mit dem ESP8266 geht´s leider nicht. Wie von @combie bereits erwähnt :disappointed:

Okay, dann ist das Thema vorerst mal durch...
Vielen Dank an alle und ich wünsche euch noch ein frohes Fest und einen guten Rutsch ins neue Jahr.
Frank

Du könntest darum mal versuchen zu überlegen Deinen Sketch blockadefrei sprich ohne delay() zu programmieren.
So geht en nähmlich ohne mehrere Paralell ablaufende Programme haben zu müssen.
Grüße Uwe

Das beantwortet ja die Frage nicht. :wink:

Bitte.

Nu hast aber die Antwort: Geht nicht. Ich lese raus: Zufrieden bist damit aber auch nicht :slight_smile: - ne im ernst. die Handvoll Zeilen umschreiben ist doch nicht so schlimm...