Desarrollar rápidamente aplicaciones basadas en microcontroladores en tiempo real utilizando MicroPython

Por Jacob Beningo

Colaboración de Digi-Key's North American Editors

Los sistemas integrados de tiempo real se están volviendo extremadamente complejos y requieren un profundo conocimiento no sólo de los intrincados microcontroladores de 32 bits, sino también de los sensores, algoritmos, protocolos de Internet, y diversas aplicaciones de usuario final. Con ciclos de desarrollo más cortos y más características, los equipos de desarrollo necesitan encontrar formas de acelerar el diseño y al mismo tiempo poder ofrecer puertos al código para nuevos productos: una plataforma de desarrollo integrada y flexible es necesario.

Hay varias plataformas específicas de microcontroladores que están disponibles para ayudar a acelerar el proceso de desarrollo, pero el problema con estas soluciones es que limitan los desarrolladores a un único proveedor de microcontroladores. Ofrecer puerto al software de una plataforma a otra, puede ser costoso y consume mucho tiempo.

Una solución única y novedosa que está ganando una amplia aceptación e impulso es acoplar el microcontrolador de bajo nivel del hardware con un lenguaje de programación de alto nivel como Python. Una de esas soluciones es MicroPython. Este se ejecuta en diferentes piezas de proveedores de microcontroladores y es de código abierto, por lo que está fácilmente disponible para el uso personalización de los desarrolladores según sus propias necesidades.

MicroPython.org lo describe como una implementación ágil y eficiente del lenguaje de programación Python 3 que incluye un pequeño subconjunto de la biblioteca estándar de Python optimizado para ejecutarse en microcontroladores y en entornos reducidos. MicroPython comenzó como un proyecto inicial que no sólo fue financiado satisfactoriamente, sino que acumuló un gran número de seguidores, y ahora ha sido utilizado con éxito en proyectos en diversos sectores como sistemas industriales y basados en el espacio.

Seleccionar el microcontrolador adecuado

MicroPython se ejecuta en varios microcontroladores y no hay grandes limitaciones para trasladar MicroPython a más de microcontroladores siempre que haya suficiente memoria RAM, Flash y potencia de procesamiento para ejecutar el intérprete. Dicho esto, hay varios requisitos clave que un desarrollador debe buscar en un microcontrolador a utilizar para ejecutar MicroPython:

  • Un mínimo de 256 Kbytes de flash
  • Un mínimo de 16 Kbytes de RAM
  • Un mínimo de 80 MHz de reloj de la CPU

Estas son recomendaciones generales y los desarrolladores pueden apartarse de ellas según sus necesidades de aplicación, así como cuánto tiempo desea dedicar a la personalización del kernel MicroPython. Por ejemplo, MicroPython puede ser modificado para utilizar mucho menos de 256 Kbytes de flash. Estas recomendaciones son para proporcionar a un desarrollador con la mejor experiencia y para proporcionar espacio para el crecimiento en su código de aplicación.

MicroPython ya ha sido portado a diferentes series de microcontroladores que pueden utilizarse como un punto de partida ideal para migrar a una nueva plataforma o para seleccionar un microcontrolador que ya es compatible. El directorio principal del código fuente de MicroPython se muestra en la Figura 1. A partir de aquí, el lector puede ver diferentes dispositivos con soporte de microcontrolador como:

Imagen de ejemplo de la estructura de directorios de carpeta

Figura 1: Ejemplo de estructura de directorios de carpetas que muestra las plataformas de microcontrolador disponibles que admiten actualmente MicroPython. Estas incluyen ARM, CC3200, esp8266, Microchip PIC, y STM32. (Fuente de la imagen: Beningo Embedded Group)

Cada carpeta enumerada en el directorio raíz es una carpeta de alto nivel que contiene los controladores y soporte general para esa familia de chips. Puede haber varias placas de desarrollo o procesadores compatibles dentro de cada carpeta. Por ejemplo, la carpeta stmhal admite placas de desarrollo como la Placa Discovery STM32F429 de STMicroelectronicsy el Nodo Discovery IoT STM32 (STM32L), entre varios otros, tales como la pyboard STM32F405 de Adafruit. La carpeta ESP8266 contiene soporte para la placa adaptadora de Adafruit Huzzah para el ESP8266 y Placa de pila Feather Huzzah.

Las placas de desarrollo que pueden ejecutar MicroPython son rentables, y es una buena idea para un desarrollador comprar varias para poder observar cuánta memoria, espacio de almacenamiento y potencia de procesamiento necesita para una aplicación. Por ejemplo, el desarrollador puede empezar utilizando el pyboard STM32F405 y decidir que para el futuras funciones y actualizaciones, quiere pasar a un STM32F429 en su producto final. El STM32F429 tiene 2 MB de memoria flash, 256 Kbytes de RAM y RAM especial con estado de espera cero, llamado CCM.

El código de la aplicación MicroPython que un programador escribe no necesariamente tiene que ser almacenado en la memoria flash interna del microcontrolador. El kernel MicroPython necesita estar en el microcontrolador pero el código de la aplicación puede estar en un medio de almacenamiento externo, como Panasonic 8 GB microSD card. Utilizando un dispositivo de almacenamiento de memoria externa para almacenar el código de la aplicación proporciona la oportunidad de usar un microcontrolador con menos memoria y potencialmente ahorrar en costos totales del sistema.

Póngase en marcha con MicroPython

MicroPython viene preinstalada en el pyboard STM32F405 de Adafruit. Sin embargo, cualquier otro kit de desarrollo o hardware personalizado requerirá que el desarrollador tenga que descargar la fuente MicroPython, construir la fuente para la junta de destino y, a continuación, encender el microcontrolador con el software. Obtener acceso al código fuente MicroPython es fácil, ya que está todo alojado en GitHub. Hay varios pasos que el desarrollador tiene que seguir para configurar el la cadena de herramientas y obtener el entorno configurado para construir MicroPython. En este ejemplo, vamos a construir MicroPython para la placa Discovery STM32F429.

En primer lugar, el desarrollador desea crear una máquina virtual basada en Linux o utilizar una instalación de Linux nativo. Una vez que Linux está disponible desde un terminal, un desarrollador desea instalar la cadena de herramientas compiladoras de ARM usando el siguiente comando:

sudo apt-get install gcc-arm-none-eabi

 

Si la instalación de Linux es reciente, el sistema de control de revisiones, git, puede no estar instalado. Git puede ser instalado desde el terminal utilizando el siguiente comando:

 

sudo apt-get install git

 

Una vez instalado el git, la fuente MicroPython puede ser retirada del repositorio ejecutando el siguiente comando en el terminal:

 

git clone https://github.com/micropython/micropython.git

El proceso se puede ejecutar durante unos pocos minutos, pero el desarrollador debe ver la secuencia demostrada (Figura 2).

Imagen del clonado del repositorio MicroPython al sistema local de archivos

Figura 2: La clonación del repositorio MicroPython al sistema local de archivos donde el desarrollador puede entonces construir MicroPython para su placa objetivo o personalizar el kernel para sus propias aplicaciones. (Fuente de la imagen: Beningo Embedded Group)

Una vez que la fuente MicroPython ha sido clonada al sistema local de archivos, deberían cambiar a ese directorio y, a continuación, en la terminal ejecutar un "cd stmhal". El directorio stmhal contiene el makefile para MicroPython para los microcontroladores STM32. También hay una carpeta de "placas" que el desarrollador pueda revisar que muestra todas las placas STM32 actualmente compatibles. Desde el terminal el desarrollador puede entonces construir cualquier placa que se encuentra en la carpeta "placas". Por ejemplo, el desarrollador puede construir la placa Discovery STM32F4 escribiendo el siguiente comando:

make BOARD=STM32F4DISC

MicroPython tardará varios minutos en la construcción. Mientras la construcción está en curso, el desarrollador desea instalar la actualización de la herramienta del firmware del dispositivo (DFU) que se usa para programar MicroPython sobre USB en un microcontrolador. La herramienta sólo necesita ser instalada una vez y puede hacerse escribiendo el siguiente comando en un terminal:

sudo apt-get install dfu-util

Cuando se completa la construcción de MicroPython y el dfu-util ha sido instalado, el desarrollador está listo para cargar MicroPython en su microcontrolador. El desarrollador tendrá que poner primero su microcontrolador en modo gestor de arranque DFU. Esto puede hacerse mediante la configuración de los pines de arranque para cargar el gestor de arranque interno en restablecer, en lugar de ejecutar código desde flash.

Con el microcontrolador en modo gestor de arranque y conectado al computador host a través de USB, se puede utilizar el dfu-util para descargar MicroPython utilizando el siguiente comando:

dfu-util -a 0 -d 0483:df11 -D build-STM32F4DISC/firmware.dfu

El dfu-util utilizará el archivo dfu generado por el proceso de compilación. El proceso tardará varios minutos, ya que el microcontrolador se borrará completamente y será reprogramado. El proceso será algo muy similar al proceso que se muestra (Figura 3). Tan pronto como la herramienta se completa, los puentes de arranque deben ajustarse a la carga del flash interno y luego el microcontrolador puede reiniciarse. MicroPython ahora se ejecuta en el microcontrolador de destino.

Imagen de MicroPython que se está cargando en un microcontrolador utilizando el dfu-util

Figura 3: MicroPython se está cargando en un microcontrolador utilizando el dfu-util. (Fuente de la imagen: Beningo Embedded Group)

Interconexión de sensores y dispositivos conectados

La mayor ventaja de utilizar un lenguaje de programación de alto nivel como MicroPython para desarrollar software integrado en tiempo real es que el software es independiente del hardware subyacente. Esto significa que un programador puede desarrollar un script para ejecutar MicroPython en un pyboard, y con pocas modificaciones, ejecutar el script en el ESP8266 o la placa Discovery STM32F4. Examinemos cómo se ve un script MicroPython básico que interconecta Bosch Sensortec Bmp280 barómetro y sensor de temperatura a un bus I2C y, a continuación, transmite los datos a través de un enlace serial Bluetooth mediante el módulo Bluetooth RN-42 de Microchip Techonolpgy.

El BMP280 es un barómetro basado en I2C y sensor de temperatura que tiene una dirección esclava I2C por defecto de decimal 119. La manera más fácil de hacer la interfaz a la pyboard es utilizar DFRobot's placa Gravity, que proporciona un robusto conector para facilitar el acceso para alimentar el dispositivo y el acceso I2C. El desarrollador puede seleccionar el bus I2C1 o I2C2 para conectarse con la placa Gravity. Una vez que las placas están conectadas, el script MicroPython es sencillo.

En primer lugar, el desarrollador debe importar la clase I2C de la biblioteca pyb. La biblioteca pyb proporciona acceso a funciones tales como periférico de microcontrolador SPI, I2C y UART. Antes de usar cualquier periférico, el desarrollador debe crear una instancia de la clase de periféricos para crear un objeto que se pueda utilizar para controlar el periférico. Una vez que la clase de periférico se ha inicializado, el desarrollador puede realizar cualquier otras inicializaciones tales como la verificación de que los dispositivos están presentes antes de entrar en el bucle principal de la aplicación. El código de la aplicación principal hará una muestra el sensor una vez por segundo. Aquí se muestra un ejemplo de cómo hacer esto (Listado de Código 1).

Copy

from pyb import I2C

 

GlobalTemp = 0.0

GlobalBarometer = 0.0

 

# Initialize and Instantiate I2C peripheral 2

I2C2 = I2C(2,I2C.MASTER, baudrate=100000)

 

while True:

            SensorSample()

            pyb.delay(1000)

 

