Discobrille mit den WS2812b RGB LED Ringen

 Eine Discobrille mit den WS2812b RGB LED Ring


Hallo und herzlich willkommen zu einer weiteren Blogreihe zu unserem WS2812b RGB LED Ring. Auch diesmal geht es wieder um das aktuelle Thema „Upcycling“. Niemand soll schließlich sagen können, das Maker nicht auch an die Umwelt denken :).
Diesmal „upcyclen“ wir eine alte Sonnenbrille zu einer stylischen „Discobrille“ für den nächsten Discobesuch. Obwohl ich zwar selbst nicht mehr zu den Discogängern gehöre, finde ich, dass auch mal ein Projekt für unsere jüngeren Maker unter den Lesern dabei sein sollte. Ich hoffe euch gefällt die Idee der Disco LED Brille für den nächsten Discobesuch! Damit fallt ihr bestimmt in der (Tanz) Masse so richtig auf. Die WS2812b LED Ringe fungieren hier als „Brillengläser“. Das Gestell rund um die beiden Augenringe kann man sich aus einer alten Sonnenbrille zusammenbauen. Als MC nutzen wir wieder aus Platzgründen den Arduino Nano, als Stromversorgung eine USB Powerbank vom Handy. Für die Verkabelung empfehle ich, die Elektronik und Stromversorgung von der Brille auszulagern, daher reicht es von dem linken oder rechten Ohr Bügel ein dünnes, 3 adriges Kabel über das Oberteil in eine Elektronikbox in der Hemdtasche oder Hosentasche verschwinden zu lassen. In dieser Elektronikbox baut ihr den Taster und weitere Sensoren (in den kommenden Teilen) zur Steuerung der Brille ein. Für unseren heutigen Blog brauchen wir folgendes Hardwaresetup:

Als Teile benötigt ihr nicht viel:

1 xAZ-Delivery Nano V 3

2 x RGB LED Ring WS2812b mit 12 RGB LEDs 5V für Arduino 50 mm Außendurchmesser

1x Taster

1x Usb Powerbank und USB Kabel passend für Arduino Nano

 

Wenn wir die Schaltung fertig aufgebaut haben, müssen wir nun als nächstes die Adafruit Neopixel- Bibliothek einbinden, damit wir die LED ’s des Ringes einfach ansteuern können.
Die benötigte Bibliothek kann über 2 Wege unserer IDE hinzugefügt werden:
1.) Die Bibliothek auf der URL https://github.com/adafruit/Adafruit_NeoPixel herunterladen. Dann in der IDE im Menüpunkt Sketch -> Bibliothek einbinden ->Zip Bibliothek manuell hinzufügen.
2.) Im Bibliotheksverwalter als Suchbegriff „Adafruit Neopixel“ eingeben, auf die Bibliothek Klicken und dann „Installieren“ aussuchen. Bitte mindestens Version 1.2.4 auswählen.

Wenn bis hierhin alles geklappt hat, bitte folgenden Code auf den Nano hochgeladen:

 

#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 Umbrella
#define MaxAninmationsAvail 1
#define STATUS_PIN 13 // Animation Control Button
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;
// The size of the angle of one sector (1/6 of a colour wheel), and of a complete
// cycle of the colour wheel.
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;

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

//Manual Operations
bool ButtonAPress  = false;
byte LedMode = 2;

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

bool OLDONOFFStatus = false;
bool PlayIntro = false; // Intro
bool PlayOutro = false; // Outro
bool ChangeAnimation = false;
bool RunOnce = true;  

//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 initializing
}

//Interrupts end

