Estruturas de controle — Arduino e IoT — semana 3 do 1o trimestre

Informatica · Conteudo · publicado em 02/10/2026
Semana 3 · Estruturas de controle — Material de Apoio Arduino

Semana 3 de 13· 1o trimestre · 01/02 a 30/04

Estruturas de controle

if, switch e laco. O aluno já sabe a lógica; muda a sintaxe.

Aula 1 — if, else e o operador ternario

Objetivos

  • Escrever uma condição com if, else if e else e dizer qual caminho o programa tomou.
  • Trocar um if de duas linhas pelo operador ternário e explicar o que se perde nessa troca.
  • Montar condição composta com && e || sem confundir os dois operadores lógicos.
  • Usar uma guarda de tempo para o programa reagir a uma entrada sem refazer a classificação a cada volta do laço.

Material

  • 1 ESP32 DevKit V1 por aluno, com o cabo USB
  • 1 protoboard de 830 pontos e 1 jogo de jumpers por aluno
  • 2 a 5 LEDs de 3,3 V e 2 a 5 resistores de 330 Ω por aluno
  • 1 módulo de alimentação MB102 por dupla
  • Folha com quatro faixas de 0 a 100 impressa, uma por dupla

Conceitos

As três partes da condição

if (tensao > 3.0) {
  Serial.println("alta");
}

A condição precisa ser um valor booleano, ou seja, verdadeiro ou falso. tensao > 3.0 é uma pergunta que devolve true ou false; tensao sozinho, sem comparação, não serve.

As chaves delimitam o bloco. Elas não são enfeite: o C++ usa as chaves para saber onde o comando termina, e sem elas ele assume que o comando é só a próxima frase. É a diferença mais visível entre o C++ e o JavaScript na hora de escrever.

Encadeamento e a ordem dos testes

if (valor < 30)      { ... }
else if (valor < 70) { ... }
else                 { ... }

Os testes são lidos de cima para baixo, e o primeiro que for verdadeiro ganha; os outros nem são olhados. Por isso valor < 30 vem antes de valor < 70: invertido, todo número abaixo de 70 cairia na faixa baixa.

O else do final não tem condição: é o "nenhum dos anteriores deu certo". E ele é opcional — omitir o else apenas faz o programa não fazer nada nesse caminho.

O operador ternário

faixa = (valor < 70) ? "baixa" : "alta";

É um if de duas saídas escrito em uma linha: condição, interrogação, resultado se verdadeiro, dois-pontos, resultado se falso.

O que se perde é o nome do caminho. Com if e else, o bloco tem nome e o programa lê como uma frase. Com o ternário, o valor final importa e o motivo fica escondido. Regra do curso: ternário para escolher um valor, if para fazer uma coisa.

Condição composta e o curto-circuito

&& é "e", || é "ou". Ambos aceitam várias condições:

if (tensao > 1.0 && tensao < 3.0) { ... }   // dentro da faixa
if (digitalRead(PINO) == LOW || contador > 10) { ... }

O detalhe que só aparece na prática: quando o lado esquerdo já decide o resultado, o lado direito não é avaliado. Isso se chama curto-circuito e é o que salva expressões como indice > 0 && TABELA[indice - 1] != -1, onde o lado direito seria uma leitura fora do vetor.

A guarda que evita refazer tudo

No loop do sketch, o if está dentro de if (Serial.available() > 0). Sem essa guarda, a classificação rodaria milhares de vezes por segundo com o número velho, e o serial viraria uma parede de texto repetido. A guarda pergunta "chegou algo novo?" antes de agir.

Atividade

Montagem:

  • Resistor de 330 Ω entre o GPIO2 e um lado do LED.
  • O outro lado do LED na trilha + da protoboard.
  • Jumper macho-fêmea levando o GPIO2 ao resistor.

O lado longo do LED é o anodo e vai para o positivo.

  1. Grave a resolução, abra o monitor serial a 115200 baud e confirme que aparece o convite para digitar um número.
  2. Digite 10, depois 50, depois 90, e anote as três faixas devolvidas na ordem em que apareceram.
  3. Digite 0 e depois -5. Diga qual dos dois caminhos do código Cada um tomou e por que a ordem dos testes importa.
  4. Escreva a mesma classificação usando só o operador ternário, em no máximo três linhas, e grave. Compare as duas versões no caderno: qual delas dá para ler em voz alta?
  5. Escreva uma condição que acenda o LED só quando o número for maior que 50 ou igual a 10, e diga qual dos dois operadores lógicos você usou.
  6. Acrescente uma quarta faixa ao código, entre 70 e 90, com o nome que você escolher, sem alterar as três que já funcionam. Confirme que os números das outras faixas continuam caindo na faixa certa.

