Trilha de aprendizado · Nível 10 · Tutorial 1

Temporização não bloqueante com millis

Controlar duas sequências de LEDs com intervalos independentes no Uno R3 próprio, mantendo o fluxo principal disponível e tratando corretamente o retorno da contagem de millis.

  • Nível: Intermediário
  • Duração: 16 min
  • 7 passos
Temporização não bloqueante com millis

O que você vai percorrer

  1. Esperar o momento sem parar o loop Compare espera bloqueante, espera ativa e consulta temporal curta antes de montar a lógica com millis. 2 min
  2. Consultar o relógio e guardar uma referência Organize leituras e referências temporais antes de construir a condição de tempo decorrido. 2 min
  3. Trocar delay por uma condição de tempo decorrido Substitua a pausa bloqueante por uma comparação de tempo decorrido e uma atualização condicional da referência. 2 min
  4. Continuar funcionando quando a contagem volta a zero Rastreie a comparação temporal quando o contador de `millis()` passa do maior valor de 32 bits para zero e preserve a verificação por subtração sem sinal. 2 min
  5. Controlar dois LEDs com tempos independentes Implemente duas sequências de LEDs independentes no Uno R3 usando uma única leitura de millis por passagem no loop. 4 min
  6. Entender os limites do tempo observado Reconheça que a temporização por millis atende o evento na próxima observação disponível e mantenha o restante do loop curto. 2 min
  7. Validar e explicar a solução completa Consolide a solução com dois LEDs e valide como as verificações temporais independentes mantêm o fluxo principal em atividade. 2 min

O que você vai aprender

  • Substituir uma sequência com delay por verificações de tempo decorrido.
  • Manter duas saídas com marcas de tempo independentes, sem espera ativa.
  • Rastrear a comparação temporal em casos comuns e próximos ao retorno da contagem a zero.
  • Explicar por que o programa continua atendendo outras atividades entre as mudanças dos LEDs.

Antes de começar

  • Cálculos e conversões numéricas em C/C++
  • Controle de LEDs com saídas digitais
  • Decisões com if e else no firmware
  • Funções com parâmetros e retorno

Passo 1 de 7

Esperar o momento sem parar o loop

Compare espera bloqueante, espera ativa e consulta temporal curta antes de montar a lógica com millis.

Uma pausa pode bloquear o programa

O problema da pausa

Quando um sketch chama delay(300), ele fica nessa chamada por cerca de 300 ms. Nesse período, loop() não chega às instruções seguintes: não lê outra entrada, não atualiza outra saída nem toma outra decisão.

Isso pode servir a um teste bem simples. Mas, quando o programa precisa continuar atento a mais de uma atividade, a espera deixa de ser apenas uma pausa visual: ela interrompe o atendimento do restante do fluxo.

Dois jeitos de lidar com o tempo

Comparação entre um fluxo bloqueado por uma longa espera e um fluxo que repete verificações curtas e outras tarefas.

À esquerda, a espera ocupa o fluxo. À direita, o programa verifica se chegou a hora e volta a percorrer as demais tarefas.

A ideia não bloqueante

Consultar e seguir

Na temporização não bloqueante, o programa não fica esperando o relógio avançar. A cada passagem por loop(), ele faz uma consulta rápida: “já é o momento da ação?”.

Se ainda não for, ele simplesmente continua para as próximas instruções. Se for, executa uma ação curta — por exemplo, mudar o nível de um LED — e continua também.

Não há duas partes do sketch executando ao mesmo tempo. Há um único fluxo que retorna rapidamente às tarefas e as intercala em muitas passagens por loop(). Nas próximas etapas, construiremos essa consulta temporal com millis().

Exemplo

Exemplo mental

Imagine um loop() que precisa alternar um LED periodicamente e também verificar uma entrada:

  • Com uma pausa bloqueante, a checagem da entrada só ocorre antes ou depois da pausa.
  • Com uma consulta temporal curta, o loop() pode verificar se é hora de mudar o LED e, na mesma passagem, verificar a entrada.

A resposta à entrada ainda depende de o loop() chegar até sua leitura, mas não fica retida por uma espera longa.

Explique o fluxo disponível

Espera ativa ainda é espera

Trocar delay() por um while que apenas espera o tempo passar não resolve o problema. Embora a condição seja testada repetidamente, o processador permanece dentro do while até ela ser satisfeita.

Como loop() não avança para o restante do código durante esse laço, as outras atividades continuam sem atendimento. Uma solução não bloqueante faz uma verificação curta e sai dela imediatamente quando ainda não é hora de agir.

Compare os três fluxos

Considere um LED que ainda não deve mudar e uma entrada que precisa ser lida durante o intervalo. Compare as três estratégias: delay(), um while usado somente para esperar o tempo passar e uma consulta temporal curta seguida da leitura da entrada.

Em 3 a 5 frases, indique qual estratégia deixa o fluxo disponível e justifique por que as outras duas bloqueiam o atendimento da entrada.

Escreva pelo menos 120 caracteres (0/120).

Passo 2 de 7

Consultar o relógio e guardar uma referência

Organize leituras e referências temporais antes de construir a condição de tempo decorrido.

Os dados da temporização

Três dados, três papéis

millis() consulta quantos milissegundos se passaram desde o início do programa. No Uno R3, seu resultado tem o tipo unsigned long, com 32 bits.

Nesta etapa, separe três papéis:

  • agora: instante lido na passagem atual por loop();
  • anterior: instante registrado quando ocorreu uma mudança anterior;
  • intervalo: duração desejada entre mudanças.

Os três representam tempo e, por isso, devem usar unsigned long. O sufixo UL mantém constantes como 300UL compatíveis com esse tipo.

Linha do tempo com um marco anterior, o instante atual e o trecho que representa o intervalo desejado.

A leitura atual, a referência anterior e o intervalo têm funções diferentes.

Ler o instante atual

Leitura atual a cada passagem

Faça uma nova leitura no começo de cada passagem por loop(). Ela retrata o instante em que o programa está executando aquela passagem.

Leitura de millis

Trecho para estudar; ainda não decide nem altera o LED.

cpp
unsigned long agora = millis();

Dica

O que precisa persistir

A variável agora pode ser local porque será lida de novo na próxima passagem. Já anterior não pode nascer dentro de loop(): se fosse criada ali, perderia a marca guardada assim que a passagem terminasse.

Persistência e referência inicial

Guardar a referência entre passagens

Declare a marca anterior fora de setup() e loop(), para que ela exista durante toda a execução. Em setup(), inicialize-a com uma leitura de millis().

O intervalo também é um valor que permanece disponível para o programa. A palavra const deixa claro que este exemplo não pretende alterá-lo.

Declarações e inicialização

Estrutura de dados temporais. A lógica que usa esses valores será construída na próxima etapa.

cpp
const unsigned long intervalo = 300UL;
unsigned long anterior;

void setup() {
  anterior = millis();
}

void loop() {
  unsigned long agora = millis();

  // Outras atividades curtas podem continuar aqui.
}
Comparação entre uma leitura local renovada em cada ciclo e uma marca persistente mantida fora do loop.

agora é renovado em cada passagem; anterior permanece guardado entre elas.

Pratique: identificar os papéis

Confira a organização

Relacione cada elemento ao seu papel neste esqueleto de temporização. Ainda não há condição de decisão: o foco é organizar os dados corretamente.

Papéis temporais

Associe cada elemento à sua função.

Faça as associações.

Toque em um item e depois no par correspondente.

Complete a declaração

Pratique: preservar a referência

Complete a declaração que permite guardar a referência temporal entre passagens por loop().

cpp
const unsigned long intervalo = 300UL;

 ;

void setup() {
  anterior = millis();
}

void loop() {
  unsigned long agora = millis();
}

Passo 3 de 7

Trocar delay por uma condição de tempo decorrido

Substitua a pausa bloqueante por uma comparação de tempo decorrido e uma atualização condicional da referência.

Verificar sem esperar

A condição de tempo decorrido

A cada passagem por loop(), compare o instante lido em agora com a marca persistente anterior:

agora - anterior >= intervalo

A subtração produz o tempo decorrido desde a última mudança. Se ele já alcançou o intervalo, faça uma ação curta: alterne o LED e registre uma nova marca. Caso contrário, não faça nada nessa condição e o restante de loop() continua imediatamente.

Mantenha agora, anterior e intervalo como unsigned long.

