diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a4dfa..08decdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,24 @@ Tudo o que muda de relevante no ESPDocs está aqui. O formato segue o ## [Não lançado] +## [1.9.0] - 2026-11-06 + +### Adicionado + +- Comparador de séries mais visual, com três painéis acima da tabela de especificações, que + acompanham os chips escolhidos: + - Radar com notas de 0 a 10 em desempenho, memória, conectividade, baixo consumo, + IA/TinyML, periféricos e segurança. As notas são editoriais, baseadas nos datasheets, e + também aparecem numa tabela. Ao passar o mouse sobre um chip, os outros esmaecem. + - Consumo de energia: barras de corrente em deep sleep e light sleep, em escala + logarítmica, e a duração teórica de uma bateria de 2000 mAh em deep sleep, com o aviso + de que a autodescarga e a placa encurtam esse tempo. + - Suporte a TinyML: TensorFlow Lite Micro, ESP-DL, ESP-NN acelerado, instruções vetoriais + e ESP-SR, marcados como Sim, Parcial, Não ou Previsto. +- Os números ficam em `data/comparacao-metricas.js`, separados dos textos traduzidos. +- Quatro cores de série para gráficos (`--serie-1` a `--serie-4`), com variantes para os + temas claro e escuro e validadas para daltonismo. + ## [1.8.0] - 2026-11-03 ### Adicionado @@ -327,7 +345,8 @@ entrada resume tudo desde o primeiro commit, em 2025-10-21, até a imagem Docker otimização de imagens (GHSA-2464-fhr4-w9xw), e `postcss` fixado acima da cópia embutida no `next`. -[Não lançado]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.8.0...HEAD +[Não lançado]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.9.0...HEAD +[1.9.0]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.8.0...v1.9.0 [1.8.0]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.7.0...v1.8.0 [1.7.0]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.6.1...v1.7.0 [1.6.1]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.6.0...v1.6.1 diff --git a/app/[locale]/comparacao/page.jsx b/app/[locale]/comparacao/page.jsx index 1ffd29e..5cb1dcc 100644 --- a/app/[locale]/comparacao/page.jsx +++ b/app/[locale]/comparacao/page.jsx @@ -4,6 +4,7 @@ import Header from "@/components/Header"; import Footer from "@/components/Footer"; import PlacasSugeridas from "@/components/PlacasSugeridas"; import PageHero from "@/components/PageHero"; +import ComparacaoGraficos from "@/components/ComparacaoGraficos"; import { useEffect, useState, useMemo, Fragment } from "react"; import { usePathname } from "next/navigation"; import Link from "next/link"; @@ -651,6 +652,10 @@ export default function Comparacao() { +
+ +
+

diff --git a/app/globals.css b/app/globals.css index 87a5828..f1561c0 100644 --- a/app/globals.css +++ b/app/globals.css @@ -134,6 +134,11 @@ --ok-soft: #e8f6ee; --danger: #9b1c1c; --danger-soft: #fdeded; + /* Séries dos gráficos (paleta categórica validada para daltonismo). */ + --serie-1: #2a78d6; + --serie-2: #eb6834; + --serie-3: #1baf7a; + --serie-4: #eda100; --code-bg: #07090d; --code-ink: #f4f6f8; --glow-a: rgb(13 148 136 / 0.1); @@ -170,6 +175,10 @@ --ok-soft: #0f2a1c; --danger: #ff9b9b; --danger-soft: #331517; + --serie-1: #3987e5; + --serie-2: #d95926; + --serie-3: #199e70; + --serie-4: #c98500; --code-bg: #040609; --code-ink: #f4f6f8; --glow-a: rgb(45 212 191 / 0.1); diff --git a/components/ComparacaoGraficos.jsx b/components/ComparacaoGraficos.jsx new file mode 100644 index 0000000..53999b1 --- /dev/null +++ b/components/ComparacaoGraficos.jsx @@ -0,0 +1,298 @@ +"use client"; + +import { useState } from "react"; +import { Check, Minus, CircleDashed, Clock } from "lucide-react"; +import { EIXOS_RADAR, METRICAS } from "@/data/comparacao-metricas"; + +const CORES = ["var(--serie-1)", "var(--serie-2)", "var(--serie-3)", "var(--serie-4)"]; + +const T = { + pt: { + visao: "Visão geral", visaoLede: "Notas de 0 a 10 em cada área, relativas às outras séries da Espressif. Quanto maior a área, mais completo o chip, mas repare em onde cada um se destaca.", + eixos: { desempenho: "Desempenho", memoria: "Memória", conectividade: "Conectividade", consumo: "Baixo consumo", ia: "IA / TinyML", perifericos: "Periféricos", seguranca: "Segurança" }, + notaEditorial: "Notas editoriais do ESPDocs, baseadas nos datasheets.", verNotas: "Ver as notas em tabela", area: "Área", + consumo: "Consumo de energia", consumoLede: "Corrente típica do chip dormindo, em escala logarítmica: cada linha da grade é dez vezes a anterior. Barra menor dura mais na bateria.", + deep: "Deep sleep", light: "Light sleep", nd: "não divulgado", + bateria: `Duração teórica de uma bateria de 2000 mAh só em deep sleep. Na prática, a autodescarga e o resto da placa encurtam muito:`, anos: "anos", meses: "meses", + tinyml: "Suporte a TinyML", tinymlLede: "O que roda de aprendizado de máquina embarcado em cada chip, pelo suporte oficial da Espressif.", + recurso: "Recurso", + recursos: { tflm: "TensorFlow Lite Micro", espDl: "ESP-DL (modelos otimizados)", espNn: "ESP-NN com kernels acelerados", vetorial: "Instruções vetoriais / IA", espSr: "ESP-SR (palavra de ativação, comandos de voz)" }, + estado: { sim: "Sim", parcial: "Parcial", nao: "Não", previsto: "Previsto" }, + tinymlNota: "Parcial: roda com limitações (pouca RAM no C2, versões antigas da ESP-DL no ESP32, kernels genéricos do ESP-NN). Previsto: chip anunciado, suporte ainda em desenvolvimento.", + }, + en: { + visao: "Overview", visaoLede: "Scores from 0 to 10 in each area, relative to the other Espressif series. A larger area means a more complete chip, but look at where each one stands out.", + eixos: { desempenho: "Performance", memoria: "Memory", conectividade: "Connectivity", consumo: "Low power", ia: "AI / TinyML", perifericos: "Peripherals", seguranca: "Security" }, + notaEditorial: "ESPDocs editorial scores, based on the datasheets.", verNotas: "Show scores as a table", area: "Area", + consumo: "Power consumption", consumoLede: "Typical current while the chip sleeps, on a logarithmic scale: each grid line is ten times the previous one. A shorter bar lasts longer on battery.", + deep: "Deep sleep", light: "Light sleep", nd: "not disclosed", + bateria: `Theoretical life of a 2000 mAh battery in deep sleep alone. In practice, self-discharge and the rest of the board cut it a lot:`, anos: "years", meses: "months", + tinyml: "TinyML support", tinymlLede: "What embedded machine learning runs on each chip, according to Espressif's official support.", + recurso: "Feature", + recursos: { tflm: "TensorFlow Lite Micro", espDl: "ESP-DL (optimized models)", espNn: "ESP-NN with accelerated kernels", vetorial: "Vector / AI instructions", espSr: "ESP-SR (wake word, voice commands)" }, + estado: { sim: "Yes", parcial: "Partial", nao: "No", previsto: "Planned" }, + tinymlNota: "Partial: runs with limitations (little RAM on the C2, older ESP-DL releases on the ESP32, generic ESP-NN kernels). Planned: chip announced, support still in development.", + }, + es: { + visao: "Visión general", visaoLede: "Notas de 0 a 10 en cada área, relativas a las demás series de Espressif. Cuanto mayor el área, más completo el chip, pero fíjese en dónde destaca cada uno.", + eixos: { desempenho: "Rendimiento", memoria: "Memoria", conectividade: "Conectividad", consumo: "Bajo consumo", ia: "IA / TinyML", perifericos: "Periféricos", seguranca: "Seguridad" }, + notaEditorial: "Notas editoriales de ESPDocs, basadas en las hojas de datos.", verNotas: "Ver las notas en tabla", area: "Área", + consumo: "Consumo de energía", consumoLede: "Corriente típica del chip en reposo, en escala logarítmica: cada línea de la cuadrícula es diez veces la anterior. Una barra menor dura más con batería.", + deep: "Deep sleep", light: "Light sleep", nd: "no divulgado", + bateria: `Duración teórica de una batería de 2000 mAh solo en deep sleep. En la práctica, la autodescarga y el resto de la placa la reducen mucho:`, anos: "años", meses: "meses", + tinyml: "Soporte de TinyML", tinymlLede: "Qué aprendizaje automático embebido corre en cada chip, según el soporte oficial de Espressif.", + recurso: "Recurso", + recursos: { tflm: "TensorFlow Lite Micro", espDl: "ESP-DL (modelos optimizados)", espNn: "ESP-NN con kernels acelerados", vetorial: "Instrucciones vectoriales / IA", espSr: "ESP-SR (palabra de activación, comandos de voz)" }, + estado: { sim: "Sí", parcial: "Parcial", nao: "No", previsto: "Previsto" }, + tinymlNota: "Parcial: funciona con limitaciones (poca RAM en el C2, versiones antiguas de ESP-DL en el ESP32, kernels genéricos de ESP-NN). Previsto: chip anunciado, soporte aún en desarrollo.", + }, + fr: { + visao: "Vue d'ensemble", visaoLede: "Notes de 0 à 10 dans chaque domaine, relatives aux autres séries Espressif. Plus la surface est grande, plus la puce est complète, mais regardez où chacune se distingue.", + eixos: { desempenho: "Performances", memoria: "Mémoire", conectividade: "Connectivité", consumo: "Basse conso.", ia: "IA / TinyML", perifericos: "Périphériques", seguranca: "Sécurité" }, + notaEditorial: "Notes éditoriales d'ESPDocs, d'après les fiches techniques.", verNotas: "Voir les notes en tableau", area: "Domaine", + consumo: "Consommation d'énergie", consumoLede: "Courant typique de la puce en veille, en échelle logarithmique : chaque ligne de la grille vaut dix fois la précédente. Une barre plus courte tient plus longtemps sur batterie.", + deep: "Deep sleep", light: "Light sleep", nd: "non publié", + bateria: `Durée théorique d'une batterie de 2000 mAh en deep sleep seul. En pratique, l'autodécharge et le reste de la carte la réduisent beaucoup :`, anos: "ans", meses: "mois", + tinyml: "Prise en charge de TinyML", tinymlLede: "Ce qui tourne en apprentissage automatique embarqué sur chaque puce, selon le support officiel d'Espressif.", + recurso: "Fonction", + recursos: { tflm: "TensorFlow Lite Micro", espDl: "ESP-DL (modèles optimisés)", espNn: "ESP-NN avec noyaux accélérés", vetorial: "Instructions vectorielles / IA", espSr: "ESP-SR (mot d'éveil, commandes vocales)" }, + estado: { sim: "Oui", parcial: "Partiel", nao: "Non", previsto: "Prévu" }, + tinymlNota: "Partiel : fonctionne avec des limites (peu de RAM sur le C2, anciennes versions d'ESP-DL sur l'ESP32, noyaux ESP-NN génériques). Prévu : puce annoncée, support en cours de développement.", + }, + de: { + visao: "Überblick", visaoLede: "Noten von 0 bis 10 je Bereich, relativ zu den anderen Espressif-Serien. Je größer die Fläche, desto vollständiger der Chip, aber achten Sie darauf, wo jeder glänzt.", + eixos: { desempenho: "Leistung", memoria: "Speicher", conectividade: "Konnektivität", consumo: "Geringer Verbrauch", ia: "KI / TinyML", perifericos: "Peripherie", seguranca: "Sicherheit" }, + notaEditorial: "Redaktionelle Noten von ESPDocs, basierend auf den Datenblättern.", verNotas: "Noten als Tabelle anzeigen", area: "Bereich", + consumo: "Energieverbrauch", consumoLede: "Typischer Strom des schlafenden Chips, logarithmisch: jede Gitterlinie ist das Zehnfache der vorigen. Ein kürzerer Balken hält länger mit Batterie.", + deep: "Deep Sleep", light: "Light Sleep", nd: "nicht veröffentlicht", + bateria: `Theoretische Laufzeit eines 2000-mAh-Akkus allein im Deep Sleep. In der Praxis verkürzen Selbstentladung und der Rest der Platine sie stark:`, anos: "Jahre", meses: "Monate", + tinyml: "TinyML-Unterstützung", tinymlLede: "Welches eingebettete maschinelle Lernen auf jedem Chip läuft, laut offiziellem Espressif-Support.", + recurso: "Funktion", + recursos: { tflm: "TensorFlow Lite Micro", espDl: "ESP-DL (optimierte Modelle)", espNn: "ESP-NN mit beschleunigten Kernels", vetorial: "Vektor- / KI-Befehle", espSr: "ESP-SR (Aktivierungswort, Sprachbefehle)" }, + estado: { sim: "Ja", parcial: "Teilweise", nao: "Nein", previsto: "Geplant" }, + tinymlNota: "Teilweise: läuft mit Einschränkungen (wenig RAM beim C2, ältere ESP-DL-Versionen beim ESP32, generische ESP-NN-Kernels). Geplant: Chip angekündigt, Unterstützung in Entwicklung.", + }, + "zh-CN": { + visao: "总览", visaoLede: "各领域 0 到 10 分,相对于乐鑫其他系列。面积越大芯片越全面,但请留意每款芯片的强项。", + eixos: { desempenho: "性能", memoria: "内存", conectividade: "连接", consumo: "低功耗", ia: "AI / TinyML", perifericos: "外设", seguranca: "安全" }, + notaEditorial: "ESPDocs 根据数据手册给出的编辑评分。", verNotas: "以表格查看评分", area: "领域", + consumo: "功耗", consumoLede: "芯片休眠时的典型电流,对数刻度:每条网格线是上一条的十倍。条越短,电池续航越长。", + deep: "Deep sleep", light: "Light sleep", nd: "未公布", + bateria: `2000 mAh 电池仅在 deep sleep 下的理论续航。实际上自放电和板上其他元件会大幅缩短:`, anos: "年", meses: "个月", + tinyml: "TinyML 支持", tinymlLede: "根据乐鑫官方支持,每款芯片能运行哪些嵌入式机器学习。", + recurso: "功能", + recursos: { tflm: "TensorFlow Lite Micro", espDl: "ESP-DL(优化模型)", espNn: "ESP-NN 加速内核", vetorial: "向量 / AI 指令", espSr: "ESP-SR(唤醒词、语音命令)" }, + estado: { sim: "是", parcial: "部分", nao: "否", previsto: "计划中" }, + tinymlNota: "部分:可运行但有限制(C2 内存小、ESP32 仅旧版 ESP-DL、ESP-NN 通用内核)。计划中:芯片已发布,支持仍在开发。", + }, +}; + +/* Deep sleep puro com 2000 mAh, só para dar escala humana à corrente. */ +function duracaoBateria(uA, t) { + const horas = 2000 / (uA / 1000); + const anos = horas / 8760; + return anos >= 1 ? `${Math.round(anos)} ${t.anos}` : `${Math.round(anos * 12)} ${t.meses}`; +} + +function Radar({ chips, t }) { + const [foco, setFoco] = useState(null); + const C = 170, R = 112; + const n = EIXOS_RADAR.length; + const ponto = (i, v) => { + const a = -Math.PI / 2 + (i * 2 * Math.PI) / n; + return [C + Math.cos(a) * R * (v / 10), C + Math.sin(a) * R * (v / 10)]; + }; + return ( + + {[2, 4, 6, 8, 10].map((v) => ( + ponto(i, v).join(",")).join(" ")} fill="none" stroke="var(--line)" strokeWidth={1} /> + ))} + {EIXOS_RADAR.map((eixo, i) => { + const [x, y] = ponto(i, 10); + const [lx, ly] = ponto(i, 12.4); + const anchor = Math.abs(lx - C) < 8 ? "middle" : lx > C ? "start" : "end"; + return ( + + + + {t.eixos[eixo]} + + + ); + })} + {chips.map((chip, ci) => { + const notas = METRICAS[chip]?.notas; + if (!notas) return null; + const pts = EIXOS_RADAR.map((e, i) => ponto(i, notas[e])); + const apagado = foco !== null && foco !== chip; + return ( + setFoco(chip)} onMouseLeave={() => setFoco(null)} style={{ opacity: apagado ? 0.25 : 1, transition: "opacity .15s" }}> + p.join(",")).join(" ")} fill={CORES[ci]} fillOpacity={0.12} stroke={CORES[ci]} strokeWidth={2} strokeLinejoin="round" /> + {pts.map(([x, y], i) => ( + + {`${chip} · ${t.eixos[EIXOS_RADAR[i]]}: ${notas[EIXOS_RADAR[i]]}/10`} + + ))} + + ); + })} + + ); +} + +function Legenda({ chips }) { + return ( +
    + {chips.map((chip, i) => ( +
  • +
  • + ))} +
+ ); +} + +/* Escala log de 1 µA a 1000 µA. */ +function BarrasConsumo({ titulo, campo, chips, t, mostrarBateria }) { + const pct = (v) => Math.max(2, (Math.log10(Math.max(v, 1)) / 3) * 100); + return ( +
+

{titulo}

+
+ + {mostrarBateria && ( +
+

{t.bateria}

+
    + {chips.filter((c) => METRICAS[c]?.deepSleep != null).map((c) => ( +
  • {c} ≈ {duracaoBateria(METRICAS[c].deepSleep, t)}
  • + ))} +
+
+ )} +
+ ); +} + +function Estado({ valor, t }) { + const map = { + sim: { Icon: Check, cls: "text-ok font-medium" }, + parcial: { Icon: CircleDashed, cls: "text-gold font-medium" }, + previsto: { Icon: Clock, cls: "text-muted" }, + nao: { Icon: Minus, cls: "text-muted" }, + }; + const { Icon, cls } = map[valor] ?? map.nao; + return ( + + + ); +} + +export default function ComparacaoGraficos({ chips, locale = "pt" }) { + const t = T[locale] ?? T.pt; + return ( +
+
+

{t.visao}

+

{t.visaoLede}

+
+ +
+ +

{t.notaEditorial}

+
+ {t.verNotas} + + + + + {chips.map((c) => )} + + + + {EIXOS_RADAR.map((e) => ( + + + {chips.map((c) => )} + + ))} + +
{t.area}{c.replace("ESP32-", "")}
{t.eixos[e]}{METRICAS[c]?.notas[e] ?? "–"}
+
+
+
+
+ +
+
+

{t.consumo}

+

{t.consumoLede}

+
+ + +
+
+ +
+

{t.tinyml}

+

{t.tinymlLede}

+
+ + + + + {chips.map((c, i) => ( + + ))} + + + + {Object.keys(t.recursos).map((r) => ( + + + {chips.map((c) => )} + + ))} + +
{t.recurso} + + +
{t.recursos[r]}
+
+

{t.tinymlNota}

+
+
+
+ ); +} diff --git a/data/comparacao-metricas.js b/data/comparacao-metricas.js new file mode 100644 index 0000000..4bbd95c --- /dev/null +++ b/data/comparacao-metricas.js @@ -0,0 +1,40 @@ +/* + * Métricas numéricas da página de comparação, independentes de idioma. + * + * Correntes em µA, tiradas do menor valor típico dos datasheets (as mesmas + * faixas que aparecem em series.json > consumo_energia). null = não divulgado. + * + * As notas do radar (0 a 10) são editoriais do ESPDocs: posicionam cada série + * em relação às outras da Espressif, não medem nada em absoluto. + * + * TinyML: "sim" | "parcial" | "nao" | "previsto". + */ + +export const EIXOS_RADAR = ["desempenho", "memoria", "conectividade", "consumo", "ia", "perifericos", "seguranca"]; + +export const METRICAS = { + "ESP32": { deepSleep: 10, lightSleep: 800, notas: { desempenho: 6, memoria: 5, conectividade: 6, consumo: 3, ia: 5, perifericos: 7, seguranca: 3 }, + tinyml: { tflm: "sim", espDl: "parcial", espNn: "parcial", vetorial: "nao", espSr: "sim" } }, + "ESP32-S2": { deepSleep: 20, lightSleep: 750, notas: { desempenho: 4, memoria: 6, conectividade: 3, consumo: 3, ia: 3, perifericos: 6, seguranca: 6 }, + tinyml: { tflm: "sim", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, + "ESP32-S3": { deepSleep: 7, lightSleep: 240, notas: { desempenho: 7, memoria: 8, conectividade: 6, consumo: 6, ia: 9, perifericos: 8, seguranca: 7 }, + tinyml: { tflm: "sim", espDl: "sim", espNn: "sim", vetorial: "sim", espSr: "sim" } }, + "ESP32-S31": { deepSleep: null, lightSleep: 100, notas: { desempenho: 8, memoria: 9, conectividade: 10, consumo: 5, ia: 9, perifericos: 9, seguranca: 9 }, + tinyml: { tflm: "previsto", espDl: "previsto", espNn: "previsto", vetorial: "sim", espSr: "previsto" } }, + "ESP32-C2": { deepSleep: 5, lightSleep: 140, notas: { desempenho: 2, memoria: 2, conectividade: 4, consumo: 8, ia: 1, perifericos: 2, seguranca: 4 }, + tinyml: { tflm: "parcial", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, + "ESP32-C3": { deepSleep: 5, lightSleep: 130, notas: { desempenho: 3, memoria: 4, conectividade: 5, consumo: 7, ia: 3, perifericos: 4, seguranca: 6 }, + tinyml: { tflm: "sim", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, + "ESP32-C5": { deepSleep: 12, lightSleep: 60, notas: { desempenho: 5, memoria: 6, conectividade: 9, consumo: 7, ia: 4, perifericos: 6, seguranca: 8 }, + tinyml: { tflm: "sim", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, + "ESP32-C6": { deepSleep: 7, lightSleep: 35, notas: { desempenho: 3, memoria: 5, conectividade: 8, consumo: 9, ia: 4, perifericos: 6, seguranca: 7 }, + tinyml: { tflm: "sim", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, + "ESP32-C61": { deepSleep: 10, lightSleep: 50, notas: { desempenho: 3, memoria: 5, conectividade: 6, consumo: 8, ia: 3, perifericos: 5, seguranca: 7 }, + tinyml: { tflm: "sim", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, + "ESP32-P4": { deepSleep: 12, lightSleep: 75, notas: { desempenho: 10, memoria: 10, conectividade: 2, consumo: 6, ia: 10, perifericos: 10, seguranca: 9 }, + tinyml: { tflm: "sim", espDl: "sim", espNn: "sim", vetorial: "sim", espSr: "sim" } }, + "ESP32-H2": { deepSleep: 7, lightSleep: 25, notas: { desempenho: 1, memoria: 3, conectividade: 5, consumo: 10, ia: 2, perifericos: 4, seguranca: 7 }, + tinyml: { tflm: "sim", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, + "ESP32-H4": { deepSleep: null, lightSleep: null, notas: { desempenho: 2, memoria: 4, conectividade: 5, consumo: 8, ia: 2, perifericos: 5, seguranca: 8 }, + tinyml: { tflm: "previsto", espDl: "nao", espNn: "nao", vetorial: "nao", espSr: "nao" } }, +};