Problema con comparaciones en un loop (?)

Hola. Soy nuevo en Arduino y en el foro.
Tengo un problema con un pequeño código que lee un valor AnalogRead() y lo informa a un puerto serie mediante Serial.println().
El código originalmente funcionaba bien pero al estar contantemente enviando datos al puerto, cuando en mi software intentaba cerrar el puerto, mi programa se cerraba abruptamente, incluso el IDE del mismo. Descubrí que si el puerto no estaba transmitiendo esto no ocurría.
Decidí hacer que se envien datos sólo cuando lo requiero para poder dejar de recibirlos antes de cerrar el puerto. Desde entonces el programa no me envía correctamente los datos.
Mando el ejemplo que intento hacer andar

#define TOMILLIGAUSS 3.756010  
#define NOFIELD 514L 

// Define las salidas para los leds
int Azul  = 6;
int Verde = 7;
int Rojo  = 8;
String Estado = "OFF";  
String Trans = "OFF";

void setup() 
{
  
  Serial.begin(9600);
  pinMode(Azul, OUTPUT);
  pinMode(Rojo, OUTPUT);
  pinMode(Verde, OUTPUT);
}

void loop() 
{
  
   int raw = analogRead(0);
  
      
   if (Trans == "ON"){
      Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
     }
     
  float gauss = (raw - NOFIELD) * TOMILLIGAUSS; 


  if (Estado == "ON"){    // Maneja los LEDS sólo si está en modo ON
    if (gauss > 2)  
      {
        digitalWrite(Verde, HIGH);    // Enciende el led Verde
        digitalWrite(Rojo, LOW);      // Apaga el Rojo
      }
    else if (gauss < -2) 
      {
        digitalWrite(Rojo, HIGH);     // Enciende el led Rojo
        digitalWrite(Verde, LOW);     // Apaga el verde
      }
    else   
      {
        digitalWrite(Rojo, LOW);     // Apaga el Led Rojo
        digitalWrite(Verde, LOW);    // Apaga el Led Verde
      }
  }
  
  if (Estado == "OFF")  // Si está en OFF mantiene todo apagado
    {
      digitalWrite(Azul, LOW);
      digitalWrite(Verde, LOW);
      digitalWrite(Rojo, LOW);
    }

 // Si recibe un Valor desde el puerto serie
  if (Serial.available() > 0) {
      String incoming = Serial.readString();
       
    //Si recibe un ON  -> Enciende 
    if(incoming == "ON"){
       digitalWrite(Azul, HIGH);
       Serial.println(incoming);
       incoming = "";
       Estado = "ON";
    }
    
    //Si recibe un OFF  -> Apaga 
    if(incoming == "OFF"){
       digitalWrite(Azul, LOW);
       digitalWrite(Verde, LOW);
       digitalWrite(Rojo, LOW);
       Serial.println(incoming);
       incoming = "";
       Estado = "OFF";
    }
    
    //Si recibe un DATAON  -> Comienza a transmitir datos
    if(incoming == "DATAON"){
       Serial.println(incoming);
       incoming = "";
       Trans = "ON";
    } 

    //Si recibe un DATAOFF  -> Deja de transmitir datos
    if(incoming == "DATAOFF"){
      Serial.println(incoming);
      incoming = "";
       Trans = "OFF";
    }
    
    Serial.flush();
        
 } 

 delay(400);
}

Agregué un "Serial.println(incoming);" para poder ver si está recibiendo el parámetro que le envio.
El procedimiento que hago es:

  • Conecto al puerto
  • Envio ON para encender los leds
  • Envio DATAON para empezar a recibir datos, calculo e informo desde el soft..
  • Envio DATAOFF para dejar de recibir....
  • Envio OFF para apagar los leds
  • Cierro el puerto

Desde el DATAOFF que ya no recibo más nada, pero el arduino parece recibir porque puede encender y apagar los leds con ON y OFF pero no recibo el informe de nada, ni valores de lectura ni el valor de "Incoming".

¿Hay algún error en el código que no esté viendo?

Utilizo Arduino Nano. Atmega328P (Old bootloader)

¡¡¡Desde ya muchas gracias!!!

EDITO:
Indudablemente hay algo raro que no estoy entendiendo.
Si en lugar de hacer la comparacion "Incoming == ON" hago "Incoming != OFF" el programa funciona perfectamente. Pero cuando desabilito el informe "Serial.println(incoming);" que hice para debugear, deja de informar absolutamente todo. Por el momento lo soluciono desde mi programa evitando hacer cálculos a valores no numéricos, pero me encantaría saber dónde está el error.

Prueba esto
no uses delay(400) cuando tienes una lectura del puerto serie.
Hay muchas cosas repetidas que he eliminado.
Basicamente el código luce bien, quitando lo repetido o poniendo la impresion en el momento en que corresponde.

#define TOMILLIGAUSS 3.756010
#define NOFIELD 514L

// Define las salidas para los leds
int Azul  = 6;
int Verde = 7;
int Rojo  = 8;
String Estado = "OFF";
String Trans = "OFF";
float gauss;

void setup()
{

  Serial.begin(9600);
  pinMode(Azul, OUTPUT);
  pinMode(Rojo, OUTPUT);
  pinMode(Verde, OUTPUT);
}

void loop() {

  int raw = analogRead(A0);

    // Si recibe un Valor desde el puerto serie
  if (Serial.available() > 0) {
      String incoming = Serial.readString();
      Serial.println(incoming);
      
      //Si recibe un ON  -> Enciende
      if (incoming == "ON") {
        digitalWrite(Azul, HIGH);
        Estado = "ON";
      }

      //Si recibe un OFF  -> Apaga
      if (incoming == "OFF") {
        digitalWrite(Azul, LOW);
        digitalWrite(Verde, LOW);
        digitalWrite(Rojo, LOW);
        Serial.println(incoming);
        Estado = "OFF";
      }
  
      //Si recibe un DATAON  -> Comienza a transmitir datos
      if (incoming == "DATAON") {
          // Serial.println(incoming);
          Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
      }

      //Si recibe un DATAOFF  -> Deja de transmitir datos
      if (incoming == "DATAOFF") {
          // Serial.println(incoming);
      }
      incoming = "";
  }

  gauss = (raw - NOFIELD) * TOMILLIGAUSS;

  if (Estado == "ON") {   // Maneja los LEDS sólo si está en modo ON
    if (gauss > 2)        {
      digitalWrite(Verde, HIGH);    // Enciende el led Verde
      digitalWrite(Rojo, LOW);      // Apaga el Rojo
    }
    else if (gauss < -2)  {
      digitalWrite(Rojo, HIGH);     // Enciende el led Rojo
      digitalWrite(Verde, LOW);     // Apaga el verde
    }
    else   {
      digitalWrite(Rojo, LOW);     // Apaga el Led Rojo
      digitalWrite(Verde, LOW);    // Apaga el Led Verde
    }
  } 

  if (Estado == "OFF") { // Si está en OFF mantiene todo apagado
    digitalWrite(Azul, LOW);
    digitalWrite(Verde, LOW);
    digitalWrite(Rojo, LOW);
  }
}

A ver, razonemos un poco el problema...
Tu quieres manejar tu artefacto remotamente comunicación vía Serial, si literalmente cortas la comunicación (lo harías con Serial.end() ) ¿cómo reiniciarías la comunicación?
Afortunadamente tu código no cierra efectivamente la comunicación, lo que deberías hacer es dejar de leer el puerto, pero otra vez ¿cómo le vuelves a enviar comandos si los ignoras?

En resumen, tu idea está bien encaminada, aunque tal vez haya que acomodarla.

Por ejemplo, no usar las cadenas como control sino setear variables booleanas, y con ellas determinar si hace o no tal acción.

Lo que si, nunca puedes dejar de leer el puerto serie sino pierdes el control.

¿Me explico?

Muchas gracias por la respuesta!! Ahora lo pruebo y te cuento.

EDITO:
Voy a tener en cuenta las recomendaciones. Pero leyendo el código me doy cuenta que no haría lo que necesito. Cuando Arduino recibe el valor DATAON pone la variable Trans = ON. El programa envía DATAON una sola vez. Arduino compara si trans es igual ON en el bucle para mandar los datos al puerto hasta que reciba un valor DATAOFF.
Me parece que en tu caso envía el dato una sola vez por cada vez que recibe el DATAON.

Gracias por la respuesta.
A ver. Originalmente el código del arduino lo que hacía era "imprimir" en el puerto serie el valor obtenido desde analoread(). Lo hacía constantemente con ese delay de 400.
Yo en ningun momento cierro definitivamente la conexión con el puerto sino que le paso un parámerto al arduino para que me envie los valores o deje de hacerlo cuando ya no los necesito. Pero siempre dejo el puerto abierto por sin quiero volver a solicitarle el envio de los datos.
Todo esto hasta que realmente cierre la conexión con el puerto desde el soft principàl.
El problema es que si yo cerraba el puerto mientras arduino seguía imprimiendo datos en él mi programa se cerraba abruptamente. De seguro un problema con su librería de conexión a puertos com. Pero de esta manera pude solucionarlo.
¿Se entiende más o menos?

Yo entendí perfectamente, te expliqué por qué sigue respondiendo el comando del LED, ya que dijiste que te parecía extraño.

Saludos

Claro. Los leds funcionan perfectamente. El problema lo tengo con los serial.prinln().
Agregué uno en cada comparación para ver si el arduino estaba recibiendo el parámetro que yo le mando y así es.
Recibo en el serial el parámetro seguido del valor RAW.
Si saco ese serial.print() debajo de cada comparación y dejo sólo el del valor RAW no recibo nada... Cosa rara nro1. Lo solucioné ignorando los datos no numéricos.
Y si comparo "trans==ON" tampoco recibo nada. Esto lo solucioné comparando "trans != OFF". Cosa rara nro2.
Jaja.

Gracias por la ayuda!

Y si lo haces asi?

#define TOMILLIGAUSS 3.756010  
#define NOFIELD 514L 

// Define las salidas para los leds
int Azul  = 6;
int Verde = 7;
int Rojo  = 8;
bool Estado = false;  
bool Trans = false;

void setup() 
{
  
  Serial.begin(9600);
  pinMode(Azul, OUTPUT);
  pinMode(Rojo, OUTPUT);
  pinMode(Verde, OUTPUT);
}

void loop() 
{
  
   int raw = analogRead(0);
  
      
   if (Trans){
      Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
     }
     
  float gauss = (raw - NOFIELD) * TOMILLIGAUSS; 


  if (Estado){    // Maneja los LEDS sólo si está en modo ON
    if (gauss > 2)  
      {
        digitalWrite(Verde, HIGH);    // Enciende el led Verde
        digitalWrite(Rojo, LOW);      // Apaga el Rojo
      }
    else if (gauss < -2) 
      {
        digitalWrite(Rojo, HIGH);     // Enciende el led Rojo
        digitalWrite(Verde, LOW);     // Apaga el verde
      }
    else   
      {
        digitalWrite(Rojo, LOW);     // Apaga el Led Rojo
        digitalWrite(Verde, LOW);    // Apaga el Led Verde
      }
  }
  
  if (!Estado)  // Si está en OFF mantiene todo apagado
    {
      digitalWrite(Azul, LOW);
      digitalWrite(Verde, LOW);
      digitalWrite(Rojo, LOW);
    }

 // Si recibe un Valor desde el puerto serie
  if (Serial.available() > 0) {
      String incoming = Serial.readString();
       
    //Si recibe un ON  -> Enciende 
    if(incoming == "ON"){
       digitalWrite(Azul, HIGH);
       Serial.println(incoming);
       incoming = "";
       Estado = true;
    }
    
    //Si recibe un OFF  -> Apaga 
    if(incoming == "OFF"){
       digitalWrite(Azul, LOW);
       digitalWrite(Verde, LOW);
       digitalWrite(Rojo, LOW);
       Serial.println(incoming);
       incoming = "";
       Estado = false;
    }
    
    //Si recibe un DATAON  -> Comienza a transmitir datos
    if(incoming == "DATAON"){
       Serial.println(incoming);
       incoming = "";
       Trans = true;
    } 

    //Si recibe un DATAOFF  -> Deja de transmitir datos
    if(incoming == "DATAOFF"){
      Serial.println(incoming);
      incoming = "";
       Trans = false;
    }
    
    Serial.flush();
        
 } 

 delay(400);
}

Apa. A simple vista está muy bueno. Mañana lo pruebo y te cuento.
Muchísimas gracias otra vez!!!

Es en serio, tu problema son los Serial.print?
Es algo que te pones a probar 1 a 1 y lo vas resolviendo.
Que sentido tiene esto

 if(incoming == "ON"){
       digitalWrite(Azul, HIGH);
       Serial.println(incoming);

Imprimirá siempre ON porque esta dentro de la condición ON.
De hecho tus comparaciónes son útiles para cambiar estados pero luego no lo son para imprimir algo que es recibido luego del Serial.readString()
Todo lo que ahi se recibe debería imprimirse. Claro que si escribes mal tmb se imprimirá pero es algo menor comparado con lo que haces, y te permite ver en que te has equivocado.

Los print() los puso para debug, después los quita

Saludos

Si. ese es mi problema y no logro entender porque.
Por ejemplo, el código anterior de Gatul funciona perfectamente. Hace lo mismo que el que yo hice pero tengo el mismo problema...
Vuelvo a intentar explicar lo que busco hacer:
El siguiente es solo un fragmento del código de Gatul para que se pueda entender más claramente...

void loop() 
{
  
   int raw = analogRead(0);
       
   if (Trans){
      Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
     }
     
   // Si recibe un Valor desde el puerto serie
  if (Serial.available() > 0) {
      String incoming = Serial.readString();
      
    //Si recibe un DATAON  -> Comienza a transmitir datos
    if(incoming == "DATAON"){
       Serial.println(incoming);
       incoming = "";
       Trans = true;
    } 

    //Si recibe un DATAOFF  -> Deja de transmitir datos
    if(incoming == "DATAOFF"){
      Serial.println(incoming);
      incoming = "";
       Trans = false;
    }
    
    Serial.flush();
        
 } 

 delay(400);
}

Yo desde mi software hago algo así como:
Com Open
Com send DATAON
ahí arduino me devuelve DATAON (que efectivamente siempre será ese el valor al estar dentro de la comparación pero lo puse para saber si arduino estaba recibiendo el parámetro que le envio) seguido de los valores de lectura de analogread mediante

Serial.println(raw);

por ejemplo:

DATAON
513
514
517
514

y así hasta que yo le haga un COM SEND DATAOFF para que arduino ponga Trans=false y deje de hacer los serial.println()

Ahora, esto funciona de maravillas, pero si yo elimino los println() que usé dentro de cada comparación dejo de recibir el valor raw en cada vuelta del loop que es lo que realmente necesito.

O sea que el siguiente código no me devuelve absolutamente nada y yo sé que arduino está recibiendo las órdenes que yo le doy...

void loop() 
{
  
   int raw = analogRead(0);
       
   if (Trans){
      Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
     }
     
   // Si recibe un Valor desde el puerto serie
  if (Serial.available() > 0) {
      String incoming = Serial.readString();
      
    //Si recibe un DATAON  -> Comienza a transmitir datos
    if(incoming == "DATAON"){
       //Serial.println(incoming);
       incoming = "";
       Trans = true;
    } 

    //Si recibe un DATAOFF  -> Deja de transmitir datos
    if(incoming == "DATAOFF"){
      //Serial.println(incoming);
      incoming = "";
       Trans = false;
    }
    
    Serial.flush();
        
 } 

 delay(400);
}

Por qué será que esto:

 if (Trans){
      Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
     }

no imprime nada si no está acompañado del print dentro de las comparaciones. Nótese que yo envio el DATAON una sola vez y me quedo haciendo cálculos con los valores que recibo y no vuelvo a enviar nada hasta mandar el DATAOFF. O sea que arduino me devuelve el DATAON seguido de las lecturas.....
¿Se entiende lo raro de la consulta? Será que hace falta algo para que el comando

Serial.println(raw);

se haga efectivo?

Bueno. perdón por ser tan repetitivo y muchas gracias por la ayuda.

Prueba cambiando

if(incoming == "DATAON"){

por

if(incoming.indexOf("DATAON") >= 0){

lo mismo con DATAOFF, ON y OFF

No. Tampoco. De hecho deja de funcionar.
Recibo el ON al encender.
Recibo DATAON al activar la recepsión de datos. Pero no recibo las lecturas RAW.
Al enviar el DATAOFF y el OFF ya no recibo nada, ni siquiera los DATAOFF ni OFF para debug, aunque sé que los recibe porque los leds prenden o apagan según el comando que envie....
Raro todo.... jajajaja

Ya está (o al menos a mi me funciona bien en wokwi)

if (Serial.available() > 0) {
      String incoming = Serial.readString();
      incoming.trim();  // agrega esta sentencia

El problema con .indexOf() es que "ON" también está dentro de "DATAON" y en el orden que haces las comparaciones "DATAON" lo toma como "ON".
Yo probé cambiando el orden (primero "DATAON" y "DATAOFF", luego "ON" y "OFF" y funcionó bien).
Pero creo que esto resuelve el problema de tu código original y el de mi primer modificación.
Te dejo el código tal y como lo simulé en wokwi

#define TOMILLIGAUSS 3.756010
#define NOFIELD 514L

// Define las salidas para los leds
int Azul  = 6;
int Verde = 7;
int Rojo  = 8;
bool Estado = false;
bool Trans = false;

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(50);
  Serial.print(".");
  pinMode(Azul, OUTPUT);
  pinMode(Rojo, OUTPUT);
  pinMode(Verde, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  int raw = analogRead(0);

  digitalWrite(LED_BUILTIN, Trans);

  if (Trans) {
    Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
  }

  float gauss = (raw - NOFIELD) * TOMILLIGAUSS;

  if (Estado) {   // Maneja los LEDS sólo si está en modo ON
    if (gauss > 2)
    {
      digitalWrite(Verde, HIGH);    // Enciende el led Verde
      digitalWrite(Rojo, LOW);      // Apaga el Rojo
    }
    else if (gauss < -2)
    {
      digitalWrite(Rojo, HIGH);     // Enciende el led Rojo
      digitalWrite(Verde, LOW);     // Apaga el verde
    }
    else
    {
      digitalWrite(Rojo, LOW);     // Apaga el Led Rojo
      digitalWrite(Verde, LOW);    // Apaga el Led Verde
    }
  }
  else  // Si está en OFF mantiene todo apagado
  {
    digitalWrite(Azul, LOW);
    digitalWrite(Verde, LOW);
    digitalWrite(Rojo, LOW);
  }
  // Si recibe un Valor desde el puerto serie
  if (Serial.available() > 0) {
    String incoming = Serial.readString();
    incoming.trim();
    //Serial.println(incoming);
    //Si recibe un ON  -> Enciende
    if (incoming == "ON") {
      digitalWrite(Azul, HIGH);
      incoming = "";
      Estado = true;
    }
    //Si recibe un OFF  -> Apaga
    if (incoming == "OFF") {
      digitalWrite(Azul, LOW);
      digitalWrite(Verde, LOW);
      digitalWrite(Rojo, LOW);
      //       Serial.println(incoming);
      incoming = "";
      Estado = false;
    }
    //Si recibe un DATAON  -> Comienza a transmitir datos
    if (incoming == "DATAON") {
      incoming = "";
      Trans = true;
    }
    //Si recibe un DATAOFF  -> Deja de transmitir datos
    if (incoming == "DATAOFF") {
      //      Serial.println(incoming);
      incoming = "";
      Trans = false;
    }
    Serial.flush();
  }
  delay(400);
}

Le agregué un par de sentencias para que el LED de la placa refleje el estado de Trans a modo de debug.

Hay un detalle, readString() por defecto tiene un timeout de 1 segundo, o sea, detiene el código durante 1 segundo esperando la llegada de más caracteres.
Con Serial.setTimeout() se puede reducir ese tiempo.

Muchas gracias. Seguramente las comparaciones siguen funcionando de maravillas
pero sigo sin recibir los valores de:

if (Trans) {
    Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
  }

Que es lo verdaderamente necesito...

Bueno. Creo que lo mejor que puedo hacer es agregar un print en blanco al final del loop. De esta forma recibo los datos. No sé porque pero bueno.... funciona.

#define TOMILLIGAUSS 3.756010
#define NOFIELD 514L

// Define las salidas para los leds
int Azul  = 6;
int Verde = 7;
int Rojo  = 8;
bool Estado = false;
bool Trans = false;

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(50);
  Serial.print(".");
  pinMode(Azul, OUTPUT);
  pinMode(Rojo, OUTPUT);
  pinMode(Verde, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  int raw = analogRead(0);

  digitalWrite(LED_BUILTIN, Trans);

  if (Trans) {
    Serial.println(raw);      // Acá pretendo hacer el println sólo si esta habilitada la transmisión
  }

  float gauss = (raw - NOFIELD) * TOMILLIGAUSS;

  if (Estado) {   // Maneja los LEDS sólo si está en modo ON
    if (gauss > 2)
    {
      digitalWrite(Verde, HIGH);    // Enciende el led Verde
      digitalWrite(Rojo, LOW);      // Apaga el Rojo
    }
    else if (gauss < -2)
    {
      digitalWrite(Rojo, HIGH);     // Enciende el led Rojo
      digitalWrite(Verde, LOW);     // Apaga el verde
    }
    else
    {
      digitalWrite(Rojo, LOW);     // Apaga el Led Rojo
      digitalWrite(Verde, LOW);    // Apaga el Led Verde
    }
  }
  else  // Si está en OFF mantiene todo apagado
  {
    digitalWrite(Azul, LOW);
    digitalWrite(Verde, LOW);
    digitalWrite(Rojo, LOW);
  }
  // Si recibe un Valor desde el puerto serie
  if (Serial.available() > 0) {
    String incoming = Serial.readString();
    incoming.trim();
    //Serial.println(incoming);
    //Si recibe un ON  -> Enciende
    if (incoming == "ON") {
      digitalWrite(Azul, HIGH);
      //Serial.println(incoming);
      incoming = "";
      Estado = true;
    }
    //Si recibe un OFF  -> Apaga
    if (incoming == "OFF") {
      digitalWrite(Azul, LOW);
      digitalWrite(Verde, LOW);
      digitalWrite(Rojo, LOW);
      //Serial.println(incoming);
      incoming = "";
      Estado = false;
    }
    //Si recibe un DATAON  -> Comienza a transmitir datos
    if (incoming == "DATAON") {
      //Serial.println(incoming);
      incoming = "";
      Trans = true;
    }
    //Si recibe un DATAOFF  -> Deja de transmitir datos
    if (incoming == "DATAOFF") {
      //Serial.println(incoming);
      incoming = "";
      Trans = false;
    }
    Serial.println();
    Serial.flush();
  }
  delay(400);
}

Me llama mucho la atención que no funcione con tu placa, si puedo cuando salga del trabajo lo pruebo en un Nano como tuyo, a ver si en físico hay alguna diferencia, pero te aseguro que hasta ahora todo lo que simulé en wokwi anduvo exactamente igual en la placa real.

Se que no sirve de mucho pero te adjunto una captura, fijate el LED de la placa encendido porque Trans es true y los datos que está enviando el Nano y recibiendo la terminal virtual (datos aleatorios como pasaría en una placa real que no tiene nada conectado al pin A0)


Si acaso probaste el código de #16, ¿al menos el LED de la placa enciende cuando envías "DATAON"?


Esto no sería muy normal pero no creo que se deba al Nano.
A ver si buscamos solucionar algo en el código que en realidad no es por culpa de arduino.
¿Qué programa usas para enviar/recibir datos?
¿Sobre cuál S. O.?
¿Estás usando el USB de Nano o cableaste los pines 0 y 1?

La diferencia siempre esta en como tiene uno seteado lo que sigue a continuación de los datos, me refiero a CR y LF.

Por eso usé trim(). :wink: