Skip to content
EhuntabiPublic

About

Firmware para la pantalla táctil (Guition JC1060P470C con ESP32-P4) monitoriza y controla dispositivos Victron y panel Nordelettronica NE185/NE187 en autocaravana, frigo, niveles de depósitos y logs.

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Latest commit

 

History

1,115 Commits

Folders and files

Repository files navigation

Joint SPL 145 Control — VictronSolarDisplay port for Guition JC1060P470C_I (ESP32-P4)

Español | English

La versión no se escribe aquí a mano (se queda vieja): se ve en el equipo en Ajustes → Acerca de, o con git describe --tags --match "v*.*".


Índice de documentación / Documentation index

Si quieres... / If you want to... Lee / Read
Instalar el firmware sin usar terminal / Install without a terminal docs/INSTALACION.md
Desarrollar: mapa del código, comandos, hardware / Develop: code map, commands, hardware CLAUDE.md
Flujo multi-PC (sobremesa ↔ portátil) / Multi-PC dev workflow docs/DESARROLLO.md
Referencia de hardware: pinouts, esquemáticos, protocolo NE185 / Hardware reference docs/
Historial de cambios por versión / Version changelog Releases de GitHub
Guía operativa para la autocaravana (uso interno, portátil) / Motorhome operations guide (internal) AUTOCARAVANA-SETUP.md

Capturas / Screenshots


Vista general / Overview

Batería / Battery

Solar

DC/DC

Inversor / Inverter

Monitor de batería / Battery monitor

Frigo / Fridge

Autocaravana / Motorhome

Ajustes / Settings

Español

Port del proyecto victronsolardisplayesp (multi-device) de CamdenSutherland, que a su vez es un fork de VictronSolarDisplayEsp de wytr.

Port realizado por Ehuntabi.

Hardware

Componente Detalle
Pantalla Guition JC1060P470C_I (7", 1024x600, DSI, touch GT911)
SoC principal ESP32-P4 (RISC-V dual core 400 MHz, 32 MB PSRAM, 16 MB Flash)
SoC WiFi/BT ESP32-C6 (SDIO vía esp_hosted: CLK=18, CMD=19, D0..D3=14..17)
RTC RX8025T (I²C 0x32, SDA=7 / SCL=8 compartido con GT911 y ES8311) + pila CR1220
microSD SDMMC (slot 0 IOMUX: CLK=43, CMD=44, D0..D3=39..42; LDO interno canal 4 a 3,3 V), ver components/datalogger/datalogger.c:241
Sensores temperatura DS18B20 1-Wire en GPIO 4 (JP1 pin 13, pullup 4.7 kΩ)
Ventilador frigo PWM LEDC en GPIO 5 (JP1 pin 15), 25 kHz, con rampa de 10 %/s (10 s de 0 a 100; igual al arrancar que al parar). El pin 15 ataca un MOSFET IRLR7843TRPBF (R3 de 100 Ω en serie en puerta, R4 de 10 k de pull-down, D2 1N5819 de recirculación (flyback), C2 de 100 µF)
Audio Codec ES8311 + amplificador NS4150 (PA_CTRL=11, I²S MCLK=13/BCLK=12/LRCK=10/DOUT=9)
Cámara OmniVision OV02C10 (MIPI-CSI 2 lanes, RAW10 1928x1092, ~37 fps, JPEG por HW). Comparte el I²C (SDA=7 / SCL=8); sin pines reset/pwdn dedicados
NE185 (autocaravana) RS-485 con el cuadro Nordelettronica NE185 vía MAX485 (U8), UART1 TX=GPIO 26 / RX=GPIO 27, 38400 8N1, conector J4 (DE/RE automático)
GPS u-blox NEO-M9N por UART2 RX=GPIO 3 / TX=GPIO 2 (conector JP1), 38400 8N1. Solo se escucha NMEA; pone el reloj en hora solo

Pinout detallado con diagrama del JP1 (pines color-coded) y del CN2 en docs/pinout_guition_jc1060p470c_i.pdf.

Funcionalidades

UI inspirada en la vista Overview de Venus OS

  • Cards oscuras con borde de color por rol (cyan/verde/naranja/rojo/púrpura), métricas con número grande + unidad pequeña, pills de estado, gauges circulares para SOC y sombras suaves.
  • Iconos PNG propios en headers de cards (batería de coche, panel solar, conversor DC/DC, casa) generados como lv_img_dsc_t embebidos en flash.
  • Helpers reutilizables (main/ui/ui_card.h): ui_card_create, ui_card_set_title_img, ui_metric_create_compact/_large, ui_pill_create, ui_arc_soc_create, ui_card_pulse, paleta semántica UI_COLOR_*.
  • Pulso de actividad en la card al recibir un BLE record (sombra anima 50→100→50 % en 600 ms, sin acumular animaciones).
  • Pestañas Live/Settings ocultas — navegación con icono ⚙/🏠 discreto en la barra inferior + swipe horizontal del tabview. El botón Home cierra la subpágina y deja Settings en su menú principal.
  • Auto-vuelta a Live tras 60 s sin actividad táctil.

Pantalla de bienvenida (splash)

  • Logo de la furgo recortado con IA (rembg/u2net) y embebido a 720x400 RGB565 en flash.
  • Animación de barra de progreso mientras dura la inicialización (BLE, audio, SD, BT).
  • Configurable en Settings → Display: Logo furgo (default) o Sin splash.

Vistas Live

  • Overview (default): fila horizontal Solar — Bat — Cargas con arc SOC central; debajo TTG + “PV X.X kWh · Cargas Y.Y kWh” acumulados del día.
  • Battery Monitor: card naranja con arc SOC grande, V/A/W + pill enriquecido (“Cargando 5.5 A” / “Descargando 3.2 A”), barra bipolar de corriente ±50 A, sparkline de potencia (90 puntos), TTG / Consumido / Aux. Tap abre el histórico.
  • Solar Charger: 2 cards (Solar verde + Salida batería naranja) con pill de estado (Bulk/Absorción/Float).
  • Default Battery: 3 cards en fila (DC/DC cyan, Batería naranja con arc + sparkline + bar bipolar, Solar verde). En la card DC/DC el rótulo cambia entre ⚡ Alternador (verde) o 🔋 Bat. motor (gris) según la tensión.
  • Simple devices (Inverter, Smart Lithium, AC Charger, etc.): card único con métricas apiladas; color e icono según tipo.
  • Pill rojo “FALLO: X” en la card cuando un dispositivo Victron reporta alarm_reason (BMV/Inverter) o charger_error (Solar/DCDC/Orion).

Settings

Páginas con cards de borde de color, dropdown scrollable cuando hay overflow, separadores entre sub-bloques:

  • Historial en gráficos (púrpura): chart 24 h de batería y de temperaturas frigo. Swipe izquierda/derecha navega por fecha en los logs guardados en SD (header muestra la fecha seleccionada).
  • Wi-Fi (naranja): SSID, contraseña y switch on/off que se aplica en caliente, sin reiniciar la placa. (Cambiar SSID o contraseña sí necesita apagar y encender el switch para que entre.) Ojo: con el AP apagado, la pantalla de la cabina se queda sin telemetría.
  • Pantalla (cyan): card combinada Brillo pantalla + Salvapantallas; card Modo nocturno en una línea (switch + Inicio/Fin + brillo nocturno, brillo aplicado automáticamente entre las horas configuradas según RTC); Pantalla de bienvenida (logo furgo o sin splash).
  • Tarjeta SD (azul): carrusel de capturas y visor de imágenes de la SD.
  • Sonido y alertas (rojo): volumen, switch silenciar avisos, umbrales SoC y temperatura del frigorífico.
  • Autocaravana (rosa): agrupa lo propio del vehículo — Opciones Frigo (sensores DS18B20, control PWM del ventilador según T_Aletas con rampa de 10 %/s, umbrales y aprovechamiento de excedente solar), Victron Keys (MAC + clave AES por dispositivo, hasta 8), Modo ausente, Energía del viaje (kWh/Ah cargados/consumidos y horas del viaje en curso), Bombonas (botón de cambio de bombona y estadística de cuánto dura cada una) y Auto-encendido de luz interior + bomba al arrancar.
  • Acerca de (gris): uptime, RAM libre, IP del AP, chip, versión IDF, Backup configuración (exportar/importar a /sdcard/config_backup.json), Último reset + contador de resets por watchdog/panic, botón Reboot con confirmación.

Barra inferior

  • Reloj + fecha (RTC), iconos interactivos:
    • 🔵 BLE — gris si no hay datos en 5 s.
    • 🔊 / 🔇 Volumen — tap = mute/unmute.
    • 📶 Wi-Fi — tap = toggle AP on/off en caliente. Verde: portal web accesible. Azul: la web no responde (se rearma sola). Gris: Wi-Fi deshabilitado.
    • 📡 GPS — gris: el módulo no manda nada; naranja: llegan tramas pero sin fix; verde: con posición. Tap = Ajustes → GPS.
    • 💾 SD — verde: tarjeta montada y escribiendo; naranja/triángulo rojo: no está montada o lleva fallos de escritura seguidos (el detalle, en Ajustes → Tarjeta SD).
    • ⚙ / 🏠 — navega entre Live y Settings.
    • 🌡 Exterior: +26.0 °C — temperatura del DS18B20 exterior, siempre visible a la derecha.

BLE Victron

  • Hasta 8 dispositivos por MAC + clave AES (NVS).
  • Records soportados: 0x01 Solar Charger, 0x02 Battery Monitor, 0x03 Inverter, 0x04 DC/DC Converter, 0x05 Smart Lithium, 0x09 SBP, 0x0A Lynx Smart BMS, 0x0B Multi RS, 0x0C VE.Bus, 0x0D DC Energy Meter, 0x0F Orion XS.
  • Detección automática del tipo y vista correspondiente.

Cámara y modo vigilancia (autocaravana)

  • Sensor OmniVision OV02C10 por MIPI-CSI (2 lanes, RAW10 1928x1092, ~37 fps), driver propio portado del kernel Linux; ISP por hardware del ESP32-P4 (revelado del RAW con perfil IPA, no bypass) y JPEG por hardware. Comparte el bus I²C (SDA=7/SCL=8) con touch/RTC; sin pines dedicados.
  • Solo captura si alguien necesita la imagen: vigilancia encendida, o el auto-brillo pidiendo la luz ambiente (camera_set_luma_wanted). En reposo no hace DQBUF, los buffers del driver se llenan y el GDMA se para solo por contrapresión — que es justo lo que deja libre la SD. Nunca streaming continuo.
    • La cámara solo captura cuando alguien pide imagen (portal, snapshot o vigilancia), no en bucle.
    • La foto del portal no captura: monta el JPEG con la última miniatura publicada. Por eso, con la cámara en reposo, camera_snapshot_jpeg() la despierta, espera un fotograma NUEVO (hasta 4 s) y, si no llega, devuelve fallo en vez de servir una foto vieja.
  • GET /snapshot: última imagen en JPEG (recortada 960x528, calidad 78, ~80-150 KB).
  • Modo ausente / vigilancia: se activa desde Ajustes → Autocaravana o con GET /ausente?on (cuenta atrás de 10 s y apaga la pantalla). Detección de movimiento por rejilla de luminancia 32x18; al detectar guarda una foto JPEG en /sdcard/vigilancia, así que las fotos se conservan al reiniciar (tope de 300 por sesión, anti-runaway). Galería en GET /vigilancia (y /vigilancia/<id> para cada foto); descarga completa en GET /data/vigilancia.tar. Se sale con 4 toques en cualquier esquina (dentro de 3 s) o GET /ausente?off. (Vídeo H.264: pendiente.)
  • Cerrojo cámara↔SD (camera_sd_bus_lock): cámara y SDMMC comparten GDMA; los escritores de SD (datalogger) serializan su E/S con este mutex para no provocar reinicios por INT_WDT.

NE185 — control de la autocaravana (RS-485)

  • El P4 hace de maestro RS-485 del cuadro de distribución Nordelettronica NE185 (sustituye al panel NE187 retirado). MAX485 (U8) en UART1 (TX=GPIO 26 / RX=GPIO 27, 38400 8N1), DE/RE automático por puerta NAND; conector J4 (A/B/GND/+5V).
  • Polling cada 100 ms alternando dos comandos "idle" que imitan al NE187 (el NE185 solo devuelve datos si los ve alternados). Un botón = 8 tramas de pulsación (necesita ≥2) + 5 de reposo. Checksum + comprobación estructural; lectura robusta por barrido de fase (trama de 20 bytes y variante de 15 sin eco).
  • Datos: nivel de agua limpia (0-4/4) y grises (lleno/vacío), luz interior/exterior, bomba, presencia de 230 V (shore), tensión de batería de servicio y de motor, y "fresco" (trama válida < 30 s).
  • Controlable desde la UI (Overview) y por POST /control. Encendido automático de cargas al despertar (configurable, NVS).

Display satélite de cabina (UDP + HTTP)

  • El satélite es la 5" de la cabina (~/joint/cabina5). La 3,5" (~/joint/35cabina) está retirada desde el 09-nov-2026.
  • Broadcast UDP (1 Hz) desde el AP del 7" (192.168.4.255:4242). Payload compacto (40 bytes, versión 5, CRC32) con SoC/V/A de batería, canal auxiliar del shunt (batería motor), frigo (temperatura + ventilador), aguas del NE185, epoch_local (el reloj, que el satélite no tiene) y gps_estado. Protocolo compartido en main/net/mini_proto.h, byte a byte idéntico en los dos firmwares. Es "plan B" porque esp_hosted no exporta ESP-NOW.
  • Solo se manda lo FRESCO (bat_fresh, dcdc_fresh, cd.fresh), no lo que hubo alguna vez: si el SmartShunt deja de hablar, a los 30 s se manda el centinela de "no hay dato" y el satélite lo enseña apagado. Mandar el último valor conocido sería un dato viejo con pinta de actual, que es peor que no tener dato.
  • La vuelta va por HTTP, no por UDP: el satélite le manda a la P4 los apuntes del cuaderno con POST /api/viaje (ver abajo). Por TCP se sabe con certeza que han llegado.

Cuaderno de viaje (POST /api/viaje)

  • La 5" de la cabina abre y cierra viajes y le manda los apuntes; la P4 es la dueña de los datos: crea /sdcard/viajes/AAAA-MM-DD_Destino/, escribe el diario, un CSV por tipo (repostajes.csv, paradas.csv, pernoctas.csv, aguas.csv, itvs.csv…) y, al cerrar, un resumen.txt en castellano llano.
  • Totales del viaje: combustible y litros, peajes, gas, mantenimiento (con la ITV dentro), aguas y alojamiento (precio por noche × noches, más los servicios que se paguen aparte). Si aparece más de una moneda, el resumen avisa en vez de dar un total que parece bueno y no lo es.
  • El cuentakilómetros del cuadro, al salir y al llegar: la cabina los pide al empezar y al cerrar el viaje (campo opcional km_cuadro del inicio y del fin; 0 = no apuntado) y el resumen escribe las dos lecturas y su diferencia frente al recorrido del GPS. Es el número que dice lo que ha hecho el viaje de verdad: si el GPS midió menos, se perdió un tramo (un túnel, un parking cubierto), y si al llegar marcan menos que al salir el resumen lo dice en vez de restar. Los dos viven en la NVS del viaje y se borran con sus totales.
  • Los apuntes sin viaje (una salida puntual: repostar, ITV, taller) van al historial del vehículo en /sdcard/vehiculo, no a un viaje.
  • Cuenta los apuntes aplicados y los compara con los que dice el satélite haber generado: si falta alguno, el viaje se marca INCOMPLETO al descargarlo, en vez de dárte­lo por bueno.
  • La telemetría del viaje (cada 5 min) escribe solo lecturas frescas: con el SmartShunt apagado deja el hueco vacío en vez de repetir el último porcentaje hora tras hora. Un hueco se entiende; una lectura inventada se cree.
  • Un apunte que no quepa se rechaza entero con 413 y se dice por el log. Antes se recortaba en silencio: el JSON quedaba a medias, no parseaba, se devolvía 400 json inválido y el satélite lo descartaba por malo — un dato perdido disfrazado de dato inválido.

GPS (NEO-M9N)

  • Solo escucha NMEA a 38400 (lo de fábrica en la serie M9; los M8 venían a 9600, y probar a 9600 y no ver nada es el despiste clásico). No se le manda nada por UBX.
  • La brújula no está en la P4: la lleva la 5" en su vista de inclinación (IMU BNO055 en un I2C propio). Aquí solo hay GPS, que da posición y hora, no rumbo.
  • Pone el reloj en hora solo, y únicamente con trama válida (A): el módulo da una hora aproximada de su reloj interno antes de fijar, y usarla sería empeorar la que ya tiene el RTC.
  • Pantalla propia en Ajustes → GPS, con los tres estados diciendo QUÉ HACER: rojo "sin señal del módulo" (con el recordatorio de que RX/TX van cruzados), ámbar "buscando satélites" con los que ve, verde "posición fijada" con latitud, longitud y altitud. Debajo, las últimas tramas en crudo para diagnosticar.
  • El icono de la barra se toca y lleva a esa página. Un indicador que avisa de que algo va mal y no lleva a donde se mira obliga a buscarlo por los menús.
  • Mientras no hay posición, el log cuenta cada 10 s cuántas tramas llegan, cuántos satélites se ven y una trama de muestra; si no llega nada, recuerda que los cables van cruzados, que la alimentación es de 3,3 V y que son 38400 baudios. Se calla solo en cuanto fija.

Datalogger y persistencia

  • Frigo: buffer circular RAM 200 entradas + CSV diario en /sdcard/frigo/YYYY-MM-DD.csv.
  • Batería: 24 h en PSRAM (~945 KB) con muestreo cada 10 s y avg/max/min por intervalo. CSV diario en /sdcard/bateria/YYYY-MM-DD.csv (7 columnas).
  • kWh acumulados del día (/main/energy_today.c): integra V·I·Δt desde BMV; prefiere yield_today_centikwh del SmartSolar para PV. Reset automático a medianoche. Persistencia NVS cada 5 min.
  • Bombonas (main/data/bombonas.c): apunta la fecha/hora de cada cambio de bombona y saca la duración de cada una (la actual, la media y las últimas). Ojo con la distinción, que es el motivo de que exista: comprarla ya lo apunta el cuaderno de la cabina como gasto, pero comprar y usar son momentos distintos —una bombona puede pasarse meses de repuesto en el hueco—, así que lo que mide la duración es el cambio. Historial de 12 cambios en NVS y copia en /sdcard/vehiculo/bombonas.csv. Botón Deshacer para el dedazo, y si el reloj no está en hora no apunta nada en vez de inventar una fecha.
  • Energía del viaje (main/data/trip_computer.c): kWh y Ah cargados/consumidos, horas activas, aporte solar y kilómetros recorridos (odómetro por GPS, muestreo cada 5 s; descarta saltos por debajo de 15 m —deriva estando parado— y por encima de 1 km, y al recuperar el fix se re-ancla en vez de contar la línea recta: peca por defecto antes que inventar). El recorrido se ve en Ajustes → GPS. Además, mientras hay viaje abierto se escribe un ruta.csv en su carpeta (posición cada 30 s, solo si el vehículo se ha movido ≥20 m: la decisión la toma el propio contador de km, para no tener dos criterios distintos de "se mueve"). Los puntos se acumulan en RAM y bajan a la tarjeta cada 5 min o cada 10 puntos, lo que llegue antes, siguiendo el mismo patrón que datalogger y battery_history: en FAT cada apertura reescribe FAT y directorio, y escribir punto a punto multiplicaba por diez el desgaste. En el cierre del viaje se vuelca lo pendiente. Persistencia NVS cada 5 min. Guarda además el tiempo CONDUCIENDO (suma de los intervalos del GPS —cada 5 s— con la velocidad del RMC por encima de 4 km/h; sin fix no cuenta, igual que el odómetro), que es lo que la pantalla de 5" enseña en su tarjeta del viaje y lo que sale como Conduciendo: en el resumen: no son las horas con el viaje abierto, porque las noches paradas de un viaje de tres días no son horas de volante. Los pone a cero el inicio de viaje del cuaderno de la cabina (/api/viaje), y el fin de viaje los cierra tras volcarlos al apartado ENERGIA del resumen.txt — así ese apartado es del viaje y no de una mezcla. En Ajustes queda un Poner a cero suelto para medir algo aparte.
  • Flush a SD periódico de frigo y batería; flush adicional antes de cualquier reset programado.
  • Backup horario del epoch del sistema en NVS (rtc_backup/epoch).

Qué se borra solo de la tarjeta

  • Logs diarios a 120 días (log_cleanup.c; las fotos de vigilancia siguen a 60): /sdcard/frigo, /sdcard/bateria y /sdcard/ne185v (este último escribe una línea por minuto). Solo borra ficheros con el nombre exacto AAAA-MM-DD.csv, y nunca hoy ni ayer aunque se le pida menos retención, para no chocar con una escritura en curso. Si el reloj no tiene fecha fiable, aborta en vez de borrar a ciegas.
  • Fotos de vigilancia a 60 días: son carpetas de sesión /sdcard/vigilancia/AAAAMMDD_HHMMSS/ con hasta 300 JPEG dentro — unos 60-120 KB cada uno, o sea hasta ~35 MB por sesión. El tope de 300 es POR SESIÓN y las sesiones no tienen límite: nada las borraba, y al llenarse la tarjeta se lleva por delante el cuaderno de viaje. Retención elegida por el usuario; son pruebas de un allanamiento y por eso 60 días y no menos.
  • NO se toca nunca: /sdcard/viajes, /sdcard/vehiculo ni /sdcard/config_backup. Eso es justo lo que hay que conservar.

RTC y hora

  • Zona horaria Europe/Madrid por defecto (CET/CEST), aplicada desde NVS al arrancar y preservada en el backup de configuración.
  • El RTC almacena hora local; mktime la interpreta con la TZ activa.

Salvapantallas

  • Modos Atenuar o Rotar vistas (Live → LogFrigo → LogBateria cada N min).
  • Periodo y brillo de reposo configurables en Settings → Display.

Watchdog + auto-recovery

  • Hardware (IDF): TWDT 10 s + INT_WDT 500 ms, ambos con panic + reset al disparar.
  • Software (/main/watchdog.c): task monitor adicional que comprueba salud de LVGL cada 3 s; si el lock falla 3 veces consecutivas fuerza un reset controlado. También vigila por heartbeat las tareas de app (>10 s sin latido → reset). Grace period de 30 s al boot.
  • A propósito NO hace flush a SD antes de ese reset: si el cuelgue lo causa el propio subsistema SD/FAT (mutex retenido por una tarea muerta), el flush haría deadlock y el reset nunca ocurriría. Se prefiere perder el último bloque de muestras a no reiniciar.
  • El motivo se traduce entero: además de los de siempre, el P4 informa de USB (el reinicio de grabar por cable), JTAG, error de efuse, bajón de tensión y bloqueo de CPU (doble excepción). Hasta el 24-ago-2026 esos cinco salían como "Unknown" — el de bloqueo de CPU es un cuelgue de los gordos disfrazado de "no se sabe", y encima no contaba en el contador: la placa se reiniciaba sola sin dejar rastro.
  • Contador de resets por TWDT/INT_WDT/panic/bloqueo de CPU y por watchdog SW en NVS, visible en Settings → About junto al motivo del último reinicio. Un reset forzado por el monitor SW aparece como Watchdog SW (UI congelada) o Watchdog SW (tarea muda), no como un genérico "Software".
  • v1.4.4: todo acceso a la SD se serializa con la cámara vía camera_sd_bus_lock() para evitar contención SDMMC → INT_WDT (recurrente en versiones previas). Los flushes periódicos de datalogger, battery_history y ne185_vlog tenían un stat() de comprobación de cabecera que se colaba FUERA de ese cerrojo — arreglado.
  • v1.5.2: la actualización OTA se reiniciaba a medias con cualquier versión, por contención de caché entre núcleos durante la escritura de flash (spi_flash_op_block_func bloqueando al núcleo que no escribe; si se alarga por carga concurrente, el TWDT lo detecta como colgado). CONFIG_SPIRAM_XIP_FROM_PSRAM mueve la ejecución del firmware a PSRAM para que ese bloqueo no le quite el código a ningún núcleo, más el margen del TWDT subido a 10 s.

Portal web

  • AP P4Config automático al arrancar (el nombre de fábrica; una placa que ya tenga otro guardado sigue con el suyo hasta cambiarlo en Ajustes → Wi-Fi).
  • El portal web está siempre encendido, igual que el AP y el UDP: PORTAL_SIEMPRE_ENCENDIDO en config_server_ap.c.
    • Si el móvil ya estaba asociado y aun así no responde, hay un botón Reactivar portal web en Ajustes → Wi-Fi.
  • Seguridad — dos niveles (check_basic_auth / check_basic_auth_strict en config_server_auth.c):
    • Descargas de datos — aceptan la clave del portal o la de administración: los .csv, el HTML de datos y los .tar de frigo, batería, solar, histórico, viaje, capturas, vigilancia y logs. La app manda la del portal, así que la clave de administración solo sale del móvil al grabar firmware, al borrar un viaje o al bajar config.tar.
    • Abierto — estado, cámara y los apuntes del satélite: con PORTAL_REQUIRE_BASIC_AUTH a 1, este nivel exige el usuario y la clave del portal (los mismos que usan la app y las cabinas), no la de administración.
    • Estricto — /ota, /api/pantalla/subir, /save, /keys, /data/config.tar (el respaldo puede llevar las AES), borrar un viaje y las capturas de wifi/victron_keys: piden además la clave de administración. Son los que reescriben el firmware, entregan las claves AES de los Victron o las enseñan en una captura; sin esto, prestar el Wi-Fi a un invitado equivalía a darle control total. La app sí usa /ota (tiene un botón de actualizar firmware): con la clave de administración puesta, esa actualización desde el móvil da 401 y hay que hacerla desde el navegador, que es donde se teclea la clave; la app lo dice con un mensaje propio. El resto de la app (estado, control, cámara, descargas y apuntes) va por el nivel abierto. El navegador la pide una vez y la recuerda. Las dos claves se consultan en Ajustes → Wi-Fi.
    • Son dos claves distintas (portal vs. administración) a propósito: si el nivel abierto pidiera la de administración, generar esa clave dejaría fuera a las cabinas, que solo tienen la del portal.
    • Sigue siendo HTTP plano sobre WPA2-PSK: protege del invitado al que le has dado el Wi-Fi, no de quien ya está dentro capturando tráfico.
    • El AP va con WPA2-PSK y clave aleatoria (config_server_ap.c, vía esp_hosted/C6); host y esclavo van emparejados, ver documentacion/C6_FIRMWARE_ACTUALIZACION.md. TLS (esp_https_server) se descarta por ahora: exige certificado autofirmado, memoria extra por conexión (compite con cámara/display/BLE) y solo protegería de quien ya tiene la clave Wi-Fi.
  • Nav consistente en todas las páginas: Dashboard · Logs · Keys.
  • http://192.168.4.1/dashboard (default): grid responsive con SoC, V/A/W, PV, DC/DC y energía de hoy. Auto-refresh 2 s via GET /api/state (JSON).
  • /data/frigo y /data/bateria: gráficos SVG con auto-escala Y y downsample > 1500 puntos; polígono semitransparente max-min + línea avg.
  • Descargas sin sacar la SD: /data/frigo.csv y /data/bateria.csv (día en curso), y los .tar completos de /data/frigo.tar, /data/bateria.tar, /data/logs.tar, /data/config.tar, /data/capturas.tar y /data/vigilancia.tar.
  • /data/ne185v.csv — log de diagnóstico NE185 vs SmartShunt (día en curso, sin página ni gráfica). Una muestra por minuto en /sdcard/ne185v/ con los bytes 12/13 de la trama NE185 sin convertir junto al voltaje que da el SmartShunt por BLE. Sirve para deducir por regresión la fórmula real del NE185: la documentada, V = (raw − 30)/10, viene del NE334 y nunca se ha medido aquí, porque el voltaje se lee del shunt y no de este bus. Columnas: timestamp,ne_raw_serv,ne_raw_mot,ne_fresh,shunt_centivolts,shunt_fresh (usar solo las filas con ambos fresh = 1).
  • Actualización por Wi-Fi (OTA) en http://192.168.4.1:8081/ota (servidor pesado, puerto 8081: /ota no está en el 80): se sube el .bin desde el navegador, sin cable ni PC.
  • /snapshot: última foto de la cámara en JPEG. /capturas y /captura?n=<0..23>: capturas de las pantallas del propio display.
  • /keys: configuración de claves Victron.
  • Memoria interna justa: el heap interno vive en ~65-70 KB libres. Medido con /api/perf (09-nov-2026), una descarga grande lo bajaba a ~650 B porque lwIP tiene en vuelo hasta (ventana + buffer de envío) de pbufs; se bajaron los dos a 16 KB (CONFIG_LWIP_TCP_*) y el mínimo durante una descarga sube a ~10 KB, costando ~10 % más de tiempo (4,5 → 5,0 s en 2 MB). Si alguna vez hay que apurar la memoria, el siguiente escalón es 8 KB (más margen, descargas ~3 veces más lentas).
  • GET /api/perf (clave de administración): números de rendimiento para diagnosticar sin adivinar — % de CPU por tarea y núcleo, pila mínima libre de cada una, heap interno (libre / bloque mayor / mínimo histórico), coste del volcado a la SD (media y máximo) y coste del volcado de LVGL por bloque. La foto por tarea se toma al pedir el endpoint (necesita uxTaskGetSystemState, que paraliza el planificador unos ms; desde v5.10 no se llama nunca de forma periódica porque era lo que hacía destellar la pantalla). El resumen que va al log cada 10 min lleva el % de CPU global (medido con los contadores de ralenti, sin parar nada), heap interno libre, y los costes de SD y LVGL; acaba en /data/logs.tar. Medido el 09-nov-2026 con la pantalla normal: ~12 % de un núcleo para taskLVGL, ~0,23 ms por bloque de LVGL (máximo 1,5 ms), volcado a SD ~19 ms y heap interno libre ~66 KB (mínimo visto 14 KB).
  • Sincronización automática de hora (<img> GET → /settime, compatible captive portal).

Backup/restore de configuración

  • Exporta brightness, view_mode, zona horaria, modo nocturno, screensaver, alerts y flag debug. OJO: el export NUEVO ya NO mete las claves AES de los Victron a propósito (el respaldo es un fichero de texto en la SD) a /sdcard/config_backup.json con cJSON. Pero un fichero de una versión anterior sí puede llevarlas (el que hay hoy en la tarjeta de la P4 las lleva, del 2026-07-09), y por eso /data/config.tar sigue pidiendo la clave de administración.
  • Importa desde el mismo fichero. La contraseña Wi-Fi no se exporta por seguridad.
  • Botones Exportar/Importar en Settings → About.

Compilación

Requisitos: ESP-IDF v5.5.5 exactamente, target esp32p4 (es la que usan el CI, release.sh y los .bin publicados).

git clone https://github.com/Ehuntabi/p4
cd p4
idf.py set-target esp32p4
idf.py build
scripts/grabar.sh /dev/ttyACM0   # graba lo PUBLICADO (y comprueba que lo esta)

Configuración inicial

  1. Encender la pantalla — el AP Wi-Fi P4Config arranca automáticamente.
  2. Conectarse al AP desde móvil o PC y abrir http://192.168.4.1 — por defecto se carga el Dashboard.
  3. Navegar a Keys (link superior) e introducir la MAC y clave AES de cada dispositivo Victron.
  4. Volver al Dashboard para ver los datos en vivo.

Estado del hardware

  • SD Card ✅ — slot 0 IOMUX dedicado.
  • RTC RX8025T ✅ — soporta pérdida de pila con backup en NVS.
  • Cámara OV02C10 ✅ — MIPI-CSI con ISP por hardware. Comparte GDMA con la SD: captura a demanda, nunca streaming continuo.
  • NE185 (RS-485) ✅ — MAX485 en UART1, en uso como maestro del cuadro.
  • GPS NEO-M9N ⏳ — código listo y grabado (23-ago-2026), módulo por conectar: RX al GPIO 3, TX al GPIO 2, 3,3 V y GND, todo en el JP1. En cuanto hable, la pantalla se pone en hora sola. Indicador en la barra inferior y página propia en Ajustes → GPS con las tramas en crudo.
  • DS18B20 ✅ — bus 1-Wire en GPIO 4 (JP1 pin 13). Pullup 4.7 kΩ a 3.3 V externo. El bus necesita resets de warm-up antes de enumerar.
  • Ventilador — PWM 25 kHz en GPIO 5 (JP1 pin 15), con rampa de 10 %/s: 10 s de 0 a 100 % y otros 10 s de vuelta.

Créditos

  • Proyecto original: VictronSolarDisplayEsp por wytr.
  • Fork multi-device: victronsolardisplayesp por CamdenSutherland.
  • Port ESP32-P4 / Guition JC1060P470C_I + UI inspirada en la vista Overview de Venus OS + extensiones: Ehuntabi.

English

Port of victronsolardisplayesp (multi-device) by CamdenSutherland, itself a fork of VictronSolarDisplayEsp by wytr.

Ported by Ehuntabi.

Hardware

Component Details
Display Guition JC1060P470C_I (7", 1024x600, DSI, GT911 touch)
Main SoC ESP32-P4 (RISC-V dual core 400 MHz, 32 MB PSRAM, 16 MB flash)
WiFi/BT SoC ESP32-C6 (SDIO via esp_hosted: CLK=18, CMD=19, D0..D3=14..17)
RTC RX8025T (I²C 0x32, SDA=7 / SCL=8 shared with GT911 and ES8311) + CR1220 cell
microSD SDMMC slot 0 IOMUX (CLK=43, CMD=44, D0..D3=39..42), internal LDO ch 4 (3.3 V)
Temp sensors DS18B20 1-Wire on GPIO 4 (JP1 pin 13, 4.7 kΩ pull-up)
Fridge fan LEDC PWM on GPIO 5 (JP1 pin 15), 25 kHz, with a 10 %/s ramp (10 s from 0 to 100; same up and down). Pin 15 drives an IRLR7843TRPBF MOSFET (R3 100 Ω series gate resistor, R4 10 k pull-down, D2 1N5819 flyback, C2 100 µF)
Audio ES8311 codec + NS4150 amp (PA_CTRL=11, I²S MCLK=13/BCLK=12/LRCK=10/DOUT=9)
Camera OmniVision OV02C10 (MIPI-CSI 2 lanes, RAW10 1928x1092, ~37 fps, HW JPEG). Shares I²C (SDA=7 / SCL=8); no dedicated reset/pwdn pins
NE185 (camper) RS-485 to the Nordelettronica NE185 panel via MAX485 (U8), UART1 TX=GPIO 26 / RX=GPIO 27, 38400 8N1, J4 connector (automatic DE/RE)
GPS u-blox NEO-M9N on UART2 RX=GPIO 3 / TX=GPIO 2 (JP1 header), 38400 8N1. NMEA listen-only; sets the clock by itself

Detailed pinout with colour-coded JP1 and CN2 diagrams in docs/pinout_guition_jc1060p470c_i.pdf.

Features

UI inspired by the Venus OS Overview

  • Dark cards with role-coloured borders (cyan/green/orange/red/purple), big-number metrics, status pills, circular SOC gauges, soft shadows.
  • Custom PNG icons in headers (car battery, solar panel, DC/DC, house) embedded as lv_img_dsc_t.
  • Reusable helpers in main/ui/ui_card.h: ui_card_create, ui_card_set_title_img, ui_metric_create_compact/_large, ui_pill_create, ui_arc_soc_create, ui_card_pulse, palette UI_COLOR_*.
  • Activity pulse on card shadow when a BLE record arrives (600 ms ease-in-out, no anim stacking).
  • Live/Settings tabs hidden — navigation via ⚙/🏠 icon in the bottom bar + horizontal swipe. The Home button closes any sub-page and leaves Settings on its main menu.
  • Auto-return to Live after 60 s of touch inactivity.

Splash screen

  • Camper photo cut out with AI (rembg/u2net) and embedded at 720x400 RGB565 in flash.
  • Animated progress bar during init (BLE, audio, SD, BT).
  • Configurable in Settings → Display: Camper logo (default) or No splash.

Live views

  • Overview (default): horizontal row Solar — Battery — Loads with central SOC arc; below: TTG + “PV X.X kWh · Loads Y.Y kWh” today's totals.
  • Battery Monitor: orange card with big SOC arc, V/A/W + enriched pill (“Charging 5.5 A” / “Discharging 3.2 A”), bipolar current bar ±50 A, power sparkline (90 points), TTG / Consumed / Aux. Tap opens history.
  • Solar Charger: 2 cards (green Solar + orange Battery output) with charger state pill (Bulk/Absorption/Float).
  • Default Battery: 3 cards (cyan DC/DC, orange Battery with arc + sparkline + bipolar bar, green Solar). DC/DC label switches between ⚡ Alternator (green) and 🔋 Engine bat. (grey) by voltage.
  • Simple devices (Inverter, Smart Lithium, AC Charger, etc.): single card with stacked metrics.
  • Red “FAULT: X” pill when a Victron device reports alarm_reason (BMV/Inverter) or charger_error (Solar/DCDC/Orion).

Settings

Pages with role-coloured cards, scrollbar visible on overflow, separators between sub-blocks:

  • Chart history (purple): 24 h battery + frigo temperature charts. Swipe left/right navigates by date across SD logs (header shows the active day).
  • Wi-Fi (orange): SSID, password and an on/off switch applied live, with no board restart. (Changing SSID or password does need the switch toggled off and on to take effect.) Note: with the AP off, the cabin display gets no telemetry.
  • Display (cyan): combined Brightness + Screensaver card; single-line Night mode card (switch + start/end + night brightness, auto-applied within the time window by RTC); Splash screen (camper logo or none).
  • SD card (blue): screenshot carousel and SD image viewer.
  • Sound & alerts (red): volume, mute switch, SoC and freezer temperature thresholds.
  • Motorhome (pink): groups everything vehicle-specific — Fridge options (DS18B20 sensors, PWM fan based on T_Fins with a 10 %/s ramp, thresholds and solar-surplus usage), Victron Keys (MAC + AES key per device, up to 8), Away mode, Energía del viaje (kWh/Ah charged/discharged and hours for the current trip), Bombonas (gas-bottle change button plus how long each one lasts) and Auto switch-on of the interior light + pump at wake.
  • About (gray): uptime, free RAM, AP IP, chip, IDF version, Configuration backup (export/import to /sdcard/config_backup.json), Last reset + WDT/panic reset counter, Reboot button.

Bottom bar

  • Clock + date (RTC), interactive icons:
    • 🔵 BLE — grey if no data for 5 s.
    • 🔊 / 🔇 Volume — tap to mute/unmute.
    • 📶 Wi-Fi — tap to toggle the AP live. Green: web portal reachable. Blue: web not answering (it re-arms itself). Grey: Wi-Fi disabled.
    • 📡 GPS — grey: the module sends nothing; orange: frames arriving but no fix; green: position. Tap = Settings → GPS.
    • 💾 SD — green: card mounted and writing; orange/red triangle: not mounted or repeated write failures (details in Settings → SD card).
    • ⚙ / 🏠 — navigate between Live and Settings.
    • 🌡 Exterior: +26.0 °C — outdoor DS18B20 reading, always visible on the right.

Victron BLE

  • Up to 8 devices by MAC + AES key (NVS).
  • Records: 0x01 Solar Charger, 0x02 Battery Monitor, 0x03 Inverter, 0x04 DC/DC Converter, 0x05 Smart Lithium, 0x09 SBP, 0x0A Lynx Smart BMS, 0x0B Multi RS, 0x0C VE.Bus, 0x0D DC Energy Meter, 0x0F Orion XS.
  • Automatic device-type detection and matching view.

Camera & surveillance mode (camper)

  • OmniVision OV02C10 sensor over MIPI-CSI (2 lanes, RAW10 1928x1092, ~37 fps), custom driver ported from the Linux kernel; hardware ISP on the ESP32-P4 (RAW development with an IPA profile, not bypassed) and hardware JPEG. Shares the I²C bus (SDA=7/SCL=8) with touch/RTC; no dedicated pins.
  • It only captures when someone needs the image: surveillance on, or auto-brightness asking for the ambient light (camera_set_luma_wanted). While idle it doesn't DQBUF, the driver buffers fill and the GDMA stops on its own through backpressure — which is exactly what frees the SD. Never continuous streaming.
    • Until 2026-08-24 it grabbed a frame every 2 s no matter what, to feed an auto-brightness that has been DISABLED in main.c for a long time: the camera and its DMA working 24/7, in a battery-powered vehicle, for a value nobody read.
    • The portal snapshot doesn't capture: it encodes the last published thumbnail. So with the camera idle, camera_snapshot_jpeg() wakes it, waits for a NEW frame (up to 4 s) and, if none arrives, fails instead of serving a stale photo.
  • GET /snapshot: latest image as JPEG (cropped 960x528, quality 78, ~80-150 KB).
  • Away / surveillance mode: enabled from Settings → Motorhome or via GET /ausente?on (10 s countdown, then the screen turns off). Motion detection via a 32x18 luminance grid; on a hit it stores a JPEG under /sdcard/vigilancia, so photos survive a reboot (capped at 300 per session as a runaway guard). Gallery at GET /vigilancia (and /vigilancia/<id> per photo); full download at GET /data/vigilancia.tar. Exit with 4 taps in any corner (within 3 s) or GET /ausente?off. (H.264 video: pending.)
  • Camera↔SD lock (camera_sd_bus_lock): camera and SDMMC share GDMA; SD writers (datalogger) serialize their I/O with this mutex to avoid INT_WDT resets.

NE185 — camper control (RS-485)

  • The P4 acts as RS-485 master of the Nordelettronica NE185 distribution panel (replacing the removed NE187 panel). MAX485 (U8) on UART1 (TX=GPIO 26 / RX=GPIO 27, 38400 8N1), automatic DE/RE via NAND gate; J4 connector (A/B/GND/+5V).
  • Polling every 100 ms alternating two "idle" commands that mimic the NE187 (the NE185 only returns data when it sees them alternated). One button = 8 press frames (needs ≥2) + 5 idle frames. Checksum + structural check; robust reading via phase sweep (20-byte frame and a 15-byte echo-less variant).
  • Data: fresh water level (0-4/4) and grey water (full/empty), interior/exterior light, pump, presence of 230 V (shore), service and engine battery voltage, and "fresh" (valid frame < 30 s).
  • Controllable from the UI (Overview) and via POST /control. Automatic load switch-on on wake (configurable, NVS).

Cabin satellite display (UDP + HTTP)

  • The satellite is the 5" cabin display (~/joint/cabina5). The 3.5" (~/joint/35cabina) was retired on 2026-11-09.
  • UDP broadcast (1 Hz) from the 7" AP (192.168.4.255:4242). Compact payload (40 bytes, version 5, CRC32) with battery SoC/V/A, shunt aux channel (starter battery), fridge (temperature + fan), NE185 water levels, epoch_local (the clock the satellite doesn't have) and gps_estado. Shared protocol in main/net/mini_proto.h, byte-for-byte identical in both firmwares. It's "plan B" because esp_hosted doesn't export ESP-NOW.
  • Only FRESH data is sent (bat_fresh, dcdc_fresh, cd.fresh), never "we had it once": if the SmartShunt goes quiet, after 30 s the no-data sentinel is sent and the satellite greys it out. Sending the last known value would be stale data wearing a current-data face, which is worse than no data at all.
  • The return path is HTTP, not UDP: the satellite posts trip notes to the P4 with POST /api/viaje (see below). Over TCP you know for certain they arrived.
  • The vehicle's odometer, on departure and on arrival (2026-10-16): the cabin asks for both when starting and closing the trip (optional km_cuadro field in inicio and fin; 0 = not recorded) and the summary prints both readings and their difference against the GPS distance. That is the number that says what the trip really did: if the GPS measured less, a stretch was lost (a tunnel, an underground car park), and if the arrival reading is lower than the departure one the summary says so instead of subtracting. Both live in the trip's NVS and are cleared with its totals.

Datalogger and persistence

  • Frigo: 200-entry RAM ring + daily CSV at /sdcard/frigo/YYYY-MM-DD.csv.
  • Battery: 24 h in PSRAM (~945 KB) sampled every 10 s with avg/max/min per bin. Daily CSV at /sdcard/bateria/YYYY-MM-DD.csv (7 columns).
  • Today’s kWh accumulator (main/energy_today.c): integrates V·I·Δt from BMV; prefers yield_today_centikwh from SmartSolar for PV. Auto-reset at midnight. NVS persistence every 5 min.
  • Bombonas (main/data/bombonas.c): records the date/time of every gas-bottle change and derives how long each lasts (current one, average, last few). The distinction is the point: buying a bottle is already logged as an expense by the cabin notebook, but buying and fitting are different moments —a spare can sit in the locker for months— so it is the change that measures duration. 12 changes kept in NVS plus a copy in /sdcard/vehiculo/bombonas.csv. An Undo button covers mis-taps, and if the clock is not set it records nothing rather than inventing a date.
  • Energía del viaje (main/data/trip_computer.c): kWh and Ah charged/discharged, active hours, solar contribution and distance travelled (GPS odometer sampled every 5 s; discards jumps below 15 m —drift while parked— and above 1 km, and re-anchors after a lost fix instead of counting the straight line: it under-reports rather than invents). Distance is shown in Settings → GPS. While a trip is open a ruta.csv track is also written into its folder (a fix every 30 s, only when the vehicle has moved ≥20 m — the decision comes from the km counter itself, so there is a single definition of "moving"). Points are buffered in RAM and flushed every 5 min or every 10 points, whichever comes first — the same pattern as datalogger and battery_history: on FAT every open rewrites the FAT and the directory entry, so writing point by point multiplied wear tenfold. Pending points are flushed when the trip closes. NVS persistence every 5 min. Since 2026-10-16 it also keeps the DRIVING time (sum of the GPS intervals —every 5 s— with the RMC speed above 4 km/h; without a fix nothing is counted, same as the odometer), which is what the 5" screen shows in its trip card and what appears as Conduciendo: in the summary: not the hours with the trip open, because the parked nights of a three-day trip are not hours at the wheel. Zeroed by the cabin notebook's trip start (/api/viaje) and closed by the trip end, after they are written into the resumen.txt ENERGIA section — so that section belongs to that trip only. A standalone Poner a cero remains in Settings. Until 2026-08-24 they were reset by hand only, so a summary could carry energy from previous trips.
  • Periodic SD flush of frigo and battery logs; extra flush before any scheduled reset.
  • Hourly NVS backup of the system epoch (rtc_backup/epoch).

What gets deleted from the card automatically

  • Daily logs after 120 days (log_cleanup.c; surveillance photos stay at 60): /sdcard/frigo, /sdcard/bateria and /sdcard/ne185v (the latter writes one line per minute and nothing cleaned it until 2026-08-24). Only files named exactly YYYY-MM-DD.csv, and never today or yesterday even if asked for a shorter retention, so it can't race an in-flight write. If the clock has no trustworthy date it aborts instead of deleting blindly.
  • Surveillance photos after 60 days: session folders /sdcard/vigilancia/YYYYMMDD_HHMMSS/ holding up to 300 JPEGs — 60-120 KB each, i.e. up to ~35 MB per session. The 300 cap is PER SESSION and sessions were unbounded: nothing ever deleted them, and a full card takes the trip notebook down with it.
  • Never touched: /sdcard/viajes, /sdcard/vehiculo or /sdcard/config_backup. That's exactly what must be kept.

RTC and time

  • Europe/Madrid timezone by default (CET/CEST), applied from NVS at boot and preserved in the configuration backup.
  • RTC stores local time; mktime interprets it with the active TZ.

Screensaver

  • Modes Dim or Rotate views (cycle Live → FrigoLog → BatteryLog every N min).
  • Period and idle brightness configurable in Settings → Display.

Watchdog + auto-recovery

  • Hardware (IDF): TWDT 10 s + INT_WDT 500 ms, both panic + reset.
  • Software (main/watchdog.c): monitor task that checks LVGL health every 3 s; if the lock fails 3 times in a row it forces a controlled reset. It also watches app tasks by heartbeat (>10 s silent → reset). 30 s grace period at boot.
  • It deliberately does NOT flush to SD before that reset: if the hang is caused by the SD/FAT subsystem itself (mutex held by a dead task), the flush would deadlock and the reset would never happen. Losing the last block of samples beats not rebooting.
  • Reset counter for TWDT/INT_WDT/panic and SW watchdog in NVS, shown in Settings → About next to the last reset reason. A reset forced by the SW monitor reads as Watchdog SW (UI congelada) or Watchdog SW (tarea muda) rather than a generic "Software".
  • v1.4.4: every SD access is serialized with the camera via camera_sd_bus_lock() to avoid SDMMC contention → INT_WDT (recurring in earlier versions). The periodic flushes in datalogger, battery_history and ne185_vlog had a header-check stat() that slipped OUTSIDE that lock — fixed.
  • v1.5.2: Wi-Fi OTA updates kept restarting mid-upload on any version, due to cross-core cache contention during flash writes (spi_flash_op_block_func blocks the core not writing; if that stretches out under concurrent load, the TWDT sees it as hung). CONFIG_SPIRAM_XIP_FROM_PSRAM moves firmware execution to PSRAM so that block no longer starves either core of code to run, plus the TWDT margin raised to 10 s.

Web portal

  • P4Config AP starts automatically on boot (factory name; a board that already has another one stored keeps it until it is changed in Settings → Wi-Fi).
  • The web portal is always up, and so are the AP and UDP: PORTAL_SIEMPRE_ENCENDIDO in config_server_ap.c.
  • Security — two tiers (check_basic_auth / check_basic_auth_strict in config_server_auth.c):
    • Open — state, camera, downloads and the satellite's trip notes: with PORTAL_REQUIRE_BASIC_AUTH set to 1, this tier asks for the portal user and password (the same ones the app and the cabins use), not the admin key. It was closed when we found the AP had no key at all: esp_hosted brought the network up open with its factory name, so "being on the Wi-Fi" meant being on the street. It stays closed even though the AP has used real WPA2 since 2026-09-23 (last bullet): reopening it is a separate decision and has not been made.
    • Strict — /ota, /save, /keys and the wifi/victron_keys screenshots: these also require the admin key. They rewrite the firmware, hand over the Victron AES keys or show those keys in a screenshot; without this, lending someone the Wi-Fi key amounted to giving them full control. The app does use /ota (it has a firmware-update button): with the admin key set, that update from the phone returns 401 and has to be done from the browser, where the key is typed; the app says so with its own message. The rest of the app (state, control, camera, downloads, trip notes) uses the open tier. A browser asks once and remembers. Both keys are shown in Settings → Wi-Fi.
    • The two tiers use different keys (portal vs. admin) on purpose: if the open tier required the admin key, generating that key would lock the cabins out, since they only hold the portal one.
    • It is still plain HTTP over WPA2-PSK: it protects against the guest you handed the Wi-Fi to, not against someone already inside capturing traffic.
    • The AP uses WPA2-PSK with a random key (config_server_ap.c, via esp_hosted/C6); host and slave firmware go in pairs, see documentacion/C6_FIRMWARE_ACTUALIZACION.md. TLS (esp_https_server) is set aside for now: it needs a self-signed certificate, extra memory per connection (competing with camera/display/BLE) and would only protect against someone who already has the Wi-Fi key.
  • Consistent navigation across pages: Dashboard · Logs · Keys.
  • http://192.168.4.1/dashboard (default): responsive grid with SoC, V/A/W, PV, DC/DC and today's energy. Auto-refresh every 2 s via GET /api/state (JSON).
  • /data/frigo and /data/bateria: SVG charts with Y-axis autoscale and downsample over 1500 points; semitransparent max-min envelope + average line.
  • Downloads without pulling the SD card: /data/frigo.csv and /data/bateria.csv (current day), plus full archives at /data/frigo.tar, /data/bateria.tar, /data/logs.tar, /data/config.tar, /data/capturas.tar and /data/vigilancia.tar.
  • /data/ne185v.csv — NE185 vs SmartShunt diagnostic log (current day; no page, no chart). One sample per minute under /sdcard/ne185v/ holding the raw, unconverted bytes 12/13 of the NE185 frame next to the battery voltage reported by the SmartShunt over BLE. Its purpose is to derive the NE185's real formula by regression: the documented one, V = (raw − 30)/10, is inherited from the NE334 and has never been measured here, because voltage is read from the shunt rather than from this bus. Columns: timestamp,ne_raw_serv,ne_raw_mot,ne_fresh,shunt_centivolts,shunt_fresh (use only rows where both fresh = 1).
  • Wi-Fi firmware update (OTA) at http://192.168.4.1:8081/ota (heavy server, port 8081: /ota is not on port 80): upload the .bin from the browser — no cable, no PC.
  • /snapshot: latest camera frame as JPEG. /capturas and /captura?n=<0..23>: screenshots of the display's own screens.
  • /keys: Victron key configuration page.
  • Automatic time sync via <img> GET → /settime (captive-portal compatible).

Configuration backup/restore

  • Exports brightness, view_mode, timezone, night mode, screensaver, alerts and debug flag. NOTE: Victron AES keys are deliberately NOT exported (the backup is plain text on the SD card) to /sdcard/config_backup.json via cJSON.
  • Imports from the same file. The Wi-Fi password is not exported for safety.
  • Export/Import buttons in Settings → About.

Build

Requirements: ESP-IDF v5.5.5 exactly, target esp32p4 (the one used by CI, release.sh and the published .bins).

git clone https://github.com/Ehuntabi/p4
cd p4
idf.py set-target esp32p4
idf.py build
scripts/grabar.sh /dev/ttyACM0   # graba lo PUBLICADO (y comprueba que lo esta)

First-time setup

  1. Power on the display — the P4Config Wi-Fi AP starts automatically.
  2. Connect to the AP from your phone or PC and open http://192.168.4.1 — the Dashboard loads by default.
  3. Click Keys in the top nav and enter the MAC and AES key for each Victron device.
  4. Back to Dashboard to see live data.

Hardware status

  • SD card ✅ — dedicated IOMUX slot 0.
  • RTC RX8025T ✅ — survives battery loss with NVS backup.
  • OV02C10 camera ✅ — MIPI-CSI with hardware ISP. Shares GDMA with the SD card: capture on demand, never continuous streaming.
  • NE185 (RS-485) ✅ — MAX485 on UART1, in use as master of the distribution panel.
  • DS18B20 ✅ — 1-Wire bus on GPIO 4 (JP1 pin 13). External 4.7 kΩ pull-up to 3.3 V. The bus needs warm-up resets before enumerating.
  • Fan — 25 kHz PWM on GPIO 5 (JP1 pin 15), with a 10 %/s ramp: 10 s from 0 to 100 % and another 10 s back.

Credits

  • Original project: VictronSolarDisplayEsp by wytr.
  • Multi-device fork: victronsolardisplayesp by CamdenSutherland.
  • ESP32-P4 / Guition JC1060P470C_I port + UI inspired by the Venus OS Overview + extensions: Ehuntabi.

This project keeps the same license as the original. See LICENSE.

About

Firmware para la pantalla táctil (Guition JC1060P470C con ESP32-P4) monitoriza y controla dispositivos Victron y panel Nordelettronica NE185/NE187 en autocaravana, frigo, niveles de depósitos y logs.

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages