Usando a função millis() ao invés do delay()


#1

Olá!

Sabemos que a função delay() gera uma pausa calculada na execução de uma programação, congelando o status da rotina durante este tempo. Por isso, ao usar o delay, não conseguimos executar nenhuma ação enquanto esta pausa é executada.

O delay é otimo para pequenos exemplos, como o blink:

(...)
void loop() {
    digitalWrite(redLedPin, HIGH);      //Liga o led vermelho
    delay(1000);                        //Mantém o led ligado por 1 segundo
    digitalWrite(redLedPin, LOW);       //Desliga o led
    delay(1000);                        //Mantém o led desligado por 1 segundo
}

O problema é quando precisamos executar outras ações enquanto o delay está acontecendo. Usando o exemplo acima, digamos que a gente queira piscar o led vermelho a cada 1 segundo e, a cada 300ms, piscar o led verde. Apesar de ser possível colocando uma série de delays, o problema fica mais complexo a cada nova instrução.

Para resolver este problema e termos mais autonomia na programação, podemos substituir o delay por outra técnica, que não trava a execução (loop). Para isso usaremos a função millis(). Esta função retorna o tempo em milissegundos desde quando o arduino foi ligado ou reiniciado. Desta forma, conseguimos verificar a quanto tempo nosso programa está em execução.

Sabendo o tempo total podemos, por exemplo, executar uma ação a cada 1 segundo sem travar o loop, basta para isso verificar se se passou 1 segundo desde a última execução. Este seria o modelo:

se (tempoAtual menos ultimoTempoGravado for maior que 1 segundo)
então (acendeOuApagaLed e salva ultimoTempoGravado como tempoAtual)

Veja o exemplo:

const int redLedPin = 13;       //Pino onde está o LED vermelho
int redLedState = LOW;          //Estado inicial do led

long previousMillis = 0;        // Variável de controle do tempo
long redLedInterval = 1000;     // Tempo em ms do intervalo a ser executado

(...)
void loop()
{
	unsigned long currentMillis = millis();    //Tempo atual em ms

	//Lógica de verificação do tempo
	if (currentMillis - previousMillis > redLedInterval) { 
		previousMillis = currentMillis;    // Salva o tempo atual

		//E aqui muda o estado do led
		//verificando como ele estava anteriormente
		if (redLedState == LOW) {
			redLedState = HIGH;
		} else {
			redLedState = LOW;
		}

		digitalWrite(redLedPin, redLedState);
	}
}

agora ficou fácil de realizar qualquer execução sem precisar travar o loop.

E vocês, utilizam outras técnicas?


Arduino sem delay travante
Importância do delay
#2

Oi @rafaheringer, essa técnica é boa, porém tem um problema nela. se após

if (currentMillis - previousMillis > redLedInterval) { ... }

o código executado demorar mais do que redLedInterval, o led vermelho não irá trocar de valor no intervalo correto.
Para programas pequenos isso não faz diferença, mas é sempre bom ter em mente esse detalhe. Já cheguei a procurar biblioteca nos arduino para executar eventos regulares e achei algumas bibliotecas que implementam timers, porém as que eu achei utilizam millis e então possuem o mesmo problema (na descrição de uma delas mesmo comenta sobre esse problema).
Não cheguei a testar na prática, mas pelo que parece para realizar esssas tarefas com mais precisão é preciso utilizar interrupções, que param a execução do código, executam a tarefa especificada (piscar o led, por exemplo), e depois a execução continua de onde parou.
Procurando um pouco, achei esse exemplo:
http://homediyelectronics.com/projects/arduino/arduinotimerinterruptexample/


#3

Também uso o millis().

Eu estou fazendo um projeto para controle de iluminação que precisa de vários timers que podem ter valores diferentes, operando de forma independente.

Para isso criei uma classe para manipular os timers. Dentro do loop eu inicio e faço a verificação do estado dos timers para chavear as saídas.

class Timer
{
    public:
        Timer();
        void setTime(unsigned long time); // Define o tempo em que o timer permanecera ativo
        void setTimeType(int type); // Define a precisao, em milissegundos, segundos ou minutos
        int getTimeType(); // Retorna a precisao definida para o timer
        void startTimer(); // Inicia o timer
        void stopTimer(); // Encerra o timer
        unsigned long getTimeLeft(); // Retorna o tempo restante do timer
        unsigned long getTimeLimit(); // Retorna o tempo definido para o timer permanecer ativo
        boolean getTimerStatus(); // Retorna o estado to timer, ativo ou inativo
    private:
        unsigned long _millis;
        unsigned long _time;
        unsigned long _timeLimit;
        unsigned long _timeLeft;
        unsigned long _timeElapsed;
        unsigned long _initialMillis;
        boolean _status;
        int _type;
        void _handle();
};

Timer::Timer()
{
    _millis = 0;
    _time = 0;
    _timeLimit = 0;
    _timeLeft = 0;
    _timeElapsed = 0;
    _initialMillis = 0;
    _status = false;
    _type = 0;
}

void Timer::setTime(unsigned long time)
{
    _time = time;
}

void Timer::setTimeType(int type)
{
    _type = type;
}

int Timer::getTimeType()
{
    return _type;
}

unsigned long Timer::getTimeLimit()
{
    return _time;
}

void Timer::startTimer()
{
    _status = true;

    _millis = millis();

    _initialMillis = _millis;

    switch(_type)
    {
    case 1: // MS
        _timeLimit = _time;
        break;
    case 2: // SS
        _timeLimit = _time * 1000;
        break;
    case 3: // MI
        _timeLimit = _time * 60000;
        break;
    }
}

void Timer::stopTimer()
{
    _status = false;
}

void Timer::_handle()
{
    if (_status == true)
    {
        _millis = millis();
        _timeElapsed = _millis - _initialMillis;
        if (_timeLimit > _timeElapsed)
        {
            switch(_type)
            {
            case 1: // MS
                _timeLeft = _timeLimit - _timeElapsed;
                break;
            case 2: // SS
                _timeLeft = ceil((_timeLimit - _timeElapsed) / 1000);
                break;
            case 3: // MI
                _timeLeft = ceil((_timeLimit - _timeElapsed) / 60000);
                break;
            }
        }
        else
        {
            _timeLeft = 0;
            _status = false;
        }
    }
    else
    {
        _timeLeft = 0;
    }
}

unsigned long Timer::getTimeLeft()
{
    _handle();
    return _timeLeft;
}

boolean Timer::getTimerStatus()
{
    _handle();
    return _status;
}

#4

Tem uma biblioteca bem legal que eu gosto de usar para fazer temporização:

Ela usa o Timer1 do ATmega328 para marcar tempo. Você pode configurar ela para gerar uma interrupção a cada X microssegundos e chamar uma função. Vem um exemplo “Interrupt” junto com a biblioteca que mostra como piscar um LED usando esse recurso, enquanto se imprime pela serial o número de vezes que o LED piscou, sem uma coisa interferir com a outra.


#5

Eu uso muito a biblioteca Timer http://playground.arduino.cc/Code/Timer

Ela tem algumas funções como oscilar uma porta digital ou instanciar uma função que se repete a cada N milissegundos ou o delay não bloqueante.


#6

Oiii
No meu caso, usei o Delay(). Achei que o programa acabou ficando muito extenso. Gostaria de uma ajuda de como posso usar o Mliis() e “diminuir” esse programa.

Obs.:Ótima explicação! Abraços.

[code]
const int botao = 8;
int estbotao = 0;
int porta = 13;
int f = 0;

void setup () {
Serial.begin(9600);
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
pinMode(8, INPUT);

}
void loop() {
while (porta>=9) //Rotina de Subida
{
digitalWrite(porta, HIGH); //Ascende Led Correspondente aos andares
if (porta==13)
Serial.println(“Primeiro Andar”);
else if (porta==12)
Serial.println(“Segundo Andar”);
else if (porta==11)
Serial.println(“Terceiro Andar”);
else if (porta==10)
Serial.println(“Quarto Andar”);
else if (porta==9)
Serial.println(“Quinto Andar”);
Serial.println(“Tempo no Andar:”); // Rotina para contar o tempo de 20 segundos
delay (5000);
Serial.println(“5 Segundos”);
delay (5000);
Serial.println(“10 Segundos”);
delay (5000);
Serial.println(“15 Segundos”);
delay (5000);
digitalWrite(porta, LOW);
estbotao = digitalRead(botao); // Desliga o Led aceso Correspondente ao andar

if (estbotao==HIGH)                     // Condição para decidir se o botão foi apertado
{
  for(f=porta;f<=12;f++)               // Rotina para Descer ao primeiro andar apos acionar o botao
     {
       digitalWrite(f, HIGH);
       delay (1000);
       digitalWrite(f, LOW);
     }
  porta=12;                            //Acende Led Correspondente ao primeiro andar apos ascionar o botao e conta o tempo de 40 Segundos
  digitalWrite(13, HIGH);
  Serial.println("botao Pressionado");
  Serial.println("Tempo no Andar");
  delay (5000);
  Serial.println("5 Segundos");
  delay (5000);
  Serial.println("10 Segundos");
  delay (5000);
  Serial.println("15 Segundos");
  delay (5000);
  Serial.println("20 Segundos");
  delay (5000);
  Serial.println("25 Segundos");
  delay (5000);
  Serial.println("30 Segundos");
  delay (5000);
  Serial.println("35 Segundos");
  delay (5000);
  digitalWrite(13, LOW);
}
else
  porta=porta -1;                   // Decremento
}

porta=10;
while (porta<=12)                    //Rotina de Descida
{
digitalWrite(porta, HIGH);               //Acende Led Correspondente aos andares
if (porta==12)
   Serial.println("Segundo Andar");
else if (porta==11)
   Serial.println("Terceiro Andar");
else if (porta==10)
   Serial.println("Quarto Andar");
Serial.println("Tempo no Andar");              // Rotina para contar o tempo de 20 segundos
delay (5000);
Serial.println("5 Segundos");
delay (5000);
Serial.println("10 Segundos");
delay (5000);
Serial.println("15 Segundos");
delay (5000);
digitalWrite(porta, LOW);                  // Desliga o Led aceso Correspondente ao andar     
estbotao = digitalRead(botao);
if (estbotao==HIGH)
 {
     for(f=porta;f<=12;f++)              // Rotina para Descer ao primeiro andar apos acionar o botao
     {
       digitalWrite(f, HIGH);
       delay (1000);
       digitalWrite(f, LOW);
     }
  porta=12;
  digitalWrite(13, HIGH);                       //Ascende Led Correspondente ao primeiro andar apos ascionar o botao e conta o tempo de 40 Segundos
  Serial.println("botao Pressionado");
  Serial.println("Tempo no Andar");
  delay (5000);
  Serial.println("5 Segundos");
  delay (5000);
  Serial.println("10 Segundos");
  delay (5000);
  Serial.println("15 Segundos");
  delay (5000);
  Serial.println("20 Segundos");
  delay (5000);
  Serial.println("25 Segundos");
  delay (5000);
  Serial.println("30 Segundos");
  delay (5000);
  Serial.println("35 Segundos");
  delay (5000);
  digitalWrite(13, LOW);
}
else
  porta=porta +1;                        // incremento
}

}[/code]


#7

Eu crio uma struct com as variáveis que um temporizador geralmente tem: enable, reset, efetivo e preset. E depois crio um array dessa struct com a quantidade de temporizadores que vou usar no meu programa. E então usando o timer1 eu vou incrementando os presets e atualizando as variáveis do temporizador. O importante é calibrar e deixar a execução do timer1 pequena o suficiente para não tomar muito tempo da CPU e atrapalhar seu programa.
Acho que essa é mais ou menos a abordagem usada em temporizadores de CLPs…


#8

Olá pessoal, alguém aí pode me ajudar com um codigo q está com sérios problemas de execução
É só pra ele ler tanto o valor do controle IR quanto da serial, mais quando atribuo a serial o controle para de funcionar

=======#include <NewTone.h>

#include <IRremote.h>

const int RECEBE_PINO = 13;
int Led1 = 2;
int Led2 = 3;
int Led3 = 4;
int Led4 = 5;
int Led5 = 6;
int Led6 = 7;
int Led7 = 8;
int Led8 = 9;
int Led9 = 10;
int Led10 = 11;
int inputVariable1 = 0;
int inputVariable = 0;
int altiFalante = 12;
int numero =-5;
IRrecv ir_recebe(RECEBE_PINO);
decode_results codigo_recebido;

void setup() {
pinMode(Led1,OUTPUT);
pinMode(Led2,OUTPUT);
pinMode(Led3,OUTPUT);
pinMode(Led4,OUTPUT);
pinMode(Led5,OUTPUT);
pinMode(Led6,OUTPUT);
pinMode(Led7,OUTPUT);
pinMode(Led8,OUTPUT);
pinMode(Led9,OUTPUT);
pinMode(Led10,OUTPUT);
pinMode(altiFalante,OUTPUT);
Serial.begin(9600);
ir_recebe.enableIRIn();

}
void loop()
{
if (Serial.available()>0){
numero=Serial.read();
Serial.println(numero);
}
{
if (ir_recebe.decode(&codigo_recebido)){
if (codigo_recebido.value !=0 ){
Serial.println(codigo_recebido.value, HEX);
ir_recebe.resume();
}
}

if (codigo_recebido.value == 0xFF08F7){
if (numero==‘1’)
{
inputVariable1 = 10;
NewTone(altiFalante,800,300); // Pino, Frequência, Duraç
}
}
if (codigo_recebido.value == 0xFFC03F)
{
inputVariable1 = 20;
NewTone(altiFalante,800,300); // Pino, Frequência, Duraç

}
if (codigo_recebido.value == 0xFF807F)
{
inputVariable1 = 30;
NewTone(altiFalante,800,300); // Pino, Frequência, Dura
}
if (codigo_recebido.value == 0xFF609F)
{
inputVariable1 = 40;
NewTone(altiFalante,800,300);
}
if (codigo_recebido.value == 0xFF906F)
{
inputVariable1 = 50;
NewTone(altiFalante,800,300); // Pino, Frequência, Duraç
}
if (codigo_recebido.value == 0xFFB847)
{
inputVariable1 = 60;
NewTone(altiFalante,100,300); // Pino, Frequência, Duraç
}
if (codigo_recebido.value == 0xFFF807)
{
inputVariable1 = 70;

NewTone(altiFalante,100,300); // Pino, Frequência, Duraç
}
if (codigo_recebido.value == 0xFF804F)
{
inputVariable1 = 80;
NewTone(altiFalante,100,300); // Pino, Frequência, Duraç
}
if (codigo_recebido.value == 0xFF9867)
{
inputVariable1 = 90;

NewTone(altiFalante,100,300); // Pino, Frequência, Duraç
}
if (codigo_recebido.value == 0xFF8877)
{
inputVariable1 = -10;
NewTone(altiFalante,100,300); // Pino, Frequência, Duraç

}
if (codigo_recebido.value == 0xFF28D7)
{

}
if (codigo_recebido.value == 0xFFF00F)
{

inputVariable1 = 0;
NewTone(altiFalante,100,300); // Pino, Frequência, Duraç

}
}

digitalWrite (Led6,HIGH);
delay (inputVariable1);
digitalWrite (Led5,HIGH);
digitalWrite (Led7,HIGH);
delay (inputVariable1);
digitalWrite (Led6,LOW);
delay (inputVariable1);
digitalWrite (Led4,HIGH);
digitalWrite (Led8,HIGH);
delay (inputVariable1);
digitalWrite (Led5,LOW);
digitalWrite (Led7,LOW);
delay (inputVariable1);
digitalWrite (Led3,HIGH);
digitalWrite (Led9,HIGH );
delay(inputVariable1);
digitalWrite (Led4,LOW);
digitalWrite (Led8,LOW);
delay (inputVariable1);
digitalWrite (Led2,HIGH);
digitalWrite (Led10,HIGH);
delay (inputVariable1);
digitalWrite (Led3,LOW);
digitalWrite (Led9,LOW);
delay (inputVariable1);
digitalWrite (Led1,HIGH );
delay (inputVariable1);
digitalWrite (Led2,LOW);
digitalWrite (Led10,LOW);
delay (inputVariable1);
digitalWrite (Led1,LOW);
delay (inputVariable1);
digitalWrite (Led1,HIGH);
delay (inputVariable1);
digitalWrite (Led2,HIGH);
digitalWrite (Led10,HIGH);
delay (inputVariable1);
digitalWrite (Led1,LOW);
delay (inputVariable1);
digitalWrite (Led3,HIGH);
digitalWrite (Led9,HIGH) ;
delay (inputVariable1);
digitalWrite (Led2,LOW);
digitalWrite (Led10,LOW);
delay (inputVariable1);
digitalWrite (Led4,HIGH);
digitalWrite (Led8,HIGH);
delay (inputVariable1);
digitalWrite (Led3,LOW);
digitalWrite (Led9,LOW);
delay (inputVariable1);
digitalWrite (Led5,HIGH);
digitalWrite (Led7,HIGH);
delay (inputVariable1);
digitalWrite (Led4,LOW);
digitalWrite (Led8,LOW);
delay (inputVariable1);
digitalWrite (Led6,HIGH);
delay (inputVariable1);
digitalWrite (Led5,LOW);
digitalWrite (Led7,LOW);
delay (inputVariable1);
digitalWrite (Led6,LOW);
delay (inputVariable1);
}


#9

Boa tarde, sou novo no mundo do arduino e estou com uma duvida, montei um circuito em que o arduino efetua a leitura de um sensor indutivo que montei junto com um transistor para funcionar como uma chave pullup no arduino, fiz todo o sistema funcionar mas o unico problema que enfrento é que quando a chave pullup é acionada eu preciso que uma saída fique acionada por exatamente 32ms a partir do primeiro instante de acionamento da chave, mas com os comandos de delay eu consigo que ele conte 32ms depois que a chave foi solta, se eu continuar com ela acionada ele continua com a saida ativa e não inicia a contagem dos 32ms.


#10

Tenho uma aplicação rodando para controlar vários relés com função liga e desliga usando delay por 300 ms. Porém um dos relés necessito de um tempo de 4000 ms o que trava os outros relés quando o comando serial é enviado. Alguém pode me ajudar a otimizar meu código com o millis:

Código:

int val = 0;
void setup() //funcao que é executada ao iniciar a aplicação
{

Serial.begin(9600);

pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
pinMode(14, OUTPUT);
pinMode(15, OUTPUT);
pinMode(16, OUTPUT);
pinMode(17, OUTPUT);
pinMode(18, OUTPUT);

digitalWrite(2, HIGH);
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
digitalWrite(7, HIGH);
digitalWrite(8, HIGH);
digitalWrite(9, HIGH);
digitalWrite(10, HIGH);
digitalWrite(11, HIGH);
digitalWrite(12, HIGH);
digitalWrite(13, HIGH);
digitalWrite(14, HIGH);
digitalWrite(15, HIGH);
digitalWrite(16, HIGH);
digitalWrite(17, HIGH);
digitalWrite(18, HIGH);
}

void loop()
{
if (Serial.available() > 0)
{
val = Serial.parseInt();
//inicio

if (val == 1) { // test for command 1 then turn on LED
digitalWrite(2, LOW); // turn off LED

delay(300);

digitalWrite(2, HIGH); // turn on LED
}
else if (val == 2) // test for command 0 then turn off LED
{
digitalWrite(3, LOW); // turn off LED
delay(300);
digitalWrite(3, HIGH); // turn on LED
}
else if (val == 3) // test for command 0 then turn off LED
{
digitalWrite(4, LOW); // turn off LED
delay(300);
digitalWrite(4, HIGH); // turn on LED
}
else if (val == 4) // test for command 0 then turn off LED
{
digitalWrite(5, LOW); // turn off LED
delay(300);
digitalWrite(5, HIGH); // turn on LED
}
else if (val == 5) // test for command 0 then turn off LED
{
digitalWrite(6, LOW); // turn off LED
delay(300);
digitalWrite(6, HIGH); // turn on LED
}
else if (val == 6) // test for command 0 then turn off LED
{
digitalWrite(7, LOW); // turn off LED
delay(300);
digitalWrite(7, HIGH); // turn on LED
}
else if (val == 7) // test for command 0 then turn off LED
{
digitalWrite(8, LOW); // turn off LED
delay(300);
digitalWrite(8, HIGH); // turn on LED
}
else if (val == 8) // test for command 0 then turn off LED
{
digitalWrite(9, LOW); // turn off LED
delay(300);
digitalWrite(9, HIGH); // turn on LED
}
else if (val == 9) // test for command 0 then turn off LED
{
digitalWrite(10, LOW); // turn off LED
delay(300);
digitalWrite(10, HIGH); // turn on LED
}
else if (val == 10) // test for command 0 then turn off LED
{
digitalWrite(11, LOW); // turn off LED
delay(300);
digitalWrite(11, HIGH); // turn on LED
}
else if (val == 11) // test for command 0 then turn off LED
{
digitalWrite(12, LOW); // turn off LED
delay(300);
digitalWrite(12, HIGH); // turn on LED
}
else if (val == 12) // test for command 0 then turn off LED
{
digitalWrite(14, LOW); // turn off LED
delay(300);
digitalWrite(14, HIGH); // turn on LED
}
else if (val == 13) // test for command 0 then turn off LED
{
digitalWrite(15, LOW); // turn off LED
delay (4000);
digitalWrite(15, HIGH);

}
else if (val == 14) // test for command 0 then turn off LED
{
digitalWrite(16, LOW); // turn off LED
delay(300);
digitalWrite(16, HIGH); // turn on LED
}
else if (val == 15) // test for command 0 then turn off LED
{
digitalWrite(17, LOW); // turn off LED
delay(300);
digitalWrite(17, HIGH); // turn on LED
}
else if (val == 16) // test for command 0 then turn off LED
{
digitalWrite(18, LOW); // turn off LED
delay(300);
digitalWrite(18, HIGH); // turn on LED
}

}
}