29 abril 2022

Cambiadora AMYC Global Change. Con NV10 traga. Apaño. BILLETERO NV10 POR LUMINA.

 PARA REPASAR, y fuentes de información: La máquina de cambio se compone, a grandes rasgos, de un lector de billetes (originalmente un LUMINA de Money Controls -hoy extinguida-) que por ser raro, no dispongo de repuesto y se convierte en complicada la gestión de reprogramaciones y actualizaciones para billetes nuevos. Por eso, hace ya un tiempo (años), se ha cambiado por un NV10 de Innovative Technologies. Un transformador toroidal alimenta a 24Vac a la placa principal (Macmoney Newmac/5). En ella está la regulación de alimentación y el control. Añade dos placas externas, una para controlar un pequeño display, y otra de I/O con pulsadores para manejar la configuración y enchufar el billetero. Esta controla dos hoppers tipo U de Azkoyen. 

 

Billetero LUMINA de Money Controls (original)
 

Billetero NV10 de Innovative Technologies (modificación)

 

En principio, la adaptación no tiene demasiada complicación. El Lumina usa, en este caso, el protocolo paralelo, y el NV10 también soporta ese protocolo. Así, que solo se trata de modificar la sujeción a la máquina, y cablear los pines correspondientes del NV10 (ver página 10 del manual del NV10) directamente a los pines apropiados del Lumina (ver página 6 del manual del Lumina). Y, sin más complicaciones, ¡ha funcionado bien varios años...!


Conexión al billetero CN6


EL PROBLEMA:

Desde hace unas semanas la máquina traga de vez en cuando algún billete. Se le cambia el billetero NV10 y se rehace el cableado de la conexión de éste. Pero el problema sigue. Así que se retira para taller.

En el taller, en test, observo algo curioso. Siempre que traga el billete parece patinar un poco. O cuando yo, con la mano, sujeto el billete para no dejarlo pasar fácilmente, el billete pasa, pero la máquina no lo da por aceptado. 

Así, que con un analizador lógico barato, y desde el magnífico entorno Open Source de análisis de señal Sigrok ,me dispongo a intentar estudiar las señales del NV10 y descubrir qué pasa.  


Estudio de señales y timing entre billetero y máquina


ANALIZANDO PROCESO DE ACEPTACIÓN CORRECTO:

Según el análisis de la documentación del selector de billetes NV10, el proceso de aceptación en esta máquina (protocolo paralelo) sería como sigue (Ver FOTOaceptación):

Las señales de crédito (pines del 1 al 4 del NV10) permanecen, en reposo, a nivel alto. Cuando introduces un billete, que el NV10 reconoce, baja la linea correspondiente al billete en cuestión (en el ejemplo billete de 5€=linea D0) durante 100ms.

La linea Escrow (pin 10) se usa para poder rechazar o aceptar el billete. Si la mantenemos a nivel alto, aceptará  siempre. Si la ponemos a nivel bajo antes de la aceptación, una vez leído un billete, e indicado con una primera  bajada de 100ms de la línea correspondiente, el NV10 espera hasta 30 segundos, a que la línea Escrow se suba a nivel alto. Así indicamos que queremos aceptar el billete. El billete pasará al interior y el NV10 volverá a confirmar todo el proceso de aceptación volviendo a bajar el pin Accept correspondiente. 

Se puede provocar la devolución del billete subiendo a nivel alto la línea Inhibit  correspondiente antes de la aceptación total. O inhibiendo todas las lineas Inhibit, hacer que devuelva todo y que el billetero permanezca inactivo.


(FOTOaceptación) D0,D1,D2=Accept1,3,2*; D3,D4,D5=Inhibit1,2,3; D7=Escrow


En la "FOTOaceptación" y "FOTOaceptaciónBIS", se pueden observar las señales involucradas en un proceso de aceptación correcto, real, con esta máquina. Obsérvese que la máquina cambiadora inhibe la entrada de billetes mientras los hoppers están pagando (D4 y D5 a nivel alto un rato después de aceptar). 

Nótese también, que no tengo monitorizado la linea Accept4 e inhibit4 porque "mi analizador lógico" sólo tiene 8 lineas de datos, así que he inhibido, e ignorado, el billete de 50€ para las pruebas. (*)También había cometido un error y están intercambiados de su forma lógica los pines D1 y D2 que deberían haber sido Accept2 y Accept3, pero no. Nótese el pie de "FOTOaceptación". Siento estos errores y la mala edición de las fotos. Fue todo un proceso que ahora, acabado, pretendo documentar, con lo que conservo. Pero cuando empecé no pretendía hacer esta entrada.


(FOTOaceptaciónBIS) Aceptación correcta de 5,10,y 20€


NO ENTIENDO EL PROBLEMA:

 Aquí, en la siguiente imagen, enseño monitorizadas otras señales, en donde sí, esta vez, la máquina se traga billetes. El primer billete de 5 euros y el tercero de 20 euros. El segundo billete de 10 euros es aceptado correctamente.

 

Traga billete de 5, acepta de 10, y vuelve a tragar de 20€


Aquí sucede algo que no acabo de entender, y que me gustaría, si alguien tiene alguna idea, me la comentase...

Cuando el billete, después de ordenar aceptarlo (escrow a High) por alguna razón patina, la máquina, no sé como (¿sugerencias?) lo detecta. Baja escrow justo el tiempo que el billete patina o lo mantengo sujeto a propósito. Y además sube las líneas de inhibición para ordenar rechazarlo. Pero el billete ya ha pasado, y el NV10 no es capaz de hacer caso. Acaba dando el segundo pulso de confirmación de aceptación, pero la máquina ya no le hace caso. 


MIS HIPÓTESIS DEL FALLO:

He pensado varias ideas de porque podría estar fallando.

Probé con varios NV10. Con algunos mejor que con otros, pero con todos traga a veces.

Pensé en un fallo de alimentación. Que cuando el billete se atasca o patina, el consumo del billetero sea mayor y no sean estables los niveles lógicos. De hecho he revisado alimentación y cambiado condensadores para subir unas décimas la tensión de +12Vdc. Todo sin conseguir resultados. 

Además, con todo, parece una respuesta intencionada de la máquina de cambio, puesto que se eleva el nivel de las líneas de inhibición para rechazar el billete. Pero, ¿cómo puede detectar esto la máquina si el billetero, funcionando en paralelo, y según el manual, no se lo comunica de ninguna manera?

Valoré que sea por el timing de las señales. Si el billete patina, tardará más de lo debido en entrar. Algo que la máquina sí puede medir. Pero las lineas de inhibición suben antes de que todo el billete entre. La linea de escrow permanece baja todo el tiempo y el exacto que el billete permanece atascado o patinando. Es decir, la máquina se da cuenta antes de que billete entre.

He consultado, y me sugieren que el problema no tiene solución. Que la máquina no adapta bien con los NV10. Y puesto que los Lumina se consideran obsoletos, la única opción es un kit de adaptación para otro modelo que no tengo. Es decir, que gastes  en un kit y en otro billetero...

Por curiosidad, reinstalo el Lumina viejo (original de la máquina) que no acepta billetes nuevos,  pero sí viejos. Y la observación, es que el billete entra mas suave que en el NV10, pero que si lo sujeto o freno lo suficiente, el mismo proceso, con las señales, se repite, y el billete también se lo traga.

 Acabo convencido de que la máquina tiene algún sistema de seguridad por software o hardware que no integra bien con el NV10. Sobre todo cuando ya no es  nuevo y no va fino. 

Pero la solución del kit me da una idea: puedo intentar hacerlo yo. 

No parece una cosa difícil. Con un Arduino puedo leer y aceptar primero el billete, siguiendo el protocolo estricto que marca el manual;  y cuando lo tenga dentro, pasarle, a la máquina, las señales apropiadas para que interprete la aceptación, ya sin "patinaje" ni "atasco" posible.  


A LA FAENA:

Así que me puse a la faena. Todo muy prototipo, sin intención de que fuese muy reproducible. Pero como finalmente ha salido factible y satisfactorio, he limpiado un poco el código (mi nivel de programación es limitada) y dejo por aquí mis notas, fotos y cositas (algunas algo cutres) para recordar, si hace falta en un futuro, volver a ello. O como siempre, por si a alguien más le sirve como aprendizaje. También valoraría, si alguien sabe como la máquina detecta la mala entrada del billete, algún comentario.

 

 

Pruebas y programación de Arduino como kit intermedio de adaptación

 

He usado un Arduino Nano. Las señales IN/OUT vistas desde el punto de vista del Arduno las he organizado como sigue:

 

------------------------------------------------------------------

DESDE CONECTOR NV10

IN                                                        OUT

D9 (pin12)-----ACCEPT1

D10(pin13)----ACCEPT2   

D11(pin14)----ACCEPT3

D12(pin15)----ACCEPT4

                       INHIBIT1---D4(pin7)

                       INHIBIT2---D5(pin8)

                       INHIBIT3---D6(pin9)

                       INHIBIT4---D7(pin10)

 

                       ESCROW---D8(pin11)

 

DESDE EL CONECTOR "A" LA MAQUINA 

IN                                                        OUT

                         ACCEPT1---D0(pin2)

                         ACCEPT2---D1(pin1)

                         ACCEPT3---D2(pin5)

                         ACCEPT4---D3(pin6)

D14-A0(pin19)--INHIBIT1

D15-A1(pin20)--INHIBIT2

D16-A2(pin21)--INHIBIT3

D17-A3(pin22)--INHIBIT4

 

D18-A4(pin23)--ESCROW

                        


                          LED---------D13(pin16,conectado internamente)

D19-A5(pin24); A6(pin25); A7(pin26)---Conectados a masa para prevenir ruido.

---------------------------------------------------------------

Aquí el esquema eléctrico de las conexiones. Puede descargarse aquí.

Esquema electrico del adaptador


 

Y aquí dejo el código. Puede descargarse también aquí.




/////////////////////////////////
// Sancos v0.3 28-01-2022      //
// Para adaptar poner un NV10  //
// en cambiadora AMYC MacMoney //
// GlobalChange, con original  //
// Lumina de Money Controls    //
//                             //
// El NV10 a pelo, si no va    //
// fino, cuando patina, la     //
// maquina por algun motivo    //
// baja señal de escrow y sube //
// inhabilitaciones. Y traga el//
// billete.                    //
//                             //
// Con Arduino manejaré NV10   //
// y luego pasaré los datos y  //
// timing directamente a la    //
// maquina sin funcionamiento  //
// ya del billetero            //
/////////////////////////////////
#define ARDUINO_NANO // Uso Nano. Cambiar si otro


#ifdef ARDUINO_NANO  //Añadir si otro
  #define ACCEPT1_NV10 9
  #define ACCEPT2_NV10 10
  #define ACCEPT3_NV10 11
  #define ACCEPT4_NV10 12
  #define INHIBIT1_NV10 4
  #define INHIBIT2_NV10 5
  #define INHIBIT3_NV10 6
  #define INHIBIT4_NV10 7
  #define ESCROW_NV10 8

  #define ACCEPT1_MAQ 0
  #define ACCEPT2_MAQ 1
  #define ACCEPT3_MAQ 2
  #define ACCEPT4_MAQ 3
  #define INHIBIT1_MAQ 14
  #define INHIBIT2_MAQ 15
  #define INHIBIT3_MAQ 16
  #define INHIBIT4_MAQ 17
  #define ESCROW_MAQ 18

  #define LED 13
#endif

void setup()
{
  pinMode(ACCEPT1_NV10,INPUT_PULLUP); //Accepts desde el NV10
  pinMode(ACCEPT2_NV10,INPUT_PULLUP);
  pinMode(ACCEPT3_NV10,INPUT_PULLUP);
  pinMode(ACCEPT4_NV10,INPUT_PULLUP);
  pinMode(INHIBIT1_MAQ,INPUT_PULLUP); //Inhibits desde maquina
  pinMode(INHIBIT2_MAQ,INPUT_PULLUP);
  pinMode(INHIBIT3_MAQ,INPUT_PULLUP);
  pinMode(INHIBIT4_MAQ,INPUT_PULLUP); 
  pinMode(ESCROW_MAQ,INPUT_PULLUP);   //Escrow desde maquina

  pinMode(ACCEPT1_MAQ,OUTPUT);  // Accepts hacia la maquina
  pinMode(ACCEPT2_MAQ,OUTPUT);  
  pinMode(ACCEPT3_MAQ,OUTPUT); 
  pinMode(ACCEPT4_MAQ,OUTPUT); 
  pinMode(INHIBIT1_NV10,OUTPUT);  // Inhibits hacia el NV10
  pinMode(INHIBIT2_NV10,OUTPUT);  
  pinMode(INHIBIT3_NV10,OUTPUT); 
  pinMode(INHIBIT4_NV10,OUTPUT); 
  pinMode(ESCROW_NV10,OUTPUT);    // 'ESCROW' hacia el NV10
  
  pinMode(LED,OUTPUT); // LED del arduino indicaciones de estado 
  digitalWrite(LED,LOW);
}

void loop()
{
  int billete;
 
  billete=DetectarBillete();
  if (billete)
    {
      if (ConfirmarBillete(billete)) TranspasoAMaquina(billete);        
    }
}




/*************************************/
/*            FUNCIONES              */
/*************************************/

int DetectarBillete()
{
  /*
   * Estado de reposo
   * NV10 espera a que "aparezca" un billete
   * ESCROW low, INHIBITS segun indique maquina
   * ACCEPTs a maquina HIGH
   * Pendiente de algun ACCEPT en NV10
   */
  int quelineaLOW;

  digitalWrite(INHIBIT1_NV10,digitalRead(INHIBIT1_MAQ)); // Acepta billetes
  digitalWrite(INHIBIT2_NV10,digitalRead(INHIBIT2_MAQ)); // según cambiadora
  digitalWrite(INHIBIT3_NV10,digitalRead(INHIBIT3_MAQ));
  digitalWrite(INHIBIT4_NV10,digitalRead(INHIBIT4_MAQ));
  digitalWrite(ACCEPT1_MAQ,HIGH); //Aseguro no datos a maquina
  digitalWrite(ACCEPT2_MAQ,HIGH); //mientras leo billetero
  digitalWrite(ACCEPT3_MAQ,HIGH);
  digitalWrite(ACCEPT4_MAQ,HIGH);

  digitalWrite(ESCROW_NV10,LOW);   //En disposicion usar ESCROW 
  quelineaLOW=LeerBill();
  return quelineaLOW;
}


int ConfirmarBillete(int billete_en_entrada)
{
  /*Un billete ha sido detectado en la entrada
   *Aceptarlo y pendiente de confirmar que entra a cajon
   */
  unsigned long tiempo; 
  tiempo=millis();
  
  digitalWrite(ESCROW_NV10,HIGH);  //Acepto billete
    do
      {
        /* 
         *  Perder tiempo hasta que se 
         *  confirme aceptacion billete
         *  LeerBill()=al billete_en_entrada de la lectura anterior
         */
      }
    while ((billete_en_entrada != LeerBill()) && ((millis()-tiempo) < 6000));
    if ((millis()-tiempo)>5999)
      {
        billete_en_entrada=0; //Algo paso, no valido
        digitalWrite(INHIBIT1_NV10,HIGH);   // Inhibiciones 
        digitalWrite(INHIBIT2_NV10,HIGH);   // todas HIGH. 
        digitalWrite(INHIBIT3_NV10,HIGH);   // Devuelve
        digitalWrite(INHIBIT4_NV10,HIGH);
        digitalWrite(LED,HIGH); //Marca error
        delay(31000);  //Mas de 30seg sin confirmacion NV10 intenta devolver
        digitalWrite(LED,LOW);
      }
  return (billete_en_entrada);  //paso billete admitido o 0 si fallo
      
}


int TranspasoAMaquina(int transBill)
{
  /*    
   * Inhibimos NV10 mientras    
   * transpasamos valor a maquina
   */
   int linAccept=0;

   digitalWrite(INHIBIT1_NV10,HIGH); //Inhibir NV10
   digitalWrite(INHIBIT2_NV10,HIGH);
   digitalWrite(INHIBIT3_NV10,HIGH);
   digitalWrite(INHIBIT4_NV10,HIGH);

   linAccept=transBill-1; // Calcula pin
   // Accept1-Pin0; ... Accept4-Pin3
   digitalWrite(linAccept,LOW); //Envio pulso billete
   delay(100);
   digitalWrite(linAccept,HIGH);
   
   do
   {
     //Esperar maquina accepte (ESCROW HIGH)
   }while (digitalRead(ESCROW_MAQ) == LOW);

   delay(1000); //simulo retraso entrar billete

   digitalWrite(linAccept,LOW); //Otro pulso confirma billete
   delay(100);
   digitalWrite(linAccept,HIGH);

   do
   {
     //Esperar maquina confirme aceptacion (ESCROW LOW)
   }while (digitalRead(ESCROW_MAQ) == HIGH);

   
   // Espero a volver el tiempo que tarda en pagar monedas
   delay(1500*(1<<linAccept)); //Aprox. 300ms por moneda
   //uso operacion de bits porque en C no funcion potencia
  
   return 0; //Proceso acabado.
}


int LeerBill()
{
        // leer lineas ACCEPT NV10, 
        // asegura pulso largo (activo LOW)
        // pasa valor 0(si no LOW) 1,2,3,4 (segun linea LOW)
    int linea[4]={ACCEPT1_NV10,ACCEPT2_NV10,ACCEPT3_NV10,ACCEPT4_NV10};    
    int lectura,i;

    for (int i = 0;i<=3;i=i+1)
    {
      lectura=digitalRead(linea[i]);
       if (lectura == LOW)
         {
           delay(50);
           if (lectura == digitalRead(linea[i]))
           //Si pulso largo
             {
              do
                {
                 // Pulso LOW, esperar a que HIGH
                }
              while (lectura == digitalRead(linea[i]));
              delay(10);
              return (i+1); // Pulso ACCEPT1  
             }
           else
             {
               //Pulso demasiado corto   
             }
         }
  
    }

    return 0; //No hubo pulso o fue corto
}
   


Para hacerlo físicamente he utilizado una stripboard porque solo se trata de un prototipo que no cuento reproducir. Una stripboard tiene los pads unidos en tiras. Uso un taladro para cortar las pistas en ciertos puntos (en mi esquemita particular "X" rojas) y con un cuter divido algunas pistas, perpendicularmente, para colocar los conectores IDC (líneas discontinuas rojas).

Mi esquemita stripboard para entenderme yo


Y su correspondiente acabado



Al voltear izquierda es derecha...


Y aquí ya, con el resultado final, colocación y pruebas a fondo....

Testeando todo con billete de 5€

Resultado final  



03 febrero 2016

RELÉ. Improvisando RETARDO A LA DESCONEXIÓN con circuito RC. (Electrónica interesante para principiantes)

(EN) RELAY. Throw a OFF DELAY together with RC circuit.
(FR) RELAIS. RETARDÉ À L'OUVERTURE improvisé avec RC circuit.
(PT) RELÉ. Improvisando RETARDO NA DESENERGIZAÇÃO con circuito RC. 
-------------------------------------------------------------------------------------------------------------------------------------------

PARA REPASAR: No hace falta un gran nivel de conocimiento teórico para entender el proceso de esta entrada. Todo lo he ido haciendo por aproximaciones prácticas (prueba error). Pero sería interesante conocer un par de conceptos. Incluso podría ser un ejercicio interesante para un principiante a la electrónica. 
Algunos enlaces interesantes que he encontrado a bote pronto: 
Si ha llamado la atención esta entrada es casi seguro que sabemos lo que es un relé. Pero por si acaso siempre nos quedará la Wikipedia. 
Una descripción teórica del asunto de la carga y descarga de un condensador y el tiempo que tarda en ello.
Y aunque a groso modo no son tan dificiles los cálculos, aquí una web para probar con diferentes valores.
También una pequeña reseña sobre rectificación de corriente alterna.
Y sobre diodo de protección en relés.

EL PROBLEMA:

En mi caso particular, el aparato en cuestión, era un "Bully conejo" del fabricante "Falgas".  Pero esto no es importante. Saber que se trata de uno de esos móviles (muñecos, coches, animalitos,...) que al introducir una moneda, balancean mediante poleas y correas accionadas por un motor eléctrico. Durante un determinado tiempo, el aparato se mueve, suena una musiquilla, y puedes accionar algún sonido (bocinas, alarmas,...). 
El caso era que el aparato en cuestión tiene una música de reclamo, que mientras la máquina está en reposo y espera, suena a menudo para llamar la atención. Esto era un problema para colocar la máquina en ciertos sitios. La gente se quejaba de que era repetitivo, molesto y pesado. 
Generalmente este tipo de máquinas disponen de algún microswitch o mecanismo para habilitar o deshabilitar este tipo de reclamo sonoro. Aquí nos encontramos con un aparato viejo y sin manuales, que ya ha pasado por varias manos y remiendos, y que aunque se intentó (16 combinaciones de 4 switches), no hemos logrado encontrar la forma.
Así que la primera solución que se nos ocurrió parecía sencilla y efectiva: cortar el cable con la señal de audio y con un relé cerrar el contacto solamente cuando el motor recibe tensión y se mueve. Todo de lo más simple.
Pero apareció un problema. Cuando acaba la partida y se agota el tiempo, el motor para, pero la locución sonora tarda unos segundos con una despedida: "Hasta luego amigos". Que queda cortada por un par de segundos y un efecto muy chapucero: "Hasta lue..."
Así que nada. Se compra un relé con retardo a la desconexión y punto. Pero en los distribuidores de nuestra pequeña ciudad solo encontramos cosas sofisticadas que nos permitían retardos a la conexión y a la desconexión y con rangos amplios pero que costaban más de 40 euros... Así que hemos decidido tirar un poco cabeza y entretenernos con lo que habia por el taller.


DESARROLLO DE LA SOLUCIÓN: 

 Para retrasar un poco la desconexión del relé habría que aportarle corriente a la bobina un rato después de haber desaparecido la tensión que activa el motor y que es la que en un principio activa nuestro relé. Esto sugiere un condensador gordo que se cargue mientras hay tensión y luego se descargue cuando esta desaparezca. Así que debería estar en paralelo con la bobina del relé como si fuese una batería.


Idealizando. Primera aproximación. Retardo a la desconexión.
Para que la carga del condensador no sea instantánea ni con corrientes grandes deberíamos pensar en alguna resistencia. Y por lo tanto un circuito RC y la constante de tiempo τ (tau). El tiempo de carga será cinco veces esa constante.


Idealizando. Segunda aproximación. Retardo a la desconexión.
Claro que, para que el condensador cargue y no se descarge hasta que falte el aporte de la tensión tenemos que pensar en corriente continua. Y por lo tanto rectificaremos con un puente de diodos. He probado con un diodo solo como rectificador de media onda, pero la resistencia se calienta más. Durante esa media onda que no aprovechamos, el condensador descarga y la resistencia trabaja más. Así que mejor rectificamos la onda completa. 

Idealizando. Tercera aproximación. Retardo a la desconexión.

