Controlador multitouch música (22 / 26 paso)

Paso 22: Firmware

Cargar el firmware siguiente a la Junta Directiva:

www.Monome.org www.Monome.org www.Monome.org www.Monome.org * * / / / asignaciones de PIN y puerto / / si está utilizando hardware que no sea el escudo unsped, usted querrá pasar por esta parte del código, / / y cambiar los números pin y atmega 168 puertos que usas. Aquí, estamos usando las asignaciones de pines del puerto / entradas / salidas no el esquema número de pin de la placa arduino (e.g. pin digital de arduino 8 = PORTB perno 1) / / sin embargo, en los comentarios, dan las equivalencias entre números de pin digital de arduino y la / / numeración de puerto y la broca del atmega168. IMPORTANTE - usted necesitará fijar los datos de dos variables de registro de dirección para que coincida con su / / asignaciones de pin
. byte PORTD_Data_Direction = 0 x 02; //all entradas excepto pin 1 byte (TX) PORTB_Data_Direction = 0xFF; //all salidas byte PORTC_Data_Direction = 0xFC; //A2-A5 son salidas, A0, A1 entradas //Connections a cambio 595 registro //dataPin = A4 = PORTC, bit 4 //clockPin = A2 = PORTC, //latchPin bit 2 = A3 = PORTC, bit 3 #define DATA_PORT_595 (PORTC) byte dataPin595 = 4; byte dataPin595MaskHigh = 1 << dataPin595; byte dataPin595MaskLow = ~ dataPin595MaskHigh; #define CLOCK_PORT_595 (PORTC) byte clockPin595 = 2; byte clockPin595MaskHigh = 1 << clockPin595; byte clockPin595MaskLow = ~ clockPin595MaskHigh; #define LATCH_PORT_595 (PORTC) byte latchPin595 = 3; byte latchPin595MaskHigh = 1 << latchPin595; byte latchPin595MaskLow = ~ latchPin595MaskHigh; Conexiones con el Max7219 (unidades de LED) / / dataIn = pin arduino A5 = PORTC, bit 5 / / carga = arduino perno 11 = PORTB bit 4 / reloj = pin arduino 12 = PORTB, 5 #define LED_DATA_PORT (PORTC) Cuarteto MaxDataPin = 5; byte DataMaskHigh = 1 << MaxDataPin; byte DataMaskLow = ~ DataMaskHigh; bytes de LED_CLOCK_PORT (PORTB) #define MaxClockPin = 5; byte ClockMaskHigh = 1 << MaxClockPin; byte ClockMaskLow = ~ ClockMaskHigh; bytes de LED_LOAD_PORT (PORTB) #define MaxLoadPin = 4; byte LoadMaskHigh = 1 << MaxLoadPin; byte LoadMaskLow = ~ LoadMaskHigh; conexiones para el cambio 165 registro //use para fijar espejado mirrorXLEDs booleano = true; Boolean mirrorYLEDs = false; Boolean mirrorXButtons = false; Boolean mirrorYButtons = false; Pasador de extremo y asignaciones de Puerto / /---/ / Global variables byte byte0, byte1; almacenamiento de entrada byte de datos en serie WaitingForAddress = 1; 1 cuando esperamos el siguiente byte serial a un valor de dirección, dirección de 0 bytes lo contrario = 0 x 00; bytes de basura para mantener la dirección de la función estatal de byte = 0 x 00; bytes de basura para sostener el byte de estado valor x = 0 x 00; bytes de basura para mantener x posición byte y = 0 x 00; bytes de basura para mantener y posición byte z = 0 x 00; bytes de basura para iterar sobre botones / / las siguientes variables se utilizan para almacenar banderas que si hemos recibido un mensaje dado, / / el valor de los datos de ese mensaje. por ejemplo, IntensityChange = 0 no == ningún mensaje de cambio de la intensidad recibida, / / IntensityChange = 1 == un cambio de intensidad mensaje ha sido recibido, y su valor será de IntensityVal / / el código que finalmente actúa sobre el mensaje entrante restablecerá la variable 'Cambiar' a 0 una vez el / / mensaje se ha manejado. byte IntensityChange = 0; byte IntensityVal = 0; byte DisplayTestChange = 0; byte DisplayTestVal = 0; byte ShutdownModeChange = 0; byte ShutdownModeVal = 0; Estas variables se utilizan para manejar los mensajes de ADC y tienda que puertos están habilitados actualmente, / / y cuales no. byte ADCnum; byte ADCstate; byte ADCEnableState [4]; ledmem Byte [8]; memoria para los Estados LED - 64 bits. byte j = 0; variable temporal para botón bucle int i = 0; variable temporal para bucle id de etc. int = 0; almacenamiento temporal para entrante mensaje ID byte firstRun = 1; 1 cuando tenemos todavía recibir un comando de LED, 0 después de eso. para asegurar que nuestro estado led de la memoria se inicializa correctamente cuando recibimos el primer LED mensaje int kButtonUpDefaultDebounceCount = 12; Utilizado en botón contra rebotes int kButtonNewEvent = 1; Utilizado para almacenar si el estado de un botón ha cambiado o no. t bytes = 0; variable temporal utilizada en el procesamiento del botón / * botón matriz de VARIABLES de Estados de botón 1/0, uso 8 bytes (64 bits) para contador de botón debounce, matriz de 8 x 8 * / byte button_current [8]; button_last Byte [8]; button_state Byte [8]; button_debounce_count Byte [8] [8]; button_event Byte [8]; / * FIN de botón matriz de VARIABLES * / / * MAX 7219 instrucciones direcciones * / byte max7219_reg_noop = 0 x 00; definir max7219 registros (leído el documento técnico para explicación) byte max7219_reg_digit0 = 0 x 01; byte max7219_reg_digit1 = 0 x 02; byte max7219_reg_digit2 = 0 x 03; byte max7219_reg_digit3 = 0 x 04; byte max7219_reg_digit4 = 0 x 05; byte max7219_reg_digit5 = 0 x 06; byte max7219_reg_digit6 = 0x07; byte max7219_reg_digit7 = 0x08; byte max7219_reg_decodeMode = 0 x 09; byte max7219_reg_intensity = 0x0a; byte max7219_reg_scanLimit = 0x0b; max7219_reg_shutdown octeto 0x0c; byte max7219_reg_displayTest = 0x0f; / * FINAL de MAX 7219 instrucciones direcciones * / / / putByte - función auxiliar que envía un sólo byte al máximo chip void putByte (datos del octeto) {bytes i = 8; máscara de bytes; while(i > 0) {máscara = 0 x 01 << (i - 1); / / obtener la máscara de bits si (datos y máscara) / comprobar y enviar valor de este bit {LED_DATA_PORT | = DataMaskHigh;} más {LED_DATA_PORT & = DataMaskLow;} / / pulso del reloj de MAX LED_CLOCK_PORT y = ClockMaskLow; / / digitalWrite (reloj BAJA); "garrapata" prepeare poco entrada LED_CLOCK_PORT | = ClockMaskHigh; / / digitalWrite (reloj, alta); binario de la entrada "tock"--i; //maxSingle movimiento de bits menor}} es la función de "fácil" para un //dig solo max7219 es la fila, y seg es la columna llamada empuje y seg se refieren a nombres de pin de tecnología doc void maxSingle (dig byte, byte seg) {LED_LOAD_PORT & = LoadMaskLow; / / digitalWrite (carga, LOW); / / iniciar putByte(dig); / / especificar registro putByte(seg); / / ((data & 0x01) * 256) + datos >> 1); poner datos LED_LOAD_PORT | = LoadMaskHigh; digitalWrite(load,HIGH); } / / buttonInit - función auxiliar que ceros el botón Estados buttonInit(void) vacío {bytes; para (i = 0; i < 8; i ++) {button_current [i] = 0 x 00; button_last [i] = 0 x 00; button_state [i] = 0 x 00; button_event [i] = 0 x 00;}} / / buttonCheck - comprueba el estado de un botón determinado. void buttonCheck (fila de byte, byte índice) {si (((button_current [fila] ^ button_last[row]) & (1 << índice)) & & / / si el estado actual del botón físico es diferente de la ((button_current [fila] ^ button_state[row]) & (1 << índice))) {/ / última estado botón físico y la corriente debounced estado si (button_current [fila] & (1 << índice)) {/ / si el estado actual del botón físico es deprimido button_event [fila] = kButtonNewEvent << índice; / / cola un nuevo evento de botón inmediatamente button_state [fila] | = (1 << índice); y el estado debounced abajo. } else {button_debounce_count [fila] [índice] = kButtonUpDefaultDebounceCount;} / / de lo contrario el botón fue presionado anteriormente y ahora / / ha sido liberado por lo que establecemos nuestro contador debounce. } else if (((button_current [fila] ^ button_last[row]) & (1 << índice)) == 0 & & / si el estado actual del botón físico es el mismo que (button_current [fila] ^ button_state[row]) & (1 << índice)) {/ físico último botón Estado, pero la física actual / estado de botón es diferente de la actual debounce / / estado... Si (button_debounce_count [fila] [índice] > 0 & y--button_debounce_count [fila] [índice] == 0) {/ / si el el contador debounce tiene / / se reduce a 0 (significado el / el botón ha sido para / / kButtonUpDefaultDebounceCount / / iteraciones / / button_event [fila] = kButtonNewEvent << índice; / / cola un evento de cambio de estado de botón si (button_current [fila] & (1 << índice)) {/ / y cambiar los botones debounce estado. button_state [fila] | = (1 << índice);} else {button_state [fila] & = ~ (1 << índice);}}} } void buttonpress () {para (me = 0; i < 8; i ++) {LATCH_PORT_595 & = latchPin595MaskLow; / / conjunto clavija de enganche bajo para las salidas no cambian durante el envío de bits para (j = 0; j < 8; j ++) {CLOCK_PORT_595 & = clockPin595MaskLow;//digitalWrite(clockPin,LOW); si (j == me) {//if corriente igual de índice que valoro, conjunto bajo DATA_PORT_595 y = dataPin595MaskLow;//digitalWrite(A4,LOW);} más {//set el resto de los pines DATA_PORT_595 alta | = dataPin595MaskHigh; //digitalWrite (A4 ALTO); } CLOCK_PORT_595 | = clockPin595MaskHigh;//digitalWrite(clockPin,HIGH); } //set cierre perno alto-envía datos a salidas LATCH_PORT_595 | = latchPin595MaskHigh; //digitalWrite (latchPin, HIGH); Desaceleración se pone aquí para perder un poco de tiempo mientras esperamos el estado de la salida / / pins para resolver. Sin este bucle pérdida de tiempo, una sola pulsación se muestra como / / dos prensas (el botón y su vecino) volátiles int desaceleración = 0; mientras que (desaceleración < 15) {desaceleración ++;} button_last [i] = button_current [i]; para (id = 0; id < 8; id ++) {interruptor (id) {caso 0: t = digitalRead(A0); break; caso 1: t = digitalRead(A1); break; caso 2: t = digitalRead(2); break; case 3: t = digitalRead(3); break; caso 4: t = digitalRead(4); break; caso 5: t = digitalRead(5); break; caso 6: t = digitalRead(6); break; caso 7: t = digitalRead(7); break;} t = (t == 0); //invert t if(t) {button_current [i] | = (1 << id);} else {button_current [i] & = ~ (1 << id);} buttonCheck (i, id); Si (button_event [i] y (1 << id)) {button_event [i] & = ~ (1 << id); //zero el botón evento si (button_state [i] & (1 << id)) {Serial.write(1);} else {Serial.write(byte(0));} si (mirrorXButtons) {id = 7-id;} si (mirrorYButtons) {i = 7-i;} Serial.Write(((ID) << 4) | (i)); //set}}} todos los pines de 595 alto LATCH_PORT_595 & = latchPin595MaskLow; / / configurar pin de cierre baja por lo que las salidas no cambian durante el envío de bits para (j = 0; j < 8; j ++) {CLOCK_PORT_595 & = clockPin595MaskLow;//digitalWrite(clockPin,LOW); DATA_PORT_595 | = dataPin595MaskHigh;//digitalWrite(A4,HIGH); CLOCK_PORT_595 | = clockPin595MaskHigh;//digitalWrite(clockPin,HIGH); } //set cierre perno alto-envía datos a salidas LATCH_PORT_595 | = latchPin595MaskHigh; //digitalWrite (latchPin, HIGH); } ISR(TIMER2_COMPA_vect) {{si (Serial.available()) {si (WaitingForAddress == 1) {byte0 = Serial.read(); dirección = byte0 >> 4; WaitingForAddress = 0; } / / fin si (WaitingForAddress == 1); Si (Serial.available()) {WaitingForAddress = 1; byte1 = Serial.read(); switch(address) {caso 2: estado = byte0 & 15; x = byte1 >> 4; y = byte1 & 15; si (estado == 0) {ledmem [7-y] & = ~ (1 << x);} else {ledmem [7-y] | = (1 << x);} break; caso 3: IntensityChange = 1; IntensityVal = byte1 y 15; rotura; caso 4: DisplayTestChange = 1; DisplayTestVal = byte1 y 15; rotura; caso 5: estado = byte1 & 0x0F; ADCEnableState [(byte1 >> 4) & 0 x 03] = Estado; rotura; caso 6: ShutdownModeChange = 1; ShutdownModeVal = byte1 y 15; rotura; caso 7: Si (firstRun == 1) {para (x = 0; x < 8; x ++) {ledmem [x] = 0;} firstRun = 0;} x = ((byte0 & 15) & 0x7); Este valor de la máscara para no escribir a una dirección no válida. y = byte1; Si (mirrorYLEDs) {y = 7-y;} si (mirrorXLEDs) {x=flipByte(x);} ledmem [x] = y; rotura; caso 8: Si (firstRun == 1) {para (x = 0; x < 8; x ++) {ledmem [x] = 0;} firstRun = 0;} x = ((byte0 & 15) & 0x7); y = byte1; Si (mirrorYLEDs) {x = 7-x;} si (mirrorXLEDs) {y=flipByte(y);} para (z = 0; z < 8; z ++) {si (y & (1 << z)) {ledmem [z] | = 1 << x;} else {ledmem [z] & = ~ (1 << x);}} break; } / / fin switch(address)} / / fin si (Serial.available()} / / fin si (Serial.available();} / / fin mientras que (Serial.available() > 16); } void whoosh(void) {/ / reactivar la interrupción de desbordamiento para / / temporizador 1 - ha necesitado por delay(...) TIMSK0 | = (1 << TOIE0); para (int j = 0; j < 9; j ++) {para (int i = 0; i < 8; i ++) {maxSingle (i + 1, 1 << j);} delay(125);} / / y apagar la interrupción. TIMSK0 & = ~ (1 << TOIE0); } void setup () {DDRD = PORTD_Data_Direction; DDRB = PORTB_Data_Direction; DDR = PORTC_Data_Direction; Serial.Begin(57600); buttonInit(); iniciación de la maxSingle 7219 máximo (max7219_reg_scanLimit, 0x07); maxSingle(max7219_reg_intensity,0x0F); maxSingle (max7219_reg_shutdown, 0 x 01); no en modo de apagado maxSingle (max7219_reg_displayTest, 0 x 00); vaciar los registros, gire todos los LEDs de h para (me = 1; i < = 8; i ++) {maxSingle(i,0); ledmem [i-1] = 0;} (cli); //stop interrupciones //set timer2 interrumpir cada TCCR2A 128us = 0; / ajustar todo registro de TCCR2A a 0 TCCR2B = 0; / / igual para TCCR2B TCNT2 = 0; //initialize valor de contador a 0 / / sistema compara partido registro para incrementos de 7,8 khz OCR2A = 255; / = (16 * 10 ^ 6) / (7812.5 * 8) - 1 (debe ser < 256) / / Activar modo CTC TCCR2A | = (1 << WGM21); Sistema poco CS11 para 8 prescaler TCCR2B | = (1 << CS11); habilitar interrupción de timer compara TIMSK2 | = (1 << OCIE2A); (SEI); interrupciones //allow / / / / configurar el contador de 8 bits 2, salida compara apagada, / / / / normal generación de forma de onda (lo que puede significar) / / TCCR2A = 0; / / contador para ser registrado en 16Mhz/8 = 2 Mhz / / TCCR2B = 1 << CS21; / / / / establecer la máscara de interrupción para que tengamos una interrupción / / / / desbordamiento de timer 2, es decir, después de 256 ciclos de reloj. / / Se ejecuta la rutina de interrupción del timer 2 todos / / / / 128 nosotros. TIMSK2 = 1 << TOIE2; / / / Nota: en mi esfuerzo por tratar de que esto / / / / código para trabajar confiablemente a 115200 baudios / / / / fui sobre deshabilitar sin usar temporizadores que / / / / se establecen por el framework de Arduino. / / El marco establece dos timer 2 y / / / / temporizador 1. Utilizamos el timer 2 para conducir el / / / / serie de interrupción de la lectura, tan sólo podemos / / / / desactivar timer1 y su interrupción. / / / / MUY importante Nota: Si desea usar / / / / ANALOGWRITE / / / / / / desactivar el temporizador 1 se desconecta el temporizador / / / usa para PWM, que es la base de analogWrite. / Si desea utilizar analogWrite, quite / / / / las líneas de abajo. / / / / DESHABILITAR contador PWM / / TIMSK0 & = ~ (1 << TOIE0); / / TCCR1B & = ~ (1 << CS12); TCCR1B & = ~ (1 << CS11); TCCR1B & = ~ (1 << CS10); / / FIN desactivar el PWM contador / / / / / / también, desactivar las interrupciones de temporizador 0 / / / / realmente importante Nota Si usted quiere usar / / / retardo / / / eliminar esta línea y también / / / / 'whoosh', que activa y desactiva entonces / / / / intterupt temporizador 0. Usted querrá / / / / para deshacerse de esas líneas también. TIMSK0 & = ~ (1 << TOIE0); bonito del patrón para asegurarme que el firmware / / subido correctamente. Whoosh(); Asegúrese de apagar las luces. para (int i = 0; i < 8; i ++) {maxSingle(i+1,0);}} void sendADC (int puerto, int valor) {/ / Serial.write ((1 << 4) | ((puerto << 2) y 0x0C) | ((valor >> 8) & 0 x 03)); Serial.Write (valor & 0xFF); } / / //int actual [4]; int anterior [4]; tolerancia de int = 7; void checkADCs() {/ / / / para (int adcCounter = 0; adcCounter < 4; adcCounter ++) / / {/ / / si (ADCEnableState [adcCounter]! = 0) / / {/ / corriente [adcCounter] = analogRead(adcCounter); / / / si (abs(previous[adcCounter]-current[adcCounter]) > tolerancia) / / {/ / anterior [adcCounter] = corriente [adcCounter]; / / sendADC(adcCounter,current[adcCounter]); / /} / / / /} / / / /} / /} byte flipByte (numToFlip bytes) {espejo bytes = 0; para (int j = 0; j < 8; j ++) {copia byte = numToFlip; espejo | = (copia >> j) & 1; si (j < 7) {espejo = espejo << 1;}} vuelta espejo;} void loop () {/ los Estados LED, enviar a la matriz de LED. para (int i = 0; me < 8; i ++) {byte ledmemMirror = 0; si (mirrorXLEDs) {ledmemMirror=flipByte(ledmem[i]);} más {ledmemMirror = ledmem [i];} int y; si (mirrorYLEDs) {y =(7-i) + 1;} más {y = i + 1;} maxSingle(y,ledmemMirror);} si (IntensityChange == 1) {IntensityChange = 0; maxSingle (max7219_reg_intensity, IntensityVal y 15);} si (DisplayTestChange == 1) {DisplayTestChange = 0; maxSingle (max7219_reg_displayTest, DisplayTestVal y 15);} si (ShutdownModeChange == 1) {ShutdownModeChange = 0; maxSingle (max7219_reg_shutdown ShutdownModeVal y 15); } / / comprobar botón presiona buttonpress(); / / comprobar el estado de la ADCs / / checkADCs(); }

Debería ver el LED enciende para arriba de una columna en una hora y un desplazamiento en la pantalla cuando haya terminado el firmware subir:


Si tus leds no están conectados correctamente, les fija ahora antes de pasar al siguiente paso.

Artículos Relacionados

Controlador MIDI controlado de la llama

Controlador MIDI controlado de la llama

Un gran problema en el mundo de la producción de música digital es mantener esa calidez analógica (que resonaba de tubos y sistemas de casetes) de música digital de hoy en día. Muchos jurarán que sistemas analógicos tienen un sonido que nunca pueda s
Regulador del RGB música con AGC

Regulador del RGB música con AGC

Aquí voy a mostrar cómo crear un controlador RGB musical que luces leds RGB a la música. También he añadido un AGC (control automático de ganancia), que mantiene la tensión en el mismo nivel. Esto es muy útil, porque juegas con frecuencia música en d
Controlar el reproductor de música de mac con arduino

Controlar el reproductor de música de mac con arduino

Controlar iTunes y Vox los 2 jugadores más populares de la música en ordenadores mac con un arduinoPaso 1: Cosas necesariasArduino (estoy usando uno pero cualquiera debería hacer).Cable USB para arduino.IR recibir diodo.Control remoto universal IR.ca
Cómo hacer un programador USB de youtube

Cómo hacer un programador USB de youtube

esto es un controlador simple youtube que "actúa" como su presionar J, K o LPaso 1: Lo que usted necesita-Un teclado inútil-Un soldador-Pegamento-Hoja vidrio (recomendado)Paso 2: Asignación de las teclas poner el teclado pcb oh superior del tecl
Pintura de LED sensible al sonido (en el relativamente barato)

Pintura de LED sensible al sonido (en el relativamente barato)

este instructable le mostrará cómo hacer un cuadro Triptico con luces de LED que responden a la música. Seré honesto y decir que esto no es el primer proyecto de este tipo, pero se trata de cómo hice esta y es relativamente más barato que otros proye
POP música "Convertible" Arcade estilo controlador (ASC)

POP música "Convertible" Arcade estilo controlador (ASC)

Pop'n música es un juego de arcade de simulación musical producido por Konami, con diseño de color brillante y personajes de dibujos animados lindo pero sorprendentemente difícil de maestro. Konami ha lanzado algunos títulos en la plataforma de Plays
Física controlador para iPad o iPhone

Física controlador para iPad o iPhone

me gusta jugar en mi iPad pero a veces deja ganas de más porque carecen de un controlador físico. Me puse a crear una solución simple, barata y personalizable. Puede trabajar con casi cualquier aplicación y con casi cualquier teléfono inteligente o t
Robótica básica parte 2 - agregar el microcontrolador y el Motor controlador