// begin Program
void setup() 
  {
    
    strip.begin();
    strip.show();   // Initialize all pixels to 'off'    
    pinMode(BUTTON_CHANGEANIMATION, INPUT_PULLUP);
    pinMode(STATUS_PIN,OUTPUT);
    digitalWrite(STATUS_PIN, HIGH);
    randomSeed(analogRead(0));
    noInterrupts(); // All Interrupts disable
    TCCR1A = 0x00;  
    TCCR1B =  0x02;
    TCNT1 = 0;      // Register initalization
    OCR1A =  33353;      // Load Output Compare Register  
    TIMSK1 |= (1 << OCIE1A);  // Activate Timer Compare Interrupt 
    interrupts();   // Activate all Interrupts
    Serial.begin(9600);
    Serial.flush(); 
  }

// Help Functions
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)
   {
    ShouldAnimation = !ShouldAnimation;
    delay(250);
    ButtonAPress = false; 
  }
} 

void AnimationControl ()
{
  int GetSelAnimation = 0;
   
  if (GetONOFFStatus != OLDONOFFStatus)
    {
    OLDONOFFStatus = GetONOFFStatus;
    if (GetONOFFStatus) 
      {
      randomSeed(analogRead(3));     
      GetSelAnimation = 1;
      ShouldAnimation = GetSelAnimation;
      Serial.print ("System ON. Selected: ");
      Serial.println (ShouldAnimation);
      } else
      {
      Serial.print ("System OFF. Seconds:");
      TimerStartFlagFlag = false;
      Serial.println (TimerSeconds);
      ShouldAnimation = 0;
      }
  }   
}

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

void loop() 
  {
    AnimationControl();
    RunAnimations();
    CheckConfigButtons();
  }

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


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

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 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_RaiseRainbow(true);   
        PlayIntro = false;
        }
        if  ((!(PlayIntro)) && (!(PlayOutro)))
        {
          Ani_Rainbow(20);
        }
       if  (PlayOutro) 
        {
        Intro_RaiseRainbow(false);             
        PlayOutro  = false;
        PlayIntro = true;
        IsAnimation =  ShouldAnimation;
        }      
       break;
   }   
}

 

Jetzt einfach kurz den Taster betätigen und das Schauspiel genießen :) . Ein weiterer Tastendruck schaltet die Brille wieder aus.

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

Letzter Artikel Mehrere „Programme“ für unsere Discobrille mit den WS2812b RGB LED Ring
Neuer Artikel Mehrere „Feuer-Programme“ für unsere Stimmungslaterne

Kommentar

Tobias - September 10, 2019

In der Tat hat sich ein kleiner Fehler in das Fritzing Bild eingeschlichen. Natürlich müssen die Ringmassen auch mit GND verbunden werden. Für die Verbindung der Ringe ist folgendes zu beachten :
D6 > DI (Ring1) DO (Ring1) → DI (Ring2)

Sascha - September 6, 2019

Moin
Von der Idee her, finde ich es gut, aber mir stellt sich die Frage ob es funktioniert wenn die Ringe keinen Null bekommen.

Geht dir nichts an - September 6, 2019

Die Fritzing funktioniert so nicht, Masse (gnd) ist zwar zwischen den einzelnen Ringen verbunden, aber nicht zum Breadboard … dafür aber 2 mal die 5V Verbindung.
P.S.: Wenn ihr nächstes mal das Bild mit halb so vielen Pixeln veröffentlicht, dann sieht man auch solche Fehler nicht mehr. (Oder in anderen Worten: Bitte einen link zu einem Fritzing Bild mit höherer Auflösung damit die Leute auch erkennen können was wohin geht, denn hier sind die Beschriftungen nicht mehr zu erkennnen)

Bernd - September 6, 2019

Der Schaltplan sieht ja ganz nett aus, aber die zwei RGB LED Ringe brauchen nicht nur die GND Verbindung untereinander sondern auch noch die GND Verbindung zum Breadboard, oder nur zwei schwarze Verbindungen von den Ringen zum Breadboard auf GND. Etwa so wie bei zwei den roten +5V Verbindungen.

Hinterlasse einen Kommentar

Kommentare müssen vor der Veröffentlichung überprüft werden

Erforderliche Angabe