Kitabı oku: «Taller de Arduino. Experimentando con Arduino MKR 1010», sayfa 4
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 <nombre de la librería> o alternativamente #include “nombre de la librería”. En su código puede declarar todas las librerías que quiera, aunque en realidad no tienen sentido declarar una librería que no va a usar en su programa; sin embargo, no existe límite para ello. 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.
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 <LiquidCrystal.h>
El nombre de la librería está delimitado por paréntesis angulares y, además, la línea no termina con un punto y coma (;) como es usual. Se dará cuenta de que todas las librerías que forman parte del core de Arduino llevan la h.
En la figura 2.7 se pueden observar dos librerías estándar que vienen incluidas “de serie” con el IDE de Arduino.
Finalmente, denominamos librerías añadidas o contributivas (figura 2.7) a aquellas aportadas por algunos usuarios o aficionados altruistas o por empresas que han desarrollado hardware y que proporcionan estas librerías para compatibilizarlo con Arduino. Aunque hoy en día, la empresa Arduino está ofreciendo poco a poco y con cada nueva versión de su IDE, nuevas librerías del hardware que va apareciendo en el mercado. De hecho, no hay más que observar el propio buscador e instalador (figura 2.8) que trae integrado por defecto el IDE, para examinar los cientos de posibles librerías que se pueden instalar y utilizar en sus proyectos.
CAPÍTULO 3
Transmisión en serie Pines analógicos y digitales
3.1 Monitor serie
La placa Arduino MKR puede establecer comunicación con el ordenador a través de una conexión por un cable USB para recibir los programas y que queden grabados en el microcontrolador. Solo es necesario indicar el número de puerto USB (figura 3.1) donde está conectado el MKR y, desde el IDE visible en el PC, presionar el botón de enviar o subir.
Además, dentro de la interfaz IDE dispone de la opción monitor serie que posibilita la visualización de datos procedentes de Arduino. Es decir, que la comunicación serie es en los dos sentidos. Puede, por tanto, monitorizar lo que pudiera enviar Arduino y verlo en una ventana del IDE. Si su placa está captando datos de temperatura o de humedad o de lo que sea del entorno ambiental, estos datos se pueden observar en tiempo real en el PC. Pero ojo, no puede guardarlos, solo ir visualizándolos a medida que los adquiere por Arduino.
Para ilustrar de forma práctica esta característica examinará un sketch que envíe desde Arduino un mensaje de texto. El contenido del mismo lo verá en la ventana del monitor serie del IDE.
En el programa mostrado en la figura 3.2 se observan dos líneas nuevas:
Serial.begin(9600);
Esta sentencia debe colocarse dentro de la estructura de configuración setup() y establece la velocidad en bits por segundo (baudios) entre la placa de Arduino y el PC. Debe ser igual en ambos dispositivos. La segunda sentencia envía al PC el texto (entre paréntesis y comillas) añadiendo un salto de línea.
Serial.println(“Esto del arduino es para frikis”);
Al estar dentro de la estructura loop() se ejecutará indefinidamente; de ahí que se escriba la dichosa frase un montón de veces dentro de la ventana del programa monitor (figura 3.3). Pero, además, podría utilizar el programa monitor para enviarle órdenes a Arduino a través de texto o códigos ASCII. Es decir, se puede gobernar Arduino desde el ordenador.
El campo que abre está lleno de posibilidades, tanto en la visualización de datos como la del propio control Arduino. Así que, a partir de aquí, se utilizará este recurso para la ejecución de proyectos.
3.2 Pines digitales
Ha llegado el momento de empezar a poner a trabajar su Arduino. Y lo va a hacer con los pines de la placa que sirven para manejar señales digitales. Las patillas digitales Arduino MKR 1010 están numeradas del 0 al 14, ambas incluidas. Sin embargo, como se observa en la figura 3.4, solo hay que utilizar los pines del 0 al 12, ya que los pines 13 y 14 están reservados para la comunicación serie entre el PC y el propio Arduino. De hecho, al lado de dichos números están serigrafiados en la placa, las etiquetas TX y RX, que son las abreviaturas de transmisión y recepción serie, respectivamente. El estudio de las patillas analógicas se pospone para más adelante. Por ahora nos vamos a centrar en lo más básico y sencillo: escribir y leer unos y ceros. No obstante, antes de empezar hay que conocer cómo funcionan esos dispositivos que normalmente están asociados a los pines digitales. Usará los diodos led como salidas para mostrar un uno o un cero (encendido/apagado). Los interruptores o pulsadores se utilizarán para proporcionar información digital de entrada a Arduino, lo que se abordará en el siguiente punto.
Una señal analógica (figura 3.5) es continua y puede tomar infinitos valores. Una señal digital es discontinua y solo puede tomar dos valores o estados: 0 y 1. Estos dos valores pueden ser impulsos eléctricos de baja y alta tensión, interruptores abiertos o cerrados, pulsadores, etc.