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:
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.
4 comments
Tobias
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
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
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
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.