Arduino-Executar duas coisas ao mesmo tempo - Aula 9 - AI - Mundo Projetado
> Cursos > Arduino para Iniciantes (AI) > Arduino-Executar duas coisas ao mesmo tempo – Aula 9 – AI
Na aula anterior, vimos sobre as bibliotecas do Arduino.
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.
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.
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:
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.
Programa sem a solução
Primeiro, observe e tente entender o programa abaixo (leia os comentários):
// 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
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
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
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.
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:
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:
void acendeA(){
if(comutarA){ // Se Azul deve ser realmente comutado
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:
//-------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
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.
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 comutarV = false;
acendeA();
acendeV();
if(comutarA){
comutarA=false;
void acendeV(){
if(comutarV){
tempoV=millis();
comutarV=false;
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.
36 thoughts on "Arduino-Executar duas coisas ao mesmo tempo – Aula 9 – AI"
Fiz a programação com 2 botões e 2 led, porém o led não apaga.
Olá. É difícil te ajudar sem saber o seu código ou sua montagem. Qualquer coisa me mande o código por email (mundoprojetado@gmail.com), que aí eu consigo te ajudar.
Thiago maia costa disse:
29 de outubro de 2019 às 00:19
Sua ajuda salvou um projeto meu. Parabens pelo artigo.
29 de outubro de 2019 às 00:28
Caramba, que ótima notícia, Thiago! Eu que agradeço pelo retorno.
Funciona perfeitamente, adicionei mais um led e continuou a funcionar , agora ao incluir este codigo num projeto que tenho ele fica lento e não aceita as alterações de tempo de piscar.
Cheguei a uma conclusão: se eu tiver um if (percent <= 40 ), e dentro deste IF existirem delay's, quando o valor do if , for <= 40, o temporizador fica lento, não consigo entender o porquê , se os void' do temporizador, não estão dentro do Void Loop!
Gostei muito da arquitetura deste temporizador.
Que bom que você gostou, esse temporizador é uma mão na roda mesmo. Posso não ter entendido o problema ao certo, mas qualquer delay que você adicionar em qualquer parte do código interfere em todas as funções. Isto porque o programa fica "preso" no delay e não executa as demais funções. É por isto que ele fica lento e por isto que você deve evitar de utilizar delays neste caso.
E verdade Fabio mas continuo com o mesmo problema que tinha com oo delay(), consegui um delay com: while(millis() < agora + 2000){
Mas o comportamento é o mesmo do delay
lcd.print("Atencao URGENTE");
lcd.print("Falta de agua caixa");
lcd.print("Ver agua na Cisterna");
lcd.print("Ver as bombas");
while(millis() < agora + 2000){
//delay(2000); /
Eai, Carlos. Gostei da solução. Então, o problema que eu consigo identificar agora é no comando "lcd.clear()". Este comando é um tanto quanto lento para ser executado e provavelmente ele é o que está gerando um atraso no programa. Tente substituí-lo pelo comando "lcd.print(" ")" imprimindo vários caracteres vazios (para isso você também vai precisar setar o cursor onde deseja limpar). Não sei exatamente se vai resolver o problema, mas o comando print tem uma execução mais rápida que o comando clear. Tente fazer isto e me fala se ajudou em algo.
6 de março de 2020 às 16:16
não teria que colocar um estado inicial para os leds?
Olá, Johnny. Se não me engano, o Arduino já começa com os pinos em nível baixo (LOW). Como o código é ilustrativo, não me interessa muito definir o estado inicial dos LEDs, embora eu pudesse ter feito isto sim.
numero = ledPinos[random(0, 6)]; // sorteia um número de 5 a 10
digitalWrite(numero, HIGH); // acende o LED no endereço do pino sorteado
delay(tempo); // espera
digitalWrite(numero, LOW); // apaga o mesmo LED
Boa tarde. Dei uma olhada e simulei seu programa aqui e percebi o seguinte: o LED que permanece aceso é o LED ligado ao pino 1. Isto ocorre, pois o pino 1 é utilizado pela comunicação serial do Arduino com o computador (monitor serial). Ou seja, quando o Arduino printa o comando "Ligado" ou "Desligado", este LED é influenciado. Para resolver isto, você pode ou trocar de pino ou então desativar a comunicação serial. O outro problema que percebi é que a sequência de acionamento dos LEDs possui diversos delays. E a rotina de verificação do botão só é executada depois que o programa passa por todos os delays e os acionamentos. Como este processo demora alguns segundos, a rotina de verificação do botão fica atrasada e não consegue responder de forma 'imediata'. Para resolver isto existem algumas lógicas diferentes que você poderia implementar, mas elas vão se basear no conteúdo que apresentei neste post. Isto é, ou você cria uma função delay que fica verificando o botão constantemente ou então sujeita o acionamento dos LEDs a um tempo utilizando o comando millis(). Qualquer dúvida que surgir no processo, pode voltar a perguntar.
Michel De Witte disse:
Tem como alterar seu código para trabalhar com esse tempo após algum acionamento?
Por exemplo, eu utilizo um controle remoto de três botoes e um dos botoes precisa de um delay de três segundos acionado, só que como você disse, nesses três segundos os outros dois botoes do controle remoto não atuam.
Tem sim, é só você manipular a variável 'tempoA' (ou o nome que você der à ela) no momento desejado. Na sua aplicação, você pode, por exemplo, ficar verificando os três botões constantemente e, quando o botão de 3 segundos for pressionado, você cria o delay dele com a função 'espera' mostrada no post. Espero que tenha dado para entender. Senão, é só falar que mostro um código de exemplo.
JOAO MANUEL DE LIMA disse:
4 de junho de 2020 às 19:33
Olá, tenho um aquecedor simples no quarto do meu filho, para que ele não fique ligado a noite toda, usei um dht22, pra monitorar a temperatura, mostrar num LCD 16×2 e caso fique abaixo de 17°C, liga o aquecedor via relé. O primeiro problema é que a temperatura fica oscilando entre 17 e 16,9 fazendo com que o aquecedor fique "piscando". Pra resolver coloquei um delay de 10 min pra ligado ou desligado. Eu gostaria que o LCD mostrasse a temperatura em tempo real, mas ele acaba congelando junto com o relé e só se altera a cada 10 min. Me perdi no seu exemplo, entendi a lógica mas não consegui escrevê-lo, pode me dar uma luz?
Olá, João. Achei bem interessante seu projeto. E posso te ajudar sim. Uma forma mais direta de resolver o seu problema seria criar uma função delay parecida com a mostrada no post (função 'espera') para substituir o seu delay de 10 minutos. Desta forma, você usaria uma variável dentro desta função delay para o programa saber se já se passaram os 10 minutos. Aqui no site tem um projeto onde utilizei uma técnica parecida, se quiser dar uma olhada: https://mundoprojetado.com.br/acendimento-automatico-da-luz/.
E, qualquer coisa, pode retornar o contato, se preferir, pelo e-mail: mundoprojetado@gmail.com
Adquiri um kit de arduino e tô aprendendo construindo algumas coisas, consegui deixar funcional, vou tentar refinar mais o código, tô lendo tudo o que você já postou aqui e seu conteúdo tá ajudando bastante. Vou tentar fazer o seu projeto de automação da lâmpada com um sensor de presença PIR (só tenho um ultrassônico). Muito obrigado
7 de junho de 2020 às 19:09
Olá, João. Desculpa a demora a responder. É ótimo saber que o conteúdo está te ajudando. Espero poder manter isto!
O projeto da luz automática com o sensor de presença PIR é de fato uma solução bem viável. Isso porque o sensor ultrassônico não funciona muito bem em superfícies inclinadas, o que atrapalha bastante na detecção. No início deste ano, cheguei a desenvolver uma nova versão para este projeto utilizando um sensor de gestos. Funcionou melhor que o sensor ultrassônico, mas ainda preciso de um tempo para finalizar o projeto e publicá-lo aqui no site.
Agora que tudo funcionou, gostaria de usar um módulo HC-06 pra enviar os dados pro celular e salvá-los, já li e fiz o seu tutorial com o app Inventor, mas não consegui fazer com que ele salve os dados pra analisar depois. Como proceder?
Uma forma de fazer isto é salvar os dados utilizando o elemento "TinyDB" do AppInventor. Ele fica localizado na aba "Storage" na tela de criação do aplicativo. E, com o TinyDB, é possível criar códigos para armazenar/ler dados na memória do celular. Entretanto nunca mexi com este tipo de aplicação, então não sei te dar um bom direcionamento de como criar o código.
Franco Nascimento disse:
A unica problemática do código é que a função millis zera seu valor após um grande tempo do microcontrolador ligado, ou seja, para aplicações de longo prazo, esta logica não é viável
Exatamente! Obrigado pelo comentário.
4 de julho de 2020 às 20:12
Alguem saberia dizer qual é a solução viavel para longo prazo?
Saberiam dizer qual recurso devo usar para longo prazo?
6 de julho de 2020 às 15:09
Olá, Cesar. Dependendo do caso, é interessante utilizar os timers do microcontrolador. Mas o método apresentado funciona bem, o único problema seria quando o millis() atinge seu valor máximo. Para fugir disso, você pode utilizar o comando 'abs': abs(millis()-tempoA) > tempo. Desta forma, quando millis() atingir o valor máximo e zerar, o módulo dele menos o 'tempoA' será maior do que o tempo especificado. Claro que não é uma solução perfeita, mas já resolve um pouco o problema.
Paulo HAbermann disse:
Olá Fábio… Obrigado por compartilhar, muito útil, mas apesar de ter funcionado (parcialmente) tenho um loop que ainda atrapalha. Quando defino um tempo menor que 1s só funciona quando termina o loop que já existia no meu código. O código tem a finalidade de ler a hora da internet a cada segundo e a contagem do millis, só funciona depois que termina o delay do meu loop, mas acho que entendi o porque. Preciso criar uma varialvel para a leitura e retirar o delay do loop. vou testar e se não funcionar eu mando o código pra vc. Obrigado.
9 de novembro de 2020 às 12:09
Olá, Paulo. Obrigado pelo comentário. E você está correto na sua ideia, qualquer delay dentro do código vai atrapalhar os processos que deveriam ser executados "ao mesmo tempo". O problema é que é chatinho remover alguns delays do programa. Mas espero que você tenha conseguido. Qualquer coisa, pode perguntar que eu tento ajudá-lo.
Olá Fábio! Obrigado pelo conteúdo do site, aprendi bastante aqui.
Você pode tirar uma dúvida?
Eu tentei adaptar esse código para utilizá-lo com microservos, eu estou com problemas em fazer dois servos funcionarem de forma independente e a minha programação não deu certo.
Os servos não fazem o sweep completo. Eles fazem a ida, esperam o delay e depois voltam.
Basicamente o que eu queria é:
Servo 1: Ir de 0° -> 25° -> 0° a cada 120s
Servo 2: Ir de 0° -> 25° -> 0° a cada 262s
Segue a programação que fiz:
void movS1();
void movS2();
void espera();
unsigned long tempoS1=0;
bool SweepS1 = false;
bool SweepS2 = false;
movS1();
movS2();
void movS1()
if(SweepS1)
for (i= 0;i0;i–)
S1.write (i);
tempoS1=millis();
SweepS1=false;
void movS2()
if(SweepS2)
S2.write (i);
tempoS2=millis();
SweepS2=false;
if(!SweepS1 && millis()-tempoS1>=120000)
SweepS1=true;
if(!SweepS2 && millis()-tempoS2>=262000)
SweepS2=true;
Que Deus abençoe você e sua família! Abraços!
Olá, Fabrício. Cheguei a te responder pelo e-mail. Amém e que Deus abençoe você e sua família também!
16 de junho de 2021 às 00:35
tentei implementar no meu código com botões, eu preciso que o botão A aumente em 0,5 o tempo que pisca, e o botão B diminua 0,5.
eu tentei implementar mas quando coloquei o botão B o led piscou a cada 2s por padrão, em vez de 1s
void padrao();
void botaoA();
void botaoB();
unsigned long tempoP=0;
unsigned long tempoA=0;
unsigned long tempoB=0;
bool comutarP = false;
bool comutarB = false;
while (tempo>0){
padrao();
botaoA();
botaoB();
void padrao(){
// LED pisca
if(comutarP){
tempoP=millis();
comutarP=false;
void botaoA() {
buttonA = digitalRead(2);
while (buttonA>0){
Serial.println(buttonA);
tempo = (tempo + 500);
buttonA = (buttonA-1);
void botaoB(){
if(comutarB){
tempoB=millis();
comutarB=false;
while (buttonB > 0){
Serial.println(buttonB);
tempo = (tempo – 500);
buttonB = (buttonB-1);
if(!comutarP && millis()-tempoP>=tempo){
comutarP = true;
if(!comutarA && millis()-tempoA>=tempo){
if(!comutarB && millis()-tempoB>=tempo){
comutarB = true;
Olá, Rebeca. Te respondi por e-mail.
Aislã disse:
8 de abril de 2022 às 01:59
Fábio você sabe como faço para usar mais de um caractere em char? Ex.: 12, 50, 100 ou uma palavra inteira…tem que usar um outro comando??
8 de abril de 2022 às 22:42
Olá, Aislã. O char por sim só armazena apenas um caractere. Para armazenar uma cadeia de caracteres (cadeia de chars), você precisa criar um vetor de char da seguinte forma:
char teste[] = "ola";
Os colchetes na declaração servem para declarar a variável como vetor, aí ela passa a aceitar mais de um caractere. Normalmente, quando você declara um vetor, você precisa informar também a quantidade de elementos dele, mas nesse tipo de declaração de char não é necessário. De toda forma, no caso acima seria 3 + 1 por causa das 3 letras + o caractere terminador (\0) que é inserido automaticamente. Então, outra forma de declarar o vetor é a seguinte:
char teste[4] = "ola";
Entretanto, a linguagem usada para programar o Arduino (podemos considerar que é o C++) aceita um tipo de variável chamado String, que facilita essa criação de cadeia de caracteres. Então, ao invés dos exemplos acima, você pode simplesmente fazer:
String teste = "ola";
15 de junho de 2022 às 17:38
Tenho u problema parecido no void loop. Preciso processar subrotinas (switch ) para regulagem de valores no display mas só roda sozinha no void loop.
O que devo fazer …?
19 de junho de 2022 às 16:17
Boa tarde, Carlos. Não entendi muito bem seu problema. O que roda sozinha no void loop? Qualquer coisa me mande seu código por e-mail (mundoprojetado@gmail.com) que consigo analisar melhor o problema.