Entradas e saídas: botao, potenciometro e LED — Arduino e IoT — semana 5 do 1o trimestre
Semana 5 de 13· 1o trimestre · 01/02 a 30/04
Entradas e saídas: botao, potenciometro e LED
A primeira aula com dois lados: o mundo real fala, o ESP32 responde.
Aula 1 — Botao: entrada digital, pull-up e anti-vibracao
Objetivos
- Declarar um pino como
INPUT_PULLUPe explicar o que o resistor interno está fazendo. - Ler o botão com
digitalReade traduzir o nível invertido em "apertado" e "solto". - Aplicar anti-vibração por tempo e por estado para contar um aperto só uma vez.
- Montar o circuito do botão sem resistor externo e dizer por que o kit não tem um pull-up de 10 kΩ.
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
- 2 a 5 push buttons de 4 pinos por aluno
- 1 módulo de alimentação MB102 por dupla
- Folha com o esquema do botão e a tabela de níveis impressa, uma por dupla
Conceitos
Entrada digital e o pino flutuante
Um pino de entrada lê HIGH ou LOW. Quando nada está ligado nele, ele não lê nem um nem outro: lê flutuante, e o valor depende do ruído da mesa, da mão do aluno, do jumper vizinho. O programa não dá erro — ele lê lixo e age sobre ele.
É por isso que toda entrada precisa de um resistor de pull-up ou de pull-down: para segurar um nível quando ninguém está mandando sinal.
O pull-up interno e a leitura invertida
INPUT_PULLUP liga, dentro do próprio ESP32, um resistor que leva o pino para 3,3 V. A configuração do botão é:
| Situação | Nível no pino | digitalRead devolve |
|---|---|---|
| Botão solto | o pull-up segura | HIGH |
| Botão apertado | o botão puxa para o GND | LOW |
Duas consequências que o aluno precisa internalizar: a leitura é invertida (apertado é LOW, não HIGH), e o circuito é mais simples porque o resistor não precisa existir na protoboard. Um botão são dois fios: um para o pino, um para o GND.
O INPUT_PULLDOWN também existe e faz o contrário: segura o pino em LOW e o botão puxa para 3,3 V. A leitura também é invertida. O curso usa INPUT_PULLUP porque ele é mais robusto contra ruído e não depende de 3,3 V chegar no botão.
Anti-vibração: por que o botão gruda
Contato mecânico não fecha uma vez. Ele vibra: fecha, abre por um instante, fecha, abre — tudo isso dentro de um ou dois milissegundos. Um laço que olha o botão a cada volta do loop pega essa vibração umas dez vezes e produz dez contagens de um aperto só.
Há duas formas de corrigir, e a aula usa as duas:
- Por tempo: só olhar o botão a cada 50 ms. A vibração acaba antes da próxima leitura.
- Por estado: contar só na borda, quando o estado muda de solto para apertado. A leitura continua a cada 50 ms, mas o
contagem++só acontece uma vez por aperto.
A segunda é mais robusta: mesmo que a vibração atravesse a janela de tempo, ela não gera contagem nova porque o estado já mudou.
Como o botão se comporta com o pull-up
Quando o aluno aperta o botão, ele não envia 3,3 V para o ESP32: ele puxa o pino para o GND. Isso é o oposto do que a maioria dos iniciantes imagina, e é a causa de "meu botão só funciona invertido".
Esse mesmo arranjo é o que o dia 9 usa no display, e o que o dia 11 reaproveita no projeto final: botão entre pino e GND, com o pull-up interno fazendo o resto.
Atividade
Montagem:
- Um dos lados do push button no
GPIO15, por jumper. - O lado vizinho do mesmo botão na trilha
GNDda protoboard, com outro jumper até oGNDda placa. - Resistor de 330 Ω entre o
GPIO2e um lado do LED de resposta. - O lado livre do LED na trilha
+da protoboard.
No push button de 4 pinos, os pinos do mesmo lado já estão ligados internamente. O botão só conduz em pares diagonalmente opostos: pinos da mesma fileira, nunca o par vizinho da outra fileira.
- Grave a resolução e confirme que o serial imprime a linha de cabeçalho
contagem | leitura | comentario. - Sem nada no botão, anote o que o serial mostra na coluna
leitura. Diga qual nível a entrada está lendo e por que. - Aperte e solte o botão uma vez. Anote quanto a contagem subiu. Se subiu mais de um, esse é o contato vibrando: escreva no caderno que correção de anti-vibração está faltando.
- Confira se
pinMode(PINO_BOTAO, INPUT_PULLUP)está nosetup, e escreva no caderno o que aconteceria se essa linha fosse removida. - Descomente, comente e teste: escreva uma versão sem a comparação
&& !estadoAnterior, mantendo oif (apertado). Meça quanto a contagem sobe em dez apertos e compare com a versão da resolução. - Troque o botão do
GPIO15para oGPIO4, mantenha oGNDe grave. Confirme que funciona igual e escreva por que o resistor de 330 Ω do LED não pode cumprir o papel do pull-up do botão.
Nota: 10 pontos. Critério de fim: um aperto do botão soma exatamente 1 na contagem, e o item 5 está medido e escrito.
Resolucao
// Aula 1 do dia 5: botão, entrada digital, pull-up e anti-vibração. // // Três coisas acontecem nesta aula e as três precisam ser mostradas separadas: // o pino começa como entrada, o pull-up interno segura o nível alto sozinho, // e o contato do botão vibra no instante do aperto. #include <Arduino.h> const int PINO_BOTAO = 15; const int PINO_LED = 2; const int ANTIVIBRACAO_MS = 50; // espera o contato assentar // O botão só conta depois de passar este tempo sem mudar de estado. É a // prática do dia: menos de 50 ms é imperceptível para o olho. unsigned long instanteDaLeitura = 0; bool estadoAnterior = false; bool botaoSolto = true; int contagem = 0; void setup() { // INPUT_PULLUP liga, dentro da placa, um resistor que puxa o pino para 3,3 V // quando ninguém está apertando. Sem resistor externo no kit, é este que // segura o nível. pinMode(PINO_BOTAO, INPUT_PULLUP); pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 5, aula 1: botao com pull-up ==="); Serial.println(); Serial.println("Aperte o botao quantas vezes quiser e veja a contagem subir."); Serial.println("contagem | leitura | comentario"); } void loop() { unsigned long agora = millis(); // Guarda de tempo: só olha o botão a cada 50 ms. Fora desse tempo, o // botão seria lido milhares de vezes por volta do laço. if (agora - instanteDaLeitura >= ANTIVIBRACAO_MS) { instanteDaLeitura = agora; // Com INPUT_PULLUP a leitura é INVERTIDA: solto devolve HIGH, // apertado devolve LOW. Este `== LOW` é o que traduz isso para // "apertado", e é onde o aluno costuma errar. bool apertado = (digitalRead(PINO_BOTAO) == LOW); // Anti-vibração por estado: conta só na BORDA, quando muda de solto para // apertado. Sem esta comparação, um aperto só vira uma contagem porque o // contato vibra por alguns milissegundos e o laço lê essa vibração umas // dez vezes. if (apertado && !estadoAnterior) { contagem++; digitalWrite(PINO_LED, !digitalRead(PINO_LED)); } estadoAnterior = apertado; botaoSolto = !apertado; Serial.print(contagem); Serial.print(" | "); Serial.print(apertado ? "LOW" : "HIGH"); Serial.print(" | "); Serial.println(botaoSolto ? "solto (pull-up segura)" : "apertado (puxa para GND)"); } }
Por que assim e não de outro jeito. O digitalRead está protegido por duas camadas: o if de tempo impede a leitura a cada volta do loop, e a comparação apertado && !estadoAnterior impede a contagem repetida. Uma sem a outra resolve; as duas juntas é o que o botão de verdade exige, e o item 5 prova isso na frente da turma.
A leitura invertida está escrita como digitalRead(PINO_BOTAO) == LOW, e o nome da variável é apertado. Assim o resto do código fala português — if (apertado) em vez de if (digitalRead(...) == LOW) repetido quatro vezes. Nomear a leitura na hora é o que impede o aluno de inverter o sentido em metade do programa.
O digitalWrite(PINO_LED, !digitalRead(PINO_LED)) inverte o LED a cada aperto. Ele usa digitalRead num pino de saída, e isso funciona: em um pino declarado OUTPUT, o digitalRead devolve o nível que foi colocado nele. É o mesmo truque que o alternar do dia 4, e o aluno já viu.
O cabeçalho do setup diz para apertar o botão, e o loop não tem delay. A leitura acontece a cada 50 ms, o suficiente para o olho acompanhar e imperceptível para quem aperta.
Criterios de correcao
| Critério | Pontos |
|---|---|
Botão entre GPIO15 e GND, com par diagonal oposto | 3 pontos |
INPUT_PULLUP no setup e leitura invertida com == LOW | 3 pontos |
| Anti-vibração por tempo e por estado presentes | 3 pontos |
| Item 5 medido e escrito no caderno | 1 ponto |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
Esquecer INPUT_PULLUP | Contagem pula sozinha, sem ninguem apertar | "Sem pull-up o pino fica flutuante e le qualquer coisa. Toda entrada precisa de um resistor que segure o nivel." |
Ler apertado como HIGH | "Apertou e o LED apagou" | "Com pull-up, apertado e LOW. O botao puxa para o GND. Inverta a comparacao." |
| Botão ligado entre pino e 3,3 V | Nada acontece ao apertar | "O botao vai para o GND, não para o positivo. E o pull-up que segura o lado de cima." |
| Pinos diagonalmente opostos do botão | Botao as vezes funciona, as vezes não | "No push button de 4 pinos so conduz o par diagonalmente oposto. Os da mesma fileira já estão ligados." |
| Anti-vibracao so por tempo, com janela curta | Aparicao de contagem dobrada em aperto rapido | "Use os dois: tempo para não ler tao direto, e comparacao de borda para contar so na mudanca de estado." |
Comparar com == LOW e esquecer o ! do estado anterior | Contagem sobe a cada leitura enquanto o botao fica apertado | "Falta o !. So conta quando muda de solto para apertado, não a cada leitura enquanto esta apertado." |
Usar INPUT_PULLDOWN sem trocar o circuito | Leitura invertida do jeito errado | "PULLDOWN também inverte. Com ele, apertado e HIGH e o circuito precisa ir para o 3,3 V." |
| Colocar 330 Ω no botao achando que e o pull-up | Botao funciona inconsistemente em bancada fria | "330 Ω e resistor de LED. Pull-up de botao e da ordem de 10 kΩ, ou então o interno da placa." |
Desafio extra
Escreva uma função bool botaoApertado(int pino) que encapsule o INPUT_PULLUP e a leitura invertida, devolvendo true quando o botão está apertado. Chame-a em dois botões diferentes, GPIO15 e GPIO4, e faça o LED da placa acender quando os dois estiverem apertados ao mesmo tempo, apagar quando só um estiver e ficar piscando quando nenhum estiver. Escreva no caderno quantas linhas o loop tem com a função e quantas tinha sem ela.
A resolucao, compilada
// Aula 1 do dia 5: botão, entrada digital, pull-up e anti-vibração. // // Três coisas acontecem nesta aula e as três precisam ser mostradas separadas: // o pino começa como entrada, o pull-up interno segura o nível alto sozinho, // e o contato do botão vibra no instante do aperto. #include <Arduino.h> const int PINO_BOTAO = 15; const int PINO_LED = 2; const int ANTIVIBRACAO_MS = 50; // espera o contato assentar // O botão só conta depois de passar este tempo sem mudar de estado. É a // prática do dia: menos de 50 ms é imperceptível para o olho. unsigned long instanteDaLeitura = 0; bool estadoAnterior = false; bool botaoSolto = true; int contagem = 0; void setup() { // INPUT_PULLUP liga, dentro da placa, um resistor que puxa o pino para 3,3 V // quando ninguém está apertando. Sem resistor externo no kit, é este que // segura o nível. pinMode(PINO_BOTAO, INPUT_PULLUP); pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 5, aula 1: botao com pull-up ==="); Serial.println(); Serial.println("Aperte o botao quantas vezes quiser e veja a contagem subir."); Serial.println("contagem | leitura | comentario"); } void loop() { unsigned long agora = millis(); // Guarda de tempo: só olha o botão a cada 50 ms. Fora desse tempo, o // botão seria lido milhares de vezes por volta do laço. if (agora - instanteDaLeitura >= ANTIVIBRACAO_MS) { instanteDaLeitura = agora; // Com INPUT_PULLUP a leitura é INVERTIDA: solto devolve HIGH, // apertado devolve LOW. Este `== LOW` é o que traduz isso para // "apertado", e é onde o aluno costuma errar. bool apertado = (digitalRead(PINO_BOTAO) == LOW); // Anti-vibração por estado: conta só na BORDA, quando muda de solto para // apertado. Sem esta comparação, um aperto só vira uma contagem porque o // contato vibra por alguns milissegundos e o laço lê essa vibração umas // dez vezes. if (apertado && !estadoAnterior) { contagem++; digitalWrite(PINO_LED, !digitalRead(PINO_LED)); } estadoAnterior = apertado; botaoSolto = !apertado; Serial.print(contagem); Serial.print(" | "); Serial.print(apertado ? "LOW" : "HIGH"); Serial.print(" | "); Serial.println(botaoSolto ? "solto (pull-up segura)" : "apertado (puxa para GND)"); } }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia05 aula1.
Aula 2 — PWM e potenciometro: o analogico entra no digital
Objetivos
- Explicar o que é PWM e por que ele produz brilho intermediário em um pino digital.
- Ler o potenciômetro com
analogReade saber que o retorno vai de 0 a 4095. - Converter a faixa do ADC para a faixa do PWM com
mape ler o resultado como porcentagem. - Suavizar a leitura com filtro de média e justificar por que o LED deixa de piscar sozinho.
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 potenciômetro de 10 kΩ de 3 pinos por aluno
- 1 módulo de alimentação MB102 por dupla
- Folha com as três faixas numéricas do dia impressa, uma por dupla
Conceitos
PWM: brilho falso com pino honesto
analogWrite(pino, 255) não coloca 3,3 V no pino. Ele alterna o pino entre HIGH e LOW muito rápido, e a fração de tempo em que fica ligado se chama duty cycle:
| Valor | Duty cycle | O que o olho vê |
|---|---|---|
| 0 | 0% | apagado |
| 64 | 25% | brinco fraco |
| 128 | 50% | meia luz |
| 255 | 100% | aceso |
O pino continua digital: só existem HIGH e LOW. A sensação de "analogico" vem do olho, que não consegue seguir a piscada e integra o que enxerga. É por isso que o nome é modulação por largura de pulso: o que muda é a largura do pulso, não a altura.
A frequência padrão do ESP32 é alta o suficiente para isso não aparecer. O duty cycle é o que o aluno controla — e o valor vai de 0 a 255, não de 0 a 100 nem de 0 a 4095.
O ADC: o pino que lê tensão
analogRead é o outro sentido da mesma história: em vez de mandar um nível para fora, ele lê uma tensão e devolve um inteiro.
O ADC do ESP32 tem 12 bits, o que dá 4096 níveis — de 0 a 4095. Não é 0 a 4096, e o map com 4095 no fim é o que garante que o valor máximo do potenciômetro chegue em 255 e não em 254.
Os pinos do ADC no ESP32 são os que só fazem entrada: os GPIO32 a GPIO39. O GPIO34 é o mais usado, porque é analógico puro e não tem pull-up nem pull-down interno. Se o aluno ligar o potenciômetro num pino comum como o GPIO2, a leitura sai errada.
map converte faixa, não unidade
int brilho = map(leituraBruta, 0, 4095, 0, 255);
map não converte voltas em luminosidade, nem pessoas emvoted. Ele só recomeça a reta: onde a leitura valia 0 agora vale 0, onde valia 4095 agora vale 255, e o meio é escalado junto.
É por isso que ele não serve para converter volts. Para isso, a conta é multiplicação e divisão: leitura * 3.3 / 4095.0. Com 3 inteiro em vez de 3.3, o resultado perde o resto e dá metade do valor — o mesmo defeito da aula 2 do dia 2, agora com consequência visível.
Filtro de média
O cursor do potenciômetro é uma peça de metal sobre uma trilha. O contato é imperfeito, e a leitura crua salta de um valor para outro sozinha, mesmo com a mão longe do potenciômetro. Com o analogWrite ligado direto nesse número, o LED pisca sozinho num ritmo rápido e constante.
A correção é somar oito leituras e dividir por oito. O resultado é uma média, o ruído se cancela parcialmente, e o LED fica parado. O custo é a latência: a leitura passa a descrever o que o cursor fez recentemente, e não o que ele está fazendo agora.
Atividade
Montagem:
- O pino do meio do potenciômetro no
GPIO34, por jumper. - O pino de uma das pontas na trilha
GNDda protoboard, ligado aoGNDda placa. - O pino da outra ponta na trilha
+da protoboard, ligado ao3V3da placa. - Resistor de 330 Ω entre o
GPIO2e um lado do LED. - O lado livre do LED na mesma trilha
+do potenciômetro.
O potenciômetro de 3 pinos tem duas pontas de alimentação e um pino central, que é a varredura. Se o pino central não ir no GPIO34, a leitura sai qualquer coisa.
- Grave a resolução e gire o cursor do potenciômetro de uma ponta à outra. Confirme que as quatro colunas do serial andam juntas.
- Anote os valores da coluna
crunas duas pontas do curso e no meio do cursor. Diga se os três números são simétricos. - Complete a tabela do caderno:
cruvale 4095 na uma ponta e 0 na outra. Quanto vale opwmem cada uma? Use a fórmula e não a tela. - Remova temporariamente o filtro de média, deixe uma leitura só e grave. Gire o cursor lentamente e anote o que o LED faz. Depois volte o filtro e repita.
- Troque
analogReadResolution(12)poranalogReadResolution(10)e grave. Anote o valor máximo que aparece na colunacrucom o cursor no fim do curso e explique, em uma frase, por que omapcom 4095 nunca chega em 255. - Escreva no caderno a linha que converte a leitura do ADC em volts com precisão de centésimos, e diga qual é a diferença entre essa linha e a linha do
mapda atividade.
Nota: 10 pontos. Critério de fim: o brilho do LED acompanha o cursor de forma contínua e o item 5 está respondido com o número máximo observado.
Resolucao
// Aula 2 do dia 5: PWM e potenciômetro, o analógico entra no digital. // // O potentiômetro entrega uma TENSÃO, não um número. O `analogRead` transforma // essa tensão em um inteiro de 0 a 4095 (12 bits), e o `analogWrite` faz o // caminho inverso: número de 0 a 255 vira duty cycle. Este sketch liga as // duas pontas e mostra os dois números crus antes de convertê-los. #include <Arduino.h> const int PINO_POTENCIOMETRO = 34; // GPIO34 é só entrada: serve para o potenciômetro const int PINO_LED = 2; const int LEITURA_MAXIMA_ADC = 4095; const int PWM_MAXIMO = 255; const int INTERVALO_MS = 100; const int TAMANHO_DA_AMOSTRA = 8; // quantas leituras somar antes de dividir // Instante da última leitura. Esta aula NÃO usa delay para esperar: o laço // precisa ficar livre para responder ao potenciômetro enquanto imprime. unsigned long instanteDaLeitura = 0; int leituraBruta = 0; void setup() { pinMode(PINO_LED, OUTPUT); // Pede ao ADC a máxima precisão: 12 bits, de 0 a 4095. Sem esta linha o // ESP32 usa 10 bits, de 0 a 1023, e o `map` com 4095 nunca chega no fim. analogReadResolution(12); // A atenuação é a faixa de tensão que o ADC mede. ADC_11db mede até perto // de 3,3 V — é o que o potenciômetro do kit entrega. analogSetPinAttenuation(PINO_POTENCIOMETRO, ADC_11db); Serial.begin(115200); Serial.println(); Serial.println("=== dia 5, aula 2: PWM e potenciometro ==="); Serial.println(); Serial.println("Gire o cursor do potenciometro."); Serial.println("cru | volts | pwm | brilho %"); } void loop() { unsigned long agora = millis(); if (agora - instanteDaLeitura >= INTERVALO_MS) { instanteDaLeitura = agora; // Filtro de média: soma 8 leituras e divide. O potentiômetro tem um // cursor de metal que suja a trilha, e a leitura crua fica pulando de // 2047 para 2049 sozinha. A média não deixa o LED piscar sozinho. int soma = 0; for (int i = 0; i < TAMANHO_DA_AMOSTRA; i++) { soma += analogRead(PINO_POTENCIOMETRO); } int leituraCrua = soma / TAMANHO_DA_AMOSTRA; // Guardando em variável em vez de repetir a conta: o `analogWrite` é o // único lugar que precisa do número, e ele precisa do valor suavizado. leituraBruta = leituraCrua; // analogWrite NÃO devolve o que o pino tem: ele MANDA um duty cycle de 0 // a 255. Escrever 255 é tensão cheia; 0 é desligado. int brilho = map(leituraBruta, 0, LEITURA_MAXIMA_ADC, 0, PWM_MAXIMO); analogWrite(PINO_LED, brilho); float volts = leituraBruta * 3.3 / LEITURA_MAXIMA_ADC; int percentual = brilho * 100 / PWM_MAXIMO; Serial.print(leituraBruta); Serial.print(" | "); Serial.print(volts); Serial.print(" | "); Serial.print(brilho); Serial.print(" | "); Serial.println(percentual); } }
Por que assim e não de outro jeito. As quatro colunas do serial existem para o professor mostrar que um número físico vira três números diferentes conforme a pergunta que se faz: cru, tensão, duty cycle. O aluno que só imprime o duty cycle não sabe mais se o potenciômetro está no fim ou se o map está errado.
O filtro de média soma oito leituras. Oito foi escolhido porque é o suficiente para abafar o salto sem deixar o LED atrasar; sete ou quinze servem igual, e o professor pode pedir a troca e perguntar por que ainda funciona.
A tensão é calculada com 3.3 e não com map, e a linha está logo abaixo da do brilho. Essa proximidade é o argumento do dia: as duas conversões estão lado a lado, e só uma delas está certa.
A atenuação ADC_11db é o que permite medir até perto de 3,3 V. Sem ela, o ADC lê até cerca de 1,1 V e a leitura satura no meio do curso — o defeito que aparece quando o aluno troca o potenciômetro pelo LDR no dia 8 sem ajustar a linha.
Criterios de correcao
| Critério | Pontos |
|---|---|
Potenciômetro com o pino central no GPIO34 e as pontas em 3V3 e GND | 3 pontos |
pinMode do LED em OUTPUT antes do analogWrite | 2 pontos |
analogReadResolution(12) presente e item 5 respondido com o número medido | 3 pontos |
| Filtro de média presente e o efeito da remoção descrito no caderno | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
Potenciometro no GPIO2 | Leitura sempre perto de zero ou de 4095 | "O ADC so existe nos pinos GPIO32 a GPIO39. Use o GPIO34." |
Usar 4096 no map | Brilho maximo em 254, nunca chega a 255 | "O ADC vai de 0 a 4095: são 4096 niveis, numerados de zero. O ultimo e 4095." |
| Escrever 3 inteiro na conversao para volts | Tensão aparece com metade do valor | "Ponha 3.3. Inteiro descarta o resto da divisão e o número sai pela metade." |
Achar que analogWrite manda tensão | Aluno acha que 128 e 1,65 volt | "analogWrite manda duty cycle, de 0 a 255. Não ha tensão intermedia no pino." |
Esquecer analogReadResolution(12) | Leitura para em 1023 e o brilho nunca chega ao fim | "Sem essa linha o ADC usa 10 bits. Ponha 12 e o número vai até 4095." |
| Ponto central do potenciometro no GND | Leitura fixa, cursor não muda nada | "O pino central e a varredura. Ele vai no pino analogico; as pontas vao para 3V3 e GND." |
| Filtro de media com divisor errado | Brilho para na metade depois da mediao | "A soma foi de oito leituras, então divide por oito. Divisor diferente muda a escala toda." |
| Fio do 3V3 do potenciometro no 5V | Leitura satura em 4095 | "O ADC do ESP32 vai até 3,3 V. Alimenta o circuito com o 3V3, e o 5V fica para o LED." |
Desafio extra
Faça o LED responder ao potenciômetro de forma não linear: em vez do map linear, escreva uma linha que acenda o LED só quando o cursor passar da metade, e aí apague completamente nos últimos 20% do curso. Depois explique no caderno por que essa linha não é um map e qual seria o problema de usar map para chegar nela.
A resolucao, compilada
// Aula 2 do dia 5: PWM e potenciômetro, o analógico entra no digital. // // O potentiômetro entrega uma TENSÃO, não um número. O `analogRead` transforma // essa tensão em um inteiro de 0 a 4095 (12 bits), e o `analogWrite` faz o // caminho inverso: número de 0 a 255 vira duty cycle. Este sketch liga as // duas pontas e mostra os dois números crus antes de convertê-los. #include <Arduino.h> const int PINO_POTENCIOMETRO = 34; // GPIO34 é só entrada: serve para o potenciômetro const int PINO_LED = 2; const int LEITURA_MAXIMA_ADC = 4095; const int PWM_MAXIMO = 255; const int INTERVALO_MS = 100; const int TAMANHO_DA_AMOSTRA = 8; // quantas leituras somar antes de dividir // Instante da última leitura. Esta aula NÃO usa delay para esperar: o laço // precisa ficar livre para responder ao potenciômetro enquanto imprime. unsigned long instanteDaLeitura = 0; int leituraBruta = 0; void setup() { pinMode(PINO_LED, OUTPUT); // Pede ao ADC a máxima precisão: 12 bits, de 0 a 4095. Sem esta linha o // ESP32 usa 10 bits, de 0 a 1023, e o `map` com 4095 nunca chega no fim. analogReadResolution(12); // A atenuação é a faixa de tensão que o ADC mede. ADC_11db mede até perto // de 3,3 V — é o que o potenciômetro do kit entrega. analogSetPinAttenuation(PINO_POTENCIOMETRO, ADC_11db); Serial.begin(115200); Serial.println(); Serial.println("=== dia 5, aula 2: PWM e potenciometro ==="); Serial.println(); Serial.println("Gire o cursor do potenciometro."); Serial.println("cru | volts | pwm | brilho %"); } void loop() { unsigned long agora = millis(); if (agora - instanteDaLeitura >= INTERVALO_MS) { instanteDaLeitura = agora; // Filtro de média: soma 8 leituras e divide. O potentiômetro tem um // cursor de metal que suja a trilha, e a leitura crua fica pulando de // 2047 para 2049 sozinha. A média não deixa o LED piscar sozinho. int soma = 0; for (int i = 0; i < TAMANHO_DA_AMOSTRA; i++) { soma += analogRead(PINO_POTENCIOMETRO); } int leituraCrua = soma / TAMANHO_DA_AMOSTRA; // Guardando em variável em vez de repetir a conta: o `analogWrite` é o // único lugar que precisa do número, e ele precisa do valor suavizado. leituraBruta = leituraCrua; // analogWrite NÃO devolve o que o pino tem: ele MANDA um duty cycle de 0 // a 255. Escrever 255 é tensão cheia; 0 é desligado. int brilho = map(leituraBruta, 0, LEITURA_MAXIMA_ADC, 0, PWM_MAXIMO); analogWrite(PINO_LED, brilho); float volts = leituraBruta * 3.3 / LEITURA_MAXIMA_ADC; int percentual = brilho * 100 / PWM_MAXIMO; Serial.print(leituraBruta); Serial.print(" | "); Serial.print(volts); Serial.print(" | "); Serial.print(brilho); Serial.print(" | "); Serial.println(percentual); } }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia05 aula2.
