26 de mayo de 2014

El LED parpadeante

En la entrada anterior introduje brevemente a la Raspberry Pi (RPi). Ahora toca elaborar nuestro primer proyecto usando esta plataforma. Vamos a programar en Python uno de los pines de GPIO (Entrada/Salida de Propósito General) para controlar el parpadeo de un LED. Este proyecto es considerado el “¡Hola Mundo!” de la electrónica.

El hardware

Además de la RPi correctamente configurada y funcional, requeriremos los siguientes componentes:
  • Un protoboard.
  • Un LED (diodo emisor de luz).
  • Una resistencia de 330Ω (bandas naranja, naranja, café).
  • Dos cables conectores.
Es muy importante conectar los componentes de forma correcta para evitar todo tipo de problemas. El ánodo del LED (la patita larga) se conecta al pin GPIO 24 de la RPi, mientras que el cátodo (la patita corta) se conecta a la resistencia. El otro extremo de la resistencia se conecta a tierra (cualquiera de los pines GND de la RPi). La siguiente figura, elaborada con Fritzing, muestra cómo se ven los componentes ya conectados:

Vista de protoboard Fritzing de un LED conectado a la RPi.

La resistencia se necesita con el fin de evitar que el LED reciba demasiada corriente y se dañe. Para no complicar demasiado las cosas, se recomienda utilizar una resistencia entre 270Ω y 330Ω (si se requiere ser más precisos al respecto, se debe aplicar la Ley de Ohm y conocer la tensión del LED así como la corriente que pasa por éste).

La siguiente foto muestra físicamente la forma en que se ven las conexiones de nuestros componentes electrónicos:

Vista física de un LED conectado a la RPi.

Hay que notar que en la foto se aprecia un GPIO breakout (la tarjeta de interfaz negra) que facilita conectar la RPi al protoboard a través de un cable plano. En este caso, los cables conectores a utilizar deben ser macho-macho. Si no se cuenta con el breakout, se pueden usar cables conectores macho-hembra para conectar directamente los pines GPIO de la RPi al protoboard.

El software

Ahora veremos la manera de escribir nuestro programa en Python. Primeramente necesitamos importar el módulo RPi.GPIO:
import RPi.GPIO as GPIO
De esta forma podemos referirnos al módulo simplemente usando el identificador GPIO. Las distribuciones recientes de Raspian traen pre-instalado este módulo para Python 2.7.

Como segundo paso, necesitamos indicar la manera en que estaremos haciendo referencia a los números de pin de la RPi. El módulo RPi.GPIO permite hacerlo de dos formas:
  • BOARD: Los pines se numeran usando la distribución física de la cabecera de expansión P1 de 26 pines.
  • BCM: Los pines se numeran según la designación de canales de Broadcom SoC (System-on-a-chip). En otras palabras, se usan los números GPIO.
La siguiente figura (adaptada de elinux.org) muestra la correspondencia entre la numeración de los pines BOARD (fondo blanco) y los pines BCM (fondo naranja):

Numeración de pines BOARD (blanco) y BCM (naranja),
para la RPi modelo B, revisión 2.

Por ejemplo, el pin GPIO 24 corresponde al pin físico 18, mientras que el pin GPIO 17 corresponde al pin físico 11.

La función GPIO.setmode() recibe como argumento GPIO.BOARD o GPIO.BCM para indicar el esquema de numeración a utilizar:
GPIO.setmode(GPIO.BCM)
En nuestro proyecto usaremos la numeración BCM. Hay que notar que se debe tener algo de cuidado con este esquema, ya que hay algunas diferencias sutiles entre las revisiones 1 y 2 del modelo B de la RPi. Sin embargo, el proyecto que estamos construyendo debe funcionar sin problemas con cualquiera de las dos revisiones. En nuestro programa de Python podemos utilizar el valor de la constante simbólica GPIO.RPI_REVISION para averiguar el número de revisión.

El tercer paso consiste en indicar la dirección (entrada o salida, usando GPIO.IN o GPIO.OUT, respectivamente) que usaremos con el pin 24:
GPIO.setup(24, GPIO.OUT)
La documentación le llama a lo anterior “establecer un canal”.

Ahora, para controlar nuestro LED solo tenemos que usar la función GPIO.output(), indicando como argumentos el número de pin y GPIO.HIGH si queremos prender el LED o GPIO.LOW si lo queremos apagar. Por ejemplo:
GPIO.output(24, GPIO.HIGH)
Para producir un efecto de parpadeo necesitamos prender el LED, pausar por un tiempo, apagar el LED, volver a pausar, y repetir lo anterior cuantas veces queramos. Para pausar un momento nuestro programa en Python podemos usar la función sleep() del módulo time, la cual recibe como argumento la cantidad de segundos que se debe detener.

