From 5177b82791771c2ed1ae109d92aec3523a286da9 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Mon, 27 Jul 2026 18:13:21 +0200 Subject: [PATCH] feat(camera): la webcam arranca sola al ejecutar en una ESP32-CAM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Un sketch de ESP32-CAM existe para capturar, asi que esperar el click en el toggle se lee como 'la camara esta rota': el guest arranca, esp_camera_init completa contra el OV2640 modelado, y cam_hal agota el tiempo eternamente esperando fotogramas que nadie envia — sin pedir siquiera el permiso. Ahora la webcam se solicita al iniciar la ejecucion; el prompt del navegador ES el consentimiento, y el toggle queda como apagador manual. Solo una vez por ejecucion: parar el stream a mano no lo re-dispara. --- .../src/components/simulator/CameraToggle.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frontend/src/components/simulator/CameraToggle.tsx b/frontend/src/components/simulator/CameraToggle.tsx index c84571d3..fe2c95f6 100644 --- a/frontend/src/components/simulator/CameraToggle.tsx +++ b/frontend/src/components/simulator/CameraToggle.tsx @@ -16,6 +16,7 @@ */ import React from 'react'; import { useWebcamFrames } from '../../hooks/useWebcamFrames'; +import { useSimulatorStore } from '../../store/useSimulatorStore'; interface CameraToggleProps { boardId: string | null; @@ -33,6 +34,25 @@ export const CameraToggle: React.FC = ({ boardId }) => { stop, } = useWebcamFrames(); + // Auto-start when the sketch RUNS. An ESP32-CAM sketch exists to capture, so + // waiting for a click on this toggle just reads as "the camera is broken": + // the guest boots, esp_camera_init succeeds against the modelled OV2640, and + // then cam_hal times out forever waiting for frames nobody is sending. Ask + // for the webcam the moment the run starts - the browser's permission prompt + // is the user consent, so the toggle stays as the manual off-switch. Only + // once per run: stopping the stream by hand must not re-trigger it. + const running = useSimulatorStore((st) => st.running); + const autoStartedRef = React.useRef(false); + React.useEffect(() => { + if (!running) { + autoStartedRef.current = false; + return; + } + if (autoStartedRef.current || !boardId || status === 'streaming') return; + autoStartedRef.current = true; + void start(boardId); + }, [running, boardId, status, start]); + const handleClick = () => { if (!boardId) return; if (status === 'streaming') {