Mehrere „Programme“ für unsere Discobrille mit den WS2812b RGB LED Ring - AZ-Delivery

Hallo und herzlich willkommen zum zweiten Teil der „Discobrille“ Blogreihe.

In diesem Teil erweitern wie schon von vielen von euch erwartet, die Funktionalitäten unserer Brille, aber wir werden auch in diesem Teil auf Kommentare aus dem ersten Teil eingehen. Ich stelle euch daher einen aktualisierten Verdrahtungsplan zur Verfügung. Des Weiteren werde ich explizit auf Details der Verdrahtung der WS1812b eingehen.
Darüber hinaus spendieren wir unserer Hardware ein kleines Firmwareupgrade, das es ermöglicht, über einen Tastendruck auf dem Taster aus bis zu 4 verschiedenen, spannenden Leucht-Animationen auszuwählen.

Bitte verdrahtet eure Hardware nach diesem aktualisierten Verdrahtungsplan:

Discobrille - Fritzing Plan

Da uns auch speziell Fragen zu der Detailverdrahtung der Ringe erreicht haben, hier mal speziell dieser Teil vergrößert dargestellt:

Discobrille - Fritzing - Detail

 

Man sieht auf dem Bild, das die Datenleitung zwischen den Ringen „durchgeschliffen“ wird. D.h. wird von dem Mikrocontroller der Datenausgang Ausgang D6 auf den ersten Ring nach DI (Data in) verbunden, und dann von diesem Ring wiederum vom Pin DO (Data Out) zum zweiten Ring nach DI (Data in) verbunden. Diese Verbindungsart ist aufgrund der Funktionsweise des seriellen Datenbusses notwendig. 


Nun können wir uns an das Firmwareupgrade wagen, und folgenden Code auf unseren Arduino hochladen:

 

 

#include <Adafruit_NeoPixel.h>

#define BUTTON_CHANGEANIMATION  12    // Digital IO pin connected to the button.  This will be
// driven with a pull-up resistor so the switch should
// pull the pin to ground momentarily.  On a high -> low
// transition the button press logic will execute.
#define PIXEL_PIN    6    // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT 24   // All Pixels on Strip
#define MaxAninmationsAvail 4

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_RGB + NEO_KHZ800);

const int hueRedLow = 0;
const int hueRedHigh = 255;
const int hueBlue = 170;
const int angleMin = 0;
const int angleSector = 60;
const int angleMax = 360;
const int brightMin = 0;
const int brightMax = 255;

byte hue, brightness;
// The saturation is fixed at 255 (full) to remove blead-through of different
// colours.
// It could be linked to another potentiometer if a demonstration of hue
// is desired.
byte saturation = 255;

// interrut Control
bool A60telSecInterruptOccured = true;
byte A60telSeconds24 = 0;
byte A4telSeconds24 = 0;

// Timer Variables
int TimerSeconds = 0;  // Zähler
int TimerAlarmSet = 15; // 15 Sekunden Timer
bool TimerStartFlagFlag = false;
bool TimerStop = true;

//Manual Operations
bool ButtonAPress  = false;

//Intenal LED LightEffects (LED 13)
byte LedMode = 2;

//AnimationControl
int ShouldAnimation  = 0;
int IsAnimation  = 0;
int OLDLightBorder = 0;
bool GetONOFFStatus = false;

bool OLDONOFFStatus = false;
bool PlayIntro = false; //Spiele Intro
bool PlayOutro = false; //Spiele Outro
bool ChangeAnimation = false;
bool RunOnce = true;  // Für Auschalt Amation - Anmation 0

//universal variables
byte a, c, d, e, f;
unsigned int r, g, b;

//Interrupt Routines

ISR(TIMER1_COMPA_vect)
{

  bool LEDChange, PressedZ;
  //Switch Abfrage
  PressedZ = digitalRead(BUTTON_CHANGEANIMATION);
  if ((PressedZ == LOW) and (ButtonAPress == false))
  {
    ButtonAPress = true;
  }
  TCNT1 = 0;      // Register mit 0 initialisieren
}

//Interrupts ende
// begin Program

void setup()
{
  strip.begin();
  strip.show();   // Initialize all pixels to 'off'
  pinMode(BUTTON_CHANGEANIMATION, INPUT_PULLUP);
  randomSeed(analogRead(0));
  noInterrupts(); // Alle Interrupts temporär abschalten
  TCCR1A = 0x00;
  TCCR1B =  0x02;
  TCNT1 = 0;      // Register mit 0 initialisieren
  OCR1A =  33353;      // Output Compare Register vorbelegen
  TIMSK1 |= (1 << OCIE1A);  // Timer Compare Interrupt aktivieren
  interrupts();   // alle Interrupts scharf schalten
  Serial.begin(9600);
  Serial.flush();
}


//Hilfsfunktionen


void HSBToRGB(
  unsigned int inHue, unsigned int inSaturation, unsigned int inBrightness,
  unsigned int *oR, unsigned int *oG, unsigned int *oB )
{
  if (inSaturation == 0)
  {
    // achromatic (grey)
    *oR = *oG = *oB = inBrightness;
  }
  else
  {
    unsigned int scaledHue = (inHue * 6);
    unsigned int sector = scaledHue >> 8; // sector 0 to 5 around the color wheel
    unsigned int offsetInSector = scaledHue - (sector << 8);  // position within the sector
    unsigned int p = (inBrightness * ( 255 - inSaturation )) >> 8;
    unsigned int q = (inBrightness * ( 255 - ((inSaturation * offsetInSector) >> 8) )) >> 8;
    unsigned int t = (inBrightness * ( 255 - ((inSaturation * ( 255 - offsetInSector )) >> 8) )) >> 8;
    switch ( sector ) {
      case 0:
        *oR = inBrightness;
        *oG = t;
        *oB = p;
        break;
      case 1:
        *oR = q;
        *oG = inBrightness;
        *oB = p;
        break;
      case 2:
        *oR = p;
        *oG = inBrightness;
        *oB = t;
        break;
      case 3:
        *oR = p;
        *oG = q;
        *oB = inBrightness;
        break;
      case 4:
        *oR = t;
        *oG = p;
        *oB = inBrightness;
        break;
      default:    // case 5:
        *oR = inBrightness;
        *oG = p;
        *oB = q;
        break;
    }
  }
}

void CheckConfigButtons ()    // InterruptRoutine
{
  bool PressedZ;

  if (ButtonAPress == true)
  {
    if (ShouldAnimation < MaxAninmationsAvail )
    {
      ShouldAnimation++;
      Serial.println ("phase1");
      Serial.println (ShouldAnimation);

      //ShouldAnimation = 1;
    } else
    {
      ShouldAnimation = 0;
    }
    delay(700);
    ButtonAPress = false;
  }
}

void AnimationControl ()
{
  int GetSelAnimation = 0;

  if (GetONOFFStatus != OLDONOFFStatus)
  {
    OLDONOFFStatus = GetONOFFStatus;
    if (GetONOFFStatus)
    {
      ShouldAnimation = 1;
    } else
    {
      ShouldAnimation = 0;
    }
  }
}

// Main Loop  -----------------------------------------------------------------------

void loop()
{
  AnimationControl();
  RunAnimations();
  CheckConfigButtons();
}
// Main Loop  ----------------------------------------------------------------------- Ende
//Intros

void Intro_CountUp (byte r, byte g, byte b, int delaytime, bool dir)
{
  if (dir)
  {
    for ( int i = 0; i < strip.numPixels(); i++)
    {
      strip.setPixelColor(i, r, g, b);    //Calulate RGB Values for Pixel
      strip.show();   // Show results :)
      delay(delaytime);
    }
  } else
  {
    for ( int i = 0; i < strip.numPixels() + 1; i++)
    {
      byte pos = strip.numPixels() - i;
      strip.setPixelColor(pos, r, g, b);    //Calulate RGB Values for   Pixel
      strip.show();   // Show results :)
      delay(delaytime);
    }
  }
}


void Intro_RaiseRainbow(bool risefall)
{
  brightness = 255;
  int Rainbowcolor = 0;
  if (risefall)
  {
    for (int i = 0; i < strip.numPixels(); i++)
    {
      hue = map(i + Rainbowcolor, angleMin, 60, hueRedLow, hueRedHigh); //Set  Color
      HSBToRGB(hue, saturation, brightness, &r, &g, &b); //Set  Color
      strip.setPixelColor(i, r, g, b);     //Calulate RGB Values for   Pixel
      strip.show();
      delay(40);
    }
  } else
  {
    for (int i = 0; i < strip.numPixels(); i++)
    {
      strip.setPixelColor(i, 0, 0, 0);
      strip.show();
      delay(40);
    }
  }
}


//Outtros



//Animations

void Ani_AllOff ()
{
  for ( int i = 0; i < strip.numPixels(); i++)
  {
    strip.setPixelColor(i, 0, 0, 0);     // all off
  }
  strip.show();
}


void Ani_AllOn (byte r, byte g, byte b)
{
  for ( int i = 0; i < strip.numPixels(); i++)
  {
    strip.setPixelColor(i, r, g, b);     // all on
  }
  strip.show();
}

void Ani_Starshower ()
{
  int array[10] ;
  for ( int i = 0; i < strip.numPixels(); i++)
  {
    strip.setPixelColor(i, 0, 0, 15);     // all blue based
  }
  for (int i = 0; i < 10; i++)
  {
    int selected = random(strip.numPixels());
    strip.setPixelColor(selected, 255, 255, 255); // White
  }
  strip.show();
  delay(100);
  for ( int i = 0; i < strip.numPixels(); i++)
  {
    strip.setPixelColor(i, 0, 0, 15);     // all blue based
  }
  strip.show();
  delay(500);
}

void Ani_Rainbow(byte delaytime)
{
  brightness = 100;
  int Rainbowcolor = 0;
  do
  {
    for (int i = 0; i < strip.numPixels(); i++)
    {
      hue = map(i + Rainbowcolor, angleMin, 60, hueRedLow, hueRedHigh);
      HSBToRGB(hue, saturation, brightness, &r, &g, &b);
      strip.setPixelColor(i, r, g, b);
    }
    strip.show();   // Show results :)
    delay(delaytime);
    Rainbowcolor++ ;
  } while (Rainbowcolor < 61);
}

void Ani_Two_Color ()
{
  //byte Segments = PIXEL_COUNT / 5;
  byte Divider = random (1, 10);
  bool color;
  int x = 1;
  b = 0;
  for (int s = 0; s > -1; s = s + x)
  {
    color = false;
    for ( int i = 0; i < strip.numPixels(); i++)
    {
      a = i / Divider;
      if (!(a == b))
      {
        b = a;
        color = !color;
      }
      if (color) {
        strip.setPixelColor(i, 0, s, 0);  //grün
      }
      if (!(color)) {
        strip.setPixelColor(i, s, 0, 0);  //rot
      }
    }
    strip.show();
    if (s == 255)
    {
      x = -1;
      delay(2000);
    }
    delay(10);
  }
  strip.show();
}

void Ani_Halloween()
{
  a = -10;
  for (int i = 0; i < strip.numPixels(); i++)
  {
    strip.setPixelColor(i, random(1, 254), random(1, 204), random(1, 254));
    e = e + a;
    f = f + a;
    if (f <= 0)
    {
      a = +10;
    }
    if (f >= 60)
    {
      a = -10;
    }
  }
  strip.show();   // Show results :)
  delay(300);
}

void FadeColor ()
{
  byte brightness = 0;
  byte saturation = 0;
  int Colori = 49 ;
  do
  {
    for (int i = 0; i < strip.numPixels(); i++)
    {
      //     wdt_reset();

      HSBToRGB(Colori, saturation, brightness, &r, &g, &b); //Set  Color
      strip.setPixelColor(i, r, g, b);     //Calulate RGB Values for   Pixel
    }
    brightness ++;
    strip.show();   // Show results :)
    delay(40);
  } while (brightness < 50);
}

