Archivo de la etiqueta: Librerías Arduino

Crear Librerias Arduino

Las librerías son trozos de código hechos por terceros que usamos en nuestro sketch. Esto nos facilita mucho la programación y permite la abstracción haciendo que nuestro programa sea más sencillo de hacer y de entender. En este apartado veremos cómo escribir o modificar librerías.

Librerías Arduino: https://www.arduino.cc/en/Main/Libraries

El IDE de Arduino incluye una serie de librerías ya instaladas: https://www.arduino.cc/en/Reference/Libraries

Listado de librerías del playground de Arduino: http://playground.arduino.cc/Main/LibraryList, pero existen otras muchas librerías creadas por usuarios o por los fabricantes de HW para facilitar el uso de esos dispositivos con Arduino.

Este tutorial explica como crear una librería: http://arduino.cc/en/Hacking/LibraryTutorial. Explica cómo convertir la función morse en en una librería.

Ejemplo ‘morse.ino’:

 
int pin = 13;

void setup()
{
  pinMode(pin, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

Para convertir en una librería de código morse, vemos que hay dos funciones dot() y dash() para iluminar un led durante 250 ms y 1 segundo y  una variable que es ledPin que determina que pin usar. Este es un estilo de programación clásico usando funciones en lugar de objetos.

Para una librería se necesitan al menos dos ficheros:

  • Un fichero de cabecera con la extensión .h. Este fichero tiene las definiciones de la librería, básicamente un listado de todo lo que hay dentro de la librería
  • Un fichero fuente con la extensión .cpp. Este fichero el que contiene el código

En este caso la librería se va a llamar morse y generamos un fichero de cabecera llamado morse.h.

Veamos el código de morse.h donde se define la clase Morse donde tiene una línea por cada función o método y también una línea por cada variable o propiedad de la clase.

 
class Morse
{
  public:
    Morse(int pin);	//constructor
    void dot();
    void dash();
  private:
    int _pin;
};

Una clase es una colección de funciones (métodos) y variables (propiedades) que se guardan todas juntas en un solo lugar. Las funciones pueden ser públicas (public), es decir, pueden llamarse por quien usa la librería o privadas (private), es decir, que solo pueden llamarse desde dentro de la propia clase. Todas las clases tienen una función llamada constructor, que es usada para crear una instancia de la clase. El constructor tiene el mismo nombre que la clase y no tiene tipo de variable de devolución.

En el fichero de cabecera de una librería es necesario la declaración #include que de acceso a los tipos y constantes estándar del lenguaje de Arduino (esto se añade automáticamente en los sketches pero no a las librerías). Esta declaración debe ponerse antes de la definición de la clase. La declaración debe ser:

  • Versión IDE 1.x: #include “Arduino.h”
  • Versión IDE 0.x: #include “WProgram.h”

También es común poner todo el fichero de cabecera entre estas instrucciones:

 
#ifndef Morse_h
#define Morse_h

// the #include statment and code go here...

#endif

Esto evita problemas si alguien accidentalmente incluye dos veces la librería, lo que provocaría un error de compilación. A esto se llama guardián de inclusión múltiple o include guard.

 
// Guardián de inclusión múltiple
#ifndef FICHERO_YA_INCLUIDO
#define FICHERO_YA_INCLUIDO

Así se evita que un compilador poco sofisticado abra otra vez el mismo conjunto de ficheros cuando se incluye un fichero de cabecera dos o más veces. Puede darse el caso de no poner las inclusiones en el inicio de un fichero.

La directiva #include existe en dos versiones. En una se pone el nombre de fichero entre comillas, en la otra entre paréntesis angulares (el signo menor y mayor como “comillas”).

 
#include "fichero_con_comillas.h"
#include <fichero_entre_menor_y_mayor.h>

La versión con los paréntesis angulares busca los ficheros en todos los directorios que se han especificado en la llamada al compilador – normalmente con la opción “-I”. Estos directorios se suelen rastrear por el fichero incluido en el orden en que aparecen en la línea de comando.

Cuando se incluye un fichero entre comillas, entonces el compilador busca este fichero primero en el mismo directorio que el fichero actualmente compilado y después en los demás directorios. Es decir, la versión con comillas se diferencia de la versión con paréntesis angulares únicamente por buscar primero en el directorio del fichero compilado. Tras no encontrarlo ahí actua igual.

Cuando se crea una librería se debe documentar poniendo un comentario al comienzo de la librerías con el nombre, breve descripción, quien la ha escrito, fecha, licencia, etc…

El fichero de cabecera ‘Morse.h’ queda:

 
/*
  Morse.h - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/
#ifndef Morse_h
#define Morse_h

#include "Arduino.h"

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

Una vez hecho el fichero de cabecera hay que codificar el fichero fuente ‘Morse.cpp’.

Primero deben ponerse las declaraciones, esto da al resto de código acceso a las funciones estándar de Arduino y a las definiciones del fichero de cabecera:

 
#include "Arduino.h"
#include "Morse.h"

Lo siguiente es poner el constructor de la clase. Esto define que ocurre cuando se crea una instancia de la clase. En este caso el usuario debe especificar cual es el pin que se va a usar. Configuramos el pin como salida y los guardamos en una variable privada para usarlo desde otras funciones.

 
Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

El código “Morse::” antes del nombre de la función indica que la función es parte de la clase Morse. Esto se ve en todas las funciones de la clase. La variable llamada “_pin” es una variable privada tal y como se ha definido en el fichero de cabecera y se pone el simbolo “_” delante por convención para indicar que es privada y para diferenciarlo del argumento de la función, pero puede llamarse de cualquier forma mientras coincida con la definición en el fichero de cabecera.

Después de definir el constructor, se deben definir las funciones o métodos de la clase. Son las funciones que se habían definido anteriormente en el sketch:

 
void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

También es habitual añadir el comentario del fichero al principio del fichero. El fichero ‘Morse.cpp’ queda de la siguiente forma:

 
/*
  Morse.cpp - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/

#include "Arduino.h"
#include "Morse.h"

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

De esta forma ya tenemos una librería completa. Ahora para incluirla en nuestro IDE debemos crear un directorio Morse dentro del subdirectorio “libraries” del directorio de nuestro entorno de trabajo definido en las propiedades del IDE. Copiar Morse.h y Morse.cpp dentro del directorio Morse y abrir o reiniciar el IDE de Arduino. A partir de este momento veremos nuestra librería disponible en el IDE y podremos incluirla en los sketches con la declaración #include <Morse.h>. La librería será compilada por los sketches que la usen.

El anterior sketch quedaría ahora sustituido por:

 
#include <Morse.h>

Morse morse(13);

void setup()
{
}

void loop()
{
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}

Podemos ver que primero se llama a la declaración de la librería Morse. Esto hace que la librería esté disponible en el sketch y lo incluye en el código enviado a la placa Arduino, lo que hace que si la librería es muy pesada, ocupe mucha más memoria nuestro sketch y si no voy a usar una librería es mejor no incluirla para ahorrar espacio.

También observamos que creamos una instancia de la clase Morse llamada “morse”. Al ejecutar esta línea el constructor de la clase es llamado pasando un argumento, creando así el objeto “morse” en nuestro sketch. Luego podemos llamar a los métodos dot() y dash() precedidos del prefijo morse del nombre del objeto.

Es posible tener múltiples instancias de la clase Morse, cada una un pin diferente guardado en la variable privada “_pin”.

Si creamos una librería es conveniente crear el fichero keywords.txt dentro del directorio Morse. De esta forma conseguiremos resaltar las palabras clave que definamos en el fichero keywords. En cada línea del fichero keywords.txt se indica el nombre de la palabra clave y seguido por un tabulador, el tipo de keyword.

  • Las clases deben ser del tipo KEYWORD1 que se resaltan en naranja.
  • Las funciones deben ser del tipo KEYWORD2 que se resaltan en marrón

Para que el fichero keywords.txt se aplique al IDE es necesario reiniciar el IDE.

El fichero keywords.txt quedaría:

Morse KEYWORD1
dash KEYWORD2
dot KEYWORD2

Recomendación de uso de KEYWORDS:

  • Datatypes and Classes (KEYWORD1)
  • Methods and Functions (KEYWORD2)
  • Instances (KEYWORD2)
  • Class (KEYWORD3)
  • Constants (LITERAL1)
Type ID Purpose Color
KEYWORD1 Datatypes Brown #cc6600,plain
KEYWORD2 Method and function names Brown #cc6600,plain
KEYWORD3 setup, loop, Serial, and a few more Brown, bold #cc6600,bold
LITERAL1 Constants Blue #006699,plain
LITERAL2 Apparently, not used Blue #006699,plain

Las KEYWORDS predefinidas en Arduino están en el fichero C:\Program Files (x86)\Arduino\lib

Más explicación de keywords: https://spencer.bliven.us/index.php/2012/01/18/arduino-ide-keywords/

También es aconsejable ofrecer ejemplos de uso de la librería para que los posibles usuarios sepan usarla. Esto se hace creando un directorio “examples” dentro del directorio Morse y añadir en el subdirectorio los sketches de ejemplos que serán visibles desde el IDE.

La librerías morse con los ejemplos y el fichero keywords.txt se puede descargar en: http://www.arduino.cc/en/uploads/Hacking/Morse.zip

Para más información sobre la creación de librerías con un buen “estilo Arduino”, ver la Guía de Estilo de API. Guia de estilo para escribir librerías: http://arduino.cc/en/Reference/APIStyleGuide

IMPORTANTE: Como se ve en el código de una librería usamos código de de Arduino y por lo tanto podría la librería funcionará con cualquier placa compatible. Pero generalmente las librerías tienen código a bajo nivel que puede que solo valga para un tipo de procesadores y en ese caso habrá que adaptar la librería a las instrucciones del microprocesador con el que queramos usar la librería.

Para saber la arquitectura soportada por una librería ver https://arduinolibraries.info

Librería adaptada para Arduino y ESP8266: https://github.com/adafruit/Adafruit_NeoPixel donde se ha creado el fichero esp8266.c para que cuando compile con una placa ESP8266 o ESP32 sea compatible.

Más información:

Ejemplo Librería Semáforo

Para crear una clase semáforo que representa un semáforo y cuyo programa de uso es:

 
#include <Semaforo.h>

Semaforo miSemaforo(2,3,4,12);

void setup() { 
}

void loop() {
  miSemaforo.Verde();
  delay(3000);
  miSemaforo.Rojo();
  delay(3000);
}

semaforo.h

 
/*
Semaforo.h - Libreria para control de luces de un Semaforo
Por Leo Lamolda, marzo de 2018, para LMDAPPS
experimentosconarduino.blogspot.es.com
*/
#ifndef Semaforo_h
#define Semaforo_h

#include "Arduino.h"

class Semaforo
{  
 public:
  Semaforo(int rojo, int amarillo, int verde, int buzzer);
  void Rojo();
  void Verde();
  void Cambia();
  bool EsRojo();
  bool EsVerde();
 private:
        int _rojo;
        int _amarillo;
        int _verde;
        int _buzzer;
} ;

#endif

Semaforo.cpp

 
/*
Semaforo.h - Libreria para control de luces de un Semaforo
Por Leo Lamolda, marzo de 2018, para LMDAPPS
experimentosconarduino.blogspot.es.com
*/

#include "Semaforo.h"

Semaforo::Semaforo(int rojo, int amarillo, int verde, int buzzer)
{ 
 pinMode(rojo,OUTPUT);     digitalWrite(rojo,LOW);
    pinMode(amarillo,OUTPUT); digitalWrite(amarillo,LOW);
    pinMode(verde,OUTPUT);    digitalWrite(verde,LOW);
    pinMode(buzzer,OUTPUT);   digitalWrite(buzzer,LOW);
   _rojo = rojo;
   _amarillo=amarillo;
   _verde=verde;
   _buzzer=buzzer;             
}

void Semaforo::Rojo(){
 digitalWrite(_rojo,LOW);
  digitalWrite(_verde,LOW);
  for (int i=0;i<5;i++){
        digitalWrite(_amarillo,HIGH);
        digitalWrite(_buzzer,HIGH);
        delay(200);
        digitalWrite(_amarillo,LOW);
        digitalWrite(_buzzer,LOW);
        delay(200);
    } 
 digitalWrite(_rojo,HIGH);
}

void Semaforo::Verde(){
    digitalWrite(_rojo,LOW);
    digitalWrite(_amarillo,LOW);
    digitalWrite(_verde,HIGH);
}

void Semaforo::Cambia(){
 if (EsVerde()) Rojo();
  else Verde(); 
}

bool Semaforo::EsRojo(){
 return digitalRead(_rojo);
}

bool Semaforo::EsVerde(){
 return digitalRead(_verde);
}

Y si tenemos dos semáforos podemos hacer:

 
#include <Semaforo.h>

Semaforo miSemaforo1(2,3,4,12);
Semaforo miSemaforo2(8,9,10,12);

void setup() {  
}

void loop() {
  miSemaforo1.Rojo();
  delay(500);
  miSemaforo2.Verde();
  delay(3000);
  miSemaforo2.Rojo();
  delay(500);
  miSemaforo1.Verde();
  delay(3000);
}

 

Más información:  http://experimentosconarduino.blogspot.com/2018/04/un-clasico-semaforo-con-arduino-y.html

Otros Ejemplos de Librerías

Ver ejemplos simples de librerías:

Librería para el manejo de una API: https://github.com/jecrespo/apisiar_arduino/tree/master/apisiarlib

Guía de estilo para escribir una librería de Arduino

Para facilitar el entendimiento de las librerías o APIs de Arduino y hacer que el entorno de Arduino sea más homogéneo, desde arduino.cc dan unas pautas para escribir librerías al “estilo Arduino”. Algunas de estas prácticas van en contra de la programación programación profesional, pero esto hace posible a los principiantes comenzar a trabajar de forma sencilla con Arduino.

  • Se amable con el usuario. Tener una modelo claro del concepto que va tratar la librería y las funciones que se van a implementar.
  • Organizar las funciones pública alrededor de los datos y funcionalidades que el usuario quiere. Frecuentemente el conjunto de comandos para un módulo electrónico son demasiado complicado, pero podemos reorganizar las funciones desde un punto de vista de uso a alto nivel. Pensar en lo que hace la mayoría de las personas y organizar la API en función de ese uso. Un ejemplo es la librería https://github.com/adafruit/Adafruit-BMP085-Library. La función readPressure() hace todos los pasos para obtener la presión final, siendo transparente para el usuario todos los complejos pasos intermedios. Esto abstrae al usuario no solo de los comandos I2C necesarios, sino de de los cálculos intermedios.
  • Usar palabras claras para escribir la librerías. Usar nombres de funciones y variables claros que expresen claramente lo que son o hacen y no usar términos técnicos.
  • Evitar palabras que tengan diferente significado para el público en general.
  • Documentar todo. Al escribir ejemplos usar esta guía: http://www.arduino.cc/en/Reference/StyleGuide
  • Usar las librerías del core de Arduino y su estilo:
    • Usar read() para leer entradas y write() para escribir salidas
    • Usar las librerías Stream.h y print.h cuando se estén manejando byte streams. Si no es posible al menos intentar usar su modelo de API.
    • Para aplicaciones de redes, usar las librerías de Client y Server como base.
    • Usar begin() para inicializar unas instancia de una librería, normalmente con unos parámetros de configuración. Usar end() para finalizarla.
  • Usar funciones camel case, no con guión bajo. Por ejemplo analogRead en lugar de analog_read. Esto es una adopción de processing por motivos de facilidad la lectura.
  • No usar nombre de constantes largos que son difíciles de leer.
  • Evitar usar argumentos booleanos. En su lugar es preferible ofrecer dos funciones diferentes con nombres que describan la diferencia entre ellas.
  • No asumir conocimiento de punteros. Los principiantes en C suelen encontrar dificultades con el uso de & y *, en la medida de lo posible tratar de evitar el uso de punteros.
  • Al usar la comunicación serie de cualquier tipo, permitir al usuario especificar el objeto de stream en lugar de solo “Serial”, esto hará la librería compatible con los los puertos del Arduino Mega y Due y también poder usar interfaces alternativos como el SoftwareSerial. ver el uso de begin(Stream &serial) en las librerías https://github.com/firmata/arduino y https://github.com/andrewrapp/xbee-arduino
  • Cuando se escriba una librería que ofrezca comunicación byte-stream heredada de la la clase Stream de Arduino, de forma que la librería pueda ser usada por otras librerías que acepte objetos Stream: si es posible, el método read() inmediatamente accede a los datos del buffer sin esperar a que lleguen más datos y si es posible el método write() debe guardar los datos al buffer de transmisión, pero debe esperar si el buffer no tiene suficiente espacio para guardar inmediatamente todo los datos salientes. La función yield() debe llamarse mientras se espera. De esta forma mantenemos compatibilidad con el restos de librerías Stream.

Unos ejemplos de librerías que definen realmente bien las funciones de alto nivel son:

Viendo los métodos públicos que definen es muy fácil entender que es lo que hacen.

Otro buen ejemplo de abstracción de la librerías wire (I2C) se puede ver en: https://github.com/adafruit/RTClib donde con una serie de métodos obtengo los datos del RTC siendo totalmente transparente para el usuario el uso de la librería wire.

Es frecuente que nos encontremos que algunas librerías se quedan cortas para algunas funcionalidades avanzadas, puesto que como se ha visto en la guía de estilo se tiende a simplificar. Un ejemplo de esto es la librería ethernet donde muchas de las funcionalidades del Wiznet 5100 no están implementadas en la librería, pero esto no impide que nosotros podamos añadirlas.

Más información: https://www.arduino.cc/en/Reference/APIStyleGuide

Modificar librerías

Es habitual que al usar una librería necesitemos una propiedad o un método que no esté implementado o queramos modificar el comportamiento, para ello podemos modificar localmente la librería que tengamos instalada en nuestro IDE.

En el caso que queramos colaborar en una librería hecha, podemos hacer un fork de esa librería en nuestra cuenta de github y añadir una nueva funcionalidad o corregir ese fallo e incluso podemos hacer un pull request para que se añada en la librería original nuestra modificación. También podemos tener nuestra propia librería como un fork de la librería original.

Un pull request es una petición que el propietario de un fork de un repositorio hace al propietario del repositorio original para que este último incorpore los commits que están en el fork. Explicación de pull request: http://aprendegit.com/que-es-un-pull-request/

Para modificar la librería time y añadir una funcionalidad, lo primero es hacer un fork del código https://github.com/PaulStoffregen/Time en tu repositorio, en mi caso en https://github.com/jecrespo/Time y luego clonarlo en el ordenador para modificarlo.

Ejemplo de librería Ethernet modificada: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Otros/Ethernet%20Modificada

Añado los métodos:

  • uint8_t *getRemoteIP(uint8_t remoteIP[]);//adds remote ip address
  • uint8_t *getRemoteMAC(uint8_t remoteMAC[]);
  • uint16_t getRemotePort();

Ejercicios Librerías

Hacer Librería Morse

Ejercicio: Hacer la librería Morse y probarla con los leds del montaje.

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio15-Libreria_Morse

Hacer Librería Detecta Flanco

Ejercicio: Hacer una librería que detecte un flanco basado en la clase detecta flanco del ejercicio https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio12-Clase_Detecta_Flanco_cpp .

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio18-Libreria_Detecta_Flanco

Hacer Librería Coche

Ejercicio: Hacer la librería coche para aplicar en un sketch como el visto en https://aprendiendoarduino.wordpress.com/2017/07/08/clases-y-objetos/

 
#include “Coche.h”
 
Coche MiCocheRC(6,7)
 
void setup() {
MiCoche.Arranca()
}
 
void loop() {
char valor = leeBluetooth();
 
switch (valor) {
    case ‘F’:
      MiCoche.Adelante();
      break;
    case ‘R’:
      MiCoche.Derecha();
      break;
    case ‘L’:
       MiCoche.Izquierda();
      break;
    case ‘B’:
       MiCoche.Atras();
      break;
    case ‘S’:
       MiCoche.Para();
      break;
  }
}

Crear los métodos y configurarlos:

  • Arranca()
  • Adelante()
  • Derecha()
  • Izquierda()
  • Atras()
  • Para()
  • MuestraGasolina()
  • EchaGasolina()
  • EncidendeLuces()
  • ApagaLuces()
  • Pita()

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio13-Libreria_Coche

Crear una librería para NTP

Ejercicio: A partir del ejemplo de Arduino para obtener la hora de un servidor NTP https://www.arduino.cc/en/Tutorial/UdpNtpClient, crear una librería para obtener de forma sencilla la hora con una llamada a la función readNTP().

Ver Ejercicio39-NTP para obtener fecha y hora en Arduino sin necesidad de un RTC usando NTP: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio39-NTP

Basado en:

Protocolo:

Las firmas de tiempo que se usan en NTP, son de 32 bits indicando la parte entera en segundos desde  el 1 de Enero de 1900, y una parte fraccionaria también de 32 bits. Por ello la resolución teórica de NTP sería de 232 segundos =136 años, y una resolución teórica de 2E-32 segundos, o sea 0,233 nanosegundos.

Nota: Ojo al llamar al constructor de algo p.e. ethernet sin hacer el begin ethernet antes.

Solución: https://github.com/jecrespo/simpleNTP  

Anuncios

Arduino MKRFOX1200

La apuesta de Arduino por sigfox se llama Arduino MKRFOX1200: https://store.arduino.cc/arduino-mkrfox1200 que se presentó en el Arduino Day de 2017.

Esta placa lleva un microcontrolador Atmel SAMD21 de 32 bits como el resto de la familia MKR de Arduino y un módulo Sigfox ATA8520 también de Atmel.

Microcontrolador: http://www.atmel.com/Images/Atmel-42181-SAM-D21_Summary.pdf

Módulo sgifox: http://www.atmel.com/Images/Atmel-9372-Smart-RF-ATA8520_Datasheet.pdf

Características técnicas:

Microcontroller SAMD21 Cortex-M0+ 32bit low power ARM MCU
Board Power Supply (USB/VIN) 5V
Supported Batteries 2x AA or AAA
Circuit Operating Voltage 3.3V
Digital I/O Pins 8
PWM Pins 12 (0, 1, 2, 3, 4, 5, 6, 7, 8, 10, A3 – or 18 -, A4 -or 19)
UART 1
SPI 1
I2C 1
Analog Input Pins 7 (ADC 8/10/12 bit)
Analog Output Pins 1 (DAC 10 bit)
External Interrupts 8 (0, 1, 4, 5, 6, 7, 8, A1 -or 16-, A2 – or 17)
DC Current per I/O Pin 7 mA
Flash Memory 256 KB
SRAM 32 KB
EEPROM no
Clock Speed 32.768 kHz (RTC), 48 MHz
LED_BUILTIN 6
Full-Speed USB Device and embedded Host
LED_BUILTIN 6
Antenna power 2dB
Carrier frequency 868 MHz

IMPORTANTE: Los Arduino con microcontrolador que integra interfaz USB como los leonardo o los SAMD21 usan Serial como el interfaz para comunicación USB y Serial1 es el puerto UART que disponen, que en el caso de los leonardo son los pines 0 y 1 y en el caso de los SAMD21 son los pines 13 y 14.

Esquematico de la placa: https://www.arduino.cc/en/uploads/Main/MKRFox1200-schematic.pdf

Es una placa perfecta para IoT para usar en una red celular y de bajo consumo. Ideal para proyectos donde hay movilidad. Al comprar este dispositivo obtienes una suscripción gratuita de dos años (con hasta 140 mensajes diarios) a Sigfox y acceso gratuito al servicio de geolocalización que permite hacer un seguimiento del HW sin un módulo GPS. El plan se activará automáticamente después de que se haya enviado el cuarto mensaje.

Covertura de sigfox: https://www.sigfox.com/en/coverage. La frecuencia de Sigfox es 868 MHz.

Pasado los dos años de subscripción, aunque a día de hoy no hay posibilidad de obtener una suscripción de sigfox para desarrolladores o makers, sigfox ha asegurado que se creará un plan de suscripción antes que caduquen las primeras suscripciones en abril de 2019.

La alimentación de esta placa puede ser a 5V mediante el USB o usando dos pilas AA o AAA a través de bornero, conmutando automáticamente entre las dos fuentes. Mediante el Vin también es posible alimentarlo a una fuente regulada de 5V.

La placa está diseñada para alimentarse a 3V a través del bornero, por lo tanto no es posible alimentarlo mediante una batería Li-Po o Li-Ion

Una de las principales características de esta placa es el bajo consumo, puede funcionar con dos pilas AA de 1.5V durante 6 meses con un uso normal.

El microntrolador SAMD21 se puede poner en modo sleep gracias a la librería Low Power https://github.com/arduino-libraries/ArduinoLowPower. En este caso es interesante el uso del bajo consumo que deja dormida la placa y en este modo no aparece el USB. Para despertarla hacer doble click en el botón de reset.

Al igual que el resto de Arduinos con MCU SAMD21 funciona a 3.3V y los pines no son tolerante a voltajes de 5V.

Primeros pasos con MKRFOX1200

Web oficial de Arduino MKRFOX1200:

Getting started: https://www.arduino.cc/en/Guide/MKRFox1200

Getting started SigFox: http://makers.sigfox.com/getting-started/

Librería SigFox: https://www.arduino.cc/en/Reference/SigFox

Tutorial MUY bueno de Luis Del Valle: https://programarfacil.com/blog/arduino-blog/arduino-mkrfox1200-sigfox-lpwan/

Configuración Inicial MKRFOX1200

La placa MKRFOX1200 se programa con el IDE de Arduino, pero para poder hacerlo es necesario instalar el soporte para las placas con microcontrolador SAMD. Para ello hay que ir al gestor de placas e instalar “Arduino SAMD Boards (32-bits ARM Cortex-M0+)” o simplemente buscar MKRFOX en el buscador del gestor de tarjetas.

Luego seleccionar desde el menú Herramientas seleccionar la placa MKRFOX1200.

Para poder usar el MKRFOX1200 con la red de Sigfox es necesario registrarlo, para ello debe usarse el siguiente tutorial llamado primera configuración: https://www.arduino.cc/en/Tutorial/SigFoxFirstConfiguration

Para ejecutar el ejemplo FirstConfiguration, para ello habrá que instalar las librerias Arduino:

Los datos de nuestro modem Sigfox para registrarlo son ID y PAC. Luego hay que registrarlo en la web: https://backend.sigfox.com/activate y seguir las instrucciones de la web que es muy sencilla:

  • Poner placa y país, en España Cellnex es la empresa que tienes Sigfox.
  • Crear una cuenta o sino entrar en la que tienes.

Una vez registrado tarda unos minutos en aparecer los datos y asignará el dispositivo a tu usuario y aparecerá dentro del panel de control en la opción del menú ASSOCIATED DEVICE.

Con esto ya podemos empezar a mandar datos al backend de Sigfox.

Manejo MKRFOX1200 y Sigfox

Para empezar a usar el Arduino MKRFOX1200 y Sigfox, al igual que con cualquier otro dispositivo o librería de Arduino, lo mejor es revisar los ejemplos que vienen al instalar el soporte para esta placa: https://github.com/arduino-libraries/SigFox/tree/master/examples

Para poder acceder a los ejemplos seguir: Archivo – Ejemplos – Arduino Sigfox for MKR1200

El primer ejemplo es FirstConfiguration que ya hemos visto: https://github.com/arduino-libraries/SigFox/blob/master/examples/FirstConfiguration/FirstConfiguration.ino

Después de registrar el MKRFOX1200, para probar el funcionamiento de la placa puede usarse el ejemplo Sigfox Event Trigger donde se manda un mensaje de alarma de dos fuentes diferntes conectadas los pines de interrupción 0 y 1: https://www.arduino.cc/en/Tutorial/SigFoxEventTrigger

Más información: https://www.arduino.cc/en/Tutorial/SigFoxEventTrigger

Podría usarse para conectar un sensor de puerta y uno de ventana y cada vez que se abra mande un mensaje. Luego para que mande un correo o SMS habrá que configurar el callback en el backend de Sigfox.

Otros ejemplo son:

Otros ejemplo de monitores de condiciones atmosféricas:

Librería Sigfox

Arduino, además de ofrecernos un HW con Sigfox a buen precio, nos da una librería muy fácil de usar y más aun a quienes están acostumbrados a la programación de Arduino.

SigFox – Esta librería permite el uso de transceiver de Sigfox ATAB8520E en las placas Arduino MKRFOX1200.

Librería: https://www.arduino.cc/en/Reference/SigFox

  • begin() – Inicializa el módulo Sigfox
  • beginPacket() – Comienza el proceso de mandar un paquete
  • write() – Manda datos binarios al backend de Sigfox
  • print() – Manda caracteres al backend de Sigfox
  • endPacket() – Finaliza el proceso de enviar paquetes iniciado con beginPacket()
  • parsePacket() – Comprueba la presencia de un paquete Sigfox antes de leer.
  • SigVersion() – Devuelve la versión de firmware del módulo
  • ID() – Devuelve el Sigfox ID del módulo que es único
  • PAC() – Devuelve el PAC del módulo, que es la clave secreta correspondiente al ID. El PAC no es transferible y debe regenerarse al cambiar de dueño el módulo.
  • reset() – resetea el módulo de sigfox
  • internalTemperature() – Devuelve la temperatura del sensor interno
  • debug() – Habilita el debug y deshabilita las funciones de ahorro de energía.
  • noDebug() – Deshabilita el debug
  • available() – Devuelve el número de bytes disponibles para leer.
  • read() – Lee los datos entrantes de Sigfox.

Callbacks

Un callback se puede traducir como una llamada de vuelta, devolución de llamada o una retrollamada. Es una de las configuraciones más importantes de un DEVICE TYPE ya que nos permite añadir, modificar o eliminar Callbacks. Los callbacks van asociados a los DEVICE TYPE y no a los DEVICES.

Sirve para enviar todos los datos que recibimos desde este DEVICE TYPE a otro sitio. El caso típico es poder llamar a alguna plataforma del IoT. Si por ejemplo queremos hacer una gráfica de las temperaturas, en el backend de SigFox no podemos hacer esto. Por eso existen las Callbacks para reenviar todos esos datos a una plataforma que permita gestionar esa información y dar un aspecto visual más atractivo.

Sigfox hace que sea fácil recoger los datos enviados por los dispositivos del servicio en la nube mediante el uso de callbacks. Las callbacks son un servicio que permite a Sigfox enviar un evento a un servidor externo después de recibir el evento. Por ejemplo, un dispositivo podría enviar un mensaje Sigfox al ocurrir un evento (una ventana abierta), es posible recibir una notificación una vez que se haya producido este evento. Esta sería la idea de usar un callback. El servidor Sigfox transmitirá el mensaje a través de una solicitud POST / GET a su propio servidor o enviar un correo electrónico. Además de definir su propio servidor y sus datos, Sigfox también le permite transferir sus datos con de forma simplificada como AWS IoT y Microsoft Azure.

Para configurar un callback personalizado, debe estar el dispositivo y cuenta registrados y configurado un dispositivo con un tipo de dispositivo y grupo.

Navega a la pestaña ‘Tipo de dispositivo’ en la barra de navegación. Luego, busca el Tipo de dispositivo de tu dispositivo y haz clic en el botón de filtro. Seleccione el ‘Nombre’ del dispositivo dentro de la entrada de búsqueda. Lo llevarán a la página ‘Información’. Desde aquí puede ver todos los datos sobre el dispositivo que configuró. Ahora navegue a ‘callback’ en el lado izquierdo de la página. Si esta es la primera vez que configura una callback, la página debe estar vacía. Haga clic en el botón ‘Nuevo’ en la esquina superior derecha y se le mostrará una lista de los diferentes tipos de devoluciones de llamadas

Haga clic en el elemento ‘Callbacks personalizados’. Ahora tendrá una página similar a la siguiente, con varias opciones de configuración diferentes.

Los campos a rellenar son:

  • Custom Payload Config. Este campo permite especificar cómo desea que Sigfox decodifique el mensaje de su dispositivo.
  • Body: Este es el contenido principal del mensaje. Se puede especificar cualquier dato personalizado dentro de la carga útil. Puede ver todas las variables disponibles en la sección Sintaxis de URL.

Más información: http://makers.sigfox.com/getting-started/

Estas callbacks transfieren todos los datos recibidos desde los dispositivos asociados a este DEVICE TYPE a una infraestructura externa. Para obtener más información, consulte la documentación. Callback documentation: https://backend.sigfox.com/apidocs/callback

Callback para mandar un correo:

Callback para mandar los datos a una web/base de datos externa:

Esta es la llamada a la API: https://www.aprendiendoarduino.com/servicios/SMS/saveSMS.php?telefono=6359871xx&mensaje=alarm_bike_{device}_lat_ {lat}_long_{lng}&pin=xxxx

Protocolo HTTP

Protocolo HTTP

Hypertext Transfer Protocol o HTTP (en español protocolo de transferencia de hipertexto) es el protocolo de comunicación que permite las transferencias de información en la WWW. Se trata de un protocolo de capa 7 de aplicación.

En arduino con la librería ethernet solo trabajamos con la capa de aplicación, todas las otras capas de la pila TCP/IP ya están implementadas por Hardware, ya sea con la ethernet shield o el módulo WiFi correspondiente. Aunque si queremos realizar algunas funciones de capas inferiores, podemos hacerlo con los comandos adecuados comunicándonos con el chip ethernet o wifi via SPI.

Veamos algunos protocolos de la capa de aplicación que serán los que tengamos que implementar en nuestro arduino directamente o usando la librería adecuada:

HTTP es un protocolo muy importante puesto que es el que se va a usar para comunicar Arduino con cualquier elemento de la WWW o de una intranet. En el IoT es uno de los protocolos más usados y sobre todo si queremos obtener o mandar datos a servidores o usar las APIs que nos ofrecen algunos servicios para obtención de información, por ejemplo, para obtener el tiempo meteorológico de la AEMET https://opendata.aemet.es/centrodedescargas/inicio y con esos datos que Arduino actúe de una forma u otra.

Hypertext Transfer Protocol o HTTP (en español protocolo de transferencia de hipertexto) es el protocolo usado en cada transacción de la World Wide Web. HTTP fue desarrollado por el World Wide Web Consortium y la Internet Engineering Task Force.

HTTP define la sintaxis y la semántica que utilizan los elementos de software de la arquitectura web (clientes, servidores, proxies) para comunicarse. Es un protocolo orientado a transacciones y sigue el esquema petición-respuesta entre un cliente y un servidor. Al cliente que efectúa la petición (un navegador web) se lo conoce como “user agent” (agente del usuario). A la información transmitida se la llama recurso y se la identifica mediante un localizador uniforme de recursos (URL).

HTTP es un protocolo sin estado, es decir, que no guarda ninguna información sobre conexiones anteriores. El desarrollo de aplicaciones web necesita frecuentemente mantener estado. Para esto se usan las cookies, que es información que un servidor puede almacenar en el sistema cliente. Esto le permite a las aplicaciones web instituir la noción de “sesión”, y también permite rastrear usuarios ya que las cookies pueden guardarse en el cliente por tiempo indeterminado.

Una transacción HTTP está formada por un encabezado seguido, opcionalmente, por una línea en blanco y algún dato. El encabezado especificará cosas como la acción requerida del servidor, o el tipo de dato retornado, o el código de estado. El uso de campos de encabezados enviados en las transacciones HTTP le dan gran flexibilidad al protocolo. Estos campos permiten que se envíe información descriptiva en la transacción, permitiendo así la autenticación, cifrado e identificación de usuario. Ejemplos de encabezados: HTTP_ACCEPT y HTTP_USER_AGENT.

Más información sobre HTTP:

Para intercambio de archivos por HTTP usamos MIME: http://es.wikipedia.org/wiki/Multipurpose_Internet_Mail_Extensions

Líneas de encabezado o headers, son muy importantes y dan información adicional de la conexión y el comportamiento puede cambiar en función de ellas:

Interesante HTTP quick guide: https://www.tutorialspoint.com/http/http_quick_guide.htm

Métodos de Petición HTTP

Lo más importante para comunicar arduino por HTTP con otros dispositivos, ya sean servidores, ordenadores, otros Arduinos, etc… es conocer los métodos GET y POST del protocolo HTTP. HTTP define 8 métodos que indica la acción que desea que se efectúe sobre el recurso identificado. Lo que este recurso representa, si los datos pre-existentes o datos que se generan de forma dinámica, depende de la aplicación del servidor. A menudo, el recurso corresponde a un archivo o la salida de un ejecutable que residen en el servidor.

GET

GET: Pide una representación del recurso especificado. Por seguridad no debería ser usado por aplicaciones que causen efectos ya que transmite información a través de la URI agregando parámetros a la URL. La petición puede ser simple, es decir en una línea o compuesta de la manera que muestra el ejemplo.

Ejemplo simple:

GET /images/logo.png HTTP/1.1 obtiene un recurso llamado logo.png

Ejemplo con parámetros:

GET /index.php?page=main&lang=es HTTP/1.1

POST

POST: Envía los datos para que sean procesados por el recurso identificado. Los datos se incluirán en el cuerpo de la petición. Esto puede resultar en la creación de un nuevo recurso o de las actualizaciones de los recursos existentes o ambas cosas.

Los otros métodos de HTTP:

Más información:

Entender los métodos get y post:

Un explicación muy buena de HTTP también se puede encontrar en:  http://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html

HTTP request

Un cliente HTTP debe formar una petición HTTP (request) al servidor de una forma determinada para que sea entendida por el servidor. Cuando Arduino trabaja como cliente hay que programar esta petición correctamente, sino el servidor nos mandará un mensaje de error.

Formación de un HTTP request, esta petición habrá que programar en Arduino:

Trama en HTTP, fijaros en el uso de cr (retorno de carro – carriage return – ASCII 13) y lf (line feed – nueva linea – ASCII 10): http://www1.ju.edu.jo/ecourse/abusufah/cpe532_Spr06/notes/BookOnLine/HTTP%20Request%20Message.htm

HTTP/1.1 se definió en el estándar RFC2616,que es la más usada actualmente. En junio de 2014 RFC2616 se retiró y HTTP/1.1 se redefinió en RFCs 7230, 7231, 7232, 7233, 7234, and 7235, HTTP/2 está en proceso de definición.

Y cuando usar GET o POST?: http://www.w3.org/2001/tag/doc/whenToUseGet.html#checklist

HTTP response

Después de recibir e interpretar el servidor un HTTP request, el servidor debe responder con un mensaje de respuesta:

Para cumplir con el protocolo HTTP, arduino debe implementar estas respuestas cuando lo uso como servidor web, como devolución a un request mandado por un cliente como puede ser un browser o navegador. De esta forma puedo implementar en Arduino una web embebida.

Por lo tanto Arduino podemos programarlo para comportarse como cliente, como servidor o como ambos.

Veamos esto gráficamente:

Ejercicio: Ver las tramas HTTP con las funciones de depuración del navegador y también con wireshark, un web sniffer on-line y algún plugin para el navegador.

Listado de web sniffers: http://scraping.pro/web-sniffers-review/

Servidor Web Embebido en Arduino

Para poder implementar un servidor web embebido en un Arduino e interactuar con él, se debe programar los mensajes http en Arduino para responder al navegador de forma adecuada.

La secuencia que se produce en una web embebida para encender y apagar un led es:

  • El navegador manda un http request GET a la IP de Arduino cuando pongo su IP en el navegador. p.e. http://192.168.1.15
  • Arduino recibe la petición que comienza por “GET / HTTP/1.1”
  • Arduino devuelve el http response con “HTTP/1.0 200K” y luego la web con el código html, haciendo print sobre el cliente ethernet y cierra la comunicación.
  • El navegador recibe el http respnse y muestra la web, en este caso un botón.
  • Al pulsar el botón en el navegador, el código HTML ya está configurado para mandar una petición POST.
  • Arduino recibe la petición que comienza por “POST / HTTP/1.1” y enciende o apaga el led según corresponda.
  • Luego Arduino muestra la web con el estado del led actualizado.

Ver este proceso con wireshark o con las herramienta de desarrollador del navegador pulsando F12.

Ver sketch en: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_2017/tree/master/Ejercicio40-Boton_Mejorado_DHCP

Diagrama de flujo:

Más información:

Programa botón para diferentes Arduino. Comparar:

Librerías HTTP

Libreria Webduino

Webduino es una librería muy popular que nos permite implementar un servidor web en nuestro Arduino.

La web del creador: https://code.google.com/p/webduino/

El reference de la librería: https://code.google.com/p/webduino/wiki/Documentation

Repositorio de la librería: https://github.com/sirleech/Webduino

Snippet Webduino en el playground: http://playground.arduino.cc/Main/WebduinoFileServer

Para Shields con Microchip ENC28J60 no es válida esta librería puesto que necesita SW adicional para implementar la pila TCP/IP.

Una presentación que explica como funciona: https://docs.google.com/presentation/d/1QUG4XJTK3jKtU-eYUfM1DvUdRaKBrp__LEwZQfz9s6E/edit#slide=id.i0

Hilo de soporte de la librería: http://forum.arduino.cc/index.php/topic,37851.0.html

Ejercicio 29-Webduino Entender cómo funciona la librería y ver el ejemplo webdemo

Luego hacer la aplicación web buzzer.

Streaming: http://arduiniana.org/libraries/streaming/

Class Templates: http://www.cprogramming.com/tutorial/templates.html

Solución en https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio29-Webduino

Otras Librerías

Otras librerías para implementar un servidor web en Arduino:

Y una librería para implementar un cliente HTTP:

Librería http client: https://github.com/amcewen/HttpClient

Librerías Arduino

Las librerías son trozos de código hechos por terceros que usamos en nuestro sketch. Esto nos facilita mucho la programación y hace que nuestro programa sea más sencillo de hacer y de entender. En este curso no veremos como hacer o modificar una librería pero en este curso debemos ser capaces de buscar una librería, instalarla, aprender a usar cualquier librería y usarla en un sketch.

Las librerías normalmente incluyen los siguientes archivos comprimidos en un archivo ZIP o dentro de un directorio. Estas siempre contienen:

  • Un archivo .cpp (código de C++)
  • Un archivo .h o encabezado de C, que contiene las propiedades y métodos o funciones de la librería.
  • Un archivo Keywords.txt, que contiene las palabras clave que se resaltan en el IDE (opcional).
  • Muy posiblemente la librería incluye un archivo readme con información adicional de lo que hace y con instrucciones de como usarla.
  • Directorio denominado examples con varios sketchs de ejemplo que nos ayudará a entender cómo usar la librería (opcional).

Como instalar librerías: http://arduino.cc/en/Guide/Libraries

Hay varios métodos de instalar librerías:

  • Mediante el IDE de Arduino de forma automática. Admite la instalación desde un fichero zip o desde una carpeta ya descomprimida.
  • Instalación Manual. Descomprimiendo en un directorio la librería y copiandolo en el directorio de librerías. Generalmente Mi Documentos – Arduino – libraries. Aquí se guardan las librerías contribuidas por el usuario como lo denomina el IDE.
  • Desde el gestor de librerías. A partir de la versión 1.6.2 del IDE de Arduino se incorpora el gestor de librerías que facilita el trabajo. Esta herramienta es accesible desde Programa → Incluir Librería → Gestionar Librerías. Desde aquí podemos ver las librerías instaladas, buscar librerías disponibles, instalar librerías y actualizarlas.
    Esta herramienta también nos permite gestionar las librerías instaladas manualmente.
    Desde C:\Users\nombre_usuario\AppData\Local\Arduino15, podemos ver en formato json el listado de librerías y placas disponibles desde el gestor de librerías y tarjetas.

La librerías instaladas se guardan en el directorio indicado desde las preferencias del IDE.

Todas las librerías disponibles en el gestor de librerías pueden encontrarse en http://www.arduinolibraries.info/

IMPORTANTE: Para añadir una librería a nuestro proyecto simplemente se añade a nuestro código la palabra clave #include seguido del nombre de la librería.

Más información: https://aprendiendoarduino.wordpress.com/2017/06/20/librerias-arduino-3/

Práctica: Instalación de Librerías

Instalar las librerías:

Más información: https://aprendiendoarduino.wordpress.com/2017/06/20/uso-de-librerias-arduino/

Práctica: Comparación de Timers

Compara el funcionamiento y limitaciones de las librerías MsTimer2.h y Timer.h

Ejecutar el ejercicio04 y comparar el funcionamiento de los dos timers.

Solución Ejercicio04: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio04-Compara_Timers

MsTimer2 – Solo permite un temporizador que se ejecuta gracias a la interrupción asociada al timer 2 que dispone Arduino. Tiene prioridad de ejecución por encima de cualquier otra operación que se esté ejecutando.

Timer – Permite muchos temporizadores. Ejecuta la instrucción temporizada cuando puede en función de cuando se llama a “t.update();”, si hay retrasos en el loop la función se retrasa.

¿Alguna otra librería que queráis aprender a manejar? Enviar correo a aprendiendoarduino@gmail.com

Crear Librerías Arduino


NOTA: antes de crear una librería, es importante saber como son las librería de Arduino, como instalarlas y como usarlas. Recomiendo leer estos dos capítulos antes de empezar a crear tu librería Arduino:


Las librerías son trozos de código hechos por terceros que usamos en nuestro sketch. Esto nos facilita mucho la programación y permite la abstracción haciendo que nuestro programa sea más sencillo de hacer y de entender. En este apartado veremos cómo escribir o modificar librerías.

Librerías Arduino: https://www.arduino.cc/en/Main/Libraries

El IDE de Arduino incluye una serie de librerías ya instaladas: https://www.arduino.cc/en/Reference/Libraries

Listado de librerías del playground de Arduino: http://playground.arduino.cc/Main/LibraryList, pero existen otras muchas librerías creadas por usuarios o por los fabricantes de HW para facilitar el uso de esos dispositivos con Arduino.

Este tutorial explica como crear una librería: http://arduino.cc/en/Hacking/LibraryTutorial. Explica cómo convertir la función morse en en una librería.

Ejemplo ‘morse.ino’:

 
int pin = 13;

void setup()
{
  pinMode(pin, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

Para convertir en una librería de código morse, vemos que hay dos funciones dot() y dash() para iluminar un led durante 250 ms y 1 segundo y  una variable que es ledPin que determina que pin usar. Este es un estilo de programación clásico usando funciones en lugar de objetos.

Para una librería se necesitan al menos dos ficheros:

  • Un fichero de cabecera con la extensión .h. Este fichero tiene las definiciones de la librería, básicamente un listado de todo lo que hay dentro de la librería
  • Un fichero fuente con la extensión .cpp. Este fichero el que contiene el código

En este caso la librería se va a llamar morse y generamos un fichero de cabecera llamado morse.h.

Veamos el código de morse.h donde se define la clase Morse donde tiene una línea por cada función o método y también una línea por cada variable o propiedad de la clase.

 
class Morse
{
  public:
    Morse(int pin);	//constructor
    void dot();
    void dash();
  private:
    int _pin;
};

Una clase es una colección de funciones (métodos) y variables (propiedades) que se guardan todas juntas en un solo lugar. Las funciones pueden ser públicas (public), es decir, pueden llamarse por quien usa la librería o privadas (private), es decir, que solo pueden llamarse desde dentro de la propia clase. Todas las clases tienen una función llamada constructor, que es usada para crear una instancia de la clase. El constructor tiene el mismo nombre que la clase y no tiene tipo de variable de devolución.

En el fichero de cabecera de una librería es necesario la declaración #include que de acceso a los tipos y constantes estándar del lenguaje de Arduino (esto se añade automáticamente en los sketches pero no a las librerías). Esta declaración debe ponerse antes de la definición de la clase. La declaración debe ser:

  • Versión IDE 1.x: #include “Arduino.h”
  • Versión IDE 0.x: #include “WProgram.h”

También es común poner todo el fichero de cabecera entre estas instrucciones:

 
#ifndef Morse_h
#define Morse_h

// the #include statment and code go here...

#endif

Esto evita problemas si alguien accidentalmente incluye dos veces la librería, lo que provocaría un error de compilación. A esto se llama guardián de inclusión múltiple o include guard.

 
// Guardián de inclusión múltiple
#ifndef FICHERO_YA_INCLUIDO
#define FICHERO_YA_INCLUIDO

Así se evita que un compilador poco sofisticado abra otra vez el mismo conjunto de ficheros cuando se incluye un fichero de cabecera dos o más veces. Puede darse el caso de no poner las inclusiones en el inicio de un fichero.

La directiva #include existe en dos versiones. En una se pone el nombre de fichero entre comillas, en la otra entre paréntesis angulares (el signo menor y mayor como “comillas”).

 
#include "fichero_con_comillas.h"
#include <fichero_entre_menor_y_mayor.h>

La versión con los paréntesis angulares busca los ficheros en todos los directorios que se han especificado en la llamada al compilador – normalmente con la opción “-I”. Estos directorios se suelen rastrear por el fichero incluido en el orden en que aparecen en la línea de comando.

Cuando se incluye un fichero entre comillas, entonces el compilador busca este fichero primero en el mismo directorio que el fichero actualmente compilado y después en los demás directorios. Es decir, la versión con comillas se diferencia de la versión con paréntesis angulares únicamente por buscar primero en el directorio del fichero compilado. Tras no encontrarlo ahí actaa igual.

Cuando se crea una librería se debe documentar poniendo un comentario al comienzo de la librerías con el nombre, breve descripción, quien la ha escrito, fecha, licencia, etc…

El fichero de cabecera ‘Morse.h’ queda:

 
/*
  Morse.h - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/
#ifndef Morse_h
#define Morse_h

#include "Arduino.h"

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

Una vez hecho el fichero de cabecera hay que codificar el fichero fuente ‘Morse.cpp’.

Primero deben ponerse las declaraciones, esto da al resto de código acceso a las funciones estándar de Arduino y a las definiciones del fichero de cabecera:

 
#include "Arduino.h"
#include "Morse.h"

Lo siguiente es poner el constructor de la clase. Esto define que ocurre cuando se crea una instancia de la clase. En este caso el usuario debe especificar cual es el pin que se va a usar. Configuramos el pin como salida y los guardamos en una variable privada para usarlo desde otras funciones.

 
Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

El código “Morse::” antes del nombre de la función indica que la función es parte de la clase Morse. Esto se ve en todas las funciones de la clase. La variable llamada “_pin” es una variable privada tal y como se ha definido en el fichero de cabecera y se pone el simbolo “_” delante por convención para indicar que es privada y para diferenciarlo del argumento de la función, pero puede llamarse de cualquier forma mientras coincida con la definición en el fichero de cabecera.

Después de definir el constructor, se deben definir las funciones o métodos de la clase. Son las funciones que se habían definido anteriormente en el sketch:

 
void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

También es habitual añadir el comentario del fichero al principio del fichero. El fichero ‘Morse.cpp’ queda de la siguiente forma:

 
/*
  Morse.cpp - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/

#include "Arduino.h"
#include "Morse.h"

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

De esta forma ya tenemos una librería completa. Ahora para incluirla en nuestro IDE debemos crear un directorio Morse dentro del subdirectorio “libraries” del directorio de nuestro entorno de trabajo definido en las propiedades del IDE. Copiar Morse.h y Morse.cpp dentro del directorio Morse y abrir o reiniciar el IDE de Arduino. A partir de este momento veremos nuestra librería disponible en el IDE y podremos incluirla en los sketches con la declaración #include <Morse.h>. La librería será compilada por los sketches que la usen.

El anterior sketch quedaría ahora sustituido por:

 
#include <Morse.h>

Morse morse(13);

void setup()
{
}

void loop()
{
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}

Podemos ver que primero se llama a la declaración de la librería Morse. Esto hace que la librería esté disponible en el sketch y lo incluye en el código enviado a la placa Arduino, lo que hace que si la librería es muy pesada, ocupe mucha más memoria nuestro sketch y si no voy a usar una librería es mejor no incluirla para ahorrar espacio.

También observamos que creamos una instancia de la clase Morse llamada “morse”. Al ejecutar esta línea el constructor de la clase es llamado pasando un argumento, creando así el objeto “morse” en nuestro sketch. Luego podemos llamar a los métodos dot() y dash() precedidos del prefijo morse del nombre del objeto.

Es posible tener múltiples instancias de la clase Morse, cada una un pin diferente guardado en la variable privada “_pin”.

Si creamos una librería es conveniente crear el fichero keywords.txt dentro del directorio Morse. De esta forma conseguiremos resaltar las palabras clave que definamos en el fichero keywords. En cada línea del fichero keywords.txt se indica el nombre de la palabra clave y seguido por un tabulador, el tipo de keyword.

  • Las clases deben ser del tipo KEYWORD1 que se resaltan en naranja.
  • Las funciones deben ser del tipo KEYWORD2 que se resaltan en marrón

Para que el fichero keywords.txt se aplique al IDE es necesario reiniciar el IDE.

El fichero keywords.txt quedaría:

Morse KEYWORD1
dash KEYWORD2
dot KEYWORD2

También es aconsejable ofrecer ejemplos de uso de la librería para que los posibles usuarios sepan usarla. Esto se hace creando un directorio “examples” dentro del directorio Morse y añadir en el subdirectorio los sketches de ejemplos que serán visibles desde el IDE.

La librerías morse con los ejemplos y el fichero keywords.txt se puede descargar en: http://www.arduino.cc/en/uploads/Hacking/Morse.zip

Para más información sobre la creación de librerías con un buen “estilo Arduino”, ver la Guía de Estilo de API. Guia de estilo para escribir librerías: http://arduino.cc/en/Reference/APIStyleGuide

IMPORTANTE: Como se ve en el código de una librería usamos código de de Arduino y por lo tanto podría la librería funcionará con cualquier placa compatible. Pero generalmente las librerías tienen código a bajo nivel que puede que solo valga para un tipo de procesadores y en ese caso habrá que adaptar la librería a las instrucciones del microprocesador con el que queramos usar la librería.

Ejercicios Librerías

Hacer Librería Morse

Ejercicio15: Hacer la librería Morse y probrala con los leds del montaje.

Github clone link: https://github.com/curso-programacion-arduino/Ejercicio15.git  

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino

Hacer Librería Coche

Ejercicio13: Hacer la librería coche para aplicar en un sketch como el visto en https://aprendiendoarduino.wordpress.com/2017/07/08/clases-y-objetos/

 
#include “Coche.h”
 
Coche MiCocheRC(6,7)
 
void setup() {
MiCoche.Arranca()
}
 
void loop() {
char valor = leeBluetooth();
 
switch (valor) {
    case ‘F’:
      MiCoche.Adelante();
      break;
    case ‘R’:
      MiCoche.Derecha();
      break;
    case ‘L’:
       MiCoche.Izquierda();
      break;
    case ‘B’:
       MiCoche.Atras();
      break;
    case ‘S’:
       MiCoche.Para();
      break;
  }
}

Crear los métodos y configurarlos:

  • Arranca()
  • Adelante()
  • Derecha()
  • Izquierda()
  • Atras()
  • Para()
  • MuestraGasolina()
  • EchaGasolina()
  • EncidendeLuces()
  • ApagaLuces()
  • Pita()

Github clone link: https://github.com/curso-programacion-arduino/Ejercicio13.git   

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino

Crear una librería para NTP

Ejercicio: A partir del ejemplo de Arduino para obtener la hora de un servidor NTP https://www.arduino.cc/en/Tutorial/UdpNtpClient, crear una librería para obtener de forma sencilla la hora con una llamada a la función readNTP().

Ver Ejercicio39-NTP para obtener fecha y hora en Arduino sin necesidad de un RTC usando NTP: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio39-NTP

Basado en:

Protocolo:

Las firmas de tiempo que se usan en NTP, son de 32 bits indicando la parte entera en segundos desde  el 1 de Enero de 1900, y una parte fraccionaria también de 32 bits. Por ello la resolución teórica de NTP sería de 232 segundos =136 años, y una resolución teórica de 2E-32 segundos, o sea 0,233 nanosegundos.

Nota: Ojo al llamar al constructor de algo p.e. ethernet sin hacer el begin ethernet antes.

Solución: https://github.com/jecrespo/simpleNTP