Quiero armar "la personita": una mascota virtual en pixel art para mi M5StickC Plus2 que hay que cuidar (comida, agua, energía y ánimo) y que además me avisa en la pantalla cuando termina una sesión de Claude Code, con el nombre de la carpeta del proyecto. No sé programar: guíame en español, explícame en 2 o 3 líneas qué vas a hacer en cada paso antes de hacerlo, y avanza de a un paso.
- Trabaja SOLO dentro de la carpeta `~/personita` (créala). La única excepción son los pasos 5 y 6, que guardan la clave de los avisos y el aviso de Claude Code en `~/.claude`.
- Haz SOLO lo que pide este documento. No agregues funciones, librerías, archivos ni servicios extra.
- NUNCA me pidas que escriba la clave de mi WiFi en el chat. Yo la escribo directamente en `secretos.h`.
- NUNCA abras puertos del router ni uses servicios en internet para los avisos: todo pasa dentro de mi red de la casa.
- Detén el trabajo y pregúntame antes de: instalar algo que no esté en este documento, reemplazar o borrar un archivo fuera de `~/personita`, o subir un programa al M5 por primera vez (reemplaza el programa de fábrica).
- Al terminar cada paso, escribe "✅ Paso N: [qué quedó hecho y cómo lo verificaste]" y espera a que yo diga "sigue".
- Si un mismo error se repite 3 veces, detente, explícame qué intentaste y pregúntame cómo seguir.
- Detecta mi sistema operativo. Con el M5 conectado por USB, encuentra su puerto serial (`arduino-cli board list` o `ls /dev/cu.*` en Mac). Si no aparece en Windows, dime que instale el driver CH9102 desde la página oficial de M5Stack y espera.
- Instala `arduino-cli` (Mac: Homebrew; Windows: winget).
- Agrega el catálogo de placas `https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json`, instala la plataforma `m5stack:esp32` y la librería oficial `M5StickCPlus2` (autor M5Stack). La placa es `m5stack:esp32:m5stack_stickc_plus2`.
- Solo en Mac con chip Apple: el `ctags` que trae Arduino es para Intel y pediría Rosetta. Pregúntame antes, y si acepto, instala `universal-ctags` con Homebrew, respalda el original como `ctags.intel-original` y reemplázalo por un enlace al nativo.
- Verificación: `arduino-cli board listall` muestra `M5StickCPlus2`.
Cada letra es un píxel. Colores:
K #1b1630 contorno · H #4b2a6b pelo · h #7a4aa8 brillo del pelo · P #2f2f3d audífonos · p #ee6c4d detalle coral (también el elástico del moño) · S #f4c7a1 piel · s #e2a882 sombra de piel · E #1b1630 ojos · W #ffffff brillo de ojos · M #c0504d boca · C #ee6c4d polerón · c #c9553b sombra del polerón · N #2e4460 pantalón · B #f2f2f2 zapatillas · G #c9dfae piel enferma · g #a9c78c sombra enferma · D #5ab0e6 gota y lágrima · Z #ffffff zetas de sueño. El punto es transparente.
Dibujo base (24 × 33):
```
..........KKKK..........
.........KHhhHK.........
........KHHhHHHK........
........KHhHHHHK........
........KHHHHHHK........
........KppppppK........
......KKHHHHHHHHKK......
.....KHHhhHHHHHHHHK.....
....KHHhhHHHHHHHHHHK....
...KPKHHHHHHHHHHHHKPK...
..KPpKHHSSSSSSSSHHKpPK..
..KPpKHSSSSSSSSSSHKpPK..
..KPpKSSEESSSSEESSKpPK..
..KPpKSSEWSSSSEWSSKpPK..
..KPPKSsSSSSSSSSsSKPPK..
...KKKSSSSSMMSSSSSKKK...
.....KSSSSSSSSSSSSK.KSK.
......KKSSSSSSSSKK..KSSK
........KKSSSSKK....KSSK
..........KSSK......KCCK
........KKCCCCKK...KCCK.
.......KCCCCCCCCK.KCCK..
......KCCCCCCCCCCKCCK...
.....KCCKCCCCCCCCCCK....
.....KCCKCCCCCCCCK......
.....KCCKCCCCCCCCK......
.....KSSKccccccccK......
......KK.KNNNNNNK.......
.........KNNKKNNK.......
.........KNNK.KNNK......
.........KNNK.KNNK......
........KBBBK.KBBBK.....
........KKKKK.KKKKK.....
```
Las 7 caras cambian las columnas 6 a 17 de algunas filas (fila: texto nuevo):
- Normal: el dibujo base.
- Feliz: 15 `SSSSMSSMSSSS`, 16 `SSSSSMMSSSSS`.
- Hambre: 15 `SSSSSMMSSSSS`, 16 `SSSSSMMSSSSS`.
- Sed: 15 `SSSSSSSSSSSS`, 16 `SSSSSMMSSSSS`, y una gota D arriba a la derecha (fila 1 col 20; filas 2 y 3 cols 19–21; fila 4 col 20).
- Sueño: 12 `SSSSSSSSSSSS`, 13 `SSEESSSSEESS`, 15 `SSSSSSSSSSSS`, 16 `SSSSSMSSSSSS`, y una Z (fila 0 cols 19–23; fila 1 col 22; fila 2 col 21; fila 3 col 20; fila 4 cols 19–23).
- Triste: 14 `SDSSSSSSSSsS`, 15 `SSSSSMMSSSSS`, 16 `SSSSMSSMSSSS`.
- Enferma: 12 `SSSSSSSSSSSS`, 13 `SSEESSSSEESS`, 15 `SSSSMSMSSSSS`, 16 `SSSSSMSMSSSS`, y en las filas 10 a 18, columnas 6 a 17, S pasa a G y s pasa a g.
Escribe un script en Python que convierta esto a `firmware/personita/sprites.h` (paleta en RGB565 con índice 0 transparente, y una matriz por cara) y otro que genere una imagen PNG con las 7 caras para que yo la revise. Verificación: muéstrame la imagen y espera mi visto bueno. Si quiero otros colores, cambia solo la paleta.
Programa que muestre a la personita (escala 3) sobre fondo #0d1b2a y que el botón grande (A) cambie de cara. Pon todo el código en `app.cpp` y deja `personita.ino` vacío con un comentario (así no depende de ctags). Compila, pídeme permiso para subirlo y súbelo. Verificación: te confirmo que veo las 7 caras.
- Pantalla horizontal (240 × 135). Dibuja todo en un `M5Canvas` y mándalo de una vez, sin parpadeo. Personita a la izquierda; debajo, una pila chica con el porcentaje de batería (verde > 50, amarillo 20–50, rojo < 20). A la derecha, 4 barras: Comida, Agua, Energía, Ánimo. Abajo, el menú `< Comer >`. Usa la fuente `efontJA_12/16` para que salgan tildes y ñ.
- Vive con los horarios de una persona, según la hora local:
- **Almuerzo de 13:00 a 14:00 y cena de 18:00 a 19:00.** Comer solo funciona dentro de esos horarios y deja la comida en 100. Fuera de horario, Comer muestra "Todavía no es hora" y no hace nada. Si se salta una comida, sigue con hambre hasta la siguiente.
- **Se acuesta entre 21:00 y 22:00 y despierta sola a las 7:00.** En ese horario, Dormir la acuesta. Si a las 22:00 sigue despierta, se duerme sola, pero baja el ánimo 20 por acostarse tarde.
- **Siesta**: de día solo puede dormir si la energía está bajo 25, y despierta sola cuando llega a 100 o cuando la despiertas.
- Si no hay hora válida (sin reloj ni internet), no aplica los horarios y deja comer y dormir en cualquier momento.
- Cada necesidad va de 100 a 0 y baja con el tiempo:
- Comida: se vacía en 17 h despierta y a la mitad de velocidad durmiendo. Así, después de cenar a las 19:00 llega con hambre al almuerzo del día siguiente, sin quedar en 0.
- Agua: se vacía en 6 h despierta, a la mitad durmiendo. Se puede beber a cualquier hora.
- Energía: baja de 100 a 0 en 16 h despierta. **Durmiendo SUBE de 0 a 100 en 6 h**: súmala en cada actualización mientras duerme, nunca la restes, y guárdala.
- Ánimo: se vacía en 10 h despierta y no baja durmiendo.
- Mientras duerme, la pantalla baja el brillo pero muestra "Durmiendo" y el porcentaje de energía subiendo, para que se vea que está cargando. Al despertar a las 7:00, vuelve el brillo normal.
- Botón lateral (B): recorre Comer, Beber, Dormir/Despertar, Jugar. Botón A: ejecuta. Beber +50. Al comer o beber queda feliz 1 minuto. Jugar: muestra "¡Agítame!" 8 segundos. Agitar el M5 (aceleración > 2 g, máximo una vez cada 10 segundos, solo despierta) sube ánimo +25 y baja energía −5. Si una acción no está permitida por el horario, explícalo en la línea del menú por 3 segundos.
- Cerca de cada comida y de la hora de dormir, te avisa con un pitido corto al empezar el horario (13:00, 18:00 y 21:00).
- La cara muestra lo más urgente: enferma, después durmiendo, después feliz, y si algo baja de 25, la cara de esa necesidad.
- Se enferma si dos necesidades quedan en 0 por 3 horas; se mejora cuando todas superan 50. Enferma no puede jugar.
- Te llama con 3 pitidos si algo está urgente: máximo una vez por hora y NUNCA entre las 22:00 y las 8:00.
- Guarda el estado con `Preferences` cada minuto y después de cada acción. Al encender, descuenta el tiempo que estuvo apagado (máximo 7 días) usando el reloj interno (RTC, guardado en UTC) y la hora de internet (NTP); cuando llega la hora de internet, corrige el RTC. Pregúntame mi zona horaria y usa su cadena POSIX (Chile continental: `<-04>4<-03>,M9.1.6/24,M4.1.6/24`).
- Antes de subirlo, prueba la lógica de los horarios y de la energía con un modo de prueba que acelere el tiempo (1 minuto real = 1 hora) y una hora simulada: comer a las 12:00 no funciona, a las 13:30 sí; acostarla a las 21:30 y ver que la energía sube hasta 100 y despierta a las 7:00. Después quita el modo de prueba.
- Verificación: compila, súbelo y yo te confirmo que las barras y el menú funcionan.
- Crea `firmware/personita/secretos.h` con `WIFI_NOMBRE`, `WIFI_CLAVE` y `CLAVE_AVISOS`, más un `secretos.h.ejemplo` y un `.gitignore` que excluya `secretos.h`. Genera `CLAVE_AVISOS` al azar (24 bytes, base64 url) sin mostrarla en el chat, y guárdala también en `~/.claude/personita-clave` con permisos solo para mi usuario.
- Pídeme que escriba yo mi WiFi en `secretos.h` y avísame que el M5 solo usa redes de 2,4 GHz. Revisa que ya no tenga los datos de ejemplo, sin mostrar su contenido.
- En el M5: conectar al WiFi sin bloquear la pantalla, reintentar cada 30 segundos y anunciarse como `personita.local` (mDNS) cada vez que conecta. Servidor HTTP en el puerto 80 con una sola ruta, `POST /aviso`, con parámetro `proyecto` y cabecera `X-Clave`:
- clave comparada en tiempo constante; si no calza, 401;
- máximo 20 avisos por minuto (si se pasa, 429);
- nombre limpiado: solo letras, números, `- _ .` y espacio, máximo 24 caracteres; si queda vacío, 400;
- fila de máximo 5 avisos, sin repetir un proyecto que ya está esperando.
- Mientras haya avisos, tapan las barras: nombre del proyecto en coral (cortado con ".." si no cabe), "terminó", "+N más" y "A: visto"; suena un doble pitido y el botón A marca el primero como visto.
- Verificación: prueba con `curl -4` (sin el -4 se cuelga por IPv6) sin clave (401), con clave mala (401), con la clave buena (200) y con un nombre con `