Files
ESPDocs/public/formacoes/2/slide.html
T
Lucas RayanandClaude Sonnet 5.5 f9b64b3354
Release / Plano (reaproveitar a imagem?) (push) Successful in 12s
CI / Lint e auditorias (push) Successful in 1m51s
CI / Trivy (dependências e segredos) (push) Successful in 25s
CI / Imagem Docker (linux/arm64) (push) Successful in 2m35s
Release / ci (push) Successful in 4m52s
Release / Publica a imagem (linux/arm64) (push) Successful in 54s
Release / Cria a release no Gitea (push) Skipped
Release / Deploy no Coolify (push) Successful in 22s
fix(formacao-2): resistor de 10 kΩ do slide 6 em cor única, distinta do GPIO e do VCC
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 08:45:05 -03:00

1812 lines
72 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">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LASER · Formação em ESP32 · Módulo 2 · Slides</title>
<meta name="author" content="Lucas Rayan Guerra">
<meta name="description" content="Slides da formação presencial do módulo 2 da formação em ESP32 da LASER, Liga Acadêmica de Sistemas Embarcados da UFPE.">
<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;500;600;700&display=swap" rel="stylesheet">
<link rel="icon" type="image/svg+xml" href="../../marca/espdocs-marca.svg">
<style>
/* =========================================================================
Slides da formação em ESP32 da LASER.
O palco tem 1280x720 fixos e é escalado por transform, então toda medida
aqui é em pixel de projeção: o que você vê na tela é o que sai no projetor,
em qualquer resolução.
O elemento de assinatura é o FEIXE: a linha que atravessa o rodapé de toda
página. Ela é ao mesmo tempo o traço da logo da LASER, uma trilha de placa
e a barra de progresso da formação. O trecho já percorrido recebe o
gradiente da marca; o que falta fica pontilhado.
========================================================================= */
:root {
/* Base escura, tirada do prussiano da marca em vez de um preto neutro. */
--breu: #041d2b;
--breu-2: #06283a;
--breu-3: #0a3a53;
--prussiano: #003049;
--vermelho: #D62828;
--laranja: #F77F00;
--ambar: #FCBF49;
--creme: #EAE2B7;
--gelo: #f2f8fb;
--texto: #cddde6;
--texto-2: #8fabbb;
--linha: rgba(146, 187, 207, 0.22);
--linha-forte: rgba(146, 187, 207, 0.42);
--painel: rgba(255, 255, 255, 0.045);
--painel-2: rgba(255, 255, 255, 0.075);
--feixe: linear-gradient(90deg, #1b6d92, var(--creme), var(--ambar), var(--laranja), var(--vermelho));
--display: "Outfit", system-ui, sans-serif;
--corpo: "Inter", system-ui, sans-serif;
--mono: "JetBrains Mono", ui-monospace, monospace;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
height: 100%;
background: var(--breu);
color: var(--texto);
font-family: var(--corpo);
overflow: hidden;
-webkit-font-smoothing: antialiased;
}
/* Brilho ambiente: o mesmo "feixe" da capa da apostila, aqui difuso e parado. */
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
background:
radial-gradient(60% 55% at 78% 12%, rgba(252, 191, 73, 0.10), transparent 70%),
radial-gradient(50% 50% at 12% 92%, rgba(0, 48, 73, 0.55), transparent 70%);
}
/* -------------------------------------------------------------------------
1. Palco 16:9
------------------------------------------------------------------------- */
#palco {
position: absolute;
top: 50%;
left: 50%;
width: 1280px;
height: 720px;
margin: -360px 0 0 -640px;
transform-origin: center center;
z-index: 1;
}
.slide {
position: absolute;
inset: 0;
padding: 48px 76px 70px;
display: none;
flex-direction: column;
opacity: 0;
transition: opacity 0.34s ease, transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
transform: translateY(10px);
}
.slide.ativo {
display: flex;
opacity: 1;
transform: none;
}
/* -------------------------------------------------------------------------
2. Tipografia
------------------------------------------------------------------------- */
.rotulo {
font-family: var(--mono);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ambar);
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
}
.rotulo::after {
content: "";
height: 1px;
flex: 1;
background: linear-gradient(90deg, rgba(252, 191, 73, 0.5), transparent);
}
h1 {
font-family: var(--display);
font-size: 76px;
font-weight: 300;
line-height: 1.02;
letter-spacing: -0.028em;
color: var(--gelo);
margin: 0;
}
h1 strong { font-weight: 800; display: block; }
h2 {
font-family: var(--display);
font-size: 44px;
font-weight: 700;
line-height: 1.1;
letter-spacing: -0.022em;
color: var(--gelo);
margin: 0 0 8px;
}
h3 {
font-family: var(--display);
font-size: 25px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ambar);
margin: 0 0 10px;
}
.linha-fina {
font-size: 21px;
line-height: 1.4;
color: var(--texto-2);
margin: 0 0 21px;
max-width: 900px;
font-weight: 300;
}
p { font-size: 22px; line-height: 1.5; margin: 0 0 16px; }
p:last-child { margin-bottom: 0; }
strong { color: var(--gelo); font-weight: 600; }
em { color: var(--creme); font-style: italic; }
code {
font-family: var(--mono);
font-size: 0.88em;
background: var(--painel-2);
border: 1px solid var(--linha);
border-radius: 5px;
padding: 1px 6px;
color: var(--creme);
}
a { color: var(--ambar); text-decoration: none; border-bottom: 1px solid rgba(252, 191, 73, 0.4); }
/* -------------------------------------------------------------------------
3. O feixe: rodapé, progresso e identidade
------------------------------------------------------------------------- */
.rodape {
position: absolute;
left: 76px;
right: 76px;
bottom: 30px;
height: 34px;
display: flex;
align-items: center;
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--texto-2);
pointer-events: none;
}
.rodape .trilho {
position: absolute;
left: 0;
right: 0;
top: 0;
height: 2px;
background-image: linear-gradient(90deg, var(--linha) 0 6px, transparent 6px 12px);
background-size: 12px 1px;
background-repeat: repeat-x;
background-position: 0 1px;
}
.rodape .percorrido {
position: absolute;
left: 0;
top: 0;
height: 2px;
background: var(--feixe);
background-size: 1204px 2px;
transition: width 0.34s cubic-bezier(0.22, 1, 0.36, 1);
border-radius: 2px;
}
/* A estrela da marca viaja na ponta do feixe. */
.rodape .ponta {
position: absolute;
top: 1px;
width: 11px;
height: 11px;
margin: -5px 0 0 -5.5px;
color: var(--laranja);
transition: left 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}
.rodape .ponta::before,
.rodape .ponta::after {
content: "";
position: absolute;
inset: 0;
background: currentColor;
clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%);
}
.rodape .ponta::after { filter: blur(4px); opacity: 0.85; }
.rodape-texto {
position: absolute;
top: 14px;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
align-items: baseline;
}
.rodape-num { color: var(--creme); font-weight: 600; letter-spacing: 0.08em; }
/* -------------------------------------------------------------------------
4. Capa e aberturas de capítulo
------------------------------------------------------------------------- */
.slide.capa { justify-content: center; }
.capa-marca {
display: flex;
align-items: center;
gap: 18px;
margin-bottom: 46px;
}
.capa-marca img { height: 30px; width: auto; }
.capa-marca span {
font-family: var(--mono);
font-size: 12.5px;
line-height: 1.5;
letter-spacing: 0.06em;
color: var(--texto-2);
border-left: 1px solid var(--linha-forte);
padding-left: 18px;
text-transform: uppercase;
}
.capa-selo {
font-family: var(--mono);
font-size: 13px;
font-weight: 700;
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--ambar);
margin-bottom: 22px;
}
.capa-sub {
font-size: 22px;
line-height: 1.5;
color: var(--texto-2);
max-width: 690px;
margin: 30px 0 0;
font-weight: 300;
}
.capa-autor {
position: absolute;
right: 76px;
bottom: 96px;
text-align: right;
font-family: var(--mono);
font-size: 12.5px;
line-height: 1.7;
letter-spacing: 0.06em;
color: var(--texto-2);
text-transform: uppercase;
}
.capa-autor strong { display: block; color: var(--creme); letter-spacing: 0.1em; }
/* Abertura de capítulo: o número é a superfície, não um enfeite ao lado. */
.slide.secao { justify-content: center; }
.secao-num {
position: absolute;
right: 60px;
top: 50%;
transform: translateY(-50%);
font-family: var(--display);
font-size: 340px;
font-weight: 800;
line-height: 0.8;
letter-spacing: -0.05em;
color: transparent;
-webkit-text-stroke: 2px rgba(252, 191, 73, 0.28);
pointer-events: none;
user-select: none;
}
.slide.secao h1 { font-size: 62px; font-weight: 800; max-width: 780px; letter-spacing: -0.03em; }
.slide.secao .capa-sub { max-width: 620px; }
.secao-itens {
margin-top: 34px;
display: flex;
gap: 12px;
flex-wrap: wrap;
max-width: 720px;
}
.secao-itens span {
font-family: var(--mono);
font-size: 13px;
letter-spacing: 0.06em;
color: var(--texto);
border: 1px solid var(--linha);
border-radius: 999px;
padding: 7px 15px;
background: var(--painel);
}
/* -------------------------------------------------------------------------
5. Corpo dos slides
------------------------------------------------------------------------- */
.corpo { flex: 1; min-height: 0; display: flex; flex-direction: column; }
h2 + .corpo { margin-top: 14px; }
.duas { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; flex: 1; min-height: 0; }
.duas.assimetrica { grid-template-columns: 1.15fr 1fr; }
.duas.assimetrica-inv { grid-template-columns: 1fr 1.3fr; }
.duas.terco { grid-template-columns: 1fr 2fr; }
.tres { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; flex: 1; min-height: 0; }
.quatro { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; flex: 1; min-height: 0; }
.centro { display: flex; flex-direction: column; justify-content: center; }
ul.lista { list-style: none; margin: 0; padding: 0; }
ul.lista li {
position: relative;
padding-left: 30px;
font-size: 22px;
line-height: 1.42;
margin-bottom: 17px;
}
ul.lista li::before {
content: "";
position: absolute;
left: 0;
top: 12px;
width: 14px;
height: 1.5px;
background: var(--laranja);
}
ul.lista.miuda li { font-size: 19px; margin-bottom: 12px; }
/* Cartão: unidade básica de agrupamento. */
.cartao {
background: var(--painel);
border: 1px solid var(--linha);
border-radius: 14px;
padding: 22px 24px;
position: relative;
overflow: hidden;
}
.cartao + .cartao { margin-top: 16px; }
.cartao.compacto { padding: 13px 17px; border-radius: 11px; }
.cartao.compacto + .cartao.compacto { margin-top: 11px; }
.cartao.compacto .cartao-titulo { font-size: 18px; margin-bottom: 5px; }
.cartao.compacto p, .cartao.compacto li { font-size: 16px; line-height: 1.38; }
.cartao-titulo {
font-family: var(--display);
font-size: 21px;
font-weight: 700;
color: var(--gelo);
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 10px;
}
.cartao p, .cartao li { font-size: 18.5px; line-height: 1.45; }
.cartao ul.lista li { font-size: 18.5px; margin-bottom: 9px; padding-left: 24px; }
.cartao ul.lista li::before { top: 11px; width: 11px; }
.cartao.destaque { border-color: rgba(252, 191, 73, 0.4); background: rgba(252, 191, 73, 0.07); }
.cartao.alerta { border-color: rgba(214, 40, 40, 0.5); background: rgba(214, 40, 40, 0.1); }
.cartao.alerta .cartao-titulo { color: #ffb3b3; }
.cartao.dica { border-color: rgba(32, 201, 151, 0.38); background: rgba(32, 201, 151, 0.08); }
.cartao.dica .cartao-titulo { color: #7ff0cd; }
/* Marcador numérico só onde a ordem é informação de verdade. */
.etapa { display: flex; gap: 20px; align-items: flex-start; margin-bottom: 20px; }
.etapa-num {
flex: none;
width: 42px;
height: 42px;
border-radius: 50%;
border: 1.5px solid var(--laranja);
color: var(--ambar);
font-family: var(--display);
font-size: 20px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.etapa h3 { font-size: 22px; margin-bottom: 4px; color: var(--gelo); }
.etapa p { font-size: 19px; color: var(--texto); }
/* Frase única, quando o slide é uma afirmação e não uma lista. */
.slide.impacto { justify-content: center; align-items: flex-start; }
.impacto-frase {
font-family: var(--display);
font-size: 54px;
font-weight: 300;
line-height: 1.16;
letter-spacing: -0.025em;
color: var(--gelo);
max-width: 1000px;
margin: 0;
}
.impacto-frase strong { font-weight: 800; color: var(--ambar); }
.impacto-nota {
margin-top: 30px;
font-family: var(--mono);
font-size: 15px;
letter-spacing: 0.08em;
color: var(--texto-2);
max-width: 760px;
line-height: 1.7;
text-transform: none;
}
/* -------------------------------------------------------------------------
6. Tabelas
------------------------------------------------------------------------- */
table {
width: 100%;
border-collapse: collapse;
font-size: 18px;
line-height: 1.35;
}
table.miuda { font-size: 15.5px; }
table.microscopica { font-size: 13.6px; }
th {
font-family: var(--mono);
font-size: 0.75em;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ambar);
text-align: left;
padding: 0 12px 8px;
border-bottom: 1.5px solid rgba(252, 191, 73, 0.45);
white-space: nowrap;
}
td { padding: 6px 12px; border-bottom: 1px solid var(--linha); vertical-align: top; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) td { background: rgba(255, 255, 255, 0.028); }
td.chave { color: var(--gelo); font-weight: 600; white-space: nowrap; }
td.mono, .mono { font-family: var(--mono); }
td.sim { color: #6fdcb0; font-weight: 600; }
td.nao { color: #ff8b8b; }
caption {
caption-side: bottom;
text-align: left;
padding-top: 12px;
font-size: 14px;
color: var(--texto-2);
font-style: italic;
}
/* -------------------------------------------------------------------------
7. Código
------------------------------------------------------------------------- */
pre {
margin: 0;
background: rgba(0, 14, 22, 0.62);
border: 1px solid var(--linha);
border-left: 3px solid var(--laranja);
border-radius: 12px;
padding: 20px 24px;
overflow: hidden;
font-family: var(--mono);
font-size: 17px;
line-height: 1.52;
color: #dbeaf2;
tab-size: 2;
}
pre.miuda { font-size: 15px; line-height: 1.5; }
pre code { background: none; border: none; padding: 0; font-size: inherit; color: inherit; }
.c-com { color: #7c9aab; font-style: italic; }
.c-key { color: #d9a2ff; font-weight: 600; }
.c-fun { color: #7fd0ff; font-weight: 600; }
.c-str { color: #9fe8a8; }
.c-num { color: var(--ambar); font-weight: 600; }
.c-pre { color: #ff9f6e; font-weight: 600; }
.legenda {
margin-top: 14px;
font-size: 16.5px;
line-height: 1.45;
color: var(--texto-2);
}
/* -------------------------------------------------------------------------
8. Figuras
------------------------------------------------------------------------- */
figure { margin: 0; display: flex; flex-direction: column; min-height: 0; flex: 1; }
figure img {
max-width: 100%;
min-height: 0;
flex: 1 1 auto;
object-fit: contain;
object-position: center;
margin: auto;
border-radius: 10px;
}
figure figcaption { flex: none; }
/* Diagramas de pinagem e blocos vêm em fundo claro: uma moldura clara evita
o retângulo branco solto no meio do slide escuro. */
figure.clara img {
background: #fdfdfb;
padding: 12px;
border: 1px solid var(--linha-forte);
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
}
figcaption { font-size: 15.5px; color: var(--texto-2); margin-top: 12px; line-height: 1.4; }
/* -------------------------------------------------------------------------
9. Grade das 12 séries
------------------------------------------------------------------------- */
.grade-chips { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; flex: 1; }
.chip {
background: var(--painel);
border: 1px solid var(--linha);
border-top: 2px solid var(--cor-chip, var(--laranja));
border-radius: 10px;
padding: 10px 13px 11px;
display: flex;
flex-direction: column;
}
.chip-nome {
font-family: var(--display);
font-size: 18.5px;
font-weight: 800;
color: var(--gelo);
letter-spacing: -0.015em;
line-height: 1.1;
}
.chip-papel {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--cor-chip, var(--laranja));
margin: 3px 0 7px;
filter: brightness(1.35) saturate(0.9);
}
.chip p { font-size: 12.5px; line-height: 1.32; color: var(--texto-2); margin: 0 0 8px; }
/* Ficha em uma linha só: quatro números soltos não precisam de tabela. */
.chip-ficha {
margin: auto 0 0;
font-family: var(--mono);
font-size: 10.5px;
letter-spacing: 0.02em;
color: var(--creme);
border-top: 1px solid var(--linha);
padding-top: 7px;
line-height: 1.5;
}
.chip-ficha b { color: var(--ambar); font-weight: 600; }
/* Faixa de leituras: uma frase curta por coluna, sem virar cartão. */
.leituras { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--linha); gap: 30px; }
.leituras p { font-size: 16px; line-height: 1.38; color: var(--texto-2); }
.leituras b { color: var(--ambar); font-weight: 600; }
/* Legenda das letras da nomenclatura. */
.letras { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-top: 8px; }
.letra { border: 1px solid var(--linha); border-radius: 12px; padding: 18px 20px; background: var(--painel); }
.letra b {
font-family: var(--display);
font-size: 46px;
font-weight: 800;
color: var(--ambar);
line-height: 1;
display: block;
margin-bottom: 8px;
}
.letra span { font-size: 17px; line-height: 1.4; color: var(--texto); display: block; }
/* Números grandes usados como evidência, não como enfeite. */
.metrica { display: flex; flex-direction: column; }
.metrica b {
font-family: var(--display);
font-size: 52px;
font-weight: 800;
color: var(--ambar);
line-height: 1;
letter-spacing: -0.03em;
}
.metrica span { font-family: var(--mono); font-size: 12.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--texto-2); margin-top: 8px; }
/* -------------------------------------------------------------------------
10. Navegação, panorama e ajuda
------------------------------------------------------------------------- */
#panorama {
position: fixed;
inset: 0;
z-index: 20;
background: rgba(4, 29, 43, 0.97);
backdrop-filter: blur(10px);
padding: 40px 48px;
overflow-y: auto;
display: none;
grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
gap: 18px;
align-content: start;
}
#panorama.aberto { display: grid; }
.miniatura {
background: var(--painel);
border: 1px solid var(--linha);
border-radius: 10px;
padding: 14px 15px;
cursor: pointer;
text-align: left;
font-family: var(--corpo);
color: var(--texto-2);
transition: border-color 0.15s, background 0.15s, transform 0.15s;
display: flex;
flex-direction: column;
gap: 7px;
}
.miniatura:hover, .miniatura:focus-visible {
border-color: var(--ambar);
background: rgba(252, 191, 73, 0.09);
transform: translateY(-2px);
outline: none;
}
.miniatura.atual { border-color: var(--laranja); background: rgba(247, 127, 0, 0.13); }
.miniatura b { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; color: var(--ambar); }
.miniatura span { font-family: var(--display); font-size: 15.5px; font-weight: 600; color: var(--gelo); line-height: 1.25; }
.miniatura i { font-style: normal; font-size: 12.5px; color: var(--texto-2); line-height: 1.3; }
#ajuda {
position: fixed;
left: 50%;
bottom: 26px;
transform: translateX(-50%);
z-index: 25;
display: flex;
gap: 8px;
background: rgba(4, 29, 43, 0.9);
border: 1px solid var(--linha-forte);
border-radius: 999px;
padding: 9px 16px;
font-family: var(--mono);
font-size: 11.5px;
letter-spacing: 0.08em;
color: var(--texto-2);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
white-space: nowrap;
}
#ajuda.visivel { opacity: 1; }
#panorama.aberto ~ #ajuda { display: none; }
#ajuda kbd {
background: var(--painel-2);
border: 1px solid var(--linha);
border-radius: 4px;
padding: 1px 6px;
color: var(--creme);
font-family: inherit;
}
#ajuda span + span { margin-left: 6px; }
/* Tela preta para tirar o projetor da frente sem perder o lugar. */
#cortina {
position: fixed;
inset: 0;
background: #000;
z-index: 30;
display: none;
}
#cortina.aberta { display: block; }
/* -------------------------------------------------------------------------
11. Acessibilidade e impressão
------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
.slide, .rodape .percorrido, .rodape .ponta, .miniatura { transition: none !important; }
}
@media print {
/* 1280 x 720 px a 96 dpi = 338,667 mm x 190,5 mm. Assim a página do PDF
tem exatamente a mesma caixa do palco e nada precisa ser reescalado. */
@page { size: 338.667mm 190.5mm; margin: 0; }
html, body { overflow: visible; height: auto; background: var(--breu); }
body::before { display: none; }
#palco {
position: static;
width: auto;
height: auto;
margin: 0;
transform: none !important;
}
.slide {
position: relative;
display: flex !important;
opacity: 1 !important;
transform: none !important;
width: 1280px;
height: 720px;
overflow: hidden;
page-break-after: always;
break-after: page;
/* O brilho ambiente é fixo na tela; no papel cada página precisa do seu. */
background:
radial-gradient(60% 55% at 78% 12%, rgba(252, 191, 73, 0.10), transparent 70%),
radial-gradient(50% 50% at 12% 92%, rgba(0, 48, 73, 0.55), transparent 70%),
var(--breu);
}
/* Sem isto o Chrome fecha o PDF com uma página em branco. */
.slide:last-child { page-break-after: auto; break-after: auto; }
#panorama, #ajuda, #cortina { display: none !important; }
* { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
}
/* -------------------------------------------------------------------------
Módulo 2: peças visuais. Aqui o slide mostra, não explica: o LCD é
desenhado em CSS, os números ganham tamanho de cartaz e o código só
aparece em trechos curtos.
------------------------------------------------------------------------- */
.centralizada { text-align: center; }
.centro-v { align-items: center; }
.corpo .centro-v, .corpo.centro-v { align-content: center; }
/* LCD 16x2 desenhado: fundo verde-limão, caracteres em matriz monoespaçada. */
.lcd {
display: inline-flex;
flex-direction: column;
gap: 4px;
padding: 18px 26px;
background: #0b6b3a;
border-radius: 10px;
box-shadow: inset 0 0 0 10px #0a0a0a, 0 18px 48px rgba(0, 0, 0, 0.45);
}
.lcd > div {
font-family: var(--mono);
font-weight: 500;
font-size: 34px;
line-height: 1.15;
letter-spacing: 0.08em;
white-space: pre;
color: #102a06;
background: #7fbf1f;
padding: 2px 12px;
min-width: 16.4ch;
}
.lcd > div:first-child { border-radius: 6px 6px 0 0; padding-top: 10px; }
.lcd > div:last-child { border-radius: 0 0 6px 6px; padding-bottom: 10px; }
.lcd-grande > div { font-size: 46px; }
.lcd-medio > div { font-size: 26px; }
.lcd-mini { padding: 10px 14px; box-shadow: inset 0 0 0 6px #0a0a0a; }
.lcd-mini > div { font-size: 17px; }
.lcd.blocos > div:first-child { color: #2a4a0e; letter-spacing: 0.02em; }
.lcd.apagado > div { background: #2b3a18; }
/* Fluxo da abertura: peça → seta → peça. */
.fluxo { flex-direction: row; display: flex; align-items: center; justify-content: center; gap: 18px; }
.fluxo-peca { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.fluxo-peca svg { width: 150px; height: 212px; }
.fluxo-peca .lcd { margin: 48px 0 40px; }
.fluxo-peca .lcd > div { font-size: 26px; }
.fluxo-peca b { font-family: var(--display); font-size: 30px; color: var(--gelo); margin-top: 8px; }
.fluxo-peca span { font-family: var(--mono); font-size: 16px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ambar); }
.fluxo-seta { position: relative; width: 120px; height: 4px; background: var(--feixe); border-radius: 2px; margin-bottom: 70px; }
.fluxo-seta::after { content: ""; position: absolute; right: -4px; top: -8px; border: 10px solid transparent; border-left: 14px solid var(--vermelho); border-right: 0; }
.fluxo-seta span { position: absolute; left: 0; right: 0; top: -34px; text-align: center; font-family: var(--mono); font-size: 16px; color: var(--texto-2); }
/* Placar DHT11 x DHT22. */
.placar { display: flex; flex-direction: column; justify-content: center; gap: 14px; }
.placar-linha { display: grid; grid-template-columns: 220px 1fr 1fr; align-items: baseline; padding: 14px 0; border-bottom: 1px solid var(--linha); }
.placar-rotulo { font-family: var(--mono); font-size: 15px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--texto-2); }
.placar .antes { font-family: var(--display); font-size: 40px; color: var(--texto-2); }
.placar .depois { font-family: var(--display); font-size: 52px; font-weight: 700; color: var(--ambar); }
.placar .ganha { color: #6fdcb0; }
.placar .perde { color: var(--texto); font-weight: 400; font-size: 40px; }
.placar-legenda { display: grid; grid-template-columns: 220px 1fr 1fr; font-family: var(--mono); font-size: 15px; letter-spacing: 0.2em; color: var(--texto-2); }
.placar-legenda span:first-child { grid-column: 2; }
/* Desenhos grandes em SVG. */
svg.desenho { width: 100%; height: auto; max-height: 470px; margin: auto; display: block; }
/* Matriz de pontos de um caractere. */
.matriz { display: grid; grid-template-columns: repeat(5, 46px); gap: 6px; justify-content: center; }
.matriz i { display: block; width: 46px; height: 46px; border-radius: 6px; background: rgba(127, 191, 31, 0.14); }
.matriz i.l { background: #7fbf1f; box-shadow: 0 0 18px rgba(127, 191, 31, 0.45); }
.matriz.pequena { grid-template-columns: repeat(5, 40px); gap: 5px; }
.matriz.pequena i { width: 40px; height: 40px; }
.grau { display: flex; gap: 28px; align-items: center; justify-content: center; }
pre.bits { margin: 0; padding: 0; background: none; border: 0; font-size: 22px; line-height: 45px; }
/* Número de cartaz. */
.numero-grande { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 34px 20px; border: 1px solid var(--linha); border-radius: 16px; background: var(--painel); }
.numero-grande b { font-family: var(--display); font-size: 92px; line-height: 1; color: var(--gelo); }
.numero-grande span { margin-top: 14px; font-size: 22px; color: var(--texto); }
.numero-grande small { font-family: var(--mono); font-size: 16px; color: var(--texto-2); letter-spacing: 0.1em; }
.numero-grande.destaque { border-color: var(--ambar); background: rgba(252, 191, 73, 0.08); }
.numero-grande.destaque b { color: var(--ambar); font-size: 140px; }
/* Arquivos do Wokwi. */
.arquivo { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 40px 18px; border-radius: 16px; border: 1px solid var(--linha); background: var(--painel); }
.arquivo-icone { font-family: var(--mono); font-size: 64px; line-height: 1; color: var(--ambar); }
.arquivo b { font-family: var(--mono); font-size: 26px; color: var(--gelo); }
.arquivo span { font-size: 20px; color: var(--texto-2); }
/* Trilha de passos. */
.trilha { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; align-content: center; }
.trilha-passo { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 12px; }
.trilha-passo b { display: grid; place-items: center; width: 96px; height: 96px; border-radius: 50%; border: 2px solid var(--ambar); font-family: var(--display); font-size: 44px; color: var(--ambar); }
.trilha-passo.destaque b { background: #2e9e4f; border-color: #2e9e4f; color: #fff; }
.trilha-passo span { font-family: var(--display); font-size: 30px; font-weight: 700; color: var(--gelo); }
.trilha-passo code { font-size: 15px; line-height: 1.5; }
/* Legenda de fios. */
ul.fios { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
ul.fios li { display: flex; align-items: center; gap: 14px; font-size: 19px; color: var(--texto); }
ul.fios i { flex: none; width: 34px; height: 8px; border-radius: 4px; }
ul.fios b { font-family: var(--mono); color: var(--gelo); min-width: 82px; }
/* Código em slide: só trechos curtos, com respiro. */
pre.codigo { align-self: center; }
/* Testes: três telas lado a lado. */
.testes figure img { border-radius: 10px; box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45); }
.testes figcaption { text-align: center; font-size: 19px; }
.testes figcaption b { display: block; font-family: var(--display); font-size: 26px; color: var(--gelo); }
/* Veredito 5 V x 3,3 V. */
.veredito { text-align: center; padding: 34px 24px; border-radius: 16px; border: 2px solid; }
.veredito.ruim { border-color: rgba(214, 40, 40, 0.7); background: rgba(214, 40, 40, 0.08); }
.veredito.bom { border-color: rgba(111, 220, 176, 0.7); background: rgba(111, 220, 176, 0.07); }
.veredito .tensao { font-family: var(--display); font-size: 96px; font-weight: 800; line-height: 1; }
.veredito.ruim .tensao, .veredito.ruim span { color: #ff8b8b; }
.veredito.bom .tensao, .veredito.bom span { color: #6fdcb0; }
.veredito p { font-size: 20px; margin: 16px 0; }
.veredito span { font-family: var(--mono); font-size: 18px; letter-spacing: 0.08em; }
pre.terminal { background: #02121b; font-size: 22px; }
pre.terminal b { color: var(--ambar); }
/* Sintomas. */
.duas .sintoma { display: flex; align-items: center; gap: 20px; }
.sintoma p { font-size: 20px; margin: 0; }
.corpo.duas:has(.sintoma) { grid-template-rows: 1fr 1fr; align-items: center; row-gap: 26px; }
/* Ajustes de leitura a distância. */
.corpo.codigo-lado { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: 28px; align-items: center; }
.corpo.codigo-lado.largo { grid-template-columns: minmax(0, 1fr) 280px; }
pre.codigo { font-size: 18px; line-height: 1.5; margin: 0; overflow: hidden; }
pre.codigo.longo { font-size: 14.6px; line-height: 1.4; padding: 16px 20px; }
.tres > .arquivo, .quatro.desafios > .cartao { align-self: center; }
.desafios p { font-size: 18px; }
.trilha { flex: 1; }
.trilha-passo code { font-size: 17px; }
.testes { align-items: center; }
.testes figure { flex: none; }
.testes figure img { flex: none; width: 100%; height: auto; }
ul.fios li { font-size: 22px; }
/* Lateral dos slides de código: o LCD e o terminal precisam caber inteiros. */
.codigo-lado .lcd-medio > div { font-size: 21px; }
.codigo-lado pre.terminal { font-size: 18px; line-height: 1.5; }
/* Quando não funciona: quatro cartões do mesmo tamanho, LCD em cima. */
.corpo.sintomas { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 20px; }
.sintomas .sintoma { display: flex; align-items: center; gap: 26px; padding: 22px 26px; border: 1px solid var(--linha); border-radius: 14px; background: var(--painel); }
.sintomas .lcd-mini { flex: none; }
.sintomas .lcd-mini > div { font-size: 15px; min-width: 16ch; }
.sintomas b { display: block; font-family: var(--display); font-size: 23px; color: var(--gelo); margin-bottom: 4px; }
.sintomas p { font-size: 18px; margin: 0; color: var(--texto); }
.corpo.sintomas { grid-template-rows: auto auto; align-content: center; flex: none; margin: auto 0; }
.sintomas .sintoma { min-height: 150px; }
/* Scanner: código em cima, saída embaixo, os dois na largura toda. */
.corpo.empilhado { display: flex; flex-direction: column; justify-content: center; gap: 22px; }
.empilhado pre.codigo { font-size: 19px; align-self: stretch; }
.empilhado .saida { display: flex; align-items: center; gap: 28px; }
.empilhado .saida pre.terminal { margin: 0; flex: none; font-size: 20px; }
.empilhado .saida .legenda { margin: 0; }
/* Desafios: quatro cartões de altura igual, conteúdo no mesmo eixo. */
.corpo.desafios { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; align-content: center; flex: 1; }
.desafio { display: flex; flex-direction: column; align-items: center; text-align: center; height: 300px; padding: 34px 22px 26px; border: 1px solid var(--linha); border-radius: 16px; background: var(--painel); }
.desafio.destaque { border-color: var(--ambar); background: rgba(252, 191, 73, 0.07); }
.desafio-icone { display: grid; place-items: center; width: 76px; height: 76px; border-radius: 50%; border: 2px solid var(--ambar); font-family: var(--display); font-size: 36px; line-height: 1; color: var(--ambar); margin-bottom: 22px; }
.desafio b { font-family: var(--display); font-size: 26px; color: var(--gelo); margin-bottom: 10px; }
.desafio p { font-size: 18px; line-height: 1.45; margin: 0; color: var(--texto); }
.desafio code { font-size: 15px; }
</style>
</head>
<body>
<div id="palco">
<!-- ══════════════════════════════ ABERTURA ══════════════════════════════ -->
<section class="slide capa" data-secao="Abertura" data-titulo="Capa">
<div class="capa-marca">
<img src="../assets/laser.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>Estação de<strong>temperatura e umidade</strong></h1>
<p class="capa-sub">DHT22 + LCD 16x2 + ESP32-S3, simulados no Wokwi.</p>
<div class="capa-autor">
<strong>Lucas Rayan Guerra</strong>
Ciência Embarcada · ESPDocs<br>
linkedin.com/in/lucasrguerra
</div>
</section>
<section class="slide" data-secao="Abertura" data-titulo="Medir, processar, mostrar">
<div class="rotulo">Hoje</div>
<h2>Medir, processar, mostrar</h2>
<div class="corpo fluxo">
<div class="fluxo-peca">
<svg viewBox="0 0 120 170" aria-hidden="true">
<rect x="18" y="10" width="84" height="112" rx="12" fill="#f2f8fb"/>
<circle cx="60" cy="30" r="10" fill="#041d2b"/>
<g fill="#041d2b"><rect x="30" y="50" width="60" height="6" rx="3"/><rect x="30" y="64" width="60" height="6" rx="3"/><rect x="30" y="78" width="60" height="6" rx="3"/><rect x="30" y="92" width="60" height="6" rx="3"/></g>
<g stroke="#cddde6" stroke-width="5"><line x1="38" y1="122" x2="38" y2="165"/><line x1="53" y1="122" x2="53" y2="165"/><line x1="68" y1="122" x2="68" y2="165"/><line x1="83" y1="122" x2="83" y2="165"/></g>
</svg>
<b>DHT22</b><span>mede</span>
</div>
<div class="fluxo-seta"><span>1 fio</span></div>
<div class="fluxo-peca">
<svg viewBox="0 0 120 170" aria-hidden="true">
<rect x="25" y="8" width="70" height="154" rx="6" fill="#0a3a53" stroke="#8fabbb" stroke-width="2"/>
<rect x="35" y="18" width="50" height="44" rx="3" fill="#cddde6"/>
<g fill="#FCBF49"><circle cx="31" cy="75" r="3"/><circle cx="31" cy="90" r="3"/><circle cx="31" cy="105" r="3"/><circle cx="31" cy="120" r="3"/><circle cx="31" cy="135" r="3"/><circle cx="89" cy="75" r="3"/><circle cx="89" cy="90" r="3"/><circle cx="89" cy="105" r="3"/><circle cx="89" cy="120" r="3"/><circle cx="89" cy="135" r="3"/></g>
</svg>
<b>ESP32-S3</b><span>processa</span>
</div>
<div class="fluxo-seta"><span>I2C</span></div>
<div class="fluxo-peca larga">
<div class="lcd"><div>Temp: 26.5°C</div><div>Umid: 62.0 %</div></div>
<b>LCD 16x2</b><span>mostra</span>
</div>
</div>
</section>
<section class="slide" data-secao="Abertura" data-titulo="O resultado">
<div class="rotulo">Ao fim das duas horas</div>
<h2>Isto, rodando no seu navegador</h2>
<div class="corpo">
<figure><img src="circuito-wokwi.png" alt="O circuito completo rodando no Wokwi, com o LCD mostrando Temp: 26.5°C e Umid: 62.0 %."></figure>
</div>
</section>
<!-- ══════════════════════════════ 1 · SENSOR ══════════════════════════════ -->
<section class="slide secao" data-secao="1 · Sensor" data-titulo="§1 · O DHT22">
<div class="secao-num">1</div>
<div class="rotulo">Parte um</div>
<h1>O sensor DHT22</h1>
<p class="capa-sub">Dois sensores numa caixinha, um fio de dados.</p>
</section>
<section class="slide" data-secao="1 · Sensor" data-titulo="DHT22 contra DHT11">
<div class="rotulo">1 · Sensor</div>
<h2>Por que o DHT22, e não o DHT11 do módulo 1</h2>
<div class="corpo placar">
<div class="placar-linha"><span class="placar-rotulo">Precisão</span><span class="antes">±2 °C</span><span class="depois">±0,5 °C</span></div>
<div class="placar-linha"><span class="placar-rotulo">Faixa</span><span class="antes">0 a 50 °C</span><span class="depois">−40 a 80 °C</span></div>
<div class="placar-linha"><span class="placar-rotulo">Resolução</span><span class="antes">1 °C</span><span class="depois">0,1 °C</span></div>
<div class="placar-linha"><span class="placar-rotulo">Leituras</span><span class="antes ganha">1 por segundo</span><span class="depois perde">1 a cada 2 s</span></div>
<div class="placar-legenda"><span>DHT11</span><span>DHT22</span></div>
</div>
</section>
<section class="slide" data-secao="1 · Sensor" data-titulo="Os quatro pinos">
<div class="rotulo">1 · Sensor</div>
<h2>Quatro pinos, um sem uso</h2>
<div class="corpo">
<svg class="desenho" viewBox="0 0 900 440" role="img" aria-label="Esquema do DHT22: o pino GND vai ao terra, o DATA vai ao GPIO12, o NC fica sem ligação e o VCC vai ao 3V3; um resistor de 10 kΩ liga a linha de DATA à linha de 3V3.">
<rect x="110" y="10" width="210" height="170" rx="20" fill="#f2f8fb"/>
<circle cx="215" cy="46" r="18" fill="#041d2b"/>
<g fill="#041d2b"><rect x="145" y="82" width="140" height="11" rx="5.5"/><rect x="145" y="104" width="140" height="11" rx="5.5"/><rect x="145" y="126" width="140" height="11" rx="5.5"/><rect x="145" y="148" width="140" height="11" rx="5.5"/></g>
<text x="215" y="174" font-family="Outfit, sans-serif" font-size="14" font-weight="700" fill="#041d2b" text-anchor="middle">DHT22</text>
<g fill="none" stroke-width="6" stroke-linejoin="round" stroke-linecap="round">
<polyline points="285,180 285,236 650,236" stroke="#cddde6"/>
<polyline points="195,180 195,306 650,306" stroke="#FCBF49"/>
<polyline points="150,180 150,416 650,416" stroke="#ff5a5a"/>
<line x1="240" y1="180" x2="240" y2="206" stroke="#4b6474"/>
</g>
<!-- Resistor: terminais curtos e o corpo em zigue-zague no meio do vão de 110 px. -->
<g fill="none" stroke="#6fdcb0" stroke-width="4.5" stroke-linejoin="round" stroke-linecap="round">
<line x1="480" y1="306" x2="480" y2="332"/>
<polyline points="480,332 464,339 496,350 464,361 496,372 464,383 480,390"/>
<line x1="480" y1="390" x2="480" y2="416"/>
</g>
<circle cx="480" cy="306" r="8" fill="#6fdcb0"/>
<circle cx="480" cy="416" r="8" fill="#6fdcb0"/>
<path d="M232 212 l16 16 M248 212 l-16 16" stroke="#8fabbb" stroke-width="3"/>
<g font-family="JetBrains Mono, monospace" font-size="17" font-weight="600">
<text x="160" y="204" fill="#ff8b8b">VCC</text>
<text x="205" y="292" fill="#FCBF49">DATA</text>
<text x="254" y="226" fill="#8fabbb">NC</text>
<text x="295" y="222" fill="#f2f8fb">GND</text>
</g>
<text x="516" y="358" font-family="Outfit, sans-serif" font-size="28" font-weight="700" fill="#6fdcb0">10 kΩ</text>
<text x="516" y="382" font-family="Inter, sans-serif" font-size="16" fill="#8fabbb">pull-up para o 3V3</text>
<g font-family="Outfit, sans-serif" font-size="24" font-weight="700" text-anchor="middle">
<rect x="650" y="213" width="210" height="46" rx="10" fill="#0a3a53" stroke="#cddde6"/><text x="755" y="244" fill="#f2f8fb">GND</text>
<rect x="650" y="283" width="210" height="46" rx="10" fill="rgba(252,191,73,0.14)" stroke="#FCBF49"/><text x="755" y="314" fill="#FCBF49">GPIO12</text>
<rect x="650" y="393" width="210" height="46" rx="10" fill="rgba(214,40,40,0.16)" stroke="#ff5a5a"/><text x="755" y="424" fill="#ff8b8b">3V3</text>
</g>
<text x="755" y="198" font-family="Inter, sans-serif" font-size="15" fill="#8fabbb" text-anchor="middle">no ESP32-S3</text>
</svg>
</div>
</section>
<section class="slide" data-secao="1 · Sensor" data-titulo="Um fio, 40 bits">
<div class="rotulo">1 · Sensor</div>
<h2>Uma medição = 40 bits num fio só</h2>
<div class="corpo">
<svg class="desenho" viewBox="0 0 900 330" role="img" aria-label="Diagrama de tempo: o ESP32 pede, o sensor confirma, e cada bit é um pulso alto curto para 0 ou longo para 1.">
<g stroke="rgba(146,187,207,0.3)" stroke-dasharray="3 6"><line x1="70" y1="100" x2="880" y2="100"/><line x1="70" y1="220" x2="880" y2="220"/></g>
<polyline fill="none" stroke="#FCBF49" stroke-width="5" stroke-linejoin="round"
points="70,100 110,100 110,220 250,220 250,100 300,100 300,220 380,220 380,100 450,100 450,220 510,220 510,100 555,100 555,220 615,220 615,100 745,100 745,220 805,220 805,100 880,100"/>
<g font-family="Outfit, sans-serif" font-size="28" font-weight="700" text-anchor="middle">
<text x="180" y="280" fill="#cddde6">pede</text>
<text x="340" y="280" fill="#cddde6">confirma</text>
<text x="533" y="70" fill="#6fdcb0">0</text>
<text x="680" y="70" fill="#F77F00">1</text>
</g>
<g font-family="JetBrains Mono, monospace" font-size="20" text-anchor="middle" fill="#8fabbb">
<text x="533" y="280">26 µs</text>
<text x="680" y="280">70 µs</text>
</g>
<rect x="510" y="85" width="45" height="30" fill="none" stroke="#6fdcb0" stroke-width="2" rx="4"/>
<rect x="615" y="85" width="130" height="30" fill="none" stroke="#F77F00" stroke-width="2" rx="4"/>
</svg>
</div>
<p class="legenda centralizada">16 bits de umidade · 16 de temperatura · 8 de verificação</p>
</section>
<section class="slide impacto" data-secao="1 · Sensor" data-titulo="O pull-up">
<div class="rotulo">1 · Sensor</div>
<p class="impacto-frase">
Ninguém empurra a linha para cima.<br>
<strong>Quem puxa é o resistor.</strong>
</p>
<p class="impacto-nota">Sem o pull-up de 10 kΩ, a linha sobe devagar demais, os tempos saem errados e a leitura vira "Falha no sensor".</p>
</section>
<!-- ═══════════════════════════ 2 · LCD E I2C ═══════════════════════════ -->
<section class="slide secao" data-secao="2 · LCD e I2C" data-titulo="§2 · LCD e I2C">
<div class="secao-num">2</div>
<div class="rotulo">Parte dois</div>
<h1>O display e o I2C</h1>
<p class="capa-sub">32 caracteres, 4 fios, 1 endereço.</p>
</section>
<section class="slide" data-secao="2 · LCD e I2C" data-titulo="16 por 2">
<div class="rotulo">2 · LCD</div>
<h2>16 colunas × 2 linhas × (5 × 8 pontos)</h2>
<div class="corpo duas assimetrica centro-v">
<div class="lcd lcd-grande" aria-label="LCD mostrando Temp: 26.5°C e Umid: 62.0 %">
<div>Temp: 26.5°C</div><div>Umid: 62.0 %</div>
</div>
<div class="matriz" aria-label="Um caractere ampliado: a letra T numa grade de 5 por 8 pontos">
<i class="l"></i><i class="l"></i><i class="l"></i><i class="l"></i><i class="l"></i>
<i></i><i></i><i class="l"></i><i></i><i></i>
<i></i><i></i><i class="l"></i><i></i><i></i>
<i></i><i></i><i class="l"></i><i></i><i></i>
<i></i><i></i><i class="l"></i><i></i><i></i>
<i></i><i></i><i class="l"></i><i></i><i></i>
<i></i><i></i><i class="l"></i><i></i><i></i>
<i></i><i></i><i></i><i></i><i></i>
</div>
</div>
<p class="legenda centralizada">Cada caractere é uma grade de 5 × 8. E você pode desenhar até 8 caracteres seus.</p>
</section>
<section class="slide" data-secao="2 · LCD e I2C" data-titulo="A plaquinha I2C">
<div class="rotulo">2 · LCD</div>
<h2>A plaquinha atrás do display</h2>
<div class="corpo duas centro-v">
<div class="numero-grande"><b>16</b><span>fios sem a plaquinha</span></div>
<div class="numero-grande destaque"><b>4</b><span>fios com o PCF8574<br><small>VCC · GND · SDA · SCL</small></span></div>
</div>
<p class="legenda centralizada">O trimpot azul da plaquinha ajusta o contraste. Tela vazia? Gire ele primeiro.</p>
</section>
<section class="slide" data-secao="2 · LCD e I2C" data-titulo="Como o I2C funciona">
<div class="rotulo">2 · I2C</div>
<h2>Dois fios, vários dispositivos</h2>
<div class="corpo">
<svg class="desenho" viewBox="0 0 900 340" role="img" aria-label="Barramento I2C: o ESP32 à esquerda liga as linhas SDA e SCL a três dispositivos, cada um com seu endereço: LCD em 0x27, sensor de pressão em 0x76 e memória em 0x50.">
<rect x="20" y="95" width="160" height="150" rx="14" fill="#0a3a53" stroke="#FCBF49" stroke-width="2"/>
<text x="100" y="165" font-family="Outfit, sans-serif" font-size="28" font-weight="700" fill="#f2f8fb" text-anchor="middle">ESP32</text>
<text x="100" y="195" font-family="Inter, sans-serif" font-size="17" fill="#8fabbb" text-anchor="middle">controlador</text>
<line x1="180" y1="140" x2="880" y2="140" stroke="#4ea8ff" stroke-width="5"/>
<line x1="180" y1="200" x2="880" y2="200" stroke="#c77dff" stroke-width="5"/>
<text x="200" y="128" font-family="JetBrains Mono, monospace" font-size="18" fill="#4ea8ff">SDA · dados</text>
<text x="200" y="228" font-family="JetBrains Mono, monospace" font-size="18" fill="#c77dff">SCL · relógio</text>
<g font-family="Inter, sans-serif" text-anchor="middle">
<g transform="translate(420,0)">
<line x1="0" y1="140" x2="0" y2="270" stroke="#4ea8ff" stroke-width="3"/><line x1="30" y1="200" x2="30" y2="270" stroke="#c77dff" stroke-width="3"/>
<rect x="-45" y="270" width="120" height="60" rx="10" fill="#FCBF49"/>
<text x="15" y="297" font-size="18" font-weight="700" fill="#041d2b">LCD</text>
<text x="15" y="320" font-family="JetBrains Mono, monospace" font-size="17" fill="#041d2b">0x27</text>
</g>
<g transform="translate(600,0)">
<line x1="0" y1="140" x2="0" y2="270" stroke="#4ea8ff" stroke-width="3"/><line x1="30" y1="200" x2="30" y2="270" stroke="#c77dff" stroke-width="3"/>
<rect x="-45" y="270" width="120" height="60" rx="10" fill="#0a3a53" stroke="#8fabbb"/>
<text x="15" y="297" font-size="18" font-weight="700" fill="#cddde6">pressão</text>
<text x="15" y="320" font-family="JetBrains Mono, monospace" font-size="17" fill="#8fabbb">0x76</text>
</g>
<g transform="translate(780,0)">
<line x1="0" y1="140" x2="0" y2="270" stroke="#4ea8ff" stroke-width="3"/><line x1="30" y1="200" x2="30" y2="270" stroke="#c77dff" stroke-width="3"/>
<rect x="-45" y="270" width="120" height="60" rx="10" fill="#0a3a53" stroke="#8fabbb"/>
<text x="15" y="297" font-size="18" font-weight="700" fill="#cddde6">memória</text>
<text x="15" y="320" font-family="JetBrains Mono, monospace" font-size="17" fill="#8fabbb">0x50</text>
</g>
</g>
<text x="640" y="60" font-family="Outfit, sans-serif" font-size="26" font-weight="600" fill="#f2f8fb" text-anchor="middle">"0x27, é com você."</text>
<text x="640" y="90" font-family="Inter, sans-serif" font-size="17" fill="#8fabbb" text-anchor="middle">só responde quem tem o endereço chamado</text>
</svg>
</div>
</section>
<section class="slide" data-secao="2 · LCD e I2C" data-titulo="Pull-up no I2C">
<div class="rotulo">2 · I2C</div>
<h2>O I2C também precisa de pull-up</h2>
<div class="corpo tres centro-v">
<div class="numero-grande"><b>DHT22</b><span>10 kΩ</span></div>
<div class="numero-grande"><b>SDA</b><span>4,7 kΩ</span></div>
<div class="numero-grande"><b>SCL</b><span>4,7 kΩ</span></div>
</div>
<p class="legenda centralizada">Os três resistores vão para o <strong>3V3</strong>. Nunca para o 5V: os pinos do ESP32-S3 não toleram 5 V.</p>
</section>
<!-- ══════════════════════════════ 3 · WOKWI ══════════════════════════════ -->
<section class="slide secao" data-secao="3 · Wokwi" data-titulo="§3 · Wokwi">
<div class="secao-num">3</div>
<div class="rotulo">Parte três</div>
<h1>O simulador Wokwi</h1>
<p class="capa-sub">Um laboratório de eletrônica no navegador. Sem instalar nada, sem conta.</p>
</section>
<section class="slide" data-secao="3 · Wokwi" data-titulo="Três arquivos">
<div class="rotulo">3 · Wokwi</div>
<h2>Um projeto = três arquivos</h2>
<div class="corpo tres">
<div class="arquivo"><div class="arquivo-icone">{ }</div><b>sketch.ino</b><span>o programa</span></div>
<div class="arquivo"><div class="arquivo-icone">◫</div><b>diagram.json</b><span>peças e fios</span></div>
<div class="arquivo"><div class="arquivo-icone">≡</div><b>libraries.txt</b><span>bibliotecas</span></div>
</div>
<p class="legenda centralizada">Os três estão em <span class="mono">formacoes/2/wokwi/</span>, prontos para colar.</p>
</section>
<section class="slide" data-secao="3 · Wokwi" data-titulo="Mão na massa">
<div class="rotulo">3 · Wokwi</div>
<h2>Quatro passos</h2>
<div class="corpo trilha">
<div class="trilha-passo"><b>1</b><span>Abra</span><code>wokwi.com/projects/new/esp32-s3</code></div>
<div class="trilha-passo"><b>2</b><span>Cole</span><code>sketch.ino + diagram.json</code></div>
<div class="trilha-passo"><b>3</b><span>Adicione</span><code>DHT sensor library<br>LiquidCrystal I2C</code></div>
<div class="trilha-passo destaque"><b>▶</b><span>Simule</span><code>botão verde</code></div>
</div>
</section>
<!-- ═════════════════════════════ 4 · MONTAGEM ═════════════════════════════ -->
<section class="slide secao" data-secao="4 · Montagem" data-titulo="§4 · Montagem">
<div class="secao-num">4</div>
<div class="rotulo">Parte quatro</div>
<h1>Montagem</h1>
<p class="capa-sub">Tudo na coluna esquerda da placa.</p>
</section>
<section class="slide" data-secao="4 · Montagem" data-titulo="O circuito">
<div class="rotulo">4 · Montagem</div>
<div class="corpo duas assimetrica-inv centro-v" style="margin-top:0">
<div>
<h2>O circuito</h2>
<ul class="fios">
<li><i style="background:#ff3b3b"></i><b>3V3</b> DHT22 e os 3 resistores</li>
<li><i style="background:#ffd400"></i><b>GPIO12</b> dados do DHT22</li>
<li><i style="background:#3b5bff"></i><b>GPIO8</b> SDA do LCD</li>
<li><i style="background:#9b30d0"></i><b>GPIO9</b> SCL do LCD</li>
<li><i style="background:#ff3b3b"></i><b>5V</b> VCC do LCD</li>
<li><i style="background:#111;border:1px solid #8fabbb"></i><b>GND</b> sensor e LCD</li>
</ul>
</div>
<figure><img src="circuito-wokwi.png" alt="Circuito completo no Wokwi."></figure>
</div>
</section>
<!-- ══════════════════════════════ 5 · CÓDIGO ══════════════════════════════ -->
<section class="slide secao" data-secao="5 · Código" data-titulo="§5 · O programa">
<div class="secao-num">5</div>
<div class="rotulo">Parte cinco</div>
<h1>O programa</h1>
<p class="capa-sub">Configurar, iniciar, repetir.</p>
</section>
<section class="slide" data-secao="5 · Código" data-titulo="Configuração">
<div class="rotulo">5 · Código · 1 de 3</div>
<h2>Configuração</h2>
<div class="corpo codigo-lado">
<pre class="codigo"><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></code></pre>
<div class="centro">
<div class="cartao destaque"><div class="cartao-titulo">Mudou um fio?</div><p>Mude uma linha. Tudo que depende da montagem está no topo.</p></div>
<div class="cartao"><div class="cartao-titulo">0x27</div><p>O endereço I2C do display.</p></div>
</div>
</div>
</section>
<section class="slide" data-secao="5 · Código" data-titulo="Desenhando o grau">
<div class="rotulo">5 · Código · 1 de 3</div>
<h2>Desenhando o símbolo de grau</h2>
<div class="corpo duas centro-v">
<div class="grau">
<div class="matriz pequena">
<i></i><i class="l"></i><i class="l"></i><i></i><i></i>
<i class="l"></i><i></i><i></i><i class="l"></i><i></i>
<i class="l"></i><i></i><i></i><i class="l"></i><i></i>
<i></i><i class="l"></i><i class="l"></i><i></i><i></i>
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
</div>
<pre class="bits"><code><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-com">0</span>
<span class="c-com">0</span>
<span class="c-com">0</span>
<span class="c-com">0</span></code></pre>
</div>
<div class="centro">
<pre class="miuda"><code><span class="c-key">byte</span> grau[<span class="c-num">8</span>] = { ... };
lcd.<span class="c-fun">createChar</span>(<span class="c-num">0</span>, grau);
lcd.<span class="c-fun">write</span>(<span class="c-num">0</span>);</code></pre>
<p class="legenda">Bit <strong>1</strong> = ponto aceso.</p>
</div>
</div>
</section>
<section class="slide" data-secao="5 · Código" data-titulo="setup()">
<div class="rotulo">5 · Código · 2 de 3</div>
<h2>setup(): uma vez, ao ligar</h2>
<div class="corpo codigo-lado">
<pre class="codigo longo"><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>
<div class="centro">
<div class="lcd lcd-medio"><div>LASER ESP32-S3</div><div>Iniciando...</div></div>
<p class="legenda">A mensagem cobre os <strong>2 s</strong> que o DHT22 leva para a primeira leitura.</p>
</div>
</div>
</section>
<section class="slide" data-secao="5 · Código" data-titulo="loop(): ler">
<div class="rotulo">5 · Código · 3 de 3</div>
<h2>loop(): ler, e desconfiar da leitura</h2>
<div class="corpo codigo-lado">
<pre class="codigo"><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> {</code></pre>
<div class="centro">
<div class="lcd lcd-medio"><div>Falha no sensor </div><div> </div></div>
<p class="legenda"><strong>isnan()</strong>: a leitura falhou. Mostre o erro, não um número inventado.</p>
</div>
</div>
</section>
<section class="slide" data-secao="5 · Código" data-titulo="loop(): mostrar">
<div class="rotulo">5 · Código · 3 de 3</div>
<h2>loop(): mostrar, e esperar 2 segundos</h2>
<div class="corpo codigo-lado">
<pre class="codigo"><code>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>);
<span class="c-com">// ... envia o mesmo texto pelo Serial ...</span>
<span class="c-fun">delay</span>(<span class="c-num">2000</span>); <span class="c-com">// 1 leitura a cada 2 s</span></code></pre>
<div class="centro">
<div class="lcd lcd-medio"><div>Temp: 26.5°C </div><div>Umid: 62.0 % </div></div>
<p class="legenda">Os espaços em <strong>"C&nbsp;&nbsp;&nbsp;"</strong> apagam as sobras da leitura anterior.</p>
</div>
</div>
</section>
<section class="slide" data-secao="5 · Código" data-titulo="Testado no Wokwi">
<div class="rotulo">5 · Código</div>
<h2>Três testes, três telas</h2>
<div class="corpo tres testes">
<figure><img src="lcd-funcionando.png" alt="LCD: Temp 26.5°C, Umid 62.0 %"><figcaption><b>Sala</b> 26,5 °C · 62 %</figcaption></figure>
<figure><img src="lcd-frio.png" alt="LCD: Temp -5.2°C, Umid 88.4 %"><figcaption><b>Frio</b> −5,2 °C · 88,4 %</figcaption></figure>
<figure><img src="lcd-falha.png" alt="LCD: Falha no sensor"><figcaption><b>Fio solto</b> erro detectado</figcaption></figure>
</div>
<p class="legenda centralizada">Na simulação, clique no DHT22 e mova os controles de temperatura e umidade.</p>
</section>
<!-- ═════════════════════════════ 6 · BANCADA ═════════════════════════════ -->
<section class="slide secao" data-secao="6 · Bancada" data-titulo="§6 · Para a bancada">
<div class="secao-num">6</div>
<div class="rotulo">Parte seis</div>
<h1>Da tela para a bancada</h1>
<p class="capa-sub">O que o Wokwi perdoa e o hardware não.</p>
</section>
<section class="slide" data-secao="6 · Bancada" data-titulo="Os pull-ups escondidos">
<div class="rotulo">6 · Bancada</div>
<h2>A plaquinha I2C traz pull-ups para 5 V</h2>
<div class="corpo duas centro-v">
<div class="veredito ruim">
<div class="tensao">≈ 4 V</div>
<p>no GPIO, com a plaquinha em 5 V,<br>mesmo com os 4,7 kΩ para 3V3</p>
<span>✗ fora da especificação</span>
</div>
<div class="veredito bom">
<div class="tensao">3,3 V</div>
<p><strong>Conversor de nível BSS138</strong><br>ou remover os pull-ups da plaquinha</p>
<span>✓ seguro</span>
</div>
</div>
<p class="legenda centralizada">Teste: multímetro entre SDA e VCC da plaquinha. Entre 2 e 10 kΩ, há um pull-up.</p>
</section>
<section class="slide" data-secao="6 · Bancada" data-titulo="Scanner I2C">
<div class="rotulo">6 · Bancada</div>
<h2>Tela vazia? Descubra o endereço</h2>
<div class="corpo empilhado">
<pre class="codigo"><code><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++;
}
}</code></pre>
<div class="saida">
<pre class="terminal"><code>Procurando dispositivos I2C...
Dispositivo em <b>0x27</b></code></pre>
<p class="legenda">Saída real no Wokwi.<br>Na sua placa pode aparecer <strong>0x3F</strong>.</p>
</div>
</div>
</section>
<section class="slide" data-secao="6 · Bancada" data-titulo="Quando não funciona">
<div class="rotulo">6 · Bancada</div>
<h2>Quando não funciona</h2>
<div class="corpo sintomas">
<div class="sintoma"><div class="lcd lcd-mini"><div>Falha no sensor </div><div> </div></div><div><b>"Falha no sensor"</b><p>Pull-up de 10 kΩ, fio no GPIO12, VCC e GND.</p></div></div>
<div class="sintoma"><div class="lcd lcd-mini"><div> </div><div> </div></div><div><b>Aceso e vazio</b><p>Contraste, endereço, ou SDA e SCL trocados.</p></div></div>
<div class="sintoma"><div class="lcd lcd-mini blocos"><div>████████████████</div><div> </div></div><div><b>Linha de blocos</b><p>A energia chegou, o I2C não.</p></div></div>
<div class="sintoma"><div class="lcd lcd-mini apagado"><div> </div><div> </div></div><div><b>Tudo apagado</b><p>Falta o 5V ou o <code>lcd.backlight()</code>.</p></div></div>
</div>
</section>
<section class="slide" data-secao="Fechamento" data-titulo="Desafios">
<div class="rotulo">Fechamento</div>
<h2>Desafios</h2>
<div class="corpo desafios">
<div class="desafio"><div class="desafio-icone">↕</div><b>Mín e máx</b><p>Guarde a menor e a maior temperatura desde que a placa ligou.</p></div>
<div class="desafio"><div class="desafio-icone">!</div><b>Alerta</b><p>Acenda o LED RGB do GPIO38 acima de 30 °C.</p></div>
<div class="desafio"><div class="desafio-icone">≈</div><b>Sensação</b><p>Mostre o índice de calor com <code>computeHeatIndex()</code>.</p></div>
<div class="desafio destaque"><div class="desafio-icone">⏱</div><b>Sem delay</b><p>Reescreva o <code>loop()</code> usando <code>millis()</code>.</p></div>
</div>
</section>
<section class="slide capa" data-secao="Fechamento" data-titulo="Obrigado">
<div class="capa-marca">
<img src="../assets/laser.svg" alt="LASER">
<span>Liga Acadêmica de Sistemas Embarcados<br>Centro de Informática · UFPE</span>
</div>
<div class="capa-selo">Módulo 2 · Encerramento</div>
<h1>Obrigado.<strong>Perguntas?</strong></h1>
<p class="capa-sub">
Apostila, código e arquivos do Wokwi em <strong>espdocs.cienciaembarcada.com.br/formacoes/2</strong>.
</p>
<div class="capa-autor">
<strong>Lucas Rayan Guerra</strong>
contato@lucasrguerra.dev.br<br>
linkedin.com/in/lucasrguerra<br>
espdocs.cienciaembarcada.com.br
</div>
</section>
</div><!-- /#palco -->
<div id="panorama" role="dialog" aria-label="Panorama dos slides" aria-modal="true"></div>
<div id="cortina" aria-hidden="true"></div>
<div id="ajuda">
<span><kbd>←</kbd> <kbd>→</kbd> navegar</span>
<span><kbd>O</kbd> panorama</span>
<span><kbd>F</kbd> tela cheia</span>
<span><kbd>B</kbd> tela preta</span>
</div>
<script>
(function () {
"use strict";
var palco = document.getElementById("palco");
var panorama = document.getElementById("panorama");
var cortina = document.getElementById("cortina");
var ajuda = document.getElementById("ajuda");
var slides = Array.prototype.slice.call(palco.querySelectorAll(".slide"));
var atual = 0;
var timerAjuda;
/* ---------------------------------------------------------------------
Rodapé: o feixe. Montado por script para não repetir markup em cada
slide, e porque a posição da ponta depende do índice.
--------------------------------------------------------------------- */
slides.forEach(function (slide, i) {
var rodape = document.createElement("div");
rodape.className = "rodape";
rodape.setAttribute("aria-hidden", "true");
rodape.innerHTML =
'<div class="trilho"></div>' +
'<div class="percorrido"></div>' +
'<div class="ponta"></div>' +
'<div class="rodape-texto">' +
'<span>' + (slide.dataset.secao || "") + '</span>' +
'<span class="rodape-num">' + String(i + 1).padStart(2, "0") +
' <span style="color:var(--texto-2)">/ ' + slides.length + '</span></span>' +
'</div>';
slide.appendChild(rodape);
slide.setAttribute("role", "group");
slide.setAttribute("aria-label", (i + 1) + " de " + slides.length + ": " + (slide.dataset.titulo || ""));
});
/* Cada slide desenha o seu próprio trecho de feixe uma vez, na carga. Não dá
para desenhar só o ativo: na impressão todos aparecem de uma vez, e os
outros 59 sairiam sem barra de progresso. */
function desenharFeixe() {
slides.forEach(function (slide, i) {
/* No primeiro slide já aparece um toque de feixe, senão a ponta
fica solta no canto. */
var fracao = slides.length > 1 ? i / (slides.length - 1) : 1;
var largura = (0.04 + fracao * 0.96) * 100;
var barra = slide.querySelector(".percorrido");
var ponta = slide.querySelector(".ponta");
if (barra) barra.style.width = largura + "%";
if (ponta) ponta.style.left = largura + "%";
});
}
/* ---------------------------------------------------------------------
Escala do palco
--------------------------------------------------------------------- */
function ajustarEscala() {
var escala = Math.min(window.innerWidth / 1280, window.innerHeight / 720);
palco.style.transform = "scale(" + escala + ")";
}
/* ---------------------------------------------------------------------
Navegação
--------------------------------------------------------------------- */
function ir(indice, semHash) {
indice = Math.max(0, Math.min(slides.length - 1, indice));
slides[atual].classList.remove("ativo");
atual = indice;
slides[atual].classList.add("ativo");
if (!semHash) history.replaceState(null, "", "#" + (atual + 1));
if (panorama.classList.contains("aberto")) marcarMiniatura();
}
var avancar = function () { ir(atual + 1); };
var recuar = function () { ir(atual - 1); };
/* ---------------------------------------------------------------------
Panorama
--------------------------------------------------------------------- */
slides.forEach(function (slide, i) {
var b = document.createElement("button");
b.className = "miniatura";
b.type = "button";
b.innerHTML =
'<b>' + String(i + 1).padStart(2, "0") + '</b>' +
'<span>' + (slide.dataset.titulo || "Slide " + (i + 1)) + '</span>' +
'<i>' + (slide.dataset.secao || "") + '</i>';
b.addEventListener("click", function () { fecharPanorama(); ir(i); });
panorama.appendChild(b);
});
function marcarMiniatura() {
var minis = panorama.querySelectorAll(".miniatura");
for (var i = 0; i < minis.length; i++) minis[i].classList.toggle("atual", i === atual);
var ativa = minis[atual];
if (ativa) ativa.scrollIntoView({ block: "nearest" });
}
function abrirPanorama() {
panorama.classList.add("aberto");
marcarMiniatura();
panorama.querySelectorAll(".miniatura")[atual].focus();
}
function fecharPanorama() { panorama.classList.remove("aberto"); }
function alternarPanorama() {
panorama.classList.contains("aberto") ? fecharPanorama() : abrirPanorama();
}
/* ---------------------------------------------------------------------
Teclado, toque e ponteiro
--------------------------------------------------------------------- */
document.addEventListener("keydown", function (e) {
if (e.metaKey || e.ctrlKey || e.altKey) return;
var k = e.key;
if (cortina.classList.contains("aberta")) {
cortina.classList.remove("aberta");
e.preventDefault();
return;
}
if (k === "Escape") {
if (panorama.classList.contains("aberto")) { fecharPanorama(); e.preventDefault(); }
return;
}
if (k === "ArrowRight" || k === "ArrowDown" || k === "PageDown" || k === " " || k === "Enter") {
avancar(); e.preventDefault();
} else if (k === "ArrowLeft" || k === "ArrowUp" || k === "PageUp" || k === "Backspace") {
recuar(); e.preventDefault();
} else if (k === "Home") {
ir(0); e.preventDefault();
} else if (k === "End") {
ir(slides.length - 1); e.preventDefault();
} else if (k === "o" || k === "O") {
alternarPanorama(); e.preventDefault();
} else if (k === "f" || k === "F") {
if (document.fullscreenElement) document.exitFullscreen();
else document.documentElement.requestFullscreen().catch(function () {});
e.preventDefault();
} else if (k === "b" || k === "B" || k === ".") {
cortina.classList.add("aberta"); e.preventDefault();
}
});
cortina.addEventListener("click", function () { cortina.classList.remove("aberta"); });
/* Clique na metade direita avança, na esquerda recua. Não vale sobre o
panorama nem sobre links. */
document.addEventListener("click", function (e) {
if (panorama.contains(e.target) || cortina.classList.contains("aberta")) return;
if (e.target.closest("a, button")) return;
(e.clientX > window.innerWidth * 0.5) ? avancar() : recuar();
});
var toqueX = null;
document.addEventListener("touchstart", function (e) { toqueX = e.changedTouches[0].clientX; }, { passive: true });
document.addEventListener("touchend", function (e) {
if (toqueX === null) return;
var d = e.changedTouches[0].clientX - toqueX;
if (Math.abs(d) > 55) (d < 0) ? avancar() : recuar();
toqueX = null;
}, { passive: true });
/* A ajuda aparece ao mexer o mouse e some sozinha, para não poluir a
projeção. */
document.addEventListener("mousemove", function () {
ajuda.classList.add("visivel");
clearTimeout(timerAjuda);
timerAjuda = setTimeout(function () { ajuda.classList.remove("visivel"); }, 2600);
});
window.addEventListener("resize", ajustarEscala);
window.addEventListener("hashchange", function () {
var n = parseInt(location.hash.slice(1), 10);
if (n && n - 1 !== atual) ir(n - 1, true);
});
/* --------------------------------------------------------------------- */
/* -----------------------------------------------------------------------
Rede de segurança do 16:9
O palco tem 1280x720 fixos, mas o conteúdo é texto: uma fonte que não
carregou, um zoom de navegador ou uma correção de texto podem empurrar
um slide para fora da caixa. Em vez de deixar o excesso vazar (na tela
ele seria cortado, no PDF viraria página extra), cada slide que não
couber encolhe o próprio corpo até caber.
----------------------------------------------------------------------- */
function medirSobra(slide) {
var caixa = slide.getBoundingClientRect();
var esc = caixa.width / 1280 || 1;
var st = getComputedStyle(slide);
var limite = 720 - parseFloat(st.paddingBottom);
var pior = 0;
var filhos = slide.querySelectorAll("*");
for (var i = 0; i < filhos.length; i++) {
var el = filhos[i];
if (el.closest(".rodape") || el.classList.contains("secao-num") || el.ownerSVGElement) continue;
var b = el.getBoundingClientRect();
if (!b.height && !b.width) continue;
var d = (b.bottom - caixa.top) / esc - limite;
if (d > pior) pior = d;
}
return pior;
}
function encaixarSlides() {
var visivel = slides[atual];
slides.forEach(function (slide) {
var corpo = slide.querySelector(".corpo");
if (!corpo) return;
var eraAtivo = slide.classList.contains("ativo");
if (!eraAtivo) slide.classList.add("ativo");
corpo.style.zoom = "";
var sobra = medirSobra(slide);
if (sobra > 1) {
var altura = corpo.getBoundingClientRect().height / (slide.getBoundingClientRect().width / 1280 || 1);
/* Nunca abaixo de 0,8: menor que isso o texto deixa de ser
legível projetado, e o problema é de conteúdo, não de escala. */
var fator = Math.max(0.8, (altura - sobra) / altura);
corpo.style.zoom = fator.toFixed(3);
if (medirSobra(slide) > 1) corpo.style.zoom = (fator - 0.02).toFixed(3);
}
if (!eraAtivo) slide.classList.remove("ativo");
});
visivel.classList.add("ativo");
}
ajustarEscala();
var inicial = parseInt(location.hash.slice(1), 10);
slides[0].classList.add("ativo");
if (inicial && inicial >= 1 && inicial <= slides.length) ir(inicial - 1);
ajuda.classList.add("visivel");
timerAjuda = setTimeout(function () { ajuda.classList.remove("visivel"); }, 4200);
/* As fontes chegam depois do primeiro layout; medir antes disso dá número
errado. O segundo passe cobre a impressão, que reflui tudo de uma vez. */
function conferir() { desenharFeixe(); encaixarSlides(); }
conferir();
if (document.fonts && document.fonts.ready) document.fonts.ready.then(conferir);
window.addEventListener("beforeprint", conferir);
})();
</script>
</body>
</html>