Para conseguir el circuito definitivo solo nos queda pensar en las polaridades del condensador. Para usar capacidades grandes, usaremos uno electrolítico, que no puede ser polarizado al revés. Y por si acaso, para evitar la corriente de rebote inducida en la bobina del relé al desconectarse, colocaremos el diodo de protección correspondiente. 


Relay. Off delay with RC
Idea definitiva para hacer relé con retardo a la desconexión con circuito RC.
Ahora a hacer pruebas reales:
El condensador ha de soportar los 220v con holgura. Tengo uno de 400v, y 220μF.

La descarga ha de mantenernos el relé unos segundos (dos o tres). El relé es de 220v y mantiene el contacto con bastante menos tensión. Pero no aguantará hasta que el condensador descargue del todo. Pensemos en unos cinco o seis segundos.
T=5τ
T=5RC
R=T/5C
R(ohm)=6segundos/5*0,000220Faradios=5454Ω.

Buscamos resistencias de más de 6KΩ. Y para calcular la potencia pensamos en el caso peor. Durante un momento soportará los 220v. 
W=V^2/R=220^2/6000=8,06w
Por el taller no encuentro de esa potencia, pero tengo tres de 2w de 22000 ohmios. En paralelo, unos 7300Ω. Y aguantarían unos 6w.
W=V^2/R=220^2/7300=6,6w

 
Testing. Relay. Off delay.
Probando. Retardo a la desconexión de un relé.


Vamos un poco escasos, pero probamos. No se calientan y todo sale. Conseguimos un retraso de unos 3-4 segundos perfectos para nuestro proposito.
 
Probando. Retardo a la desconexión sobre aparato real.


 Así que montamos y resolvemos un montaje para dejar definitivamente. 


Montaje definitivo. Relé con retardo a la desconexión.

El aparato en cuestión lleva casi un año funcionando sin problemas. 


Montaje y colocación final. Relé con retardo a la desconexión con circuito RC.








15 septiembre 2015

PinoLed. PINO DE NAVIDAD CON LED. Estudio de multiplexado de leds y sonido con Arduino.

(EN) LedTree. LED CHRISTMAS TREE. Study about sound and leds multiplexing with Arduino.
(FR) ArbreLed. ARBRE DE NOËL AVEC LEDS. Étude de multiplexage de leds et son avec Arduino.
(PT) ÁrvoreLed. ÁRVORE DE NATAL COM LEDS. Estudo de multiplexagem de led e som com Arduino.
------------------------------------------------------------------------------------------------------------------------------------ 


PARA REPASAR E INFORMARSE: Para este montaje usaré un Arduino Uno. Hay un montón de información en la web. Aquí un enlace a su página oficial. 

Básicamente se trata de manejar leds, así que es interesante tener claro como se polariza un led. En este blog ya se ha tratado el tema.

Mi idea ha sido con transistores como conmutadores. Así que es interesante tener una idea simple de como funcionan en corte y en saturación. Hay mucha información en la web. Aquí hay un ejemplo que me ha gustado. Más concretamente uso un array de transistores, el integrado ULN2003.



PLANTEAMIENTO INICIAL  Y MOTIVACIÓN DEL PROYECTO.

Con la proximidad de la navidad, allá por noviembre del 2014, comentamos  entre amiguetes makers, el montar un árbol de navidad con arduino. En principio, la idea de montar unos leds sobre una plaquita pcb en forma de pino, y encender y apagar, me pareció algo demasiado sencillo. Pero al poco tiempo caí en la idea de algo interesante:
Arduino tiene un número relativamente limitado de entradas y salidas (yo tengo un Arduino Uno; 14 digitales y 6 analógicas). Estas no pueden soportar más de 40mA cada una, y mejor no pasar de 20mA. Y en total no más de 300mA, todas juntas. Pensemos, intentando no pasar de 150 o 200mA, por si acaso.
Así que, imposible usar directamente más de 20 leds, y esta cantidad, con mucho cuidado.
¿Cómo hacer, entonces, para manejar más? Pues montarlos en una matriz, y no encenderlos todos al mismo tiempo. Una matriz que habrá que controlar continuamente filas y columnas y que si además sincronizamos con una musiquilla se convierte (para mi nivel al menos) en un ejercicio interesante de sincronismo y programación.
Además el propósito va a ser diseñar y montar yo el circuito. Activar filas y columnas parece una cosa factible con transistores...



MATRIZ. FILAS Y COLUMNAS.

En una red o matriz de leds podemos conectar una serie de filas a unas salidas del arduino, y una serie de columnas a otras. Si ponemos a nivel alto determinada salida alimentaremos positivamente toda una linea de leds (en nuestro dibujito las filas). De esos leds se encederán los que tengan su cátodo también a negativo o nivel bajo (en nuestro caso, la columna apropiada). Solo debemos cuidarnos de no activar más de una linea de leds al mismo tiempo para que no interfieran unas lineas con otras.

leds matrix
Graficamente se entiende mejor la activación o no de los leds en una matriz

En nuestro ejemplo (con lógica positiva), si activáramos a nivel alto dos filas, el activar una columna a nivel bajo encendería los dos leds de las diferentes filas. El truco es activar solo una fila, y las columnas apropiadas un instante y luego hacer lo mismo con otra fila. Todo lo suficientemente rápido como para que nuestro ojo no se de cuenta.



LIMITACIÓN DE LA CORRIENTE. Y USO DE TRANSISTORES.

Cada led debería tener una resistencia que limitase su corriente para que no se queme.
Y para no consumir demasiada corriente de las salidas de nuestro arduino, la primera idea es usar transistores que aislen las salidas del arduino respecto de la intensidad que consuman nuestros leds.
Así mi primera idea ha sido polarizar un par de transistores, que según estén sus bases, a nivel alto conducirán, o a nivel bajo estarán en corte.

Switch on led with transistors
El transistor en corte nos aisla de masa y conecta con tensión a través de la resistencia. En saturacíón conduce y nos conecta directamene a masa.



Una vez, así presentado el esquema,  es fácil caer en la cuenta de que podemos sacar la resistencia inferior y dejar el transistor conectado a colector abierto. Sólo nos interesa que el cátodo del diodo se conecte a masa o no.
Y de esta forma, la única resistencia que queda será la que nos limita la corriente por el diodo, de forma elemental.

Two transistors for a led
Cuando el led conduce e ilumina, el transistor superior "aisla" la masa, y el inferior la "une" por lo que el cirucuito se convierte en un sencillo "resistencia en serie con led".




EL INTEGRADO ULN2003A PARA NUESTRO MONTAJE.

 El integrado ULN2003A es un conjunto de resistencias, transistores y diodos. Se compone de siete circuitos iguales que en las hojas de características suelen venir representados como una puerta lógica NOT. Es básicamente un adaptador de potencia. Por la entrada nosotros introducimos una señal lógica TTL. Unas resistencias polarizan y atacan la base de un transistor tipo darlington (dos conectados para aumentar potencia) y esto nos da una salida negada (a masa si la entrada está a nivel alto, a colector de los transistores si la entrada es a nivel bajo)por la que puede pasar mucha más intensidad que por nuestros pines del arduino. Para simplificar y pensar electrónicamente tenemos nuestro transistor básico (pudiéndonos despreocupar de las adaptaciones de la señal ttl de nuestro arduino): un 0 o nivel bajo de nuestro arduino, nos pone a "nuestro" transistor en corte (o abierto colector-emisor) y por tanto aislado de masa; un 1 o nivel alto provocará a "nuestro" transistor a saturación (o cerrado colector-emisor) y por tanto conectado a masa.
También existen unos diodos, con un contacto en común, que no usaremos aquí, pensados para poner con selenoides, para cortocircuitar los transitorios de desconexión de las bobinas, generalmente de relés. Aquí no hay de eso, así que no los usamos.


PRUEBAS Y DISEÑO. 

Pues con estas ideas hacemos unas pruebas, para ver que nuestras ideas eléctricas funcionan. Y corregir errores y decisiones iniciales como la patilla común de los leds RGB, valores de las resistencias, etc.


LLevando a la práctica la teoría de nuestro PinoLed.


Y todas las conclusiones se han conviertido en el siguiente esquema definitivo

LedTree Diagram
Aquí está como ha quedado el esquema definitivo de mi PinoLed.

VALOR DE LAS RESISTENCIAS.

La corriente que circulará por la carga más importante (leds) la "recojo" por Vin y no por 5V por dos razones: para liberar de trabajo al regulador de tensión; y pensando en poder conectar con baterías diferentes y hacer que los leds alumbren más o menos.
Pensando, además, que hay leds de diferentes colores y tipos (finalmente he optado también por jugar con un par de leds RGB), y que éstos tienen diferentes umbrales de tensión a partir de los cuales conducen, la elección del valor de la resistencia limitadora de corriente ha sido un poco a prueba y error.
Originalmente, en el prototipo sobre protoboard he empezado con resistencias de 1K. Pensaba en una de las opciones menos favorables: 20v de entrada (Vin) y led rojo (tensión umbral aproximada 1,8v según pruebas propias) y curándome un poco en salud con la corriente (I diodo máx.<20ma aria-haspopup="true" class="goog-spellcheck-word" data-blogger-="" id=":1b.167" role="menuitem" span="" style="background: yellow;" tabindex="-1">escaped
-br="">
R=(Vin-Vd)/Idmax=(20-1,8)/0,020=910 ohmios.
Una vez, en fase de programación del Arduino, al multiplexar, el brillo de los leds baja notablemente porque la tensión sólo se aplica a cada linea 1/6 parte del tiempo. Por lógica he bajado el valor de las resistencias 1/6 parte (1000/6=166,66). Por seguridad mejor 180 que 150 ohmios.


DETALLES Y RESULTADOS DE LA CONSTRUCCIÓN.
Con la construcción no quería gastar demasiado dinero. Se trataba de un ejercicio. Así que he optado por cartulina y leds pequeños de 3mm. La circuitería está montada sobre un escudo casero del que ya he hablado en otra entrada. Hubiera sido más vistoso y luminoso usar leds grandes. Seguro que hay ideas mucho más trabajadas, pero esta es la mía...
En mi montaje, en el lugar del potenciometro para el volumen he puesto un puente, optando así por la potencia máxima, pero esto puede ser molesto para algunas personas así que indico la posibilidad de regular el volumen. Como altavoz uso un buzzer pasivo.
En la foto se puede ver, todavía sin acabar, mi idea original, con una pila de 9v. Pero, aunque válida, no ha resultado ser una opción demasiado buena. Esto requiere una interesante explicación que comentaré en los siguientes párrafos. De momento unas fotos para hacerse una idea:
 
LedTree assembly
Idea del proceso de montaje y detalles del PinoLed



PinoLed Photo
Una foto del PinoLed durante las pruebas




Y el video, un poco cutre (también en YouTube), da una pequeña muestra del resultado final de mi ejercicio, bautizado como PinoLed.



APUNTES SOBRE LA PROGRAMACIÓN.

También dejo por aquí a la vista mi programa del arduino. Probablemente muy simple y poco elegante para los muy iniciados. Pero espero que útil para aprender a los que, como yo, no lo son tanto.  Fue desarrollado y probado con un Arduino Uno R3 y con el IDE de programación 1.6.1.
Básicamente se trata de la función "Visualizar()" que está constantemente mostrando el brillo de cada led que la matriz "diodoFilCol[7][7]" guarda en sus índices (0 apagado, 255 brillo máximo).
Estos valores del brillo de cada led se van modificando según una "secuencia()" temporal para que coincida con la música también definida en esta función.
Para que no sea demasiado repetitivo, de forma medio chapucera (en honor al nombre del blog), he creado un par de posibles estados en donde, sin música, las luces hacen un par de efectos aleatorios: uno jugando con encender y apagar leds al azar; otro jugando con subir y bajar el brillo de diferentes colores. De manera más o menos accidental puede volverse al estado inicial de secuencia leds-música, y si no, al cabo de un tiempo mínimo,  de manera forzada (establecido ahora para las pruebas en 60 segundos) .
Otras funciones organizan de forma predefinida efectos simples de luces para simplificar la repetición en la programación.

Por si alguien se anima a probar, dejo aquí una copia del archivo .ino definitivo comprimido en zip.

Y para quien solo quiera curiosear y echar un vistazo rápido, aquí está el código.




////////////////////////////////////////////////
// Para hacer funcionar PinoLed               //
// Matriz de 36 leds y un buzzer              //
// http//:electronicaychapuzas.blogspot.com.es//
// by Sancos v1.1                             //
////////////////////////////////////////////////

byte diodoFilCol[7][7];   //definira estado diodos:0 apagado 255 encendido
// fila y columna define que pines uso.No usamos fila0 ni columna0
byte fila[7]={255,15,14,2,4,7,8};     //No usare fila0. 14=A0, 15=A1
byte columna[7]={255,3,5,6,9,10,11};  //No usare columna0
//Podría usar pwm por comodidad. Por eso utilizo estas salidas.
//Pero para poder usar Tone() sin problemas no usaré PWM
//Ademas, después de montado el circuito, he caido en que hubiera
//sido mas interesante el PWM en las filas.
int v=2; //Velocidad tempo
int i,c,n,f,luz;   //Contadores uso general
int estado; //Que estamos haciendo? 0-inicio; 1-secuencia;
unsigned long tiempo0,tiempo;   //Contadores tiempo
short Do2,Si2,Do3,Re3,Mi3,Fa3,Sol3,La3,Si3,Do4;  //Notas que vamos a usar
void setup()
{
  // Filas. Activas a nivel bajo. Reposo a nivel alto.
  for (f=1; f<7; f=f+1)
  {
    pinMode(fila[f],OUTPUT);
    digitalWrite(fila[f],HIGH);
  }
  
  // Audio implementado por pin 12.
  //señal de audio. Reposo a nivel bajo
  pinMode(12,OUTPUT);
  digitalWrite(12,LOW);
  
  // Notas
    Do2=130.813;
    Si2=246.942;
    Do3=261.626;
    Re3=293.665;
    Mi3=329.628;
    Fa3=349.228;
    Sol3=391.995;
    La3=440;
    Si3=493.883;
    Do4=523.251;
  
  //Columnas. Activas a nivel alto. Reposo a nivel bajo.
  for (c=1; c<7; c=c+1)
  {
    pinMode(columna[c],OUTPUT);
    digitalWrite(columna[c],LOW);
  }
  
  //Inicializamos el estado de cada led a apagado.
  //La matriz diodoFilCol define cada diodo
  // desde 0 (apagado) a 255 (brillo maximo)
  for (f=1; f<7; f=f+1)
  {
    for (c=0; c<7; c=c+1)
    {
      diodoFilCol[f][c]=0;
    }
    
 }
 
  tone(12,440,1000);   //Test al inicio
  delay(5000);  //Esperamos unos segundos antes de arrancar
  tiempo0=millis();   //Tomamos tiempo inicial para temporizaciones
  estado=1;
  
}



void loop()
{
  if (estado==0) Test();  //Test al inicio
  if (estado==1) Secuencia();  //Secuencia programada con musica
  if (estado==2) Luces();  //Luces al azar y silencio
  if (estado==3) Rgb();
  Visualizar();
}

// Visualizar la matriz diodoFilCol.
void Visualizar()
{
  for (c=1; c<7; c=c+1)  //Repetir con las seis columnas
  {
  digitalWrite(columna[c],HIGH);  //Activar columna
    for (n=1; n<255; n=n+4)   //Pasos lo mas cortos posible sin que parpadeen los leds
    {
      for (f=1; f<7; f=f+1)  //Activar  las filas según brillo en matriz
      {
        //Activamos o no más veces según numero en matriz mas alto
        if ((255-n)<(diodoFilCol[f][c])) digitalWrite(fila[f],LOW);   
        else digitalWrite(fila[f],HIGH);     
      }
    }
    digitalWrite(columna[c],LOW);  //Desactivar columna
    
    for (f=1; f<7; f=f+1)    //Desactivar filas
    {
      digitalWrite(fila[f],HIGH);
    } 
  }
}




/********************/
/* Efectos de luces */
/********************/


// Apagar todos los leds (0), o encender(255)
void Todos(int brillo)
{
  
   for (f=0; f<7; f=f+1)
  {
    for (c=0; c<7; c=c+1)
    {
      diodoFilCol[f][c]=brillo;
    }
    
  }
}


// Iluminar un led en concreto.
void Led(int fil,int col,int brillo)
{
  diodoFilCol[fil][col]=brillo;
  
}


// Iluminar una columna en concreto.
void Columna(int col, int brillo)
{
  for (c=0;c<7;c=c+1)
  {
    diodoFilCol[c][col]=brillo;
  }
}


//Iluminar una fila en concreto.
void Fila(int fil, int brillo)
{
  for (c=0;c<7;c=c+1)
  {
    diodoFilCol[fil][c]=brillo;
  }
}



//Iluminar todos rojos
void Rojos(int brillo)
{
  Led(1,1,brillo);
  Led(1,4,brillo);
  Led(2,3,brillo);
  Led(2,6,brillo);
  Led(3,2,brillo);
  Led(3,5,brillo);
  Led(4,2,brillo);
  Led(4,5,brillo);
  Led(5,2,brillo);
  Led(5,5,brillo);
  Led(6,2,brillo);
  Led(6,5,brillo);
}

//Iluminar todos verdes
void Verdes(int brillo)
{
  Led(1,2,brillo);
  Led(1,5,brillo);
  Led(2,2,brillo);
  Led(2,5,brillo);
  Led(3,3,brillo);
  Led(3,6,brillo);
  Led(4,3,brillo);
  Led(4,6,brillo);
  Led(5,3,brillo);
  Led(5,6,brillo);
  Led(6,3,brillo);
  Led(6,6,brillo);
}

//Iluminar todos amarillos
void Amarillos(int brillo)
{
  //Para amarillo 180 de rojo y 255 de verde
  //porque el led rojo alumbra más con menos tension
  int correccionbrillo;
  correccionbrillo=((brillo*180)/255);
  
  Led(1,1,correccionbrillo); Led(1,2,brillo); Led(1,3,0);
  Led(1,4,correccionbrillo); Led(1,5,brillo); Led(1,6,0);
  Led(2,1,brillo);
  Led(2,4,brillo);
  Led(3,1,brillo);
  Led(3,4,brillo);
  Led(4,1,brillo);
  Led(4,4,brillo);
  Led(5,1,brillo);
  Led(5,4,brillo);
  Led(6,1,brillo);
  Led(6,4,brillo);
}


void AzarLed(int cuantas, int pausa)  //Cuantas luces encendemos; cada una  pausa ms
{                                  // tiempo aprox. funcion = cuantas * pausa
  long int filaazar, columnaazar;
  Todos(0);
  randomSeed(analogRead(3)); //El pin A3 está al aire
  for (i=0; i<cuantas; i=i+1);
  {
    filaazar=random(1,7);
    columnaazar=random(1,7);
    Led(filaazar,columnaazar,255);
    delay(pausa);
  }
}

void AzarFila(int cuantas, int pausa) //Cuantas filas encendemos cada pausa ms
{                                     //tiempo aprox.funcion=cuantas*pausa
  long int filaazar;
  Todos(0);
  randomSeed(analogRead(3));  //A3 al aire
  for (i=0; i<cuantas; i=i+1);
  {
    filaazar=random(1,7);
    Fila(filaazar,255);
    delay(pausa);
  }
}

void AzarColumna(int cuantas, int pausa) //Cuantas columnas encendemos cada pausa ms
{                                     //tiempo aprox.funcion=cuantas*pausa
  long int columnaazar;
  Todos(0);
  randomSeed(analogRead(3));  //A3 al aire
  for (i=0; i<cuantas; i=i+1);
  {
    columnaazar=random(1,7);
    Columna(columnaazar,255);
    delay(pausa);
  }
}


/***********************/
/*     Secuencias      */
/***********************/

void Test()
{
  tiempo=(millis()-tiempo0);
  
    if (tiempo<1000)
      {
      Todos(255);
      }
    if ((tiempo>=1000)&&(tiempo<2000))
      {
      Todos(0);  
      Led(1,1,255); Led(1,4,255); //Rojo
      }
    if ((tiempo>=2000)&&(tiempo<3000))
      {
      Todos(0);
      Led(1,1,0); Led(1,4,0);  
      Led(1,2,255); Led(1,5,255);  //Verde
      }
    if ((tiempo>=3000)&&(tiempo<4000))
      {
      Todos(0);  
      Led(1,2,0); Led(1,5,0);  
      Led(1,3,255); Led(1,6,255);  //Azul
      }
    if ((tiempo>=4000)&&(tiempo<4500))
      {
      Todos(0);  
      Led(1,3,200); Led(1,6,200); //Apagando azul
      Led(1,5,5); //Encendiendo verde
      }
    if ((tiempo>=4500)&&(tiempo<5000))
      {
      Todos(0);  
      Led(1,3,100); Led(1,6,100); //Apagando azul
      Led(1,15,5); //Encendiendo verde
      }
    if ((tiempo>=5000)&&(tiempo<5500))
      {
      Todos(0);  
      Led(1,3,50); Led(1,6,50); //Apagando azul
      Led(1,5,50); //Encendiendo verde
      }
    if ((tiempo>=5500)&&(tiempo<6000))
      {
      Todos(0);  
      Led(1,3,25); Led(1,6,25); //Apagando azul
      Led(1,5,100); //Encendiendo verde
      }  
    if ((tiempo>=6000)&&(tiempo<6500))
      {
      Todos(0);  
      Led(1,3,15); Led(1,6,15); //Apagando azul
      Led(1,5,200); //Encendiendo verde
      }
    if ((tiempo>=6500)&&(tiempo<7000))
      {
      Todos(0);  
      Led(1,3,0); Led(1,6,0); //Apagando azul
      Led(1,5,255); //Encendiendo verde
      }
      
    if ((tiempo>=7000))
      {
      estado=1;
      tiempo0=millis();
      }
}


//Luces al azar para reposo
void Luces()
{
  long int quehacer,fil,col;
  randomSeed(analogRead(3));  //A3 al aire
  quehacer=random(1,100);
  fil=random(1,7);
  col=random(1,7);
  if (quehacer<3) Todos(0);
  if ((quehacer>=5)&&(quehacer<45)) Led(fil,col,255); 
  if ((quehacer>=45)&&(quehacer<85)) Led(fil,col,0);
  if (quehacer==85) estado=3; //ejecuta Rgb()
  if ((quehacer>85)&&(estado<89)) Fila(fil,255);
  if ((quehacer>89)&&(estado<94)) Columna(col,255);
  if ((quehacer>99)) { estado=1; tiempo0=millis(); }
  if ((millis()-tiempo0)>60000){ estado=1; tiempo0=millis(); } //aqui aseguro musica cada 60 segundos
}

//Led de colores
void Rgb()
{
  long int quehacer; //luz interesa global
  randomSeed(analogRead(3));  //A3 al aire
  quehacer=random(1,100);
  if (quehacer>60) luz=luz+1;
  if (luz>250) luz=luz-1;
  if ((luz>200)&&(quehacer>95)) luz=0;
  if (quehacer>98) {luz=0; estado=2;} //volver a luces()
  if (quehacer<33) Rojos(luz);
  if ((quehacer>33)&&(quehacer<66)) Verdes(luz);
  if ((quehacer>66)&&(quehacer<98)) Amarillos(luz);
  if ((quehacer>99)&&(luz>200)) { estado=1; tiempo0=millis(); }
}

//Programar secuencia a repetir

void Secuencia()

{
  tiempo=(millis()-tiempo0);
  
  /***************    MUSICA     ******************/
  //Compas1
    if (tiempo<(500/v))
      {
      if (tiempo<(400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(500/v))&&(tiempo<(1000/v)))
      {
      if (tiempo<(900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(1000/v))&&(tiempo<(2000/v)))
      {
      if (tiempo<(1750/v)) tone(12,Mi3,(100/v));
      }
      
      
    if ((tiempo>=(2000/v))&&(tiempo<(2500/v)))
      {
      if (tiempo<(2400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(2500/v))&&(tiempo<(3000/v)))
      {
      if (tiempo<(2900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(3000/v))&&(tiempo<(4000/v)))
      {
      if (tiempo<(3750/v)) tone(12,Mi3,(100/v));
      }
      
      
    //Compas2   
    if ((tiempo>=(4000/v))&&(tiempo<(4500/v)))
      {
      if (tiempo<(4400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(4500/v))&&(tiempo<(5000/v)))
      {
      if (tiempo<(4900/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(5000/v))&&(tiempo<(5750/v)))
      {
      if (tiempo<(5600/v)) tone(12,Do3,(100/v));
      }
    if ((tiempo>=(5750/v))&&(tiempo<(6000/v)))
      {
      if (tiempo<(5900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(6000/v))&&(tiempo<(8000/v)))
      {
      if (tiempo<(6500/v)) tone(12,Mi3,(100/v));
      }  
      
      
    //Compas3
    if ((tiempo>=(8000/v))&&(tiempo<(8500/v)))
      {
      if (tiempo<(8400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(8500/v))&&(tiempo<(9000/v)))
      {
      if (tiempo<(8900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(9000/v))&&(tiempo<(9750/v)))
      {
      if (tiempo<(9700/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(9750/v))&&(tiempo<(10000/v)))
      {
      if (tiempo<(9900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(10000/v))&&(tiempo<(10500/v)))
      {
      if (tiempo<(10400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(10500/v))&&(tiempo<(11000/v)))
      {
      if (tiempo<(10900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(11000/v))&&(tiempo<(11500/v)))
      {
      if (tiempo<(11400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(11500/v))&&(tiempo<(11750/v)))
      {
      if (tiempo<(11700/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(11750/v))&&(tiempo<(12000/v)))
      {
      if (tiempo<(11900/v)) tone(12,Mi3,(100/v));
      } 
     
     
   //Compas4
    if ((tiempo>=(12000/v))&&(tiempo<(12500/v)))
      {
      if (tiempo<(12400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(12500/v))&&(tiempo<(13000/v)))
      {
      if (tiempo<(12900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(13000/v))&&(tiempo<(13500/v)))
      {
      if (tiempo<(13400/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(13500/v))&&(tiempo<(14000/v)))
      {
      if (tiempo<(13900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(14000/v))&&(tiempo<(15000/v)))
      {
      if (tiempo<(14900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(15000/v))&&(tiempo<(15750/v)))
      {
      if (tiempo<(15700/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(15750/v))&&(tiempo<(16000/v)))
      {
      if (tiempo<(15700/v)) tone(12,Do2,(100/v));
      }
     
     
    //Compas5
    if ((tiempo>=(16000/v))&&(tiempo<(16500/v)))
      {
      if (tiempo<(16400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(16500/v))&&(tiempo<(17000/v)))
      {
      if (tiempo<(16900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(17000/v))&&(tiempo<(18000/v)))
      {
      if (tiempo<(17750/v)) tone(12,Mi3,(100/v));
      }
      
      
    if ((tiempo>=(18000/v))&&(tiempo<(18500/v)))
      {
      if (tiempo<(18400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(18500/v))&&(tiempo<(19000/v)))
      {
      if (tiempo<(18900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(19000/v))&&(tiempo<(20000/v)))
      {
      if (tiempo<(19750/v)) tone(12,Mi3,(100/v));
      }
      
     
    
    //Compas6
     if ((tiempo>=(20000/v))&&(tiempo<(20500/v)))
      {
      if (tiempo<(20400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(20500/v))&&(tiempo<(21000/v)))
      {
      if (tiempo<(20900/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(21000/v))&&(tiempo<(21750/v)))
      {
      if (tiempo<(21700/v)) tone(12,Do3,(100/v));
      }
    if ((tiempo>=(21750/v))&&(tiempo<(22000/v)))
      {
      if (tiempo<(21900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(22000/v))&&(tiempo<(24000/v)))
      {
      if (tiempo<(22500/v)) tone(12,Mi3,(100/v));
      }  
     
      
    //Compas7
    if ((tiempo>=(24000/v))&&(tiempo<(24500/v)))
      {
      if (tiempo<(24400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(24500/v))&&(tiempo<(25000/v)))
      {
      if (tiempo<(24900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(25000/v))&&(tiempo<(25750/v)))
      {
      if (tiempo<(25700/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(25750/v))&&(tiempo<(26000/v)))
      {
      if (tiempo<(25900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(26000/v))&&(tiempo<(26500/v)))
      {
      if (tiempo<(26400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(26500/v))&&(tiempo<(27000/v)))
      {
      if (tiempo<(26900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(27000/v))&&(tiempo<(27500/v)))
      {
      if (tiempo<(27400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(27500/v))&&(tiempo<(27750/v)))
      {
      if (tiempo<(27700/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(27750/v))&&(tiempo<(28000/v)))
      {
      if (tiempo<(27900/v)) tone(12,Mi3,(100/v));
      } 
      
      
    //Compas8
    if ((tiempo>=(28000/v))&&(tiempo<(28500/v)))
      {
      if (tiempo<(28400/v)) tone(12,La3,(100/v));
      } 
    if ((tiempo>=(28500/v))&&(tiempo<(29000/v)))
      {
      if (tiempo<(28900/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(29000/v))&&(tiempo<(29500/v)))
      {
      if (tiempo<(29400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(29500/v))&&(tiempo<(30000/v)))
      {
      if (tiempo<(29900/v)) tone(12,Re3,(100/v));
      } 
    if ((tiempo>=(30000/v))&&(tiempo<(30500/v)))
      {
      if (tiempo<(30400/v)) tone(12,Do3,(100/v));
      }  
    if ((tiempo>=(30500/v))&&(tiempo<(32000/v)))
      {
      if (tiempo<(31900/v)) noTone(12);
      }
    
    
    
  /*****************   LUCES    *******************/
  //Compas1
    if (tiempo<(500/v))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(500/v))&&(tiempo<(1000/v)))
      {
        //Mi3
        Todos(0);
        Amarillos(255);
      }
    if ((tiempo>=(1000/v))&&(tiempo<(2000/v)))
      {
        //Mi3
        Todos(0);
        Verdes(255);
      }
      
      
    if ((tiempo>=(2000/v))&&(tiempo<(2500/v)))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(2500/v))&&(tiempo<(3000/v)))
      {
        //Mi3
        Amarillos(255);
      }
    if ((tiempo>=(3000/v))&&(tiempo<(4000/v)))
      {
        //Mi3
        Verdes(255);
      }
      
      
  //Compas2   
    if ((tiempo>=(4000/v))&&(tiempo<(4500/v)))
      {
        //Mi3
        Fila(1,0);
        Fila(2,0);
      }
    if ((tiempo>=(4500/v))&&(tiempo<(5000/v)))
      {
        //Sol3
        Fila(3,0);
      }
    if ((tiempo>=(5000/v))&&(tiempo<(5750/v)))
      {
        //Do3
        Fila(4,0);
      }
    if ((tiempo>=(5750/v))&&(tiempo<(6000/v)))
      {
        //Re3
        Fila(5,0);
      }
    if ((tiempo>=(6000/v))&&(tiempo<(6500/v)))
      {
        //Mi3
        Fila(6,0);
      }  
    if ((tiempo>=(6500/v))&&(tiempo<(7000/v)))
      {
        //Silencio
        Led(1,6,255);
      }
    if ((tiempo>=(7000/v))&&(tiempo<(7500/v)))
      {
        //Silencio  
      } 
    if ((tiempo>=(7500/v))&&(tiempo<(8000/v)))
      {
        //Silencio
        Led(1,6,0);
        Led(1,4,180); Led(1,5,255);
      }    
  
      
      
  //Compas3
    if ((tiempo>=(8000/v))&&(tiempo<(8500/v)))
      {
        //Fa3
        Todos(0);
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(8500/v))&&(tiempo<(9000/v)))
      {
        //Fa3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
        Fila(1,0);Fila(2,0); 
      }
    if ((tiempo>=(9000/v))&&(tiempo<(9500/v)))  //Varia de nota
      {
        //Fa3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
        Fila(1,0);Fila(2,0); 
      }
    if ((tiempo>=(9500/v))&&(tiempo<(10000/v)))  //Varia de nota
      {
        //Fa3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(10000/v))&&(tiempo<(10500/v)))
      {
        //Fa3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(10500/v))&&(tiempo<(11000/v)))
      {
        //Mi3
        Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(11000/v))&&(tiempo<(11500/v)))
      {
        //Mi3
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(11500/v))&&(tiempo<(11750/v)))
      {
        //Mi3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(11750/v))&&(tiempo<(12000/v)))
      {
        //Mi3
      
      } 
     
     
 //Compas4
    if ((tiempo>=(12000/v))&&(tiempo<(12500/v)))
      {
        //Mi3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(12500/v))&&(tiempo<(13000/v)))
      {
        //Re3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);  
      }
    if ((tiempo>=(13000/v))&&(tiempo<(13500/v)))
      {
        //Re3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0); 
      }
    if ((tiempo>=(13500/v))&&(tiempo<(14000/v)))
      {
        //Mi3
        Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(14000/v))&&(tiempo<(15000/v)))
      {
        //Re3
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
        
        
      }
    if ((tiempo>=(15000/v))&&(tiempo<(15750/v)))
      {
        //Sol3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
        Fila(1,0);Fila(2,0);
        Led(1,1,255);
      }
    if ((tiempo>=(15750/v))&&(tiempo<(16000/v)))
      {
        //Do2
        Todos(0);
        Led(1,1,255);
      }
     
     
  //Compas5
    if ((tiempo>=(16000/v))&&(tiempo<(16500/v)))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(16500/v))&&(tiempo<(17000/v)))
      {
        //Mi3
         Todos(0);
        Amarillos(255);
      }
    if ((tiempo>=(17000/v))&&(tiempo<(18000/v)))
      {
        //Mi3
        Todos(0);
        Verdes(255);
      }
      
      
    if ((tiempo>=(18000/v))&&(tiempo<(18500/v)))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(18500/v))&&(tiempo<(19000/v)))
      {
        //Mi3
         Amarillos(255);
      }
    if ((tiempo>=(19000/v))&&(tiempo<(20000/v)))
      {
        //Mi3
        Verdes(255);
      }
      
     
    
    //Compas6
     if ((tiempo>=(20000/v))&&(tiempo<(20500/v)))
      {
        //Mi3
        Fila(6,0);
      }
    if ((tiempo>=(20500/v))&&(tiempo<(21000/v)))
      {
        //Sol3
        Fila(5,0);
      }
    if ((tiempo>=(21000/v))&&(tiempo<(21750/v)))
      {
        //Do3
        Fila(4,0);
      }
    if ((tiempo>=(21750/v))&&(tiempo<(22000/v)))
      {
        //Re3
        Fila(3,0);
      }
    if ((tiempo>=(22000/v))&&(tiempo<(24000/v)))
      {
        //Mi3
        Fila(2,0);
        Fila(1,0);
      }  
     
      
    //Compas7
    if ((tiempo>=(24000/v))&&(tiempo<(24500/v)))
      {
        //Fa3
        Todos(0);
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(24500/v))&&(tiempo<(25000/v)))
      {
        //Fa3
        Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(25000/v))&&(tiempo<(25500/v)))  //No coincide con nota
      {
        //Fa3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(25500/v))&&(tiempo<(26000/v)))  //No coincide con nota
      {
        //Fa3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(26000/v))&&(tiempo<(26500/v)))
      {
        //Fa3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(26500/v))&&(tiempo<(27000/v)))
      {
         //Mi3
         Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(27000/v))&&(tiempo<(27500/v)))
      {
        //Mi3
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(27500/v))&&(tiempo<(27750/v)))
      {
        //Mi3
         Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(27750/v))&&(tiempo<(28000/v)))
      {
        //Mi3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0);
      } 
      
      
    //Compas8
    if ((tiempo>=(28000/v))&&(tiempo<(28500/v)))
      {
        //La3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);
      } 
    if ((tiempo>=(28500/v))&&(tiempo<(29000/v)))
      {
        //Sol3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(29000/v))&&(tiempo<(29500/v)))
      {
        //Fa3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(29500/v))&&(tiempo<(30000/v)))
      {
        //Re3
        Todos(0);
      } 
    if ((tiempo>=(30000/v))&&(tiempo<(30500/v)))
      {
        //Do3
        Todos(255);
      }  
    if ((tiempo>=(30500/v))&&(tiempo<(32000/v)))
      {
        //Tres silencios corchea
      if (tiempo<(31900/v)) noTone(12);
      }
    if (tiempo>=(32000/v))
      {
        tiempo0=millis();
        estado=2; //ejecuta Luces()
      }
}



INTERESANTE PROBLEMILLA NO PREVISTO CON PILA DE 9V Y EXCESIVO CONSUMO.

Al final, casi siempre hay algo que podría haber estado mejor pensado. Y aquí he caído en la cuenta, cuando ya todo estaba demasiado montado.
Mi idea original era alimentar todo con una pila de 9v oculta debajo del pino de cartulina. Y en principio todo parece bien pensado, pero haciendo pruebas y test durante la programación me he encontrado con dos detalles:

Con la batería  todo va bien unos minutos, pero enseguida el arduino se apaga y enciende (el regulador de 5v necesita más de 7 en Vin). Cosa que si alimento con el usb (aunque usamos menos tensión), todo parece ir mejor.

Y originalmente las resistencias eran de menos potencia pero se calentaban mucho más de lo deseable. Tuve que montarlas de más watios. Así que tenía que haber exceso de consumo por algún sitio...

Repensando sobre el esquema del circuito ya montado me he dado cuenta que cuando los leds están apagados porque la fila está desactivada, la resistencia limitadora conduce de Vin a masa.  Así que todas las lineas conducen y consumen corriente. Incluso más en reposo.
Esto, aunque no llega a los límites del arduino porque es a través de los transistores, sí que agota la batería demasiado rápido. Las baterías de 9v tampoco son una maravilla en capacidad de carga.
Y si pensamos en una alimentación alta para que los leds alumbren mejor. Más allá de los 15v nos vamos a consumos peligrosos de más de 500mA ((15v/180ohm)*6 resistencias=0,5A).

Así que la idea de alimentarlo con baterías funciona, pero nos saldrá cara. Habrá que fijarse más en esta cuestión para otros proyectos.

Pero en este punto ha quedado éste.