Go Down

Topic: substituir delay por Millis não funciona no laço for (Read 368 times) previous topic - next topic

wfranco

Oi pessoal, estou quebrando a cabeça para tentar substituir o delay pelo millis() mas não estou conseguindo, a ideia é ligar 6 leds usando as saidas PWM para dimerizar eles fiz um for aumentado a intensidade do brilho passo a passo e deixando eles acessos depois de um delay eles começam a apagar suavemente, funciona perfeitamente com o delay mas com o millis() não tive muito sucesso, se alguem puder dar uma dica ficaria grato, já tentei usar varios exemplos aqui do fórum e da IDE do blink  sem delay mas no meu caso o laço for dificulta um pouco. segue abaixo o código que uso com o delay.

Code: [Select]
// Ativar o fade nas luzes
      Serial.println("ligando luzes");
      for (int i = 0; i <= 110; i += 2) {
        analogWrite(ledPin, i);
        delay(20);
      }
      for (int j = 0; j <= 110; j += 2) {
        analogWrite(ledPin2, j);
        delay(20);
      }
      for (int k = 0; k <= 110; k += 2) {
        analogWrite(ledPin3, k);
        delay(20);
      }

      for (int i = 0; i <= 110; i += 2) {
        analogWrite(ledPin4, i);
        delay(20);
      }
      for (int j = 0; j <= 110; j += 2) {
        analogWrite(ledPin5, j);
        delay(20);
      }
      for (int k = 0; k <= 110; k += 2) {
        analogWrite(ledPin6, k);
        delay(20);

      }


      delay(2000);


      Serial.println("Desligando luzes");
      for (int l = brilho; l >= 0; l -= 2) {
        analogWrite(ledPin, l);
        delay(20);
      }

      for (int p = brilho; p >= 0; p -= 2) {
        analogWrite(ledPin2, p);
        delay(20);
      }

      for (int r = brilho; r >= 0; r -= 2) {
        analogWrite(ledPin3, r);
        delay(20);
      }


      for (int l = brilho; l >= 0; l -= 2) {
        analogWrite(ledPin4, l);
        delay(20);
      }

      for (int p = brilho; p >= 0; p -= 2) {
        analogWrite(ledPin5, p);
        delay(20);
      }

      for (int r = brilho; r >= 0; r -= 2) {
        analogWrite(ledPin6, r);
        delay(20);
      }

bubulindo

Porque não simplificar um pouco mais??

Code: [Select]


#define TEMPORIZACAO   2
#define SUBIDA 1
#define DESCIDA 0
#define INTERVALO 2000


unsigned char pinos[6] = {pino1, pino2, pino3, pino4, pino5, pino6};//aqui ficam todos os pinos
unsigned char estado = SUBIDA; //isto serve para definir se estás a acender ou apagar os LEDs
unsigned char estado_anterior = SUBIDA;
unsigned long tempo = 0;


switch (estado) {

case SUBIDA:
      for (unsigned char pino = 0; pino <= 6; pino ++) { //circular por todos os pinos...
           for (int i = 0; i <= 110; i += 2) {
               analogWrite(pinos[pino], i);
               delay(20);
           }
      }//end for pinos.
      estado = TEMPORIZACAO; //faz uma temporizacao...
      estado_anterior = SUBIDA;
      break;

case DESCIDA:
      for (unsigned char pino = 0; pino <= 6; pino ++) { //circular por todos os pinos...
             for (int l = brilho; l >= 0; l -= 2) {
                  analogWrite(pinos[pino], l);
                  delay(20);
             }
      }//end for pinos
      estado = TEMPORIZACAO;
      estado_anterior = DESCIDA;
      break;

case TEMPORIZACAO:
      if (tempo == 0) tempo == millis(); //se quando chegamos aqui, o tempo é 0, significa que nenhuma temporização está a decorrer.
     
      if (millis() - tempo >= INTERVALO) { //acabou-se a temporização...
           if (estado_anterior == DESCIDA) estado = SUBIDA;
           else estado = DESCIDA;
           tempo = 0; //para a lógica não entrar de novo aqui.
           estado_anterior = TEMPORIZACAO; //preciosismos...
      }
      break;
}


Eu não experimentei este código, mas suponho que funcione. Basicamente isto é uma máquina de estados. O teu sistema tem 3 estados... incrementar, decrementar e um intervalo. Tomei a liberdade de colocar o mesmo intervalo entre ambos os estados.

Sempre que as condições para passar de estado se realizam, ele salta para o outro.
This... is a hobby.

wfranco

OK, realmente ficou mais simplificado o laço for valeu, alterei o seu código para usar a serial como controle, funciona certo, porém o  fluxo do millis() entre subir e descer ainda não temporiza, minha necessidade é um pouco diferente , quando eu mandar um "S" para subir pela serial os leds devem acender da forma que você já alterou no código conta-se um tempo pelo millis() e depois os leds se apagam sozinhos o mesmo serve para a descida apenas invertendo o sentido dos leds.

Este projeto e para uma escada automatizada se eu subir a escada os leds se acendem até o topo depois se apagam e se descer da mesma forma ao contrário, só preciso deste millis() após os leds se acenderem para não parar o Arduíno pois preciso fazer outra verificação neste intervalo.

Code: [Select]
#define TEMPORIZACAO   2
#define SUBIDA 1
#define DESCIDA 0
#define INTERVALO 5000


int pino1 = 2;
int pino2 = 3;
int pino3 = 4;
int pino4 = 5;
int pino5 = 6;
int pino6 = 7;




unsigned char pinos[6] = {pino1, pino2, pino3, pino4, pino5, pino6};//aqui ficam todos os pinos
unsigned char estado = SUBIDA; //isto serve para definir se estás a acender ou apagar os LEDs
unsigned char estado_anterior = SUBIDA;
unsigned long tempo = 0;





void setup() {
  const int pino1 = 2;
  const int pino2 = 3;
  const int pino3 = 4;
  const int pino4 = 5;
  const int pino5 = 6;
  const int pino6 = 7;
  Serial.begin(9600);


}

void loop() {

  if (Serial.available()) {

    unsigned char estado = Serial.read();

    switch (estado) {
      //****aqui eu aciono um botão para acender os leds ou Digito S para subir****
      case 'S': //SUBIDA
        for (unsigned char pino = 0; pino <= 6; pino ++) { //circula por todos os pinos...
          for (int i = 0; i <= 110; i += 2) {
            analogWrite(pinos[pino], i);
            delay(20);
            //****ao final deste for todos os leds devem acender individualmente de 0 a 110 e permenecer acessos até o ultimo led.****
            //****OK todos os led acendem da forma que preciso
          }
        }//end for pinos.
        estado = TEMPORIZACAO; //faz uma temporizacao...
        estado_anterior = SUBIDA;


        //conta tempo
        if (tempo == 0) tempo == millis(); //se quando chegamos aqui, o tempo é 0, significa que nenhuma temporização está a decorrer.

        if (millis() - tempo >= INTERVALO) { //acabou-se a temporização...
          if (estado_anterior == DESCIDA) estado = SUBIDA;
          else estado = DESCIDA;
          tempo = 0; //para a lógica não entrar de novo aqui.
          estado_anterior = TEMPORIZACAO; //preciosismos...
        }
        break;



        //DESCIDA
        for (unsigned char pino = 0; pino <= 6; pino ++) { //circular por todos os pinos...
          for (int l = 110; l >= 0; l -= 2) {
            analogWrite(pinos[pino], l);
            delay(20);
          }
        }//end for pinos
        estado = TEMPORIZACAO;
        estado_anterior = DESCIDA;
        break;
      //****Neste ponto queria por o millis() para contar um tempo de 2 segunos automaticamente.****
      //****E em seguida desligar os leds dimmerizando ao contrário.****
      case 'D': //DESCIDA
        for (unsigned char pino = 0; pino <= 6; pino ++) { //circular por todos os pinos...
          for (int l = 110; l >= 0; l -= 2) {
            analogWrite(pinos[pino], l);
            delay(20);
          }
        }//end for pinos
        estado = TEMPORIZACAO;
        estado_anterior = DESCIDA;
        break;

      case 'T': //TEMPORIZACAO
        if (tempo == 0) tempo == millis(); //se quando chegamos aqui, o tempo é 0, significa que nenhuma temporização está a decorrer.

        if (millis() - tempo >= INTERVALO) { //acabou-se a temporização...
          if (estado_anterior == DESCIDA) estado = SUBIDA;
          else estado = DESCIDA;
          tempo = 0; //para a lógica não entrar de novo aqui.
          estado_anterior = TEMPORIZACAO; //preciosismos...
        }
        break;
    }
  }
}