Finalmente, se recomienda invocar a la función GPIO.cleanup() para reiniciar todos los canales de GPIO y con esto asegurarnos que el LED quede apagado y también evitar algunos mensajes de advertencia que pudieran aparecer la siguiente vez que se corra nuestro programa. Dado que deseamos que siempre ocurra esto al final del programa (sin importar si éste terminó de forma normal o a raíz de una excepción), lo más conveniente es colocar la instrucción GPIO.cleanup() como parte de la cláusula finally de una instrucción try.

El siguiente programa completo integra lo que se discutió anteriormente:
# Archivo: parpadea.py

import RPi.GPIO as GPIO
import time

# Constantes
TIEMPO_PAUSA = 0.5     # Las pausas duran medio segundo.
PIN_LED = 24           # Pin donde esta conectado
                       # el anodo del LED.

GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_LED, GPIO.OUT)

try:
    # Ciclo infinito.
    # Para terminar el programa de debe presionar Ctrl-C.
    while True:
        GPIO.output(PIN_LED, GPIO.HIGH)
        time.sleep(TIEMPO_PAUSA)
        GPIO.output(PIN_LED, GPIO.LOW)
        time.sleep(TIEMPO_PAUSA)
finally:
    GPIO.cleanup()
Para correr el programa, requerimos ejecutar el siguiente comando desde una terminal en el mismo directorio donde radica el archivo parpadea.py:
sudo python parpadea.py
El comando debe empezar con sudo debido a que el programa de Python necesita privilegios de administrador para poder manipular los puertos de GPIO.

Cuando corremos el programa, el LED se prende por medio segundo, y luego se apaga durante otro medio segundo. Esto se repite indefinidamente hasta que  presionemos Ctrl-C para terminar el programa. Podemos cambiar el valor de la constante simbólica TIEMPO_PAUSA para hacer que el LED parpadee más lento o más rápido.

Usamos la constante simbólica PIN_LED para indicar el número de pin GPIO al que conectamos el ánodo del LED. Si después queremos usar otro número de pin, solo tenemos que cambiar este dato en un solo lugar de nuestro programa y no en múltiples sitios.

Si por fin logramos hacer funcionar este proyecto, entonces estamos listos para hacer proyectos más complicados e interesantes. ¡Yupi!

18 de abril de 2014

Raspberry Pi

A mediados de la década pasada, el Dr. Eben Upton y varios de sus colegas de la Universidad de Cambridge, Reino Unido, se dieron cuenta de que las nuevas generaciones de jóvenes y niños estaban careciendo del interés y las habilidades que generaciones previas habían demostrado hacia la computación.

Eben Christopher Upton,
principal arquitecto de la Raspberry Pi

Tenían la percepción de que un joven que ingresaba a la universidad para estudiar ciencia de la computación en la década de los años ochenta y noventa le gustaba usualmente programar como hobby. Lamentablemente, esto ya no estaba ocurriendo hoy en día. Ellos observaron que las nuevas generaciones, a pesar de ser usuarios acérrimos de la tecnología, no saben programar, y sus conocimientos sobre el funcionamiento interno de una computadora resultan superficiales en el mejor de los casos.

En palabras del propio Upton:
Algo había cambiado con respecto a la manera en que los niños estaban interactuando con las computadoras. Identificamos varios problemas: el predominio de clases orientadas al uso de Word y Excel dentro del currículo de las TICs (tecnologías de información y comunicación); el fin de la era punto-com; y el surgimiento de las PCs y las consolas de video juegos que vinieron a sustituir a los equipos Amiga, BBC Micro, Spectrum ZX y Commodore 64 que las personas de generaciones anteriores habían utilizado para aprender a programar.
No hay mucho que un pequeño grupo pueda hacer para abordar el problema de un currículo académico inadecuado o el final de una burbuja financiera. Sin embargo, sentíamos que sí podíamos hacer algo al respecto a la situación en la que las computadoras se habían convertido en aparatos tan costosos y arcanos que los padres de familia habían tenido que prohibir que se les usara para realizar experimentos de programación. Así fue que, del 2006 al 2008, empezamos a diseñar lo que finalmente se convirtió en la Raspberry Pi.

Desde que salieron las primeras micro-computadoras ha sido una tradición usar nombres de fruta para nombrar productos y compañías de tecnología. Así pues tenemos a Apple y BlackBerry, pero hubo también en el Reino Unido otras compañías como Apricot Computers, Orange, Tangerine y Acorn Computing, entre otras. Así pues, Raspberry Pi continuó con esta tradición. Pi se debe a que originalmente se deseaba construir una computadora que solo pudiera correr Python, aunque finalmente resultó en un producto mucho más general.

