Archivo de la categoría: 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

Práctica: Funciones Definidas de Usuario

Menú Interactivo

Hacer un menú interactivo a través del puerto serie donde al pulsar la opción 1 enciende el led del pin 9, al pulsar la opción 2 apaga el led del pin 9 y al pulsar la opción 3 mido el voltaje que tengo en la entrada analógica A0 conectada a un potenciómetro y la muestra por pantalla.

Instalación:

Solución Ejercicio16: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio16-Menu_Interactivo

Función Detecta flanco

Señales digitales:

Hacer una función que detecte flancos ascendentes y descendentes, para ser reutilizada en otros proyectos.

Ponerla en un ejemplo con alguno de los botones.

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio09-Funcion_Detecta_Flanco

Para ejercicio detecta flanco, probar con dos botones en los pines 2 y 3. La función detecta flanco solo funciona con un pulsador, pero cuando se intenta usar con dos pulsadores ya no funciona, la solución es crear un objeto.

Código para detectar flanco en dos pulsadores que falla: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/blob/master/Ejercicio09-Funcion_Detecta_Flanco_Error/Ejercicio09-Funcion_Detecta_Flanco_Error.ino

Dado Digital

Usando las funciones de números aleatorios hacer un dado digital que genere un número aleatorio entre 1 y 4 y encienda un led aleatorio cada vez que se pulse el botón A.

Usar la función de detección de flanco hecha en el anterior ejercicio.

Random Numbers

  • randomSeed() – Inicializa el generador de número pseudo-aleatorios
  • random() – Genera números pseudo-aleatorios

Paso 1 – Generar un valor aleatorio entre 1 y 4 al pulsar el botón

Paso 2 – Apagar el led anteriormente encendido y encender el nuevo led.

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio10-Dado

Ejercicio propuesto. Hacer una ruleta electrónica con un neopixel ring

Tratamiento Avanzado de Strings

Como ya se ha visto anteriormente el tratamiento de strings es un parte muy importante en Arduino puesto que se usa muy frecuentemente y principalmente usamos en las comunicaciones, ya sea puerto serie, bluetooth, XBee, http, etc…

El uso de strings hace un uso intensivo de memoria lo que hace que podamos tener comportamientos extraños en los sketchs o incluso tengamos desbordamiento de memoria.

A la hora de usar strings en Arduino, podemos hacer uso de la clase String https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/ que nos ofrece unos métodos y es muy sencilla de usar, a cambio de ser poco eficiente a nivel de SRAM o usar los stringshttps://arduino.cc/reference/en/language/variables/data-types/string/ como arrays de chars https://arduino.cc/reference/en/language/variables/data-types/char/ que es más complejo de manejar pero más potente y tenemos más control del uso de memoria y pueden usarse muchas de las funciones estandard de C++.

String (Objeto)

Arduino nos ofrece una clase llamada String que facilita el uso de de las cadenas de caracteres con unos métodos muy sencillos de usar y poder usar los operadores que conocemos para los Strings.

Se trata de una clase que permite usar y manipular cadenas de texto de una forma más sencilla que los strings. Puedes concatenar, añadir, buscar, etc… usando los métodos/funciones que ofrece esta clase.

Los Strings tienen un uso intensivo de memoria, pero son muy útiles y se van a utilizar mucho en el apartado de comunicación, por ese motivo es importante aprender a manejar los Strings.

Tener en cuenta que al no ser un tipo de dato propiamente dicho sino una clase, tienes unas funciones asociadas (métodos), operadores y unas propiedades. Es una abstracción del dato y para aprender a usarlo hay que leerse la documentación correspondiente.

Toda la información de la clase String está en: https://arduino.cc/reference/en/language/variables/data-types/stringobject/

Ver documentación de Arduino sobre la clase String:

Tutoriales de uso de String:https://arduino.cc/en/Tutorial/BuiltInExamples#strings

