Entradas digitales II

Como nos gusta hacer, veamos el código del instructivo anterior y revisemos las instrucciones nuevas. Para ello copiaremos el último código que vimos para un fácil análisis:

/*
 * Curso de Arduino - Entradas digitales - sin pullup
 * ArduinoHobby.com
 */
 
#define PIN_DIGITAL 8

void setup() {
  Serial.begin(9600);
  pinMode(PIN_DIGITAL, INPUT_PULLUP);
}

int ultimo = 0;

void loop() {
  int nuevo = digitalRead(PIN_DIGITAL);
  
  if (nuevo != ultimo) {
    Serial.print("Leido: ");
    Serial.println((nuevo?"HIGH":"LOW"));
    
    ultimo = nuevo;
  }
}

Ahora veamos de arriba a abajo las novedades.

El objeto Serial

Serial es un objeto (ya veremos más adelante que significa) lo que es parecido a una función y nos brinda una lista de opciones para comunicarnos por el puerto Serie de nuestro Arduino. En otras palabras, es nuestro recurso para interactuar con la PC (u otros dispositivos) vía USB. Debido a que es un objeto y no una función estándar, las funciones tienen el formato del tipo: Serial.funcion() donde todas las funciones comienzan con el nombre del objeto seguido de un punto.

Para utilizar el puerto Serie debemos primero inicializarlo. La inicialización se realiza mediante el comando:

Serial.begin(9600);

Que le dice al objeto Serial que inicialice el puerto Serie a la velocidad indicada, en nuestro caso 9600 baudios. Esta velocidad debe coincidir con la velocidad de nuestro Monitor Serie para que podamos comunicarnos con nuestro Arduino como ya explicamos en la entrega anterior. La palabra “begin” en Inglés significa “comenzar”. Una vez inicializado el puerto Serie podemos comenzar a utilizarlo para enviar y recibir información.

Más abajo vemos dos métodos más (un método es similar a una función, pero le pertenece a un objeto como en el caso de Serial). Veamos el primero:

Serial.print("Leido: ");

El método Serial.print() que lleva como argumento o parámetro un texto (o dato) envía o imprime (print = imprimir en Inglés) el texto entre paréntesis por el puerto Serie. Cuando imprimimos por el puerto Serie es lo mismo que decir “enviamos por”. El texto o dato enviado se imprimirá en nuestro Monitor Serie (si lo tenemos abierto) a continuación de lo que ya haya escrito en el mismo.

Si deseamos imprimir algo en una línea nueva, deberemos indicarle específicamente el “salto de línea” al enviar el texto a imprimir. Veamos el último método que tenemos en nuestro Sketch:

Serial.println((nuevo?"HIGH":"LOW"));

El método Serial.println() es similar al método Serial.print() pero envía un salto de línea al final del texto. Es decir, además de imprimir el texto en el Monitor Serie, hará un salto de línea y el siguiente texto impreso saldrá en la línea inmediata inferior a la usada. Si bien lo que se encuentra dentro del método Serial.println() no es precisamente un texto, sí es un dato y veremos cómo se evalúa o comprende más abajo.

La función pinMode() a fondo

Ya vimos que la función pinMode() inicializa un pin para uso digital. Su función es además definir si el pin se usará para entrada o para salida. Ya explicamos que la función pinMode() acepta 2 parámetros, siendo el primero el pin que queremos inicializar y el segundo el modo. Respecto al modo, ya vimos antes la constante OUTPUT que se usa para indicar el modo salida, para salidas digitales.

En el instructivo anterior vimos que en el primer Sketch usamos el modo INPUT (en Inglés entrada) para inicializar un pin como entrada digital. Cuando hicimos eso y subimos el Sketch vimos en el Monitor Serie que el estado del pin era errático, cambiando de HIGH a LOW y viceversa. Se explicó que ésto es porque los pines quedan en un estado “indefinido”.

Luego para mantener el estado fijo cambiamos el modo de inicialización a INPUT_PULLUP. Luego de ello, el modo quedó fijo en HIGH. Y desde ahí nosotros conectamos el pulsador a GND para que cuando lo presionemos el estado cambie a LOW, y de lo contrario se mantuviera en HIGH.

Recapitulando, OUTPUT define el modo del pin como salida. INPUT define el modo del pin como entrada. INPUT_PULLUP define el modo del pin como entrada pero además fija su estado en reposo como HIGH.

Variables

La siguiente línea con código nuevo es la que dice:

int ultimo = 0;

La misma es una declaración e inicialización de una variable. Una variable es un contenedor en donde podemos guardar uno o más datos. La variable posee un nombre y se declara mediante la sintaxis: <tipo> <nombre>. En nuestro caso el tipo es “int” que equivale a un “entero” o número entero y el nombre es “ultimo”. Los tipos de datos numéricos poseen límites, es decir, mínimo y máximo valor que pueden alojar. Esto es así ya que las variables no tienen capacidad infinita. Más adelante veremos los distintos tipos de variables que podemos definir y usar.

Por ahora veamos el tipo de variable int que dijimos equivale a un entero. Este tipo de variable puede contener un número entero con valores del -32.768 al 32.767 (en un Arduino UNO o Mega 2560) que es más que suficiente para lo que necesitamos.

Lo que sigue en la instrucción se denomina inicialización. La inicialización es cuando luego de declarar una variable le damos un valor “inicial”. También se denomina inicialización si le damos otro valor luego. En ese caso se puede decir también que se reinicializó. La inicialización de una variable puede ocurrir en la misma línea o en una instrucción aparte. Veámoslo en un ejemplo:

// Inicializacion en una linea
int variable = 1; 

// Inicializacion en 2 lineas
int variable2; // Declaracion de la variable como int (entero)
variable2 = 1; // Inicializacion de la variable en 1

Por último cabe destacar que los nombres de variables deben cumplir las siguiente reglas:

  • Deben comenzar con una letra del alfabeto (sin ñ ni acentos) o con un guión bajo ‘ _ ‘
  • Luego de la primera letra los nombres pueden contener también números
  • Las letras mayúsculas son consideradas distintas a las minúsculas
  • Hay ciertas palabras reservadas que no pueden usarse como por ejemplo una variable no puede llamarse “switch” ni “for”

Leyendo el estado digital de un pin

Luego de inicializar un pin como entrada (INPUT), podemos leer su estado mediante la función digitalRead(). Como la función lee un estado, debe hacer algo con el valor leído. Por ello, esta función se dice que “retorna” (o devuelve) un resultado o valor. En este caso, digitalRead() devuelve HIGH o LOW. Estas constantes equivalen a los valores enteros 0 y 1. Por lo tanto, para poder guardar el valor leído por digitalRead() necesitaremos una variable que pueda almacenar un 0 y un 1. La línea de nuestro programa que usa digitalRead() dice:

int nuevo = digitalRead(PIN_DIGITAL);

Como vimos antes, estamos declarando una variable de tipo entero y de nombre “nuevo”. En la misma línea la inicializamos con el valor que retornará digitalRead() usado con la constante PIN_DIGITAL que ya sabemos apunta al pin 8 (ver definición de constante al comienzo del Sketch). Luego de ejecutar la línea anterior la variable “nuevo” existirá y contendrá el valor 1 (HIGH) o 0 (LOW) dependiendo del estado del pin 8.

Como vimos en la explicación sobre variables, la sentencia anterior se puede escribir en 2 líneas así:

int nuevo; // Declaracion de la variable "nuevo" de tipo entero
nuevo = digitalRead(PIN_DIGITAL); // Inicializacion de la variable nuevo con el retorno de la funcion

Estructuras de control: if

En una entrega anterior ya vimos que las estructuras de control sirven para realizar una o más acciones de acuerdo a ciertas condiciones. En este caso veremos una nueva estructura de control del tipo condicional. La instrucción se llama if (del Inglés “si”) y se usa para ejecutar instrucciones “si” una condición específica se cumple.

La sintaxis del condicional if es: if (condicion) {…}. Si la condición dentro de los paréntesis se cumple (es verdadero), las instrucciones dentro de las llaves se ejecutan, de lo contrario, nada se ejecuta. En nuestro código tenemos:

  if (nuevo != ultimo) {
    Serial.print("Leido: ");
    Serial.println((nuevo?"HIGH":"LOW"));
    
    ultimo = nuevo;
  }

Significa que las 3 instrucciones dentro de las llaves solo se ejecutarán si las variables “nuevo” y “ultimo” no son iguales, de lo contrario, nada se ejecuta. Existe una variante del condicional if que nos permite ejecutar algo si la condición NO se cumple. La construcción sería así:

if (condicion) {
  // Instrucciones si la condicion es verdadero
}
else {
  // Instrucciones si la condicion es falso
}

Y el elemento extra en la expresión anterior es el else (en Inglés “si no”) que nos permite declarar una serie de instrucciones a ejecutar en caso que la condición de falso.

Operador ternario ?:

Nos queda por revisar lo que estaba adentro de nuestro método Serial.println(). La expresión extraída de la línea sería:

(nuevo ? "HIGH" : "LOW")

Y refiere a un tipo de operador especial que funciona parecido a un condicional if..else. A este operador se lo conoce como operador ternario y su sintaxis es: <condición> ? <verdadero> : <falso>. La diferencia con un if..else radica en que el operador ternario se usa para asignaciones (inicializaciones) o para devolver valores. No puede ejecutar instrucciones como resultado de la condición. En nuestro caso, como nuevo tomará el valor retornado por digitalRead(), entonces su valor será HIGH o LOW. Como vimos, HIGH = 1 y LOW = 0. En C/C++ false = 0 (la constante es “false” o falso en Inglés), y verdadero es cualquier número distinto de 0 (la constante es “true” o verdadero en Inglés).

Habiendo dicho lo anterior, HIGH = verdadero y LOW = falso. Entonces, si el valor de la variable “nuevo” es HIGH (verdadero) entonces el operador ternario devolverá “HIGH” (con las comillas incluidas) que es equivalente a la constante de cadena (texto) HIGH, la cual como vimos se imprimirá en el Monitor Serie. De lo contrario, devolverá “LOW”. Y así es como imprimimos HIGH o LOW dependiendo de lo que nos haya retornado la función digitalRead().

El código explicado

Veamos ahora el mismo código pero explicado linea por linea con comentarios:

/*
 * Curso de Arduino - Entradas digitales
 * Version comentada
 * ArduinoHobby.com
 */
 
// Definimos la constante PIN_DIGITAL para que apunte al pin 8
#define PIN_DIGITAL 8

// Funcion especial de Arduino. Se ejecuta una sola vez al inicio
void setup() {
  // Inicializamos el puerto Serie a 9600 baudios de velocidad
  Serial.begin(9600);
  // Inicializamos el pin 8 como entrada. Su estado por defecto sera HIGH
  pinMode(PIN_DIGITAL, INPUT_PULLUP);
}

// Declaramos la variable "ultimo" y la inicializamos en 0
int ultimo = 0;

// Funcion especial de Arduino. Se ejecuta continuamente
void loop() {
  /* 
   * Declaramos la variable "nuevo" y la inicializamos con el valor retornado por la
   * funcion digitalRead() que lee el estado del pin 8
   */
  int nuevo = digitalRead(PIN_DIGITAL);
  
  // Si el estado nuevo leido es diferente al leido antes
  if (nuevo != ultimo) {
    // Envio al puerto Serie el texto "Leido: "
    Serial.print("Leido: ");
    // Envio al puerto Serie "HIGH" si el estado leido es HIGH y "LOW" si el estado leido es LOW
    Serial.println((nuevo?"HIGH":"LOW"));
    
    // Guardo el ultimo estado leido para compararlo luego
    ultimo = nuevo;
  }
}

¿Qué sigue?

Seguiremos publicando páginas del curso en los próximos días. Si desea saber cuando una página nueva sea publicada, no dude en agregarnos en Facebook haciendo click en el ícono de Facebook o poniéndonos “Me gusta” (Like) en el siguiente enlace: https://www.facebook.com/arduinohobby/.

Deja un comentario