Quiero cambiar los delay por millis y no entiendo como

He mirado en varias webs y videos de YouTube pero no consigo entender como hacer para cambiar mis delays por millis en el código. Tengo entendido que para mi proyecto es mucho mejor usar millis y por eso quiero cambiarlo. El proyecto es un vehículo que se controla con arduino, un HC-06 y una app creada por mi en AppInventor. Tiene dos motores DC y dos servos para hacer un giro horizontal y otro vertical. Los servos no son del todo precisos y puede ser por el delay porque corriente suficiente tienen. En la parte que controla los motores no he colocado ningún delay pero yo creo que estaría bien ponerle algo de 100 milisegundos aprox. Si me explicáis cómo hacerlo lo agradezco un montón. Dejo por aquí el código:

/*
  Ocupación pines
  0 TX
  1 RX
  2 ECHO ULTRASONIDOS
  3 izqA
  4 TRIG ULTRASONIDOS
  5 izqB
  6 derA
  7
  8
  9 derB
  10 SERVO HORIZONTAL
  11 SERVO VERTICAL
  12
*/

#include <SoftwareSerial.h> // Libreria de Tx Rx Serial para Bluetooth 
#include <Servo.h>// Incluye la Libreria Servo

int bluetoothTx = 0; // bluetooth tx al pin 0
int bluetoothRx = 1; // bluetooth rx al pin 1

SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);

int izqA = 3;
int izqB = 5;
int derA = 6;
int derB = 9;
int vel = 200;            // Velocidad de los motores (0-255)
int estado = 'c';         // inicia detenido

Servo ser_hor, ser_ver; // Creamos los Objetos para los Servos Horizontal y Vertical
int servopos_hor = 90; // Posicion Inicial del Servo Horizontal
int servopos_ver = 90; // Posicion Inicial del Servo Vertical


void setup() {
  Serial.begin(9600);
  bluetooth.begin(9600); // Setea la conexion Serial del Bluetooth al Móvil
  pinMode(derA, OUTPUT);
  pinMode(derB, OUTPUT);
  pinMode(izqA, OUTPUT);
  pinMode(izqB, OUTPUT);
  ser_hor.attach(10);// Servo Horizontal conectado al pin 10
  ser_ver.attach(11);// Servo Vertical conectado al pin 11
}

void loop() {
  //Lee desde el bluetooth y escribe al USB Serial
  if (bluetooth.available() >= 2 )
  {
    estado = Serial.read();
    unsigned int servopos = bluetooth.read();
    unsigned int servopos1 = bluetooth.read();
    unsigned int realservo = (servopos1 * 256) + servopos;
    Serial.println(realservo);

    if (realservo >= 1000 && realservo < 1180) {
      int servo1 = realservo;
      servo1 = map(servo1, 1000, 1180, 0, 180);
      ser_hor.write(servo1);
      Serial.println("ser_hor ON");
      delay(10);

    }

    if (realservo >= 2000 && realservo < 2180) {
      int servo2 = realservo;
      servo2 = map(servo2, 2000, 2180, 0, 180);
      ser_ver.write(servo2);
      Serial.println("ser_ver ON");
      delay(10);

    }
  }
  if (estado == 'a') {       // Boton desplazar al Frente
    analogWrite(derB, vel);
    analogWrite(izqB, 0);
    analogWrite(derA, 0);
    analogWrite(izqA, vel);
  }
  if (estado == 'b') {      // Boton IZQ
    analogWrite(derB, vel);
    analogWrite(izqB, 0);
    analogWrite(derA, 0);
    analogWrite(izqA, 0);
  }
  if (estado == 'c') {     // Boton Parar
    analogWrite(derB, 0);
    analogWrite(izqB, 0);
    analogWrite(derA, 0);
    analogWrite(izqA, 0);
  }
  if (estado == 'd') {      // Boton DER
    analogWrite(derB, 0);
    analogWrite(izqB, 0);
    analogWrite(izqA, vel);
    analogWrite(derA, 0);
  }

  if (estado == 'e') {      // Boton Reversa
    analogWrite(derA, vel);
    analogWrite(izqA, 0);
    analogWrite(derB, 0);
    analogWrite(izqB, vel);
  }
}

Rapidamente... con delay(100) paras TODO el codigo por una decima. No solo lo que pasa donde tu pones la instruccion.
Si quieres que algo se demore una decima puedes hacerlo con milis, guardas el tiempo en una variable...

variable=milis();

y luego solo ejecutas la accion que quieres retrasar si han pasado mas de 100 milisengundos...

if milis()>variable +100;

es decir, si el tiempo actual (el nuevo milis) es mayor al anterior mas 100.
De esta manera el lugar donde de la instruccion IF se ejecuta solo cuando pasan 100 milisegundos, que es lo que querias, pero el resto del codigo sigue fluyendo.

2 Likes

Diferencias

Delay() -> Función nativa del Arduino para detenerse en esa línea X tiempo
Millis() o Micros() -> Función nativa que retorna milisegundos o microsegundos desde que se inició el Arduino (cuando entra en el setup() hasta el momento actual.

¿Qué es un timer en programación?
Un proceso que se ejecuta tras X tiempo. En el caso de Arduino lo puedes construir con micros(), millis() o si tus tiempos son >= 1seg con un RTC.

Timer -> "Si pasa X tiempo haz..."
Delay -> "Detén todo en esta línea".

Ejemplo en proyecto. Quieres que un led haga blink cada segundo mientras un servomotor no para de ir de 0º a 180º. Con delay() no puedes hacer esto sin que se te detenga el servomotor, en cambio con un timer hecho con millis() sí puedes.

Ahora bien, no pienses que cambiar de delay() a millis() es cambiar una línea por otra, porque no funciona así.

¿Cómo hacer un timer con millis()?

Pseudocódigo:
Si tiempo actual es mayor igual que mi intervalo, entonces cambiar el estado.

unsigned long tiempo = 0; 
uin32_t intervalo = 3000; // 3 segundos
setup(){}
loop(){
   //¿El tiempo actual menos el tiempo pasado es mayor o igual que el intervalo que quiero?
  if(millis() - tiempo >= intervalo){
    //acción, por ejemplo apagar un led.
   //importante, actualizar el tiempo
   tiempo = millis();
  }
}

¿Por qué unsigned long? por el tamaño de la variable y para evitar valores negativos (importante para el siguiente punto de abajo)
¿Por qúe restamos y no comparamos tiempo + intervalo >= millis? porque la variable long tiene un límite de tamaño. Pasados 49/50 días con el Arduino sin reiniciar/apagar llegaría al máximo de la memoria con el error consiguiente (Desbordamiento). Si ya de por sí la variable puede llegar al máximo si además le sumamos el intervalo hacemos que llegue al límite incluso antes.

En resumen y para no tener que explicar el tema de la memoria y las variables, APLICA el unsigned y RESTA en el timer. Si tu proyecto va a estar funcionando días lo necesitarás y si no, igual es buena práctica hacer las cosas bien.

1 Like

Tenemos muchos tutoriales en Documentación.
Este esta entre los mejores

pero hay otros 4 que puedes encontrar en Indice de Temas tutoriales de la misma sección.

1 Like

Entendía lo que era el millis el problema era que no sabía como aplicarlo en mi código... Ahora tengo otra duda con la parte de los motores. Para los servos tengo dos sliders en la app, y cuando los muevo me entran los motores en marcha. Es por que coge las letras en ASCII y los convierte en números que están dentro del rango del map de los servos? Creo que es eso pero no estoy seguro. Y en caso de ser así cómo lo soluciono? Cambio las letras por números más grandes que no entren en el rango 0 a 180?

En la lectura del BT haz un Serial.print con el caracter que se envía desde la App. Luego usa los if o un switch para cambiar las posiciones... Ejemplo si letra = A... servo = 45º... No hay más. Saludos.

Muchísimas gracias por tu tiempo. Por último hay algo que me falla con los motores y es lo siguiente. Le doy a avanzar y avanza, le doy a parar y para pero llega un momento en el que no hace nada o que desde que le mando el comando tarda muchísimo en hacerlo y tiene un retraso demasiado grande. No creo que sea por la comunicación bluetooth, debo tener algo mal en la programación, tu lo ves?

Por ejemplo donde tengo los motores con 4 if, es mejor hacer un switch case o poner else if?

Evalúa 4 veces.

 if(c == 0) { }
 else if(c== 1) { }
 else if(c == 2) { }
 else if(c == 3) { }

Evalúa y sale tras coincidencia (con el break por supuesto)

 switch (c) {
  case 0:  break;
  case 1:   break;
  case 2:  break;
  case 3:  break;
 }

Y dado que la comparativa es un char yo me decantaría por el switch pero para gustos colores.

Al final me he decantado por el switch, me parece mas lógico en este caso. Lo otro que he comentado sabrías ayudarme? Mando un par de comandos por la app y al tercero ya no me hacen caso los motores.

Tus comandos son 'a', 'b', 'c' y 'd' como es posible que al tercero no funcione?
Tu problema es como envias la acción y la posicion del servo.

No entiendo muy bien a lo que te refieres... Que debo cambiar? Los comandos que son letras por números por ejemplo? Y a la posición del servo que le ocurre? Ese programa solo con los servos me funcionó bien.

Vale, he conseguido que funcionen por separado los servos y los motores. El problema es el siguiente:
Con la aplicación mando unos valores de 1000 a 1180 para un servo y 2000 a 2180 para el otro, y con el map lo transformo de 0 a 180 para que arduino le diga a que grados tiene que girar.

Para mover los motores mando letras, 'a' 'b' 'c' 'd' y 'e'. En ASCII es del 97 al 101.

El problema es que se solapan, cuando muevo el servo entran en funcionamiento los motores y cuando muevo los motores los servos se mueven. No se como aislar los datos enviados de los servos y motores para que esto no ocurra...

No tiene sentido que utilices la función map() cuando lo resuelves con un par de restas.

servo1 = servo1 - 1000;

y

servo2 = servo2 - 2000;

Saludos

He cambiado bastantes cosas con un compañero y ya funciona todo a las mil y una maravillas. Por si tenéis curiosidad que cómo lo he hecho dejo el código por aquí. Muchas gracias a los que me habéis ayudado.

/*
  Ocupación pines
  0 TX
  1 RX
  2 izqA
  3 izqB
  4 derA
  5 derB
  6 trigger ultrasonidos
  7 echo ultrasonidos
  8
  9
  10 SERVO HORIZONTAL
  11 SERVO VERTICAL
  12
  13
*/

//#include <SoftwareSerial.h> // Libreria de Tx Rx Serial para Bluetooth
#include <Servo.h>// Incluye la Libreria Servo

int bluetoothTx = 0; // bluetooth tx al pin 0
int bluetoothRx = 1; // bluetooth rx al pin 1

const int Trigger = 6;   //Pin digital 2 para el Trigger del sensor
const int Echo = 7;   //Pin digital 3 para el Echo del sensor

//SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);
Servo ser_hor, ser_ver; // Creamos los Objetos para los Servos Horizontal y Vertical
int servopos_hor = 90; // Posicion Inicial del Servo Horizontal
int servopos_ver = 90; // Posicion Inicial del Servo Vertical

int izqA = 2;
int izqB = 3;
int derA = 4;
int derB = 5;
int vel = 180;    // Velocidad de los motores (0-255)
char estado ;     //crea la variable estado que recibirá datos por la app
String string_servo; //cadena de carácteres para indicar la posición al servo

void setup() {
  Serial.begin(9600); // inicia monitor serie a 9600 baudios
  ser_hor.attach(10); // Servo Horizontal conectado al pin 3
  ser_ver.attach(11); // Servo Vertical conectado al pin 11
  pinMode(derA, OUTPUT);
  pinMode(derB, OUTPUT);
  pinMode(izqA, OUTPUT);
  pinMode(izqB, OUTPUT);
  pinMode(Trigger, OUTPUT); //pin como salida
  pinMode(Echo, INPUT);  //pin como entrada
  digitalWrite(Trigger, LOW);//Inicializamos el pin con 0

}

void loop() {

  long t; //timepo que demora en llegar el eco
  long d; //distancia en centimetros

  digitalWrite(Trigger, HIGH);
  delayMicroseconds(10);          //Enviamos un pulso de 10us
  digitalWrite(Trigger, LOW);

  t = pulseIn(Echo, HIGH); //Obtenemos el ancho del pulso
  d = t / 59;              //Escalamos el tiempo a una distancia en cm
  if (d < 8 && d > 4) {    //Si la distancia detectada por el ultrasonidos es entre 4 y 8cm
    analogWrite(derA, 0);
    analogWrite(derB, 0);  //Se detienen los motores para evitar colisión
    analogWrite(izqA, 0);
    analogWrite(izqB, 0);
    delay(100);
    analogWrite(derA, 0);
    analogWrite(derB, vel); //Echa marcha atrás medio segundo
    analogWrite(izqA, vel);
    analogWrite(izqB, 0);
    delay(500);
    analogWrite(derA, 0);
    analogWrite(derB, 0);   //Vuelve a detenerse
    analogWrite(izqA, 0);
    analogWrite(izqB, 0);
    delay(100);
  }

  /*Serial.print("Distancia: ");
    Serial.print(d);      //Enviamos serialmente el valor de la distancia
    Serial.print("cm");
    Serial.println();
    delay(100);          //Hacemos una pausa de 100ms
    //Lee desde el bluetooth y escribe al USB Serial
  */
  if (Serial.available() > 0 ) //Mira a ver si hay datos disponibles por el monitor serie mayores a 0
  {
    estado = Serial.read();    //Lee los datos del monitor serie

    switch (estado) {          //Si estado es 'a','b','c','d','e','f' o ,'g'
      case 'a'://Adelante
        analogWrite(derA, vel);
        analogWrite(derB, 0);
        analogWrite(izqA, 0);
        analogWrite(izqB, vel);
        Serial.println("case 'a'");
        break;

      case 'b'://Izquierda
        analogWrite(derA, 0);
        analogWrite(derB, 0);
        analogWrite(izqA, 0);
        analogWrite(izqB, vel);
        Serial.println("case 'b'");
        break;

      case 'c'://Parar
        analogWrite(derA, 0);
        analogWrite(derB, 0);
        analogWrite(izqA, 0);
        analogWrite(izqB, 0);
        Serial.println("case 'c'");
        break;

      case 'd'://Derecha
        analogWrite(derA, vel);
        analogWrite(derB, 0);
        analogWrite(izqA, 0);
        analogWrite(izqB, 0);
        Serial.println("case 'd'");
        break;

      case 'e'://Marcha atrás
        analogWrite(derA, 0);
        analogWrite(derB, vel);
        analogWrite(izqA, vel);
        analogWrite(izqB, 0);
        Serial.println("case 'e'");
        break;

      case'f'://Espera los datos del slider en la app para mover el servo horizontal
        delay(10); //hace una espera para que lleguen todos los datos
        while (Serial.available()) { //un bucle para que saque todos los datos del serial
          char c = Serial.read(); //lee el dato entrante de la segunda celda del serial
          string_servo += c; //junta los datos entrantes en una cadena de caracteres

        }
        if (string_servo.length() > 0) //se verifica que la cadena tenga un largo mayor a cero
        {
          byte horizontal = string_servo.toInt();//Convierte los datos string_servo a int y lo guarda en horizontal como byte
          ser_hor.write(horizontal); //manda al servo el dato en forma de entero
          string_servo = ""; //limpia la variable para leer posteriormente nuevos datos
          Serial.print(" servo_horizontal ");
          Serial.println(horizontal);
        }
        break;

      case 'g'://Espera los datos del slider en la app para mover el servo vertical
        delay(10); //hace una espera para que lleguen todos los datos
        while (Serial.available()) { //un bucle para que saque todos los datos del serial
          char g = Serial.read(); //lee el dato entrante de la segunda celda del serial
          string_servo += g; //junta los datos entrantes en una cadena de caracteres

        }
        if (string_servo.length() > 0) //se verifica que la cadena tenga un largo mayor a cero
        {
          byte vertical = string_servo.toInt(); //Convierte los datos string_servo a int y lo guarda en vertical como byte
          ser_ver.write(vertical); //manda al servo el dato en forma de entero
          string_servo = ""; //limpia la variable para leer posteriormente nuevos datos
          Serial.print(" servo_vertical ");
          Serial.println(vertical);
        }
        break;
    }

  }
}

Al final seguiste usando los delay e incluso tienes

while (Serial.available()) {

Con lo que este post ha perdido toda lógica.

Por cierto la marcha atrás en caso de detectar un obstáculo no tiene sentido porque detrás puede aparecer uno, por eso todos los dispositivos de limpieza que usan sensores para moverse hacen una rotación haciendo que las ruedas giren en sentidos opuestos las unas de las otras cómo hacen los tanques para moverse sobre su propio eje.

He cambiado bastante el código y como he mencionado antes, funciona perfectamente. El post tiene lógica por que se han resuelto algunas dudas. Que yo haya cambiado el código no borra lo que he aprendido con los anteriores mensajes.

Por favor no me compares un tanque con un robot aspiradora... El ultrasonidos esta en el frontal, si al detectar un objeto se ha detenido, significa que lo que tiene detrás ya lo ha atravesado. Si hay un objeto por alguna razón cósmica sin sentido pues ya lo pasará por encima como buen tanque (y no aspiradora) que es! Un saludo jeje

no comparo un tanque con un robot aspiradora, te explico que esos dispositivos ROTAN no se paran y dan marcha atrás. Sé de lo que hablo porque precisamente he hecho varios proyectos así.
Razónalo de esta manera, ninguna aspiradora de esa si detecta una pared y te pones justo detrás te va a dar en los pies ¿Por qué? porque va a rotar hasta que encuentre el camino libre, tú código lo que hace es ir atrás y le da igual si hay un objeto o no.

A ver si lo entendemos así una rueda debe ir en en sentido horario y otra en antihorario para que rote, y para que lo haga correctamente debes tener en cuenta que deben ir parejos en velocidad.
O lo que es lo mismo la aspiradora debe rotar sobre su eje sin irse en alguna diagonal poco a poco.

Te puedo asegurar que estoy harto cansado de hacer esos proyectos los cuales por cierto se hacen sin delay ya que las ASPIRADORAS (aunque realmente barren) tienen más motores para cumplir con sus funciones de limpieza y tu con delay paras esos motores, leds etc. Es decir tu proyecto es incorrecto no es que yo compare cosas incorrectas, es que yo llevo años programando estas cosas y usted aún no sabe usar millis(), pero no pasa nada, igual debido a ciertas funciones cómo volver hacia los cargadores etc no se usan microcontroladores sino microprocesadores y en vez de Arduino se usa Phyton... pero qué sabré yo...

Y lo siento, habrás aprendido sobre millis() pero si al final en tu post no lo llevas a cabo, tienes el proyecto además mal planteado y cuando te explican cómo debe ser R q R puuuuuuues eso, el post no tiene sentido ya.

Un saludo y suerte con el proyecto confiemos en que tu aspiradora no se use con mascotas en el hogar o irá atropellando marcha atrás a todo perro y gato cada vez que encuentre obstáculo y ánimo también barriendo o succionado con motores detenidos por el uso de delay()

Te explico una cosa simple que se ve cuando un profesor enseña a un alumno programación: LEY DE MURPHY. Si algo puede pasar, acabará pasando. Sea cual sea el lenguaje o el proyecto gran parte de las líneas se usa para validar posibles errores ya sean por culpa del usuario o situaciones que se den. Lo que ocurre es que la gente que programa hoy en día pues programa en base a lo que ve en internet no es que se hayan sacado un título y tú crees que:
una mascota, un niño, una persona caminando cerca de la aspiradora, un objeto que se cae al suelo, un mobiliario que se cambia de posición u otra aspiradora misma son " razones cósmicas ". Yo lo llamo "mal programador" es diferente. Tu proyecto es tan endeble en su lógica que sería imposible hacer funcionar 2 o más con el mismo código en la misma habitación pues eventualmente chocarían entre sí. Esto no es una crítica, pero es interesante que cuando la gente que algo puede saber da un consejo, alguien que no sepa determina que son "razones cósmicas, osea absurdos" cuando el absurdo lo vemos otros en sus códigos.

Aspiradora detecta pared ¿Qué el perro está detras? RAZÓN COSMICA no plateada, ATROPEYAR, ATROPEYAR, ATROPEYAR!!!! no es una aspiradora es un coche de choques.

Voy a acercarme a la aspiradora de frente para apagarla o cogerla... RAZÓN COSMICA, me detecta y se va hacia la pared a chocar de gratis...

A ver si así no sé, replanteamos un poco los proyectos... Aparte que tu aspiradora deja de hacer funciones por los delay(). Me extiendo mucho pero a ver si así alguno no cae en estos errores.

Madre mia... Si eres un amargado no postees nada, de esta manera no ayudas. Si haces una crítica hazla constructiva por lo menos. Para empezar, no soy ingeniero informático y tampoco he aprendido mucho sobre el tema (no quiero), por lo que mis conocimientos de programación son limitados y básicos. Partiendo de esta premisa deberías entender que cuando alguien pregunta cosas "lógicas" para ti significa que no tiene ese conocimiento por lo que no entiendo tus ganas de sentirte superior diciendo "tal es ilógico", "no se que no tiene sentido"...etc Cuando probablemente haya niños de 15 años programando código 1000 veces mejor que tu, siempre hay alguien mejor, lo que hay que ser es humilde y ayudar, no lo que tu haces.

Mi TANQUE con ORUGAS no es un vehículo autónomo como la aspiradora para vagos. Tengo el móvil en la mano con flechitas para controlarlo. Te lo explico sencillamente. Cuando le doy a avanzar, estoy presente, y veo por donde va a avanzar. Si tengo a mi perro (no tengo perro) delante, no lo voy a utilizar. Es un vehículo para hobbie. No tiene que ser perfecto.

Cuando echa marcha atrás es medio segundo. Como mucho atropella una molécula.

Los delay se lo que hacen, soy consciente de ello, pero lo he probado y no me deja de funcionar nada, por lo que seguiré adelante con ellos.

Es curioso que estés harto de programar pero un lenguaje tan sencillo como el nativo, el español, no lo entiendas y no te hayas dado cuenta aún de que el proyecto no es una aspiradora. Creo que deberías aprender a usar tus capacidades cognitivas, si lo haces, yo aprenderé a usar millis. Un saludo rey de las aspiradoras!