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
- 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>
1812 lines
72 KiB
HTML
1812 lines
72 KiB
HTML
<!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="#FCBF49" 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" stroke="#ff5a5a"/>
|
||
</g>
|
||
<circle cx="480" cy="306" r="8" fill="#FCBF49"/>
|
||
<circle cx="480" cy="416" r="8" fill="#ff5a5a"/>
|
||
<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="#FCBF49">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"><Wire.h></span>
|
||
<span class="c-pre">#include</span> <span class="c-str"><DHT.h></span>
|
||
<span class="c-pre">#include</span> <LiquidCrystal_I2C.h>
|
||
|
||
<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 "</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 < <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>
|