[DUDA] como Interrumpir "Modo Automatico" dentro de un switch? [SOLUCIONADO]

Hola, estoy haciendo un proyecto de un robot car (MKR Wifi 1010) controlado tanto manualmente como automaticamente a través de un HC-SR04 mediante una app creada con App Inventor. El problema que me encuentro es cuando activo el comando del modo automatico ya que entra en bucle el codigo y por mucho que le de a parar, el programa sigue sin detenerse nunca.

case 'A': do {
      flag = 1; 
      Automatico();
      Serial.println("flag = 1");
      if (gestureCharacteristic.written()) {
        Serial.println("flag = 0");
        distancia = sensorChoques();
        flag = 0;
        estado = gestureCharacteristic.value();
        maquinaEstados(estado);
      }
    }while(flag = 1);
    break;

He estado informandome y doy por hecho que mi problema es por culpa de los delay() que dejan “pillado” el programa mientras el void va funcionando.
Mi problema es que no soy capaz de convertir mi codigo del void Automatico() con millis().
Si alguno fuese capaz de ayudarme se lo agradeceria mucho, gracias.
Lo suyo seria que cuando le enviase el comando “S” (PARAR()), se parase en cualquier momento.

void Automatico(){
  ADELANTE();
  myservo.write(90);
  delay(500);
  distanciaMedida = sensorChoques();

  if (distanciaMedida <= 20){
    PARAR();
    delay(500);
    myservo.write(0);
    delay(1000);
    sensorChoques();
    distanciaDerecha = sensorChoques();
    delay(500);
    myservo.write(90);
    delay(500);
    delay(1000);
    myservo.write(180);
    delay(1000);
    sensorChoques();
    distanciaIzquierda = distance;
    delay(500);
    myservo.write(90);
    delay(2000);
    if((distanciaDerecha <= 20)&&(distanciaIzquierda <= 20)){
      ATRAS();
      delay(360);
      DERECHA();
      delay(360);
      DERECHA();
      delay(360);
    }
    else if(distanciaDerecha < distanciaIzquierda){
      IZQUIERDA();
      delay(360);
    }
    else if(distanciaDerecha > distanciaIzquierda){
      DERECHA();
      delay(360);
    }
    else{
      ADELANTE();
    }
  }
  else{
    ADELANTE();
  }
}

ROBOTCAR.ino (6.72 KB)

**Moderador:**Hola, bienvenido al foro Arduino.
En la sección proyectos tienes estos dos hilos que debiste haber leído antes de postear

Como tu consulta es para otra sección lo muevo a Software.
En el título no debe usarse la palabra Ayuda. Edita!!
Los códigos se postean usando etiquetas. Edita
Agradezco que hayas posteado debidamente el código. Solo ubicaste mal tu tópico.
Normas del foro

Gracias por decirmelo y redireccionar el post, es la primera vez que posteo algo. Ya he cambiado el titulo. Esque me encuentro con el error que una vez entra en el void Automatico() ya no puedo salir por mucho que envie el comando de Salir y no se como puedo solucionar este error, gracias.

Hi,
Cambia esta instruccion de }while(flag = 1); a }while(flag == 1);

Hola, gracias por contestarme pero esa modificación que me has dicho no ha solucionado mi problema. De hecho sigue quedandose en bucle dentro del void Automatico() y de ahi no sale por mucho que envie la comanda de salir del modo automatico.
A continuación pongo todo el codigo aquí por si le sirve a alguien para poder ayudarme, gracias.

#include <Arduino_MKRRGB.h>
#include <ArduinoBLE.h>
#include <SPI.h>
#include <WiFiNINA.h>
#include <NewPing.h>
#include <Servo.h> 

const char* deviceServiceUuid = "0000FFE0-0000-1000-8000-00805F9B34FB";
const char* deviceServiceCharacteristicUuid = "0000FFE1-0000-1000-8000-00805F9B34FB";


#define LT_R !digitalRead(3)
#define LT_M !digitalRead(1)
#define LT_L !digitalRead(2)


