Battery Powered Attendance system using Face Recognition on ESP32-CAM Board

Published  October 13, 2021   3
Student Attendance System using ESP32-CAM Development Board

The ESP32-CAM is a tiny board that comes with a camera, microSD card, and of course the ESP32 microcontroller for less than ten dollars. It also has Wi-Fi and Bluetooth capabilities making it an ideal choice for IoT projects. But working with the ESP32-CAM board can be an intimidating task because it requires an external programing board and it isn't as breadboard-friendly as compared to other microcontroller boards like Arduino nano, NodeMCU etc.

So, in this tutorial, we are going to build an Attendance system using Face Recognition by leveraging the power of the ESP-32 CAM board. Along with the ESP32-CAM board we will have an integrated power supply with 5-volt and 3.3-volt output that is powered by a 18650 cell.  It also has headers for directly mounting FTDI board and USB to TTL module with voltage switching options between 3.3V and 5V. Apart from this the board has a lot of additional features which are listed below, all these features will enable you to build interesting battery powered IoT projects. This board was made possible by PCBWay, who not only sponsored this project but also fabricated this ESP32 Attendance System PCB board for us. We will discuss more on this later for now, lets get back to the features of our project . The board provides, 

  • Access to all ESP32-CAM GPIO pins through both male and female connectors.
  • Multiple connector pins for +5-volts, +3.3-volts, and Ground.
  • Dedicated LEDs for indicating power and network connectivity.
  • Powered with a 18650 cell and comes with a charger and booster circuit.
  • This board can be directly mounted on a wall for applications like Face Recognition, Motion detection, etc. 
  • IO0 Jumper for Programming Mode.

To test this ESP32-CAM development board we are going to build a Face Recognition-based Attendance system. It uses the CameraWebServer code from the ESP board for face recognition. Upon recognizing the face, the face ID with the name associated with it will be sent to Google Sheets.  You can also check our previous project where we used ESP32-CAM for building a Face Recognition Based Door Lock.

Components Required for Building ESP32-CAM Development Board

  • TP4056 Li-Ion Battery Charger IC
  • FP6291 Boost Converter IC
  • Micro USB 2.0 B type 5 Pin Connector
  • 7× Resistor (2×1k, 1.2k, 6k, 51k, 100ohm, 48k)
  • 8× Capacitor (2×0.1µf, 4×10µf, 2×20µf)
  • 4× LEDs
  • 1× Inductor (4.7µH)
  • 1× Diode (1N5388BRLG)
  • 18650 Lithium cell
  • ESP32-CAM Board
  • AMS1117 3.3V Voltage Regulator

ESP32-CAM Development Board Circuit Diagram

ESP32 Cam board based Attendance System Circuit Diagram

The circuit diagram for ESP32-CAM Board is given above. This circuit has three main parts, one is the battery charging circuit, the second is DC to DC boost converter circuit and the third is the ESP32-CAM part. The Booster part is used to boost the battery voltage from 3.7v to 4.5v-6v. Here in this circuit, a Micro USB 2.0 B type 5 Pin Connector is on the Charger side. The complete working of the circuit can also be found in the video at the bottom of this page.

Charging Circuit

ESP32-CAM Board Charging Circuit

The battery charger circuit is designed around a dedicated lithium-ion battery charger TP4056 IC. TP4056 is a complete constant-current/constant-voltage linear charger for single-cell Lithium-ion batteries. Its SOP package and low external component count make the TP4056 ideally suited for portable applications. This IC handles the Battery charging operation by processing the 5V DC input supply received through the Micro USB socket. The LEDs connected with it indicate the charging status.

Booster Circuit

ESP32-CAM Board Booster Circuit

The DC-DC Boost Converter Circuit is designed using the DC-DC Boost Converter FP6291 IC. This 1 MHz DC-DC Step-Up Boost IC Can be used in the application, for example, getting stable 5V from 3V battery. The Boost Converter circuit gets the input supply through battery terminals (+ and -) is processed by FP6291 IC to give a stable 5V DC supply via the standard USB socket at its output.

ESP32-CAM Programming Circuit 

ESP32-CAM Programming Circuit

This section of the circuit consists of an ESP32-CAM board with one slot for FTDI board and another slot for USB to TTL converter so that we can directly plug any of the board and program the ESP32-CAM board directly.  

Fabricating PCB for ESP32-CAM Development Board

ESP32-CAM Development Board PCB Fabrication

Now that we have the schematic, and we can proceed with laying out the PCB for the Ultrasonic range finder using ATtiny85. You can design the PCB using any PCB software of your choice. Below are the 3D model views of the top layer and bottom layer of the ESP32-CAM Development Board:

While designing the circuits and PCBs, you can also make your circuit, and PCB designs public so that other users can copy or edit them and can take benefit from your work. We have also made this ESP32-CAM Development Board PCB design file and GERBER file public, check the link given below:

Gerber Files

Ordering PCB from PCBWay

Now after finalizing the design, you can proceed with ordering the PCB:

Step 1: Get into https://www.pcbway.com/, sign up if this is your first time. Then, in the PCB Prototype tab, enter the dimensions of your PCB, the number of layers, and the number of PCBs you require.

Ordering PCB from PCBWay

Step 2: Proceed by clicking on the ‘Quote Now’ button. You will be taken to a page to set a few additional parameters like the Board type, Layers, Material for PCB, Thickness, and More, most of them are selected by default, if you are opting for any specific parameters, you can select it in here.

PCB Specification Selection from PCBWay

Step 3: The final step is to upload the Gerber file and proceed with the payment. To make sure the process is smooth, PCBWAY verifies if your Gerber file is valid before proceeding with the payment. This way, you can be sure that your PCB is fabrication friendly and will reach you as committed.

Gerber Files from PCBWay

Assembling the ESP32-CAM Development Board PCB

After a few days, we received our PCB in a neat package and the PCB quality was good as always. The top layer and the bottom layer of the board are shown below:

ESP32-CAM Development Board PCB

After making sure the tracks and footprints were correct. I proceeded with assembling the PCB. The completely soldered board looks like the below:

ESP32-CAM Development Board

Setting-Up IFTTT for Sending Attendance Data to Google Sheets

As mentioned earlier attendance data received from ESP32-CAM will be sent to Google Sheets. For that, we are going to use IFTTT applets. This applet will get the data from ESP32-CAM using webhooks and then update it on Google Sheets.

First login to IFTTT and search for ‘Webhooks’.

IFTTT Webhooks

Now to get the Private key, click on ‘Documentation’. Copy this key somewhere, it will be used in the code.

IFTTT API Key

As we have the private key, we will create an applet using Webhooks and Google Sheets services. To create an applet, click on ‘Create’ in the top right corner.

Webhooks Applet

 Now in the next window, select If This Then That.

IFTTT Webhooks Selections

In the ‘This’ field we will use webhooks to get the web requests from the ESP32-CAM Board.

Webhooks Services Choose

Now select ‘Receive a Web Request’ trigger and then enter the event name as attendance and then click on ‘Create Trigger.’

Webhooks Web Service Recieve

After this, click on ‘Then That’, search for ‘Google Sheets’ and select the Google Sheets icon.

Webhooks Google Sheet

Then in the next step select the “Add a row to spreadsheet” action.

IFTTT Webhooks Google Sheet

 

After that, fill in the action fields. Give the spreadsheet a name, select a Google Drive folder path, and leave the "Formatted row" as default. If you leave the drive folder path blank, IFTTT will save the spreadsheet in a folder called "IFTTT" in your Google Drive folder. Finally, select “Create action” from the drop-down menu.

Webhooks Google Sheet Create Action

Programming ESP32-CAM for Student Attendance System

We explained Face Recognition with ESP32-CAM in the previous article; here we will modify the same code for marking attendance according to recognized face. The complete code is divided into three parts. One is the main code for the camera and attendance where the ESP32 gets the face recognition data from the web page and update it on google sheets, and the other two codes are for the web page and camera index. Here we are explaining some important parts of the code.

So, as usual, start the code by including all the required library files. "esp_camera.h" library is used for initializing the camera for face recognition while "WiFi.h" is used for connecting the ESP32-CAM board to Wi-Fi.

#include "esp_camera.h"
#include <WiFi.h>

In the next line, uncomment the camera module that you are using with ESP32. In the code, three different camera models are defined. In this case, we’re using the AI-THINKER Model.

//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_M5STACK_PSRAM
#define CAMERA_MODEL_AI_THINKER

Now you need to add your network credentials: the SSID and password on the following lines:

const char* ssid = "*********";
const char* password = "*********";

Then, you need to enter your unique IFTTT URL resource. For that go back IFTTT page and then webhooks documentation and copy the link that is given for triggering an event and paste it into the resource as shown below.

Webhooks Private Key Trigger Events

const char *server = "maker.ifttt.com";
const char* resource = "/trigger/attendance/with/key/hUAAAz0AVvc6-NW1UmqWXXv6VQWmpiGFxx3sV5rnaM9";

We are not going to make any changes in void setup() loop so leave it as it is. The next step will be getting the face ids from the web page and then updating the attendance sheet according to that. Here we will assign a different name according to IDs and then send these names to google sheets using if conditions.

Serial.print(FACE_ID);
  if (FACE_ID == 0){
      student = "Thor";
      makeIFTTTRequest();
      delay(10000);
  }
  if (FACE_ID == 1){
      student = "Ashish Mishra";
      makeIFTTTRequest();
      delay(10000);
  }
………………………..   
}

Testing the ESP32-CAM Development Board with Attendance System

Finally, to upload the code, connect the FDTI/USB to TTL to the development board, and select the ‘ESP32 Wrover Module’ as your board type. Also, change the other settings as shown in the below picture:

ESP32-CAM Development Board with Attendance System Tools

Don’t forget to connect the IO0 pin to GND before uploading the code and also press the ESP32 reset button and then click on the upload button.

After uploading the code, remove the IO0 and GND pin. Then open the serial monitor and change the baud rate to 115200. After that, press the ESP32 reset button, it will print the ESP IP address and port no. on the serial monitor.

ESP32-CAM Development Board with Attendance System Code

Now navigate to the browser and enter the ESP IP address that is copied from the Serial monitor to access the camera streaming. It will take you to the streaming page. To start the video streaming, click on the ‘Start Stream’ button at the bottom of the page.

ESP32-CAM based Attendance System Navigation

To recognize the faces with ESP32-CAM, first, we have to enroll the faces. For that, turn on the Face recognition and detection features from settings and then click on the Enroll Face button. It takes several attempts to save the face. After saving the face, it detects the face as subject 0 where zero is the face ID. In this way, you can enroll multiple faces.

ESP32-CAM based Attendance System Face Detection

Now if a face is recognized in the video feed, ESP32-CAM will update the google sheet with the name that is associated with that particular id.

ESP32-CAM Development Board Attendance System

So that’s all about building the ESP32-CAM development board and then using it to build an attendance system. Complete code and Working video is given below.

Code

app_httpd.cpp

#include "esp_http_server.h"
#include "esp_timer.h"
#include "esp_camera.h"
#include "img_converters.h"
#include "camera_index.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "fd_forward.h"
#include "dl_lib.h"
#include "fr_forward.h"
#define ENROLL_CONFIRM_TIMES 5
#define FACE_ID_SAVE_NUMBER 7
#define FACE_COLOR_WHITE  0x00FFFFFF
#define FACE_COLOR_BLACK  0x00000000
#define FACE_COLOR_RED    0x000000FF
#define FACE_COLOR_GREEN  0x0000FF00
#define FACE_COLOR_BLUE   0x00FF0000
#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN)
#define FACE_COLOR_CYAN   (FACE_COLOR_BLUE | FACE_COLOR_GREEN)
#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED)
//int FACE_ID;
extern int FACE_ID;
typedef struct {
        size_t size; //number of values used for filtering
        size_t index; //current value index
        size_t count; //value count
        int sum;
        int * values; //array to be filled with values
} ra_filter_t;
typedef struct {
        httpd_req_t *req;
        size_t len;
} jpg_chunking_t;
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
static ra_filter_t ra_filter;
httpd_handle_t stream_httpd = NULL;
httpd_handle_t camera_httpd = NULL;
static mtmn_config_t mtmn_config = {0};
static int8_t detection_enabled = 0;
static int8_t recognition_enabled = 0;
static int8_t is_enrolling = 0;
static face_id_list id_list = {0};
static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){
    memset(filter, 0, sizeof(ra_filter_t));
    filter->values = (int *)malloc(sample_size * sizeof(int));
    if(!filter->values){
        return NULL;
    }
    memset(filter->values, 0, sample_size * sizeof(int));
    filter->size = sample_size;
    return filter;
}
static int ra_filter_run(ra_filter_t * filter, int value){
    if(!filter->values){
        return value;
    }
    filter->sum -= filter->values[filter->index];
    filter->values[filter->index] = value;
    filter->sum += filter->values[filter->index];
    filter->index++;
    filter->index = filter->index % filter->size;
    if (filter->count < filter->size) {
        filter->count++;
    }
    return filter->sum / filter->count;
}
static void rgb_print(dl_matrix3du_t *image_matrix, uint32_t color, const char * str){
    fb_data_t fb;
    fb.width = image_matrix->w;
    fb.height = image_matrix->h;
    fb.data = image_matrix->item;
    fb.bytes_per_pixel = 3;
    fb.format = FB_BGR888;
    fb_gfx_print(&fb, (fb.width - (strlen(str) * 14)) / 2, 10, color, str);
}
static int rgb_printf(dl_matrix3du_t *image_matrix, uint32_t color, const char *format, ...){
    char loc_buf[64];
    char * temp = loc_buf;
    int len;
    va_list arg;
    va_list copy;
    va_start(arg, format);
    va_copy(copy, arg);
    len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg);
    va_end(copy);
    if(len >= sizeof(loc_buf)){
        temp = (char*)malloc(len+1);
        if(temp == NULL) {
            return 0;
        }
    }
    vsnprintf(temp, len+1, format, arg);
    va_end(arg);
    rgb_print(image_matrix, color, temp);
    if(len > 64){
        free(temp);
    }
    return len;
}
static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id){
    int x, y, w, h, i;
    uint32_t color = FACE_COLOR_YELLOW;
    if(face_id < 0){
        color = FACE_COLOR_RED;
    } else if(face_id > 0){
        color = FACE_COLOR_GREEN;
    }
    fb_data_t fb;
    fb.width = image_matrix->w;
    fb.height = image_matrix->h;
    fb.data = image_matrix->item;
    fb.bytes_per_pixel = 3;
    fb.format = FB_BGR888;
    for (i = 0; i < boxes->len; i++){
        // rectangle box
        x = (int)boxes->box[i].box_p[0];
        y = (int)boxes->box[i].box_p[1];
        w = (int)boxes->box[i].box_p[2] - x + 1;
        h = (int)boxes->box[i].box_p[3] - y + 1;
        fb_gfx_drawFastHLine(&fb, x, y, w, color);
        fb_gfx_drawFastHLine(&fb, x, y+h-1, w, color);
        fb_gfx_drawFastVLine(&fb, x, y, h, color);
        fb_gfx_drawFastVLine(&fb, x+w-1, y, h, color);
#if 0
        // landmark
        int x0, y0, j;
        for (j = 0; j < 10; j+=2) {
            x0 = (int)boxes->landmark[i].landmark_p[j];
            y0 = (int)boxes->landmark[i].landmark_p[j+1];
            fb_gfx_fillRect(&fb, x0, y0, 3, 3, color);
        }
#endif
    }
}
static int run_face_recognition(dl_matrix3du_t *image_matrix, box_array_t *net_boxes){
    dl_matrix3du_t *aligned_face = NULL;
    int matched_id = 0;
    aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3);
    if(!aligned_face){
        Serial.println("Could not allocate face recognition buffer");
        return matched_id;
    }
    if (align_face(net_boxes, image_matrix, aligned_face) == ESP_OK){
        if (is_enrolling == 1){
            int8_t left_sample_face = enroll_face(&id_list, aligned_face);

            if(left_sample_face == (ENROLL_CONFIRM_TIMES - 1)){
                Serial.printf("Enrolling Face ID: %d\n", id_list.tail);
            }
            Serial.printf("Enrolling Face ID: %d sample %d\n", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
            rgb_printf(image_matrix, FACE_COLOR_CYAN, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
            if (left_sample_face == 0){
                is_enrolling = 0;
                Serial.printf("Enrolled Face ID: %d\n", id_list.tail);
            }
        } else {
            matched_id = recognize_face(&id_list, aligned_face);
            if (matched_id >= 0) {
                Serial.printf("Match Face ID: %u\n", matched_id);
                rgb_printf(image_matrix, FACE_COLOR_GREEN, "Hello Subject %u", matched_id);
                FACE_ID = matched_id;
            } else {
                Serial.println("No Match Found");
                rgb_print(image_matrix, FACE_COLOR_RED, "Intruder Alert!");
                matched_id = -1;
            }
        }
    } else {
        Serial.println("Face Not Aligned");
        //rgb_print(image_matrix, FACE_COLOR_YELLOW, "Human Detected");
    }
    dl_matrix3du_free(aligned_face);
    return matched_id;
}
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
    jpg_chunking_t *j = (jpg_chunking_t *)arg;
    if(!index){
        j->len = 0;
    }
    if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
        return 0;
    }
    j->len += len;
    return len;
}
static esp_err_t capture_handler(httpd_req_t *req){
    camera_fb_t * fb = NULL;
    esp_err_t res = ESP_OK;
    int64_t fr_start = esp_timer_get_time();
    fb = esp_camera_fb_get();
    if (!fb) {
        Serial.println("Camera capture failed");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }
    httpd_resp_set_type(req, "image/jpeg");
    httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
    size_t out_len, out_width, out_height;
    uint8_t * out_buf;
    bool s;
    bool detected = false;
    int face_id = 0;
    if(!detection_enabled || fb->width > 400){
        size_t fb_len = 0;
        if(fb->format == PIXFORMAT_JPEG){
            fb_len = fb->len;
            res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
        } else {
            jpg_chunking_t jchunk = {req, 0};
            res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
            httpd_resp_send_chunk(req, NULL, 0);
            fb_len = jchunk.len;
        }
        esp_camera_fb_return(fb);
        int64_t fr_end = esp_timer_get_time();
        Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
        return res;
    }
    dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
    if (!image_matrix) {
        esp_camera_fb_return(fb);
        Serial.println("dl_matrix3du_alloc failed");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }
    out_buf = image_matrix->item;
    out_len = fb->width * fb->height * 3;
    out_width = fb->width;
    out_height = fb->height;
    s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf);
    esp_camera_fb_return(fb);
    if(!s){
        dl_matrix3du_free(image_matrix);
        Serial.println("to rgb888 failed");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }
    box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config);
    if (net_boxes){
        detected = true;
        if(recognition_enabled){
            face_id = run_face_recognition(image_matrix, net_boxes);
        }
        draw_face_boxes(image_matrix, net_boxes, face_id);
        free(net_boxes->box);
        free(net_boxes->landmark);
        free(net_boxes);
    }
    jpg_chunking_t jchunk = {req, 0};
    s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk);
    dl_matrix3du_free(image_matrix);
    if(!s){
        Serial.println("JPEG compression failed");
        return ESP_FAIL;
    }
    int64_t fr_end = esp_timer_get_time();
    Serial.printf("FACE: %uB %ums %s%d\n", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start)/1000), detected?"DETECTED ":"", face_id);
    return res;
}
static esp_err_t stream_handler(httpd_req_t *req){
    camera_fb_t * fb = NULL;
    esp_err_t res = ESP_OK;
    size_t _jpg_buf_len = 0;
    uint8_t * _jpg_buf = NULL;
    char * part_buf[64];
    dl_matrix3du_t *image_matrix = NULL;
    bool detected = false;
    int face_id = 0;
    int64_t fr_start = 0;
    int64_t fr_ready = 0;
    int64_t fr_face = 0;
    int64_t fr_recognize = 0;
    int64_t fr_encode = 0;
    static int64_t last_frame = 0;
    if(!last_frame) {
        last_frame = esp_timer_get_time();
    }
    res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
    if(res != ESP_OK){
        return res;
    }
    while(true){
        detected = false;
        face_id = 0;
        fb = esp_camera_fb_get();
        if (!fb) {
            Serial.println("Camera capture failed");
            res = ESP_FAIL;
        } else {
            fr_start = esp_timer_get_time();
            fr_ready = fr_start;
            fr_face = fr_start;
            fr_encode = fr_start;
            fr_recognize = fr_start;
            if(!detection_enabled || fb->width > 400){
                if(fb->format != PIXFORMAT_JPEG){
                    bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
                    esp_camera_fb_return(fb);
                    fb = NULL;
                    if(!jpeg_converted){
                        Serial.println("JPEG compression failed");
                        res = ESP_FAIL;
                    }
                } else {
                    _jpg_buf_len = fb->len;
                    _jpg_buf = fb->buf;
                }
            } else {
                image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
                if (!image_matrix) {
                    Serial.println("dl_matrix3du_alloc failed");
                    res = ESP_FAIL;
                } else {
                    if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)){
                        Serial.println("fmt2rgb888 failed");
                        res = ESP_FAIL;
                    } else {
                        fr_ready = esp_timer_get_time();
                        box_array_t *net_boxes = NULL;
                        if(detection_enabled){
                            net_boxes = face_detect(image_matrix, &mtmn_config);
                        }
                        fr_face = esp_timer_get_time();
                        fr_recognize = fr_face;
                        if (net_boxes || fb->format != PIXFORMAT_JPEG){
                            if(net_boxes){
                                detected = true;
                                if(recognition_enabled){
                                    face_id = run_face_recognition(image_matrix, net_boxes);
                                }
                                fr_recognize = esp_timer_get_time();
                                draw_face_boxes(image_matrix, net_boxes, face_id);
                                free(net_boxes->box);
                                free(net_boxes->landmark);
                                free(net_boxes);
                            }
                            if(!fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)){
                                Serial.println("fmt2jpg failed");
                                res = ESP_FAIL;
                            }
                            esp_camera_fb_return(fb);
                            fb = NULL;
                        } else {
                            _jpg_buf = fb->buf;
                            _jpg_buf_len = fb->len;
                        }
                        fr_encode = esp_timer_get_time();
                    }
                    dl_matrix3du_free(image_matrix);
                }
            }
        }
        if(res == ESP_OK){
            size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
            res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
        }
        if(res == ESP_OK){
            res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
        }
        if(res == ESP_OK){
            res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
        }
        if(fb){
            esp_camera_fb_return(fb);
            fb = NULL;
            _jpg_buf = NULL;
        } else if(_jpg_buf){
            free(_jpg_buf);
            _jpg_buf = NULL;
        }
        if(res != ESP_OK){
            break;
        }
        int64_t fr_end = esp_timer_get_time();
        int64_t ready_time = (fr_ready - fr_start)/1000;
        int64_t face_time = (fr_face - fr_ready)/1000;
        int64_t recognize_time = (fr_recognize - fr_face)/1000;
        int64_t encode_time = (fr_encode - fr_recognize)/1000;
        int64_t process_time = (fr_encode - fr_start)/1000;        
        int64_t frame_time = fr_end - last_frame;
        last_frame = fr_end;
        frame_time /= 1000;
        uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time);
        Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps), %u+%u+%u+%u=%u %s%d\n",
            (uint32_t)(_jpg_buf_len),
            (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time,
            avg_frame_time, 1000.0 / avg_frame_time,
            (uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time,
            (detected)?"DETECTED ":"", face_id
        );
    }
    last_frame = 0;
    return res;
}
static esp_err_t cmd_handler(httpd_req_t *req){
    char*  buf;
    size_t buf_len;
    char variable[32] = {0,};
    char value[32] = {0,};
    buf_len = httpd_req_get_url_query_len(req) + 1;
    if (buf_len > 1) {
        buf = (char*)malloc(buf_len);
        if(!buf){
            httpd_resp_send_500(req);
            return ESP_FAIL;
        }
        if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
            if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK &&
                httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) {
            } else {
                free(buf);
                httpd_resp_send_404(req);
                return ESP_FAIL;
            }
        } else {
            free(buf);
            httpd_resp_send_404(req);
            return ESP_FAIL;
        }
        free(buf);
    } else {
        httpd_resp_send_404(req);
        return ESP_FAIL;
    }
    int val = atoi(value);
    sensor_t * s = esp_camera_sensor_get();
    int res = 0;
    if(!strcmp(variable, "framesize")) {
        if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
    }
    else if(!strcmp(variable, "quality")) res = s->set_quality(s, val);
    else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
    else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
    else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val);
    else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val);
    else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val);
    else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val);
    else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val);
    else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val);
    else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
    else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
    else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val);
    else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val);
    else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val);
    else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val);
    else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val);
    else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val);
    else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val);
    else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val);
    else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val);
    else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val);
    else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val);
    else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val);
    else if(!strcmp(variable, "face_detect")) {
        detection_enabled = val;
        if(!detection_enabled) {
            recognition_enabled = 0;
        }
    }
    else if(!strcmp(variable, "face_enroll")) is_enrolling = val;
    else if(!strcmp(variable, "face_recognize")) {
        recognition_enabled = val;
        if(recognition_enabled){
            detection_enabled = val;
        }
    }
    else {
        res = -1;
    }
    if(res){
        return httpd_resp_send_500(req);
    }
    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
    return httpd_resp_send(req, NULL, 0);
}
static esp_err_t status_handler(httpd_req_t *req){
    static char json_response[1024];
    sensor_t * s = esp_camera_sensor_get();
    char * p = json_response;
    *p++ = '{';
    p+=sprintf(p, "\"framesize\":%u,", s->status.framesize);
    p+=sprintf(p, "\"quality\":%u,", s->status.quality);
    p+=sprintf(p, "\"brightness\":%d,", s->status.brightness);
    p+=sprintf(p, "\"contrast\":%d,", s->status.contrast);
    p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);
    p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect);
    p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode);
    p+=sprintf(p, "\"awb\":%u,", s->status.awb);
    p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain);
    p+=sprintf(p, "\"aec\":%u,", s->status.aec);
    p+=sprintf(p, "\"aec2\":%u,", s->status.aec2);
    p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level);
    p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value);
    p+=sprintf(p, "\"agc\":%u,", s->status.agc);
    p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain);
    p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling);
    p+=sprintf(p, "\"bpc\":%u,", s->status.bpc);
    p+=sprintf(p, "\"wpc\":%u,", s->status.wpc);
    p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma);
    p+=sprintf(p, "\"lenc\":%u,", s->status.lenc);
    p+=sprintf(p, "\"vflip\":%u,", s->status.vflip);
    p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
    p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
    p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar);
    p+=sprintf(p, "\"face_detect\":%u,", detection_enabled);
    p+=sprintf(p, "\"face_enroll\":%u,", is_enrolling);
    p+=sprintf(p, "\"face_recognize\":%u", recognition_enabled);
    *p++ = '}';
    *p++ = 0;
    httpd_resp_set_type(req, "application/json");
    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
    return httpd_resp_send(req, json_response, strlen(json_response));
}
static esp_err_t index_handler(httpd_req_t *req){
    httpd_resp_set_type(req, "text/html");
    httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
    return httpd_resp_send(req, (const char *)index_html_gz, index_html_gz_len);
}
void startCameraServer(){
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_uri_t index_uri = {
        .uri       = "/",
        .method    = HTTP_GET,
        .handler   = index_handler,
        .user_ctx  = NULL
    };
    httpd_uri_t status_uri = {
        .uri       = "/status",
        .method    = HTTP_GET,
        .handler   = status_handler,
        .user_ctx  = NULL
    };
    httpd_uri_t cmd_uri = {
        .uri       = "/control",
        .method    = HTTP_GET,
        .handler   = cmd_handler,
        .user_ctx  = NULL
    };
    httpd_uri_t capture_uri = {
        .uri       = "/capture",
        .method    = HTTP_GET,
        .handler   = capture_handler,
        .user_ctx  = NULL
    };
   httpd_uri_t stream_uri = {
        .uri       = "/stream",
        .method    = HTTP_GET,
        .handler   = stream_handler,
        .user_ctx  = NULL
    };
    ra_filter_init(&ra_filter, 20);    
    mtmn_config.min_face = 80;
    mtmn_config.pyramid = 0.7;
    mtmn_config.p_threshold.score = 0.6;
    mtmn_config.p_threshold.nms = 0.7;
    mtmn_config.r_threshold.score = 0.7;
    mtmn_config.r_threshold.nms = 0.7;
    mtmn_config.r_threshold.candidate_number = 4;
    mtmn_config.o_threshold.score = 0.7;
    mtmn_config.o_threshold.nms = 0.4;
    mtmn_config.o_threshold.candidate_number = 1;
    mtmn_config = mtmn_init_config();
    face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);   
    Serial.printf("Starting web server on port: '%d'\n", config.server_port);
    if (httpd_start(&camera_httpd, &config) == ESP_OK) {
        httpd_register_uri_handler(camera_httpd, &index_uri);
        httpd_register_uri_handler(camera_httpd, &cmd_uri);
        httpd_register_uri_handler(camera_httpd, &status_uri);
        httpd_register_uri_handler(camera_httpd, &capture_uri);
    }
    config.server_port += 1;
    config.ctrl_port += 1;
    Serial.printf("Starting stream server on port: '%d'\n", config.server_port);
    if (httpd_start(&stream_httpd, &config) == ESP_OK) {
        httpd_register_uri_handler(stream_httpd, &stream_uri);
    }
}

 

camera_index.h

//File: index.html.gz, Size: 3663
#define index_html_gz_len 3663
const uint8_t index_html_gz[] = {
 0x1F, 0x8B, 0x08, 0x08, 0x60, 0x15, 0x36, 0x5C, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E,
 0x68, 0x74, 0x6D, 0x6C, 0x00, 0xDD, 0x5C, 0xEB, 0x72, 0x9B, 0xC8, 0x12, 0xFE, 0x7F, 0x9E, 0x02,
 0x93, 0xDD, 0x08, 0x6A, 0x91, 0x2C, 0xC9, 0x8A, 0xE3, 0x20, 0x0B, 0x1F, 0x5B, 0x76, 0x92, 0xAD,
 0xCA, 0x6D, 0xE3, 0x3D, 0xBB, 0x5B, 0xB5, 0xB5, 0x95, 0x8C, 0x60, 0x90, 0x26, 0x46, 0x8C, 0x02,
 0x83, 0x2E, 0xD1, 0xF2, 0x1C, 0xE7, 0x81, 0xCE, 0x8B, 0x9D, 0x9E, 0x19, 0x40, 0xA0, 0x8B, 0x65,
 0x49, 0x89, 0x94, 0x5A, 0xBB, 0xCA, 0x1A, 0xA0, 0xBB, 0xA7, 0xBB, 0xBF, 0xBE, 0x0C, 0x88, 0xF1,
 0xF9, 0x91, 0x43, 0x6D, 0x36, 0x19, 0x60, 0xA5, 0xC7, 0xFA, 0x9E, 0xF5, 0xAF, 0x73, 0xF9, 0xA1,
 0xC0, 0xCF, 0x79, 0x0F, 0x23, 0x47, 0x0E, 0xC5, 0x61, 0x1F, 0x33, 0xA4, 0xD8, 0x3D, 0x14, 0x84,
 0x98, 0xB5, 0xD4, 0x88, 0xB9, 0xE5, 0x33, 0x75, 0xFE, 0xB2, 0x8F, 0xFA, 0xB8, 0xA5, 0x0E, 0x09,
 0x1E, 0x0D, 0x68, 0xC0, 0x54, 0xC5, 0xA6, 0x3E, 0xC3, 0x3E, 0x90, 0x8F, 0x88, 0xC3, 0x7A, 0x2D,
 0x07, 0x0F, 0x89, 0x8D, 0xCB, 0xE2, 0xC0, 0x20, 0x3E, 0x61, 0x04, 0x79, 0xE5, 0xD0, 0x46, 0x1E,
 0x6E, 0xD5, 0xF2, 0xB2, 0x18, 0x61, 0x1E, 0xB6, 0x6E, 0x6E, 0xDF, 0x9D, 0xD4, 0x95, 0xB7, 0xBF,
 0xD5, 0x1B, 0xA7, 0xD5, 0xF3, 0x63, 0x79, 0x6E, 0x46, 0x13, 0xB2, 0x09, 0x3F, 0xEE, 0x50, 0x67,
 0x32, 0x75, 0x61, 0x9A, 0xB2, 0x8B, 0xFA, 0xC4, 0x9B, 0x98, 0x97, 0x01, 0x08, 0x35, 0x5E, 0x62,
 0x6F, 0x88, 0x19, 0xB1, 0x91, 0x11, 0x22, 0x3F, 0x2C, 0x87, 0x38, 0x20, 0x6E, 0xB3, 0x83, 0xEC,
 0xBB, 0x6E, 0x40, 0x23, 0xDF, 0x31, 0x1F, 0xD5, 0xCE, 0xF8, 0x6F, 0xD3, 0xA6, 0x1E, 0x0D, 0xCC,
 0x47, 0x37, 0xCF, 0xF9, 0x6F, 0x53, 0xC8, 0x09, 0xC9, 0x17, 0x6C, 0xD6, 0x4E, 0x07, 0xE3, 0xB8,
 0x57, 0x9F, 0xE6, 0xCE, 0x9C, 0xC1, 0x99, 0x10, 0xDB, 0x8C, 0x50, 0xBF, 0xD2, 0x47, 0xC4, 0x9F,
 0x3A, 0x24, 0x1C, 0x78, 0x68, 0x62, 0xBA, 0x1E, 0x1E, 0xC7, 0x8F, 0xFA, 0xD8, 0x8F, 0x8C, 0xC2,
 0x75, 0x7E, 0xBE, 0xEC, 0x90, 0x40, 0x9E, 0x33, 0x61, 0xAA, 0xA8, 0xEF, 0x4B, 0xC2, 0x8C, 0xD7,
 0xA7, 0x3E, 0x6E, 0x0A, 0xC2, 0x51, 0x80, 0x06, 0x70, 0xC8, 0x3F, 0x9A, 0x7D, 0xE2, 0x4B, 0x27,
 0x99, 0x27, 0x8D, 0xEA, 0x60, 0x5C, 0x50, 0xFC, 0xE4, 0x94, 0xFF, 0x36, 0x07, 0xC8, 0x71, 0x88,
 0xDF, 0x35, 0xCF, 0xF8, 0x65, 0x1A, 0x38, 0x38, 0x28, 0x07, 0xC8, 0x21, 0x51, 0x68, 0x36, 0xE0,
 0x4C, 0x1F, 0x05, 0x5D, 0x90, 0xC1, 0xE8, 0xC0, 0x2C, 0xD7, 0xAA, 0xB3, 0x13, 0x01, 0xE9, 0xF6,
 0x98, 0xC9, 0xCF, 0xC4, 0x8F, 0x12, 0x6C, 0x0A, 0x66, 0xE4, 0x54, 0x11, 0x8A, 0x20, 0x8F, 0x74,
 0xFD, 0x32, 0x61, 0xB8, 0x1F, 0x9A, 0x21, 0x0B, 0x30, 0xB3, 0x7B, 0xB1, 0x4B, 0xBA, 0x51, 0x80,
 0xA7, 0xA9, 0x02, 0xD5, 0x44, 0x36, 0x0C, 0xCA, 0x23, 0xDC, 0xB9, 0x23, 0xAC, 0x9C, 0x4C, 0xD6,
 0xC1, 0x2E, 0x0D, 0x70, 0x46, 0x50, 0xEE, 0x78, 0xD4, 0xBE, 0x2B, 0x87, 0x0C, 0x05, 0x6C, 0x91,
 0x18, 0xB9, 0x0C, 0x07, 0xF3, 0xB4, 0x18, 0x0C, 0x5E, 0xA0, 0x4C, 0x05, 0x24, 0x87, 0xC4, 0xF7,
 0x88, 0x8F, 0x57, 0x89, 0x95, 0x12, 0x8A, 0xA4, 0xE2, 0x5C, 0x62, 0x86, 0x42, 0xFA, 0xDD, 0xCC,
 0x03, 0x62, 0xD2, 0xA6, 0x74, 0x7C, 0xAD, 0x5A, 0xFD, 0xB1, 0xD9, 0xC3, 0xC2, 0x5F, 0x28, 0x62,
 0xF4, 0x7E, 0x27, 0xF3, 0xD8, 0xF8, 0x77, 0x1F, 0x3B, 0x04, 0x29, 0xDA, 0x0C, 0x3C, 0xE5, 0xAC,
 0x0A, 0x9E, 0xD6, 0x15, 0xE4, 0x3B, 0x8A, 0x46, 0x03, 0x02, 0xDE, 0x46, 0x22, 0x14, 0x3C, 0x38,
 0x03, 0x61, 0x3F, 0xC0, 0xFA, 0x74, 0x1D, 0x0C, 0x49, 0x44, 0xAC, 0x06, 0x62, 0x89, 0x05, 0x7D,
 0x34, 0x2E, 0xE7, 0xAC, 0xE0, 0x87, 0x89, 0x25, 0x90, 0x6A, 0xB6, 0x06, 0x27, 0x87, 0x3D, 0xA5,
 0xAC, 0xF0, 0xD0, 0xD2, 0x13, 0x73, 0x85, 0x89, 0x39, 0x73, 0xFF, 0x29, 0x28, 0xA7, 0x19, 0xFB,
 0xA8, 0x13, 0x31, 0x46, 0xFD, 0x70, 0x8D, 0x9B, 0x3F, 0x45, 0x21, 0x23, 0xEE, 0xA4, 0x9C, 0x80,
 0x62, 0x86, 0x03, 0x04, 0xF5, 0xAA, 0x83, 0xD9, 0x08, 0x63, 0x48, 0x5D, 0x1F, 0x0D, 0x01, 0xEE,
 0x6E, 0xD7, 0xC3, 0x53, 0x3B, 0x0A, 0x42, 0xA8, 0x1C, 0x03, 0x4A, 0x80, 0x32, 0x68, 0x16, 0x00,
 0xC8, 0x13, 0x96, 0xED, 0xCE, 0x94, 0x46, 0x8C, 0xAB, 0x04, 0x2A, 0x52, 0x90, 0x47, 0xD8, 0x04,
 0x46, 0xD2, 0xED, 0xD5, 0xD4, 0xE7, 0xD5, 0x39, 0x1E, 0xD3, 0xEE, 0x61, 0xFB, 0x0E, 0x3B, 0x3F,
 0x15, 0xCB, 0x85, 0x28, 0x35, 0x15, 0xE2, 0x0F, 0x22, 0x56, 0xE6, 0x05, 0x61, 0xB0, 0xC6, 0x1E,
 0xE1, 0x89, 0x64, 0x8A, 0x7A, 0x3D, 0x8B, 0x59, 0xF3, 0xC9, 0x60, 0xAC, 0x54, 0x0B, 0x82, 0x2C,
 0x0F, 0x75, 0xB0, 0x97, 0x89, 0x4B, 0x9C, 0x28, 0xE3, 0x29, 0x09, 0x82, 0x5C, 0xF5, 0xC8, 0x55,
 0xA8, 0xC6, 0xD3, 0x1F, 0x0B, 0x82, 0x14, 0x31, 0x36, 0x0A, 0xA7, 0x42, 0xEC, 0x01, 0x0C, 0xB2,
 0x20, 0xC2, 0x99, 0x91, 0x59, 0x8B, 0x2B, 0x01, 0xF2, 0xBB, 0x18, 0x00, 0x1C, 0x1B, 0xE9, 0x30,
 0x57, 0x52, 0x97, 0x4D, 0x6F, 0x56, 0x15, 0x50, 0x3B, 0x96, 0x40, 0x2E, 0x44, 0x7C, 0x6A, 0x56,
 0x8E, 0xBA, 0x56, 0xCF, 0x6A, 0x23, 0x38, 0xBA, 0xE0, 0x0A, 0x5E, 0x35, 0xE7, 0x10, 0x4C, 0x3A,
 0x81, 0xEB, 0x16, 0xFB, 0x84, 0xEB, 0x9E, 0x54, 0x4F, 0x1A, 0x73, 0xD9, 0xCF, 0xE7, 0x29, 0xF6,
 0x8A, 0x66, 0x86, 0x71, 0xA2, 0xA0, 0xD9, 0xA3, 0x43, 0x1C, 0x4C, 0x8B, 0xA2, 0x1A, 0xCF, 0x1A,
 0x4E, 0x7A, 0x1D, 0x41, 0x5C, 0x0E, 0x71, 0x91, 0xA0, 0x5E, 0xB3, 0xEB, 0xB5, 0x84, 0xA0, 0x02,
 0x16, 0xA2, 0x8E, 0x87, 0x9D, 0x34, 0xD4, 0x1C, 0xEC, 0xA2, 0xC8, 0x63, 0x05, 0xED, 0x50, 0x95,
 0xFF, 0xC6, 0xC2, 0xD7, 0x7F, 0xF2, 0x36, 0xDE, 0x12, 0xBE, 0xFC, 0x6B, 0x9A, 0x26, 0x08, 0x1A,
 0x0C, 0x30, 0x82, 0x73, 0x36, 0x96, 0xAD, 0x66, 0xB1, 0xB8, 0x89, 0xB0, 0x58, 0xD2, 0x60, 0xE6,
 0xDC, 0x93, 0xA6, 0xFF, 0xE2, 0x5C, 0xA6, 0x4B, 0xED, 0x28, 0x9C, 0x05, 0xF9, 0x12, 0x0A, 0x33,
 0x55, 0x27, 0xF4, 0x88, 0x70, 0x63, 0xE4, 0xFB, 0xDC, 0xB6, 0x32, 0x0B, 0x60, 0xE2, 0xE9, 0x12,
 0xA5, 0x16, 0xF1, 0xC9, 0xAB, 0x98, 0xB4, 0xEB, 0x22, 0x28, 0xD5, 0x0C, 0x6B, 0x25, 0xA4, 0x30,
 0x8F, 0x92, 0x90, 0x3D, 0x40, 0x1F, 0xD6, 0x8B, 0xFA, 0x9D, 0x69, 0xC2, 0x5E, 0x83, 0xDC, 0x90,
 0x02, 0x82, 0x6E, 0x07, 0x69, 0x55, 0xA3, 0x6A, 0x9C, 0xC0, 0x1F, 0xBD, 0xE0, 0x30, 0xA9, 0x72,
 0xBD, 0xBE, 0xD0, 0x7D, 0x9F, 0xCC, 0xF7, 0xEB, 0x24, 0x80, 0xE6, 0xAC, 0x59, 0x85, 0x4F, 0xA1,
 0x71, 0xD7, 0x2A, 0x3C, 0xE0, 0x57, 0x38, 0x7C, 0x9D, 0x53, 0x17, 0xFD, 0xB5, 0xD4, 0x11, 0x7D,
 0xFA, 0xA5, 0x2C, 0xF3, 0xEF, 0x60, 0x58, 0xE4, 0x54, 0xD8, 0x37, 0x0E, 0xCB, 0xF5, 0x09, 0xB7,
 0xF4, 0x45, 0x55, 0x49, 0xED, 0x2E, 0xCB, 0x6A, 0x02, 0x62, 0x7C, 0x68, 0x21, 0x01, 0xB4, 0x92,
 0xE6, 0xC2, 0x99, 0x55, 0x73, 0xBB, 0xC4, 0xF3, 0xCA, 0x1E, 0x1D, 0xCD, 0x55, 0x8F, 0x82, 0x9F,
 0xE7, 0xFD, 0x3A, 0xEF, 0xFE, 0x7B, 0x65, 0x47, 0x10, 0x73, 0xDF, 0x40, 0xF6, 0xFE, 0x93, 0x68,
 0x06, 0xCA, 0x3D, 0x49, 0xB2, 0xCE, 0xA3, 0x0F, 0x60, 0x5D, 0x74, 0x98, 0xAC, 0x91, 0x71, 0x25,
 0x1C, 0x11, 0x58, 0x89, 0xCD, 0x35, 0xA3, 0x01, 0x0D, 0x89, 0x58, 0xE6, 0x05, 0xD8, 0x43, 0xBC,
 0xC8, 0x2F, 0xB6, 0xE1, 0xB9, 0xE6, 0x91, 0xBB, 0x94, 0xCA, 0x94, 0x6D, 0xF4, 0x61, 0x4B, 0x87,
 0x8A, 0xAC, 0x00, 0x49, 0xBC, 0x0A, 0xE7, 0x15, 0x8A, 0x7B, 0xC1, 0xB7, 0xF5, 0x7B, 0x63, 0x38,
 0x09, 0xDC, 0x6E, 0x80, 0x27, 0xA9, 0x58, 0x23, 0xF9, 0x34, 0xE5, 0x4A, 0x6F, 0x79, 0x8F, 0x16,
 0x71, 0x2D, 0xAD, 0xAE, 0x34, 0xC2, 0x78, 0x8E, 0x65, 0xD1, 0x23, 0xE9, 0x02, 0x4B, 0x55, 0x17,
 0xA0, 0xCF, 0x92, 0x4D, 0xB8, 0x26, 0xC9, 0x41, 0x3E, 0xF4, 0xB0, 0xCB, 0xC4, 0xC2, 0x9B, 0x57,
 0xC7, 0x93, 0x42, 0x84, 0x94, 0x67, 0xDD, 0x5B, 0xE2, 0x99, 0xAD, 0x9F, 0x52, 0xDF, 0x2C, 0xA3,
 0xE5, 0x31, 0xB5, 0x9C, 0x3C, 0x55, 0x3C, 0x2D, 0xB1, 0xC2, 0x3C, 0x38, 0xD3, 0x97, 0x09, 0x0C,
 0x46, 0xE0, 0x3F, 0xB4, 0xFA, 0x29, 0x5F, 0x3F, 0xAF, 0xBE, 0x14, 0x27, 0xCB, 0x9E, 0x85, 0x94,
 0x48, 0x5B, 0x6C, 0x2E, 0x0A, 0x1A, 0x73, 0x98, 0xCD, 0x70, 0x5F, 0x58, 0x79, 0xC0, 0x6A, 0xAB,
 0x8F, 0xA0, 0x58, 0x72, 0x17, 0xC2, 0x6D, 0x26, 0xD8, 0xB6, 0xE8, 0xDE, 0xD9, 0xF2, 0xAC, 0x76,
 0xCA, 0x6F, 0xF6, 0x2A, 0xB6, 0x47, 0xC3, 0x1C, 0x0E, 0xA8, 0x03, 0x9A, 0x44, 0x0C, 0x37, 0xE5,
 0x92, 0xEE, 0x49, 0xE2, 0xD4, 0x27, 0xCB, 0xD3, 0x2E, 0x87, 0x41, 0x1E, 0x9A, 0xA2, 0x66, 0x35,
 0x7E, 0xAF, 0x93, 0x5F, 0x45, 0x31, 0x3C, 0x86, 0xFE, 0xC6, 0xEF, 0x5B, 0x4C, 0x1B, 0x8B, 0x30,
 0xCB, 0xA7, 0x41, 0x6D, 0x71, 0x09, 0x16, 0x57, 0x7A, 0xC4, 0x71, 0xB0, 0x5F, 0xB8, 0x39, 0x8E,
 0x67, 0x77, 0xFC, 0xC7, 0xC9, 0x2D, 0xBF, 0x3C, 0x98, 0x3D, 0x9D, 0x38, 0xE7, 0xCF, 0x00, 0xF2,
 0x4F, 0x06, 0xE4, 0x92, 0x5F, 0xB1, 0x3D, 0x14, 0x86, 0x2D, 0x95, 0xDF, 0x8B, 0xE7, 0x1E, 0x2E,
 0x08, 0x12, 0x87, 0x0C, 0x15, 0xE2, 0xB4, 0x54, 0x8F, 0x76, 0xE9, 0xDC, 0x35, 0x71, 0x5D, 0x2C,
 0x86, 0x15, 0x40, 0xB5, 0xA5, 0x16, 0x96, 0xE5, 0xAA, 0xE0, 0x9A, 0x9D, 0x52, 0xAD, 0xC7, 0x8F,
 0x9E, 0x3D, 0x7D, 0x7A, 0xDA, 0x7C, 0xEC, 0x77, 0xC2, 0x41, 0xF2, 0xF7, 0x57, 0x71, 0x09, 0x16,
 0xBD, 0x8C, 0xC1, 0x42, 0x34, 0x3C, 0x3F, 0x16, 0xD2, 0xE6, 0x34, 0x38, 0x06, 0x15, 0x56, 0x28,
 0x95, 0xE4, 0xC6, 0x32, 0xBD, 0x52, 0x92, 0x10, 0x82, 0xB4, 0x83, 0x82, 0x25, 0x24, 0x82, 0x4C,
 0xC4, 0xB4, 0x22, 0x4A, 0x9A, 0x2A, 0x22, 0xBB, 0x43, 0xC7, 0xF3, 0xAA, 0x0B, 0x6B, 0x92, 0xB0,
 0x4F, 0xA8, 0xB0, 0xB3, 0x4A, 0x20, 0xB0, 0x09, 0x76, 0x7E, 0x33, 0xB2, 0x82, 0x26, 0xD3, 0x2F,
 0x71, 0x7B, 0x6E, 0xFD, 0x2F, 0xA7, 0x76, 0x03, 0xD4, 0xC7, 0x3C, 0xDA, 0x93, 0x93, 0xAB, 0xC5,
 0xCC, 0x43, 0x90, 0x71, 0xAA, 0xD6, 0x7B, 0x2C, 0x02, 0x17, 0xE0, 0x5D, 0xEA, 0xD6, 0x05, 0x29,
 0x32, 0x05, 0x8B, 0xF3, 0xAB, 0xA9, 0x8A, 0xC9, 0x8A, 0xBA, 0x8C, 0x44, 0xBC, 0xAC, 0x51, 0x48,
 0x88, 0xA3, 0x03, 0x11, 0x59, 0x43, 0xE4, 0x45, 0xE0, 0xDA, 0x5A, 0x55, 0xB5, 0xFE, 0xF3, 0xC7,
 0x8B, 0x4B, 0x0D, 0x92, 0xAC, 0x3A, 0xAE, 0xD5, 0xAB, 0x55, 0xFD, 0xFC, 0x58, 0x92, 0x6C, 0x2C,
 0xEB, 0x99, 0x6A, 0xDD, 0x0A, 0x51, 0xF5, 0x33, 0x10, 0x55, 0xAD, 0x37, 0xB6, 0x17, 0x75, 0xA6,
 0x5A, 0x42, 0x12, 0x08, 0x19, 0x3F, 0x3D, 0x3D, 0xDB, 0x5E, 0xD0, 0x53, 0xD0, 0xE9, 0x37, 0x90,
 0x74, 0x06, 0xD6, 0x9D, 0xEE, 0x62, 0xDC, 0xA9, 0x6A, 0x71, 0x39, 0xA7, 0x8D, 0xEA, 0xB8, 0x71,
 0xB6, 0x83, 0x9C, 0x27, 0x6A, 0x72, 0x2B, 0xC9, 0x43, 0x36, 0x1D, 0xA9, 0x56, 0xFB, 0xE7, 0xE7,
 0x5A, 0x03, 0x74, 0xAC, 0x3F, 0x3B, 0xDD, 0x5E, 0x76, 0x43, 0xB5, 0x7E, 0xE1, 0x4A, 0x9E, 0xD4,
 0x41, 0x50, 0x63, 0x07, 0x25, 0x4F, 0x54, 0xEB, 0xA5, 0x90, 0x04, 0x52, 0xC6, 0xB5, 0xA7, 0x3B,
 0xA8, 0x04, 0xE1, 0xF5, 0x8B, 0x90, 0x04, 0xF1, 0xC5, 0xC3, 0xEB, 0x81, 0x92, 0xA0, 0x50, 0x0A,
 0xD7, 0xDC, 0x93, 0xA7, 0x8B, 0xD5, 0xA7, 0x70, 0xF9, 0xBE, 0x34, 0xFE, 0x1C, 0x41, 0x4D, 0x67,
 0x93, 0x8D, 0x93, 0x38, 0xE1, 0x03, 0x93, 0xE4, 0xE0, 0x61, 0xF9, 0x9B, 0xD3, 0x24, 0x7B, 0x4A,
 0xA0, 0x5A, 0xB5, 0xEA, 0x1A, 0x0B, 0x04, 0x6F, 0xBE, 0x0A, 0x0A, 0xE6, 0x82, 0x01, 0xAA, 0x02,
 0xA2, 0x44, 0x0E, 0x2B, 0x7D, 0x34, 0x86, 0x18, 0x3D, 0x51, 0x73, 0x79, 0xBD, 0x55, 0x89, 0x58,
 0xA2, 0x2D, 0x1A, 0xAB, 0xD6, 0xE9, 0xC9, 0x3A, 0x7F, 0xEF, 0x00, 0x47, 0x47, 0x74, 0x70, 0x1F,
 0x87, 0xE1, 0xC6, 0x88, 0xCC, 0x58, 0x55, 0xEB, 0x2A, 0x1B, 0xEF, 0x82, 0x4B, 0xB9, 0xBE, 0x03,
 0x2E, 0x39, 0x75, 0x24, 0x34, 0xE5, 0x7A, 0x02, 0x4D, 0x5D, 0x9D, 0x65, 0xC4, 0xD7, 0x04, 0x66,
 0x9D, 0xB6, 0xBB, 0xE0, 0xC2, 0x9B, 0x78, 0x80, 0x42, 0xB6, 0x31, 0x2A, 0x29, 0x23, 0x94, 0xB5,
 0x64, 0x74, 0x30, 0x44, 0x32, 0x55, 0xFE, 0x01, 0x78, 0x84, 0x88, 0x45, 0x81, 0x78, 0xFA, 0xBE,
 0x31, 0x22, 0x33, 0x56, 0xE8, 0x87, 0xD9, 0xF8, 0x60, 0xA8, 0xE4, 0xD4, 0xF9, 0x27, 0xE0, 0x32,
 0xC0, 0x36, 0x41, 0xDE, 0x07, 0xEC, 0xBA, 0xD0, 0xB2, 0x36, 0xC7, 0xA6, 0xC0, 0x0E, 0xF8, 0xC8,
 0x63, 0xE5, 0x46, 0x1C, 0x6F, 0xBC, 0x46, 0x9C, 0x13, 0xF7, 0xB5, 0x16, 0x8A, 0xD5, 0xE5, 0xEB,
 0x96, 0x37, 0x34, 0xD3, 0x73, 0xCB, 0x15, 0x42, 0x0D, 0x84, 0xE0, 0xAE, 0xB8, 0xE7, 0xDB, 0x5A,
 0x46, 0x5D, 0xB5, 0x5E, 0x04, 0x68, 0x22, 0xBE, 0x86, 0xDD, 0x65, 0xD1, 0xF3, 0x1E, 0x3B, 0xCA,
 0xAF, 0x70, 0x23, 0xB7, 0xCB, 0x0A, 0xEC, 0x45, 0x80, 0xB1, 0xBF, 0x9B, 0x94, 0x27, 0xD0, 0xCC,
 0x60, 0xB0, 0x9B, 0x10, 0x58, 0xB0, 0xDE, 0xE2, 0x01, 0x41, 0xDF, 0xC3, 0x82, 0x0B, 0x8D, 0x3A,
 0x1B, 0xA7, 0x05, 0xF0, 0xA8, 0xD6, 0xE5, 0xEF, 0x57, 0x1B, 0x17, 0x29, 0xF9, 0xF0, 0xE9, 0x21,
 0x11, 0x2E, 0xAB, 0x53, 0xA2, 0xA0, 0xBA, 0x70, 0xB3, 0xB9, 0x3C, 0x73, 0x1E, 0x7A, 0xC3, 0xB9,
 0xC4, 0xAE, 0x54, 0x41, 0xF1, 0x7C, 0x46, 0xCD, 0x99, 0xF9, 0x30, 0x1B, 0xBF, 0x5D, 0x05, 0x03,
 0x25, 0x3E, 0x74, 0x11, 0xD9, 0xBC, 0xAF, 0xA4, 0x8C, 0x02, 0x29, 0xE5, 0x05, 0x8C, 0xF6, 0x05,
 0x97, 0x9C, 0xF6, 0x60, 0x98, 0x25, 0x56, 0x1F, 0x1A, 0x38, 0x50, 0xA4, 0x4F, 0x9D, 0xCD, 0x1F,
 0x47, 0x24, 0x7C, 0xAA, 0x05, 0xA8, 0xBD, 0x86, 0xC1, 0xC6, 0x5D, 0x26, 0x15, 0xF0, 0x8D, 0xDB,
 0xCB, 0x65, 0xC4, 0xE8, 0x2E, 0x9D, 0xE5, 0x36, 0xF2, 0xFD, 0xC9, 0x2E, 0x6D, 0xA5, 0xED, 0xD1,
 0xC8, 0xD9, 0x5E, 0x02, 0xF4, 0x94, 0xB7, 0xAE, 0x4B, 0xEC, 0xED, 0xBB, 0x12, 0x74, 0x94, 0x97,
 0xB4, 0xFF, 0x40, 0xFE, 0x6F, 0x5C, 0xC5, 0xB1, 0xBD, 0x79, 0x81, 0xC0, 0x36, 0xA0, 0x78, 0xD3,
 0x56, 0x6E, 0x6F, 0xDE, 0xDC, 0xBE, 0x7D, 0xBF, 0x9F, 0xEA, 0x00, 0x73, 0x1E, 0xA8, 0x30, 0x70,
 0x6B, 0x0F, 0x5D, 0x13, 0x40, 0x89, 0xFA, 0x36, 0x38, 0xD5, 0x25, 0x50, 0xD7, 0xB7, 0xEF, 0xF6,
 0x85, 0x52, 0xFD, 0x70, 0x30, 0xD5, 0xBF, 0x07, 0x9C, 0x3E, 0x78, 0x78, 0x88, 0xBD, 0x2D, 0xB0,
 0x92, 0x8C, 0x1C, 0x2F, 0xE5, 0x15, 0x1F, 0x1D, 0xEC, 0x46, 0x2E, 0x53, 0xE5, 0x1F, 0x70, 0x1B,
 0x07, 0x51, 0xF1, 0x41, 0x28, 0xBD, 0x4D, 0xF2, 0x48, 0x4E, 0xD5, 0xBA, 0x19, 0x0F, 0x68, 0x18,
 0x05, 0x0F, 0x6C, 0xA8, 0xCB, 0x11, 0xD9, 0xE5, 0xC9, 0xE0, 0x4C, 0x15, 0x89, 0x48, 0xFA, 0x68,
 0x90, 0x3F, 0xD9, 0xCF, 0x30, 0xA9, 0x57, 0x1B, 0x5F, 0x15, 0x15, 0x2E, 0xFC, 0x5B, 0x02, 0xD3,
 0xDD, 0xA2, 0xEF, 0x74, 0x79, 0xDF, 0x79, 0xD1, 0xDE, 0x4F, 0x29, 0xEB, 0x1E, 0xAC, 0xE1, 0x74,
 0x0F, 0xDA, 0x70, 0x14, 0xF9, 0x6D, 0x67, 0x06, 0xD3, 0x96, 0x37, 0x11, 0x09, 0x23, 0xDC, 0x3B,
 0x6F, 0x73, 0x03, 0x91, 0x7F, 0xA8, 0x3E, 0xDE, 0x25, 0x75, 0x52, 0x35, 0x8A, 0x99, 0x73, 0x32,
 0xCB, 0x9B, 0x27, 0x5F, 0x35, 0x6B, 0x4E, 0xD6, 0x6A, 0xBB, 0x4B, 0xD2, 0x70, 0x4B, 0x6C, 0x4C,
 0x3C, 0xFE, 0xD2, 0xE3, 0xA6, 0x80, 0xE4, 0x78, 0x25, 0x26, 0x4A, 0x5B, 0x1E, 0xED, 0x82, 0x4D,
 0x7D, 0x17, 0x6C, 0xF2, 0x1A, 0x15, 0xE1, 0x39, 0xFD, 0x46, 0x9D, 0xA6, 0x56, 0x3F, 0xFB, 0x96,
 0xF0, 0x74, 0x06, 0x9B, 0xD7, 0x34, 0xE0, 0x51, 0xAD, 0xAB, 0x77, 0xFB, 0xA9, 0x69, 0x7C, 0xB2,
 0x07, 0xD6, 0xB4, 0x9D, 0x2A, 0x98, 0x30, 0xEA, 0xD0, 0x4B, 0xB1, 0xD1, 0x16, 0x68, 0x8C, 0xB8,
 0xE2, 0xBF, 0xEF, 0x09, 0x8D, 0xD1, 0xC3, 0xD1, 0xF8, 0xCA, 0x1D, 0x66, 0xF4, 0x3D, 0xE0, 0x13,
 0xA0, 0xD1, 0x87, 0x6E, 0x1F, 0x6D, 0x8C, 0x51, 0xC2, 0xA7, 0x5A, 0xEF, 0xD1, 0x48, 0x79, 0xF1,
 0xFA, 0x72, 0x2F, 0x58, 0xA5, 0x93, 0x1E, 0x06, 0xAF, 0xCC, 0xE4, 0x43, 0x63, 0xE6, 0x61, 0x7F,
 0xF3, 0xA4, 0xE2, 0x4C, 0xAA, 0xF5, 0x0A, 0xFB, 0xA1, 0xD2, 0xA6, 0x41, 0xB2, 0xED, 0x68, 0x2F,
 0xA8, 0x89, 0x99, 0x0F, 0x03, 0x99, 0x34, 0xFA, 0xD0, 0x78, 0xF5, 0xFA, 0x24, 0x08, 0x68, 0xB0,
 0x31, 0x64, 0x09, 0x9F, 0x6A, 0xBD, 0x2C, 0xBF, 0x16, 0xA3, 0xBD, 0xC0, 0x95, 0xCE, 0x7A, 0x18,
 0xC4, 0x32, 0x9B, 0x0F, 0x0D, 0xDA, 0xD0, 0xF5, 0xC8, 0x60, 0x63, 0xC8, 0x04, 0x97, 0x6A, 0xFD,
 0x56, 0x7E, 0x0E, 0x9F, 0x7B, 0x81, 0x4B, 0xCE, 0x78, 0x18, 0xB0, 0x12, 0x6B, 0x0F, 0x0D, 0x95,
 0x63, 0x8F, 0x36, 0x06, 0x0A, 0x78, 0x54, 0xEB, 0xBA, 0xFD, 0xBB, 0xA2, 0x5D, 0xD3, 0x91, 0xCF,
 0x5F, 0xFC, 0x53, 0x6E, 0xDE, 0xE8, 0x7B, 0x41, 0x8C, 0x4F, 0x7D, 0x18, 0xBC, 0x84, 0xD1, 0x87,
 0x46, 0x4B, 0xBC, 0x04, 0xDC, 0x41, 0x9B, 0x97, 0xC3, 0x94, 0x91, 0xBF, 0xFB, 0x02, 0x23, 0xE5,
 0x0A, 0xED, 0xA7, 0x20, 0x66, 0xF3, 0xEE, 0x63, 0xD1, 0x3E, 0x33, 0xF2, 0xD0, 0x38, 0xB9, 0xC8,
 0xC6, 0x1F, 0x1C, 0xCC, 0xB6, 0x79, 0xF1, 0x22, 0xC7, 0xAB, 0x5A, 0xCF, 0xE1, 0x40, 0xB9, 0x16,
 0x07, 0xFB, 0x5A, 0x72, 0xE4, 0xE7, 0xDF, 0x07, 0x6A, 0x05, 0x7B, 0xBF, 0x0B, 0xE0, 0x60, 0x81,
 0x47, 0xBB, 0xFE, 0x56, 0xEF, 0x53, 0x17, 0xD8, 0x13, 0xF8, 0xDE, 0xCB, 0xE3, 0xFD, 0x02, 0x38,
 0x53, 0x62, 0x6F, 0x18, 0xE6, 0xEC, 0xDE, 0x07, 0x8C, 0xE9, 0x66, 0x04, 0xF1, 0x58, 0x40, 0xEE,
 0x41, 0x5E, 0x87, 0x94, 0x24, 0x93, 0x8F, 0x6E, 0x30, 0x2B, 0x87, 0x8C, 0x78, 0x9E, 0x6A, 0xBD,
 0xC0, 0x4C, 0xB9, 0xE5, 0xC3, 0xF3, 0x63, 0x49, 0xF0, 0x70, 0x29, 0xC9, 0x0B, 0xFF, 0x7C, 0xDF,
 0x38, 0xEA, 0xAB, 0xD6, 0x2D, 0xDF, 0x44, 0x0D, 0xB2, 0xF8, 0xD1, 0xE6, 0xC2, 0x84, 0x13, 0xB1,
 0x1F, 0x50, 0x50, 0x2A, 0x03, 0x29, 0xD9, 0xAA, 0xAA, 0x2A, 0xE9, 0x28, 0x77, 0xCE, 0xBA, 0x11,
 0xC4, 0x0A, 0x8F, 0xB2, 0xF5, 0xD3, 0xF1, 0x6F, 0x61, 0xED, 0xD5, 0x5F, 0xD6, 0x9E, 0x1F, 0xFB,
 0x68, 0x89, 0xBB, 0x57, 0xA0, 0x70, 0x2E, 0x77, 0xB1, 0xAF, 0x10, 0x95, 0x6D, 0xA6, 0x10, 0x9E,
 0x98, 0xED, 0xA7, 0xC9, 0xCC, 0x9A, 0xDB, 0x67, 0x93, 0x3E, 0xB0, 0x7D, 0x58, 0xD2, 0x8A, 0x1D,
 0x37, 0x49, 0x3F, 0xE4, 0xC3, 0xCC, 0xFD, 0xFF, 0xFB, 0xEF, 0xBA, 0x98, 0x21, 0xFD, 0x6E, 0x4E,
 0x31, 0x55, 0x09, 0x03, 0xBB, 0xA5, 0xAE, 0xDA, 0x9A, 0xB1, 0xC2, 0xF2, 0xE3, 0x65, 0xA6, 0xCF,
 0x11, 0x2F, 0xF1, 0xF5, 0x79, 0x68, 0x07, 0x64, 0xC0, 0xAC, 0x7F, 0x39, 0xD4, 0x8E, 0xFA, 0xD8,
 0x67, 0x15, 0xE4, 0x38, 0x37, 0x43, 0x18, 0xBC, 0x22, 0x21, 0xC3, 0xE0, 0x05, 0xAD, 0x74, 0xFD,
 0xF6, 0x75, 0x5B, 0x6E, 0x51, 0x79, 0x45, 0x91, 0x83, 0x9D, 0x92, 0xE1, 0x46, 0xBE, 0x90, 0xA3,
 0xE9, 0xD3, 0x74, 0xA8, 0x74, 0xB4, 0x2B, 0x7D, 0xEA, 0x41, 0xD0, 0xB6, 0x9B, 0xB2, 0x3C, 0x68,
 0x57, 0x15, 0x9E, 0xE3, 0xFA, 0xD4, 0x46, 0x21, 0x2E, 0xA5, 0x89, 0x5E, 0x32, 0xDB, 0xAD, 0xAB,
 0x4A, 0xB2, 0xF6, 0xB9, 0xA8, 0xF1, 0x0D, 0x4F, 0x60, 0xF4, 0x5D, 0x53, 0x10, 0x89, 0x47, 0x8A,
 0x25, 0x53, 0x8C, 0xE5, 0x97, 0xF3, 0x65, 0xEA, 0x63, 0xC9, 0x22, 0x1E, 0x5C, 0xE6, 0x89, 0x65,
 0x64, 0xA5, 0xD4, 0x51, 0xA7, 0x4F, 0x18, 0xA7, 0x2C, 0xD5, 0x4A, 0x09, 0x55, 0x52, 0x4A, 0xCC,
 0x00, 0xB3, 0x28, 0xF0, 0x9B, 0x31, 0x00, 0x1B, 0x32, 0xE5, 0xBA, 0xF5, 0xF1, 0x87, 0xA9, 0x1D,
 0x1F, 0x8B, 0x97, 0x5D, 0xA9, 0x77, 0x31, 0x44, 0x41, 0xEB, 0x87, 0xE9, 0x55, 0x85, 0x38, 0xF1,
 0x63, 0x98, 0x03, 0xC6, 0xED, 0xF8, 0x63, 0xD3, 0xE5, 0xFF, 0x71, 0x41, 0xBB, 0xD6, 0x2B, 0xAC,
 0x87, 0x7D, 0xED, 0xA6, 0x65, 0x4D, 0x39, 0x37, 0xF5, 0x70, 0xC5, 0xA3, 0x5D, 0xED, 0x63, 0x80,
 0x3F, 0x47, 0x18, 0x84, 0x31, 0xAA, 0xFC, 0x30, 0xBD, 0x8E, 0x15, 0x97, 0xF8, 0x24, 0xEC, 0x61,
 0xC7, 0x50, 0x42, 0x86, 0x58, 0x14, 0x9A, 0x70, 0xFA, 0xA6, 0x22, 0xC7, 0xF1, 0x47, 0x3D, 0xD6,
 0x63, 0x98, 0x46, 0xB1, 0x5B, 0x99, 0x97, 0x3D, 0x6A, 0x8B, 0x57, 0x3A, 0x2B, 0x34, 0x20, 0x5D,
 0xE2, 0x37, 0xA5, 0x6E, 0xB8, 0x75, 0x05, 0x33, 0x81, 0x7B, 0x78, 0x48, 0x71, 0x00, 0x38, 0x1A,
 0x5A, 0x49, 0xC6, 0x61, 0x49, 0x8F, 0x0D, 0x77, 0x81, 0x20, 0xC0, 0x7D, 0x3A, 0xC4, 0x79, 0x9A,
 0xEE, 0x72, 0x21, 0x69, 0x7E, 0x96, 0x74, 0xE3, 0x2A, 0xDB, 0x6B, 0xDE, 0x3A, 0xAA, 0xC6, 0x46,
 0x6F, 0xA5, 0xD0, 0x15, 0x3C, 0xB5, 0xD8, 0x20, 0x2D, 0xED, 0xCA, 0x68, 0x1B, 0xD7, 0x3A, 0x70,
 0x5E, 0xB7, 0x8E, 0x34, 0x3F, 0xF2, 0xBC, 0xA3, 0xD6, 0xB5, 0xFE, 0xF7, 0xDF, 0xD7, 0x4D, 0x1E,
 0x04, 0x37, 0xCD, 0x19, 0xE2, 0xAD, 0x56, 0x4B, 0x86, 0xC2, 0x05, 0x38, 0x32, 0xC3, 0xDE, 0x68,
 0xB7, 0x8E, 0x8E, 0xDA, 0x46, 0x76, 0xDC, 0x6A, 0xEB, 0xA6, 0xB8, 0x2E, 0x80, 0x36, 0x92, 0x4F,
 0x38, 0x6B, 0x5C, 0x3F, 0x7E, 0x7C, 0x73, 0xD4, 0x6A, 0xB5, 0x2F, 0x78, 0x88, 0x99, 0x47, 0x70,
 0xA8, 0x95, 0x10, 0xB6, 0xA5, 0x5C, 0xE2, 0x5C, 0xB4, 0x2F, 0xB0, 0x36, 0xD4, 0x4D, 0x97, 0xFF,
 0x29, 0xA1, 0x6E, 0xFE, 0x82, 0xE6, 0x6A, 0x4C, 0x37, 0xB0, 0x16, 0xEA, 0x20, 0x1C, 0xF3, 0xB1,
 0x2B, 0xC6, 0xA5, 0xF4, 0xAD, 0xA4, 0x1C, 0xAD, 0xAB, 0x8D, 0x75, 0x13, 0xF3, 0x3F, 0xA5, 0x62,
 0xE3, 0x48, 0x69, 0x60, 0xDE, 0xF6, 0x45, 0x4F, 0xF3, 0x75, 0xB3, 0x0B, 0x7F, 0x74, 0x3D, 0x6E,
 0x66, 0x70, 0x42, 0x34, 0x04, 0x93, 0x5B, 0x11, 0xB1, 0x34, 0xB8, 0xF4, 0x3C, 0xAD, 0x24, 0x77,
 0xE0, 0x95, 0xF4, 0x0A, 0x74, 0xA2, 0x1B, 0xC4, 0xB3, 0x41, 0xF8, 0x98, 0xFA, 0xB6, 0x47, 0xEC,
 0xBB, 0x96, 0xC6, 0x1D, 0x87, 0x21, 0x45, 0xE4, 0xDE, 0xE0, 0x37, 0xD4, 0xC1, 0x7A, 0x1C, 0x83,
 0x7A, 0x22, 0xEE, 0x64, 0x84, 0xCA, 0xF0, 0xF9, 0x98, 0xC4, 0x60, 0x96, 0x73, 0x90, 0x66, 0x32,
 0xA2, 0x95, 0xAB, 0xCA, 0xA7, 0x90, 0x27, 0x61, 0xBC, 0x84, 0xE4, 0x3E, 0xD5, 0x8A, 0x3D, 0x36,
 0xA7, 0x63, 0x1B, 0x94, 0x22, 0x1A, 0x80, 0xF2, 0x67, 0x1B, 0xEC, 0xFD, 0xCB, 0x38, 0xAA, 0xF1,
 0xD0, 0xD5, 0x93, 0xE8, 0xFC, 0x34, 0x0B, 0x5F, 0xE8, 0x53, 0x37, 0x1E, 0xE6, 0xC3, 0xAB, 0xC9,
 0xCF, 0x10, 0x5C, 0xB2, 0x72, 0x41, 0x98, 0xDC, 0xAD, 0xA3, 0x99, 0x95, 0x57, 0xA0, 0xF6, 0x56,
 0x53, 0x67, 0x9D, 0x10, 0xC8, 0xFA, 0xAB, 0xC9, 0x0A, 0xAD, 0x0E, 0x48, 0xFD, 0xD5, 0xA4, 0xB9,
 0x46, 0x06, 0x84, 0x74, 0x35, 0x61, 0xBE, 0x7C, 0x03, 0xE5, 0x40, 0x82, 0x35, 0x22, 0xBE, 0x43,
 0x47, 0x90, 0xD3, 0x74, 0xA0, 0x81, 0x4A, 0x15, 0xE2, 0x83, 0x0D, 0x2F, 0x7F, 0x7D, 0xFD, 0xAA,
 0x55, 0xCA, 0x37, 0xD8, 0x52, 0x6C, 0x7C, 0x96, 0x0C, 0x9F, 0x2A, 0xBC, 0x8E, 0x73, 0x28, 0x7F,
 0x2A, 0x99, 0x67, 0xB5, 0x12, 0x07, 0x94, 0x53, 0x7C, 0x84, 0x18, 0xBC, 0x5B, 0x90, 0x40, 0x07,
 0x99, 0x80, 0xA6, 0x57, 0x0C, 0x13, 0x3E, 0xDF, 0x4C, 0x18, 0x54, 0x2E, 0x34, 0x00, 0xF8, 0xF1,
 0xC5, 0x07, 0xBB, 0x03, 0xD5, 0xEA, 0x1A, 0x31, 0x5C, 0xF1, 0xE9, 0x08, 0xC2, 0x40, 0x4A, 0x8E,
 0x0D, 0xBA, 0xC8, 0x8F, 0xC5, 0x85, 0x7E, 0xF1, 0x82, 0x84, 0xF5, 0xAA, 0x38, 0x3D, 0x04, 0x7B,
 0x4E, 0xB5, 0xE6, 0xD5, 0x05, 0xB0, 0x9B, 0x9F, 0x41, 0xBA, 0xE1, 0x17, 0xB9, 0x3B, 0x90, 0x04,
 0xB1, 0xB1, 0x55, 0x9C, 0x65, 0xB9, 0xD0, 0xE3, 0x05, 0x5F, 0x88, 0xE3, 0xB9, 0x9D, 0x45, 0x5A,
 0xB0, 0x1A, 0x1C, 0x9E, 0xDF, 0xBA, 0x11, 0xDE, 0x4B, 0x90, 0xFB, 0x66, 0x15, 0x68, 0xD9, 0x3D,
 0x41, 0x36, 0xFF, 0xBD, 0x5F, 0x49, 0x6F, 0x06, 0x45, 0xBD, 0xC0, 0xCC, 0x40, 0x37, 0x82, 0xAC,
 0x63, 0xAD, 0xA8, 0x28, 0x71, 0xA2, 0x79, 0x74, 0x8F, 0x62, 0x98, 0x6B, 0x3E, 0xBC, 0x97, 0x20,
 0xFF, 0x4E, 0x05, 0xE8, 0x12, 0x2D, 0xE8, 0x12, 0xE9, 0x46, 0x94, 0xE9, 0x92, 0x95, 0xBD, 0x74,
 0xF6, 0xD1, 0x3D, 0xC2, 0xD3, 0x82, 0xA7, 0x1B, 0xE3, 0xD5, 0x54, 0x85, 0x57, 0x24, 0x41, 0x81,
 0xD1, 0x82, 0x02, 0x23, 0xDD, 0x18, 0x65, 0x0A, 0x64, 0x25, 0x33, 0x55, 0x60, 0xB2, 0x26, 0xFD,
 0xE4, 0x0D, 0x15, 0xE8, 0xF0, 0x65, 0x0D, 0xE1, 0xAC, 0xF8, 0xEA, 0xC6, 0xE5, 0x3D, 0xB4, 0xE9,
 0x1E, 0x4F, 0xD0, 0xF5, 0x72, 0x41, 0xD7, 0x4B, 0xDD, 0x78, 0x72, 0x7E, 0x29, 0x1B, 0x09, 0x14,
 0x6F, 0xA2, 0x4D, 0x78, 0x45, 0x33, 0x88, 0xF6, 0x85, 0x7F, 0x42, 0xF0, 0x4E, 0xE6, 0x58, 0x92,
 0xBA, 0x9A, 0x31, 0x5D, 0x68, 0xC8, 0xC3, 0x01, 0xD3, 0x4A, 0xEF, 0x3C, 0x0C, 0xAB, 0x8C, 0xE4,
 0xAD, 0x4B, 0xA5, 0xFD, 0xF3, 0x73, 0x85, 0x06, 0x8A, 0xF8, 0x0F, 0x03, 0x4A, 0x90, 0xED, 0x50,
 0x55, 0xE4, 0x26, 0x72, 0x05, 0xF3, 0x7F, 0xCB, 0x01, 0x21, 0xA5, 0xB0, 0x1E, 0x09, 0x15, 0x17,
 0xF3, 0xFD, 0x1B, 0xF8, 0x88, 0x63, 0x4F, 0x89, 0xA3, 0x24, 0x5A, 0xE8, 0x26, 0x3F, 0xD2, 0x3A,
 0xDA, 0x44, 0x37, 0x8E, 0x26, 0xA9, 0x47, 0x41, 0x4B, 0xDE, 0x5B, 0x32, 0x15, 0x41, 0xC7, 0x2F,
 0x07, 0xD1, 0xF1, 0x4B, 0x41, 0xC7, 0x2F, 0x00, 0xD8, 0x2C, 0x03, 0x7A, 0x52, 0x43, 0x30, 0xA3,
 0xAA, 0x27, 0xBD, 0x10, 0x5A, 0x57, 0x33, 0xBF, 0xCC, 0x4C, 0x16, 0x95, 0xF2, 0x48, 0x6E, 0xD7,
 0x3E, 0x3F, 0x16, 0xFF, 0x6A, 0xEE, 0xFF, 0x81, 0x09, 0x07, 0x8B, 0x81, 0x4E, 0x00, 0x00
};

 

CameraWebServer.ino

#include "esp_camera.h"
#include <WiFi.h>
//
// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
//            or another board which has PSRAM enabled
//
// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_M5STACK_PSRAM
#define CAMERA_MODEL_AI_THINKER
const int WiFi_led = 14;
const char* ssid = "jervisdesktop";
const char* password = "joydip98";
int FACE_ID;
String student, student1, student2; 
const char *server = "maker.ifttt.com";
const char* resource = "/trigger/attendance/with/key/hUAAAz0AVvc6-NW1UmqWXXv6VQWmpiGFxx3sV5rnaM9";
#if defined(CAMERA_MODEL_WROVER_KIT)
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    21
#define SIOD_GPIO_NUM    26
#define SIOC_GPIO_NUM    27
#define Y9_GPIO_NUM      35
#define Y8_GPIO_NUM      34
#define Y7_GPIO_NUM      39
#define Y6_GPIO_NUM      36
#define Y5_GPIO_NUM      19
#define Y4_GPIO_NUM      18
#define Y3_GPIO_NUM       5
#define Y2_GPIO_NUM       4
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    15
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23
#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM        5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       32
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21
#elif defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22
#else
#error "Camera model not selected"
#endif
void startCameraServer();
void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  //drop down frame size for higher initial frame rate
  sensor_t * s = esp_camera_sensor_get();
  s->set_framesize(s, FRAMESIZE_QVGA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  while (WiFi.status() == WL_CONNECTED) {
    digitalWrite(WiFi_led, HIGH);
    Serial.print("LED High");
  }
  startCameraServer();
  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");
}
void loop() {
  // put your main code here, to run repeatedly:
  Serial.print("Face ID: ");
  Serial.print(FACE_ID);
  if (FACE_ID == 1){
      student = "Thor";
      makeIFTTTRequest();
      delay(10000);
  }  
  if (FACE_ID == 2){
      student = "Ashish Mishra";
      makeIFTTTRequest();
      delay(10000);
  }
  if (FACE_ID == 3){
      student = "Joydip Dutta";
      makeIFTTTRequest();
      delay(10000);
  }
  if (FACE_ID == 4){
      student = "Ashish Choudhary";
      makeIFTTTRequest();
      delay(10000);
  }    
}
void makeIFTTTRequest() {
  Serial.print("Connecting to "); 
  Serial.print(server);  
  WiFiClient client;
  int retries = 5;
  while(!!!client.connect(server, 80) && (retries-- > 0)) {
    Serial.print(".");
  }
  Serial.println();
  if(!!!client.connected()) {
    Serial.println("Failed to connect...");
  }  
  Serial.print("Request resource: "); 
  Serial.println(resource);
  // Temperature in Celsius
 // String jsonObject = String("{\"value1\":\"") + student + "\"}";
 String jsonObject = String("{\"value1\":\"") + student + "\",\"value2\":\"" + student1
                      + "\",\"value3\":\"" + student2 + "\"}";                      
  // Comment the previous line and uncomment the next line to publish temperature readings in Fahrenheit                    
  /*String jsonObject = String("{\"value1\":\"") + (1.8 * bme.readTemperature() + 32) + "\",\"value2\":\"" 
                      + (bme.readPressure()/100.0F) + "\",\"value3\":\"" + bme.readHumidity() + "\"}";*/                      
  client.println(String("POST ") + resource + " HTTP/1.1");
  client.println(String("Host: ") + server); 
  client.println("Connection: close\r\nContent-Type: application/json");
  client.print("Content-Length: ");
  client.println(jsonObject.length());
  client.println();
  client.println(jsonObject);        
  int timeout = 5 * 10; // 5 seconds             
  while(!!!client.available() && (timeout-- > 0)){
    delay(100);
  }
  if(!!!client.available()) {
    Serial.println("No response...");
  }
  while(client.available()){
    Serial.write(client.read());
  }  
  Serial.println("\nclosing connection");
  client.stop(); 
}
Video

Have any question realated to this Article?

Ask Our Community Members

Comments

I'm confused by the schematic. I assume that switch U6 is an ordinary slide switch: in one position pin 2 is connected to pin 1, and in the other position pin 2 is connected to pin 3. When pin 2 is connected to pin 3 there apprears to be a short circuit from battery positive to battery negative (ground). What am I missing? It also appears that the LED in series with R6 will be lighted continually, until it discharges the battery.

Well Ashish, it seem you have changed the schematic to correct the problem I pointed out, but without acknowledging that there was a problem. Your original schematic had the battery positive terminal connected to pin 3, now it is connected to pin 1.