Archivo de la categoría: C++

Funciones definidas por usuario

Una función es un bloque de código que tiene un nombre y un conjunto de instrucciones que son ejecutadas cuando se llama a la función. Son funciones setup() y loop() de las que ya se ha hablado.

Las funciones de usuario pueden ser escritas para realizar tareas repetitivas y para reducir el tamaño de un programa. Segmentar el código en funciones permite crear piezas de código que hacen una determinada tarea y volver al área del código desde la que han sido llamadas.

Las funciones se declaran asociadas a un tipo de valor. Este valor será el que devolverá la función, por ejemplo ‘int’ se utilizará cuando la función devuelva un dato numérico de tipo entero. Si la función no devuelve ningún valor entonces se colocará delante la palabra “void”, que significa “función vacía”

Sintaxis:

tipo nombreFunción (parámetros) {

  instrucciones;

}

Para llamar a una función, simplemente:

nombreFunción(parámetros);

En una función que devuelve un valor siempre debe tener la instrucción Return, este termina una función y devuelve un valor a quien ha llamado a la función: http://arduino.cc/en/Reference/Return

Ventajas del uso de funciones:

  • Ayuda a tener organizado el código.
  • Una función codifica una tarea en un lugar de nuestro sketch, así que la función solo debe ser pensada una sola vez.
  • Reduce la probabilidad de errores al modificar el código.
  • Hacen que el tamaño del sketch sea menor porque el código de la función es reutilizado.
  • Facilita la lectura del código.
  • Hace más sencillo reutilizar código en otros sketches.

Más información: http://arduino.cc/en/Reference/FunctionDeclaration

Nombres de funciones

Generalmente los nombres de las funciones deben ser en minúscula, con las palabras separadas por un guión bajo, aplicándose éstos tanto como sea necesario para mejorar la legibilidad.

“mixedCase” (primera palabra en minúscula) es aceptado únicamente en contextos en donde éste es el estilo predominante con el objetivo de mantener la compatibilidad con versiones anteriores.

En el caso de las clases, los nombres deben utilizar la convención “CapWords” (palabras que comienzan con mayúsculas).

Las funciones en Arduino pueden estar dentro del mismo fichero .ino o en otro fichero con extensión .ino dentro del directorio del sketch.

Paso por Valor y Paso por Referencia

Hasta ahora siempre hemos declarado los parámetros de nuestras funciones del mismo modo. Sin embargo, éste no es el único modo que existe para pasar parámetros.

La forma en que hemos declarado y pasado los parámetros de las funciones hasta ahora es la que normalmente se conoce como “por valor”. Esto quiere decir que cuando el control pasa a la función, los valores de los parámetros en la llamada se copian a “objetos” locales de la función, estos “objetos” son de hecho los propios parámetros.

int funcion(int n, int m) { 
  n = n + 2; 
  m = m - 5; 
  return n+m; 
}

int a = 10;
int b = 20;
Serial.println(funcion(a,b));
Serial.println(funcion(10,20));

Empezamos haciendo a = 10 y b = 20, después llamamos a la función “funcion” con las objetos a y b como parámetros. Dentro de “funcion” esos parámetros se llaman n y m, y sus valores son modificados. Sin embargo al retornar al programa que lo llama, a y b conservan sus valores originales. Lo que pasamos no son los objetos a y b, sino que copiamos sus valores a los objetos n y m. Es lo mismo que hacer funcion(10,20), cuando llamamos a la función con parámetros constantes. Si los parámetros por valor no funcionasen así, no sería posible llamar a una función con valores constantes o literales.

Las referencias sirven para definir “alias” o nombres alternativos para un mismo objeto. Para ello se usa el operador de referencia (&).

Por ejemplo:

int a;
int &r = a;
a = 10;
Serial.println(r);

En este ejemplo los identificadores a y r se refieren al mismo objeto, cualquier cambio en una de ellos se produce en el otro, ya que son, de hecho, el mismo objeto. El compilador mantiene una tabla en la que se hace corresponder una dirección de memoria para cada identificador de objeto. A cada nuevo objeto declarado se le reserva un espacio de memoria y se almacena su dirección. En el caso de las referencias, se omite ese paso, y se asigna la dirección de otro objeto que ya existía previamente. De ese modo, podemos tener varios identificadores que hacen referencia al mismo objeto, pero sin usar punteros.

Si queremos que los cambios realizados en los parámetros dentro de la función se conserven al retornar de la llamada, deberemos pasarlos por referencia. Esto se hace declarando los parámetros de la función como referencias a objetos. Por ejemplo:

