G: MicroPython

Un microcontrolador es un circuito integrado programable. Normalmente incluye una unidad central de procesamiento (CPU), memoria y periféricos de entrada/salida (Wikipedia).

En los últimos años ha habido una revolución en el campo de los microcontroladores tras la aparición de la iniciativa Arduino, que es una plataforma electrónica de código abierto basada en software y hardware fáciles de usar. A partir de dicha iniciativa (de la cual han salido múltiples clones muy baratos) el público en general ha comenzado a adoptar los microcontroladores para crear sus propios dispositivos. Quizás el ejemplo más paradigmático de dicha adocpción fue la aparición de las impresoras 3D caseras, que basan mucho de su funcionamiento en este tipo de microcontroladores.

Además de Arduino hay otra serie de microcontroladores, como por ejemplo los basado en los chips ESP32 que son en líneas generales más potentes: procesador más rápido, más memoria, conectividad WiFi y Bluetooth integrada… (ESP32 vs. Arduino: The Differences).

MicroPython es una implementación de Python 3 y algunas de sus bibliotecas estándar más básicas diseñado para trabajar sobre microcontroladores. De hecho los creadores de MicroPython han desarrollado su propia familia de microcontroladores (pyboard). Sin embargo, MicroPython puede instalarse y usarse en otros microcontroladores. En este notebook hablaremos sobre su instalación y uso más básico en la familia de microcontroladores ESP32 (en realidad la mayoría de lo que aquí se cuente es perfectamente válido para otros microcontroladores compatibles con MicroPython).

Microcontroladores ESP32

Esta familia de microcontroladores tiene un aspecto similar al siguiente:

_images/ESP32.jpg

Como se puede ver en la imagen vienen con un puerto USB con el que conectarlos al ordenador y/o alimentarlo (puede funcionar sin conexión a ordenador). Además tiene multitud de pines con diversos usos, aunque la mayoría son usados para poder conectar diversos sensores y/o actuadores. Dichos sensores son muy baratos y permiten hacer mediciones de gran cantidad de variables:

  • Temperatura

  • Humedad

  • Fines de carrera

  • Presencia de gases o particulas en el aire

  • Luz

  • Presión

  • Presencia

Los actuadores que se pueden acoplar también tienen una gran heterogeneidad:

  • Luces (leds)

  • Relés (u optoacopladores)

  • Motores de corriente continua

  • Motores paso a paso

  • Servomotores

  • Buzzers

Además estos microcontroladores traen de serie Bluetooth y WiFi (con un alcance limitado, todo hay que decirlo), con lo que se amplian mucho las posibilidad de conectividad.

Instalando MicroPython en el ESP32

Estas instrucciones están basadas en esta página.

Para instalar MicroPython en el microcontrolador vamos a hacer uso de la herramienta esptool. Para instalarla:

> conda install -c conda-forge esptool

Se trata de un script Python capaz de conectarse a las placas, formatear la memoria de la misma y volcar en ellas el firmware de MicroPython. Conectaremos nuestro microcontrolador a nuestro ordenador a través de un cable USB y comprobaremos en que ruta se ha desplegado (en Linux típicamente en /dev/ttyXXXX. En lo que sigue asumiremos que la placa aparece en /dev/ttyUSB0.

Para poder instalar el firmware, primero tenemos que borrar la memoria flash de la placa:

> esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash

A continuación bajaremos el firmware que vamos a descargar la última versión de dicho firmware. En el momento de la escritura de este tutorial dicha versión es la 1.18 (esp32-20220117-v1.18.bin). Una vez descargado lo cargaremos dentro de la memoria del microcontrolador:

> esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-20220117-v1.18.bin

¡Ya tendremos la placa preparada para ejecutar código Python!

Ejecutando órdenes en el microcontrolador (accediendo a la consola REPL)

Estas instrucciones están basadas en esta página.

La manera más básica para ejecutar código Python en nuestro microcontrolador es accediendo a la consola REPL (Read Evaluate Print Loop). Para conectar a dicha consola tenemos que usar algún programa terminal que permita conectar a un puerto serie de nuestro ordenador. En Linux por ejemplo podemos usar picocom. En Windows se puede usar TeraTerm y en Mac se puede usar el comando screen. Para instalar picocom en distribuciones de Linux basadas en Debian:

> sudo apt install picocom

Y para conectarnos con ella al microcontrolador:

> picocom /dev/ttyUSB0 -b115200

¡Ya estamos dentro de la placa!. Podemos ejecutar cualquier instrucción Python y la placa ejecutará dicho código y nos mostrará los resultados. Por ejemplo:

>>> print("ProAm!")
ProAm!
>>> for i in range(1, 6):
...     print(i)
1
2
3
4
5

Interaccionando con los pines del microcontrolador

Vamos a hacer un circuito muy sencillo que nos permita encender y apagar un led. Para ello vamos a conectar dicho led siguiendo este esquema:

_images/ESP32_led.jpg

Básicamente vamos a conectar la patilla positiva del led (¡la patilla larga!) a uno de los pines GPIO (pines de entrada/salida), el 5 en la figura. La otra patilla negativa (la corta) la pondremos en serie con una resistencia de \(220 \Omega\) hasta el pin de tierra (ground o GND). En nuestro caso nuestro circuito real tiene esta pinta (en este ejemplo hemos conectado el led a otro pin GPIO, el 23):

_images/ESP32_led_real.jpg

Pues bien, para encender dicho led solo tenemos que ejecutar el siguiente código:

>>> import machine                         # Módulo para tener acceso a los pines
>>> import time                            # Funciones básicas de control de tiempo

>>> pin = machine.Pin(23, machine.Pin.OUT) # Vamos a usar el pin para "salida" (encenderlo o apagarlo). Ajustar el número a donde has conectado el led

>>> pin.on()                               # Encendemos el pin

Y para apagarlo:

>>> pin.off()

Y si queremos hacer una secuencia de 10 encendidos / apagados:

>>> def toggle(p):
...     p.value(not p.value())

>>> for i in range(20):
...     toggle(p)
...     time.sleep_ms(500)        # Esperamos 500 ms

Por cierto, para salir de picocom hay que pulsar Ctrl+a+q (pulsar Ctrl y sin soltar pulsar a y después q).

Las placas de MicroPython permiten conectar con una consola de tipo REPL pero mediante un interfaz web y a través del módulo WiFi, con lo que no habría ni que conectarlas al ordenador físicamente para poder interaccionar con ellas. Puedes encontrar un poco más de información al respecto en Access WebREPL.

Accediendo al sistema de archivos

Para esta sección nos hemos basado en esta página

Las placas que preparemos con micropython tienen un sistema de archivos simple al cual podemos subir los ficheros que necesitemos. Para acceder a dicho sistema de ficheros, mandar o recuperar ficheros del mismo así como ejecutar programas que tengamos escritos en nuestro ordenador vamos a hacer uso del programa Ampy (Adafruit MicroPython tool). Podemos instalarlo con:

> pip3 install adafruit-ampy

Podemos consultar los ficheros que tiene nuestro microcontrolador:

> ampy --port /dev/ttyUSB0 ls

Podemos traernos algun fichero que esté en el microcontrolador a nuestro ordenador:

> ampy --port /dev/ttyUSB0 get boot.py boot.py

El primer boot.py es el fichero en el microcontrolador. El segundo boot.py es el nombre del fichero que se guardará en nuestro ordenador.

También podemos mandar un fichero main.py (enlace) a la placa con el siguiente comando:

> ampy --port /dev/ttyUSB0 put main.py

El contenido de dicho fichero es casi idéntico al ejemplo que ejecutamos antes en la consola REPL:

import machine
import time

pin = machine.Pin(5, machine.Pin.OUT)

pin.on()

def toggle(p):
    p.value(not p.value())

for i in range(100):
    toggle(p)
    time.sleep_ms(500)

Si reseteamos la placa se ejecutará directamente ese programa. De hecho, cuando la placa se inicializa primero ejecuta el fichero boot.py y a continuación se ejecuta main.py.

Para borrar un fichero del sistema de ficheros del microcontrolador:

> ampy --port /dev/ttyUSB0 rm main.py

Moviendo un motor paso a paso

Los motores paso a paso (stepper) es un motor de corriente continua en el que se puede controlar la rotación del mismo en un determinado número de pasos por cada rotación. Puedes encontrar más información sobre estos motores en Motor paso a paso – tipos y ejemplos del uso de motores paso a paso.

En este ejemplo vamos a conectar un motor paso a paso haciendo uso de una controladora de motor (driver) similar a las famosas A4988. Dichas controladoras simplifican el control de estos motores ya que usualmente solo necesitamos conectar 3 pines:

  • EN (enable): Para activar el driver

  • DIR: Para indicar la dirección de giro (sentido de las agujas del reloj (CW) o sentido contrario de las agujas del reloj (CCW)

  • STEP: Para indicar el momento en el que debe darse el paso

Además hay que conectar los pines de GND y 5V (o 3.3V, dependiendo del voltaje al que funcione tu microcontrolador) y una fuente de alimentación externa (típicamente de 12V) ya que los motores paso a paso usualmente requieren una intensidad de corriente mucho mayor que la que proporciona un microcontrolador. Haremos un ciruito como el de la foto:

_images/ESP32_motor_real.jpg

Para este ejemplo hemos conectado los siguientes pines:

ESP32

Driver

GPIO 5

STEP

GPIO 19

DIR

GND

EN

GND

GND

3.3V

Vin

Y podemos proceder a programar la placa. Haremos uso de una biblioteca que se llama AccelStepper (repositorio). Pero ojo, el código del repositorio tiene un bug. Nosotros lo hemos corregido. Puedes bajar el fichero necesario de: AccelStepper.py. Lo mandamos a la placa para poder usarlo en nuestro programa:

> ampy --port /dev/ttyUSB0 put AccelStepper.py

El programa que vamos a ejecutar en nuestro microcontrolador es el siguiente mueveMotor.py:

from accelstepper import AccelStepper      # Importamos la biblioteca

motor = AccelStepper(1, 5, 19, 0, 0, 1)    # Creamos un objeto de tipo AccelStepper. El primer parámetro es para indicar que usamos un driver de tipo DIR STEP. El segundo parámetro es el pin de STEP y el tercero el de STEP. Los siguientes parámetros no sirven, pero hay que ponerlos.

motor.set_acceleration(200)                # Indicamos la aceleración ( pasos / s^2 )
motor.set_max_speed(400)                   # Indicamos la velocidad máxima (pasos por segundo)

for i in range(10):

    if i % 2 == 0:
        motor.move_to(1600)                # Nos movemos al paso 1600 (una vuelta son 1600 pasos según nuestro driver y motor)
    else:
        motor.move_to(0)

    while motor.distance_to_go() != 0:     # Mientras queden pasos por dar
        motor.run()                        # Esta función hay que llamarla lo más rápido posible. Es la que calcula si hay que mandar o no un paso al motor de acuerdo a la velocidad actual

Para ejecutar el programa podemos usar el comando run de ampy:

> ampy --port /dev/ttyUSB0 run mueveMotor.py

El motor debe dar 10 vueltas alternando la dirección entre vuelta y vuelta.