cambiar delay por millis acciones bluetooth

hola a todos! y feliz día! :slight_smile:
Estoy inicinándome en Ardruino y aprendiendo a base de prueba y error, pero llevo unos días atascado en un punto y necesito pedir ayuda. He intentado buscarlo por internet y por el foro, pero no encuentro nada, cosas similares pero por mas que lo intento versionar no me funciona, asi que agradezco sumamente la ayuda.
quiero controlar por bluetooth una serie de luces RGB que crean distintos efectos en una lampara impresa en 3d, por ahora funciona todo perfecto, tengo distintas opciones, encendido, colores, fade.. el problema es que cuando llega el comando 9, (es el que realiza el fade), se queda ahi por el fin de los tiempos.
He manejado todo con while, if, break, millis... y nada me funciona.
todas las trancisiones de colores se realizan con la misma función:

void fade(int led, int valor, int valorold){
  
  if (valor > valorold){
    for (int i = valorold; i <= valor; i++){
      analogWrite(led, i);
      delay(20);
    }
  }
    else {
      for (int i = valorold; i >= valor; i--){
      analogWrite(led, i);
      delay(20);
    }
   }

en principio quiero cambiar el delay por millis pero hasta donde he hecho no funciona nada:

void fade(int led, int valor, int valorold){             
  unsigned long tiempo;
   
     
  if (valor > valorold){
    for (int i = valorold; i <= valor; i++){
      tiempo = millis();
      if (millis() - tiempo <= 20){
      analogWrite(led, i);
      }
    }
  }
    else {
      for (int i = valorold; i >= valor; i--){
      tiempo = millis();
      if (millis() - tiempo <= 20){
      analogWrite(led, i);
      }
    }
   }
  }

el primero funciona bien, pero me bloquea ardruino hasta que termine, y en el fade tengo que cuando acabe empiece de nuevo, así que ardruino nunca vuelve a responder, y el segundo va a su bola totalmente.

Muchas gracias y disculpen a los mas avanzados... jeje, un saludo!!

Hola.

Va "a su bola" porque if (millis() - tiempo <= 20) es cierto desde el mismo instante en que asignas tiempo = millis(), y escribe todo "de tiron"

No hay forma de evitar que quede "colgado" en espera de los 20 mseg que quieres.
Aunque uses millis() el bucle for() tiene que esperar esos 20 mseg para escribir otro valor de i, por lo tanto el uso de millis no te soluciona nada e incluso te enmaraña la programacion.
Este seria el programa corregido:

void fade(int led, int valor, int valorold)
{             
  unsigned long tiempo;
   
     
  if (valor > valorold)
  {
     for (int i = valorold; i <= valor; i++)
     {
	analogWrite(led,i);
	while((millis() - tiempo) <= 20);  // Espera forzada a 20 mseg
	tiempo=millis();
      }
  }
  else
  {
      for (int i = valorold; i >= valor; i--)
      {
         analogWrite(led, i);
	 while((millis() - tiempo) <= 20);   // Espera forzada a 20 mseg
	 tiempo=millis();
      }
  }
}

La funcion millis() tiene un uso mas apropiado cuando el flujo del programa sigue y periodicamente analizamos si hemos llegado o rebasado el tiempo prefijado.
Aquí no.

Muchas gracias! no entendía muy bien lo de los millis, pero con este ejemplo ya me queda claro, de todas formas como decías no sale del bucle de esta forma.
yo tengo que por serial entran cuatro datos: comando, Red, Green y Blue.
Cuando el comando es 0, los colores cambian en función de la cantidad que mando en sus respectivos colores.
y si el comando es 9, 0,0,0* se inicia un fade por el espectro de color.
todo funciona bien, los datos llegan por bluetooth desde una aplicación movil.
Entonces, yo puedo poner el color cuantas veces quiera y después cuando mando el fade, empieza la transición. el problema está en que quiero poder quitar el fade cuando le mande otro comando diferente al 91, que es el bloque repetitivo. he llegado hasta aquí con la corrección de antes pero como hasta que no sale del bucle no vuelve a recibir datos, no salgo nunca.

</>
void fade(int led, int valor, int valorold)
{             
  unsigned long tiempo;
   
     
  if (valor > valorold)
  {
     for (int i = valorold; i <= valor; i++)
     {
  analogWrite(led,i);
  while((millis() - tiempo) <= 20);  // Espera forzada a 20 mseg
  tiempo=millis();
  if (comando != 91) {
    break;
  }
      }
  }
  else
  {
      for (int i = valorold; i >= valor; i--)
      {
         analogWrite(led, i);
   while((millis() - tiempo) <= 20);   // Espera forzada a 20 mseg
   tiempo=millis();
   if (comando != 91) {
    break;
  }
      }
  }
}  
</>

¿Que puedo hacer? (muchas gracias){

Adrian7557
Lo primero edita tu ultimo post y coloca el codigo del programa entre los tags de code: </>, porque asi es mas facil de leer y de analizar.
A partir de ahí seguimos.

Mientras editas tu post, y para que puedas avanzar en tu programa, haz estas modificaciones y nos cuentas:

//--------------------------
//Esto debe ir al comienzo del programa donde declaras las demas variables:

boolean comando9 = false;
unsigned long tiempo;

//--------------------------
// Cuando recibes un comando <9> ejecutas esto además de lo que ya
// hagas:

boolean comando9 = true;
tiempo=millis();

//---------------------------
// Cuando recibes un comando que anula al comando <9>
// haces esto ademas de lo que ya hagas:

comando9=false;

//---------------------------
// esto que sigue va colocado en el flujo principal del loop()...

if(comando9 && ((millis() - tiempo) >= 20))
{
  if (valor > valorold)
  {
     for (int i = valorold; (i <= valor) && (comando9); i++) analogWrite(led,i);
  }
  else
  {
      for (int i = valorold; (i >= valor) && (comando9); i--) analogWrite(led, i);
  }
  tiempo=millis();
}
// ... aqui sigue el flujo del loop()...

Espero que entiendas los comentarios para poder hacer las modificaciones y probar.

Perdona lo del code, no lo sabia... llevo rato dandole vueltas y no consigo encajar ese codigo en el programa y tampoco se como hacerlo, he estado probando lo que entiendo de lo que pusiste pero no funciona... no se si me lo estaré inventando todo...

Este es el codigo completo, tal cual me esta funcionando, pero sin poder salir del bucle una vez dentro.

los datos que recibe es en formato 0,0,0,0* que seria comando,rojo,verde,azul. si enviara 9,0,0,0* es el dichoso bucle.

const int ledrojo = 11;            // Conexiones de los LEDs
const int ledverde = 9;
const int ledazul = 10;

int rojo = 0;                      // Variables a utilizar por el programa
int verde = 0;
int azul = 0;
int comando = 0;
 
int rojoold = 255;                 // Valor anterior de las variables
int verdeold = 255;
int azulold = 255;

int pausa;                         // Variable referente al periodo de tiempor entre acción y acción, ecuación F1

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

  pinMode(ledrojo, OUTPUT);
  pinMode(ledverde, OUTPUT);
  pinMode(ledazul, OUTPUT);
 
  digitalWrite(ledrojo, HIGH);
  digitalWrite(ledverde, HIGH);
  digitalWrite(ledazul, HIGH);
 
  Serial.println("Introduce el color en formato 0,R,G,B*");
  
}

void loop() {

  while (Serial.available()>0){               // Función para recibir los datos
    comando = Serial.parseInt();              // Comando selector del programa
    rojo = Serial.parseInt();                 // Lectura rojo
    verde = Serial.parseInt();                // Lectura verde
    azul = Serial.parseInt();                 // Lectura azul
   
    if (Serial.read() == '*'){                // Fin de lectura, entonces ejecuta

   
      if (comando == 0){                          // Inicia el selector de color
        Serial.print("Su color deseado es: ");    // Muestra mensaje 
        Serial.print(rojo);
        Serial.print(", ");
        Serial.print(verde);
        Serial.print(", ");
        Serial.println(azul);
      
        rojo = 255 - constrain(rojo, 0, 255);     // Limita los valores entre 0 y 255 e invierte su valor
        verde = 255 - constrain(verde, 0, 255);
        azul = 255 - constrain(azul, 0, 255);

        pausa = 20;
        
        fade(ledrojo, rojo, rojoold);             // Trancisiones de un color a otro, función declarada al final, F1
        fade(ledverde, verde, verdeold);
        fade(ledazul, azul, azulold);

        rojoold = rojo;                           // Transfiere el valor a la variable antigua para volver a comenzar
        verdeold = verde;
        azulold = azul;

        Serial.println("Color conseguido");   
      }


     if (comando == 9){                                     // Inicia la transición entre colores de forma automática
       Serial.println("Comenzamos la transición");

        pausa = 20;
      
        fade(ledrojo, 255, rojoold);                          // Trancisiones de un color a 0, función declarada al final, F1
        fade(ledverde, 255, verdeold);
        fade(ledazul, 255, azulold);

        pausa = 50;
        
        fade(ledrojo, 0, 255);                              // Comodín inicio trancision
        comando = 91;
     }
  
     while (comando == 91){                                 // Trancisión de colores
       
        fade(ledazul, 0, 255);  
        fade(ledrojo, 255, 0);    
        fade(ledverde, 0, 255);
        fade(ledazul, 255, 0);
        fade(ledrojo, 0, 255);  
        fade(ledverde, 255, 0);
     }
 }
  }
}


void fade(int led, int valor, int valorold)
{             
  unsigned long tiempo;
   
     
  if (valor > valorold)
  {
     for (int i = valorold; i <= valor; i++)
     {
  analogWrite(led,i);
  while((millis() - tiempo) <= 20);  // Espera forzada a 20 mseg
  tiempo=millis();
      }
  }
  else
  {
      for (int i = valorold; i >= valor; i--)
      {
         analogWrite(led, i);
   while((millis() - tiempo) <= 20);   // Espera forzada a 20 mseg
   tiempo=millis();
      }
  }
}

Si no, creo que desisto.... pero es que no tiene que ser tan imposible, todas los leds por ir que compras hacen un fade y si le mandas la nueva señal cambian directamente.
Muchas gracias por todo, que lo consiga o no a he aprendido tres cosas nuevas! jeje

Lo que voy a decir suena complicado pero no imposible.

Las funciones fade deberían ser solo para actualizar valores, no para ejecutar ciclos.
El detalle aquí, es que hay que recrear toda la funcionalidad del programa, pero sin for, while o delay

Hola! gracias por la ayuda, pero no entiendo exactamente que quieres decir, podrías ejemplificarlo? lo que necesito realmente es que si yo estoy ejecutando un fade de colores y le mando otro comando por serial, cambie de acción, no se quede en el bucle encajado.
muchas gracias!

Bueno, la verdad no tengo mucho que decir a menos de que expliques mejor qué debe realizar cada comando.

Comando 0: ajustas brillo mínimo a cada color?
Comando 9: incrementas cada color, desde el mínimo hasta el máximo; o viceversa. Cierto?
Luego alterna colores o cómo es? :confused:

@Adrian7557 lee los PM que te he enviado.

Si yo mando el comando 0 realiza una trancision desde el color que tiene actual al nuevo valor que envio en formato rgb.. 0,0,0,0 * y si mando el comando 9,0,0,0* realiza un fade a traves de todo el espectro de color. Todo funciona bien. El problema es que cuando estoy en el comando 9 y quiero pasar del fade a otro color el rojo por ejemplo. Y le envio el codigo 0,255,0,0* no hace nada porque esta el programa atrapado en un bucle for infinito del fade.... Es una lampara led. Y lo que tiene que hacer basicamente es lo que hace. Todas las bombillas led que vienen con mando. Cambiar el color a deseado. O hacer una trancision entre colores. El problema es que en mi programa puedo cambiar el color tantas veces quiera. Pero una vez que entre en el fade no puedo salir de el... Tieme que ser todo igual pero si mando un un nuevo comando, salga del bucle y cambie a lo nuevo que recibe... Un saludo y feliz año a todos!

Vale Adrian7557, he hecho un programita que quizá te solucione el problema.
Está listo para cargar en el IDE y ejecutarlo.
Pruebalo y ya nos cuentas:

//==========================================
// Control de tiras de led (para Adrian7557)
//  ---<  by Alfaville,   01-01-2017  >---
//==========================================
//
// Colores
#define RO      0   // Color rojo
#define VE      1   // Color verde
#define AZ      2   // Color azul
// Estados
#define NADA    0   // No hace nada
#define UP      1   // Aumenta el brillo
#define DOWN    2   // Disminuye el brillo
#define LON      3   // Pone el led ON
#define LOFF     4   // Pone el led OFF
#define INTER   5   // Pone el led intermitente

// Elegir una fase inmediata mayor que las pausa mayor a usar
//#define FASE   86400000UL  // 24 horas en milisegundos
//#define FASE   28800000UL  // 8 horas en milisegundos
//#define FASE   3600000UL   // 1 hora en milisegundos
//#define FASE   60000UL     // 1 minuto en milisegundos
#define FASE   10000UL     // 10 segundos en milisegundos

// Definimos una estructura para encapsular
// un color LED
struct Led
{
	byte estado;           // Estado el led: 0= no se modifica, 1=aumentar brillo, 2= disminuir brillo
	byte pin;              // Pin del que cuelga
	int actual;            // Brillo actual
	int offset;            // Recorrido hasta el nuevo valor
//	unsigned long pausa;   // Pausa entre steps de cambio (en mseg.)
	unsigned long tiempo;  // Tiempo ultimo step (mseg)
};

int comando = 0;
// Valores actuales
// Usamos <long> para poder expandir los comandos
long rojo = 0;
long verde = 0;
long azul = 0;

unsigned long pausa;     // Duracion de cada step, (mseg)
int nAux;
// Permite invertir la salida hacia los LED
boolean invertir=false;
// Pines de los led
const byte led[3] = {11,9,10};   // los de Adrian7557
//const byte led[3] = {10, 13, 9 };   // los mios
// Declara las estructuras para los tres colores
struct Led tira[3];


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

  pinMode(led[RO], OUTPUT);
  pinMode(led[VE], OUTPUT);
  pinMode(led[AZ], OUTPUT);
 
  digitalWrite(led[RO], HIGH);
  digitalWrite(led[VE], HIGH);
  digitalWrite(led[AZ], HIGH);
 
  // Inicializacion de los colores
  // Rojo-Verde y Azul
  for(byte n=0;n<3;n++)
  {
	tira[n].estado=LOFF;   // OFF
	tira[n].pin=led[n];
	tira[n].actual=0;
	tira[n].offset=0;
	tira[n].tiempo=0;
  }
  
  Serial.println("Introduce el color en formato 0,R,G,B*");
  pausa=100;
}