def SensorSample():

            #Read the Temperature Data

            TempSample = I2C2.readfrom_mem(119, 0xFA,3)

 

            #Read the Pressure Data

            PressureSample = I2C2.readfrom_mem(119, 0xF7,3)

Listado de código 1: MicroPython script que inicializa el I2C y periféricos se comunica con la placa DFRobot Gravity para adquirir datos del sensor de temperatura y barómetro. (Fuente de código: Beningo Embedded Group)

Realizar un muestreo de datos del sensor y no hacer nada con ellos no es tan útil para la demostración de cuán potente puede ser MicroPython para un equipo de desarrollo. Muchos equipos de desarrollo se enfrentan con desafíos técnicos para conectar sus dispositivos sensores a Internet o a un sensor local utilizando Bluetooth.

Una forma sencilla de agregar la funcionalidad Bluetooth a un proyecto es utilizar RN-42. RN-42 puede colocarse en un modo donde el microcontrolador simplemente envía los datos UARTque deben transmitirse a través de Bluetooth, y RN-42 gestiona toda la pila de Bluetooth (Figura 4).

Imagen de pyboard ejecutando MicroPython conectado al módulo Bluetooth RN-42 a través de UART

Figura 4: Conectar el pyboard ejecutando MicroPython a un módulo Bluetooth RN-42 a través de UART. (Fuente de la imagen: Beningo Embedded Group)

Una vez que la placa Bluetooth está conectada, el desarrollador puede crear una secuencia muy simple que lleva los datos recibidos del sensor y los transmite a través de Bluetooth a un dispositivo móvil que pueda guardar los datos o remitirlos a la nube para su posterior análisis. Se muestra el script de ejemplo (listado de código 2). En este ejemplo, UART1 está configurado a 115200 bps, 8 bits, sin paridad y transmisión de un solo bit de parada.

Copy

from pyb import uart

from pyb import I2C

 

GlobalTemp = 0.0

GlobalBarometer = 0.0

 

# Initialize and Instantiate I2C peripheral 2

I2C2 = I2C(2,I2C.MASTER, baudrate=100000)

 

# Configure Uart1 for communication

Uart1 = pyb.UART(1,115200)

Uart1.init(115200, bits=8, parity=None, stop=1)

 

while True:

            SampleSensor()

            pyb.delay(1000)

 

def SensorSample():

            #Read the Temperature Data

            TempSample = I2C2.readfrom_mem(119, 0xFA,3)

 

            #Read the Pressure Data

            PressureSample = I2C2.readfrom_mem(119, 0xF7,3)

 

            #Convert Sample data to string

            data = “#,temperature=”str(TempSample)+”,pressure”+str(PressureSample)+”,#,\n\r”

 

            #Write the data to Bluetooth

            Uart1.write(data)

Listado de código 2: Ejemplo de script MicroPython inicializa UART1 y se comunica con un dispositivo externo. (Fuente de código: Beningo Embedded Group)

No sólo es el código de la aplicación Python fácilmente portado a otras plataformas de hardware, la aplicación utiliza bibliotecas comunes y funcionalidad que ya se ha implementado para ayudar a los desarrolladores a acelerar su desarrollo. La creación de la aplicación anterior se puede hacer en una hora o menos, en comparación s una semana o más potencialmente para un desarrollador que comienza de los más bajos niveles de software y tiene que ir avanzando.

Consejos y trucos para el desarrollo de software en tiempo real

El desarrollo de aplicaciones integradas usando MicroPython es fácil, pero conseguir el rendimiento en tiempo real del sistema no es tan sencillo como uno podría pensar. Mientras MicroPython ofrece enormes ventajas para la simplificación y la reutilización del código, obtener una sincronización predecible y coherente del sistema de distribución puede ser difícil si el desarrollador no está consciente de algunas curiosidades y bibliotecas.

MicroPython incluye un recolector de basura que se ejecuta en segundo plano y gestiona la memoria heap y otros recursos. El recolector es no determinista, de modo que los desarrolladores que esperan el comportamiento determinista pueden verse en problemas si el recolector comienza a ejecutarse en un tiempo de sección crítica. Hay varias recomendaciones que los desarrolladores deben seguir para asegurarse de que esto no suceda.

Primero, los desarrolladores pueden importar la biblioteca de recolección de basura, GC, y utilizar los métodos de habilitar y deshabilitar para el control cuando el recolector está habilitado o deshabilitado. El desarrollador puede deshabilitar la recolección de elementos no utilizados antes de una sección crítica y luego activarlo después como se muestra (Listado de Código 3).

Copy

import gc

 

gc.disable()

 

#My time critical code

 

gc.enable()

Listado de código 3: Desactivar el recolector MicroPython antes de una sección de código de tiempo crítico. (Fuente de código: Beningo Embedded Group)

Segundo, el desarrollador también puede controlar manualmente el proceso de recolección de basura. Cuando el desarrollador crea y destruye objetos, se asigna memoria en el montón. El recolector de basura se ejecuta y libera el espacio no utilizado. Ya que se hace a intervalos irregulares, los desarrolladores pueden utilizar el método de recolección para ejecutar la recolección de basura periódicamente y garantizar que el espacio del montón no se sature con la basura. Cuando se hace esto, la recolección de basura se puede ejecutar desde 10 milisegundos a menos de un milisegundo por ejecución. Realizar manualmente la recolección de basura también se asegura de que el desarrollador de la aplicación tiene el control sobre el código temporizado no determinista. Esto les permite decidir cuándo ejecutar la recolección de basura y asegura que su aplicación tiene el rendimiento en tiempo real.

Hay varias otras mejores prácticas que los desarrolladores pueden seguir para quienes están interesados en escribir código en tiempo real. Estos incluyen:

  • Utilizar búferes preasignados para canales de comunicación
  • Utilizar el método readinto al utilizar periféricos de comunicación
  • Evitar la documentación de python tradicional utilizando ###
  • Minimizar la creación y destrucción de objetos en tiempo de ejecución
  • Supervisar los tiempos de ejecución de la aplicación

Los desarrolladores interesados en entender más las "mejores prácticas" pueden revisar documentación de optimización de MicroPython aquí.

Conclusión

MicroPython es una interesante plataforma para desarrolladores que deseen implementar aplicaciones integradas en tiempo real que sean escépticos sobre el hardware del microcontrolador subyacente. Los programadores pueden escribir sus scripts de Python de alto nivel utilizando las bibliotecas estándar suministradas en MicroPython y ejecutarlas en cualquier microcontrolador compatible. Esto proporciona muchas ventajas a los desarrolladores, entre ellas:

  • Mejora de la reutilización de la aplicación
  • Acelerar el tiempo de llegada al mercado
  • Desacoplamiento de la aplicación desde el hardware

MicroPython no es perfecto para cada aplicación, pero hasta el momento ha tenido éxito en aplicaciones industriales y sistemas espaciales, junto con la rápida creación de prototipos y pruebas de concepto. 

Descargo de responsabilidad: Las opiniones, creencias y puntos de vista expresados por los autores o participantes del foro de este sitio web no reflejan necesariamente las opiniones, las creencias y los puntos de vista de Digi-Key Electronics o de las políticas oficiales de Digi-Key Electronics.

Acerca de este autor

Jacob Beningo

Jacob Beningo is an embedded software consultant who currently works with clients in more than a dozen countries to dramatically transform their businesses by improving product quality, cost and time to market. He has published more than 200 articles on embedded software development techniques, is a sought-after speaker and technical trainer and holds three degrees which include a Masters of Engineering from the University of Michigan. Feel free to contact him at jacob@beningo.com, at his website www.beningo.com, and sign-up for his monthly Embedded Bytes Newsletter.

Acerca de este editor

Digi-Key's North American Editors