Nota: 10 pontos. Critério de fim: o serial responde corretamente aos cinco números testados e a quarta faixa foi acrescentada sem quebrar as outras.

Resolucao

// Aula 1 do dia 3: if, else e o operador ternário.
//
// O aluno já sabe a lógica de condição; o que muda é a sintaxe e a
//indentação. Este sketch lê um número que o professor digita no monitor
// serial e classifica, para que a turma veja o caminho tomado no Serial.

#include <Arduino.h>

const int PINO_LED = 2;
const int LIMITE_BAIXO = 30;
const int LIMITE_ALTO = 70;

int voltas = 0;

void setup() {
  pinMode(PINO_LED, OUTPUT);

  Serial.begin(115200);
  Serial.println();
  Serial.println("=== dia 3, aula 1: if, else e ternário ===");
  Serial.println();
  Serial.println("Digite um número de 0 a 100 e aperte Enter.");
  Serial.println("O programa diz em que faixa ele caiu.");
}

void loop() {
  // Só age quando chegou byte novo no monitor. Sem esta guarda, o programa
  // entraria no if a cada volta e classificaria o número velho mil vezes.
  if (Serial.available() > 0) {
    int valor = Serial.parseInt();

    if (valor <= 0) {
      Serial.println("Digite um número maior que zero.");
    } else if (valor < LIMITE_BAIXO) {
      Serial.print(valor);
      Serial.println("  ->  faixa baixa");
      digitalWrite(PINO_LED, LOW);
    } else if (valor < LIMITE_ALTO) {
      Serial.print(valor);
      Serial.println("  ->  faixa media");
      digitalWrite(PINO_LED, HIGH);
    } else {
      Serial.print(valor);
      Serial.println("  ->  faixa alta");
      digitalWrite(PINO_LED, HIGH);
    }

    voltas++;
    Serial.print("  classifications: ");
    Serial.println(voltas);
    Serial.println("Digite outro numero.");
    Serial.println();
  }

  // while sem condição de parada seria o laço infinito. Aqui ele só espera:
  // passa pelo loop milhares de vezes por segundo, e não faz nada até o
  // professor digitar alguma coisa. Isso é normal e não trava a placa.
}

Por que assim e não de outro jeito. O programa espera o número digitado no monitor serial em vez de um analogRead. Nesta aula a condição é o assunto, não o sensor: um valor que muda sozinho deixaria a turma classificando números que não escolheu, e a discussão do caminho tomado seria curta demais.

A ordem dos testes é valor <= 0, depois < LIMITE_BAIXO, depois < LIMITE_ALTO. O 0 vem primeiro de propósito: se viesse depois, 0 < 30 seria verdadeiro e o zero cairia na faixa baixa, e o aluno não veria o caminho de erro.

O else final e o de erro estão separados de propósito. O caminho apertado/solt do dia 5 vai nascer daqui: a estrutura de decidir o que fazer com cada estado já está pronta, e no dia 5 só troca o número por digitalRead.

O loop termina sem delay e sem qualquer espera. Ele passa por ali dezenas de milhares de vezes por segundo sem fazer nada, e isso não é defeito: é o que permite responder ao que o professor digitar no mesmo instante.

Criterios de correcao

CritérioPontos
Sketch gravado e as cinco faixas devolvidas corretamente no serial3 pontos
pinMode declarado no setup antes de qualquer digitalWrite2 pontos
Versão só com ternário, gravada, e comparação escrita no caderno3 pontos
Item 5 com o operador lógico nomeado2 pontos

Erros comuns

ErroComo apareceCorrecao
Usar = dentro da condiçãoif (valor = 0) e o programa entra sempre"Um igual so ordena. Para perguntar são dois: ==."
Esquecer as chaves no primeiro ifSó a linha seguinte entra no if, o resto roda sempre"O C++ assume que o comando e so a proxima frase. Se a ideia era varias, abra e feche chave."
Testar a faixa larga antes da estreitavalor < 70 antes de valor < 30 e tudo cai na faixa baixa"Os testes vao do mais especifico para o mais geral. O primeiro que der certo ganha."
Confundir && com ``Condição que aceita faixa que devia recusar"Leia em voz alta. 'E' e &&, com dois sinais. 'Ou' e ``, com duas barras."
Escrever else com condiçãoelse (valor < 70) não compila"else não tem condição: ele e o 'nenhum dos anteriores deu certo'."
Não fechar a chave do elseErro de compilacao apontando varias linhas depois"A chave que abriu o if fecha no fim do else, e não antes dele."
Colocar a ação fora do ifLED aceso o tempo todo, sem depender do número"A ação que depende da condição fica dentro das chaves. Fora delas, ela roda sempre."

Desafio extra

Troque a faixa media por uma condição composta que dependa de dois parâmetros ao mesmo tempo: o número digitado e o estado de um botão. Faça o programa só aceitar números na faixa media quando o botão estiver apertado, e escrever "recusado" quando o número estiver na faixa e o botão estiver solto. Explique no caderno qual das duas condições você pôs primeiro, e o que aconteceria se invertesse.

>

A resolucao, compilada

// Aula 1 do dia 3: if, else e o operador ternário.
//
// O aluno já sabe a lógica de condição; o que muda é a sintaxe e a
//indentação. Este sketch lê um número que o professor digita no monitor
// serial e classifica, para que a turma veja o caminho tomado no Serial.

#include <Arduino.h>

const int PINO_LED = 2;
const int LIMITE_BAIXO = 30;
const int LIMITE_ALTO = 70;

int voltas = 0;

void setup() {
  pinMode(PINO_LED, OUTPUT);

  Serial.begin(115200);
  Serial.println();
  Serial.println("=== dia 3, aula 1: if, else e ternário ===");
  Serial.println();
  Serial.println("Digite um número de 0 a 100 e aperte Enter.");
  Serial.println("O programa diz em que faixa ele caiu.");
}

void loop() {
  // Só age quando chegou byte novo no monitor. Sem esta guarda, o programa
  // entraria no if a cada volta e classificaria o número velho mil vezes.
  if (Serial.available() > 0) {
    int valor = Serial.parseInt();

    if (valor <= 0) {
      Serial.println("Digite um número maior que zero.");
    } else if (valor < LIMITE_BAIXO) {
      Serial.print(valor);
      Serial.println("  ->  faixa baixa");
      digitalWrite(PINO_LED, LOW);
    } else if (valor < LIMITE_ALTO) {
      Serial.print(valor);
      Serial.println("  ->  faixa media");
      digitalWrite(PINO_LED, HIGH);
    } else {
      Serial.print(valor);
      Serial.println("  ->  faixa alta");
      digitalWrite(PINO_LED, HIGH);
    }

    voltas++;
    Serial.print("  classifications: ");
    Serial.println(voltas);
    Serial.println("Digite outro numero.");
    Serial.println();
  }

  // while sem condição de parada seria o laço infinito. Aqui ele só espera:
  // passa pelo loop milhares de vezes por segundo, e não faz nada até o
  // professor digitar alguma coisa. Isso é normal e não trava a placa.
}

Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia03 aula1.

Aula 2 — for, while, do-while e o laco que não acaba

Objetivos

  • Escolher entre for, while e do-while sabendo dizer quantas vezes o laço roda.
  • Escrever o laço com variável de controle no cabeçalho, em vez de deixar o incremento solto no corpo.
  • Usar break e continue para sair cedo ou pular uma iteração sem enshrinar o laço inteiro.
  • Substituir delay dentro de laço por comparação com millis, rodando duas tarefas ao mesmo tempo.

Material

  • 1 ESP32 DevKit V1 por aluno, com o cabo USB
  • 1 protoboard de 830 pontos e 1 jogo de jumpers por aluno
  • 2 a 5 LEDs de 3,3 V e 2 a 5 resistores de 330 Ω por aluno
  • 1 módulo de alimentação MB102 por dupla
  • Folha com os três tipos de laço lado a lado, uma por dupla

Conceitos

Os três laços, e quando usar cada um

LaçoTestaUse quando
forantes de cada voltavocê sabe quantas vezes repete
whileantes de cada voltavocê repete até uma condição virar falsa
do-whiledepois da voltao corpo tem de rodar pelo menos uma vez

O do-while é o único que garante uma execução. Ele é o natural para ler um sensor pelo menos uma vez antes de decidir se continua, e por isso o dia 8, no DHT11, ele reaparece.

A variável de controle vive no cabeçalho

for (int i = 1; i <= 5; i++) {
  Serial.println(i);
}

Três partes: int i = 1 é a inicialização, i <= 5 é a condição, e i++ é o incremento. O i só existe dentro do laço: depois da chave final ele não existe mais, e usá-lo fora é erro de compilação.

Escrever o incremento solto no meio do corpo funciona, mas espalha a condição por duas partes do código. Quando o laço chegar a 40 linhas, ninguém acha onde ele termina. O cabeçalho é o contrato do laço com quem lê.

break e continue

break sai do laço na hora. continue pula para o fim da volta atual e recomeça o teste. A diferença aparece com clareza no laço de 1 a 20 pulando os múltiplos de 3: continue ainda roda os outros números, break para tudo.

O break tem um uso escondido e importante: sair cedo de uma busca dentro de uma lista. Achou o valor? break, e o laço para de procurar.

O laço que não acaba, e o que ele custa

while (true) {
  Serial.println(".");
}

Esse laço não acaba nunca, e o programa para de responder a tudo. Não trava a placa: a placa continua ligada, o monitor continua passando linhas. O que para é o resto do programa.

Este é o dia em que delay deixa de ser aceitável. Um delay de 1 segundo dentro do laço não é laço infinito, mas produz o mesmo efeito prático: durante aquele segundo, o processador não olha botão nenhum, não lê sensor nenhum, não responde a nada. No dia 5, com o botão, isso vira "o botão só funciona uma vez por segundo".

A saída é comparar o tempo com millis:

if (agora - instanteAnterior >= INTERVALO) {
  instanteAnterior = agora;
  // faz o serviço e sai
}

A tarefa não espera: ela olha a hora, faz o serviço e sai na hora. O laço continua girando e as outras tarefas continuam sendo vistas.

E atenção ao detalhe do unsigned: millis devolve unsigned long, e é por isso que o padrão é agora - instanteAnterior >= INTERVALO. Se o processador reiniciar o relógio no meio, a subtração em unsigned continua dando a diferença certa. Comparar os dois instantes direto, com >=, quebraria nessa hora.

Atividade

Montagem:

  • Resistor de 330 Ω entre o GPIO2 e um lado do LED.
  • O outro lado do LED na trilha + da protoboard.
  • Jumper macho-fêmea levando o GPIO2 ao resistor.

O lado longo do LED é o anodo e vai para o positivo.

  1. Grave a resolução e confirme que o LED pisca rápido e que a coluna do contador lento sobe uma vez por segundo.
  2. Conte por dez segundos quantas linhas da coluna lenta aparecem. Compare com a conta de voltasLentas no serial e diga por que o número não é exatamente 10.
  3. Escreva um laço for de 1 a 20 que imprima no serial apenas os números que não são múltiplos de 3, usando continue.
  4. Repita o item 3 usando while no lugar do for, com a variável de controle declarada antes do laço. Diga qual das duas versões tem a variável de controle visível depois do laço.
  5. Descomente a versão errada que está no fim do arquivo de código, grave e meça quanto tempo o serial leva para mostrar dez linhas. Depois compare com o tempo da versão com millis.
  6. Troque INTERVALO_LENTO de 1000 para 300 e diga o que acontece com o Serial que hoje é lento: fica igual ao rápido ou ainda é mais lento?

Nota: 10 pontos. Critério de fim: as duas tarefas rodam ao mesmo tempo no sketch final e o serial mostra as duas colunas.

Resolucao

// Aula 2 do dia 3: for, while, do-while e o laço que não acaba.
//
// Esta é a aula em que `delay` dentro de laço deixa de ser aceitável. O
// sketch roda duas tarefas ao mesmo tempo — um contador rápido e uma contagem
// lenta — usando `millis`, que é a aula que vale do dia inteiro.
//
// A versão ERRADA está comentada no fim do arquivo, para o professor colar no
// projetor e deixar a turma ver a diferença.

#include <Arduino.h>

const int PINO_LED = 2;
const int INTERVALO_RAPIDO = 100;   // tarefa rápida, em milissegundos
const int INTERVALO_LENTO = 1000;    // tarefa lenta, em milissegundos

// Instante da última execução de cada tarefa. `unsigned long` é o tipo que o
// `millis` devolve; declarar com `int` estoura depois de cerca de 24 dias de
// programa rodando sem parar.
unsigned long instanteRapido = 0;
unsigned long instanteLento = 0;

int voltasRapidas = 0;
int voltasLentas = 0;

void setup() {
  pinMode(PINO_LED, OUTPUT);

  Serial.begin(115200);
  Serial.println();
  Serial.println("=== dia 3, aula 2: dois laços ao mesmo tempo ===");
  Serial.println();
  Serial.println("Dois tarefas sem delay no meio: o LED pisca e o contador");
  Serial.println("continua contando enquanto isso.");
  Serial.println();
  Serial.println("tarefas | lenta | rapida");
}

// Esta função roda a cada 100 ms e sai na hora. Ela nunca espera ninguém.
void tarefaRapida() {
  voltasRapidas++;
  digitalWrite(PINO_LED, voltasRapidas % 2 == 0 ? HIGH : LOW);
}

// Esta roda a cada 1 s. As duas convivem porque nenhuma delas segura o
// processador: cada uma só olha a hora e sai.
void tarefaLenta() {
  voltasLentas++;
  Serial.print(voltasLentas);
  Serial.print("   |   ");
  Serial.println(voltasRapidas);
}

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

  // Subtrair e comparar é mais seguro que comparar os instantes direto:
  // quando o processador reinicia o millis volta a zero, e a subtração
  // continua dando a diferença certa.
  if (agora - instanteRapido >= INTERVALO_RAPIDO) {
    instanteRapido = agora;
    tarefaRapida();
  }

  if (agora - instanteLento >= INTERVALO_LENTO) {
    instanteLento = agora;
    tarefaLenta();
  }

  // Loop vazio de propósito. Passa por aqui dezenas de milhares de vezes por
  // segundo sem fazer nada, e é exatamente isso que permite o processador
  // responder às duas tarefas. Um loop ocupado aqui travaria as duas.

  // ------------------------------------------------------------------
  // VERSÃO ERRADA — o que o aluno escreve na primeira tentativa:
  //
  //   void loop() {
  //     digitalWrite(PINO_LED, HIGH);
  //     delay(100);
  //     digitalWrite(PINO_LED, LOW);
  //     delay(900);
  //     Serial.println("tarefa lenta");   // só sai depois de 1 s parado
  //   }
  //
  // Compila. Não dá erro. Só que o Serial só imprime uma vez por segundo, e
  // qualquer botão apertado nesse intervalo só é lido no fim do segundo.
  // ------------------------------------------------------------------
}

Por que assim e não de outro jeito. O sketch roda duas tarefas que não se atrapalham, e isso é o ponto inteiro da aula. A rápida pisca o LED a cada 100 ms; a lenta imprime no serial a cada segundo. Com delay, as duas seriam a mesma coisa.

A comparação é agora - instante >= INTERVALO e não agora >= instante + INTERVALO. As duas formas funcionam, mas a primeira sobrevive ao reinício do relógio, porque a subtração de dois unsigned long dá o tempo decorrido mesmo quando o contador passou do zero.

O instantX é atualizado depois de decidir, não antes. Atualizar antes faria a tarefa disparar a cada volta do laço, e o nome da variável deixaria de dizer a que momento ela se refere.

A versão errada está no fim do arquivo, comentada, e não é decorativa: ela é o que o aluno escreve na primeira tentativa, e o professor precisa dela na tela para medir o mesmo tempo nos dois códigos. Se ele só acredita em frase, o botão do dia 5 vai parecer quebrado.

O loop é um loop vazio. Ele passa por ali dezenas de milhares de vezes por segundo sem fazer nada, e é exatamente isso que deixa as duas tarefas rodarem livremente. Um laço ocupado aqui travaria as duas.

Criterios de correcao

CritérioPontos
Duas tarefas rodando juntas, com as duas colunas no serial3 pontos
pinMode declarado no setup antes de qualquer digitalWrite2 pontos
millis com unsigned long e comparação por subtração3 pontos
Itens 3 e 4 escritos e a diferença entre for e while respondida2 pontos

Erros comuns

ErroComo apareceCorrecao
while (true) com delay dentroPrograma que responde uma vez por segundo"Esse e o laco que não acaba. Troque o delay por comparacao com millis."
Declarar int agora = millis();Contador que falha depois de alguns dias rodando"O millis devolve unsigned long. Guardar em int estoura a conta."
Comparar agora >= instante + 1000Quebra quando o processador reinicia o relogio"Prefira agora - instante >= 1000. A subtracao continua valendo quando o relogio da a volta."
Escrever if (i = 3) no forLaco que roda com i sempre valendo 3"Para testar, dois iguais. Um igual so substitui a variável."
Colocar o i++ no fim do corpo e não no cabecalhoLaco de 40 linhas onde ninguem acha o fim"O incremento vai no cabecalho, junto com o teste. E ele que diz onde o laco termina."
Confundir while e do-whileAluno espera o corpo rodar antes de testar"O do-while testa depois. Por isso o corpo dele roda pelo menos uma vez, mesmo com a condição já falsa."
Usar break no lugar de continue ao pular númeroO laco para no primeiro múltiplo de 3"break sai do laco inteiro. Para pular so um, e continue."
Comentar a versão errada no código do aluno"Se eu descomentar isso, da loop infinito""Descomente de verdade e meça. O serial vai mostrar a diferença de tempo na sua frente."

Desafio extra

Acrescente uma terceira tarefa que rode a cada 500 ms e faça o LED piscar duas vezes seguidas, e escreva no caderno quantas voltas da tarefa rápida acontecem dentro de uma volta da tarefa lenta. Em seguida, inverta as três velocidades e verifique se a contagem continua batendo — se não bater, descubra por que e escreva a resposta em duas linhas.

>

A resolucao, compilada

// Aula 2 do dia 3: for, while, do-while e o laço que não acaba.
//
// Esta é a aula em que `delay` dentro de laço deixa de ser aceitável. O
// sketch roda duas tarefas ao mesmo tempo — um contador rápido e uma contagem
// lenta — usando `millis`, que é a aula que vale do dia inteiro.
//
// A versão ERRADA está comentada no fim do arquivo, para o professor colar no
// projetor e deixar a turma ver a diferença.

#include <Arduino.h>

const int PINO_LED = 2;
const int INTERVALO_RAPIDO = 100;   // tarefa rápida, em milissegundos
const int INTERVALO_LENTO = 1000;    // tarefa lenta, em milissegundos

// Instante da última execução de cada tarefa. `unsigned long` é o tipo que o
// `millis` devolve; declarar com `int` estoura depois de cerca de 24 dias de
// programa rodando sem parar.
unsigned long instanteRapido = 0;
unsigned long instanteLento = 0;

int voltasRapidas = 0;
int voltasLentas = 0;

void setup() {
  pinMode(PINO_LED, OUTPUT);

  Serial.begin(115200);
  Serial.println();
  Serial.println("=== dia 3, aula 2: dois laços ao mesmo tempo ===");
  Serial.println();
  Serial.println("Dois tarefas sem delay no meio: o LED pisca e o contador");
  Serial.println("continua contando enquanto isso.");
  Serial.println();
  Serial.println("tarefas | lenta | rapida");
}

// Esta função roda a cada 100 ms e sai na hora. Ela nunca espera ninguém.
void tarefaRapida() {
  voltasRapidas++;
  digitalWrite(PINO_LED, voltasRapidas % 2 == 0 ? HIGH : LOW);
}

// Esta roda a cada 1 s. As duas convivem porque nenhuma delas segura o
// processador: cada uma só olha a hora e sai.
void tarefaLenta() {
  voltasLentas++;
  Serial.print(voltasLentas);
  Serial.print("   |   ");
  Serial.println(voltasRapidas);
}

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

  // Subtrair e comparar é mais seguro que comparar os instantes direto:
  // quando o processador reinicia o millis volta a zero, e a subtração
  // continua dando a diferença certa.
  if (agora - instanteRapido >= INTERVALO_RAPIDO) {
    instanteRapido = agora;
    tarefaRapida();
  }

  if (agora - instanteLento >= INTERVALO_LENTO) {
    instanteLento = agora;
    tarefaLenta();
  }

  // Loop vazio de propósito. Passa por aqui dezenas de milhares de vezes por
  // segundo sem fazer nada, e é exatamente isso que permite o processador
  // responder às duas tarefas. Um loop ocupado aqui travaria as duas.

  // ------------------------------------------------------------------
  // VERSÃO ERRADA — o que o aluno escreve na primeira tentativa:
  //
  //   void loop() {
  //     digitalWrite(PINO_LED, HIGH);
  //     delay(100);
  //     digitalWrite(PINO_LED, LOW);
  //     delay(900);
  //     Serial.println("tarefa lenta");   // só sai depois de 1 s parado
  //   }
  //
  // Compila. Não dá erro. Só que o Serial só imprime uma vez por segundo, e
  // qualquer botão apertado nesse intervalo só é lido no fim do segundo.
  // ------------------------------------------------------------------
}

Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia03 aula2.