Consultar, comparar e seguir

Diagrama de uma linha do tempo: quando o tempo decorrido ainda é menor que o intervalo, o loop segue; ao atingir ou ultrapassar o intervalo, o LED alterna e a referência é atualizada.

A consulta ocorre rapidamente em toda passagem pelo loop; não há uma espera dentro da condição.

Dica

Por que usar >=

Não use == para procurar exatamente 300 ms. O programa pode consultar em 299 ms e voltar a consultar somente em 301 ms. Com >=, a mudança ocorre na primeira consulta que perceber o limite ou um valor maior.

Atribua anterior = agora apenas dentro do if. Se essa atribuição ficasse fora, a referência seria reiniciada em todas as passagens e o tempo decorrido poderia nunca chegar a 300 ms.

Aplicar a um LED

Sketch completo: LED integrado

Este sketch alterna o LED integrado a cada intervalo observado de 300 ms. Copie-o para a IDE Arduino local, selecione seu Uno R3 e a porta correta, compile e grave. Não é preciso montar componente externo nesta etapa.

Blink não bloqueante

O LED integrado deve alternar aproximadamente a cada 300 ms, enquanto loop() continua sendo executado entre as trocas.

cpp
const unsigned long intervalo = 300UL;
unsigned long anterior;
bool ledLigado = false;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  anterior = millis();
}

void loop() {
  unsigned long agora = millis();

  if (agora - anterior >= intervalo) {
    ledLigado = !ledLigado;
    digitalWrite(LED_BUILTIN, ledLigado ? HIGH : LOW);
    anterior = agora;
  }

  // Outras tarefas curtas podem ser atendidas aqui.
}
Saída esperada
LED integrado: alterna aproximadamente a cada 300 ms.
Monitor Serial: nenhuma saída.

O efeito é físico no LED integrado da placa. Como o sketch não usa `Serial`, o Monitor Serial permanece sem mensagens.

Praticar a lógica

Monte o trecho não bloqueante

Complete a verificação e a atualização. A marca deve mudar somente junto com a alternância do LED.

Condição e nova referência

Preencha as lacunas do trecho.

cpp
if () {
  ledLigado = !ledLigado;
  digitalWrite(LED_BUILTIN, ledLigado ? HIGH : LOW);
  anterior = ;
}

Rastrear o limite

Rastrear uma consulta depois do limite

Considere anterior = 1000UL e intervalo = 300UL. O loop() consulta a condição em 1299 ms e, na passagem seguinte, em 1302 ms. A condição não precisa observar exatamente 1300 ms para funcionar.

Primeira passagem que atende à condição

Qual alternativa descreve corretamente essas duas passagens por loop()?

Passo 4 de 7

Continuar funcionando quando a contagem volta a zero

Rastreie a comparação temporal quando o contador de millis() passa do maior valor de 32 bits para zero e preserve a verificação por subtração sem sinal.

Visualize o retorno a zero

Um relógio que dá a volta

millis() devolve um contador unsigned long de 32 bits. No Uno R3, ele avança em milissegundos desde o início do programa, mas não cresce para sempre: depois de aproximadamente 49,7 dias, passa do maior valor representável para 0.

Esse retorno não exige um caso especial quando a condição usa a forma já aprendida:

agora - anterior >= intervalo

Como os valores são sem sinal, a subtração acompanha a contagem cíclica. Para a aplicação funcionar corretamente, use intervalos muito menores que uma volta completa e avalie a condição com frequência.

A contagem é cíclica

Diagrama circular mostrando uma contagem de 32 bits que passa do maior valor para zero, com um intervalo curto atravessando essa passagem.

A passagem pelo zero continua pertencendo à mesma sequência de valores sem sinal.

A subtração conserva o intervalo curto

Exemplo

Rastreamento através do retorno

Considere uma referência registrada pouco antes do retorno e uma consulta feita logo depois:

  • anterior = 4294967200UL
  • agora = 80UL

A subtração sem sinal calcula agora - anterior dentro da faixa de 32 bits. Embora a representação numérica tenha voltado a valores baixos, o resultado é 176 ms: o tempo realmente transcorrido entre essas duas marcas.

Calcule o intervalo decorrido

Acompanhe a mesma referência sem precisar esperar dias nem alterar o relógio da placa. Os valores servem apenas para rastrear a expressão.

Exemplo com intervalo desejado de 150 ms.
anterioragoraagora - anteriorAtingiu 150 ms?
4294967200429496725050 msNão
4294967200096 msNão
429496720080176 msSim

Use a forma que resiste ao retorno

Atenção

Por que não somar o prazo?

Não troque a subtração por uma comparação de “prazo” como esta:

agora >= anterior + intervalo

Se anterior = 4294967200UL e intervalo = 150UL, a soma dá a volta e resulta em 54UL. Antes de transcorrer 150 ms, por exemplo quando agora ainda vale 4294967250UL, a comparação agora >= 54UL seria verdadeira indevidamente.

Mantenha a diferença entre o instante atual e a referência:

agora - anterior >= intervalo

Condição de validade

A subtração modular preserva o tempo decorrido enquanto ele cabe na faixa de 32 bits. Em outras palavras, não pode transcorrer uma volta completa do contador entre a marca e a avaliação.

Neste tutorial, os intervalos são de centenas de milissegundos e loop() consulta o relógio repetidamente, muito antes desse limite. Se uma volta inteira ocorresse sem avaliação, a diferença sozinha não informaria quantas voltas aconteceram nem recuperaria a duração total.

Pratique sem esperar dias

Rastreie três situações

Associe cada situação ao resultado correto. Em todos os casos, considere valores unsigned long de 32 bits e a condição agora - anterior >= intervalo.

Decida sem depender da posição no contador

Faça as associações entre cada conjunto de marcas e sua conclusão.

Toque em um item e depois no par correspondente.

Passo 5 de 7

Controlar dois LEDs com tempos independentes

Implemente duas sequências de LEDs independentes no Uno R3 usando uma única leitura de millis por passagem no loop.

Separar as duas sequências

Duas referências, duas saídas

Cada saída guarda seus próprios dados: uma marca da última mudança e um estado atual. Assim, o LED integrado pode alternar a cada 300 ms enquanto o LED externo alterna a cada 700 ms, sem que uma sequência reinicie a outra.

No início de cada passagem por loop(), faça uma única leitura de millis() em agora. Depois, teste as duas condições em if separados. Se as duas condições forem verdadeiras naquela passagem, os dois blocos são atendidos, um após o outro, sem espera ativa.

Referências independentes

Diagrama com uma leitura compartilhada do tempo alimentando duas verificações independentes: uma para o LED integrado e outra para o LED externo.

A leitura atual pode ser compartilhada; a marca anterior e o estado de cada LED não.

Montar e executar

Atenção

Montagem segura

Com a placa desconectada do USB, monte o LED externo vermelho em série com um resistor de 1 kΩ: pino digital 7 → resistor → ânodo do LED; cátodo do LED → GND. Confira a polaridade antes de energizar.

Nesta prática, alimente a placa somente pelo USB. Não conecte uma fonte externa a VIN nem ao pino de 5 V.

Esquema do LED externo

Esquema de ligação: pino digital 7, resistor de 1 kΩ, ânodo do LED vermelho, cátodo e GND.

Use este esquema apenas como referência visual da montagem: D7 → resistor de 1 kΩ → ânodo; cátodo → GND.

Sketch completo

Copie o sketch para a IDE Arduino local, selecione seu Uno R3 e a porta correta, compile e grave. O LED integrado deve alternar aproximadamente a cada 300 ms; o LED externo, aproximadamente a cada 700 ms. Não há delay(), laço de espera nem necessidade de Monitor Serial.

Dois LEDs sem bloqueio

Sketch para editar, compilar e gravar no Uno R3.

cpp
const byte pinoExterno = 7;
const unsigned long intervaloInterno = 300UL;
const unsigned long intervaloExterno = 700UL;

unsigned long anteriorInterno;
unsigned long anteriorExterno;
bool estadoInterno = LOW;
bool estadoExterno = LOW;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pinoExterno, OUTPUT);

  digitalWrite(LED_BUILTIN, estadoInterno);
  digitalWrite(pinoExterno, estadoExterno);

  anteriorInterno = millis();
  anteriorExterno = anteriorInterno;
}

void loop() {
  unsigned long agora = millis();

  if (agora - anteriorInterno >= intervaloInterno) {
    estadoInterno = !estadoInterno;
    digitalWrite(LED_BUILTIN, estadoInterno);
    anteriorInterno = agora;
  }

  if (agora - anteriorExterno >= intervaloExterno) {
    estadoExterno = !estadoExterno;
    digitalWrite(pinoExterno, estadoExterno);
    anteriorExterno = agora;
  }
}
Saída esperada
LED_BUILTIN alterna aproximadamente a cada 300 ms.
LED vermelho externo no pino 7 alterna aproximadamente a cada 700 ms.
Nenhuma saída é exibida no Monitor Serial.

O resultado é físico, observado nos LEDs da placa e da montagem. O sketch não envia mensagens ao Monitor Serial.

Praticar a independência

Referências separadas

Complete as duas marcas temporais usadas nas verificações. Cada if deve consultar e atualizar somente a referência da saída que ele controla.

Complete as referências

Preencha as marcas corretas, sem criar uma referência compartilhada.

cpp
if (agora -  >= intervaloInterno) {
  estadoInterno = !estadoInterno;
  digitalWrite(LED_BUILTIN, estadoInterno);
  anteriorInterno = agora;
}

if (agora -  >= intervaloExterno) {
  estadoExterno = !estadoExterno;
  digitalWrite(pinoExterno, estadoExterno);
  anteriorExterno = agora;
}

Prever e observar

Duas condições verdadeiras

Em uma passagem por loop(), tanto o intervalo de 300 ms quanto o de 700 ms já foram atingidos. O que ocorre?

Observar a montagem

Após gravar o sketch, descreva em uma ou duas frases o que você observou nos dois LEDs. Inclua se a alternância de um pareceu impedir a do outro.

Escreva pelo menos 30 caracteres (0/30).

Passo 6 de 7

Entender os limites do tempo observado

Reconheça que a temporização por millis atende o evento na próxima observação disponível e mantenha o restante do loop curto.

O instante observado pode chegar depois do prazo

Uma observação, não um despertador

millis() deixa o loop() seguir adiante, mas a condição temporal só é avaliada quando o programa chega ao respectivo if.

Se o intervalo de um LED é 300 ms e o loop() volta a essa verificação aos 340 ms, a mudança ocorre aos 340 ms. O programa não consegue executar retroativamente a ação que seria desejada em 300 ms. A condição com >= aceita esse atendimento tardio.

Temporização não bloqueante significa não ficar esperando o tempo passar. Ela não promete que cada mudança ocorrerá no instante físico exato do limite.

Linha do tempo: prazo e atendimento

Duas linhas do tempo: eventos ideais regulares na parte superior e verificações do loop chegando um pouco depois na parte inferior.

A saída muda na primeira verificação que encontra o tempo decorrido igual ou maior que o intervalo.

A referência passa a ser o instante atendido

Exemplo

Rastreando um atendimento atrasado

Considere uma saída cujo último atendimento foi em 0 ms e cujo intervalo é 300 ms. O loop() só alcança esse if em 340 ms. Como a condição já foi atingida, a saída muda nessa passagem.

Consequência de `anterior = agora`

A política usada neste tutorial atualiza a referência com anterior = agora quando a saída muda. Portanto, depois do atendimento atrasado, a nova espera começa no instante realmente observado.

Atrasos ocasionais deslocam os próximos atendimentos; esta política não os compensa.
Instante observadoTesteAção e referência
0 msinicializaçãoanterior = 0 ms
300 msnenhuma verificaçãonenhuma mudança
340 ms340 - 0 >= 300muda; anterior = 340 ms
639 ms639 - 340 < 300não muda
640 ms ou depoisagora - 340 >= 300próxima mudança

Responsividade depende de voltar ao loop

Não bloqueante não significa instantâneo

Para sinalização simples, esse deslocamento geralmente é adequado: o LED continua alternando e o loop() continua disponível para as demais verificações.

Porém, as atividades entre uma consulta e outra precisam ser curtas. Um cálculo ou laço que ocupe muito tempo atrasa a próxima chegada ao if. Trocar delay() por outro laço que espera o relógio avançar continua bloqueando o fluxo principal.

millis() organiza a decisão temporal; ele não torna automaticamente rápida uma tarefa demorada.

Atualize a referência

Se anterior era 0 ms, o intervalo é 300 ms e a condição é atendida quando agora vale 340 ms, após a mudança anterior passa a ___ ms.

Confira a ideia central

O efeito de uma tarefa demorada

Verdadeiro ou falso: retirar delay() garante que o LED mudará exatamente a cada 300 ms, mesmo se outra tarefa no loop() levar muito tempo para terminar.

Passo 7 de 7

Validar e explicar a solução completa

Consolide a solução com dois LEDs e valide como as verificações temporais independentes mantêm o fluxo principal em atividade.

Reveja o fluxo, não uma espera

Checklist da solução

Uma solução completa com duas saídas temporizadas mantém estes critérios juntos:

  • unsigned long para instantes, marcas e intervalos;
  • uma marca persistente para cada LED;
  • uma única leitura de millis() em agora a cada passagem;
  • comparação por subtração: agora - anterior >= intervalo;
  • dois if independentes;
  • nenhuma chamada a delay() ou laço esperando o relógio avançar.

Assim, cada passagem por loop() consulta as duas condições e termina rapidamente. Se nenhum prazo venceu, nada muda nos LEDs — mas o programa já está livre para começar outra passagem pelo loop.

Uma passagem curta pelo loop

Diagrama com uma leitura de relógio seguida por dois ramos independentes para dois LEDs e uma escala circular mostrando a passagem da contagem máxima para zero.

Os dois ramos usam o mesmo instante agora, mas cada um compara sua própria marca e seu próprio intervalo.

Corrija os pontos essenciais do sketch

Complete a lógica integrada

Complete as três lacunas sem transformar as duas temporizações em uma única referência compartilhada.

cpp
const byte pinoExterno = 7;
const unsigned long intervaloInterno = 300UL;
const unsigned long intervaloExterno = 700UL;

unsigned long anteriorInterno;
unsigned long anteriorExterno;
bool estadoInterno = LOW;
bool estadoExterno = LOW;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pinoExterno, OUTPUT);
  anteriorInterno = millis();
  anteriorExterno = anteriorInterno;
}

void loop() {
  unsigned long agora = millis();

  if (agora - anteriorInterno  intervaloInterno) {
    estadoInterno = !estadoInterno;
    digitalWrite(LED_BUILTIN, estadoInterno);
    anteriorInterno = ;
  }

  if (agora - anteriorExterno >= ) {
    estadoExterno = !estadoExterno;
    digitalWrite(pinoExterno, estadoExterno);
    anteriorExterno = agora;
  }
}

Teste o retorno e a independência

Teste mental próximo ao retorno

Sem esperar dias na placa, rastreie este caso: anterior = 4294967200UL, agora = 80UL e intervalo = 150UL. Qual é o resultado de agora - anterior >= intervalo?

Valide uma alteração isolada

Na IDE Arduino local, altere somente esta linha do sketch para testar uma cadência diferente no LED externo:

const unsigned long intervaloExterno = 1000UL;

Mantenha intervaloInterno = 300UL e todo o restante inalterado. Compile, grave no Uno R3 e observe os dois LEDs. Em 2 ou 3 frases, relate o que mudou no LED externo, o que permaneceu no LED integrado e por que o loop continua disponível entre as mudanças.

Escreva pelo menos 80 caracteres (0/80).

Fechamento

Resumo

Síntese final

  • Use unsigned long para millis(), marcas anteriores e intervalos; escreva intervalos constantes com sufixo UL.
  • Guarde uma marca persistente por saída e atualize-a somente quando a saída for alterada.
  • Teste tempo decorrido com agora - anterior >= intervalo; essa forma continua válida ao atravessar o retorno da contagem, desde que não transcorra uma volta completa sem avaliação.
  • Leia millis() uma vez por passagem e aplique dois if independentes para que os LEDs possam ser atendidos separadamente ou na mesma passagem.
  • millis() não elimina tarefas demoradas: o restante do loop também precisa ser curto para que as verificações ocorram com frequência.

Tutorial concluído

Parabéns! Você concluiu: Temporização não bloqueante com millis

Você consolidou uma temporização não bloqueante com duas saídas independentes, comparação segura diante do retorno de millis() e um loop que permanece disponível entre as mudanças.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso