当前位置:   article > 正文

【ESP32CAM识别图形左边界】(四)网页显示传感器数据_esp32cam传感器

esp32cam传感器

 系列文章目录

ESP32CAM识别图形左边界(一):初步使用ESP32CAM

ESP32CAM识别图形左边界(二):网页添加补光灯按钮

ESP32CAM识别图形左边界(三):识别图像左边界

ESP32CAM识别图形左边界(四):网页显示传感器数据


目录

 系列文章目录

前言

一、初始的html文件

1.删除多余的演示按钮

2.增加页面配置

3.增加Javascript脚本程序

4.怎么实现自动刷新?

5.常见错误

三、ino程序修改

(1)app_httpd.cpp文件

四、整个文件程序

总结


前言

        之前的ESP32CAM的网页修改都是比较简单的按钮形式,也就是点击开或者关这种互动形式的数据,碰到一个粉丝有另外一种需求,那就是页面可以实时回显ESP32作为主控处理接收到的传感器数据,比如显示温湿度传感器、超声波传感器、红外传感器的数据或者检测结果。这个在ESP32代码里面的改动其实不是很多,主要的修改还是前端的页面设计,也就是需要修改html文件,实现前端页面的显示,以及获取数据的操作。


一、初始的html文件

        之前一直没找到原始的index_ov2640.html文件,后来终于被我找到了,这里直接放链接了--初始index_ov2640.html文件

二、页面html文件修改

1.删除多余的演示按钮

        这边为了演示,只保留了少数的开关,比如图像精度的设置、之前添加的闪光灯按钮,还有就是人脸识别的相关按钮。

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1">
  6. <title>ESP32</title>
  7. <style>
  8. body {
  9. font-family: Arial,Helvetica,sans-serif;
  10. background: #181818;
  11. color: #EFEFEF;
  12. font-size: 16px
  13. }
  14. h2 {
  15. font-size: 18px
  16. }
  17. section.main {
  18. display: flex
  19. }
  20. #menu,section.main {
  21. flex-direction: column
  22. }
  23. #menu {
  24. display: none;
  25. flex-wrap: nowrap;
  26. min-width: 340px;
  27. background: #363636;
  28. padding: 8px;
  29. border-radius: 4px;
  30. margin-top: -10px;
  31. margin-right: 10px;
  32. }
  33. #content {
  34. display: flex;
  35. flex-wrap: wrap;
  36. align-items: stretch
  37. }
  38. figure {
  39. padding: 0px;
  40. margin: 0;
  41. -webkit-margin-before: 0;
  42. margin-block-start: 0;
  43. -webkit-margin-after: 0;
  44. margin-block-end: 0;
  45. -webkit-margin-start: 0;
  46. margin-inline-start: 0;
  47. -webkit-margin-end: 0;
  48. margin-inline-end: 0
  49. }
  50. figure img {
  51. display: block;
  52. width: 100%;
  53. height: auto;
  54. border-radius: 4px;
  55. margin-top: 8px;
  56. }
  57. @media (min-width: 800px) and (orientation:landscape) {
  58. #content {
  59. display:flex;
  60. flex-wrap: nowrap;
  61. align-items: stretch
  62. }
  63. figure img {
  64. display: block;
  65. max-width: 100%;
  66. max-height: calc(100vh - 40px);
  67. width: auto;
  68. height: auto
  69. }
  70. figure {
  71. padding: 0 0 0 0px;
  72. margin: 0;
  73. -webkit-margin-before: 0;
  74. margin-block-start: 0;
  75. -webkit-margin-after: 0;
  76. margin-block-end: 0;
  77. -webkit-margin-start: 0;
  78. margin-inline-start: 0;
  79. -webkit-margin-end: 0;
  80. margin-inline-end: 0
  81. }
  82. }
  83. section#buttons {
  84. display: flex;
  85. flex-wrap: nowrap;
  86. justify-content: space-between
  87. }
  88. #nav-toggle {
  89. cursor: pointer;
  90. display: block
  91. }
  92. #nav-toggle-cb {
  93. outline: 0;
  94. opacity: 0;
  95. width: 0;
  96. height: 0
  97. }
  98. #nav-toggle-cb:checked+#menu {
  99. display: flex
  100. }
  101. .input-group {
  102. display: flex;
  103. flex-wrap: nowrap;
  104. line-height: 22px;
  105. margin: 5px 0
  106. }
  107. .input-group>label {
  108. display: inline-block;
  109. padding-right: 10px;
  110. min-width: 47%
  111. }
  112. .input-group input,.input-group select {
  113. flex-grow: 1
  114. }
  115. .range-max,.range-min {
  116. display: inline-block;
  117. padding: 0 5px
  118. }
  119. button {
  120. display: block;
  121. margin: 5px;
  122. padding: 0 12px;
  123. border: 0;
  124. line-height: 28px;
  125. cursor: pointer;
  126. color: #fff;
  127. background: #ff3034;
  128. border-radius: 5px;
  129. font-size: 16px;
  130. outline: 0
  131. }
  132. button:hover {
  133. background: #ff494d
  134. }
  135. button:active {
  136. background: #f21c21
  137. }
  138. button.disabled {
  139. cursor: default;
  140. background: #a0a0a0
  141. }
  142. input[type=range] {
  143. -webkit-appearance: none;
  144. width: 100%;
  145. height: 22px;
  146. background: #363636;
  147. cursor: pointer;
  148. margin: 0
  149. }
  150. input[type=range]:focus {
  151. outline: 0
  152. }
  153. input[type=range]::-webkit-slider-runnable-track {
  154. width: 100%;
  155. height: 2px;
  156. cursor: pointer;
  157. background: #EFEFEF;
  158. border-radius: 0;
  159. border: 0 solid #EFEFEF
  160. }
  161. input[type=range]::-webkit-slider-thumb {
  162. border: 1px solid rgba(0,0,30,0);
  163. height: 22px;
  164. width: 22px;
  165. border-radius: 50px;
  166. background: #ff3034;
  167. cursor: pointer;
  168. -webkit-appearance: none;
  169. margin-top: -11.5px
  170. }
  171. input[type=range]:focus::-webkit-slider-runnable-track {
  172. background: #EFEFEF
  173. }
  174. input[type=range]::-moz-range-track {
  175. width: 100%;
  176. height: 2px;
  177. cursor: pointer;
  178. background: #EFEFEF;
  179. border-radius: 0;
  180. border: 0 solid #EFEFEF
  181. }
  182. input[type=range]::-moz-range-thumb {
  183. border: 1px solid rgba(0,0,30,0);
  184. height: 22px;
  185. width: 22px;
  186. border-radius: 50px;
  187. background: #ff3034;
  188. cursor: pointer
  189. }
  190. input[type=range]::-ms-track {
  191. width: 100%;
  192. height: 2px;
  193. cursor: pointer;
  194. background: 0 0;
  195. border-color: transparent;
  196. color: transparent
  197. }
  198. input[type=range]::-ms-fill-lower {
  199. background: #EFEFEF;
  200. border: 0 solid #EFEFEF;
  201. border-radius: 0
  202. }
  203. input[type=range]::-ms-fill-upper {
  204. background: #EFEFEF;
  205. border: 0 solid #EFEFEF;
  206. border-radius: 0
  207. }
  208. input[type=range]::-ms-thumb {
  209. border: 1px solid rgba(0,0,30,0);
  210. height: 22px;
  211. width: 22px;
  212. border-radius: 50px;
  213. background: #ff3034;
  214. cursor: pointer;
  215. height: 2px
  216. }
  217. input[type=range]:focus::-ms-fill-lower {
  218. background: #EFEFEF
  219. }
  220. input[type=range]:focus::-ms-fill-upper {
  221. background: #363636
  222. }
  223. .switch {
  224. display: block;
  225. position: relative;
  226. line-height: 22px;
  227. font-size: 16px;
  228. height: 22px
  229. }
  230. .switch input {
  231. outline: 0;
  232. opacity: 0;
  233. width: 0;
  234. height: 0
  235. }
  236. .slider {
  237. width: 50px;
  238. height: 22px;
  239. border-radius: 22px;
  240. cursor: pointer;
  241. background-color: grey
  242. }
  243. .slider,.slider:before {
  244. display: inline-block;
  245. transition: .4s
  246. }
  247. .slider:before {
  248. position: relative;
  249. content: "";
  250. border-radius: 50%;
  251. height: 16px;
  252. width: 16px;
  253. left: 4px;
  254. top: 3px;
  255. background-color: #fff
  256. }
  257. input:checked+.slider {
  258. background-color: #ff3034
  259. }
  260. input:checked+.slider:before {
  261. -webkit-transform: translateX(26px);
  262. transform: translateX(26px)
  263. }
  264. select {
  265. border: 1px solid #363636;
  266. font-size: 14px;
  267. height: 22px;
  268. outline: 0;
  269. border-radius: 5px
  270. }
  271. .image-container {
  272. position: relative;
  273. min-width: 160px
  274. }
  275. .close {
  276. position: absolute;
  277. right: 5px;
  278. top: 5px;
  279. background: #ff3034;
  280. width: 16px;
  281. height: 16px;
  282. border-radius: 100px;
  283. color: #fff;
  284. text-align: center;
  285. line-height: 18px;
  286. cursor: pointer
  287. }
  288. .hidden {
  289. display: none
  290. }
  291. </style>
  292. </head>
  293. <body>
  294. <section class="main">
  295. <div id="logo">
  296. <label for="nav-toggle-cb" id="nav-toggle">&#9776;&nbsp;&nbsp;相机设置</label>
  297. </div>
  298. <div id="content">
  299. <div id="sidebar">
  300. <input type="checkbox" id="nav-toggle-cb" checked="checked">
  301. <nav id="menu">
  302. <div class="input-group" id="framesize-group">
  303. <label for="framesize">Resolution</label>
  304. <select id="framesize" class="default-action">
  305. <option value="10">UXGA(1600x1200)</option>
  306. <option value="9">SXGA(1280x1024)</option>
  307. <option value="8">XGA(1024x768)</option>
  308. <option value="7">SVGA(800x600)</option>
  309. <option value="6">VGA(640x480)</option>
  310. <option value="5" selected="selected">CIF(400x296)</option>
  311. <option value="4">QVGA(320x240)</option>
  312. <option value="3">HQVGA(240x176)</option>
  313. <option value="0">QQVGA(160x120)</option>
  314. </select>
  315. </div>
  316. <div class="input-group" id="light-group">
  317. <label for="lightbtn">Light Button</label>
  318. <div class="switch">
  319. <input id="lightbtn" type="checkbox" class="default-action">
  320. <label class="slider" for="lightbtn"></label>
  321. </div>
  322. </div>
  323. <section id="buttons">
  324. <button id="get-still">Get Still</button>
  325. <button id="toggle-stream">Start Stream</button>
  326. <button id="face_enroll" class="disabled" disabled="disabled">Enroll Face</button>
  327. </section>
  328. </nav>
  329. </div>
  330. <figure>
  331. <div id="stream-container" class="image-container hidden">
  332. <div class="close" id="close-stream">×</div>
  333. <img id="stream" src="">
  334. </div>
  335. </figure>
  336. <div id="dht"></div>
  337. </div>
  338. </section>
  339. <script>
  340. document.addEventListener('DOMContentLoaded', function (event) {
  341. var baseHost = document.location.origin
  342. var streamUrl = baseHost + ':81'
  343. const hide = el => {
  344. el.classList.add('hidden')
  345. }
  346. const show = el => {
  347. el.classList.remove('hidden')
  348. }
  349. const disable = el => {
  350. el.classList.add('disabled')
  351. el.disabled = true
  352. }
  353. const enable = el => {
  354. el.classList.remove('disabled')
  355. el.disabled = false
  356. }
  357. const updateValue = (el, value, updateRemote) => {
  358. updateRemote = updateRemote == null ? true : updateRemote
  359. let initialValue
  360. if (el.type === 'checkbox') {
  361. initialValue = el.checked
  362. value = !!value
  363. el.checked = value
  364. } else {
  365. initialValue = el.value
  366. el.value = value
  367. }
  368. if (updateRemote && initialValue !== value) {
  369. updateConfig(el);
  370. } else if(!updateRemote){
  371. if(el.id === "aec"){
  372. value ? hide(exposure) : show(exposure)
  373. } else if(el.id === "agc"){
  374. if (value) {
  375. show(gainCeiling)
  376. hide(agcGain)
  377. } else {
  378. hide(gainCeiling)
  379. show(agcGain)
  380. }
  381. } else if(el.id === "awb_gain"){
  382. value ? show(wb) : hide(wb)
  383. } else if(el.id === "face_recognize"){
  384. value ? enable(enrollButton) : disable(enrollButton)
  385. }
  386. }
  387. }
  388. function updateConfig (el) {
  389. let value
  390. switch (el.type) {
  391. case 'checkbox':
  392. value = el.checked ? 1 : 0
  393. break
  394. case 'range':
  395. case 'select-one':
  396. value = el.value
  397. break
  398. case 'button':
  399. case 'submit':
  400. value = '1'
  401. break
  402. default:
  403. return
  404. }
  405. const query = `${baseHost}/control?var=${el.id}&val=${value}`
  406. fetch(query)
  407. .then(response => {
  408. console.log(`request to ${query} finished, status: ${response.status}`)
  409. })
  410. }
  411. document
  412. .querySelectorAll('.close')
  413. .forEach(el => {
  414. el.onclick = () => {
  415. hide(el.parentNode)
  416. }
  417. })
  418. // read initial values
  419. fetch(`${baseHost}/status`)
  420. .then(function (response) {
  421. return response.json()
  422. })
  423. .then(function (state) {
  424. document
  425. .querySelectorAll('.default-action')
  426. .forEach(el => {
  427. updateValue(el, state[el.id], false)
  428. })
  429. })
  430. const view = document.getElementById('stream')
  431. const viewContainer = document.getElementById('stream-container')
  432. const stillButton = document.getElementById('get-still')
  433. const streamButton = document.getElementById('toggle-stream')
  434. const enrollButton = document.getElementById('face_enroll')
  435. const closeButton = document.getElementById('close-stream')
  436. const stopStream = () => {
  437. window.stop();
  438. streamButton.innerHTML = 'Start Stream'
  439. }
  440. const startStream = () => {
  441. view.src = `${streamUrl}/stream`
  442. show(viewContainer)
  443. streamButton.innerHTML = 'Stop Stream'
  444. }
  445. // Attach actions to buttons
  446. stillButton.onclick = () => {
  447. stopStream()
  448. view.src = `${baseHost}/capture?_cb=${Date.now()}`
  449. show(viewContainer)
  450. }
  451. closeButton.onclick = () => {
  452. stopStream()
  453. hide(viewContainer)
  454. }
  455. streamButton.onclick = () => {
  456. const streamEnabled = streamButton.innerHTML === 'Stop Stream'
  457. if (streamEnabled) {
  458. stopStream()
  459. } else {
  460. startStream()
  461. }
  462. }
  463. enrollButton.onclick = () => {
  464. updateConfig(enrollButton)
  465. }
  466. // Attach default on change action
  467. document
  468. .querySelectorAll('.default-action')
  469. .forEach(el => {
  470. el.onchange = () => updateConfig(el)
  471. })
  472. // Custom actions
  473. // Gain
  474. const agc = document.getElementById('agc')
  475. const agcGain = document.getElementById('agc_gain-group')
  476. const gainCeiling = document.getElementById('gainceiling-group')
  477. agc.onchange = () => {
  478. updateConfig(agc)
  479. if (agc.checked) {
  480. show(gainCeiling)
  481. hide(agcGain)
  482. } else {
  483. hide(gainCeiling)
  484. show(agcGain)
  485. }
  486. }
  487. // Exposure
  488. const aec = document.getElementById('aec')
  489. const exposure = document.getElementById('aec_value-group')
  490. aec.onchange = () => {
  491. updateConfig(aec)
  492. aec.checked ? hide(exposure) : show(exposure)
  493. }
  494. // AWB
  495. const awb = document.getElementById('awb_gain')
  496. const wb = document.getElementById('wb_mode-group')
  497. awb.onchange = () => {
  498. updateConfig(awb)
  499. awb.checked ? show(wb) : hide(wb)
  500. }
  501. // Detection and framesize
  502. const detect = 0
  503. const recognize = 0
  504. const framesize = document.getElementById('framesize')
  505. framesize.onchange = () => {
  506. updateConfig(framesize)
  507. if (framesize.value > 5) {
  508. updateValue(detect, false)
  509. updateValue(recognize, false)
  510. }
  511. }
  512. detect.onchange = () => {
  513. if (framesize.value > 5) {
  514. alert("Please select CIF or lower resolution before enabling this feature!");
  515. updateValue(detect, false)
  516. return;
  517. }
  518. updateConfig(detect)
  519. if (!detect.checked) {
  520. disable(enrollButton)
  521. updateValue(recognize, false)
  522. }
  523. }
  524. recognize.onchange = () => {
  525. if (framesize.value > 5) {
  526. alert("Please select CIF or lower resolution before enabling this feature!");
  527. updateValue(recognize, false)
  528. return;
  529. }
  530. updateConfig(recognize)
  531. if (recognize.checked) {
  532. enable(enrollButton)
  533. updateValue(detect, true)
  534. } else {
  535. disable(enrollButton)
  536. }
  537. }
  538. })
  539. </script>
  540. </body>
  541. </html>

2.增加页面配置

        页面增加一个手动触发的按钮,方便测试,以及文本显示区域。在<nav id="menu"> </nav>标签内部增加下面内容。

  1. <div class="input-group" id="datavar-consle-b">
  2. <button type="button" onclick="loadXMLDoc()">请求数据</button>
  3. </div>
  4. <div class="input-group" id="datavar-consle-t">
  5. <label for="contrast">变量:</label>
  6. <input class="textbox" id="datavar-text" name="datavar" type="text" value="" />
  7. </div>

         真正的实现,也就是从ESP32服务端获取传感器显示数据到页面的就是loadXMLDoc()函数

