2 Commits
Author SHA1 Message Date
Lucas RayanandClaude Opus 5.5 df39436dcb fix(comparacao): eixo do gráfico de consumo em text-xs para passar na auditoria de design
CI / Lint e auditorias (push) Successful in 1m26s
CI / Trivy (dependências e segredos) (push) Successful in 18s
CI / Imagem Docker (linux/arm64) (push) Successful in 4m26s
Release / Deploy no Coolify (push) Skipped
Release / Plano (reaproveitar a imagem?) (push) Successful in 7s
Release / ci (push) Skipped
Release / Publica a imagem (linux/arm64) (push) Successful in 19s
Release / Cria a release no Gitea (push) Successful in 7s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 11:28:18 -03:00
Lucas RayanandClaude Opus 5.5 cc52b6e978 feat(comparacao): radar de notas, gráfico de consumo e suporte a TinyML; release 1.9.0
Release / Plano (reaproveitar a imagem?) (push) Successful in 12s
CI / Lint e auditorias (push) Failing after 2m21s
CI / Trivy (dependências e segredos) (push) Successful in 26s
Release / ci (push) Failing after 2m47s
CI / Imagem Docker (linux/arm64) (push) Skipped
Release / Publica a imagem (linux/arm64) (push) Skipped
Release / Cria a release no Gitea (push) Skipped
Release / Deploy no Coolify (push) Skipped
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 11:23:40 -03:00
5 changed files with 372 additions and 1 deletions

No files matched your search

+20 -1
View File
@@ -6,6 +6,24 @@ Tudo o que muda de relevante no ESPDocs está aqui. O formato segue o
## [Não lançado] ## [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 ## [1.8.0] - 2026-11-03
### Adicionado ### 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 otimização de imagens (GHSA-2464-fhr4-w9xw), e `postcss` fixado acima da cópia
embutida no `next`. 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.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.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 [1.6.1]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.6.0...v1.6.1
+5
View File
@@ -4,6 +4,7 @@ import Header from "@/components/Header";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
import PlacasSugeridas from "@/components/PlacasSugeridas"; import PlacasSugeridas from "@/components/PlacasSugeridas";
import PageHero from "@/components/PageHero"; import PageHero from "@/components/PageHero";
import ComparacaoGraficos from "@/components/ComparacaoGraficos";
import { useEffect, useState, useMemo, Fragment } from "react"; import { useEffect, useState, useMemo, Fragment } from "react";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
@@ -651,6 +652,10 @@ export default function Comparacao() {
</div> </div>
</section> </section>
<div className="shell mt-8">
<ComparacaoGraficos chips={selectedSeries} locale={currentLocale} />
</div>
<section className="shell mt-8" aria-labelledby="tabela-titulo"> <section className="shell mt-8" aria-labelledby="tabela-titulo">
<div className="flex flex-wrap items-center justify-between gap-3"> <div className="flex flex-wrap items-center justify-between gap-3">
<h2 id="tabela-titulo" className="text-base font-semibold text-ink"> <h2 id="tabela-titulo" className="text-base font-semibold text-ink">
+9
View File
@@ -134,6 +134,11 @@
--ok-soft: #e8f6ee; --ok-soft: #e8f6ee;
--danger: #9b1c1c; --danger: #9b1c1c;
--danger-soft: #fdeded; --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-bg: #07090d;
--code-ink: #f4f6f8; --code-ink: #f4f6f8;
--glow-a: rgb(13 148 136 / 0.1); --glow-a: rgb(13 148 136 / 0.1);
@@ -170,6 +175,10 @@
--ok-soft: #0f2a1c; --ok-soft: #0f2a1c;
--danger: #ff9b9b; --danger: #ff9b9b;
--danger-soft: #331517; --danger-soft: #331517;
--serie-1: #3987e5;
--serie-2: #d95926;
--serie-3: #199e70;
--serie-4: #c98500;
--code-bg: #040609; --code-bg: #040609;
--code-ink: #f4f6f8; --code-ink: #f4f6f8;
--glow-a: rgb(45 212 191 / 0.1); --glow-a: rgb(45 212 191 / 0.1);
+298
View File
@@ -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 (
<svg viewBox="-70 0 480 340" className="mx-auto w-full max-w-xl" role="img" aria-label={`${t.visao}: ${chips.join(", ")}`}>
{[2, 4, 6, 8, 10].map((v) => (
<polygon key={v} points={EIXOS_RADAR.map((_, i) => 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 (
<g key={eixo}>
<line x1={C} y1={C} x2={x} y2={y} stroke="var(--line)" strokeWidth={1} />
<text x={lx} y={ly} textAnchor={anchor} dominantBaseline="middle" className="fill-[var(--muted)] text-[13px] font-medium">
{t.eixos[eixo]}
</text>
</g>
);
})}
{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 (
<g key={chip} onMouseEnter={() => setFoco(chip)} onMouseLeave={() => setFoco(null)} style={{ opacity: apagado ? 0.25 : 1, transition: "opacity .15s" }}>
<polygon points={pts.map((p) => p.join(",")).join(" ")} fill={CORES[ci]} fillOpacity={0.12} stroke={CORES[ci]} strokeWidth={2} strokeLinejoin="round" />
{pts.map(([x, y], i) => (
<circle key={i} cx={x} cy={y} r={4} fill={CORES[ci]} stroke="var(--surface)" strokeWidth={2}>
<title>{`${chip} · ${t.eixos[EIXOS_RADAR[i]]}: ${notas[EIXOS_RADAR[i]]}/10`}</title>
</circle>
))}
</g>
);
})}
</svg>
);
}
function Legenda({ chips }) {
return (
<ul className="flex flex-wrap gap-x-5 gap-y-2 text-sm font-medium text-ink">
{chips.map((chip, i) => (
<li key={chip} className="inline-flex items-center gap-2">
<span className="h-3 w-3 rounded-full" style={{ background: CORES[i] }} aria-hidden="true" />
{chip}
</li>
))}
</ul>
);
}
/* 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 (
<div>
<h4 className="text-sm font-semibold text-ink">{titulo}</h4>
<div className="relative mt-3">
<div className="pointer-events-none absolute inset-y-0 left-24 right-20 sm:left-28" aria-hidden="true">
{[0, 1, 2, 3].map((d) => (
<div key={d} className="absolute inset-y-0 border-l border-line" style={{ left: `${(d / 3) * 100}%` }} />
))}
</div>
<ul className="relative space-y-2.5">
{chips.map((chip, i) => {
const v = METRICAS[chip]?.[campo];
return (
<li key={chip} className="flex items-center gap-3 text-sm" title={v == null ? `${chip}: ${t.nd}` : `${chip}: ${v} µA`}>
<span className="w-21 shrink-0 font-medium text-ink sm:w-25">{chip}</span>
<span className="relative h-5 flex-1">
{v != null && <span className="absolute inset-y-0 left-0 rounded-r" style={{ width: `${pct(v)}%`, background: CORES[i] }} />}
</span>
<span className="w-17 shrink-0 text-right tabular-nums text-ink-soft">{v == null ? <span className="text-muted">{t.nd}</span> : `${v} µA`}</span>
</li>
);
})}
</ul>
<div className="relative mt-1.5 h-4 text-xs text-muted" aria-hidden="true">
<div className="absolute inset-y-0 left-24 right-20 sm:left-28">
{["1", "10", "100", "1000 µA"].map((r, d) => (
<span key={r} className="absolute -translate-x-1/2 whitespace-nowrap" style={{ left: `${(d / 3) * 100}%` }}>{r}</span>
))}
</div>
</div>
</div>
{mostrarBateria && (
<div className="mt-4 text-sm text-muted">
<p>{t.bateria}</p>
<ul className="mt-2 flex flex-wrap gap-x-5 gap-y-1">
{chips.filter((c) => METRICAS[c]?.deepSleep != null).map((c) => (
<li key={c}><span className="font-medium text-ink">{c}</span> ≈ {duracaoBateria(METRICAS[c].deepSleep, t)}</li>
))}
</ul>
</div>
)}
</div>
);
}
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 (
<span className={`inline-flex items-center gap-1.5 whitespace-nowrap ${cls}`}>
<Icon className="h-4 w-4" aria-hidden="true" />
{t.estado[valor] ?? t.estado.nao}
</span>
);
}
export default function ComparacaoGraficos({ chips, locale = "pt" }) {
const t = T[locale] ?? T.pt;
return (
<div className="space-y-8">
<section className="panel p-5 sm:p-7" aria-labelledby="radar-titulo">
<h2 id="radar-titulo" className="text-base font-semibold text-ink">{t.visao}</h2>
<p className="mt-1 max-w-2xl text-sm text-muted">{t.visaoLede}</p>
<div className="mt-5 grid items-center gap-6 lg:grid-cols-[1fr_auto]">
<Radar chips={chips} t={t} />
<div className="space-y-4 lg:w-64">
<Legenda chips={chips} />
<p className="text-xs text-muted">{t.notaEditorial}</p>
<details className="text-sm">
<summary className="min-h-11 cursor-pointer content-center font-medium text-ink-soft">{t.verNotas}</summary>
<table className="mt-2 w-full text-left text-xs">
<thead>
<tr className="border-b border-line text-muted">
<th scope="col" className="py-1.5 pr-2 font-medium">{t.area}</th>
{chips.map((c) => <th key={c} scope="col" className="py-1.5 pr-2 font-medium">{c.replace("ESP32-", "")}</th>)}
</tr>
</thead>
<tbody>
{EIXOS_RADAR.map((e) => (
<tr key={e} className="border-b border-line last:border-b-0">
<th scope="row" className="py-1.5 pr-2 font-medium text-muted">{t.eixos[e]}</th>
{chips.map((c) => <td key={c} className="py-1.5 pr-2 tabular-nums text-ink">{METRICAS[c]?.notas[e] ?? "–"}</td>)}
</tr>
))}
</tbody>
</table>
</details>
</div>
</div>
</section>
<div className="grid gap-8 lg:grid-cols-2">
<section className="panel p-5 sm:p-7" aria-labelledby="consumo-titulo">
<h2 id="consumo-titulo" className="text-base font-semibold text-ink">{t.consumo}</h2>
<p className="mt-1 text-sm text-muted">{t.consumoLede}</p>
<div className="mt-5 space-y-7">
<BarrasConsumo titulo={t.deep} campo="deepSleep" chips={chips} t={t} mostrarBateria />
<BarrasConsumo titulo={t.light} campo="lightSleep" chips={chips} t={t} />
</div>
</section>
<section className="panel p-5 sm:p-7" aria-labelledby="tinyml-titulo">
<h2 id="tinyml-titulo" className="text-base font-semibold text-ink">{t.tinyml}</h2>
<p className="mt-1 text-sm text-muted">{t.tinymlLede}</p>
<div className="mt-5 overflow-x-auto">
<table className="w-full min-w-[26rem] border-collapse text-left text-sm">
<thead>
<tr className="border-b border-line">
<th scope="col" className="py-2.5 pr-3 font-medium text-muted">{t.recurso}</th>
{chips.map((c, i) => (
<th key={c} scope="col" className="py-2.5 pr-3 font-semibold text-ink">
<span className="inline-flex items-center gap-1.5">
<span className="h-2.5 w-2.5 rounded-full" style={{ background: CORES[i] }} aria-hidden="true" />
{c.replace("ESP32-", "")}
</span>
</th>
))}
</tr>
</thead>
<tbody>
{Object.keys(t.recursos).map((r) => (
<tr key={r} className="border-b border-line last:border-b-0">
<th scope="row" className="py-3 pr-3 align-top font-medium text-ink-soft">{t.recursos[r]}</th>
{chips.map((c) => <td key={c} className="py-3 pr-3 align-top"><Estado valor={METRICAS[c]?.tinyml[r]} t={t} /></td>)}
</tr>
))}
</tbody>
</table>
</div>
<p className="mt-4 text-xs text-muted">{t.tinymlNota}</p>
</section>
</div>
</div>
);
}
+40
View File
@@ -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" } },
};