Files
ESPDocs/public/formacoes/2/apostila.html
T
Lucas RayanandClaude Opus 5.5 1860e34cbb
Release / Plano (reaproveitar a imagem?) (push) Successful in 7s
CI / Lint e auditorias (push) Successful in 1m48s
CI / Trivy (dependências e segredos) (push) Successful in 12s
CI / Imagem Docker (linux/arm64) (push) Failing after 2m27s
Release / ci (push) Failing after 4m31s
Release / Publica a imagem (linux/arm64) (push) Skipped
Release / Cria a release no Gitea (push) Skipped
feat(formacoes): módulo 2 (DHT22 + LCD 16x2 no Wokwi), PDFs e página de entrada
- Formação 2 da LASER: slides visuais, apostila, projeto do Wokwi e scanner
  I2C, validados no simulador (leitura normal, temperatura negativa e sensor
  desconectado). Circuito com pull-ups de 10 kΩ e 4,7 kΩ para 3V3 e aviso
  sobre os pull-ups de 5 V da plaquinha PCF8574.
- PDFs prontos dos slides e das apostilas dos módulos 1 e 2;
  slides-pdf agora também exporta a apostila.
- Página de entrada em /formacoes e /formacoes/N, gerada por
  scripts/formacoes-viewer.mjs a partir do formacao.json de cada pasta.
- A apostila passou de index.html para apostila.html.
- Corrige o link do Fritzing da apostila 1 (/apostilas/1 não existe).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:11:28 -03:00

801 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>LASER · Formação em ESP32 · Módulo 2: Estação de Temperatura e Umidade</title>
<meta name="author" content="Lucas Rayan Guerra">
<meta name="description" content="Apostila do módulo 2 da formação em ESP32 da LASER: DHT22, LCD 16x2 I2C e ESP32-S3, simulados no Wokwi com o framework Arduino.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700;800&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<link rel="icon" type="image/svg+xml" href="../../marca/espdocs-marca.svg">
<link rel="stylesheet" href="../assets/apostila.css">
</head>
<body>
<p class="aviso-carregando">Paginando a apostila…</p>
<!-- ══════════════════════════════ CAPA ══════════════════════════════ -->
<section class="pagina-capa">
<div class="capa">
<img class="capa-curvas" src="../assets/capa-curvas.svg" alt="">
<div class="capa-marca">
<img src="../assets/laser-escuro.svg" alt="LASER">
<span>Liga Acadêmica de Sistemas Embarcados<br>Centro de Informática · UFPE</span>
</div>
<div class="capa-selo">Formação em ESP32 · Módulo 2</div>
<h1 class="capa-titulo">Estação de<br><strong>temperatura e umidade</strong></h1>
<p class="capa-sub">
Um DHT22, um display LCD 16x2 e um ESP32-S3, primeiro no simulador Wokwi e depois na
bancada. Dois protocolos de comunicação, um programa curto e os cuidados elétricos que
fazem o circuito funcionar fora da tela também.
</p>
<div class="capa-rodape">
<div>
<strong>Lucas Rayan Guerra</strong>
Ciência Embarcada · ESPDocs
linkedin.com/in/lucasrguerra
</div>
<div style="text-align:right">
<strong>Material de apoio</strong>
Formação presencial · uso livre para fins educacionais
</div>
</div>
</div>
</section>
<!-- ══════════════════════════ APRESENTAÇÃO ══════════════════════════ -->
<section class="capitulo" id="apresentacao">
<div class="capitulo-abertura">
<div>
<h2>Apresentação</h2>
<p class="subtitulo">O que vamos construir, o que você precisa e como usar esta apostila.</p>
</div>
</div>
<p>
No módulo 1 lemos um sensor e imprimimos o resultado no monitor serial, que só existe
enquanto há um computador ligado à placa. Neste módulo o projeto ganha <strong>uma tela
própria</strong>: um display LCD de 16 colunas por 2 linhas mostra a temperatura e a
umidade medidas por um <strong>DHT22</strong>, e o conjunto passa a funcionar sozinho,
ligado em qualquer carregador USB.
</p>
<p>
Toda a formação acontece no <strong>Wokwi</strong>, um simulador de microcontroladores que
roda no navegador. Ele compila o mesmo código que você gravaria na placa, simula o ESP32-S3,
o sensor e o display, e deixa você mudar a temperatura com um controle deslizante. Assim
cada pessoa monta e testa o circuito sem precisar de componentes, e quem quiser repetir em
casa com peças de verdade encontra no capítulo 7 o que muda fora do simulador.
</p>
<h3>O que você vai levar deste módulo</h3>
<ul>
<li>Montar e simular um projeto completo de ESP32 no Wokwi, do zero.</li>
<li>Entender como o DHT22 transmite uma medição por um único fio.</li>
<li>Entender o barramento I2C: dois fios, endereços e resistores de pull-up.</li>
<li>Escrever no LCD 16x2, inclusive com um caractere que você mesmo desenha.</li>
<li>Saber o que precisa mudar para o mesmo circuito funcionar na bancada.</li>
</ul>
<h3>Pré-requisitos</h3>
<p>
Ter acompanhado o módulo 1 ajuda, mas não é obrigatório. Basta saber o que são
<code>setup()</code> e <code>loop()</code> num programa Arduino. Para a simulação você
precisa só de um navegador atualizado; não é preciso instalar nada nem criar conta.
</p>
<h3>Os arquivos deste módulo</h3>
<p>
Tudo o que aparece nesta apostila foi compilado e executado no Wokwi, e os arquivos estão
na mesma pasta da formação:
</p>
<table>
<thead><tr><th style="width:34%">Arquivo</th><th>Conteúdo</th></tr></thead>
<tbody>
<tr><td class="destaque"><a href="wokwi/sketch.ino">sketch.ino</a></td><td>O programa da estação</td></tr>
<tr><td class="destaque"><a href="wokwi/diagram.json">diagram.json</a></td><td>O circuito: peças, posições e fios</td></tr>
<tr><td class="destaque"><a href="wokwi/libraries.txt">libraries.txt</a></td><td>As bibliotecas que o Wokwi deve instalar</td></tr>
<tr><td class="destaque"><a href="wokwi/scanner-i2c.ino">scanner-i2c.ino</a></td><td>Programa de diagnóstico do barramento I2C</td></tr>
<tr><td class="destaque"><a href="modulo-2-wokwi.zip">modulo-2-wokwi.zip</a></td><td>Os quatro arquivos acima num só pacote</td></tr>
<tr><td class="destaque"><a href="LASER-ESP32-Modulo-2-Apostila.pdf">Apostila em PDF</a></td><td>Esta apostila, pronta para imprimir</td></tr>
<tr><td class="destaque"><a href="LASER-ESP32-Modulo-2-Slides.pdf">Slides em PDF</a></td><td>Os slides da formação, em 16:9</td></tr>
</tbody>
</table>
<div class="grupo">
<p class="junto">Ao longo do texto você encontra quatro tipos de caixa:</p>
<div class="caixa dica"><div class="caixa-titulo">Dica</div><p>Atalho, boa prática ou recomendação que economiza tempo.</p></div>
<div class="caixa atencao"><div class="caixa-titulo">Atenção</div><p>Ponto que costuma causar problema se passar despercebido.</p></div>
<div class="caixa erro"><div class="caixa-titulo">Erro comum</div><p>Armadilha concreta, com o sintoma que ela produz e como escapar.</p></div>
<div class="caixa curiosidade"><div class="caixa-titulo">Curiosidade</div><p>Contexto ou detalhe técnico que ajuda a fixar o conceito.</p></div>
</div>
</section>
<!-- ═════════════════════════════ SUMÁRIO ═════════════════════════════ -->
<section class="capitulo">
<div class="capitulo-abertura">
<div>
<h2>Sumário</h2>
<p class="subtitulo">Oito capítulos, da peça solta ao circuito funcionando.</p>
</div>
</div>
<nav class="sumario">
<a href="#cap1"><span class="num">1</span><span class="txt">O projeto</span><span class="pontos"></span></a>
<a href="#cap2"><span class="num">2</span><span class="txt">O sensor DHT22</span><span class="pontos"></span></a>
<a href="#cap3"><span class="num">3</span><span class="txt">O display LCD 16x2 e o barramento I2C</span><span class="pontos"></span></a>
<a href="#cap4"><span class="num">4</span><span class="txt">O simulador Wokwi</span><span class="pontos"></span></a>
<a href="#cap5"><span class="num">5</span><span class="txt">Montagem</span><span class="pontos"></span></a>
<a href="#cap6"><span class="num">6</span><span class="txt">O programa</span><span class="pontos"></span></a>
<a href="#cap7"><span class="num">7</span><span class="txt">Da simulação para a bancada</span><span class="pontos"></span></a>
<a href="#cap8"><span class="num">8</span><span class="txt">Problemas comuns e desafios</span><span class="pontos"></span></a>
</nav>
</section>
<!-- ═══════════════════════════ CAPÍTULO 1 ═══════════════════════════ -->
<section class="capitulo" id="cap1">
<div class="capitulo-abertura">
<div class="capitulo-numero">1</div>
<div>
<h2>O projeto</h2>
<p class="subtitulo">Três peças, dois protocolos e uma tela que mostra o ambiente.</p>
</div>
</div>
<p>
A estação é composta por três peças. O <strong>DHT22</strong> mede temperatura e umidade
relativa do ar. O <strong>ESP32-S3</strong> pede uma medição a cada dois segundos, formata
os números e os envia ao <strong>LCD 16x2</strong>, que os mantém na tela até a próxima
atualização.
</p>
<figure>
<img src="circuito-wokwi.png" alt="Circuito completo no simulador Wokwi: o DHT22 à esquerda, a placa ESP32-S3-DevKitC-1 ao centro, três resistores no alto ligados ao 3V3 e o LCD 16x2 embaixo mostrando Temp: 26.5°C e Umid: 62.0 %." style="max-height:120mm">
<figcaption>
O circuito deste módulo rodando no Wokwi. O display mostra a leitura que o DHT22 simulado
está informando: 26,5 °C e 62 % de umidade.
</figcaption>
</figure>
<p>
Cada peça conversa com o ESP32 de um jeito diferente, e é isso que torna o projeto bom para
aprender. O DHT22 usa um <strong>protocolo próprio de um fio</strong>, em que o valor de
cada bit está na duração de um pulso. O LCD usa o <strong>I2C</strong>, um barramento
padronizado de dois fios em que cada dispositivo tem um endereço. Quem entende esses dois
protocolos já entende a maior parte dos sensores e módulos baratos do mercado.
</p>
<h3>Lista de material</h3>
<p>No Wokwi as peças já existem. Para montar em bancada você vai precisar de:</p>
<table>
<thead><tr><th style="width:44%">Item</th><th style="width:10%">Qtd.</th><th>Observação</th></tr></thead>
<tbody>
<tr><td class="destaque">ESP32-S3-DevKitC-1</td><td>1</td><td>Qualquer variante de memória serve</td></tr>
<tr><td class="destaque">Sensor DHT22 (AM2302)</td><td>1</td><td>O componente branco de 4 pinos</td></tr>
<tr><td class="destaque">Display LCD 16x2 com módulo I2C</td><td>1</td><td>A plaquinha PCF8574 vem soldada atrás</td></tr>
<tr><td class="destaque">Conversor de nível I2C (BSS138)</td><td>1</td><td>Explicado no capítulo 7</td></tr>
<tr><td class="destaque">Resistor de 10 kΩ</td><td>1</td><td>Pull-up do DHT22 (marrom, preto, laranja)</td></tr>
<tr><td class="destaque">Resistor de 4,7 kΩ</td><td>2</td><td>Pull-ups do I2C (amarelo, violeta, vermelho)</td></tr>
<tr><td class="destaque">Protoboard, jumpers e cabo USB de dados</td><td>—</td><td>Cabo só de carga não grava a placa</td></tr>
</tbody>
</table>
</section>
<!-- ═══════════════════════════ CAPÍTULO 2 ═══════════════════════════ -->
<section class="capitulo" id="cap2">
<div class="capitulo-abertura">
<div class="capitulo-numero">2</div>
<div>
<h2>O sensor DHT22</h2>
<p class="subtitulo">Dois sensores numa caixinha e um protocolo de um fio só.</p>
</div>
</div>
<p>
Dentro do DHT22 existem dois elementos de medição e um pequeno microcontrolador. A umidade
é medida por um <strong>sensor capacitivo</strong>: um material que absorve vapor d'água
e, com isso, muda a capacitância. A temperatura vem de um <strong>termistor</strong>, um
resistor cujo valor varia com o calor. O microcontrolador interno lê os dois, aplica a
calibração gravada de fábrica e entrega os números já prontos, em formato digital.
</p>
<h3>DHT22 ou DHT11</h3>
<p>No módulo 1 usamos o DHT11. O DHT22 tem a mesma pinagem e o mesmo protocolo, mas mede melhor:</p>
<table>
<thead><tr><th style="width:34%">Característica</th><th style="width:33%">DHT11</th><th>DHT22</th></tr></thead>
<tbody>
<tr><td class="destaque">Faixa de temperatura</td><td>0 a 50 °C</td><td class="sim">−40 a 80 °C</td></tr>
<tr><td class="destaque">Precisão de temperatura</td><td>±2 °C</td><td class="sim">±0,5 °C</td></tr>
<tr><td class="destaque">Faixa de umidade</td><td>20 a 90 %</td><td class="sim">0 a 100 %</td></tr>
<tr><td class="destaque">Precisão de umidade</td><td>±5 %</td><td class="sim">±2 a 5 %</td></tr>
<tr><td class="destaque">Resolução</td><td>1 °C e 1 %</td><td class="sim">0,1 °C e 0,1 %</td></tr>
<tr><td class="destaque">Intervalo mínimo entre leituras</td><td class="sim">1 s</td><td>2 s</td></tr>
<tr><td class="destaque">Alimentação</td><td>3,3 a 5 V</td><td>3,3 a 6 V</td></tr>
</tbody>
<caption>Valores típicos dos datasheets da Aosong. Confira sempre o do seu fornecedor.</caption>
</table>
<p>
O preço é a única desvantagem real do DHT22, e o intervalo de dois segundos entre leituras
raramente importa: temperatura e umidade de um ambiente não mudam mais rápido do que isso.
</p>
<h3>Os quatro pinos</h3>
<table>
<thead><tr><th style="width:16%">Pino</th><th style="width:22%">Nome</th><th>Ligação</th></tr></thead>
<tbody>
<tr><td class="destaque">1</td><td>VCC</td><td>3V3 do ESP32</td></tr>
<tr><td class="destaque">2</td><td>DATA</td><td>GPIO12, com pull-up de 10 kΩ para o 3V3</td></tr>
<tr><td class="destaque">3</td><td>NC</td><td>Não conectado</td></tr>
<tr><td class="destaque">4</td><td>GND</td><td>GND do ESP32</td></tr>
</tbody>
<caption>Pinos contados da esquerda para a direita, com a grade do sensor virada para você.</caption>
</table>
<h3>Como uma medição atravessa um único fio</h3>
<p>
O ESP32 e o sensor dividem o mesmo fio de dados, e por isso nenhum dos dois "força" nível
alto: os dois apenas puxam a linha para o terra quando precisam de um zero e a soltam no
resto do tempo. Quem traz a linha de volta para o nível alto é o resistor de
<strong>pull-up</strong>. Uma leitura acontece assim:
</p>
<div class="passo">
<div class="passo-num">1</div>
<div><h3>O ESP32 pede a medição</h3><p>Puxa a linha para baixo por cerca de 1 ms e depois a solta.</p></div>
</div>
<div class="passo">
<div class="passo-num">2</div>
<div><h3>O sensor confirma</h3><p>Responde com 80 µs em nível baixo seguidos de 80 µs em nível alto.</p></div>
</div>
<div class="passo">
<div class="passo-num">3</div>
<div>
<h3>Chegam 40 bits</h3>
<p>
Cada bit começa com 50 µs em baixo. Depois vem um pulso alto: se ele dura cerca de
26 µs é um <strong>0</strong>; se dura cerca de 70 µs é um <strong>1</strong>.
São 16 bits de umidade, 16 de temperatura e 8 de verificação.
</p>
</div>
</div>
<figure>
<svg class="diagrama" viewBox="0 0 640 150" role="img" aria-label="Diagrama de tempo: pedido do ESP32, resposta do sensor, um bit 0 com pulso alto curto e um bit 1 com pulso alto longo.">
<g stroke="#9aa9b3" stroke-width="0.8" stroke-dasharray="2 3" fill="none">
<line x1="50" y1="40" x2="630" y2="40"/><line x1="50" y1="100" x2="630" y2="100"/>
</g>
<text x="44" y="43" font-size="9" text-anchor="end" fill="#5b6b75" font-family="JetBrains Mono, monospace">alto</text>
<text x="44" y="103" font-size="9" text-anchor="end" fill="#5b6b75" font-family="JetBrains Mono, monospace">baixo</text>
<polyline fill="none" stroke="#003049" stroke-width="2.2" stroke-linejoin="round"
points="50,40 70,40 70,100 170,100 170,40 205,40 205,100 255,100 255,40 305,40 305,100 345,100 345,40 375,40 375,100 415,100 415,40 500,40 500,100 540,100 540,40 630,40"/>
<g font-family="JetBrains Mono, monospace" font-size="9.5" fill="#D62828" text-anchor="middle">
<text x="120" y="30">~1 ms</text><text x="280" y="30">80 µs</text><text x="360" y="30">26 µs</text><text x="457" y="30">70 µs</text>
</g>
<g font-family="Inter, sans-serif" font-size="10" fill="#1d2b33" text-anchor="middle">
<text x="120" y="125">ESP32 pede</text><text x="255" y="125">sensor confirma</text><text x="360" y="125">bit 0</text><text x="477" y="125">bit 1</text><text x="595" y="125" fill="#5b6b75">…40 bits</text>
</g>
</svg>
<figcaption>
O valor de cada bit está na <strong>largura</strong> do pulso alto. Sem o pull-up a
linha sobe devagar demais, os tempos saem errados e a leitura falha.
</figcaption>
</figure>
<div class="caixa atencao">
<div class="caixa-titulo">Sensor solto ou módulo em plaquinha</div>
<p>
O DHT22 também é vendido soldado numa plaquinha de três pinos, que já traz o resistor de
pull-up. Se o seu for assim, o resistor externo de 10 kΩ é dispensável. O sensor solto,
de quatro pinos, <strong>precisa</strong> dele.
</p>
</div>
</section>
<!-- ═══════════════════════════ CAPÍTULO 3 ═══════════════════════════ -->
<section class="capitulo" id="cap3">
<div class="capitulo-abertura">
<div class="capitulo-numero">3</div>
<div>
<h2>O display LCD 16x2 e o barramento I2C</h2>
<p class="subtitulo">Trinta e dois caracteres, quatro fios e um endereço.</p>
</div>
</div>
<p>
O LCD 16x2 mostra duas linhas de dezesseis caracteres. Cada caractere é uma matriz de
<strong>5 por 8 pontos</strong>, e o controlador do display, o HD44780 ou um compatível,
já sabe desenhar letras, números e símbolos comuns. Ele também guarda até
<strong>oito caracteres personalizados</strong>, que você desenha ponto a ponto. É assim
que este projeto mostra o símbolo de grau.
</p>
<p>
Sozinho, o display precisa de pelo menos seis fios de dados e controle. Por isso ele quase
sempre vem com uma plaquinha soldada atrás, baseada no chip <strong>PCF8574</strong>, que
converte esses sinais para o barramento <strong>I2C</strong>: com ela, bastam quatro fios
para alimentar e controlar o display. A plaquinha também tem um <strong>trimpot</strong>
para ajustar o contraste.
</p>
<h3>Como o I2C funciona</h3>
<p>
O I2C usa dois fios: <strong>SDA</strong>, por onde passam os dados, e
<strong>SCL</strong>, o relógio que marca o ritmo da conversa. O ESP32 é o
<em>controlador</em>: é sempre ele quem inicia a comunicação. Os outros dispositivos são
<em>alvos</em>, e cada um tem um endereço de 7 bits. Vários dispositivos podem dividir os
mesmos dois fios, porque só responde quem tiver o endereço chamado.
</p>
<table>
<thead><tr><th style="width:30%">Plaquinha</th><th style="width:22%">Endereço</th><th>Como saber</th></tr></thead>
<tbody>
<tr><td class="destaque">PCF8574 ou PCF8574T</td><td class="mono">0x27</td><td>A mais comum, e a usada no Wokwi</td></tr>
<tr><td class="destaque">PCF8574A</td><td class="mono">0x3F</td><td>Mesma placa, outro chip</td></tr>
</tbody>
<caption>Na dúvida, o programa de scanner do capítulo 7 descobre o endereço por você.</caption>
</table>
<p>
Assim como no DHT22, as linhas do I2C são de <strong>dreno aberto</strong>: os
dispositivos só puxam para baixo, e são resistores de pull-up que trazem SDA e SCL de volta
ao nível alto. Valores entre 2,2 kΩ e 10 kΩ funcionam na velocidade padrão; aqui usamos
4,7 kΩ, o valor mais comum.
</p>
<div class="caixa erro">
<div class="caixa-titulo">O display liga mas não mostra nada</div>
<p>
Com o contraste no mínimo, a tela acende e parece vazia; com ele no máximo, aparecem
só retângulos cheios. Antes de desconfiar do código, gire o trimpot azul atrás do
display com uma chave de fenda até os caracteres aparecerem. No Wokwi isso não
existe, porque o contraste simulado já vem ajustado.
</p>
</div>
</section>
<!-- ═══════════════════════════ CAPÍTULO 4 ═══════════════════════════ -->
<section class="capitulo" id="cap4">
<div class="capitulo-abertura">
<div class="capitulo-numero">4</div>
<div>
<h2>O simulador Wokwi</h2>
<p class="subtitulo">Um laboratório de eletrônica que abre no navegador.</p>
</div>
</div>
<p>
O Wokwi simula o processador do ESP32-S3 instrução por instrução e simula também cada peça
ligada a ele. O programa é compilado com o mesmo <strong>núcleo Arduino da Espressif</strong>
que você instalaria na IDE, então o código que funciona aqui é o mesmo que vai para a placa.
</p>
<h3>Os três arquivos de um projeto</h3>
<table>
<thead><tr><th style="width:28%">Arquivo</th><th>Para que serve</th></tr></thead>
<tbody>
<tr><td class="destaque">sketch.ino</td><td>O programa, exatamente como na IDE do Arduino.</td></tr>
<tr><td class="destaque">diagram.json</td><td>As peças, a posição de cada uma, os fios e as propriedades, como a temperatura inicial do DHT22.</td></tr>
<tr><td class="destaque">libraries.txt</td><td>Uma biblioteca por linha. O Wokwi baixa todas antes de compilar.</td></tr>
</tbody>
</table>
<h3>Abrindo o projeto deste módulo</h3>
<div class="passo">
<div class="passo-num">1</div>
<div><h3>Crie um projeto vazio</h3><p>Acesse <strong>wokwi.com/projects/new/esp32-s3</strong>. Não é preciso criar conta para simular.</p></div>
</div>
<div class="passo">
<div class="passo-num">2</div>
<div><h3>Substitua o programa e o circuito</h3><p>Cole o conteúdo de <code>sketch.ino</code> na aba de mesmo nome e o de <code>diagram.json</code> na aba <em>diagram.json</em>, apagando o que já estava lá.</p></div>
</div>
<div class="passo">
<div class="passo-num">3</div>
<div><h3>Adicione as bibliotecas</h3><p>Abra a aba <em>Library Manager</em>, clique em <strong>+</strong> e adicione <strong>DHT sensor library</strong> e <strong>LiquidCrystal I2C</strong>. O Wokwi cria o <code>libraries.txt</code> sozinho.</p></div>
</div>
<div class="passo">
<div class="passo-num">4</div>
<div><h3>Simule</h3><p>Clique no botão verde. A primeira compilação leva alguns segundos; depois o display acende e o monitor serial aparece embaixo.</p></div>
</div>
<div class="caixa dica">
<div class="caixa-titulo">Mude o clima com um clique</div>
<p>
Com a simulação rodando, clique no DHT22: aparecem dois controles deslizantes, um de
temperatura e outro de umidade. Mova-os e veja o display acompanhar na leitura seguinte.
É o jeito mais rápido de testar o que o programa faz com valores extremos.
</p>
</div>
<div class="caixa atencao">
<div class="caixa-titulo">Salvar exige conta</div>
<p>
Sem login a simulação funciona normalmente, mas o projeto se perde ao fechar a aba. Para
guardar o seu trabalho, entre com uma conta gratuita e clique em <strong>Save</strong>,
ou mantenha os três arquivos no seu computador.
</p>
</div>
</section>
<!-- ═══════════════════════════ CAPÍTULO 5 ═══════════════════════════ -->
<section class="capitulo" id="cap5">
<div class="capitulo-abertura">
<div class="capitulo-numero">5</div>
<div>
<h2>Montagem</h2>
<p class="subtitulo">Nove ligações e três resistores.</p>
</div>
</div>
<p>
Todos os pinos usados ficam na <strong>coluna esquerda</strong> da placa, o que deixa a
fiação curta e sem cruzamentos. As cores seguem uma convenção que vale a pena adotar
também na bancada: vermelho para alimentação, preto para terra, e uma cor por sinal.
</p>
<table>
<thead><tr><th style="width:38%">De</th><th style="width:22%">Para</th><th>Cor e função</th></tr></thead>
<tbody>
<tr><td class="destaque">DHT22 VCC</td><td>3V3</td><td>Vermelho · alimentação</td></tr>
<tr><td class="destaque">DHT22 DATA</td><td>GPIO12</td><td>Amarelo · dados do sensor</td></tr>
<tr><td class="destaque">DHT22 GND</td><td>GND</td><td>Preto · terra</td></tr>
<tr><td class="destaque">Resistor 10 kΩ</td><td>GPIO12 ↔ 3V3</td><td>Pull-up do DHT22</td></tr>
<tr><td class="destaque">LCD VCC</td><td>5V</td><td>Vermelho · o display precisa de 5 V</td></tr>
<tr><td class="destaque">LCD GND</td><td>GND</td><td>Preto · terra</td></tr>
<tr><td class="destaque">LCD SDA</td><td>GPIO8</td><td>Azul · dados do I2C</td></tr>
<tr><td class="destaque">LCD SCL</td><td>GPIO9</td><td>Roxo · relógio do I2C</td></tr>
<tr><td class="destaque">2 resistores de 4,7 kΩ</td><td>GPIO8 e GPIO9 ↔ 3V3</td><td>Pull-ups do I2C</td></tr>
</tbody>
<caption>GPIO8 e GPIO9 são os pinos I2C padrão do ESP32-S3 no núcleo Arduino.</caption>
</table>
<figure>
<img src="circuito-wokwi.png" alt="Fiação no Wokwi: um barramento vermelho de 3V3 no alto alimenta o DHT22 e os três resistores; do resistor de 10 kΩ desce um fio amarelo até o GPIO12, e dos dois resistores de 4,7 kΩ descem os fios azul e roxo até o GPIO8 e o GPIO9, que seguem para o SDA e o SCL do LCD." style="max-height:92mm">
<figcaption>
O fio vermelho no alto funciona como um barramento de 3V3: dele saem a alimentação do
DHT22 e o lado de cima dos três resistores. O lado de baixo de cada resistor encontra a
linha de sinal correspondente.
</figcaption>
</figure>
<div class="caixa curiosidade">
<div class="caixa-titulo">Por que pull-up para 3V3, e não para 5V</div>
<p>
Os pinos do ESP32-S3 trabalham em 3,3 V e <strong>não toleram 5 V</strong>. Um resistor
de pull-up define a tensão de repouso da linha, então ligá-lo ao 5V colocaria 5 V
direto no GPIO. O display é alimentado em 5 V, mas as linhas de sinal precisam ficar
em 3,3 V. Na bancada isso exige um cuidado a mais, explicado no capítulo 7.
</p>
</div>
</section>
<!-- ═══════════════════════════ CAPÍTULO 6 ═══════════════════════════ -->
<section class="capitulo" id="cap6">
<div class="capitulo-abertura">
<div class="capitulo-numero">6</div>
<div>
<h2>O programa</h2>
<p class="subtitulo">Um programa curto, lido em três partes.</p>
</div>
</div>
<p>
O programa é lido aqui em três partes, exatamente como foi compilado e executado no Wokwi.
O arquivo completo, pronto para colar, está em <a href="wokwi/sketch.ino">wokwi/sketch.ino</a>.
</p>
<h3>1. Configuração</h3>
<pre class="miudo"><code><span class="c-pre">#include</span> <span class="c-str">&lt;Wire.h&gt;</span>
<span class="c-pre">#include</span> <span class="c-str">&lt;DHT.h&gt;</span>
<span class="c-pre">#include</span> &lt;LiquidCrystal_I2C.h&gt;
<span class="c-pre">#define</span> PINO_DHT <span class="c-num">12</span> <span class="c-com">// fio amarelo</span>
<span class="c-pre">#define</span> TIPO_DHT DHT22
<span class="c-pre">#define</span> PINO_SDA <span class="c-num">8</span> <span class="c-com">// I2C padrão do ESP32-S3</span>
<span class="c-pre">#define</span> PINO_SCL <span class="c-num">9</span>
DHT <span class="c-fun">dht</span>(PINO_DHT, TIPO_DHT);
LiquidCrystal_I2C <span class="c-fun">lcd</span>(<span class="c-num">0x27</span>, <span class="c-num">16</span>, <span class="c-num">2</span>); <span class="c-com">// endereço, colunas, linhas</span>
<span class="c-com">// Símbolo de grau: 8 linhas de 5 pixels</span>
<span class="c-key">byte</span> grau[<span class="c-num">8</span>] = { <span class="c-num">0b01100</span>, <span class="c-num">0b10010</span>, <span class="c-num">0b10010</span>, <span class="c-num">0b01100</span>, <span class="c-num">0</span>, <span class="c-num">0</span>, <span class="c-num">0</span>, <span class="c-num">0</span> };</code></pre>
<p>
As três bibliotecas cuidam do trabalho pesado: <code>Wire</code> é o I2C do ESP32,
<code>DHT</code> fala o protocolo do sensor e <code>LiquidCrystal_I2C</code> traduz
comandos como "escreva na linha 2" para o que a plaquinha PCF8574 entende. Os
<code>#define</code> reúnem no topo tudo o que depende da montagem: se você mudar um fio de
pino, só precisa mudar uma linha.
</p>
<p>
O vetor <code>grau</code> desenha o símbolo de grau. Cada número é uma linha do caractere,
e cada bit <code>1</code> acende um dos cinco pontos daquela linha:
</p>
<figure>
<svg class="diagrama" viewBox="0 0 640 150" role="img" aria-label="Grade de 5 por 8 pontos mostrando o desenho do símbolo de grau, com o valor binário de cada linha ao lado.">
<g font-family="JetBrains Mono, monospace" font-size="11" fill="#1d2b33">
<text x="250" y="26">0b01100</text><text x="250" y="42">0b10010</text><text x="250" y="58">0b10010</text><text x="250" y="74">0b01100</text>
<text x="250" y="90" fill="#9aa9b3">0</text><text x="250" y="106" fill="#9aa9b3">0</text><text x="250" y="122" fill="#9aa9b3">0</text><text x="250" y="138" fill="#9aa9b3">0</text>
</g>
<g fill="#003049">
<rect x="177" y="14" width="14" height="14"/><rect x="193" y="14" width="14" height="14"/>
<rect x="161" y="30" width="14" height="14"/><rect x="209" y="30" width="14" height="14"/>
<rect x="161" y="46" width="14" height="14"/><rect x="209" y="46" width="14" height="14"/>
<rect x="177" y="62" width="14" height="14"/><rect x="193" y="62" width="14" height="14"/>
</g>
<g fill="none" stroke="#c9d3d9" stroke-width="0.8">
<rect x="160" y="13" width="80" height="128"/>
<path d="M176 13V141M192 13V141M208 13V141M224 13V141M160 29H240M160 45H240M160 61H240M160 77H240M160 93H240M160 109H240M160 125H240"/>
</g>
<rect x="330" y="40" width="180" height="50" rx="6" fill="#7fae1a"/><text x="420" y="74" font-family="JetBrains Mono, monospace" font-size="26" fill="#1d2b0a" text-anchor="middle">26.5°C</text>
<text x="420" y="108" font-family="Inter, sans-serif" font-size="10" fill="#5b6b75" text-anchor="middle">o que aparece no display</text>
</svg>
<figcaption>
<code>lcd.createChar(0, grau)</code> guarda o desenho na posição 0 da memória do display,
e <code>lcd.write(0)</code> o imprime como qualquer outro caractere.
</figcaption>
</figure>
<h3>2. setup()</h3>
<pre class="miudo"><code><span class="c-key">void</span> <span class="c-fun">setup</span>() {
Serial.<span class="c-fun">begin</span>(<span class="c-num">115200</span>);
Wire.<span class="c-fun">begin</span>(PINO_SDA, PINO_SCL);
dht.<span class="c-fun">begin</span>();
lcd.<span class="c-fun">init</span>();
lcd.<span class="c-fun">backlight</span>();
lcd.<span class="c-fun">createChar</span>(<span class="c-num">0</span>, grau);
lcd.<span class="c-fun">setCursor</span>(<span class="c-num">0</span>, <span class="c-num">0</span>);
lcd.<span class="c-fun">print</span>(<span class="c-str">"LASER ESP32-S3"</span>);
lcd.<span class="c-fun">setCursor</span>(<span class="c-num">0</span>, <span class="c-num">1</span>);
lcd.<span class="c-fun">print</span>(<span class="c-str">"Iniciando..."</span>);
<span class="c-fun">delay</span>(<span class="c-num">2000</span>); <span class="c-com">// o DHT22 precisa de ~2 s para a primeira leitura</span>
lcd.<span class="c-fun">clear</span>();
}</code></pre>
<p>
<code>Wire.begin(8, 9)</code> inicia o I2C nos pinos da montagem. Em seguida o display é
inicializado, a luz de fundo é ligada e o caractere de grau é registrado. A mensagem
"Iniciando..." não é só enfeite: ela cobre os <strong>dois segundos</strong> que o DHT22
precisa depois de ligado para entregar a primeira medição válida.
</p>
<h3>3. loop()</h3>
<pre class="miudo"><code><span class="c-key">void</span> <span class="c-fun">loop</span>() {
<span class="c-key">float</span> umidade = dht.<span class="c-fun">readHumidity</span>();
<span class="c-key">float</span> temperatura = dht.<span class="c-fun">readTemperature</span>();
<span class="c-key">if</span> (<span class="c-fun">isnan</span>(umidade) || <span class="c-fun">isnan</span>(temperatura)) {
lcd.<span class="c-fun">setCursor</span>(<span class="c-num">0</span>, <span class="c-num">0</span>);
lcd.<span class="c-fun">print</span>(<span class="c-str">"Falha no sensor "</span>);
Serial.<span class="c-fun">println</span>(<span class="c-str">"Falha na leitura do DHT22"</span>);
} <span class="c-key">else</span> {
lcd.<span class="c-fun">setCursor</span>(<span class="c-num">0</span>, <span class="c-num">0</span>);
lcd.<span class="c-fun">print</span>(<span class="c-str">"Temp: "</span>);
lcd.<span class="c-fun">print</span>(temperatura, <span class="c-num">1</span>);
lcd.<span class="c-fun">write</span>(<span class="c-num">0</span>); <span class="c-com">// o caractere de grau criado acima</span>
lcd.<span class="c-fun">print</span>(<span class="c-str">"C "</span>);
lcd.<span class="c-fun">setCursor</span>(<span class="c-num">0</span>, <span class="c-num">1</span>);
lcd.<span class="c-fun">print</span>(<span class="c-str">"Umid: "</span>);
lcd.<span class="c-fun">print</span>(umidade, <span class="c-num">1</span>);
lcd.<span class="c-fun">print</span>(<span class="c-str">" % "</span>);
Serial.<span class="c-fun">printf</span>(<span class="c-str">"Temperatura: %.1f C | Umidade: %.1f %%\n"</span>, temperatura, umidade);
}
<span class="c-fun">delay</span>(<span class="c-num">2000</span>); <span class="c-com">// o DHT22 aceita no máximo uma leitura a cada 2 s</span>
}</code></pre>
<p>
A cada volta, o programa lê umidade e temperatura. Se qualquer uma vier como
<code>NaN</code>, a sigla de <em>not a number</em> com que a biblioteca avisa que a leitura
falhou, o display mostra "Falha no sensor". Caso contrário, os valores são escritos com
uma casa decimal. O <code>delay(2000)</code> respeita o intervalo mínimo do DHT22.
</p>
<div class="caixa erro">
<div class="caixa-titulo">Restos de texto na tela</div>
<p>
O LCD só sobrescreve as posições em que você escreve; o resto continua como estava.
Se a temperatura cair de "26.5" para "-5.2" e depois para "8.0", o texto fica mais
curto e sobram caracteres da leitura anterior. Os espaços em <code>"C "</code> e
<code>" % "</code> existem para apagar essas sobras. Chamar <code>lcd.clear()</code> a
cada leitura também resolveria, mas faz a tela piscar.
</p>
</div>
<h3>Os três testes feitos no Wokwi</h3>
<table>
<thead><tr><th style="width:36%">Situação</th><th>O que o display mostrou</th></tr></thead>
<tbody>
<tr><td class="destaque">Sala, 26,5 °C e 62 %</td><td><img src="lcd-funcionando.png" alt="LCD mostrando Temp: 26.5°C e Umid: 62.0 %" style="height:24mm"></td></tr>
<tr><td class="destaque">Frio, −5,2 °C e 88,4 %</td><td><img src="lcd-frio.png" alt="LCD mostrando Temp: -5.2°C e Umid: 88.4 %" style="height:24mm"></td></tr>
<tr><td class="destaque">Fio de dados desconectado</td><td><img src="lcd-falha.png" alt="LCD mostrando Falha no sensor" style="height:24mm"></td></tr>
</tbody>
<caption>O terceiro teste confirma que o programa detecta a falha em vez de mostrar números inventados.</caption>
</table>
</section>
<!-- ═══════════════════════════ CAPÍTULO 7 ═══════════════════════════ -->
<section class="capitulo" id="cap7">
<div class="capitulo-abertura">
<div class="capitulo-numero">7</div>
<div>
<h2>Da simulação para a bancada</h2>
<p class="subtitulo">O que o Wokwi perdoa e o hardware de verdade não.</p>
</div>
</div>
<p>
O simulador é ideal para aprender e testar a lógica, mas ele modela os componentes como
peças perfeitas. Na bancada, três detalhes fazem diferença.
</p>
<h3>1. Os pull-ups escondidos na plaquinha I2C</h3>
<p>
Quase toda plaquinha PCF8574 vendida no Brasil já traz <strong>resistores de pull-up
ligados ao VCC dela</strong>. Com o display alimentado em 5 V, esses resistores puxam SDA e
SCL para 5 V. Somar os nossos 4,7 kΩ para 3V3 não resolve: os dois resistores formam um
divisor, e a linha fica em torno de 4 V, ainda acima do que o ESP32-S3 suporta.
</p>
<p>Há duas saídas corretas:</p>
<table>
<thead><tr><th style="width:32%">Solução</th><th>Como fazer</th></tr></thead>
<tbody>
<tr>
<td class="destaque">Conversor de nível (recomendado)</td>
<td>Um módulo bidirecional com BSS138, de quatro canais. O lado <strong>LV</strong> vai no 3V3 e nos GPIO8/9; o lado <strong>HV</strong> vai no 5V e no SDA/SCL do display. O módulo já tem pull-ups dos dois lados, então os 4,7 kΩ externos saem do circuito.</td>
</tr>
<tr>
<td class="destaque">Remover os pull-ups da plaquinha</td>
<td>Dessoldar os dois resistores ligados a SDA e SCL na plaquinha e manter os 4,7 kΩ para 3V3, exatamente como no Wokwi. O PCF8574 reconhece 3,3 V como nível alto mesmo alimentado em 5 V.</td>
</tr>
</tbody>
</table>
<div class="caixa dica">
<div class="caixa-titulo">Como saber se a sua plaquinha tem pull-ups</div>
<p>
Com tudo desligado, meça com o multímetro a resistência entre o pino <strong>SDA</strong>
e o <strong>VCC</strong> da plaquinha. Algo entre 2 kΩ e 10 kΩ indica um pull-up. Se não
houver continuidade, ela não tem, e os 4,7 kΩ para 3V3 bastam.
</p>
</div>
<div class="caixa atencao">
<div class="caixa-titulo">"Mas eu liguei direto e funcionou"</div>
<p>
É comum funcionar: o resistor limita a corrente e o ESP32 tem diodos de proteção que
desviam o excesso. Funcionar não é o mesmo que estar dentro da especificação. A Espressif
não garante os pinos acima de 3,6 V, e um circuito fora da especificação pode falhar
com o tempo ou em outra placa. Em projeto que vai ficar ligado, use o conversor.
</p>
</div>
<h3>2. Descubra o endereço do display</h3>
<p>
Se o display acende mas não mostra nada mesmo com o contraste ajustado, o endereço pode
ser <code>0x3F</code> em vez de <code>0x27</code>. Este programa varre o barramento e
imprime o endereço de cada dispositivo que responder. No Wokwi ele encontra o display em
<code>0x27</code>:
</p>
<pre class="miudo"><code><span class="c-com">// Scanner I2C: lista o endereço de cada dispositivo no barramento.</span>
<span class="c-com">// Use antes do programa principal se o LCD não mostrar nada.</span>
<span class="c-pre">#include</span> <span class="c-str">&lt;Wire.h&gt;</span>
<span class="c-key">void</span> <span class="c-fun">setup</span>() {
Serial.<span class="c-fun">begin</span>(<span class="c-num">115200</span>);
Wire.<span class="c-fun">begin</span>(<span class="c-num">8</span>, <span class="c-num">9</span>); <span class="c-com">// SDA, SCL</span>
<span class="c-fun">delay</span>(<span class="c-num">500</span>);
Serial.<span class="c-fun">println</span>(<span class="c-str">"Procurando dispositivos I2C..."</span>);
<span class="c-key">int</span> encontrados = <span class="c-num">0</span>;
<span class="c-key">for</span> (<span class="c-key">byte</span> endereco = <span class="c-num">1</span>; endereco &lt; <span class="c-num">127</span>; endereco++) {
Wire.<span class="c-fun">beginTransmission</span>(endereco);
<span class="c-key">if</span> (Wire.<span class="c-fun">endTransmission</span>() == <span class="c-num">0</span>) {
Serial.<span class="c-fun">printf</span>(<span class="c-str">"Dispositivo em 0x%02X\n"</span>, endereco);
encontrados++;
}
}
<span class="c-key">if</span> (encontrados == <span class="c-num">0</span>) Serial.<span class="c-fun">println</span>(<span class="c-str">"Nenhum dispositivo. Confira SDA, SCL e alimentacao."</span>);
}
<span class="c-key">void</span> <span class="c-fun">loop</span>() {}</code></pre>
<pre><code>Procurando dispositivos I2C...
Dispositivo em 0x27</code></pre>
<h3>3. Gravando na placa</h3>
<p>
Na IDE do Arduino, instale o suporte ao ESP32 e as bibliotecas <strong>DHT sensor
library</strong> (aceitando a dependência <strong>Adafruit Unified Sensor</strong>) e
<strong>LiquidCrystal I2C</strong>, de Frank de Brabander. Selecione a placa
<strong>ESP32S3 Dev Module</strong> e lembre do detalhe do módulo 1: na porta marcada
<strong>USB</strong>, a opção <em>USB CDC On Boot</em> precisa estar em
<strong>Enabled</strong> para o monitor serial funcionar; na porta <strong>UART</strong>,
em <strong>Disabled</strong>.
</p>
</section>
<!-- ═══════════════════════════ CAPÍTULO 8 ═══════════════════════════ -->
<section class="capitulo" id="cap8">
<div class="capitulo-abertura">
<div class="capitulo-numero">8</div>
<div>
<h2>Problemas comuns e desafios</h2>
<p class="subtitulo">O que fazer quando não funciona, e para onde ir quando funcionar.</p>
</div>
</div>
<table>
<thead><tr><th style="width:34%">Sintoma</th><th>Causa provável</th></tr></thead>
<tbody>
<tr><td class="destaque">"Falha no sensor" o tempo todo</td><td>Fio de dados fora do GPIO12, falta do pull-up de 10 kΩ ou VCC e GND invertidos.</td></tr>
<tr><td class="destaque">Display aceso e sem texto</td><td>Contraste desajustado, endereço errado ou SDA e SCL trocados.</td></tr>
<tr><td class="destaque">Retângulos cheios na linha de cima</td><td>O display recebeu energia mas não foi inicializado: o I2C não está chegando.</td></tr>
<tr><td class="destaque">Display apagado</td><td>Falta o 5V no VCC do display ou o <code>lcd.backlight()</code>.</td></tr>
<tr><td class="destaque">Caracteres estranhos no fim da linha</td><td>Sobras da leitura anterior; veja a caixa "Restos de texto na tela".</td></tr>
<tr><td class="destaque">Erro de compilação com <code>DHT.h</code></td><td>Biblioteca não instalada, ou instalada sem a Adafruit Unified Sensor.</td></tr>
</tbody>
</table>
<h3>Desafios</h3>
<div class="passo">
<div class="passo-num">1</div>
<div><h3>Mínima e máxima</h3><p>Guarde a menor e a maior temperatura desde que a placa ligou e alterne a segunda linha entre a umidade e esses dois valores.</p></div>
</div>
<div class="passo">
<div class="passo-num">2</div>
<div><h3>Alerta de calor</h3><p>Ligue um LED, ou use o LED RGB que já existe no GPIO38 da placa, quando a temperatura passar de 30 °C. Faça a luz de fundo do display piscar junto.</p></div>
</div>
<div class="passo">
<div class="passo-num">3</div>
<div><h3>Sensação térmica</h3><p>A biblioteca DHT tem a função <code>computeHeatIndex()</code>. Mostre o índice de calor no lugar da umidade quando o ar estiver quente e úmido.</p></div>
</div>
<div class="passo">
<div class="passo-num">4</div>
<div><h3>Sem <code>delay()</code></h3><p>Reescreva o <code>loop()</code> usando <code>millis()</code>, para que o programa possa fazer outras coisas, como ler um botão, entre uma leitura e outra.</p></div>
</div>
<div class="caixa dica">
<div class="caixa-titulo">Próximo módulo</div>
<p>
A estação já mede e mostra. O passo natural é publicar esses dados: o ESP32-S3 tem
Wi-Fi, e com poucas linhas a mais a mesma leitura pode ir para uma página web ou para
um servidor MQTT.
</p>
</div>
<div class="caixa dica">
<div class="caixa-titulo">Dúvidas, correções e contato</div>
<p>
Correções e sugestões são bem-vindas e ajudam as próximas turmas. Fale direto com o
autor por <a href="mailto:contato@lucasrguerra.dev.br">contato@lucasrguerra.dev.br</a>
ou pelo LinkedIn, em
<a href="https://linkedin.com/in/lucasrguerra">linkedin.com/in/lucasrguerra</a>.
</p>
</div>
<p style="margin-top:12mm;padding-top:4mm;border-top:0.8pt solid var(--linha);font-size:8.5pt;color:var(--tinta-3);text-align:center">
LASER · Liga Acadêmica de Sistemas Embarcados · Centro de Informática · UFPE<br>
Formação em ESP32 · Módulo 2: Estação de Temperatura e Umidade
</p>
</section>
<script>
window.PagedConfig = {
before: () => document.querySelector(".aviso-carregando")?.remove(),
};
</script>
<script src="../assets/paged.polyfill.js"></script>
</body>
</html>