Raspberry Pi, modelo-B.

Teniendo un costo aproximado de tan solo 35 dólares estadounidenses, la Raspberry Pi (RPi) es una computadora completa contenida en una placa del tamaño de una tarjeta de crédito. La placa del modelo-B aloja un SoC (System-on-a-chip) Broadcom BCM2835 que incluye:
  • Un procesador ARM11 a 700 MHz. Este es el mismo CPU RISC de 32 bits que utilizan varios teléfonos inteligentes de Apple y Nokia.
  • Una GPU (Unidad de Procesamiento Gráfico) VideoCore IV.
  • 512 MB de memoria RAM.
El reducido costo de la RPi se debe en gran medida a que éste no incluye los dispositivos periféricos necesarios para su operación. El monitor, mouse, teclado, etc. deben ser provistos por el usuario (los equipos viejos de cómputo son ideales para obtener las partes que hacen falta).

Para interactuar con el mundo exterior, la RPi cuenta con varias entradas y salidas. A continuación se listan las más relevantes:
  • Puerto microUSB. Sirve para conectar la placa a la fuente de alimentación eléctrica.
  • Ranura para tarjeta SD. A falta de un disco duro o unidad de estado sólido, el sistema operativo, aplicaciones y archivos de usuario se almacenan en una tarjeta de memoria SD.
  • Conector HDMI. Se utiliza para conectarse a un monitor o televisión de alta definición.
  • Salida de video compuesto. Ésta es una alternativa para conectarse a un monitor o televisión con entradas RCA.
  • Salida de audio analógico minijack de 3.5mm. Sirve para conectarse a bocinas con alimentación externa.
  • 2 puertos USB. Se utiliza para conectarse a diversos dispositivos USB tales como: mouse, teclado, disco duro externo, adaptador Wi-Fi, etc.
  • Puerto Ethernet. Sirve para conectarse a una red de área local de manera alámbrica.
  • Pines GPIO (Entrada/Salida de Propósito General). Permiten manipular desde software distintos componentes electrónicos como LEDs, sensores, interruptores, motores, etc.
El sistema operativo de software libre Raspian, basado en la distribución Debian de Linux, es el sistema operativo oficial de la RPi. Cuenta con una interfaz gráfica de usuario (GUI) y más de 35,000 paquetes de software que pueden ser instalados de manera gratuita.


La configuración de mi RPi con todos sus dispositivos periféricos se puede apreciar en la siguiente foto:

Raspberry Pi con todos sus dispositivos periféricos.

Se puede observar que la RPi está conectada a una pequeña televisión de 7 pulgadas vía la salida de video compuesto. Utilizo uno de los puertos USB de la RPi para conectar un hub USB. En dicho hub, el cual cuenta con su propio alimentador externo, conecto el teclado y el mouse. El otro puerto USB de la RPi lo uso para un adaptador Wi-Fi que permite conectarme a Internet. Uso un cable plano y tarjeta de interfaz (breakout board) para conectar los puertos GPIO a un protoboard. Una tarjeta SD de 8 GB sirve como medio de almacenamiento. Finalmente, uso un adaptador de corriente alterna para alimentar a la RPi a través de su puerto microUSB.

Si se habilita el servidor de SSH (secure shell) de la RPi, es posible conectarse a ella de manera remota desde otras computadoras. Esto permite utilizar la RPi de manera “descabezada”, es decir, sin tener conectado un monitor, mouse o teclado, reduciendo así la cantidad de cables y dispositivos necesarios para su correcta operación. Personalmente me acomoda más usar la RPi de esta manera.

Raspberry Pi “descabezada”.

Pero, ¿para qué sirve?

No existe una manera única de utilizar una RPi, pero sus usos más comunes se resumen a continuación:
  • Computación de propósito general. La RPi se puede utilizar para navegar en Internet, ver videos y elaborar documentos usando LiberOffice.
  • Aprendizaje de programación. El propósito principal de la RPi es permitir que los niños y jóvenes aprendan a programar y a experimentar con computadoras. Raspian viene con varios intérpretes y compiladores pre-instalados. Para los principiantes trae Scratch, un lenguaje de programación gráfico elaborado en MIT. Los usuarios más avanzados pueden experimentar con Python, el lenguaje oficial de la RPi, o con otros lenguajes como C, Java, Ruby, Perl o incluso ensamblador ARM.
  • Plataforma de proyectos. La RPi no solo se distingue de una computadora regular por su tamaño y precio, sino también por su capacidad para integrarse con proyectos de electrónica del mismo estilo de los que se pueden hacer con la plataforma Arduino (de hecho, se pueden utilizar Arduino y RPi juntos).
