Muito provavelmente, em algum momento, você irá precisar fazer duas coisas ao mesmo tempo. Porém, surge um obstáculo que te impede de fazer isso. Talvez uma função delay atrapalhando? Nesta aula, vamos aprender por que executar duas coisas “ao mesmo” tempo e como fazer isso.

Na aula anterior, vimos sobre as bibliotecas do Arduino.


O que e por que?

A programação é algo sequencial, isto é, cada comando é executado na sequência de outro. Entretanto, há momentos em que a lógica de uma função atrapalha na lógica de outra. E isto inviabiliza que as duas funções sejam executadas “juntas”. Juntas, nesse caso, se refere a intervalos de tempo muito próximos e não exatamente no mesmo microssegundo. Veja o exemplo abaixo para entender melhor.

Você está usando um display e quer desenhar um coração pulsando no canto da tela, mas, ao mesmo tempo, você precisa que o programa verifique se algum botão foi apertado. Considere que seu coração pulsa de 1 em 1 segundo ou em um tempo maior que centenas de milissegundos. Para que o seu coração pulse, você irá utilizar a função delay, que basicamente “congela” o seu programa pelo tempo que você determinar.

Com isso, se o seu programa ficar “congelado”, não será possível conferir se algum botão foi pressionado. A não ser que você aperte o botão, coincidentemente, toda vez que o programa sair do delay. Portanto, você necessita de uma solução para fazer seu coração pulsar de 1 em 1 segundo e também precisa que o programa verifique constantemente (100% do tempo) se algum botão foi apertado.

Sendo assim, vamos ver a solução no tópico abaixo.


Como fazer

Existem diferentes formas de se chegar ao resultado, mas essencialmente todas são parecidas. Porém, vou ensinar o método genérico, que pode ser praticamente copiado e colado para quase toda situação.

Considerações

Antes de ver o método, é preciso entender alguns comandos importantes. Existe uma função chamada millis() que retorna o tempo, em milissegundos, desde que o Arduino começou a rodar o programa. Esse tempo é zerado toda vez que o Arduino é desligado ou resetado. E ele é como se fosse um relógio, que, quando chamado, nos diz as “horas”.

É importante mencionar que esse valor tem o objetivo de contar muito tempo, então ele retorna um valor do tipo unsigned long. Isto quer dizer que, é uma variável que não usa um bit para guardar valores negativos (unsigned) e que usa mais bytes para armazenar dados (long).

Portanto, ela é capaz de guardar valores na seguinte faixa [0, 4,294,967,295]. E se você fizer as contas, é possível perceber que o Arduino irá contar até cerca de 50 dias. Depois dos 50 dias, o valor volta para zero e o ciclo continua.

Agora, para ajudar na explicação do método, considere o circuito abaixo:

arduino circuito timer
Circuito feito no Tinkercad

Com base nas aulas anteriores, você já deveria saber montar o esquema. No circuito acima, nós desejamos o seguinte funcionamento:

  • O LED azul tem que comutar de estado a cada 3 segundos (se estava aceso irá apagar e vice-versa)
  • O LED verde tem que comutar de estado a cada 1 segundo (se estava aceso irá apagar e vice-versa)

Os dois não podem interferir um com o outro.

Método genérico

Programa sem a solução

Primeiro, observe e tente entender o programa abaixo (leia os comentários):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Nome das saidas
#define azul 2 // Assimila à palavra "azul" o numero 2
#define verde 3 // Assimila à palavra "verde" o numero 3

//Chamada das funçoes
void acendeA(); // Funçao que controla o acendimento do led Azul
void acendeV(); // Funçao que controla o acendimento do led verde

void setup(){
  pinMode(azul, OUTPUT);
  pinMode(verde, OUTPUT);
}

void loop(){
  acendeA(); //Chama a funçao azul
  acendeV(); //Chama a funçao verde
}

void acendeA(){ // Funçao que controla o acendimento do led Azul
  digitalWrite(azul, !digitalRead(azul)); // Comando que comuta o estado do pino, se era alto vira baixo e vice-versa
  delay(3000);
}

void acendeV(){ // Funçao que controla o acendimento do led Verde
  digitalWrite(verde, !digitalRead(verde)); // Comando que comuta o estado do pino, se era alto vira baixo e vice-versa
  delay(1000);
}

