Sensores e a leitura que vira dado — Arduino e IoT — semana 8 do 1o trimestre
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
DHT11e fazer aparecer no monitor serial em graus Celsius. - Explicar por que o
DHT11precisa 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
DHT11de 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:
- O código vira saída, puxa
DATAparaLOWpor 20 ms. E o "acordar". - O código solta a linha e espera cerca de 80 microssegundos. O sensor percebe que foi chamado.
- O sensor manda 40 bits, um a um, da esquerda mais significativa para a menos significativa. Cada bit comeca com 50 microssegundos em
LOWe depois fica emHIGHpor 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. - 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ódigo | O que acontece |
|---|---|
| 200 ms | maioria das leituras NaN |
| 1000 ms | parte das leituras NaN |
| 2000 ms ou mais | leitura 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 sensor | Vai 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:
VCCdoDHT11na trilha+da protoboard.DATAdoDHT11noGPIO4.- Resistor de 10 k ohm entre o
DATAe a trilha+. GNDdoDHT11na 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.
- Monte o circuito do enunciado e grave o sketch da resolucao.
- Conte quantas linhas com
okaparecem em 30 segundos. Escreva o número e calcule quantas você esperava. Os dois números batem? - Troque
INTERVALO_LEITURAde 2000 para 200, grave e espere 20 segundos. Conte as leiturasoke asNaN. Em uma frase: o que o sensor esta fazendo quando o código pergunta antes da hora. - 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.
- Descomente o
pinModeque vira a saída duas vezes, de modo que o sensor não consiga responder. Grave e conte as leituras. Explique por que oNaNaparece mesmo com todo o resto correto. - Anote quantas leituras
NaNapareceram 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ério | Pontos |
|---|---|
Circuito com resistor de 10 k do DATA para a trilha positiva | 2 pontos |
DATA no GPIO4 e GND na trilha negativa, conferidos na bancada | 1 ponto |
INTERVALO_LEITURA em 2000 ms ou mais, e o motivo explicado | 3 pontos |
Leitura invalida tratada com isnan, e não comparada com zero | 2 pontos |
| Item 6 respondido: nenhuma leitura invalida com o intervalo correto | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
| 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
LDRcomanalogReade interpretar o número como nivel de luz. - Montar o divisor de tensão do
LDRcom 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
CLAROouESCUROcom 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.
| Componente | Vai para |
|---|---|
Uma perna do LDR | trilha + (5 V) |
A outra perna do LDR | o no do pino GPIO34 |
| Resistor de 10 k, uma perna | o no do GPIO34 |
| A outra perna do resistor | trilha - |
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ção | Faixa medida | Serve para |
|---|---|---|
ADC_0db | até 1,1 V | nunca usar nesta aula |
ADC_11db | até 3,3 V | a 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
LDRna trilha+da protoboard. - A outra perna do
LDRnoGPIO34. - 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.
- Monte o circuito do enunciado e grave o sketch da resolucao.
- 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. - 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. - 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. - Escreva o
LIMIAR_ESCUROcom 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. - Some com o
delay(5)de dentro do laco da media e compare a colunabrutacom 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ério | Pontos |
|---|---|
Divisor de tensão montado com o LDR e o resistor de 10 k, GPIO34 no meio | 2 pontos |
analogSetPinAttenuation com ADC_11db e analogReadResolution(12) no setup | 2 pontos |
LIMIAR_ESCURO calibrado na bancada do aluno, e não copiado do colega | 3 pontos |
| Histerese com dois limiares, e o estado não alterna com a luz parada | 2 pontos |
Item 6 respondido: a media e igual a leitura solta quando o delay some | 1 ponto |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
| 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.
