A veces las cosas no son como parecen

Electrónica: 

 

Cuando la gente empieza a elaborar programas en Arduino que involucran el envío de comandos con opciones las cosas se vuelven raras.

He visto últimamene este problema y me he decidido a escribir este pequeño artículo. Por supuesto, cuando yo empecé a escribir aplicaciones de ese tipo en Arduino me encontré con el mismo problema ya que todos estamos habituados a simplemente enviar comandos en puerto serie del tipo ATD123456789 (sé que es obvio pero este comando se envía a un módem para realizar una llamada al número 123456789) y no hay que preocuparse de ciertas cosas.
Veamos un ejemplo.

void setup() {
  Serial.begin(9600);
}

void loop() {
  int lon = 0;
 
  while (Serial.available()) {
    lon++;
    char c = Serial.read();
    Serial.println(c);
    //delay(1000);
  }
  if (lon > 0) {
    Serial.println("---");
    Serial.print("Lon: ");
    Serial.println(lon);
    Serial.println("---");
  }
}
Para simplificar vamos a suponer que los comandos son comandos sin opciones, si quieres enviar comandos con opciones sólo hay que quedarse con los diferentes tokens. Además en lugar de tener un char *comando o su equivalente en array para almacenar el comando simplemente voy imprimiendo caracteres. Esto no implica ningún tipo de limitación en la explicación del ejemplo.
Supongamos que enviamos el comando bajar_persiana. A primera vista podría parecer que lon = 14 y si tuviéramos un char *comando en comando tendríamos bajar_persiana (he obviado el return que además es diferente en Linux y en Windows).
Si grabas este programa en tu Arduino (incluyendo el comentario del delay) puede que te sorprendas y veas que (por lo general) se van imprimiendo uno a uno los caracteres seguidos de lon = 1. En cambio, si incluimos el delay se mostrarán uno a uno los caracteres pero esta vez lon = 14. Esto último es lo que espera encontrar la mayoría de la gente.
¿Qué implica esto? ¿Por qué ocurre?
La respuesta a la primera pregunta implica que enviaremos un comando no implementado, esto es, en lugar de bajar_persiana pasarás b, después a, después j...
Antes de responder a la segunda pregunta diré que he encontrado gente que al cambiar un método o función veían cómo se enviaba correctamente o no el comando, volviéndose locos. En Arduino es habitual tener variables globales lo que tiende a apuntarlas como responsables directamente. En realidad el envío de un comando de forma correcta o no en estos programas depende de lo que tarde en ejecutarse el método/función ¿por qué? La clave de todo es que el puerto serie de Arduino es asíncrono. Esto es, cuando se ejecuta el loop podemos tener dos posibilidades:
 -No hay nada en el serial
 -Hay algo en el serial
El problema viene con la segunda posibilidad, según lo que tarde lo que estemos haciendo antes del Serial puede haber, un caracter, dos... todos. Y no sabemos cuántos. Imagínate que en la primera iteración del loop sólo ha llegado un caracter: lo imprimimos y lon = 1. En la seguna iteración hemos tardado suficiente para que en el Serial hayan 3 caracteres más; se imprimiran y lon = 3, etc.
¿Cómo solucionamos el problema? Pues además de nuestro while necesitamos esperar al retorno de carro (si no recuerdo mal yo espero '\n' puesto que windows es '\r\n' y linux '\n' De hecho es lo que hacemos todos con el teclado jjjjj) para asegurarnos que tenemos el comando completo. Lo esencial es 'cambiar el chip' y tener claro que el Serial es asíncrono y no sabemos que hay en el buffer del Serial pero sí sabemos que cuando encontramos un '\n' tenemos que el usuario ha enviado un comando.

Espero que haya servido a alguien y no haya sido un rollo.

Social_buttons: