Taller de Arduino. Experimentando con Arduino MKR 1010

- -
- 100%
- +
En el caso de Arduino es todavía más simple. Utilizará un tipo de matriz de una sola dimensión. Por ejemplo, se podría declarar un array de las siguientes formas:
int minume[6];
//se define un array que tendrá 6 elementos de tipo entero (int).
int minume[] = {2, 4, 8, 3, 6};
//se define el array y se le rellena con valores numéricos.
char mensaje[6] = “hola”;
//ahora almacena caracteres individuales.
Los arrays son “zero indexed”, lo que significa que, al referirse a una matriz, el primer elemento de la matriz está en el índice 0. Por lo tanto:
minume[0] == 2, minume[1] == 4, y sucesivamente.
Esto también quiere decir que en una matriz con 10 elementos el índice 9 es el último elemento. Por lo tanto:
int myArray[10]={9,3,2,4,3,2,7,8,9,11};
// myArray[9] = 11.
// myArray[10] es inválido y contiene información aleatoria.
Las variables de tipo string (‘s’ minúscula) se representan como un tipo particular de arrays de caracteres (tipo char) que terminan con el carácter NULL. Por ejemplo:
char Str1[15];
// Declara un array de caracteres sin inicializarlo.
char Str2[8] = {‘a’, ‘r’, ‘d’, ‘u’, ‘i’, ‘n’, ‘o’};
// Declara un array de caracteres (con un carácter extra) y el compilador añadirá el carácter NULL requerido.
char Str3[8] = {‘a’, ‘r’, ‘d’, ‘u’, ‘i’, ‘n’, ‘o’, ‘\0’};
// Explicita el carácter NULL.
char Str4[ ] = “Arduino”;
// Inicializa con un string constante entre comillas dobles; el compilador medirá el tamaño del array para ajustar el string constante y carácter NULL para finalizar,
char Str5[8] = “Arduino”;
// Inicializa el array con un tamaño explícito y un string constante,
char Str6[15] = “Arduino”;
//Inicializar el array, dejando un espacio extra para un string más largo,
Generalmente, los string se finalizan con un carácter NULL (código ASCII: 0). Esto permite a funciones como Serial.print(), establecer dónde está el final del string. De otra forma, seguiría leyendo los siguientes bytes de la memoria que no forman parte del string. Los string siempre se definen entre comillas dobles (“Abc”) y los caracteres siempre se definen dentro de comillas simples (‘A’). A menudo es conveniente, al trabajar con grandes cantidades de texto, como proyectos con displays o LCD, configurar un array de string, es decir: una matriz de cadenas de textos.
El tipo String (‘S’ mayúscula) merece una explicación, ya que es un poco más que un tipo de variable. De hecho, es un objeto (en el sentido de la programación orientada a objetos). Los objetos cuentan con propiedades y funciones especiales. Propiedades y funciones son disponibles de forma nativa en el núcleo de Arduino y se pueden ver como una entidad preexistente, incluso si su IDE no contiene ninguna línea. Una vez más, el núcleo de Arduino proporciona funciones de gran alcance ya preparadas para utilizar directamente.
El tipo de datos String es diferente que el tipo de datos de cadena (nótese la mayúscula S para este tipo de datos en comparación con el anterior). Este tipo de datos es en realidad una construcción a partir del tipo de datos de cadena, pero se trata como un objeto o instancia en lugar de una sencilla matriz de caracteres. Lo que esto significa es que tiene una gran cantidad de funcionalidad integrada con el tipo de datos String. Se ha establecido con anterioridad la definición de las variables, pero los objetos tienen un concepto similar llamado: “construcción”. Para objetos String, se hace referencia a la construcción en términos similares a la declaración de una variable. Declarar un tipo String en el núcleo de Arduino incluye un constructor de objeto, que es un concepto de programación orientada a objetos que se pueden obviar, ya que el núcleo de Arduino lo hace por usted, de tal manera que es mucho más fácil.
Por ejemplo, suponga que tiene una secuencia de caracteres que se leen de un sensor en una variable de cadena denominada miDato. Además, suponga que necesita convertir todos los caracteres en letras mayúsculas. Con el tipo de datos de cadena string, tendría que escribir el código para hacer la conversión.
Si define miDato como un objeto String, entonces podría escribir la conversión simplemente como:
miDato = miDato.ToUpperCase ();
Y ya está. La razón por la que esto funciona es porque dentro del objeto String existe una función (también denominada método) que contiene el código para hacer la conversión por usted. Solo tiene que definir la variable como:
miDato = String (100);
Así define una cadena denominada miDato con espacio suficiente para 99 caracteres. Para utilizar una función integrada de este tipo, escriba el nombre de la variable seguida de un punto (llamado el operador de punto), seguida por la función que desea llamar.
Por ejemplo:
miDato = miDato.ToLowerCase ();
Dicha funcionalidad es común con los lenguajes de programación como C++, C # y Java (programación orientada a objetos). Aunque Arduino C no es exactamente un lenguaje de programación orientada a objetos, contiene algunas de las características. En la tabla 2.1 se muestran algunas de las funciones incorporadas que están disponibles cuando se utilizan objetos String (puede consultar la tabla completa en la web oficial de Arduino).
Existen más tipos de variables que irá conociendo a medida que avance en la programación de Arduino MKR. Si desea consultarlos ahora puede visitar la página: http://Arduino.cc/en/Reference/HomePage.
Las variables deberán tomar nombres descriptivos para hacer el código más legible. Nombres de variables pueden ser contactoSensor o pulsador. Sirven para ayudar al programador y a cualquier otra persona a “leer” el código y entender lo que representa la variable. Nombres de variables como var o valor facilitan muy poco que el código sea inteligible. Una variable puede ser cualquier nombre o palabra que no sea una palabra reservada en el entorno de Arduino.
FunciónDescripcióncompareTo(String two)Comprueba si dos cadenas son igualesconcat(String two)Combina dos cadenas en una nueva cadenaequals(String two)Realiza la comparación entre mayúsculas y minúsculas entre dos cadenasreplace(String one, String two)Reemplaza todas las apariciones de un carácter o de una subcadena por otratoLowerCase()Devuelve una copia de la cadena original con todos los caracteres en minúsculaslength()Devuelve la longitud en caracteres de la cadenaTrim()Devuelve una copia de la cadena original con todos los espacios en blanco, antes y después de la cadena, eliminadosTabla 2.1
2.2.3 Operadores aritméticos, lógicos y booleanos
Los operadores aritméticos que se incluyen en el entorno de programación de Arduino son: la suma, la resta, la multiplicación y la división. Estos devuelven la suma, diferencia, producto o cociente de dos operandos. Esto es lo que normalmente se denominan matemáticas de andar por casa.
y = y + 3; // Sume 3 a la variable y
x = x - 7; // Reste 7 a la variable x
i = j * 6; // Realice el producto de 6 y de la variable j
r = r / 5; // Divida la variable r entre 5
Los operadores de comparación de una variable se utilizan con frecuencia en las sentencias condicionales del tipo IF (las veremos más adelante) para comprobar si una condición es verdadera o falsa; es decir, para tomar decisiones en el programa. Los símbolos de los operadores de comparación se muestran a continuación.
x == y // x es igual a y
x != y // x no es igual a y
x < y // x es menor que y
x > y // x es mayor que y
x <= y // x es menor o igual que y
x >= y // x es mayor o igual que y
Los operadores booleanos son una forma de comparar dos expresiones y devolver un TRUE o FALSE dependiendo del operador. Existen tres operadores lógicos:
&& Operador AND
|| Operador OR
! Operador NOT
Desde nuestro punto de vista es más productivo memorizar los diferentes tipos de operadores que los diferentes y variados tipos de variables. La razón se asienta en la experiencia que afirma que los operadores constituyen la toma de decisiones fundamental en los programas y elegir un operador u otro puede determinar el funcionamiento correcto o no de su proyecto. En cambio, la elección de una variable u otra no tiene por qué ser determinante.
2.2.4 Estructuras de control: condicionales y ciclos
Son instrucciones que permiten tomar decisiones durante la ejecución del programa y hacer diversas repeticiones de acuerdo a unos parámetros, cuyos conceptos son de gran importancia a la hora de empezar a programar y que no dejo de repetir a mis alumnos, aunque mis palabras acostumbran a caer en saco roto, lo que se refleja en el fracaso de sus proyectos. Los cuatro jinetes del apocalipsis son los siguientes:
* If
* Switch/case
* For
* While
Estructuras condicionales. Sirven para tomar decisiones después de evaluar condiciones lógicas. Tenemos dos principales: If y Switch/case.
* If. Es una estructura (figura 2.2) simple que se utiliza para evaluar si una determinada condición se ha alcanzado, como, por ejemplo, determinar si un valor analógico es igual a un valor de referencia preestablecido y ejecutar una serie de operaciones que se escriben dentro de llaves si es cierta la condición. Si es falsa (la condición no se cumple), el programa salta y no ejecuta las operaciones que están dentro de las llaves. Un ejemplo de uso de la misma es el siguiente:


Se evalúa si la variable x es igual a 15. Si se cumple la condición se le suma el valor 30 y después le suma 1000. Entonces la variable x contendrá el valor 1045. Si no es igual, solo le suma 1000, ya que se salta las posibles operaciones que hubiera dentro de las llaves (en este caso, la suma del valor 30). Hay que tener cuenta el uso especial del símbolo ‘=’ dentro de if: x = 15 podría parecer que es válido, pero, sin embargo, no lo es; ya que esa expresión asigna el valor 15 a la variable x. Por eso, dentro de la estructura if, se utiliza x==15, que, en este caso, lo que hace el programa es comprobar si el valor de x es 15. Ambas cosas son distintas. Dentro de las estructuras if, cuando se pregunte por un valor se debe poner el signo doble de igual “==”. Una variedad muy utilizada y más completa de la estructura anterior es la denominada if/else que responde la idea “si esto no se cumple se hace esto otro”. Variando el ejemplo anterior se puede evaluar, en un nuevo ejemplo, si la variable x es igual a 15; si no es así, se le suma un valor de 1000 (opción “else”). Pero ahora, en el caso de que fuera igual a 15, se le suma el valor de 30 como en el sketch anterior. Sin embargo, no se le suma después el valor de 1000 (figura 2.3).


* Switch/case. Una estructura switch compara el valor de una variable con el valor especificado en las sentencias case. Cuando se encuentra una sentencia case cuyo valor coincide con dicha variable, el código de esa sentencia se ejecuta. La palabra clave break sale de la estructura switch y suele usarse al final de cada case. Sin una sentencia break, la sentencia switch continuaría ejecutando las siguientes expresiones hasta encontrar un break o hasta llegar al final de la sentencia switch. Volviendo al ejemplo anterior, se podría comparar el valor de la variable x con distintos valores y, en función de si es igual a alguno de ellos, ejecutar las operaciones o expresiones a partir de ese case o caso particular (figura 2.4).


En el programa se comprueba si x vale 15 (de ser así, le suma el valor de 30) o si vale 67 (en este caso multiplica por dos su valor). Si x posee cualquier otro valor distinto se le añade el valor 1000.
Estructuras de bucle o de ciclo. Sirven para ejecutar continuamente un conjunto de operaciones o sentencias hasta que se cumplan ciertas condiciones lógicas, aritméticas o booleanas. Las dos más importantes son: For y While.
* For. Esta estructura se usa para repetir un bloque de sentencias encerradas entre llaves un número determinado de veces. Cada vez que se ejecutan las instrucciones del bucle se vuelve a evaluar la condición y, si deja de cumplir, se sale de este bucle continuo. La estructura for tiene tres partes separadas por (;). Su formato es el siguiente:
for (inicialización; condición; expresión)
La inicialización de una variable local se produce una sola vez y la condición se comprueba cada vez que se termina la ejecución de las instrucciones dentro del bucle. Si la condición sigue cumpliéndose, las instrucciones del bucle se vuelven a ejecutar. Cuando la condición no se cumple, el bucle termina. A continuación, se muestra un sketch para clarificar su uso (figura 2.5):


El sketch (figura 2.5) hace parpadear veinte veces, y solo veinte, la patilla 13 de Arduino con un intervalo de medio segundo. En este caso, la variable de inicialización i se pone a cero. A continuación, se comprueba (condición) el valor de i en cada ejecución de todo lo que va entre llaves, si esta variable es menor que el valor 20. Si es así, sigue realizando el bucle, si no es así, se sale de la estructura for. Por último, apreciar que cada vez que se ejecuta un bucle la variable i se incrementa en uno (expresión).
* While. Una estructura de tipo (figura 2.6) es un bucle de ejecución continua mientras se cumpla la expresión colocada entre paréntesis en la cabecera del bucle. La variable de prueba tendrá que cambiar para salir del bucle. La situación podrá cambiar a expensas de una expresión dentro el código del bucle o también por el cambio de un valor en una entrada. Se presenta un ejemplo para aclarar su utilización. while (x < 200)


En este caso se evalúa si la variable x es menor que 200. Si es así, se suma 500 a otra variable llamada z y además se autoincrementa la variable x. Cuando esta variable sea igual a 200, se sale del bucle while. Existe una variedad de esta última estructura que es la llamada: do while. Funciona de la misma manera que el bucle while, con la salvedad de que la condición se prueba al final del bucle. El bucle siempre se ejecutará al menos una vez. A lo largo del libro se utilizará más de una vez.
2.3 Funciones
Una función es un conjunto de líneas de código que realizan una tarea específica. Las funciones pueden tomar parámetros que modifiquen su funcionamiento. Las funciones son utilizadas para descomponer grandes problemas en tareas simples, y para implementar operaciones que son comúnmente utilizadas durante un programa y, de esta manera, reducir la cantidad de código. Cuando se invoca una función se le pasa el control a la misma; una vez que esta finalizó con su tarea, el control se devuelve al punto desde el cual la función fue llamada. Una función puede llamarse múltiples veces e incluso llamarse a sí misma (función recurrente). Las funciones pueden recibir datos desde fuera al ser llamadas a través de los parámetros y puede entregar un resultado.
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”.
Para llamar a una función, simplemente:
nombredelafunció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. Si se define una función y no pone return el valor devuelto es cero. No da error de compilación. Generalmente, los nombres de las funciones deben ser en minúscula, con las palabras separadas por un guion bajo, aplicándose estos tanto como sea necesario para mejorar la legibilidad.
La forma en que se ha 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. Esto se observa claramente en el siguiente sketch:

Empezará haciendo a = 10 y b = 20, después llamará a la función “función_ pepe” con los objetos a y b como parámetros. Dentro de “función_pepe” 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 pasa no son los objetos a y b, sino que copia sus valores a los objetos n y m. Es lo mismo que hacer funcion(10,20), cuando llama 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.
Si quisiera que los cambios realizados en los parámetros dentro de la función se conservasen al retornar de la llamada, deberá pasarlos por referencia. Este punto es un poco más complejo y, como no lo necesita, de momento no se va a abordar. Es necesario recordar que su ruta de trabajo en este libro es: “Si no lo necesita, no lo aprenda”.
2.4 Librerías
En el mundo de Arduino, una librería es un trozo de código que incluye en su sketch y que proporciona funciones determinadas que, simplemente, llama cuando le interesa. Por ejemplo, si va a utilizar un determinado sensor, puede buscar si existe una librería asociada. En caso de que la encuentre, puede añadirla a su programa y utilizar las funciones que ofrece para manejar dicho sensor. No tiene que conocer como está hecha o programada, simplemente debe saber qué funciones le ofrece y cómo utilizarlas. Es lo mismo que saber conducir un coche, no está obligado a entender el funcionamiento del motor o cómo es capaz de moverse. Algunas librerías funcionan por sí mismas, es decir, incluyéndolas directamente en su programa; otras precisan de un hardware adicional para ser utilizadas. Normalmente, cuando adquiere una shield nueva, para, por ejemplo, controlar un servomotor, descarga la librería asociada que se ocupa de manejar este hardware, facilitándole mucho el trabajo, ya que se despreocupa de la comunicación entre dicha shield y Arduino.
Es importante señalar que no debe confundir el concepto de función con el de librería. De hecho, una librería es un conjunto de funciones con un objetivo específico. La declaración de librerías, tanto en C como en C++, se debe hacer al principio de todo su código; antes de la declaración de cualquier función o línea de código, debe indicarle al compilador qué librerías usar, para saber qué términos están correctos en la escritura de su código y cuáles no. La sintaxis es la siguiente: #include
Por otra parte, las librerías propias de Arduino se catalogan en tres tipos:
* Librería core.
* Librerías estándar.
* Librerías añadidas o contributivas.
La librería principal o core se proporciona con el IDE de Arduino y es fundamental para los usuarios principiantes y para los más experimentados. Oculta gran parte de la complejidad que tradicionalmente supone trabajar con un microcontrolador. Los miembros del equipo de desarrollo de Arduino que participaron en enseñar a los estudiantes a usar microcontroladores en sus proyectos, reconocieron que el poco uso de muchos microcontroladores tradicionales, por parte de la mayoría principiantes y neófitos en el campo de la electrónica, era la dificultad de su programación. Estudiaron cuáles eran la acciones o proyectos que muchos de sus estudiantes querían llevar a cabo con un microcontrolador y, basándose en esto, diseñaron una biblioteca central que evitara la complejidad de las tareas más engorrosas e hiciera la programación más fácil. La mayoría de los proyectos leen datos en los pines de entrada y escriben datos en los pines de salida. La librería core hace que estas tareas comunes sean sencillas de utilizar. Por ejemplo, para leer el valor de una patilla digital, solo tiene que utilizar la función digitalRead(). Lo mismo pasa con la función serial(), que permitía interactuar con el programa monitor sin excesiva dificultad.
Cuando haya descargado e instalado el IDE de Arduino observará que algunas librerías llamadas estándar se incluyeron con la instalación. Las bibliotecas estándar son las que el equipo de desarrollo Arduino pensó que eran necesarias por muchas personas en sus propios proyectos. Estas librerías no se incluyen por defecto, como la librería core, debido a que Arduino ha limitado incluirlas automáticamente, ya que sería una pérdida de recursos (sobre todo, en memoria del propio microcontrolador) dejando poco espacio para su propio código. Para utilizar las librerías estándar, tiene que incluirlas explícitamente en sus sketch. Para ello, es necesario agregar una sentencia #include en la parte superior del programa. Por ejemplo, si quiere incluir la librería LiquidCrystal, que se utiliza para mostrar datos sobre una pantalla LCD, hay que añadir lo siguiente al principio del sketch:
#include