void loop()
{
	while(Serial.available()>0)              // Función para recibir los datos
	{
		comando = Serial.parseInt();              // Comando selector del programa
		rojo = Serial.parseInt();                 // Lectura rojo
		verde = Serial.parseInt();                // Lectura verde
		azul = Serial.parseInt();                 // Lectura azul
	
		if(Serial.read() == '*')                 // Fin de lectura, entonces ejecuta
		{
        	     Serial.print("Comando ");
		     Serial.print(comando);
		     Serial.print(" = ");
		     Serial.print(rojo);
		     Serial.print(", ");
		     Serial.print(verde);
		     Serial.print(", ");
		     Serial.println(azul);
				
                     switch(comando)
                     {
                          case 0:         // Comando de FADE (color 0-255)
				// Limita los valores al rango: 0 - 255
				// invirtiendo su valor
				rojo = constrain(rojo, 0, 255);
				verde = constrain(verde, 0, 255);
				azul = constrain(azul, 0, 255);
				// Calcula los offset y los estados
                                // Solo cambia el led si color > 0
				// -- Rojo --
				if(rojo>0) asigna(RO,(byte)rojo);
				// -- Verde --
				if(verde>0) asigna(VE,(byte)verde);
				// -- Azul --
				if(azul>0) asigna(AZ,(byte)azul);
                                break;
                          case 1:
                          break;
                          case 2:
                          break;
                          case 3:
                          break;
                          case 4:
                          break;
                          case 5:
                          break;
                          case 6:         // Comando de PAUSA (color <long>)
//                                pausa=(unsigned long)(rojo*100+verde*10+azul);
                                pausa=(unsigned long)rojo;
                                break;
                          case 7:         // Comando de INVERSION
                                invertir = !invertir;
                                break;
                          case 8:         // Comando de INTERMITENCIA (color <long>)
                                // Pone intermitencia si color > 0
                                if(rojo!=0)
                                {
                                     tira[RO].estado=INTER;
                                     tira[RO].tiempo=millis();
                                }
                                if(verde!=0)
                                {
                                     tira[VE].estado=INTER;
                                     tira[VE].tiempo=millis();
                                }
                                if(azul!=0)
                                {
                                     tira[AZ].estado=INTER;
                                     tira[AZ].tiempo=millis();
                                }
                                break;
                          case 9:         // Comando de ON-OFF (color <long>)
                                if(rojo<0) tira[RO].estado=LOFF;
                                else if(rojo>0) tira[RO].estado=LON;

                                if(verde<0) tira[VE].estado=LOFF;
                                else if(verde>0) tira[VE].estado=LON;

                                if(azul<0) tira[AZ].estado=LOFF;
                                else if(azul>0) tira[AZ].estado=LON;
                                break;
                     }
		}
	}
	// Control del tiempo para lanzar los cambios de brillo
        // ¡¡¡¡  Este es el corazón de todo  ¡¡¡¡
	brillo();
}

// Ejecuta un paso de fade para cada uno de los tres
// colores, respetando el tiempo de <pausa>
void brillo()
{
	for(byte n=0;n<3;n++)
	{
		if((((millis()-tira[n].tiempo)%FASE)>=pausa)&&(tira[n].estado!=NADA))
                {
                    switch(tira[n].estado)
		    {
                        case NADA: break;         // Inactivo
                        case UP:                  // Hacer fade up
                              tira[n].actual++;
                              break;
                        case DOWN:                // Hacer fade down
                              tira[n].actual--;
                              break;
                        case LON:                 // Enciende led al 100%
                              tira[n].actual=255;
                              break;
                        case LOFF:                // Apaga led al 0%
                              tira[n].actual=0;
                              break;
                        case INTER:               // Intermitencia segun <pausa>
                              if(tira[n].actual<127) tira[n].actual=255;
                              else tira[n].actual=0;
                              break;
		    }

                    // Chequea si queremos la salida invertida
                    nAux=tira[n].actual;
                    if(invertir) nAux=255-nAux;
		    analogWrite(tira[n].pin,nAux);   // Ejecuta step invertido
                    if(tira[n].offset>0) tira[n].offset--;                          // Ajusta cuenta
		    if((tira[n].offset<=0)&&(tira[n].estado!=INTER)) tira[n].estado=NADA;    // Ajusta estado
		    tira[n].tiempo=millis();                   // Guarda tiempo
                }
	}
}

// --------------------------------------    
// Establece el nuevo valor para un color
// 1º - calcula el offset y lo guarda
// 2º - calcula el estado
// 3º - guarda tiempo actual
// Parametros:
//    <color>= color RO, VE, o AZ
//    <valor>= valor del brillo
// --------------------------------------    
void asigna(byte color,byte valor)
{
	nAux=tira[color].actual-valor;         // Compueba para estado
	if(nAux==0) tira[color].estado=NADA;   // No hay variacion
	if(nAux<0) tira[color].estado=UP;      // Aumentar
	if(nAux>0) tira[color].estado=DOWN;    // Disminuir
	tira[color].offset=abs(nAux);          // Guarda offset
	tira[color].tiempo=millis();           // Toma valor del tiempo
}