Mostrando las entradas con la etiqueta Raspberry Pi. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Raspberry Pi. Mostrar todas las entradas

31 de julio de 2017

pyboard: Python en un microchip

A finales del año 2013, el físico y programador australiano Damien George arrancó desde el Reino Unido una campaña en Kickstarter para recaudar fondos con el fin de desarrollar una plataforma de hardware libre basada en una placa con un microcontrolador que soportara directamente el lenguaje Python. Esencialmente quería diseñar algo parecido a un Arduino, pero que pudiera programarse de manera eficiente en Python en lugar de los lenguajes comúnmente disponibles para programar microcontroladores (C/C++ y ensamblador). La meta inicial de George era recaudar 15,000 GBP (aproximadamente equivalente a 25,000 USD de aquel entonces) para elaborar su proyecto, pero al finalizar la campaña había recaudado 97,803 GBP (más de 160,000 USD). Y así fue como surgió el pyboard.

Damien P. George, creador del pyboard.
Fuente: dpgeorge.net

El software

Central en el proyecto del pyboard fue el desarrollo de MicroPython, un subconjunto de Python 3 elaborado también por Damien George. Al re-escribir este intérprete desde cero, George logró que fuera suficientemente pequeño para poder caber y correr en un microcontrolador.


MicroPython utiliza muy poca memoria RAM e incluye una pequeña fracción de la biblioteca estándar de Python. Aún así, MicroPython soporta características avanzadas como son: enteros de tamaño arbitrario, cerraduras léxicas (closures), listas por comprensión, generadores, manejo de excepciones e incluseo un shell interactivo. Al correr directamente sobre los fierros, MicroPython funge efectivamente como el sistema operativo del pyboard.

Teóricamente, los requisitos mínimos de memoria para que un sistema pueda soportar MicroPython son: 128 kibibytes de ROM y 8 kibibytes de RAM. Gracias a eso ha sido migrado exitosamente a otras plataformas. Además del pyboard, el sitio oficial ofrece el firmware requerido para correr MicroPython en los microchips WiPy, ESP8266, ESP32, Espruino Pico, STM32F4, NUCLEO-L476RG y micro:bit.

El hardware

La placa del pyboard contiene un microcontrolador STM32F405RG de STMicroelectronics con las siguientes características:
  • Procesador RISC de 32 bits ARM Cortex-M4F.
  • Una velocidad de reloj de 168 MHz.
  • Registros de 32 bits: 
    • 16 para valores enteros.
    • 32 para valores de punto flotante de precisión sencilla.
  • 192 kibibytes de memoria RAM para almacenar datos volátiles.
  • 1024 kibibytes de memoria flash ROM para almacenar el código del programa.
  • 13 temporizadores independientes.
  • Soporte de protocolos UART, I2C, SPI y CAN para comunicación con periféricos.
  • Hardware generador de números aleatorios (TRNG, true random number generator).

El pyboard v1.1.
Fuente: MicroPython Store

La placa del pyboard tiene un tamaño de 4.1 cm × 3.2 cm × 0.2 cm y cuenta adicionalmente con los siguientes elementos:
  • Conector micro-USB: sirve como fuente de alimentación y también permite establecer comunicación serial con una computadora anfitriona.
  • Ranura para tarjeta microSD de capacidad estándar (SDSC) y alta capacidad (SDHC).
  • Acelerómetro de tres ejes.
  • Reloj en tiempo real (oscilador de cristal) a 32 kHz.
  • Dos botones: RST (hard reset) y USR (funcionalidad definida por el usuario).
  • Cuatro ledes (colores: rojo, verde, amarillo y azul).
  • 30 pines GPIO (general-purpose input/output).  

Comparando pyboard con Arduino

La principal ventaja de un pyboard sobre un Arduino tiene que ver con la forma en que soportan Python. En diversas entradas de este blog hemos programado el Arduino usando Python, pero no debemos perder de vista que el Arduino debe estar siempre conectado a una computadora anfitriona a través de un cable USB. Esto es así porque el programa de Python corre en el sistema anfitrión y no en el Arduino. El Arduino funge como esclavo que corre un ejecutable (típicamente escrito en C/C++) que implementa el protocolo de Firmata y que le permite establecer una comunicación serial en dos sentidos con el programa que lo controla y que corre en la PC anfitriona. El pyboard, por otro lado, puede correr programas en Python sin necesidad de estar conectado en todo momento a una computadora anfitriona. El pyboard tiene instalado localmente el intérprete de MicroPython y solo basta transferir (vía la conexión USB) a su memoria flash el programa *.py que deseamos correr. Una vez hecho esto, podemos desconectar el pyboard del sistema anfitrión y solo requerimos proveerle a la placa un suministro eléctrico alternativo, por ejemplo un banco de energía (como los que se usan para recargar las pilas de los teléfonos celulares). A partir de ese momento nuestro programa en Python se ejecuta automáticamente al encender o reiniciar el pyboard.

Un pyboard (en su estuche protector)
conectado a un banco de energía.

Cuando el pyboard está conectado a la computadora anfitriona podemos llevar a cabo una sesión interactiva con el shell de MicroPython corriendo directamente sobre la placa. Para lograr lo anterior tenemos que abrir una ventana de línea de comando en el sistema anfitrión y luego correr algún emulador de terminal tonta (por ejemplo screen o picocom) que permita conectarse al pyboard. Cuando corremos el shell podemos jugar y experimentar directamente con todas las facilidades provistas por la placa. El shell también puede ser bastante útil durante el proceso de depuración de nuestros programas. No existe una funcionalidad similar a ésta cuando se programa el Arduino usando C/C++.