bubulindo

Ok... o código que tinhas não fazia isso, logo o meu código também não. LOL

Por exemplo, o teu código para descer começa com as escadas estando acesas indo até zero. No entanto, queres que as luzes das escadas fiquem acesas quando sobes e apenas desliguem quando desces?

Como pretendes que os LED's apaguem? Duma vez? Com dimming? Existe temporização ao chegar ao cimo das escadas?

Faz assim, pensa em todos os estados que o teu sistema vai ter... e define-os. Não precisam de ser complexos, apenas o que vai acontecer. Tendo isso torna-se mais simples de fazer o que eu tinha colocado inicialmente mas da forma que tu pretendes.

This... is a hobby.

wfranco

OK, só explicando o fluxo: tenho uma escada e um sensor de presença embaixo e outro em cima, passei pelo sensor de baixo para subir, os led começam a acender de zero a 110,conto um tempo com o millis(),e os led começam a apagar de baixo para cima dimerizando, o mesmo para a descida somente ao contrário

A minha maior duvida esta depois que subo ou desço para poder contar o tempo em MILLIS(),  e assim apagar os leds depois de contar o tempo, por exemplo subi e o ultimo led ja atingiu o seu brilho maximo definido, é neste momento que preciso começar a contar o tempo para poder ir desligando os leds.

poderia tentar fazer uma flag quando
 
