Hello and welcome to the last part of our Disco Glasses series.
In today's part we will summarize all relevant information for the construction of the glasses, show some pictures of the animation sequences and of course give our glasses a functional upgrade again. The function upgrade consists of a vibration sensor, activated by your dancing movements in the disco, to give our Micro Controller information about your "dance behaviour" on the dance floor. The light sequences of the glasses are controlled from this information.
In other words, your glasses respond to your rhythmic dance moves! Well, how does it all work technically?
As a core element, we first need a vibration sensor. This is available in the shop in several versions. Each of these sensors reacts slightly differently to movements. So the end result will of course also depend on the sensor used. I recommend that KY-002n Sensor Module to be used as a vibration sensor. I also have this in my own experimental constructiontand used in developing the code. It is important that the sensor is activated by mass. This means that in the event of a "shock", the sensor used closes the contact after GND for a short time.
Type: If necessary, try different sensors of your own to get your best result. A selection of alternative sensors can be found at the end of this blog!
The second new hardware component requires an even simpler switch. This switch is used to switch between the old self-contained mode known from Part 2 on the new mode reacting to the movements. Switching between modes can be done at any time. For example, during an ongoing programme!
Now we come to the wiring of the two new components in our glasses.
As can be seen on the switchboard, the vibration sensor sits between GND and Port 10 and the switch between GND and Port 11 of the processor. The wiring of the button has not changed:
Also the wiring of the two WS2812 rings has not changed compared to the previous part of the series:
Please upload the following functionally updated code to your glasses after the hardware update:
'35; includes <NeoPixel Adafruit.h> \35; 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. \35; define PIXEL PIN 6 Digital IO pin connected to the NeoPixels. \35; define SHOCK SENSOR 10 Shock/ Vibration Sensor attached \35; define SWITCH MODE 11 // Operation Mode \35; define PIXEL COUNT 24 All Pixels on Strip \35; define MaxAninmationsAvail 4 NeoPixel Adafruit strip = NeoPixel Adafruit(PIXEL COUNT, PIXEL PIN, NEO RGB + NEW KHZ800); const int Hue = 0; const int HueRedHigh = 255; const int hue = 170; const int angleMin = 0; const int angleSector = 60; const int AngleMax = 360; const int brightMin = 0; const int brightMax = 255; exchange Whoo!, brightness; // The saturation is fixed at 255 (full) to remove blead-through of different /colours. exchange saturation = 255; // interrut Control bool A60telSecInterruptOccured = true; bool FunctionMode = false; exchange A60telSeconds24 = 0; exchange A4telSeconds24 = 0; // Timer Variables int TimeSeconds = 0; // Counter int TimeAlarmSet = 15; // 15 Second Timer bool TimerStartFlagFlag = false; bool TimerStop = true; //Manual Operations bool ButtonAPress = false; //AnimationControl int ShouldAnimation = 0; int IsAnimation = 0; int OLDLightBorder = 0; bool GetONOFFStatus = false; bool OLDONOFFStatus = false; bool PlayIntro = false; //Play Intro bool PlayOutro = false; //Play Outro bool ChangeAnimation = false; bool RunOnce = true; // Power Off Animation - Anmation 0 //universal variables byte a, c, d, e, f; unsigned int r, g, b; //Interrupt Routines ISR(TIMER1_COMPA_vect) { bool LEDChange, PressedZ; PressedZ = digitalRead(BUTTON_CHANGEANIMATION); //Read Push Button FunctionMode = digitalRead(MODE_SWITCH); if ((PressedZ == LOW) and (ButtonAPress == false)) { ButtonAPress = true; } TCNT1 = 0; // Register initialisation } //Interrupts end Begin Main Program void setup() { strip.begin(); strip.show(); // Initialize all pixels to 'off' pinMode(BUTTON_CHANGEANIMATION, INPUT_PULLUP); pinMode(SHOCK_SENSOR, INPUT_PULLUP); pinMode(MODE_SWITCH, INPUT_PULLUP); randomSeed(analogRead(0)); noInterrupts(); // Disable all Interrrupts TCCR1A = 0x00; TCCR1B = 0x02; TCNT1 = 0; // Register initialisation OCR1A = 33353; // Load Output Compare Register TIMSK1 |= (1 << OCIE1A); // Activate Timer Compare Interrupt interrupts(); // Enable all Interrupts } //Helper 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) { if (ShouldAnimation < MaxAninmationsAvail ) { ShouldAnimation++; } else { ShouldAnimation = 0; } delay(400); 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 ----------------------------------------------------------------------- End //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); } } } //Animations Outtros 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] ; bool shockValue = true; bool PressedT = true; 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(); if (FunctionMode) { delay(500); } else { do { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } while ((shockValue) && (!FunctionMode) && ( PressedT)) ; } } void Ani_Rainbow(byte delaytime) { brightness = 100; int Rainbowcolor = 0; bool shockValue = true; bool PressedT = true; 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 :) if (FunctionMode) { delay(delaytime); } else { do { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } while ((shockValue) && (!FunctionMode) && ( PressedT)) ; } Rainbowcolor++ ; } while (Rainbowcolor < 61); } void Ani_Two_Color () { bool shockValue = true; bool PressedT = true; 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) { if (FunctionMode) { x = -1; delay(200); } else { do { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } while ((shockValue) && (!FunctionMode) && ( PressedT)) ; x = -1; } } delay(10); } strip.show(); } void Ani_Halloween() { bool shockValue = true; bool PressedT = true; 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 :) if (FunctionMode) { delay(300); } else { do { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } while ((shockValue) && (!FunctionMode) && ( PressedT)) ; } } void FadeColor () { byte brightness = 0; byte saturation = 0; int Colori = 49 ; do { for (int i = 0; i < strip.numPixels(); i++) { 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; } }
Durch kurzen Druck auf den Taster können die Animationen nun auch wieder nacheinander aufgerufen werden. Neu ist, das jetzt durch einfaches betätigen des Schalters zwischen den Funktionsmodi „Standalone“ und „Feedback“ umgeschaltet werden kann.
Es reagiert dabei jede! der insgesamt vier Animationen leicht unterschiedlich auf den Vibrationssensor, falls der Modus „Feedback“ gewählt wurde.
Als kleine Vorstellungshilfe habe ich einmal alle 4 unterschiedlichen Animationen fotografiert:
Starshower:
Rainbow:
Motion of two colors:
Halloween:
Bitte beachtet, das in beiden! Modi die Animationen nicht sofort auf die nächste Animation umschalten, sondern immer erst die aktuelle Sequenz beenden, bevor die nächste Animation gestartet wird.
Ich wünsche nun viel Spaß beim Nachbauen der Discobrille. Vielleicht möchtest du ja weitere Animationssequenzen dazu programmieren, falls dir die 4 vorhandenen nicht ausreichen sollten?
Probiere auch mal andere Sensoren aus, wie zb:
Oder Ihr versucht es mit einem Sensor aus den verschiedenen
Schreibt mir eure Ideen oder Fragen in die Kommentare.