Archivos de la categoría ‘–Electrónica–’

El vúmetro es un dispositivo indicador en equipos de audio para mostrar el nivel de señal en unidades de volumen, también es llamado “indicador del volumen”.

Consta de un instrumento de bobina móvil o galvanómetro con una balística (amortiguamiento) determinada, alimentado por medio de un rectificador de onda completa que se conecta a la línea de audio mediante una resistencia en serie. No necesita más fuente de energía para su funcionamiento que la señal de entrada. Esencialmente permite visualizar las variaciones de la tensión en la señal de audio, rectificándola y obteniendo el valor medio. Este se obtiene por la balística del instrumento usado, no por una integración capacitiva. Lo que exige que el galvanómetro de un vúmetro no sea fabricado igual que otros tipos de medidores eléctricos (como los amperímetros, voltímetros, etc.) para conformar el estándar SVI (Standard Volume Indicator).

El vúmetro no fue diseñado para medir explícitamente la tensión de la señal, sino para que los usuarios tuvieran una referencia u objetivo de 0 VU, identificado como el 100% o 0 decibelios, en telefonía y en la modulación de los transmisores de la época, por lo que no era muy importante que el dispositivo no fuera extremadamente lineal o preciso para bajos niveles. En efecto, la escala de -20 VU a +3 VU, con 0 VU al 70% de la escala, fue limitado por la tecnología de la época. La mitad superior de la escala solo cubre 6 dB, permitiendo ajustar con precisión solo los niveles alrededor de 0 VU.

Se ha reemplazado en muchos equipos el tradicional vúmetro de aguja por indicadores luminosos con LEDs. Además del nivel ponderado, algunos vúmetros digitales también muestran los picos o máximos. Como regla general, los niveles de grabación deben ser tales que no superen el área roja más allá de 0 VU, o solo en raras ocasiones. Si el volumen de grabación es demasiado alto, la calidad del sonido y respuesta en frecuencia es generalmente más pobre y los efectos de saturación y recorte pueden ser especialmente problemáticos para un sistema de grabación digital. Por el contrario si el nivel es demasiado bajo, los niveles de ruido serán altos en relación a la señal principal que se está grabando.

Actualmente, la mayoría de las computadoras utilizan pantallas LED o LCD para indicar el nivel de sonido, por otro lado, dan un aspecto colorido y dramático.
Este proyecto consta de 6 visualizaciones diferentes que muestran gráficamente la señal de audio estéreo que ingresa a través de dos entradas analógicas del PIC.
Tiene múltiples aplicaciones, ya que podría ser un indicador de nivel de audio, control de nivel de líquido, voltímetro o amperímetro en una fuente de alimentación, indicador de temperatura, medición de una señal de RF, etc.

Datasheet PIC16F877A

El diagrama esquemático del vúmetro estéreo se observa en la siguiente imagen:

(click para ampliar)

Lista de Componentes
1 Microcontrolador PIC16F877A
1 Display LCD 16×2 con controlador Hitachi HD44780 o compatible
1 Cristal de Cuarzo 20MHz
2 Capacitores Cerámicos 27pF 50V
2 Capacitores Electrolíticos 1uF 63V
2 Diodos Rectificadores 1N4001
1 Trimpot horizontal multivuelta 10K
1 Resistencia 10KΩ 0.25W
1 Resistencia 4K7Ω 0.25W
2 Resistencias 330KΩ 0.25W
1 Pulsador N.A. o Push-Button N.A.

El código fuente del firmware del microcontrolador está escrito en el lenguaje PICBasic. Puede ser compilado sin ningún inconveniente utilizando la última versión del compilador PICBasic Pro Trial Version 3.0.7.1 y cargar el firmware en el PIC16F877A utilizando el programador PICKit 3 v3.01.

#CONFIG
        __config _HS_OSC & _WDT_OFF & _CP_OFF & _BOREN_OFF & _LVP_OFF

#ENDCONFIG

' *******************************************************************************
DEFINE OSC 20     ' Oscillator 20 MHz
' *******************************************************************************
ADCON1 = 00000000      ' Set all PORTA and PORTE as analog
PAUSE 500              ' Time to initiation of the LCD
DEFINE LCD_DREG PORTB  ' LCD data PORT B ships
DEFINE LCD_DBIT 4      ' LCD uses less than 0 or greater Bit 4
DEFINE LCD_RSREG PORTB ' LCD RS in the PORTB
DEFINE LCD_RSBIT 0     ' LCD RS PORTB.0
DEFINE LCD_EREG PORTB  ' LCD E in the PORTB
DEFINE LCD_EBIT 1      ' LCD E PORTB.1
DEFINE LCD_BITS 4      ' LCD using 4 or 8 bits for data
DEFINE LCD_LINES 2     ' Number of lines lcd
' *******************************************************************************
DEFINE ADC_BITS 8      ' Result of 8 or 10-bit ADC
DEFINE ADC_CLOCK 3     ' Set clock
DEFINE ADC_SAMPLEUS 50 ' Set sampling time in microseconds
' *******************************************************************************
CHANNEL var BYTE             ' Variable name
LINE    VAR BYTE             ' Variable name
COUNTER VAR BYTE             ' Variable name
REST    VAR BYTE             ' Variable name
Mark1   VAR BIT              ' Variable name
Mark2  var BYTE              ' Variable name
pushbutton    Var PORTB.2    ' button in the PORTB.2
TRISA =  %11111111
TRISB =  %00000100
TRISC =  %00000000

' *******************************************************************************
LCDOUT 254,64,1,2,5,5,5,5,2,1                      ' position CGRAM 0
LCDOUT 254,72,24,4,26,2,2,26,4,24                  ' 1 position CGRAM
LCDOUT $FE,1                                       ' Clear screen
' *******************************************************************************
GRAF1 :
Mark2 = 1
LCDOUT 254,64 , 0, 16, 16 , 16, 16 , 16, 16 , 0 ' 0 position CGRAM
LCDOUT 254,72 , 0, 24, 24 , 24, 24, 24 , 24, 0  ' 1 position CGRAM
LCDOUT 254,80 , 0, 28, 28 , 28, 28, 28 , 28, 0  ' CGRAM Position 2
LCDOUT 254,88 , 0, 30, 30 , 30, 30, 30 , 30, 0  ' 3 position CGRAM
LCDOUT 254,96 , 0, 31, 31 , 31, 31, 31 , 31, 0  ' 4 position CGRAM
PAUSE 300
GOTO BAR
' *******************************************************************************
GRAF2 :
Mark2 = 2
LCDOUT 254,64 , 0, 24, 24 , 24, 24, 24 , 24, 0 ' 0 position CGRAM
LCDOUT 254,72 , 0, 24, 24 , 24, 24, 24 , 24, 0 ' 1 position CGRAM
LCDOUT 254,80 , 0, 27, 27 , 27, 27, 27 , 27, 0 ' CGRAM Position 2
LCDOUT 254,88 , 0, 27, 27 , 27, 27, 27 , 27, 0 ' 3 position CGRAM
LCDOUT 254,96 , 0, 27, 27 , 27, 27, 27 , 27, 0 ' 4 position CGRAM
PAUSE 300
GOTO BAR
' *******************************************************************************
GRAF3 :
Mark2 = 3
LCDOUT 254,64 , 0, 0 , 0, 16, 16 , 0, 0 , 0 ' 0 position CGRAM
LCDOUT 254,72 , 0, 0 , 0, 24, 24 , 0, 0 , 0 ' 1 position CGRAM
LCDOUT 254,80 , 0, 0 , 0, 28, 28 , 0, 0 , 0 ' 2 Position CGRAM 
LCDOUT 254,88 , 0, 0 , 0, 30, 30 , 0, 0 , 0 ' 3 position CGRAM
LCDOUT 254,96 , 0, 0 , 0, 31, 31 , 0, 0 , 0 ' 4 position CGRAM
PAUSE 300
GOTO BAR
' *******************************************************************************
GRAF4 :
Mark2 = 4
LCDOUT 254,64 , 0, 16, 16 , 16, 16 , 16, 16 , 0 ' 0 position CGRAM
LCDOUT 254,72 , 0, 16, 16 , 16, 16 , 16, 16 , 0 ' 1 position CGRAM
LCDOUT 254,80 , 0, 20, 20, 20 , 20, 20 , 20, 0  ' 2 Position CGRAM  
LCDOUT 254,88 , 0, 20, 20, 20 , 20, 20 , 20, 0  ' 3 position CGRAM
LCDOUT 254,96 , 0, 21, 21 , 21, 21, 21 , 21, 0  ' 4 position CGRAM
PAUSE 300
GOTO BAR
' *******************************************************************************
GRAF5 :
Mark2 = 5
LCDOUT 254,64 , 0, 0 , 0, 4 , 0, 0 , 0, 0    ' 0 position CGRAM
LCDOUT 254,72 , 0, 0 , 4, 14 , 4, 0 , 0, 0   ' 1 Position CGRAM
LCDOUT 254,80 , 0, 0 , 14, 14 , 14, 0, 0, 0  ' 2 Position CGRAM 
LCDOUT 254,88 , 0, 4 , 14, 31 , 14, 4, 0, 0  ' 3 position CGRAM
LCDOUT 254,96 , 0, 14, 31, 31, 31 , 14, 0, 0 ' 4 position CGRAM
PAUSE 300
GOTO BAR
' *******************************************************************************
GRAF6 :
Mark2 = 6
LCDOUT 254,64,0,14,31,31,31,14,0,0           ' 0 position CGRAM
LCDOUT 254,72,0,14,31,31,31,14,0,0           ' 1 position CGRAM
LCDOUT 254,80,0,14,31,31,31,14,0,0           ' 2 Position CGRAM
LCDOUT 254,88,0,14,31,31,31,14,0,0           ' 3 position CGRAM
LCDOUT 254,96,0,0,0,31,0,0,0,0               ' 4 position CGRAM
PAUSE 300
' *******************************************************************************
BAR :
IF Mark1 = 1 THEN                             ' If true
LINE = $2: ADCIN 7, CHANNEL: Mark1 = 0        ' 1st line LCD shows L channel
ELSE                                          ' If it is not
LINE = $c0: ADCIN 6, CHANNEL: Mark1 = 1       ' 2nd Line LCD shows R channel
ENDIF 'End of the comparison
' *******************************************************************************
REST = ( CHANNEL // 5 )    ' Operation subtracted from the division
LCDOUT $FE,LINE            ' Start writing in the LCD
FOR COUNTER = 1 TO(CHANNEL/5) 'Count forward
LCDOUT 4                   ' 4 position CGRAM Print
NEXT COUNTER               ' Returns to COUNTER
IF REST = 1 THEN  LCDOUT 0 ' Print CGRAM position 0
IF REST = 2 THEN  LCDOUT 1 ' Print CGRAM Position 1
IF REST = 3 THEN  LCDOUT 2 ' Prints 2 position CGRAM
IF REST = 4 THEN  LCDOUT 3 ' 3 position CGRAM Print
LCDOUT 20, 20 , 20         ' Clearing 3 characters
' *******************************************************************************
IF (pushbutton = 1) and (Mark2 = 6) THEN GRAF1   '1 ° Viewing 
IF (pushbutton = 1) and (Mark2 = 1) THEN GRAF2   '2 ° Viewing
IF (pushbutton = 1) and (Mark2 = 2) THEN GRAF3   '3 ° Viewing 
IF (pushbutton = 1) and (Mark2 = 3) THEN GRAF4   '4 ° Viewing 
IF (pushbutton = 1) and (Mark2 = 4) THEN GRAF5   '5 ° Viewing 
IF (pushbutton = 1) and (Mark2 = 5) THEN GRAF6   '6 ° Viewing 
' *******************************************************************************
GOTO BAR
END

PICBasic Pro Trial Version 3.0.7.1 [incluye MPASM v8.90, Microcode Studio 5 (MCSX) para PBP v3, PBP 3.0.7.1]
Tamaño: 123.1MB
SHA1 Hash: C449786CFE50B26EB6818F9C35E10C718C36F6AD
Descarga

PICKit 3 v3.01
Tamaño: 8.4MB
SHA1 Hash: 34600B01B759F65DF993E1FA6408B4B3B3BA4AC9
Descarga

Además también se encuentra disponible para descargar el firmware ya compilado, listo para cargar en el microcontrolador.

VuMeter v1.0 HEX
Tamaño: 2KB
SHA1 Hash: 9D1F393C4EA3B561377988757898DAE7
Descarga

Arduino es una plataforma prácticamente infinita para todo tipo de aplicaciones como lo hemos visto en este humilde blog de entre millones que existen en la web. En este espacio comenzamos con aplicaciones de Hacking, luego incursionamos en la fascinante disciplina de la Robótica y últimamente avanzamos sobre el increíble universo de los Videojuegos.

Anteriormente publicamos juegos (basados en la librería VGAx desarrollada por Sandro Maffiodo) como, por ejemplo:

También construimos juegos más sencillos basados simplemente en LEDs y pulsadores como

Y no hace mucho tiempo atrás comenzamos con la construcción de videojuegos utilizando matrices de LEDs

En esta ocasión continuaremos con la construcción de juegos utilizando matrices de LEDs controladas por el circuito integrado MAX7219.

Tetris es un videojuego de puzzle originalmente diseñado y programado por Alekséi Pázhitnov en la Unión Soviética. Fue lanzado el 6 de junio de 1984, mientras trabajaba para el Centro de Computación Dorodnitsyn de la Academia de Ciencias de la Unión Soviética en Moscú, RSFS de Rusia. Su nombre deriva del prefijo numérico griego tetra- (todas las piezas del juego, conocidas como Tetrominós que contienen cuatro segmentos) y del tenis, el deporte favorito de Pázhitnov.

En el Tetris se juega con los tetrominós, el caso especial de cuatro elementos de poliominós. Los poliominós se han utilizado en los rompecabezas populares por lo menos desde 1907, y el nombre fue dado por el matemático Solomon W. Golomb en 1953. Sin embargo, incluso la enumeración de los pentominós data de la antigüedad.

El juego (o una de sus muchas variantes) está disponible para casi cada consola de videojuegos y sistemas operativos de PC, así como en dispositivos tales como las calculadoras gráficas, teléfonos móviles, reproductores de multimedia portátiles, PDAs, reproductores de música en red e incluso como huevo de pascua en productos no mediáticos como los osciloscopios. También ha inspirado servicios de mesa y ha sido jugado en los costados de varios edificios, manteniendo el récord de ser el juego completamente funcional más grande del mundo gracias al esfuerzo de estudiantes holandeses en 1995 que iluminaron 15 pisos del Departamento de Ingeniería Eléctrica en la Universidad Técnica de Delft.

Mientras que las versiones de Tetris se vendieron para una amplia gama de plataformas de ordenadores domésticos de los años 1980, fue la muy exitosa versión portátil para la Game Boy lanzada en 1989 que estableció al juego como uno de los más populares de todos los tiempos. La edición número 100 del Electronic Gaming Monthly tuvo al Tetris en el primer lugar como el “mejor juego de todos los tiempos”. En 2007, Tetris ocupó el segundo lugar en los “100 mejores videojuegos de todos los tiempos” para IGN. Ha vendido más de 70 millones de copias. En enero de 2010, se anunció que el Tetris ha vendido más de 100 millones de unidades para teléfonos celulares (móviles) sólo desde el año 2005.

Efectos mentales de Tetris

De acuerdo con el Dr. Richard Haier, jugar al Tetris de forma prolongada puede llevar a una actividad cerebral más eficiente durante el juego.

La primera vez que se juega al Tetris, aumentan la función y actividad cerebral, incrementándose también el consumo de energía y glucosa por parte de este. A medida que el jugador de Tetris se vuelve más hábil, el cerebro reduce su consumo de energía y glucosa, indicando una actividad cerebral más eficiente para el juego. Jugar al Tetris de forma moderada (media hora al día por un período de tres meses) incrementa las funciones cognoscitivas tales como “pensamiento crítico, razonamiento, procesamiento del lenguaje”, elevándose también el espesor de la corteza cerebral.

El juego puede provocar que se imaginen combinaciones de Tetris de forma involuntaria aun cuando no se esté jugando (el llamado Efecto Tetris), aunque esto puede ocurrir con cualquier videojuego o situación real que proyecte las imágenes o escenarios de forma repetida, tales como rompecabezas.

La Matriz de LEDs

Una matriz LEDs es un display formado por múltiples LEDs en distribución rectangular. Existen distintos tamaños, siendo el más habitual los cuadrados de 8×8 LEDs.
Podemos combinar varios módulos para formar un display mucho mayor. En estos display podemos mostrar textos, dibujos o animaciones, como desplazar un texto (scroll).
Por lo demás, son diodos LEDs totalmente normales, organizados en forma de matriz, que tendremos que multiplexar para poder iluminar uno u otro punto.
Si los diodos se unen por el positivo, se dice que son matrices de Ánodo común, y si se une por el negativo decimos que son de Cátodo común. Dependiendo del fabricante podemos encontrar de ambos tipos.

Encender una matriz de LEDs directamente con Arduino requiere emplear una gran cantidad de pines, lo cual supondría un gran desperdicio de recursos. Por este motivo, lo normal es siempre utilizar un controlador específicamente diseñado para esta función. Un controlador habitualmente empleado por ser barato y sencillo es el circuito integrado MAX7219.

Circuito Integrado MAX7219

Encender una matriz de 8×8 LED requeriría 16 señales digitales y un trabajo constante del procesador para refrescar la imagen. Eso es una cantidad enorme de recursos para cualquier autómata, que estaríamos desperdiciando para simplemente encender un display.
Por este motivo, utilizamos el circuito integrado MAX7219 que está especialmente diseñado para encender displays de 7 segmentos y matrices de LEDs, liberando al procesador para hacer tareas mucho más valiosas y productivas.
La comunicación con el circuito integrado MAX7219 se realiza mediante el bus SPI por lo que sólo se requieren 3 pines de Arduino (SS, MOSI y SCK). Además, ni siquiera “ocupamos” en su totalidad estos pines, ya que con el mismo bus podemos controlar múltiples dispositivos.
Por último, las placas MAX7219 generalmente incorporan un puerto de entrada y salida, de forma que podemos combinar múltiples controladores sin ninguna dificultad.
MAX7219 Datasheet

El diagrama esquemático de esta variante de Arduino Tetris se observa en la siguiente imagen:

(click para ampliar)

Lista de Componentes
1 Resistencia 10KΩ 0.25W
5 Pulsadores N.A. o Push-Buttons N.A.
1 Transistor NPN 2SC1815
2 Módulos con Matrices de LEDs 8×8 con Circuito Integrado MAX7219 c/u. Cátodo Común [Color Rojo en caso de aplicar Red Mod]
1 Buzzer Pasivo 5V
1 Placa Arduino Nanino
Librería LedControl desarrollada por wayoda

#include <LedControl.h>

LedControl lc = LedControl(12, 11, 10, 2); // (dataPin, clockPin, csPin, totalDevices)

int lc0[] = {0, 0, 0, 0, 0, 0, 0, 0};
int lc1[] = {0, 0, 0, 0, 0, 0, 0, 0};
long active[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
long screen[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int tmpCol = 0;


int figura = 0;
int figuraNext = 0;
int fromLeft = 0;
int fromRight = 0;
int angle = 0;
int colCheck = 0;
int moveCheck = 0;
int score = 0;
int started = 0;
int lcRows = 16;
int lcCols = 8;
int allLines = 0;
int currLines = 0;
int brickDelay = 0;
int defDelay = 500;
int level = 0;

boolean sound = true;
//Pinos

int rotate_button = 2;
int left_button   = 3;
int down_button   = 4;
int right_button  = 5;
int start_button  = 6;
int speaker_pin   = 7;
int sound_button  = 8;

byte X[8] =
{
  0b00000,
  0b10001,
  0b01010,
  0b00100,
  0b01010,
  0b10001,
  0b00000,
  0b00000
};

byte O[8] =
{
  0b00000,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b00000,
  0b00000
};

byte L[8] =
{
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11111,
  0b11111,
  0b00000
};

byte J[8] =
{
  0b00011,
  0b00011,
  0b00011,
  0b00011,
  0b00011,
  0b11111,
  0b11111,
  0b00000
};

byte T[8] =
{
  0b00000,
  0b00000,
  0b11111,
  0b11111,
  0b01110,
  0b01110,
  0b00000,
  0b00000
};

byte I[8] =
{
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b00000
};

byte Z[8] =
{
  0b00000,
  0b00000,
  0b11110,
  0b11110,
  0b01111,
  0b01111,
  0b00000,
  0b00000
};

byte S[8] =
{
  0b00000,
  0b00000,
  0b01111,
  0b01111,
  0b11110,
  0b11110,
  0b00000,
  0b00000
};

//Nuty
int length = 99;
char notes[] = "EbCDCbaaCEDCbbCDECaa DFAGFEECEDCbbCDECaa EbCDCbaaCEDCbbCDECaa DFAGFEECEDCbbCDECaa ECDbCab ECDbCEAJ ";
int beats[] =      // Som
{
  2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 2,
  2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 1,
  2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 2,
  2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 1,
  5, 5, 5, 5, 5, 5, 7, 2, 5, 5, 5, 5, 2, 2, 5, 5, 3
};
int tempo = 128;        // Tempo


void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speaker_pin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speaker_pin, LOW);
    delayMicroseconds(tone);
  }
}


