Hola y bienvenidos a la última parte de nuestra gama Disco Glasses.
Por parte de hoy volveremos a resumir toda la información relevante para la construcción de las gafas, mostraremos algunas imágenes de las secuencias de animación y, por supuesto, donaremos una actualización funcional a nuestras gafas. La actualización funcional en la parte de hoy consiste en un sensor de vibración, que se activa por sus movimientos de baile en la discoteca y por lo tanto da a nuestro micro controlador información sobre su "comportamiento de baile" en la pista de baile. A partir de esta información, se controlan las secuencias luminosas de las gafas.
En otras palabras, sus gafas responden a sus movimientos de baile rítmico! Bueno, ¿cómo funciona todo técnicamente?
Como elemento central, primero necesitamos un sensor de vibración. Esto se puede encontrar en nuestra tienda en varias versiones. Cada uno de los sensores mencionados reacciona ligeramente diferente a los movimientos. Por lo tanto, el resultado final, por supuesto, también dependerá del sensor utilizado. Recomiendo el Módulo de sensor KY-002n como sensor de vibración. También he hecho esto en mi propia configuración de pruebaTy al desarrollar el código. Es importante que el sensor cambie activamente a tierra. Esto significa que en caso de un "shock", el sensor utilizado cierra brevemente el contacto después de GND.
Propina: Pruebe diferentes sensores si es necesario para obtener su mejor resultado. Una selección de sensores alternativos se puede encontrar al final de este blog!
El segundo nuevo componente de hardware es un conmutador más simple. Este interruptor se utiliza para cambiar entre el antiguo modo autosuficiente conocido de la Parte 2 al nuevo modo que reacciona a los movimientos. El cambio entre modos puede tener lugar en cualquier momento. También, por ejemplo, durante un programa en curso!
Pero pasemos al cableado de los dos nuevos componentes de nuestras gafas.
Como se puede ver en el esquema, el sensor de vibración se encuentra entre GND y el puerto 10 y el conmutador entre GND y el puerto 11 del procesador. El cableado del botón, por otro lado, no ha cambiado:
También el cableado de los dos anillos WS2812 no ha cambiado en comparación con la parte anterior de la serie:
Cargue el siguiente código actualizado en sus gafas después de la actualización de hardware:
#include <Adafruit_NeoPixel.H> #define BUTTON_CHANGEANIMATION 12 Pin de E/S digital conectado al botón. Esto será accionado con una resistencia pull-up por lo que el interruptor debe Tire del pasador al suelo momentáneamente. En un alto -> bajo transición de la lógica de pulsación de botón se ejecutará. #define PIXEL_PIN 6 Pin de E/S digital conectado a los NeoPixels. #define SHOCK_SENSOR 10 Sensor de choque / vibración conectado #define MODE_SWITCH 11 Modo de operación #define PIXEL_COUNT 24 Todos los píxeles en la tira #define MaxAninmationsAvail 4 Adafruit_NeoPixel Tira = 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, "No soy; La saturación se fija en 255 (completa) para eliminar el "No soy Byte Saturación = 255; Interrut Control Bool A60telSecInterruptOccured = Verdad; Bool FunctionMode = Falso; Byte A60telSeconds24 = 0; Byte A4telSeconds24 = 0; Variables del temporizador Int TimerSeconds = 0; Contador Int TimerAlarmSet = 15; 15 Segundo Temporizador Bool TimerStartFlagFlag = Falso; Bool TimerStop = Verdad; Operaciones manuales Bool ButtonAPress = Falso; AnimationControl Int ShouldAnimation = 0; Int IsAnimation = 0; Int OLDLightBorder = 0; Bool GetONOFFStatus = Falso; Bool OLDONOFFStatus = Falso; Bool PlayIntro = Falso; Play Intro Bool PlayOutro = Falso; Juega Outro Bool ChangeAnimation = Falso; Bool Runonce = Verdad; Animación de apagado - Anmación 0 variables universales Byte Un, C, D, e, F; Unsigned Int R, G, B; Interrumpir rutinas Isr(TIMER1_COMPA_vect) { Bool LEDChange, PressedZ; PressedZ = digitalRead(BUTTON_CHANGEANIMATION); Leer botón de pulsación FunctionMode = digitalRead(MODE_SWITCH); Si ((PressedZ == Bajo) Y (ButtonAPress == Falso)) { ButtonAPress = Verdad; } TCNT1 = 0; Registrar la inicialización } Interrupciones finalizar programa principal Vacío Configuración() { Tira.Comenzar(); Tira.Mostrar(); Inicializar todos los píxeles en 'desactivado' pinMode(BUTTON_CHANGEANIMATION, INPUT_PULLUP); pinMode(SHOCK_SENSOR, INPUT_PULLUP); pinMode(MODE_SWITCH, INPUT_PULLUP); randomSeed(analogRead(0)); noInterrupts(); Desactivar todos los Interrrupts TCCR1A = 0x00; TCCR1B = 0x02; TCNT1 = 0; Registrar la inicialización OCR1A = 33353; Registro de comparación de salida de carga TIMSK1 |= (1 << OCIE1A); Activar interrupción de comparación de temporizador Interrumpe(); Habilitar todas las interrupciones } Funciones auxiliares Vacío HSBToRGB ( Unsigned Int inHue, Unsigned Int insaturación, Unsigned Int inBrightness, Unsigned Int *O, Unsigned Int *Og, Unsigned Int *Ob ) { Si (insaturación == 0) { acromático (gris) *O = *Og = *Ob = inBrightness; } Más { Unsigned Int scaledHue = (inHue * 6); Unsigned Int Sector = scaledHue >> 8; sector 0 a 5 alrededor de la rueda de color Unsigned Int offsetInSector = scaledHue - (Sector << 8); posición dentro del sector Unsigned Int P = (inBrightness * ( 255 - insaturación )) >> 8; Unsigned Int Q = (inBrightness * ( 255 - ((insaturación * offsetInSector) >> 8) )) >> 8; Unsigned Int T = (inBrightness * ( 255 - ((insaturación * ( 255 - offsetInSector )) >> 8) )) >> 8; Interruptor ( Sector ) { Caso 0: *O = inBrightness; *Og = T; *Ob = P; Romper; Caso 1: *O = Q; *Og = inBrightness; *Ob = P; Romper; Caso 2: *O = P; *Og = inBrightness; *Ob = T; Romper; Caso 3: *O = P; *Og = Q; *Ob = inBrightness; Romper; Caso 4: *O = T; *Og = P; *Ob = inBrightness; Romper; Predeterminado: caso 5: *O = inBrightness; *Og = P; *Ob = Q; Romper; } } } Vacío CheckConfigButtons () InterruptRoutine { Bool PressedZ; Si (ButtonAPress == Verdad) { Si (ShouldAnimation < MaxAninmationsAvail ) { ShouldAnimation++; } Más { ShouldAnimation = 0; } Retraso(400); ButtonAPress = Falso; } } Vacío AnimationControl () { Int GetSelAnimation = 0; Si (GetONOFFStatus != OLDONOFFStatus) { OLDONOFFStatus = GetONOFFStatus; Si (GetONOFFStatus) { ShouldAnimation = 1; } Más { ShouldAnimation = 0; } } } ----------------------------------------------------------------------- de bucle principal Vacío Bucle() { AnimationControl(); RunAnimations(); CheckConfigButtons(); } Bucle principal ----------------------------------------------------------------------- Final Intros Vacío Intro_CountUp (Byte R, Byte G, Byte B, Int retraso, Bool Dir) { Si (Dir) { Para ( Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Tira.setPixelColor(Ⅰ, R, G, B); Calular valores RGB para píxeles Tira.Mostrar(); Mostrar resultados :) Retraso(retraso); } } Más { Para ( Int Ⅰ = 0; Ⅰ < Tira.numPixels() + 1; Ⅰ++) { Byte Pos = Tira.numPixels() - Ⅰ; Tira.setPixelColor(Pos, R, G, B); Calular valores RGB para píxeles Tira.Mostrar(); Mostrar resultados :) Retraso(retraso); } } } Vacío Intro_RaiseRainbow(Bool aumento) { Brillo = 255; Int Rainbowcolor = 0; Si (aumento) { Para (Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Hue = Mapa(Ⅰ + Rainbowcolor, angleMin, 60, hueRedLow, hueRedHigh); Establecer color HSBToRGB(Hue, Saturación, Brillo, &R, &G, &B); Establecer color Tira.setPixelColor(Ⅰ, R, G, B); Calular valores RGB para píxeles Tira.Mostrar(); Retraso(40); } } Más { Para (Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Tira.setPixelColor(Ⅰ, 0, 0, 0); Tira.Mostrar(); Retraso(40); } } } Animaciones Outtros Vacío Ani_AllOff () { Para ( Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Tira.setPixelColor(Ⅰ, 0, 0, 0); todo fuera } Tira.Mostrar(); } Vacío Ani_AllOn (Byte R, Byte G, Byte B) { Para ( Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Tira.setPixelColor(Ⅰ, R, G, B); todo en } Tira.Mostrar(); } Vacío Ani_Starshower () { Int Matriz[10] ; Bool shockValue = Verdad; Bool PressedT = Verdad; Para ( Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Tira.setPixelColor(Ⅰ, 0, 0, 15); todo basado en azul } Para (Int Ⅰ = 0; Ⅰ < 10; Ⅰ++) { Int Seleccionado = Aleatorio(Tira.numPixels()); Tira.setPixelColor(Seleccionado, 255, 255, 255); Blanco } Tira.Mostrar(); Retraso(100); Para ( Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Tira.setPixelColor(Ⅰ, 0, 0, 15); todo basado en azul } Tira.Mostrar(); Si (FunctionMode) { Retraso(500); } Más { hacer { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } Mientras ((shockValue) && (!FunctionMode) && ( PressedT)) ; } } Vacío Ani_Rainbow(Byte retraso) { Brillo = 100; Int Rainbowcolor = 0; Bool shockValue = Verdad; Bool PressedT = Verdad; hacer { Para (Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Hue = Mapa(Ⅰ + Rainbowcolor, angleMin, 60, hueRedLow, hueRedHigh); HSBToRGB(Hue, Saturación, Brillo, &R, &G, &B); Tira.setPixelColor(Ⅰ, R, G, B); } Tira.Mostrar(); Mostrar resultados :) Si (FunctionMode) { Retraso(retraso); } Más { hacer { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } Mientras ((shockValue) && (!FunctionMode) && ( PressedT)) ; } Rainbowcolor++ ; } Mientras (Rainbowcolor < 61); } Vacío Ani_Two_Color () { Bool shockValue = Verdad; Bool PressedT = Verdad; Byte Divisor = Aleatorio (1, 10); Bool Color; Int X = 1; B = 0; Para (Int s = 0; s > -1; s = s + X) { Color = Falso; Para ( Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Un = Ⅰ / Divisor; Si (!(Un == B)) { B = Un; Color = !Color; } Si (Color) { Tira.setPixelColor(Ⅰ, 0, s, 0); Grün } Si (!(Color)) { Tira.setPixelColor(Ⅰ, s, 0, 0); Putrefacción } } Tira.Mostrar(); Si (s == 255) { Si (FunctionMode) { X = -1; Retraso(200); } Más { hacer { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } Mientras ((shockValue) && (!FunctionMode) && ( PressedT)) ; X = -1; } } Retraso(10); } Tira.Mostrar(); } Vacío Ani_Halloween() { Bool shockValue = Verdad; Bool PressedT = Verdad; Un = -10; Para (Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { Tira.setPixelColor(Ⅰ, Aleatorio(1, 254), Aleatorio(1, 204), Aleatorio(1, 254)); e = e + Un; F = F + Un; Si (F <= 0) { Un = +10; } Si (F >= 60) { Un = -10; } } Tira.Mostrar(); Mostrar resultados :) Si (FunctionMode) { Retraso(300); } Más { hacer { shockValue = digitalRead(SHOCK_SENSOR); PressedT = digitalRead(BUTTON_CHANGEANIMATION); FunctionMode = digitalRead(MODE_SWITCH); } Mientras ((shockValue) && (!FunctionMode) && ( PressedT)) ; } } Vacío FadeColor () { Byte Brillo = 0; Byte Saturación = 0; Int Colori = 49 ; hacer { Para (Int Ⅰ = 0; Ⅰ < Tira.numPixels(); Ⅰ++) { HSBToRGB(Colori, Saturación, Brillo, &R, &G, &B); Establecer color Tira.setPixelColor(Ⅰ, R, G, B); Calular valores RGB para píxeles } Brillo ++; Tira.Mostrar(); Mostrar resultados :) Retraso(40); } Mientras (Brillo < 50); } Vacío RunAnimations() { Si (!(ShouldAnimation == IsAnimation)) { PlayOutro = Verdad; ChangeAnimation = Verdad; } Interruptor (IsAnimation) { Caso 0: todos Los LedsOFF Si (PlayIntro) { PlayIntro = Falso; Runonce = Verdad; } Si ((!(PlayIntro)) && (!(PlayOutro))) { Si (Runonce) { Ani_AllOff (); } Runonce = Falso; } Si (PlayOutro) { PlayOutro = Falso; PlayIntro = Verdad; Runonce = Verdad; IsAnimation = ShouldAnimation; } Romper; Caso 1: Si (PlayIntro) { Intro_CountUp (0, 0, 15, 100, Verdad); PlayIntro = Falso; } Si ((!(PlayIntro)) && (!(PlayOutro))) { Ani_Starshower(); } Si (PlayOutro) { Intro_CountUp (0, 0, 0, 100, Falso); PlayOutro = Falso; PlayIntro = Verdad; IsAnimation = ShouldAnimation; } Romper; Caso 2: Si (PlayIntro) { Intro_RaiseRainbow(Verdad); PlayIntro = Falso; } Si ((!(PlayIntro)) && (!(PlayOutro))) { Ani_Rainbow(20); } Si (PlayOutro) { Intro_RaiseRainbow(Falso); PlayOutro = Falso; PlayIntro = Verdad; IsAnimation = ShouldAnimation; } Romper; Caso 3: Si (PlayIntro) { Ani_AllOff (); PlayIntro = Falso; } Si ((!(PlayIntro)) && (!(PlayOutro))) { Ani_Two_Color (); Ani_Two_Color (byte hue, byte tail,byte brightness,byte delaytime) } Si (PlayOutro) { PlayOutro = Falso; PlayIntro = Verdad; IsAnimation = ShouldAnimation; } Romper; Caso 4: Si (PlayIntro) { Ani_AllOff (); PlayIntro = Falso; } Si ((!(PlayIntro)) && (!(PlayOutro))) { Ani_Halloween (); // } Si (PlayOutro) { PlayOutro = Falso; PlayIntro = Verdad; IsAnimation = ShouldAnimation; } Romper; } }
Al pulsar el botón, las animaciones ahora se pueden llamar una tras otra. Lo nuevo es que ahora se puede cambiar entre los modos de función independiente y de retroalimentación simplemente presionando el interruptor.
¡Todos reaccionan! de las cuatro animaciones ligeramente diferentes en el sensor de vibración si se selecciona el modo "Feedback".
Como una pequeña ayuda de rendimiento una vez fotografié las 4 animaciones diferentes:
Ducha Estelar:
Arco iris:
Movimiento de dos colores:
Víspera de Todos los Santos:
Tenga en cuenta que en ambos! No cambia las animaciones a la siguiente animación inmediatamente, pero siempre sale de la secuencia actual antes de que se inicie la siguiente animación.
Te deseo mucha diversión recreando las gafas de discoteca. Tal vez usted quiere programar más secuencias de animación si los 4 existentes no son suficientes para usted?
Pruebe con otros sensores, como:
O puede intentar usar un sensor de los diversos
Escribe tus ideas o preguntas en los comentarios.