feat(painel): Ao vivo em duas colunas e página "Ligações da placa"
Ao vivo no PC: a câmera deixa de ficar presa no topo e ganha cartões embaixo (Gestos, Celular, Câmera, Acontecimentos, Aparência e Personalidade); à direita ficam Vida e O que ele aprendeu. No celular segue uma coluna só, na mesma ordem de antes (display: contents + order). Ligações da placa (#/ligacoes): mapa do conector de 26 pinos do Orange Pi Zero 3 com os dispositivos registrados (lib/ligacoes.ts): - PIR: VCC no 5 V (2), OUT no pino que a placa informa (CARINHA_PIR, padrão gpiochip1:70 = PC6 = pino 11), GND no 9; avisos de 3,3 V nos GPIOs e da ordem dos terminais que muda entre módulos; - PCA9685: 3,3 V (1), SDA (3), SCL (5), GND (6); servos na fonte externa com terra comum; - tela SPI do overlay tft35: pinos 18, 19, 21, 22, 23 e 24 reservados. Estado ao vivo de cada um (sinal do PIR, PCA9685 no I²C), destaque dos pinos ao tocar no dispositivo, atalhos em Ajustes, Ao vivo e Ctrl+K e tópico no guia. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+16
-2
@@ -74,6 +74,20 @@ export const TOPICOS: Record<string, TopicoGuia> = {
|
||||
],
|
||||
dica: 'Para ensinar rápido: quando ele fizer algo que você gostou, toque na tela do celular (vale como carinho) ou fale com ele logo em seguida.',
|
||||
},
|
||||
ligacoes: {
|
||||
titulo: 'Ligações da placa',
|
||||
emoji: '🔌',
|
||||
onde: 'Ajustes › Ligações da placa (também em Ao vivo › Acontecimentos)',
|
||||
resumo:
|
||||
'Mapa do conector de 26 pinos do Orange Pi Zero 3 com o que está ligado em cada pino: o sensor de presença (PIR), a placa dos servos (PCA9685) e os pinos reservados para a tela SPI. O pino do PIR vem da própria placa, então o desenho acompanha a configuração.',
|
||||
itens: [
|
||||
{ emoji: '🚶', nome: 'PIR', texto: 'VCC no 5 V (pino 2 ou 4), OUT no pino 11 (PC6) e GND num terra (pino 9, por exemplo). Nunca ligue o VCC no pino 11: os GPIOs são de 3,3 V. A ordem dos terminais muda entre módulos; leia o que está escrito no sensor.' },
|
||||
{ emoji: '🦾', nome: 'PCA9685', texto: 'VCC no 3,3 V (pino 1), SDA no 3, SCL no 5 e GND no 6. Os servos usam a fonte externa no borne V+, com terra comum com a placa.' },
|
||||
{ emoji: '🖥️', nome: 'Tela SPI (tft35)', texto: 'O overlay tft35 reserva os pinos 18, 19, 21, 22, 23 e 24. Não ligue sensores neles.' },
|
||||
{ emoji: '🟢', nome: 'Estado ao vivo', texto: 'Cada dispositivo mostra se a placa está lendo: o PIR diz se o sinal está alto agora, e a PCA9685 se responde no I²C.' },
|
||||
],
|
||||
dica: 'Sinal do PIR alto o tempo todo, por muito tempo, costuma ser fio trocado (VCC e OUT invertidos), sensor apontado para uma fonte de calor ou o ajuste de tempo do HC-SR501 no máximo.',
|
||||
},
|
||||
cuidados: {
|
||||
titulo: 'Cuidar do Carinha',
|
||||
emoji: '❤️',
|
||||
@@ -354,12 +368,12 @@ export const TOPICOS: Record<string, TopicoGuia> = {
|
||||
/** Ordem do tutorial (página Guia). */
|
||||
export const ORDEM_GUIA = [
|
||||
'inicio', 'vida', 'aprendizado', 'cuidados', 'humores', 'ritmo', 'sono', 'celular', 'camera', 'gestos', 'atividades', 'criar', 'ideias', 'aparencia',
|
||||
'personalidade', 'acontecimentos', 'eventos', 'presenca', 'tela', 'sensores', 'sensibilidade',
|
||||
'personalidade', 'acontecimentos', 'ligacoes', 'eventos', 'presenca', 'tela', 'sensores', 'sensibilidade',
|
||||
'reacoes', 'conversa', 'movimento', 'atalhos',
|
||||
];
|
||||
|
||||
/** Tópico de cada tela, para o ⓘ do topo. */
|
||||
export const TOPICO_DA_TELA: Record<string, string> = {
|
||||
'ao-vivo': 'inicio', eventos: 'eventos', conversa: 'conversa', reacoes: 'reacoes',
|
||||
movimento: 'movimento', ajustes: 'tela', rosto: 'aparencia', guia: 'inicio', criar: 'criar', 'dia-a-dia': 'aprendizado',
|
||||
movimento: 'movimento', ajustes: 'tela', rosto: 'aparencia', guia: 'inicio', criar: 'criar', 'dia-a-dia': 'aprendizado', ligacoes: 'ligacoes',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Ligações da placa: o conector de 26 pinos do Orange Pi Zero 3 e o que está ligado em cada pino.
|
||||
* O pino do PIR vem ao vivo da placa (CARINHA_PIR, padrão gpiochip1:70 = PC6 = pino 11).
|
||||
*/
|
||||
|
||||
export type TipoPino = '5v' | '3v3' | 'gnd' | 'gpio' | 'i2c' | 'spi' | 'uart';
|
||||
|
||||
export interface Pino {
|
||||
num: number;
|
||||
nome: string;
|
||||
tipo: TipoPino;
|
||||
/** Linha no gpiochip1 (libgpiod), quando é um GPIO. */
|
||||
linha?: number;
|
||||
funcao?: string;
|
||||
}
|
||||
|
||||
// Conector de 26 pinos (2 fileiras): ímpares de um lado, pares do outro.
|
||||
export const PINOS: Pino[] = [
|
||||
{ num: 1, nome: '3,3 V', tipo: '3v3' },
|
||||
{ num: 2, nome: '5 V', tipo: '5v' },
|
||||
{ num: 3, nome: 'PH5', tipo: 'i2c', linha: 229, funcao: 'I²C3 SDA' },
|
||||
{ num: 4, nome: '5 V', tipo: '5v' },
|
||||
{ num: 5, nome: 'PH4', tipo: 'i2c', linha: 228, funcao: 'I²C3 SCL' },
|
||||
{ num: 6, nome: 'GND', tipo: 'gnd' },
|
||||
{ num: 7, nome: 'PC9', tipo: 'gpio', linha: 73 },
|
||||
{ num: 8, nome: 'PH2', tipo: 'uart', linha: 226, funcao: 'UART5 TX' },
|
||||
{ num: 9, nome: 'GND', tipo: 'gnd' },
|
||||
{ num: 10, nome: 'PH3', tipo: 'uart', linha: 227, funcao: 'UART5 RX' },
|
||||
{ num: 11, nome: 'PC6', tipo: 'gpio', linha: 70 },
|
||||
{ num: 12, nome: 'PC11', tipo: 'gpio', linha: 75 },
|
||||
{ num: 13, nome: 'PC5', tipo: 'gpio', linha: 69 },
|
||||
{ num: 14, nome: 'GND', tipo: 'gnd' },
|
||||
{ num: 15, nome: 'PC8', tipo: 'gpio', linha: 72 },
|
||||
{ num: 16, nome: 'PC15', tipo: 'gpio', linha: 79 },
|
||||
{ num: 17, nome: '3,3 V', tipo: '3v3' },
|
||||
{ num: 18, nome: 'PC14', tipo: 'gpio', linha: 78 },
|
||||
{ num: 19, nome: 'PH7', tipo: 'spi', linha: 231, funcao: 'SPI1 MOSI' },
|
||||
{ num: 20, nome: 'GND', tipo: 'gnd' },
|
||||
{ num: 21, nome: 'PH8', tipo: 'spi', linha: 232, funcao: 'SPI1 MISO' },
|
||||
{ num: 22, nome: 'PC7', tipo: 'gpio', linha: 71 },
|
||||
{ num: 23, nome: 'PH6', tipo: 'spi', linha: 230, funcao: 'SPI1 CLK' },
|
||||
{ num: 24, nome: 'PH9', tipo: 'spi', linha: 233, funcao: 'SPI1 CS0' },
|
||||
{ num: 25, nome: 'GND', tipo: 'gnd' },
|
||||
{ num: 26, nome: 'PC10', tipo: 'gpio', linha: 74 },
|
||||
];
|
||||
|
||||
export const NOME_TIPO: Record<TipoPino, string> = {
|
||||
'5v': '5 V', '3v3': '3,3 V', gnd: 'Terra (GND)', gpio: 'GPIO (3,3 V)', i2c: 'I²C', spi: 'SPI', uart: 'Serial',
|
||||
};
|
||||
|
||||
export interface Fio {
|
||||
pino: number;
|
||||
/** Nome do terminal no dispositivo (VCC, OUT, GND, SDA...). */
|
||||
terminal: string;
|
||||
cor: string;
|
||||
}
|
||||
|
||||
export interface Dispositivo {
|
||||
id: string;
|
||||
nome: string;
|
||||
emoji: string;
|
||||
cor: string;
|
||||
descricao: string;
|
||||
fios: Fio[];
|
||||
avisos: string[];
|
||||
/** 'em uso' = o servidor usa; 'reservado' = a placa reserva os pinos (overlay), mas o Carinha não usa. */
|
||||
situacao: 'em uso' | 'reservado';
|
||||
}
|
||||
|
||||
export function pinoDaLinha(linha: number) {
|
||||
return PINOS.find((p) => p.linha === linha)?.num ?? null;
|
||||
}
|
||||
|
||||
/** Dispositivos registrados. O pino do PIR é o que a placa informa (CARINHA_PIR). */
|
||||
export function dispositivos(pinoPir: number | null): Dispositivo[] {
|
||||
const saida = pinoPir ?? 11;
|
||||
return [
|
||||
{
|
||||
id: 'pir',
|
||||
nome: 'Sensor de presença (PIR)',
|
||||
emoji: '🚶',
|
||||
cor: '#34c759',
|
||||
situacao: 'em uso',
|
||||
descricao: 'Módulo PIR (tipo HC-SR501 ou AM312). Avisa quando alguém se mexe na frente: acorda o rosto e liga a câmera no modo presença.',
|
||||
fios: [
|
||||
{ pino: 2, terminal: 'VCC', cor: '#ff3b30' },
|
||||
{ pino: saida, terminal: 'OUT', cor: '#ffcc00' },
|
||||
{ pino: 9, terminal: 'GND', cor: '#8e8e93' },
|
||||
],
|
||||
avisos: [
|
||||
'A ordem dos 3 terminais muda de um módulo para outro: leia o que está escrito na plaquinha do sensor (no HC-SR501, tire a cúpula branca para ver VCC, OUT e GND).',
|
||||
`Nunca ligue o VCC (5 V) no pino ${saida}: os GPIOs da placa são de 3,3 V e queimam com 5 V.`,
|
||||
'A saída do HC-SR501 e do AM312 é de 3,3 V, segura para o GPIO. Se o seu sensor soltar 5 V na saída, use um divisor de tensão.',
|
||||
'O VCC pode ir em qualquer 5 V (pino 2 ou 4) e o GND em qualquer terra (6, 9, 14, 20 ou 25).',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'pca9685',
|
||||
nome: 'Placa dos servos (PCA9685)',
|
||||
emoji: '🦾',
|
||||
cor: '#0a84ff',
|
||||
situacao: 'em uso',
|
||||
descricao: 'Controla os servos do braço por I²C (barramento /dev/i2c-2, endereço 0x40).',
|
||||
fios: [
|
||||
{ pino: 1, terminal: 'VCC', cor: '#ff9500' },
|
||||
{ pino: 3, terminal: 'SDA', cor: '#0a84ff' },
|
||||
{ pino: 5, terminal: 'SCL', cor: '#5ac8fa' },
|
||||
{ pino: 6, terminal: 'GND', cor: '#8e8e93' },
|
||||
],
|
||||
avisos: [
|
||||
'O VCC da PCA9685 vai no 3,3 V (pino 1): é só a lógica.',
|
||||
'Os servos NÃO são alimentados pela placa: o borne V+ da PCA9685 recebe a fonte externa (6 V), com o GND da fonte ligado ao GND da placa (terra comum).',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'tela',
|
||||
nome: 'Tela SPI 3,5" (overlay tft35)',
|
||||
emoji: '🖥️',
|
||||
cor: '#af52de',
|
||||
situacao: 'reservado',
|
||||
descricao: 'O overlay tft35 está ativo no boot e reserva estes pinos para uma tela SPI. O Carinha não usa essa tela: não ligue outros sensores neles.',
|
||||
fios: [
|
||||
{ pino: 18, terminal: 'DC', cor: '#af52de' },
|
||||
{ pino: 19, terminal: 'MOSI', cor: '#af52de' },
|
||||
{ pino: 21, terminal: 'MISO', cor: '#af52de' },
|
||||
{ pino: 22, terminal: 'RESET', cor: '#af52de' },
|
||||
{ pino: 23, terminal: 'CLK', cor: '#af52de' },
|
||||
{ pino: 24, terminal: 'CS', cor: '#af52de' },
|
||||
],
|
||||
avisos: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -37,6 +37,7 @@ export function itensDaPaleta(): ItemPaleta[] {
|
||||
registrarNaPaleta(() => [
|
||||
...ROTAS.map((r) => ({ id: 'ir-' + r.id, grupo: 'Ir para', titulo: r.titulo, fazer: () => nav.ir(r.id) })),
|
||||
{ id: 'ir-rosto', grupo: 'Ir para', titulo: 'Rosto (estilo, formato e cor)', fazer: () => nav.ir('rosto') },
|
||||
{ id: 'ir-ligacoes', grupo: 'Ir para', titulo: 'Ligações da placa (pinos do PIR, servos e tela)', fazer: () => nav.ir('ligacoes') },
|
||||
{ id: 'ir-dia-a-dia', grupo: 'Ir para', titulo: 'Dia a dia (rotina e decisões do Carinha)', fazer: () => nav.ir('dia-a-dia') },
|
||||
{ id: 'ir-criar', grupo: 'Ir para', titulo: 'Criar com IA (expressões, atividades, figurinhas)', fazer: () => nav.ir('criar') },
|
||||
...Object.entries(ATIVIDADES_INFO).map(([id, a]) => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Rotas por hash (#/conversa): o servidor só precisa entregar index.html em "/". */
|
||||
|
||||
export type Rota = 'ao-vivo' | 'eventos' | 'conversa' | 'reacoes' | 'movimento' | 'ajustes' | 'rosto' | 'guia' | 'criar' | 'dia-a-dia';
|
||||
export type Rota = 'ao-vivo' | 'eventos' | 'conversa' | 'reacoes' | 'movimento' | 'ajustes' | 'rosto' | 'guia' | 'criar' | 'dia-a-dia' | 'ligacoes';
|
||||
|
||||
export const ROTAS: { id: Rota; titulo: string; atalho: string }[] = [
|
||||
{ id: 'ao-vivo', titulo: 'Ao vivo', atalho: 'g v' },
|
||||
@@ -17,6 +17,7 @@ export const SECUNDARIAS: { id: Rota; titulo: string; volta: Rota }[] = [
|
||||
{ id: 'guia', titulo: 'Guia', volta: 'ajustes' },
|
||||
{ id: 'criar', titulo: 'Criar com IA', volta: 'reacoes' },
|
||||
{ id: 'dia-a-dia', titulo: 'Dia a dia', volta: 'ao-vivo' },
|
||||
{ id: 'ligacoes', titulo: 'Ligações da placa', volta: 'ajustes' },
|
||||
];
|
||||
|
||||
export const tituloDa = (id: Rota) => [...ROTAS, ...SECUNDARIAS].find((r) => r.id === id)?.titulo ?? '';
|
||||
|
||||
Reference in New Issue
Block a user