Creación de un paquete de tarjetas compatibles con Arduino

Tarjeta Arduino Mega con breadboard que tiene un led

Contexto

Hoy vamos a realizar la creación de un paquete de tarjetas compatibles con Arduino que podamos instalar en nuestro IDE y seleccionar las nuevas tarjetas creadas para compilar y grabarles el programa. El tema surgió a raíz de esta entrada donde explicábamos como instalar nuevos paquetes ya existentes y Rene preguntó sobre como crearlos. Así que vamos a dar un paseo por los entresijos del entorno Arduino.

Antes de empezar, lanzo unos de mensajes:

  • La tarea de realizar una nueva tarjeta, en el sentido del diseño de la electrónica, no la vamos a realizar. Doy por hecho que tenéis ya vuestra tarjeta diseñada. En caso contrario, mi consejo es que busquéis los esquemáticos de una tarjeta ya existente lo más parecida a vuestra idea final y a partir de ahí la modifiquéis en base a vuestras necesidades.
  • Tampoco vamos a ver como crear bootloaders, compiladores, toolchains… Esa es una tarea muy ardua. Nos centraremos en un microcontrolador cuya arquitectura ya esté soportado por Arduino y, por tanto, ya existan estos elementos. Centrándonos nosotros solo en su uso.
  • Dependiendo del diseño o las modificaciones realizadas, tampoco será necesario crear una nueva tarjeta para el IDE. En muchos casos podréis realizar vuestro diseño y seguir identificándola en el IDE como la tarjeta original de la que habéis partido. Como ejemplo la tarjeta mCore, la cual en el IDE se identifica como «Arduino UNO» y a la hora de hacer el programa se utilizan sus librerías para aprovechar las características que incorpora.
  • El caso en el que sí sería necesario realizar esto, sería cuando usamos un microcontrolador que disponga de un número distinto de pines y/o periféricos en los mismos, por lo que creando una tarjeta nueva podemos indicar al compilador los pines y periféricos que se pueden usar.
  • Y por último, un par de disclaimers:
    • Lo que aquí voy a reflejar son los resultados de unos pocos días de prueba sobre el tema. Por lo que es posible que haya detalles o pasos que no sean exhaustivos, queden olvidados o resulten ser erróneos para algunos casos.
    • Puede resultar un poco denso de leer y procesar, así que paciencia!!!

Empezamos

Para empezar, lo primero fue buscar algo de información sobre el tema. Y entre los primeros resultado apareció esta página (en inglés) de Instructables que, sin ser exhaustiva, explica muy bien los pasos. Así que hice una pequeña prueba siguiéndolos hasta conseguir tener una copia de la tarjeta Arduino Mega (sin realizar ningún cambio) con la que pude compilar.

Así pues, vamos a realizar una segunda prueba un poco más seria para recopilar los pasos. En este caso vamos a ser un poquito más atrevidos y realizar algún cambio a la tarjeta. Como no tenemos ninguna tarjeta distinta/modificada, vamos a utilizar una tarjeta Arduino Mega y el cambio que vamos a hacer va a ser invertir el número de 2 pines, por ejemplo el 8 y el 9. ¿Que significará eso?

  • Pues que cuando pongamos el pin 8 en el programa, veremos que se utiliza el pin marcado como 9 sobre la tarjeta.
  • Y viceversa, cuando pongamos el pin 9 en el programa, veremos que se utiliza el pin marcado como 8 sobre la tarjeta.

Como veis, es un cambio innecesario y que solo puede llevar confusión si lo fuéramos a usar regularmente. Pero es algo sencillo que nos permitirá conocer el proceso sin complicarnos demasiado.

Otro detalle interesante a realizar, aunque no será imprescindible, antes de empezar es actualizar los paquetes que ya tengamos instalados.

También os recomiendo echar un ojo a la lista de tarjetas soportadas de manera no oficial. Aquí podremos ver toda clase de tarjetas y paquetes que nos podrán servir de ejemplo y apoyo cuando no sepamos que hacer.

¿Qué vamos a conseguir?

Antes de empezar con el proceso, voy a hablaros de cual va a ser el resultado de nuestro trabajo. Lo que vamos a obtener son 2 ficheros (o más):

  • Un fichero en formato JSON el cual tendrá el «manifiesto» (información e integridad) de nuestro paquete. El nombre del fichero debe ser en el formato package_name_index.json (siendo name el nombre de nuestro paquete) ya que en caso contrario el IDE no lo reconocerá. Vamos a ponerle el nombre package_zms_boards_index.json.
  • Un fichero, o varios, en formato ZIP que incluirán comprimidos todos los ficheros de nuestro paquete: definición de tarjetas, bootloaders, compiladores, otras herramientas, etc. (cada uno en su propio fichero). Solo vamos a querer tener una definición de tarjeta y nada más, por lo que con un fichero nos bastará. El nombre del mismo será en la forma nameversion.zip en nuestro caso zms_boards-0.0.1.zip. Los números de versión irán en función de como queramos indicarlos.

Cuando tengamos estos dos ficheros ya estará el paquete realizado. Pero entonces necesitaremos instalarlos en nuestro IDE para poder usarlo.

Instalación

lo primero sería publicar estos 2 ficheros en un servidor web y entonces, siguiendo los pasos que comentaba en la entrada original, añadir la URL de donde hemos publicado el fichero JSON en las preferencias del IDE para que lo lea. A partir de ahí podremos ir al gestor de tarjetas para instalar el paquete.

Como ejemplo, esta es la URL del paquete con las tarjetas de Adafruit: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json

Pero… ¿Que pasa si no quiero publicarlo, no puedo acceder a un servidor web o todavía estoy en fase de pruebas y es pronto para su publicación? Pues aquí el formato de la URL viene en nuestra ayuda. ¿Veis el «http://» del principio? esa parte de la URL se denomina esquema y hace referencia al protocolo que se empleará para acceder al recurso. Http, simplificando, hace referencia a que se realizará a través de un servidor web y, por suerte para nosotros, existe otro esquema que hace referencia a nuestro sistema de archivos local, llamado file. Así pues, podemos realizar una URL comenzando por «file://» y continuando por la ruta en la que se encuentra nuestro fichero. En mi caso es: file:///home/victor/proyectos/pruebas/CrearTarjetasEnArduino/Test_2/package_zms_boards_index.json

Ventana para añadir URLs adicionales de tarjetas

Del mismo modo que si fuera http://, incluimos esta URL en el IDE y podremos instalar nuestro paquete directamente desde nuestro ordenador sin necesitar ningún servidor web.

Primer paso

El primer paso indicado en la entrada de Instructables comentada es buscar un paquete ya instalado similar al que queremos realizar y copiarlo en otro directorio donde vayamos a trabajar. Aunque hemos de estar atentos porque esto es un cuchillo de doble filo:

  • Lo bueno, es que al hacer una copia nos ahorra gran parte del trabajo el tener ya hechas muchas partes.
  • Lo malo es lo que pasa siempre con el corta/pega. Hemos de estar atentos para no olvidarnos de modificar algunas partes.

 

Otra opción sería partir de cero, aunque en ese caso necesitaríamos un mayor conocimiento del proceso además de que el trabajo sería más extenso.

Por lo que procederemos a buscar el directorio donde Arduino instala los paquetes. En general, será en nuestra carpeta de usuario dentro de la carpeta Arduino15 en Windows y .arduino15 en Linux.

En este directorio encontraremos los ficheros JSON de los paquetes instalados, así que cogemos el que necesitemos. Como vamos a partir de la Mega, esta es propia de Arduino, por lo que el fichero que cogeremos será package_index.json y donde lo copiamos lo renombramos a nuestro nombre.

También encontraremos un directorio llamado packages donde se encuentran los paquetes instalados. Así que vamos a buscar donde se encuentra la definición del Arduino Mega y se halla dentro de packages/arduino/hardware/avr/1.8.3 :

  • arduino es el paquete.
  • hardware es donde se encuentran los datos sobre tarjetas.
  • avr es la arquitectura del microcontrolador que lleva la tarjeta que vamos a usar. El microcontrolador del Arduino Mega es un ATmega2560 cuyo núcleo es avr.
  • 1.8.3 es la versión del paquete. Por supuesto variará según la que se halle instalada. Incluso podemos tener varias instaladas eligiendo en ese caso la que más nos interesase (seguramente la más reciente).

Y dentro de 1.8.3 tenemos más directorios y ficheros, pero de ellos hablaremos luego. Ahora vamos a copiarnos este directorio a nuestro lugar de trabajo. Y una vez hecho, lo renombramos de 1.8.3 a zms_boards (cada uno lo renombrará al nombre de su paquete).

estructura de directorios y ficheros de un paquete instalado, en este caso el de Arduino

Creación de la tarjeta

Ahora es cuando entramos en materia a fondo. En la imagen anterior vemos que hay un directorio llamado variants, si entramos en él veremos que dispone de un número indeterminado (dependiendo de qué paquete hayáis partido) de subdirectorios. Mirando sus nombres observaremos que coinciden con nombres de algunas tarjetas, lo que nos indica que aquí encontraremos datos para cada una de ellas. Por simplificar, y no volvernos locos, vamos a borrar todos los directorios menos el de la tarjeta que vamos a usar y este lo renombramos al nombre de nuestra tarjeta. Por los cambios que hemos dicho que vamos a hacer, y sin ser muy orininales, la vamos a llamar zms89mega.

Los cambios de la tarjeta

Como acabamos de decir, dentro de este directorio habrá datos sobre la tarjeta, pero, ¿que tipo de datos? pues en este directorio se encuentran ficheros de código para la compilación. Encontraremos ficheros .h, .c, .cpp, etc.

En este caso el fichero pins_arduino.h que hace referencia a las funcionalidades que podemos tener en cada pin de la tarjeta y algún otro tema relacionado.

En el tutorial de Instructables comentado, hace referencia a ficheros llamados variants.cpp y variants.h, pero en la primera prueba esos ficheros no los encontré. Sin embargo, más adelante al revisar otras tarjetas y arquitecturas, sí que vi que en algunas existían estos ficheros e incluso otros ficheros (p.e. con la palabra wifi en el nombre).

Todos estos ficheros contendrán código del denominado «bajo nivel», es decir, que hace referencia a las partes más internas de microcontrolador y la estructura concreta de la tarjeta. Por esa razón han de ser distintos para cada tarjeta que tenga distinto microcontrolador o pinout.

El fichero pins_arduino.h

Como su nombre indica, en este fichero definiremos las funcionalidades de cada pin. Y, como hemos dicho, queremos intercambiar las funcionalidades del pin 8 y el 9. Así pues vamos a estudiar el fichero.

Lo primero y más relevante para nuestro caso es ver que hay varios arrays con un nombre que empieza por digital_pin_to_, que el tamaño de los mismos coincide con el número de pines de la tarjeta y que el contenido de cada elemento está relacionado con los puertos del microcontrolador, lo cual hace sospechar que estos arrays son los que realizan la traducción entre el pin de arduino con el puerto del uC.

Además, si echamos un ojo a una línea podemos ver: _BV( 5 ) , // PH 5 ** 8 ** PWM8. El contenido del array parece la conversión de bit a la posición en el byte, pero lo interesante son los comentarios, dividido en 3 partes por los asteriscos:

  • PH5: Esto hace referencia al bit 5 del puerto H
  • 8: En este caso el número indica el número del pin en los conectores de la tarjeta.
  • PWM8: Y aquí nos indica otros usos del pin.

Y por tanto nos ayuda a saber que es lo que vamos a hacer.

Tenemos 3 arrays, así que procedemos a intercambiar las posiciones 8 y 9 en todos ellos. Ojo!!! si nos dejamos alguno de cambiar, el resultado puede ser indeterminado y seguro que los programa que hiciéramos no funcionarían correctamente.

cambio a realizar en el fichero pins_arduino.h

Bueno, tendremos que esperar a finalizar todo el proceso para poder comprobar si lo hemos hecho bien. Que nervios!!!

El fichero boards.txt

Ahora vamos a trabajar sobre el fichero boards.txt. Este fichero se encuentra directamente en la raíz del directorio que hemos copiado. Así que lo abrimos también.

En la primera línea podemos encontrar un enlace que nos cuenta un poquito sobre el formato del fichero.

Después vemos un montón de líneas que parece dar valores de configuración a distintos elementos. estos elementos aparecen jerarquizados en un estilo a tarjeta.sección.variable. Por lo que vamos a buscar las que empiecen por la tarjeta de la que partimos (mega) y sustituirlo por el nombre de la tarjeta que estamos haciendo (zms89mega). También vamos a aprovechar y eliminar el resto de líneas sobre tarjetas que no queremos (ojo!!! hay que tener cuidado y no quitar líneas generales no correspondientes a una tarjeta). Miraremos las líneas que quedan a ver si hay algún dato que queramos cambiar, como mínimo cambiaremos el nombre para que se identifique como queremos.

muestra del fichero boards.txt

Conociendo mejor el formato y nuestras necesidades veremos más cosas que se podrían cambiar, pero de momento lo dejaremos así.

El fichero platform.txt

El fichero platform.txt se encuentra en el mismo directorio y tiene un formato en al misma línea que el fichero boards.txt pero orientado a temas más generales del paquete y la plataforma. Por ejemplo vemos a ver un montón de líneas que hacen referencia a la compilación, la programación de las tarjetas y otras herramientas. como seguimos usando la arquitectura avr y no necesitamos nada especial para la compilación, dejaremos el fichero como está salvo las primeras líneas que hacen referencia al nombre y versión donde pondremos nuestros datos.

