Our disco glasses give feedback!

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:

Part 3: Fritzing switchboard

 

Also the wiring of the two WS2812 rings has not changed compared to the previous part of the series:

 

Part 3: Fritzing detail

 

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:

Effect: Starshower

Rainbow:

Part 3 -Rainbow Effect

Motion of two colors:

Part 3: Motion of two colors

 

Halloween:

Part 3- Halloween effect

 

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.

 

For arduinoProjects for beginners

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN