void loop se ejecuta incluso cuando el ciclo while interno es verdadero en arduino

Hice una aplicación móvil simple para enviar LED_ON o LED_OFF cuando se hace clic en un botón. En el siguiente código, el ciclo while no se completa completamente durante la ejecución, pero el controlador va al ciclo vacío () y continúa desde allí.

char command;
String my_final="LED_OFF";
#define led 9

void setup(){
  Serial.begin(9600);
  pinMode(led, OUTPUT);
  Serial.println("ready");
}

void loop(){
  if(Serial.available() > 0){
    my_final = "";
    while(Serial.available() > 0){
      command = (byte)Serial.read();
      my_final += command;
      Serial.println("test");
    }
    Serial.println(my_final);
    }

  if(my_final == "LED_ON"){
    Serial.println(my_final);
    analogWrite(led, 255);
    my_final == "LED_ON";
  }

  if(my_final == "LED_OFF"){
    Serial.println(my_final);
    analogWrite(led, 0);
    my_final == "LED_OFF";
  }
}

El problema principal ocurre my_final=""cuando tengo que hacer esto para aceptar una nueva entrada del bluetooth. Parece que no puedo encontrar una manera de evitar este problema.

EDITAR

Esto es lo que obtengo en el monitor serial. test L test E test D test _ test O test N.

Lo que describe en su pregunta es prácticamente imposible. ¿Realmente has visto en el depurador que la ejecución sale del whileciclo con la condición Serial.available()aún siendo verdadera?
esto es lo que obtengo en el monitor serie ...test L test E test D test _ test O test N
Quizás debería indicar lo que está obteniendo en el monitor en serie en su pregunta entonces. Actualmente, su pregunta se lee como si hubiera visto la ejecución salirse del whilebucle, lo cual es imposible de ver con un monitor en serie.
¿Qué se my_final == "LED_ON";supone que debe hacer? (al final del if(my_final == "LED_ON"){}bloque, y del mismo modo para OFF)
¿Cómo sabes que el ciclo while está completamente completado?
Olvidé eliminar esas dos líneas que usé antes por alguna razón.

Respuestas (4)

La comunicación en serie es la transmisión de datos de un byte a la vez. Su código es lo suficientemente rápido para leer y procesar un byte antes de que se reciba el siguiente.

Hay muchas soluciones posibles para esto, y usar retrasos no es bueno.

Puede enviar un carácter de marcador al final de cada comando, por ejemplo, LED_ON!. Entonces sabrá que debe seguir agregando caracteres a su matriz hasta que vea un signo de exclamación. Un candidato común para un marcador es el carácter de nueva línea.

Otra solución es asegurarse de que todos los comandos tengan la misma longitud (por ejemplo, 6 bytes). Entonces podrías simplemente esperar hasta que recibas tantos caracteres, es decir

if(Serial.available() >= 6) ...

Tenga en cuenta que en la segunda solución, si pierde un solo byte, no podrá recibir comandos correctamente hasta que vuelva a sincronizar. Para volver a sincronizar, podría, por ejemplo, desechar el contenido de Serialdespués de un tiempo de espera, si un comando incompleto permanece allí durante demasiado tiempo.

Otra opción: cambie los caracteres a un búfer de uno en uno, dejando caer el más antiguo por cada uno nuevo, y verifique si el contenido del búfer es un comando válido. O use comandos de un solo carácter.
@Jules usando un búfer circular requeriría una garantía de que los comandos no pueden contener otros comandos como subcadenas.
Un protocolo más extensible podría ser reservar los primeros bytes (1 a 4, dependiendo de la longitud máxima esperada del comando/mensaje) para codificar la longitud del mensaje. Entonces su algoritmo es básicamente 'leer el tamaño del mensaje -> leer el mensaje'.
@aroth Claro, eso también es posible. Sin embargo, aún se necesitaría un algoritmo de resincronización.

@9600 Baudrate, está recibiendo datos a 10 bits por milisegundo. Un carácter es de 8 bits + bits de inicio y parada en un marco. Estás leyendo el búfer más rápido que él. Entonces, una solución simple sería poner un retraso de> 1 ms más o menos después de la lectura en serie (), dentro del ciclo.

Esta es quizás la cosa más simple que el OP podría intentar para confirmar la fuente de su problema. Sin embargo, a la larga, no lo recomendaría. Hay tantas formas en las que el código basado en retrasos puede fallar...
Sí, es cierto... la pérdida de datos puede ocurrir cuando se opera a una velocidad de transmisión más alta con un flujo de datos continuo...
Sí. Desbordamiento de búfer en ese caso. Acordado.
evitar el desbordamiento del búfer se puede hacer solo delay(1)ing después de read()cuandoSerial.available() == 0
@MITURAJ En realidad quise decir otra cosa. Cuando escribe una cadena como "LED_OFF" en un puerto COM en una computadora, no hay garantía de que todos los caracteres se envíen sin demora. Lo mismo ocurre con los microcontroladores en los que UART ISR no tiene la prioridad más alta.

El almacenamiento en búfer hasta que un salto de línea se vea así (mi código arduino está un poco oxidado, así que perdone los errores simples; el algoritmo debería ser correcto):

finished = false;
command = "";
while (!finished) {
   if (Serial.available() > 0) {
      c = (byte)Serial.read();
      if (c == '\n') {
         finished = true;
      }
      else {
         command += c;
      }
   }
}

Este ciclo seguirá leyendo caracteres y agregando estos caracteres al comando variable.

Esto supone que el enlace de comunicaciones es perfecto, en el sentido de que no perderá ni alterará los caracteres. Para hacer esto más sólido, puede poner un control para asegurarse de que el tiempo para recibir el comando no sea demasiado largo.

También es posible que desee responder a cada comando con una respuesta ack o un huh? (nack) respuesta, dejando que el servidor sepa lo que está pasando.

¡Feliz fabricación!

Qué sucede cuando el flujo de datos entrante se ve así:

LE..Td.. D_O..Td.. N?

Td es un pequeño retraso aleatorio.

Si su while(Serial.available() > 0){es más rápido que Td, habrá reiniciado my_final = "";con solo LEen él. Es decir, a continuación recibes D_O, y luego lo reinicias de nuevo.

Exactamente ese es el problema al que me enfrento aquí. ¿Hay alguna manera de superar esto?
@ganeshvicky Buffer hasta el salto de línea.
¿Te refieres a usar la función delay()? Lo siento, soy algo nuevo en esto.
@ganeshvicky No, con retraso no puedes leer el puerto serie. necesita almacenar en búfer hasta que reciba un \n, pero aún tendrá que sondear Serial.available(), por lo que tendrá que resolver algo.