Condiciones y comparaciones en Arduino

Comparaciones.

En el lenguaje C ++ (y casi en todos los lenguajes) existe un concepto como boolean, que toma dos valores: verdadero y falso, true false, 1 y 0. Como tipo de datos para trabajar con valores l贸gicos, tenemos boolean (sin贸nimo – bool), que puede tomar valores 0 (falso) o 1 (cierto). El mismo valor exacto devuelve el resultado de comparar dos n煤meros o variables, para la comparaci贸n tenemos varios operadores de comparaci贸n:

  • == identidad (a == b), a es id茅ntico a b.
  • ! = desigualdad (a! = b) a es distinto de b, negaci贸n.
  • = igualdad (a = b) a es igual a b
  • > = mayor o igual que (a> = b)
  • <= menor o igual (a <= b)
  • > mayor (a> b)
  • < menor (a <b)

En los ejemplos abstractos anteriores con a y b sucede lo siguiente: el par茅ntesis 鈥渄evuelve鈥 un valor booleano, que es el resultado de comparar n煤meros. Por ejemplo, si tenemos a = 10 y b = 20 entonces ( a > b ) devolver谩 el valor falso. Por ejemplo ( a! = b ) devolver谩 cierto ya que a realmente no es igual a b. Para vincular varios valores l贸gicos, se utilizan operadores l贸gicos:

  • NO l贸gico, negaci贸n. Operador anal贸gico no
  • && Y l贸gico. Operador anal贸gico y
  • || OR l贸gico. Operador anal贸gico o
byte a = 10, b = 20;
(a > b);  // false
(a != b); // true
boolean flag = true;
flag;   // true
!flag;  // false 
!(a > b); // true
//flagA = true, flagB = false;
(flagA && flagB); // false
//flagA = true, flagB = true;
(flagA and flagB);  // true
//flagA = true, flagB = false;
(flagA || flagB); // true
//flagA = false, flagB = false;
(flagA or flagB);  // false

Comparaciones en Coma Flotante.

Comparaci贸n de float, los n煤meros no son tan simples debido a la peculiaridad del tipo 芦punto flotante禄 – a veces los c谩lculos se realizan con un peque帽o error, por lo que la comparaci贸n puede no funcionar correctamente. 

float val1 = 0.1;
// val1 == 0.100000000
float val2 = 1.1 - 1.0;
// val2 == 0.100000023 !!!
// aparentemente val1 == val2
// pero la comparaci贸n devolver谩 falso
if (val1 == val2);  // false

Tenga cuidado al comparar n煤meros en coma flotante, especialmente con operaciones estrictas == , >= y <=: 隆el resultado puede ser incorrecto e il贸gico!


El 芦if禄 condicional.

Operador condicional Si (Ingl茅s 芦if芦) le permite bifurcar la ejecuci贸n del programa dependiendo de valores l贸gicos, es decir resultados de los operadores de comparaci贸n, que consideramos anteriormente, as铆 como directamente de las variables booleanas.

if ( valor_l贸gico ) {  
 // ejecutado si valor_l贸gico = verdadero
}

Operador else (Ingl茅s 芦lo contrario禄) funciona en conjunto con el operador if y permite tomar medidas en caso de incumplimiento del if:

if ( valor_l贸gico ) {  
 // ejecutado si valor_l贸gico - verdadero
} else {  
 // ejecutado si valor_l贸gico - falso
}

Tambi茅n hay una tercera construcci贸n que le permite ramificar el c贸digo a煤n m谩s, se llama –else if– otra opci贸n:

if ( valor l贸gico 1 ) {  
 // ejecutado si log. valor 1 - verdadero
} else if ( valor l贸gico 2 ) {    
 // ejecutado si log. valor 2 - verdadero
} else {  
  // hecho de manera diferente 
}

Veamos todos estos operadores en acci贸n en un gran ejemplo:

// al realizar una acci贸n 
// dentro de una condici贸n, {} son opcionales
if ( a > b ) c = 10; // si a es mayor que b, entonces c = 10 
else c = 20;       // si no, entonces c = 20
// en lugar de comparar, puedes usar el log. variable
boolean myFlag, myFlag2;
// si myFlag es verdadero, entonces asigna 10 a c
if ( myFlag ) c = 10; 
// condiciones complejas
// si ambos indicadores son verdaderos, entonces establece c en 10
if ( myflag && myFlag2 ) c = 10; 
// al realizar dos o m谩s acciones
// 隆dentro de la condici贸n, {} son obligatorios!
if ( myFlag ) {  
  c = 10;
  b = c;
} else {  
  c = 20;
  b = a;
}
byte buttonState;
if ( buttonState == 1 ) a = 10;      // si buttonState es 1 
else if ( buttonState == 2 ) a = 20; // si no, buttonState 2  
m谩s a = 30;           

As铆 es como funciona el operador condicional if, lo que le permite administrar el programa y crear acciones ramificadas en funci贸n de diferentes condiciones. Observe el 煤ltimo bloque en el ejemplo anterior, donde usamos else if para seleccionar una acci贸n en funci贸n del valor de la misma variable . Hay un operador switch, para hacer el c贸digo m谩s hermoso. Hablemos de ello un poco m谩s abajo.

Caracter铆stica Booleana

En la lecci贸n sobre tipos de datos, mencion茅 que boolean adquiere el significado cierto, si le asigna un n煤mero distinto de cero, es decir, el operador if, puede alimentar cualquier n煤mero y devolver谩 verdadero de todos modos, excepto en cero. Esto es 煤til en algunos casos, pero tambi茅n puede dar lugar a errores que son dif铆ciles de detectar. if ( 50 ) {} – Si, el c贸digo se ejecutar谩 para esta condici贸n.

Orden de condiciones

El orden de las condiciones juega un papel muy importante a la hora de optimizar su c贸digo e intentar hacerlo m谩s r谩pido en algunos casos. El punto es muy simple: las expresiones / valores booleanos se verifican de izquierda a derecha, y si al menos un valor hace que toda la expresi贸n sea incorrecta (false), se detiene la verificaci贸n adicional de las condiciones. Por ejemplo, si en la expresi贸n:

if ( a && b && c ) {  
  // hacer algo
}

Si al inicio a tiene el valor de聽false, la comprobaci贸n de otras expresiones (b y聽c) ya no se ejecutar谩.聽Cu谩ndo puede ser importante: por ejemplo, hay alg煤n tipo de bandera y expresi贸n que se eval煤a directamente en la condici贸n y se verifica de inmediato.聽En este caso, si se omite la bandera, el microcontrolador no perder谩 tiempo en c谩lculos innecesarios.聽Por ejemplo:

if ( flag && analogRead ( 0 ) > 500 ) {   
  // hacer algo
}

Si se omite la bandera, el microcontrolador no gastar谩 100 渭s adicionales en trabajar con el ADC e inmediatamente ignorar谩 el resto de las expresiones l贸gicas.聽Esto es, por supuesto, irrelevante ahora, pero a veces incluso 100 渭s en un bucle es decisivo, solo recuerde que el orden de las condiciones es importante.


Operador ternario.

Signo de聽interrogaci贸n -?-, u operador ternario, es un an谩logo m谩s corto para escribir la construcci贸n:

驴condici贸n? expresi贸n1: expresi贸n2

Funciona as铆: la condici贸n se eval煤a, si es verdadera, entonces聽toda la expresi贸n devuelve el valor de 1, y si es falsa,聽toda la acci贸n聽devuelve el valor de la expresi贸n, 2. Ejemplo:

byte a, b;
a = 10;
// si a <9, b obtiene el valor 200
// de lo contrario b obtiene 100
b = ( a > 9 ) ? 100: 200;

A modo de comparaci贸n, aqu铆 hay una construcci贸n similar con if-else.

a = 10;
if ( a > 9 ) b = 100; 
else if b = 200;

Otra opci贸n con c谩lculo:

byte a = 10, b = 5;
// suma el resultado de la expresi贸n a * 10, si a> 10
// de lo contrario, sume + 10
b + = ( a > 9 ) ? ( a * 10 ) : ( a + 10 ) ;

Del mismo modo, puede utilizar el operador聽?聽para enviar datos y texto al puerto serie (m谩s sobre eso m谩s adelante):

Serial.println((a > 9) "mayor que 9" : "menor que 9" ) ;

驴Es posible hacer con el operador聽?聽construcciones m谩s complejas, como聽else if ?聽

void setup() {
  Serial.begin(9600);
  // el c贸digo muestra el "tama帽o" de la variable de valor
  
 	byte value = 5;
  
  // construcci贸n if-else
 if (value > 19) Serial.println("> 19");
  else if (value > 9) Serial.println("> 9");
  else Serial.println("< 9");
  // con operador -?-
  Serial.println(( (value > 9) ? ( (value > 19) ? "> 19" : "> 9" ) : "< 9" ));
}

Operador de selecci贸n.

El operador de selecci贸n聽switch permite crear una construcci贸n que ramifica las acciones seg煤n el valor de una variable.聽La sintaxis es:

switch (valor) {
  case 0:
     // ejecutar si valor == 0
    break;
  case 1:
    // ejecutar si valor == 1
    break;
  case 2:
  case 3:
  case 4:// ejecutar si valor == 2, 3 o 4// 胁褘锌芯谢薪懈褌褜, 械褋谢懈 蟹薪邪褔械薪懈械 == 2, 3 懈谢懈 4
    break;
  default:
    // ejecutar si el valor no coincide con ninguno de los casos
    break;
}

El operador聽default no es necesario.聽Eel operador break es obligatorio, de lo contrario, la comparaci贸n ir谩 m谩s all谩, como se muestra para los casos 2, 3 y 4.

Usando operadores condicionales y de selecci贸n, se construye la l贸gica del programa.聽El operador condicional lo ayudar谩 a comparar el valor del sensor y decidir qu茅 hacer a continuaci贸n.聽El operador de selecci贸n se adaptar谩 perfectamente a los modos cambiantes del programa o al sondear los botones presionados en el control remoto IR.


NOTICIA IMPORTANTE

Debe tener mucho cuidado al trabajar con el operador聽switch porque el c贸digo dentro de las llaves聽switch () { }, es聽un bloque de c贸digo para todos los casos.聽En consecuencia, los case son solo atajos para la transici贸n entre secciones de este bloque.聽Por qu茅 es tan importante: todos los case est谩n en el mismo 谩mbito, es decir, dentro del switch. Las variables locales con el mismo nombre no se pueden declarar:

switch (mode) {
  case 0:
    long val = 100;
    break;
  case 1:
    long val = 100; // resultar谩 en un error en la variable
    break;
  case 2:
    break;
}

Adem谩s, no se recomienda crear variables locales dentro de los case, ya que esto puede romper el c贸digo.

switch ( modo ) {  
  case 0:
    break ;
  case 1:
    // variable local
    long val = 100;
    break ;
  case 2:
    // con modo == 2, el resultado saldr谩
    // 隆solo si eliminamos la variable local de arriba!
     Serial.println("hola");
    break ;
}

Directivas condicionales #if #else.

Adem谩s de la directiva聽#define que le dice al preprocesador que reemplace el juego de caracteres con otro juego de caracteres, tambi茅n hay directivas condicionales que le permiten hacer la llamada de 聽compilaci贸n condicional: teniendo la misma l贸gica que if-else, estas construcciones le permiten hacer alguna elecci贸n antes de compilar el c贸digo en s铆.聽Un excelente ejemplo es el 芦n煤cleo禄 del propio Arduino: la mayor铆a de las funciones est谩n escritas con las especificaciones de cada procesador, y antes de compilar el c贸digo, se selecciona el que corresponde al microcontrolador seleccionado actualmente del conjunto de opciones para implementar la funci贸n.聽En pocas palabras, la compilaci贸n condicional permite, por condiciones, incluir o excluir un c贸digo particular de la compilaci贸n principal, es decir,聽primero, el preprocesador analiza el c贸digo, algo se incluye en 茅l, algo no, y luego se realiza la compilaci贸n.

Adem谩s, por ejemplo, no podemos declarar ninguna constante o macro mediante聽#define m谩s de una vez, esto resultar谩 en un error.聽La compilaci贸n condicional permite la ramificaci贸n siempre que sea posible.聽Para la compilaci贸n condicional, disponemos de las directivas #if,聽#elif,聽#else,聽#endif聽#ifdef,聽#ifndef.

  • #if聽– anal贸gico聽Si聽en construcci贸n l贸gica
  • #elif聽– otra cosa si 聽en construcci贸n l贸gica
  • #else- m谩s聽en construcci贸n l贸gica
  • #endif – una directiva que acaba con la construcci贸n condicional
  • #ifdef聽– si est谩 芦definido禄
  • #ifndef聽– si 芦no est谩 definido禄
  • defined聽– este operador regresa聽true si la palabra especificada se 芦define禄 mediante聽#define, y false– si no.聽Se utiliza para construcciones de compilaci贸n condicional.

Veamos c贸mo usarlos con un ejemplo:

#define TEST 1 // define TEST como 1
#if (TEST == 1) // si TEST 1
#define VALUE 10 // define VALUE como 10
#elif (TEST == 0) // TEST 0
#define VALUE 20 // define VALUE como 20
#else // si no
#define VALUE 30 // define VALUE como 30
#endif // fin de la construccion

Por tanto, tenemos una constante definida聽VALUE que depende de la 芦configuraci贸n禄聽TEST.聽El dise帽o le permite incluir o excluir fragmentos de c贸digo antes de la compilaci贸n, por ejemplo, un fragmento sobre depuraci贸n:

#define DEBUG 1
void setup() {
#if (DEBUG == 1)
  Serial.begin(9600);
  Serial.println("Hello!");
#endif
}

Por lo tanto, estableciendo聽DEBUG puede incluir o excluir cualquier fragmento de c贸digo.

El preprocesador tiene dos directivas m谩s:聽#ifdef聽y聽#ifndef, le permiten incluir o excluir secciones de c贸digo por condici贸n:聽#ifdef聽– 驴est谩 definido?聽#ifndef- 驴no est谩 definido?聽Definido o no definido, por supuesto, estamos hablando de #define.

#define TEST // define TEST 
#ifdef TEST // si TEST est谩 definido 
#define VALUE 10 // define VALUE como 10 
#else // si No. 
#define VALUE 20 // define VALUE como 20 
#endif // fin de la condici贸n

Es en la compilaci贸n condicional donde se construye toda la versatilidad de las bibliotecas para Arduino, porque cuando seleccionas una placa, se 芦crea禄 autom谩ticamente un valor predeterminado para el nombre del microcontrolador, se ven as铆:

  • __AVR_ATmega32U4__
  • __AVR_ATmega2560__
  • __AVR_ATmega328P__
  • Y m谩s de este estilo

Esto le permite crear c贸digo gen茅rico usando la construcci贸n con聽#ifdef聽o聽#if defined ():

#if defined(__AVR_ATmega32U4__)
// c贸digo para Leonardo / Micro / Pro Micro
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// c贸digo para Mega (1280 o 2560)
#elif defined(__AVR_ATmega328P__)
// c贸digo para UNO / Nano / Pro Mini
#endif

As铆, un c贸digo personal estar谩 disponible para microcontroladores (placas Arduino) de diferentes modelos, que ser谩 transferido al compilador cuando esta placa sea seleccionada de la lista de placas.


Deja un comentario