Make a button to interact with esp32cam webpage

Hi, it's my first post here, so forgive me if it is in the wrong topic or whatever.
what I am trying to do is a button inside the app that let me turn on and off face recognition in esp32cam webpage(I also need to turn resolution to CIF to activate it). So far i managed to make the stream work and avoid the "header fields are too long" issue, following other topics in here. I also came across a topic that helped to make a button that interact with an html page but I tried and it did not work for me. Since i can't acess the full webpage i'm stuck with only the stream and can't really use face recognition on my app. The code i am using for esp32 is not the same in the esp32 camera arduino example, instead it is another one created by someone and shared with the community. So the html is not encoded and i can post the full html code here. I don't know how to post images here so i can't really show my progress on the app so far. I am using customwebview to make the stream work. I tried really hard before asking this here, but really couldn't do it (I don't have too much experience with any tipe of coding)... so thanks in advance and sorry again if anything is wrong.

the html of my code:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>ESP32 OV2460</title>
        <style> 
          body { font-family: Arial,Helvetica,sans-serif; background: #181818; color: #EFEFEF; font-size: 16px } h2 { font-size: 18px } section.main { display: flex } #menu,section.main { flex-direction: column } #menu { display: none; flex-wrap: nowrap; min-width: 340px; background: #363636; padding: 8px; border-radius: 4px; margin-top: -10px; margin-right: 10px; } #content { display: flex; flex-wrap: wrap; align-items: stretch } figure { padding: 0px; margin: 0; -webkit-margin-before: 0; margin-block-start: 0; -webkit-margin-after: 0; margin-block-end: 0; -webkit-margin-start: 0; margin-inline-start: 0; -webkit-margin-end: 0; margin-inline-end: 0 } figure img { display: block; width: 100%; height: auto; border-radius: 4px; margin-top: 8px; } @media (min-width: 800px) and (orientation:landscape) { #content { display:flex; flex-wrap: nowrap; align-items: stretch } figure img { display: block; max-width: 100%; max-height: calc(100vh - 40px); width: auto; height: auto } figure { padding: 0 0 0 0px; margin: 0; -webkit-margin-before: 0; margin-block-start: 0; -webkit-margin-after: 0; margin-block-end: 0; -webkit-margin-start: 0; margin-inline-start: 0; -webkit-margin-end: 0; margin-inline-end: 0 } } section#buttons { display: flex; flex-wrap: nowrap; justify-content: space-between } #nav-toggle { cursor: pointer; display: block } #nav-toggle-cb { outline: 0; opacity: 0; width: 0; height: 0 } #nav-toggle-cb:checked+#menu { display: block } .input-group { display: flex; flex-wrap: nowrap; line-height: 22px; margin: 5px 0 } .input-group>label { display: inline-block; padding-right: 10px; min-width: 47% } .input-group input,.input-group select { flex-grow: 1 } .range-max,.range-min { display: inline-block; padding: 0 5px } button { display: block; margin: 5px; padding: 0 12px; border: 0; line-height: 28px; cursor: pointer; color: #fff; background: #ff3034; border-radius: 5px; font-size: 16px; outline: 0 } button:hover { background: #ff494d } button:active { background: #f21c21 } button.disabled { cursor: default; background: #a0a0a0 } input[type=range] { -webkit-appearance: none; width: 100%; height: 22px; background: #363636; cursor: pointer; margin: 0 } input[type=range]:focus { outline: 0 } input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 2px; cursor: pointer; background: #EFEFEF; border-radius: 0; border: 0 solid #EFEFEF } input[type=range]::-webkit-slider-thumb { border: 1px solid rgba(0,0,30,0); height: 22px; width: 22px; border-radius: 50px; background: #ff3034; cursor: pointer; -webkit-appearance: none; margin-top: -11.5px } input[type=range]:focus::-webkit-slider-runnable-track { background: #EFEFEF } input[type=range]::-moz-range-track { width: 100%; height: 2px; cursor: pointer; background: #EFEFEF; border-radius: 0; border: 0 solid #EFEFEF } input[type=range]::-moz-range-thumb { border: 1px solid rgba(0,0,30,0); height: 22px; width: 22px; border-radius: 50px; background: #ff3034; cursor: pointer } input[type=range]::-ms-track { width: 100%; height: 2px; cursor: pointer; background: 0 0; border-color: transparent; color: transparent } input[type=range]::-ms-fill-lower { background: #EFEFEF; border: 0 solid #EFEFEF; border-radius: 0 } input[type=range]::-ms-fill-upper { background: #EFEFEF; border: 0 solid #EFEFEF; border-radius: 0 } input[type=range]::-ms-thumb { border: 1px solid rgba(0,0,30,0); height: 22px; width: 22px; border-radius: 50px; background: #ff3034; cursor: pointer; height: 2px } input[type=range]:focus::-ms-fill-lower { background: #EFEFEF } input[type=range]:focus::-ms-fill-upper { background: #363636 } .switch { display: block; position: relative; line-height: 22px; font-size: 16px; height: 22px } .switch input { outline: 0; opacity: 0; width: 0; height: 0 } .slider { width: 50px; height: 22px; border-radius: 22px; cursor: pointer; background-color: grey } .slider,.slider:before { display: inline-block; transition: .4s } .slider:before { position: relative; content: ""; border-radius: 50%; height: 16px; width: 16px; left: 4px; top: 3px; background-color: #fff } input:checked+.slider { background-color: #ff3034 } input:checked+.slider:before { -webkit-transform: translateX(26px); transform: translateX(26px) } select { border: 1px solid #363636; font-size: 14px; height: 22px; outline: 0; border-radius: 5px } .image-container { position: relative; min-width: 160px } .close { position: absolute; right: 5px; top: 5px; background: #ff3034; width: 16px; height: 16px; border-radius: 100px; color: #fff; text-align: center; line-height: 18px; cursor: pointer } .hidden { display: none } 
        </style>
    </head>
    <body>
        <figure>
            <div id="stream-container" class="image-container hidden">
                <div class="close" id="close-stream">×</div>
                <img id="stream" src="" crossorigin="anonymous">
            </div>
        </figure>    
        <section class="main">
            <div id="logo">
                <label for="nav-toggle-cb" id="nav-toggle">&#9776;&nbsp;&nbsp;Toggle OV2640 settings</label>
            </div>
            <div id="content">
                <div id="sidebar">
                    <input type="checkbox" id="nav-toggle-cb" checked="checked">
                    <nav id="menu">
                        <section id="buttons">
                            <button id="get-still">Get Still</button>
                            <button id="toggle-stream">Start Stream</button>
                            <button id="face_enroll" class="disabled" disabled="disabled">Enroll Face</button>
                        </section> 
                        <div class="input-group" id="face_detect-group">
                            <label for="face_detect">Face Detection</label>
                            <div class="switch">
                                <input id="face_detect" type="checkbox" class="default-action">
                                <label class="slider" for="face_detect"></label>
                            </div>
                        </div>
                        <div class="input-group" id="face_recognize-group">
                            <label for="face_recognize">Face Recognition</label>
                            <div class="switch">
                                <input id="face_recognize" type="checkbox" class="default-action">
                                <label class="slider" for="face_recognize"></label>
                            </div>
                        </div>                        
                        <div class="input-group" id="flash-group">
                            <label for="flash">Flash</label>
                            <div class="range-min">0</div>
                            <input type="range" id="flash" min="0" max="255" value="0" onchange="try{fetch(document.location.origin+'/control?var=flash&val='+this.value);}catch(e){}" class="default-action">
                            <div class="range-max">255</div>
                        </div>                        
                        <div class="input-group" id="framesize-group">
                            <label for="framesize">Resolution</label>
                            <select id="framesize" class="default-action">
                                <option value="10">UXGA(1600x1200)</option>
                                <option value="9">SXGA(1280x1024)</option>
                                <option value="8">XGA(1024x768)</option>
                                <option value="7">SVGA(800x600)</option>
                                <option value="6">VGA(640x480)</option>
                                <option value="5" selected="selected">CIF(400x296)</option>
                                <option value="4">QVGA(320x240)</option>
                                <option value="3">HQVGA(240x176)</option>
                                <option value="0">QQVGA(160x120)</option>
                            </select>
                        </div>
                        <div class="input-group" id="quality-group">
                            <label for="quality">Quality</label>
                            <div class="range-min">10</div>
                            <input type="range" id="quality" min="10" max="63" value="10" class="default-action">
                            <div class="range-max">63</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="facename-group">
                            <label for="facename">Face Name</label>
                            <select id="faceid" class="default-action">
                              <option value="0">0</option>
                              <option value="1">1</option>
                              <option value="2">2</option>
                              <option value="3">3</option>
                              <option value="4">4</option>
                              <option value="5">5</option>
                              <option value="6">6</option>
                            </select>
                            <input type="text" id="facename" size="6" style="height:16px">
                            <button onclick="var ifr = document.getElementById('ifr');var ifrlab = document.getElementById('ifrlab');ifr.style.display='block';ifrlab.style.display='block';ifr.src=document.location.origin+'/control?facename='+document.getElementById('faceid').value+';'+document.getElementById('facename').value;">Set</button>
                        </div>    
                        <div class="input-group" id="facename-group">
                            <label for="facename"></label>
                            <button onclick="var ifr = document.getElementById('ifr');ifr.src=document.location.origin+'/control?deleteface'">Clear Face</button>
                        </div> 
                        <div class="input-group" id="contrast-group">
                            <label id="ifrlab" style="display:none;" for="ifr">Name List</label>
                            <iframe id="ifr" style="display:none;width:170px"></iframe>
                        </div>  
                        <div class="input-group" id="controlstate-group">
                            <label for="controlstate">Control State</label>
                            <div class="switch">
                                <input id="controlstate" type="checkbox" class="default-action">
                                <label class="slider" for="controlstate"></label>
                            </div>
                        </div>
                    </nav>
                </div>
            </div>
        </section>
        
        <script>
          document.addEventListener('DOMContentLoaded', function (event) {
            var baseHost = document.location.origin
            var streamUrl = baseHost + ':81'
          
            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 === "face_recognize"){
                  value ? enable(enrollButton) : disable(enrollButton)
                }
              }
            }
          
            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)
                  })
              })
          
            const view = document.getElementById('stream')
            const viewContainer = document.getElementById('stream-container')
            const stillButton = document.getElementById('get-still')
            const streamButton = document.getElementById('toggle-stream')
            const enrollButton = document.getElementById('face_enroll')
            const closeButton = document.getElementById('close-stream')
          
            const stopStream = () => {
              window.stop();
              streamButton.innerHTML = 'Start Stream'
            }
          
            const startStream = () => {
              view.src = `${streamUrl}/stream`
              show(viewContainer)
              streamButton.innerHTML = 'Stop Stream'
            }
          
            // Attach actions to buttons
            stillButton.onclick = () => {
              stopStream()
              view.src = `${baseHost}/capture?_cb=${Date.now()}`
              show(viewContainer)
            }
          
            closeButton.onclick = () => {
              stopStream()
              hide(viewContainer)
            }
          
            streamButton.onclick = () => {
              const streamEnabled = streamButton.innerHTML === 'Stop Stream'
              if (streamEnabled) {
                stopStream()
              } else {
                startStream()
              }
            }
          
            enrollButton.onclick = () => {
              updateConfig(enrollButton)
            }
          
            // Attach default on change action
            document
              .querySelectorAll('.default-action')
              .forEach(el => {
                el.onchange = () => updateConfig(el)
              })
          
            // Custom actions
          
            // Detection and framesize
            const detect = document.getElementById('face_detect')
            const recognize = document.getElementById('face_recognize')
            const framesize = document.getElementById('framesize')
            
            const controlstate = document.getElementById('controlstate')
          
            framesize.onchange = () => {
              updateConfig(framesize)
              if (framesize.value > 5) {
                updateValue(detect, false)
                updateValue(recognize, false)
              }
            }
          
            detect.onchange = () => {
              if (framesize.value > 5) {
                alert("Please select CIF or lower resolution before enabling this feature!");
                updateValue(detect, false)
                return;
              }
              updateConfig(detect)
              if (!detect.checked) {
                disable(enrollButton)
                updateValue(recognize, false)
              }
            }
          
            recognize.onchange = () => {
              if (framesize.value > 5) {
                alert("Please select CIF or lower resolution before enabling this feature!");
                updateValue(recognize, false)
                return;
              }
              updateConfig(recognize)
              if (recognize.checked) {
                enable(enrollButton)
                updateValue(detect, true)
              } else {
                disable(enrollButton)
              }
            }

            controlstate.onchange = () => {
              if (controlstate.checked) {
                fetch(document.location.origin+'/control?controlstate=1');
              } else {
                fetch(document.location.origin+'/control?controlstate=0');
              }  
            }            
          })
        </script>
    </body>
</html>

(I edited your post to make the html readable)

Use the upload button

image

to upload files and images to your posts

thanks!

I tried using these two methods on the right side, none have worked ...

There are no elements in your html with an "id" tag, e.g. id="img1", and you do not set the value of your variable id...

really, i though the id was here:

                        </div>
                        <div class="input-group" id="face_recognize-group">
                            <label for="face_recognize">Face Recognition</label>
                            <div class="switch">
                                <input id="face_recognize" type="checkbox" class="default-action">
                                <label class="slider" for="face_recognize"></label>
                            </div>
                        </div>  

and i thought i had set the id value here :

2

I have no idea what to do, can't even start thinking on how to change the resolution to CIF, wich i'll have too. :grimacing: ... could you shed a light please?(just tell me if it can be done and how.) Thanks.

This is all I can see

image

Sorry, i didn't understant what you meant. You can't see the rest of the image? is there any information that is missing?

Looking more into the code I am using, I realized that the author share a series of url to change camera settings:

http:// 192.168.4.1             //網頁首頁管理介面 Interface de gerenciamento da página inicial da web
http:// 192.168.41:81/stream   //取得串流影像      嵌入網頁語法 Obtenha streaming de vídeo e incorpore sintaxe de página da web <img src="http://192.168.4.1:81/stream">
http:// 192.168.4.1/capture     //取得影像         嵌入網頁語法 Obter sintaxe de página da web incorporada de imagem <img src="http://192.168.4.1/capture">
http:// 192.168.4.1/status      //取得影像狀態值 Obtenha o valor do status da imagem

自訂指令格式 Formato de comando personalizado  http://192.168.4.1/control?cmd=P1;P2;P3;P4;P5;P6;P7;P8;P9
http:// 192.168.4.1/control?facename=matched_id;name  //設定姓名 Nome do conjunto
http:// 192.168.4.1/control?constrolstate=state       //state=0 or 1 設定是否執行函式 Defina se deseja executar a função void FaceMatched(), void FaceNoMatched()

官方指令格式 Formato de comando oficial http://192.168.4.1/control?var=xx&val=xx
http:// 192.168.4.1/control?var=flash&val=value          //閃光燈 clarão value= 0~255
http:// 192.168.4.1/control?var=framesize&val=value      //解析度 Resolução value = 10->UXGA(1600x1200), 9->SXGA(1280x1024), 8->XGA(1024x768) ,7->SVGA(800x600), 6->VGA(640x480), 5 selected=selected->CIF(400x296), 4->QVGA(320x240), 3->HQVGA(240x176), 0->QQVGA(160x120), 11->QXGA(2048x1564 for OV3660)
http:// 192.168.4.1/control?var=quality&val=value        //畫質 Qualidade da imagem value = 10 ~ 63
http:// 192.168.4.1/control?var=brightness&val=value     //亮度 brilho value = -2 ~ 2
http:// 192.168.4.1/control?var=contrast&val=value       //對比 Contraste value = -2 ~ 2

Connecting to esp32cam as a router, on the pc browser I managed to use "http://192.168.4.1/control?var=framesize&val=value5" to change the resolution, althoug it went to QQVGA(160x120) instead of CIF. Then i went back to "http://192.168.4.1:81/stream" and the changes were applied.

However i tried to do this in appinventor:

and it did not work.

Also, in my pc browser, I tried to use "http:// 192.168.4.1/control?constrolstate=state1" to try to turn on face rec., but coming back to "http:// 192.168.4.1:81/stream" has'nt changed anything.

Got any other hint?

It would also be cool to know if what I want to do is possible in the 1.0.4 version of camerawebserver from esp32 library.

Thanks!

ps: i had to put a space after every http:// that i mentioned, because it recognized as a external link and would not let me publish.

Managed to solve my problem with this:

I couldn't make "http:// 192.168.4.1/control?var=framesize&val=value" work, thoug. I can put any acceptable value, it always goes to QQVGA(160x120) first time, second time I try it freezes the esp32cam. The only way I could make this work was to change the initial resolution to CIF in the code. If anyone knows why this happens and how to solve it, plz tell me.

This is also working with 1.0.4 esp32cam camera example library.

thanks for the help.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.