void RunAnimations()
{

  if (!(ShouldAnimation == IsAnimation))
  {
    PlayOutro = true;
    ChangeAnimation = true;
  }

  switch (IsAnimation)
  {
    case 0:                                    // all LedsOFF
      if (PlayIntro)
      {
        PlayIntro = false;
        RunOnce = true;
      }
      if   ((!(PlayIntro)) &&  (!(PlayOutro)))
      {
        if (RunOnce) {
          Ani_AllOff ();
        }
        RunOnce = false;
      }
      if  (PlayOutro)
      {
        PlayOutro  = false;
        PlayIntro = true;
        RunOnce = true;
        IsAnimation = ShouldAnimation;
      }
      break;
    case 1:
      if (PlayIntro)
      {
        Intro_CountUp (0, 0, 15, 100, true);
        PlayIntro = false;
      }
      if  ((!(PlayIntro)) && (!(PlayOutro)))
      {
        Ani_Starshower();
      }
      if  (PlayOutro)
      {
        Intro_CountUp (0, 0, 0, 100, false);
        PlayOutro  = false;
        PlayIntro = true;
        IsAnimation =  ShouldAnimation;
      }
      break;
    case 2:
      if (PlayIntro)
      {
        Intro_RaiseRainbow(true);
        PlayIntro = false;
      }
      if  ((!(PlayIntro)) && (!(PlayOutro)))
      {
        Ani_Rainbow(20);
      }
      if  (PlayOutro)
      {
        Intro_RaiseRainbow(false);
        PlayOutro  = false;
        PlayIntro = true;
        IsAnimation =  ShouldAnimation;
      }
      break;
    case 3:
      if (PlayIntro)
      {
        Ani_AllOff ();
        PlayIntro = false;
      }
      if  ((!(PlayIntro)) && (!(PlayOutro)))
      {
        Ani_Two_Color (); // Ani_Two_Color (byte hue,byte tail,byte brightness,byte delaytime)
      }
      if  (PlayOutro)
      {
        PlayOutro  = false;
        PlayIntro = true;
        IsAnimation =  ShouldAnimation;
      }
      break;
    case 4:
      if (PlayIntro)
      {
        Ani_AllOff ();
        PlayIntro = false;
      }
      if  ((!(PlayIntro)) && (!(PlayOutro)))
      {
        Ani_Halloween (); //
      }
      if  (PlayOutro)
      {
        PlayOutro  = false;
        PlayIntro = true;
        IsAnimation =  ShouldAnimation;
      }
      break;
  }
}

 

Funktionsweise: Durch kurzen Druck auf den Taster können jetzt folgende Animationen nacheinander aufgerufen werden. Ein

  • Starshower
  • Rainbow
  • Motion of Two Colors
  • Halloween


Bitte beachtet, dass die Animationen nicht sofort auf die nächste Animation umschalten, sondern immer erst die aktuelle Sequenz beenden, bevor die nächste Animation gestartet werden.


Auch hier seid ihr natürlich herzlich eingeladen, weitere Animationssequenzen zu programmieren, falls euch die 4 nicht ausreichen sollten. Die prinzipielle Vorgehensweise dazu ist aus dem Code ersichtlich.
Im nächsten und letzten Teil der Reihe werden wir unsere Brille auf „Umweltreize“ reagieren lassen.


Ich wünsche viel Spaß beim Nachbauen und bis zum nächsten mal.

Für arduinoProjekte für anfänger

2 Kommentare

Andreas Wolter

Andreas Wolter

@Sebastian: zu Beginn des Codes wird mit der Zeile
#define PIXEL_COUNT 24
die Anzahl aller Pixel festgelegt (hier zwei Ringe je 12 Pixel, also 24). Diese Konstante wird dann später verwendet, wenn das Neopixelsobjekt instanziiert wird.
Wenn Sie fünf Ringe verwenden, müssten Sie dort statt 24 dann 5x 12 = 60 eintragen. Dann sollte überall dort, wo mit der for-Schleife bis strip.numPixels() gezählt wird, jeder der Neopixel angesprochen werden.

Grüße,
Andreas Wolter
AZ-Delivery Blog

Sebastian

Sebastian

Hi,
ich möchte 5 von der 38mm Ringen nutzen. Wo genau muss ich denn die Anzahl der Ringe einstellen?

Kommentar hinterlassen

Alle Kommentare werden von einem Moderator vor der Veröffentlichung überprüft

Empfohlene Blogbeiträge

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery