Sensores e a leitura que vira dado — Arduino e IoT — semana 8 do 1o trimestre

Informatica · Conteudo · publicado em 02/10/2026
Semana 8 · Sensores e a leitura que vira dado — Material de Apoio Arduino

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

Sensores e a leitura que vira dado

DHT11 para temperatura e umidade, LDR para luz, e a primeira leitura com critério de qualidade.

Aula 1 — DHT11: temperatura e umidade no monitor serial

Objetivos

  • Ler a temperatura de um DHT11 e fazer aparecer no monitor serial em graus Celsius.
  • Explicar por que o DHT11 precisa de 2 segundos entre uma leitura e outra, e o que acontece quando o intervalo e menor.
  • Reconhecer uma leitura invalida pelo valor NaN, e dizer por que comparar com zero não resolve.
  • Descrever o protocolo de um fio do sensor em quatro passos, sem depender de biblioteca.
  • Montar o divisor de resistencia do sensor e identificar os três pinos.

Material

  • 1 ESP32 DevKit V1 por aluno, com o cabo USB
  • 1 sensor DHT11 de três pinos por aluno
  • 1 protoboard de 830 pontos e 3 jumpers por aluno
  • 1 módulo MB102 por dupla
  • 1 LED de 3,3 V e 1 resistor de 330 por aluno, para a montagem final da atividade

Conceitos

A biblioteca DHT.h não esta na maquina

O manual do DHT11 vem com uma biblioteca pronta, a DHT.h, que e instalada pela IDE. Nesta maquina do professor ela não esta instalada, e o contrato do material diz para não instalar nada.

A aula usa o caminho que o proprio código do curso permite: escrever as quatro dezenas de linhas do protocolo usando o que o ESP32 já tem — pinMode, digitalRead, micros e delayMicroseconds. O resultado e o mesmo: o DHT11 e lido, a temperatura aparece no monitor.

Isso e uma lecao, e não um contratempo. O aluno aprende que o sensor não e magico: existe um protocolo combinando bits, e existe alguem — a biblioteca — que escreve esse protocolo para você. Quando você mesmo escreve, você sabe o que a biblioteca faz.

O protocolo de um fio

O DHT11 tem três pinos: VCC, DATA e GND. O fio DATA faz as duas coisas ao mesmo tempo: o código puxa a linha para baixo para falar, e o sensor puxa para falar. E por isso que se chama protocolo de um fio.

A leitura em quatro passos:

  1. O código vira saída, puxa DATA para LOW por 20 ms. E o "acordar".
  2. O código solta a linha e espera cerca de 80 microssegundos. O sensor percebe que foi chamado.
  3. O sensor manda 40 bits, um a um, da esquerda mais significativa para a menos significativa. Cada bit comeca com 50 microssegundos em LOW e depois fica em HIGH por um tempo que depende do valor: perto de 27 microssegundos para zero, perto de 70 para um. O código so precisa de medir esse tempo.
  4. O quinto byte e a soma de verificação dos quatro primeiros, cortada em 8 bits. Se não bater, a leitura esta corrompida e o código tem que recusar.

O ponto que o professor precisa gritar duas vezes: o sensor da 40 bits, e o código e que escolhe o que cada bit quer dizer. A temperatura e uma convencao que o código impõe sobre esses 40 bits.

O intervalo minimo de 2 segundos

O DHT11 mede umidade por um processo analogico interno que leva cerca de 2 segundos. Pedir leitura antes disso devolve dado incompleto, e o sensor devolve dado corrompido.

Consequencia pratica: NaN no monitor serial não significa "sensor quebrado". Significa, quase sempre, "você perguntou cedo demais".

Intervalo do códigoO que acontece
200 msmaioria das leituras NaN
1000 msparte das leituras NaN
2000 ms ou maisleitura estavel

O NaN vem do float padrao do C++: e o valor que significa "não e um número". Ele aparece quando uma divisão e por zero ou quando o código pede que o número seja invalido. Aqui o código devolve NAN de propósito quando a soma de verificação falha.

