feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
/**
|
|
|
|
|
* Camera button rendered on the canvas header for ESP32-CAM boards.
|
|
|
|
|
*
|
|
|
|
|
* Click → asks for webcam permission → starts streaming JPEG frames at
|
|
|
|
|
* 10 fps over the existing simulator WebSocket. The frames flow into
|
|
|
|
|
* the QEMU OV2640+I²S peripheral so user sketches that call
|
|
|
|
|
* `esp_camera_fb_get()` see real webcam content (subject to the
|
|
|
|
|
* upstream-driver caveats documented in test/test-esp32-cam/autosearch/10).
|
|
|
|
|
*
|
|
|
|
|
* Visual states:
|
|
|
|
|
* idle grey camera icon
|
|
|
|
|
* requesting orange (pulsing) — browser permission prompt visible
|
|
|
|
|
* streaming green + frame counter — frames flowing
|
|
|
|
|
* denied red, click-to-retry
|
|
|
|
|
* error red, hover for message
|
|
|
|
|
*/
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import { useWebcamFrames } from '../../hooks/useWebcamFrames';
|
2026-07-27 23:13:21 +07:00
|
|
|
import { useSimulatorStore } from '../../store/useSimulatorStore';
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
|
|
|
|
|
interface CameraToggleProps {
|
|
|
|
|
boardId: string | null;
|
2026-08-04 21:18:10 +07:00
|
|
|
/** Per-board frame byte budget (see useWebcamFrames). Omit for the
|
|
|
|
|
* default QEMU ESP32-CAM bound. */
|
|
|
|
|
maxFrameBytes?: number;
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
}
|
|
|
|
|
|
2026-08-04 21:18:10 +07:00
|
|
|
export const CameraToggle: React.FC<CameraToggleProps> = ({ boardId, maxFrameBytes }) => {
|
feat(webcam): universal compatibility — any webcam on any PC ✅
User goal: ESP32-CAM live preview that works with any webcam,
regardless of resolution, brand, or scene complexity. The previous
fixed-quality 0.28 was fragile (intermittent decode errors on
moving/textured scenes) and capped visual quality unnecessarily.
Two-layer fix; either alone is insufficient:
LAYER A — Bounded JPEG encoder (frontend, this repo)
frontend/src/hooks/useWebcamFrames.ts:
encodeBoundedJpeg() walks a quality ladder [0.6, 0.5, ..., 0.1]
until the JPEG fits in MAX_FRAME_BYTES (23 000). If even q=0.1
overshoots — extreme HD/4K scenes — falls back to a 240×180
downscaled canvas at q=0.4. Guarantees every emitted frame fits
the deliverable budget regardless of webcam hardware.
The hook now exposes lastQualityUsed + lastDownscaled so UI can
surface when auto-tuning kicks in.
frontend/src/components/simulator/CameraToggle.tsx:
Tooltip shows "(auto-tuned to q=0.X)" or "(auto-downscaled, q=0.X)"
while streaming so users see what the encoder picked.
LAYER B — Multi-lap descriptor ring walker (qemu-lcgamboa, submodule)
Bumps the QEMU per-frame deliverable cap from 8 KiB to ~32 KiB by
letting the walker reset the descriptor ring up to 4 times per
VSYNC. Submodule pointer bumped to eb8b7a5d.
Combined, the demo now supports:
- Cheap 480p webcams: q=0.6, 5-10 KiB JPEGs, sharp
- Logitech mid-range: q=0.5-0.6, 8-15 KiB JPEGs, sharp
- HD 1080p webcams: q=0.4-0.6, 15-23 KiB JPEGs, sharp
- 4K complex scenes: downscaled, still readable
Documented as bug closure in:
test/test-esp32-cam/autosearch/15_universal_webcam_compat.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:48:24 +07:00
|
|
|
const {
|
|
|
|
|
status,
|
|
|
|
|
errorMessage,
|
|
|
|
|
framesSent,
|
|
|
|
|
lastFrameBytes,
|
|
|
|
|
lastQualityUsed,
|
|
|
|
|
lastDownscaled,
|
|
|
|
|
start,
|
|
|
|
|
stop,
|
2026-08-04 21:18:10 +07:00
|
|
|
} = useWebcamFrames(maxFrameBytes);
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
|
2026-07-27 23:13:21 +07:00
|
|
|
// 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]);
|
|
|
|
|
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
const handleClick = () => {
|
|
|
|
|
if (!boardId) return;
|
|
|
|
|
if (status === 'streaming') {
|
|
|
|
|
stop();
|
|
|
|
|
} else {
|
|
|
|
|
void start(boardId);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const isOn = status === 'streaming';
|
feat(webcam): universal compatibility — any webcam on any PC ✅
User goal: ESP32-CAM live preview that works with any webcam,
regardless of resolution, brand, or scene complexity. The previous
fixed-quality 0.28 was fragile (intermittent decode errors on
moving/textured scenes) and capped visual quality unnecessarily.
Two-layer fix; either alone is insufficient:
LAYER A — Bounded JPEG encoder (frontend, this repo)
frontend/src/hooks/useWebcamFrames.ts:
encodeBoundedJpeg() walks a quality ladder [0.6, 0.5, ..., 0.1]
until the JPEG fits in MAX_FRAME_BYTES (23 000). If even q=0.1
overshoots — extreme HD/4K scenes — falls back to a 240×180
downscaled canvas at q=0.4. Guarantees every emitted frame fits
the deliverable budget regardless of webcam hardware.
The hook now exposes lastQualityUsed + lastDownscaled so UI can
surface when auto-tuning kicks in.
frontend/src/components/simulator/CameraToggle.tsx:
Tooltip shows "(auto-tuned to q=0.X)" or "(auto-downscaled, q=0.X)"
while streaming so users see what the encoder picked.
LAYER B — Multi-lap descriptor ring walker (qemu-lcgamboa, submodule)
Bumps the QEMU per-frame deliverable cap from 8 KiB to ~32 KiB by
letting the walker reset the descriptor ring up to 4 times per
VSYNC. Submodule pointer bumped to eb8b7a5d.
Combined, the demo now supports:
- Cheap 480p webcams: q=0.6, 5-10 KiB JPEGs, sharp
- Logitech mid-range: q=0.5-0.6, 8-15 KiB JPEGs, sharp
- HD 1080p webcams: q=0.4-0.6, 15-23 KiB JPEGs, sharp
- 4K complex scenes: downscaled, still readable
Documented as bug closure in:
test/test-esp32-cam/autosearch/15_universal_webcam_compat.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:48:24 +07:00
|
|
|
|
|
|
|
|
// Build a streaming tooltip that exposes the adaptive encoder state.
|
|
|
|
|
// Users see "auto-tuned" hints when the encoder had to drop quality
|
|
|
|
|
// or downscale — useful diagnostic for HD/4K webcams.
|
|
|
|
|
const streamingTooltip = () => {
|
|
|
|
|
const kb = (lastFrameBytes / 1024).toFixed(1);
|
|
|
|
|
const q = lastQualityUsed.toFixed(2);
|
|
|
|
|
const tuneNote = lastDownscaled
|
|
|
|
|
? ` (auto-downscaled, q=${q})`
|
|
|
|
|
: lastQualityUsed < 0.3
|
|
|
|
|
? ` (auto-tuned to q=${q})`
|
|
|
|
|
: ` (q=${q})`;
|
|
|
|
|
return `Streaming webcam (${framesSent} frames, last=${kb} KB${tuneNote}) — click to stop`;
|
|
|
|
|
};
|
|
|
|
|
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
const tooltip =
|
|
|
|
|
status === 'streaming'
|
feat(webcam): universal compatibility — any webcam on any PC ✅
User goal: ESP32-CAM live preview that works with any webcam,
regardless of resolution, brand, or scene complexity. The previous
fixed-quality 0.28 was fragile (intermittent decode errors on
moving/textured scenes) and capped visual quality unnecessarily.
Two-layer fix; either alone is insufficient:
LAYER A — Bounded JPEG encoder (frontend, this repo)
frontend/src/hooks/useWebcamFrames.ts:
encodeBoundedJpeg() walks a quality ladder [0.6, 0.5, ..., 0.1]
until the JPEG fits in MAX_FRAME_BYTES (23 000). If even q=0.1
overshoots — extreme HD/4K scenes — falls back to a 240×180
downscaled canvas at q=0.4. Guarantees every emitted frame fits
the deliverable budget regardless of webcam hardware.
The hook now exposes lastQualityUsed + lastDownscaled so UI can
surface when auto-tuning kicks in.
frontend/src/components/simulator/CameraToggle.tsx:
Tooltip shows "(auto-tuned to q=0.X)" or "(auto-downscaled, q=0.X)"
while streaming so users see what the encoder picked.
LAYER B — Multi-lap descriptor ring walker (qemu-lcgamboa, submodule)
Bumps the QEMU per-frame deliverable cap from 8 KiB to ~32 KiB by
letting the walker reset the descriptor ring up to 4 times per
VSYNC. Submodule pointer bumped to eb8b7a5d.
Combined, the demo now supports:
- Cheap 480p webcams: q=0.6, 5-10 KiB JPEGs, sharp
- Logitech mid-range: q=0.5-0.6, 8-15 KiB JPEGs, sharp
- HD 1080p webcams: q=0.4-0.6, 15-23 KiB JPEGs, sharp
- 4K complex scenes: downscaled, still readable
Documented as bug closure in:
test/test-esp32-cam/autosearch/15_universal_webcam_compat.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:48:24 +07:00
|
|
|
? streamingTooltip()
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
: status === 'requesting'
|
|
|
|
|
? 'Asking for camera permission…'
|
|
|
|
|
: status === 'denied'
|
|
|
|
|
? `Permission denied. Click to retry.`
|
|
|
|
|
: status === 'error'
|
|
|
|
|
? `Webcam error: ${errorMessage ?? 'unknown'}`
|
|
|
|
|
: 'Use webcam as ESP32-CAM camera';
|
|
|
|
|
|
|
|
|
|
const color =
|
|
|
|
|
status === 'streaming'
|
|
|
|
|
? '#3fb950'
|
|
|
|
|
: status === 'requesting'
|
|
|
|
|
? '#f0883e'
|
|
|
|
|
: status === 'denied' || status === 'error'
|
|
|
|
|
? '#f85149'
|
|
|
|
|
: '#ccc';
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleClick}
|
|
|
|
|
disabled={!boardId || status === 'requesting'}
|
|
|
|
|
title={tooltip}
|
2026-08-04 21:12:50 +07:00
|
|
|
// Ants while the button wants a click DURING a run (idle after a
|
|
|
|
|
// failed/stopped auto-start, denied, error): a camera sketch is
|
|
|
|
|
// starving without frames. Never while the sim is stopped — the
|
|
|
|
|
// button is not asking for anything then. Solid border once
|
|
|
|
|
// streaming or asking.
|
2026-08-12 01:01:56 +07:00
|
|
|
// Static layout is in .canvas-cam-mic-btn so the toolbar @container
|
|
|
|
|
// query can collapse the label to icon-only (an inline font-size would
|
|
|
|
|
// beat it). Only state-driven styles stay inline below.
|
|
|
|
|
className={`canvas-cam-mic-btn${
|
2026-08-04 21:12:50 +07:00
|
|
|
running && boardId && status !== 'streaming' && status !== 'requesting'
|
2026-08-12 01:01:56 +07:00
|
|
|
? ' velxio-btn-ants'
|
|
|
|
|
: ''
|
|
|
|
|
}`}
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
style={{
|
2026-07-27 23:27:30 +07:00
|
|
|
// backgroundColor, NOT the background shorthand: inline shorthand
|
|
|
|
|
// would wipe the class's background-image and kill the ants.
|
|
|
|
|
backgroundColor: isOn ? 'rgba(63,185,80,0.15)' : 'transparent',
|
|
|
|
|
border: `1px solid ${
|
|
|
|
|
isOn ? '#3fb950' : status === 'requesting' ? '#555' : 'transparent'
|
|
|
|
|
}`,
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
color,
|
|
|
|
|
cursor: boardId ? 'pointer' : 'not-allowed',
|
2026-07-28 00:40:54 +07:00
|
|
|
// Inline only while requesting. 'none' here would OVERRIDE the
|
2026-08-04 21:12:50 +07:00
|
|
|
// .velxio-btn-ants class animation (inline beats class), which is
|
|
|
|
|
// exactly how the ants shipped dead the first time.
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
animation:
|
|
|
|
|
status === 'requesting'
|
|
|
|
|
? 'velxio-pulse 1s ease-in-out infinite'
|
2026-07-28 00:40:54 +07:00
|
|
|
: undefined,
|
feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.
Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.
Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
added to isQemuBoard list — Run button now starts the QEMU bridge
for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component
Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)
Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow
.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
not part of this repo)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<svg
|
|
|
|
|
width="16"
|
|
|
|
|
height="16"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
strokeWidth="2"
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
>
|
|
|
|
|
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" />
|
|
|
|
|
<circle cx="12" cy="13" r="4" />
|
|
|
|
|
</svg>
|
|
|
|
|
<span>
|
|
|
|
|
{isOn ? `Camera ${framesSent}` : 'Camera'}
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
};
|