Se você observar, verá que o delay de uma função atrapalha o funcionamento da outra. Pois, quando o LED azul acende, ele entra no delay e congela o programa. E, assim, o LED verde não faz nada até que se passem 3 segundos.

Solução

Vou explicar a lógica da resolução do problema para apenas um dos LEDs, pois o outro será a mesma coisa. Portanto, faremos o seguinte: O LED terá uma variável tempo assimilada a ele, e ela será responsável por armazenar a hora que o LED foi comutado.

unsigned long tempoA=0; //Variavel para armazenar o tempo do LED AZUL

E para guardar o tempo nela é só fazer:

tempoA=millis();

Com isso, criaremos uma função (chamada “espera”) que verificará se o tempo gasto, desde de que o LED foi comutado, é maior do que 3 segundos. Isso traduzido, é o relógio do Arduino (função millis()) menos a hora em que o LED foi comutado (variável tempoA). Essa diferença Δt está ilustrada abaixo:

diferença entre os tempos

Então basta testar se millis()-tempoA>3000 (3segundos em milissegundos é 3000). Porém, ainda falta um elemento, que é uma variavel (booleana) para informar se está na hora ou não de comutar o LED.

bool comutarA = false;

Vou chama-la de comutarA e ela será acionada toda vez que millis()-tempoA for maior que 3000. E claro, nosso LED só irá comutar se ela for TRUE. Sendo assim, nossa função “acenderA” fica assim:

1
2
3
4
5
6
7
void acendeA(){
  if(comutarA){ // Se Azul deve ser realmente comutado
    digitalWrite(azul, !digitalRead(azul)); // Comando que comuta o estado do pino, se era alto vira baixo e vice-versa
    tempoA=millis(); // Marca a hora em que o LED foi comutado
    comutarA=false; // Define comutarA para falso para o programa não executar a rotina indevidamente
  }
}

Observe que comutarA foi setada para falso, para que a rotina seja executada apenas uma vez. E, quem seta ela para verdadeiro é nossa função “espera”. Agora, em nossa função espera, é só verificar se Δt é maior que 3000. Além disso, a nossa função só fará essa verificação se a variável “comutarA” for falsa. Isso porque, o programa deve esperar 3 segundos APÓS o LED comutar, que é quando “comutarA” é falsa. Portanto:

1
2
3
4
5
6
void espera(){
  //-------LED Azul
  if(!comutarA && millis()-tempoA>=3000){ // Se comutarA for falsa(!) e(&&) Δt for maior ou igual a 3000.
    comutarA = true; // Aciona comutarA para avisar o LED que ele deve comutar
  }
}

Código Final

Por fim, basta colocar nossa função espera no void loop também. E está pronto nosso código. Veja o código final abaixo e leia os comentários para entender melhor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Nome das saidas
#define azul 2 // Assimila à palavra "azul" o numero 2
#define verde 3

void acendeA();// Funçao que controla o acendimento do led Azul
void acendeV();// Funçao que controla o acendimento do led verde
void espera(); // Funçao delay

unsigned long tempoA=0; // Variavel para armazenar o tempo do LED AZUL
unsigned long tempoV=0; // Variavel para armazenar o tempo do LED VERDE

bool comutarA = false;
bool comutarV = false;

void setup(){
  pinMode(azul, OUTPUT);
  pinMode(verde, OUTPUT);
}

void loop(){
  acendeA();
  acendeV();
  espera();
}

void acendeA(){
  if(comutarA){
    digitalWrite(azul, !digitalRead(azul)); // Comando que comuta o estado do pino, se era alto vira baixo e vice-versa
    tempoA=millis();
    comutarA=false;
  }
}

void acendeV(){
  if(comutarV){
    digitalWrite(verde, !digitalRead(verde)); // Comando que comuta o estado do pino, se era alto vira baixo e vice-versa
    tempoV=millis();
    comutarV=false;
  }
}

void espera(){
  //-------LED Azul
  if(!comutarA && millis()-tempoA>=3000){
    comutarA = true;
  }
  //-------LED Verde
  if(!comutarV && millis()-tempoV>=1000){
    comutarV = true;
  }
}

Agora é só usar o exemplo dado como base para montar seu próprio projeto e conseguir resolver esse problema que dá dor de cabeça em muitos.

Arduino – Como usar o microcontrolador fora da plaquinha – Aula 10 – AI