29 de junio de 2015

Termómetro digital

El proyecto que en esta ocasión construiremos integra un par de temas que discutimos anteriormente en este blog: “Midiendo los fríos y calores” y “Visualizador de siete segmentos”. Usaremos nuestro Arduino Uno para construir el prototipo de termómetro ambiental digital que se muestra en la siguiente foto:

Termómetro ambiental digital construido
a partir de: Arduino Uno, sensor de temperatura y
visualizador de tres dígitos de siete segmentos.

Utilizaremos un visualizador multiplexado de tres dígitos de siete segmentos para desplegar en grados Celsius la temperatura ambiente obtenida a partir de las lecturas de un sensor TMP36. Este proyecto contrasta con nuestro proyecto anterior en el que la información del sensor de temperatura solo se imprimía en una terminal en la pantalla de la computadora.

Visualizador multiplexado

Para controlar un visualizador de siete segmentos (SSD por sus siglas en inglés) se necesitan conectar nueve pines: siete pines para controlar los segmentos, un pin para el punto decimal, y otro pin que sirve de ánodo o cátodo común.

Si queremos desplegar n dígitos usando n SSDs individuales, necesitaríamos un total de 9 × n conexiones. Sin embargo, si usamos un visualizador multiplexado, también conocido como MD por sus siglas en inglés (multiplexed display), solo requerimos 8 + n pines: siete pines para los segmentos, un pin para el punto decimal, y n pines de ánodo o cátodo común (un pin por cada dígito). En nuestro proyecto queremos usar tres dígitos, por lo que bajamos de 27 conexiones (9 × 3)  a tan solo 11 (8 + 3) al momento de utilizar multiplexado.

Visualizador multiplexado o MD (multiplexed display)
de tres dígitos, de siete segmentos por cada dígito.

La técnica de multiplexado se basa en una característica que tiene la vista humana conocida como persistencia de la retina, en donde conservamos la sensación de ver una imagen aún cuando ésta ya despareció. Si creamos una intermitencia encendiendo y apagando un led, al momento en que ésta sea lo suficientemente rápida ya no apreciamos que el led llega a apagarse sino que nos da la sensación de que está permanentemente encendido.

Para el MD de n dígitos el multiplexado consiste en encender los segmentos deseados del dígito a desplegar en la primera posición, pausar por Δ segundos y apagar después todos los segmentos. Lo anterior lo repetimos secuencialmente con el dígito de la segunda posición, después con el dígito de la tercera posición y así hasta llegar al dígito de la n-ésima posición para luego comenzar nuevamente con el dígito de la primera posición y continuar así sucesivamente. El valor de Δ debe ser suficientemente breve para dar la impresión de un encendido continuo. A base de prueba y error determinamos que 0.001 segundos (una milésima de segundo) genera el efecto deseado en nuestro programa de Python.

El MD para nuestro proyecto es uno de la serie KW3-561 el cual puede verse de manera esquemática así:

Pines y segmentos de un visualizador
multiplexado de tres dígitos.

El MD tiene doce pines numerados del 1 al 12 con la siguiente correspondencia (notar que el pin 6 no se utiliza):
  • Pin 1: Segmento E
  • Pin 2: Segmento D
  • Pin 3: Segmento P (punto decimal)
  • Pin 4: Segmento C
  • Pin 5: Segmento G
  • Pin 6: No se utiliza
  • Pin 7: Segmento B
  • Pin 8: Ánodo o cátodo común de DIG_3 (dígito de la tercera posición)
  • Pin 9: Ánodo o cátodo común de DIG_2 (dígito de la segunda posición)
  • Pin 10: Segmento F
  • Pin 11: Segmento A
  • Pin 12: Ánodo o cátodo común de DIG_1 (dígito de la primera posición)
Solo uno de DIG_1, DIG_2 o DIG_3 debe estar encendido en un momento dado. Esto quiere decir que, por ejemplo, si enciendo DIG_1, debo apagar DIG_2 y DIG_3; cualquier segmento que encienda a partir de ese momento solo se verá reflejado en la posición correspondiente a DIG_1.

A partir de lo ya discutido estamos en condiciones de esbozar un algoritmo un poco más formal para realizar el multiplexado de un MD de tres dígitos de la forma DD.D:
  • Sea n un número en base decimal (0 ≤ n < 100) conformado por tres dígitos d1, d2 y d3, en donde n = d1×10 + d2 + d3×0.1; es decir, d1 son las decenas, d2 son las unidades y d3 son las décimas de unidad. Repetir lo siguiente mientras se desee desplegar n en el MD:
    • Para i de 1 hasta 3:
      • Mandar una señal de encendido a la posición de DIG_i, y una señal de apagado a todas las demás posiciones.
      • Encender o apagar los segmentos A, B, C, D, E, F y G correspondientes al valor del dígito di (la entrada del “Visualizador de siete segmentos” detalla puntalmente la manera de hacer esto). Adicionalmente, si i = 2, encender el segmento P (punto decimal), de lo contrario apagarlo.
      • Pausar por Δ segundos.
      • Apagar todos los segmentos correspondientes al dígito di

Armando el proyecto

Esta es la lista de componentes necesarios para nuestro proyecto:
  • Arduino Uno.
  • MD de ánodo común KW3-561ASN (dígitos rojos de 14.2 mm de altura).
  • Sensor de temperatura TMP36.
  • Protoboard.
  • Ocho resistencias de 330Ω (bandas naranja, naranja, café). 
  • Cables conectores.
La siguiente figura, elaborada con Fritzing, muestra la manera de conectar estos componentes:

Vista de protoboard Fritzing para
el termómetro ambiental digital.

Cada pin asociado a un segmento del MD debe quedar conectado, con una resistencia de 330Ω de por medio, a un pin digital del Arduino. Estas son las conexiones que se muestran en la figura anterior:

  • El pin digital 2 del Arduino se conecta al pin 1 (SEG_E) del MD.
  • El pin digital 3 del Arduino se conecta al pin 2 (SEG_D) del MD.
  • El pin digital 4 del Arduino se conecta al pin 3 (SEG_P) del MD.
  • El pin digital 5 del Arduino se conecta al pin 4 (SEG_C) del MD.
  • El pin digital 6 del Arduino se conecta al pin 5 (SEG_G) del MD.
  • El pin digital 7 del Arduino se conecta al pin 11 (SEG A) del MD.
  • El pin digital 8 del Arduino se conecta al pin 10 (SEG_F) del MD.
  • El pin digital 9 del Arduino se conecta al pin 7 (SEG_B) del MD.
Por otro lado, los pines de ánodo común deben conectarse directamente de la siguiente manera:
  • El pin digital 10 del Arduino se conecta al pin 12 (DIG_1) del MD.
  • El pin digital 11 del Arduino se conecta al pin 9 (DIG_2) del MD.
  • El pin digital 12 del Arduino se conecta al pin 8 (DIG_3) del MD.
Recordemos que el sensor de temperatura TMP36 tiene tres pines. Viendo este componente de frente a su parte plana:
  • El pin de 5V del Arduino se conecta al pin Vs (pin izquierdo) del TMP36.
  • El pin analógico A0 del Arduino se conecta al pin Vout (pin central) del TMP36.
  • Algún pin GND del Arduino se conecta al pin GND (pin derecho) del TMP36.

El software

El código fuente completo en Python 3 del proyecto se muestra a continuación. Contiene diversos comentarios con el fin de ayudar a su comprensión.
#!/usr/bin/env python3
# Archivo: termometro_digital.py

import pyfirmata
from time import time

# Valor de delta para el multiplexado.
PAUSA_MULTIPLEXADO = 0.001

# Leer la temperatura después de este número de segundos.
PAUSA_LECTURAS = 60

# Iniciar la placa de Arduino.
# IMPORTANTE: Cambiar la cadena de caracteres que indica
# el puerto serie que utiliza nuestra plataforma.
placa = pyfirmata.Arduino('/dev/ttyACM0')

# Crear un thread iterador, de lo contrario la placa puede
# seguir enviando datos por la conexión serial hasta
# producir un desbordamiento.
pyfirmata.util.Iterator(placa).start()

# Inicializar el pin de entrada analógica 0 del Arduino
# para conectarlo al pin Vout del TMP36.
entrada_temperatura = placa.get_pin('a:0:i')
entrada_temperatura.enable_reporting()

# Incicializar los pines de salida digital para los
# segmentos del MD.
segmento = [
    placa.get_pin('d:4:o'), # SEG_P/Ard_4/MD_3
    placa.get_pin('d:6:o'), # SEG_G/Ard_6/MD_5
    placa.get_pin('d:8:o'), # SEG_F/Ard_8/MD_10
    placa.get_pin('d:2:o'), # SEG_E/Ard_2/MD_1
    placa.get_pin('d:3:o'), # SEG_D/Ard_3/MD_2
    placa.get_pin('d:5:o'), # SEG_C/Ard_5/MD_4
    placa.get_pin('d:9:o'), # SEG_B/Ard_9/MD_7
    placa.get_pin('d:7:o')  # SEG_A/Ard_7/MD_11
]

# Inicializar los pines de salida digital para las
# conexiones del ánodo común de las posiciones DIG_1,
# DIG_2 y DIG_3 del MD.
posicion = [
    placa.get_pin('d:10:o'), # DIG_1/Ard_10/MD_12
    placa.get_pin('d:11:o'), # DIG_2/Ard_11/MD_9
    placa.get_pin('d:12:o'), # DIG_3/Ard_12/MD_8
]

# Diseños de los dígitos individuales. Este programa no
# utiliza los diseños de la A a la F.
patron = [
    # ABCDEFGP <-- Segmentos
    # --------
    0b11111100, # 0
    0b01100000, # 1
    0b11011010, # 2
    0b11110010, # 3
    0b01100110, # 4
    0b10110110, # 5
    0b10111110, # 6
    0b11100000, # 7
    0b11111110, # 8
    0b11110110, # 9
    0b11101110, # A
    0b00111110, # b
    0b10011100, # C
    0b01111010, # d
    0b10011110, # E
    0b10001110  # F
]

def voltios_a_celsius(v):
    """Convierte un voltaje v obtenido de un sensor
       TMP36 a grados Celsius.
    """
    return 100 * (v - 0.5)

def binario(n):
    """Devuelve como lista el valor binario de n.

    La lista resultante es de la forma:

       [bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7]

    Los bit8 en adelante son ignorados.
    """
    resultado = []
    for i in range(8):
        resultado.append(n & 1)
        n >>= 1
    return resultado

def activa_posicion(n):
    """Enciende el pin correspondiente al dígito de la
    posición n de un MD, apagando los pines restantes.

        n = 0: DIG_1
        n = 1: DIG_2
        n = 2: DIG_3
    """
    for i, d in enumerate(posicion):
        d.write(i == n)

def despliega_digito(d, puntodec=False):
    """Despliega el digito d en la posición actualmente
    activa (DIG_1, DIG_2 o DIG_3) del MD.

    Si d es menor a 0 o mayor a 15 apaga todos los
    segmentos. Si puntodec es True, enciende el segmento
    correspondiente al punto decimal.
    """
    d = ((patron[d] if 0 <= d <= 15 else 0b00000000)
         | puntodec)
    for pin, bit in zip(segmento, binario(d)):
        # El operador 'not' de la siguiente instrucción se
        # necesita debido a que el MD es de ánodo común.
        # Dicho operador se debe omitir si el MD es de
        # cátodo común.
        pin.write(not bit)

def despliega_numero(n):
    """Despliega el número n en un visualizador
    multiplexado (MD) con tres dígitos de siete segmentos
    (SSD).

    El valor desplegado siempre tiene la forma:

        DD.D

    donde D es un dígito del 0 al 9. Despliega 88.8 si
    el valor de n es menor a cero o mayor o igual a 100.
    """
    if n < 0 or n >= 100:
        fmt = '888'
    else:
        fmt = '{:03}'.format(round(n * 10))
    for i, d in enumerate(fmt):
        activa_posicion(i)
        despliega_digito(int(d), i == 1)
        placa.pass_time(PAUSA_MULTIPLEXADO)
        despliega_digito(-1)

def lee_temperatura():
    """Realiza la lectura del voltaje de un sensor de
    temperatura TMP36 y devuelve el valor obtenido
    convertido a grados Celsius.
    """
    while True:
        v = entrada_temperatura.read()
        if v != None:
            v *= 5
            return voltios_a_celsius(v)

# El algoritmo de multiplexado comienza aquí.
try:
    tiempo = 0
    while True:

        # Tomar nueva lectura de temperatura después de
        # PAUSA_LECTURAS segundos.
        # Esta condición es verdadera en la primera
        # iteración (tiempo == 0).
        if time() > tiempo + PAUSA_LECTURAS:
            tiempo = time()
            temperatura = lee_temperatura()

        despliega_numero(temperatura)

except KeyboardInterrupt:
    # Terminar programa cuando se presione Ctrl-C.

    # Apagar los SSDs de las tres posiciones.
    for i in range(3):
        activa_posicion(i)
        despliega_digito(-1)

finally:
    placa.exit()
Para correr el programa se necesita tener el Arduino conectado por el puerto USB a la computadora anfitriona y ejecutar el siguiente comando desde una terminal en el mismo directorio donde se encuentra el archivo termometro_digital.py:
python3 termometro_digital.py
Si todo resulta bien se debe poder ver la temperatura ambiente en el visualizador. El programa corre hasta que presionemos Ctrl-C para terminar.

8 comentarios:

  1. Hola no tengo mucha experiencia en arduino pero quiero intentar realizar este termómetro pero no se como ejecutar el codigo en arduino sin que me de error

    ResponderBorrar
    Respuestas
    1. Hola Sergio. ¿Exactamente qué error te está apareciendo?

      Te recomiendo que revises primero las instrucciones de http://edupython.blogspot.mx/2014/06/como-programar-tu-arduino.html antes de intentar hacer el proyecto del termómetro digital.

      Borrar
  2. Vale muchas gracias por compartir Ariel, se me cayeron ayer dos en la farmacia y aunque soy algo manitas no se si voy a ser capaz de repararlos correctamente

    ResponderBorrar
  3. Muchas gracias! Buen trabajo. Podrias hacer un termometro con 2 display de 7 segmentos y un TMP36?

    ResponderBorrar