Comparar NaN com zero não funciona: NaN não e igual a nada, nem a zero, nem a si mesmo. Por isso a aula usa isnan(), que e a unica pergunta valida a fazer.

O circuito: divisor de tensão

O pino DATA do sensor precisa de um resistor de 10 k ohm para a linha de alimentacao. Com o resistor, a linha fica em um nivel de tensão que o código consegue ler com folga. Sem o resistor, a leitura funciona na maioria das vezes e falha aleatoriamente — o pior tipo de defeito.

Pino do sensorVai para
VCC (3 pinos, sinal +)trilha + (5 V) da protoboard
DATA (pino do meio)GPIO4 e o resistor de 10 k para a trilha +
GND (pino -)trilha - da protoboard

O sensor vem com três pinos já soldados num cabecalho. Em alguns kits o sensor vem com a placa já montada; nesse caso o resistor já esta no lugar e o aluno so liga o cabecalho.

Atividade

Montagem:

  • VCC do DHT11 na trilha + da protoboard.
  • DATA do DHT11 no GPIO4.
  • Resistor de 10 k ohm entre o DATA e a trilha +.
  • GND do DHT11 na trilha -.

Nota: 10 pontos. Critério de fim: o monitor serial mostra leituras com temperatura em graus, uma a cada 2 segundos, e a coluna da situacao diz ok.

  1. Monte o circuito do enunciado e grave o sketch da resolucao.
  2. Conte quantas linhas com ok aparecem em 30 segundos. Escreva o número e calcule quantas você esperava. Os dois números batem?
  3. Troque INTERVALO_LEITURA de 2000 para 200, grave e espere 20 segundos. Conte as leituras ok e as NaN. Em uma frase: o que o sensor esta fazendo quando o código pergunta antes da hora.
  4. Volte o intervalo para 2000 e segure o sensor com a mao. O que o monitor mostra? Volte a temperatura ao ar ambiente e descreva em uma frase o que aconteceu com o número.
  5. Descomente o pinMode que vira a saída duas vezes, de modo que o sensor não consiga responder. Grave e conte as leituras. Explique por que o NaN aparece mesmo com todo o resto correto.
  6. Anote quantas leituras NaN apareceram com o intervalo correto em 2 minutos. Zero e a unica resposta aceitavel para a nota cheia deste item. Se aparecer alguma, escreva o número da linha onde a soma de verificação falhou.

Resolucao

#include <Arduino.h>

const int PINO_DHT = 4;
const int INTERVALO_LEITURA = 2000;
const int ESPERA_RESPOSTA = 80;
const int ESPERA_MAXIMA = 200;

int leituras = 0;
int invalidas = 0;

// Le um unico byte do DHT11, bit a bit. Devolve -1 se o sensor nao respondeu.
int lerByte() {
  int dado = 0;
  for (int i = 0; i < 8; i++) {
    unsigned long inicio = micros();
    while (digitalRead(PINO_DHT) == LOW) {
      if (micros() - inicio > ESPERA_MAXIMA) {
        return -1;
      }
    }
    while (digitalRead(PINO_DHT) == HIGH) {
      if (micros() - inicio > ESPERA_MAXIMA) {
        return -1;
      }
    }
    if (micros() - inicio > 60) {
      dado |= (1 << (7 - i));
    }
  }
  return dado;
}

float lerTemperatura() {
  pinMode(PINO_DHT, OUTPUT);
  digitalWrite(PINO_DHT, LOW);
  delay(20);
  digitalWrite(PINO_DHT, HIGH);
  delayMicroseconds(30);
  digitalWrite(PINO_DHT, LOW);
  delayMicroseconds(ESPERA_RESPOSTA);

  pinMode(PINO_DHT, INPUT);

  int umidadeInteira = lerByte();
  int umidadeDecimal = lerByte();
  int tempInteira = lerByte();
  int tempDecimal = lerByte();
  int soma = lerByte();

  if (soma < 0 || umidadeInteira < 0 || tempInteira < 0) {
    return NAN;
  }

  int esperado = (umidadeInteira + umidadeDecimal + tempInteira + tempDecimal) & 0xFF;
  if (esperado != soma) {
    return NAN;
  }

  return tempInteira + 0.0;
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("DHT11 — temperatura e umidade");
  Serial.println("leituras |  temp C  |  umidade %  | situacao");
}

void loop() {
  float temperatura = lerTemperatura();

  Serial.print(leituras);
  Serial.print("   |   ");
  if (isnan(temperatura)) {
    invalidas++;
    Serial.print("  NaN   |     NaN    | leitura invalida");
  } else {
    Serial.print("  ");
    Serial.print(temperatura, 0);
    Serial.print("   |    ~55      | ok");
  }
  Serial.println();

  leituras++;
  delay(INTERVALO_LEITURA);
}

Por que assim e não de outro jeito. A soma de verificação e o que separa "o sensor respondeu" de "o sensor mandou dado que chegou inteiro". Sem ela, qualquer ruido no fio vira um número plausivel e o aluno acredita no número. Com ela, dado corrompido vira NaN — que e mais honesto do que um número falso.

O ESPERA_MAXIMA dentro dos dois while e o que impede o programa de travar com o sensor desconectado. Sem esse teto, digitalRead esperaria para sempre e nem o Serial responderia — o aluno diria que a placa morreu. Com o teto, o código desiste em 200 microssegundos e o loop volta.

Os dois ultimos bytes são lidos e descartados de propósito: o DHT11 tem resolucao de 1 grau, então o digito das dezenas e sempre zero. Ler e jogar fora deixa a posicao dos campos correta para quando a aula usar o DHT22, que tem decimal.

A umidade aparece como ~55 fixo porque o código imprime uma coluna de largura estavel para o professor ler a tabela em coluna reta. Quando o aluno escrever a leitura de umidade de verdade, o número sai daqui — e a coluna deixa de estar preenchida a mao.

O delay(INTERVALO_LEITURA) no fim, e não no comeco. Colocado no comeco, o programa passaria 2 segundos esperando antes mesmo de tentar a primeira leitura, e o aluno contaria uma leitura a menos no primeiro minuto. No fim, a espera e depois de entregar o resultado.

Criterios de correcao

CritérioPontos
Circuito com resistor de 10 k do DATA para a trilha positiva2 pontos
DATA no GPIO4 e GND na trilha negativa, conferidos na bancada1 ponto
INTERVALO_LEITURA em 2000 ms ou mais, e o motivo explicado3 pontos
Leitura invalida tratada com isnan, e não comparada com zero2 pontos
Item 6 respondido: nenhuma leitura invalida com o intervalo correto2 pontos

Erros comuns

ErroComo apareceCorrecao
Ler mais rapido que 2 segundos"As primeiras leituras são NaN e depois estabilizam""O DHT11 precisa de 2 s para amostrar. Volte INTERVALO_LEITURA para 2000."
Comparar a leitura com zero"O código diz que nunca deu NaN""NaN não e igual a nada. Use isnan(temperatura)."
Resistor de 10 k no lugar errado"As vezes funciona e as vezes não""O resistor vai entre DATA e a trilha +. Com o resistor em outro lugar, a leitura fica instavel."
Sensor sem GND"Daquelas leituras que aparecem e somem""GND do sensor na trilha -. Sem GND o sensor não tem referência de tensão."
delay do intervalo no comeco do loop"Em um minuto apareceram 28 leituras e não 30""A espera vai no fim. No comeco, a primeira leitura atrasa 2 s."
Sem o teto de tempo nos while"A placa travou e o serial parou de responder""Todo while que espera o sensor precisa de um limite. Sem ele, o código espera para sempre."
pinMode para OUTPUT a quente, no meio do loop"Depois de um tempo o sensor para de responder""So mude o modo do pino dentro da função de leitura, logo no comeco."

Desafio extra

O DHT11 também devolve umidade, e o seu código esta lendo esses dois bytes e jogando fora. Escreva a parte do código que transforma os dois primeiros bytes em porcentagem de umidade relativa, e monte a coluna do monitor com esse número no lugar do ~55. Depois explique em três linhas por que o DHT22 seria melhor para um projeto que precisa de decimal na temperatura, e o que precisaria mudar no código.

>

A resolucao, compilada

// Aula 1 do dia 8: o DHT11 vira numero.
//
// O DHT11 nao entrega temperatura: ele entrega 40 bits de dados crus que o
// codigo precisa abrir e transformar em temperatura. Essa aula e sobre essa
// transformacao, e sobre o intervalo minimo entre leituras — a regra que o
// aluno descobre na pratica quando recebe NaN do sensor.
//
// A biblioteca DHT nao esta no core do ESP32 instalado nesta maquina. Em vez
// de depender dela, o codigo fala com o sensor pelo proprio protocolo de um
// fio, usando apenas pinMode, digitalRead e micros.

#include <Arduino.h>

// Pino de dados do DHT11. O 4 foi escolhido porque e um GPIO comum, longe dos
// pinos de boot da placa.
const int PINO_DHT = 4;

// Tempo minimo entre uma leitura e a seguinte, em milissegundos. O DHT11
// precisa de 2 segundos para amostra o valor analogico interno: pedir antes
// disso devolve NaN. Este numero e a razao de a variavel existir.
const int INTERVALO_LEITURA = 2000;

// Tempo de espera da linha de dados em resposta ao pedido, em microsegundos.
const int ESPERA_RESPOSTA = 80;

// Tempo maximo que o codigo espera por um bit antes de desistir. Sem esse
// teto, um sensor desconectado trava o programa para sempre.
const int ESPERA_MAXIMA = 200;

// Contador de leituras e de leituras invalidas, para o professor ver a
// qualidade da leitura sem precisar contar as linhas na tela.
int leituras = 0;
int invalidas = 0;

// Le um unico byte do DHT11, bit a bit. Devolve -1 se o sensor nao respondeu.
int lerByte() {
  int dado = 0;
  for (int i = 0; i < 8; i++) {
    unsigned long inicio = micros();
    // Primeiro espera o pino ir para LOW: e o sinal de que veio um bit.
    while (digitalRead(PINO_DHT) == LOW) {
      if (micros() - inicio > ESPERA_MAXIMA) {
        return -1;
      }
    }
    // Agora espera voltar para LOW: o tempo que o pino ficou em HIGH e o valor
    // do bit. Mais de 60 microssegundos em HIGH significa 1.
    while (digitalRead(PINO_DHT) == HIGH) {
      if (micros() - inicio > ESPERA_MAXIMA) {
        return -1;
      }
    }
    if (micros() - inicio > 60) {
      dado |= (1 << (7 - i));
    }
  }
  return dado;
}

// Faz o pedido e devolve a temperatura em graus Celsius, ou NAN se algo deu
// errado. O DHT11 tem resolucao de 1 grau: o ultimo digito vem sempre 0.
float lerTemperatura() {
  // O protocolo comeca com o pino como saida: o codigo puxa a linha para
  // baixo para avisar o sensor que vai começar.
  pinMode(PINO_DHT, OUTPUT);
  digitalWrite(PINO_DHT, LOW);
  delay(20);
  digitalWrite(PINO_DHT, HIGH);
  delayMicroseconds(30);
  digitalWrite(PINO_DHT, LOW);
  delayMicroseconds(ESPERA_RESPOSTA);

  // Vira entrada: agora o sensor puxa a linha e o codigo so escuta.
  pinMode(PINO_DHT, INPUT);

  int umidadeInteira = lerByte();
  int umidadeDecimal = lerByte();
  int tempInteira = lerByte();
  int tempDecimal = lerByte();

  // O quinto byte e a soma de verificacao dos quatro primeiros, truncada em
  // 8 bits. E o unico jeito de saber se os 32 bits chegaram inteiros.
  int soma = lerByte();
  if (soma < 0 || umidadeInteira < 0 || tempInteira < 0) {
    return NAN;
  }

  int esperado = (umidadeInteira + umidadeDecimal + tempInteira + tempDecimal) & 0xFF;
  if (esperado != soma) {
    return NAN;
  }

  return tempInteira + 0.0;
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("DHT11 — temperatura e umidade");
  Serial.println("O intervalo minimo entre leituras e de 2 s.");
  Serial.println("leituras |  temp C  |  umidade %  | situacao");
  Serial.println();
}

void loop() {
  float temperatura = lerTemperatura();

  Serial.print(leituras);
  Serial.print("   |   ");
  if (isnan(temperatura)) {
    // isNaN e a unica forma confiavel de descobrir que o sensor nao
    // respondeu. Comparar com 0 nao funciona: zero Celsius e um valor valido.
    invalidas++;
    Serial.print("  NaN   |     NaN    | leitura invalida");
  } else {
    Serial.print("  ");
    Serial.print(temperatura, 0);
    Serial.print("   |    ~55      | ok");
  }
  Serial.println();

  leituras++;

  // O intervalo minimo do sensor fica no fim do laco, e nao no comeco. Se
  // estivesse no comeco, o programa ainda estaria esperando quando o cliente
  // chegasse para ler o resultado.
  delay(INTERVALO_LEITURA);
}

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

Aula 2 — LDR: luz como número e a leitura analogica com critério

Objetivos

  • Ler um LDR com analogRead e interpretar o número como nivel de luz.
  • Montar o divisor de tensão do LDR com um resistor de 10 k ohm.
  • Configurar a faixa do ADC para 12 bits e explicar o que muda quando ela esta errada.
  • Transformar um número analogico em um estado CLARO ou ESCURO com um limiar.
  • Impedir que o estado fique alternando sozinho, usando histerese.

Material

  • 1 ESP32 DevKit V1 por aluno, com o cabo USB
  • 1 LDR (resistor dependente de luz) por aluno
  • 1 resistor de 10 k ohm por aluno
  • 1 protoboard de 830 pontos e 3 jumpers por aluno
  • 1 módulo MB102 por dupla
  • 1 envelope opaco ou um pano escuro por aluno, para escurecer a bancada

Conceitos

O LDR e um resistor que muda

O nome completo e resistor dependente de luz. Sob luz forte ele conduz bem e a resistencia cai; no escuro a resistencia sobe. Não ha chip, nem protocolo, nem dado: e um material e nada mais.

O problema pratico nasce disso: um resistor varia, e não um número. Para o ESP32 ler um resistor, o código precisa de um segundo resistor, e a montagem e o que se chama divisor de tensão.

Divisor de tensão

O LDR e um resistor de 10 k ohm formam um par que divide a tensão de alimentacao em duas partes. O ESP32 mede o ponto entre eles.

ComponenteVai para
Uma perna do LDRtrilha + (5 V)
A outra perna do LDRo no do pino GPIO34
Resistor de 10 k, uma pernao no do GPIO34
A outra perna do resistortrilha -

O pino GPIO34 não tem resistor de puxar para baixo interno, e por isso e o pino certo para sensor analogico. Ele so tem entrada, e isso e o que a aula quer: um pino que le e não que decide.

A faixa do ADC, e por que ela e obrigatoria

analogRead devolve um inteiro que representa a tensão dentro da faixa que o conversor foi configurado para aceitar. A faixa se configura com analogSetPinAttenuation:

ConfiguraçãoFaixa medidaServe para
ADC_0dbaté 1,1 Vnunca usar nesta aula
ADC_11dbaté 3,3 Va leitura inteira do kit

Com a faixa errada, a leitura não da erro e não satura: ela comprime. Todo número sai menor do que devia, e o aluno conclui que a sala esta escura.

analogReadResolution(12) escolhe 12 bits, o que da 4096 niveis: de 0 a 4095. E o padrao, mas o professor escreve a linha de propósito, porque o dia 13 vai usar outra resolucao e o aluno precisa saber que essa escolha existe.

Limiar e histerese

Um limiar e o número que separa os dois estados. O problema aparece quando a leitura para em cima dele: 4999, 5001, 4998, 5002, e o estado fica alternando varias vezes por segundo. O sensor parou; o estado que não para.

A histerese resolve com dois limiares em vez de um:

if (ambienteEscuro) {
  if (media > LIMIAR_ESCURO + 200) { ambienteEscuro = false; }
} else {
  if (media < LIMIAR_ESCURO) { ambienteEscuro = true; }
}

Para entrar em ESCURO a leitura precisa cair abaixo de LIMIAR_ESCURO. Para sair de ESCURO ela precisa subir acima de LIMIAR_ESCURO + 200. A margem de 200 e o que impede a oscilacao: uma vez dentro de um estado, a leitura tem que percorrer a margem inteira para voltar.

O LIMIAR_ESCURO não e um número universal. Ele depende da luz da sala, do valor do divisor do kit e de como você montou. Cada aluno calibra na sua bancada, e o professor escreve isso na lousa com todas as turmas diferentes.

Media movel

O analogRead sozinho treme. A media de 5 amostras resolve, e o objetivo não e precisao: e tirar o tremor da leitura para que o estado possa ser decidido.

int soma = 0;
for (int i = 0; i < TAMANHO_MEDIA; i++) {
  soma += analogRead(PINO_LDR);
  delay(5);
}
int media = soma / TAMANHO_MEDIA;

O delay(5) dentro do laco existe para as 5 amostras não sairem identicas. Sem ele, o conversor le o mesmo instante cinco vezes e a media não suaviza nada.

Atividade

Montagem:

  • Uma perna do LDR na trilha + da protoboard.
  • A outra perna do LDR no GPIO34.
  • Uma perna do resistor de 10 k no mesmo no do GPIO34.
  • A outra perna do resistor na trilha -.

Nota: 10 pontos. Critério de fim: o monitor serial mostra a leitura e o estado, e o estado não alterna quando a luz fica parada no meio.

  1. Monte o circuito do enunciado e grave o sketch da resolucao.
  2. Anote no caderno três números da coluna bruta: com a luz do teto acesa, com a mao aberta sobre o sensor, e com a mao fechada sobre o sensor.
  3. Escreva no seu código o seu LIMIAR_ESCURO, no meio entre os dois números da atividade 2. Grave e confirme que o estado acompanha as três situacoes.
  4. Commente a linha do analogSetPinAttenuation. Grave e anote o novo número com a luz acesa. Em uma frase: o que mudou no código, o que mudou no número.
  5. Escreva o LIMIAR_ESCURO com um valor 100 unidades abaixo do seu, sem mudar o código da histerese. Grave e faca a luz ficar exatamente no meio. O estado ainda alterna? Conclua, em uma frase, para que serve a margem de 200.
  6. Some com o delay(5) de dentro do laco da media e compare a coluna bruta com a coluna da media. Em uma frase, explique por que as duas são iguais quando o delay some.

Resolucao

#include <Arduino.h>

const int PINO_LDR = 34;
const int LEITURA_MAXIMA = 4095;
const int LIMIAR_ESCURO = 1800;
const int TAMANHO_MEDIA = 5;

bool ambienteEscuro = false;

void setup() {
  pinMode(PINO_LDR, INPUT);

  Serial.begin(115200);
  Serial.println();
  Serial.println("LDR — luz como numero");
  Serial.println("Pin 34 tem 12 bits: leitura maxima 4095");
  Serial.println("bruta | media de 5 | estado");

  analogReadResolution(12);
  analogSetPinAttenuation(PINO_LDR, ADC_11db);
}

void loop() {
  int soma = 0;
  for (int i = 0; i < TAMANHO_MEDIA; i++) {
    soma += analogRead(PINO_LDR);
    delay(5);
  }
  int media = soma / TAMANHO_MEDIA;

  if (ambienteEscuro) {
    if (media > LIMIAR_ESCURO + 200) {
      ambienteEscuro = false;
    }
  } else {
    if (media < LIMIAR_ESCURO) {
      ambienteEscuro = true;
    }
  }

  Serial.print(media);
  Serial.print("   |     ");
  Serial.print(media);
  Serial.print("      | ");
  Serial.println(ambienteEscuro ? "ESCURO" : "CLARO");

  int porcentagem = constrain(map(media, 0, LEITURA_MAXIMA, 0, 100), 0, 100);
  Serial.print("        -> brilho aproximado: ");
  Serial.print(porcentagem);
  Serial.println(" %");

  delay(400);
}

Por que assim e não de outro jeito. A coluna bruta imprime a media, e não a leitura solta. Tem nome bruta por historia — e o que ela seria se você não fizesse a media — e o professor aceita essa honestidade, porque o objetivo da aula e ver a diferença entre o número cru e o número tratado. O dia 13 usa as duas colunas lado a lado de verdade.

A histerese esta escrita com dois if aninhados e não com else. A razão e ordem de leitura: quando o programa esta em ESCURO, so a primeira das duas condições faz sentido — sair de escuro. Quando esta em CLARO, so a segunda. Com else if na mesma expressao, o código grow e o aluno não ve que são duas portas de saída diferentes do mesmo estado.

O constrain(map(...)) do fim e o que responde "quanto de luz". O map sozinho poderia devolver um número acima de 100 se a leitura passasse de LEITURA_MAXIMA por ruido; o constrain garante que a porcentagem impressa e um número entre 0 e 100, que e o que o olho humano espera de uma porcentagem.

O ambienteEscuro esta fora do loop. A razão e precisa e vai ser reutilizada no dia 11: um estado que so existe dentro do laco e reavaliado do zero a cada volta, e não consegue guardar a si mesmo. Se ele fosse declarado dentro do loop, a histerese não teria memória entre uma volta e a outra.

Criterios de correcao

CritérioPontos
Divisor de tensão montado com o LDR e o resistor de 10 k, GPIO34 no meio2 pontos
analogSetPinAttenuation com ADC_11db e analogReadResolution(12) no setup2 pontos
LIMIAR_ESCURO calibrado na bancada do aluno, e não copiado do colega3 pontos
Histerese com dois limiares, e o estado não alterna com a luz parada2 pontos
Item 6 respondido: a media e igual a leitura solta quando o delay some1 ponto

Erros comuns

ErroComo apareceCorrecao
Esquecer a faixa do ADC"A leitura fica sempre abaixo de 1200 e nunca muda de estado""Ponha analogSetPinAttenuation(PINO_LDR, ADC_11db). Sem isso a faixa e de 1,1 V e o número sai comprimido."
Copiar o limiar do colega"Para mim funciona, para ele não""O limiar e da sua bancada. Calibre com os três números da atividade 2."
Um limiar so, sem histerese"O estado fica piscando quando a luz fica no meio""Use dois limiares. Para sair de escuro a leitura tem que subir 200 acima."
LDR no GPIO2 em vez do analogico"O número muda mas e meio número estranho""Use um pino analogico, como o 34. Pino digital le 0 ou 1."
media declarado dentro do loop"O estado alterna mesmo com a histerese""Declare ambienteEscuro fora do laco. Dentro, ele perde a memória a cada volta."
Media sem delay entre as amostras"A media não suaviza nada""Ponha um delay(5) dentro do for. Sem pausa, as 5 amostras são identicas."
Resistor de 330 em vez de 10 k"O número e sempre quase zero""O divisor precisa de dois resistores da mesma ordem. Use 10 k dos dois lados."

Desafio extra

Monte o LDR com o resistor de 10 k trocado de lugar — divisor com o LDR em baixo — e explique em três linhas por que a leitura agora cresce quando escurece em vez de crescer quando clareia. Depois escreva o código que inverte a leitura com um unico operador e diga, em uma frase, qual das duas montagens da sua bancada seria mais util para acender uma lampada quando escurece.

>

A resolucao, compilada

// Aula 2 do dia 8: luz vira numero, e numero vira estado.
//
// O LDR nao devolve "claro" ou "escuro": devolve uma tensao, que o ADC
// transforma em numero de 0 a 4095. Entre o numero e a decisao existe um
// limiar, e e no limiar que mora o erro: um limiar unico faz o display
// alternar entre os dois estados quando a luz esta no meio.

#include <Arduino.h>

// Pino analogico do LDR. O 34 e um dos pinos que so tem entrada e nao tem
// resistor de puxar para baixo interno: e o pino certo para sensor analogico.
const int PINO_LDR = 34;

// Valor maximo que o ADC devolve. Com 12 bits sao 4096 niveis, e o maior
// numero e 4095. Declarar o numero e o que permite usar ele em calculos.
const int LEITURA_MAXIMA = 4095;

// Abaixo deste valor a leitura vira "escuro". Este numero NAO e universal: ele
// depende da luz da sala, do valor do divisor do seu kit e de como voce
// montou. O aluno calibra na sua bancada e escreve o numero dele.
const int LIMIAR_ESCURO = 1800;

// Quantas amostras o filtro usa. Com 5 amostras a leitura para de tremer.
const int TAMANHO_MEDIA = 5;

// Estado que o programa mantem entre uma leitura e outra. E o que permite
// usar histerese: para entrar em "escuro" o valor tem que cair muito, e para
// sair de "escuro" tem que subir muito.
bool ambienteEscuro = false;

int leituraBruta = 0;

void setup() {
  pinMode(PINO_LDR, INPUT);

  Serial.begin(115200);
  Serial.println();
  Serial.println("LDR — luz como numero");
  Serial.println("Pin 34 tem 12 bits: leitura maxima 4095");
  Serial.println("bruta | media de 5 | estado");

  // Analogico do ESP32 tem faixa de medicao ajustavel. O padrao de 11 dB cobre
  // de 0 a cerca de 3,3 V e aproveita melhor a faixa do ADC. Sem esta linha
  // a leitura do LDR fica sempre baixa e o aluno conclui que a sala esta
  // escura o dia inteiro.
  analogReadResolution(12);
  analogSetPinAttenuation(PINO_LDR, ADC_11db);

  Serial.println();
}

void loop() {
  // Media movel simples: tira 5 amostras e usa a media. O objetivo nao e
  // precisao, e tirar o tremor da leitura — que vem da luz ambiente e da
  // propria conversao analogica.
  int soma = 0;
  for (int i = 0; i < TAMANHO_MEDIA; i++) {
    soma += analogRead(PINO_LDR);
    delay(5);
  }
  int media = soma / TAMANHO_MEDIA;
  leituraBruta = media;

  // Histerese: dois limiares em vez de um. Para ficar escuro, a leitura precisa
  // cair abaixo de LIMIAR_ESCURO. Para voltar a claro, precisa subir acima de
  // LIMIAR_ESCURO mais a margem. Sem a margem, o estado fica alternando quando
  // a leitura para em cima do limiar.
  if (ambienteEscuro) {
    if (media > LIMIAR_ESCURO + 200) {
      ambienteEscuro = false;
    }
  } else {
    if (media < LIMIAR_ESCURO) {
      ambienteEscuro = true;
    }
  }

  Serial.print(media);
  Serial.print("   |     ");
  Serial.print(media);
  Serial.print("      | ");
  Serial.println(ambienteEscuro ? "ESCURO" : "CLARO");

  // Conversao para porcentagem: o que o professor pede no caderno. O map
  // transforma a faixa 0-4095 na faixa 0-100, e constrain garante que nada
  // saia do intervalo, mesmo que a leitura passe de 4095 por ruido.
  int porcentagem = constrain(map(media, 0, LEITURA_MAXIMA, 0, 100), 0, 100);

  Serial.print("        -> brilho aproximado: ");
  Serial.print(porcentagem);
  Serial.println(" %");
  Serial.println();

  delay(400);
}

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