void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' , 'D', 'E', 'F', 'G', 'J', 'A', 'B'};
  int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956, 850, 760, 716, 637, 603, 568, 507 };

  for (int i = 0; i < 14; i++) {
    if (names[i] == note) {
      playTone(tones[i], duration);
    }
  }
}


void updateColumn(int colnum)
{
    lc0[colnum] = active[colnum] >> (lcRows / 2);
    lc1[colnum] = active[colnum];
    lc.setColumn(0,colnum,(screen[colnum] >> (lcRows / 2)) | lc0[colnum]);
    lc.setColumn(1,colnum,screen[colnum] | lc1[colnum]);
}



void buttonDelay(int bdelay)
{
    if(brickDelay > bdelay)
    {
        brickDelay -= bdelay;
    }
    delay(bdelay);
}


void splashScreen()
{
     int up[] =
     {
         B11101110,   //   o o o   o o o
         B01001000,   //     o     o
         B01001100,   //     o     o o
         B01001000,   //     o     o
         B01001110,   //     o     o o o
         B00000000,   //
         B11101110,   //   o o o   o o o
         B01001010    //     o     o   o
     };

     int down[] =
     {
         B01001100,   //     o     o o
         B01001010,   //     o     o   o
         B01001001,   //     o     o     o
         B00000000,   //
         B01000111,   //     o       o o o
         B01000100,   //     o       o
         B01000010,   //     o         o
         B01001110    //     o     o o o
     };

     for(int rownum = 0; rownum < 8; rownum++)
     {
         lc.setRow(0,rownum,up[rownum]);
         lc.setRow(1,rownum,down[rownum]);
     }
}


void setup() {
  pinMode(speaker_pin,  OUTPUT);
  pinMode(rotate_button,INPUT_PULLUP);
  pinMode(down_button,  INPUT_PULLUP);
  pinMode(right_button, INPUT_PULLUP);
  pinMode(left_button,  INPUT_PULLUP);
  pinMode(start_button, INPUT_PULLUP);
  pinMode(sound_button, INPUT_PULLUP);

  lc.shutdown(0,false);
  lc.shutdown(1,false);

  lc.setIntensity(0,5);
  lc.setIntensity(1,5);

  lc.clearDisplay(0);
  lc.clearDisplay(1);
  Serial.begin(9600);

  randomSeed(analogRead(0));
}


//LOOP
void loop()
{



 if(started == 0)
 {
     splashScreen();

     for (int i = 0; i < length; i++)
     {

 if(digitalRead(sound_button) == LOW)
 {
  sound =! sound;
  delay(300);
  }

         if(digitalRead(start_button) == LOW)
         {
             started = 1;
             break;
         }
         if (notes[i] == ' ')
         {
             delay(beats[i] * tempo);   //Pausa
         }
         else
         {
          if (sound == 1){
             playNote(notes[i], beats[i] * tempo);
          }
          else
          {
             digitalWrite(speaker_pin,LOW);
            }
         }


        delay(tempo / 2);
     }
 }



 else
 {

     lc.clearDisplay(0);
     lc.clearDisplay(1);
     memset(lc0, 0, sizeof(lc0));
     memset(lc1, 0, sizeof(lc1));
     memset(active, 0, sizeof(active));
     memset(screen, 0, sizeof(screen));
     tmpCol = 0;

     while(started == 1)
     {

         if(allLines < 100)
         {
             level = 0;              //Level 0
         }
         else if(allLines < 200)
         {
             level = 1;              //Level 1
         }
         else if(allLines < 300)
         {
             level = 2;              //Level 2
         }
         else if(allLines < 400)
         {
             level = 3;              //Level 3
         }
         else if(allLines < 500)
         {
             level = 4;              //Level 4
         }
         else
         {
             level = 5;              //Level 5
         }


         defDelay = (5 - level) * 100;
         brickDelay = defDelay;


         if(figura == 0)
         {
              figura = random(1,8);
         }
         else
         {
             figura = figuraNext;
         }
         figuraNext = random(1,8);
         angle = 0;



         switch(figura)
         {
             case 1:
             //"O"
                 active[3] = 131072 + 65536;
                 active[4] = 131072 + 65536;
                 fromLeft = 3;
                 fromRight = 3;
                 break;

             case 2:
             //"L"
                 active[3] = 262144 + 131072 + 65536;
                 active[4] = 65536;
                 fromLeft = 3;
                 fromRight = 3;
                 break;

             case 3:
             //"J"
                 active[3] = 65536;
                 active[4] = 262144 + 131072 + 65536;
                 fromLeft = 3;
                 fromRight = 3;
                 break;

             case 4:
             //"T"
                 active[2] = 131072;
                 active[3] = 131072 + 65536;
                 active[4] = 131072;
                 fromLeft = 2;
                 fromRight = 3;
                 break;

             case 5:
             //"I"
                 active[3] = 524288 + 262144 + 131072 + 65536;
                 fromLeft = 3;
                 fromRight = 4;
                 break;

             case 6:
             //"Z"
                 active[2] = 131072;
                 active[3] = 131072 + 65536;
                 active[4] = 65536;
                 fromLeft = 2;
                 fromRight = 3;
                 break;

             case 7:
             //"S"
                 active[2] = 65536;
                 active[3] = 131072 + 65536;
                 active[4] = 131072;
                 fromLeft = 2;
                 fromRight = 3;
                 break;
         }



         for(int krok = 0; krok < lcRows + 1; krok++)
         {
             colCheck = 0;


             for(int i = 0; i < (lcCols / 2); i++)
             {
                 if((digitalRead(left_button) == LOW) && (fromLeft > 0))
                 {
                     moveCheck = 0;
                     for(int colnum = fromLeft; colnum < (lcCols - fromRight); colnum++)
                     {
                         if((active[colnum] & screen[colnum - 1]) == 0)
                         {
                             moveCheck++;
                         }
                     }

                     if(moveCheck == (lcCols - fromLeft - fromRight))
                     {
                         for(int colnum = (fromLeft - 1); colnum < (lcCols - fromRight); colnum++)
                         {
                             if(colnum < (lcCols - 1))
                             {
                                 active[colnum] = active[colnum+1];
                             }
                             else
                             {
                                 active[colnum] = 0;
                             }
                             updateColumn(colnum);
                         }
                         fromLeft--;
                         fromRight++;
                         playNote('E', 10);
                         buttonDelay(200);
                     }
                 }
             }





             for(int i = 0; i < (lcCols / 2); i++)
             {
                 if((digitalRead(right_button) == LOW) && (fromRight > 0))
                 {
                     moveCheck = 0;
                     for(int colnum = fromLeft; colnum < (lcCols - fromRight); colnum++)
                     {
                         if((active[colnum] & screen[colnum + 1]) == 0)
                         {
                             moveCheck++;
                         }
                     }

                     if(moveCheck == (lcCols - fromLeft - fromRight))
                     {
                         for(int colnum = (lcCols - fromRight); colnum > (fromLeft - 1); colnum--)
                         {
                             if(colnum > 0)
                             {
                                 active[colnum] = active[colnum-1];
                             }
                             else
                             {
                                 active[colnum] = 0;
                             }
                             updateColumn(colnum);
                         }
                         fromLeft++;
                         fromRight--;
                         playNote('E', 10);
                         buttonDelay(200);
                     }
                 }
             }



             if(digitalRead(down_button) == LOW)
             {
                 brickDelay = 0;
                 playNote('b', 10);
             }
             else
             {
                 brickDelay = defDelay;
             }



             for(int i = 0; i < (lcCols / 2); i++)
             {
                 if(digitalRead(rotate_button) == LOW)
                 {

                     switch(figura)
                     {
                         case 1:
                         //"O"
                             break;

                         case 2:
                         //"L"
                             switch(angle)
                             {
                                 case 0:
                                 // . o .        . . .
                                 // . o .  --->  o o o
                                 // . o o        o . .
                                     if((fromLeft > 0)
                                     && (((active[fromLeft + 1] | (active[fromLeft + 1] << 1)) & screen[fromLeft - 1]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft + 1] | (active[fromLeft + 1] << 1));
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . . .        o o .
                                 // o o o  --->  . o .
                                 // o . .        . o .
                                     if((((active[fromLeft + 2] << 1) & screen[fromLeft]) == 0)
                                     && ((((active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1)) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft + 2] << 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft + 1] | (active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // o o .        . . o
                                 // . o .  --->  o o o
                                 // . o .        . . .
                                     if((fromRight > 0)
                                     && (((active[fromLeft] >> 1) & screen[fromLeft]) == 0)
                                     && ((((active[fromLeft + 1] << 1) & active[fromLeft + 1]) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft] >> 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft];
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = ((active[fromLeft + 1] << 1) | active[fromLeft + 1]);
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . o        . o .
                                 // o o o  --->  . o .
                                 // . . .        . o o
                                     if(((((active[fromLeft] << 1) | (active[fromLeft] >> 1)) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft] >> 1) & screen[fromLeft + 2]) == 0)
                                     && (krok < lcRows))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 2] | (active[fromLeft + 2] >> 1));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = ((active[fromLeft + 2] >> 1) & (active[fromLeft + 2] >> 2));
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 3:
                         //"J"
                             switch(angle)
                             {
                                 case 0:
                                 // . o .        o . .
                                 // . o .  --->  o o o
                                 // o o .        . . .
                                     if((fromRight > 0)
                                     && ((((active[fromLeft] << 2) | (active[fromLeft] << 1)) & screen[fromLeft]) == 0)
                                     && (((active[fromLeft] << 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = ((active[fromLeft] << 2) | (active[fromLeft] << 1));
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = ((active[fromLeft + 1] << 1) & (active[fromLeft + 1] >> 1));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = active[fromLeft + 1];
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // o . .        . o o
                                 // o o o  --->  . o .
                                 // . . .        . o .
                                     if((krok < lcRows)
                                     && ((((active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1)) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft + 2] << 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] | (active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = (active[fromLeft + 2] << 1);
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . o o        . . .
                                 // . o .  --->  o o o
                                 // . o .        . . o
                                     if((fromLeft > 0)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft - 1]) == 0)
                                     && ((((active[fromLeft + 1] >> 1) | (active[fromLeft + 1] >> 2)) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft - 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft] | (active[fromLeft + 1] >> 2));
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . .        . o .
                                 // o o o  --->  . o .
                                 // . . o        o o .
                                     if((((active[fromLeft] >> 1) & screen[fromLeft]) == 0)
                                     && ((((active[fromLeft] << 1) | (active[fromLeft >> 1])) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft] >> 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = ((active[fromLeft + 1] << 1) | active[fromLeft + 2]);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 4:
                         //"T"
                             switch(angle)
                             {
                                 case 0:
                                 // . . .        . o .
                                 // o o o  --->  o o .
                                 // . o .        . o .
                                     if(((active[fromLeft + 1] << 1) & screen[fromLeft + 1]) == 0)
                                     {
                                         //active[fromLeft]
                                         active[fromLeft + 1] = active[fromLeft + 1] | (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . o .        . o .
                                 // o o .  --->  o o o
                                 // . o .        . . .
                                     if((fromRight > 0)
                                     && ((active[fromLeft] & screen[fromLeft + 2])== 0))
                                     {
                                         //active[fromLeft]
                                         active[fromLeft + 1] = active[fromLeft + 1] & (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = active[fromLeft];
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . o .        . o .
                                 // o o o  --->  . o o
                                 // . . .        . o .
                                     if((((active[fromLeft + 1] >> 1) & screen[fromLeft + 1]) == 0)
                                     && (krok < lcRows))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft + 1] | (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         //active[fromLeft + 2]
                                         fromLeft++;
                                         krok++;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                     if((fromLeft > 0)
                                     && ((active[fromLeft + 1] & screen[fromLeft - 1]) == 0))
                                     {
                                         active[fromLeft - 1] = active[fromLeft + 1];
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft] & (active[fromLeft] >> 1);
                                         updateColumn(fromLeft);
                                         fromLeft--;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 5:
                         //"I"
                             switch(angle)
                             {
                                 case 0:
                                 // . o . .        . . . .
                                 // . o . .  --->  o o o o
                                 // . o . .        . . . .
                                 // . o . .        . . . .
                                     if((fromLeft > 0)
                                     && (fromRight > 1)

                                     && ((((((active[fromLeft] >> 1) & (active[fromLeft] << 2)) & screen[fromLeft - 1]) & screen[fromLeft + 1]) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft - 1] = ((active[fromLeft] >> 1) & (active[fromLeft] << 2));
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft - 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft];
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = active[fromLeft];
                                         updateColumn(fromLeft + 2);
                                         fromLeft--;
                                         fromRight -= 2;
                                         krok -= 2;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . . . .        . . o .
                                 // o o o o  --->  . . o .
                                 // . . . .        . . o .
                                 // . . . .        . . o .
                                     if((krok < (lcRows - 1))
                                     && (((active[fromLeft] << 1) | (active[fromLeft] >> 1) | (active[fromLeft] >> 2)) & screen[fromLeft + 2]) == 0)
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = 0;
                                         updateColumn(fromLeft + 1);

                                         active[fromLeft + 2] = (active[fromLeft + 2] | (active[fromLeft + 2] << 1) | (active[fromLeft + 2] >> 1) | (active[fromLeft + 2] >> 2));
                                         updateColumn(fromLeft + 2);
                                         active[fromLeft + 3] = 0;
                                         updateColumn(fromLeft + 3);
                                         fromLeft += 2;
                                         fromRight++;
                                         krok += 2;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . . o .        . . . .
                                 // . . o .  --->  . . . .
                                 // . . o .        o o o o
                                 // . . o .        . . . .
                                     if((fromLeft > 1)
                                     && (fromRight > 0)

                                     && ((((((active[fromLeft] << 1) & (active[fromLeft] >> 2)) & screen[fromLeft - 2]) & screen[fromLeft - 1]) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft - 2] = ((active[fromLeft] << 1) & (active[fromLeft] >> 2));
                                         updateColumn(fromLeft - 2);
                                         active[fromLeft - 1] = active[fromLeft - 2];
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft - 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft];
                                         updateColumn(fromLeft + 1);
                                         fromLeft -= 2;
                                         fromRight--;
                                         krok--;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . . .        . o . .
                                 // . . . .  --->  . o . .
                                 // o o o o        . o . .
                                 // . . . .        . o . .
                                     if((krok < (lcRows))
                                     && (((active[fromLeft] >> 1) | (active[fromLeft] << 1) | (active[fromLeft] << 2)) & screen[fromLeft + 1]) == 0)
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);

                                         active[fromLeft + 1] = (active[fromLeft + 1] | (active[fromLeft + 1] >> 1) | (active[fromLeft + 1] << 1) | (active[fromLeft + 1] << 2));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         active[fromLeft + 3] = 0;
                                         updateColumn(fromLeft + 3);
                                         fromLeft++;
                                         fromRight += 2;
                                         krok++;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 6:
                         //"Z"
                             switch(angle)
                             {
                                 case 0:
                                 // . . .        . o .
                                 // o o .  --->  o o .
                                 // . o o        o . .
                                     if(((active[fromLeft + 1] & screen[fromLeft]) == 0)
                                     && (((active[fromLeft + 1] << 1) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = active[fromLeft + 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . o .        o o .
                                 // o o .  --->  . o o
                                 // o . .        . . .
                                     if((fromRight > 0)
                                     && ((((active[fromLeft] << 2) & (active[fromLeft] << 1)) & screen[fromLeft]) == 0)
                                     && (((active[fromLeft] & active[fromLeft + 1]) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = ((active[fromLeft] << 2) & (active[fromLeft] << 1));
                                         updateColumn(fromLeft);
                                         //active[fromLeft + 1]
                                         active[fromLeft + 2] = (active[fromLeft] >> 1);
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // o o .        . . o
                                 // . o o  --->  . o o
                                 // . . .        . o .
                                     if((krok < lcRows)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft + 2] << 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = (active[fromLeft + 2] | (active[fromLeft + 2] << 1));
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . o        . . .
                                 // . o o  --->  o o .
                                 // . o .        . o o
                                     if((fromLeft > 0)
                                     && (((active[fromLeft] & active[fromLeft + 1]) & screen[fromLeft - 1]) == 0)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft] & active[fromLeft + 1]);
                                         updateColumn(fromLeft - 1);
                                         //active[fromLeft]
                                         active[fromLeft + 1] = (active[fromLeft - 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 7:
                         //"S"
                             switch(angle)
                             {
                                 case 0:
                                 // . . .        o . .
                                 // . o o  --->  o o .
                                 // o o .        . o .
                                     if(((active[fromLeft + 1] << 1) & screen[fromLeft]) == 0)
                                     {
                                         active[fromLeft] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft);
                                         //active[fromLeft + 1]
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // o . .        . o o
                                 // o o .  --->  o o .
                                 // . o .        . . .
                                     if((fromRight > 0)
                                     && (((active[fromLeft + 1] << 1) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft] & (active[fromLeft] << 1)) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft] & active[fromLeft + 1]);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = (active[fromLeft] << 1);
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . o o        . o .
                                 // o o .  --->  . o o
                                 // . . .        . . o
                                     if((krok < lcRows)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         //active[fromLeft + 1]
                                         active[fromLeft + 2] = (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . o .        . . .
                                 // . o o  --->  . o o
                                 // . . o        o o .
                                     if((fromLeft > 0)
                                     && ((active[fromLeft + 1] & ((active[fromLeft + 1] >> 1)) & screen[fromLeft - 1]) == 0)
                                     && ((active[fromLeft + 1] & screen[fromLeft]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft + 1] & (active[fromLeft + 1] >> 1));
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft + 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft - 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;
                     }
                     playNote('E', 10);
                     buttonDelay(200);
                 }
             }


             //Restart
             if(digitalRead(start_button) == LOW)
             {
                 memset(lc0, 0, sizeof(lc0));
                 memset(lc1, 0, sizeof(lc1));
                 memset(active, 0, sizeof(active));
                 memset(screen, 0, sizeof(screen));
                 score = 0;
                 allLines = 0;
                 figura = 0;
                 break;
             }

             for(int colnum = 0; colnum < lcCols; colnum++)
             {

                 if((screen[colnum] & (active[colnum] >> 1)) == 0)
                 {
                     colCheck++;
                 }

                 else
                 {
                     colCheck = 0;
                     if(krok == 0)
                     {
                         started = 0;
                     }
                 }
             }

             if((colCheck == lcCols) && (krok < lcRows))
             {
                 for(int colnum = 0; colnum < lcCols; colnum++)
                 {
                     active[colnum] = active[colnum] >> 1;
                     updateColumn(colnum);
                 }
             }
             else
             {
                 break;
             }
             delay(brickDelay);
         }

         for(int colnum = 0; colnum < lcCols; colnum++)
         {
             screen[colnum] = screen[colnum] | (lc0[colnum] << (lcRows / 2));
             screen[colnum] = screen[colnum] | lc1[colnum];
             lc0[colnum] = 0;
             lc1[colnum] = 0;
             active[colnum] = 0;
         }



         currLines = 0;
         for(int rownum = 0; rownum < lcRows; rownum++)
         {
             colCheck = 0;
             for(int colnum = 0; colnum < lcCols; colnum++)
             {
                 if(((screen[colnum] >> rownum) & 1) == 1)
                 {
                     colCheck++;
                 }
             }
             if(colCheck == lcCols)
             {
                 //Animacja kasowania
                 for(int colnum = 0; colnum < lcCols; colnum++)
                 {
                     tmpCol = ~((int) round(pow(2, rownum)));
                     screen[colnum] = screen[colnum] & tmpCol;
                     updateColumn(colnum);

                     switch(currLines)
                     {
                         case 0:
                             playNote('b', 20);
                             break;
                         case 1:
                             playNote('D', 20);
                             break;
                         case 2:
                             playNote('F', 20);
                             break;
                         case 3:
                             playNote('A', 20);
                             break;
                     }
                     delay(30);

                     tmpCol = (int) (round(pow(2, rownum)) - 1);
                     tmpCol = screen[colnum] & tmpCol;
                     screen[colnum] = (screen[colnum] >> (rownum + 1));
                     screen[colnum] = (screen[colnum] << rownum);
                     screen[colnum] = screen[colnum] | tmpCol;

                 }


                 for(int colnum = 0; colnum < lcCols; colnum++)
                 {
                     updateColumn(colnum);
                 }
                 rownum--;
                 currLines++;
                 allLines++;
             }
         }

         if(currLines > 0)
         {
             score += (int) round(pow(4, currLines-1));
         }
    }

gameOver();
// == Game Over ==

 }
}

void gameOver()
{
  playNote('F', 80);
  playNote('A', 60);
  playNote('F', 80);
  playNote('A', 60);

     int cima[] =
     {
         B11111111,   //     o     o o o
         B11111111,   //   o o o    o
         B11111111,   //   o   o     o o
         B11111111,   //     o     o
         B11111111,   //     o     o o o
         B11111111,   //
         B11111111,   //   o o o   o o o
         B11111111    //     o     o   o
     };

     int baixo[] =
     {
         B11111111,   //     o     o o
         B11111111,   //     o     o   o
         B11111111,   //     o     o     o
         B11111111,   //
         B11111111,   //     o       o o o
         B11111111,   //     o       o
         B11111111,   //     o         o
         B11111111    //     o     o o o
     };

 for(int rownum = 8; rownum >= 0; rownum--)
     {
         lc.setRow(1,rownum,baixo[rownum]);
         delay(100);
     }

     for(int rownum = 8; rownum >= 0; rownum--)
     {
         lc.setRow(0,rownum,cima[rownum]);
         delay(100);
     }

     delay(1800);

}

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

Red Mod
La cinta Kapton es resistente al calor y eléctricamente aislante que se usa mucho en electrónica e impresión 3D. Si has estado en esta afición por un tiempo hay una buena probabilidad de que tengas un rollo a mano.

Una o dos capas de cinta Kapton aplicada a las matrices las hacen menos propensas a lavarse bajo luz brillante. El color de la cinta es lo suficientemente similar a los LEDs rojos que brillan a través con poca dificultad, mientras que la mayoría de la luz ambiente está bloqueada.

La película de enmascarar “Rubylith” (empleada en diseño gráfico) probablemente funcionaría igual de bien, o quizás mejor. Este film puede conseguirse en tiendas de arte decentes de la vieja escuela.

Los componentes electrónicos no tienen cambio, devolución ni garantía“. Advertencia asquerosa si las hay. ¿Cuántas veces sufrimos dolores de estómago a causa de esa “advertencia”? ¿100 veces, 1000 veces? Y estoy seguro que muchas veces más. La realidad es que la padecemos cada vez que vamos a comprar componentes para nuestros proyectos a cualquier comercio de electrónica. Luego de finalizar nuestra compra, cansados de renegar con algún vendedor de la tercera edad con conocimientos obsoletos (son los peores porque además padecen de Complejo Mesiánico) o bien con conocimientos nulos si son de la primera edad, llegamos a casa, preparamos una bebida “espirituosa” y comenzamos con la construcción de nuestro nuevo “Frankenstein”. Luego de varias horas de denodado esfuerzo, sangre, sudor y lágrimas, no funciona. ¿El problema? La respuesta se encuentra en las primeras 9 palabras de este párrafo. Una vez más la ira incontrolable se apodera de nuestro cuerpo y nuestra mente brillante.

Como una solución a ese martirio, me complace compartir la presente publicación como un vaso de agua en el vasto desierto de la ignorancia y apatía de quienes nos proveen de nuestros preciados componentes electrónicos para nuestros proyectos. Con este “Probador Universal de Componentes” podremos probar o testear nuestros componentes antes de conectarlos a nuestros proyectos y evitarnos esa interminable agonía que nos invade cuando nuestro montaje no funciona sin causas aparentes.

Con este “Probador Universal de Componentes” podemos comprobar el estado de los siguientes componentes:

  • Transistores Bipolares NPN.
  • Transistores Bipolares PNP.
  • Transistor Unijuntura Programable (PUT).
  • Transistor Unijuntura (UJT).
  • Circuito Integrado Temporizador 555.
  • Circuito Integrado Amplificador Operacional Simple.
  • Tiristor Convencional (SCR).
  • Triodo para Corriente Alterna (TRIAC).

El lector perspicaz seguramente ya tendrá la idea formada que este probador universal se encuentra conformado por diversos circuitos de prueba individuales específicos para cada componente enumerado en la lista anterior. El diagrama esquemático se observa en las siguientes imágenes:

(click para ampliar)

Como se observa en el diagrama esquemático el circuito se compone de 5 módulos o circuitos de prueba individuales que podemos construir por separado acorde a nuestras necesidades. En el circuito impreso, al igual que en el diagrama esquemático, se observan las líneas separando cada circuito de prueba individualmente:

(click para ampliar)

(click para ampliar)

(click para ampliar)

Existen dos maneras de construir este montaje. La primera, utilizada para esta publicación, consiste en armar la placa en su totalidad. La otra manera es realizar el montaje de el/los módulo/s que seleccionemos a nuestro criterio y de esa manera se reduce el tiempo y costo de armado ya que no se construirán todos los circuitos de prueba.

Lista de Componentes
Fuente de Alimentación
1 Transformador 9V + 9V 300mA
2 Diodos Rectificadores 1N4001
1 Capacitor Electrolítico 1000uF 16V

Módulo de Prueba para Circuito Integrado Amplificador Operacional Simple
2 Resistencias 120KΩ 0.25W
1 Resistencia 100KΩ 0.25W
1 Resistencia 15KΩ 0.25W
2 Resistencias 1KΩ 0.25W
1 Resistencias 560Ω 0.25W
2 Capacitores Electrolíticos 10uF 16V
1 Diodo LED 5mm [color a elección]

Módulo de Prueba para SCR o TRIAC
1 Resistencia 15KΩ 0.25W
1 Resistencia 2K2Ω 0.25W
1 Resistencia 1KΩ 0.25W
2 Resistencias 820Ω 0.25W
1 Resistencia 390Ω 0.25W
1 Resistencia 100Ω 0.25W
1 Capacitor Electrolítico 33uF 16V
2 Capacitores Electrolíticos 22uF 16V
1 Capacitor Cerámico 10nF 50V
2 Transistores Unijuntura Programables 2N6027
1 Diodo LED 5mm [color a elección]

Módulo de Prueba para Circuito Integrado Temporizador 555
1 Resistencia 3M9Ω 0.25W
1 Resistencia 10KΩ 0.25W
2 Resistencias 560Ω 0.25W
1 Capacitor Cerámico 220nF 50V
2 Diodos LEDs 5mm [colores a elección]

Módulo de Prueba para UJT o PUT
1 Resistencia 15KΩ 0.25W
1 Resistencia 2K7Ω 0.25W
1 Resistencia 820Ω 0.25W
1 Resistencia 470Ω 0.25W
2 Resistencias 330Ω 0.25W
1 Resistencia 100Ω 0.25W
1 Resistencia 47Ω 0.25W
1 Capacitor Electrolítico 22uF 16V
1 Transistor NPN 2A238
1 Transistor PNP 2A258
1 Diodo LED 5mm [color a elección]

Módulo de Prueba para Transistores Bipolares NPN o PNP
1 Resistencia 47KΩ 0.25W
1 Resistencia 10KΩ 0.25W
1 Resistencia 330Ω 0.25W
1 Resistencia 270Ω 0.25W
1 Resistencia 220Ω 0.25W
1 Capacitor Electrolítico 1uF 16V
1 Circuito Integrado NE555
1 Circuito Integrado CD4027
4 Diodos Rápidos de Señal 1N4148
2 Diodos LEDs 5mm [colores a elección]

La placa armada en su totalidad se observa en las siguientes imágenes:

(click para ampliar)

(click para ampliar)

(click para ampliar)

IMPORTANTE: el Transistor Unijuntura Programable 2A6027 utilizado originalmente debió ser sustituido por el PUT 2N6027 ya que actualmente se encuentra discontinuada su producción. La disposición de pines es diferente a la serigrafía que se encuentra en la placa original y debe ser instalado como se muestra en la siguiente imagen:

(click para ampliar)

Todo el conjunto puede alimentarse con una batería de 9V, por lo tanto una versión portátil de este “Probador Universal de Componentes” es bastante sencilla de realizar. Para esto debemos eliminar el transformador y los dos diodos rectificadores 1N4001 para luego conectar la batería en paralelo al capacitor electrolítico de 1000uF sin suprimir el mismo.
Un detalle menor pero que resultaría práctico es seleccionar de manera adecuada el color de los diodos LEDs para facilitar la visualización y detección rápida de anomalías en los componentes bajo prueba.

IMPORTANTE: el siguiente vídeo muestra la utilización del “Probador Universal de Componentes” empleando como prueba componentes nuevos ya que al momento de realizar el vídeo no disponía en mi poder de componentes defectuosos, por lo tanto los valores arrojados representan el estado de componentes en óptimas condiciones. Cualquier patrón diferente al mostrado nos indicará que estamos en presencia de uno o varios componentes defectuosos. La prueba de PUT/UJT no fué llevada a cabo ya que no disponía de dichos componentes al momento de realizar el vídeo.

En publicaciones anteriores compartí con ustedes 3 circuitos amplificadores de audio con el circuito integrado LM386 (ver Amplificador de Audio LM386 o Amplificador de Audio LM386 v2) y el último publicado fué Amplificador de Audio TDA7297 15W + 15W RMS (Ideal PC) que emplea el circuito integrado TDA7297.

Debido al éxito del Amplificador de Audio TDA7297 15W + 15W RMS (Ideal PC) publicado en este humilde blog, decidí construir un nuevo amplificador más potente que puede operar en modo estéreo o puente para cuando se requiera de más potencia como amplificador para el subwoofer de tu sistema de cine en casa, conocido comercialmente como “Home Theater”.

El circuito tiene como elemento principal un circuito integrado TDA7294, en el cual se han agrupado todos los componentes necesarios para conformar una etapa de potencia de audio. Este circuito integrado, fabricado por ST Microelectronics, es monolítico con encapsulado Multiwatt15, clase AB para aplicaciones de alta fidelidad. Es ampliamente utilizado en los amplificadores de audio comerciales.Como puede observarse la cantidad de componentes externos es mínima, además cuenta con funciones de “Stand-By” y “Silencio”, protección contra corto circuitos y protección contra exceso de temperatura.

Los circuitos integrados deben ser dotados de un generoso disipador de calor como se observa en la imagen de abajo. La grasa siliconada debe aplicarse en su justa medida (con una capa delgada es suficiente), ya que en exceso produce el efecto contrario. Además puede incluirse un led indicador de encendido del color y/o brillo que deseemos con una resistencia acorde al mismo.

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

El circuito aquí presentado utiliza dos circuitos integrados TDA7294 y puede utilizarse en 2 modos de operación: estéreo o bridge. En modo estéreo todos los puentes (4 en total: JB1, JB2, JB3, JB4) deben encontrarse abiertos, o sea, sin soldar al PCB y entrega una potencia de salida de 80W + 80W estéreo. Los parlantes deben conectarse a los conectores etiquetados como Lout y Rout respectivamente y la entrada de audio se conecta a Lin y Rin.
En modo bridge todos los puentes (4 en total: JB1, JB2, JB3, JB4) deben estar instalados en el PCB y entrega una potencia de salida de 180W mono. El parlante debe conectarse a los conectores etiquetados como Lout (+) y Rout (-) y la entrada de audio se conecta a Lin. Rin se cortocircuitará a tierra mediante JB2. En este modo el fabricante (ST Microelectronics) recomienda que la impedancia del parlante no sea menor a 8 ohmios.

El transformador recomendado para este amplificador tiene una tensión el bobinado secundario de 25-0-25 VAC con una corriente de 6A como mínimo. La fuente de alimentación simétrica ya se encuentra en el PCB del amplificador y consiste en un puente rectificador de 8A y 2 capacitores electrolíticos blindados de 15000uF con una tensión de trabajo de 50V como mínimo.

Lista de Componentes
5 Resistencias 22KΩ 0.25W [metalfilm]
2 Resistencias 680Ω 0.25W [metalfilm]
2 Resistencias 100KΩ 0.25W [metalfilm]
1 Resistencia 27KΩ 0.25W [metalfilm]
1 Resistencia 2K7Ω 0.25W [metalfilm]
2 Resistencias 47KΩ 0.25W [metalfilm]
4 Capacitores 100nF 100V [poliéster]
2 Capacitores 560nF 100V [poliéster]
2 Capacitores 220nF 100V [poliéster]
2 Capacitores Electrolíticos 15000uF 50V [blindados]
8 Capacitores Electrolíticos 22uF 50V
2 Diodos de Conmutación Rápida 1N4148
2 Circuitos Integrados TDA7294
1 Puente Rectificador KBU610 [1000V 6A]
1 Transformador con una tensión en el bobinado secundario de 25-0-25 VAC con una corriente de 6A como mínimo
30cm de Alambre para Bobinado 1mm diámetro

A continuación se encuentra el PCB en escala 1:1 (el documento PDF debe ser impreso con la escala al 100%), el cual se encuentra listo para la aplicación por transferencia térmica (método “de planchado” ). Además se encuentra la vista de disposición de los componentes (layout) como información complementaria.

Amplificador de Audio TDA7294 PCB

Amplificador de Audio TDA7294 Layout

Arduino es una plataforma prácticamente infinita para todo tipo de aplicaciones como lo hemos visto en este humilde blog de entre millones que existen en la web. En este espacio comenzamos con aplicaciones de Hacking, luego incursionamos en la fascinante disciplina de la Robótica y últimamente avanzamos sobre increíble universo de los Videojuegos.

Anteriormente publicamos juegos (basados en la librería VGAx desarrollada por Sandro Maffiodo) como, por ejemplo:

Y juegos más sencillos basados simplemente en LEDs y pulsadores como

En esta ocasión continuaremos con la construcción de juegos sencillos, pero algo más elaborados utilizando matrices de LEDs controladas por el circuito integrado MAX7219.
Pong (o Tele-Pong) fue un videojuego de la primera generación de videoconsolas publicado por Atari, creado por Nolan Bushnell y lanzado el 29 de noviembre de 1972. Pong está basado en el deporte de tenis de mesa (o ping pong). La palabra Pong es una marca registrada por Atari Interactive, mientras que la palabra genérica «pong» es usada para describir el género de videojuegos «bate y bola». La popularidad de Pong dio lugar a una demanda de infracción de patentes y ganada por parte de los fabricantes de Magnavox Odyssey, que poseía un juego similar.
Pong es un juego de deportes en dos dimensiones que simula un tenis de mesa. El jugador controla en el juego una paleta moviéndola verticalmente en la parte izquierda de la pantalla, y puede competir tanto contra un oponente controlado por computadora, como con otro jugador humano que controla una segunda paleta en la parte opuesta. Los jugadores pueden usar las paletas para pegarle a la pelota hacia un lado u otro. El objetivo consiste en que uno de los jugadores consiga más puntos que el oponente al finalizar el juego. Estos puntos se obtienen cuando el jugador adversario falla al devolver la pelota.

Este videojuego es un verdadero clásico y es sumamente adictivo para jugar durante breves intervalos de tiempo. Ya sea esperando un micro o taxi, alguna hora o minutos libres en la Universidad o Colegio, o simplemente de noche hasta que logramos conciliar el sueño.
Esta variante contiene tres niveles de dificultad aumentado la velocidad de desplazamiento de la pelota a medida que avanzamos de nivel. En el último nivel la pelota se desplaza muy rápidamente, no apto para cardíacos, y si logramos superar este nivel ocurre una secuencia que podrán observar en el gameplay que se encuentra al final de la publicación y solo apto para verdaderos fanáticos.

La Matriz de LEDs

Una matriz LEDs es un display formado por múltiples LEDs en distribución rectangular. Existen distintos tamaños, siendo el más habitual los cuadrados de 8×8 LEDs.
Podemos combinar varios módulos para formar un display mucho mayor. En estos display podemos mostrar textos, dibujos o animaciones, como desplazar un texto (scroll).
Por lo demás, son diodos LEDs totalmente normales, organizados en forma de matriz, que tendremos que multiplexar para poder iluminar uno u otro punto.
Si los diodos se unen por el positivo, se dice que son matrices de Ánodo común, y si se une por el negativo decimos que son de Cátodo común. Dependiendo del fabricante podemos encontrar de ambos tipos.

Encender una matriz de LEDs directamente con Arduino requiere emplear una gran cantidad de pines, lo cual supondría un gran desperdicio de recursos. Por este motivo, lo normal es siempre utilizar un controlador específicamente diseñado para esta función. Un controlador habitualmente empleado por ser barato y sencillo es el circuito integrado MAX7219.

Circuito Integrado MAX7219

Encender una matriz de 8×8 LED requeriría 16 señales digitales y un trabajo constante del procesador para refrescar la imagen. Eso es una cantidad enorme de recursos para cualquier autómata, que estaríamos desperdiciando para simplemente encender un display.
Por este motivo, utilizamos el circuito integrado MAX7219 que está especialmente diseñado para encender displays de 7 segmentos y matrices de LEDs, liberando al procesador para hacer tareas mucho más valiosas y productivas.
La comunicación con el circuito integrado MAX7219 se realiza mediante el bus SPI por lo que sólo se requieren 3 pines de Arduino (SS, MOSI y SCK). Además, ni siquiera “ocupamos” en su totalidad estos pines, ya que con el mismo bus podemos controlar múltiples dispositivos.
Por último, las placas MAX7219 generalmente incorporan un puerto de entrada y salida, de forma que podemos combinar múltiples controladores sin ninguna dificultad.
MAX7219 Datasheet

El diagrama esquemático de esta variante de Arduino Pong se observa en la siguiente imagen:

(click para ampliar)

Lista de Componentes
1 Resistencia 10KΩ 0.25W
1 Potenciómetro Lineal 10KΩ
1 Transistor NPN 2SC1815
2 Módulos con Matrices de LEDs 8×8 con Circuito Integrado MAX7219 c/u. Cátodo Común [Color Rojo en caso de aplicar Red Mod]
1 Buzzer Pasivo 5V
1 Placa Arduino Nanino
Librería LedControl desarrollada por wayoda

#include <LedControl.h>

#define DATA_PIN    12
#define CLOCK_PIN   11
#define CS_PIN      10
#define NUM_DEVICES 2
#define KNOB_PIN    A0
#define RANDOM_PIN  A1
#define SPEAKER_PIN 7

#define N_a   440
#define N_f   349
#define N_cH  523
#define N_eH  659
#define N_fH  698
#define N_gS  415

int step_col, step_row, subidas, OLD_X, sensor, row, col, sc, sr, dly;
int row_bar = 0;
int X = 4;
int cnt = 0;
int desarma = 0;

boolean showBars_enabled = false;

LedControl LC = LedControl(DATA_PIN, CLOCK_PIN, CS_PIN, NUM_DEVICES);

byte MSG[8 * 20];
byte SPACE[8] = {
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000
};
byte W[8] = {
    B00000000,
    B01000010,
    B01000010,
    B01000010,
    B01000010,
    B01011010,
    B01100110,
    B00000000
};
byte I[8] = {
    B00000000,
    B00111100,
    B00011000,
    B00011000,
    B00011000,
    B00011000,
    B00111100,
    B00000000
};
byte N[8] = {
    B00000000,
    B01000010,
    B01100010,
    B01010010,
    B01001010,
    B01000110,
    B01000010,
    B00000000
};
byte E[8] = {
    B00000000,
    B01111110,
    B01000000,
    B01111100,
    B01000000,
    B01000000,
    B01111110,
    B00000000
};
byte R[8] = {
    B00000000,
    B01111100,
    B01000010,
    B01111100,
    B01001000,
    B01000100,
    B01000010,
    B00000000
};

void loose()
{
    delay(80);

    for(row = 0; (row < 16); row++) {
        setROW(row, 0xFF);
        tone(SPEAKER_PIN, (row * 500), 10);
        delay(20);
    }

    delay(130);

    for(row = 15; (row >= 0); row--) {
        setROW(row, 0x00);
        tone(SPEAKER_PIN, (row * 500), 10);
        delay(20);
    }
}

void showBars(boolean show)
{
    showBars_enabled = show;

    if(show) {
        row_bar = (2 + random(5));
    }

    setROW(row_bar, (show == true ? 0xFF : 0x00));
}

void setLED(int l, int c, boolean state)
{
    int disp = 0;

    if(l > 7) {
        l = (l - 8);
        disp = 1;
    }

    LC.setLed(disp, l, c, state);
}

void setROW(int r, char ch)
{
    int disp = 0;

    if(r > 7) {
        r = (r - 8);
        disp = 1;
    }

    LC.setRow(disp, r, ch);
}

void copyLetter(byte *ch,int pos)
{
    int i;

    for(i = 0; (i < 8); i++) {
        *(MSG + i + (pos * 8)) = *( ch + i);
    }
}

void showMSG(int qtde, int tempo)
{
    int idx;

    LC.clearDisplay(0);
    LC.clearDisplay(1);

    for(idx = 0; (idx < (8 * qtde)); idx++) {
        for(row = 0; (row < 16); row++) {
            setROW(row, *(MSG + row + idx));
        }
		
        delay(tempo);
    }

    LC.clearDisplay(0);
    LC.clearDisplay(1);
}

void beep(int frequencyInHertz, long timeInMilliseconds)
{
    int x;
    long delayAmount = (long)(1000000 / frequencyInHertz);
    long loopTime = (long)((timeInMilliseconds * 1000) / (delayAmount * 2));

    for(row = 0; (row < 16); row++) {
        setROW(row,0xFF);
    }

    for (x = 0; (x < loopTime); x++) {
        digitalWrite(SPEAKER_PIN, HIGH);
        delayMicroseconds(delayAmount);
        digitalWrite(SPEAKER_PIN, LOW);
        delayMicroseconds(delayAmount);
    }

    LC.clearDisplay(0);
    LC.clearDisplay(1);

    delay(20);
}

void march()
{
    beep(N_a, 500);
    beep(N_a, 500);
    beep(N_a, 500);
    beep(N_f, 350);
    beep(N_cH, 150);

    beep(N_a, 500);
    beep(N_f, 350);
    beep(N_cH, 150);
    beep(N_a, 1000);

    beep(N_eH, 500);
    beep(N_eH, 500);
    beep(N_eH, 500);
    beep(N_fH, 350);
    beep(N_cH, 150);

    beep(N_gS, 500);
    beep(N_f, 350);
    beep(N_cH, 150);
    beep(N_a, 1000);
}

void winner()
{
    copyLetter(SPACE, 0);
    copyLetter(SPACE, 1);

    copyLetter(W, 2);
    copyLetter(I, 3);
    copyLetter(N, 4);
    copyLetter(N, 5);
    copyLetter(E, 6);
    copyLetter(R, 7);

    copyLetter(SPACE, 8);
    copyLetter(SPACE, 9);

    showMSG(9, 80);

    march();

    setup();
}

void setup() {
    pinMode(KNOB_PIN, INPUT);      // Potentiometer 10K Linear (GND, KNOB_PIN, +5v)
	pinMode(RANDOM_PIN, INPUT);    // Random Seed
    pinMode(SPEAKER_PIN, OUTPUT);  // Passive Buzzer

    LC.shutdown(0, false);
    LC.setIntensity(0, 8);
    LC.clearDisplay(0);

    LC.shutdown(1, false);
    LC.setIntensity(1, 8);
    LC.clearDisplay(1);

    randomSeed(analogRead(RANDOM_PIN) * millis());

    loose();

    dly = 500;

    setLED(15, X, true);
    setLED(15, (X + 1), true);
    setLED(15, (X + 2) ,true);
    OLD_X = (-1);
    subidas = 0;

    step_col = 1;
    step_row = 1;

    sc = step_col;
    sr = step_row;

    row = ((-1) + random(3));
    col = random(8);

    showBars(false);
}

void loop()
{
    sensor = analogRead(KNOB_PIN);

    X = map(sensor, 0, 980, 1, 6);

    if(X != OLD_X) {
        OLD_X = X;
        setROW(15, 0x00);
        setLED(15, (X - 1),true);
        setLED(15, X, true);
        setLED(15, (X + 1), true);
    }

    if(cnt == 0) {
        setLED(row, col, false);

        if((subidas == 1 || random(2) == 1) && showBars_enabled == false && row == 0) {
            showBars(true);
            subidas = 0;
            desarma = (2 + random(5));
        }

        if((subidas == desarma) && (showBars_enabled == true)) {
            showBars(false);
            subidas = 0;
        }

        if(col == 7) {
            sc = -step_col;
            tone(SPEAKER_PIN, 1000, 20);
        }

        if(col == 0) {
            sc = step_col;
            tone(SPEAKER_PIN, 1000, 20);
        }

        if(row == 0) {
            sr = step_row;
            subidas++;
            dly -= 5;
            tone(SPEAKER_PIN, 1000, 20);
        }

        if(dly <= 190) {
            winner();
        }

        if(showBars_enabled == true && sr > 0 && row < row_bar) {
            if(row == (row_bar - 1)) {
                sr = -step_row;
                tone(SPEAKER_PIN, 1000, 20);
            }
        }

        if(row == 14)
        {
            if(col >= (X - 1) && col <= (X + 1)) {
                sr = -step_row;
				tone(SPEAKER_PIN, 1500, 20);
			}
            else if(col == (X - 2) && (sc > 0)) {
                sr = -step_row;
                sc = -step_col;
                tone(SPEAKER_PIN, 1500, 20);
            }
            else if(col == (X + 2) && (sc < 0)) {
                sr = -step_row;
                sc = step_col;
                tone(SPEAKER_PIN, 1500, 20);
            }
        }

        row += sr;
        col += sc;

        if(col == 8) {
            col = 7;
        }

        if(col == (-1)) {
            col = 0;
        }

        setLED(row, col, true);

        if(row == 15) {
            setup();
        }
    }

    cnt++;

    if(cnt == dly) {
        cnt = 0;
    }
}

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

Red Mod
La cinta Kapton es resistente al calor y eléctricamente aislante que se usa mucho en electrónica e impresión 3D. Si has estado en esta afición por un tiempo hay una buena probabilidad de que tengas un rollo a mano.

Una o dos capas de cinta Kapton aplicada a las matrices las hacen menos propensas a lavarse bajo luz brillante. El color de la cinta es lo suficientemente similar a los LEDs rojos que brillan a través con poca dificultad, mientras que la mayoría de la luz ambiente está bloqueada.

La película de enmascarar “Rubylith” (empleada en diseño gráfico) probablemente funcionaría igual de bien, o quizás mejor. Este film puede conseguirse en tiendas de arte decentes de la vieja escuela.

Este destellador de potencia puede controlar lámparas hasta de 100W en la red de 110V y hasta 200W en la red de 220V, en función del puente de diodos, que en este caso es de 1A, para una lámpara de hasta 200W. Para lámparas de mayor potencia se debe reemplazar los diodos del puente rectificador teniendo en cuenta la siguiente ecuación:

P = V * I     (Potencia = Voltaje * Intensidad)

Donde P es la potencia de la lámpara a controlar, V es el voltaje de la red domiciliaria e I es la corriente que deben soportar los diodos del puente rectificador.

Por ejemplo, para una lámpara de 200W de potencia:
200W = 220V * I
Despejando I de la ecuación, tenemos:
I = (200W / 220V)
I = 0,90A

Para no llevar los componentes al límite de su capacidad (principalmente el puente rectificador), dejamos un margen de 20W.

Ahora analicemos el caso para una lámpara de 600W, el triple de potencia del ejemplo anterior:
600W = 220V * I
Despejando I de la ecuación, tenemos:
I = (600W / 220V)
I = 2,72A

Un total de casi 3A de corriente y una tensión de 220V. En este caso debemos reemplazar los diodos del puente rectificador 1N4007 (1000V – 1A) por otros diodos que toleren los valores calculados anteriormente, como por ejemplo: 1N5408 (1000V – 3A).

El tiristor SCR TIC106D soporta una tensión de trabajo de 400V y corriente de hasta 5A. Debe armarse con disipador de calor.
El control es de onda completa y el ajuste de frecuencia se hace con P1. C1 debe tener valores entre 470nF y 1uF y es de poliéster con una tensión de trabajo por lo menos de 400V.

Datasheet TIC106D

Lista de Componentes
1 Resistencia 47KΩ 0.25W
1 Resistencia 100KΩ 0.25W
1 Resistencia 1KΩ 0.25W
1 Potenciómetro Lineal para PCB 1MΩ
1 Tiristor SCR TIC106D
1 Disipador de Calor Aleta para encapsulado TO-220
1 Diodo de Conmutación Rápida 1N4148
1 Capacitor Poliéster 1uF 400V
1 Lámpara de Neón 90V (NO LED, sino el famoso “foquito naranja” que tenía la plancha de la abuela)
4 Diodos Rectificadores 1N4007, o un puente rectificador 1000V – 1A (tiene la apariencia de una pastilla negra)
2 Borneras de 2 Pines
1 Placa Para Prototipado de 5cm x 5cm

(click para ampliar)

Como primer paso, montaremos sin soldar los componentes, a modo de ubicarlos estratégicamente y nos resulte sencillo soldar formando las pistas de nuestro Destellador de Potencia. Tampoco debemos olvidar los correspondientes jumpers de interconexión entre componentes. Este primer paso también es fundamental para reducir el tamaño de nuestra placa en caso de no utilizarla totalmente.

(click para ampliar)

Ahora ya conocemos el espacio físico que ocuparán los componentes y las pistas que trazaremos posteriormente en el proceso de soldado, por lo tanto podemos redimensionar nuestra placa cortando con una sierra Junior la/s parte/s que no utilizaremos.

(click para ampliar)

A partir de este paso comenzaremos con el proceso de soldado e interconexión entre los componenetes. Generalmente es recomendable soldar primero los componentes de bajo perfil, así que soldaremos los jumpers.

(click para ampliar)

(click para ampliar)

Continuaremos soldando el diodo de conmutación rápida 1N4148.

(click para ampliar)

A continuación soldaremos las resistencias.

(click para ampliar)

(click para ampliar)

Seguiremos por soldar el puente rectificador.

(click para ampliar)

Ahora soldaremos la lámpara de neón.

(click para ampliar)

Soldaremos las borneras de conexión.

(click para ampliar)

El potenciómetro lineal.

(click para ampliar)

A continuación soldaremos el capacitor de poliéster de alta tensión.

(click para ampliar)

IMPORTANTE: El tiristor TIC106D debe ser dotado de un disipador de calor como se observa en la imagen de abajo. La grasa siliconada debe aplicarse en su justa medida (con una película delgada es suficiente), ya que en exceso produce el efecto contrario

(click para ampliar)

(click para ampliar)

Como paso final, soldaremos el tiristor y terminaremos nuestro montaje.

(click para ampliar)

Las pistas formadas y las interconexiones durante el proceso de soldado de los componentes se observan en la siguiente imagen:

(click para ampliar)

Algunas imágenes adicionales de nuestro montaje terminado.

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

Para el video he utilizado una lámpara de la línea “Philips Disco E27 60W” Color Verde 80mm de diámetro.

En publicaciones anteriores hemos visto diferentes mecanismos de control para nuestro robot, tanto manuales (Construyendo Nuestro Propio Robot Desde Cero – Parte 3) como autónomos (Arduino Robot Seguidor de Líneas y Laberintos). Ahora veremos una alternativa más, también inalámbrica, al igual que en la publicación anterior (Arduino Robot Controlado por Bluetooth y Android App) y que en estos últimos años ha crecido de manera exponencial en el mercado. El lector perspicaz ya advirtió que se trata de la tecnología Wi-Fi (Wireless Fidelity), omnipresente en el mercado y en nuestros hogares.

En cuanto empieces a buscar algo de Wi-Fi económico, te encontrarás con el módulo Wi-Fi ESP8266, fabricado por ESPRESSIF Systems, el cual es muy parecido a los módulos Bluetooth de la publicación anterior, y que al igual que ellos incluye toda la electrónica necesaria para la comunicación RF en la banda Wi-Fi, así como la pila TCP/IP y se comunica con nosotros a través de un puerto serie.

De hecho, exactamente al igual que los módulos Bluetooth HC-06 y HC-05, se gobierna mediante comandos AT, algo que ya no tiene secretos para los seguidores de este blog, y todo por un precio similar al de los módulos Bluetooth.
Lo primero, es decir que es un módulo muy sencillo y diseñado desde el principio con la “Internet of Things” en mente (IoT), y por eso incluye todo lo necesario para conectarse a un punto de acceso Wi-Fi o actuar por sí mismo como punto de acceso Wi-Fi mediante comandos AT, vía un puerto serie, que puede ser configurado a diferentes velocidades.

Este circuito integrado es una pequeña maravilla tecnológica. Está diseñado y pensado para ser la solución ideal para todo el que necesite un “Todo en uno Wi-Fi” para proyectos de hobby, IoT o producto comercial sin complicaciones. Y lo más importante, lo consigue.

  • Voltaje de alimentación: 3.3V
  • Procesador interno de 32 bits a 80 MHz (se puede incrementar hasta 160MHz).
  • 80KB de DRAM.
  • 35KB de IRAM, memoria rápida para el procesador.
  • 512KB (hasta 1MB, dependiendo de la versión del módulo) de memoria flash para nuestros programas.
  • Pila TCP/IP Wi-Fi a 2.4 GHz (b/g/n).
  • 30 metros de alcance teórico.

Nota del Autor: de hecho, el módulo ESP8266 incluye un pequeño procesador interno que podríamos programar para funcionar de modo autónomo y dispone de un par de puertos GPIO (General Purpose Input Output) para su uso como activadores. Es posible programarlo desde el IDE Arduino, y más interesante aún, con MicroPython.

Lista de Componentes
1 Plataforma Turtle2WD con placa Arduino UNO R3 y Shield de Control de Motores ensamblados anteriormente.
1 Adaptador Sparkfun FTDI USB – UART TTL 3.3V
1 Módulo Wi-Fi ESP8266 (ESP-01)
1 Convertidor Bidireccional de Niveles Lógicos Sparkfun.
1 Capacitor Electrolítico 1000uF 6.3V
2 Baterías AA 1.5V de Alto Rendimiento
1 Portapilas para 2 Baterías AA

Antes de instalar el módulo en nuestro robot, modificaremos algunos parámetros de su configuración mediante comandos AT. Para llevar a cabo esta tarea sólo necesitamos descargar el documento que contiene el set de instrucciones AT ESP8266 y un emulador de terminal RS232.

Set de Instrucciones AT ESP8266

Termite 3.3
Tamaño: 294KB
SHA1 Hash: 5A7DEBD9E1F245EB87FAEF397187752D5CFF1841
Descarga     Mirror

A continuación conectaremos nuestro módulo al Adaptador FTDI USB – UART TTL 3.3V como se muestra en la siguiente imagen:

(click para ampliar)

Ahora, el emulador de terminal (en nuestro caso, Termite) debe estar configurado de la siguiente manera, que son los parámetros por defecto de nuestro módulo Wi-Fi.

Habiendo logrado una conexión exitosa en el paso anterior, ahora debemos enviar los comandos AT necesarios para configurar nuestro módulo. En la siguiente imagen se observa la secuencia de comandos enviados:

AT
AT+CWSAP_DEF="TURTLE2WD","",1,0,4
AT+UART_DEF=19200,8,1,0,0

Nuestros comandos enviados se encuentran en color azul, en color verde se encuentran las respuestas de nuestro módulo y el eco local de la consola. Con la ejecución exitosa de estos comandos modificamos el SSID de nuestro módulo por “TURTLE2WD” para identificarlo fácilmente y redujimos la velocidad del puerto serie a 19200bps lo cual nos permite utilizar cables de mayor longitud para el montaje en caso de ser necesario.

Habiendo finalizado exitosamente los pasos anteriores, compilaremos y cargaremos en nuestro Arduino Uno R3 el sketch correspondiente, cuyo código fuente se encuentra a continuación:

/*
Caracteristicas:
- Control por Wi-Fi utilizando el modulo ESP8266
- Interfaz web con jquey para envio de peticiones GET
- Id de botones para envio de datos
W: Adelante
S: Atras
A: Izquierda
D: Derecha
L: Inicio/Parada Modo Vehiculo

Q: Incrementar Velocidad
T: Disminuir Velocidad
X: Encendido/Apagado Luz Frontal

- Detencion automatica en caso de detectar bordes.
*/

#define     PWM_RIGHT   3     // (Control Velocidad Motor Derecho) Timer2 8 bits D3
#define     PWM_LEFT    11    // (Control Velocidad Motor Izquierdo) Timer2 8 bits D11
#define     DIR_A       12    // (Control de Direccion de Motores) D12
#define     DIR_B       13    // (Control de Direccion de Motores) D13
#define     MAX_SPEED   200   // Velocidad Maxima
#define     MIN_SPEED   100   // Velocidad Minima
// -----

// Iluminacion
#define     LED_LIGHT   7
// -----

// Sensores Reflectivos Detectores de Bordes
#define     BORDER_L    A2    // Sensor Izquierdo Externo
#define     BORDER_R    A5    // Sensor Derecho Externo

// Serial commands
#define     DIR_FORWARD     'W'
#define     DIR_BACKWARD    'S'
#define     DIR_LEFT        'A'
#define     DIR_RIGHT       'D'
#define     START_STOP_V    'L'
#define     INC_SPEED       'Q'
#define     DEC_SPEED       'T'
#define     FRONT_LIGHT     'X'
// -----

#define     DEBUG   false

boolean stopped;

byte speed, connectionid;

char data;

void forward() {
    digitalWrite(DIR_A, LOW);
    digitalWrite(DIR_B, LOW);
}

void backward() {
    digitalWrite(DIR_A, HIGH);
    digitalWrite(DIR_B, HIGH);
}

void rotateleft() {
    digitalWrite(DIR_A, LOW);
    digitalWrite(DIR_B, HIGH);
}

void rotateright() {
    digitalWrite(DIR_A, HIGH);
    digitalWrite(DIR_B, LOW);
}

void stop() {
    analogWrite(PWM_LEFT, 0);
    analogWrite(PWM_RIGHT, 0);
}

void setspeed(boolean sp) {
    if (sp) {
        if (speed < MAX_SPEED) {
            speed += 50;
            analogWrite(PWM_LEFT, speed);
            analogWrite(PWM_RIGHT, speed);
        }
    }
    else
    {
        if (speed > MIN_SPEED) {
            speed -= 50;
            analogWrite(PWM_LEFT, speed);
            analogWrite(PWM_RIGHT, speed);
        }
    }
}

void setlight() {
    digitalWrite(LED_LIGHT, !(digitalRead(LED_LIGHT)));
}

boolean isborder() {
    if ((digitalRead(BORDER_L) == HIGH) || (digitalRead(BORDER_R) == HIGH))
        return true;
    else
        return false;
}

String sendData(String command, const word timeout, boolean debug) {
    String response = "";

    Serial.print(command); // send the read character to the esp8266

    unsigned long time = millis();

    while ((time + timeout) > millis()) {
        while(Serial.available()) {
            // The esp has data so display its output to the serial window
            char c = Serial.read(); // read the next character.
            response += c;
        }
    }

    if (debug)
        Serial.print(response);

    return response;
}

void setup() {
    pinMode(PWM_LEFT, OUTPUT);
    pinMode(PWM_RIGHT, OUTPUT);
    pinMode(DIR_A, OUTPUT);
    pinMode(DIR_B, OUTPUT);
    pinMode(LED_LIGHT, OUTPUT);

    pinMode(BORDER_L, INPUT);
    pinMode(BORDER_R, INPUT);

    digitalWrite(LED_LIGHT, LOW);

    stopped = true;

    speed = MIN_SPEED;

    Serial.begin(19200);

    sendData("AT+CIPMUX=1\r\n", 1000, DEBUG); // configure for multiple connections
    sendData("AT+CIPSERVER=1,80\r\n", 1000, DEBUG); // turn on server on port 80
}

void loop() {
    if (Serial.available()) {
        if (Serial.find("+IPD,")) {
            delay(100);
            connectionid = (Serial.read() - 48);

            Serial.find("cmd=");
            data = Serial.read();

            if (data == START_STOP_V) {
                if (stopped) {
                    forward();

                    analogWrite(PWM_LEFT, speed);
                    analogWrite(PWM_RIGHT, speed);

                    stopped = false;
                }
                else
                {
                    stop();
                    stopped = true;
                }
            }

            switch (data) {
                case DIR_FORWARD:
                    if (!stopped)
                        forward();
                    break;

                case DIR_BACKWARD:
                    if (!stopped)
                        backward();
                    break;

                case DIR_LEFT:
                    if (!stopped)
                        rotateleft();
                    break;

                case DIR_RIGHT:
                    if (!stopped)
                        rotateright();
                    break;

                case INC_SPEED:
                    if (!stopped)
                        setspeed(true);
                    break;

                case DEC_SPEED:
                    if (!stopped)
                        setspeed(false);
                    break;

                case FRONT_LIGHT:
                    setlight();
                    break;

                default:
                    break;
            }

            String closecommand = "AT+CIPCLOSE=" + String(connectionid) + "\r\n";
            sendData(closecommand, 100, DEBUG);
        }
    }

    if (isborder())
        stop();
}

En este punto nos encontramos en la etapa final. A continuación conectaremos nuestro módulo Wi-Fi a la placa Arduino UNO R3, como se observa en la siguiente imagen:

(click para ampliar)

Por último necesitamos la interfaz web desde la cual controlaremos nuestro robot. Consta de un documento HTML y jQuery, una biblioteca multiplataforma de JavaScript, que permite simplificar la manera de interactuar con los documentos HTML, manipular el árbol DOM, manejar eventos, desarrollar animaciones y agregar interacción con la técnica AJAX a páginas web. jQuery es la biblioteca de JavaScript más utilizada.

Interfaz Web
Tamaño: 27.5KB
SHA1 Hash: 0C9AE81B92C8243C362C2DFA94C79065D8D3447E
Descarga