Robótica básica parte 2 - agregar el microcontrolador y el Motor controlador

Esta semana nuestros alumnos en la escuela de canto de largo (http://www.longridgeschool.org) une la electrónica en el chasis del robot que se completó en la parte 1:.Encontramos que había que sustituir a los separadores en el robot porque eran demas
Música DIY activado controlador de LED

Música DIY activado controlador de LED

he decidido hacer un instructable sobre esto ya que no pude encontrar uno cuando lo necesitaba. Este es un proyecto DIY muy sencillo. primero de todo vas a necesitar un transistor TIP 31, fuente de alimentación de 12V, protoboard, alambre y de L.E. D
Compatible con mano protésica con Control sensoriomotor y la retroalimentación sensorial para amputados de la extremidad superior

Compatible con mano protésica con Control sensoriomotor y la retroalimentación sensorial para amputados de la extremidad superior

El objetivo principal de este proyecto es proporcionar una mano protésica altamente funcional y de bajo costo para personas con amputaciones por debajo del codoAunque se han desarrollado muchos dispositivos prostéticos de vanguardia, todavía se convi
Tacto: Bajo costo, avanzada mano protésica

Tacto: Bajo costo, avanzada mano protésica

En este instructable te voy a mostrar cómo hacer tacto, una mano prostética barata, abrir-fuente. Tacto superior a otros modelos de la mano protésica de código abierto de varias maneras: cuesta sólo $100 para todos los componentes ($250 para también
Sensores de infrarrojos MultiTouch para aumentar los objetos y la piel humana

Sensores de infrarrojos MultiTouch para aumentar los objetos y la piel humana

He estado construyendo costumbre IR soluciones de detección por un tiempo ahora. Estos incluyen sensores de proximidad, sensores de detección de objeto y sensores al tacto much. La intención detrás de este instructable es compartir algunas de las exp
Controladores para Robots baratos, parte 2 de velocidad: controlador PID

Controladores para Robots baratos, parte 2 de velocidad: controlador PID

¡ Hola! Esto es parte de un tutorial de dos partes sobre cómo hacer un controlador de velocidad de un resorte de tapa y pluma de botella! Si no lo has hecho todavía, ¡ pruébelo! Si te gusta, por que no votar por mí en el concurso de sensores! ¡ Graci
Música y luces en caja para niños

Música y luces en caja para niños

Música + show de luces en caso de niños.Video aquí: Video: reproductor de música prueba de niño + luces de¿Por qué hacer esto?Hace unos años hice una máquina mucho más compleja para un niño con discapacidad que contenía un iPod donde se podían selecc