WS2812 als Array

Hallo :slight_smile:
Ich möchte gerne mehrere WS 2812 Leds (über Arduino Uno) als Array in einer Funktion ansteuern. Diese sollen in einem bestimmten Zeitfenster angehen und dann wieder aus.
Ausgeschrieben wäre es folgendermaßen:

#include <Adafruit_NeoPixel.h>
#define PIN         7
#define NUMPIXELS   4
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pixels.begin();
  pixels.show();
}

void loop() {
 int a1= 1000;
 int a2= 8000;
 int b1= 4000;
 int b2= 15000;

   if (millis() >= a1 && millis()< b1)
  {
      pixels.setPixelColor(2, pixels.Color(0, 100, 0));
    }
  else {
     pixels.setPixelColor(2, pixels.Color(0, 0, 0));}

  if (millis() >= a2 && millis()< b2)
  {
    pixels.setPixelColor(3, pixels.Color(0, 0, 100));
  }
   else {
     pixels.setPixelColor(3, pixels.Color(0, 0, 0));}
}

Wie kann ich dies nun anlegen ohne jeden einzelnen Befehl ausführlich zu schreiben?
Also so in diese Richtung:

#include <Adafruit_NeoPixel.h>
#define PIN         7
#define NUMPIXELS   4
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  pixels.begin();
  pixels.show();
}

void loop() {

 const int LEDs = 2;
 const uint32_t LEDposition[LEDs]={2,3};
 static uint32_t a[LEDs]={1000,8000};
 static uint32_t b[LEDs]={4000,15000};
 const uint32_t farbe[LEDs]={pixels.Color(0, 100, 0), pixels.Color(100, 0, 0)};

   if (millis()>= a[LEDposition] && millis()< b[LEDposition])
  {
      pixels.setPixelColor(LEDposition, farbe[LEDposition]);
     pixels.show();
    }
  else {
     pixels.setPixelColor(LEDposition, pixels.Color(0, 0, 0));
     pixels.show();}
}

vielen Dank :slight_smile:

der Ansatz hat schon gepasst.
Nur würde ich die Sachen die Zusammengehören in eine Struktur packen.

So ca läuft das:

#include <Adafruit_NeoPixel.h>
#define PIN         7  // du 7 ich 8
#define NUMPIXELS   4
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

struct Program
{
  uint16_t led;        // der Pixel
  uint32_t on;         // die EIN zeit
  uint32_t off;        // die AUS zeit
  uint32_t color;      // die Farbe
  uint32_t previousMillis;  // timestamp für das letzte schalten
  uint8_t state;       // ist aktuell ein oder aus
};

Program program []
{
  {2, 1000, 8000, 0x008800, 0, 0},
  {3, 4000, 15000, 0x880000, 0, 0}
};

void doBlinkiblinki()
{
  uint32_t currentMillis = millis();
  for (auto &i : program)
  {
    if (i.state == 0 && currentMillis - i.previousMillis > i.off)
    {
      Serial.print(i.led); Serial.println(F(" go on"));
      pixels.setPixelColor(i.led, i.color);
      pixels.show();
      i.state = 1;
      i.previousMillis = currentMillis;
    }
    else if (i.state == 1 && currentMillis - i.previousMillis > i.on)
    {
      Serial.print(i.led); Serial.println(F(" go off"));
      pixels.setPixelColor(i.led, 0);
      pixels.show();
      i.state = 0;
      i.previousMillis = currentMillis;
    }
  }
}
void setup() {
  pixels.begin();
  pixels.show();
  Serial.begin(115200);
}

void loop() {
  doBlinkiblinki();
}
1 Like

Du fragst nach einer Funktion:

#include <Adafruit_NeoPixel.h>
#define PIN         7
#define NUMPIXELS   4
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pixels.begin();
  pixels.show();
}

void loop() {
  animation(0);
  animation(1);
  pixels.show();
}

void animation(byte pos)
{
  const byte led[]       = {2, 3};
  const unsigned int a[] = {1000, 8000};
  const unsigned int b[] = {4000, 15000};
  const uint32_t farbe[] = {pixels.Color(0, 100, 0), pixels.Color(0, 0, 100)};

  if (millis() >= a[pos] && millis() < b[pos])
  {
    pixels.setPixelColor(led[pos], farbe[pos]);
  }
  else {
    pixels.setPixelColor(led[pos], pixels.Color(0, 0, 0));
  }
}

Zu langsam :pensive:

1 Like

@noiasca @agmue vielen Dank für die schnellen Lösungen :slight_smile: Leider kann ich nur einmal die Lösung anklicken :confused:
bei der Lösung @noiasca hätte ich noch eine Rückfrage:
was hat es mit dem "i" auf sich? bzw. wie greift "doBlinkiblinki" auf die Werte in der struct zu?

a) du kannst agmues Beitrag noch mit einem Herchen versehen.
b) das "i" ist teil eines "Auto ranged based for"
Eine Schleife die durch alle Instanzen des Array program geht
Der Typ (du verwendest vieleicht sonst z.B. ein "for (int j = 0 ...." ist in dem Fall auto.
Das heißt er nimmt automatisch den Typ der Struktur --> also Program.
mit dem & sagst du dem Compiler dass i eine Referenz sein soll.
So, damit steht im jeden Durchgang im i genau eine Instanz der Struktur program zur Verfügung. Beim ersten Durchlauf also

  {2, 1000, 8000, 0x008800, 0, 0},

somit
i.on = 1000,
i.off = 8000
i.color = 0x008800
usw

Ah okay. Es könnte sein, dass ich später nochmal nachfragen muss aber habe es glaube erstmal erfasst. Das erscheint mir als sehr praktisch Danke :slight_smile:

einfach ins Hirn einbrennen

for (auto Referenz Variable Doppelpunkt Array)

irgendwann merkst dir, dass du nur hinten das Array anpassen musst der Rest flutscht dann.
In anderen Programmiersprachen kennst du das vieleicht als "foreach".

Auto range based for

:+1:

Danke für das Herzchen!

Man kann aus der Funktion auch eine Methode in einer Klasse machen:

// https://forum.arduino.cc/t/ws2812-als-array/966319/3
// WS2812 als Array
#include <Adafruit_NeoPixel.h>
#define PIN         7  // du 7 ich 8
#define NUMPIXELS   4
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

class Program
{
    const uint16_t pixel;     // der Pixel
    const uint32_t ein;       // die EIN zeit
    const uint32_t aus;       // die AUS zeit
    const uint32_t farbe;     // die Farbe
    uint32_t previousMillis;  // timestamp für das letzte schalten
    bool state;               // ist aktuell ein oder aus
  public:
    Program(const uint16_t pixel, const uint32_t ein, const uint32_t aus, const uint32_t farbe) :
      pixel(pixel), ein(ein), aus(aus), farbe(farbe), previousMillis(0), state(0)
    {}
    void doBlinkiblinki()
    {
      uint32_t currentMillis = millis();
      if (state == 0 && currentMillis - previousMillis > aus)
      {
        Serial.print(pixel); Serial.println(F(" go on"));
        pixels.setPixelColor(pixel, farbe);
        pixels.show();
        state = 1;
        previousMillis = currentMillis;
      }
      else if (state == 1 && currentMillis - previousMillis > ein)
      {
        Serial.print(pixel); Serial.println(F(" go off"));
        pixels.setPixelColor(pixel, 0);
        pixels.show();
        state = 0;
        previousMillis = currentMillis;
      }
    }
};

Program program []
{
  {2, 1000,  8000, 0x008800},
  {3, 4000, 15000, 0x880000}
};

void setup() {
  pixels.begin();
  pixels.show();
  Serial.begin(115200);
}

void loop() {
  for (auto &i : program) i.doBlinkiblinki();
}

Die Ähnlichkeit mit #2 ist natürlich gewollt :slightly_smiling_face:

Eine Struktur/Klasse kann also nicht nur unterschiedliche Datentypen, sondern auch Funktionen beinhalten.

Eine Struktur ist von sich aus public, eine Klasse private, sonst gibt es keinen Unterschied.

Danke :slight_smile:
ich hätte vllt. noch eine Frage zu deiner ersten Lösung: ist es möglich die Werte eines Arrays im "else" Abschnitt zu multiplizieren und zurückzugeben?

also im Sinne von:

else {
    pixels.setPixelColor(led[pos], pixels.Color(0, 0, 0));
    
    a[pos]= a[pos]*2;
  }
return a[pos];

Das geht nicht mit Konstanten, aber mit einer Variablen:

else {
    pixels.setPixelColor(led[pos], pixels.Color(0, 0, 0));
    rueck = a[pos]*2;
  }
return rueck;

Ohne den Zusammenhang zu kennen, ist Hilfe aber schwierig, denn möglicherweise suchst Du nach einer Referenz.

Die Frage wäre in Bezug auf die if (millis() >= a[pos] && millis() < b[pos]) Schleife.
Also, dass diese dann mit den multiplizierten Werten in den Arrays a[pos] und b[pos] wieder beginnt :slight_smile:

vermutlich anstelle millis() >= a[pos] dann millis() >= rueck oder? ich probiere es mal

der Effekt wäre, dass die beiden Leds nach einer einstellbaren Zwischenzeit in der sie aus sind erneut angehen. Also die if (millis() >= a[pos] && millis() < b[pos]) fortlaufend wieder beginnt nur mit anderen Werten in den Arrays a[pos] und b[pos].

dann lösche das const, mache also eine Variable draus und erhöhe direkt die Variable(n).

Am Ende einer Funktion werden die Variablen gelöscht. Wenn Du das nicht möchtest, mache sie static. Im ganzen Satz:

Dann ersetze das const durch static, mache also eine statische Variable draus und erhöhe direkt die Variable(n).

danke leider bekomme ich es nicht hin :confused:
bei diesem Code gehen sie nicht noch einmal an:

void animation(byte pos)
{
  const byte led[] = {2, 3};
  static uint32_t a[] = {1000, 3000};
  static uint32_t b[] = {4000, 6000};
  const uint32_t farbe[] = {pixels.Color(0, 100, 0), pixels.Color(0, 0, 100)};
  uint32_t  rueck1 = a[pos];
  uint32_t  rueck2 = b[pos];

  if (millis()>= rueck1 && millis() < rueck2)
  {
    pixels.setPixelColor(led[pos], farbe[pos]);
  }
  else {
    pixels.setPixelColor(led[pos], pixels.Color(0, 0, 0));
      rueck1 = a [pos]*6;
      rueck2 = b [pos]*6;
  }
 
}
void animation(byte pos)
{
  const byte led[]       = {2, 3};
  static uint32_t a[] = {1000, 8000};
  static uint32_t b[] = {4000, 15000};
  const uint32_t farbe[] = {pixels.Color(0, 100, 0), pixels.Color(0, 0, 100)};

  if (millis() >= a[pos] && millis() < b[pos])
  {
    pixels.setPixelColor(led[pos], farbe[pos]);
  }
  else {
    pixels.setPixelColor(led[pos], pixels.Color(0, 0, 0));
    a[pos]=a[pos]*6;
    b[pos]=b[pos]*6;
  }
}

ungeprüft.

pass auf, irgendwann werden dir deine Variablen überlaufen...

Das geht nur genau einmal nach Reset.

Das entspricht Deinem ursprünglichen Programm, da ist das auch so.

Zur Wiederholung benötigst Du eine Differenz, also sowas wie:

if (state == 0 && currentMillis - previousMillis > aus)

ah okay ja so richtig funktioniert es nicht. Mit der Differenz muss ich erstmal überlegen wie das geht danke :slight_smile:

Deine Uhr wird um Mitternacht zurückgesetzt, beim µC ist das Reset. "Wir treffen uns um 8 Uhr" funktioniert genau einmal am Tag. Um Mitternacht läuft Deine Uhr über, startet wieder bei 0:00 Uhr. Hingegen läuft millis() nach 2³² Millisekunden (ca. 49 Tagen) über.

"Wir treffen uns jede Stunde Wir treffen uns in einer Stunde." funktioniert immer und durchgehend, auch bei falsch gehenden Uhren.

Die Differenz gewährleistet einen reibungslosen Übergang beim Millisüberlauf.

#include <Adafruit_NeoPixel.h>
#define PIN         7
#define NUMPIXELS   4
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pixels.begin();
  pixels.show();
}

void loop() {
  animation(0);
  animation(1);
}

void animation(byte pos)
{
  uint32_t jetzt = millis();
  const byte pixel[]       = {2, 3};
  const unsigned int ein[] = {1000 , 8000};
  const unsigned int aus[] = {4000, 15000};
  const uint32_t farbe[] = {pixels.Color(0, 100, 0), pixels.Color(0, 0, 100)};
  static bool state[] = {0, 0};
  static uint32_t vorhin[] = {0, 0};

  if (!state[pos] && jetzt - vorhin[pos] >= aus[pos])
  {
    pixels.setPixelColor(pixel[pos], farbe[pos]);
    pixels.show();
    state[pos] = true;
    vorhin[pos] = jetzt;
  }
  else if (state[pos] && jetzt - vorhin[pos] >= ein[pos])
  {
    pixels.setPixelColor(pixel[pos], pixels.Color(0, 0, 0));
    pixels.show();
    state[pos] = false;
    vorhin[pos] = jetzt;
  }
}
1 Like