25 de junio de 2014

Cómo programar a tu Arduino

En esta entrada explicaremos cómo se programa un Arduino usando el lenguaje de programación Python.

Arte conceptual de Toby Shelton para la película
Cómo entrenar a tu dragón de DreamWorks Animation.

Opciones para programar al Arduino

Partimos primero del supuesto de que tenemos una computadora anfitriona (con Windows, Linux o MacOS) conectada a un Arduino a través de un cable USB tipo A-B (como el que usamos comúnmente para conectar una computadora a una impresora), tal como se muestra en la siguiente figura:


Arduino conectado a la computadora 
anfitriona a través del cable USB.

A partir de esta configuración, hay dos lugares en donde un programa puede ser ejecutado:
  1. Directamente en el Arduino.
  2. En la computadora anfitriona. 
En la opción 1, la computadora anfitriona sirve para desarrollar el programa y compilarlo para producir un archivo ejecutable que se descarga posteriormente al Arduino a través de la conexión USB. Dicho archivo no debe sobrepasar el espacio de la memoria flash del microcontrolador (32 Kbytes en el caso del Arduino Uno).

En la opción 2, por otro lado, se instala en el Arduino un firmware que permite que un programa corriendo en la computadora anfitriona controle las acciones del Arduino a través del intercambio de datos vía la conexión USB. El firmware que usualmente se utiliza con este fin se llama Firmata.

Para la opción 1 comúnmente se utiliza el lenguaje de programación C++ junto con el ambiente de desarrollo integrado (IDE) de Arduino. Una ventaja de este esquema es que, una vez cargado el programa en el Arduino, es posible retirar la computadora anfitriona por completo. Dado que la conexión USB sirve también como fuente de alimentación, es necesario suplir al Arduino de una fuente alterna. Por ejemplo, es posible conectar al Arduino a una pila de nueve voltios.

Arduino con pila de 9V como fuente de alimentación
y sin conexión a la computadora anfitriona.

En cuanto a la opción 2, ésta tiene la desventaja fundamental de que nunca podemos retirar la computadora anfitriona; sin embargo, tiene dos ventajas significativas:
  • Ya que nuestros programas corren en la computadora anfitriona y no directamente en el Arduino, no estamos restringidos a tener que usar C++. Podemos usar cualquier lenguaje de programación en la que exista una interfaz con Firmata: Processing, Pascal, Perl, C#, PHP, Java, JavaScript, Clojure, Ruby y por su puesto Python, por mencionar algunos.
  • El código de nuestros programas no está limitado al tamaño de la memoria flash del microcontrolador, sino al tamaño de la memoria de la computadora anfitriona (kilobytes vs. gigabytes).
Dado que deseamos programar el Arduino usando Python, estamos obligados a utilizar la opción 2.

Instalación del software

Hay que seguir estos tres pasos:
  1. Descargar e instalar el software de Arduino, según el sistema operativo de la computadora anfitriona, tal como se describe en la página Comienza a usar Arduino. Al momento de configurar el IDE, hay que anotar el puerto serie (serial port) al que se conecta el Arduino, ya que más adelante haremos referencia a éste. Según el sistema operativo, el puerto serie puede ser algo así como COM3 (Windows), /dev/ttyACM0 (Linux) o /dev/tty.usbserial (MacOS).
  2. Descargar Firmata en el Arduino. En el IDE de Arduino seleccionar File del menú principal. De ahí seleccionar: Examples/Firmata/StandardFirmata.



    Posteriormente, presionar Ctrl-U para compilar y copiar el código de Firmata al Arduino. Después de un momento debe aparecer el texto “Done uploading” en la barra de mensajes del IDE.
  3. Descargar e instalar la biblioteca pyFirmata en la computadora anfitriona. Esta biblioteca permite que un programa de Python, corriendo en una computadora anfitriona, se comunique vía USB con un Arduino corriendo Firmata. Desde una terminal (posiblemente con privilegios de administrador), teclear:
    pip install pyfirmata
    Si el sistema de administración de paquetes de Python pip no está instalado, referirse a la siguiente liga: http://pip.readthedocs.org/en/latest/installing.html. En algunos sistemas en necesario usar el comando pip3 en lugar de pip para instalar bibliotecas de Python 3.

Resumen del API de pyFirmata

NOTA: Todo el código presentado aquí fue probado con Python 3.4.

Para comenzar a usar pyFirmata debemos importar el paquete correspondiente al inicio de nuestro programa:
import pyfirmata
Podemos determinar qué versión de pyFirmata estamos usando:
print('pyFirmata version:', pyfirmata.__version__)
A continuación creamos un objeto que representa nuestra placa (board) de Arduino. Para ello necesitamos indicar como cadena de caracteres el puerto serie que anotamos durante la configuración del IDE ('/dev/ttyACM0' en mi caso, ya que estoy usando Linux):
placa = pyfirmata.Arduino('/dev/ttyACM0')
Con el objeto referido por la variable placa podemos determinar la versión de Firmata que está usando nuestro Arduino. El método get_firmata_version() devuelve una tupla con dos valores: la versión mayor y la versión menor. Podemos utilizar una asignación paralela para extraer los valores de la tupla en dos variables:
v_mayor, v_menor = placa.get_firmata_version()
print('Firmata version mayor:', v_mayor)
print('Firmata version menor:', v_menor)
Al correr estas instrucciones, la salida en mi sistema me indica que la versión mayor es 2 y la versión menor es 3, por lo que el número completo de versión es 2.3.

El método get_pin() sirve para activar un pin en el Arduino, configurándolo según la cadena que se le envía como argumento. Dicha cadena está compuesta de a o d (para indicar que el pin es analógico o digital), el número de pin, y el modo (i para entrada, o para salida, p para PWM). Estos tres elementos se separan entre sí usando un carácter de dos punto (:). Por ejemplo d:12:o indica que el pin 12 es una salida digital. Este es un ejemplo de cómo se utiliza el método:
salida = placa.get_pin('d:12:o')
El siguiente ejemplo activa el pin 8 del Arduino como una entrada digital.
entrada = placa.get_pin('d:8:i')
Después del código de arriba, es necesario invocar el método enable_reporting() sobre el objeto que representa el pin de entrada para poder leer las señales recibidas:
entrada.enable_reporting()
Si se utilizan uno o más pines de entrada es necesario crear un thread iterador, de lo contrario la placa puede seguir enviando datos por la conexión serial hasta producir un desbordamiento. Para crear dicho thread usamos la siguiente instrucción:
pyfirmata.util.Iterator(placa).start()
Para escribir a un pin de salida digital, usamos el método write() enviando como argumento False (0V) o True (5V), según la cantidad de voltaje que deseemos producir como salida. Ejemplo:
salida.write(True)
En lugar de False y True se puede usar 0 y 1, respectivamente.

Usamos el método read() para leer de un pin de entrada digital. Este método devuelve 0 si el voltaje de entrada es menor a 2.5V, o 1 si es mayor a 2.5V. Por ejemplo:
x = entrada.read()
Posteriormente, podemos usar la variable x en una instrucción condicional (if) para determinar qué valor efectivamente se leyó.

Para pausar nuestro programa por una cierta cantidad de tiempo se recomienda usar el método pass_time() sobre el objeto que representa nuestra placa de Arduino. Se le envía como argumento el número de segundos que deseamos que dure la pausa. La siguiente porción de código detiene el programa en curso durante medio segundo:
placa.pass_time(0.5)
Por último, para terminar nuestro programa de manera limpia, se debe invocar el método exit() así:
placa.exit()

Un ejemplo completo

Realicemos ahora un pequeño proyecto que nos permita integrar todo lo que describimos en la sección anterior. Vamos a usar un botón para controlar el prendido y apagado de un LED.

Además del Arduino Uno conectado vía USB a la computadora anfitriona, vamos a requerir el siguiente hardware:
  • Un protoboard.
  • Un botón o pulsador (push button).
  • Un LED (diodo emisor de luz).
  • Una resistencia de 330Ω (bandas naranja, naranja, café).
  • Una resistencia de 10KΩ (bandas café, negro, naranja).
  • Cinco cables conectores.
La siguiente figura, elaborada con Fritzing, muestra la manera de conectar todos los componentes:

Vista de protoboard Fritzing para
el LED controlado por un botón.

Usando un par de cables, conectamos un pin de tierra (GND) y un de pin de 5V del Arduino a los buses respectivos de alimentación negativa (línea azul) y positiva (línea roja) del protoboard.

El ánodo del LED (la patita larga) se conecta al pin 12 del Arduino, mientras que el cátodo (la patita corta) se conecta a la resistencia de 330Ω. El otro extremo de esta resistencia se conecta a tierra (bus negativo).

El push button 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 inferiores del botón al bus positivo. Una de las patitas superiores del botón se conecta a una resistencia de 10KΩ, mientras el otro extremo de la resistencia se conecta a tierra (bus negativo). La patita superior que queda del botón se conecta al pin 8 del Arduino. Dada esta disposición, se dice que la resistencia es de tipo pull-down. En términos prácticos esto significa que el valor que va leer el pin 8 por omisión (cuando el botón no está presionado) es 0V, y 5V cuando el botón está presionado.

Físicamente las conexiones de nuestros componentes electrónicos se ven como se muestra en la siguiente foto:

Vista física del LED controlado
por un botón.

El programa completo en Python 3.4 se muestra a continuación:
# Archivo: led_boton.py

import pyfirmata

placa = pyfirmata.Arduino('/dev/ttyACM0')

print('Firmata version: %d.%d' % placa.get_firmata_version())
print('pyFirmata version:', pyfirmata.__version__)

pyfirmata.util.Iterator(placa).start()

entrada = placa.get_pin('d:8:i')
entrada.enable_reporting()
salida = placa.get_pin('d:12:o')

try:
    encendido = False
    while True:
        if entrada.read():
            encendido = not encendido
            salida.write(encendido)
            placa.pass_time(0.2)

finally:
    salida.write(False)
    placa.exit()
Para correr el programa, requerimos ejecutar el siguiente comando desde una terminal en el mismo directorio donde radica el archivo led_boton.py:
python3 led_boton.py
Cuando corremos el programa, el LED se prende cuando presionamos el botón y se apaga cuando lo volvemos a presionar, y así indefinidamente hasta que presionemos Ctrl-C para terminar el programa.

Más información

13 comentarios:

  1. Muy interesante! Creo que cabe también mencionar que existe un tercer método para conectar un Arduino a una Computadora que usa Python. Y es muy parecido al método que mencionas, pero acá tienes que tú mismo implementar un protocolo de comunicación serial. Utilizar en el programa de Arduino que puede ser "parcialmente" autónomo (como lo explicas en la opción 1.) pero que envíe y reciba información mediante un protocolo particular al canal Serial (puerto USB). Desde la PC, puedes entonces mediante la librería de PySerial abrir una conexión a un puerto serial y utilizar los comandos de lectura y escritura en dicho puerto (el que estará conectado al Arduino) para enviar y recibir mensajes en el protocolo definido. Esta opción es mucho más compleja, pero más completa para ejemplificar el funcionamiento de dos entes autónomos en un ambiente interactivo. ... digo, es solo una sugerencia. Un abrazo y gracias por compartir esta información.

    ResponderEliminar
    Respuestas
    1. Muchas gracias por tu comentario Rafaín. Efectivamente la técnica que comentas es un caso más general de la opción 2 para poder usar Python para programar al Arduino. Vale la pena comentar que pyFirmata utiliza la biblioteca PySerial que mencionas.

      De hecho, incluso podríamos decir que hay un cuarto método, y es aún más complejo que todos los anteriores. Consistiría en escribir un compilador que traduzca código de Python generando código nativo del microcontrolador ATmega328 de Atmel.

      A fin de cuentas, las dos opciones que menciono en esta entrada del blog (el programa corre directamente en el Arduino o corre en la computadora anfitriona) siguen siendo en términos generales las únicas dos opciones que existen. Cualquier otra cosa es un detalle (no necesariamente trivial) de implementación.

      Un abrazo para ti también mi amigo.

      Eliminar
  2. Te agradezco muchísimo que hayas escrito sobre como programar con python un arduino. he de realizar un trabajo en la universidad en el he de usar un arduino y alli a nosotros nos ensenyaron el lenguage python pero no nos han enseñado nada de C++ y asi de entrada no sabia muy bien como empezar... Este articulo del blog me ha aligerado mucho el trabajo. muchas gracias y te animo vivamente a que sigas adelante con este blog y con tus proyectos!

    ResponderEliminar
  3. soy completamente nuevo en esto y este tema es bastante complicado y he leido y releido tus indicaciones y la verdad no se en que momento escribis en arduino y en que momento en python

    ResponderEliminar
  4. yo lo leí 3 veces para entenderlo, ,, creo que es así descargas la libreria fimata en arduino lo instalas entras en la opción ejemplo lo ejecutas (compilas) y hasta allí es la parte de arduino ;luego todo lo demas se hace en phyton que se estara comunicando via serial con tu arduino por eso no desconectar tus cables,,, te recomiendo practica algo lo mas básico con arduino y phyton y luego intentar hacer este tutorial,yo lo probare recien :)

    ResponderEliminar
  5. Muy muy interesante esta librería pero creo que con la pyserial es menos complicado de entender para ejemplos mucho más grandes en los que tengas que tocar código en el IDLE de Arduino.

    ResponderEliminar
  6. Muy buen blog y muchas gracias por la información, una pregunta.... para poder grabar y despues leer unos datos del eeprom del arduino con Python es posible?

    ResponderEliminar
    Respuestas
    1. Nunca lo he hecho, pero al parecer sí es posible. Revisa la siguiente liga:

      https://github.com/thearn/Python-Arduino-Command-API

      Gracias por tu comentario.

      Eliminar
  7. Por favor, como hago para pasar un sketh completo escrito en Phyton para que sea interpretado y corrido en Arduino.
    Muchas gracias
    alabombarda@hotmail.com

    ResponderEliminar
  8. Disculpen, el ejemplo ya lo hice y funciona, pero no comprendo como hago con un programa ya realizado
    Gracias
    alabombarda@hotmail.com

    ResponderEliminar