Photo by Chris Ried / Unsplash

Crea tus propias librerías para Arduino

Arduino 28 de jun. de 2023

Si has estado creando con Arduino, seguramente ya hayas recurrido a librerías externas para facilitarte acceso a módulos o facilitarte la vida de alguna manera. Pero, ¿sabías que tú también puedes crear tus propias librerías para simplificar tus programas y reutilizar código útil en otros proyectos?

¡Vamos a ver cómo podemos hacer esto creando una librería de cero!

¿Qué es una librería?

Una librería en Arduino es una serie de rutinas, clases y trozos de código que han sido escritos para usarse en varios proyectos y así acelerar el ritmo de trabajo del programador.

¿No querrías tener que, por ejemplo, reescribir el mismo código para mover un motor paso a paso en cada uno de tus que incluya este componente, no?

Siempre puedes copiar y pegar el código ya usado, pero esto puede dar lugar a errores, y ya ni hablamos sobre utilizar trozos de código de terceras personas.

Imaginemos que usamos un módulo complejo con nuestro Arduino, ¿imaginas lo tedioso que sería tener que escribir el código que interactuase con este hardware para cada módulo que utilizáramos? Por este mismo motivo, los módulos de Arduino casi siempre vienen con una librería que nos ofrece una serie de funciones con las que hablar con los componentes del módulo en cuestión.

En resumen, una librería no es más que código escrito para ser incorporado en otros programas, piensa en ellas como colecciones de funciones preparadas de antemano para ayudarte a alcanzar algún objetivo en concreto.

Escribiendo la librería

Escribir una librería en Arduino es una tarea sencilla, pero requiere conocer un poco de programación en C++. En este artículo, crearemos una librería muy básica para controlar un motor paso a paso, como el que vimos en este artículo anteriormente:

Controla tu motor paso a paso 28BYJ-48 con L298N
Tutorial sobre como controlar un motor paso a paso 28BYJ-48 con Arduino y el puente H L298N.
Cómo controlar un motor paso a paso

El código de nuestra librería lo dividiremos en dos partes:

  • Una cabecera que incluirá las definiciones de todas nuestras funciones (dentro de una clase en nuestro caso);
  • un archivo de código fuente que describirá el código de las funciones declaradas en la cabecera.

Después de escribir estos archivos, será cuestión de incluir la cabecera en nuestro programa, como con cualquier otra librería, pero primero empecemos por escribir la cabecera:

Ver en Github
#ifndef libPaso_h
#define libPaso_h

#include "Arduino.h"

class MotorPaso{
    public:
        MotorPaso(int pin1, int pin2, int pin3, int pin4);
        void begin()const;
        void move(bool direction);
    private:
        void setPins();
    private:
        int mState;
        int mPin1;
        int mPin2;
        int mPin3;
        int mPin4;
};

#endif
Cabecera de nuestra librería

Vamos a desgranar el código, para poder entenderlo bien.

#ifndef libPaso_h
#define libPaso_h
// ...
#endif
Protección para no incluir el archivo varias veces.

Primero empezamos escribiendo unas protecciones para el preprocesador. Básicamente le dicen al compilador que si la variable indicada, en este caso libPaso_h (pero podría ser cualquier valor), no ha sido definida, que la defina y continúe incluyendo el resto del documento, hasta llegar al final del condicional. Esto evita que la cabecera se incluya más de una vez, problema que podría surgir si, por ejemplo, varios archivos incluyeran esta cabecera. Simplemente recuerda añadir estas protecciones en tus archivos de cabecera, ¡recordando cambiar el valor a definir!

#include "Arduino.h"
incluimos las librerías de Arduino.

A continuación, incluimos las librerías de Arduino. Esto se hace normalmente automáticamente en tus programas de Arduino, pero en el caso de librerías, es necesario hacerlo manualmente.

class MotorPaso{
// ...
};
Clase MotorPaso

Procedemos a el bloque principal de nuestra cabecera, definimos una nueva clase que llamaremos MotorPaso. Será con esta clase que crearemos después un objeto que controle el motor paso a paso del programa principal.

class MotorPaso{
    public:
        MotorPaso(int pin1, int pin2, int pin3, int pin4);
        void begin()const;
        void move(bool direction);
    private:
        void setPins();
};
Miembros de MotorPaso

En cuanto a sus miembros, definimos un constructor que tomará los pines del motor paso a paso, una función para definir los modos de dichos pines y una función que usaremos para mover el motor hacia delante o hacia atrás.

class MotorPaso{
    private:
        int mState;
        int mPin1;
        int mPin2;
        int mPin3;
        int mPin4;
};
Variables de MotorPaso

Y en cuanto a las variables, tendremos los valores de los pines que usaremos para mover el motor y una variable que nos indicará en qué estado nos encontramos actualmente.

Recuerda que puedes consultar el artículo sobre el motor paso a paso para encontrar las tablas con los valores para cada estado:

Controla tu motor paso a paso 28BYJ-48 con L298N
Tutorial sobre como controlar un motor paso a paso 28BYJ-48 con Arduino y el puente H L298N.
Aquí encontraras las tablas de estados.

En el archivo de código fuente simplemente nos dedicamos a definir todo lo declarado en la cabecera:

Ver en Github
#include <libPaso.h>

MotorPaso::MotorPaso(int pin1, int pin2, int pin3, int pin4)
: mPin1(pin1)
, mPin2(pin2)
, mPin3(pin3)
, mPin4(pin4)
, mState(0)
{}

void MotorPaso::begin()const{
    pinMode(mPin1,OUTPUT);
    pinMode(mPin2,OUTPUT);
    pinMode(mPin3,OUTPUT);
    pinMode(mPin4,OUTPUT);
}

void MotorPaso::setPins(){
    switch(mState){
        case 0:
            digitalWrite(mPin1,HIGH);
            digitalWrite(mPin2,LOW);
            digitalWrite(mPin3,LOW);
            digitalWrite(mPin4,LOW);
            break;
        case 1:
            digitalWrite(mPin1,LOW);
            digitalWrite(mPin2,HIGH);
            digitalWrite(mPin3,LOW);
            digitalWrite(mPin4,LOW);
            break;
        case 2:
            digitalWrite(mPin1,LOW);
            digitalWrite(mPin2,LOW);
            digitalWrite(mPin3,HIGH);
            digitalWrite(mPin4,LOW);
            break;
        case 3:
            digitalWrite(mPin1,LOW);
            digitalWrite(mPin2,LOW);
            digitalWrite(mPin3,LOW);
            digitalWrite(mPin4,HIGH);
            break;
    }
}

void MotorPaso::move(bool direction){
    // activamos los pins correspondientes para el estado actual
    setPins();
    // avanzamos o retrocedemos según la dirección
    if(direction)
        mState++;
    else
        mState--;
    // recolocamos el estado en 0 o 3 cuando llega al limite
    if(mState<0)mState=3;
    if(mState>3)mState=0;
}
Código fuente de nuestra librería

A partir de ahora vamos función a función. No dejes que la nomenclatura te confunda, el MotorPaso:: solo indica que la función es un miembro de esa clase.

MotorPaso::MotorPaso(int pin1, int pin2, int pin3, int pin4)
: mPin1(pin1)
, mPin2(pin2)
, mPin3(pin3)
, mPin4(pin4)
, mState(0)
{}

Empezamos con el constructor; se trata de un constructor vacío con una lista de inicialización en la que asignamos los valores de las variables de los pines a los valores que se nos suministra.

void MotorPaso::begin()const{
    pinMode(mPin1,OUTPUT);
    pinMode(mPin2,OUTPUT);
    pinMode(mPin3,OUTPUT);
    pinMode(mPin4,OUTPUT);
}

Pasamos a una función para ajustar los pinModes; normalmente esto lo haríamos en el constructor, pero tratándose de Arduino, es mejor hacer este tipo de operaciones en el setup, por lo que no lo incluimos en el constructor.

La función setPins simplemente se encarga de asignar valores altos o bajos a los pines según el estado actual:

void MotorPaso::setPins(){
    switch(mState){
        case 0:
            digitalWrite(mPin1,HIGH);
            digitalWrite(mPin2,LOW);
            digitalWrite(mPin3,LOW);
            digitalWrite(mPin4,LOW);
            break;
        case 1:
            digitalWrite(mPin1,LOW);
            digitalWrite(mPin2,HIGH);
            digitalWrite(mPin3,LOW);
            digitalWrite(mPin4,LOW);
            break;
        case 2:
            digitalWrite(mPin1,LOW);
            digitalWrite(mPin2,LOW);
            digitalWrite(mPin3,HIGH);
            digitalWrite(mPin4,LOW);
            break;
        case 3:
            digitalWrite(mPin1,LOW);
            digitalWrite(mPin2,LOW);
            digitalWrite(mPin3,LOW);
            digitalWrite(mPin4,HIGH);
            break;
    }
}

Y por último tenemos la función que hace que el motor se mueva. Toma un booleano como variable de entrada para definir la dirección del movimiento. Simplemente actualizamos el estado de los pines con la función setPins definida anteriormente y después incrementamos y disminuimos el valor del estado actual, teniendo cuidado de recolocarlo en el valor más alto o más bajo si alcanzamos el último estado del bucle, con tal de hacer que dé la vuelta por completo:

void MotorPaso::move(bool direction){
    // activamos los pins correspondientes para el estado actual
    setPins();
    // avanzamos o retrocedemos según la dirección
    if(direction)
        mState++;
    else
        mState--;
    // recolocamos el estado en 0 o 3 cuando llega al limite
    if(mState<0)mState=3;
    if(mState>3)mState=0;
}

Instalamos la librería

Una vez escrita, la instalación es muy sencilla, simplemente basta con navegar hasta nuestro directorio de Arduino en el ordenador y encontrar o crear una carpeta llamada “libraries”:

Explorador de Windows mostrando el directorio de Arduino.
Creamos una nueva carpeta llamada "libraries" en nuestro directorio de Arduino

Es aquí donde moveremos nuestros dos archivos dentro de una carpeta con el nombre de la librería. ¡Y con eso ya estaría instalada!

Escribimos un programa que use nuestra nueva librería

Pero claro, la librería por sí sola no hará nada, necesitamos escribir un programa que la utilice:

Ver en Github
#include <libPaso.h>

// creamos un objeto de motor paso a paso
// con los pines indicados
MotorPaso miMotor(2,3,4,5);

// definimos el tiempo de espera entre pasos
const unsigned long delayTime=5;
unsigned long t0;

void setup(){
    // begin() se encarga de los pinModes
    miMotor.begin();
    // iniciamos nuestro temporizador 
    t0=millis();
}

void loop(){
    // nos movemos hacia delante por 3 segundos
    while(millis()-t0<=3000){
        miMotor.move(true);
        delay(delayTime);
    }
    // reseteamos t0
    t0=millis();
    // nos movemos hacia atras 4 segundos
    while(millis()-t0<=4000){
        miMotor.move(false);
        delay(delayTime);
    }
    // reseteamos t0
    t0=millis();
}
Movemos el motor con nuestra librería.

Este programa se parece mucho al que hicimos para controla el motor paso a paso, pero ahora eso está controlado por nuestra librería. Para utilizar nuestra nueva librería basta con incluirla al principio del programa:

#include <libPaso.h>

Y después crear un objeto de MotorPaso, al que llamamos miMotor en este caso, con los pines del motor paso a paso:

MotorPaso miMotor(2,3,4,5);

Procedemos a establecer los pinModes en el setup() y nos movemos unos segundo hacía delante y otros hacia atrás, controlando el tiempo entre pasos de forma externa con un delay().

Esquemático

Utilizamos el mismo circuito que en nuestro artículo anterior:

Esquemático de conexiones.
Si tu motor paso a paso tiene 4 cables, puedes conectarlos a las salidas de los motores A y B

Y comprobamos que nuestra librería funciona perfectamente.

Conclusiones

Está claro que esta librería no está nada optimizada, pues he preferido hacerla más sencilla y fácil de entender, pero como desafío, sería un buen ejercicio optimizar la función setPins() para ir moviéndose por un contenedor, incluir el tiempo entre pasos en la misma clase (y sin delay()), o implementar el movimiento de medio paso. ¡Ánimos!


💡
En calidad de Afiliado de Amazon, obtengo ingresos por las compras adscritas que cumplen los requisitos aplicables

Si estás buscando una alternativa económica a Arduino, sobre todo si es la segunda o tercera placa, recomiendo las alternativas económicas de Elegoo que puedes encontrar en Amazon, tal y como explico en mi guía de compras:

Elegoo compatible Arduino UNO

Ver en Amazon

Etiquetas