safwan092

ESp32 Cam with AI Using Google Teachable Machine and DeepSeek Generated Code

Jan 14th, 2025
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 38.33 KB | None | 0 0
  1. // Replace with your desired hostname
  2. const char* hostnamee = "esp32";
  3.  
  4. const char* ssid = "Project"; //your network SSID
  5. const char* password = "12345678"; //your network password
  6.  
  7. const char* apssid = "ESP32-CAM";
  8. const char* appassword = "12345678";
  9.  
  10. #include <ESPmDNS.h>
  11. #include <WiFi.h>
  12. #include <esp32-hal-ledc.h>
  13. #include "soc/soc.h"
  14. #include "soc/rtc_cntl_reg.h"
  15. #include "esp_camera.h"
  16. #include "esp_http_server.h"
  17. #include "img_converters.h"
  18.  
  19. String Feedback = "";
  20.  
  21. String Command = "";
  22. String cmd = "";
  23. String P1 = "";
  24. String P2 = "";
  25. String P3 = "";
  26. String P4 = "";
  27. String P5 = "";
  28. String P6 = "";
  29. String P7 = "";
  30. String P8 = "";
  31. String P9 = "";
  32.  
  33.  
  34. byte ReceiveState = 0;
  35. byte cmdState = 1;
  36. byte strState = 1;
  37. byte questionstate = 0;
  38. byte equalstate = 0;
  39. byte semicolonstate = 0;
  40.  
  41. typedef struct {
  42. httpd_req_t *req;
  43. size_t len;
  44. } jpg_chunking_t;
  45.  
  46. #define PART_BOUNDARY "123456789000000000000987654321"
  47. static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
  48. static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
  49. static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
  50.  
  51. httpd_handle_t stream_httpd = NULL;
  52. httpd_handle_t camera_httpd = NULL;
  53.  
  54.  
  55. #define PWDN_GPIO_NUM 32
  56. #define RESET_GPIO_NUM -1
  57. #define XCLK_GPIO_NUM 0
  58. #define SIOD_GPIO_NUM 26
  59. #define SIOC_GPIO_NUM 27
  60.  
  61. #define Y9_GPIO_NUM 35
  62. #define Y8_GPIO_NUM 34
  63. #define Y7_GPIO_NUM 39
  64. #define Y6_GPIO_NUM 36
  65. #define Y5_GPIO_NUM 21
  66. #define Y4_GPIO_NUM 19
  67. #define Y3_GPIO_NUM 18
  68. #define Y2_GPIO_NUM 5
  69. #define VSYNC_GPIO_NUM 25
  70. #define HREF_GPIO_NUM 23
  71. #define PCLK_GPIO_NUM 22
  72.  
  73. void setup() {
  74. WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
  75.  
  76. Serial.begin(115200);
  77. Serial.setDebugOutput(true);
  78. Serial.println();
  79.  
  80. camera_config_t config;
  81. config.ledc_channel = LEDC_CHANNEL_0;
  82. config.ledc_timer = LEDC_TIMER_0;
  83. config.pin_d0 = Y2_GPIO_NUM;
  84. config.pin_d1 = Y3_GPIO_NUM;
  85. config.pin_d2 = Y4_GPIO_NUM;
  86. config.pin_d3 = Y5_GPIO_NUM;
  87. config.pin_d4 = Y6_GPIO_NUM;
  88. config.pin_d5 = Y7_GPIO_NUM;
  89. config.pin_d6 = Y8_GPIO_NUM;
  90. config.pin_d7 = Y9_GPIO_NUM;
  91. config.pin_xclk = XCLK_GPIO_NUM;
  92. config.pin_pclk = PCLK_GPIO_NUM;
  93. config.pin_vsync = VSYNC_GPIO_NUM;
  94. config.pin_href = HREF_GPIO_NUM;
  95. config.pin_sscb_sda = SIOD_GPIO_NUM;
  96. config.pin_sscb_scl = SIOC_GPIO_NUM;
  97. config.pin_pwdn = PWDN_GPIO_NUM;
  98. config.pin_reset = RESET_GPIO_NUM;
  99. config.xclk_freq_hz = 20000000;
  100. config.pixel_format = PIXFORMAT_JPEG;
  101.  
  102. if (psramFound()) {
  103. config.frame_size = FRAMESIZE_UXGA;
  104. config.jpeg_quality = 10;
  105. config.fb_count = 2;
  106. } else {
  107. config.frame_size = FRAMESIZE_SVGA;
  108. config.jpeg_quality = 12;
  109. config.fb_count = 1;
  110. }
  111.  
  112. esp_err_t err = esp_camera_init(&config);
  113. if (err != ESP_OK) {
  114. Serial.printf("Camera init failed with error 0x%x", err);
  115. ESP.restart();
  116. }
  117.  
  118. sensor_t * s = esp_camera_sensor_get();
  119. // initial sensors are flipped vertically and colors are a bit saturated
  120. if (s->id.PID == OV3660_PID) {
  121. s->set_vflip(s, 1); // flip it back
  122. s->set_brightness(s, 1); // up the brightness just a bit
  123. s->set_saturation(s, -2); // lower the saturation
  124. }
  125. // drop down frame size for higher initial frame rate
  126. s->set_framesize(s, FRAMESIZE_QVGA);
  127.  
  128.  
  129. ledcAttachPin(4, 4);
  130. ledcSetup(4, 5000, 8);
  131.  
  132. WiFi.mode(WIFI_AP_STA);
  133.  
  134. for (int i = 0; i < 2; i++) {
  135. WiFi.begin(ssid, password);
  136.  
  137. delay(1000);
  138. Serial.println("");
  139. Serial.print("Connecting to ");
  140. Serial.println(ssid);
  141.  
  142. long int StartTime = millis();
  143. while (WiFi.status() != WL_CONNECTED) {
  144. delay(500);
  145. if ((StartTime + 5000) < millis()) break;
  146. }
  147.  
  148. if (WiFi.status() == WL_CONNECTED) {
  149. WiFi.softAP((WiFi.localIP().toString() + "_" + (String)apssid).c_str(), appassword);
  150. Serial.println("");
  151. Serial.println("STAIP address: ");
  152. Serial.println(WiFi.localIP());
  153. Serial.println("");
  154.  
  155. for (int i = 0; i < 5; i++) {
  156. ledcWrite(4, 10);
  157. delay(200);
  158. ledcWrite(4, 0);
  159. delay(200);
  160. }
  161. break;
  162. }
  163. }
  164.  
  165. if (WiFi.status() != WL_CONNECTED) {
  166. WiFi.softAP((WiFi.softAPIP().toString() + "_" + (String)apssid).c_str(), appassword);
  167.  
  168. for (int i = 0; i < 2; i++) {
  169. ledcWrite(4, 10);
  170. delay(1000);
  171. ledcWrite(4, 0);
  172. delay(1000);
  173. }
  174. }
  175.  
  176. Serial.println("");
  177. Serial.println("APIP address: ");
  178. Serial.println(WiFi.softAPIP());
  179. Serial.println("");
  180.  
  181. //***********************************************************************SA
  182. // Start mDNS service
  183. if (!MDNS.begin(hostnamee)) {
  184. Serial.println("Error setting up MDNS responder!");
  185. while (1) {
  186. delay(1000);
  187. }
  188. }
  189. Serial.println("mDNS responder started");
  190. //***********************************************************************SA
  191.  
  192. startCameraServer();
  193.  
  194. //***********************************************************************SA
  195. // Add service to MDNS
  196. MDNS.addService("http", "tcp", 80);
  197.  
  198. Serial.print("Camera Ready! Use 'http://");
  199. Serial.print(hostnamee);
  200. Serial.println(".local' to connect");
  201. //***********************************************************************SA
  202.  
  203. pinMode(4, OUTPUT);
  204. digitalWrite(4, LOW);
  205.  
  206.  
  207. }
  208.  
  209. void loop() {
  210.  
  211. }
  212.  
  213. static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len) {
  214. jpg_chunking_t *j = (jpg_chunking_t *)arg;
  215. if (!index) {
  216. j->len = 0;
  217. }
  218. if (httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) {
  219. return 0;
  220. }
  221. j->len += len;
  222. return len;
  223. }
  224.  
  225. static esp_err_t capture_handler(httpd_req_t *req) {
  226. camera_fb_t * fb = NULL;
  227. esp_err_t res = ESP_OK;
  228.  
  229. fb = esp_camera_fb_get();
  230. if (!fb) {
  231. Serial.println("Camera capture failed");
  232. httpd_resp_send_500(req);
  233. return ESP_FAIL;
  234. }
  235.  
  236. httpd_resp_set_type(req, "image/jpeg");
  237. httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
  238. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  239.  
  240. size_t fb_len = 0;
  241. if (fb->format == PIXFORMAT_JPEG) {
  242. fb_len = fb->len;
  243. res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
  244. } else {
  245. jpg_chunking_t jchunk = {req, 0};
  246. res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk) ? ESP_OK : ESP_FAIL;
  247. httpd_resp_send_chunk(req, NULL, 0);
  248. fb_len = jchunk.len;
  249. }
  250. esp_camera_fb_return(fb);
  251. return res;
  252. }
  253.  
  254. static esp_err_t stream_handler(httpd_req_t *req) {
  255. camera_fb_t * fb = NULL;
  256. esp_err_t res = ESP_OK;
  257. size_t _jpg_buf_len = 0;
  258. uint8_t * _jpg_buf = NULL;
  259. char * part_buf[64];
  260.  
  261. res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  262. if (res != ESP_OK) {
  263. return res;
  264. }
  265.  
  266. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  267.  
  268. while (true) {
  269. fb = esp_camera_fb_get();
  270. if (!fb) {
  271. Serial.println("Camera capture failed");
  272. res = ESP_FAIL;
  273. } else {
  274. if (fb->format != PIXFORMAT_JPEG) {
  275. bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
  276. esp_camera_fb_return(fb);
  277. fb = NULL;
  278. if (!jpeg_converted) {
  279. Serial.println("JPEG compression failed");
  280. res = ESP_FAIL;
  281. }
  282. } else {
  283. _jpg_buf_len = fb->len;
  284. _jpg_buf = fb->buf;
  285. }
  286. }
  287.  
  288. if (res == ESP_OK) {
  289. res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
  290. }
  291. if (res == ESP_OK) {
  292. res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
  293. }
  294. if (res == ESP_OK) {
  295. size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
  296. res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
  297. }
  298. if (fb) {
  299. esp_camera_fb_return(fb);
  300. fb = NULL;
  301. _jpg_buf = NULL;
  302. } else if (_jpg_buf) {
  303. free(_jpg_buf);
  304. _jpg_buf = NULL;
  305. }
  306. if (res != ESP_OK) {
  307. break;
  308. }
  309. }
  310.  
  311. return res;
  312. }
  313.  
  314.  
  315. static esp_err_t cmd_handler(httpd_req_t *req) {
  316. char* buf;
  317. size_t buf_len;
  318. char variable[128] = {0,};
  319. char value[128] = {0,};
  320. String myCmd = "";
  321.  
  322. buf_len = httpd_req_get_url_query_len(req) + 1;
  323. if (buf_len > 1) {
  324. buf = (char*)malloc(buf_len);
  325. if (!buf) {
  326. httpd_resp_send_500(req);
  327. return ESP_FAIL;
  328. }
  329. if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
  330. if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK &&
  331. httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) {
  332. }
  333. else {
  334. myCmd = String(buf);
  335. }
  336. }
  337. free(buf);
  338. } else {
  339. httpd_resp_send_404(req);
  340. return ESP_FAIL;
  341. }
  342.  
  343. Feedback = ""; Command = ""; cmd = ""; P1 = ""; P2 = ""; P3 = ""; P4 = ""; P5 = ""; P6 = ""; P7 = ""; P8 = ""; P9 = "";
  344. ReceiveState = 0, cmdState = 1, strState = 1, questionstate = 0, equalstate = 0, semicolonstate = 0;
  345. if (myCmd.length() > 0) {
  346. myCmd = "?" + myCmd;
  347. for (int i = 0; i < myCmd.length(); i++) {
  348. getCommand(char(myCmd.charAt(i)));
  349. }
  350. }
  351.  
  352. if (cmd.length() > 0) {
  353. Serial.println("");
  354. Serial.println("cmd= " + cmd + " ,P1= " + P1 + " ,P2= " + P2 + " ,P3= " + P3 + " ,P4= " + P4 + " ,P5= " + P5 + " ,P6= " + P6 + " ,P7= " + P7 + " ,P8= " + P8 + " ,P9= " + P9);
  355. Serial.println("");
  356.  
  357. if (cmd == "your cmd") {
  358.  
  359. }
  360. else if (cmd == "ip") {
  361. Feedback = "AP IP: " + WiFi.softAPIP().toString();
  362. Feedback += "<br>";
  363. Feedback += "STA IP: " + WiFi.localIP().toString();
  364. }
  365. else if (cmd == "mac") {
  366. Feedback = "STA MAC: " + WiFi.macAddress();
  367. }
  368. else if (cmd == "restart") {
  369. ESP.restart();
  370. }
  371. else if (cmd == "digitalwrite") {
  372. ledcDetachPin(P1.toInt());
  373. pinMode(P1.toInt(), OUTPUT);
  374. digitalWrite(P1.toInt(), P2.toInt());
  375. }
  376. else if (cmd == "digitalread") {
  377. Feedback = String(digitalRead(P1.toInt()));
  378. }
  379. else if (cmd == "analogwrite") {
  380. if (P1 == "4") {
  381. ledcAttachPin(4, 4);
  382. ledcSetup(4, 5000, 8);
  383. ledcWrite(4, P2.toInt());
  384. }
  385. else {
  386. ledcAttachPin(P1.toInt(), 9);
  387. ledcSetup(9, 5000, 8);
  388. ledcWrite(9, P2.toInt());
  389. }
  390. }
  391. else if (cmd == "analogread") {
  392. Feedback = String(analogRead(P1.toInt()));
  393. }
  394. else if (cmd == "touchread") {
  395. Feedback = String(touchRead(P1.toInt()));
  396. }
  397. else if (cmd == "resetwifi") {
  398. for (int i = 0; i < 2; i++) {
  399. WiFi.begin(P1.c_str(), P2.c_str());
  400. Serial.print("Connecting to ");
  401. Serial.println(P1);
  402. long int StartTime = millis();
  403. while (WiFi.status() != WL_CONNECTED) {
  404. delay(500);
  405. if ((StartTime + 5000) < millis()) break;
  406. }
  407. Serial.println("");
  408. Serial.println("STAIP: " + WiFi.localIP().toString());
  409. Feedback = "STAIP: " + WiFi.localIP().toString();
  410.  
  411. if (WiFi.status() == WL_CONNECTED) {
  412. WiFi.softAP((WiFi.localIP().toString() + "_" + P1).c_str(), P2.c_str());
  413. for (int i = 0; i < 2; i++) {
  414. ledcWrite(4, 10);
  415. delay(300);
  416. ledcWrite(4, 0);
  417. delay(300);
  418. }
  419. break;
  420. }
  421. }
  422. }
  423. else if (cmd == "flash") {
  424. ledcAttachPin(4, 4);
  425. ledcSetup(4, 5000, 8);
  426. int val = P1.toInt();
  427. ledcWrite(4, val);
  428. }
  429. else if (cmd == "serial") {
  430. if (P1 != ""&P1 != "stop") Serial.println(P1);
  431. if (P2 != ""&P2 != "stop") Serial.println(P2);
  432. Serial.println();
  433. }
  434. else {
  435. Feedback = "Command is not defined";
  436. }
  437.  
  438. if (Feedback == "") Feedback = Command;
  439.  
  440. const char *resp = Feedback.c_str();
  441. httpd_resp_set_type(req, "text/html");
  442. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  443. return httpd_resp_send(req, resp, strlen(resp));
  444. }
  445. else {
  446. int val = atoi(value);
  447. sensor_t * s = esp_camera_sensor_get();
  448. int res = 0;
  449.  
  450. if (!strcmp(variable, "framesize")) {
  451. if (s->pixformat == PIXFORMAT_JPEG)
  452. res = s->set_framesize(s, (framesize_t)val);
  453. }
  454. else if (!strcmp(variable, "quality")) res = s->set_quality(s, val);
  455. else if (!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
  456. else if (!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
  457. else if (!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
  458. else if (!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
  459. else if (!strcmp(variable, "flash")) {
  460. ledcAttachPin(4, 4);
  461. ledcSetup(4, 5000, 8);
  462. ledcWrite(4, val);
  463. }
  464. else {
  465. res = -1;
  466. }
  467.  
  468. if (res) {
  469. return httpd_resp_send_500(req);
  470. }
  471.  
  472. if (buf) {
  473. Feedback = String(buf);
  474. const char *resp = Feedback.c_str();
  475. httpd_resp_set_type(req, "text/html");
  476. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  477. return httpd_resp_send(req, resp, strlen(resp));
  478. }
  479. else {
  480. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  481. return httpd_resp_send(req, NULL, 0);
  482. }
  483. }
  484. }
  485.  
  486. static esp_err_t status_handler(httpd_req_t *req) {
  487. static char json_response[1024];
  488.  
  489. sensor_t * s = esp_camera_sensor_get();
  490. char * p = json_response;
  491. *p++ = '{';
  492. p += sprintf(p, "\"flash\":%d,", 0);
  493. p += sprintf(p, "\"framesize\":%u,", s->status.framesize);
  494. p += sprintf(p, "\"quality\":%u,", s->status.quality);
  495. p += sprintf(p, "\"brightness\":%d,", s->status.brightness);
  496. p += sprintf(p, "\"contrast\":%d,", s->status.contrast);
  497. p += sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
  498. p += sprintf(p, "\"vflip\":%u", s->status.vflip);
  499. *p++ = '}';
  500. *p++ = 0;
  501. httpd_resp_set_type(req, "application/json");
  502. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  503. return httpd_resp_send(req, json_response, strlen(json_response));
  504. }
  505.  
  506.  
  507.  
  508.  
  509. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  510. static const char PROGMEM INDEX_HTML[] = R"rawliteral(
  511. <!doctype html>
  512. <html>
  513. <head>
  514. <meta charset="utf-8">
  515. <meta name="viewport" content="width=device-width,initial-scale=1">
  516. <meta http-equiv="Access-Control-Allow-Headers" content="Origin, X-Requested-With, Content-Type, Accept">
  517. <meta http-equiv="Access-Control-Allow-Methods" content="GET,POST,PUT,DELETE,OPTIONS">
  518. <meta http-equiv="Access-Control-Allow-Origin" content="*">
  519. <title>Detect Objects</title>
  520. <style>
  521. 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:flex;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:0;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;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:none}.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}
  522.  
  523. /* Enhanced CSS for result display */
  524. #result {
  525. background: #2c2c2c;
  526. padding: 15px;
  527. border-radius: 8px;
  528. margin-top: 20px;
  529. font-size: 14px;
  530. line-height: 1.6;
  531. }
  532.  
  533. .result-item {
  534. margin-bottom: 10px;
  535. }
  536.  
  537. .result-label {
  538. display: block;
  539. margin-bottom: 5px;
  540. font-weight: bold;
  541. color: #ff3034;
  542. }
  543.  
  544. .progress-bar {
  545. width: 100%;
  546. height: 20px;
  547. background: #444;
  548. border-radius: 10px;
  549. overflow: hidden;
  550. position: relative;
  551. }
  552.  
  553. .progress-bar-fill {
  554. height: 100%;
  555. background: #ff3034;
  556. border-radius: 10px;
  557. transition: width 0.3s ease;
  558. }
  559.  
  560. .progress-bar-text {
  561. position: absolute;
  562. top: 50%;
  563. left: 10px;
  564. transform: translateY(-50%);
  565. color: #fff;
  566. font-size: 12px;
  567. }
  568.  
  569. .result-final {
  570. margin-top: 15px;
  571. padding: 10px;
  572. background: #1e1e1e;
  573. border-radius: 5px;
  574. text-align: center;
  575. font-size: 16px;
  576. }
  577.  
  578. .result-final-content {
  579. display: flex;
  580. flex-direction: column;
  581. align-items: center;
  582. justify-content: center;
  583. gap: 10px;
  584. }
  585.  
  586. .result-final-icon {
  587. font-size: 40px; /* Larger icon */
  588. }
  589.  
  590. .result-final-percentage {
  591. font-size: 24px; /* Larger percentage */
  592. font-weight: bold;
  593. color: #ff3034;
  594. }
  595.  
  596. /* Icons for trash types */
  597. .icon-empty::before {
  598. content: "πŸ—‘οΈ"; /* Empty trash icon */
  599. }
  600.  
  601. .icon-paper::before {
  602. content: "πŸ“„"; /* Paper trash icon */
  603. }
  604.  
  605. .icon-bottle::before {
  606. content: "🍢"; /* Bottle trash icon */
  607. }
  608. </style>
  609. <script src="https:\/\/ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
  610. <script src="https:\/\/cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script>
  611. <script src="https:\/\/cdn.jsdelivr.net/npm/@teachablemachine/[email protected]/dist/teachablemachine-image.min.js"></script>
  612. <script src="https:\/\/cdn.jsdelivr.net/npm/@teachablemachine/[email protected]/dist/teachablemachine-pose.min.js"></script>
  613. </head>
  614. <body>
  615. <section class="main">
  616. <figure>
  617. <div id="stream-container" class="image-container hidden">
  618. <div class="close" id="close-stream" style="display:none">Γ—</div>
  619. <img id="stream" src="" style="display:none" crossorigin="anonymous">
  620. <canvas id="canvas" width="0" height="0"></canvas>
  621. </div>
  622. </figure>
  623. <section id="buttons">
  624. <table>
  625. <tr><td><button id="restart" onclick="try{fetch(document.location.origin+'/control?restart');}catch(e){}">Restart</button></td><td><button id="get-still" style="display:none">Get Still</button></td><td><button id="toggle-stream" style="display:none"></td></tr>
  626. </table>
  627. </section>
  628. <div id="logo">
  629. <label for="nav-toggle-cb" id="nav-toggle">&#9776;&nbsp;&nbsp;Toggle settings</label>
  630. </div>
  631. <div id="content">
  632. <div id="sidebar">
  633. <input type="checkbox" id="nav-toggle-cb">
  634. <nav id="menu">
  635. <div class="input-group">
  636. <label for="kind">Kind</label>
  637. <select id="kind">
  638. <option value="image">image</option>
  639. <option value="pose">pose</option>
  640. </select>
  641. </div>
  642. <div class="input-group">
  643. <label style="visibility: hidden;" for="modelPath">Model Path</label>
  644. <input type="text" style="visibility: hidden;" id="modelPath" value="https:\/\/teachablemachine.withgoogle.com/models/hCGSo2WQw/">
  645. </div>
  646. <div class="input-group">
  647. <label for="btnModel"></label>
  648. <button type="button" id="btnModel" onclick="LoadModel();">Start Recognition</button>
  649. </div>
  650. <div class="input-group" id="flash-group">
  651. <label for="flash">Flash</label>
  652. <div class="range-min">0</div>
  653. <input type="range" id="flash" min="0" max="255" value="0" class="default-action">
  654. <div class="range-max">255</div>
  655. </div>
  656. <div class="input-group" id="framesize-group">
  657. <label for="framesize">Resolution</label>
  658. <select id="framesize" class="default-action">
  659. <option value="10">UXGA(1600x1200)</option>
  660. <option value="9">SXGA(1280x1024)</option>
  661. <option value="8">XGA(1024x768)</option>
  662. <option value="7">SVGA(800x600)</option>
  663. <option value="6">VGA(640x480)</option>
  664. <option value="5" selected="selected">CIF(400x296)</option>
  665. <option value="4">QVGA(320x240)</option>
  666. <option value="3">HQVGA(240x176)</option>
  667. <option value="0">QQVGA(160x120)</option>
  668. </select>
  669. </div>
  670. <div class="input-group" id="quality-group">
  671. <label for="quality">Quality</label>
  672. <div class="range-min">10</div>
  673. <input type="range" id="quality" min="10" max="63" value="10" class="default-action">
  674. <div class="range-max">63</div>
  675. </div>
  676. <div class="input-group" id="brightness-group">
  677. <label for="brightness">Brightness</label>
  678. <div class="range-min">-2</div>
  679. <input type="range" id="brightness" min="-2" max="2" value="0" class="default-action">
  680. <div class="range-max">2</div>
  681. </div>
  682. <div class="input-group" id="contrast-group">
  683. <label for="contrast">Contrast</label>
  684. <div class="range-min">-2</div>
  685. <input type="range" id="contrast" min="-2" max="2" value="0" class="default-action">
  686. <div class="range-max">2</div>
  687. </div>
  688. <div class="input-group" id="hmirror-group">
  689. <label for="hmirror">H-Mirror</label>
  690. <div class="switch">
  691. <input id="hmirror" type="checkbox" class="default-action" checked="checked">
  692. <label class="slider" for="hmirror"></label>
  693. </div>
  694. </div>
  695. <div class="input-group" id="vflip-group">
  696. <label for="vflip">V-Flip</label>
  697. <div class="switch">
  698. <input id="vflip" type="checkbox" class="default-action" checked="checked">
  699. <label class="slider" for="vflip"></label>
  700. </div>
  701. </div>
  702. </nav>
  703. </div>
  704. </div>
  705. </section>
  706. <br>
  707. <div id="result"><div>
  708.  
  709. <script>
  710. document.addEventListener('DOMContentLoaded', function (event) {
  711. var baseHost = document.location.origin
  712. var streamUrl = baseHost + ':81'
  713. const hide = el => {
  714. el.classList.add('hidden')
  715. }
  716. const show = el => {
  717. el.classList.remove('hidden')
  718. }
  719. const disable = el => {
  720. el.classList.add('disabled')
  721. el.disabled = true
  722. }
  723. const enable = el => {
  724. el.classList.remove('disabled')
  725. el.disabled = false
  726. }
  727. const updateValue = (el, value, updateRemote) => {
  728. updateRemote = updateRemote == null ? true : updateRemote
  729. let initialValue
  730. if (el.type === 'checkbox') {
  731. initialValue = el.checked
  732. value = !!value
  733. el.checked = value
  734. } else {
  735. initialValue = el.value
  736. el.value = value
  737. }
  738. if (updateRemote && initialValue !== value) {
  739. updateConfig(el);
  740. }
  741. }
  742. function updateConfig (el) {
  743. let value
  744. switch (el.type) {
  745. case 'checkbox':
  746. value = el.checked ? 1 : 0
  747. break
  748. case 'range':
  749. case 'select-one':
  750. value = el.value
  751. break
  752. case 'button':
  753. case 'submit':
  754. value = '1'
  755. break
  756. default:
  757. return
  758. }
  759. const query = `${baseHost}/control?var=${el.id}&val=${value}`
  760. fetch(query)
  761. .then(response => {
  762. console.log(`request to ${query} finished, status: ${response.status}`)
  763. })
  764. }
  765. document
  766. .querySelectorAll('.close')
  767. .forEach(el => {
  768. el.onclick = () => {
  769. hide(el.parentNode)
  770. }
  771. })
  772. // read initial values
  773. fetch(`${baseHost}/status`)
  774. .then(function (response) {
  775. return response.json()
  776. })
  777. .then(function (state) {
  778. document
  779. .querySelectorAll('.default-action')
  780. .forEach(el => {
  781. updateValue(el, state[el.id], false)
  782. })
  783. })
  784. const view = document.getElementById('stream')
  785. const viewContainer = document.getElementById('stream-container')
  786. const stillButton = document.getElementById('get-still')
  787. const streamButton = document.getElementById('toggle-stream')
  788. const closeButton = document.getElementById('close-stream')
  789. const stopStream = () => {
  790. //window.stop();
  791. view.src="";
  792. streamButton.innerHTML = 'Start Stream'
  793. }
  794. const startStream = () => {
  795. view.src = `${streamUrl}/stream`
  796. show(viewContainer)
  797. streamButton.innerHTML = 'Stop Stream'
  798. }
  799. // Attach actions to buttons
  800. stillButton.onclick = () => {
  801. stopStream()
  802. try{
  803. view.src = `${baseHost}/capture?_cb=${Date.now()}`
  804. }
  805. catch(e) {
  806. view.src = `${baseHost}/capture?_cb=${Date.now()}`
  807. }
  808. show(viewContainer)
  809. }
  810. closeButton.onclick = () => {
  811. stopStream()
  812. hide(viewContainer)
  813. }
  814. streamButton.onclick = () => {
  815. const streamEnabled = streamButton.innerHTML === 'Stop Stream'
  816. if (streamEnabled) {
  817. stopStream()
  818. } else {
  819. startStream()
  820. }
  821. }
  822. // Attach default on change action
  823. document
  824. .querySelectorAll('.default-action')
  825. .forEach(el => {
  826. el.onchange = () => updateConfig(el)
  827. })
  828. })
  829. </script>
  830.  
  831. <script>
  832. var getStill = document.getElementById('get-still');
  833. var ShowImage = document.getElementById('stream');
  834. var canvas = document.getElementById("canvas");
  835. var context = canvas.getContext("2d");
  836. var modelPath = document.getElementById('modelPath');
  837. var result = document.getElementById('result');
  838. var kind = document.getElementById('kind');
  839. let Model;
  840.  
  841. async function LoadModel() {
  842. if (modelPath.value=="") {
  843. result.innerHTML = "Please input model path.";
  844. return;
  845. }
  846.  
  847. result.innerHTML = "Please wait for loading model.";
  848.  
  849. const URL = modelPath.value;
  850. const modelURL = URL + "model.json";
  851. const metadataURL = URL + "metadata.json";
  852. if (kind.value=="image") {
  853. Model = await tmImage.load(modelURL, metadataURL);
  854. }
  855. else if (kind.value=="pose") {
  856. Model = await tmPose.load(modelURL, metadataURL);
  857. }
  858. maxPredictions = Model.getTotalClasses();
  859. result.innerHTML = "";
  860.  
  861. getStill.style.display = "block";
  862. getStill.click();
  863. }
  864.  
  865. async function predict() {
  866. var data = "";
  867. var maxClassName = "";
  868. var maxProbability = "";
  869.  
  870. canvas.setAttribute("width", ShowImage.width);
  871. canvas.setAttribute("height", ShowImage.height);
  872. context.drawImage(ShowImage, 0, 0, ShowImage.width, ShowImage.height);
  873.  
  874. if (kind.value=="image")
  875. var prediction = await Model.predict(canvas);
  876. else if (kind.value=="pose") {
  877. var { pose, posenetOutput } = await Model.estimatePose(canvas);
  878. var prediction = await Model.predict(posenetOutput);
  879. }
  880.  
  881. if (maxPredictions>0) {
  882. for (let i = 0; i < maxPredictions; i++) {
  883. const className = prediction[i].className;
  884. const probability = prediction[i].probability.toFixed(2);
  885. const percentage = (prediction[i].probability * 100).toFixed(2);
  886.  
  887. data += `
  888. <div class="result-item">
  889. <div class="result-label">${className}</div>
  890. <div class="progress-bar">
  891. <div class="progress-bar-fill" style="width: ${percentage}%"></div>
  892. <div class="progress-bar-text">${percentage}%</div>
  893. </div>
  894. </div>
  895. `;
  896.  
  897. if (i == 0 || prediction[i].probability > maxProbability) {
  898. maxClassName = className;
  899. maxProbability = probability;
  900. }
  901. }
  902.  
  903. // Determine the icon based on the maxClassName
  904. let iconClass = "icon-empty"; // Default icon
  905. if (maxClassName.toLowerCase().includes("paper")) {
  906. iconClass = "icon-paper";
  907. } else if (maxClassName.toLowerCase().includes("bottle")) {
  908. iconClass = "icon-bottle";
  909. }
  910.  
  911. result.innerHTML = data;
  912. result.innerHTML += `
  913. <div class="result-final">
  914. <div class="result-final-content">
  915. <div class="result-final-icon ${iconClass}"></div>
  916. <div class="result-final-percentage">${(maxProbability * 100).toFixed(2)}%</div>
  917. </div>
  918. </div>
  919. `;
  920.  
  921. $.ajax({url: document.location.origin+'/control?serial='+maxClassName+";"+maxProbability+';stop', async: false});
  922. }
  923. else
  924. result.innerHTML = "Unrecognizable";
  925.  
  926. getStill.click();
  927. }
  928.  
  929. ShowImage.onload = function (event) {
  930. if (Model) {
  931. try {
  932. document.createEvent("TouchEvent");
  933. setTimeout(function(){predict();},250);
  934. }
  935. catch(e) {
  936. predict();
  937. }
  938. }
  939. }
  940. </script>
  941. </body>
  942. </html>)rawliteral";
  943. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  944.  
  945.  
  946.  
  947. static esp_err_t index_handler(httpd_req_t *req) {
  948. httpd_resp_set_type(req, "text/html");
  949. return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
  950. }
  951.  
  952. void startCameraServer() {
  953. httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  954.  
  955. httpd_uri_t index_uri = {
  956. .uri = "/",
  957. .method = HTTP_GET,
  958. .handler = index_handler,
  959. .user_ctx = NULL
  960. };
  961.  
  962. httpd_uri_t status_uri = {
  963. .uri = "/status",
  964. .method = HTTP_GET,
  965. .handler = status_handler,
  966. .user_ctx = NULL
  967. };
  968.  
  969. httpd_uri_t cmd_uri = {
  970. .uri = "/control",
  971. .method = HTTP_GET,
  972. .handler = cmd_handler,
  973. .user_ctx = NULL
  974. };
  975.  
  976. httpd_uri_t capture_uri = {
  977. .uri = "/capture",
  978. .method = HTTP_GET,
  979. .handler = capture_handler,
  980. .user_ctx = NULL
  981. };
  982.  
  983. httpd_uri_t stream_uri = {
  984. .uri = "/stream",
  985. .method = HTTP_GET,
  986. .handler = stream_handler,
  987. .user_ctx = NULL
  988. };
  989.  
  990. Serial.printf("Starting web server on port: '%d'\n", config.server_port); //Server Port
  991. if (httpd_start(&camera_httpd, &config) == ESP_OK) {
  992. httpd_register_uri_handler(camera_httpd, &index_uri);
  993. httpd_register_uri_handler(camera_httpd, &cmd_uri);
  994. httpd_register_uri_handler(camera_httpd, &status_uri);
  995. httpd_register_uri_handler(camera_httpd, &capture_uri);
  996. }
  997.  
  998. config.server_port += 1; //Stream Port
  999. config.ctrl_port += 1; //UDP Port
  1000. Serial.printf("Starting stream server on port: '%d'\n", config.server_port);
  1001. if (httpd_start(&stream_httpd, &config) == ESP_OK) {
  1002. httpd_register_uri_handler(stream_httpd, &stream_uri);
  1003. }
  1004. }
  1005.  
  1006. void getCommand(char c)
  1007. {
  1008. if (c == '?') ReceiveState = 1;
  1009. if ((c == ' ') || (c == '\r') || (c == '\n')) ReceiveState = 0;
  1010.  
  1011. if (ReceiveState == 1)
  1012. {
  1013. Command = Command + String(c);
  1014.  
  1015. if (c == '=') cmdState = 0;
  1016. if (c == ';') strState++;
  1017.  
  1018. if ((cmdState == 1) && ((c != '?') || (questionstate == 1))) cmd = cmd + String(c);
  1019. if ((cmdState == 0) && (strState == 1) && ((c != '=') || (equalstate == 1))) P1 = P1 + String(c);
  1020. if ((cmdState == 0) && (strState == 2) && (c != ';')) P2 = P2 + String(c);
  1021. if ((cmdState == 0) && (strState == 3) && (c != ';')) P3 = P3 + String(c);
  1022. if ((cmdState == 0) && (strState == 4) && (c != ';')) P4 = P4 + String(c);
  1023. if ((cmdState == 0) && (strState == 5) && (c != ';')) P5 = P5 + String(c);
  1024. if ((cmdState == 0) && (strState == 6) && (c != ';')) P6 = P6 + String(c);
  1025. if ((cmdState == 0) && (strState == 7) && (c != ';')) P7 = P7 + String(c);
  1026. if ((cmdState == 0) && (strState == 8) && (c != ';')) P8 = P8 + String(c);
  1027. if ((cmdState == 0) && (strState >= 9) && ((c != ';') || (semicolonstate == 1))) P9 = P9 + String(c);
  1028.  
  1029. if (c == '?') questionstate = 1;
  1030. if (c == '=') equalstate = 1;
  1031. if ((strState >= 9) && (c == ';')) semicolonstate = 1;
  1032. }
  1033. }
Advertisement
Add Comment
Please, Sign In to add comment