esp32-cam-webserver/index_ov2640.h

590 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* primary HTML for the OV2640 camera module
*/
const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ESP32 OV2640</title>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/style.css">
<style>
@media (min-width: 800px) and (orientation:landscape) {
#content {
display:flex;
flex-wrap: nowrap;
align-items: stretch
}
}
</style>
</head>
<body>
<section class="main">
<div id="logo">
<label for="nav-toggle-cb" id="nav-toggle" style="float:left;">&#9776;&nbsp;&nbsp;Settings&nbsp;&nbsp;&nbsp;&nbsp;</label>
<button id="swap-viewer" style="float:left;" title="Swap to simple viewer">Simple</button>
<button id="get-still" style="float:left;">Get Still</button>
<button id="toggle-stream" style="float:left;" class="hidden">Start Stream</button>
<div id="wait-settings" style="float:left;" class="loader" title="Waiting for camera settings to load"></div>
</div>
<div id="content">
<div class="hidden" id="sidebar">
<input type="checkbox" id="nav-toggle-cb" checked="checked">
<nav id="menu">
<div class="input-group hidden" id="lamp-group">
<label for="lamp">Light</label>
<div class="range-min">Off</div>
<input type="range" id="lamp" min="0" max="100" value="0" class="default-action">
<div class="range-max">Full</div>
</div>
<div class="input-group hidden" id="autolamp-group">
<label for="autolamp">Auto Lamp</label>
<div class="switch">
<input id="autolamp" type="checkbox" class="default-action" title="Lamp only on when camera active">
<label class="slider" for="autolamp"></label>
</div>
</div>
<div class="input-group" id="framesize-group">
<label for="framesize">Resolution</label>
<select id="framesize" class="default-action">
<option value="13">UXGA (1600x1200)</option>
<option value="12">SXGA (1280x1024)</option>
<option value="11">HD (1280x720)</option>
<option value="10">XGA (1024x768)</option>
<option value="9">SVGA (800x600)</option>
<option value="8">VGA (640x480)</option>
<option value="7">HVGA (480x320)</option>
<option value="6">CIF (400x296)</option>
<option value="5">QVGA (320x240)</option>
<option value="3">HQVGA (240x176)</option>
<option value="1">QQVGA (160x120)</option>
<option value="0">THUMB (96x96)</option>
</select>
</div>
<div class="input-group" id="quality-group">
<label for="quality">Quality</label>
<div class="range-min">Low<br><span style="font-size: 80%;">(fast)</span></div>
<!-- Note; the following element is 'flipped' in CSS so that it slides from High to Low
As a result the 'min' and 'max' values are reversed here too -->
<input type="range" id="quality" min="6" max="63" value="10" class="default-action">
<div class="range-max">High<br><span style="font-size: 80%;">(slow)</span></div>
</div>
<div class="input-group" id="brightness-group">
<label for="brightness">Brightness</label>
<div class="range-min">-2</div>
<input type="range" id="brightness" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="contrast-group">
<label for="contrast">Contrast</label>
<div class="range-min">-2</div>
<input type="range" id="contrast" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="saturation-group">
<label for="saturation">Saturation</label>
<div class="range-min">-2</div>
<input type="range" id="saturation" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="special_effect-group">
<label for="special_effect">Special Effect</label>
<select id="special_effect" class="default-action">
<option value="0" selected="selected">No Effect</option>
<option value="1">Negative</option>
<option value="2">Grayscale</option>
<option value="3">Red Tint</option>
<option value="4">Green Tint</option>
<option value="5">Blue Tint</option>
<option value="6">Sepia</option>
</select>
</div>
<div class="input-group" id="awb-group">
<label for="awb">AWB</label>
<div class="switch">
<input id="awb" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="awb"></label>
</div>
</div>
<div class="input-group" id="awb_gain-group">
<label for="awb_gain">AWB Gain</label>
<div class="switch">
<input id="awb_gain" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="awb_gain"></label>
</div>
</div>
<div class="input-group" id="wb_mode-group">
<label for="wb_mode">WB Mode</label>
<select id="wb_mode" class="default-action">
<option value="0" selected="selected">Auto</option>
<option value="1">Sunny</option>
<option value="2">Cloudy</option>
<option value="3">Office</option>
<option value="4">Home</option>
</select>
</div>
<div class="input-group" id="aec-group">
<label for="aec">AEC SENSOR</label>
<div class="switch">
<input id="aec" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="aec"></label>
</div>
</div>
<div class="input-group" id="aec2-group">
<label for="aec2">AEC DSP</label>
<div class="switch">
<input id="aec2" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="aec2"></label>
</div>
</div>
<div class="input-group" id="ae_level-group">
<label for="ae_level">AE Level</label>
<div class="range-min">-2</div>
<input type="range" id="ae_level" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="aec_value-group">
<label for="aec_value">Exposure</label>
<div class="range-min">0</div>
<input type="range" id="aec_value" min="0" max="1200" value="204" class="default-action">
<div class="range-max">1200</div>
</div>
<div class="input-group" id="agc-group">
<label for="agc">AGC</label>
<div class="switch">
<input id="agc" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="agc"></label>
</div>
</div>
<div class="input-group hidden" id="agc_gain-group">
<label for="agc_gain">Gain</label>
<div class="range-min">1x</div>
<input type="range" id="agc_gain" min="0" max="30" value="5" class="default-action">
<div class="range-max">31x</div>
</div>
<div class="input-group" id="gainceiling-group">
<label for="gainceiling">Gain Ceiling</label>
<div class="range-min">2x</div>
<input type="range" id="gainceiling" min="0" max="6" value="0" class="default-action">
<div class="range-max">128x</div>
</div>
<div class="input-group" id="bpc-group">
<label for="bpc">BPC</label>
<div class="switch">
<input id="bpc" type="checkbox" class="default-action">
<label class="slider" for="bpc"></label>
</div>
</div>
<div class="input-group" id="wpc-group">
<label for="wpc">WPC</label>
<div class="switch">
<input id="wpc" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="wpc"></label>
</div>
</div>
<div class="input-group" id="raw_gma-group">
<label for="raw_gma">Raw GMA</label>
<div class="switch">
<input id="raw_gma" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="raw_gma"></label>
</div>
</div>
<div class="input-group" id="lenc-group">
<label for="lenc">Lens Correction</label>
<div class="switch">
<input id="lenc" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="lenc"></label>
</div>
</div>
<div class="input-group" id="hmirror-group">
<label for="hmirror">H-Mirror Stream</label>
<div class="switch">
<input id="hmirror" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="hmirror"></label>
</div>
</div>
<div class="input-group" id="vflip-group">
<label for="vflip">V-Flip Stream</label>
<div class="switch">
<input id="vflip" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="vflip"></label>
</div>
</div>
<div class="input-group" id="rotate-group">
<label for="rotate">Rotate in Browser</label>
<select id="rotate" class="default-action">
<option value="90">90&deg; (Right)</option>
<option value="0" selected="selected">0&deg; (None)</option>
<option value="-90">-90&deg; (Left)</option>
</select>
</div>
<div class="input-group" id="dcw-group">
<label for="dcw">DCW (Downsize EN)</label>
<div class="switch">
<input id="dcw" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="dcw"></label>
</div>
</div>
<div class="input-group" id="colorbar-group">
<label for="colorbar">Test Pattern</label>
<div class="switch">
<input id="colorbar" type="checkbox" class="default-action">
<label class="slider" for="colorbar"></label>
</div>
</div>
<div class="input-group" id="preferences-group">
<label for="reboot" style="line-height: 2em;">Preferences</label>
<button id="reboot" title="Reboot the camera module">Reboot</button>
<button id="save_prefs" title="Save Preferences on camera module">Save</button>
<button id="clear_prefs" title="Erase saved Preferences on camera module">Erase</button>
</div>
<div class="input-group" id="cam_name-group">
<label for="cam_name">
<a href="/dump" title="System Info" target="_blank">Name</a></label>
<div id="cam_name" class="default-action"></div>
</div>
<div class="input-group" id="code_ver-group">
<label for="code_ver">
<a href="https://github.com/easytarget/esp32-cam-webserver"
title="ESP32 Cam Webserver on GitHub" target="_blank">Firmware</a></label>
<div id="code_ver" class="default-action"></div>
</div>
<div class="input-group hidden" id="stream-group">
<label for="stream_url" id="stream_link">Stream</label>
<div id="stream_url" class="default-action">Unknown</div>
</div>
</nav>
</div>
<figure>
<div id="stream-container" class="image-container hidden">
<div class="close close-rot-none" id="close-stream">×</div>
<img id="stream" src="">
</div>
</figure>
</div>
</section>
</body>
<script>
document.addEventListener('DOMContentLoaded', function (event) {
var baseHost = document.location.origin;
var streamURL = 'Undefined';
var viewerURL = 'Undefined';
const header = document.getElementById('logo')
const settings = document.getElementById('sidebar')
const waitSettings = document.getElementById('wait-settings')
const lampGroup = document.getElementById('lamp-group')
const autolampGroup = document.getElementById('autolamp-group')
const streamGroup = document.getElementById('stream-group')
const camName = document.getElementById('cam_name')
const codeVer = document.getElementById('code_ver')
const rotate = document.getElementById('rotate')
const view = document.getElementById('stream')
const viewContainer = document.getElementById('stream-container')
const stillButton = document.getElementById('get-still')
const streamButton = document.getElementById('toggle-stream')
const closeButton = document.getElementById('close-stream')
const streamLink = document.getElementById('stream_link')
const framesize = document.getElementById('framesize')
const swapButton = document.getElementById('swap-viewer')
const savePrefsButton = document.getElementById('save_prefs')
const clearPrefsButton = document.getElementById('clear_prefs')
const rebootButton = document.getElementById('reboot')
const hide = el => {
el.classList.add('hidden')
}
const show = el => {
el.classList.remove('hidden')
}
const disable = el => {
el.classList.add('disabled')
el.disabled = true
}
const enable = el => {
el.classList.remove('disabled')
el.disabled = false
}
const updateValue = (el, value, updateRemote) => {
updateRemote = updateRemote == null ? true : updateRemote
let initialValue
if (el.type === 'checkbox') {
initialValue = el.checked
value = !!value
el.checked = value
} else {
initialValue = el.value
el.value = value
}
if (updateRemote && initialValue !== value) {
updateConfig(el);
} else if(!updateRemote){
if(el.id === "aec"){
value ? hide(exposure) : show(exposure)
} else if(el.id === "agc"){
if (value) {
show(gainCeiling)
hide(agcGain)
} else {
hide(gainCeiling)
show(agcGain)
}
} else if(el.id === "awb_gain"){
value ? show(wb) : hide(wb)
} else if(el.id === "lamp"){
if (value == -1) {
hide(lampGroup)
hide(autolampGroup)
} else {
show(lampGroup)
show(autolampGroup)
}
} else if(el.id === "cam_name"){
camName.innerHTML = value;
window.document.title = value;
console.log('Name set to: ' + value);
} else if(el.id === "code_ver"){
codeVer.innerHTML = value;
console.log('Firmware Build: ' + value);
} else if(el.id === "rotate"){
rotate.value = value;
applyRotation();
} else if(el.id === "stream_url"){
streamURL = value;
viewerURL = value + 'view';
stream_url.innerHTML = value;
stream_link.setAttribute("title", `Open the standalone stream viewer :: ${viewerURL}`);
stream_link.style.textDecoration = "underline";
stream_link.style.cursor = "pointer";
streamButton.setAttribute("title", `Start the stream :: ${streamURL}`);
show(streamGroup)
console.log('Stream URL set to: ' + streamURL);
console.log('Stream Viewer URL set to: ' + viewerURL);
}
}
}
function updateConfig (el) {
let value
switch (el.type) {
case 'checkbox':
value = el.checked ? 1 : 0
break
case 'range':
case 'select-one':
value = el.value
break
case 'button':
case 'submit':
value = '1'
break
default:
return
}
const query = `${baseHost}/control?var=${el.id}&val=${value}`
fetch(query)
.then(response => {
console.log(`request to ${query} finished, status: ${response.status}`)
})
}
document
.querySelectorAll('.close')
.forEach(el => {
el.onclick = () => {
hide(el.parentNode)
}
})
// read initial values
fetch(`${baseHost}/status`)
.then(function (response) {
return response.json()
})
.then(function (state) {
document
.querySelectorAll('.default-action')
.forEach(el => {
updateValue(el, state[el.id], false)
})
hide(waitSettings);
show(settings);
show(streamButton);
//startStream();
})
// Put some helpful text on the 'Still' button
stillButton.setAttribute("title", `Capture a still image :: ${baseHost}/capture`);
const stopStream = () => {
window.stop();
streamButton.innerHTML = 'Start Stream';
streamButton.setAttribute("title", `Start the stream :: ${streamURL}`);
hide(viewContainer);
}
const startStream = () => {
view.src = streamURL;
view.scrollIntoView(false);
streamButton.innerHTML = 'Stop Stream';
streamButton.setAttribute("title", `Stop the stream`);
show(viewContainer);
}
const applyRotation = () => {
rot = rotate.value;
if (rot == -90) {
viewContainer.style.transform = `rotate(-90deg) translate(-100%)`;
closeButton.classList.remove('close-rot-none');
closeButton.classList.remove('close-rot-right');
closeButton.classList.add('close-rot-left');
} else if (rot == 90) {
viewContainer.style.transform = `rotate(90deg) translate(0, -100%)`;
closeButton.classList.remove('close-rot-left');
closeButton.classList.remove('close-rot-none');
closeButton.classList.add('close-rot-right');
} else {
viewContainer.style.transform = `rotate(0deg)`;
closeButton.classList.remove('close-rot-left');
closeButton.classList.remove('close-rot-right');
closeButton.classList.add('close-rot-none');
}
console.log('Rotation ' + rot + ' applied');
}
// Attach actions to controls
streamLink.onclick = () => {
stopStream();
window.open(viewerURL, "_blank");
}
stillButton.onclick = () => {
stopStream();
view.src = `${baseHost}/capture?_cb=${Date.now()}`;
view.scrollIntoView(false);
show(viewContainer);
}
closeButton.onclick = () => {
stopStream();
hide(viewContainer);
}
streamButton.onclick = () => {
const streamEnabled = streamButton.innerHTML === 'Stop Stream'
if (streamEnabled) {
stopStream();
} else {
startStream();
}
}
// Attach default on change action
document
.querySelectorAll('.default-action')
.forEach(el => {
el.onchange = () => updateConfig(el)
})
// Custom actions
// Gain
const agc = document.getElementById('agc')
const agcGain = document.getElementById('agc_gain-group')
const gainCeiling = document.getElementById('gainceiling-group')
agc.onchange = () => {
updateConfig(agc)
if (agc.checked) {
show(gainCeiling)
hide(agcGain)
} else {
hide(gainCeiling)
show(agcGain)
}
}
// Exposure
const aec = document.getElementById('aec')
const exposure = document.getElementById('aec_value-group')
aec.onchange = () => {
updateConfig(aec)
aec.checked ? hide(exposure) : show(exposure)
}
// AWB
const awb = document.getElementById('awb_gain')
const wb = document.getElementById('wb_mode-group')
awb.onchange = () => {
updateConfig(awb)
awb.checked ? show(wb) : hide(wb)
}
// Detection and framesize
rotate.onchange = () => {
applyRotation();
updateConfig(rotate);
}
framesize.onchange = () => {
updateConfig(framesize)
}
swapButton.onclick = () => {
window.open('/?view=simple','_self');
}
// saveFaceButton.onclick = () => {
// if (confirm("Saving the current face database?")) {
// updateConfig(saveFaceButton);
// }
// }
// clearFaceButton.onclick = () => {
// if (confirm("Removing the face database?")) {
// updateConfig(clearFaceButton);
// }
// }
savePrefsButton.onclick = () => {
if (confirm("Save the current preferences?")) {
updateConfig(savePrefsButton);
}
}
clearPrefsButton.onclick = () => {
if (confirm("Remove the saved preferences?")) {
updateConfig(clearPrefsButton);
}
}
rebootButton.onclick = () => {
if (confirm("Reboot the Camera Module?")) {
updateConfig(rebootButton);
// Some sort of countdown here?
hide(settings);
hide(viewContainer);
header.innerHTML = '<h1>Rebooting!</h1><hr>Page will reload after 30 seconds.';
setTimeout(function() {
location.replace(document.URL);
}, 30000);
}
}
})
</script>
</html>)=====";
size_t index_ov2640_html_len = sizeof(index_ov2640_html)-1;