martes, 14 de agosto de 2012

Tarea 2: Tautología

Para la Tarea 1, debemos crear una expresión lógica que sea una tautología y que ademas cumpliera con ciertos requisitos. Los requisitos a cumplir son:



  • Que cuente por lo menos con 3 variables.
  • Usar por lo menos 4 ocurrencias conectivas ( ands, ors)
  • Tener por lo menos un ^(and), un v(or), y un ¬(not).


  • Tautología

    En lógica, una tautología es una fórmula bien formada de un sistema de lógica proposicional que resulta verdadera para cualquier interpretación; es decir, para cualquier asignación de valores de verdad que se haga a sus fórmulas.

    Un ejemplo sencillo de una tautología es la expresión (p ^ q) -> p, como se puede ver:



    "Mi Tautología"

    La tautología que encontré fue:

    ¬(a ^ (b v c)) v (a v b)  

    Para encontrarla, inicie con una expresión sencilla, y fui agregando más términos hasta completar con las restricciones, despúes comprobé con la tabla de verdad si era o no tautología. Inicialmente no lo fue, ya que mis primeros intentos no arrojaban verdadero para todas las combinaciones, pero modificando poco a poco la expresión, logré convertirla en tautología.

    Diagrama de Árbol

    Estructurando la expresión como un árbol:
    Tabla de Verdad

    La tabla de verdad de la expresión anterior es la siguiente:

     a    b    c     b v c   a ^ (b v c)     ¬(a ^ (b v c)     a v b     ¬(a ^ (b v c)) v (a v b)    
     0  0  0 0 0101
    00110101
    01010111
    01110111
    10000111
    10111011
    11011011
    11111011


    Entonces sí S = ¬(a ^ (b v c)) v (a v b) es verdadera en todas las combinaciones posibles, podemos comprobar que:

      \vDash S 
    (S es una tautología)

    Referencias:

    martes, 7 de agosto de 2012

    Reporte 1 - Verificación y Validación de Software


    Verificación y Validación de Software

    Conjunto de procesos de comprobación y análisis que aseguran que el software que se desarrolla está acorde a su especificación y cumple las necesidades de los clientes. 
    Existen actividades de Verificación y Validación en cada etapa del proceso de desarrollo del software.
    Verificación:

    La Verificación de Software se hace la pregunta: ¿Estamos construyendo el producto correctamente?
    En ella se comprueba que el software cumple los requisitos funcionales y no funcionales de su especificación.

    La verificación de software es una disciplina más estrecha y compleja de la ingeniería de software cuyo objetivo es asegurar que el software pueda satisfacer completamente todos los requerimientos esperados.

    Hay dos acercamientos fundamentales en la verificación de software:

    • Verificación dinámica, también conocida como Prueba de Experimentación, útil para encontrar bugs.
    • Verificación estática, tambíen conocida como análisis. Ésta es útil para provar si un programa es correcto aunque puede resultar en falsos positivos.
    Validación:

    La Validación de Software se hace la pregunta: ¿Estamos construyendo el producto correcto?

    Comprueba que el software cumple las expectativas que el cliente espera. Nunca se va a poder demostrar que el software está completamente libre de defectos.
    Existen actividades de Verificación y Validación en cada etapa del proceso de desarrollo del software.

    Diferencia entre ambas

    La verificación y la validación no son la misma cosa:
    • Verificación: ¿Estamos construyendo el producto correctamente?
    El papel de la verificación comprende comprobar que el software está de acuerdo con su especificación. Se comprueba que el sistema cumple los requerimientos funcionales y no funcionales que se le han especificado.
    • Validación: ¿Estamos construyendo el producto concreto?
    La validación es un proceso mas general. Se debe asegurar que el software cumple las expectativas del cliente. Va mas allá de comprobar si el sistema está acorde con su especificación, para probar que el software hace lo que el usuario espera a diferencia de lo que se ha especificado.

    Es importante llevar a cabo la validación de los requerimientos del sistema de forma inicial. 

    Es fácil cometer errores y omisiones durante la fase de análisis de requerimientos del sistema y, en tales casos, el software final no cumplirá la expectativas de los clientes. Sin embargo, en la realidad, la validación de los requerimientos no puede descubrir todos los problemas que presenta la aplicación. Algunos defectos en los requerimientos solo pueden descubrirse cuando la implementación del sistema es completa.  


    Técnicas de comprobación y análisis
    • Las inspecciones del software analizan y comprueban las representaciones del sistema como el documento de requerimientos, los diagramas de diseño y y el código fuente del programa. Se aplica a todas las etapas del proceso de desarrollo. Las inspecciones se complementan  con algún tipo de análisis automático del texto fuente o de los documentos asociados. Las inspecciones del software y los análisis automatizados son técnicas de verificación y validación estáticas puesto que no requieren que el sistema se ejecute.
    • Las pruebas del software consiste en contrastar las respuestas de una implementación del software a series de datos de prueba y examinar las respuestas del software y su comportamiento operacional, para comprobar que se desempeñe conforme a lo requerido. Llevar a cabo las pruebas es una técnica dinámica de la verificación y validación ya que requiere disponer de un prototipo ejecutable del sistema.
    Debug


    Al proceso de eliminación de los errores que se descubren durante las fases de prueba se denomina 
    depuración. Entonoces, sí la verificación y validación establece la existencia de defectos en el programa,la depuración es el proceso que localiza el origen y corrige estos defectos.

    Los mejores depuradores buscan patrones en los resultados de las pruebas donde el defecto se detecta, y para localizar el defecto utilizan el conocimiento que tienen sobre el tipo de defecto, el patrón de salida, así como del lenguaje y proceso de programación. El conocimiento del proceso es importante. Los depuradores conocen los errores de los programadores comunes (como olvidad incrementar un contador, errores de direccionamiento de punteros) y los comparan contra los patrones observados.

    Localizar los fallos es un proceso complejo porque los fallos no necesariamente se localizan cerca del punto en que se detectan. Para localizar un fallo de un programa el programador responsable de la depuración tiene que diseñar programas de prueba  adicionales que repitan el fallo original y que ayudan a descubrir el origen del fallo.

    Las herramientas de depuración son habitualmente parte de las herramientas de apoyo al lenguaje y que sirven de base al compilador. 

    ¿Por qué existe la Verificación y Validación de Software?

    Para un sistema de gran importancia, como uno de votación electrónica, es conveniente que una autoridad independiente lleve a cabo las pruebas de verificación. Para sistemas de menor importancia, la verificación puede realizarse internamente.

    Las pruebas de verificación de los programas pueden comprender lo siguiente:
    • Probar los programas para asegurar que reúnen los estándares exigidos y ejecutan las tareas esperadas, incluyendo auditorías de código (ver infra). 
    • Asegurar que la documentación del sistema es la adecuada y esté completa. 
    • Verificar que el sistema es capaz de funcionar bajo las condiciones normales esperadas y potenciales condiciones adversas. 
    • Garantizar que se cuenta con medidas de seguridad y que estas se ajustan a los estándares establecidos. 
    • Asegurar que se cuenta con las debidas medidas de control de calidad. 
    Referencia:

    Idea de Proyecto - Where's Wally?

    Mi idea personal para proyecto de redes neuronales es una red neuronal que pueda detectar la presencia de el legendario Wally en una imágen.


    Típicamente el objetivo de este juego es encontrar al personaje en una imágen. La dificultad difiere, puede ser tan simple como encontrarlo entre algunas personas con ropas diferentes a él, o encontrarlo en un lugar lleno de colores y ropas similares a las suyas.


    Las características principales suelen ser, su camisa de rayas rojas con blancas, la gorra de invierno de los mismos colores, y su característica cara con gafas.

    Referencias

    martes, 29 de mayo de 2012

    Proyecto de Simulación - Air Hockey

    Descripción
    El hockey de aire (también llamado hockey de mesa, aerohockey o tejo) es un deporte en el cual dos personas compiten para anotar puntos en la portería contraria.


    En particular pensamos en el proyecto para simulación de colisiones, la velocidad del disco de hockey disminuyendo con la poca fricción de la mesa, y otros efectos físicos que mencionaremos más adelante.

    Por lo tanto las reglas del juego y el gameplay en sí no son en realidad importantes, aunque implementamos unas ligeras funcionalidades para incrementar la puntuación y anotar puntos.

    Teoría(Física)
    En este proyecto, tenemos tres efectos de física importantes que deseamos simular:
    • Colisiones. El disco choca con una pared-limitante, o con el mazo.
    • Fricción. El disco y los mazos(ambos pueden ser controlados con el mouse) deben de disminuir su velocidad una cantidad considerable al dejarlos moverse.
    • Fuerza. La velocidad y dirección del disco debe ser resultado de una "fuerza" aplicada con el mazo. 
    Para comprender esto, necesitamos saber como funcionan en mecánica todos estos fenómenos.
    Colisiones

    En cuanto a las colisiones, nos referimos a un tipo específico, las colisiones elásticas. En física, estas son colisiones entre dos o más cuerpos en las que éstos no sufren deformaciones permanentes durante el impacto. En una colisión elástica se conservan tanto el momento lineal como la energía cinética del sistema, y no hay intercambio de masa entre los cuerpos, que se separan después del choque.




    Ahora en nuestro caso, dos cuerpos de dos dimensiones chocaran(el disco y el mazo), y la velocidad total de cada uno debe ser separada en dos velocidades perpendiculares: una tangente a la normal común de la superficie de los cuerpos colisionando al punto de contacto, y la otra junto a la linea de colisión. Este gif obtenido de Wikipedia, explica perfectamente este fenómeno:
    Fricción


    Fricción es la fuerza resistiva generada por el movimiento de dos superficies que hacen contacto una con la otra. Es el producto indirecto de una de las cuatro fuerzas fundamentales conocidas. La fricción de un sistema es imposible de predeterminar estríctamente desde principios teóricos. Matemáticamente, la expresión para la fricción incluye una sola constante que incorpora todos los factores que puedan causarla, el coeficiente de fricción (COF). La ecuación es simplemente escrita  fx = μxF, donde fdefine la forma y la medida de la fricción, y F es la fuerza normal ejercida por ambas superficies, una sobre la otra.

    Todos los coeficientes de fricción son magnitudes escalares sin dimensiones. La fuerza fundamental responsable por la mayoría de la fricción es también la que permite la formación de los enlaces químicos, la fuerza electroestática. En una consideración inicial, podría decirse que la gravedad es la causa inicial de la fricción, debido a la fuerza que ejerce la gravedad hacia la tierra es la fuente de la variable F.




    En el hockey de mesa, es importante señalar que la fricción que estamos considerando, no es la del disco con la mesa, o el mazo con la mesa. Debido a que prácticamente el disco flota de un lado a otro con la mínima fuerza, la única fricción que estamos tomando en cuenta es la del aire que las mesas de hockey utilizan normalmente. Esta fuerza en contra del movimiento es bastante pequeña, pero existe, de otra forma el disco continuaría moviéndose indefinidamente.


    Herramientas
    • Python-Pygame
      • Pygame es un set de módulos de Python diseñado para escribir juegos. Pygame permite crear juegos y programas de multimedia en el lenguaje python. Es bastante portable y corre en prácticamente cualquier plataforma y sistema operativo.
    • ¿Por qué Pygame?
      • Al inicio no considerábamos Pygame como una opción, debido a que como la mayoría de personas, solamente por el nombre o por ver algunos ejemplos, podemos darnos a la idea de que Pygame es una librería para hacer juegos, y nada más. Pero buscando en Internet ,encontramos ejemplos bastantes interesantes de como aplicar fenómenos físicos a objetos dibujados por Pygame, lo cual prácticamente cumplía nuestras expectativas, lo que hizo que lo tomáramos en serio.
    Física en Pygame


    Para entender un poco el potencial de Pygame, se explicará a continuación algunas de las funciones que utilizamos para poder añadir un poco de realidad a la simulación.


    Limites y Rebotes


    Creando una simulación como la nuestra, deseamos que tenga un limite, en este caso sería la mesa, para evitar simular una región infinita. El ejemplo que utilizamos para esto, es una pared virtual en la cual las partículas rebotan.


    Lo primero que nuestra función de rebote debe tener sería verificar si una partícula paso un cierto límite. Los cuatro limites en una ventana serían entonces:
    • x = 0 (pared izquierda)
    • x = ancho (pared derecha)
    • x = 0 (parte superior)
    • x = alto (parte inferior)
    Ya que la simulación tiene pasos de tiempo discretos, es difícil verificar una partícula en el punto exacto en el que toca el límite, pero es posible verificar si ha viajado un poco después del límite. Si la velocidad de la partícula es baja, entonces la partícula tiene poca probabilidad de haber pasado muy lejos del limite(la distancia máxima que habrá excedido después del límite es entonces, igual a la velocidad. En este caso ignoraremos éste fenómeno, y nos concentraremos en simplemente reflectar el ángulo de la partícula lo más precisamente posible.

    Entonces cuando una partícula excede el límite, calculamos cuanto fue lo que excedió. Por ejemplo, sí el valor de x de la partícula es mayor al ancho de la ventana menos el tamaño de la partícula, ha superado el límite derecho, entonces calculamos el excedente:


    d = self.x - (width - self.size)
    

    Y reflejamos la posición en el límite (rebotando) poniendo las coordenadas x de la siguiente manera:


    self.x = (width - self.size) - d
    

    Esto puede ser simplificado para que no necesitemos la variable d, de la siguiente manera:


    self.x = 2*(width - self.size) - self.x


    La parte más importante de rebotar una partícula, es reflejar el ángulo en el límite, que es donde nuestros vectores empiezan a ser útiles(aunque es más útil cuando los límites no son lineares). Un limite vertical tiene un ángulo de 0, mientras que uno horizontal tiene un ángulo de pi. Entonces reflejamos verticalmente partículas que reboten, restando su ángulo actual de 0, y por pi en el caso de rebotes horizontales.

    La función de rebote entonces debe quedarnos así:


    def bounce(self):
    
        if self.x > width - self.size:
           self.x = 2*(width - self.size) - self.x
           self.angle = - self.angle
    
       elif self.x < self.size:
            self.x = 2*self.size - self.x
            self.angle = - self.angle
    
        if self.y > height - self.size:
            self.y = 2*(height - self.size) - self.y
            self.angle = math.pi - self.angle
    
        elif self.y < self.size:
            self.y = 2*self.size - self.y
            self.angle = math.pi - self.angle
    
    
    

    Ahora podemos llamar a la función rebote, después de cada movimiento, pero antes de mostrarlo en pantalla para poder comprobar si hay un rebote en las partículas.


    Movimiento


    Casi todas las simulaciones tienen valores que cambian con el tiempo. Un valor que cambia bastante seguido en nuestra simulación, es la posición de un objeto(el disco y el mazo), en el caso de que este en movimiento.
    Para nuestra simulación hicimos 2 cosas a estos objetos:
    1. Dar a los objetos velocidad y dirección
    2. Usar trigonometría básica para convertir los vectores en movimiento.
    Como se mencionó anteriormente, nuestra simulación es en tiempo discreto, lo que significa que dividimos el tiempo en pasos individuales. En cada paso actualizamos la simulación un poco para después mostrar la nueva situación. Entonces seguimos actualizando la simulación hasta que el usuario salga.

    La forma más simple de representar el movimiento es crear dos atributos, dx y dy. Entonces cada paso por el ciclo principal del programa, añadir dx a x y dy a y. Entonces, por ejemplo, si una particula tiene dx = 2 y dy = 1, seguira una diagonal desde la izquierda a la derecha, y desde arriba hacia abajo. Éste es un método simple y correcto para la mayoría de las situaciones. Pero cuando necesitamos trabajar en la interacción entre dos objetos, las cosas se vuelven más complejas.

    El otro método es entonces crear atributos para representar la rapidez y dirección (velocidad). Esto requiere un poco más de trabajo inicial, pero hace todo más simple. El método es bueno también para crear objetos con velocidad constante pero dirección variada. Entonces para darle velocidad a nuestra partícula, necesitamos darle dos atributos:

    self.speed = 0.01
    self.angle = 0
    

    Ya que haremos cálculos de trigonometría, haremos uso del módulo math de python, para el uso de funciones como sin y cos para calcular senos y cosenos.
    Ahora debemos agregar una función move a nuestro objeto. La función debería entonces cambiar las coordenadas x, y de la partícula basada en la velocidad y el ángulo de movimiento. Entonces podemos calcular el cambio en x y y, como muestra el diagrama:

    The mathematics of movement

    Es mas simple considerar un ángulo de 0 cuando apunta hacia arriba, a pesar del hecho de que el eje y realmente apunta hacia abajo en gráficos de computadora.

    Para calcular el cambio en x y y, usamos algunas funciones trigonométricas de nivel de secundaria como se puede observar:

    def move(self):
    
    self.x += math.sin(self.angle) * self.speed
    
    self.y -= math.cos(self.angle) * self.speed
    

    Otro punto a tomar en cuenta es que los ángulos están todos en radiantes. Entonces debemos de tomar en cuenta que 1 radian es 180°/pi. Entonces si queremos que una partícula se mueva hacia adelante(izquierda o derecha), el ángulo debería ser pi/2 = 90°. En python podemos hacer eso usando math también, de la siguiente manera:

    self.angle = math.pi/2
    
    De esta forma podemos llamar la función move dentro del ciclo principal del programa, antes de llamar la función que dibuja los elementos
    Colisiones


    Crear partículas con movimiento, y que tengan un limite es bastante interesante, pero no es suficiente. En el hockey de mesa un punto importante son las colisiones entre el disco y el mazo, por lo cual es imprescindible agregar ese tipo de interacción al programa. Entonces lo que necesitábamos hacer era:
    • Checar si dos partículas han chocado
    • Hacer que dichas partículas reboten
    Las cosas se empiezan a poner difíciles cuando tenemos múltiples partículas interactuando unas con otras. De hecho, es matemáticamente imposible resolver las ecuaciones que describen tres o más objetos que interactuan. En nuestra simulación se hicieron simplificaciones, que aunque son un tanto imprecisas, representan una razonable aproximación a la realidad.

    Primero para probar si dos partículas se sobreponen unas a otras, necesitamos comparar cada partícula con las demás. Podemos hacer éso con un simple for anidado, justo después de moverlos y checar si han tocado un límite, pero antes de dibujarlos en la pantalla:

    for i, particle in enumerate(my_particles):
        particle.move()
        particle.bounce()
        for particle2 in my_particles[i+1:]:
            collide(particle, particle2)
        particle.display()
    

    El segundo for anidado no es un completo, es decir no iteramos todas las partículas. Si tuviéramos dos ciclos completos, compararíamos cada partícula con las demás dos veces cada una(además de compararla consigo mismo). En cambio, comparamos cada partícula con cada partícula con un índice superior que ella en el arreglo. Entonces necesitamos saber el índice de la partícula actual, el cual podemos conseguir usando enumerate. Usamos el valor de i para cortar el arreglo desde i+1 hasta el final para construir el segundo for.
    Ahora podemos definir la función colisión. La primera cosa que la función debe hacer es checar si dos partículas han colisionado. Esto es muy simple debido a que las partículas son ambos círculos en el caso del hockey de mesa, pero contienen un sprite superpuesto. Entonces para verificar la colisión, medimos la distancia entre ellas, utilizando math.hypot(), y probamos si el valor es menor que sus radios combinados.
    def collide(p1, p2):
        dx = p1.x - p2.x
        dy = p1.y - p2.y
        distance = math.hypot(dx, dy)
        if distance < p1.size + p2.size:
            print 'Chocaron!'
    

    Hasta esta parte sería suficiente con imprimir algo en caso de que esto sea cierto.
    Como mencionamos en la teoría, cuando dos partículas circulares chocan, hacen contacto en un punto infinitamente pequeño. El ángulo de este punto es la tangente de la partícula en este punto. Entonces el ángulo de salida es perpendicular a la linea que se une el centro de las dos partículas.
    Entonces podemos tratar la colisión como si las partículas estuvieran rebotando con una superficie plana con el ángulo de la tangente. Podemos encontrar dicho ángulo con la siguiente fórmula:
    tangent = math.atan2(dy, dx)
    
    Para reflejar el ángulo de las partículas en la superficie, restamos el ángulo actual menos dos veces la tangente, para lo cual necesitamos conocer el ángulo en el que nuestra partícula viajaba, el mismo que usamos en el movimiento.


    p1.angle = 2*tangent - p1.angle
    p2.angle = 2*tangent - p2.angle
    



    Entonces necesitamos cambiar las velocidades de las dos partículas, debido a que transfieren las energías uno a otro. Podemos hacer esto con una simple tupla.


    (p1.speed, p2.speed) = (p2.speed, p1.speed)
    


    Entonces podemos agregar una elasticidad para reducir la energía de ambas partículas después de la colisión.


    p1.speed *= elasticity
    p2.speed *= elasticity
    Código


    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    # Referencias:
    # Imagen de fondo obtenida de https://engineering.purdue.edu/477grp6/images/lrprice/air_hockey_concept.png
    # 
    # Código basado en el tutorial http://razonartificial.com/tutoriales-pygame/
    # Código basado en el tutorial http://www.petercollingridge.co.uk/book/export/html/6
    # Código basado en el tutorial http://www.petercollingridge.co.uk/pygame-physics-simulation/mouse-interactions
    
    #modulos
    import sys, pygame, random, math
    from pygame.locals import *
    
    #Constantes
    width = 800
    height = 600
    handles = []
    pucks = []
    nHandles = 2
    npocks = 1
    puntuacion = [0,0]#jugador A, jugador B
    
    #Clases
    class Puck(pygame.sprite.Sprite):
            def __init__(self, x, y, id_):
                self.inicial = [x,y]
         self.anterior = [0, 0]
         self.cambio = [0.0, 0.0, 0.0]#X, Y y Contador
         self.id_ = id_
                pygame.sprite.Sprite.__init__(self)
                self.image = load_image("images/ball.png", True)
                self.diametro = width*0.07
                self.image = pygame.transform.scale(self.image, (int(self.diametro) ,int(self.diametro) ))
                self.rect = self.image.get_rect()
                self.rect.centerx = x
                self.rect.centery = y
                self.speed = [0.8, 0.8]
         self.coeficiente_arrastre = 0.001
                self.acceleration =  [0, 0]
         self.velocidad_juego = 4
    
            def actualizar(self, time, bandera):
                if bandera == self.id_:
      if self.cambio[2] == 15: 
          self.cambio[2] = self.cambio[1] = self.cambio[0] = 0
    
                    pos = pygame.mouse.get_pos() 
                    self.rect.centerx = pos[0]
                    self.rect.centery = pos[1]
      
      self.cambio[2] = self.cambio[2]+1
      self.speed[0] = (self.cambio[0] + (float(pos[0] - self.anterior[0]))/(time*16) )
      self.speed[0]/self.cambio[2]
      self.speed[1] = (self.cambio[1] + (float(pos[1] - self.anterior[1]))/(time*16) )
      self.speed[1]/self.cambio[2]
    
                    self.anterior[0] += self.speed[0] * time
                    self.anterior[1] += self.speed[1] * time
      
                else:
                    self.cambio[2] = self.cambio[1] = self.cambio[0] = 0
                    self.anterior[0] = self.rect.centerx
                    self.anterior[1] = self.rect.centery
    
      self.speed[0] = self.speed[0]*(1-self.coeficiente_arrastre)
      self.speed[1] = self.speed[1]*(1-self.coeficiente_arrastre)
                    self.rect.centerx += self.speed[0] * time
                    self.rect.centery += self.speed[1] * time
    
                    if (self.rect.left <= 0 or self.rect.right >= width) and (self.rect.bottom <= height*(1.0/3) or self.rect.top > height*(2.0/3) ):
                        self.speed[0] = - self.speed[0]
                        self.rect.centerx += self.speed[0] * time
      elif (self.rect.right < 0 or self.rect.left > width) and not (self.rect.bottom <= height*(1.0/3) or self.rect.top > height*(2.0/3) ):
    
                        if self.rect.centerx <= 0:
              puntuacion[0] = puntuacion[0] + 1
                        if self.rect.centerx > width:
              puntuacion[1] = puntuacion[1] + 1
    
          self.rect.centerx = self.inicial[0]
          self.rect.centery = self.inicial[1]
          self.speed[0] = self.speed[1] = 0
    
                    if self.rect.top <= 0 or self.rect.bottom >= height:
                        self.speed[1] = -self.speed[1]
                        self.rect.centery += self.speed[1] * time
    
                    for i in range(len(handles)):
                        if  math.hypot((handles[i].rect.centerx - self.rect.centerx), (handles[i].rect.centery - self.rect.centery) ) <= (self.diametro):
    
                            angulo = math.atan2(self.rect.centery - handles[i].rect.centery, self.rect.centerx-handles[i].rect.centerx )
       angulo_invertido = math.atan2(-self.speed[1], -self.speed[0])
       angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
       self.speed[1] = math.sin(angulo_reflexion)
       self.speed[0] = math.cos(angulo_reflexion)
    
                    for i in range(len(pucks)):
                        if math.hypot((pucks[i].rect.centerx - self.rect.centerx), (pucks[i].rect.centery - self.rect.centery) ) <= (self.diametro) and pucks[i].id_ != self.id_ :
                            angulo = math.atan2(self.rect.centery - pucks[i].rect.centery, self.rect.centerx-pucks[i].rect.centerx )
       angulo_invertido = math.atan2(-self.speed[1],-self.speed[0])
       angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
       self.speed[1] = math.sin(angulo_reflexion)
       self.speed[0] = math.cos(angulo_reflexion)
    
    
    class Handle(pygame.sprite.Sprite):
            def __init__(self, x, y, id_):
                self.inicial = [float(x), float(y)]
         self.anterior = [0.0, 0.0]
         self.cambio = [0.0, 0.0, 0.0]#X, Y y Contador
                self.id_ = id_
                pygame.sprite.Sprite.__init__(self)
                self.image = load_image("images/handle.png", True)
                self.diametro = width*0.15
                self.image = pygame.transform.scale(self.image, (int(self.diametro) ,int(self.diametro) ))
                self.rect = self.image.get_rect()
                self.rect.centerx = float(x)
                self.rect.centery = float(y)
                self.speed = [0.2, 0.2]
         self.coeficiente_arrastre = 0.012
                self.acceleration =  [0, 0]
    
            def inteligencia(self, time):
                if pucks[0].rect.centerx <= width:
                    if pucks[0].rect.centerx < self.rect.centerx :
                        self.rect.centerx += 5
      else:
                        self.rect.centerx -= 5
    
                    if pucks[0].rect.centery < self.rect.centery :
                        self.rect.centery += 5
      else:
                        self.rect.centery -= 5      
         else:
                    if pucks[0].rect.centerx < self.rect.centerx :
                        self.rect.centerx += 5
      else:
                        self.rect.centerx -= 5
    
                    if pucks[0].rect.centery < self.rect.centery :
                        self.rect.centery += 5
      else:
                        self.rect.centery -= 5
    
     
         if self.rect.left <= 0:
                    self.rect.centerx = width/2
         if self.rect.right >= width/2:
                    self.rect.centerx = width/2
    
         if self.rect.top <= 0:
      self.rect.centery = 0 
                if self.rect.bottom >= height:
      self.rect.centery = height
    
    
            def actualizar(self, time, bandera):
                if bandera == self.id_:
      if self.cambio[2] == 15: 
          self.cambio[2] = self.cambio[1] = self.cambio[0] = 0
                    pos = pygame.mouse.get_pos() 
                    self.rect.centerx = pos[0]
                    self.rect.centery = pos[1]
      
      self.cambio[2] = self.cambio[2]+1
      self.speed[0] = (self.cambio[0] + (float(pos[0] - self.anterior[0]))/(time*16) )
      self.speed[0]/self.cambio[2]
      self.speed[1] = (self.cambio[1] + (float(pos[1] - self.anterior[1]))/(time*16) )
      self.speed[1]/self.cambio[2]
    
                    self.anterior[0] += self.speed[0] * time
                    self.anterior[1] += self.speed[1] * time
      
                else:
                    self.cambio[2] = self.cambio[1] = self.cambio[0] = 0
                    self.anterior[0] = self.rect.centerx
                    self.anterior[1] = self.rect.centery
    
      self.speed[0] = self.speed[0]*(1-self.coeficiente_arrastre)
      self.speed[1] = self.speed[1]*(1-self.coeficiente_arrastre)
                    self.rect.centerx += self.speed[0] * time
                    self.rect.centery += self.speed[1] * time
    
                    if self.rect.left <= 0 or  self.rect.right >= width:
                        self.speed[0] = - self.speed[0]
                        self.rect.centerx += self.speed[0] * time
    
                    if self.rect.top <= 0 or self.rect.bottom >= height:
                        self.speed[1] = -self.speed[1]
                        self.rect.centery += self.speed[1] * time
    
    
         if self.rect.left <= width/2:
                    self.rect.centerx = width/2
    
                    for i in range(len(handles)):
                        if  math.hypot((handles[i].rect.centerx - self.rect.centerx), (handles[i].rect.centery - self.rect.centery) ) <= (self.diametro/2) and handles[i].id_ != self.id_ :
    
                            angulo = math.atan2(self.rect.centery - handles[i].rect.centery, self.rect.centerx-handles[i].rect.centerx )
       angulo_invertido = math.atan2(-self.speed[1], -self.speed[0])
       angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
       self.speed[1] = math.sin(angulo_reflexion)
       self.speed[0] = math.cos(angulo_reflexion)
    
                    for i in range(len(pucks)):
                        if math.hypot((pucks[i].rect.centerx - self.rect.centerx), (pucks[i].rect.centery - self.rect.centery) ) <= (self.diametro/2) :
                            angulo = math.atan2(self.rect.centery - pucks[i].rect.centery, self.rect.centerx-pucks[i].rect.centerx )
       angulo_invertido = math.atan2(-self.speed[1],-self.speed[0])
       angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
       self.speed[1] = math.sin(angulo_reflexion)
       self.speed[0] = math.cos(angulo_reflexion)
    
    #Funciones
    
    def select_handle(vector):
        pos = pygame.mouse.get_pos()
        for i in range(len(vector)):
            if  math.hypot(int(vector[i].rect.centerx-pos[0]), int(vector[i].rect.centery-pos[1]) ) <= (vector[i].diametro/2):
                return vector[i].id_
        return -1
    
    ##########################################
    # Carga la imagen y la convierte al formato
    # optimizado de pygame
    ##########################################
    def load_image(filename, transparent=False):
                try: image = pygame.image.load(filename)
                except pygame.error, message:
                        raise SystemExit, message
                image = image.convert()
                if transparent:
                        color = image.get_at((0,0))
                        image.set_colorkey(color, RLEACCEL)
                return image
    
    def main():
        screen = pygame.display.set_mode((width, height))
        pygame.display.set_caption("Clase de simulación")
    
        background_image = load_image("images/background.png")
        fuente = pygame.font.Font(None, 40)
        fuente_a = pygame.font.Font(None, 80)
        fuente_b = pygame.font.Font(None, 80)
    
        puntos_a = fuente_a.render('0', True, (255, 255, 255), (159, 182, 205))
        puntos_a_r = puntos_a.get_rect()
    
        puntos_b = fuente_b.render('0', True, (255, 255, 255), (159, 182, 205))
        puntos_b_r = puntos_b.get_rect()
    
        principal = fuente.render('', True, (255, 255, 255), (159, 182, 205))
        principal_r = principal.get_rect()
    
        principal_r.centerx = 400
        principal_r.centery = 160
    
        puntos_a_r.centerx = 100
        puntos_a_r.centery = 90
    
        puntos_b_r.centerx = 700
        puntos_b_r.centery = 90
    
        clock = pygame.time.Clock() 
        screen = pygame.display.set_mode((width, height))
        bandera = -1
    
    #    for i in range(nHandles):
        handles.append( Handle(random.randrange(0,width), random.randrange(0,height), 0))
        handles.append( Handle(random.randrange(0,width), random.randrange(0,height), 1))
    
        for i in range(nHandles, nHandles+npocks):
         pucks.append( Puck(random.randrange(0,width), random.randrange(0,height), i) )
    
        while True:
            time = clock.tick(60)
            for event in pygame.event.get():
                if event.type == QUIT:
                    sys.exit(0)
                if event.type == pygame.MOUSEBUTTONDOWN:
                    pos = pygame.mouse.get_pos()
                    bandera = select_handle(handles)
      if bandera < 0: 
       bandera = select_handle(pucks)
                elif event.type == pygame.MOUSEBUTTONUP:
                    bandera = -1
    
            # En esta parte se ponen las cosas que se redibujan
    
    #        for i in handles:
    #            i.actualizar(time, bandera)
    #            screen.blit(i.image, i.rect)
    
            screen.blit(background_image, (0, 0))
            handles[0].inteligencia(time)
            handles[1].actualizar(time, bandera)
    
            for i in pucks:
                i.actualizar(time, bandera)
                screen.blit(i.image, i.rect)
    
     puntos_a = fuente_a.render(str(puntuacion[0]), True, (255, 255, 255), (159, 182, 205))
     puntos_b = fuente_a.render(str(puntuacion[1]), True, (255, 255, 255), (159, 182, 205))
    
            screen.blit(handles[1].image, handles[1].rect)
            screen.blit(handles[0].image, handles[0].rect)
     screen.blit(puntos_a, puntos_a_r)
     screen.blit(puntos_b, puntos_b_r)
     screen.blit(principal, principal_r)
    
            #-------------------> Aqui se termian de redibujar los componentes
            pygame.display.flip()
            #-------------------> Aqui termina el while 
    
        return 0
    
    
    if __name__ == '__main__':
        pygame.init()
        main()
    


    Referencias:

    miércoles, 23 de mayo de 2012

    Wiki Contributions Week 16 - Final

    Autoevaluation

    Starting the semester, everyone was joining groups which would be working on different things that would help to build the project. I joined the CUDA group, not knowing that I needed a CUDA enabled GPU, which I obsiously don't have. So I started trying to help there, looking for good sources and information which could help the others in the group, but I kinda felt like I wasn't doing much, so I tried another alternatives. Then I started to program some MPI examples, and others stuff, just to get a feel of how MPI programming works, but not joining another group in the process. There I learned some interesting stuff using mpi4py, but still not doing a great progress.

    Finally, somehow I ended with the cluster group, but I didn't know anything about what they were doing, and I was supposed to do a Web Interface for the Cluster in Ruby, which resulted in a failure because I couldn't do any notable progress, which is entirely my fault, and because of that, others had to do my part.
    In conclusion, I didn't do anything to add to the cluster.Period.

    Nominations

    Juan Carlos, Rafael, and Roberto the only ones with own initiative.

    Proyecto de Móviles - Emmergency

    lunes, 21 de mayo de 2012

    Proyecto de Integrados - Arduino Facebook RSS Reader

    Descripción:

    Mediante una pequeña pantalla LCD de 16x2, el Arduino muestra las notificaciones de un determinado usuario de Facebook(en este caso yo). Estas notificaciones son descargadas por un script de Python, que usando la librería feedparser graba las últimas 10 notificaciones, y las envía mediante conexión serial al Arduino, que después las mostrará en el LCD.

    Materiales:
    • Arduino UNO R3
    • Protoboard
    • Display LCD 16x2(JHD 162A)
    • Potenciómetro rotacional lineal.
    • Cable para conexiones
    • Cable USB-Arduino
    Diagrama de Conexiones

    Circuito

    Para conectar el display LCD al arduino, se conectan los siguientes pines:
    • Pin LCD RS al pin digital 12
    • Pin LCD Enable al pin digital 11
    • Pin  LCD D4 al pin digital 5
    • Pin  LCD D5 al pin digital 4
    • Pin  LCD D6 al pin digital 3
    • Pin  LCD D7 al pin digital 2
    • Pin LCD Bklt - a GND.
    • Pin LCD Bklt + al pin 13 del Arduino.
    Adicionalmente, se conecta un potenciómetro de rotación lineal a +5V y GND del Arduino, con su salida al pin VO(pin 3) del display LCD.

    Funcionamiento

    Funcionamiento del Display LCD

    El LCD tiene un interfaz paralelo, significando esto que el microcontrolador tiene que manipular varios pines del interfaz a la vez para controlarlo. El interfaz consta de los siguientes pines:
    • Un pin de selección de registro (RS) que controla en qué parte de la memoria del LCD estás escribiendo datos. Puedes seleccionar bien el regisro de datos, que mantiene lo que sale en la pantalla, o un registro de instrucción, que es donde el controlador del LCD busca las instrucciones para saber cual es lo siguiente que hay que hacer.
    • El pin de lectura/escritura (R/W)que selecciona el modo de lectura o el de escritura.
    • Un pin para habilitar (enable) que habilita los registros.
    • 8 pines de datos (D00-D07). Los estados de estos pines (nivel alto o bajo) son los bits que estás escribiendo a un registro cuando escribes, o los valores de lectura cuando estás leyendo.
    Hay también un pin de contraste del display (Vo), pines de alimentación (+5V y GND) y pines de retro-iluminación (Bklt+ y Bklt-), que te permiten alimentar el LCD, controlar el contraste del display, o encender y apagar la retro-iluminación, respectivamente.

    El proceso de controlar el display involucra la colocación de los datos que componen la imagen de lo que quieres mostrar, en los registros de datos, y luego, colocar las instrucciones, en el registro de instrucciones. La librería LiquidCrystal te simplifica todo este proceso de forma que no neesitas saber las instrucciones de bajo nivel.

    Los LCD-s compatibles con Hitachi pueden ser controlados de dos modos: 4 bits u 8 bits. El modo de 4 bits requiere siete pines de E/S de Arduino, mientras el modo de 8 bits requiere 11 pines. Para mostrar texto en la pantalla, puedes hacer la mayoría de las cosas en modo 4 bits.

    Funcionamiento del Arduino
    El Arduino sirve de interfaz entre el script de python que descarga las notificaciones, y como se muestran en el display. El IDE de Arduino tiene una librería llamada LiquidCrystal, que ayuda bastante al manejo de un display conectado con el Arduino. Esta librería permite a la placa Arduino controlar displays LCD basados en el chipset Hitachi HD44780 (o compatibles), que se encuentra en la mayoría de LCDs de texto. La biblioteca trabaja en modo 4-bit o en 8-bit (es decir, por medio de 4 u 8 líneas de datos, además de RS, ENABLE, y, opcionalmente, las líneas de control RW).

    Contiene funciones como:
    • begin(). Usada para definir las dimensiones del display.
    • clear(). Usada para borrar los contenidos del display.
    • write(), print(). Usadas para mostrar contenido en el display.
    • setCursor(). Usada para definir donde escribir en el display.
    También cuenta con una función para poder hacer scroll al texto, es decir moverlo fuera de la pantalla para poder mostrar largas cadenas de texto que normalmente no podrían ser mostradas. Pero yo no hice uso de esta función, ya que el scroll que hace es total, y al mover todo el texto, cosas que yo deseo sean estáticas también se moverían por lo tanto tuve que implementar un scroll propio, que será explicado el el código.

    Entonces, la función del Arduino es recibir las notificaciones desde el script de python mediante una conexión serial, para después imprimirlas en la pantalla LCD. La conexión serial me permite enviar una cadena de texto u otra información char por char, entonces para que el Arduino pueda identificar que información se está enviando, antes de enviar cualquier cosa, el primer char es un identificador, como la letra "N" para las notificaciones, o la letra "T" para actualizar el reloj. Con esto podemos asegurarnos que no se confunda la información. 

    Funcionamiento del Código Python

    El código en Python es un script simple, que usando la librería feedparser, descarga las notificaciones de mi Facebook personal(copiando el link rss de mi Facebook, por lo cual EN EL CÓDIGO QUE PUBLICARÉ SERÁ OMITIDO, Y ES IMPORTANTE SI DESEAN PROBARLO ESPECIFICAR SU PROPIO LINK RSS DE FACEBOOK).

    Después de descargarlas, les realizo varias funciones a cada notificación, como quitarles acentos para evitar problemas en el display, cortarlos a un tamaño que pueda ser mostrado, y demás.Primero el código establece la conexión con el Arduino, después actualiza la hora con la hora de la computadora, entonces las notificaciones son enviadas al Arduino. Y el código espera hasta que se cumple un tiempo determinado(5 minutos aproximadamente por defecto) para poderse reiniciar y actualizar las notificaciones.


    Imágenes del Circuito en Funcionamiento





    Video


    Código Python:

    #!usr/bin/python
    import serial, time
    import feedparser
    import unicodedata
      
    #Funcion usada para quitar los acentos de las notificaciones, para poder evitar errores al
    #mostrar caracteres en el display LCD
    def reemplazar_acentos(s):
       return ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn'))
     
    #Funcion que maneja operaciones para cortar las notificaciones a una longitud que pueda ser
    #mostrada por el display LCD.
    def cortar_notificacion(notif):
     string = ""
     list_string = notif.split()
      
     if "evento" in notif:
      for i in range(len(list_string)):
       if list_string[i] == "evento":
        string+=list_string[i]
        string+=" "
        break
       else:
        string+=list_string[i]+" "
      string+=list_string[i+1]+" "
      string+=list_string[i+2]+"."
      
      
     elif "solicitud" in notif:
      for i in range(len(list_string)):
       if list_string[i] == "solicitud":
        string+=list_string[i]
        string+="."
        break
       else:
        string+=list_string[i]+" "
       
     else:
      for i in range(len(notif)):
       if notif[i] == ":":
        string+="."
        break
       elif notif[i] == ".":
        string+=notif[i]
        break
       else:
        string+=notif[i]
     if len(string) > 60:
      new_string = ""
      for i in range(57):
       new_string+=string[i]
      new_string+="..."
      string = new_string
     return string
     
    #Funcion que descarga las notificaciones y las guarda en una lista, con el string de la notificacion, y su fecha(no usada actualmente)
    def get_notificaciones():
     parser = feedparser.parse('Link al RSS de Facebook')
     notificaciones = []
     for i in range(0, 10):
      notificaciones.append(cortar_notificacion(reemplazar_acentos(parser.entries[i].title)))
     return notificaciones
     
    #Funcion que da formato a la fecha descargada
    def formatear_fecha(fecha):
     datos = fecha.split()
     dia = datos[2]
     mes = datos[3]
     hora = datos[4]
     new_hora = ""
     for i in range(len(hora)-2):
      new_hora += hora[i]
     hora = new_hora
     datos = "%s/%s %s"%(dia, mes, hora)
     return datos
      
    if __name__ == "__main__":
     try:
      #Abrimos el puerto serial, este puede variar dependiendo de la conexion, en este ejemplo es
      #COM3
      print "Abriendo puerto serial"
      ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
      #Esperamos 2 segundos, es importante o se puede perder la primera escritura en la conexion
      while True:
       time.sleep(2)
       #Actualizamos el reloj y obtenemos las notificaciones
       print "Actualizando el reloj"
       notificaciones = get_notificaciones()
       #Damos el formato de fecha que deseamos al tiempo actual, y lo enviamos al Arduino
       tiempo = time.strftime("%H:%M:%S", time.localtime())
       ser.write("T")
       ser.write(tiempo)
       print tiempo
       #Obtenemos el tiempo actual para compararlo despues y reiniciar cuando llegue a un
       #determinado tiempo para actualizar las notificaciones
       t = time.time()
       control = True
       i=0
       #Repetimios hasta que control cambie a false(se cumpla el tiempo de actualizacion)
       while(control):
        #Leemos un string enviado desde el arduino, este string debe ser un 'YES' o un
        #'NO', esto para poder saber si el Arduino esta listo para recibir una nueva
        #notificacion
        string = ser.read(100)
        #Si esta listo, y aun no enviamos las 10 notificaciones, escribimos una notificacion
        if "YES" in string and i<10:
         print 'yes'
         ser.write("N")
         ser.write(notificaciones[i].encode('utf-8'))
         print "Escribiendo notificacion: %s"%notificaciones[i]
         i+=1
        #Comparamos el tiempo actual con el inicial, si se cumplio el tiempo solicitado salimos
        #del while
        if "REINICIAR" in string:
            control = False
           #Abrimos y cerramos el puerto para reiniciar la conexion, y actualizar, esperamos de nuevo 2 segundos
           print "Reiniciando"
           ser.write('R')
        
     except serial.SerialException as e:
      print e

    Código Arduino:
    
    
    #include <TimedAction.h>
    #include <LiquidCrystal.h>
    //Variable LCD que usaremos para controlar la pantalla
    LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
    //Variables para controlar el reloj
    byte hora = 12, minu = 0, seg = 0;
    //Variable usada para contar el tiempo que paso desde
    //otra comparacion de tiempo, y saber cuando se cumple
    //1 segundo sin usar delays
    long previousMillis = 0; 
    //Strings usadas para almacenar las notificaciones
    String string = "";
    String notifications[10];
    //Variables para controlar cual notificacion se
    //muestra actualmente
    int currentString = 0, maxim = 0, num = 0;
    //Especie de threads, que corren cada cierto tiempo
    TimedAction scrollAction = TimedAction(250, scroll);
    TimedAction readSerialAction = TimedAction(1, readSerial);
     
    //Setup del Arduino, definimos el LCD, y encendemos el pin 13
    //que esta conectado al backlight del display LCD
    //Tambien inicializamos una conexion serial con 9600 de baud rate
    //e imprimimos YES para dar a conocer al script de python que
    //es posible leer  una notificacion
    void setup(){
      lcd.begin(60, 2);
      pinMode(13, OUTPUT);
      digitalWrite(13, HIGH);
      Serial.begin(9600);
      Serial.print("YES");
    }
     
    //Funcion simple que converte los numeros del reloj(ints)
    //en un string, agregandole un 0 si es menor a 10 para poder
    //mostrarlo correctamente.
    String getClockShowNumber(int number){
      if(number < 10){
        return ("0"+number);
      }
      else{
       return (""+number);
      }
    }
    //Funcion que actualiza el reloj si ya se cumplio 1 segundo
    //desde la ultima vez que se actualizo
    void updateDisplay(){
        delay(250);
        unsigned long currentMillis = millis();
        if(currentMillis - previousMillis > 1000) {
          previousMillis = currentMillis;
          Clock();
          char text_buffer[51];
          sprintf(text_buffer,"<%02d/%02d>%02d:%02d:%02d                                   ",
          num+1, 10, hora, minu, seg);
          lcd.setCursor(0, 0);
          lcd.print(text_buffer);
          text_buffer[8] = 0;
        }
    }
    //Funcion que checa los valores del reloj para sumarlos
    //correctamente
    void Clock(){
        //updateDisplay();
        seg = seg + 1;
        if(seg > 59){
          seg = 0;
          minu = minu + 1;
          if(minu > 59){
            minu = 0;
            hora = hora + 1; 
            if(hora>23){
              hora = 1;
            }
          }
        }
      }
      
    //Funcion que hace un scroll especial al LCD, dando scroll
    //solamente a la segunda linea del display, y dejando la
    //primera intacta
    void scroll(){
      String temp_string = notifications[num] + "   ";
      for(int i = 0; i < temp_string.length()-16; i++){
        lcd.setCursor(0, 1);
        for(int j=i; j<16+i; j++){
            //readSerialAction.check();
            lcd.print(temp_string[j]);
        }
        updateDisplay();
      }
    }
     
    //Funcion que actualiza el reloj despues de recibir la hora
    //actualizada desde el script de python en la computadora
    void updateClock(String time){
       //Arreglo[hora, min, seg]
       String temp[3] = {"", "", ""};
       int currentString = 0;
       for(int index = 0; index < time.length(); index++){
         if(time[index] == ':'){
           currentString++;
         }
         else{
           temp[currentString] += time[index];
         }
       }
       //Serial.print(temp[0]+":"+temp[1]+":"+temp[2]);
        hora = temp[0].toInt();
        minu = temp[1].toInt();
        seg = temp[2].toInt();
    }
     
    //Funcion que lee trafico desde la conexion serial, identifica
    //que tipo de datos se recibiran, y los almacena o actua dependiendo
    //del tag especificado.
    void readSerial(){
      int i = 0;
      boolean control = true;
    if(Serial.available()){
              switch(Serial.read()){
                case 'R':
                  Serial.print("NO");
                  //Serial.print("Reset");
                  for(int i=0; i<10; i++){
                    notifications[i] = "";
                  }
                  //lcd.clear();
                  currentString = 0;
                  maxim = 0;
                  num = 0;
                  Serial.print("YES");
                  break;
                   
                case 'N':
                  Serial.print("NO");
                  //Serial.print("Notification");
                  while(control){
                    updateDisplay();
                    string = "";
                    char c;
                    while(Serial.available() > 0){
                         control = false;
                         c = Serial.read();
                         string+=c;
                    }
                  }
                  notifications[currentString] = string;
                  currentString++;
                  maxim = currentString;
                  if(maxim == 10){
                    Serial.print("REINICIAR");
                    delay(100);
                  }else{
                     Serial.print("YES"); 
                  }
                  break;
                case 'T':
                  Serial.print("NO");
                  //Serial.print("Clock");
                  String time = "";
                  while(control){
                    updateDisplay();
                    char c;
                    while(Serial.available() > 0){
                         control = false;
                         c = Serial.read();
                         time+=c;
                    }
                  }
                  updateClock(time);
                  Serial.print("YES");
                  break;
              }
            }
            else{
             if(maxim > 0){
                if(num < maxim-1){
                    num++;
                }
                else{
                    num = 0;
                }
                updateDisplay();
                scrollAction.check();
                 
              }
            }
    }
     
    //Loop, simplemente checa cada cierto tiempo si hay trafico serial
    void loop(){
            readSerialAction.check();
     
      }

    Referencias: