safwan092

ESP32_Cam_Face_Detection_Door_Lock

Jan 25th, 2023
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.29 KB | None | 0 0
  1. #include <ArduinoWebsockets.h>
  2. #include "esp_http_server.h"
  3. #include "esp_timer.h"
  4. #include "esp_camera.h"
  5. #include "camera_index.h"
  6. #include "Arduino.h"
  7. #include "fd_forward.h"
  8. #include "fr_forward.h"
  9. #include "fr_flash.h"
  10.  
  11. const char* ssid = "network"; // diisi wifi_name
  12. const char* password = "123456789"; // diisi wifi password
  13.  
  14. #define ENROLL_CONFIRM_TIMES 5
  15. #define FACE_ID_SAVE_NUMBER 7
  16.  
  17. // Select camera model
  18. #define CAMERA_MODEL_AI_THINKER // camera model
  19. #include "camera_pins.h"
  20. #define Relay 2 // relay IO2
  21.  
  22. using namespace websockets;
  23. WebsocketsServer socket_server;
  24.  
  25. static int AUTO_MODE = 0;
  26. static int MANUAL_MODE = 1;
  27. static int OPENED = 2;
  28. static int CLOSED = 3;
  29.  
  30. int doorMode; // mode lock (AUTO_MODE / MANUAL MODE)
  31. int controlMode; // solenoid lock state (OPENED / CLOSED)
  32. boolean activateRelay; // relay state (true / false)
  33. long prevMillis=0;
  34. int interval = 5000; // interval auto closed untuk solenoid lock-nya
  35.  
  36. camera_fb_t * fb = NULL;
  37.  
  38. long current_millis;
  39. long last_detected_millis = 0;
  40.  
  41. void app_facenet_main();
  42. void app_httpserver_init();
  43.  
  44. typedef struct
  45. {
  46. uint8_t *image;
  47. box_array_t *net_boxes;
  48. dl_matrix3d_t *face_id;
  49. } http_img_process_result;
  50.  
  51.  
  52. static inline mtmn_config_t app_mtmn_config()
  53. {
  54. mtmn_config_t mtmn_config = {0};
  55. mtmn_config.type = FAST;
  56. mtmn_config.min_face = 80;
  57. mtmn_config.pyramid = 0.707;
  58. mtmn_config.pyramid_times = 4;
  59. mtmn_config.p_threshold.score = 0.6;
  60. mtmn_config.p_threshold.nms = 0.7;
  61. mtmn_config.p_threshold.candidate_number = 20;
  62. mtmn_config.r_threshold.score = 0.7;
  63. mtmn_config.r_threshold.nms = 0.7;
  64. mtmn_config.r_threshold.candidate_number = 10;
  65. mtmn_config.o_threshold.score = 0.7;
  66. mtmn_config.o_threshold.nms = 0.7;
  67. mtmn_config.o_threshold.candidate_number = 1;
  68. return mtmn_config;
  69. }
  70. mtmn_config_t mtmn_config = app_mtmn_config();
  71.  
  72. face_id_name_list st_face_list;
  73. static dl_matrix3du_t *aligned_face = NULL;
  74.  
  75. httpd_handle_t camera_httpd = NULL;
  76.  
  77. typedef enum
  78. {
  79. START_STREAM,
  80. START_DETECT,
  81. SHOW_FACES,
  82. START_RECOGNITION,
  83. START_ENROLL,
  84. ENROLL_COMPLETE,
  85. DELETE_ALL,
  86. } en_fsm_state;
  87. en_fsm_state g_state;
  88.  
  89. typedef struct
  90. {
  91. char enroll_name[ENROLL_NAME_LEN];
  92. } httpd_resp_value;
  93.  
  94. httpd_resp_value st_name;
  95.  
  96. void setup() { // awalnya solenoid lock dan relaynya dalam kondisi tertutup & MANUAL_MODE lock
  97. Serial.begin(115200);
  98. Serial.setDebugOutput(true);
  99. Serial.println();
  100.  
  101. pinMode(Relay, OUTPUT);
  102. digitalWrite(Relay, LOW);
  103. controlMode = CLOSED;
  104. activateRelay = false;
  105. doorMode = MANUAL_MODE;
  106.  
  107. camera_config_t config;
  108. config.ledc_channel = LEDC_CHANNEL_0;
  109. config.ledc_timer = LEDC_TIMER_0;
  110. config.pin_d0 = Y2_GPIO_NUM;
  111. config.pin_d1 = Y3_GPIO_NUM;
  112. config.pin_d2 = Y4_GPIO_NUM;
  113. config.pin_d3 = Y5_GPIO_NUM;
  114. config.pin_d4 = Y6_GPIO_NUM;
  115. config.pin_d5 = Y7_GPIO_NUM;
  116. config.pin_d6 = Y8_GPIO_NUM;
  117. config.pin_d7 = Y9_GPIO_NUM;
  118. config.pin_xclk = XCLK_GPIO_NUM;
  119. config.pin_pclk = PCLK_GPIO_NUM;
  120. config.pin_vsync = VSYNC_GPIO_NUM;
  121. config.pin_href = HREF_GPIO_NUM;
  122. config.pin_sscb_sda = SIOD_GPIO_NUM;
  123. config.pin_sscb_scl = SIOC_GPIO_NUM;
  124. config.pin_pwdn = PWDN_GPIO_NUM;
  125. config.pin_reset = RESET_GPIO_NUM;
  126. config.xclk_freq_hz = 20000000;
  127. config.pixel_format = PIXFORMAT_JPEG;
  128. //init with high specs to pre-allocate larger buffers
  129. if (psramFound()) {
  130. config.frame_size = FRAMESIZE_UXGA;
  131. config.jpeg_quality = 10;
  132. config.fb_count = 2;
  133. } else {
  134. config.frame_size = FRAMESIZE_SVGA;
  135. config.jpeg_quality = 12;
  136. config.fb_count = 1;
  137. }
  138.  
  139. #if defined(CAMERA_MODEL_ESP_EYE)
  140. pinMode(13, INPUT_PULLUP);
  141. pinMode(14, INPUT_PULLUP);
  142. #endif
  143.  
  144. // camera init
  145. esp_err_t err = esp_camera_init(&config);
  146. if (err != ESP_OK) {
  147. Serial.printf("Camera init failed with error 0x%x", err);
  148. return;
  149. }
  150.  
  151. sensor_t * s = esp_camera_sensor_get();
  152. s->set_framesize(s, FRAMESIZE_QVGA);
  153.  
  154. #if defined(CAMERA_MODEL_M5STACK_WIDE)
  155. s->set_vflip(s, 1);
  156. s->set_hmirror(s, 1);
  157. #endif
  158.  
  159. WiFi.begin(ssid, password);
  160. while (WiFi.status() != WL_CONNECTED) {
  161. delay(500);
  162. Serial.print(".");
  163. }
  164. Serial.println("");
  165. Serial.println("WiFi connected");
  166.  
  167. app_httpserver_init();
  168. app_facenet_main();
  169. socket_server.listen(82); // port web socket
  170.  
  171. Serial.print("Camera Ready! Use 'http://");
  172. Serial.print(WiFi.localIP()); // untuk mengoutput IP address yang akan diakses
  173. Serial.println("' to connect");
  174. }
  175.  
  176. static esp_err_t index_handler(httpd_req_t *req) { // menampilkan interface HTML
  177. httpd_resp_set_type(req, "text/html");
  178. httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
  179. return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len);
  180. }
  181.  
  182. httpd_uri_t index_uri = {
  183. .uri = "/",
  184. .method = HTTP_GET,
  185. .handler = index_handler,
  186. .user_ctx = NULL
  187. };
  188.  
  189. void app_httpserver_init ()
  190. {
  191. httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  192. if (httpd_start(&camera_httpd, &config) == ESP_OK)
  193. Serial.println("httpd_start");
  194. {
  195. httpd_register_uri_handler(camera_httpd, &index_uri);
  196. }
  197. }
  198.  
  199. void app_facenet_main()
  200. {
  201. face_id_name_init(&st_face_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);
  202. aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3);
  203. read_face_id_from_flash_with_name(&st_face_list);
  204. }
  205.  
  206. static inline int do_enrollment(face_id_name_list *face_list, dl_matrix3d_t *new_id) // fungsi untuk capture wajah
  207. {
  208. ESP_LOGD(TAG, "START ENROLLING");
  209. int left_sample_face = enroll_face_id_to_flash_with_name(face_list, new_id, st_name.enroll_name);
  210. ESP_LOGD(TAG, "Face ID %s Enrollment: Sample %d",
  211. st_name.enroll_name,
  212. ENROLL_CONFIRM_TIMES - left_sample_face);
  213. return left_sample_face;
  214. }
  215.  
  216. static esp_err_t send_face_list(WebsocketsClient &client) // mengirim semua wajah yang telah terdaftar
  217. {
  218. client.send("delete_faces"); // tell browser to delete all faces
  219. face_id_node *head = st_face_list.head;
  220. char add_face[64];
  221. for (int i = 0; i < st_face_list.count; i++) // loop current faces
  222. {
  223. sprintf(add_face, "listface:%s", head->id_name);
  224. client.send(add_face); //send face to browser
  225. head = head->next;
  226. }
  227. }
  228.  
  229. static esp_err_t delete_all_faces(WebsocketsClient &client) // menghapus semua wajah yang telah terdaftar
  230. {
  231. delete_face_all_in_flash_with_name(&st_face_list);
  232. client.send("delete_faces");
  233. }
  234.  
  235. void handle_message(WebsocketsClient &client, WebsocketsMessage msg) // mengambil data dari interface HTML
  236. {
  237. if (msg.data().substring(0, 8) == "capture:") { // kondisi menerima data "capture/mendaftarkan" wajah
  238. g_state = START_ENROLL;
  239. char person[FACE_ID_SAVE_NUMBER * ENROLL_NAME_LEN] = {0,};
  240. msg.data().substring(8).toCharArray(person, sizeof(person));
  241. memcpy(st_name.enroll_name, person, strlen(person) + 1);
  242. client.send("CAPTURING");
  243. }
  244. if (msg.data().substring(0, 7) == "remove:") { // kondisi menerima data "remove" wajah
  245. char person[ENROLL_NAME_LEN * FACE_ID_SAVE_NUMBER];
  246. msg.data().substring(7).toCharArray(person, sizeof(person));
  247. delete_face_id_in_flash_with_name(&st_face_list, person);
  248. send_face_list(client); // reset faces in the browser
  249. }
  250. if (msg.data() == "delete_all") { // kondisi menerima data "delete all" wajah
  251. delete_all_faces(client);
  252. }
  253.  
  254. if (msg.data() == "auto_door_lock") { // kondisi menerima data "auto door lock" untuk mengubah state menjadi auto_door_lock
  255. g_state = START_RECOGNITION;
  256.  
  257. doorMode = AUTO_MODE;
  258. activateRelay = false;
  259. controlMode = CLOSED;
  260. digitalWrite(Relay,LOW);
  261.  
  262. client.send("AUTO DOOR MODE");
  263. }
  264. if (msg.data() == "manual_door_lock") { // kondisi menerima data "manual door lock" untuk mengubah state menjadi manual_door_lock
  265. g_state = START_STREAM;
  266.  
  267. doorMode = MANUAL_MODE;
  268. activateRelay = false;
  269. controlMode = CLOSED;
  270. digitalWrite(Relay,LOW);
  271.  
  272. client.send("MANUAL DOOR MODE");
  273. }
  274. if (msg.data() == "open_door" && activateRelay == false && doorMode == MANUAL_MODE && controlMode == CLOSED) { // kondisi menerima data "open door" dan akan membuka door lock apabila state adalah manual_door_lock dan lock dalam kondisi closed
  275. activateRelay = true;
  276. controlMode = OPENED;
  277. digitalWrite(Relay,HIGH);
  278. client.send("DOOR OPENED");
  279. }
  280. if(msg.data() == "close_door" && activateRelay == true && doorMode == MANUAL_MODE && controlMode == OPENED) { // kondisi menerima data "close door" dan akan membuka door lock apabila state adalah manual_door_lock dan lock dalam kondisi opened
  281. activateRelay = false;
  282. controlMode = CLOSED;
  283. digitalWrite(Relay,LOW);
  284. client.send("DOOR CLOSED");
  285. }
  286. }
  287.  
  288. void loop() {
  289. auto client = socket_server.accept();
  290. client.onMessage(handle_message);
  291. dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, 320, 240, 3);
  292. http_img_process_result out_res = {0};
  293. out_res.image = image_matrix->item;
  294.  
  295. send_face_list(client);
  296. client.send("STREAMING");
  297.  
  298. while (client.available()) {
  299. client.poll();
  300.  
  301. fb = esp_camera_fb_get();
  302.  
  303. if (g_state == START_DETECT || g_state == START_ENROLL || g_state == START_RECOGNITION)
  304. {
  305. out_res.net_boxes = NULL;
  306. out_res.face_id = NULL;
  307.  
  308. fmt2rgb888(fb->buf, fb->len, fb->format, out_res.image);
  309.  
  310. out_res.net_boxes = face_detect(image_matrix, &mtmn_config);
  311.  
  312. if (out_res.net_boxes) // apabila wajah terdeteksi
  313. {
  314. if (align_face(out_res.net_boxes, image_matrix, aligned_face) == ESP_OK)
  315. {
  316.  
  317. out_res.face_id = get_face_id(aligned_face);
  318. last_detected_millis = millis();
  319. if (g_state == START_DETECT) {
  320. client.send("FACE DETECTED");
  321. }
  322.  
  323. if (g_state == START_ENROLL) // kondisi untuk capture/mendaftarkan wajah
  324. {
  325. int left_sample_face = do_enrollment(&st_face_list, out_res.face_id);
  326. char enrolling_message[64];
  327. sprintf(enrolling_message, "SAMPLE NUMBER %d FOR %s", ENROLL_CONFIRM_TIMES - left_sample_face, st_name.enroll_name);
  328. client.send(enrolling_message);
  329. if (left_sample_face == 0)
  330. {
  331. ESP_LOGI(TAG, "Enrolled Face ID: %s", st_face_list.tail->id_name);
  332. g_state = START_STREAM;
  333. char captured_message[64];
  334. sprintf(captured_message, "FACE CAPTURED FOR %s", st_face_list.tail->id_name);
  335. client.send(captured_message);
  336. send_face_list(client);
  337.  
  338. }
  339. }
  340.  
  341. if (g_state == START_RECOGNITION && (st_face_list.count > 0) && doorMode == AUTO_MODE && controlMode == CLOSED && activateRelay == false) // akan mengenali wajah apabila state adalah AUTO_MODE
  342. {
  343. face_id_node *f = recognize_face_with_name(&st_face_list, out_res.face_id);
  344. if (f) // apabila wajah terkenali maka solenoid lock akan terbuka
  345. {
  346. controlMode = OPENED;
  347. activateRelay = true;
  348. digitalWrite(Relay,HIGH);
  349. prevMillis = millis();
  350.  
  351. char recognised_message[64];
  352. sprintf(recognised_message, "DOOR OPENED RECOGNISED %s", f->id_name);
  353. client.send(recognised_message);
  354. }
  355. else // apabila wajah tidak terkenali maka solenoid lock tidak akan terbuka dan akan mengirim pesan "INTRUDER ALERT!"
  356. {
  357. client.send("INTRUDER ALERT!");
  358. }
  359. }
  360. dl_matrix3d_free(out_res.face_id);
  361. }
  362. }
  363. else
  364. {
  365. if (g_state != START_DETECT) {
  366. client.send("NO FACE DETECTED");
  367. }
  368. }
  369.  
  370. if (g_state == START_DETECT && millis() - last_detected_millis > 500) { // Detecting but no face detected
  371. client.send("PLEASE SHOW FACES");
  372. }
  373.  
  374. }
  375.  
  376. client.sendBinary((const char *)fb->buf, fb->len);
  377.  
  378. esp_camera_fb_return(fb);
  379. fb = NULL;
  380.  
  381. if(doorMode == AUTO_MODE && controlMode == OPENED && activateRelay == true && millis()-prevMillis > interval) { // apabila solenoid lock dalam kondisi tertutup dan state adalah AUTO_MODE dan sudah melewati 5 detik, maka solenoid lock akan otomatis tertutup
  382. controlMode == CLOSED;
  383. activateRelay = false;
  384. digitalWrite(Relay,LOW);
  385. }
  386. }
  387. }
Add Comment
Please, Sign In to add comment