muestra del fichero platform.txt

Otros ficheros y modificaciones

Si habéis navegado por el directorio que hemos copiado, habréis visto una cantidad de ficheros y directorios mayor a los aquí nombrados. Seguramente algunos sobrarán, otros no requieran modificar, o ser modificados para otros cambios. Bootloaders, librerías, herramientas… todo este tipo de cosas pueden ir incluidas en el paquete o no según nuestras necesidades. También puede darse que incluso necesitándolos no se encuentren en nuestro paquete porque los emplearemos «heredados» de otros paquetes que los contengan.

A crear el paquete!!!

Bueno, ya tenemos los cambios hechos, así que vamos a proceder a empaquetarlos para su posterior uso en el IDE. Como ya he comentado, vamos a crear un archivo .zip, así que utilizaremos nuestro compresor favorito para tal tarea. Importante: hay que comprimir la carpeta con el nombre del paquete, no directamente los ficheros que incluye. El nombre del fichero será el que ya hemos dicho: zms_boards-0.0.1.zip.

Una vez comprimido necesitaremos 2 cosas:

  • Conocer el tamaño del fichero en bytes. Aquí simplemente mirando las propiedades del fichero o sacando un listado del directorio podremos verlo.
  • Calcular un hash para que se pueda comprobar la integridad de los datos. Vamos a emplear el SHA-256 ya que es el mismo usado por el paquete del que hemos partido.

Tras la creación del fichero zms_boards-0.0.1.zip obtenemos eltamaño y calculamos el SHA256

Recordaremos estos valores para usarlos posteriormente. En realidad será más bien un copy&paste 😂

Y por último el fichero JSON

Cuando tenemos ya todo lo demás hecho vamos a preparar el fichero JSON que identificará nuestro paquete. En realidad, podríamos haber empezado a prepararlo antes, pero se necesitan datos que hasta ahora no teníamos por lo que dejarlo para el final no es una mala idea.

JSON es un formato jerarquizado y para la especificación de paquetes de Arduino, estas serán las secciones que vamos a usar:

  • packages: Dentro del recuadro rosa en la imagen. Esta es la sección que engloba todo. Podremos especificar varios paquetes aunque en nuestro caso solo hay uno. Dentro del recuadro verde vemos los datos que servirán para identificar el paquete, nuestra web, etc.
    • Como requisito el campo name debe coincidir con el nombre del paquete.
  • platforms: Dentro del recuadro amarillo en la imagen. Igualmente podremos tener varias plataformas y también vamos a tener una (avr). Igualmente habrá campos para su identificación y en este caso tenemos 3 requisitos:
    • name debe coincidir con el campo name del fichero platforms.txt.
    • version debe coincidir con el campo version del fichero platforms.txt.
    • category debe tener el valor Contributed. Esto es debido a que no va a ser un paquete oficial de Arduino.
  • Dentro de la sección platforms tendremos los datos del fichero .zip donde se encuentran los datos que se instalarán y se usarán para compilar. Son 4 campos:
    • url: Aquí ira la URL que nos indica como podemos acceder al fichero que se va a instalar. Recordar convertir de file:// a http:// si lo vais lo publicar. Por dejarlo claro, esta será la URL del fichero .zip y no del propio JSON que estamos creando, aunque lo ideal es que ambos ficheros se encuentren en la misma ruta.
    • archiveFileName: Solo el nombre del fichero. Por lo que he leído debe haber algunos servidores que lo necesitan por separado.
    • checksum: Aquí irá el SHA256 que hemos calculado tras generar el .zip.
    • size: El tamaño del fichero en bytes.
  • boards: Dentro del recuadro azul en la imagen. Cada plataforma contendrá las tarjetas que define, teniendo aquí una lista de las mismas (en este caso de un solo elemento).
    • Nuevamente, como requisito tenemos que name debe coincidir con name de la tarjeta que define dentro del fichero boards.txt.
  • toolsDependencies: Dentro del recuadro cían en la imagen.
    • Aquí se indicarán las herramientas que se van a usar para poder trabajar con nuestra plataforma. Y por tanto dependemos de ellas. En nuestro caso serán 2:
      • El compilador avr-gcc.
      • El programador avrdude.
    • En esta sección me voy a detener en el campo packager que indica en que paquete se van a encontrar cada herramienta. como veis, pone arduino porque vamos a aprovechar las que ya hay instaladas (proporcionadas por el paquete oficial de Arduino).
      • Si quisiéramos empaquetar estas, u otras, herramientas necesitaríamos una sección aparte llamada tools y luego aquí en packager pondríamos el nombre de nuestro paquete (zms_boards).

Estructura del fichero package_zms_boards_index.json que identifica el paquete

Y bueno, ya veis en la imagen que tenemos el fichero creado, así que por fin hemos terminado y tenemos todo lo necesario.

A probar nuestro trabajo

Bueno, esto ya está. Ahora toca ver si funciona. Lo primero será instalar nuestro paquete, como ya hemos explicado, para poder usarlo.

Una vez instalado, podremos seleccionar nuestra flamante tarjeta nueva:

Selección en el IDE de la tarjeta creada

Como prueba, vamos a partir del ejemplo Blink que simplemente parpadea el led interno, así que vamos a cambiar el led interno por el pin que queremos usar. Nosotros vamos a probar el 8 y el 9 ya que son estos los que hemos cambiado. Si echáis un ojo a la imagen de portada, veréis que hemos puesto un led externo, con el cual comprobaremos que nuestro circuito funciona. Y aquí podéis ver el código modificado (también se han cambiado los delays para que parpadee más rápido):

Ejemplo de skecth arduino que parpadea un led

Ups

Aunque en el pantallazo anterior se ve que ha ido todo correcto, la verdad es que en la primera compilación dio un error diciendo que no encontraba el fichero pins_arduino.h… Tras el susto inicial, y revisar los ficheros, vemos que como habíamos renombrado a zms89mega el directorio donde está pins_arduino.h olvidamos indicar ese cambio en boards.txt. Así que lo volvemos a abrir, buscamos la línea y cambiamos zms89mega.build.variant=mega por zms89mega.build.variant=zms89mega.

«Lamentablemente» toca repetir el proceso de comprimir, calcular el SHA256 y actualizarlo en el fichero package_zms_boards_index.json.

¿Os habíais dado cuenta que las dos veces que aparece el SHA256 en las imágenes es distinto? Esta es la explicación, un pantallazo fue anterior y el otro posterior a la corrección.

La prueba

Una vez solucionado el desliz anterior podemos ya ver el resultado:

  • Compilamos con LED_PIN 8, conectamos el led al pin 8 (según la serigrafía de la placa) y no se enciende, lo cambiamos al pin 9 y se enciende.
  • Compilamos con LED_PIN 9, conectamos el led al pin 9 (según la serigrafía de la placa) y no se enciende, lo cambiamos al pin 8 y se enciende.

Y todavía podemos probar más: repetimos los pasos seleccionando esta vez como tarjeta la Arduino Mega original:

  • Compilamos con LED_PIN 8, conectamos el led al pin 9 (según la serigrafía de la placa) y no se enciende, lo cambiamos al pin 8 y se enciende.
  • Compilamos con LED_PIN 9, conectamos el led al pin 8 (según la serigrafía de la placa) y no se enciende, lo cambiamos al pin 9 y se enciende.

Así podemos ver como nuestro código se comporta distinto según la tarjeta sobre la que se compila.

Y ya con esto nos damos por satisfechos.

Epílogo

Para acabar volveré a repetir que esto ha sido una prueba básica partiendo de cero y que hay puntos mejorables: datos o ficheros que sobran, cambios menores no realizados y quién sabe qué otras cosas.

También hay partes explicadas de pasada o ignoradas por no complicar el ejemplo. Por ejemplo, los paquetes pueden estar comprimidos en un formato distinto del .zip y la checksum además de SHA256 puede ser de otros tipos.

Recalco que, dependiendo de lo que necesitéis, harán falta mas o menos ficheros y directorios. He llegado a ver paquetes con un único fichero (boards.txt teniendo simplemente el nombre de la tarjeta y poco más, pues como hemos dicho, se pueden aprovechar las partes que necesitemos de otros paquetes ya existentes).

Además, aconsejaría el pensarse si nos va a merecer la pena realizar este proceso. Ya he puesto el ejemplo de mCore en el que una tarjeta con modificaciones no necesita este proceso y sus características se aprovechan mediante librerías. Si va a ser el caso, que acabo de citar en el párrafo anterior, de simplemente crear boards.txt será sencillo y llamativo, por lo que en ese caso merecería la pena.

Durante el proceso, partiendo del tutorial de Instructables ya comentado, he ido recorriendo el camino buscando algo más de información sobre el tema. Así que os pongo aquí un par de enlaces que me han parecido más interesantes:

Por lo que os recomiendo que las visitéis si estáis interesados en el tema.

Pues poco más tengo que añadir. Como despedida repetiré lo que acabo de decir: con esto me doy por satisfecho. Han sido unas tardes intensas, pero es reconfortante ver como el esfuerzo ha servido para aprender.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

dieciseis − siete =

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.