const int pinENA = 6;
const int pinIN1 = 7;
const int pinIN2 = 8;
const int pinIN3 = 9;
const int pinIN4 = 10;
const int pinENB = 11;
  
int velocidad = 255;      

#define echoPin A0 
#define trigPin A1

long duration; // variable for the duration of sound wave travel
int distance; // variable for the distance measurement
int distancia;
int distanciaDerecha = 0;
int distanciaIzquierda = 0;
int distanciaMedida = 0;

int flag = 0;
int estado = 0;
Servo myservo;
BLEService gestureService(deviceServiceUuid); 
// BLE gesture Switch Characteristic 
BLEByteCharacteristic gestureCharacteristic(deviceServiceCharacteristicUuid, BLERead | BLEWrite);

void setup() {
 Serial.begin(9600);      
 pinMode(pinIN1, OUTPUT); 
 pinMode(pinIN2, OUTPUT);
 pinMode(pinENA, OUTPUT);
 pinMode(pinIN3, OUTPUT);
 pinMode(pinIN4, OUTPUT);
 pinMode(pinENB, OUTPUT); 
 pinMode(trigPin, OUTPUT); // Sets the trigPin as an OUTPUT
 pinMode(echoPin, INPUT); // Sets the echoPin as an INPUT
 pinMode(LT_R, INPUT);
 pinMode(LT_M, INPUT);
 pinMode(LT_L, INPUT);
 myservo.attach(5);
 myservo.write(90);
 delay(100);
 // begin ble initialization
 if (!BLE.begin()) {
   //Serial.println("starting BLE failed!");
   while (1);
 }
 // set advertised local name and service UUID:
 BLE.setLocalName("RC Sergi");
 BLE.setAdvertisedService(gestureService);
 // add the characteristic to the service
 gestureService.addCharacteristic(gestureCharacteristic);
 // add service
 BLE.addService(gestureService);
 // set the initial value for the characeristic:
 gestureCharacteristic.writeValue(-1);
 // start advertising
 BLE.advertise();
 //Serial.println("BLE gesture Peripheral");
}
void loop() {
 // listen for BLE peripherals to connect:
 BLEDevice central = BLE.central();
 // if a central is connected to peripheral:
 if (central) {
   // while the central is still connected to peripheral:
   while (central.connected()) {
     // if the remote device wrote to the characteristic,
     if (gestureCharacteristic.written()) {
        distancia = sensorChoques();
        estado = gestureCharacteristic.value();
        maquinaEstados(estado);
      }
   }
 }
}
void maquinaEstados(int estado){
 distancia = sensorChoques();
 switch(estado){
    case 'F': 
      myservo.write(90);
      delay(200);
      distancia = sensorChoques();
      if(distancia>=20){
        Serial.print("distancia ADELANTE: ");
        Serial.println(distancia);
        ADELANTE();
         }
      else{
        ATRAS();
      }
    break;

    case 'E': 
      myservo.write(180);
      delay(500);
      distancia = sensorChoques();
      if(distancia>=20){
        Serial.print("distancia IZQUIERDA: ");
        Serial.println(distancia);
        IZQUIERDA();
         }
      else{
        ATRAS();
      }
    break;

    case 'D': 
      myservo.write(0);
      delay(500);
      distancia = sensorChoques();
      if(distancia>=20){
        Serial.print("distancia DERECHA: ");
        Serial.println(distancia);
        DERECHA();
         }
      else{
        ATRAS();
      }
    break;

    case 'B':
      myservo.write(90); 
      ATRAS();
      
    break;

    case 'A': do {
      flag = 1; 
      Automatico();
      Serial.println("flag = 1");
      if (gestureCharacteristic.written()) {
        Serial.println("flag = 0");
        distancia = sensorChoques();
        flag = 0;
        estado = gestureCharacteristic.value();
        maquinaEstados(estado);
      }
    }while(flag == 1);
    break;
      

    case 'S': 
      PARAR();
    break;

    default: break;
    }

}
void ADELANTE(){
    digitalWrite(pinIN1, HIGH); 
    digitalWrite(pinIN2, LOW);
    digitalWrite(pinIN3, HIGH);
    digitalWrite(pinIN4, LOW);
    analogWrite(pinENA, velocidad);
    analogWrite(pinENB, velocidad);
}
void ATRAS(){
    digitalWrite(pinIN1, LOW); 
    digitalWrite(pinIN2, HIGH);
    digitalWrite(pinIN3, LOW);
    digitalWrite(pinIN4, HIGH); 
    analogWrite(pinENA, velocidad); 
    analogWrite(pinENB, velocidad);
  }
void DERECHA(){    
    digitalWrite(pinIN1, LOW); 
    digitalWrite(pinIN2, LOW);
    digitalWrite(pinIN3, HIGH);
    digitalWrite(pinIN4, LOW);
    analogWrite(pinENA, 0);
    analogWrite(pinENB, velocidad);
  }
void IZQUIERDA(){
    digitalWrite(pinIN1, HIGH); 
    digitalWrite(pinIN2, LOW);
    digitalWrite(pinIN3, LOW);
    digitalWrite(pinIN4, LOW);
    analogWrite(pinENA, velocidad);
    analogWrite(pinENB, 0);

  }
void PARAR(){
    digitalWrite(pinIN1, LOW); 
    digitalWrite(pinIN2, LOW);
    digitalWrite(pinIN3, LOW);
    digitalWrite(pinIN4, LOW);
    analogWrite(pinENA, 0);
    analogWrite(pinENB, 0);
}


void seguimientoLinia(){
 
  if (not LT_M){
    ADELANTE();
  }
  
  else if (not LT_R){
    DERECHA();
    while(LT_R);
  }
  
  else if (not LT_L){
    IZQUIERDA();
    while(LT_L);
  }
  else{
    PARAR();
  }
}

void Automatico(){
  ADELANTE();
  myservo.write(90);
  delay(500);
  distanciaMedida = sensorChoques();

  if (distanciaMedida <= 20){
    PARAR();
    delay(500);
    myservo.write(0);
    delay(1000);
    sensorChoques();
    distanciaDerecha = sensorChoques();
    delay(500);
    myservo.write(90);
    delay(500);
    delay(1000);
    myservo.write(180);
    delay(1000);
    sensorChoques();
    distanciaIzquierda = distance;
    delay(500);
    myservo.write(90);
    delay(2000);
    if((distanciaDerecha <= 20)&&(distanciaIzquierda <= 20)){
      ATRAS();
      delay(360);
      DERECHA();
      delay(360);
      DERECHA();
      delay(360);
    }
    else if(distanciaDerecha < distanciaIzquierda){
      IZQUIERDA();
      delay(360);
    }
    else if(distanciaDerecha > distanciaIzquierda){
      DERECHA();
      delay(360);
    }
    else{
      ADELANTE();
    }
  }
  else{
    ADELANTE();
  }
}
int sensorChoques(){
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  float duration = pulseIn(echoPin, HIGH);
  // Calculating the distance
  distance = duration * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
  return (int)distance;
}

Tu problema parece simple de identificar.
La rutina automático es esta

case 'A': 
		do {
			flag = 1;
			Automatico();
			Serial.println("flag = 1");
			if (gestureCharacteristic.written()) {
				Serial.println("flag = 0");
				distancia = sensorChoques();
				flag = 0;
				estado = gestureCharacteristic.value();
				maquinaEstados(estado);
			}
		}while(flag == 1);
		break;

Fuerzas permanentemente a que flag siga en 1, algo que no entiendo porque lo haces. Lo que se hace es ponerlo en 1 antes del loop yluego solo la acción opuesta te hace salir.
Ahora dependes de tu condicional gestureCharacteristic.written() te devuelva un TRUE para que cambies el flag pero luego veo que llamas a maquinaEstados(estado)??? WHAT!!
O sea haces una recurrencia? estas seguro?
eso a priori esta mal. Muy mal.

Resuelve eso y saldrás de automático

Hola, he probado solucionarlo como me dijiste y he acabado pudiendo salir del Automatico() sin problemas dejando el código así:

void loop() {
 // listen for BLE peripherals to connect:
 BLEDevice central = BLE.central();
 // if a central is connected to peripheral:
 if (central) {
   // while the central is still connected to peripheral:
   while (central.connected()) {
     // if the remote device wrote to the characteristic,
     if (gestureCharacteristic.written()) {
        distancia = sensorChoques();
        estado = gestureCharacteristic.value();
        maquinaEstados(estado);
      }
   }
 }
}
void maquinaEstados(int estado){
 distancia = sensorChoques();
 //flag = 1;
 switch(estado){
    case 'A':
      Automatico();
    break;
 }
}

Como digo, ahora puedo salir del Automatico() sin problemas. Mi inconveniente ahora es que dentro del Automatico() no hace el bucle como deberia (como si hacia con mi anterior código del switch que no me dejaba salir).

void Automatico(){
  ADELANTE();
  myservo.write(90);
  delay(500);
  distanciaMedida = sensorChoques();

  if (distanciaMedida <= 20){
    PARAR();
    delay(500);
    myservo.write(0);
    delay(1000);
    sensorChoques();
    distanciaDerecha = sensorChoques();
    delay(500);
    myservo.write(90);
    delay(500);
    delay(1000);
    myservo.write(180);
    delay(1000);
    sensorChoques();
    distanciaIzquierda = distance;
    delay(500);
    myservo.write(90);
    delay(2000);
    if((distanciaDerecha <= 20)&&(distanciaIzquierda <= 20)){
      ATRAS();
      delay(360);
      DERECHA();
      delay(360);
      DERECHA();
      delay(360);
    }
    else if(distanciaDerecha < distanciaIzquierda){
      IZQUIERDA();                                                    // se queda funcionando aqui
      delay(360);
    }
    else if(distanciaDerecha > distanciaIzquierda){
      DERECHA();                                                      // se queda funcionando aqui
      delay(360);
    }
    else{
      ADELANTE();                                                    // se queda funcionando aqui
    }
  }
  else{
    ADELANTE();                                                     // se queda funcionando aqui
  }
}

Cada vez que entra únicamente hace una lectura del sensor HC_SR04 al principio y luego se queda pillado después de cada if hasta salgo del modo (cosa que con mi anterior problema, esto si que me funcionaba bien).

Para resumir, cuando consigo controlar el void Automatico() y salir, entonces no hace ninguna lectura nueva del sensor una vez que empieza a funcionar el Automatico() y no hace el funcionamiento correcto.

He intentado probar todo lo que se me a ocurrido pero no he sido capaz de solucionarlo. Cuando arreglo una cosa, falla la otra y viceversa, gracias.

Creo que no te lo dije pero hoy estoy vago y no quiero releer todo el hilo.
Hay que evitar el uso de delay() y aprender a programar sin el. Si haces algo debe ser siempre sin delay().

Esta instrucción solo provoca dolores de cabeza y le sirve solo al novato que aprende a prender apagar un led pero logra en ese proceso muchas cosas. instalar el IDE y hacer que Arduino, resistencia, led, fuentes, todo funcione.
Luego dice ahhh pero esto es una pavada!!!
Entonces ve un video y un bestia muestra algo en instructables con 200 delay() y dice.. estoy por el camino correcto, se hace asi. Pero claro, ese código esta JUSTO.
Ese código es similar a algo que quieres hacer y se te ocurre modificarlo y ahi ves que no es fácil.

Por eso lo mejor es comprender que tu Arduino no se comporta como una computadora multitarea y la única manera de hacerlo (salvando la distancia) es usando millis() y programando ordenadamente y de modo prolijo.

Dicho todo esto quise reprogramarte tu secuencia automática pero tiene tantos delay() que la verdad desistí.
Esto es el comienzo, requiere una variable tipo byte para

En globales define

byte estadoAutomatico = 0;
unsigned long startA;

y ahora para comenzar haz esto y sigue... yo no haré todo.

void Automatico(){
  switch(estadoAutomatico) {
    case 0: ADELANTE();
            myservo.write(90);
            estadoAutomatico = 1;
            startA = millis();
            break;
    case 1: if (millis() - startA > 500UL) {
                distanciaMedida = sonar.ping_cm();
                estadoAutomatico = 2;
                startA = millis();
            }
            break;
  }

Pocas veces desisto pero cuando veo un código tan pero tan rebuscado, voy vengo con delay() giro a izq a derecha. No lo entiendo. No tiene comentarios. Dificil de seguir.

mira lo que sigue

PARAR();
    delay(500);
    myservo.write(0);
    delay(1000);
    distanciaDerecha = sonar.ping_cm();
    delay(500);
    myservo.write(90);
    delay(500);
    delay(1000);

Disculpa este comentario pero intento entenderte
Paras, luego nada x 500 mseg luego mueves a 0 el servo,luego nada por 1 segundo luego lees el sensor luego nada por 500 mseg, giro a 90 y luego nada 500 y mas nada por 1000 mseg.

que se supone que hace?

gracias por responderme, respecto al código en general (como muestro en un post mas arriba) consiste en una app que pueda mover el robot arriba,abajo,izq,derecha y tenga 1 modo automático). Todo esto ayudado de un sensor HC-SR04 que para evitar colisiones.

Respecto a tu pregunta de este fragmento:

PARAR();
    delay(500);
    myservo.write(0);
    delay(1000);
    distanciaDerecha = sonar.ping_cm();
    delay(500);
    myservo.write(90);
    delay(500);
    delay(1000);

Mi idea es que si detecta que un objeto se encuentra a menos de 30cm, se pare, gire izq y haga una lectura, gire hacia el otro lado, haga otra lectura y luego haga una comparación que según que lado tiene mas distancia, gire hacia allí. Los delay que he puesto (seguramente equivocado) son con la idea de que el servomotor con el sensor le de tiempo a girarse y pueda hacer la lectura una vez llegue a cada posición).

Bien, no entiendo porque pones solucionado? Si es por mi comentario anterior no me hagas caso.
Puede que consideres que soy exigente pero es lo que siento cuando veo algunos videos donde se abusa del delay() y luego pasa lo que te ocurre a ti.

Esta instrucción solo provoca dolores de cabeza y le sirve solo al novato que aprende a prender apagar un led pero logra en ese proceso muchas cosas. instalar el IDE y hacer que Arduino, resistencia, led, fuentes, todo funcione.
Luego dice ahhh pero esto es una pavada!!!
Entonces ve un video y un bestia muestra algo en instructables con 200 delay() y dice.. estoy por el camino correcto, se hace asi. Pero claro, ese código esta JUSTO.

Este comentario no era para ti, pero si para lo que la gran mayoría cree que es el camino a seguir.
Todos miran un tutorial y creen que lo que se dice ahi es LEY. De ningún modo lo es.
Muchas veces ese código funciona solo para esa situación y eso es lo que quise decir.
He visto muchos programas donde ponen delay por todos lados y claro luego quiere agregar algo muy simple y es casi imposible. Es lo que he dicho o quise decir.

Hacer un código para un robot requiere de un manejo para mi gusto algo mas elaborado que normalmente no se obtiene de los tutoriales salvo casos puntuales que están muy bien hechos.
Para mi deben hacer en base a máquinas de estado y millis() y nunca con delay() salvo que la secuencia sea repetitiva y no haya sensores involucrados.

Tu código puede combinarse con una máquina de estados pero debes ir a Documentación y leer al respecto.
Asi será mas fácil la transición.

Puse SOLUCIONADO porque conseguí solucionar mi problema y ya me funciona correctamente. No me tomé tu comentario a malas ni mucho menos. Agradezco cualquier ayuda, simplemente conseguí arreglar mi fallo y por lo tanto ya no tenia ningun problema.

Ok. Malentendi por tu post#8 que sigues con las consultas y si sigues entonces no esta resuelto pero bueno. Lo cierro entonces.

Cualquier cosa me hablas por privado.