El shell de MicroPython corriendo sobre el
pyboard y siendo visualizado en una
ventana de línea de comando en la
computadora anfitriona.

Otra diferencia entre el Arduino y el pyboard son las herramientas de desarrollo que se utilizan. Arduino necesita su propio IDE (ambiente de desarrollo integrado) para editar archivos, compilarlos y transferir los ejecutables a la placa. Para usar el pyboard no necesitamos ningún software especial, salvo el editor de nuestra preferencia. Resulta trivial la transferencia de archivos gracias a que la placa se comporta como una memoria USB al momento de conectarla a la máquina anfitriona, la cual puede estar corriendo Windows, macOS o Linux.

El pyboard tiene otra ventaja sobre Arduino que puede ser benéfica particularmente para principiantes. Al incorporar de fábrica cuatro ledes, un botón y un acelerómetro, es posible elaborar diversos proyectos interesantes en el pyboard sin requerir hardware adicional. Es posible empezar a hacer experimentos sencillos de computación física sin tener la necesidad de conseguir y conectar actuadores, sensores, resistencias y cables a un protoboard.

Caos resultado de un exceso de componentes electrónicos.
Fuente:
Harvard Business Review

Dado que el hardware del pyboard es de mayor capacidad y más sofisticado que la mayoría de los modelos existentes de Arduino, resulta también ser un tanto más costoso. Irónicamente, puede resultar que un programa escrito en C/C++ para Arduino se ejecute más rápido que su equivalente en Python para el pyboard. Esto se debe a que en estas plataformas el código de C/C++ se compila mientras que el de Python se interpreta. Como es bien sabido, los programas interpretados son usualmente más lentos y requieren más memoria que los compilados debido a la necesidad de traducir el código fuente mientras se corre. En la práctica, el impacto en los tiempos de ejecución normalmente no repercute de forma significativa en la mayoría de los programas escritos para el pyboard. Y si esto llegara a ser un problema, existe la opción de escribir porciones limitadas de código en C/C++/ensamblador que pueden ser invocadas desde el código de MicroPython para optimizar los cuellos de botella y obtener así un mejor desempeño pero sin reducir sustancialmente la productividad que se consigue al programar primordialmente en Python.

Comparando pyboard con Raspberry Pi

La Raspberry Pi (RPi) y el pyboard pertenecen a dos categorías diferentes de placas electrónicas. La RPi es una computadora monoplaca (single-board computer) que para fines prácticos es casi una computadora completa. Tiene su propio sistema operativo (usualmente alguna distribución de Linux) y para usarla necesitamos (generalmente) conectarle mouse, teclado y monitor. El modelo 3 de la RPi ya cuenta incluso con Bluetooth y Wi-Fi. Por otro lado, el pyboard (al igual que el Arduino) es un microcontrolador monoplaca (single-board microcontroller). Esto significa que un pyboard es más sencillo y tiene típicamente un propósito más restringido que una RPi. Conviene mencionar que una RPi puede servir de sistema anfitrión para programar a un pyboard.

Por sus características de hardware, una RPi modelo 3 resulta una buena opción para realizar proyectos de IoT (internet de las cosas). En contraste, el pyboard no cuenta con un esquema de comunicación inalámbrica, por lo que no resulta adecuado para este fin.

Al igual que el pyboard y el Arduino (y a diferencia de una PC común y corriente), la RPi cuenta con múltiples pines GPIO (entrada/salida de propósito general) que podemos usar para conectar y controlar diversos componentes, tales como actuadores y sensores. Al momento de programar la RPi podemos usar cualquier lenguaje soportado por su sistema operativo, por ejemplo C, C++, Java, Ruby, Python, Perl, JavaScript, etc. Como ya lo hemos mencionado varias veces, el pyboard está diseñado para programarse en Python.

Fuente: Day Break Tecnologies

Comentarios finales

El pyboard es una opción interesante en el mundo de la computación física. En primera instancia se parece al Arduino pero con el atractivo de que se programa directamente en Python. Lamentablemente es aún poco conocido, y por lo mismo existe menos documentación disponible (comparado con otras plataformas) y resulta complicado adquirirlo en países como México. En Estados Unidos se puede conseguir a través de Amazon.com o Adafruit. La empresa Adafruit realiza envíos de componentes electrónicos a México, pero hay que considerar los impuestos aduanales que se deben pagar al momento en que un producto de este tipo llega al país. En Europa y otras partes del mundo los interesados pueden comprar el pyboard en la tienda oficial de MicroPython. El costo de un pyboard v1.1 varía bastante según el proveedor, pero usualmente comienza en los 35 USD.

3 de julio de 2016

Midiendo distancias: el sensor ultrasónico

En esta entrada del blog veremos como usar la Raspberry Pi (RPi) junto con un sensor ultrasónico para medir la distancia a la que se encuentra algún objeto posicionado en frente de nuestro dispositivo.

El sensor ultrasónico que utilizaremos es el HC-SR04:

Sensor de distancia ultrasónico HC-SR04.

Este sensor es económico (tiene un precio comercial que oscila entre dos y cuatro dólares estadounidenses) pero bastante potente. Permite calcular distancias en un rango de 2 a 400 cm. y resulta relativamente fácil programarlo.

Un sensor ultrasónico funciona de manera similar a la capacidad de ecolocación que tienen ciertos animales tales como los murciélagos y delfines: el animal emite un sonido que rebota al encontrar un obstáculo; la distancia se estima a partir del tiempo que tarda en regresar el eco de la señal. El sonar que utilizan los submarinos para navegar en el océano está basado también en este mismo principio.

Ecolocación: el murciélago A genera un echo E que rebota
en la presa B generando una onda reflejada R. Con eso el
murciélago puede determinar la distancia d a su presa.
Imagen recuperada de: Wikimedia Commons.
 

Se dice que un sonido es ultrasónico cuando su onda tiene una frecuencia que se halla por arriba del espectro audible por el oído del ser humano, generalmente arriba de los veinte mil hertz.

El hardware

Además de un sensor HC-SR04 y una RPi (de cualquier generación) adecuadamente equipada y configurada, requeriremos los siguientes componentes de hardware:
  • Un protoboard
  • Dos resistencias R1 y R2 (ver discusión posterior)
  • Cables conectores

Vista esquemática del sensor HC-SR04.

El sensor HC-SR04 cuenta con cuatro pines:
  • VCC: Alimentación de 5 voltios
  • TRIG: Trigger o activador (entrada) del sensor
  • ECHO: Eco (salida) del sensor
  • GND: Tierra
Es importante mencionar que la señal de salida ECHO del HC-SR04 es de 5V. Esto hace que el sensor sea compatible con Arduino y otras plataformas de microcontroladores. Sin embargo, para la RPi los pines GPIO de entrada son de 3.3V. Si se envía una señal de 5V a un puerto desprotegido de entrada de 3.3V, éste puede resultar dañado, y eso es algo que definitivamente queremos evitar. Necesitaremos usar un circuito divisor resistivo para disminuir el voltaje de salida del sensor y así permitir que la RPi reciba la señal correspondiente sin problemas.

Un divisor resistivo consiste de dos resistencias (R1 y R2) en serie conectadas a un voltaje de entrada (Vin), el cual debe ser reducido a un voltaje de salida (Vout). En nuestro circuito, Vin corresponderá al pin ECHO, el cual requiere disminuir de 5V al Vout que necesitamos de 3.3V. El siguiente esquema muestra la disposición que deben tener las resistencias:


Un divisor resistivo se define a partir de la siguiente fórmula:


Sabemos que Vin = 5V y Vout = 3.3V. Por tanto lo que nos interesa calcular es:


Despejando tenemos:


Si conocemos el valor de la resistencia R2 podemos calcular directamente la resistencia R1. Por ejemplo, si R2 = 2kΩ, R1 tendría que ser igual a 1030.303Ω o algún otro valor cercano (por ejemplo 1kΩ).

La siguiente figura, elaborada en Fritzing, muestra la manera de conectar todos nuestros componentes:

Vista de protoboard Fritzing de un sensor HC-SR04 conectado a la RPi.

Las conexiones son las siguientes:
  • El pin VCC del sensor se conecta a cualquier pin de 5V de la RPi.
  • El pin TRIG del sensor se conecta al pin GPIO 23 de la RPi.
  • El pin GPIO 24 de la RPi se conecta a una pista disponible del protoboard. Un extremo de la resistencia R2 se conecta a esta pista, y el otro extremo se conecta a tierra (cualquiera de los pines GND de la RPi). Un extremo de la resistencia R1 se conecta también a esta pista, y el otro extremo se conecta al pin ECHO del sensor.
  • El pin GND del sensor se conecta a tierra (cualquiera de los pines GND de la RPi).

Vista física del sensor ultrasónico de distancias
colocado en el protoboard con sus respectivos
cables conectores.

El software

Para controlar el sensor y realizar las mediciones debemos efectuar lo siguiente:
  1. Poner TRIG en bajo durante dos segundos con el fin de estabilizarlo.
  2. Poner TRIG en alto por 10µs (diez microsegundos) y ponerlo inmediatamente después en bajo. En este momento el sensor envía 8 pulsos ultrasónicos de 40kHz y coloca ECHO en alto. Es necesario detectar cuando ocurre esto último para comenzar a medir el tiempo en ese preciso instante.
  3. ECHO se mantiene en alto hasta que reciba el eco reflejado por algún obstáculo. En ese momento ECHO se pone en bajo y significa que hay que terminar la medición del tiempo.
  4. A partir de la medición de tiempo obtenida ya podemos calcular la distancia como se discute a continuación.
Sabemos que la velocidad v es igual a distancia d entre tiempo t:


Despejando la distancia d, tenemos que ésta es igual a la velocidad v multiplicada por el tiempo t:


La velocidad que usaremos aquí es la del sonido, que es de 34,300 centímetros por segundo. Al final necesitamos dividir a la mitad el valor resultante debido a que nuestra medición corresponde al tiempo que tarda el pulso ultrasónico en llegar al obstáculo y regresar nuevamente al sensor.


En esta última fórmula, el tiempo t debe estar en segundos y la distancia resultante d en centímetros.

Con todo lo anterior tomado en cuenta, ya estamos en condiciones de escribir un programa en Python que controle un sensor ultrasónico HC-SR04. El código debe comenzar con las inicializaciones y configuraciones necesarias, similares a las que se explican detalladamente en la entrada titulada El LED parpadeante.

El programa completo en Python 2.7 se muestra a continuación:
# coding: utf-8

# Archivo: ultrasonico.py

# Importar las funciones necesarias de los módulos RPi,
# y time.
import RPi.GPIO as GPIO
import time

# Pin GPIO donde está conectado el activador (entrada) del
# sensor HC-SR04.
TRIG = 23

# Pin GPIO donde está conectado el eco (salida) del sensor
# HC-SR04.
ECHO = 24

# Indicar que se usa el esquema de numeración de pines
# de BCM (Broadcom SOC channel), es decir los números de
# pines GPIO (General-Purpose Input/Output).
GPIO.setmode(GPIO.BCM)

# Establecer que TRIG es un canal de salida.
GPIO.setup(TRIG, GPIO.OUT)

# Establecer que ECHO es un canal de entrada.
GPIO.setup(ECHO, GPIO.IN)

print "Medición de distancias en progreso"

try:
    # Ciclo infinito.
    # Para terminar el programa se debe presionar Ctrl-C.
    while True:

        # Apagar el pin activador y permitir un par de
        # segundos para que se estabilice.
        GPIO.output(TRIG, GPIO.LOW)
        print "Esperando a que el sensor se estabilice"
        time.sleep(2)

        # Prender el pin activador por 10 microsegundos
        # y después volverlo a apagar.
        GPIO.output(TRIG, GPIO.HIGH)
        time.sleep(0.00001)
        GPIO.output(TRIG, GPIO.LOW)

        # En este momento el sensor envía 8 pulsos
        # ultrasónicos de 40kHz y coloca su salida ECHO
        # en HIGH. Se debe detectar este evento e iniciar
        # la medición del tiempo.
        print "Iniciando eco"
        while True:
            pulso_inicio = time.time()
            if GPIO.input(ECHO) == GPIO.HIGH:
                break

        # La salida ECHO se mantendrá en HIGH hasta recibir
        # el eco reflejado por el obstáculo. En ese momento
        # el sensor pondrá ECHO en LOW y se debe terminar
        # la medición del tiempo.
        while True:
            pulso_fin = time.time()
            if GPIO.input(ECHO) == GPIO.LOW:
                break

        # La medición del tiempo es en segundos.
        duracion = pulso_fin - pulso_inicio

        # Calcular la distancia usando la velocidad del
        # sonido y considerando que la duración incluye
        # la ida y vuelta.
        distancia = (34300 * duracion) / 2

        # Imprimir resultado.
        print "Distancia: %.2f cm" % distancia

finally:
    # Reiniciar todos los canales de GPIO.
    GPIO.cleanup()
En una terminal necesitamos correr el siguiente comando para ejecutar nuestro programa:
sudo python ultrasonico.py
Mientras el programa se esté ejecutando se puede acercar y alejar algún objeto del sensor para ver si el programa funciona correctamente. Podemos usar una regla o cinta métrica para verificar los resultados. La salida del programa debe verse algo así:
Medición de distancias en progreso
Esperando a que el sensor se estabilice
Iniciando eco
Distancia: 79.30 cm
Esperando a que el sensor se estabilice
Iniciando eco
Distancia: 78.91 cm
Esperando a que el sensor se estabilice
Iniciando eco
Distancia: 79.70 cm
Esperando a que el sensor se estabilice
Iniciando eco
Distancia: 12.73 cm
Esperando a que el sensor se estabilice
Iniciando eco
Distancia: 12.49 cm
Esperando a que el sensor se estabilice
Iniciando eco
Distancia: 12.57 cm
Esperando a que el sensor se estabilice
Iniciando eco
Distancia: 78.82 cm
Esperando a que el sensor se estabilice
Es necesario presionar Ctrl-C para terminar el programa.

5 de junio de 2014

Contador binario

En esta entrada del blog de EduPython elaboraremos un nuevo proyecto para la Raspberry Pi (RPi). En esta ocasión vamos a hacer un contador binario. Utilizaremos cuatro LEDs para representar cuatro bits que nos permitirán contar del 0000 al 1111 en binario (0 al 15 en decimal). Tendremos también un botón, y cada vez que lo presionemos se incrementará en uno el contador.

La siguiente tabla muestra los valores que pueden tener los cuatro bits junto con su valor correspondiente en decimal:

Valores decimales y
sus equivalentes en binario.

Comencemos por escribir una función en Python que dado un entero n del 0 al 15, devuelva una lista con cuatro enteros que correspondan a los cuatro bits de la representación binaria de n:
def binario(n):
    resultado = []
    for i in range(4):
        resultado.append(n & 1)
        n >>= 1
    return resultado
Internamente en la computadora, los números están almacenados en binario, así que podemos inspeccionar los bits individuales de n para crear la lista con los valores que necesitamos. Los operadores & y >>= del código de arriba sirven para tal efecto en dos pasos dentro del ciclo for, el cual se repite un total de cuatro veces (una vez por cada bit que nos interesa):
  • La expresión n & 1 realiza un and (conjunción) a nivel de bits entre n y 1, efectivamente obteniendo el cero o el uno contenido en el bit 0 (el bit que está más a la derecha) de la representación binaria de n, y almacenando ese valor al final de la lista resultado usando el método append()
  • La expresión n >>= 1 desplaza todos los bits de n una posición hacia la derecha. Es decir el bit 1 original se convierte en el nuevo bit 0, el bit 2 original se convierte en el nuevo bit 1, y así sucesivamente. El bit 0 original se pierde en este proceso, pero no importa, ya que fue inspeccionado y almacenado en el paso anterior.
Como ejemplo, si invocamos la función binario() con argumento 1, ésta nos devuelve la lista [1, 0, 0, 0]. La lista resultante pudiera dar la impresión de estar invertida. Sin embargo, por simplicidad nos conviene que el bit 0 se encuentre en la primera posición de la lista, el bit 1 en la segunda, y así sucesivamente.

Vale la pena notar que la función binario() solo considera los cuatro bits menos significativos (los bits que están más a la derecha) de su entrada n. Esto significa, en términos prácticos, que la función devuelve una lista con la representación en binario de n módulo 16. Lo anterior es útil para saber interpretar el resultado para los casos en los que n esté fuera del rango del 0 al 15.

El hardware

Además de la RPi, para este proyecto requerimos los siguientes componentes:
  • Un protoboard.
  • Un botón o pulsador (push button).
  • Cuatro LEDs (diodos emisores de luz).
  • Cuatro resistencias de 330Ω (bandas naranja, naranja, café).
  • Siete cables conectores.
La siguiente figura, elaborada con Fritzing, muestra la manera de conectar los componentes:

Vista de protoboard Fritzing para el contador binario.

Usando un cable conectamos un pin de tierra (GND) de la RPi a uno de los buses para alimentación negativa (línea azul) del protoboard. El botón se coloca de tal forma que un par de patitas quede a la izquierda y el otro par a la derecha del canal central del protoboard. Conectamos una de las patitas superiores del botón a tierra (bus negativo), y una de las patitas inferiores la conectamos al pin 22 de la RPi. Colocamos los cuatro LEDs en el protoboard, en donde el cátodo (patita corta) y el ánodo (patita larga) de cada LED debe estar en su propia pista. Conectamos el cátodo de cada uno de los LEDs a una resistencia de 330Ω. El otro extremo de cada resistencia lo conectamos a tierra (bus negativo). Ahora conectamos el ánodo de cada LED a uno de los pines de la RPi:
  • Pin 18 para el LED correspondiente al bit 0 (LED inferior de la figura).
  • Pin 23 para el LED correspondiente al bit 1.
  • Pin 24 para el LED correspondiente al bit 2.
  • Pin 25 para el LED correspondiente al bit 3 (LED superior de la figura).
Con nuestros componentes ya en su lugar, estamos listos para escribir lo que falta de nuestro programa.

El software

Para controlar los LEDs, requerimos básicamente hacer lo mismo que hicimos en la entrada del LED parpadeante. Dado que son cuatro LEDs a controlar en lugar de uno, usamos una lista con los respectivos números de pin de salida. El siguiente código se encarga de establecer a los pines 18, 23, 24 y 25 de la RPi como pines de salida y además se asegura de que inicialmente se encuentren apagados:
PIN_LEDS = [18, 23, 24, 25]
for i in PIN_LEDS:
    GPIO.setup(i, GPIO.OUT, initial=GPIO.LOW)
En la RPi usaremos el pin 22 para inspeccionar el estado de nuestro botón. Esto quiere decir que tenemos que configurar dicho pin como un pin de entrada para poder leer una señal LOW (0 voltios) o HIGH (3.3 voltios), según el estado del botón.

De acuerdo a la manera en que conectamos nuestros componentes, el pin 22 recibe la señal de tierra (0 voltios) solo cuando se cierra el circuito (el botón se encuentra presionado). Pero, ¿qué pasa cuando no es así? En ese caso se dice que el pin está “flotando”, lo que puede producir resultados inesperados. Para evitar este problema podemos utilizar una resistencia pull-up, con lo que efectivamente proveemos de un valor por omisión al pin cuando el circuito no está cerrado (el botón no está presionado). Lo anterior se puede lograr vía hardware (colocando una resistencia de 10KΩ entre el pin de entrada y una alimentación de 3.3 voltios) o por software configurando el pin para que utilice la resistencia pull-up interna provista por la misma RPi. El siguiente código se encarga de este último caso:
PIN_BOTON = 22
GPIO.setup(PIN_BOTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
Con esta configuración podemos usar la función input() indicando el número de pin de entrada como argumento. En este contexto, la función devuelve LOW (0) para indicar que el botón está presionado, o HIGH (1) en caso contrario. Este comportamiento es inverso a lo que alguien podría inicialmente suponer.

El siguiente ciclo infinito verifica en cada iteración si el botón está siendo presionado en ese preciso instante. En caso afirmativo, incrementa el contador y usa su representación binaria para prender o apagar los LEDs correspondientes:
contador = 0

while True:
    if GPIO.input(PIN_BOTON) == GPIO.LOW:
        contador = (contador + 1) % 16
        for pin, bit in zip(PIN_LEDS, binario(contador)):
            GPIO.output(pin, bit)
        time.sleep(0.2)
Dentro del cuerpo del if la instrucción:
contador = (contador + 1) % 16
se utiliza para incrementar en uno a la variable contador. La operación de módulo (%) 16 sirve para reiniciar contador a cero una vez que haya llegado a su máximo valor (15). El siguiente código es equivalente, aunque menos compacto:
contador += 1
if contador == 16:
    contador = 0
La función zip() que se usa dentro de la instrucción for permite iterar simultáneamente sobre dos o más colecciones. Por ejemplo, si PIN_LEDS es [18, 23, 24, 25] y el resultado de binario(1) es [1, 0, 0, 0], entonces la función zip() hace que las variables pin y bit sean 18 y 1 en la primera iteración del for, 23 y 0 en la segunda, y así sucesivamente.

Al final del cuerpo del if usamos la función sleep() para hacer una breve pausa antes de intentar leer nuevamente el estado del botón. De no hacerlo podríamos incrementar más de una vez a contador debido al tiempo que transcurre entre que presionamos el botón y el momento en que lo soltamos.

El programa completo se muestra a continuación:
# Archivo: contador_binario.py

import RPi.GPIO as GPIO
import time

# Usar el esquema de numeracion de pines de Broadcom SoC.
GPIO.setmode(GPIO.BCM)

# Inicializar pines de salida.
PIN_LEDS = [18, 23, 24, 25]
for i in PIN_LEDS:
    GPIO.setup(i, GPIO.OUT, initial=GPIO.LOW)

# Inicializar pin de entrada.
PIN_BOTON = 22
GPIO.setup(PIN_BOTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)

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

    La lista resultante es de la forma:

       [bit0, bit1, bit2, bit3]

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

contador = 0
try:
    while True:
        if GPIO.input(PIN_BOTON) == GPIO.LOW:
            contador = (contador + 1) % 16
            for pin, bit in zip(PIN_LEDS, binario(contador)):
                GPIO.output(pin, bit)
            time.sleep(0.2)
finally:
    GPIO.cleanup()

Desde una terminal de la RPi, debemos ejecutar el siguiente comando para correr el programa:
sudo python contador_binario.py
La siguiente foto muestra cómo se ve nuestro proyecto después de correr el programa y presionar once veces el botón:

Contador binario mostrando
1011 binario (11 decimal).

30 de mayo de 2014

Código morse

En una entrada anterior elaboramos un proyecto relativamente sencillo en el que usamos la Raspberry Pi (RPi) para prender y apagar un LED dentro de un ciclo infinito. En esta ocasión vamos a utilizar la misma configuración de hardware de dicho proyecto, pero ahora con un programa más interesante que permita enviar mensajes utilizando código morse. Este es otro proyecto clásico para los recién iniciados en la RPi y otras plataformas electrónicas afines.

Creadores del código morse:
Samuel Morse y Alfred Vail

La Wikipedia explica los detalles que necesitamos conocer sobre el código morse:
Fue desarrollado por Alfred Vail mientras colaboraba en 1830 con Samuel Morse en la invención del telégrafo eléctrico. Vail creó un método según el cual cada letra o número era transmitido de forma individual con un código consistente en rayas y puntos, es decir, señales telegráficas que se diferencian en el tiempo de duración de la señal activa. La duración del punto es la mínima posible. Una raya tiene una duración de aproximadamente tres veces la del punto. Entre cada par de símbolos de una misma letra existe una ausencia de señal con duración aproximada a la de un punto. Entre las letras de una misma palabra, la ausencia es de aproximadamente tres puntos. Para la separación de palabras transmitidas el tiempo es de aproximadamente tres veces el de la raya.

El telégrafo original de Samuel Morse.
De la colección del museo Nacional Smithsoniano
de Historia de los Estados Unidos.

El alfabeto morse (junto con símbolos de numeración y puntuación) se muestra en la siguiente tabla:

Código morse

Si deseamos, por ejemplo, enviar el mensaje “hola” usando el código morse, tendríamos la siguiente secuencia de rayas y puntos:
····  −−−  ·−··  ·−
Comúnmente, los puntos y rayas son generados a partir de señales audibles o el encendido y apagado de una fuente de luz. En nuestro proyecto usaremos nuestro LED para parpadear los puntos y rayas del mensaje que deseamos enviar. En las siguientes secciones iremos construyendo nuestro programa poco a poco.

Usando diccionarios

La tabla de código morse puede ser convertida a un diccionario de Python. Un diccionario es una estructura de datos que permite asociar una cierta llave a un valor determinado. Por ejemplo:
d = { 'pollito': 'chicken',
      'gallina': 'hen',
      'lapiz':   'pencil',
      'pluma':   'pen'
    }
En este caso, las cadenas 'pollito', 'gallina', 'lapiz' y 'pluma' son las llaves del diccionario contenido en la variable d. Decimos que la llave 'pollito' está asociada al valor 'chicken', y así con cada una de las llaves. Es importante notar en la sintaxis del diccionario el uso del carácter de dos puntos (:). A la izquierda de este carácter va la llave, y a la derecha está su valor asociado. Las parejas de llave-valor están delimitadas entre sí usando comas. Otro detalle importante a notar es que las llaves deben ser siempre objetos inmutables (por ejemplo números o cadenas de caracteres).

La expresión:
d['lapiz']
devuelve la cadena 'pencil', ya que la llave 'lapiz' está asociada al valor 'pencil' dentro del diccionario d.

Por otro lado, la expresión:
d['ventana']
produce un error (arroja la excepción KeyError) ya que la llave 'ventana' no existe en el diccionario d.

Si deseamos determinar si una llave existe en el diccionario podemos utilizar el operador in:
'lapiz' in d
En este caso, la expresión anterior devolvería True. Si la llave no hubiera existido, el resultado devuelto habría sido False.

Hay muchas más operaciones que se pueden efectuar sobre un diccionario. La documentación oficial describe todos los detalles de este tipo de dato. Cabe también mencionar que los diccionarios de Python están implementados usando tablas de hash, por lo que los tiempos de acceso e inserción son bastante rápidos.

El diccionario que necesitamos para nuestro proyecto quedaría así:
codigo = {
    'A': '.-',     'B': '-...',    'C': '-.-.',
    'D': '-..',    'E': '.',       'F': '..-.',
    'G': '--.',    'H': '....',    'I': '..',
    'J': '.---',   'K': '-.-',     'L': '.-..',
    'M': '--',     'N': '-.',      'O': '---',
    'P': '.--.',   'Q': '--.-',    'R': '.-.',
    'S': '...',    'T': '-',       'U': '..-',
    'V': '...-',   'W': '.--',     'X': '-..-',
    'Y': '-.--',   'Z': '--..',    '1': '.----',
    '2': '..---',  '3': '...--',   '4': '....-',
    '5': '.....',  '6': '-....',   '7': '--...',
    '8': '---..',  '9': '----.',   '0': '-----',
    '.': '.-.-.-', ',': '--..--',  ':': '---...',
    ';': '-.-.-.', '?': '..--..',  '!': '-.-.--',
    '"': '.-..-.', "'": '.----.',  '+': '.-.-.',
    '-': '-....-', '/': '-..-.',   '=': '-...-',
    '_': '..--.-', '$': '...-..-', '@': '.--.-.',
    '&': '.-...',  '(': '-.--.',   ')': '-.--.-'
}

Filtrando el mensaje

El mensaje que deseamos enviar usando código morse estará contenido en una cadena de caracteres (string) de Python. Sin embargo dicha cadena puede tener algunos problemas. Por ejemplo, la cadena “¡Adiós Niños!” contiene caracteres que no están en nuestro diccionario. Específicamente aparecen tres casos que tenemos que considerar:
  1. Letras minúsculas.
  2. Letras acentuadas (ó) o con tilde (ñ).
  3. Símbolos especiales (¡).
Para el caso 2 definiremos la siguiente función, la cual elimina los acentos, tildes y diéresis que hay en ciertas letras que usamos en el idioma español:
import unicodedata

def convierte_ascii(c):
    return (unicodedata.normalize('NFD', c.decode('utf8'))
            .encode('ascii', 'ignore'))
Si invocamos esta función así:
convierte_ascii('¡Adiós Niños!')
nos devuelve la cadena 'Adios Ninos!'. Como se puede ver, la función se encarga parcialmente también del caso 3.  Cualquier carácter que no sea ASCII se elimina, como es el caso del signo de exclamación invertido (¡). Sin embargo, hay algunos caracteres ASCII (por ejemplo el asterisco o el signo de por ciento) que no son eliminados, por lo que tendrían que estar presentes como llaves en nuestro diccionario de código morse, pero no es así. Nos encargaremos de estos caracteres en la siguiente sección.

El caso 1 se resuelve de manera muy sencilla. Después de transformar las letras con acentos, diéresis y tildes, solo necesitamos convertir las letras resultantes a mayúsculas. Para ello usamos el método upper() sobre la cadena.

Traduciendo a código morse

Una vez que tenemos el diccionario de codigo morse y el mecanismo de filtrado, solo falta tomar cada carácter del mensaje de entrada y convertirlo a su equivalente en morse. Supongamos que la variable mensaje tiene el texto a traducir. El siguiente código se encarga de hacer la parte principal de la conversión:
resultado = []
for c in convierte_ascii(mensaje).upper():
    if c in codigo:
        resultado.append(codigo[c])
    elif c == ' ':
        resultado.append('  ')
Usamos la lista resultado para ir acumulando las cadenas resultantes de la traducción. El for trabaja sobre el mensaje filtrado, el cual solo contiene caracteres ASCII y letras mayúsculas. Para cada carácter del mensaje, si es un carácter que existe como llave en el diccionario, entonces se coloca su valor asociado (una cadena con la secuencia de puntos y rayas correspondiente) en la lista resultante. Como caso especial, si hay un espacio en el mensaje, éste se denota con dos espacios que representan un separador entre palabras. Cualquier otro carácter del mensaje de entrada es ignorado, y con ello nos encargamos completamente del caso 3 de la sección anterior.

Si el valor de la variable mensaje es '¡Adiós Niños!', después de ejecutar el ciclo for la variable resultado contiene lo siguiente:
['.-', '-..', '..', '---', '...', '  ',
 '-.', '..', '-.', '---', '...', '-.-.--']
Ahora, usaremos el método join() para convertir el contenido de esta lista en una gran cadena. A continuación tenemos un ejemplo del uso de este método:
'#'.join(['uno', 'dos', 'tres', 'cuatro'])
El método recibe una lista de cadenas y produce una sola cadena que resulta de concatenar todas las cadenas pero delimitándolas entre sí usando la cadena correspondiente al objeto receptor del método (la cadena '#' en el ejemplo). La expresión anterior produciría 'uno#dos#tres#cuatro'.

Regresando a nuestro proyecto, aquí usaremos un espacio para delimitar todas las cadenas contenidas en la lista resultado:
' '.join(resultado)
Con el mensaje '¡Adiós Niños!', la cadena de código morse después del proceso completo de  traducción sería la siguiente:
'.- -.. .. --- ...    -. .. -. --- ... -.-.--'
Hay que notar que quedó un espacio en blanco entre letras y cuatro espacios entre palabras.

Controlando el LED

Para controlar el LED desde nuestra RPi, definiremos dos constantes simbólicas que representan las duraciones en segundos de un punto y una raya:
TIEMPO_PUNTO = 0.2
TIEMPO_RAYA  = TIEMPO_PUNTO * 3
Ahora procesaremos con un for cada carácter del mensaje traducido a código morse. Los caracteres solo pueden ser puntos, rayas o espacios. Si encontramos un punto, prendemos el LED por la duración de un punto. Si encontramos una raya, prendemos el LED por la duración de una raya. Si encontramos un espacio, apagamos el LED por la duración de un punto. Al final de cada iteración del ciclo, sin importar el carácter que hayamos encontrado anteriormente, apagamos el LED por la duración de un punto.
for c in convierte_morse(mensaje):
    if c == '.':
        GPIO.output(PIN_LED, GPIO.HIGH)
        time.sleep(TIEMPO_PUNTO)
    elif c == '-':
        GPIO.output(PIN_LED, GPIO.HIGH)
        time.sleep(TIEMPO_RAYA)
    elif c == ' ':
        GPIO.output(PIN_LED, GPIO.LOW)
        time.sleep(TIEMPO_PUNTO)

    GPIO.output(PIN_LED, GPIO.LOW)
    time.sleep(TIEMPO_PUNTO)
Si analizamos con cuidado la lógica de todo nuestro código podremos percatarnos que el LED efectivamente queda apagado por la duración de un punto entre símbolos (punto y raya) de una misma letra, por la duración de tres puntos entre dos letras de una misma palabra, y por la duración de nueve puntos entre dos palabras del mismo mensaje, tal como lo especifica el artículo citado de Wikipedia.

Así queda el programa completo:
# coding: utf8

# Archivo: morse.py

# La primera línea del archivo especifica que estamos
# usando una codificación UTF-8 en nuestro archivo
# fuente. Es indispensable hacer esto en Python 2 con
# el fin de poder incluir en el archivo caracteres que
# no sean ASCII (por ejemplo letras con acentos y eñes).

import RPi.GPIO as GPIO
import time
import unicodedata

# Duración de un punto y una raya.
TIEMPO_PUNTO = 0.2
TIEMPO_RAYA  = TIEMPO_PUNTO * 3

# Pin donde está conectado el ánodo del LED.
PIN_LED = 24

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

codigo = {
    'A': '.-',     'B': '-...',    'C': '-.-.',
    'D': '-..',    'E': '.',       'F': '..-.',
    'G': '--.',    'H': '....',    'I': '..',
    'J': '.---',   'K': '-.-',     'L': '.-..',
    'M': '--',     'N': '-.',      'O': '---',
    'P': '.--.',   'Q': '--.-',    'R': '.-.',
    'S': '...',    'T': '-',       'U': '..-',
    'V': '...-',   'W': '.--',     'X': '-..-',
    'Y': '-.--',   'Z': '--..',    '1': '.----',
    '2': '..---',  '3': '...--',   '4': '....-',
    '5': '.....',  '6': '-....',   '7': '--...',
    '8': '---..',  '9': '----.',   '0': '-----',
    '.': '.-.-.-', ',': '--..--',  ':': '---...',
    ';': '-.-.-.', '?': '..--..',  '!': '-.-.--',
    '"': '.-..-.', "'": '.----.',  '+': '.-.-.',
    '-': '-....-', '/': '-..-.',   '=': '-...-',
    '_': '..--.-', '$': '...-..-', '@': '.--.-.',
    '&': '.-...',  '(': '-.--.',   ')': '-.--.-'
}

def convierte_ascii(c):
    """Convierte c a una cadena que únicamente contiene
    caracteres ASCII.
    
    Convierte caracteres con acento, diéresis o tilde al
    carácter simple correspondiente. Elimina cualquier otro
    carácter que no sea ASCII.
    """
    return (unicodedata.normalize('NFD', c.decode('utf8'))
            .encode('ascii', 'ignore'))

def convierte_morse(mensaje):
    """Convierte mensaje a una cadena de símbolos morse.
    
    Las letras se delimitan entre sí con un espacio. Las
    palabras se delimitan con cuatro espacios.
    """
    resultado = []
    for c in convierte_ascii(mensaje).upper():
        if c in codigo:
            resultado.append(codigo[c])
        elif c == ' ':
            resultado.append('  ')
    return ' '.join(resultado)

def parpadea_morse(mensaje):
    """Prende y apaga un LED para enviar mensaje usando
    código morse.
    """
    try:
        for c in convierte_morse(mensaje):
            if c == '.':
                GPIO.output(PIN_LED, GPIO.HIGH)
                time.sleep(TIEMPO_PUNTO)
            elif c == '-':
                GPIO.output(PIN_LED, GPIO.HIGH)
                time.sleep(TIEMPO_RAYA)
            elif c == ' ':
                GPIO.output(PIN_LED, GPIO.LOW)
                time.sleep(TIEMPO_PUNTO)

            GPIO.output(PIN_LED, GPIO.LOW)
            time.sleep(TIEMPO_PUNTO)
    finally:
        GPIO.cleanup()

# Coloca aquí tu mensaje.
parpadea_morse('Este es un mensaje en código morse.')
Para correr el programa, teclear en la terminal de la RPi:
sudo python morse.py
Si todo salió bien, se debe ver el LED parpadeando el mensaje indicado en código morse.

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.