Glücksrad mit Neopixeln

Grüße!

Ich möchte ein Glücksrad mit NeoPixeln bauen. Es funktioniert so weit auch, allerdings kommen jetzt die “Feinheiten” und ich weiss nicht weiter. Ich hätte gerne einen dunkler werdenden “Schwanz” der meine drei LEDs (als Zeiger) verfolgt. Dieser sollte auch kürzer werden mit abnehmender Geschwindigkeit oder verbleibender Zeit…da hört es aber bei mir auf.
Bitte schaut es euch mal an…

Danke.

#include <Adafruit_NeoPixel.h>
#include "OneButton.h"
#define PIXELPIN       4
#define NUMPIXELS      60
#define BUTTON_TAP     3

OneButton buttonTAP(BUTTON_TAP, false);

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ800);

int i = 0;
int pause = 0; // 100 Millisekunden Pause bis zur Ansteuerung der nächsten LED.

unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
unsigned long vergangen = 0;
unsigned long vergangen2 = 0;


long Drehdauer = 0;
long Bremse;
long Tail;

bool Start = false;



void setup()
{
  Serial.begin(9600);
  pixels.begin(); // Initialisierung der NeoPixel
  pixels.setBrightness(10);
  pinMode( BUTTON_TAP, INPUT );

  // Attatch click functions to the button
  buttonTAP.attachClick(singleClick);


}

void loop()
{
  // Every cycle we need to tick the button state
  buttonTAP.tick();

  pixels.show();



  if (Start == true)
  {

    unsigned long currentMillis = millis();       //Dauer
    //      Serial.println("true");
    //Serial.println(Drehdauer);
    if (currentMillis - previousMillis <= Drehdauer)
    {
      if (currentMillis - previousMillis2 >= Bremse)
      {

        previousMillis2 = currentMillis;

        Tail = (Drehdauer / 100) - ((currentMillis - previousMillis));
        if (Tail <= 0)
        {
          Tail = 0;
        }
        Serial.println(Tail);
        
        pixels.setPixelColor(i, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün
        pixels.setPixelColor(i + 1, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün
        pixels.setPixelColor(i + 2, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün
        pixels.setPixelColor(i - Tail, pixels.Color(150, 150, 0)); // Der vorherige Pixel wird abgeschaltet
        pixels.setPixelColor(i - Tail, pixels.Color(0, 0, 0));

        pixels.show(); // Durchführen der Pixel-Ansteuerung

        if (i == 0) pixels.setPixelColor(59, pixels.Color(0, 0, 0)); // Im Fall von Pixel "0" muss die vorherige (39) ausgeschaltet werden.
        pixels.show(); // Durchführen der Pixel-Ansteuerung

        i++;
        
        if (currentMillis - previousMillis >= Drehdauer / 2)
        {
          Bremse = Bremse + 1;
        }
        
        if (i == 60) i = 0; // Wenn die Variable den Wert 40 erreicht hat, wird die Variable auf den Wert "0" zurück gesetzt, da die Nummerierung der LEDs nur von 0 bis 39 geht.
      }
    }
    else
    { 
      Start = false;
      //Serial.println("false");
    }

  }
}
void singleClick()
{
  //Serial.println("press");
  Start = true;
  previousMillis = millis();
  Drehdauer = (random(100) + 30) * 50;
  Bremse = 1;

}

bist sicher dass dein Code klappt? beim Übergang vom Pixel 39 auf 0 kann das i+2 eigentlich nicht wirklich gut funktionieren oder?

mach dir eine Funktion die dir zu einer gegebnen Zahl und einem gegebenen Offset die entsprechende Pixel-Nummer zurückgibt und den Überlauf bei 39 richtig berücksichtigt.

je nach Geschwindigkeit/Schwanzlänge rufst dann mehr oder weniger oft die Funktion mit entsprechendem Offset auf.

PS.: deine Komentare sind leider verwirrend:
if (i == 60) i = 0; // Wenn die Variable den Wert 40 erreicht hat, wird die Variable auf den Wert "0" zurück gesetzt, da die Nummerierung der LEDs nur von 0 bis 39 geht.

Hallo noiasca.

Sorry für die alten Kommentare. Das sind teilweise Überbleibsel vom zu Grunde liegenden Code...ich benutze sie kaum und achte darum auch nicht darauf.

Das i+ funktioniert gut. I- funktioniert nicht wirklich gut. Denke auch das es an dem „übersprung“ liegt, habe aber noch keine Lösung.

Interessanter Weise sind auch die Farben rot und grün vertauscht wie man an den Kommentaren dahinter erkennen kann. Aber damit kann ich leben.

Du kannst dir die Frage stellen wofür NEO_GRB steht und welche andere Optionen es wohl gibt...

…mit unten stehendem Code funktioniert der Schwanz. Allerding nicht gedimmt und auch nicht in der anderen Farbe wie zu Testzwecken angegeben…

pixels.setPixelColor(i - Tail, pixels.Color(150, 150, 0));

Was meinst Du mit Offset?

Hier nochmal der Code mit geänderten Kommentaren in gänze:

#include <Adafruit_NeoPixel.h>
#include "OneButton.h"
#define PIXELPIN       4
#define NUMPIXELS      60
#define BUTTON_TAP     3

OneButton buttonTAP(BUTTON_TAP, false);

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ800);

int i = 0;

unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
unsigned long vergangen = 0;
unsigned long vergangen2 = 0;


long Drehdauer = 0;
long Bremse;
long Tail;

bool Start = false;



void setup()
{
  Serial.begin(9600);
  pixels.begin(); // Initialisierung der NeoPixel
  pixels.setBrightness(20);
  pinMode( BUTTON_TAP, INPUT );

  // Attatch click functions to the button
  buttonTAP.attachClick(singleClick);


}

void loop()
{
  // Every cycle we need to tick the button state
  buttonTAP.tick();

  pixels.show();



  if (Start == true)
  {

    unsigned long currentMillis = millis();       //Dauer
    //      Serial.println("true");
    //Serial.println(Drehdauer);
    if (currentMillis - previousMillis <= Drehdauer)
    {
      if (currentMillis - previousMillis2 >= Bremse)
      {

        previousMillis2 = currentMillis;

        Tail = (Drehdauer / 100) - ((currentMillis - previousMillis)/100);
        if (Tail <= 0)
        {
          Tail = 0;
        }
        Serial.println(Tail);
        
        pixels.setPixelColor(i, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün (bei mir Rot)
        pixels.setPixelColor(i + 1, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün (bei mir Rot)
        pixels.setPixelColor(i + 2, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün (bei mir Rot)
        pixels.setPixelColor(i - Tail, pixels.Color(150, 150, 0)); 
        pixels.setPixelColor(i - Tail, pixels.Color(0, 0, 0));

        pixels.show(); // Durchführen der Pixel-Ansteuerung

        if (i == 0) pixels.setPixelColor(59, pixels.Color(0, 0, 0)); // Im Fall von Pixel "0" muss die vorherige (59) ausgeschaltet werden.
        pixels.show(); // Durchführen der Pixel-Ansteuerung

        i++;
        
        if (currentMillis - previousMillis >= Drehdauer / 2)
        {
          Bremse = Bremse + 1;
        }
        
        if (i == 60) i = 0; // Wenn die Variable den Wert 60 erreicht hat, wird die Variable auf den Wert "0" zurück gesetzt, da die Nummerierung der LEDs nur von 0 bis 39 geht.
      }
    }
    else
    { 
      Start = false;
      //Serial.println("false");
    }

  }
}
void singleClick()
{
  //Serial.println("press");
  Start = true;
  previousMillis = millis();
  Drehdauer = (random(100) + 30) * 50;
  Bremse = 1;

}

Das NEO-GRB ist schon klar...ich habe aber auch das "Pixel Chaser" Spiel (Was ich mit Glücksrad und anderen einfachen Farbeffekten per switch-case umschalten möchte) und auch bei diesem "Originalen" Code waren die Farben vertauscht...aber das nur am Rande, es stört mich wie gesagt nicht.

Hi

NEO-GRB ... die Buchstaben R G und B deuten die Reihenfolge an, in Der die Farbwerte erwartet werden.
Wenn Du jetzt NEO-GBR schreibst (und die Lib Das bietet), dann sind wie durch Zauberhand Blau und Rot vertauscht.
Böse Zungen behaupten, Da wäre sogar Absicht hinter gewesen, Das So einstellbar zu machen.

Andere böse Zungen schwätzen auch davon, daß es durchaus verschiedene Reihenfolgen der Farbwerte gibt ... wenn man Beides kombiniert, ergibt's direkt Sinn.

MfG

PS: Verzettel Dich nicht in der 27.ten Zusatz-Blinkerei - mache erst EINE fertig - mit Dimmen und den ganzen Kram, Den Du Dir dafür vorgestellt hast.
Was bringt Es Dir, wenn zwar immer mehr Spielereien laufen, aber eben nur auf drei Zylindern und bestenfalls mit Standgas ...

Hi Postmaster.

OK, um das Thema RGB/GRB/GBR oder wie auch immer zum Abschluss zu bringen: Ich habe es soweit verstanden. Danke trotzdem für die weiteren Auführungen.

Konzentrieren wir uns auf die Probleme die noch da sind...Den Schwanz habe ich hinbekommen. Allerdings kränkelt tatsächlich der Übergang in die "neue Runde" des Glücksrades. Es bleiben immer ein paar Pixel an und irgendwann ist der Kreis gefüllt.

Darum nochmals die Frage: Was ist mit "Offset" gemeint, bzw. wie kann ich die "neue Runde" sauber gestalten?

Ohne Schwanz läuft es ja so wie es soll. Deswegen hier mein Hilfegesuch, da ich bei der 27.n (oder 28.n :wink: ) Spielerei angelangt bin...wobei ich nich bestreiten will das dafür evtl. trotzdem die Basics fehlen...ich jedoch trotz Google und Nachdenken/Probieren nicht weiter komme.

Ich denke auch das hier:

pixels.setPixelColor(i - Tail, pixels.Color(150, 150, 0));
pixels.setPixelColor(i - Tail, pixels.Color(0, 0, 0));

ist keinesfalls der Stein der Weisen weil irgendwie der Bereich fehlt. Also alles was zwischen "i" und "i-Tail" liegt...

Und warum gibt es einen Schwanz, den aver in Rot - und nicht wie ersonnen in :astonished: hmmm ja was ist das eigentlich (150, 150, 0)...braun?! :grinning:

Hi

Du hast Pixel-Nummern von 0 bis Anzahl-1.
Einfacher ist der Schwanz in Plus-Richtung.
Du hast einen Pixel, Den Du setzt - i.
Und einen Abstand davon, wo Du Den wieder abschaltest - lang.
pixels.setPixelColor(i,255,255,255);
pixels.setPixelColor((i + lang) % Anzahl,0,0,0);

Wenn der Schwanz in Minus-Richtung gehen soll, musst Du ‘lang’ von i abziehen - wenn ein Wert <0 raus kommt, musst Du die ‘Anzahl’ wieder addieren.
Würde Das in eine Funktion auslagern aka ‘uint16_t normiereNummer(uint16_t pixel,int16_t abstand)’.
Dort wird der Abstand (kann auch negativ sein) zu ‘pixel’ addiert und dann auf die Grenzen geprüft.
Nach Oben geht % (Modulo), nach Unten müssen Zahlen <0 um Anzahl (oder PixelCount) angehoben werden.

Das Spiel kannst Du dann auch mit 12 Dimm-Stufen machen - jede Dimm-Stufe hat dann halt einen anderen Abstand zum ‘i’-Pixel.

MfG

PS: Warum Dein Braun ein Rot ist … jo … ist’s vll. rot-braun? :wink:

MarcusBonus:
Darum nochmals die Frage: Was ist mit “Offset” gemeint, bzw. wie kann ich die “neue Runde” sauber gestalten?

Postmaster nennt es “Abstand”. Ich sag halt offset dazu.

Überläufe hab ich nicht mit modulo gemacht sondern einfach zwei Ifs hingeschrieben.
Wenn ich so die Serial Ausgabe beobachte funktioniert der Überlauf imho korrekt.

damit ich es starten kann musste ich die Bauds erhöhen und eine Serial-Abfrage einfügen.

#include <Adafruit_NeoPixel.h>
#include "OneButton.h"
#define PIXELPIN       4
#define NUMPIXELS      60
#define BUTTON_TAP     3

OneButton buttonTAP(BUTTON_TAP, false);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ800);

int i = 0;

unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
unsigned long vergangen = 0;
unsigned long vergangen2 = 0;

long Drehdauer = 0;
long Bremse;
long Tail;

bool Start = false;


byte getNeighbour(uint8_t actual, int8_t offset)
// returns the next or previous led from actual with the defined offset
{
  int16_t temp = actual + offset; // wir nehmen temporär ein signed int damit wir abhängig vom offset auch minus werden können ;-) welch ein Kommentar ...

  if (temp > NUMPIXELS - 1) temp = temp - NUMPIXELS; // Überlauf nach oben abfangen
  if (temp < 0) temp = temp + NUMPIXELS;             // Unterlauf nach unten abfagen
  return byte(temp);
}


void setup()
{
  Serial.begin(115200);
  pixels.begin(); // Initialisierung der NeoPixel
  pixels.setBrightness(20);
  pinMode( BUTTON_TAP, INPUT );

  // Attatch click functions to the button
  buttonTAP.attachClick(singleClick);
}

void loop()
{
  // Every cycle we need to tick the button state
  buttonTAP.tick();

  pixels.show();

  if (Serial.available())
  {
    Serial.read();
    singleClick();
  }

  if (Start == true)
  {
    unsigned long currentMillis = millis();       //Dauer
    //      Serial.println("true");
    //Serial.println(Drehdauer);
    if (currentMillis - previousMillis <= Drehdauer)
    {
      if (currentMillis - previousMillis2 >= Bremse)
      {
        previousMillis2 = currentMillis;

        Tail = (Drehdauer / 100) - ((currentMillis - previousMillis) / 100);
        if (Tail <= 0)
        {
          Tail = 0;
        }
        Serial.println(Tail);

        pixels.setPixelColor(i, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün (bei mir Rot)
        pixels.setPixelColor(i + 1, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün (bei mir Rot)
        pixels.setPixelColor(i + 2, pixels.Color(0, 255, 0)); // Pixel leuchtet in der Farbe Grün (bei mir Rot)
        pixels.setPixelColor(i - Tail, pixels.Color(150, 150, 0));
        pixels.setPixelColor(i - Tail, pixels.Color(0, 0, 0));

        pixels.show(); // Durchführen der Pixel-Ansteuerung

        if (i == 0) pixels.setPixelColor(59, pixels.Color(0, 0, 0)); // Im Fall von Pixel "0" muss die vorherige (59) ausgeschaltet werden.
        pixels.show(); // Durchführen der Pixel-Ansteuerung


        // Nur ein paar Testausgaben, damit ich ohne Hardware sehe, dass meine Funktion klappt:
        Serial.print(F("\nEin Vorläufer:           ")); Serial.println(getNeighbour(i, 2));
        Serial.print(F("Der Läufer:              ")); Serial.println(getNeighbour(i, 1));
        Serial.print(F("Ein Nachläufer:          ")); Serial.println(i);
        Serial.print(F("Ein kurzer Schweif:      ")); Serial.println(getNeighbour(i, -1));
        Serial.print(F("Eine Schweifverlängerung:")); Serial.println(getNeighbour(i, -2));
        //


        i++;

        if (currentMillis - previousMillis >= Drehdauer / 2)
        {
          Bremse = Bremse + 1;
        }

        if (i == 60) i = 0; // noiasca: Wenn die Variable den Maximalwert erreicht hat, wird die Variable auf den Wert "0" zurück gesetzt, da die Nummerierung der LEDs nur bis Maximalwert-1 gehen darf.
      }
    }
    else
    {
      Start = false;
      //Serial.println("false");
    }

  }
}

void singleClick()
{
  //Serial.println("press");
  Start = true;
  previousMillis = millis();
  Drehdauer = (random(100) + 30) * 50;
  Bremse = 1;
}

wennst jetzt die Schweife noch abhängig von deiner Geschwindigkeit machst, hast einen schönen Rundlauf mit kürzerwerdendem Schweif.

Im Prinzip kannst die Offsets für
Vorläufer - Läufer - Nachläufer - Schweif - Schweif auch mit
+1 ; 0; -1; -2, -3 machen falls dein Läufer umbedingt i sein muss.
Prinzipiell auch
0; -1; -2; -3; -4 oder wiederum
4; 3; 2; 1; 0;
… dann sparst dir das gewürge mit negativen Zahlen :wink:

so, ich hoffe ich habe dich jetzt nicht komplett verwirrt

Hi

Du kannst den Schweif auch einfach zeitlich abklingen lassen - so wird ein schnelles Drehen automatisch einen längeren Schweif ergeben, da die Leuchtzeit (nachdem der aktive Pixel weg ist).

MfG

Hi und Danke für die Hilfe.

Allerdings ergeben sich dadurch auch ein paar neue Fragen.
aber zuerst:

@postmaster:

Diese Funktion spiegelt die Drehzeit (modifiziert) wieder und sorgt dafür das der Schwanz bei langer Drehzeit lang ist und kürzer wird bis Zeit"0" = Länge “0” ist →

Tail = (Drehdauer / 100) - ((currentMillis - previousMillis)/100);
if (Tail <= 0)
{
Tail = 0;
}

“…PS: Warum Dein Braun ein Rot ist … jo … ist’s vll. rot-braun? ;)…”

Hier kann ich einsetzten was ich will…die Farbe ändert sich nicht. Ich weiss aber noch nicht warum.

Mit Modulo habe ich vor 3 Tagen das ersta mal Kontakt gehabt, aber noch nicht verstanden warum 4%5=4 ist. Ist das Für den Überlauf Sinnvoll? Kannst Du mir auf dieSprünge helfen?

@ noiasca:

“…wennst jetzt die Schweife noch abhängig von deiner Geschwindigkeit machst…”
Ich habe den Schweif abhängig von der Zeit. das sieht super aus.

“…byte getNeighbour(uint8_t actual, int8_t offset)…”
Kannst Du mir bitte erläutern was das byte “getNeighbour” mit den Variablen in der Klammer macht?!

und wo sagst Du welchen Wert “…int16_t temp = actual + offset;…” avtual und offset haben??

Ich versuche auch mal mich noch ein bisschen zu belesen…

byte getNeighbour(uint8_t actual, int8_t offset)

Die Funtktion erwartet in der Eingabe
actual den Pixel von dem weggerechnet werden soll
offset den Abstand der dazu oder weggerechnet wird

getNeighbour liefert dann ein byte retour ... nämlich genau den Pixel, der sich durch die Rechnung ergibt.

Das vertsehe ich nicht. findet die Berechnug in der Klammer statt? Wenn ja, was wird da gerechnet? (Weil ich ja sonst nirgends sehe wo was gerechnet wird).

Auch nochmal die Frage: Wo gibts Du "actual" und "offset" einen Wert?

Danke für die Mühe...ich habe vorhin mal nachgesehen...ich habe am 23.2.19 das erste mal einen Arduino in der Hand gehabt und vorher noch nie was mit programmieren zu tum gehabt. :o

@noiasca:

ich habe grad mal Deinen Code auf den Uno geflasht...es ändert sich nichts zu meinem der "Überlauf" sieht gleich aus. und ich habe bei Deinem wie bei meinem Code das Phänomen das am Ende der Runde einzelne LEDs auf der ganzen länge im Anstand von 1-4 LEDs leuchten...da stimmt irgendwas nicht mit dem Löschen.

sag mal, siehst du die Funktion nicht? Oben vor dem Setup()?

Warum dein Sketch noch nicht rund läuft: ich habe auch an deinen Ausgaben nichts geändert, das überlasse ich dir.

Beobachte die Serial-Ausgabe das funktioniert.
Versuche es zu verstehen.
Dann schmeiss deine Pixel-Ansteuerung raus und ersetze es nach meinem Modell analog Serial.print

pixels.setPixelColor(getNeighbour(i, 1), pixels.Color(0, 255, 0));

usw

Ja doch. Diese Funktion:

byte getNeighbour(uint8_t actual, int8_t offset)
// returns the next or previous led from actual with the defined offset
{
  int16_t temp = actual + offset; // wir nehmen temporär ein signed int damit wir abhängig vom offset auch minus werden können ;-) welch ein Kommentar ...

  if (temp > NUMPIXELS - 1) temp = temp - NUMPIXELS; // Überlauf nach oben abfangen
  if (temp < 0) temp = temp + NUMPIXELS;             // Unterlauf nach unten abfagen
  return byte(temp);
}

aber ich sehe nicht wo die Werte herkommen für actual und offset :confused:

Ich habe zwischenzeiltich auch festgestellt das mein Überlauf i auf null setzt und dann die ganze rechnerei für die Katz ist wenn i-3 usw. ist.

Ich würde nur gerne Deine Funktion verstehen bevor ich damit rum bastele…

…und JA, die Serial Ausgabe sieht gut aus. genau wie ich es brauche. DANKE!!! :smiley:

Ich ziehe meine Aussage zurück das mein Code wie Deiner läuft und entschuldige mich dafür :wink:

OK, dann versuche ich mich mal daran das analog der Serial ausgabe umzustricken.

Reden wir aneinander vorbei? die Werte übergebe ich bei jedem Aufruf der Funktion.
mit

pixels.setPixelColor(getNeighbour(i, 1), pixels.Color(0, 255, 0));

übergebe ich den Inhalt von i an actual, 1 ist der Offset.

Die Funktion liefert ein Byte retour, was genau der Nachbar vom aktuellen Wert +1 (weil Offset) sein soll.

Beispiel:
du bist bei i=4 und du willst den NÄCHSTEN Pixel haben.
Also übergibst du der Funktion mit

getNeighbour(i, 1);

eigentlich die 4 und 1

getNeighbour(4, 1);

4 ist dann in der Funktion actual
und 1 der Offset.

also macht

int16_t temp = actual + offset;

nichts anders als eine temporäre Variable temp = 4 + 1

die zwei nächsten IF Abfragen greifen nicht, weil wir keinen Überlauf haben
return temp weist die Funktion an, den Inhalt von temp an den Aufrufer zurückgeben.

Eigentlich ist das ein ganz normaler Funktionausfruf. Wie machst du das sonst?

Hi

Diese Funktion gibt Dir nur den Nachbar zurück.
Dafür muß die Funktion aber wissen, von welchem Pixel Du den Nachbarn haben willst.
Und, den wievielten Nachbarn Es denn treffen soll.

Daher actual = Nummer des 'Kopf'-Pixel an den der Schweif dran soll.
Und offset = der Abstand zum 'Kopf'-Pixel.
Die Funktion macht nun Nichts Anderes, als den Offset aufzuaddieren - da Dieser auch negativ sein kann, kann das Ergebnis auch nach Unten überlaufen.
Das ist die erste Zeile mit dem int16_t (wäre signed int, kann also auch negativ werden).
Die zwei IF-Abfragen prüfen, ob wir außerhalb der Pixel-Nummern sind.
Da die Pixel von Null an bis NUMPIXELS-1 nummeriert sind, sind Das unsere Grenzen - sind wir drunter, wird NUMPIXELS addiert, sind wir drüber, abgezogen.

Zu % (modulo)
4%5=4
4 geteilt durch 5 = 0, Rest 4
Modulo gibt den REST einer Division zurück.
5%5 wäre somit Null und ideal geeignet, bei rein positivem Zählsinn auf NUMPIXELS zu begrenzen - jede Zahl ab NUMPIXELS wird auf eine Zahl im Bereich 0...NUMPIXELS-1 runter gebrochen.
Gleiches machen die beiden IFs, allerdings nur 1x (was hier ausreicht).

MfG