La RPi ha sido una pieza importante en lo que se ha conocido en inglés como el “maker movement”. Este movimiento emplea las técnicas y procesos propios de la cultura de “hágalo usted mismo” para desarrollar productos tecnológicos únicos.

Los múltiples usos que puede tener una RPi.Fuente: Raspberry Pi School

En resumen, la RPi es una plataforma pequeña, económica, explorable y orientada a la educación. Definitivamente es una herramienta formidable para encausar a nuestros jóvenes y niños hacia un conocimiento más profundo sobre la computación.

Más información

  • El sitio oficial de RPi: http://www.raspberrypi.org/ (en inglés)
  • ¿Dónde puedo adquirir una RPi? Eso depende del lugar donde te encuentres. En México se puede adquirir a través de distribuidores autorizados tales como ElectronicaEstudio.com, o en algunos comercios de electrónica de la calle de República del Salvador en el Centro de la Ciudad de México, solo por mencionar algunos lugares. Adafruit.com y Amazon.com atienden pedidos internacionales, pero siempre hay que considerar los costos de envío, impuestos y restricciones aduanales de cada país. Otra opción para países de América Latina son los sitios de venta entre particulares, por ejemplo MercadoLibre.com.

17 de diciembre de 2013

Documentando programas en Python

Introducción

Para mucha gente (yo incluido), la idea de tener que documentar un programa genera el mismo entusiasmo que cuando pensamos sobre la ingesta diaria recomendada de verduras. Son de esas cosas que sabemos que son buenas y debemos procurar, pero definitivamente no nos fascinan.


Sin embargo, la verdad es ésta: no importa lo fabuloso que sea un programa, nadie lo usará si no queda claro la manera de utilizarlo. En este sentido la documentación juega un rol primordial. Aunque yo sea el único usuario de mi programa, resulta más sencillo entender y reutilizar lo que escribí hace seis meses si cuento con una documentación adecuada.

Nos debe quedar claro que, como maestros de programación, tenemos la responsabilidad de inculcar la importancia de la documentación a nuestros alumnos.

Pero, ¿por qué nos disgusta tanto la tarea de escribir documentación para nuestros programas? La razón es simple: es más divertido escribir código que documentarlo. Empero, si deseamos que nuestros programas trasciendan más allá del momento en que los escribimos necesitamos dedicarles algo de tiempo para documentarlos.

Una buena noticia es que Python cuenta con un mecanismo bastante sencillo y conveniente para elaborar la documentación técnica de un programa. A continuación expondré dicho mecanismo.

Cadenas de documentación 

En Python, un docstring o cadena de documentación es una literal de cadena de caracteres que se coloca como primer enunciado de un módulo, clase, método o función, y cuyo propósito es explicar su intención. Un ejemplo sencillo (todos estos ejemplos funcionan en Python 3):
def promedio(a, b):
    'Calcula el promedio de dos números.'
    return (a + b) / 2
Un ejemplo más completo:
def formula_cuadratica(a, b, c):
    """Resuelve una ecuación cuadrática.

    Devuelve en una tupla las dos raíces que resuelven la
    ecuación cuadrática:
    
        ax^2 + bx + c = 0.

    Utiliza la fórmula general (también conocida
    coloquialmente como el "chicharronero").

    Parámetros:
    a -- coeficiente cuadrático (debe ser distinto de 0)
    b -- coeficiente lineal
    c -- término independiente

    Excepciones:
    ValueError -- Si (a == 0)
    
    """
    if a == 0:
        raise ValueError(
            'Coeficiente cuadrático no debe ser 0.')
    from cmath import sqrt
    discriminante = b ** 2 - 4 * a * c
    x1 = (-b + sqrt(discriminante)) / (2 * a)
    x2 = (-b - sqrt(discriminante)) / (2 * a)
    return (x1, x2)
La cadena de documentación en el segundo ejemplo es una cadena multi-líneas, la cual comienza y termina con triples comillas ("""). Aquí se pueden observar el uso de las convenciones establecidas en el PEP 257 (Python Enhancement Proposals):
  • La primera línea de la cadena de documentación debe ser una línea de resumen terminada con un punto. Debe ser una breve descripción de la función que indica los efectos de ésta como comando. La línea de resumen puede ser utilizada por herramientas automáticas de indexación; es importante que quepa en una sola línea y que esté separada del resto del docstring por una línea en blanco.
  • El resto de la cadena de documentación debe describir el comportamiento de la función, los valores que devuelve, las excepciones que arroja y cualquier otro detalle que consideremos relevante.
  • Se recomienda dejar una línea en blanco antes de las triples comillas que cierran la cadena de documentación.
Todos los objetos documentables (módulos, clases, métodos y funciones) cuentan con un atributo __doc__ el cual contiene su respectivo comentario de documentación. A partir de los ejemplos anteriores podemos inspeccionar la documentación de las funciones promedio y formula_cuadratica desde el shell de Python:
>>> promedio.__doc__
'Calcula el promedio de dos números.'
>>> formula_cuadratica.__doc__
'Resuelve una ecuación cuadrática.\n\n    Devuelve en una 
tupla las dos raíces que resuelven la\n    ecuación 
cuadrática:\n    \n        ax^2 + bx + c = 0.\n\n    
Utiliza la fórmula general (también conocida\n    
coloquialmente como el "chicharronero").\n\n    
Parámetros:\n    a -- coeficiente cuadrático (debe ser 
distinto de 0)\n    b -- coeficiente lineal\n    
c -- término independiente\n\n    Excepciones:\n    
ValueError -- Si (a == 0)\n    \n    '
Sin embargo, si se está usando el shell de Python es mejor usar la función help(), dado que la salida producida queda formateada de manera más clara y conveniente:
>>> help(promedio)
Help on function promedio in module __main__:

promedio(a, b)
    Calcula el promedio de dos números.
>>> help(formula_cuadratica)
Help on function formula_cuadratica in module __main__:

formula_cuadratica(a, b, c)
    Resuelve una ecuación cuadrática.
    
    Devuelve en una tupla las dos raíces que resuelven la
    ecuación cuadrática:
    
        ax^2 + bx + c = 0.
    
    Utiliza la fórmula general (también conocida
    coloquialmente como el "chicharronero").
    
    Parámetros:
    a -- coeficiente cuadrático (debe ser distinto de 0)
    b -- coeficiente lineal
    c -- término independiente
    
    Excepciones:
    ValueError -- Si (a == 0)
Ciertas herramientas, por ejemplo shells o editores de código, pueden ayudar a visualizar de manera automática la información contenida en los comentarios de documentación. La siguiente imagen muestra como el shell del ambiente de desarrollo integrado IDLE muestra la línea de resumen como descripción emergente (tool tip) al momento en que un usuario teclea el nombre de la función:

De esta manera un usuario puede tener a su alcance de manera sencilla toda la información que necesita para poder usar nuestras funciones.

Generando documentación en páginas de HTML

Los docstrings se pueden usar también para producir documentación en páginas de HTML que pueden ser consultadas usando un navegador de web. Para ello se usa el comando pydoc desde una terminal. Por ejemplo, si las dos funciones anteriores (promedio y formula_cuadratica) se encuentran en un archivo fuente llamado ejemplos.py, podemos ejecutar el siguiente comando en una terminal dentro del mismo directorio donde está el archivo fuente:
pydoc -w ejemplos
La salida queda en el archivo ejemplos.html, y así se visualiza desde un navegador:

La documentación de pydoc explica otras monerías que puede hacer esta utilería de Python.

Docstrings vs. comentarios

Un comentario en Python inicia con el símbolo de número (#) y se extiende hasta el final de la línea. En principio los docstrings pudieran parecer similares a los comentarios, pero hay una diferencia pragmática importante: los comentarios son ignorados por el ambiente de ejecución de Python y por herramientas como pydoc; esto no es así en el caso de los docstrings.

A un nivel más fundamental hay otra diferencia aún más grande entre los docstrings y los comentarios, y ésta tiene que ver con la intención:
  • Los docstrings son documentación, y sirven para entender qué hace el código.
  • Los comentarios sirven para explicar cómo lo hace.
La documentación es para la gente que usa tu código. Los comentarios son para la gente que necesita entender cómo funciona tu código, posiblemente para extenderlo o darle mantenimiento.

Conclusiones

El uso de docstrings en Python facilita la escritura de la documentación técnica de un programa. Escribir una buena documentación requiere de disciplina y tiempo pero sus beneficios se cosechan cuando alguien (quizás mi futuro yo dentro de seis meses) necesita entender qué hace nuestro software. Los docstrings no sustituyen otras buenas prácticas de programación, como son el uso apropiado de comentarios o el empleo de nombres descriptivos para variables y funciones, por lo que resulta importante enseñar y promover con nuestros alumnos todas estas prácticas de manera conjunta desde temprano en su formación como programadores.