-
-
- Nenhum termo encontrado
-
-
- Não encontramos resultados para sua pesquisa com os filtros selecionados.
+
+
+
+ {total} {total === 1 ? "termo" : "termos"}
+ {temFiltroAtivo && com os filtros atuais }
+
+ {temFiltroAtivo && (
+
+
+ Redefinir filtros
+
+ )}
+
+
+ {total === 0 ? (
+
+
+
Nenhum termo encontrado
+
+ Nada combina com a busca e os filtros atuais. Tente uma sigla mais curta ou volte para todas as categorias.
-
-
- Limpar todos os filtros
+
+
+ Limpar filtros
) : (
-
+
{termosFiltrados.map((item) => {
+ const Icone = ICONES[item.categoriaIcone] ?? Cpu;
return (
-
-
- {/* Header do Card */}
-
-
- {item.categoriaEmoji}
- {item.categoriaNome}
-
-
- copiarLinkTermo(item.slug)}
- title="Copiar link direto para este termo"
- className="text-xs text-slate-400 hover:text-purple-600 dark:hover:text-purple-400 inline-flex items-center gap-1 p-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer"
- >
- {copiadoSlug === item.slug ? (
- <>
-
-
- Copiado!
-
- >
- ) : (
-
- )}
-
-
-
- {/* Título do Termo */}
-
- {item.termo}
-
-
- {/* Definição */}
-
-
-
+
+
+
+
+ {nomeCurto(item.categoriaNome)}
+
+ copiarLinkTermo(item.slug)}
+ aria-label={`Copiar link para ${item.termo}`}
+ className="btn-icon inline-flex h-11 w-11 items-center justify-center rounded-full text-muted transition-colors hover:bg-surface-2 hover:text-accent"
+ >
+ {copiadoSlug === item.slug ? : }
+
- {/* Caixa de Exemplo Prático */}
-
-
-
-
- Exemplo Prático
-
-
-
-
-
+
{item.termo}
+
+
+
+
+
);
@@ -488,41 +266,20 @@ export default function GlossarioPage() {
)}
- {/* 3. CTA & PRÓXIMOS PASSOS */}
-
-
-
-
- Continue Explorando
-
-
-
- Pronto para aplicar na prática?
-
-
-
- Agora que você domina os conceitos técnicos, compare as especificações das séries ou use o seletor inteligente para encontrar o microcontrolador ideal para seu projeto.
+
+
+
Do conceito ao chip certo.
+
+ Com os termos em mãos, compare as séries lado a lado ou responda ao seletor para chegar ao modelo que cabe no seu projeto.
-
-
-
-
Explorar Séries ESP32
-
+
+
+
+ Escolher meu chip
-
-
Seletor Inteligente
-
-
-
Guia de Diagnóstico & Erros
+
+ Explorar as séries
+
@@ -564,7 +321,7 @@ function TextoComLinks({ texto }) {
{parte}
diff --git a/app/layout.jsx b/app/layout.jsx
index 28299c7..879cb9d 100644
--- a/app/layout.jsx
+++ b/app/layout.jsx
@@ -1,18 +1,27 @@
-import { Outfit, Inter } from "next/font/google";
+import { JetBrains_Mono, Manrope, Space_Grotesk } from "next/font/google";
import "./globals.css";
import Providers from "@/components/Providers";
import { SITE, jsonLd } from "@/lib/seo";
-const outfit = Outfit({
+const spaceGrotesk = Space_Grotesk({
subsets: ["latin"],
- variable: "--font-outfit",
+ variable: "--font-space-grotesk",
display: "swap",
+ weight: ["500", "600", "700"],
});
-const inter = Inter({
+const manrope = Manrope({
subsets: ["latin"],
- variable: "--font-inter",
+ variable: "--font-manrope",
display: "swap",
+ weight: ["400", "500", "600", "700"],
+});
+
+const jetbrainsMono = JetBrains_Mono({
+ subsets: ["latin"],
+ variable: "--font-jetbrains",
+ display: "swap",
+ weight: ["400", "500", "600"],
});
export const metadata = {
@@ -93,14 +102,14 @@ export const metadata = {
export const viewport = {
themeColor: [
- { media: "(prefers-color-scheme: light)", color: "#f8fafc" },
- { media: "(prefers-color-scheme: dark)", color: "#020617" },
+ { media: "(prefers-color-scheme: light)", color: "#f5f6f8" },
+ { media: "(prefers-color-scheme: dark)", color: "#07090d" },
],
};
export default function RootLayout({ children }) {
return (
-
+
{/* Dados estruturados do site e da organização. É o que permite ao
Google montar o box de busca interna e entender a autoria. */}
@@ -109,7 +118,13 @@ export default function RootLayout({ children }) {
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd.siteESociedade()) }}
/>
-
+
+
+ Pular para o conteúdo
+
{children}
diff --git a/app/livro/page.jsx b/app/livro/page.jsx
new file mode 100644
index 0000000..e6a51f4
--- /dev/null
+++ b/app/livro/page.jsx
@@ -0,0 +1,359 @@
+import Header from "@/components/Header";
+import Footer from "@/components/Footer";
+import BookCover from "@/components/BookCover";
+import CodeSample from "@/components/CodeSample";
+import Link from "next/link";
+import { jsonLd, paginaMeta, SITE } from "@/lib/seo";
+import {
+ ArrowRight,
+ ArrowUpRight,
+ BookOpen,
+ Cpu,
+ Gauge,
+ Radio,
+ ShieldCheck,
+ Terminal,
+ Wifi,
+} from "lucide-react";
+
+export const metadata = paginaMeta({
+ titulo: "Microcontroladores ESP32, o livro",
+ descricao:
+ "Conheça o livro Microcontroladores ESP32: conectividade, eficiência e desempenho. 12 partes e 78 capítulos em português sobre arquitetura, energia, rádio, periféricos e firmware.",
+ caminho: "/livro",
+ keywords: [
+ "livro ESP32",
+ "Microcontroladores ESP32",
+ "livro de ESP32 em português",
+ "Lucas Rayan Guerra",
+ "Ciência Embarcada",
+ ],
+});
+
+const CHAPTERS = [
+ {
+ title: "O ecossistema ESP32",
+ desc: "Como as famílias se organizam, o que muda entre Xtensa e RISC-V e por que a escolha do chip começa pelo problema.",
+ topics: ["Famílias e sufixos", "Critérios de escolha", "Ferramentas de apoio"],
+ icon: Cpu,
+ },
+ {
+ title: "Arquitetura e memória",
+ desc: "Núcleos, caches, SRAM, PSRAM e o caminho entre o código em flash e a execução em alta velocidade.",
+ topics: ["Mapa de memória", "PSRAM na prática", "Caches e latência"],
+ icon: Gauge,
+ },
+ {
+ title: "Energia e autonomia",
+ desc: "Os modos de sono, o custo real do rádio e as decisões de hardware que definem a vida útil da bateria.",
+ topics: ["Deep sleep", "Domínios de energia", "Orçamento de consumo"],
+ icon: Gauge,
+ },
+ {
+ title: "Conectividade",
+ desc: "Wi-Fi, BLE, Thread, Zigbee e Matter: o que cada rádio resolve e onde a arquitetura começa a pesar.",
+ topics: ["Wi-Fi 6 e TWT", "BLE e malhas", "Matter e 802.15.4"],
+ icon: Radio,
+ },
+ {
+ title: "Periféricos e pinagem",
+ desc: "GPIO, ADC, I2S, USB, MIPI e as restrições que só aparecem quando o hardware já está na placa.",
+ topics: ["Pinos de strapping", "ADC e rádio", "USB nativo"],
+ icon: Wifi,
+ },
+ {
+ title: "Do protótipo ao produto",
+ desc: "Firmware, partições, OTA, segurança e os cuidados de engenharia que sobrevivem ao primeiro deploy.",
+ topics: ["ESP-IDF e Arduino", "OTA e rollback", "Secure boot"],
+ icon: ShieldCheck,
+ },
+];
+
+const ESP_IDF = `#include "driver/gpio.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#define LED GPIO_NUM_2
+
+void app_main(void)
+{
+ gpio_reset_pin(LED);
+ gpio_set_direction(LED, GPIO_MODE_OUTPUT);
+
+ while (1) {
+ gpio_set_level(LED, 1);
+ vTaskDelay(pdMS_TO_TICKS(500));
+ gpio_set_level(LED, 0);
+ vTaskDelay(pdMS_TO_TICKS(500));
+ }
+}`;
+
+const ARDUINO = `#include
+
+const char *ssid = "SUA_REDE";
+const char *senha = "SUA_SENHA";
+
+void setup() {
+ Serial.begin(115200);
+ WiFi.begin(ssid, senha);
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println(WiFi.localIP());
+}
+
+void loop() {}`;
+
+const DEEP_SLEEP = `#include "esp_sleep.h"
+#include "esp_log.h"
+
+void app_main(void)
+{
+ uint64_t intervalo_us = 30ULL * 1000000ULL;
+ esp_sleep_enable_timer_wakeup(intervalo_us);
+
+ ESP_LOGI("energia", "dormindo por 30 segundos");
+ esp_deep_sleep_start();
+}`;
+
+const FACTS = [
+ ["Idioma", "Português"],
+ ["Partes", "12"],
+ ["Capítulos", "78"],
+ ["Digital", "Amazon Kindle"],
+ ["Impresso", "UICLAP"],
+ ["ISBN", SITE.livroIsbn],
+];
+
+export default function LivroPage() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Início
+
+ /
+ Livro
+
+
+
+
+
+
+ Livro novo · 1ª edição
+
+
+ Microcontroladores ESP32
+
+
{SITE.livroSubtitulo}
+
+ Um percurso completo pelo ecossistema ESP32 — da arquitetura do chip à implantação
+ de produtos conectados. São 12 partes e 78 capítulos que cobrem Arduino e ESP-IDF,
+ FreeRTOS, Wi-Fi, BLE, Thread e Matter, periféricos, depuração, baixo consumo e segurança.
+
+
+
+
+
+ {FACTS.map(([label, value]) => (
+
+
{label}
+ {value}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ Lucas Rayan Guerra
+ Ciência Embarcada
+
+
+
+
+
+
+
+
+
Sumário
+
+ Uma linha de raciocínio, do chip ao produto.
+
+
+
+
+ A ordem acompanha o projeto real: entender o chip, medir energia, conectar, expor
+ periféricos e sustentar o produto. Os seis eixos abaixo mostram como as 12 partes
+ se organizam.
+
+
+ 78 capítulos, com exercícios e projetos ao longo do percurso.
+
+
+
+
+
+ {CHAPTERS.map((chapter) => {
+ const Icon = chapter.icon;
+ return (
+
+
+
+
+ {chapter.title}
+ {chapter.desc}
+
+ {chapter.topics.map((topic) => (
+
+ {topic}
+
+ ))}
+
+
+ );
+ })}
+
+
+
+
+
+
+
+
Código
+
+ Exemplos curtos, prontos para a bancada.
+
+
+ Os trechos abaixo são originais e servem como ponto de partida. Ajuste o pino, a rede
+ e a frequência ao seu hardware antes de gravar.
+
+
+
+
+ Sem conteúdo protegido
+
+
+ Esta página apresenta apenas a ficha do livro e exemplos autorais. O texto integral,
+ PDF, EPUB e arquivos intermediários não são distribuídos pelo ESPDocs.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Para quem é
+
+ Para quem já cansou de juntar pedaços de datasheet.
+
+
+ Estudantes, makers e engenheiros de firmware que querem uma visão coerente do
+ ecossistema — sem abandonar a fonte oficial.
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/not-found.jsx b/app/not-found.jsx
new file mode 100644
index 0000000..ebb545d
--- /dev/null
+++ b/app/not-found.jsx
@@ -0,0 +1,58 @@
+import Header from "@/components/Header";
+import Footer from "@/components/Footer";
+import Link from "next/link";
+import { ArrowRight, BookOpen, Compass, Scale, SlidersHorizontal } from "lucide-react";
+
+export const metadata = {
+ title: "Página não encontrada",
+ robots: { index: false, follow: false },
+};
+
+const CAMINHOS = [
+ { href: "/series", icon: Compass, titulo: "Séries ESP32", texto: "As 12 famílias, com pinagem e especificações." },
+ { href: "/seletor", icon: SlidersHorizontal, titulo: "Seletor de chip", texto: "Responda a oito perguntas e receba os modelos indicados." },
+ { href: "/comparacao", icon: Scale, titulo: "Comparador", texto: "Até quatro séries lado a lado." },
+ { href: "/glossario", icon: BookOpen, titulo: "Glossário", texto: "Termos de hardware explicados em português." },
+];
+
+export default function NotFound() {
+ return (
+
+
+
+
+
+
Erro 404
+
Essa página não existe.
+
+ O endereço pode ter mudado ou estar digitado errado. Escolha um destes caminhos para continuar.
+
+
+
+ Voltar ao início
+
+
+
+
+
+
+ {CAMINHOS.map(({ href, icon: Icon, titulo, texto }) => (
+
+
+
+
+
+
+ {titulo}
+ {texto}
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/app/page.jsx b/app/page.jsx
index 6d01a1f..bf376fb 100644
--- a/app/page.jsx
+++ b/app/page.jsx
@@ -1,468 +1,174 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
+import BookCover from "@/components/BookCover";
import Link from "next/link";
-import Image from "next/image";
-import {
- SlidersHorizontal,
- ShoppingBag,
- Cpu,
- Layers,
- Scale,
- BookOpen,
+import seriesData from "@/public/series.json";
+import frameworksData from "@/public/frameworks.json";
+import { SITE } from "@/lib/seo";
+import {
AlertTriangle,
- Terminal,
- Compass,
ArrowRight,
- Sparkles,
- CheckCircle2,
- Zap,
- ExternalLink,
- Info,
- Radio,
- ShieldCheck,
- Code,
- HelpCircle,
- Box
+ ArrowUpRight,
+ BookOpen,
+ Box,
+ Cpu,
+ Scale,
+ SlidersHorizontal,
+ Terminal,
} from "lucide-react";
+const FEATURED = ["ESP32", "ESP32-S3", "ESP32-C6", "ESP32-C3", "ESP32-P4", "ESP32-H2"];
+
+const TOOLS = [
+ {
+ href: "/seletor",
+ title: "Seletor inteligente",
+ desc: "Responda oito perguntas sobre o projeto e receba os chips mais compatíveis, com justificativa técnica.",
+ icon: SlidersHorizontal,
+ action: "Encontrar meu chip",
+ },
+ {
+ href: "/comparacao",
+ title: "Comparador de séries",
+ desc: "Coloque até quatro modelos lado a lado e compare núcleos, rádio, memória, periféricos e segurança.",
+ icon: Scale,
+ action: "Comparar agora",
+ },
+ {
+ href: "/diagnostico",
+ title: "Diagnóstico de falhas",
+ desc: "Causas e correções para brownout, bootloop, Guru Meditation, watchdog e falhas de flash.",
+ icon: AlertTriangle,
+ action: "Abrir diagnóstico",
+ },
+ {
+ href: "/componentes",
+ title: "Component Registry",
+ desc: "Drivers, bibliotecas e BSPs do registro oficial da Espressif, com manifesto pronto para o projeto.",
+ icon: Box,
+ action: "Explorar componentes",
+ },
+ {
+ href: "/frameworks",
+ title: "Frameworks e SDKs",
+ desc: "ESP-IDF, Arduino, Rust, Matter, MicroPython e as ferramentas que ficam em volta do chip.",
+ icon: Terminal,
+ action: "Ver frameworks",
+ },
+ {
+ href: "/catalogo",
+ title: "Catálogo de placas",
+ desc: "DevKits e módulos reais, com interface USB, sensores integrados e link para a loja oficial.",
+ icon: Cpu,
+ action: "Ver placas",
+ },
+];
+
+const ERRORS = [
+ {
+ code: "Brownout detector was triggered",
+ cause: "Queda de tensão na inicialização do rádio.",
+ fix: "Capacitor de 10–100 µF junto ao chip e fonte de 5 V com corrente suficiente.",
+ },
+ {
+ code: "Guru Meditation Error",
+ cause: "Ponteiro inválido, stack overflow ou uso incorreto de interrupção.",
+ fix: "Leia o backtrace com addr2line e dimensione a stack da task no FreeRTOS.",
+ },
+ {
+ code: "ADC2 indisponível com Wi-Fi",
+ cause: "O controlador de rádio reserva o ADC2 enquanto está ativo.",
+ fix: "Use os pinos do ADC1 para sensores analógicos que convivem com o Wi-Fi.",
+ },
+ {
+ code: "Flash Read err / bootloader",
+ cause: "Tensão ou modo de flash incorretos, ou pino de strapping preso no boot.",
+ fix: "Confira 3,3 V, pull-ups de boot e a configuração de flash no esptool.",
+ },
+];
+
export default function Home() {
- // Estatísticas da Plataforma
- const stats = [
- { value: "12", label: "Séries Documentadas", desc: "Do ESP32 clássico ao P4 e C61" },
- { value: "14", label: "Frameworks & SDKs", desc: "ESP-IDF, RainMaker, Rust, Matter, etc." },
- { value: "35+", label: "Componentes Curados", desc: "ESP Component Registry & idf_component.yml" },
- { value: "30+", label: "Guias de Diagnóstico", desc: "Soluções para bootloops, brownouts e WDT" },
- ];
-
- // Ferramentas da Plataforma (100% Clicáveis)
- const tools = [
- {
- title: "Seletor Inteligente",
- badge: "Recomendado",
- badgeColor: "bg-purple-100 text-purple-700 dark:bg-purple-950/60 dark:text-purple-300",
- description: "Responda algumas perguntas sobre seu projeto e descubra o chip ESP32 ideal com análise técnica e justificativa.",
- href: "/seletor",
- icon: ,
- iconBg: "text-purple-600 bg-purple-50 dark:bg-purple-950/40 dark:text-purple-400",
- ctaText: "Iniciar Quiz de Seleção",
- },
- {
- title: "ESP Component Registry",
- badge: "Oficial & Modular",
- badgeColor: "bg-purple-100 text-purple-700 dark:bg-purple-950/60 dark:text-purple-300",
- description: "Explore o catálogo oficial de pacotes modulares da Espressif (LVGL, RainMaker, ESP-SR, BSPs) e gere manifestos idf_component.yml.",
- href: "/componentes",
- icon: ,
- iconBg: "text-purple-600 bg-purple-50 dark:bg-purple-950/40 dark:text-purple-400",
- ctaText: "Explorar Componentes",
- },
- {
- title: "Diagnóstico de Falhas",
- badge: "30+ Soluções",
- badgeColor: "bg-amber-100 text-amber-700 dark:bg-amber-950/60 dark:text-amber-300",
- description: "Encontre a causa e o passo a passo para corrigir erros frequentes como Brownout, Guru Meditation, Bootloops e falhas de Flash.",
- href: "/diagnostico",
- icon: ,
- iconBg: "text-amber-600 bg-amber-50 dark:bg-amber-950/40 dark:text-amber-400",
- ctaText: "Resolver Erro no Código",
- },
- {
- title: "Comparador de Séries",
- badge: "Lado a Lado",
- badgeColor: "bg-indigo-100 text-indigo-700 dark:bg-indigo-950/60 dark:text-indigo-300",
- description: "Compare especificações de até 4 séries simultaneamente: núcleos, clock, memória SRAM/PSRAM, interfaces e conexões.",
- href: "/comparacao",
- icon: ,
- iconBg: "text-indigo-600 bg-indigo-50 dark:bg-indigo-950/40 dark:text-indigo-400",
- ctaText: "Comparar Modelos",
- },
- {
- title: "Explorador de Séries & Pinouts",
- badge: "12 Chips",
- badgeColor: "bg-blue-100 text-blue-700 dark:bg-blue-950/60 dark:text-blue-300",
- description: "Consulte diagramas interativos de conexões, mapas de periféricos, datasheets oficiais e restrições de pinos (strapping, ADC2).",
- href: "/series",
- icon: ,
- iconBg: "text-blue-600 bg-blue-50 dark:bg-blue-950/40 dark:text-blue-400",
- ctaText: "Ver Todas as Séries",
- },
- {
- title: "Catálogo de Placas",
- badge: "Hardware Real",
- badgeColor: "bg-emerald-100 text-emerald-700 dark:bg-emerald-950/60 dark:text-emerald-300",
- description: "Filtre placas de desenvolvimento (DevKit, Xiao, Wemos, LilyGO) por série, interface USB, sensores integrados e links de compra.",
- href: "/catalogo",
- icon: ,
- iconBg: "text-emerald-600 bg-emerald-50 dark:bg-emerald-950/40 dark:text-emerald-400",
- ctaText: "Explorar Placas Físicas",
- },
- {
- title: "Frameworks & SDKs",
- badge: "14 Frameworks",
- badgeColor: "bg-teal-100 text-teal-700 dark:bg-teal-950/60 dark:text-teal-300",
- description: "Documentação completa de frameworks oficiais: ESP-IDF, RainMaker, Rust, Matter, ESP-SR, Arduino, MicroPython e mais.",
- href: "/frameworks",
- icon: ,
- iconBg: "text-teal-600 bg-teal-50 dark:bg-teal-950/40 dark:text-teal-400",
- ctaText: "Acessar Guias de SDK",
- },
- {
- title: "Glossário Técnico",
- badge: "60+ Conceitos",
- badgeColor: "bg-pink-100 text-pink-700 dark:bg-pink-950/60 dark:text-pink-300",
- description: "Dicionário de termos técnicos essenciais: entenda RTOS, NVS, Partições OTA, eFuse, Brownout, RISC-V e protocolos industriais.",
- href: "/glossario",
- icon: ,
- iconBg: "text-pink-600 bg-pink-50 dark:bg-pink-950/40 dark:text-pink-400",
- ctaText: "Consultar Termos",
- },
- {
- title: "Sobre a Plataforma",
- badge: "Comunidade",
- badgeColor: "bg-slate-200 text-slate-700 dark:bg-slate-800 dark:text-slate-300",
- description: "Conheça a missão da ESPDocs, a autoria de Lucas Rayan Guerra e a parceria educacional com o blog Ciência Embarcada.",
- href: "/sobre",
- icon: ,
- iconBg: "text-slate-650 bg-slate-100 dark:bg-slate-800 dark:text-slate-350",
- ctaText: "Conhecer o Projeto",
- },
- ];
-
- // Chips em Destaque
- const featuredChips = [
- {
- name: "ESP32",
- slug: "ESP32",
- badge: "Padrão da Indústria",
- arch: "Xtensa LX6 Dual-Core @ 240 MHz",
- wireless: "Wi-Fi 4 (802.11 b/g/n) + BLE 4.2 / Clássico",
- highlights: "Suporte maduro, 36 pinos GPIO, ADC duplo, DAC de 8 bits",
- cor: "#007bff",
- },
- {
- name: "ESP32-S3",
- slug: "ESP32-S3",
- badge: "IA & USB Nativo",
- arch: "Xtensa LX7 Dual-Core @ 240 MHz",
- wireless: "Wi-Fi 4 + BLE 5.0 (Long Range)",
- highlights: "Instruções vetoriais para IA, USB OTG nativo, até 45 GPIOs",
- cor: "#fd7e14",
- },
- {
- name: "ESP32-C6",
- slug: "ESP32-C6",
- badge: "Wi-Fi 6 + Zigbee/Matter",
- arch: "RISC-V 32-bit Single-Core @ 160 MHz + LP Core",
- wireless: "Wi-Fi 6 (ax) + BLE 5.3 + Zigbee 3.0 & Thread (802.15.4)",
- highlights: "Protocolo Matter, Target Wake Time (TWT), segurança de hardware",
- cor: "#17a2b8",
- },
- {
- name: "ESP32-C3",
- slug: "ESP32-C3",
- badge: "Custo-Benefício RISC-V",
- arch: "RISC-V 32-bit Single-Core @ 160 MHz",
- wireless: "Wi-Fi 4 + BLE 5.0 (Mesh & Long Range)",
- highlights: "Substituto direto do ESP8266, pinout compacto, baixo consumo",
- cor: "#20c997",
- },
- {
- name: "ESP32-P4",
- slug: "ESP32-P4",
- badge: "Superprocessador 400MHz",
- arch: "Dual-Core RISC-V HP @ 400 MHz + LP Core @ 40 MHz",
- wireless: "Sem rádio interno (Focado em processamento multimídia)",
- highlights: "MIPI-CSI/DSI, codificador H.264, Ethernet 100M, 50+ GPIOs",
- cor: "#ffc107",
- },
- {
- name: "ESP32-H2",
- slug: "ESP32-H2",
- badge: "Zigbee & Thread",
- arch: "RISC-V 32-bit Single-Core @ 96 MHz",
- wireless: "IEEE 802.15.4 (Zigbee 3.0, Thread) + BLE 5.3",
- highlights: "Automação residencial ultrabaixo consumo, nós finais de rede",
- cor: "#6c757d",
- },
- ];
-
- // Problemas Comuns do Diagnóstico
- const commonErrors = [
- {
- title: "Brownout detector was triggered",
- cause: "Queda de tensão súbita na alimentação ao inicializar o Wi-Fi",
- solution: "Adicionar capacitor de desacoplamento de 10µF–100µF e usar fonte externa de 5V 1A+",
- },
- {
- title: "Guru Meditation Error: IllegalInstruction",
- cause: "Acesso a ponteiro nulo, estouro de pilha (stack overflow) ou interrupção incorreta",
- solution: "Inspecione o Backtrace usando o addr2line e aumente o tamanho da stack da task FreeRTOS",
- },
- {
- title: "ADC2 leitura inválida com Wi-Fi",
- cause: "O rádio Wi-Fi bloqueia o conversor analógico-digital ADC2 enquanto está ativo",
- solution: "Utilize exclusivamente pinos do ADC1 (GPIOs 32 a 39 no ESP32) para sensores analógicos",
- },
- {
- title: "Flash Read err / Bootloader falhou",
- cause: "Tensão de Flash incorreta (1.8V vs 3.3V) ou pino de strapping (GPIO 0, 2, 12, 15) travado",
- solution: "Verifique resistores pull-up/pull-down nos pinos de boot e confirme a frequência de Flash no esptool",
- },
- ];
+ const seriesCount = Object.keys(seriesData).length;
+ const frameworkCount = Object.keys(frameworksData).length;
+ const featured = FEATURED.map((key) => [key, seriesData[key]]).filter(([, serie]) => Boolean(serie));
return (
-
+
-
- {/* Hero Section */}
-
- {/* Pill Tag */}
-
-
- O Maior Hub Técnico de ESP32 em Português
-
+
+
+
+
+
+ Documentação aberta do ESP32
+
+
+ O ESP32,
+ sem ruído.
+
+
+ Especificações, pinagem, comparações e diagnóstico em uma leitura só.
+ Menos tradução de datasheet, mais tempo com o protótipo na bancada.
+
-
- ESPDocs
-
-
-
- Tudo o que você precisa para dominar o ecossistema ESP32
-
-
-
- Chega de perder tempo traduzindo datasheets dispersos ou decifrando bootloops no escuro.
- Compare 12 séries de chips, diagnostique erros comuns de firmware, descubra o ESP32 ideal
- para o seu projeto e consulte pinouts interativos 100% em português brasileiro.
-
+
+
+
+ Escolher meu chip
+
+
+ Explorar as {seriesCount} séries
+
+
+
- {/* Botões de Ação Hero */}
-
-
-
-
Encontrar ESP32 Ideal
-
-
-
-
-
-
Diagnosticar Falhas
-
-
-
-
-
Explorar 12 Séries
-
-
-
-
-
Comparar Chips
-
-
-
- {/* Faixa de Estatísticas */}
-
- {stats.map((stat) => (
-
-
- {stat.value}
-
-
- {stat.label}
-
-
- {stat.desc}
-
-
- ))}
-
-
-
- {/* Seção 2: Central de Ferramentas & Recursos (Cards 100% Clicáveis) */}
-
-
-
- Central de Ferramentas e Guias
-
-
- Escolha o que você deseja fazer hoje. Cada ferramenta foi desenvolvida para acelerar seu desenvolvimento e eliminar gargalos.
+
+ {seriesCount} séries
+ ·
+ {frameworkCount} frameworks
+ ·
+ 78 capítulos no livro
+ ·
+ 100% em português
-
-
- {tools.map((tool) => (
-
-
- {/* Top Bar: Icon + Badge */}
-
-
- {tool.icon}
-
-
- {tool.badge}
-
-
- {/* Content */}
-
- {tool.title}
-
-
- {tool.description}
-
-
-
- {/* Bottom Action Link */}
-
-
- ))}
-
-
-
- {/* Seção 3: Explore as Séries em Destaque */}
-
-
-
-
-
- Hardware & Silício
-
-
- Séries Populares de ESP32
-
-
- Conheça as principais famílias de chips da Espressif, da arquitetura aos protocolos sem fio.
-
-
-
-
-
Ver todas as 12 Séries
-
-
-
-
-
- {featuredChips.map((chip) => (
-
-
-
-
- {chip.name}
-
-
- {chip.badge}
-
-
-
-
-
-
- {chip.arch}
-
-
-
-
- {chip.wireless}
-
-
-
-
- {chip.highlights}
-
-
-
-
-
-
Ver Pinout e Especificações
-
-
-
- ))}
-
-
-
- {/* Seção 4: Problemas Comuns & Diagnóstico Rápido */}
-
-
-
+
+
-
-
- Enfrentando Erros ou Travamentos?
-
-
- Acesse guias práticos com causas comprovadas e soluções passo a passo para as falhas mais comuns de hardware e firmware.
-
+
Séries em destaque
+
O essencial de cada família, antes de abrir o datasheet.
-
-
-
-
Abrir Guia de 30+ Erros
+
+ Índice completo
+
-
- {commonErrors.map((err) => (
+
+ {featured.map(([key, serie]) => (
-
-
- {err.title}
+
+
+
{key}
+
{serie.arquitetura}
-
- Causa provável:
- {err.cause}
-
-
- Solução rápida:
- {err.solution}
-
+
+
+
-
-
-
Ver diagnóstico completo
-
+
{serie.descricao}
+
+
{serie.frequencia}
+
+ Ficha
+
+
))}
@@ -470,71 +176,166 @@ export default function Home() {
- {/* Seção 5: Ciência Embarcada & Curadoria */}
-
-
- {/* Background soft glow decoration */}
-
+
+
+
+
+
+
+
+ Livro novo
+
+
+ {SITE.livroTitulo}
+
+
{SITE.livroSubtitulo}
+
+ Um percurso completo pelo hardware e pelo firmware do ESP32: da arquitetura do chip
+ à implantação de produtos conectados. São 12 partes e 78 capítulos sobre Arduino,
+ ESP-IDF, FreeRTOS, Wi-Fi, BLE, Thread e Matter, periféricos, depuração, baixo consumo
+ e segurança.
+
-
-
-
-
-
-
-
-
- Acesse artigos profundos sobre programação de microcontroladores, design de hardware, Internet das Coisas (IoT), laboratórios práticos e projetos reais em nosso blog parceiro oficial.
-
+
+ 12 partes
+ ·
+ 78 capítulos
+ ·
+ ISBN {SITE.livroIsbn}
+
-
- Visitar Blog Ciência Embarcada
-
-
-
+
+
- {/* Disclaimer Oficial */}
-
-
-
-
-
- Plataforma independente e não oficial: O ESPDocs é um projeto educativo que compila, filtra e expõe dados públicos e links de documentações oficiais fornecidas pela fabricante Espressif Systems. Para projetos industriais ou decisões de engenharia críticas, sempre consulte a documentação técnica nativa do fabricante.
-
+
-
+
+
+
+
+
Ferramentas
+
+ Comece pelo problema.
+
+
+ Cada ferramenta resolve uma etapa concreta do projeto, sem cadastro e sem rodeio.
+
+
+
+
+ {TOOLS.map((tool) => {
+ const Icon = tool.icon;
+ return (
+
+
+
+
+
{tool.title}
+
{tool.desc}
+
+ {tool.action}
+
+
+
+ );
+ })}
+
+
+
+
+
+
+
Diagnóstico
+
+ O primeiro boot não precisa virar noite.
+
+
+ Os erros mais comuns têm causa conhecida. O guia parte da mensagem no terminal e chega ao ajuste de hardware ou firmware.
+
+
+
+ Abrir os 30+ diagnósticos
+
+
+
+
+
+
+
+ boot.log
+
+ 4 exemplos de 30+
+
+
+ {ERRORS.map((item) => (
+
+ {item.code}
+ {item.cause}
+ {item.fix}
+
+ ))}
+
+
+
+
+
+
+
+
Curadoria independente
+
+ Feito para consultar no meio do projeto.
+
+
+ O ESPDocs organiza dados públicos da Espressif e aponta sempre para a fonte oficial.
+ Para decisões críticas, o datasheet e o Technical Reference Manual continuam sendo a palavra final.
+
+
+
+
+ Glossário
+
+
+ Sobre o projeto
+
+
+
+
+
);
-}
\ No newline at end of file
+}
diff --git a/app/seletor/page.jsx b/app/seletor/page.jsx
index ea958c2..ef76328 100644
--- a/app/seletor/page.jsx
+++ b/app/seletor/page.jsx
@@ -5,109 +5,101 @@ import Header from "@/components/Header";
import Footer from "@/components/Footer";
import Link from "next/link";
import seriesData from "@/public/series.json";
+import PageHero from "@/components/PageHero";
import {
- Wifi,
- Bluetooth,
- Network,
- Cpu,
- Bot,
- Monitor,
- Camera,
- Home as HomeIcon,
- Sliders,
- Cable,
- Battery,
- Zap,
+ Activity,
+ AlertTriangle,
+ ArrowRight,
+ ArrowUpRight,
+ Award,
Ban,
+ Battery,
+ BookOpen,
+ Bot,
+ Cable,
+ Check,
+ CheckCircle,
ChevronLeft,
ChevronRight,
- RotateCcw,
- Sparkles,
+ Cpu,
Edit2,
- CheckCircle,
- HelpCircle,
- Award,
- SlidersHorizontal,
- ExternalLink,
- ShoppingBag,
- BookOpen,
- ShieldCheck,
- KeyRound,
- Lock,
- Radio,
- Layers,
- Microchip,
- Activity,
- Flame,
- Check,
- X,
- AlertTriangle,
- Gauge,
- Volume2,
- Tv,
- HardDrive,
Filter,
- ArrowRight,
- Lightbulb,
- Info
+ HardDrive,
+ HelpCircle,
+ Home as HomeIcon,
+ KeyRound,
+ Layers,
+ Lock,
+ Monitor,
+ Network,
+ Radio,
+ RotateCcw,
+ ShieldCheck,
+ ShoppingBag,
+ Sliders,
+ SlidersHorizontal,
+ Sparkles,
+ Volume2,
+ Wifi,
+ Zap,
} from "lucide-react";
// Dynamic icon mapper for options and presets
const iconMap = {
// Categorias
- "category_smarthome":
,
- "category_multimedia":
,
- "category_ai_edge":
,
- "category_wearable":
,
- "category_industrial":
,
- "category_basic_iot":
,
+ "category_smarthome":
,
+ "category_multimedia":
,
+ "category_ai_edge":
,
+ "category_wearable":
,
+ "category_industrial":
,
+ "category_basic_iot":
,
// Conectividade
- "connectivity_wifi6_dualband":
,
- "connectivity_wifi6_mesh":
,
- "connectivity_mesh_only":
,
- "connectivity_wifi_ble":
,
- "connectivity_bt_classic_audio":
,
- "connectivity_ethernet":
,
- "connectivity_none_local":
,
+ "connectivity_wifi6_dualband":
,
+ "connectivity_wifi6_mesh":
,
+ "connectivity_mesh_only":
,
+ "connectivity_wifi_ble":
,
+ "connectivity_bt_classic_audio":
,
+ "connectivity_ethernet":
,
+ "connectivity_none_local":
,
// Alimentação
- "power_battery_critical":
,
- "power_ulp_coprocessor":
,
- "power_always_on":
,
+ "power_battery_critical":
,
+ "power_ulp_coprocessor":
,
+ "power_always_on":
,
// Periféricos / Hardware
- "hardware_mipi_multimedia":
,
- "hardware_usb_otg":
,
- "hardware_can_twai":
,
- "hardware_dac_audio":
,
- "hardware_many_gpios":
,
- "hardware_standard_sensors":
,
+ "hardware_mipi_multimedia":
,
+ "hardware_usb_otg":
,
+ "hardware_can_twai":
,
+ "hardware_dac_audio":
,
+ "hardware_many_gpios":
,
+ "hardware_standard_sensors":
,
// Memória
- "memory_heavy_psram":
,
- "memory_moderate_sram":
,
- "memory_minimal_cost":
,
+ "memory_heavy_psram":
,
+ "memory_moderate_sram":
,
+ "memory_minimal_cost":
,
// IA
- "ai_ai_vector":
,
- "ai_standard":
,
+ "ai_ai_vector":
,
+ "ai_standard":
,
// Segurança
- "security_key_manager_dpa":
,
- "security_hardware_ecc_ecdsa":
,
- "security_matter_ecdsa":
,
- "security_secure_boot":
,
- "security_standard_tls":
,
+ "security_key_manager_dpa":
,
+ "security_hardware_ecc_ecdsa":
,
+ "security_matter_ecdsa":
,
+ "security_secure_boot":
,
+ "security_standard_tls":
,
// Ecossistema
- "ecosystem_arduino_ready":
,
- "ecosystem_advanced_idf":
,
+ "ecosystem_arduino_ready":
,
+ "ecosystem_advanced_idf":
,
};
const getOptionIcon = (questionId, value) => {
const key = `${questionId}_${value}`;
- return iconMap[key] ||
;
+ return iconMap[key] ||
;
};
// 7 Preset Configurations for 1-click recommendations
@@ -115,9 +107,7 @@ const PRESETS = [
{
id: "preset_smarthome",
title: "Smart Home & Matter",
- icon: "🏠",
badge: "Automação 802.15.4",
- color: "from-teal-500/20 to-emerald-500/10 border-teal-500/30 text-teal-700 dark:text-teal-300",
description: "Wi-Fi 6, Thread/Zigbee e baixo consumo",
answers: {
category: "smarthome",
@@ -133,9 +123,7 @@ const PRESETS = [
{
id: "preset_hmi",
title: "Painel Touch & Telas",
- icon: "🎨",
badge: "Display / Câmera",
- color: "from-cyan-500/20 to-blue-500/10 border-cyan-500/30 text-cyan-700 dark:text-cyan-300",
description: "Display LCD/MIPI, PSRAM e alta taxa de quadros",
answers: {
category: "multimedia",
@@ -151,9 +139,7 @@ const PRESETS = [
{
id: "preset_ai",
title: "Edge AI & Visão",
- icon: "🧠",
badge: "Aceleração Vetorial",
- color: "from-pink-500/20 to-purple-500/10 border-pink-500/30 text-pink-700 dark:text-pink-300",
description: "Acelerador neural, câmera e alta memória",
answers: {
category: "ai_edge",
@@ -169,9 +155,7 @@ const PRESETS = [
{
id: "preset_wearable",
title: "Wearable a Bateria",
- icon: "🔋",
badge: "Deep Sleep <10µA",
- color: "from-green-500/20 to-emerald-500/10 border-green-500/30 text-green-700 dark:text-green-300",
description: "Eficiência extrema e rádio Bluetooth LE",
answers: {
category: "wearable",
@@ -187,9 +171,7 @@ const PRESETS = [
{
id: "preset_wifi6_5ghz",
title: "Wi-Fi 6 Dual-Band (5GHz)",
- icon: "📶",
badge: "2.4 & 5 GHz",
- color: "from-indigo-500/20 to-blue-500/10 border-indigo-500/30 text-indigo-700 dark:text-indigo-300",
description: "Banda de 5 GHz com menor interferência de RF",
answers: {
category: "smarthome",
@@ -205,9 +187,7 @@ const PRESETS = [
{
id: "preset_industrial",
title: "Industrial / CAN & Rede",
- icon: "🏭",
badge: "Ethernet & TWAI",
- color: "from-amber-500/20 to-orange-500/10 border-amber-500/30 text-amber-700 dark:text-amber-300",
description: "Barramento CAN/TWAI, Ethernet e alta robustez",
answers: {
category: "industrial",
@@ -223,9 +203,7 @@ const PRESETS = [
{
id: "preset_budget",
title: "IoT Básico de Baixo Custo",
- icon: "💸",
badge: "Ultra-Econômico",
- color: "from-emerald-500/20 to-teal-500/10 border-emerald-500/30 text-emerald-700 dark:text-emerald-300",
description: "Menor custo por unidade para sensores simples",
answers: {
category: "basic_iot",
@@ -857,951 +835,493 @@ export default function Seletor() {
const topThree = recommendations.slice(0, 3);
const otherRecommendations = recommendations.slice(3);
- // Rearrange podium for premium visual styling: 2nd Place | 1st Place | 3rd Place
- const desktopPodium = useMemo(() => {
- if (topThree.length < 2) return topThree;
- if (topThree.length === 2) return [topThree[1], topThree[0]];
- return [topThree[1], topThree[0], topThree[2]];
- }, [topThree]);
-
const progress = ((currentStep + 1) / questions.length) * 100;
+ const FILTROS = [
+ { key: "wifi6", label: "Wi-Fi 6 (802.11ax)" },
+ { key: "wifi5ghz", label: "Wi-Fi dual-band (5 GHz)" },
+ { key: "bluetoothClassic", label: "Bluetooth Clássico (A2DP)" },
+ { key: "leAudio", label: "Bluetooth LE Audio" },
+ { key: "matter", label: "Matter / 802.15.4" },
+ { key: "ethernet", label: "Ethernet MAC" },
+ { key: "aiAccel", label: "Aceleração vetorial de IA" },
+ { key: "psram", label: "Suporte a PSRAM" },
+ { key: "mipiDisplayCamera", label: "Telas e câmeras (MIPI/RGB)" },
+ { key: "usbOTG", label: "USB nativo OTG" },
+ { key: "canBus", label: "Barramento CAN / TWAI" },
+ { key: "dac", label: "Conversor DAC" },
+ { key: "touch", label: "Touch capacitivo" },
+ { key: "arduinoReady", label: "Suporte ao Arduino Core" },
+ { key: "eccHardware", label: "Curvas elípticas (ECC)" },
+ { key: "ecdsaHardware", label: "Assinatura ECDSA em hardware" },
+ { key: "keyManager", label: "Key Manager em hardware" },
+ { key: "secureBootV2", label: "Secure Boot V2" },
+ ];
+
+ const filtrosAtivos = FILTROS.filter(({ key }) => filters[key]).length;
+ const limparFiltros = () => setFilters(Object.fromEntries(FILTROS.map(({ key }) => [key, false])));
+
+ const ROTULOS_ETAPA = {
+ category: "Aplicação",
+ connectivity: "Conectividade",
+ power: "Alimentação",
+ hardware: "Periféricos",
+ memory: "Memória",
+ ai: "Inteligência artificial",
+ security: "Segurança",
+ ecosystem: "Ambiente de desenvolvimento",
+ };
+
+ // O texto das perguntas vem numerado ("1. Qual é..."); a interface já mostra a etapa.
+ const semNumero = (texto) => texto.replace(/^\d+\.\s*/, "");
+
+ const pergunta = questions[currentStep];
+
return (
-
+
-
-
- {/* Top Hero Section */}
-
-
- 🎯 Seletor IoT Inteligente Espressif
-
-
-
- Qual ESP32 Escolher?
-
-
-
- Descubra o modelo ideal de ESP32 para o seu projeto com base em conectividade (Wi-Fi 6, Dual-Band, Thread/Matter), periféricos, segurança e consumo elétrico.
-
-
- {/* Mode Switcher: Wizard vs Direct Parametric Filter */}
-
-
{ setMode("wizard"); }}
- className={`flex items-center gap-2 px-5 py-2 rounded-xl text-xs font-extrabold tracking-wide uppercase transition-all duration-300 cursor-pointer ${
- mode === "wizard"
- ? "bg-white dark:bg-slate-800 text-purple-600 dark:text-purple-350 shadow-md scale-[1.02]"
- : "text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white"
- }`}
- >
-
- Assistente Guiado
+
+
+
+ setMode("wizard")} aria-pressed={mode === "wizard"} className="chip-btn">
+
+ Assistente guiado
-
- { setMode("filter"); }}
- className={`flex items-center gap-2 px-5 py-2 rounded-xl text-xs font-extrabold tracking-wide uppercase transition-all duration-300 cursor-pointer ${
- mode === "filter"
- ? "bg-white dark:bg-slate-800 text-purple-600 dark:text-purple-350 shadow-md scale-[1.02]"
- : "text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white"
- }`}
- >
-
- Filtro Paramétrico
+ setMode("filter")} aria-pressed={mode === "filter"} className="chip-btn">
+
+ Filtro por recursos
-
+
- {/* 1-CLICK PRESETS BAR */}
- {mode === "wizard" && !showResults && (
-
-
-
-
- Atalhos Rápidos por Aplicação (1 Clique)
-
-
+ {/* Assistente: pergunta */}
+ {mode === "wizard" && !showResults && !showSummary && (
+
+
+
+
+ Etapa {currentStep + 1} de {questions.length}
+ · {ROTULOS_ETAPA[pergunta.id]}
+
+
{Math.round(progress)}%
+
+
-
- {PRESETS.map((preset) => {
- const isSelected = activePreset === preset.id;
- return (
-
applyPreset(preset)}
- className={`p-3 rounded-2xl border text-left transition-all duration-300 cursor-pointer hover:shadow-md active:scale-95 flex flex-col justify-between ${
- isSelected
- ? "bg-purple-500/15 border-purple-500 shadow-sm"
- : "bg-white/80 dark:bg-slate-900/40 hover:bg-slate-50 dark:hover:bg-slate-900/80 border-slate-200 dark:border-slate-800/80"
- }`}
- >
-
-
{preset.icon}
-
- {preset.title}
-
-
- {preset.badge}
-
-
-
-
- );
- })}
+
{semNumero(pergunta.question)}
+
{pergunta.description}
+
+
+ {pergunta.options.map((option) => {
+ const isSelected = currentAnswer === option.value;
+ return (
+ handleAnswer(pergunta.id, option.value)}
+ className={`flex min-h-14 items-center gap-4 rounded-xl border p-3.5 text-left transition-colors sm:p-4 ${
+ isSelected ? "border-accent bg-accent-soft" : "border-line-strong bg-surface hover:bg-surface-2"
+ }`}
+ >
+
+ {getOptionIcon(pergunta.id, option.value)}
+
+ {option.label}
+ {isSelected && }
+
+ );
+ })}
+
+
+
+
+
+ Voltar
+
+
+
+ Recomeçar
+
+
+ {currentStep === questions.length - 1 ? "Revisar respostas" : "Avançar"}
+
+
+
)}
- {/* WIZARD MODE: PROGRESS BAR */}
- {mode === "wizard" && !showResults && !showSummary && (
-
-
-
- Etapa {currentStep + 1} de {questions.length} • {questions[currentStep].id.toUpperCase()}
-
-
- {Math.round(progress)}% Concluído
-
-
-
-
- )}
-
- {/* WIZARD STEP 1: Question Card */}
- {mode === "wizard" && currentStep < questions.length && !showResults && !showSummary && (
-
-
-
- {questions[currentStep].question}
-
-
- {questions[currentStep].description}
-
-
-
-
- {questions[currentStep].options.map((option) => {
- const isSelected = currentAnswer === option.value;
- return (
-
handleAnswer(questions[currentStep].id, option.value)}
- className={`group flex items-center gap-3.5 text-left border rounded-2xl p-3.5 sm:p-4 transition-all duration-300 cursor-pointer hover:shadow-md active:scale-[0.99] ${
- isSelected
- ? "bg-purple-500/10 dark:bg-purple-400/10 border-purple-500 dark:border-purple-400 shadow-sm"
- : "bg-slate-50/70 dark:bg-slate-900/30 hover:bg-purple-500/5 dark:hover:bg-purple-400/5 border-slate-300 dark:border-slate-800/80 hover:border-purple-400/50"
- }`}
- >
- {/* Icon Badge */}
-
- {getOptionIcon(questions[currentStep].id, option.value)}
-
-
- {/* Label */}
-
- {option.label}
+ {/* Atalhos por aplicação */}
+ {mode === "wizard" && !showResults && !showSummary && currentStep === 0 && (
+
+ Ou comece por um perfil pronto
+ Aplica as oito respostas de uma vez e mostra o resultado. Você pode ajustar depois.
+
+ {PRESETS.map((preset) => (
+
applyPreset(preset)}
+ className="card card-interactive group flex items-start gap-4 p-4 text-left sm:p-5"
+ >
+
+ {iconMap[`category_${preset.answers.category}`] || }
+
+
+ {preset.title}
+ {preset.description}
+
+ Ver recomendação
+
-
- {/* Check Indicator */}
- {isSelected && (
-
- ✓
-
- )}
-
- );
- })}
+
+
+ ))}
-
- {/* Bottom Navigation Buttons */}
-
-
-
- Voltar
-
-
-
-
- Reiniciar
-
-
-
- {currentStep === questions.length - 1 ? "Revisar" : "Avançar"}
-
-
-
-
+
)}
- {/* WIZARD STEP 2: Review Summary */}
+ {/* Assistente: revisão */}
{mode === "wizard" && showSummary && !showResults && (
-
-
-
-
- Resumo das Especificações do Projeto
-
-
- Revise suas escolhas abaixo antes de calcular o ranking de compatibilidade com os dados do catálogo de silício.
-
-
-
- {/* Double Column summary cards */}
-
- {questions.map((question) => {
- const answer = answers[question.id];
- const selectedOption = question.options.find(opt => opt.value === answer);
-
- return (
-
-
-
- {question.id.toUpperCase()}
-
- {selectedOption && (
-
-
- {getOptionIcon(question.id, answer)}
-
-
- {selectedOption.label}
-
-
- )}
-
-
{
- const questionIndex = questions.findIndex(q => q.id === question.id);
- setCurrentStep(questionIndex);
- setCurrentAnswer(answers[question.id]);
- setShowSummary(false);
- }}
- className="p-2 border border-slate-300 dark:border-slate-800 hover:border-purple-500 bg-white dark:bg-slate-900 rounded-lg text-slate-400 hover:text-purple-600 transition-all cursor-pointer shrink-0"
- title="Editar esta resposta"
- >
-
-
-
- );
- })}
-
-
-
- {
- setCurrentStep(questions.length - 1);
- setCurrentAnswer(answers[questions[questions.length - 1].id]);
- setShowSummary(false);
- }}
- className="inline-flex items-center justify-center gap-2 bg-slate-200/70 dark:bg-slate-800 text-slate-700 dark:text-slate-300 px-5 py-2.5 rounded-xl font-bold text-xs uppercase tracking-wider hover:bg-slate-300 transition-all duration-300 cursor-pointer"
- >
-
- Voltar
-
-
- setShowResults(true)}
- className="inline-flex items-center justify-center gap-2 bg-gradient-to-r from-blue-600 via-purple-600 to-pink-600 text-white px-8 py-2.5 rounded-xl font-bold text-xs uppercase tracking-wider shadow-lg hover:shadow-purple-500/20 hover:-translate-y-0.5 active:translate-y-0 transition-all duration-300 cursor-pointer"
- >
-
- Verificar Recomendações
-
-
-
- )}
-
- {/* WIZARD STEP 3: Results Podium & Detailed Cards */}
- {mode === "wizard" && showResults && recommendations.length > 0 && (
-
-
- {/* Top Banner with Reset / Re-edit Action */}
-
-
-
-
-
-
-
- Recomendações Geradas com Sucesso
-
-
- {recommendations.length} modelos compatíveis encontrados no catálogo
-
-
-
-
-
- {
- setShowResults(false);
- setShowSummary(true);
- }}
- className="flex-1 sm:flex-none inline-flex items-center justify-center gap-1.5 px-4 py-2 rounded-xl text-xs font-bold bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-slate-700 dark:text-slate-300 transition-colors cursor-pointer"
- >
-
- Ajustar Respostas
-
-
-
-
- Novo Quiz
-
-
-
-
- {/* Desktop Podium Layout */}
-
-
-
- {desktopPodium.map((rec) => {
- const seriesKey = rec.key;
- const serie = seriesData[seriesKey];
- const matchReasons = allReasons[seriesKey] || [];
- const chipAlerts = allAlerts[seriesKey] || [];
- const rankIdx = topThree.findIndex(item => item.key === seriesKey);
-
- const rankConfig = [
- {
- badge: "🥇 1º Recomendado",
- cardClass: "border-amber-500/80 dark:border-amber-400 bg-linear-to-b from-amber-500/10 via-white dark:via-slate-900 to-transparent shadow-[0_0_40px_rgba(245,158,11,0.2)] min-h-[620px] z-10 scale-[1.03]",
- badgeClass: "text-amber-800 bg-amber-500/15 border-amber-500/30 dark:text-amber-300 dark:bg-amber-500/20",
- scoreClass: "text-amber-600 dark:text-amber-400"
- },
- {
- badge: "🥈 2º Recomendado",
- cardClass: "border-slate-300 dark:border-slate-800 bg-linear-to-b from-slate-500/10 via-white dark:via-slate-900 to-transparent min-h-[580px] opacity-95 hover:opacity-100",
- badgeClass: "text-slate-700 bg-slate-500/15 border-slate-500/30 dark:text-slate-300 dark:bg-slate-800",
- scoreClass: "text-slate-600 dark:text-slate-300"
- },
- {
- badge: "🥉 3º Recomendado",
- cardClass: "border-amber-800/30 dark:border-slate-800 bg-linear-to-b from-amber-800/10 via-white dark:via-slate-900 to-transparent min-h-[580px] opacity-95 hover:opacity-100",
- badgeClass: "text-amber-900 bg-amber-800/15 border-amber-800/30 dark:text-amber-400 dark:bg-slate-800",
- scoreClass: "text-amber-700 dark:text-amber-450"
- }
- ][rankIdx];
+
+
+
Revise suas respostas
+
Confira as escolhas antes de calcular a compatibilidade com cada série.
+
+ {questions.map((question) => {
+ const answer = answers[question.id];
+ const selectedOption = question.options.find((opt) => opt.value === answer);
return (
-
- {/* Top Color Accent Line */}
-
-
-
-
-
-
- {rankConfig.badge}
-
-
-
-
-
{serie.icone}
-
- {seriesKey}
-
-
- {serie.nome_completo}
-
-
-
- Afinidade:
-
- {rec.percentage}% ({rec.score} pts)
-
-
-
-
- {/* Match Advantages */}
-
-
-
- Vantagens para seu Projeto
-
-
- {matchReasons.slice(0, 3).map((reason, idx) => (
-
-
- {reason}
-
- ))}
-
-
-
- {/* Alerts / Limitations */}
- {chipAlerts.length > 0 && (
-
-
-
- Pontos de Atenção
-
-
- {chipAlerts.slice(0, 2).map((alert, idx) => (
-
- •
- {alert}
-
- ))}
-
-
- )}
-
-
- {/* Quick Spec Badges */}
-
-
- Arquitetura
- {serie.arquitetura.split(" ")[0]} ({serie.nucleos})
-
-
- Clock Máximo
- {serie.frequencia}
-
-
- GPIOs
- {serie.gpio} pinos
-
-
- Memória SRAM
- {serie.memoria_sram}
-
-
+
+
+
{ROTULOS_ETAPA[question.id]}
+ {selectedOption &&
{selectedOption.label}
}
-
- {/* Ver Documentação CTA Button */}
-
-
- Ver Documentação Completa
-
-
-
-
+
{
+ setCurrentStep(questions.findIndex((q) => q.id === question.id));
+ setCurrentAnswer(answers[question.id]);
+ setShowSummary(false);
+ }}
+ className="grid h-11 w-11 shrink-0 place-items-center rounded-full text-muted transition-colors hover:bg-surface hover:text-accent"
+ aria-label={`Editar resposta: ${ROTULOS_ETAPA[question.id]}`}
+ >
+
+
+
);
})}
+
+
+
+ {
+ setCurrentStep(questions.length - 1);
+ setCurrentAnswer(answers[questions[questions.length - 1].id]);
+ setShowSummary(false);
+ }}
+ className="btn btn-ghost"
+ >
+
+ Voltar
+
+ setShowResults(true)} className="btn btn-primary">
+
+ Ver recomendações
+
+
+
+
+ )}
+
+ {/* Assistente: resultado */}
+ {mode === "wizard" && showResults && recommendations.length > 0 && (
+
+
+
+
Suas recomendações
+
{recommendations.length} modelos compatíveis, do mais ao menos indicado.
+
+
+ { setShowResults(false); setShowSummary(true); }} className="btn btn-ghost btn-sm">
+
+ Ajustar respostas
+
+
+
+ Recomeçar
+
- {/* Mobile Stacked Podium Layout */}
-
-
- Ranking dos Modelos Recomendados
-
-
+
{topThree.map((rec, index) => {
const seriesKey = rec.key;
const serie = seriesData[seriesKey];
const matchReasons = allReasons[seriesKey] || [];
const chipAlerts = allAlerts[seriesKey] || [];
- const mobileBadges = ["🥇 1ª Recomendação", "🥈 2ª Recomendação", "🥉 3ª Recomendação"];
- const isFirst = index === 0;
-
+ const primeiro = index === 0;
return (
-
-
-
-
-
-
- {mobileBadges[index]}
-
-
- Afinidade: {rec.percentage}%
-
-
-
-
-
{serie.icone}
-
-
{seriesKey}
-
{serie.descricao}
-
-
-
-
- {matchReasons.slice(0, 3).map((reason, idx) => (
-
-
- {reason}
-
- ))}
-
-
- {chipAlerts.length > 0 && (
-
- {chipAlerts.slice(0, 2).map((alert, idx) => (
-
- ))}
-
- )}
+
+
+
+ {primeiro && }
+ {index + 1}ª recomendação
+
+
+ Afinidade {rec.percentage}%
+
-
- Ver Detalhes do Chip
+ {seriesKey}
+ {serie.descricao}
+
+
+
+ Por que combina
+
+ {matchReasons.slice(0, 3).map((reason) => (
+
+
+ {reason}
+
+ ))}
+
+
+ {chipAlerts.length > 0 && (
+
+
+
+ Pontos de atenção
+
+
+ {chipAlerts.slice(0, 2).map((alert) => (
+ {alert}
+ ))}
+
+
+ )}
+
+
+ {[
+ ["Arquitetura", serie.arquitetura.split(" ")[0]],
+ ["Clock", serie.frequencia],
+ ["GPIO", `${serie.gpio} pinos`],
+ ["SRAM", serie.memoria_sram],
+ ].map(([rotulo, valor]) => (
+
+
{rotulo}
+ {valor}
+
+ ))}
+
+
+
+ Abrir a ficha do {seriesKey}
+
-
+
);
})}
-
+
- {/* Side-by-Side Comparison of Top Recommendations */}
-
-
+
+
-
- Comparativo Rápido dos Finalistas
-
-
- Compare as principais especificações técnicas lado a lado
-
+
Os finalistas lado a lado
+
As principais especificações das três primeiras recomendações.
-
-
t.key).join(",")}`}
- className="inline-flex items-center gap-1.5 text-xs font-bold text-purple-600 dark:text-purple-400 hover:text-purple-700 underline"
- >
-
Abrir Comparador Completo
-
+
t.key).join(",")}`} className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent hover:text-accent-strong">
+ Abrir no comparador completo
+
-
-
+
+
+ Comparação das três séries recomendadas
-
- Parâmetro
- {topThree.map(rec => (
-
- {rec.key}
-
+
+ Parâmetro
+ {topThree.map((rec) => (
+ {rec.key}
))}
-
-
- Arquitetura
- {topThree.map(rec => (
- {seriesData[rec.key].arquitetura}
- ))}
-
-
- Clock
- {topThree.map(rec => (
- {seriesData[rec.key].frequencia}
- ))}
-
-
- Wi-Fi
- {topThree.map(rec => (
- {seriesData[rec.key].wifi}
- ))}
-
-
- Bluetooth
- {topThree.map(rec => (
- {seriesData[rec.key].bluetooth}
- ))}
-
-
- Matter / 802.15.4
- {topThree.map(rec => (
- {seriesData[rec.key].matter === "Sim" ? "✅ Sim" : "❌ Não"}
- ))}
-
-
- Aceleração IA
- {topThree.map(rec => (
- {seriesData[rec.key].aceleradores_ia ? "⚡ Vetorial" : "—"}
- ))}
-
-
- PSRAM Externa
- {topThree.map(rec => (
- {seriesData[rec.key].psram_externa || "Não"}
- ))}
-
-
- Arduino Core
- {topThree.map(rec => (
- {String(seriesData[rec.key].arduino_core).startsWith("Sim") ? "✅ Suportado" : "⚠️ Apenas ESP-IDF"}
- ))}
-
+
+ {[
+ ["Arquitetura", (c) => c.arquitetura],
+ ["Clock", (c) => c.frequencia],
+ ["Wi-Fi", (c) => c.wifi],
+ ["Bluetooth", (c) => c.bluetooth],
+ ["Matter / 802.15.4", (c) => (c.matter === "Sim" ? "Sim" : "Não")],
+ ["Aceleração de IA", (c) => (c.aceleradores_ia ? "Vetorial" : "Não")],
+ ["PSRAM externa", (c) => c.psram_externa || "Não"],
+ ["Arduino Core", (c) => (String(c.arduino_core).startsWith("Sim") ? "Suportado" : "Só ESP-IDF")],
+ ].map(([rotulo, obter]) => (
+
+ {rotulo}
+ {topThree.map((rec) => (
+ {obter(seriesData[rec.key])}
+ ))}
+
+ ))}
- {/* Other Options Grid */}
{otherRecommendations.length > 0 && (
-
-
- Outras Alternativas Avaliadas
-
-
- {otherRecommendations.map((rec) => {
- const seriesKey = rec.key;
- const serie = seriesData[seriesKey];
- return (
-
-
-
{serie.icone}
-
{seriesKey}
-
Afinidade: {rec.percentage}%
-
-
- Ver Detalhes
-
-
- );
- })}
-
+
+
Outras alternativas avaliadas
+
+ {otherRecommendations.map((rec) => (
+
+
+ {rec.key}
+ {rec.percentage}%
+
+
+ ))}
+
)}
-
- {/* Navigation Actions */}
-
-
-
- Comparador Completo
-
-
-
-
- Refazer com Outros Critérios
-
-
-
+
)}
- {/* WIZARD NO MATCH */}
{mode === "wizard" && showResults && recommendations.length === 0 && (
-
-
-
-
-
- Combinação Extremamente Específica
-
-
- Não foi possível encontrar um único microcontrolador ESP32 que atenda simultaneamente a todos os requisitos conflitantes. Tente flexibilizar um dos parâmetros ou refazer a seleção.
-
-
-
-
- Refazer Quiz
-
-
- )}
-
- {/* DIRECT PARAMETRIC FILTER MODE */}
- {mode === "filter" && (
-
- {/* Filter Controls Box */}
-
-
-
-
-
- Filtro Direto por Recursos de Hardware
-
-
- Marque os blocos de silício obrigatórios para visualizar instantaneamente os modelos compatíveis.
-
-
-
-
{
- setFilters({
- wifi6: false,
- wifi5ghz: false,
- bluetoothClassic: false,
- leAudio: false,
- matter: false,
- ethernet: false,
- aiAccel: false,
- psram: false,
- mipiDisplayCamera: false,
- usbOTG: false,
- canBus: false,
- dac: false,
- touch: false,
- arduinoReady: false,
- keyManager: false,
- eccHardware: false,
- ecdsaHardware: false,
- secureBootV2: false,
- });
- }}
- className="text-xs font-bold text-slate-500 hover:text-purple-600 flex items-center gap-1 cursor-pointer"
- >
-
- Limpar Filtros
+
+
+
+
Nenhum chip atende a todos os requisitos
+
+ Esta combinação tem critérios que nenhuma série cumpre ao mesmo tempo. Flexibilize uma das respostas e tente de novo.
+
+
+ { setShowResults(false); setShowSummary(true); }} className="btn btn-primary">
+
+ Ajustar respostas
+
+
+
+ Recomeçar
-
- {/* Filter Chips Grid */}
-
- {[
- { key: "wifi6", label: "Wi-Fi 6 (802.11ax)", icon:
},
- { key: "wifi5ghz", label: "Wi-Fi Dual-Band (5 GHz)", icon:
},
- { key: "bluetoothClassic", label: "Bluetooth Clássico (A2DP)", icon:
},
- { key: "leAudio", label: "Bluetooth LE Audio", icon:
},
- { key: "matter", label: "Matter / 802.15.4", icon:
},
- { key: "ethernet", label: "Ethernet MAC", icon:
},
- { key: "aiAccel", label: "Aceleração Vetorial IA", icon:
},
- { key: "psram", label: "Suporte a PSRAM", icon:
},
- { key: "mipiDisplayCamera", label: "Telas / Câmeras (MIPI/RGB)", icon:
},
- { key: "usbOTG", label: "USB Nativo OTG", icon:
},
- { key: "canBus", label: "Barramento CAN / TWAI", icon:
},
- { key: "dac", label: "Conversor DAC", icon:
},
- { key: "touch", label: "Touch Capacitivo", icon:
},
- { key: "arduinoReady", label: "Suporte Arduino Core", icon:
},
- { key: "eccHardware", label: "Curvas Elípticas ECC", icon:
},
- { key: "ecdsaHardware", label: "Assinatura ECDSA Hardware", icon:
},
- { key: "keyManager", label: "Key Manager Hardware", icon:
},
- { key: "secureBootV2", label: "Secure Boot V2", icon:
},
- ].map(({ key, label, icon }) => {
- const isActive = filters[key];
- return (
-
setFilters(prev => ({ ...prev, [key]: !prev[key] }))}
- className={`flex items-center gap-2 p-2.5 rounded-xl border text-left text-xs font-bold transition-all duration-200 cursor-pointer ${
- isActive
- ? "bg-purple-500/15 border-purple-500 text-purple-700 dark:text-purple-300 shadow-xs"
- : "bg-slate-50 dark:bg-slate-900/40 border-slate-200 dark:border-slate-800 text-slate-700 dark:text-slate-300 hover:border-slate-400"
- }`}
- >
- {icon}
- {label}
- {isActive && }
-
- );
- })}
-
-
- {/* Results Grid */}
-
-
-
- Modelos Compatíveis ({filteredChips.length} de {Object.keys(seriesData).length})
-
-
-
- {filteredChips.length > 0 ? (
-
- {filteredChips.map(([seriesKey, serie]) => (
-
-
-
-
-
-
-
{serie.icone}
-
-
- {seriesKey}
-
-
- {serie.nome_completo}
-
-
-
-
-
-
- {serie.descricao}
-
-
- {/* Badges */}
-
-
- Wi-Fi / BT:
- {serie.wifi}
-
-
- Matter/Thread:
- {serie.matter === "Sim" ? "✅ Sim" : "❌ Não"}
-
-
- Clock:
- {serie.frequencia}
-
-
- GPIOs:
- {serie.gpio} pinos
-
-
- PSRAM:
- {serie.psram_externa || "Não"}
-
-
-
-
-
-
- Ver Detalhes do Chip
-
-
-
-
- ))}
-
- ) : (
-
-
-
-
-
- Nenhum modelo atende a todos os filtros selecionados
-
-
- Experimente desmarcar alguns dos filtros mais restritivos (ex: Wi-Fi 5 GHz + Ethernet MAC ao mesmo tempo).
-
-
- )}
-
-
+
)}
- {/* FOOTER LINKS / NEXT STEPS */}
-
-
- Próximos Passos de Desenvolvimento
-
-
-
-
-
+ {/* Filtro por recursos */}
+ {mode === "filter" && (
+
+
+
+
+
Marque o que é obrigatório
+
Só ficam as séries que têm todos os recursos marcados.
+
+ {filtrosAtivos > 0 && (
+
+
+ Limpar {filtrosAtivos} {filtrosAtivos === 1 ? "filtro" : "filtros"}
+
+ )}
-
- Catálogo de Placas
-
-
- Explore placas de desenvolvimento oficiais (DevKit, NodeMCU) e módulos com links seguros e pinouts.
-
-
-
-
-
+
+ {FILTROS.map(({ key, label }) => (
+ setFilters((prev) => ({ ...prev, [key]: !prev[key] }))} aria-pressed={filters[key]} className="chip-btn">
+ {filters[key] && }
+ {label}
+
+ ))}
-
- Comparador Lado a Lado
-
-
- Compare lado a lado matrizes completas de frequência, GPIOs, canais ADC/DAC, consumos e pinouts.
-
-
+
-
-
-
+
+ {filteredChips.length} de {Object.keys(seriesData).length} séries compatíveis
+
+
+ {filteredChips.length > 0 ? (
+
+ {filteredChips.map(([seriesKey, serie]) => (
+
+
{seriesKey}
+
{serie.descricao}
+
+ {[
+ ["Wi-Fi", serie.wifi],
+ ["Matter / Thread", serie.matter === "Sim" ? "Sim" : "Não"],
+ ["Clock", serie.frequencia],
+ ["GPIO", `${serie.gpio} pinos`],
+ ["PSRAM", serie.psram_externa || "Não"],
+ ].map(([rotulo, valor]) => (
+
+
{rotulo}
+ {valor}
+
+ ))}
+
+
+ Abrir ficha
+
+
+
+ ))}
-
- Glossário de Parâmetros
-
-
- Ficou em dúvida sobre termos de hardware como ULP, SRAM RTC, JTAG ou Matter? Consulte definições rápidas.
-
-
+ ) : (
+
+
+
Nenhuma série atende a todos os filtros
+
Desmarque os recursos mais restritivos, como Wi-Fi 5 GHz junto com Ethernet MAC.
+
Limpar filtros
+
+ )}
+
+ )}
+
+
+ Próximos passos
+
+ {[
+ { href: "/catalogo", icon: ShoppingBag, titulo: "Catálogo de placas", texto: "DevKits e módulos reais, com pinagem e link para a loja." },
+ { href: "/comparacao", icon: SlidersHorizontal, titulo: "Comparador lado a lado", texto: "Frequência, GPIOs, ADC/DAC, consumo e segurança em uma tabela." },
+ { href: "/glossario", icon: BookOpen, titulo: "Glossário técnico", texto: "Dúvida sobre ULP, SRAM RTC, JTAG ou Matter? Veja as definições." },
+ ].map(({ href, icon: Icon, titulo, texto }) => (
+
+
+
+
+
{titulo}
+
{texto}
+
+ Abrir
+
+
+
+ ))}
diff --git a/app/series/[key]/page.jsx b/app/series/[key]/page.jsx
index e2ed056..6c6402f 100644
--- a/app/series/[key]/page.jsx
+++ b/app/series/[key]/page.jsx
@@ -3,57 +3,31 @@ import Link from "next/link";
import Image from "next/image";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
+import PageHero from "@/components/PageHero";
import { notFound } from "next/navigation";
import seriesData from "@/public/series.json";
import ConnectionsDiagram from "@/components/ConnectionsDiagram";
import SeriesTabMenu from "@/components/SeriesTabMenu";
import PinRestrictions from "@/components/PinRestrictions";
-import {
- Cpu,
- Activity,
- Database,
- Wifi,
- Bluetooth,
- Battery,
- Sparkles,
- Home,
- ArrowRight,
- ArrowLeft,
- ExternalLink,
- Scale,
- FileText,
- Code,
- BookOpen,
+import {
AlertTriangle,
- Moon,
- Plug,
- Compass,
- Sliders,
- MapPin,
- Volume2,
- Fingerprint,
- MessageSquare,
- Repeat,
- Share2,
- Music,
- Clock,
- HardDrive,
- Folder,
- Thermometer,
- ChevronRight,
+ ArrowLeft,
+ ArrowRight,
+ ArrowUpRight,
+ Battery,
+ BookOpen,
+ Code,
+ Cpu,
+ Database,
+ ExternalLink,
+ FileText,
Lightbulb,
- ShieldCheck,
- KeyRound,
- Lock,
- Hash,
- Spline,
- FileSignature,
- Dices,
- Waves,
- Bug,
- Box,
Package,
- Terminal
+ Scale,
+ ShieldCheck,
+ Sliders,
+ Thermometer,
+ Wifi,
} from "lucide-react";
export default async function SerieDetail({ params }) {
@@ -77,75 +51,75 @@ export default async function SerieDetail({ params }) {
const nextKey = currentIndex < seriesKeys.length - 1 ? seriesKeys[currentIndex + 1] : null;
const processadorSpecs = [
- { label: "Arquitetura", value: serie.arquitetura, icon:
},
- { label: "Núcleos", value: serie.nucleos, icon:
},
- { label: "Frequência", value: serie.frequencia, icon:
},
- { label: "Coprocessador ULP", value: serie.coprocessador_ulp || "Não", icon:
},
- { label: "Aceleradores IA", value: serie.aceleradores_ia || "Não", icon:
},
+ { label: "Arquitetura", value: serie.arquitetura },
+ { label: "Núcleos", value: serie.nucleos },
+ { label: "Frequência", value: serie.frequencia },
+ { label: "Coprocessador ULP", value: serie.coprocessador_ulp || "Não" },
+ { label: "Aceleradores IA", value: serie.aceleradores_ia || "Não" },
];
const seg = serie.seguranca || {};
const segurancaSpecs = [
- { label: "AES", value: seg.aes || "Não", icon:
},
- { label: "SHA", value: seg.sha || "Não", icon:
},
- { label: "RSA / MPI", value: seg.rsa || "Não", icon:
},
- { label: "ECC (curvas elípticas)", value: seg.ecc || "Não", icon:
},
- { label: "ECDSA em hardware", value: seg.ecdsa || "Não", icon:
},
- { label: "HMAC", value: seg.hmac || "Não", icon:
},
- { label: "Assinatura Digital (DS)", value: seg.assinatura_digital || "Não", icon:
},
- { label: "Key Manager", value: seg.key_manager || "Não", icon:
},
- { label: "Gerador aleatório (RNG)", value: seg.rng || "Não", icon:
},
- { label: "Criptografia de flash", value: seg.criptografia_flash || "Não", icon:
},
- { label: "Secure Boot", value: seg.secure_boot || "Não", icon:
},
- { label: "Proteção contra DPA", value: seg.protecao_dpa || "Não", icon:
},
+ { label: "AES", value: seg.aes || "Não" },
+ { label: "SHA", value: seg.sha || "Não" },
+ { label: "RSA / MPI", value: seg.rsa || "Não" },
+ { label: "ECC (curvas elípticas)", value: seg.ecc || "Não" },
+ { label: "ECDSA em hardware", value: seg.ecdsa || "Não" },
+ { label: "HMAC", value: seg.hmac || "Não" },
+ { label: "Assinatura Digital (DS)", value: seg.assinatura_digital || "Não" },
+ { label: "Key Manager", value: seg.key_manager || "Não" },
+ { label: "Gerador aleatório (RNG)", value: seg.rng || "Não" },
+ { label: "Criptografia de flash", value: seg.criptografia_flash || "Não" },
+ { label: "Secure Boot", value: seg.secure_boot || "Não" },
+ { label: "Proteção contra DPA", value: seg.protecao_dpa || "Não" },
];
const ferramentasSpecs = [
- { label: "ESP-IDF mínimo", value: serie.esp_idf_minimo || "Não informado", icon:
},
- { label: "Core Arduino", value: serie.arduino_core || "Não informado", icon:
},
- { label: "Depuração", value: serie.depuracao || "Não informado", icon:
},
- { label: "Encapsulamento", value: serie.encapsulamento || "Não informado", icon:
},
- { label: "Módulos disponíveis", value: serie.modulos || "Não informado", icon:
},
+ { label: "ESP-IDF mínimo", value: serie.esp_idf_minimo || "Não informado" },
+ { label: "Core Arduino", value: serie.arduino_core || "Não informado" },
+ { label: "Depuração", value: serie.depuracao || "Não informado" },
+ { label: "Encapsulamento", value: serie.encapsulamento || "Não informado" },
+ { label: "Módulos disponíveis", value: serie.modulos || "Não informado" },
];
const conectividadeSpecs = [
- { label: "Wi-Fi", value: serie.wifi || "Não", icon:
},
- { label: "Bluetooth", value: serie.bluetooth || "Não", icon:
},
- { label: "Zigbee/Thread", value: serie.zigbee_thread || "Não", icon:
},
- { label: "Matter", value: serie.matter || "Não", icon:
},
- { label: "Ethernet", value: serie.ethernet || serie.ethernet_mac || "Não", icon:
},
- { label: "CAN", value: serie.can || "Não", icon:
},
+ { label: "Wi-Fi", value: serie.wifi || "Não" },
+ { label: "Bluetooth", value: serie.bluetooth || "Não" },
+ { label: "Zigbee/Thread", value: serie.zigbee_thread || "Não" },
+ { label: "Matter", value: serie.matter || "Não" },
+ { label: "Ethernet", value: serie.ethernet || serie.ethernet_mac || "Não" },
+ { label: "CAN", value: serie.can || "Não" },
];
const memoriaSpecs = [
- { label: "SRAM", value: serie.memoria_sram, icon:
},
- { label: "SRAM RTC", value: serie.memoria_sram_rtc, icon:
},
- { label: "ROM", value: serie.memoria_rom, icon:
},
- { label: "Flash Externa", value: serie.flash_externa, icon:
},
- { label: "PSRAM Externa", value: serie.psram_externa || "Não", icon:
},
+ { label: "SRAM", value: serie.memoria_sram },
+ { label: "SRAM RTC", value: serie.memoria_sram_rtc },
+ { label: "ROM", value: serie.memoria_rom },
+ { label: "Flash Externa", value: serie.flash_externa },
+ { label: "PSRAM Externa", value: serie.psram_externa || "Não" },
];
const perifericos = [
- { label: "GPIO", value: serie.gpio, icon:
},
- { label: "ADC", value: serie.adc, icon:
},
- { label: "DAC", value: serie.dac || "Não", icon:
},
- { label: "Touch", value: serie.touch || "Não", icon:
},
- { label: "UART", value: serie.uart, icon:
},
- { label: "SPI", value: serie.spi, icon:
},
- { label: "I2C", value: serie.i2c, icon:
},
- { label: "I2S", value: serie.i2s, icon:
},
- { label: "PWM", value: serie.pwm, icon:
},
- { label: "USB", value: serie.usb || "Não", icon:
},
+ { label: "GPIO", value: serie.gpio },
+ { label: "ADC", value: serie.adc },
+ { label: "DAC", value: serie.dac || "Não" },
+ { label: "Touch", value: serie.touch || "Não" },
+ { label: "UART", value: serie.uart },
+ { label: "SPI", value: serie.spi },
+ { label: "I2C", value: serie.i2c },
+ { label: "I2S", value: serie.i2s },
+ { label: "PWM", value: serie.pwm },
+ { label: "USB", value: serie.usb || "Não" },
];
const interfacesEspeciais = [];
- if (serie.lcd) interfacesEspeciais.push({ label: "LCD", value: serie.lcd, icon:
});
- if (serie.camera) interfacesEspeciais.push({ label: "Câmera", value: serie.camera, icon:
});
- if (serie.mipi_csi) interfacesEspeciais.push({ label: "MIPI CSI", value: serie.mipi_csi, icon:
});
- if (serie.mipi_dsi) interfacesEspeciais.push({ label: "MIPI DSI", value: serie.mipi_dsi, icon:
});
- if (serie.sdio) interfacesEspeciais.push({ label: "SDIO", value: serie.sdio, icon:
});
- if (serie.sensor_hall) interfacesEspeciais.push({ label: "Sensor Hall", value: serie.sensor_hall, icon:
});
- if (serie.sensor_temperatura) interfacesEspeciais.push({ label: "Sensor Temperatura", value: serie.sensor_temperatura, icon:
});
+ if (serie.lcd) interfacesEspeciais.push({ label: "LCD", value: serie.lcd });
+ if (serie.camera) interfacesEspeciais.push({ label: "Câmera", value: serie.camera });
+ if (serie.mipi_csi) interfacesEspeciais.push({ label: "MIPI CSI", value: serie.mipi_csi });
+ if (serie.mipi_dsi) interfacesEspeciais.push({ label: "MIPI DSI", value: serie.mipi_dsi });
+ if (serie.sdio) interfacesEspeciais.push({ label: "SDIO", value: serie.sdio });
+ if (serie.sensor_hall) interfacesEspeciais.push({ label: "Sensor Hall", value: serie.sensor_hall });
+ if (serie.sensor_temperatura) interfacesEspeciais.push({ label: "Sensor Temperatura", value: serie.sensor_temperatura });
// Consumo de Energia
const consumoEnergia = [];
@@ -168,361 +142,261 @@ export default async function SerieDetail({ params }) {
consumoEnergia.push({
label: modoLabels[modo] || modo.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
- value: consumo,
- icon:
+ value: consumo
});
});
}
const tabs = [
{
- id: 'devboard',
- label: 'Placa de Desenvolvimento',
+ id: "devboard",
+ label: "Placa de desenvolvimento",
available: serie.placa,
content: (
-
-
-
- Kit de Desenvolvimento Oficial
-
-
-
-
-
- Placa de desenvolvimento padrão da Espressif Systems para a série {key}
+
+
Kit de desenvolvimento oficial
+
+
+
+
Placa padrão da Espressif para a série {key}.
+
+ ),
+ },
+ {
+ id: "connections",
+ label: "Diagrama de conexões",
+ available: conexoes.length > 0,
+ content: (
+
+
+
+
+
Este é o diagrama do chip, não da placa
+
+ O mapa mostra as conexões do silício do {key} . Cada placa de desenvolvimento
+ expõe só parte delas, ou as roteia de outra forma.
-
- )
- },
- {
- id: 'connections',
- label: 'Diagrama de Conexões',
- available: conexoes.length > 0,
- content: (
-
-
-
-
-
-
Diagrama do SoC (System on Chip)
-
- Este mapa refere-se exclusivamente às portas e conexões do silício bruto do microcontrolador {key} . As placas de desenvolvimento podem expor apenas uma parcela destas conexões, ou roteá-las de formas diferentes dependendo do circuito impresso.
-
-
-
+
+
+
-
-
-
- )
- }
+ ),
+ },
+ ];
+
+ const secoes = [
+ { id: "processador", titulo: "Processador", icon: Cpu, specs: processadorSpecs },
+ { id: "conectividade", titulo: "Conectividade", icon: Wifi, specs: conectividadeSpecs },
+ { id: "ferramentas", titulo: "Ferramentas e disponibilidade", icon: Package, specs: ferramentasSpecs },
+ { id: "seguranca", titulo: "Segurança", icon: ShieldCheck, specs: segurancaSpecs },
+ { id: "memoria", titulo: "Memória", icon: Database, specs: memoriaSpecs },
+ ...(consumoEnergia.length > 0
+ ? [{
+ id: "energia",
+ titulo: "Consumo de energia",
+ icon: Battery,
+ specs: consumoEnergia,
+ nota: "Valores típicos a 3,3 V e 25 °C. O consumo real depende do ciclo de trabalho do rádio e da frequência de clock configurada.",
+ }]
+ : []),
+ { id: "perifericos", titulo: "Periféricos", icon: Sliders, specs: perifericos },
+ ...(interfacesEspeciais.length > 0
+ ? [{ id: "interfaces", titulo: "Interfaces especiais", icon: Sliders, specs: interfacesEspeciais }]
+ : []),
+ {
+ id: "temperatura",
+ titulo: "Limites de temperatura",
+ icon: Thermometer,
+ specs: [
+ { label: "Operação", value: serie.temperatura_operacao },
+ { label: "Armazenamento", value: serie.temperatura_armazenamento },
+ ],
+ },
+ ];
+
+ const fatos = [
+ { label: "Processador", value: String(serie.nucleos).includes("2") ? "Dual-core" : "Single-core", detail: serie.frequencia },
+ { label: "SRAM", value: serie.memoria_sram, detail: `+ ${serie.memoria_sram_rtc} de SRAM RTC` },
+ { label: "GPIO", value: serie.gpio, detail: "pinos programáveis" },
+ { label: "Arquitetura", value: String(serie.arquitetura).includes("RISC-V") ? "RISC-V" : "Xtensa", detail: serie.arquitetura },
];
return (
-
+
+
+
+
-
-
- {/* Breadcrumb Navigation */}
-
-
-
-
Voltar para Séries
-
-
- {/* Top Info Banner Card */}
-
-
-
-
{serie.icone}
-
-
- {key}
-
-
{serie.nome_completo}
-
- {serie.arquitetura}
-
-
+
+
+ {serie.datasheet ? (
+
+
+ Datasheet oficial
+
+
+ ) : (
+
+
+ Datasheet ainda não publicado
+
+ )}
+ {serie.guia_de_programacao && (
+
+
+ Guia de programação
+
+
+ )}
+ {serie.manual_tecnico && (
+
+
+ Manual técnico
+
+
+ )}
+
+
+
+
+ {fatos.map((fato) => (
+
+
{fato.label}
+ {fato.value}
+ {fato.detail}
-
- {/* Links Block */}
-
-
-
-
- {serie.descricao}
-
-
- {serie.status_documentacao && (
-
-
-
-
-
Documentação preliminar
-
{serie.status_documentacao}
-
-
-
- )}
-
-
-
- {/* Primary Features Highlight Grid */}
-
-
-
PROCESSADOR
-
{serie.nucleos.includes("2") ? "Dual-Core" : "Single-Core"}
-
{serie.frequencia}
-
-
-
-
MEMÓRIA SRAM
-
{serie.memoria_sram}
-
+ {serie.memoria_sram_rtc} RTC SRAM
-
-
-
-
GPIO DISPONÍVEIS
-
{serie.gpio}
-
Pinos I/O programáveis
-
-
-
- {/* DevBoard and Connections Tabs */}
- {/* Ficha do produto e trilha de navegação para o Google. */}
-
-
-
-
-
-
-
- {/* Specs List Cards */}
-
-
} />
-
} />
-
} />
-
} />
-
} />
-
- {/* Power Consumption section */}
- {consumoEnergia.length > 0 && (
-
-
-
- Consumo de Energia
-
-
-
-
-
-
- Especificações típicas medidas sob 3.3V com 25°C de temperatura ambiente. O consumo absoluto depende ativamente do ciclo de trabalho (duty cycle) das interfaces Wi-Fi/Bluetooth e da frequência de clock configurada via software.
-
-
-
-
-
- {consumoEnergia.map((spec, index) => (
-
-
- {spec.icon}
-
-
-
{spec.label}
-
{spec.value}
-
-
- ))}
+ {serie.status_documentacao && (
+
+
+
+
Documentação preliminar
+
{serie.status_documentacao}
)}
-
-
} />
-
- {interfacesEspeciais.length > 0 && (
-
} />
- )}
-
-
}
- specs={[
- { label: "Operação", value: serie.temperatura_operacao, icon:
},
- { label: "Armazenamento", value: serie.temperatura_armazenamento, icon:
},
- ]}
- cor={serie.cor}
- />
+
+
+
- {/* DevBoard CTA Panel */}
-
-
+
+
+ {secoes.map((secao) => (
+
+ {secao.titulo}
+
+ ))}
+
-
-
-
-
- Hardware Fisico
-
-
- Pronto para começar a prototipar?
-
-
- Explore toda a nossa grade de placas de desenvolvimento de parceiros oficiais para a série {key}. Compare dimensões, conversores USB-serial e pinagens prontas para protoboards.
+
+ {secoes.map((secao) => (
+
+ ))}
+
+
+
+
+
+
+
Pronto para prototipar o {key}?
+
+ Veja as placas de desenvolvimento disponíveis para a série, com USB, sensores integrados e link para a loja oficial.
-
-
-
Ver Placas Disponíveis
-
+
+ Ver placas
+
-
- {/* Previous / Next Nav bars */}
-
- {previousKey ? (
-
-
-
-
Anterior
-
{previousKey}
-
+
+ {previousKey ? (
+
+
+
+ Série anterior
+ {previousKey}
+
+
+ ) : (
+
+ )}
+
+
+ Comparar séries
- ) : (
-
- )}
-
-
-
- Comparar Séries
-
-
- {nextKey ? (
-
-
-
-
- ) : (
-
- )}
-
+ {nextKey ? (
+
+
+ Próxima série
+ {nextKey}
+
+
+
+ ) : (
+
+ )}
+
+
@@ -530,27 +404,28 @@ export default async function SerieDetail({ params }) {
);
}
-function SpecSection({ title, specs, cor, icon }) {
+function SpecSection({ id, titulo, icon: Icon, specs, nota }) {
return (
-
-
- {icon}
- {title}
+
+
+
+
+
+ {titulo}
-
- {specs.map((spec, index) => (
-
-
- {spec.icon}
+ {nota &&
{nota}
}
+
+ {specs.map((spec) => {
+ const negativo = /^n[ãa]o\b/i.test(String(spec.value));
+ return (
+
+
{spec.label}
+ {spec.value}
-
-
{spec.label}
-
{spec.value}
-
-
- ))}
-
-
+ );
+ })}
+
+
);
}
diff --git a/app/series/page.jsx b/app/series/page.jsx
index b93ce9a..bd02880 100644
--- a/app/series/page.jsx
+++ b/app/series/page.jsx
@@ -1,248 +1,154 @@
"use client";
+import { useMemo, useState } from "react";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
+import PageHero from "@/components/PageHero";
import Link from "next/link";
import seriesData from "@/public/series.json";
-import {
- Cpu,
- Activity,
- Database,
- Wifi,
- Bluetooth,
- Battery,
- Sparkles,
- Home,
+import {
+ AlertTriangle,
ArrowRight,
- Scale,
+ ArrowUpRight,
Compass,
+ Home,
+ Scale,
ShieldCheck,
- AlertTriangle
+ Sparkles,
} from "lucide-react";
+const FILTROS = [
+ { id: "todas", rotulo: "Todas", testa: () => true },
+ { id: "riscv", rotulo: "RISC-V", testa: (s) => s.arquitetura.includes("RISC-V") },
+ { id: "xtensa", rotulo: "Xtensa", testa: (s) => !s.arquitetura.includes("RISC-V") },
+ { id: "matter", rotulo: "Matter", testa: (s) => s.matter === "Sim" },
+ { id: "ia", rotulo: "Aceleração de IA", testa: (s) => Boolean(s.aceleradores_ia) },
+];
+
+/** Marcador de segurança: o recurso que diferencia a série, não o que todas têm. */
+function nivelSeguranca(serie) {
+ const seg = serie.seguranca || {};
+ if (seg.key_manager === "Sim") return "Key Manager";
+ if (String(seg.ecdsa).startsWith("Sim")) return "ECDSA em hardware";
+ if (String(seg.ecc).startsWith("Sim")) return "ECC em hardware";
+ if (String(seg.aes).startsWith("AES")) return "AES + SHA";
+ return "Só SHA";
+}
+
+const sim = (v) => !String(v).includes("Não");
+
export default function Series() {
- const series = Object.entries(seriesData);
+ const [filtro, setFiltro] = useState("todas");
+ const todas = Object.entries(seriesData);
+ const ativo = FILTROS.find((f) => f.id === filtro);
+ const series = useMemo(() => todas.filter(([, s]) => ativo.testa(s)), [todas, ativo]);
return (
-
+
-
-
- {/* Top Section */}
-
-
-
-
Explore o Ecossistema ESP32
+
+
+
+
+
+ {FILTROS.map((f) => (
+ setFiltro(f.id)} className="chip-btn">
+ {f.rotulo}
+
+ ))}
-
-
- Séries ESP32
-
-
-
- Conheça toda a família de microcontroladores da Espressif. Cada série foi projetada com foco em engenharia especializada, desde nós de sensores autônomos de baixo consumo até poderosos aceleradores de inteligência artificial.
+
+ {series.length} {series.length === 1 ? "série" : "séries"}
+
+
+ {series.map(([key, serie]) => {
+ const riscv = serie.arquitetura.includes("RISC-V");
+ const fatos = [
+ ["Núcleos", serie.nucleos],
+ ["Frequência", serie.frequencia],
+ ["SRAM", serie.memoria_sram],
+ ["Wi-Fi", sim(serie.wifi) ? "Sim" : "Não"],
+ ["Bluetooth", serie.bluetooth === "Não" ? "Não" : "Sim"],
+ serie.consumo_energia?.deep_sleep ? ["Deep sleep", serie.consumo_energia.deep_sleep] : null,
+ ].filter(Boolean);
+ return (
+
+
+
{key}
+ {riscv ? "RISC-V" : "Xtensa"}
+
+
{serie.descricao}
+
+
+ {fatos.map(([rotulo, valor]) => (
+
+
{rotulo}
+ {valor}
+
+ ))}
+
+
+
+ {serie.aceleradores_ia && (
+
+
+ Aceleradores de IA
+
+ )}
+
+
+ {nivelSeguranca(serie)}
+
+ {serie.matter === "Sim" && (
+
+
+ Matter
+
+ )}
+ {serie.status_documentacao && (
+
+
+ Documentação preliminar
+
+ )}
+
+
+
+ Abrir ficha
+
+
+
+ );
+ })}
+
- {/* Series Grid */}
-
- {series.map(([key, serie]) => {
- const isRiscV = serie.arquitetura.includes("RISC-V");
- return (
-
- {/* Glassmorphic card container with dynamic color borders and shadows */}
-
{
- e.currentTarget.style.borderColor = serie.cor;
- e.currentTarget.style.boxShadow = `0 15px 35px -12px ${serie.cor}35`;
- e.currentTarget.style.transform = 'translateY(-6px)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.borderColor = '';
- e.currentTarget.style.boxShadow = '';
- e.currentTarget.style.transform = '';
- }}
- >
-
- {/* Header block with Icon & Architecture */}
-
- {serie.icone}
-
- {isRiscV ? "RISC-V" : "Xtensa"}
-
-
-
- {/* Title and description */}
-
- {key}
-
-
-
- {serie.descricao}
-
-
- {/* Detailed specs micro-grid */}
-
-
-
-
- Núcleos:
-
-
{serie.nucleos}
-
-
-
-
-
-
-
- SRAM:
-
-
{serie.memoria_sram}
-
-
-
-
-
- Wi-Fi:
-
-
- {String(serie.wifi).includes("Não") ? (
- Não
- ) : (
- Sim
- )}
-
-
-
-
-
-
- Bluetooth:
-
-
- {serie.bluetooth === "Não" ? (
- Não
- ) : (
- Sim
- )}
-
-
-
- {serie.consumo_energia?.deep_sleep && (
-
-
-
- Deep Sleep:
-
-
{serie.consumo_energia.deep_sleep}
-
- )}
-
-
-
- {/* IA / Cripto / Matter Badges & Action Footer */}
-
-
- {serie.aceleradores_ia && (
-
-
- Aceleradores IA
-
- )}
-
- {serie.status_documentacao && (
-
-
- Doc. Preliminar
-
- )}
-
- {/* Marcador de segurança: mostra o recurso que DIFERENCIA a série,
- não o que todas têm. Ordem = do mais raro ao mais comum. */}
- {(() => {
- const seg = serie.seguranca || {};
- const nivel =
- seg.key_manager === "Sim" ? "Key Manager"
- : String(seg.ecdsa).startsWith("Sim") ? "ECDSA em hardware"
- : String(seg.ecc).startsWith("Sim") ? "ECC em hardware"
- : String(seg.aes).startsWith("AES") ? "AES + SHA"
- : "Só SHA";
- return (
-
-
- {nivel}
-
- );
- })()}
-
- {serie.matter === "Sim" && (
-
-
- Suporte Matter
-
- )}
-
-
-
-
-
-
- );
- })}
-
-
- {/* Quick comparison CTA Box */}
-
-
-
-
-
-
-
- Decisão Técnica
-
-
- Ficou em dúvida de qual escolher?
-
-
- Use nosso comparador técnico dinâmico para posicionar as especificações de conectividade RF, potência de rádio e suporte a barramentos de periféricos lado a lado.
+
+
+
+
Ficou em dúvida entre duas séries?
+
+ O comparador coloca até quatro modelos lado a lado: núcleos, rádio, memória, periféricos e segurança.
-
-
-
Comparar Séries
-
+
+
+ Comparar séries
+
-
+
);
-}
\ No newline at end of file
+}
diff --git a/app/sitemap.js b/app/sitemap.js
index 71c90d0..3fd2cf9 100644
--- a/app/sitemap.js
+++ b/app/sitemap.js
@@ -22,6 +22,7 @@ export default function sitemap() {
{ caminho: "/seletor", prioridade: 0.8, frequencia: "monthly", lastModified: dataRevisao },
{ caminho: "/frameworks", prioridade: 0.8, frequencia: "monthly", lastModified: dataRevisao },
{ caminho: "/componentes", prioridade: 0.8, frequencia: "weekly", lastModified: dataRevisao },
+ { caminho: "/livro", prioridade: 0.7, frequencia: "monthly", lastModified: dataRevisao },
{ caminho: "/glossario", prioridade: 0.7, frequencia: "monthly", lastModified: dataRevisao },
{ caminho: "/catalogo", prioridade: 0.6, frequencia: "weekly", lastModified: dataRevisao },
{ caminho: "/sobre", prioridade: 0.4, frequencia: "yearly", lastModified: dataRevisao },
diff --git a/app/sobre/page.jsx b/app/sobre/page.jsx
index 5f2bd86..52ce760 100644
--- a/app/sobre/page.jsx
+++ b/app/sobre/page.jsx
@@ -1,24 +1,19 @@
import { paginaMeta, jsonLd } from "@/lib/seo";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
-import Link from "next/link";
+import PageHero from "@/components/PageHero";
import Image from "next/image";
-import {
- Languages,
- GitCompare,
- Globe,
- Code,
- Target,
- HelpCircle,
- Users,
- Accessibility,
- ArrowLeft,
- Award,
- BookOpen,
- Compass,
- Terminal,
- Layers
-} from "lucide-react";
+import { ArrowUpRight, Code, Globe, GitCompare, Info, Languages } from "lucide-react";
+
+const GITHUB = "https://github.com/lucasrguerra/ESPDocs";
+
+function GithubMark({ className }) {
+ return (
+
+
+
+ );
+}
export const metadata = paginaMeta({
@@ -30,14 +25,14 @@ export const metadata = paginaMeta({
export default function Sobre() {
const features = [
- { icon:
, title: "Interface em Português", desc: "Organização e apresentação de dados técnicos totalmente em português." },
- { icon:
, title: "Comparações Visuais", desc: "Tabelas comparativas e diagramas para facilitar a escolha da série ideal." },
- { icon:
, title: "Acesso Centralizado", desc: "Links diretos para documentação oficial, repositórios e exemplos." },
- { icon:
, title: "Código Aberto", desc: "Projeto open-source com contribuições bem-vindas no GitHub." },
+ { icon: Languages, title: "Em português", desc: "Dados técnicos organizados e apresentados em português do Brasil." },
+ { icon: GitCompare, title: "Comparações visuais", desc: "Tabelas e diagramas para escolher a série certa sem abrir dez datasheets." },
+ { icon: Globe, title: "Acesso centralizado", desc: "Links diretos para a documentação oficial, repositórios e exemplos." },
+ { icon: Code, title: "Código aberto", desc: "Contribuições são bem-vindas no GitHub." },
];
return (
-
+
{/* Metadados E-E-A-T de autoria e organização para o Google */}
-
- Ir para o conteúdo
-
-
-
- {/* Top Hero Heading Block */}
-
-
-
-
- Sobre o ESPDocs
-
+
+
+
+
+ Ver o repositório
+
+
+
-
- Uma plataforma não oficial com interface em português que organiza
- informações técnicas do ESP32 e facilita o acesso à documentação oficial
- da Espressif. Desenvolvida para a comunidade de hardware livre.
-
-
-
-
-
-
- {/* Two columns details grid */}
-
-
- {/* Missao article */}
-
-
-
- Facilitar o acesso e a compreensão do ecossistema ESP32 através de uma
- interface em português que organiza especificações técnicas, compara séries,
- apresenta frameworks e centraliza links para a documentação oficial da
- Espressif. O objetivo principal é otimizar o tempo no desenvolvimento de firmware e IoT.
+
+
+
+ Missão
+
+ Facilitar o acesso e a compreensão do ecossistema ESP32 com uma interface em português que organiza
+ especificações, compara séries, apresenta frameworks e centraliza os links da documentação oficial.
+ O objetivo é economizar o seu tempo no desenvolvimento de firmware e IoT.
-
+
- {/* Por que o ESPDocs article */}
-
-
-
-
-
-
Por que o ESPDocs?
-
-
- As informações sobre os microcontroladores ESP32 muitas vezes estão dispersas em datasheets densos,
- manuais de referência (TRM), repositórios secundários e documentação técnica, tudo inteiramente em inglês.
- O ESPDocs condensa e estrutura esses dados, auxiliando na triagem rápida antes de aprofundar na engenharia oficial.
+
+ Por que o ESPDocs existe
+
+ As informações sobre os ESP32 costumam estar espalhadas entre datasheets densos, manuais de referência
+ (TRM) e repositórios secundários, tudo em inglês. O ESPDocs condensa e estrutura esses dados para a
+ triagem rápida, antes de você aprofundar na engenharia oficial.
-
-
- {features.map((f) => (
-
-
- {f.icon}
+
+ {features.map(({ icon: Icon, title, desc }) => (
+
+
+
+
+
-
-
+
))}
-
-
-
- {/* Contribuir article */}
-
-
-
-
-
-
Como Contribuir
-
-
-
- Correções de tradução, refinamento de tabelas e novos trechos funcionais de exemplos são muito bem-vindos!
- A plataforma cresce por meio da comunidade.
-
-
-
-
- Faça o fork do repositório no GitHub.
- Implemente melhorias e revise referências técnicas oficiais.
- Abra o seu Pull Request (PR) detalhando os ajustes efetuados.
-
-
-
-
- {/* Accessibility note */}
-
-
-
- O ESPDocs preza pela legibilidade, alto contraste cromático e compatibilidade de navegação por teclado.
- Caso encontre alguma barreira técnica de leitura ou navegação, sinta-se encorajado a relatar via Issue.
-
-
-
-
- {/* Sidebar area */}
-
- {/* Author Card */}
-
-
-
-
-
- Lucas Rayan Guerra , criador da iniciativa
- Ciência Embarcada
- , é Técnico em Desenvolvimento de Sistemas, estudante de Ciência da Computação na UFRPE e estuda Blockchain e Criptografia.
-
-
-
- Diretor de Software na Semine AgriTech e palestrante em feiras e conferências (como o REC'n'Play), compartilha ativamente material sobre hardware, internet das coisas e cibersegurança.
-
-
-
-
-
- "O Ciência Embarcada nasceu para criar um espaço de curadoria técnica livre, promovendo a popularização científica e do hardware embarcado."
-
-
-
-
- Visitar Ciência Embarcada
-
-
-
- {/* Repository / Contributions card */}
-
-
-
Contribua
- Open Source
-
-
-
- Ajude a expandir a cobertura do ESPDocs submetendo exemplos de códigos ou aprimoramentos técnicos.
-
-
-
-
-
-
- Ver Repositório
-
-
-
- Aviso Legal: O ESPDocs é um agregador e organizador. Manuais técnicos detalhados e repositórios oficiais referenciados são de propriedade exclusiva da Espressif Systems. Para projetos críticos comerciais, consulte sempre a documentação nativa oficial.
-
-
-
- {/* Summary sidebar card */}
-
-
Conteúdo Técnico
-
-
-
- Focado em clareza técnica
-
-
-
- Exemplos práticos de códigos
-
-
-
- Links oficiais mapeados
-
+
+
+
+ Como contribuir
+
+ Correções de tradução, ajustes em tabelas e novos exemplos de código são muito bem-vindos. O projeto
+ cresce com a comunidade.
+
+
+ {[
+ "Faça o fork do repositório no GitHub.",
+ "Implemente as melhorias e confira as referências técnicas oficiais.",
+ "Abra um Pull Request descrevendo os ajustes feitos.",
+ ].map((passo, i) => (
+
+
+ {i + 1}
+
+ {passo}
+
+ ))}
+
+
+
+
+ Acessibilidade
+
+ O ESPDocs prioriza a legibilidade, o contraste alto (nível AAA da WCAG) e a navegação completa por
+ teclado. Se encontrar alguma barreira de leitura ou navegação, abra uma issue no repositório.
+
+
+
+
+
@@ -288,4 +172,4 @@ export default function Sobre() {
);
-}
\ No newline at end of file
+}
diff --git a/components/BoardCard.jsx b/components/BoardCard.jsx
index 397f59c..7751f62 100644
--- a/components/BoardCard.jsx
+++ b/components/BoardCard.jsx
@@ -1,99 +1,67 @@
-'use client';
+"use client";
-import { Award, CheckCircle, HelpCircle, ExternalLink } from 'lucide-react';
+import { ArrowUpRight, Award, Check, CircleHelp } from "lucide-react";
export default function BoardCard({ board }) {
- const mainFields = ['Nome', 'Link', 'Imagem', 'Loja Oficial'];
-
- const technicalFields = Object.keys(board).filter(
- key => !mainFields.includes(key) && board[key]
- );
+ const mainFields = ["Nome", "Link", "Imagem", "Loja Oficial"];
- const hasFeature = (value) => {
- const yesValues = ['sim', 'opcional'];
- return yesValues.includes(value?.toString().toLowerCase().trim());
- };
+ const technicalFields = Object.keys(board).filter((key) => !mainFields.includes(key) && board[key]);
- // Filtrar apenas características que têm "Sim" ou "Opcional"
- const availableFeatures = technicalFields.filter(field => hasFeature(board[field]));
- const optionalFeatures = technicalFields.filter(field =>
- board[field]?.toString().toLowerCase().trim() === 'opcional'
- );
+ const valor = (campo) => board[campo]?.toString().toLowerCase().trim();
+ const recursos = technicalFields.filter((campo) => ["sim", "opcional"].includes(valor(campo)));
+ const lojaOficial = valor("Loja Oficial") === "sim";
return (
-
- {/* Image Container with premium glass background */}
+
{board.Imagem && (
-
)}
-
-
- {/* Loja Oficial Badge */}
- {board['Loja Oficial'] && board['Loja Oficial'].toLowerCase() === 'sim' && (
-
- )}
+
+ {lojaOficial && (
+
+
+ Loja oficial
+
+ )}
- {/* Nome */}
-
- {board.Nome || 'Placa Genérica'}
-
+
+ {board.Nome || "Placa genérica"}
+
- {/* Características Técnicas */}
- {availableFeatures.length > 0 && (
-
-
-
- Recursos Embarcados
-
-
-
- {availableFeatures.map((field) => {
- const isOptional = optionalFeatures.includes(field);
- return (
-
- {isOptional ? (
-
- ) : (
-
- )}
- {field}
-
- );
- })}
-
-
- )}
-
+ {recursos.length > 0 && (
+
+
Recursos embarcados
+
+ {recursos.map((campo) => {
+ const opcional = valor(campo) === "opcional";
+ return (
+
+ {opcional ? (
+
+ ) : (
+
+ )}
+ {campo}
+ {opcional && (opcional) }
+
+ );
+ })}
+
+
+ )}
- {/* Link de Compra */}
{board.Link && (
-
- Adquirir no AliExpress
-
+
+ Ver no AliExpress
+
+ (abre em nova aba)
)}
-
+
);
}
diff --git a/components/BookCover.jsx b/components/BookCover.jsx
new file mode 100644
index 0000000..117ad82
--- /dev/null
+++ b/components/BookCover.jsx
@@ -0,0 +1,16 @@
+export default function BookCover({ className = "" }) {
+ return (
+
+ );
+}
diff --git a/components/CodeExample.jsx b/components/CodeExample.jsx
index cb775d3..13a402f 100644
--- a/components/CodeExample.jsx
+++ b/components/CodeExample.jsx
@@ -1,19 +1,18 @@
-'use client'
+"use client";
-import { useEffect, useState } from 'react'
-import { Code, Copy, CheckCircle, ExternalLink, HelpCircle } from 'lucide-react'
+import { useEffect, useState } from "react";
+import { Check, Copy, ExternalLink } from "lucide-react";
export default function CodeExample({ framework }) {
const examplesUrl = framework?.exemplos;
- const color = framework?.cor || '#6B7280';
- const [sample, setSample] = useState('');
+ const [sample, setSample] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [copied, setCopied] = useState(false);
useEffect(() => {
- setSample('');
+ setSample("");
setError(null);
setLoading(false);
@@ -21,12 +20,11 @@ export default function CodeExample({ framework }) {
const exemploPath = framework.exemplo;
if (!exemploPath) {
- const msg = `// Exemplos completos: ${examplesUrl || 'Ver repositório'}\n// Abra o link acima para ver exemplos e código completo.\n`;
- setSample(msg);
+ setSample(`// Exemplos completos: ${examplesUrl || "ver repositório"}\n`);
return;
}
- const url = exemploPath.startsWith('/') ? exemploPath : `/${exemploPath}`;
+ const url = exemploPath.startsWith("/") ? exemploPath : `/${exemploPath}`;
let cancelled = false;
setLoading(true);
@@ -41,14 +39,14 @@ export default function CodeExample({ framework }) {
if (text && text.trim().length > 0) {
setSample(text);
} else {
- setSample(`// Arquivo de exemplo está vazio. Consulte o repositório oficial.\n`);
- setError('Arquivo vazio');
+ setSample("// O arquivo de exemplo está vazio. Consulte o repositório oficial.\n");
+ setError("arquivo vazio");
}
})
.catch((err) => {
if (cancelled) return;
- setSample(`// Não foi possível carregar o exemplo localmente.\n// Consulte o link do GitHub acima para exemplos funcionais.\n`);
- setError(err.message || 'Falha ao carregar exemplo');
+ setSample("// Não foi possível carregar o exemplo.\n// Consulte o repositório oficial para exemplos completos.\n");
+ setError(err.message || "falha ao carregar");
})
.finally(() => {
if (!cancelled) setLoading(false);
@@ -59,7 +57,7 @@ export default function CodeExample({ framework }) {
};
}, [framework?.exemplo, framework?.exemplos]);
- const lines = (sample || '').split('\n');
+ const lines = (sample || "").replace(/\n$/, "").split("\n");
async function handleCopy() {
try {
@@ -70,109 +68,50 @@ export default function CodeExample({ framework }) {
}
return (
-
-
-
-
-
- Demonstração de Código
-
-
Trecho de inicialização rápida. Copie e personalize em seu projeto.
+
+
+
+
Exemplo de código
+
Trecho de partida em {framework?.linguagem}. Ajuste pinos e configuração à sua placa.
-
-
- {framework?.linguagem || 'Código'}
-
-
-
- {copied ? (
- <>
-
- Copiado!
- >
- ) : (
- <>
-
- Copiar
- >
- )}
+
-
-
-
-
-
-
-
- {framework?.nome} example
-
-
- {loading ? 'Buscando exemplo...' : (framework?.exemplo ? 'Local Source' : 'External GitHub')}
-
-
+
+
+ {framework?.nome}
+ {loading ? "Carregando exemplo…" : framework?.linguagem}
-
-
-
+
+
{lines.map((line, idx) => (
-
- {idx + 1}
- {line === '' ? '\u00A0' : line}
-
+
+ {idx + 1}
+ {line || " "}
+
))}
-
-
+
+
{error && (
-
-
- Nota: Não foi possível carregar o arquivo de exemplo local ({error}). Use o botão "Mais Exemplos" para ver os códigos oficiais.
-
-
- )}
-
-
-
-
- Dica de Desenvolvimento: Personalize o trecho de inicialização com as pinagens e configurações corretas correspondentes à série do seu microcontrolador (ex: ESP32-S3, ESP32-C6). A documentação detalhada da API pode ser acessada na barra superior do framework.
+
+ Não foi possível carregar o exemplo local ({error}). Use “Mais exemplos” para ver o código oficial.
-
+ )}
);
-}
\ No newline at end of file
+}
diff --git a/components/CodeSample.jsx b/components/CodeSample.jsx
new file mode 100644
index 0000000..c6785e7
--- /dev/null
+++ b/components/CodeSample.jsx
@@ -0,0 +1,25 @@
+export default function CodeSample({ label, language, code, note }) {
+ const lines = code.replace(/\n$/, "").split("\n");
+
+ return (
+
+
+ {label}
+ {language}
+
+
+
+
+ {lines.map((line, index) => (
+
+ {index + 1}
+ {line || " "}
+
+ ))}
+
+
+
+ {note && {note}
}
+
+ );
+}
diff --git a/components/ConnectionsDiagram.jsx b/components/ConnectionsDiagram.jsx
index d331f8c..ccd98ed 100644
--- a/components/ConnectionsDiagram.jsx
+++ b/components/ConnectionsDiagram.jsx
@@ -1,22 +1,14 @@
"use client";
-import { useState, useEffect, useMemo } from "react";
+import { useState, useEffect, useMemo, useRef } from "react";
import { createPortal } from "react-dom";
-import {
- Search,
- X,
- Grid,
- List,
- Filter,
- AlertTriangle,
- Info,
- Tag,
- Zap,
- Sliders,
- CheckCircle,
- ExternalLink
-} from "lucide-react";
+import { AlertTriangle, CheckCircle, List, Grid, Search, Sliders, X, Zap } from "lucide-react";
+/*
+ * As cores abaixo são dado de visualização: identificam o tipo de função do
+ * pino no diagrama. O texto nunca fica sobre elas; elas aparecem só como borda
+ * ou ponto, e o rótulo usa as cores do tema (contraste AAA).
+ */
const FILTER_CATEGORIES = [
{ id: "All", name: "Todos", color: "#8b5cf6" }, // purple
{ id: "GND", name: "Terra (GND)", color: "#64748b" }, // slate
@@ -52,11 +44,11 @@ const type_colors = {
export default function ConnectionsDiagram({ connections, serie }) {
const [activeFilter, setActiveFilter] = useState("All");
const [selectedConnection, setSelectedConnection] = useState(null);
- const [hoveredConnection, setHoveredConnection] = useState(null);
const [searchQuery, setSearchQuery] = useState("");
const [viewMode, setViewMode] = useState("grid");
- const [showTooltip, setShowTooltip] = useState(null);
const [mounted, setMounted] = useState(false);
+ const dialogRef = useRef(null);
+ const gatilhoRef = useRef(null);
useEffect(() => {
setMounted(true);
@@ -64,7 +56,8 @@ export default function ConnectionsDiagram({ connections, serie }) {
const isConnectionVisible = (connection) => {
const matchesFilter = activeFilter === "All" || connection.categorias.includes(activeFilter);
- const matchesSearch = searchQuery === "" ||
+ const matchesSearch =
+ searchQuery === "" ||
connection.nome.toLowerCase().includes(searchQuery.toLowerCase()) ||
connection.numero.toString().toLowerCase().includes(searchQuery.toLowerCase());
return matchesFilter && matchesSearch;
@@ -75,641 +68,328 @@ export default function ConnectionsDiagram({ connections, serie }) {
return filter?.color || "#64748b";
};
- const visibleConnections = useMemo(() => {
- return connections.filter(isConnectionVisible);
- }, [connections, activeFilter, searchQuery]);
+ const nomeCategoria = (cat) => (cat === "Strapping" ? "Inicialização" : cat);
- const activeCategories = useMemo(() => {
- if (activeFilter === "All") return [];
- return FILTER_CATEGORIES.filter(cat =>
- visibleConnections.some(conn => conn.categorias.includes(cat.id))
- );
- }, [visibleConnections, activeFilter]);
+ const visibleConnections = useMemo(() => connections.filter(isConnectionVisible), [connections, activeFilter, searchQuery]);
+ const abrir = (connection, evento) => {
+ gatilhoRef.current = evento.currentTarget;
+ setSelectedConnection(connection);
+ };
+
+ const fechar = () => {
+ setSelectedConnection(null);
+ gatilhoRef.current?.focus?.();
+ };
+
+ // Diálogo: Esc fecha, o foco entra nele, fica preso enquanto aberto e volta ao pino de origem.
useEffect(() => {
- const handleEscape = (e) => {
+ if (!selectedConnection) return;
+ const dialogo = dialogRef.current;
+ const focaveis = () => dialogo?.querySelectorAll('button, [href], input, [tabindex]:not([tabindex="-1"])') ?? [];
+ focaveis()[0]?.focus();
+
+ const aoTeclar = (e) => {
if (e.key === "Escape") {
- setSelectedConnection(null);
- setShowTooltip(null);
+ fechar();
+ return;
+ }
+ if (e.key !== "Tab") return;
+ const itens = [...focaveis()];
+ if (itens.length === 0) return;
+ const primeiro = itens[0];
+ const ultimo = itens[itens.length - 1];
+ if (e.shiftKey && document.activeElement === primeiro) {
+ e.preventDefault();
+ ultimo.focus();
+ } else if (!e.shiftKey && document.activeElement === ultimo) {
+ e.preventDefault();
+ primeiro.focus();
}
};
-
- if (selectedConnection) {
- document.addEventListener("keydown", handleEscape);
- document.body.style.overflow = "hidden";
- }
-
+
+ document.addEventListener("keydown", aoTeclar);
+ document.body.style.overflow = "hidden";
return () => {
- document.removeEventListener("keydown", handleEscape);
- document.body.style.overflow = "unset";
+ document.removeEventListener("keydown", aoTeclar);
+ document.body.style.overflow = "";
};
}, [selectedConnection]);
- useEffect(() => {
- const handleKeyDown = (e) => {
- if (selectedConnection) return;
-
- if (e.key === "Tab") {
- const buttons = document.querySelectorAll('[data-connection-button]');
- const currentIndex = Array.from(buttons).findIndex(btn => btn === document.activeElement);
-
- if (e.shiftKey && currentIndex > 0) {
- e.preventDefault();
- buttons[currentIndex - 1].focus();
- } else if (!e.shiftKey && currentIndex < buttons.length - 1) {
- e.preventDefault();
- buttons[currentIndex + 1].focus();
- }
- }
- };
-
- document.addEventListener("keydown", handleKeyDown);
- return () => document.removeEventListener("keydown", handleKeyDown);
- }, [selectedConnection]);
-
if (!connections || connections.length === 0) {
return (
-
-
-
-
-
-
- Diagrama de conexões indisponível
-
-
-
- Este modelo ainda não possui um mapa detalhado de conexões e barramentos.
-
+
+
+
Diagrama de conexões indisponível
+
Este modelo ainda não tem um mapa detalhado de conexões e barramentos.
);
}
+ const numeroCurto = (c) => (typeof c.numero === "number" ? c.numero : String(c.numero).substring(0, 4));
+
return (
-
-
- {/* Header info */}
-
-
-
- Diagrama de Conexões Interativo
-
-
- Explore todas as conexões e multiplexação do SoC do {serie.nome_completo}. Utilize o painel de busca rápida ou filtre por categoria para acender conexões específicas e ver mapas de barramentos.
+
+
+
Diagrama de conexões interativo
+
+ Todas as conexões e a multiplexação do {serie.nome_completo}. Busque por nome ou número, filtre por função e abra um pino para ver os detalhes.
- {/* Search Panel */}
-
-
-
-
- setSearchQuery(e.target.value)}
- className="w-full pl-12 pr-10 py-3 rounded-xl border border-slate-300 dark:border-slate-800 bg-white dark:bg-slate-950/40 focus:border-purple-500/80 focus:ring-4 focus:ring-purple-500/10 text-slate-800 dark:text-slate-200 transition-all outline-none text-sm font-semibold"
- aria-label="Search connections"
- />
- {searchQuery && (
- setSearchQuery("")}
- className="absolute right-4 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600 transition-colors"
- aria-label="Clear search"
- >
-
-
- )}
-
-
- {/* View Mode Toggle */}
-
- setViewMode("grid")}
- className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all text-xs font-bold uppercase tracking-wider cursor-pointer active:scale-95 ${
- viewMode === "grid"
- ? "bg-white dark:bg-slate-900 shadow-md text-slate-900 dark:text-slate-100"
- : "text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
- }`}
- aria-label="Grid view"
- aria-pressed={viewMode === "grid"}
- >
-
- Grade
-
-
- setViewMode("list")}
- className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all text-xs font-bold uppercase tracking-wider cursor-pointer active:scale-95 ${
- viewMode === "list"
- ? "bg-white dark:bg-slate-900 shadow-md text-slate-900 dark:text-slate-100"
- : "text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
- }`}
- aria-label="List view"
- aria-pressed={viewMode === "list"}
- >
-
- Lista
-
-
+
+
+ Buscar conexão por nome ou número
+
+ setSearchQuery(e.target.value)}
+ autoComplete="off"
+ className="field pl-11 pr-4 text-sm"
+ />
+
+
+ setViewMode("grid")} aria-pressed={viewMode === "grid"} className="chip-btn">
+
+ Grade
+
+ setViewMode("list")} aria-pressed={viewMode === "list"} className="chip-btn">
+
+ Lista
+
- {/* Filters Panel */}
-
-
-
-
- Filtre por Função do Pino
-
-
-
- {visibleConnections.length} de {connections.length} ativos
-
+
+
+
Função do pino
+
+ {visibleConnections.length} de {connections.length} pinos
+
-
-
+
{FILTER_CATEGORIES.map((filter) => {
const isActive = activeFilter === filter.id;
- const count = connections.filter(c =>
- filter.id === "All" ? true : c.categorias.includes(filter.id)
- ).length;
-
- if (count === 0 && !isActive) return null; // Hide filters that don't apply to this chip
-
+ const count = connections.filter((c) => (filter.id === "All" ? true : c.categorias.includes(filter.id))).length;
+ if (count === 0 && !isActive) return null; // filtros que não se aplicam a este chip
return (
- setActiveFilter(filter.id)}
- aria-pressed={isActive}
- aria-label={`Filter by ${filter.name}`}
- className={`group relative px-4 py-2 rounded-xl text-xs font-extrabold transition-all duration-300 focus:outline-none cursor-pointer active:scale-95 ${
- isActive
- ? "shadow-lg text-white"
- : "bg-slate-50/70 dark:bg-slate-950/20 text-slate-650 dark:text-slate-350 hover:bg-slate-100/50 dark:hover:bg-slate-850/30 border border-slate-300 dark:border-slate-850/40"
- }`}
- style={{
- backgroundColor: isActive ? filter.color : undefined,
- boxShadow: isActive ? `0 8px 15px -8px ${filter.color}` : undefined
- }}
- >
-
- {filter.name}
-
- {count}
-
-
+ setActiveFilter(filter.id)} aria-pressed={isActive} className="chip-btn">
+ {filter.id !== "All" && }
+ {filter.name}
+ {count}
);
})}
-
- {/* Active Categories badges */}
- {activeFilter !== "All" && activeCategories.length > 0 && (
-
-
Subcategorias ativadas por esse filtro:
-
- {activeCategories.map(cat => (
-
-
- {cat.name}
-
- ))}
-
-
- )}
- {/* Diagram Workspace */}
{visibleConnections.length === 0 ? (
-
-
-
-
-
Nenhuma conexão encontrada
-
Tente ajustar seus termos de pesquisa ou limpe os filtros rápidos.
-
-
{
- setActiveFilter("All");
- setSearchQuery("");
- }}
- className="mt-5 px-6 py-2.5 bg-gradient-to-r from-purple-600 to-indigo-600 text-white rounded-xl hover:opacity-90 transition-all font-bold text-xs uppercase tracking-wider cursor-pointer active:scale-95 shadow-lg"
- >
- Limpar Filtros e Busca
+
+
+
Nenhuma conexão encontrada
+
Ajuste a busca ou volte para todas as funções.
+
{ setActiveFilter("All"); setSearchQuery(""); }} className="btn btn-primary btn-sm mt-5">
+ Limpar busca e filtros
) : viewMode === "grid" ? (
- /* Grid View Mode: Futuristic Electronic Chip Sockets */
-
-
- {visibleConnections.map((connection, index) => {
- const isHovered = hoveredConnection === index;
- const connectionId = `connection-${index}`;
- const baseColor = type_colors[connection.tipo] || "#94a3b8";
-
- return (
-
-
setSelectedConnection(connection)}
- onMouseEnter={() => {
- setHoveredConnection(index);
- setShowTooltip(index);
- }}
- onMouseLeave={() => {
- setHoveredConnection(null);
- setShowTooltip(null);
- }}
- onFocus={() => setHoveredConnection(index)}
- onBlur={() => setHoveredConnection(null)}
- aria-label={`Pino ${connection.nome}, número ${connection.numero}`}
- aria-describedby={showTooltip === index ? `tooltip-${index}` : undefined}
- className="w-full flex flex-col items-center justify-center p-4 rounded-2xl border-2 transition-all duration-300 cursor-pointer active:scale-95 focus:outline-none"
- style={{
- backgroundColor: isHovered ? `${baseColor}24` : `${baseColor}0c`,
- borderColor: isHovered ? baseColor : `${baseColor}40`,
- boxShadow: isHovered ? `0 0 20px -5px ${baseColor}60` : 'none',
- transform: isHovered ? "translateY(-4px)" : undefined,
- }}
+
+ {visibleConnections.map((connection) => {
+ const baseColor = type_colors[connection.tipo] || "#94a3b8";
+ return (
+
+ abrir(connection, e)}
+ aria-haspopup="dialog"
+ aria-label={`Pino ${connection.nome}, número ${connection.numero}${connection.avisos ? ", com restrições de uso" : ""}`}
+ className="flex w-full flex-col items-center rounded-xl border border-line bg-surface p-3 transition-colors hover:bg-surface-2"
+ >
+
- {/* Pad Contact Core Circle */}
-
- {typeof connection.numero === "number"
- ? connection.numero
- : connection.numero.substring(0, 4)}
-
-
- {/* Pad Name label */}
-
- {connection.nome}
-
-
- {/* Small Category dots indicator */}
-
- {connection.categorias.slice(0, 5).map((cat) => (
-
- ))}
- {connection.categorias.length > 5 && (
-
- +{connection.categorias.length - 5}
+ {numeroCurto(connection)}
+
+
{connection.nome}
+
+ {connection.categorias.slice(0, 5).map((cat) => (
+
+ ))}
+
+
+ {connection.avisos && (
+
+
+
+ )}
+
+ );
+ })}
+
+ ) : (
+
+
+ Conexões do {serie.nome_completo}
+
+
+ Pino
+ Nome
+ Funções
+ Uso
+
+
+
+ {visibleConnections.map((connection) => {
+ const baseColor = type_colors[connection.tipo] || "#94a3b8";
+ return (
+
+
+ abrir(connection, e)}
+ aria-haspopup="dialog"
+ aria-label={`Abrir detalhes do pino ${connection.nome}, número ${connection.numero}`}
+ className="grid h-11 w-11 place-items-center rounded-full border-2 bg-surface text-sm font-semibold tabular-nums text-ink hover:bg-surface-2"
+ style={{ borderColor: baseColor }}
+ >
+ {numeroCurto(connection)}
+
+
+ {connection.nome}
+
+ {connection.funcoes.join(", ")}
+
+
+ {connection.avisos ? (
+
+
+ Restrito
+
+ ) : (
+
+
+ Livre
)}
-
-
- {/* High-Voltage warnings tags */}
- {connection.avisos && (
-
- )}
-
-
- {/* Sleek Tooltip Popover */}
- {showTooltip === index && (
-
- )}
-
- );
- })}
-
+
+
+ );
+ })}
+
+
- ) : (
- /* List View Mode: Interactive Zebra Table */
-
-
-
-
-
- Pino
- Nome do Pad
- Categorias
- Multiplexação de Funções
- Restrições
-
-
-
- {visibleConnections.map((connection, index) => {
- const baseColor = type_colors[connection.tipo] || "#94a3b8";
- return (
- setSelectedConnection(connection)}
- className="hover:bg-purple-500/5 dark:hover:bg-purple-400/5 cursor-pointer transition-colors group"
- >
-
-
- {typeof connection.numero === "number"
- ? connection.numero
- : connection.numero.substring(0, 4)}
-
-
-
-
-
- {connection.nome}
-
-
-
-
-
- {connection.categorias.map((cat) => (
-
- {cat === "Strapping" ? "Inicialização" : cat}
-
- ))}
-
-
-
-
-
- {connection.funcoes.join(", ")}
-
-
-
-
- {connection.avisos ? (
-
-
- Restrito
-
- ) : (
-
-
- Livre
-
- )}
-
-
- );
- })}
-
-
+ )}
+
+ {connections.some((c) => c.avisos) && (
+
+
+
+
Notas sobre a pinagem
+
+ Pinos com o ícone de alerta têm papel especial no boot ou limitações analógicas. Abra o pino para ver os detalhes.
+ Isole os circuitos de toque capacitivo de pinos com ruído de RF acoplado.
+
)}
- {/* Global Warning notice */}
- {connections.some(c => c.avisos) && (
-
-
-
-
-
-
- Notas Importantes sobre a Pinagem
-
-
- • Pinos contendo tags de Avisos/Restrições (cor vermelha) possuem papéis especiais de Strapping na inicialização, ou limitações analógicas. Clique neles para ver detalhes.
- • Sempre garanta isolamento de circuitos capacitivos de toque em pinos com ruído de RF acoplado.
-
-
-
-
- )}
-
- {/* Interactive Overlay Dialog Details Sheet */}
{selectedConnection && mounted && createPortal(
-
setSelectedConnection(null)}
- role="dialog"
- aria-modal="true"
- aria-labelledby="modal-title"
- >
+
e.stopPropagation()}
>
- {/* Dialog Header */}
-
-
-
-
- {typeof selectedConnection.numero === "number"
- ? selectedConnection.numero
- : selectedConnection.numero.substring(0, 4)}
-
-
-
- {selectedConnection.nome}
-
-
- Pino Físico {selectedConnection.numero}
-
-
-
-
-
setSelectedConnection(null)}
- aria-label="Fechar"
- className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-colors p-2 bg-slate-100 dark:bg-slate-900 rounded-xl shrink-0 cursor-pointer active:scale-95"
+
+
+
-
-
+ {numeroCurto(selectedConnection)}
+
+
+
+ {selectedConnection.nome}
+
+
Pino físico {selectedConnection.numero}
+
+
+
+
- {/* Dialog Body Contents */}
-
-
- {/* Categories */}
+
{selectedConnection.categorias.length > 0 && (
-
-
-
- Categorias de Multiplexação
-
-
-
+
+ Categorias
+
{selectedConnection.categorias.map((cat) => (
-
- {cat === "Strapping" ? "Inicialização" : cat}
-
- ))}
-
-
- )}
-
- {/* Power Source */}
- {selectedConnection.alimentacao && (
-
-
-
- Domínio de Alimentação Interna
-
-
-
- {selectedConnection.alimentacao.map((source, index) => (
-
-
- {source}
-
- ))}
-
-
-
- )}
-
- {/* Function Multiplexation */}
-
-
-
- Barramentos de Funções Disponíveis
-
-
-
-
- {selectedConnection.funcoes.map((func, index) => (
-
-
- {func}
+
+
+ {nomeCategoria(cat)}
))}
-
-
+
+ )}
+
+ {selectedConnection.alimentacao && (
+
+
+
+ Domínio de alimentação
+
+
+ {selectedConnection.alimentacao.map((source) => (
+ {source}
+ ))}
+
+
+ )}
+
+
+ Funções disponíveis
+
+ {selectedConnection.funcoes.map((func) => (
+
+
+ {func}
+
+ ))}
+
+
- {/* Alerts/Warnings */}
{selectedConnection.avisos && (
-
-
-
-
-
- Avisos Importantes de Engenharia
-
- {selectedConnection.avisos.map((aviso, index) => (
-
- •
- {aviso}
-
- ))}
-
-
-
+
+
+
+ Avisos de engenharia
+
+
+ {selectedConnection.avisos.map((aviso) => (
+ {aviso}
+ ))}
+
+
)}
- {/* Dialog Footer */}
-
-
setSelectedConnection(null)}
- className="w-full py-3.5 rounded-xl font-bold text-white text-xs uppercase tracking-wider shadow-lg hover:opacity-90 active:scale-98 transition-all cursor-pointer"
- style={{
- backgroundColor: serie.color,
- boxShadow: `0 8px 25px -8px ${serie.color}`
- }}
- aria-label="Close modal"
- >
- Confirmar e Fechar
-
+
+ Fechar
,
document.body
)}
-
-
);
-}
\ No newline at end of file
+}
diff --git a/components/Footer.jsx b/components/Footer.jsx
index 3a0d683..9f9d3e5 100644
--- a/components/Footer.jsx
+++ b/components/Footer.jsx
@@ -1,164 +1,166 @@
-"use client";
-
-import { useEffect, useState } from "react";
import Link from "next/link";
-import { ExternalLink } from "lucide-react";
+import { ArrowUpRight } from "lucide-react";
+import { SITE } from "@/lib/seo";
+
+const COLUMNS = [
+ {
+ title: "Explorar",
+ links: [
+ { href: "/series", label: "Séries ESP32" },
+ { href: "/comparacao", label: "Comparador" },
+ { href: "/catalogo", label: "Catálogo de placas" },
+ { href: "/livro", label: "O livro" },
+ ],
+ },
+ {
+ title: "Ferramentas",
+ links: [
+ { href: "/seletor", label: "Seletor inteligente" },
+ { href: "/diagnostico", label: "Diagnóstico" },
+ { href: "/componentes", label: "Component Registry" },
+ { href: "/frameworks", label: "Frameworks e SDKs" },
+ ],
+ },
+ {
+ title: "Projeto",
+ links: [
+ { href: "/sobre", label: "Sobre o ESPDocs" },
+ { href: "/glossario", label: "Glossário" },
+ { href: SITE.repositorio, label: "Repositório", external: true },
+ { href: `${SITE.repositorio}/issues`, label: "Reportar um problema", external: true },
+ ],
+ },
+];
export default function Footer() {
- const [year, setYear] = useState(2026);
-
- useEffect(() => {
- setYear(new Date().getFullYear());
- }, []);
+ const year = new Date().getFullYear();
return (
-
-
-
-
- {/* Coluna 1: Logo & Info */}
-
-
- ESPDocs
-
-
- Plataforma brasileira e independente que centraliza e organiza informações técnicas sobre o ecossistema ESP32. Focada em clareza, usabilidade e agilidade no desenvolvimento de hardware.
+
+
+
+
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
ESPDocs
+
+
+ Documentação independente do ecossistema ESP32, escrita em português brasileiro.
+ Dados públicos, curadoria editorial e links para as fontes oficiais.
-
-
- {/* Coluna 2: Links Úteis */}
-
-
- Recursos
-
-
-
+
+ {COLUMNS.map((column) => (
+
+ {column.title}
+
+ {column.links.map((link) => (
+
+ {link.external ? (
+
+ {link.label}
+
+
+ ) : (
+
+ {link.label}
+
+ )}
+
+ ))}
+
+
+ ))}
- {/* Coluna 3: Autor & Social */}
-
- {/* Divisor & Isenção */}
-
-
- © {year} ESPDocs. Esta plataforma independente de código aberto não possui afiliação com a Espressif Systems.
-
-
- Feito para a comunidade maker 🇧🇷
+
);
-}
\ No newline at end of file
+}
diff --git a/components/Header.jsx b/components/Header.jsx
index e1b3c11..7a63f50 100644
--- a/components/Header.jsx
+++ b/components/Header.jsx
@@ -1,689 +1,301 @@
"use client";
-import { usePathname } from "next/navigation";
-import { useState, useEffect, useRef } from "react";
-import Link from "next/link";
-import { useTheme } from "next-themes";
+import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
-import {
- Home,
- Info,
- BookOpen,
- Cpu,
- SlidersHorizontal,
- Terminal,
- Scale,
- ShoppingBag,
- AlertTriangle,
- Menu,
- X,
- Sun,
- Moon,
- ChevronDown,
- Sparkles,
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { useTheme } from "next-themes";
+import {
ArrowRight,
- ExternalLink,
- Compass,
- Layers,
- Box
+ BookA,
+ BookOpen,
+ Box,
+ ChevronDown,
+ Cpu,
+ Library,
+ Menu,
+ Moon,
+ Scale,
+ Search,
+ SlidersHorizontal,
+ Sparkles,
+ Sun,
+ X,
+ AlertTriangle,
} from "lucide-react";
+const MENUS = [
+ {
+ id: "hardware",
+ label: "Hardware",
+ items: [
+ { href: "/series", label: "Séries ESP32", desc: "12 chips, pinagem e especificações", icon: Cpu },
+ { href: "/comparacao", label: "Comparador", desc: "Até quatro séries lado a lado", icon: Scale },
+ { href: "/catalogo", label: "Catálogo de placas", desc: "DevKits, pinos e onde comprar", icon: Box },
+ ],
+ },
+ {
+ id: "tools",
+ label: "Ferramentas",
+ items: [
+ { href: "/seletor", label: "Seletor inteligente", desc: "O chip certo para o seu projeto", icon: SlidersHorizontal },
+ { href: "/diagnostico", label: "Diagnóstico", desc: "Brownout, bootloop, WDT e mais", icon: AlertTriangle },
+ { href: "/componentes", label: "Component Registry", desc: "Drivers e manifests idf_component.yml", icon: Search },
+ ],
+ },
+ {
+ id: "library",
+ label: "Biblioteca",
+ items: [
+ { href: "/frameworks", label: "Frameworks e SDKs", desc: "ESP-IDF, Arduino, Rust e Matter", icon: BookOpen },
+ { href: "/glossario", label: "Glossário técnico", desc: "Os termos que aparecem nos datasheets", icon: BookA },
+ { href: "/sobre", label: "Sobre o projeto", desc: "Autoria, método e abertura", icon: Sparkles },
+ ],
+ },
+];
+
+const MOBILE_LINKS = [
+ { href: "/series", label: "Séries ESP32" },
+ { href: "/comparacao", label: "Comparador" },
+ { href: "/seletor", label: "Seletor inteligente" },
+ { href: "/diagnostico", label: "Diagnóstico" },
+ { href: "/frameworks", label: "Frameworks e SDKs" },
+ { href: "/componentes", label: "Component Registry" },
+ { href: "/glossario", label: "Glossário" },
+ { href: "/catalogo", label: "Catálogo de placas" },
+ { href: "/livro", label: "O livro" },
+ { href: "/sobre", label: "Sobre" },
+];
+
+function isActivePath(pathname, href) {
+ return href === "/" ? pathname === "/" : pathname === href || String(pathname).startsWith(`${href}/`);
+}
+
export default function Header() {
- const [drawerOpen, setDrawerOpen] = useState(false);
- const [hardwareDropdown, setHardwareDropdown] = useState(false);
- const [toolsDropdown, setToolsDropdown] = useState(false);
- const [docsDropdown, setDocsDropdown] = useState(false);
- const [mounted, setMounted] = useState(false);
const pathname = usePathname();
+ const [mounted, setMounted] = useState(false);
+ const [openMenu, setOpenMenu] = useState(null);
+ const [drawerOpen, setDrawerOpen] = useState(false);
+ const navRef = useRef(null);
+ const closeRef = useRef(null);
+ const menuButtonRef = useRef(null);
- const hardwareRef = useRef(null);
- const toolsRef = useRef(null);
- const docsRef = useRef(null);
+ useEffect(() => setMounted(true), []);
useEffect(() => {
- setMounted(true);
- }, []);
+ setOpenMenu(null);
+ setDrawerOpen(false);
+ }, [pathname]);
- // Close dropdowns on outside click
useEffect(() => {
- function handleClickOutside(e) {
- if (hardwareRef.current && !hardwareRef.current.contains(e.target)) {
- setHardwareDropdown(false);
- }
- if (toolsRef.current && !toolsRef.current.contains(e.target)) {
- setToolsDropdown(false);
- }
- if (docsRef.current && !docsRef.current.contains(e.target)) {
- setDocsDropdown(false);
+ function onPointerDown(event) {
+ if (navRef.current && !navRef.current.contains(event.target)) setOpenMenu(null);
+ }
+ function onKeyDown(event) {
+ if (event.key === "Escape") {
+ setOpenMenu(null);
+ setDrawerOpen(false);
}
}
- document.addEventListener("mousedown", handleClickOutside);
- return () => document.removeEventListener("mousedown", handleClickOutside);
- }, []);
-
- // Lock scroll when mobile menu is open
- useEffect(() => {
- if (drawerOpen) {
- document.body.style.overflow = "hidden";
- } else {
- document.body.style.overflow = "";
- }
+ document.addEventListener("mousedown", onPointerDown);
+ document.addEventListener("keydown", onKeyDown);
return () => {
- document.body.style.overflow = "";
+ document.removeEventListener("mousedown", onPointerDown);
+ document.removeEventListener("keydown", onKeyDown);
+ };
+ }, []);
+
+ useEffect(() => {
+ if (!drawerOpen) return;
+ const previous = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ closeRef.current?.focus();
+ return () => {
+ document.body.style.overflow = previous;
+ menuButtonRef.current?.focus();
};
}, [drawerOpen]);
- // Close menu on route change
- useEffect(() => {
- setDrawerOpen(false);
- setHardwareDropdown(false);
- setToolsDropdown(false);
- setDocsDropdown(false);
- }, [pathname]);
-
- function isActive(href) {
- if (href === "/") {
- return pathname === "/";
- }
- return String(pathname).startsWith(href);
- }
-
- const popularChips = [
- { name: "ESP32", desc: "Clássico Dual-Core", href: "/series/ESP32" },
- { name: "ESP32-S3", desc: "IA & USB Nativo", href: "/series/ESP32-S3" },
- { name: "ESP32-C6", desc: "Wi-Fi 6 + Zigbee/Thread", href: "/series/ESP32-C6" },
- { name: "ESP32-P4", desc: "Alto Desempenho 400MHz", href: "/series/ESP32-P4" },
- ];
-
return (
-