Code: [Select]
for (int i = 0; i <= 110; i += 2) {
               analogWrite(pinos[pino], i);
               delay(20);
if((pinos[pino]== 6 && i ==110)) { //conto um tempo em millis e executo a ação de apagar o leds}

bubulindo

Olhando ao código anterior que colocaste, o motivo pelo qual não temporiza como pretendes tem a ver com o facto que após os ciclos for terem terminado, a variável estado muda para TEMPORIZACAO e não faz o resto da lógica que pretendes.

Estou a pensar como será a melhor maneira de fazer isto e assim de repente podes criar dois estados adicionais ao código inicial que coloquei.

Tu tens SUBIDA, DESCIDA e TEMPORIZACAO, poderias adicionar SUBIDA_OFF e DESCIDA_OFF e programar ao mesmo estilo que fiz anteriormente.

A sequencia seria:

SUBIDA
TEMPORIZACAO
SUBIDA_OFF
DESLIGADO

Se activasses a descida, seria semelhante. Isto não me parece a maneira mais eficiente porque o código vai ser extremamente semelhante, mas é uma possibilidade. Outra um pouco mais exótica seria ter um if que depois colocaria os coeficientes correctos nos ciclos for.

Fazendo só para a Subida:

Code: [Select]

#define TEMPORIZACAO   2
#define SUBIDA 'S'
#define DESCIDA 3
#define DESLIGADO 0
#define SUBIDA_OFF 4
#define DESCIDA_OFF  5
#define INTERVALO 5000


unsigned char pinos[6] = {2, 3, 4, 5, 6, 7};//aqui ficam todos os pinos
unsigned char estado = SUBIDA; //isto serve para definir se estás a acender ou apagar os LEDs
unsigned char estado_anterior = SUBIDA;
unsigned long tempo = 0;

void setup() {
  Serial.begin(9600);
  for (unsigned char i = 0; i <= 6; i++){
    pinMode(pinos[i], OUTPUT);
  }
}



void loop() {

  if (Serial.available()) {

    unsigned char rec = Serial.read(); // isto estava errado e não devia compilar. Este método serve para testes apenas!! 
    if (rec == 'S') estado = SUBIDA;
    if (rec == 'D') estado = DESCIDA;
  }

    switch (estado) {
      //****aqui eu aciono um botão para acender os leds ou Digito S para subir****
      case SUBIDA: //SUBIDA
        for (unsigned char pino = 0; pino <= 6; pino ++) { //circula por todos os pinos...
          for (int i = 0; i <= 110; i += 2) {
            analogWrite(pinos[pino], i);
            delay(20);
          }
        }//end for pinos.
        estado = TEMPORIZACAO; //faz uma temporizacao...
        estado_anterior = SUBIDA;
        break;

      case SUBIDA_OFF: //SUBIDA
        for (unsigned char pino = 0; pino <= 6; pino ++) { //circula por todos os pinos...
          for (int i = 110; i >=0; i -= 2) {
            analogWrite(pinos[pino], i);
            delay(20);
          }
        }//end for pinos.
        estado = DESLIGADO;
        estado_anterior = SUBIDA_OFF;
        break;


      case TEMPORIZACAO: //TEMPORIZACAO
        if (tempo == 0) tempo == millis(); //se quando chegamos aqui, o tempo é 0, significa que nenhuma temporização está a decorrer.

        if (millis() - tempo >= INTERVALO) { //acabou-se a temporização...
          switch (estado_anterior) {
             case SUBIDA:
               estado = SUBIDA_OFF
               break;
             case SUBIDA_OFF:
               estado = DESLIGADO;
               break;
             case DESCIDA:
               estado = DESCIDA_OFF
               break;
             case DESCIDA_OFF:
               estado = DESLIGADO;
               break;
          }         
          tempo = 0; //para a lógica não entrar de novo aqui.
          estado_anterior = TEMPORIZACAO; //preciosismos...
        }
        break;
    }
  }
}
This... is a hobby.

bubulindo

Lembrei-me agora que podes fazer uma função que lida com os LEDs, o que depois torna a lógica bastante mais simples de ler.

Mais logo se tiver tempo meto aqui uma possível solução.
This... is a hobby.

wfranco

OK, fiz um teste pagar pegar o momento em que o ciclo de subida termina para marcar este tempo em uma variável e comparar com o millis() veja no código .

Code: [Select]

int pino1 = 2;
int pino2 = 3;
int pino3 = 4;
int pino4 = 5;
int pino5 = 6;
int pino6 = 7;


unsigned long flag;
int pausa = 8000;

unsigned char pinos[6] = {pino1, pino2, pino3, pino4, pino5, pino6};//aqui ficam todos os pinos

void setup() {
  const int pino1 = 2;
  const int pino2 = 3;
  const int pino3 = 4;
  const int pino4 = 5;
  const int pino5 = 6;
  const int pino6 = 7;
  Serial.begin(9600);


}

void loop() {


  if (Serial.available()) {

    unsigned char estado = Serial.read();
    switch (estado) {

      case 's': //SUBIR##########
        Serial.println("SUBINDO");
        for (unsigned char pino = 0; pino <= 5; pino ++) {
          for (int i = 0; i <= 110; i += 2) {
            analogWrite(pinos[pino], i);
            delay(20);
          }
          //tentei colocar aqui uma variavél de controle e comparar ela, para atribuir o valor do millis mas os valores
          //são sempre proximos e simplesmente não conta tempo, tentei também fazer o código sem usar a serial, usei um botão.

          //também coloquei aqui  flag=millis() e etc... também deream os mesmos resultados
         
        }
        Serial.println("flag recebendo millis()");
        flag = millis();
        Serial.println("Valor flag:");/////valor lido neste momento 7104
        Serial.println(flag);


        Serial.println("valor do millis atual");/////valor lido neste momento 7106
        Serial.println( millis());

        if (millis() - flag >= pausa) {
          Serial.println("Entrou no millis");
          for (unsigned char pino = 0; pino <= 5; pino ++) {
            for (int l = 110; l >= 0; l -= 2) {
              analogWrite(pinos[pino], l);
              delay(20);
             
            }

          }
        }
    }
  }
}

bubulindo

Experimentaste o código que meti aqui ontem? Isso deveria fazer o que pretendes para a subida sendo depois simples de criar algo semelhante para a descida.

This... is a hobby.

luisilva

Boa tarde a todos. Já há bastante tempo que não vinha aqui, mas achei este tópico interessante, por isso deixem-me fazer umas sugestões. Penso que a porta série apenas devia ser lida durante o estado DESLIGADO.
Imaginem a seguinte situação:
estão a subir as escadas (estado = SUBIDA) e que entretanto todas as luzes acendem; após isto entra-se no estado TEMPORIZAÇÃO e inicia-se a contagem do tempo; antes de terminar esta contagem de tempo é recebida a mensagem para a DESCIDA.
O que iria acontecer?

Olhando para o que se pretende fazer, também diria que seria mais simples arranjar uma forma em que se substituíssem os estados SUBIDA e DESCIDA por um ACENDE e os estados SUBIDA_OFF e DESCIDA_OFF por um APAGA (o código do estado SUBIDA é muito semelhante ao SUBIDA_OFF e muito semelhante ao DESCIDA e também ao DESCIDA_OFF, sendo assim, podia-se condensar mais, evitando repetições de código).

bubulindo

Viva Luis... há já bastante tempo realmente.

Pelo que entendi a porta série está ali para testar uma vez que o código final será baseado em sensores.

O código que lida com os leds é literalmente igual à excepção da ordem do valor de PWM e dos pinos actuados... daí que os estados definidos não sejam possíveis, na minha opinião, de optimizar. Mas estou aberto a ser corrigido, claro.
This... is a hobby.

luisilva

(...)
Pelo que entendi a porta série está ali para testar uma vez que o código final será baseado em sensores.
(...)
Exactamente por isso é que eu fiz a observação. Se alguém subir as escadas muito rápido e cruzar o sensor do cimo das escadas quando ainda se está na temporização o sistema "vai pensar" que há alguém novo que está a descer as escadas e vai começar a sequência de descida com as luzes todas ligadas. Não há grande problema que o programa fique como está, mas penso que ficada melhor se só se "olhasse" para o sensor quando o sistema está desligado. Por outro lado, também se devia verificar se a pessoa já terminou de subir (ou descer) e só aí entrar para o estado DESLIGADO (mas isto para já talvez seja complicar de mais).

(...)
O código que lida com os leds é literalmente igual à excepção da ordem do valor de PWM e dos pinos actuados... daí que os estados definidos não sejam possíveis, na minha opinião, de optimizar. Mas estou aberto a ser corrigido, claro.
Estava a pensar numa em que se substitui este código:
Code: [Select]

      case SUBIDA: //SUBIDA
        for (unsigned char pino = 0; pino <= 6; pino ++) { //circula por todos os pinos...
          for (int i = 0; i <= 110; i += 2) {
            analogWrite(pinos[pino], i);
            delay(20);
          }
        }//end for pinos.

por este:
Code: [Select]

      case ACENDE:
        for (unsigned char pino = pino_inicial; pino <= 6 && pino >= 0; pino=pino+inc) { //circula por todos os pinos...
          for (int i = 0; i <= 110; i += 2) {
            analogWrite(pinos[pino], i);
            delay(20);
          }
        }//end for pinos.

onde a variável pino_inicial,como o nome indica, guardaria o valor do pino inicial (se sobe pino_inicial = 0 e se desce pino_inicial = 6) e onde a variável inc daria o incremento (se sobe inc = 1 e se desce inc = -1).
Uma coisa semelhante pode ser feita para o estado APAGA (e até bem visto poderia apenas haver um estado ALTERA_LUZES).

NOTA: Já não programo há bastante tempo por isso pode haver algum erro que me passou.
EDIT: e havia mesmo :smiley-sweat:

luisilva

Tudo junto ficaria assim:

Code: [Select]
#define DESLIGADO     0
#define ACENDE        1
#define APAGA         2
#define TEMPORIZACAO  3

#define INTERVALO 5000

unsigned char pinos[6] = {2, 3, 4, 5, 6, 7};//aqui ficam todos os pinos
unsigned char estado = DESLIGADO; //isto serve para definir se estás a acender ou apagar os LEDs
unsigned long tempo = 0;

unsigned char pino_inicial; // define qual é o pino onde se inicia o "varrimento"
unsigned char inc; // define se o incremento é positivo ou negativo

void setup() {
  Serial.begin(9600);
  for (unsigned char i = 0; i <= 6; i++) {
    pinMode(pinos[i], OUTPUT);
  }
}


void loop() {

    switch (estado) {
      case ACENDE:
        for (unsigned char pino = pino_inicial; pino <= 6 && pino >= 0; pino += inc) { //circula por todos os pinos...
          for (int i = 0; i <= 110; i += 2) {
            analogWrite(pinos[pino], i);
            delay(20);
          }
        }//end for pinos.
        estado = TEMPORIZACAO; //faz uma temporizacao...
        break;

      case APAGA:
        for (unsigned char pino = pino_inicial; pino <= 6 && pino >= 0; pino += inc) { //circula por todos os pinos...
          for (int i = 110; i >=0; i -= 2) {
            analogWrite(pinos[pino], i);
            delay(20);
          }
        }//end for pinos.
        estado = DESLIGADO; //desliga o sistema e espera por novo comando.
        break;
        
      case TEMPORIZACAO: //TEMPORIZACAO
        if (tempo == 0) tempo = millis(); //se quando chegamos aqui, o tempo é 0, significa que nenhuma temporização está a decorrer.

        if (millis() - tempo >= INTERVALO) { //acabou-se a temporização...
          estado = APAGA;    
          tempo = 0; //para a lógica não entrar de novo aqui.
        }
        break;

      case DESLIGADO:
        if (Serial.available()) {
      
          unsigned char rec = Serial.read(); // isto estava errado e não devia compilar. Este método serve para testes apenas!!
          if (rec == 'S'){
            estado = ACENDE;
            pino_inicial = 0;
            inc = 1;
          }
          if (rec == 'D') {
            estado = ACENDE;
            pino_inicial = 6;
            inc = -1;
          }
        }
        break;
    }
}

luisilva

(...) porém o  fluxo do millis() entre subir e descer ainda não temporiza (...)
Há um pequeno "bug" no código. Onde está:
Code: [Select]
     if (tempo == 0) tempo == millis(); //se quando chegamos aqui, o tempo é 0, significa que nenhuma temporização está a decorrer.

devia estar:
Code: [Select]
     if (tempo == 0) tempo = millis(); //se quando chegamos aqui, o tempo é 0, significa que nenhuma temporização está a decorrer.

Alterando isto deveria fazer a temporização.

bubulindo

Ups... erro de principiante.

Eu iria ainda mais longe e faria apenas uma função para me ver livre dos laços for.

Code: [Select]
#define CRESCENTE   1
#define DECRESCENTE 2
#define DIMMING_ON  3
#define DIMMING_OFF 4

unsigned char Dimmer(unsigned char sentido, unsigned char estado ) {
  char pinoInc;
  char luzInc;
  unsigned char pinoIni, pinoFin;
  unsigned char luzIni, luzFin;

  if (sentido == CRESCENTE) {
    pinoInc = 1;
    pinoIni = 0;
    pinoFin = 5;
  else {
    pinoInc = -1;
    pinoIni = 5;
    pinoFin = 0;
    }
  if (estado == DIMMING_ON) {
    luzInc = 2;
    luzIni = 0;
    luzFin = 110; 
    }
  else {
    luzInc = 2;
    luzIni = 0;
    luzFin = 110; 
    }
  for (unsigned char pino = pinoIni; pino <= pinoFin; pino += pinoInc) { //circula por todos os pinos...
    for (int i = luzIni; i <= luzFin; i += luzInc) {
      analogWrite(pinos[pino], i);
      delay(20);
      }
    }//end for pinos. 
}


Assim era mais "limpo" dentro da máquina de estados.
This... is a hobby.

Go Up