3.增加Javascript脚本程序

        在原先的<script> </script>上方或者下方添加下面代码,请注意一定不要添加到内部。

  1. <script type=text/javascript>
  2. var xmlhttp;
  3. function loadXMLDoc()
  4. {
  5. if(window.XMLHttpRequest)
  6. {
  7. xmlhttp = new XMLHttpRequest();
  8. }
  9. else
  10. {
  11. xmlhttp = new ActiveXObject(Microsoft.XMLHTTP);
  12. }
  13. xmlhttp.onreadystatechange=handle;
  14. xmlhttp.open("get","/query",true);
  15. xmlhttp.send(null);
  16. }
  17. function handle()
  18. {
  19. if (xmlhttp.readyState==4 && xmlhttp.status==200)
  20. {
  21. var res = xmlhttp.responseText;
  22. var data = document.getElementById('datavar-text');
  23. console.log(res);
  24. document.getElementById('datavar-text').value = res;
  25. data.innerHTML = res;
  26. }
  27. }
  28. </script>

        这个代码的意思是生成一个http://[IP地址]/query的请求发送到ESP32服务器,然后通过handle处理结果,再通过标签datavar-text设置显示的文本值。

4.怎么实现自动刷新?

        哈哈,其实很简单,在上面的loadXMLDoc()函数上方增加下面一行代码就行了。

setInterval("loadXMLDoc()",1000);

5.常见错误

(1) 找不到loadXMLDoc()函数

        之前碰到的坑就是,控制台直接报错,找不到loadXMLDoc()函数,有可能是你把上面的程序加入到了document.addEventListener('DOMContentLoaded', function (event) { ... } 方括号内了。

(2) 找不到标签datavar-text

        可能是你单双引号敲错了,或者没有敲。

三、ino程序修改

(1)app_httpd.cpp文件

1) startCameraServer函数增加下面的内容

  1. httpd_uri_t queryvar_uri = {
  2. .uri = "/query",
  3. .method = HTTP_GET,
  4. .handler = query_handler,
  5. .user_ctx = NULL
  6. };
httpd_register_uri_handler(camera_httpd, &queryvar_uri);

2) 增加query_handler处理函数

  1. static esp_err_t query_handler(httpd_req_t *req){
  2. static char response[1024];
  3. char * p = response;
  4. int var = 10086;
  5. Serial.printf("var:%d", var);
  6. p+=sprintf(p, "%d", var);
  7. httpd_resp_set_type(req, "text/html");
  8. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  9. return httpd_resp_send(req, response, strlen(response));
  10. }

四、整个文件程序

(1)index_ov2640.html文件

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1">
  6. <title>ESP32</title>
  7. <style>
  8. body {
  9. font-family: Arial,Helvetica,sans-serif;
  10. background: #181818;
  11. color: #EFEFEF;
  12. font-size: 16px
  13. }
  14. h2 {
  15. font-size: 18px
  16. }
  17. section.main {
  18. display: flex
  19. }
  20. #menu,section.main {
  21. flex-direction: column
  22. }
  23. #menu {
  24. display: none;
  25. flex-wrap: nowrap;
  26. min-width: 340px;
  27. background: #363636;
  28. padding: 8px;
  29. border-radius: 4px;
  30. margin-top: -10px;
  31. margin-right: 10px;
  32. }
  33. #content {
  34. display: flex;
  35. flex-wrap: wrap;
  36. align-items: stretch
  37. }
  38. figure {
  39. padding: 0px;
  40. margin: 0;
  41. -webkit-margin-before: 0;
  42. margin-block-start: 0;
  43. -webkit-margin-after: 0;
  44. margin-block-end: 0;
  45. -webkit-margin-start: 0;
  46. margin-inline-start: 0;
  47. -webkit-margin-end: 0;
  48. margin-inline-end: 0
  49. }
  50. figure img {
  51. display: block;
  52. width: 100%;
  53. height: auto;
  54. border-radius: 4px;
  55. margin-top: 8px;
  56. }
  57. @media (min-width: 800px) and (orientation:landscape) {
  58. #content {
  59. display:flex;
  60. flex-wrap: nowrap;
  61. align-items: stretch
  62. }
  63. figure img {
  64. display: block;
  65. max-width: 100%;
  66. max-height: calc(100vh - 40px);
  67. width: auto;
  68. height: auto
  69. }
  70. figure {
  71. padding: 0 0 0 0px;
  72. margin: 0;
  73. -webkit-margin-before: 0;
  74. margin-block-start: 0;
  75. -webkit-margin-after: 0;
  76. margin-block-end: 0;
  77. -webkit-margin-start: 0;
  78. margin-inline-start: 0;
  79. -webkit-margin-end: 0;
  80. margin-inline-end: 0
  81. }
  82. }
  83. section#buttons {
  84. display: flex;
  85. flex-wrap: nowrap;
  86. justify-content: space-between
  87. }
  88. #nav-toggle {
  89. cursor: pointer;
  90. display: block
  91. }
  92. #nav-toggle-cb {
  93. outline: 0;
  94. opacity: 0;
  95. width: 0;
  96. height: 0
  97. }
  98. #nav-toggle-cb:checked+#menu {
  99. display: flex
  100. }
  101. .input-group {
  102. display: flex;
  103. flex-wrap: nowrap;
  104. line-height: 22px;
  105. margin: 5px 0
  106. }
  107. .input-group>label {
  108. display: inline-block;
  109. padding-right: 10px;
  110. min-width: 47%
  111. }
  112. .input-group input,.input-group select {
  113. flex-grow: 1
  114. }
  115. .range-max,.range-min {
  116. display: inline-block;
  117. padding: 0 5px
  118. }
  119. button {
  120. display: block;
  121. margin: 5px;
  122. padding: 0 12px;
  123. border: 0;
  124. line-height: 28px;
  125. cursor: pointer;
  126. color: #fff;
  127. background: #ff3034;
  128. border-radius: 5px;
  129. font-size: 16px;
  130. outline: 0
  131. }
  132. button:hover {
  133. background: #ff494d
  134. }
  135. button:active {
  136. background: #f21c21
  137. }
  138. button.disabled {
  139. cursor: default;
  140. background: #a0a0a0
  141. }
  142. input[type=range] {
  143. -webkit-appearance: none;
  144. width: 100%;
  145. height: 22px;
  146. background: #363636;
  147. cursor: pointer;
  148. margin: 0
  149. }
  150. input[type=range]:focus {
  151. outline: 0
  152. }
  153. input[type=range]::-webkit-slider-runnable-track {
  154. width: 100%;
  155. height: 2px;
  156. cursor: pointer;
  157. background: #EFEFEF;
  158. border-radius: 0;
  159. border: 0 solid #EFEFEF
  160. }
  161. input[type=range]::-webkit-slider-thumb {
  162. border: 1px solid rgba(0,0,30,0);
  163. height: 22px;
  164. width: 22px;
  165. border-radius: 50px;
  166. background: #ff3034;
  167. cursor: pointer;
  168. -webkit-appearance: none;
  169. margin-top: -11.5px
  170. }
  171. input[type=range]:focus::-webkit-slider-runnable-track {
  172. background: #EFEFEF
  173. }
  174. input[type=range]::-moz-range-track {
  175. width: 100%;
  176. height: 2px;
  177. cursor: pointer;
  178. background: #EFEFEF;
  179. border-radius: 0;
  180. border: 0 solid #EFEFEF
  181. }
  182. input[type=range]::-moz-range-thumb {
  183. border: 1px solid rgba(0,0,30,0);
  184. height: 22px;
  185. width: 22px;
  186. border-radius: 50px;
  187. background: #ff3034;
  188. cursor: pointer
  189. }
  190. input[type=range]::-ms-track {
  191. width: 100%;
  192. height: 2px;
  193. cursor: pointer;
  194. background: 0 0;
  195. border-color: transparent;
  196. color: transparent
  197. }
  198. input[type=range]::-ms-fill-lower {
  199. background: #EFEFEF;
  200. border: 0 solid #EFEFEF;
  201. border-radius: 0
  202. }
  203. input[type=range]::-ms-fill-upper {
  204. background: #EFEFEF;
  205. border: 0 solid #EFEFEF;
  206. border-radius: 0
  207. }
  208. input[type=range]::-ms-thumb {
  209. border: 1px solid rgba(0,0,30,0);
  210. height: 22px;
  211. width: 22px;
  212. border-radius: 50px;
  213. background: #ff3034;
  214. cursor: pointer;
  215. height: 2px
  216. }
  217. input[type=range]:focus::-ms-fill-lower {
  218. background: #EFEFEF
  219. }
  220. input[type=range]:focus::-ms-fill-upper {
  221. background: #363636
  222. }
  223. .switch {
  224. display: block;
  225. position: relative;
  226. line-height: 22px;
  227. font-size: 16px;
  228. height: 22px
  229. }
  230. .switch input {
  231. outline: 0;
  232. opacity: 0;
  233. width: 0;
  234. height: 0
  235. }
  236. .slider {
  237. width: 50px;
  238. height: 22px;
  239. border-radius: 22px;
  240. cursor: pointer;
  241. background-color: grey
  242. }
  243. .slider,.slider:before {
  244. display: inline-block;
  245. transition: .4s
  246. }
  247. .slider:before {
  248. position: relative;
  249. content: "";
  250. border-radius: 50%;
  251. height: 16px;
  252. width: 16px;
  253. left: 4px;
  254. top: 3px;
  255. background-color: #fff
  256. }
  257. input:checked+.slider {
  258. background-color: #ff3034
  259. }
  260. input:checked+.slider:before {
  261. -webkit-transform: translateX(26px);
  262. transform: translateX(26px)
  263. }
  264. select {
  265. border: 1px solid #363636;
  266. font-size: 14px;
  267. height: 22px;
  268. outline: 0;
  269. border-radius: 5px
  270. }
  271. .image-container {
  272. position: relative;
  273. min-width: 160px
  274. }
  275. .close {
  276. position: absolute;
  277. right: 5px;
  278. top: 5px;
  279. background: #ff3034;
  280. width: 16px;
  281. height: 16px;
  282. border-radius: 100px;
  283. color: #fff;
  284. text-align: center;
  285. line-height: 18px;
  286. cursor: pointer
  287. }
  288. .hidden {
  289. display: none
  290. }
  291. </style>
  292. </head>
  293. <body>
  294. <section class="main">
  295. <div id="logo">
  296. <label for="nav-toggle-cb" id="nav-toggle">&#9776;&nbsp;&nbsp;相机设置</label>
  297. </div>
  298. <div id="content">
  299. <div id="sidebar">
  300. <input type="checkbox" id="nav-toggle-cb" checked="checked">
  301. <nav id="menu">
  302. <div class="input-group" id="framesize-group">
  303. <label for="framesize">Resolution</label>
  304. <select id="framesize" class="default-action">
  305. <option value="10">UXGA(1600x1200)</option>
  306. <option value="9">SXGA(1280x1024)</option>
  307. <option value="8">XGA(1024x768)</option>
  308. <option value="7">SVGA(800x600)</option>
  309. <option value="6">VGA(640x480)</option>
  310. <option value="5" selected="selected">CIF(400x296)</option>
  311. <option value="4">QVGA(320x240)</option>
  312. <option value="3">HQVGA(240x176)</option>
  313. <option value="0">QQVGA(160x120)</option>
  314. </select>
  315. </div>
  316. <div class="input-group" id="light-group">
  317. <label for="lightbtn">Light Button</label>
  318. <div class="switch">
  319. <input id="lightbtn" type="checkbox" class="default-action">
  320. <label class="slider" for="lightbtn"></label>
  321. </div>
  322. </div>
  323. <div class="input-group" id="datavar-consle-b">
  324. <button type="button" onclick="loadXMLDoc()">请求数据</button>
  325. </div>
  326. <div class="input-group" id="datavar-consle-t">
  327. <label for="contrast">变量:</label>
  328. <input class="textbox" id="datavar-text" name="datavar" type="text" value="" />
  329. </div>
  330. <section id="buttons">
  331. <button id="get-still">Get Still</button>
  332. <button id="toggle-stream">Start Stream</button>
  333. <button id="face_enroll" class="disabled" disabled="disabled">Enroll Face</button>
  334. </section>
  335. </nav>
  336. </div>
  337. <figure>
  338. <div id="stream-container" class="image-container hidden">
  339. <div class="close" id="close-stream">×</div>
  340. <img id="stream" src="">
  341. </div>
  342. </figure>
  343. <div id="dht"></div>
  344. </div>
  345. </section>
  346. <script type=text/javascript>
  347. var xmlhttp;
  348. setInterval("loadXMLDoc()",1000);
  349. function loadXMLDoc()
  350. {
  351. if(window.XMLHttpRequest)
  352. {
  353. xmlhttp = new XMLHttpRequest();
  354. }
  355. else
  356. {
  357. xmlhttp = new ActiveXObject(Microsoft.XMLHTTP);
  358. }
  359. xmlhttp.onreadystatechange=handle;
  360. xmlhttp.open("get","/query",true);
  361. xmlhttp.send(null);
  362. }
  363. function handle()
  364. {
  365. if (xmlhttp.readyState==4 && xmlhttp.status==200)
  366. {
  367. var res = xmlhttp.responseText;
  368. var data = document.getElementById('datavar-text');
  369. console.log(res);
  370. document.getElementById('datavar-text').value = res;
  371. data.innerHTML = res;
  372. }
  373. }
  374. </script>
  375. <script>
  376. document.addEventListener('DOMContentLoaded', function (event) {
  377. var baseHost = document.location.origin
  378. var streamUrl = baseHost + ':81'
  379. const hide = el => {
  380. el.classList.add('hidden')
  381. }
  382. const show = el => {
  383. el.classList.remove('hidden')
  384. }
  385. const disable = el => {
  386. el.classList.add('disabled')
  387. el.disabled = true
  388. }
  389. const enable = el => {
  390. el.classList.remove('disabled')
  391. el.disabled = false
  392. }
  393. const updateValue = (el, value, updateRemote) => {
  394. updateRemote = updateRemote == null ? true : updateRemote
  395. let initialValue
  396. if (el.type === 'checkbox') {
  397. initialValue = el.checked
  398. value = !!value
  399. el.checked = value
  400. } else {
  401. initialValue = el.value
  402. el.value = value
  403. }
  404. if (updateRemote && initialValue !== value) {
  405. updateConfig(el);
  406. } else if(!updateRemote){
  407. if(el.id === "aec"){
  408. value ? hide(exposure) : show(exposure)
  409. } else if(el.id === "agc"){
  410. if (value) {
  411. show(gainCeiling)
  412. hide(agcGain)
  413. } else {
  414. hide(gainCeiling)
  415. show(agcGain)
  416. }
  417. } else if(el.id === "awb_gain"){
  418. value ? show(wb) : hide(wb)
  419. } else if(el.id === "face_recognize"){
  420. value ? enable(enrollButton) : disable(enrollButton)
  421. }
  422. }
  423. }
  424. function updateConfig (el) {
  425. let value
  426. switch (el.type) {
  427. case 'checkbox':
  428. value = el.checked ? 1 : 0
  429. break
  430. case 'range':
  431. case 'select-one':
  432. value = el.value
  433. break
  434. case 'button':
  435. case 'submit':
  436. value = '1'
  437. break
  438. default:
  439. return
  440. }
  441. const query = `${baseHost}/control?var=${el.id}&val=${value}`
  442. fetch(query)
  443. .then(response => {
  444. console.log(`request to ${query} finished, status: ${response.status}`)
  445. })
  446. }
  447. document
  448. .querySelectorAll('.close')
  449. .forEach(el => {
  450. el.onclick = () => {
  451. hide(el.parentNode)
  452. }
  453. })
  454. // read initial values
  455. fetch(`${baseHost}/status`)
  456. .then(function (response) {
  457. return response.json()
  458. })
  459. .then(function (state) {
  460. document
  461. .querySelectorAll('.default-action')
  462. .forEach(el => {
  463. updateValue(el, state[el.id], false)
  464. })
  465. })
  466. const view = document.getElementById('stream')
  467. const viewContainer = document.getElementById('stream-container')
  468. const stillButton = document.getElementById('get-still')
  469. const streamButton = document.getElementById('toggle-stream')
  470. const enrollButton = document.getElementById('face_enroll')
  471. const closeButton = document.getElementById('close-stream')
  472. const stopStream = () => {
  473. window.stop();
  474. streamButton.innerHTML = 'Start Stream'
  475. }
  476. const startStream = () => {
  477. view.src = `${streamUrl}/stream`
  478. show(viewContainer)
  479. streamButton.innerHTML = 'Stop Stream'
  480. }
  481. // Attach actions to buttons
  482. stillButton.onclick = () => {
  483. stopStream()
  484. view.src = `${baseHost}/capture?_cb=${Date.now()}`
  485. show(viewContainer)
  486. }
  487. closeButton.onclick = () => {
  488. stopStream()
  489. hide(viewContainer)
  490. }
  491. streamButton.onclick = () => {
  492. const streamEnabled = streamButton.innerHTML === 'Stop Stream'
  493. if (streamEnabled) {
  494. stopStream()
  495. } else {
  496. startStream()
  497. }
  498. }
  499. enrollButton.onclick = () => {
  500. updateConfig(enrollButton)
  501. }
  502. // Attach default on change action
  503. document
  504. .querySelectorAll('.default-action')
  505. .forEach(el => {
  506. el.onchange = () => updateConfig(el)
  507. })
  508. // Custom actions
  509. // Gain
  510. const agc = document.getElementById('agc')
  511. const agcGain = document.getElementById('agc_gain-group')
  512. const gainCeiling = document.getElementById('gainceiling-group')
  513. agc.onchange = () => {
  514. updateConfig(agc)
  515. if (agc.checked) {
  516. show(gainCeiling)
  517. hide(agcGain)
  518. } else {
  519. hide(gainCeiling)
  520. show(agcGain)
  521. }
  522. }
  523. // Exposure
  524. const aec = document.getElementById('aec')
  525. const exposure = document.getElementById('aec_value-group')
  526. aec.onchange = () => {
  527. updateConfig(aec)
  528. aec.checked ? hide(exposure) : show(exposure)
  529. }
  530. // AWB
  531. const awb = document.getElementById('awb_gain')
  532. const wb = document.getElementById('wb_mode-group')
  533. awb.onchange = () => {
  534. updateConfig(awb)
  535. awb.checked ? show(wb) : hide(wb)
  536. }
  537. // Detection and framesize
  538. const detect = 0
  539. const recognize = 0
  540. const framesize = document.getElementById('framesize')
  541. framesize.onchange = () => {
  542. updateConfig(framesize)
  543. if (framesize.value > 5) {
  544. updateValue(detect, false)
  545. updateValue(recognize, false)
  546. }
  547. }
  548. detect.onchange = () => {
  549. if (framesize.value > 5) {
  550. alert("Please select CIF or lower resolution before enabling this feature!");
  551. updateValue(detect, false)
  552. return;
  553. }
  554. updateConfig(detect)
  555. if (!detect.checked) {
  556. disable(enrollButton)
  557. updateValue(recognize, false)
  558. }
  559. }
  560. recognize.onchange = () => {
  561. if (framesize.value > 5) {
  562. alert("Please select CIF or lower resolution before enabling this feature!");
  563. updateValue(recognize, false)
  564. return;
  565. }
  566. updateConfig(recognize)
  567. if (recognize.checked) {
  568. enable(enrollButton)
  569. updateValue(detect, true)
  570. } else {
  571. disable(enrollButton)
  572. }
  573. }
  574. })
  575. </script>
  576. </body>
  577. </html>

(2)app_httpd.cpp文件

  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "esp_http_server.h"
  15. #include "esp_timer.h"
  16. #include "esp_camera.h"
  17. #include "img_converters.h"
  18. #include "camera_index.h"
  19. #include "Arduino.h"
  20. #include "fb_gfx.h"
  21. #include "fd_forward.h"
  22. #include "fr_forward.h"
  23. #define ENROLL_CONFIRM_TIMES 5
  24. #define FACE_ID_SAVE_NUMBER 7
  25. #define LINE_RECOGINE_MEDIAN_THD 80
  26. #define FACE_COLOR_WHITE 0x00FFFFFF
  27. #define FACE_COLOR_BLACK 0x00000000
  28. #define FACE_COLOR_RED 0x000000FF
  29. #define FACE_COLOR_GREEN 0x0000FF00
  30. #define FACE_COLOR_BLUE 0x00FF0000
  31. #define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN)
  32. #define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN)
  33. #define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED)
  34. typedef struct {
  35. size_t size; //number of values used for filtering
  36. size_t index; //current value index
  37. size_t count; //value count
  38. int sum;
  39. int * values; //array to be filled with values
  40. } ra_filter_t;
  41. typedef struct {
  42. httpd_req_t *req;
  43. size_t len;
  44. } jpg_chunking_t;
  45. typedef struct
  46. {
  47. float star_point[2];
  48. float end_point[2];
  49. } line_t;
  50. typedef struct tag_line_list
  51. {
  52. float score;
  53. line_t *line;
  54. int len;
  55. } line_array_t;
  56. #define PART_BOUNDARY "123456789000000000000987654321"
  57. static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
  58. static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
  59. static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
  60. static bool light_flag = 0;
  61. static bool linedetect = 0;
  62. static ra_filter_t ra_filter;
  63. httpd_handle_t stream_httpd = NULL;
  64. httpd_handle_t camera_httpd = NULL;
  65. static mtmn_config_t mtmn_config = {0};
  66. static int8_t detection_enabled = 0;
  67. static int8_t recognition_enabled = 0;
  68. static int8_t is_enrolling = 0;
  69. static face_id_list id_list = {0};
  70. static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){
  71. memset(filter, 0, sizeof(ra_filter_t));
  72. filter->values = (int *)malloc(sample_size * sizeof(int));
  73. if(!filter->values){
  74. return NULL;
  75. }
  76. memset(filter->values, 0, sample_size * sizeof(int));
  77. filter->size = sample_size;
  78. return filter;
  79. }
  80. static int ra_filter_run(ra_filter_t * filter, int value){
  81. if(!filter->values){
  82. return value;
  83. }
  84. filter->sum -= filter->values[filter->index];
  85. filter->values[filter->index] = value;
  86. filter->sum += filter->values[filter->index];
  87. filter->index++;
  88. filter->index = filter->index % filter->size;
  89. if (filter->count < filter->size) {
  90. filter->count++;
  91. }
  92. return filter->sum / filter->count;
  93. }
  94. static void rgb_print(dl_matrix3du_t *image_matrix, uint32_t color, const char * str){
  95. fb_data_t fb;
  96. fb.width = image_matrix->w;
  97. fb.height = image_matrix->h;
  98. fb.data = image_matrix->item;
  99. fb.bytes_per_pixel = 3;
  100. fb.format = FB_BGR888;
  101. fb_gfx_print(&fb, (fb.width - (strlen(str) * 14)) / 2, 10, color, str);
  102. }
  103. static int rgb_printf(dl_matrix3du_t *image_matrix, uint32_t color, const char *format, ...){
  104. char loc_buf[64];
  105. char * temp = loc_buf;
  106. int len;
  107. va_list arg;
  108. va_list copy;
  109. va_start(arg, format);
  110. va_copy(copy, arg);
  111. len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg);
  112. va_end(copy);
  113. if(len >= sizeof(loc_buf)){
  114. temp = (char*)malloc(len+1);
  115. if(temp == NULL) {
  116. return 0;
  117. }
  118. }
  119. vsnprintf(temp, len+1, format, arg);
  120. va_end(arg);
  121. rgb_print(image_matrix, color, temp);
  122. if(len > 64){
  123. free(temp);
  124. }
  125. return len;
  126. }
  127. static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id){
  128. int x, y, w, h, i;
  129. uint32_t color = FACE_COLOR_YELLOW;
  130. if(face_id < 0){
  131. color = FACE_COLOR_RED;
  132. } else if(face_id > 0){
  133. color = FACE_COLOR_GREEN;
  134. }
  135. fb_data_t fb;
  136. fb.width = image_matrix->w;
  137. fb.height = image_matrix->h;
  138. fb.data = image_matrix->item;
  139. fb.bytes_per_pixel = 3;
  140. fb.format = FB_BGR888;
  141. for (i = 0; i < boxes->len; i++){
  142. // rectangle box
  143. x = (int)boxes->box[i].box_p[0];
  144. y = (int)boxes->box[i].box_p[1];
  145. w = (int)boxes->box[i].box_p[2] - x + 1;
  146. h = (int)boxes->box[i].box_p[3] - y + 1;
  147. fb_gfx_drawFastHLine(&fb, x, y, w, color);
  148. fb_gfx_drawFastHLine(&fb, x, y+h-1, w, color);
  149. fb_gfx_drawFastVLine(&fb, x, y, h, color);
  150. fb_gfx_drawFastVLine(&fb, x+w-1, y, h, color);
  151. #if 0
  152. // landmark
  153. int x0, y0, j;
  154. for (j = 0; j < 10; j+=2) {
  155. x0 = (int)boxes->landmark[i].landmark_p[j];
  156. y0 = (int)boxes->landmark[i].landmark_p[j+1];
  157. fb_gfx_fillRect(&fb, x0, y0, 3, 3, color);
  158. }
  159. #endif
  160. }
  161. }
  162. static int run_face_recognition(dl_matrix3du_t *image_matrix, box_array_t *net_boxes){
  163. dl_matrix3du_t *aligned_face = NULL;
  164. int matched_id = 0;
  165. aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3);
  166. if(!aligned_face){
  167. Serial.println("Could not allocate face recognition buffer");
  168. return matched_id;
  169. }
  170. if (align_face(net_boxes, image_matrix, aligned_face) == ESP_OK){
  171. if (is_enrolling == 1){
  172. int8_t left_sample_face = enroll_face(&id_list, aligned_face);
  173. if(left_sample_face == (ENROLL_CONFIRM_TIMES - 1)){
  174. Serial.printf("Enrolling Face ID: %d\n", id_list.tail);
  175. }
  176. Serial.printf("Enrolling Face ID: %d sample %d\n", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
  177. rgb_printf(image_matrix, FACE_COLOR_CYAN, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
  178. if (left_sample_face == 0){
  179. is_enrolling = 0;
  180. Serial.printf("Enrolled Face ID: %d\n", id_list.tail);
  181. }
  182. } else {
  183. matched_id = recognize_face(&id_list, aligned_face);
  184. if (matched_id >= 0) {
  185. Serial.printf("Match Face ID: %u\n", matched_id);
  186. rgb_printf(image_matrix, FACE_COLOR_GREEN, "Hello Subject %u", matched_id);
  187. } else {
  188. Serial.println("No Match Found");
  189. rgb_print(image_matrix, FACE_COLOR_RED, "Intruder Alert!");
  190. matched_id = -1;
  191. }
  192. }
  193. } else {
  194. Serial.println("Face Not Aligned");
  195. //rgb_print(image_matrix, FACE_COLOR_YELLOW, "Human Detected");
  196. }
  197. dl_matrix3du_free(aligned_face);
  198. return matched_id;
  199. }
  200. static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
  201. jpg_chunking_t *j = (jpg_chunking_t *)arg;
  202. if(!index){
  203. j->len = 0;
  204. }
  205. if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
  206. return 0;
  207. }
  208. j->len += len;
  209. return len;
  210. }
  211. static esp_err_t capture_handler(httpd_req_t *req){
  212. camera_fb_t * fb = NULL;
  213. esp_err_t res = ESP_OK;
  214. int64_t fr_start = esp_timer_get_time();
  215. fb = esp_camera_fb_get();
  216. if (!fb) {
  217. Serial.println("Camera capture failed");
  218. httpd_resp_send_500(req);
  219. return ESP_FAIL;
  220. }
  221. httpd_resp_set_type(req, "image/jpeg");
  222. httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
  223. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  224. size_t out_len, out_width, out_height;
  225. uint8_t * out_buf;
  226. bool s;
  227. bool detected = false;
  228. int face_id = 0;
  229. if(!detection_enabled || fb->width > 400){
  230. size_t fb_len = 0;
  231. if(fb->format == PIXFORMAT_JPEG){
  232. fb_len = fb->len;
  233. res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
  234. } else {
  235. jpg_chunking_t jchunk = {req, 0};
  236. res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
  237. httpd_resp_send_chunk(req, NULL, 0);
  238. fb_len = jchunk.len;
  239. }
  240. esp_camera_fb_return(fb);
  241. int64_t fr_end = esp_timer_get_time();
  242. Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
  243. return res;
  244. }
  245. dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
  246. if (!image_matrix) {
  247. esp_camera_fb_return(fb);
  248. Serial.println("dl_matrix3du_alloc failed");
  249. httpd_resp_send_500(req);
  250. return ESP_FAIL;
  251. }
  252. out_buf = image_matrix->item;
  253. out_len = fb->width * fb->height * 3;
  254. out_width = fb->width;
  255. out_height = fb->height;
  256. s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf);
  257. esp_camera_fb_return(fb);
  258. if(!s){
  259. dl_matrix3du_free(image_matrix);
  260. Serial.println("to rgb888 failed");
  261. httpd_resp_send_500(req);
  262. return ESP_FAIL;
  263. }
  264. box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config);
  265. if (net_boxes){
  266. detected = true;
  267. if(recognition_enabled){
  268. face_id = run_face_recognition(image_matrix, net_boxes);
  269. }
  270. draw_face_boxes(image_matrix, net_boxes, face_id);
  271. free(net_boxes->score);
  272. free(net_boxes->box);
  273. free(net_boxes->landmark);
  274. free(net_boxes);
  275. }
  276. jpg_chunking_t jchunk = {req, 0};
  277. s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk);
  278. dl_matrix3du_free(image_matrix);
  279. if(!s){
  280. Serial.println("JPEG compression failed");
  281. return ESP_FAIL;
  282. }
  283. int64_t fr_end = esp_timer_get_time();
  284. Serial.printf("FACE: %uB %ums %s%d\n", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start)/1000), detected?"DETECTED ":"", face_id);
  285. return res;
  286. }
  287. uint8_t median(uint8_t sortArray[], uint8_t len) {
  288. uint8_t i,j,temp,flag;
  289. for(j=0; j<len-1; j++) {
  290. flag = 0;
  291. for (i=0; i<len-j; i++) {
  292. if (sortArray[j] > sortArray[j+1]) {
  293. flag = 1;
  294. temp = sortArray[j];
  295. sortArray[j] = sortArray[j+1];
  296. sortArray[j+1] = temp;
  297. }
  298. }
  299. if (!flag) {
  300. break;
  301. }
  302. }
  303. // Serial.printf(" m:%d ", sortArray[(len-1)/2]);
  304. return sortArray[(len-1)/2];
  305. }
  306. //image_matrix数据格式为一维数组,实际格式为[B G R]三个一组数据,第一行的列(共image_matrix->w列)排完,再写第N行(共image_matrix->h行)
  307. static line_array_t* recoginLines(dl_matrix3du_t *image_matrix) {
  308. uint16_t i,j,x,y;
  309. uint8_t **gray_img = NULL;
  310. uint8_t **gray_filter_img = NULL;
  311. float sx,sy;
  312. line_array_t *line_result = NULL;
  313. uint8_t medianArray[9];
  314. bool realse_flag = 1;
  315. uint16_t line_len[5] = {0};
  316. uint8_t k=0;
  317. uint8_t line_num = 0;
  318. gray_img = (uint8_t**)malloc(sizeof(uint8_t*)*image_matrix->h);
  319. if (gray_img){
  320. for(j=0;j<image_matrix->h;j++) {
  321. gray_img[j] = (uint8_t*)malloc(sizeof(uint8_t)*image_matrix->w);
  322. if (!gray_img[j]) {
  323. Serial.printf("malloc gray_img[%d] failed",j);
  324. goto __FREE_POINTER;
  325. }
  326. }
  327. } else {
  328. Serial.println("malloc gray_img failed");
  329. goto __FREE_POINTER;
  330. }
  331. //灰度化图像
  332. // Serial.println("matrix_gs:");
  333. for (j=0;j<image_matrix->h;j++) {
  334. for (i=0; i<image_matrix->w; i++) {
  335. gray_img[j][i] = uint8_t(0.114 * image_matrix->item[3*(j*image_matrix->w+i)] + 0.587 * image_matrix->item[3*(j*image_matrix->w+i)+1] + 0.2989 * image_matrix->item[3*(j*image_matrix->w+i)+2]);
  336. // Serial.printf(" %d",gray_img[j][i]);
  337. }
  338. }
  339. // Serial.println("matrix_ge:");
  340. //均值滤波
  341. gray_filter_img = (uint8_t**)malloc(sizeof(uint8_t*)*(image_matrix->h-1));
  342. if (!gray_filter_img) {
  343. Serial.println("malloc gray_filter_img failed");
  344. goto __FREE_POINTER;
  345. }
  346. gray_filter_img[0] = (uint8_t*)malloc(sizeof(uint8_t)*(image_matrix->w-1));
  347. memset(gray_filter_img[0], 0, sizeof(uint8_t)*(image_matrix->w-1));
  348. if (!gray_filter_img[0]) {
  349. Serial.println("malloc gray_filter_img[0] failed");
  350. goto __FREE_POINTER;
  351. }
  352. // Serial.println("matrix_s:");
  353. for (y=1; y<image_matrix->h-1; y++) {
  354. gray_filter_img[y] = (uint8_t*)malloc(sizeof(uint8_t)*(image_matrix->w-1));
  355. memset(gray_filter_img[y], 0, sizeof(uint8_t)*(image_matrix->w-1));
  356. if (!gray_filter_img[y]) {
  357. Serial.printf("malloc gray_filter_img[%d] failed",y);
  358. goto __FREE_POINTER;
  359. }
  360. for (x=1; x<image_matrix->w-1; x++) {
  361. medianArray[0] = gray_img[y-1][x-1];
  362. medianArray[1] = gray_img[y-1][x];
  363. medianArray[2] = gray_img[y-1][x+1];
  364. medianArray[3] = gray_img[y][x-1];
  365. medianArray[4] = gray_img[y][x];
  366. medianArray[5] = gray_img[y][x+1];
  367. medianArray[6] = gray_img[y+1][x-1];
  368. medianArray[7] = gray_img[y+1][x];
  369. medianArray[8] = gray_img[y+1][x+1];
  370. // gray_filter_img[y][x] = median(medianArray,9);
  371. // Serial.printf(" %d",gray_filter_img[y][x]);
  372. gray_filter_img[y][x] = median(medianArray,9)> LINE_RECOGINE_MEDIAN_THD ? 255: 0;
  373. }
  374. }
  375. Serial.println("matrix_e:");
  376. line_result = (line_array_t*)malloc(sizeof(line_array_t));
  377. if (line_result) {
  378. line_result->len=0;
  379. line_result->line = (line_t*)malloc(sizeof(line_t)* image_matrix->h);
  380. memset(line_result->line, 0, sizeof(line_t)* image_matrix->h);
  381. // Serial.println("line:");
  382. if (line_result->line) {
  383. for(y=1; y<image_matrix->h-1; y++) {
  384. for(x=1; x<image_matrix->w-1; x++) {
  385. // Serial.printf(" y:%d x:%d",y,x);
  386. if ((gray_filter_img[y][x-1] == 0) && (gray_filter_img[y][x] == 255)) {
  387. if (y==1) {
  388. line_result->line[y-1].star_point[0] = x;
  389. line_result->line[y-1].star_point[1] = y;
  390. line_result->line[y-1].end_point[0] = x;
  391. line_result->line[y-1].end_point[1] = y;
  392. } else {
  393. line_result->line[y-1].star_point[0] = line_result->line[y-2].end_point[0];
  394. line_result->line[y-1].star_point[1] = line_result->line[y-2].end_point[1];
  395. line_result->line[y-1].end_point[0] = x;
  396. line_result->line[y-1].end_point[1] = y;
  397. }
  398. //5个像素点阈值
  399. if ((abs(line_result->line[y-1].end_point[0] - line_result->line[y-1].star_point[0]) < 5) && (abs(line_result->line[y-1].end_point[1] - line_result->line[y-1].star_point[1]) < 5)) {
  400. line_len[k]++;
  401. } else {
  402. k++;
  403. if (k>4) {
  404. k=0;
  405. }
  406. }
  407. line_result->len++;
  408. break;
  409. }
  410. }
  411. }
  412. realse_flag = 0;
  413. } else {
  414. Serial.println("malloc line_result->line fail");
  415. goto __FREE_POINTER;
  416. }
  417. } else {
  418. Serial.println("malloc line_result fail");
  419. goto __FREE_POINTER;
  420. }
  421. line_num = 0;
  422. for(i=0; i<5; i++) {
  423. if (line_len[i] > 50) {
  424. line_num++;
  425. }
  426. }
  427. if (line_num == 1) {
  428. line_result->score = 1.0;
  429. } else {
  430. Serial.printf("line_num:%d ",line_num);
  431. line_result->score = 0.0;
  432. }
  433. __FREE_POINTER:
  434. //释放内存
  435. if (realse_flag) {
  436. if (line_result) {
  437. if (line_result->line) {
  438. free(line_result->line);
  439. }
  440. free(line_result);
  441. line_result = NULL;
  442. }
  443. }
  444. if (gray_filter_img) {
  445. for (j=0;j<image_matrix->h-1;j++) {
  446. if (gray_filter_img[j]) {
  447. free(gray_filter_img[j]);
  448. }
  449. }
  450. free(gray_filter_img);
  451. }
  452. if (gray_img) {
  453. for(j=0;j<image_matrix->h;j++) {
  454. if (gray_img[j]) {
  455. free(gray_img[j]);
  456. }
  457. }
  458. free(gray_img);
  459. }
  460. return line_result;
  461. }
  462. static void draw_lines(dl_matrix3du_t *image_matrix,line_array_t *lines) {
  463. int x0, y0, x1, y1, w, h, i, j;
  464. float temp = 0.0;
  465. uint32_t color = FACE_COLOR_RED;
  466. fb_data_t fb;
  467. fb.width = image_matrix->w;
  468. fb.height = image_matrix->h;
  469. fb.data = image_matrix->item;
  470. fb.bytes_per_pixel = 3;
  471. fb.format = FB_BGR888;
  472. for (i = 0; i < lines->len; i++){
  473. // rectangle box
  474. x0 = (int)lines->line[i].star_point[0];
  475. y0 = (int)lines->line[i].star_point[1];
  476. x1 = (int)lines->line[i].end_point[0];
  477. y1 = (int)lines->line[i].end_point[1];
  478. w = (int)x1 - x0 + 1;
  479. h = (int)y1 - y0 + 1;
  480. // Serial.printf("x0:%d y0:%d w:%d h:%d temp:%f", x0, y0, w, h, temp);
  481. if ((x0>0 && abs(x0) < image_matrix->w) && (y0>0 && abs(y0) < image_matrix->h) && (x1>0 && abs(x1) < image_matrix->w) && (y1>0 && abs(y1) < image_matrix->h)) {
  482. if (w > h) {
  483. temp = 1.0*h/w;
  484. for (j=0; j < w; j++) {
  485. fb_gfx_drawFastHLine(&fb, x0+j, y0+int(temp*j), 1, color);
  486. fb_gfx_drawFastVLine(&fb, x0+(j+1), y0+int(temp*j), 1, color);
  487. }
  488. } else {
  489. temp = 1.0*w/h;
  490. for (j=0; j < h; j++) {
  491. fb_gfx_drawFastVLine(&fb, x0+int(temp*j), y0+j, 1, color);
  492. fb_gfx_drawFastHLine(&fb, x0+int(temp*j), y0+(j+1), 1, color);
  493. }
  494. }
  495. }
  496. }
  497. }
  498. static esp_err_t stream_handler(httpd_req_t *req){
  499. camera_fb_t * fb = NULL;
  500. esp_err_t res = ESP_OK;
  501. size_t _jpg_buf_len = 0;
  502. uint8_t * _jpg_buf = NULL;
  503. char * part_buf[64];
  504. dl_matrix3du_t *image_matrix = NULL;
  505. bool detected = false;
  506. int face_id = 0;
  507. int64_t fr_start = 0;
  508. int64_t fr_ready = 0;
  509. int64_t fr_face = 0;
  510. int64_t fr_recognize = 0;
  511. int64_t fr_encode = 0;
  512. static int64_t last_frame = 0;
  513. if(!last_frame) {
  514. last_frame = esp_timer_get_time();
  515. }
  516. res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  517. if(res != ESP_OK){
  518. return res;
  519. }
  520. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  521. while(true){
  522. detected = false;
  523. face_id = 0;
  524. fb = esp_camera_fb_get();
  525. if (!fb) {
  526. Serial.println("Camera capture failed");
  527. res = ESP_FAIL;
  528. } else {
  529. fr_start = esp_timer_get_time();
  530. fr_ready = fr_start;
  531. fr_face = fr_start;
  532. fr_encode = fr_start;
  533. fr_recognize = fr_start;
  534. if(!linedetect || fb->width > 240){
  535. if(fb->format != PIXFORMAT_JPEG){
  536. bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
  537. esp_camera_fb_return(fb);
  538. fb = NULL;
  539. if(!jpeg_converted){
  540. Serial.println("JPEG compression failed");
  541. res = ESP_FAIL;
  542. }
  543. } else {
  544. _jpg_buf_len = fb->len;
  545. _jpg_buf = fb->buf;
  546. }
  547. } else {
  548. image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
  549. Serial.printf("image: w:%d h:%d size:%d %d", fb->width, fb->height, fb->len, sizeof(fb->buf));
  550. if (!image_matrix) {
  551. Serial.println("dl_matrix3du_alloc failed");
  552. res = ESP_FAIL;
  553. } else {
  554. if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)){
  555. Serial.println("fmt2rgb888 failed");
  556. res = ESP_FAIL;
  557. } else {
  558. fr_ready = esp_timer_get_time();
  559. line_array_t *lines = NULL;
  560. if (linedetect) {
  561. // //测试画图时使用
  562. // line_array_t *tempLines = (line_array_t*) malloc(sizeof(line_array_t));
  563. // tempLines->score = 0.66;
  564. // tempLines->len=4;
  565. // line_t *lineArray = (line_t*) malloc(tempLines->len * sizeof(line_t));
  566. // if (lineArray) {
  567. // lineArray[0].star_point[0] = 100;
  568. // lineArray[0].star_point[1] = 100;
  569. // lineArray[0].end_point[0] = 120;
  570. // lineArray[0].end_point[1] = 100;
  571. //
  572. // lineArray[1].star_point[0] = 50;
  573. // lineArray[1].star_point[1] = 50;
  574. // lineArray[1].end_point[0] = 50;
  575. // lineArray[1].end_point[1] = 120;
  576. //
  577. // lineArray[2].star_point[0] = 130;
  578. // lineArray[2].star_point[1] = 130;
  579. // lineArray[2].end_point[0] = 140;
  580. // lineArray[2].end_point[1] = 140;
  581. //
  582. // lineArray[3].star_point[0] = 200;
  583. // lineArray[3].star_point[1] = 200;
  584. // lineArray[3].end_point[0] = 250;
  585. // lineArray[3].end_point[1] = 150;
  586. // }
  587. // tempLines->line = lineArray;
  588. // lines = tempLines;
  589. lines = recoginLines(image_matrix);
  590. }
  591. fr_face = esp_timer_get_time();
  592. fr_recognize = fr_face;
  593. if (lines || fb->format != PIXFORMAT_JPEG) {
  594. if (lines) {
  595. detected = true;
  596. fr_recognize = esp_timer_get_time();
  597. draw_lines(image_matrix, lines);
  598. free(lines->line);
  599. free(lines);
  600. }
  601. if(!fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)){
  602. Serial.println("fmt2jpg failed");
  603. res = ESP_FAIL;
  604. }
  605. esp_camera_fb_return(fb);
  606. fb = NULL;
  607. } else {
  608. _jpg_buf = fb->buf;
  609. _jpg_buf_len = fb->len;
  610. }
  611. fr_encode = esp_timer_get_time();
  612. }
  613. dl_matrix3du_free(image_matrix);
  614. }
  615. }
  616. }
  617. if(res == ESP_OK){
  618. res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
  619. }
  620. if(res == ESP_OK){
  621. size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
  622. res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
  623. }
  624. if(res == ESP_OK){
  625. res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
  626. }
  627. if(fb){
  628. esp_camera_fb_return(fb);
  629. fb = NULL;
  630. _jpg_buf = NULL;
  631. } else if(_jpg_buf){
  632. free(_jpg_buf);
  633. _jpg_buf = NULL;
  634. }
  635. if(res != ESP_OK){
  636. break;
  637. }
  638. int64_t fr_end = esp_timer_get_time();
  639. int64_t ready_time = (fr_ready - fr_start)/1000;
  640. int64_t face_time = (fr_face - fr_ready)/1000;
  641. int64_t recognize_time = (fr_recognize - fr_face)/1000;
  642. int64_t encode_time = (fr_encode - fr_recognize)/1000;
  643. int64_t process_time = (fr_encode - fr_start)/1000;
  644. int64_t frame_time = fr_end - last_frame;
  645. last_frame = fr_end;
  646. frame_time /= 1000;
  647. uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time);
  648. Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps), %u+%u+%u+%u=%u %s%d\n",
  649. (uint32_t)(_jpg_buf_len),
  650. (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time,
  651. avg_frame_time, 1000.0 / avg_frame_time,
  652. (uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time,
  653. (detected)?"DETECTED ":"", face_id
  654. );
  655. }
  656. last_frame = 0;
  657. return res;
  658. }
  659. static esp_err_t cmd_handler(httpd_req_t *req){
  660. char* buf;
  661. size_t buf_len;
  662. char variable[32] = {0,};
  663. char value[32] = {0,};
  664. buf_len = httpd_req_get_url_query_len(req) + 1;
  665. if (buf_len > 1) {
  666. buf = (char*)malloc(buf_len);
  667. if(!buf){
  668. httpd_resp_send_500(req);
  669. return ESP_FAIL;
  670. }
  671. if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
  672. if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK &&
  673. httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) {
  674. } else {
  675. free(buf);
  676. httpd_resp_send_404(req);
  677. return ESP_FAIL;
  678. }
  679. } else {
  680. free(buf);
  681. httpd_resp_send_404(req);
  682. return ESP_FAIL;
  683. }
  684. free(buf);
  685. } else {
  686. httpd_resp_send_404(req);
  687. return ESP_FAIL;
  688. }
  689. int val = atoi(value);
  690. sensor_t * s = esp_camera_sensor_get();
  691. int res = 0;
  692. if(!strcmp(variable, "framesize")) {
  693. if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
  694. }
  695. else if(!strcmp(variable, "quality")) res = s->set_quality(s, val);
  696. else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
  697. else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
  698. else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val);
  699. else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val);
  700. else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val);
  701. else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val);
  702. else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val);
  703. else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val);
  704. else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
  705. else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
  706. else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val);
  707. else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val);
  708. else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val);
  709. else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val);
  710. else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val);
  711. else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val);
  712. else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val);
  713. else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val);
  714. else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val);
  715. else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val);
  716. else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val);
  717. else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val);
  718. else if(!strcmp(variable, "lightbtn")) {
  719. light_flag = val;
  720. digitalWrite(4, light_flag);
  721. Serial.printf("lightbtn:%d", digitalRead(4));
  722. }
  723. else if(!strcmp(variable, "linedetect")) {
  724. linedetect = val;
  725. Serial.printf("linedetect:%d", linedetect);
  726. }
  727. else if(!strcmp(variable, "face_detect")) {
  728. detection_enabled = val;
  729. if(!detection_enabled) {
  730. recognition_enabled = 0;
  731. }
  732. }
  733. else if(!strcmp(variable, "face_enroll")) is_enrolling = val;
  734. else if(!strcmp(variable, "face_recognize")) {
  735. recognition_enabled = val;
  736. if(recognition_enabled){
  737. detection_enabled = val;
  738. }
  739. }
  740. else {
  741. res = -1;
  742. }
  743. if(res){
  744. return httpd_resp_send_500(req);
  745. }
  746. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  747. return httpd_resp_send(req, NULL, 0);
  748. }
  749. static esp_err_t status_handler(httpd_req_t *req){
  750. static char json_response[1024];
  751. sensor_t * s = esp_camera_sensor_get();
  752. char * p = json_response;
  753. *p++ = '{';
  754. p+=sprintf(p, "\"framesize\":%u,", s->status.framesize);
  755. p+=sprintf(p, "\"quality\":%u,", s->status.quality);
  756. p+=sprintf(p, "\"brightness\":%d,", s->status.brightness);
  757. p+=sprintf(p, "\"contrast\":%d,", s->status.contrast);
  758. p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);
  759. p+=sprintf(p, "\"sharpness\":%d,", s->status.sharpness);
  760. p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect);
  761. p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode);
  762. p+=sprintf(p, "\"awb\":%u,", s->status.awb);
  763. p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain);
  764. p+=sprintf(p, "\"aec\":%u,", s->status.aec);
  765. p+=sprintf(p, "\"aec2\":%u,", s->status.aec2);
  766. p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level);
  767. p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value);
  768. p+=sprintf(p, "\"agc\":%u,", s->status.agc);
  769. p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain);
  770. p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling);
  771. p+=sprintf(p, "\"bpc\":%u,", s->status.bpc);
  772. p+=sprintf(p, "\"wpc\":%u,", s->status.wpc);
  773. p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma);
  774. p+=sprintf(p, "\"lenc\":%u,", s->status.lenc);
  775. p+=sprintf(p, "\"vflip\":%u,", s->status.vflip);
  776. p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
  777. p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
  778. p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar);
  779. p+=sprintf(p, "\"face_detect\":%u,", detection_enabled);
  780. p+=sprintf(p, "\"face_enroll\":%u,", is_enrolling);
  781. p+=sprintf(p, "\"face_recognize\":%u", recognition_enabled);
  782. p+=sprintf(p, "\"lightbtn\":%u", light_flag);
  783. p+=sprintf(p, "\"linedetect\":%u", linedetect);
  784. *p++ = '}';
  785. *p++ = 0;
  786. httpd_resp_set_type(req, "application/json");
  787. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  788. return httpd_resp_send(req, json_response, strlen(json_response));
  789. }
  790. static esp_err_t index_handler(httpd_req_t *req){
  791. httpd_resp_set_type(req, "text/html");
  792. httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
  793. sensor_t * s = esp_camera_sensor_get();
  794. if (s->id.PID == OV3660_PID) {
  795. return httpd_resp_send(req, (const char *)index_ov3660_html_gz, index_ov3660_html_gz_len);
  796. }
  797. return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len);
  798. }
  799. static esp_err_t query_handler(httpd_req_t *req){
  800. static char response[1024];
  801. char * p = response;
  802. int var = 10086;
  803. Serial.printf("var:%d", var);
  804. p+=sprintf(p, "%d", var);
  805. httpd_resp_set_type(req, "text/html");
  806. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  807. return httpd_resp_send(req, response, strlen(response));
  808. }
  809. void startCameraServer(){
  810. httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  811. httpd_uri_t index_uri = {
  812. .uri = "/",
  813. .method = HTTP_GET,
  814. .handler = index_handler,
  815. .user_ctx = NULL
  816. };
  817. httpd_uri_t queryvar_uri = {
  818. .uri = "/query",
  819. .method = HTTP_GET,
  820. .handler = query_handler,
  821. .user_ctx = NULL
  822. };
  823. httpd_uri_t status_uri = {
  824. .uri = "/status",
  825. .method = HTTP_GET,
  826. .handler = status_handler,
  827. .user_ctx = NULL
  828. };
  829. httpd_uri_t cmd_uri = {
  830. .uri = "/control",
  831. .method = HTTP_GET,
  832. .handler = cmd_handler,
  833. .user_ctx = NULL
  834. };
  835. httpd_uri_t capture_uri = {
  836. .uri = "/capture",
  837. .method = HTTP_GET,
  838. .handler = capture_handler,
  839. .user_ctx = NULL
  840. };
  841. httpd_uri_t stream_uri = {
  842. .uri = "/stream",
  843. .method = HTTP_GET,
  844. .handler = stream_handler,
  845. .user_ctx = NULL
  846. };
  847. ra_filter_init(&ra_filter, 20);
  848. mtmn_config.type = FAST;
  849. mtmn_config.min_face = 80;
  850. mtmn_config.pyramid = 0.707;
  851. mtmn_config.pyramid_times = 4;
  852. mtmn_config.p_threshold.score = 0.6;
  853. mtmn_config.p_threshold.nms = 0.7;
  854. mtmn_config.p_threshold.candidate_number = 20;
  855. mtmn_config.r_threshold.score = 0.7;
  856. mtmn_config.r_threshold.nms = 0.7;
  857. mtmn_config.r_threshold.candidate_number = 10;
  858. mtmn_config.o_threshold.score = 0.7;
  859. mtmn_config.o_threshold.nms = 0.7;
  860. mtmn_config.o_threshold.candidate_number = 1;
  861. face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);
  862. Serial.printf("Starting web server on port: '%d'\n", config.server_port);
  863. if (httpd_start(&camera_httpd, &config) == ESP_OK) {
  864. httpd_register_uri_handler(camera_httpd, &index_uri);
  865. httpd_register_uri_handler(camera_httpd, &cmd_uri);
  866. httpd_register_uri_handler(camera_httpd, &status_uri);
  867. httpd_register_uri_handler(camera_httpd, &capture_uri);
  868. httpd_register_uri_handler(camera_httpd, &queryvar_uri);
  869. }
  870. config.server_port += 1;
  871. config.ctrl_port += 1;
  872. Serial.printf("Starting stream server on port: '%d'\n", config.server_port);
  873. if (httpd_start(&stream_httpd, &config) == ESP_OK) {
  874. httpd_register_uri_handler(stream_httpd, &stream_uri);
  875. }
  876. }

总结

        基本上自动刷新并回显传感器就这么多内容了,主要是要让xmlhttp这个脚本自动生成一个querry请求去获取数据。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/216284
推荐阅读
相关标签
  

闽ICP备14008679号