int funcion(int &n, int &m) {
   n = n + 2; 
   m = m - 5; 
   return n+m;
}

int a = 10;
int b = 20;

Serial.println(funcion(a,b));
Serial.println("a = " + String(a) + " b = " + String(b));
//es ilegal pasar constantes como parámetros cuando estos son referencias
Serial.println(funcion(10,20));

En este caso, los objetos “a” y “b” tendrán valores distintos después de llamar a la función. Cualquier cambio de valor que realicemos en los parámetros dentro de la función, se hará también en los objetos referenciadas. Esto quiere decir que no podremos llamar a la función con parámetros constantes, ya que aunque es posible definir referencias a constantes, en este ejemplo, la función tiene como parámetros referencias a objetos variables. Y si bien es posible hacer un casting implícito de un objeto variable a uno constante, no es posible hacerlo en el sentido inverso. Un objeto constante no puede tratarse como objeto variable.

Una const reference es una referencia a que no permite cambiar la variable a través de esa referencia. Por ejemplo const int &r = a; en r tengo el valor de a pero no puedo cambiar el valor de a usando r.

No confundir este concepto con el modificador de variable static, que es utilizado para crear variables que solo son visibles dentro de una función, sin embargo, al contrario que las variables locales que se crean y destruyen cada vez que se llama a la función, las variables estáticas mantienen sus valores entre las llamadas a las funciones.

Más información:

Sobrecarga de Funciones

Hay operadores que tienen varios usos, como por ejemplo *, &, << o >>. Esto es lo que se conoce en C++ como sobrecarga de operadores. Con las funciones existe un mecanismo análogo, de hecho, en C++, los operadores no son sino un tipo especial de funciones, aunque eso sí, algo peculiares.

Así que en C++ podemos definir varias funciones con el mismo nombre, con la única condición de que el número y/o el tipo de los argumentos sean distintos. El compilador decide cuál de las versiones de la función usará después de analizar el número y el tipo de los parámetros. Si ninguna de las funciones se adapta a los parámetros indicados, se aplicarán las reglas implícitas de conversión de tipos.

Las ventajas son más evidentes cuando debemos hacer las mismas operaciones con objetos de diferentes tipos o con distinto número de objetos. También pueden usarse macros para esto, pero no siempre es posible usarlas, y además las macros tienen la desventaja de que se expanden siempre, y son difíciles de diseñar para funciones complejas. Sin embargo las funciones serán ejecutadas mediante llamadas, y por lo tanto sólo habrá una copia de cada una.

Ejemplo:

int mayor(int a, int b);
char mayor(char a, char b); 
float mayor(float a, float b);
int mayor(int a, int b, int c, int d);

int mayor(int a, int b) { 
   if(a > b) return a; else return b; 
}
 
char mayor(char a, char b) { 
   if(a > b) return a; else return b; 
}
 
float mayor(float a, float b) { 
   if(a > b) return a; else return b; 
}

int mayor(int a, int b, int c, int d) { 
   return mayor(mayor(a, b), mayor(c, d)); 
}

Las llamadas a funciones sobrecargadas se resuelven en la fase de compilación. Es el compilador el que decide qué versión de la función debe ser invocada, después de analizar, y en ciertos casos, tratar los argumentos pasados en la llamadas. A este proceso se le llama resolución de sobrecarga.

Tener en cuenta que el tipo de retorno de la función no se considera en la sobrecarga de funciones. Consideremos el caso en el que desea escribir una función que devuelve un número aleatorio, pero se necesita una versión que devolverá un entero, y otra versión que devolverá un doble.

int getRandomValue();
double getRandomValue();

Pero el compilador toma esto como un error. Estas dos funciones tienen los mismos parámetros (ninguno) y en consecuencia, la segunda getRandomValue () serán tratada como una redeclaración errónea de la primera. En consecuencia, tendrán que ser dado diferentes nombres a estas funciones.

Más información:

Sobrecarga de operadores:

Ámbito de las variables

Una variable puede ser declarada al inicio del programa antes de la parte de configuración setup(), a nivel local dentro de las funciones, y, a veces, dentro de un bloque, como para los bucles del tipo if.. for.., etc. En función del lugar de declaración de la variable así se determinará el ámbito de aplicación, o la capacidad de ciertas partes de un programa para hacer uso de ella.

Una variable global es aquella que puede ser vista y utilizada por cualquier función y estamento de un programa. Esta variable se declara al comienzo del programa, antes de setup().

Recordad que al declarar una variable global, está un espacio en memoria permanente en la zona de static data y el abuso de variables globales supone un uso ineficiente de la memoria.

Una variable local es aquella que se define dentro de una función o como parte de un bucle. Sólo es visible y sólo puede utilizarse dentro de la función en la que se declaró. Por lo tanto, es posible tener dos o más variables del mismo nombre en diferentes partes del mismo programa que pueden contener valores diferentes, pero no es una práctica aconsejable porque complica la lectura de código.

En el reference de Arduino hay una muy buena explicación del ámbito de las variables:http://arduino.cc/en/Reference/Scope

La variables estáticas solo se crean e inicializan la primera vez que la función es llamada. Ver ejemplo en: http://arduino.cc/en/Reference/Static

Más información:

Inline

Cuando usamos el nombre de una función, indicando valores para sus argumentos, dentro de un programa, decimos que llamamos o invocamos a esa función. Esto quiere decir que el procesador guarda la dirección actual, “salta” a la dirección donde comienza el código de la función, la ejecuta, recupera la dirección guardada previamente, y retorna al punto desde el que fue llamada.

Esto es cierto para las funciones que hemos usado hasta ahora, pero hay un tipo especial de funciones que trabajan de otro modo. En lugar de existir una única copia de la función dentro del código, si se declara una función como inline, lo que se hace es insertar el código de la función, en el lugar (y cada vez) que sea llamada. Esta indica al compilador que cada llamada a la función inline deberá ser reemplazado por el cuerpo de esta función. En la práctica la función inline es utilizado sólo cuando las funciones son pequeñas para evitar generar un ejecutable de tamaño considerable.

La palabra reservada inline tiene la ventaja de acelerar un programa si éste invoca regularmente a la función inline. Permite reducir considerablemente el código, en particular para los accesadores de una clase. Un accesador de clase es típicamente una función de una línea.

Ejemplo:

inline int mayor(int a, int b) {
  if(a > b) return a; 
  return b; 
}

Más información de inline:

Prototipos de Funciones

Primero recordar que en el lenguaje de Arduino al contrario que en estandar C, no es necesario declarar los prototipos de las funciones, puesto que de eso se encarga el de incluirlo el arduino builder, al igual que de añadir el main..

En C++ es obligatorio usar prototipos. Un prototipo es una declaración de una función. Consiste en una presentación de la función, exactamente con la misma estructura que la definición, pero sin cuerpo y terminada con un “;”.

En general, el prototipo de una función se compone de las siguientes secciones:

  • Opcionalmente, una palabra que especifique el tipo de almacenamiento, puede ser extern o static. Si no se especifica ninguna, por defecto será extern.
  • El tipo del valor de retorno, que puede ser void, si no necesitamos valor de retorno.
  • Modificadores opcionales.
  • El identificador de la función. Es costumbre, muy útil y muy recomendable, poner nombres que indiquen, lo más claramente posible, qué es lo que hace la función, y que permitan interpretar qué hace el programa con sólo leerlos.
  • Una lista de declaraciones de parámetros entre paréntesis. Los parámetros de una función son los valores de entrada (y en ocasiones también de salida).

Un prototipo sirve para indicar al compilador los tipos de retorno y los de los parámetros de una función, de modo que compruebe si son del tipo correcto cada vez que se use esta función dentro del programa, o para hacer las conversiones de tipo cuando sea necesario.

Normalmente, los prototipos de las funciones se declaran dentro del fichero del programa, o bien se incluyen desde un fichero externo, llamado fichero de cabecera, (para esto se usa la directiva #include).

Ya lo hemos dicho más arriba, pero las funciones son extern por defecto. Esto quiere decir que son accesibles desde cualquier punto del programa, aunque se encuentren en otros ficheros fuente del mismo programa. En contraposición las funciones declaradas static sólo son accesibles dentro del fichero fuente donde se definen.

Más información:

Bibliotecas/Librerías en C++

Junto con los compiladores de C y C++, se incluyen ciertos archivos llamados bibliotecas más comúnmente librerías. Las bibliotecas contienen el código objeto de muchos programas que permiten hacer cosas comunes, como leer el teclado, escribir en la pantalla, manejar números, realizar funciones matemáticas, etc.

Las bibliotecas están clasificadas por el tipo de trabajos que hacen, hay bibliotecas de entrada y salida, matemáticas, de manejo de memoria, de manejo de textos y como imaginarás existen muchísimas librerías disponibles y todas con una función específica.

La declaración de librerías, tanto en C como en C++, se debe hacer al principio de todo nuestro código, antes de la declaración de cualquier función o línea de código, debemos indicarle al compilador que librerías usar, para el saber qué términos están correctos en la escritura de nuestro código y cuáles no. La sintaxis es la siguiente: #include <nombre de la librería> o alternativamente #include “nombre de la librería”. En tu código puedes declarar todas las librerías que quieras aunque en realidad no tienen sentido declarar una librería que no vas a usar en tu programa, sin embargo no existe límite para esto.

La directiva de preprocesador #include se usa en los lenguajes C y C++ para “incluir” las declaraciones de otro fichero en la compilación. Esta directiva no tiene más misterio para proyectos pequeños. En cambio, puede ayudar aprovechar bien esta directiva en proyectos con un gran número de subdirectorios.

Ejemplo:

#include “iostream”
#include “string”
#include <math.h>
using namespace std;

Lo único adicional, es la línea que dice using namespace std; esta línea nos ayuda a declarar un espacio de nombre que evita tener que usarlo cada que accedemos a alguna función específica de una librería. Teniendo este namespace declarado podemos llamar por ejemplo el comando cout >>, que pertenece a la librería iostream, sin embargo sin este namespace sería std::cout >>, imagina tener que hacer esto cada vez que uses algún comando o función de las librerías, sería bastante tedioso.

A continuación pondré algunas de las librerías de uso más común de C++ y que forman parte de las librerías estándar de este lenguaje.

  • fstream: Flujos hacia/desde ficheros. Permite la manipulación de archivos desde el programar, tanto leer como escribir en ellos.
  • iosfwd: Contiene declaraciones adelantadas de todas las plantillas de flujos y sus typedefs estándar. Por ejemplo ostream.
  • iostream: Parte del a STL que contiene los algoritmos estándar, es quizá la más usada e importante (aunque no indispensable).
  • math: Contiene los prototipos de las funciones y otras definiciones para el uso y manipulación de funciones matemáticas.
  • memory: Utilidades relativas a la gestión de memoria, incluyendo asignadores y punteros inteligentes (auto_ptr). “auto_ptr” es una clase que conforma la librería memory y permite un fácil manejo de punteros y su destrucción automáticamente.
  • ostream: Algoritmos estándar para los flujos de salida.
  • Librería stdio: Contiene los prototipos de las funciones, macros, y tipos para manipular datos de entrada y salida.
  • Librería stdlib: Contiene los prototipos de las funciones, macros, y tipos para utilidades de uso general.
  • string: Parte de la STL relativa a contenedores tipo string; una generalización de las cadenas alfanuméricas para albergar cadenas de objetos.
  • vector: Parte de la STL relativa a los contenedores tipo vector; una generalización de las matrices unidimensionales C/C++
  • list: Permite implementar listas doblemente enlazadas (listas enlazadas dobles) fácilmente.
  • iterator: Proporciona un conjunto de clases para iterar elementos.
  • regex: Proporciona fácil acceso al uso de expresiones regulares para la comparación de patrones.
  • thread: Útil para trabajar programación multihilos y crear múltiples hilos en nuestra aplicación.

Más información:

Ejercicios Funciones

Ver ejemplo sencillo de uso de funciones: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio02-Funciones

Comparar las funciones con librerías: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio03-Librerias

Ejercicio: Menú interactivo con Arduino. Con todo lo visto de Strings, operadores, estructuras de control y funciones, hacer un ejemplo de un menú interactivo donde se dan varias opciones y pulsando cada una de ellas se ejecuta una acción concreta. Si el valor pulsado no es ninguna de las opciones avisar y volver a mostrar el menú hasta que se pulse una opción correcta.

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio46-Estructuras_de_Control

Librerías vs Funciones en Arduino

Como se ha visto anteriormente, las librerías son trozos de código hechas 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 luego de entender. Más adelante veremos cómo hacer una librería.

Las librerías en Arduino 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).

Más información: https://aprendiendoarduino.wordpress.com/2016/06/27/librerias-3/

Una librería a diferencia de las funciones debe estar al menos en un fichero diferente con extensión .h y opcionalmente en otro .cpp y además debe ser llamada con #include desde el sketch de arduino y estar en una ruta accesible desde el IDE de Arduino, ya sea el mismo directorio del sketch o en algunas de las rutas configuradas para librerías.

La ventaja de usar librerías frente a las funciones es que no es necesario incluir el código cada vez que se va a reutilizar sino que con tener la librería instalada en el IDE y llamarla mediante #include ya la puedo usar en mi código.

Al llamar a una librería desde un sketch, la librería completa es cargada a la placa de Arduino incrementando el tamaño del espacio usado en el microcontrolador, tanto en la memoria flash como en la RAM.

NOTA: Como instalar una librería de github: http://scidle.com/install-github-libraries-on-arduino-ide/ habla de quitar caracteres no ASCII, pero comprueba también que no funcione porque haya subdirectorios.

Las librerías que usamos para los sketches tienen una versión, que se suelen actualizar con frecuencia. También tenemos un control de versiones en el nuevo IDE a partir de 1.6.4 que nos facilita la gestión de la versión de las librerías usadas. Este aspecto es importante porque un sketch que funciona con una versión de una librería es posible que al compilarlo con otra versión en otro IDE no funcione. Por ello es importante documentar con que versión de librería está hecho o distribuir el sketch con la librería con la que se ha creado. Generalmente las librerías tienen compatibilidad hacia atrás, pero puede que no ocurra o que el comportamiento de la librería sea diferente.

Al cambiar el IDE también nos podemos encontrar que nuestro sketch no es compatible con la versión de la librería que estemos usando, que es diferente con la que se diseñó originalmente el sketch.

Listado de librerías: http://playground.arduino.cc/Main/LibraryList

Ejercicio Librerías vs Funciones

Ya hemos visto lo que son las librerías y las funciones definidas por usuario, el uso de unas u otras dependerá de varios factores como la posibilidad de reutilización del código o la experiencia del programador con el uso de librerías.

Ejemplos simples de librerías:

Programación Arduino

El lenguaje de programación de Arduino es C++. No es un C++ puro sino que es una adaptación que proveniente de avr-libc que provee de una librería de C de alta calidad para usar con GCC (compilador de C y C++) en los microcontroladores AVR de Atmel y muchas utilidades opensource específicas para las MCU AVR de Atmel como avrdude: https://learn.sparkfun.com/tutorials/pocket-avr-programmer-hookup-guide/using-avrdude

Las herramientas necesarias para programar los microcontroladores AVR de Atmel son avr-binutils, avr-gcc y avr-libc y ya están incluidas en el IDE de Arduino, pero cuando compilamos y cargamos un sketch estamos usando estas herramientas.

Aunque se hable de que hay un lenguaje propio de programación de Arduino, no es cierto, la programación se hace en C++ pero Arduino ofrece unas librerías, también llamado core, que facilitan la programación de los pines de entrada y salida y de los puertos de comunicación, así como otras librerías para operaciones específicas. El propio IDE ya incluye estas librerías de forma automática y no es necesario declararlas expresamente. Otra diferencia frente a C++ standard es la estructuctura del programa ya que no usa la función main(), sino que usa las funciones setup() y loop().

En el 99% de los casos se puede hacer un proyecto de Arduino de cierta complejidad con la librería que nos ofrece el core de Arduino y no es necesario añadir más instrucciones ni tipos de datos que los que hay en el core. Toda la información para programar con el core de Arduino se encuentra en el reference de la web de Arduino: https://www.arduino.cc/en/Reference/HomePage

Una buena chuleta para tener a mano cuando programemos. Cheat Sheet: https://dlnmh9ip6v2uc.cloudfront.net/learn/materials/8/Arduino_Cheat_Sheet.pdf

Lenguaje de programación C++

Es posible usar comandos estándar de C++ en la programación de Arduino siempre que estén incluidos en el avr libc:

En Internet hay muchas webs de referencia donde consultar dudas a la hora de programar en C++:

Un manual sencillo de entender para la programación es el “arduino programming notebook” de brian w. Evans. Puedes consultarlo o descargarlo desde:

Cuando compilamos y cargamos el programa en Arduino esto es lo que ocurre:

Variables

Una variable puede ser declarada al inicio del programa antes de la parte de configuración setup(), a nivel local dentro de las funciones, y, a veces, dentro de un bloque, como para los bucles del tipo if.. for.., etc. En función del lugar de declaración de la variable así se determinará el ámbito de aplicación, o la capacidad de ciertas partes de un programa para hacer uso de ella.

Una variable global es aquella que puede ser vista y utilizada por cualquier función y estamento de un programa. Esta variable se declara al comienzo del programa, antes de setup().

Recordad que al declarar una variable global, está un espacio en memoria permanente en la zona de static data y el abuso de variables globales supone un uso ineficiente de la memoria.

Una variable local es aquella que se define dentro de una función o como parte de un bucle. Sólo es visible y sólo puede utilizarse dentro de la función en la que se declaró. Por lo tanto, es posible tener dos o más variables del mismo nombre en diferentes partes del mismo programa que pueden contener valores diferentes, pero no es una práctica aconsejable porque complica la lectura de código.

En el reference de Arduino hay una muy buena explicación del ámbito de las variables: http://arduino.cc/en/Reference/Scope

El modificador de variable static, es utilizado para crear variables que solo son visibles dentro de una función, sin embargo, al contrario que las variables locales que se crean y destruyen cada vez que se llama a la función, las variables estáticas mantienen sus valores entre las llamadas a las funciones.

Los tipos de variables en Arduino son:

Además de usar este tipo de datos que son los que aparecen en el reference de Arduino (https://www.arduino.cc/en/Reference/HomePage), es posible usar cualquier tipo de variable de C++ estándar con las limitaciones propias de cada micorcontrolador.

Tipos de variables estándar en C++:

Más información sobre los tipos de variable Arduino en: https://aprendiendoarduino.wordpress.com/2016/06/29/tipos-de-datos-2/

Arrays

Un array es un conjunto de valores a los que se accede con un número índice. Cualquier valor puede ser recogido haciendo uso del nombre de la matriz y el número del índice. El primer valor de la matriz es el que está indicado con el índice 0, es decir el primer valor del conjunto es el de la posición 0. Un array tiene que ser declarado y opcionalmente asignados valores a cada posición antes de ser utilizado.

Para manejar arrays en C++ dispones de las funciones estándar: http://www.cplusplus.com/reference/array/array/

string (char array)

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 de chars.

Reference de Arduino para string: https://www.arduino.cc/en/Reference/String

Para manejara strings (char array) disponemos de las funciones de string.h que define diversas funciones para manipular strings y arrays http://www.cplusplus.com/reference/cstring/

También es posible usar la clase string de C++: http://www.cplusplus.com/reference/string/string/

Más información para aclarar la diferencia entre string y la clase string: https://www.tutorialspoint.com/cplusplus/cpp_strings.htm

String (Objeto)

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.

Toda la información de la clase String en el reference de Arduino https://www.arduino.cc/en/Reference/StringObject

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.

Operadores

El core de Arduino ofrece una serie de operadores según su reference:

Pero además es posible usar los operadores estnándar de C /C++ y más información: http://es.wikipedia.org/wiki/Anexo:Operadores_de_C_y_C%2B%2B

Estructuras de control

Las estructuras de control en Arduino según el reference son:

Funciones definidas por usuario

Una función es un bloque de código que tiene un nombre y un conjunto de instrucciones que son ejecutadas cuando se llama a la función. Son funciones setup() y loop() de las que ya se ha hablado.

Las funciones de usuario pueden ser escritas para realizar tareas repetitivas y para reducir el tamaño de un programa. Segmentar el código en funciones permite crear piezas de código que hacen una determinada tarea y volver al área del código desde la que han sido llamadas.

Más información sobre las funciones en C++: http://www.cplusplus.com/doc/tutorial/functions/

Ejercicio: http://jecrespo.github.io/PrimerosPasosArduino/

Programación Arduino

El lenguaje de programación de Arduino es C++. No es un C++ puro sino que es una adaptación que proveniente de avr-libc que provee de una librería de C de alta calidad para usar con GCC (compilador de C y C++) en los microcontroladores AVR de Atmel y muchas utilidades específicas para las MCU AVR de Atmel como avrdude: https://learn.sparkfun.com/tutorials/pocket-avr-programmer-hookup-guide/using-avrdude

Las herramientas necesarias para programar los microcontroladores AVR de Atmel son avr-binutils, avr-gcc y avr-libc y ya están incluidas en el IDE de Arduino, pero cuando compilamos y cargamos un sketch estamos usando estas herramientas.

Aunque se hable de que hay un lenguaje propio de programación de Arduino, no es cierto, la programación se hace en C++ pero Arduino ofrece unas librerías o core que facilitan la programación de los pines de entrada y salida y de los puertos de comunicación, así como otras librerías para operaciones específicas. El propio IDE ya incluye estas librerías de forma automática y no es necesario declararlas expresamente. Otra diferencia frente a C++ standard es la estructuctura del programa que ya hemos visto anteriormente.

Toda la información para programar Arduino se encuentra en el reference de la web de Arduino: https://www.arduino.cc/en/Reference/HomePage

Lenguaje de programación C++

Es posible usar comandos estándar de C++ en la programación de Arduino siempre que estén incluidos en el avr libc:

Características de C:

  • Es el lenguaje de programación de propósito general asociado al sistema operativo UNIX.
  • Es un lenguaje de medio nivel. Trata con objetos básicos como caracteres, números, etc… también con bits y direcciones de memoria.
  • Posee una gran portabilidad
  • Se utiliza para la programación de sistemas: construcción de intérpretes, compiladores, editores de texto, etc

Un buen libro de referencia para C:

Y un libro que se puede descargar gratuitamente Essential C: http://cslibrary.stanford.edu/101/EssentialC.pdf

Una muy buena plataforma online para aprender C: http://c.learncodethehardway.org/book/

Por supuesto en Internet hay muchas webs de referencia donde consultar dudas a la hora de programar en C++:

C++ es un lenguaje de programación diseñado a mediados de los años 1980 por Bjarne Stroustrup. La intención de su creación fue el extender al exitoso lenguaje de programación C con mecanismos que permitan la manipulación de objetos. En ese sentido, desde el punto de vista de los lenguajes orientados a objetos, el C++ es un lenguaje híbrido.

Posteriormente se añadieron facilidades de programación genérica, que se sumó a los otros dos paradigmas que ya estaban admitidos (programación estructurada y la programación orientada a objetos). Por esto se suele decir que el C++ es un lenguaje de programación multiparadigma. Actualmente existe un estándar, denominado ISO C++.

C# es un lenguaje propietario de Microsoft que mezcla las características básicas de C++ (no las avanzadas) simplificandolas al estilo Java y ofreciendo un framework. C# forma parte de la plataforma .NET

Elementos básicos en la programación de Arduino

Un manual sencillo de entender para la programación es el “arduino programming notebook” de brian w. Evans. Puedes consultarlo o descargarlo desde:

Cuando compilamos y cargamos el programa en Arduino esto es lo que ocurre:

{} entre llaves

Las llaves sirven para definir el principio y el final de un bloque de instrucciones. Se utilizan para los bloques de programación setup(), loop(), if.., etc.

Una llave de apertura “{“ siempre debe ir seguida de una llave de cierre “}”, si no es así el compilador dará errores. El entorno de programación de Arduino incluye una herramienta de gran utilidad para comprobar el total de llaves. Sólo tienes que hacer click en el punto de inserción de una llave abierta e inmediatamente se marca el correspondiente cierre de ese bloque (llave cerrada).

; punto y coma

El punto y coma “;” se utiliza para separar instrucciones en el lenguaje de programación C. También se utiliza para separar elementos en una instrucción de tipo “bucle for”.

Nota: Si olvidáis poner fin a una línea con un punto y coma se producirá en un error de compilación.

/*… */ bloque de comentarios

Los bloques de comentarios, o comentarios multi-línea son áreas de texto ignorados por el programa que se utilizan para las descripciones del código o comentarios que ayudan a comprender el programa. Comienzan con / * y terminan con * / y pueden abarcar varias líneas.

Debido a que los comentarios son ignorados por el compilador y no ocupan espacio en la memoria de Arduino pueden ser utilizados con generosidad.

// línea de comentarios

Una línea de comentario empieza con / / y terminan con la siguiente línea de código. Al igual que los comentarios de bloque, los de línea son ignoradas por el compilador y no ocupan espacio en la memoria. Una línea de comentario se utiliza a menudo después de una instrucción, para proporcionar más información acerca de lo que hace esta o para recordarla más adelante.

A la hora de programar Arduino, es fundamental usar la referencia que disponemos online en http://arduino.cc/en/Reference/HomePage o en la ayuda del IDE de Arduino. Cualquier duda sobre un comando, función, etc… debemos consultar en la referencia de Arduino.

Existe una guía de estilo para escribir código claro de Arduino y que sea fácil de entender. No es obligatorio, pero es una recomendación:

  • Documentar al máximo
  • Usar esquemas
  • Predominar la facilidad de lectura sobre la eficiencia del código
  • Poner el setup() y loop() al principio del programa
  • Usar variables descriptivas
  • Explicar el código al principio
  • Usar identación

Guia de estilo: http://arduino.cc/en/Reference/StyleGuide

También disponemos de varias cheat sheets o chuletas para cuando se empieza a programar:

Una buena guía de estilo de C++: http://informatica.uv.es/iiguia/AED/laboratorio/Estilocpp.pdf

Para documentar nuestros sketchs:

Proyectos

ArduinoCommunityLogo_EPS

Vamos a experimentar con Arduino y comprobar su funcionamiento. El procedimiento a seguir en cada práctica es:

  • Entender que vamos a hacer y leer la documentación de las instrucciones que se van a usar.
  • Leer el código del sketch y tratar de entender lo que hace. No es necesario entender todo al 100% pero tenemos que intentar comprender lo que hace el programa según el enunciado.
  • Copiar el sketch y cargarlo en Arduino
  • Ejecutar el sketch y abrir el monitor serie. Comprobar cómo funciona.
  • Hacer pequeños cambios en el sketch para cambiar el comportamiento y comprobar si cambia el comportamiento según lo esperado.

Leer una entrada digital

Leer el estado de la entrada digital 2 (pin 2) y mandar por el puerto serie su estado.

Explicación: http://www.arduino.cc/en/Tutorial/DigitalReadSerial

Instrucciones usadas:

Código: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio04-DigitalReadSerial

Tabla ASCII

Escribir por el puerto serie la tabla ASCII con el carácter y su valor en decimal, hexadecimal, octal y binario.

Explicación: http://www.arduino.cc/en/Tutorial/ASCIITable y tabla ascii: http://www.asciitable.com/

Instrucciones usadas:

Código: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio06-ASCII

Regular la intensidad del built-in led

Regular la intensidad del led en función del valor que escribamos desde el monitor serie. Arduino va a leer los datos que recibe del puerto serie. A mayor valor más intensidad.

Instrucciones usadas:

Código ejemplo sencillo: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio07-dimmer

Instrucciones usadas:

Código mejorado: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio07b-dimmer_mejorado

Leer una entrada analógica

Leer de una entrada analógica y sacar por el puerto serie el valor leído y también el voltaje que está leyendo. Luego escribir en el built-in led el valor correspondiente a lo leído.

Explicación: http://www.arduino.cc/en/Tutorial/AnalogReadSerial

Instrucciones usadas:

Código: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio11-Analog

Blink sin delay

Hacer el programa blink pero sin usar la instrucción delay() ya que su uso supone que no es posible hacer otra cosa durante el tiempo que dura el delay.

Explicación y código: http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

Instrucciones usadas:

Otra opción usando librerías: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio19-BlinkSinDelay

Análisis de caracteres

Otro ejemplo más de lectura puerto serie: http://www.arduino.cc/en/Tutorial/CharacterAnalysis

Memoria Arduino

Medir el tiempo en milisegundos que tarda Arduino en leer de la memoria RAM un texto grande y luego escribirlo por el puerto serie y luego hacer lo mismo pero el texto está guardado en la memoria flash.

Al compilar fijarse en lo que ocupa la memoria dinámica.

Ver la macro F: https://www.arduino.cc/en/Reference/PROGMEM

Código: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Otros/velocidadMemoria

Más información sobre la memoria de Arduino en: https://aprendiendoarduino.wordpress.com/2015/03/29/memoria-flash-sram-y-eeprom/

EEPROM

Leer y borrar la EEPROM de nuestro Arduino.

Leer: https://www.arduino.cc/en/Reference/EEPROM

Código:

Más ejemplos de Arduino en los built-examples que están en el IDE y están documentados en: http://www.arduino.cc/en/Tutorial/BuiltInExamples

Uso de Arduino en I+D

Veamos un ejemplo de uso de Arduino en una empresa riojana para desarrollo de nuevos productos. Artículos aparecidos en prensa:

Si analizamos los dispositivos que se ven en la imagen:

Primer programa/sketch de Arduino

En lugar del clásico “hola mundo” que es el primer programa cuando se aprende un lenguaje de programación, en Arduino el equivalente es el sketch blink.

Nuestro primer programa será hacer parpadear el led integrado que lleva Arduino (built-in led).

Este es el esquema a usar, pero el led marcado como L está también conectado al pin 13 (en el caso del Arduino UNO) y no es necesario poner un led adicional.:

Pasos a seguir:

  • Abrir la aplicación Arduino
  • Abrir el ejemplo blink

blink

  • Seleccionar la placa y el puerto adecuado

blink2

  • Cargar el programa pulsando el botón “subir”. El programa se compilará y luego se verá parpadeando los leds Tx y Rx de Arduino, indicando que se está cargando el fichero binario (.hex) en la flash del Arduino. Cuando aparezca el mensaje “subido” habremos acabado.
  • Unos segundos después veremos el LED parpadeando.

Una explicación completa del proyecto y enlaces a las funciones usadas está en: http://arduino.cc/en/Tutorial/Blink

Constantes definidas en el IDE Arduino: https://www.arduino.cc/en/Reference/Constants

Demos un paso más y modifiquemos el sketch para que parpadee más rápido y más lento. Modificar el valor de delay y volver a compilar y subir a Arduino.

Vamos a ampliarlo y modificar el programa para que cada vez que encienda y apague saque por el puerto serie la cadena “encendido”, “apagado” cuando corresponda. Luego guardarlo en nuestro entorno de trabajo.

Será necesario usar la librería Serial: http://arduino.cc/en/Reference/Serial

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/blob/master/Ejercicio01-Blink/Ejercicio01-Blink.ino

El IDE trae muchos ejemplos que podemos ver y probar: https://www.arduino.cc/en/Tutorial/BuiltInExamples