Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85d6ab736d | ||
|
|
df39436dcb | ||
|
|
cc52b6e978 |
No files matched your search
+32
-1
@@ -6,6 +6,35 @@ Tudo o que muda de relevante no ESPDocs está aqui. O formato segue o
|
|||||||
|
|
||||||
## [Não lançado]
|
## [Não lançado]
|
||||||
|
|
||||||
|
## [1.10.0] - 2026-11-06
|
||||||
|
|
||||||
|
### Alterado
|
||||||
|
|
||||||
|
- A busca ganhou papel central. Na home, uma barra grande logo abaixo do título abre a busca, e
|
||||||
|
exemplos clicáveis (pino 32 esp32-s3, usb c6, brownout, deep sleep) já chegam preenchidos.
|
||||||
|
No cabeçalho, a partir de 1280 px o botão vira um campo largo com a dica "Buscar chips,
|
||||||
|
pinos…" e o atalho Ctrl K. Em telas menores continua sendo o ícone.
|
||||||
|
- Qualquer componente pode abrir a busca com o evento `espdocs:abrir-busca`, passando uma
|
||||||
|
consulta inicial opcional.
|
||||||
|
|
||||||
|
## [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 +356,9 @@ 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.10.0...HEAD
|
||||||
|
[1.10.0]: https://git.lucasrguerra.dev.br/lucasrguerra/ESPDocs/compare/v1.9.0...v1.10.0
|
||||||
|
[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
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Header from "@/components/Header";
|
import Header from "@/components/Header";
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
import BookCover from "@/components/BookCover";
|
import BookCover from "@/components/BookCover";
|
||||||
|
import BuscaDestaque from "@/components/BuscaDestaque";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import seriesDataPt from "@/public/series.json";
|
import seriesDataPt from "@/public/series.json";
|
||||||
import frameworksDataPt from "@/public/frameworks.json";
|
import frameworksDataPt from "@/public/frameworks.json";
|
||||||
@@ -508,6 +509,8 @@ ESP32,<br className="sm:hidden" />去繁就简。
|
|||||||
: "Specifications, pinouts, comparisons and diagnostics in a single read. Less datasheet translation, more workbench time."}
|
: "Specifications, pinouts, comparisons and diagnostics in a single read. Less datasheet translation, more workbench time."}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<BuscaDestaque locale={locale} />
|
||||||
|
|
||||||
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
|
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
|
||||||
<Link href={getLinkHref("/seletor")} className="btn btn-primary w-full sm:w-auto">
|
<Link href={getLinkHref("/seletor")} className="btn btn-primary w-full sm:w-auto">
|
||||||
<SlidersHorizontal className="h-4 w-4" aria-hidden="true" />
|
<SlidersHorizontal className="h-4 w-4" aria-hidden="true" />
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Search } from "lucide-react";
|
||||||
|
|
||||||
|
const T = {
|
||||||
|
pt: { rotulo: "Buscar no ESPDocs", dica: "Pergunte do seu jeito: chip, pino, periférico ou erro", exemplos: "Experimente:", lista: ["pino 32 esp32-s3", "usb c6", "brownout", "deep sleep"] },
|
||||||
|
en: { rotulo: "Search ESPDocs", dica: "Ask your way: chip, pin, peripheral or error", exemplos: "Try:", lista: ["pin 32 esp32-s3", "usb pins c6", "brownout", "deep sleep"] },
|
||||||
|
es: { rotulo: "Buscar en ESPDocs", dica: "Pregunte a su manera: chip, pin, periférico o error", exemplos: "Pruebe:", lista: ["pin 32 esp32-s3", "pines usb c6", "brownout", "deep sleep"] },
|
||||||
|
fr: { rotulo: "Rechercher sur ESPDocs", dica: "Demandez à votre façon : puce, broche, périphérique ou erreur", exemplos: "Essayez :", lista: ["broche 32 esp32-s3", "broches usb c6", "brownout", "deep sleep"] },
|
||||||
|
de: { rotulo: "ESPDocs durchsuchen", dica: "Fragen Sie frei: Chip, Pin, Peripherie oder Fehler", exemplos: "Probieren Sie:", lista: ["pin 32 esp32-s3", "usb pins c6", "brownout", "deep sleep"] },
|
||||||
|
"zh-CN": { rotulo: "在 ESPDocs 中搜索", dica: "随意提问:芯片、引脚、外设或错误", exemplos: "试试:", lista: ["pin 32 esp32-s3", "usb c6", "brownout", "deep sleep"] },
|
||||||
|
};
|
||||||
|
|
||||||
|
function abrirBusca(consulta) {
|
||||||
|
window.dispatchEvent(new CustomEvent("espdocs:abrir-busca", { detail: { consulta } }));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Barra de busca em destaque no hero da home. Abre o mesmo diálogo do cabeçalho. */
|
||||||
|
export default function BuscaDestaque({ locale = "pt" }) {
|
||||||
|
const t = T[locale] ?? T.pt;
|
||||||
|
return (
|
||||||
|
<div className="mx-auto mt-9 w-full max-w-2xl">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => abrirBusca()}
|
||||||
|
aria-label={`${t.rotulo} (Ctrl+K)`}
|
||||||
|
className="group flex min-h-16 w-full cursor-text items-center gap-4 rounded-full border-2 border-line-strong bg-surface px-6 text-left shadow-[0_18px_40px_-20px_var(--shadow-ink)] transition-colors hover:border-accent focus-visible:border-accent"
|
||||||
|
>
|
||||||
|
<Search className="h-6 w-6 shrink-0 text-accent" aria-hidden="true" />
|
||||||
|
<span className="flex-1 truncate text-base text-muted sm:text-lg">{t.dica}</span>
|
||||||
|
<kbd className="hidden rounded-md border border-line bg-surface-2 px-2 py-1 font-mono text-xs font-semibold text-muted sm:inline">Ctrl K</kbd>
|
||||||
|
</button>
|
||||||
|
<p className="mt-4 flex flex-wrap items-center justify-center gap-2 text-sm text-muted">
|
||||||
|
<span>{t.exemplos}</span>
|
||||||
|
{t.lista.map((ex) => (
|
||||||
|
<button key={ex} type="button" onClick={() => abrirBusca(ex)} className="chip-btn">
|
||||||
|
{ex}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -183,6 +183,17 @@ export default function SearchDialog() {
|
|||||||
setAtivo(0);
|
setAtivo(0);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Outros componentes (a barra da home) abrem a busca com window.dispatchEvent(
|
||||||
|
// new CustomEvent("espdocs:abrir-busca", { detail: { consulta } })).
|
||||||
|
useEffect(() => {
|
||||||
|
function onAbrir(e) {
|
||||||
|
if (e.detail?.consulta) setConsulta(e.detail.consulta);
|
||||||
|
setAberto(true);
|
||||||
|
}
|
||||||
|
window.addEventListener("espdocs:abrir-busca", onAbrir);
|
||||||
|
return () => window.removeEventListener("espdocs:abrir-busca", onAbrir);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Atalhos globais: Ctrl/⌘+K abre; "/" abre fora de campos de texto.
|
// Atalhos globais: Ctrl/⌘+K abre; "/" abre fora de campos de texto.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onKey(e) {
|
function onKey(e) {
|
||||||
@@ -306,11 +317,11 @@ export default function SearchDialog() {
|
|||||||
type="button"
|
type="button"
|
||||||
id="botao-busca"
|
id="botao-busca"
|
||||||
onClick={() => setAberto(true)}
|
onClick={() => setAberto(true)}
|
||||||
className="flex h-11 min-w-11 shrink-0 cursor-pointer items-center justify-center gap-2 rounded-full border border-line px-3 text-ink transition-colors hover:bg-surface-2 xl:px-4"
|
className="flex h-11 min-w-11 shrink-0 cursor-pointer items-center justify-center gap-2.5 rounded-full border border-line-strong bg-surface px-3 text-ink shadow-sm transition-colors hover:border-accent hover:bg-surface-2 xl:w-72 xl:justify-start xl:px-4"
|
||||||
aria-label={isEn ? "Search ESPDocs (Ctrl+K)" : isEs ? "Buscar en ESPDocs (Ctrl+K)" : isFr ? "Rechercher sur ESPDocs (Ctrl+K)" : isZh ? "在 ESPDocs 中搜索(Ctrl+K)" : isDe ? "ESPDocs durchsuchen (Strg+K)" : "Buscar no ESPDocs (Ctrl+K)"}
|
aria-label={isEn ? "Search ESPDocs (Ctrl+K)" : isEs ? "Buscar en ESPDocs (Ctrl+K)" : isFr ? "Rechercher sur ESPDocs (Ctrl+K)" : isZh ? "在 ESPDocs 中搜索(Ctrl+K)" : isDe ? "ESPDocs durchsuchen (Strg+K)" : "Buscar no ESPDocs (Ctrl+K)"}
|
||||||
>
|
>
|
||||||
<Search className="h-4 w-4" aria-hidden="true" />
|
<Search className="h-4 w-4" aria-hidden="true" />
|
||||||
<span className="hidden text-sm font-medium xl:inline">{isEn ? "Search" : isEs ? "Buscar" : isFr ? "Rechercher" : isZh ? "搜索" : isDe ? "Suchen" : "Buscar"}</span>
|
<span className="hidden flex-1 truncate text-left text-sm text-muted xl:inline">{isEn ? "Search chips, pins…" : isEs ? "Buscar chips, pines…" : isFr ? "Chercher puces, broches…" : isZh ? "搜索芯片、引脚…" : isDe ? "Chips, Pins suchen…" : "Buscar chips, pinos…"}</span>
|
||||||
<kbd className="hidden rounded-md border border-line bg-surface-2 px-1.5 py-0.5 font-mono text-xs font-semibold text-muted xl:inline">{isZh ? "Ctrl K" : isDe ? "Strg K" : "Ctrl K"}</kbd>
|
<kbd className="hidden rounded-md border border-line bg-surface-2 px-1.5 py-0.5 font-mono text-xs font-semibold text-muted xl:inline">{isZh ? "Ctrl K" : isDe ? "Strg K" : "Ctrl K"}</kbd>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
@@ -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" } },
|
||||||
|
};
|
||||||
Reference in new issue
Block a user