Prácticas Manejo de Strings

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringLengthTrim donde se hace uso de las funciones length() y trim().

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringStartsWithEndsWith donde se hace uso de las funciones StartsWith() y EndsWith().

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringSubstring donde se hace uso de la función substring().

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringToInt donde se hace uso de la función toInt()

Otra de las funciones más útiles de String es IndexOf() con ejemplos en https://www.arduino.cc/en/Tutorial/StringIndexOf

Ejercicio: Conectarse a www.aprendiendoarduino.com y analizar el texto descargado. El texto descargado es una página web que contiene datos de una música a tocar por el buzzer

Hacerlo usando la clase String.

Obtener y sacar por el puerto serie:

  • Tamaño de la web (número de caracteres)
  • Localizar las cadenas: “Inicio Notas”, “Fin Notas”, “Inicio Duración” y “Fin Duración”
  • Obtener las notas (frecuencia) y la duración de las notas. Luego reproducirlo.

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio16-Strings_Avanzado

string (Array de chars)

Un string es un array de chars. Cuando se trabaja con grandes cantidades de texto, es conveniente usar un array de strings. Puesto que los strings son en si mismo arrays. En el reference de Arduino https://arduino.cc/reference/en/language/variables/data-types/string/

La notación de un string como array de chars es char*

El core de Arduino me ofrece varias funciones de análisis de caracteres: https://arduino.cc/reference/en/language/variables/data-types/string/

Una cadena en C++ es un conjunto de caracteres, o valores de tipo char, terminados con el carácter nulo, es decir el valor numérico 0 (\0). Internamente, en el ordenador, se almacenan en posiciones consecutivas de memoria. Este tipo de estructuras recibe un tratamiento muy especial, ya que es de gran utilidad y su uso es continuo. La manera de definir una cadena es la siguiente: char <identificador> [<longitud máxima>];

Cuando se declara una cadena hay que tener en cuenta que tendremos que reservar una posición para almacenar el carácter nulo terminador, de modo que si queremos almacenar la cadena “HOLA”, tendremos que declarar la cadena como: char Saludo[5]; Las cuatro primeras posiciones se usan para almacenar los caracteres “HOLA” y la posición extra, para el carácter nulo.

También nos será posible hacer referencia a cada uno de los caracteres individuales que componen la cadena, simplemente indicando la posición. Por ejemplo el tercer carácter de nuestra cadena de ejemplo será la ‘L’, podemos hacer referencia a él como Saludo[2].

Se puede manipular las cadenas de caracteres de la misma manera en que manipula cualquier otro tipo de array, sin embargo, es preferible hacer uso de una librería estándar especialmente escrita para manipulación de cadenas de caracteres, se trata de la librería <string.h> y que viene incluida con todo compilador de C, C++.

Reference de C++ para la clase string http://cplusplus.com/reference/string/string/ y http://cplusplus.com/reference/cstring/ con funciones como strcpy para strings null-terminated.

Los compiladores de C, C++ dan soporte a la biblioteca de funciones <string.h>, a la que accede por medio de la directiva #include <string.h>. No veremos en detalle todas las funciones contenidas en dicha biblioteca, y nos limitaremos a mostrar algunos ejemplos de ciertas funciones importantes.

  • strlen(): Obtener longitud de cadenas. Sintaxis: size_t strlen(const char *s);
  • strcpy(): Copiar cadenas. Sintaxis: char *stpcpy(char *dest, const char *src);
  • strcat(): Concatenar cadenas. Sintaxis: char *strcat(char *dest, const char *src);
  • strlwr(): Convertir a minúsculas. Sintaxis: char *strlwr(char *dest);
  • strupr(): Convertir a mayúsculas. Sintaxis: char *strupr(char *dest);
  • strchr(): Buscar carácter (hacia adelante). Sintaxis: char *strchr(char *s, int c);
  • strrchr(): Buscar carácter (hacia atras). Sintaxis: char *strrchr(char *s, int c);
  • strstr(): Buscar subcadena. Sintaxis: char *strstr(const char *s1, char *s2);
  • memset(): Establece el primer num bytes del bloque de memoria apuntado por ptr al valor especificado en value (interpretado como un unsigned char).. Sintaxis: void * memset ( void * ptr, int value, size_t num );

Reference:

En C++ también tenemos soporte a la clase cstring, que no debe confundirse con la <string.h>. Una de las ventajas que ofrece la clase cstring es que, a diferencia de las cadenas estándar, ésta posee la capacidad de crecer o disminuir su tamaño en tiempo de ejecución. Además, entre otras características destacables, la clase string soporta operaciones de asignación tales como: =, +, +=, etc.; y de comparación tales como: ==, <=, etc.

Documentacíon de la librería <string.h>: http://www.cplusplus.com/reference/cstring/

Clase string de C++: http://www.cplusplus.com/reference/string/string/

Ejercicio Strings_vs_strings: Partiendo de la base del ejercicio StringsComparisonOperators intentar hacer las operaciones de comparación de igualdad y distinto de los StringOne y StringTwo con string en lugar de String. Ver como es más complicado y para iniciarse en la programación es mejor usar String (objeto) que string (char array).

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio18-strings/_5-String_vs_string

Más información:

Ejercicios Avanzados  Strings

Ejercicio: Mandar un SMS mediante un módulo SIM800L, donde pido por consola el número y el mensaje en formato” <numero_telefono>-<mensaje>!”. Analizar esta cadena y separar en teléfono y mensaje y mandar mediante la función bool sendSms(char* number,char* text);

El primer análisis hacerlo con la clase String y luego pasar las variables teléfono y mensaje a char* que es lo que pide la librería.

Librería disponible en el gestor de librerías: https://github.com/VittorioEsposito/Sim800L-Arduino-Library-revised

HW: https://es.aliexpress.com/item/SIM800L-V2-0-5V-Wireless-GSM-GPRS-MODULE-Quad-Band-W-Antenna-Cable-Cap/32465895576.html

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio68-toCharArray

Ejercicio: Manejo de JSON mediante la librería disponible en el gestor de librerías ArduinoJson https://arduinojson.org y repositorio https://github.com/bblanchon/ArduinoJson

JSON: https://es.wikipedia.org/wiki/JSON

Abrir el ejemplo JsonParserExample de la librería y probarlo.

PROGMEM

El uso de strings como cadena de caracteres en Arduino, en lugar de usar la clase String, nos permite también almacenar los strings en la memoria flash en lugar de la SRAM gracias a PROGMEM https://arduino.cc/reference/en/language/variables/utilities/progmem/

A menudo es conveniente cuando se trabaja con grandes cantidades de texto, como un proyecto con una pantalla LCD o en comunicaciones, usar PROGMEM con arrays de strings.  Estos tienden a ser grandes estructuras, así que ponerlas en memoria de programa (flash) es a menudo deseable. El código siguiente ilustra la idea.

 
#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "String 0";   // "String 0" etc are strings to store - change to suit.
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";

// Then set up a table to refer to your strings.

const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};

char buffer[30];    // make sure this is large enough for the largest string it must hold

void setup()
{
  Serial.begin(9600);
  while(!Serial);
  Serial.println("OK");
}

void loop()
{
   for (int i = 0; i < 6; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
    Serial.println(buffer);
    delay( 500 );
  }
}

Utilidades de PROGMEM: https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html

Usar la tabla de cadenas en la memoria de programa (flash) requiere el uso de funciones especiales para recuperar los datos. La función strcpy_P copia un string desde el espacio del programa (flash) en un string en la memoria RAM (“buffer”). Debemos asegurarnos de que la cadena de recepción en la memoria RAM es lo suficientemente grande como para alojar cualquier string que está recuperando desde el espacio del programa

Las funciones para manejar PROGMEM están en http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html que podemos ver son iguales que las de <string.h>, pero seguidas de “_P”

Tener en cuenta que las variables deben ser definidas de forma global y como constantes, o definido con la palabra clave static, con el fin de trabajar con PROGMEM.

Recordar que podemos usar la macro F() junto con el métodos print y println de forma que todo lo que hay dentro de los métodos se guarda en la memoria flash.

Ejemplo: Serial.println(F(“This string will be stored in flash memory”));

Para saber como funciona PROGMEM ver  la pagina 347 y 354 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf y donde lo metes lo de MEMPROG

Prácticas Repaso Arduino

Práctica: Ejemplos Básicos con Arduino

Repasar cada uno de los ejemplos básicos:

  • Leer entradas digitales
  • Leer entrada digital sin referencia externa
  • Leer entradas analógicas
  • Leer entrada analógica mejorada
  • Escribir salida analógica
  • Escribir salida digital con acción de un botón
  • Mejora de botón para evitar rebotes
  • Contar pulsaciones

Ejemplos básicos con Arduino: http://jecrespo.github.io/ArduinoBasicExamples/

Práctica: Detectar Flanco

Hacer un sistema de encendido y apagado de un led mediante una pulsación. Con una pulsación enciende y con otra apaga el led. Para hacer esto debemos detectar flancos al encender y apagar.

Usar la resistencia interna de pull up de Arduino para detectar la pulsación de un botón en el pin 2 (leer estado de una entrada digital) y encender/apagar el led puesto en el pin 10 (no olvidar la resistencia) al detectar un flanco ascendente. Adicionalmente sacar por el monitor serie el estado de pulsación del botón con un 1 (pulsado) o un 0 (no pulsado) y en la misma línea el estado del led con un 3 (led encendido) y 0 (led apagado), de esta forma abriendo el Serial Plotter es posible ver cuando se enciende y apaga el led al detectar flancos.

Esquema de conexión:

Resultado:

Solución Ejercicio01: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio01-Detectar_Flanco

Solución a los Rebotes (Debounce)

Los rebotes son las falsas pulsaciones que se producen al hacer falsos contactos en el interruptor.

Para ello esperar un tiempo llamado debounceDelay para comprobar que el cambio de estado se mantiene y no son rebotes (ruido).

Para ello crear una variable “debounceDelay” que pueda cambiar su valor y sea el tiempo mínimo en ms que debe estar la señal estable para considerar que no es un rebote. En cada loop comprobar el tiempo desde el último cambio de estado de la señal digital y si es mayor que “debounceDelay” proceder a hacer las comprobaciones de flanco y encender o apagar el LED.

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

Solución Ejercicio02: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio02-Detectar_Flanco_Debounce

Otra forma de solucionar los rebotes por código:

http://miarduinounotieneunblog.blogspot.com.es/2016/01/pulsador-antirrebote-con-contador-de.html ¿véis algún problema con esta solución? Si hay ruido o la señal cambia continuamente, nunca sale del bucle y el programa no puede hacer otras cosas.

Solución: El código puede quedarse durante mucho tiempo en la función antirrebote(), bloqueado Arduino para hacer otras tareas.

Solucionar rebotes por HW:

Calculadora de valores para debouncing: http://protological.com/debounce-calaculator/

Práctica: Medir valor de un Condensador

Usar un Arduino para medir el valor de un Condensador. En este ejercicio se usan pines digitales de entrada y salida para manejar las corrientes y una entrada analógica para leer el voltaje.

Ver: https://www.arduino.cc/en/Tutorial/CapacitanceMeter

Esquema de conexión:

¡ATENCIÓN! Poner un condensador de 100uF y asegurarse de poner correctamente la polaridad. Símbolo – (patilla con símbolo – – – -) a masa.

La resistencia R tiene un valor de 10Kohms.

Al conectar un condensador en serie con una resistencia, a una fuente de tensión eléctrica (o comúnmente, fuente de alimentación), la corriente empieza a circular por ambos. El condensador va acumulando carga entre sus placas. Cuando el condensador se encuentra totalmente cargado, deja de circular corriente por el circuito.

TC = R * C

  • R resistencia de carga en ohms
  • C capacidad del condensador en faradios

Explicación del sketch:

  • Configurar el pin de descarga a INPUT (alta impedancia de modo que no pueda descargar el condensador). Pin 11.
  • Registre el tiempo de inicio con millis ()
  • Establecer el pin de carga en OUTPUT y ponerlo a HIGH. Pin 13.
  • Compruebe la tensión repetidamente en un bucle hasta que llegue a 63.2% de la tensión total.
  • Después de cargar, restar el tiempo actual de la hora de inicio para averiguar cuánto tiempo le costó al condensador para cargar.
  • Dividir el Tiempo en segundos por la resistencia de carga en ohmios para encontrar la Capacitancia.
  • Imprimir por serial el valor con serial.print
  • Descargue el condensador. Para hacer esto:
    • Poner a LOW el pin de carga en la entrada
    • Configurar el de descarga en OUTPUT y haga que sea LOW
    • Leer el voltaje para asegurarse de que el condensador está completamente descargado
    • Loop y hacerlo de nuevo

Para asegurarse que el condensador está descargado, asegurarse de quitar alimentación cuando lo indique el sketch por pantalla.

Solución Ejercicio03: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio03-Medidor_Condensadores

Para ampliar la explicación del ejercicio https://www.arduino.cc/en/Tutorial/CapacitanceMeter

Más Prácticas

Otros ejemplos sencillos de prácticas con Arduino en https://aprendiendoarduino.wordpress.com/2017/07/04/ejemplos-sencillos-arduino/, usando este montaje de prácticas https://aprendiendoarduino.wordpress.com/2017/07/02/montaje-practicas/

  • Leer LDR
  • Contar Pulsos
  • Mover Servo

Novedades Arduino

Desde el año 2015 ha habido varios cambios en la organización de Arduino como empresa que es interesante conocer por las consecuencias que puede tener en el desarrollo de las placas Arduino y en el software.

Arduino LLC fue la compañía creada por Massimo Banzi, David Cuartielles, David Mellis, Tom Igoe and Gianluca Martino en 2009 y era la propietaria de la marca Arduino. Las placas Arduino eran fabricadas por una spinoff llamada Smart Projects Srl creada por Gianluca Martino. En noviembre de 2014 cambiaron el nombre de la empresa que manufactura las placas Arduino de Smart Projects Srl a Arduino Srl y registraron el dominio arduino.org, esto fue el inicio de la división que se produjo poco después.

A principios de 2015 se produjo una división dentro de Arduino y desde entonces han aparecido bastante cambios para los usuarios de Arduino, la primera es que ha aparecido una nueva marca llamada “Genuino” asociada a arduino.cc y una nueva web oficial de Arduino www.arduino.org.

El 1 de octubre de 2016 durante el World Maker Faire de New York, Massimo Banzi and Federico Musto anunciaron las empresas Arduino LLC (arduino.cc) y ArduinoSRL (arduino.org) han firmado un acuerdo, cerrando las disputas iniciadas a principio de 2015. Anunciaron que el recién creado “Arduino Holding” será el punto único para la distribución de los productos Arduino actuales y futuros y continuará sacando al mercado nuevas innovaciones. Además, anunciaron la futura formación de la “Fundación Arduino” sin ánimo de lucro, que será la responsable de mantener el IDE Arduino open source y continuará fomentando el movimiento open source, pero que no se ha hecho realidad.

En el verano de 2017 donde se comunicó que BCMI, la empresa fundada por  Massimo Banzi, David Cuartielles, David Mellis and Tom Igoe, co-fundadores de Arduino ha adquirido el 100% de Arduino AG, la corporación que tenía todas las marcas registradas de Arduino. En el Comunicado Massimo Banzi dice que de esta forma se renueva el compromiso con el HW y SW open source con un crecimiento sostenible de la empresa. Su visión es permitir a cualquier innovar con la electrónica durante mucho tiempo y democratizar el IoT para los individuos, educadores, profesionales y negocios.

Enlaces con las noticias por orden cronológico:

A partir de la Nueva era Arduino

Adquisión de Arduino AG (empresa que tenía la marca Arduino en propiedad): https://blog.arduino.cc/2017/07/28/a-new-era-for-arduino-begins-today/

Nuevas placas Arduino IoT MKR WAN 1300 y MKR GSM 1400 en la Maker Faire New York 2017: https://blog.arduino.cc/2017/09/25/introducing-the-arduino-mkr-wan-1300-and-mkr-gsm-1400/

Asociación entre Arduino y ARM: https://blog.arduino.cc/2017/10/05/arduino-announces-arm-partnership/

Beta Arduino IDE: https://blog.arduino.cc/2017/10/11/be-among-the-first-to-try-arduino-ide-1-9-beta/

Soporte placas linux desde Arduino Create: https://blog.arduino.cc/2017/11/03/linux-support-comes-to-arduino-create/. Esto permite programar placas linux como si se trataran de Arduinos y que múltiples programas Arduino puedan ejecutarse en una placa y comunicarse entre ellos. Y pueden programarse remotamente,

De momento solo para placas Intel como ntel® NUC, Dell Wyse®, Gigabyte™ GB-BXT y las placas UP2 http://www.up-board.org/upsquared/

Nueva Arduino Reference: https://blog.arduino.cc/2017/12/14/new-search-engine-arduino-reference/. Cambio de aspecto https://www.arduino.cc/reference/en/, más rápida y ahora se puede colaborar en la documentación a través de https://github.com/arduino/reference-en y con un reference en español https://github.com/arduino/reference-es

Usar Arduino para programar dispositivos IoT Linux: https://blog.arduino.cc/2018/03/13/you-can-now-use-arduino-to-program-linux-iot-devices/. Se amplían los dispositivos soportados por Arduino Create para el desarrollo de IoT. Desde https://create.arduino.cc/getting-started es posible gestionar y programar muchas de las placas más populares basadas en Linux, incluyendo Raspberry Pi y BeagleBone

Arduino Yun rev 2, completamente open source y resolviendo algunos problemas de seguridad de la versión 1: https://blog.arduino.cc/2018/03/29/arduino-yun-rev-2-is-here/

Placa: https://store.arduino.cc/arduino-yun-rev-2

Nuevas placas en Arduino Day 2018: https://blog.arduino.cc/2018/05/12/the-mkr-family-gets-bigger-with-two-new-iot-boards/

MKR Wifi 1010 https://store.arduino.cc/arduino-mkr-wifi-1010, es una evolución de la placa MKR Wifi 1000 con menor consumo, equipada con un ESP32 y in firmware Wifi open source.

MKR NB 1500 https://store.arduino.cc/arduino-mkr-nb-1500, con conectividad de bajo consumo NB-IoT (narrowband IoT) que trabaja sobre redes LTE que usen NB-IoT, para España Vodafone.

Arduino engineering kit: https://blog.arduino.cc/2018/05/12/arduino-goes-to-college-with-the-new-arduino-engineering-kit/

Placas presentadas en el Maker Faire Bay Area 2018: https://blog.arduino.cc/2018/05/17/say-hello-to-the-next-generation-of-arduino-boards/

MKR Vidor 4000 https://store.arduino.cc/arduino-vidor-4000, primer Arduino basado en un chip FPGA, con WiFi y crypto chip para conexiones seguras.  

Arduino UNO Wifi Rev2 https://store.arduino.cc/arduino-uno-wiFi-rev2 con un nuevo microcontrolador ATmega4809 https://www.microchip.com/wwwproducts/en/ATMEGA4809, un módulo WiFi  u-blox Nina W102 y IMU integrada.

En la novedades sobre Arduinos no oficiales se han añadido nuevas placas al soporte del IDE Arduino: https://github.com/arduino/arduino/wiki/unofficial-list-of-3rd-party-boards-support-urls

Aunque los reyes de los procesadores no oficiales soportados por Arduino son el ESP8266 y el ESP32: