Skip to content

Final Project

This week I worked on defining my final project idea and started to getting used to the documentation process. Prior to that we also had discussions with the local instructors on probable projects. We came up with a number of project ideas and presented to the group of students in our lab and received feedbacks on its possibility. These were the topics I presented:

  • Rocker/Swing with Baby monitor
  • Crawler for disaster application
  • Autobot vacuum cleaner that returns to home for recharging
  • PET to Filaments for 3D Printing
  • Paper recycling (cleaning ink with chemical and drying)
  • Tiny Top for learning Similar to pitop but with Attiny
  • Smart Clock with LCD display that scrolls the calender of the day

From all the above topics, We finallized to work on “Crawler for disaster Application”.

Project Background

Disaster are unpredictable and happens regularly in different part of the world. Few of the common disasters to hit Bhutan are Fire, Landslide, Earthquake and Flood. During such time, people have very less time to make decision and maynot be able to run. This would cause them to be trapped inside the houses. It is very risky for people to go into the building directly and look for survivors as there is chances of another mishap. Therefore, current practice to use excavation to remove the collapsed parts in layer and look for bodies or survivors. However, If we can have a tiny eye that we can send into the collapsed house or any structure before excavation, we can reduce the risk of causing damage to the surviving individuals. Further, we can increase the survival rate of critically injured people if we can embed with suitable sensor to sense presence of life under the rubble.

Therefore, I will be working on designing and developing a small robot that can maneuver through rough terrains and send video signal through to a remote controller or a mobile device.


Project sketch and plan


Figure:: Initial Idea of the Project drawn using Microsoft Visio


Figure:: Free Hand Sketch of the project idea and application

The initial Idea of the project in presented above. The system consists of an app that will be used to communicate with the Robot to control its movement and display the video inputs coming in. The Robot will resemble a scaled version of tank that can maneuver through rough terrains. It will be fitted with a camera and ultrasonic sensors to visuallize the path and etermine obstacles. At this stage the following are the systems and components i will make use of.

  • 3D printing of the body of the system including wheel and chain
  • Circuit design to connect the sensors and camera to the Controller board.
  • Design of the Application capable of receiving video and sensor inputs.

Project Activities Tracking
The weeks in the activity tracking below donot concide with the weekly assignment but with the tasks that would be completed during the weeks assignment relevent to the final project.

Weeks Sessions/Topics Project Tasks Status
Week 1 Principle and Practics Plan your Project and Create Sketch DONE
Week 2 Computer-aided Design Create 2D and 3D model of Project -2D Done
-3D working
Week 3 Computer Controlled Cutting Create and Add logo to Project After Project Assembly
Week 4 Electronics Production Design and Produce PCB for Project Work in Progress will be completed in week 17-18
Week 5 3D Scanning and Printing Print 3D model of Project Component Print the casing for battery, Sprocket design and Printing
Week 6 Electronics Design Create a priliminary Final Project PCB desgn that can be improved further in week Created a priliminary design
Week 7 Embedded Programming Write program for interfacing the Controller with the webview and application Created the webview and programmed controller to get output from the webview
Week 8 Molding and Casting Create mold and cast for a part of final project created mold for sprocket and cast with hydrostone. However the cast was not as expected for the project and epoxy rasin was not available to make cast out of it. Will use 3D printing for sprocked instead
Week 9 Input Devices Work with different input device to choose best one for the project tested with PIR, Motion sensor, Ultrasonic sensor, IR sensor
Week 10 Networking and Communication Work with connecing ESP to WiFi, communication of the ESP with application and contol of ESP through WiFi Created two applications that can be used to contol the ESP, Also tried with bluetooth module
Week 11 Interfacing and Application Continue with previous week to improve the communication Webview and video stream from ESP32 camera module
Week 12 Application and Implication Brainstorm and find suitable improvements in the project Application of the robot is in disaster especially Earthquake rescue
Week 13 Invention, IP and Income Generate License for the Prooject The project will be licensed under open source Creative Commons license with the work that can be freely usable for interested fabbers and other innovators
Week 14 Project Development Concentrate in finalizing all components of Project Work In Progress

Materials Required and Pricing

sl.no Description Qty Price Total
1 ESP32 Cam module 1 Nu.900 Nu.900
2 Camera Module 1 - -
3 Ultrasonic Sensors 4 Nu.150 Nu.600
4 DC Motors 2 Nu.300 Nu.600
5 Bearings 5 Nu.300 Nu.300
6 3D Printing Parts 5 Nu.1000 (Estimate)
TOTAL - Nu.3400 (USD45)

2D Modelling of Project

In order to create a 2D model of my Project with GIMP, I used the path tool and convert path to selection where random shapes and curves are present. Standard shapes are created using rectangular and elleptical selection tool. Layer is a handy tool in GIMP whereby we can create different objects on different layer, edit them separately and merge them in the end.

The shape of the wheel uses standard ellipse shape while all other non standard shape use path tool with path to selection.

The Project File can be downloaded from HERE

Using Inkscape as well, i drew the unscaled 2D model of the project. The figure is shown below. Since, Inkscape uses object and object remain independent, it is easier to work with Inkscape than GIMP for creating and editing object.


The Project File can be downloaded from HERE


3D Modeling

For the 3D Design, I am using Fusion360 software. For the Project, I begin with the design of chain link followed by sprocket and the chassie. I have started by making the individual parts and then to assemble them in the end.

For the first part I have started with the design of single part of the chain link. The link which is then copied in to multiple parts and joined to create the complete link. For the initial stage, i will create individual components and then join it after 3D printing.

The 3D printable link uses a groove and ridge. This will help in creating a joint while adding flexibility in creating rotation joint.




Using the mirroring technique along a plane, I was able to create the mirrors of the individual links along a path that resembles the tank link.

The design of Sprocket is a tricky task as the design rule and mathematical relations needs to be known:

Torque or Speed

For a chain and gear system, the size and number of teeth in the gear system determines the torque and speed of the machine. For a tank, we have a single drive sprocket that will be connected directly to the motor and it will determine the torque and speed of the overall drive. Further, the size of the drive system and weight also determine the overall performance.

The torque and soeed have inverse proportionality. Which implies that, increasing the speed would reduce the torque and decreasing the speed would increase the torque.In gear and sprocket system, the ratio of the driving and driven gear/sprocket will determine how the speed and torque changed with the gear ratio. For the current project, will would want the tank to travel in terrains that have more obstacles hence torque is of importance with nominal speed. The system is also controlled and used in smaller areas and hence require lower speed.

Other terms of Interest:

  1. Pitch Diameter = Arbitary (50mm)
  2. Radial Pitch = 17mm from the track design

Module = Pitch Diameter / Number of Teeth Let Number of Teeth = 20; Module = 50/20 = 2.5 ;

OR

The formula for the number of teeth is:

N = PD
N = (πD ) / p
Where:
P = Diametral pitch
p = Circular pitch = 17.06mm
N = number of teeth
D = Pitch diameter, Assumed 50mm
Π = 3.14
Therefore: N = 3.14 x 50 /17.06
             = 9.2  (Fractional Value of N not desirable)

Using Reverse Method to determine D:         
D = N*p/3.14 
    Let N = 12;
D  = 10*17.06/3.14 =  54.33mm

And Module M = P/3.14159 = 17.06/3.14159 = 5.4303
In Case of Straight Pitch:
D (Pitch Diameter) = Pitch /(sin(180/N))
                   = 17.06/sin(18) = 55.20731
Using the above design rules and some assumptions, I begin with creating a sprocket. The sprocket will have 10 number of teeth and a pitch diameter of 55.207mm. Further the pitch of the sprocket is 17.06mm. I did a back calculation after the design of the chain link that gave an effective pitch of 17mm.

I created a number of concentric circles to give me the outer diameter, pitch diameter and inner diameter. On this, I create arcs with tangent constraints and make use of mirror to create a sprocket teeth.


The Sprocket teeth are also aligned with the track previously designed such that they would aligh properly and the teeth fit to the link. With one of the teeth designed, I make use of the pattern command to create a circular pattern of the teeth around the pitch diameter. This way there will be ten sets of identical teeths around the pitch diameter and with a desired pitch.


Further i have created some design and holes on the sprocked so that it can be fixed using a shaft. Further design will be conducted on the sprocked based on the need of the project.

The rollers for the design is necessary to hold the chain link properly in place along the base of the tank. This will have smaller dimensions compared to the sprocket. At this point, I am designing rollers of dimension 3cm each and a total of four rollers will be placed along the link.

For the current project, the size of the tank is discussed with a width of 120mm and length of 260mm. This design will not be printed by the 3D printing hardware in our lab and therefore needs to be fabricated in parts. I am creating two part chassie


3D Printing the Components

I have a couple of objects that needs to be 3D printed. The first component that i want to print is the chain motion link that can be joined and used as the link for the tank. I have the 3D designed in Fusion360 and have created a set of three links with motion animations.

The 3D model is then exported as .stl file and is inturn imported in Makerbot print software for generation of G-code.

The G-code was used to print the 3D file for movable chain link. The printed model is shown below.

The remaining parts will be printed as shown below to create the desired number of components.

Figure: Rectangular pattern to bulk print the model.

Since the 3D printed chain link would be harder, it was found that the chain link would slip on other harder surface decreasing friction and inturn the traction. Therefore, After the week of 3D printing I changed the chain link to wheel with rubber that would give better traction.

The video below shows the 3D printed sprocket:

ELECTRONICS


Controller ESP32- Cam Module

Since my robot has video as input, I am making use of ESP32 Camera module. The ESP32 camera module has the following specifications

  • WIFI module: ESP-32S
  • Processor: ESP32-D0WD
  • Built-in Flash: 32Mbit
  • RAM: Internal 512KB + External 4M PSRAM
  • Antenna: Onboard PCB antenna
  • WiFi protocol: IEEE 802.11 b/g/n/e/i
  • Bluetooth: Bluetooth 4.2 BR/EDR and BLE
  • WIFI mode: Station / SoftAP / SoftAP+Station
  • Security: WPA/WPA2/WPA2-Enterprise/WPS
  • Output image format: JPEG (OV2640 support only), BMP, GRAYSCALE
  • Supported TF card: up to 4G
  • Peripheral interface: UART/SPI/I2C/PWM
  • IO port: 9
  • UART baudrate rate: default 115200bps
  • Power supply: 5V
  • Operating temperature: -20 ℃ ~ 85 ℃
  • Storage environment: -40 ℃ ~ 90 ℃, <90%RH Transmitting power:
  • 802.11b: 17 ±2dBm(@11Mbps)
  • 802.11g: 14 ±2dBm(@54Mbps)
  • 802.11n: 13 ±2dBm(@HT20,MCS7) Receiving sensitivity:
  • CCK,1Mbps: -90 dBm
  • CCK,11Mbps: -85 dBm
  • 6Mbps(1/2 BPSK): -88 dBm
  • 54Mbps(3/4 64-QAM): -70 dBm
  • HT20,MCS7(65Mbps, 72.2Mbps): -67 dBm Power consumption:
  • Flash off: 180mA@5V
  • Flash on and brightness max: 310mA@5V
  • Deep-Sleep: as low as 6mA@5V
  • Modern-Sleep: as low as 20mA@5V
  • Light-Sleep: as low as 6.7mA@5V

  • Dimensions: 40.5mm x 27mm x 4.5mm

While the Camera Module is an excellent module for me to use, At this point, I am worried about the power requirement. I am designing with multiple voltage regulation circuits so that i can power the motor and the controller through a single power supply. This way I will not need to add multiple supplies however routing has been a difficult task. The details of the board design is shown in following sections.

Installing the ESP32 Board in Arduino IDE (Windows, Mac OS X, Linux) Referred the tutorial from:
[https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-windows-instructions/] (https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-windows-instructions/)

Added the json file from:
https://dl.espressif.com/dl/package_esp32_index.json to the Arduino Library and run the code:

/*
 *  This sketch demonstrates how to scan WiFi networks.
 *  The API is almost the same as with the WiFi Shield library,
 *  the most obvious difference being the different file you need to include:
 */
#include "WiFi.h"

void setup()
{
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);     // Set WiFi to station mode and disconnect from an AP if it was previously connected
    WiFi.disconnect();
    delay(100);
    Serial.println("Setup done");
}
void loop()
{
    Serial.println("scan start");

    int n = WiFi.scanNetworks();     // WiFi.scanNetworks will return the number of networks found
    Serial.println("scan done");
    if (n == 0) {
        Serial.println("no networks found");
    } else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            // Print SSID and RSSI for each network found
            Serial.print(i + 1);
            Serial.print(": ");
            Serial.print(WiFi.SSID(i));
            Serial.print(" (");
            Serial.print(WiFi.RSSI(i));
            Serial.print(")");
            Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
            delay(10);
        }
    }
    Serial.println("");
    delay(5000);     // Wait a bit before scanning again
}
For the ESP32-WROVER DEV board, I can make use of USB connection to directly program the board. The above code ran successfully and I was able to look at the serial monitor for the different WiFi signals SSID available.

Then I moved on to work with other code to control output devices from ESP. For the tutorial, It controls a relay module. In my case, I change the output device to an LED and control switching of the LED device. The code is shown below.

#include "WiFi.h"
#include "ESPAsyncWebServer.h"
const char* ssid = "FAB-LAB1";  //replace
const char* password =  "fab172030!"; //replace
AsyncWebServer server(80);
int relayPin = 23; // Output device changed to LED for my case
void setup(){
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, HIGH);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println(WiFi.localIP());
  server.on("/hello", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "Hello World");
  });
  server.on("/relay/off", HTTP_GET   , [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "ok");
    digitalWrite(relayPin, HIGH);
  });
   server.on("/relay/on", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
    digitalWrite(relayPin, LOW);
  });  
  server.on("/relay/toggle", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
    digitalWrite(relayPin, !digitalRead(relayPin));
  });  
  server.on("/relay", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", String(digitalRead(relayPin)));
  });
  server.begin();
} 
void loop(){}
TCP Error
Got an error that AsyncTCP.h was missing:

Solved after adding the zip file as library from: https://github.com/me-no-dev/AsyncTCP

Once the code compiled, I upload the code to the ESP module and check for its output on the IP Adress generated.


Project Board Development.

At this stage, I have developed the Final Project board. The project board consists of ESP32 Camera Module that streams camera video while controlling two motors and reading inputs from three sensors.

The images below are the Project board development.

Project Circuit Board Schematic Design.

Project Circuit Board Board Design.

Project Circuit Board Milling in Progress.

Project Circuit Board Soldering

Project Circuit Board Testing Setup

Motor Motion with Remote control through Application.

I have tried to control two motors connected to project board and motor driver. The voltage requirement of the motor is 12V while the ESP requires 3.3V. Therefore i have also designed power supply system on the project board with voltage regulators 78M12 and SOT with voltage output of 3.3V for supply to Motor and the board. Further, I designed a battery holder with series parallel connection that can house four batteries of 9V 900mAh each giving a total voltage of 18V and 3600mAh. This supply should be able to run the motor continuously for few hours.

The setup of the circuit for testing is shown belowing following which is the video of me controlling the motor directions through the application.

Embedded Programming

//Viral Science www.youtube.com/c/viralscience  www.viralsciencecreativity.com
//ESP32 Camera Surveillance Car
// Editied by Kamal K Chapagai May2022
#include "esp_camera.h"
//#include <esp32cam.h>
#include <WiFi.h>
//#define proxy_pass_request_headers off
// Select camera model
#define CAMERA_MODEL_WROVER_KIT
const char* ssid = "XXXXXX";   //Enter SSID WIFI Name
const char* password = "XXXXXX";   //Enter WIFI Password

#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_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

// GPIO Setting
extern int gpLb = 14 ; // Left 1
extern int gpLf = 13; // Left 2
extern int gpRb = 0; // Right 1
extern int gpRf = 2; // Right 2
extern int Sense1 = 32 ; // Light
extern int Sense2 = 33 ; // Light
extern int Sense3 = 15 ; // Light

extern String WiFiAddr ="";

void startCameraServer();

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  pinMode(gpLb, OUTPUT); //Left Backward
  pinMode(gpLf, OUTPUT); //Left Forward
  pinMode(gpRb, OUTPUT); //Right Forward
  pinMode(gpRf, OUTPUT); //Right Backward
  pinMode(Sense1, INPUT); //Light
  pinMode(Sense2, INPUT); //Light
  pinMode(Sense3, INPUT); //Light

  //initialize
  digitalWrite(gpLb, LOW);
  digitalWrite(gpLf, LOW);
  digitalWrite(gpRb, LOW);
  digitalWrite(gpRf, LOW);

  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_CIF);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  startCameraServer();
  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  WiFiAddr = WiFi.localIP().toString();
  Serial.println("' to connect");
}

void loop() {
  // put your main code here, to run repeatedly:
}
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//Edited by Kamal K Chapagai, May2022
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_http_server.h"
#include "esp_timer.h"
#include "esp_camera.h"
#include "img_converters.h"
#include "camera_index.h"
#include "Arduino.h"

extern int gpLb;
extern int gpLf;
extern int gpRb;
extern int gpRf;
extern int Sense1;
extern int Sense2;
extern int Sense3;
extern String WiFiAddr;
void WheelAct(int nLf, int nLb, int nRf, int nRb);
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 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 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.printf("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 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", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
    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[128];
    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){
        fb = esp_camera_fb_get();
        if (!fb) {
            Serial.printf("Camera capture failed");
            res = ESP_FAIL;
        } else {
            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.printf("JPEG compression failed");
                    res = ESP_FAIL;
                }
            } else {
                _jpg_buf_len = fb->len;
                _jpg_buf = fb->buf;
            }
        }
        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 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)"
         //   ,(uint32_t)(_jpg_buf_len),
         //   (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time,
         //   avg_frame_time, 1000.0 / avg_frame_time
        //);
    }
    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 {
        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, "\"hmirror\":%u,", s->status.hmirror);
    p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
    p+=sprintf(p, "\"colorbar\":%u", s->status.colorbar);
    *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");
    String page = "";
     page += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\">\n";
 page += "<script>var xhttp = new XMLHttpRequest();</script>";
 page += "<script>function getsend(arg) { xhttp.open('GET', arg +'?' + new Date().getTime(), true); xhttp.send() } </script>";
 //page += "<p align=center><IMG SRC='http://" + WiFiAddr + ":81/stream' style='width:280px;'></p><br/><br/>";
 page += "<p align=center><IMG SRC='http://" + WiFiAddr + ":81/stream' style='width:300px; transform:rotate(180deg);'></p><br/><br/>";

 page += "<p align=center> <button style=background-color:lightgrey;width:90px;height:80px onmousedown=getsend('go') onmouseup=getsend('stop') ontouchstart=getsend('go') ontouchend=getsend('stop') ><b>Forward</b></button> </p>";
 page += "<p align=center>";
 page += "<button style=background-color:lightgrey;width:90px;height:80px; onmousedown=getsend('left') onmouseup=getsend('stop') ontouchstart=getsend('left') ontouchend=getsend('stop')><b>Left</b></button>&nbsp;";
 page += "<button style=background-color:indianred;width:90px;height:80px onmousedown=getsend('stop') onmouseup=getsend('stop')><b>Stop</b></button>&nbsp;";
 page += "<button style=background-color:lightgrey;width:90px;height:80px onmousedown=getsend('right') onmouseup=getsend('stop') ontouchstart=getsend('right') ontouchend=getsend('stop')><b>Right</b></button>";
 page += "</p>";

 page += "<p align=center><button style=background-color:lightgrey;width:90px;height:80px onmousedown=getsend('back') onmouseup=getsend('stop') ontouchstart=getsend('back') ontouchend=getsend('stop') ><b>Backward</b></button></p>";  

 page += "<p align=center>";
 page += "<button style=background-color:yellow;width:140px;height:40px onmousedown=getsend('ledon')><b>Light ON</b></button>";
 page += "<button style=background-color:yellow;width:140px;height:40px onmousedown=getsend('ledoff')><b>Light OFF</b></button>";
 page += "</p>";

    return httpd_resp_send(req, &page[0], strlen(&page[0]));
}

static esp_err_t go_handler(httpd_req_t *req){
    WheelAct(HIGH, LOW, HIGH, LOW);
    Serial.println("Go");
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, "OK", 2);
}
static esp_err_t back_handler(httpd_req_t *req){
    WheelAct(LOW, HIGH, LOW, HIGH);
    Serial.println("Back");
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, "OK", 2);
}

static esp_err_t left_handler(httpd_req_t *req){
    WheelAct(HIGH, LOW, LOW, HIGH);
    Serial.println("Left");
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, "OK", 2);
}
static esp_err_t right_handler(httpd_req_t *req){
    WheelAct(LOW, HIGH, HIGH, LOW);
    Serial.println("Right");
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, "OK", 2);
}

static esp_err_t stop_handler(httpd_req_t *req){
    WheelAct(LOW, LOW, LOW, LOW);
    Serial.println("Stop");
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, "OK", 2);
}

static esp_err_t ledon_handler(httpd_req_t *req){
    digitalWrite(Sense1, HIGH);
    Serial.println("LED ON");
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, "OK", 2);
}
static esp_err_t ledoff_handler(httpd_req_t *req){
    digitalWrite(Sense1, LOW);
    Serial.println("LED OFF");
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, "OK", 2);
}

void startCameraServer(){
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_uri_t go_uri = {
        .uri       = "/go",
        .method    = HTTP_GET,
        .handler   = go_handler,
        .user_ctx  = NULL
    };
    httpd_uri_t back_uri = {
        .uri       = "/back",
        .method    = HTTP_GET,
        .handler   = back_handler,
        .user_ctx  = NULL
    };
    httpd_uri_t stop_uri = {
        .uri       = "/stop",
        .method    = HTTP_GET,
        .handler   = stop_handler,
        .user_ctx  = NULL
    };
    httpd_uri_t left_uri = {
        .uri       = "/left",
        .method    = HTTP_GET,
        .handler   = left_handler,
        .user_ctx  = NULL
    };
    httpd_uri_t right_uri = {
        .uri       = "/right",
        .method    = HTTP_GET,
        .handler   = right_handler,
        .user_ctx  = NULL
    }; 
    httpd_uri_t ledon_uri = {
        .uri       = "/ledon",
        .method    = HTTP_GET,
        .handler   = ledon_handler,
        .user_ctx  = NULL
    };   
    httpd_uri_t ledoff_uri = {
        .uri       = "/ledoff",
        .method    = HTTP_GET,
        .handler   = ledoff_handler,
        .user_ctx  = NULL
    };
    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);
    Serial.printf("Starting web server on port: '%d'", 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, &go_uri); 
        httpd_register_uri_handler(camera_httpd, &back_uri); 
        httpd_register_uri_handler(camera_httpd, &stop_uri); 
        httpd_register_uri_handler(camera_httpd, &left_uri);
        httpd_register_uri_handler(camera_httpd, &right_uri);
        httpd_register_uri_handler(camera_httpd, &ledon_uri);
        httpd_register_uri_handler(camera_httpd, &ledoff_uri);
    }
    config.server_port += 1;
    config.ctrl_port += 1;
    Serial.printf("Starting stream server on port: '%d'", config.server_port);
    if (httpd_start(&stream_httpd, &config) == ESP_OK) {
        httpd_register_uri_handler(stream_httpd, &stream_uri);
    }
}
void WheelAct(int nLf, int nLb, int nRf, int nRb)
{
 digitalWrite(gpLf, nLf);
 digitalWrite(gpLb, nLb);
 digitalWrite(gpRf, nRf);
 digitalWrite(gpRb, nRb);
}
//File: index.html.gz, Size: 3635 
#define index_html_gz_len 3635
const uint8_t index_html_gz[] = {
 0x1F, 0x8B, 0x08, 0x08, 0x8A, 0xF8, 0xFE, 0x5B, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E,
 0x68, 0x74, 0x6D, 0x6C, 0x00, 0xDD, 0x5C, 0xFD, 0x72, 0xDA, 0xB8, 0x16, 0xFF, 0x7F, 0x9F, 0xC2,
 0x71, 0x77, 0x8B, 0x3D, 0x6B, 0x08, 0x10, 0x92, 0xA6, 0x26, 0x90, 0x0D, 0x84, 0xB6, 0x3B, 0xD3,
 0xAF, 0x6D, 0xF6, 0xEE, 0xEE, 0xCC, 0xCE, 0x4E, 0x2B, 0x6C, 0x19, 0xD4, 0x18, 0x8B, 0xDA, 0x72,
 0x80, 0xB2, 0x7E, 0x8E, 0xFB, 0x40, 0xF7, 0xC5, 0xEE, 0x91, 0x64, 0x1B, 0x9B, 0x8F, 0x10, 0xA0,
 0x85, 0x4E, 0x9B, 0x19, 0x90, 0xE5, 0xA3, 0xA3, 0x73, 0xCE, 0xEF, 0x7C, 0x48, 0xC6, 0xEA, 0xC5,
 0x91, 0x4D, 0x2D, 0x36, 0x19, 0x62, 0xA5, 0xCF, 0x06, 0x6E, 0xF3, 0x87, 0x0B, 0xF9, 0xA5, 0xC0,
 0xBF, 0x8B, 0x3E, 0x46, 0xB6, 0x6C, 0x8A, 0xCB, 0x01, 0x66, 0x48, 0xB1, 0xFA, 0xC8, 0x0F, 0x30,
 0x6B, 0xA8, 0x21, 0x73, 0x8A, 0xE7, 0xEA, 0xFC, 0x6D, 0x0F, 0x0D, 0x70, 0x43, 0xBD, 0x23, 0x78,
 0x34, 0xA4, 0x3E, 0x53, 0x15, 0x8B, 0x7A, 0x0C, 0x7B, 0x40, 0x3E, 0x22, 0x36, 0xEB, 0x37, 0x6C,
 0x7C, 0x47, 0x2C, 0x5C, 0x14, 0x17, 0x06, 0xF1, 0x08, 0x23, 0xC8, 0x2D, 0x06, 0x16, 0x72, 0x71,
 0xA3, 0x92, 0xE5, 0xC5, 0x08, 0x73, 0x71, 0xB3, 0x73, 0xF3, 0xF6, 0xA4, 0xAA, 0xBC, 0xF9, 0xA3,
 0x5A, 0x3B, 0x2B, 0x5F, 0x1C, 0xCB, 0xBE, 0x19, 0x4D, 0xC0, 0x26, 0xFC, 0xBA, 0x4B, 0xED, 0xC9,
 0xD4, 0x81, 0x69, 0x8A, 0x0E, 0x1A, 0x10, 0x77, 0x62, 0x5E, 0xF9, 0xC0, 0xD4, 0x78, 0x81, 0xDD,
 0x3B, 0xCC, 0x88, 0x85, 0x8C, 0x00, 0x79, 0x41, 0x31, 0xC0, 0x3E, 0x71, 0xEA, 0x5D, 0x64, 0xDD,
 0xF6, 0x7C, 0x1A, 0x7A, 0xB6, 0xF9, 0xA8, 0x72, 0xCE, 0xFF, 0xEA, 0x16, 0x75, 0xA9, 0x6F, 0x3E,
 0xEA, 0x3C, 0xE3, 0x7F, 0x75, 0xC1, 0x27, 0x20, 0x9F, 0xB1, 0x59, 0x39, 0x1B, 0x8E, 0xA3, 0x7E,
 0x75, 0x9A, 0xE9, 0x39, 0x87, 0x9E, 0x00, 0x5B, 0x8C, 0x50, 0xAF, 0x34, 0x40, 0xC4, 0x9B, 0xDA,
 0x24, 0x18, 0xBA, 0x68, 0x62, 0x3A, 0x2E, 0x1E, 0x47, 0x8F, 0x06, 0xD8, 0x0B, 0x8D, 0xDC, 0x7D,
 0xDE, 0x5F, 0xB4, 0x89, 0x2F, 0xFB, 0x4C, 0x98, 0x2A, 0x1C, 0x78, 0x92, 0x30, 0x1D, 0xEB, 0x51,
 0x0F, 0xD7, 0x05, 0xE1, 0xC8, 0x47, 0x43, 0xB8, 0xE4, 0x5F, 0xF5, 0x01, 0xF1, 0xA4, 0x91, 0xCC,
 0x93, 0x5A, 0x79, 0x38, 0xCE, 0x09, 0x7E, 0x72, 0xC6, 0xFF, 0xEA, 0x43, 0x64, 0xDB, 0xC4, 0xEB,
 0x99, 0xE7, 0xFC, 0x36, 0xF5, 0x6D, 0xEC, 0x17, 0x7D, 0x64, 0x93, 0x30, 0x30, 0x6B, 0xD0, 0x33,
 0x40, 0x7E, 0x0F, 0x78, 0x30, 0x3A, 0x34, 0x8B, 0x95, 0xF2, 0xAC, 0xC3, 0x27, 0xBD, 0x3E, 0x33,
 0x79, 0x4F, 0xF4, 0x28, 0xC6, 0x26, 0xA7, 0x46, 0x46, 0x14, 0x21, 0x08, 0x72, 0x49, 0xCF, 0x2B,
 0x12, 0x86, 0x07, 0x81, 0x19, 0x30, 0x1F, 0x33, 0xAB, 0x1F, 0x39, 0xA4, 0x17, 0xFA, 0x78, 0x9A,
 0x08, 0x50, 0x8E, 0x79, 0x43, 0xA3, 0x38, 0xC2, 0xDD, 0x5B, 0xC2, 0x8A, 0xF1, 0x64, 0x5D, 0xEC,
 0x50, 0x1F, 0xA7, 0x04, 0xC5, 0xAE, 0x4B, 0xAD, 0xDB, 0x62, 0xC0, 0x90, 0xCF, 0x16, 0x89, 0x91,
 0xC3, 0xB0, 0x3F, 0x4F, 0x8B, 0x41, 0xE1, 0x05, 0xCA, 0x84, 0x41, 0x7C, 0x49, 0x3C, 0x97, 0x78,
 0x78, 0x15, 0x5B, 0xC9, 0x21, 0x4F, 0x2A, 0xFA, 0x62, 0x35, 0x14, 0x32, 0xE8, 0xA5, 0x16, 0x10,
 0x93, 0xD6, 0xA5, 0xE1, 0x2B, 0xE5, 0xF2, 0x4F, 0xF5, 0x3E, 0x16, 0xF6, 0x42, 0x21, 0xA3, 0xF7,
 0x1B, 0x99, 0xFB, 0xC6, 0x2F, 0x03, 0x6C, 0x13, 0xA4, 0x68, 0x33, 0xF0, 0x94, 0xF3, 0x32, 0x58,
 0x5A, 0x57, 0x90, 0x67, 0x2B, 0x1A, 0xF5, 0x09, 0x58, 0x1B, 0x09, 0x57, 0x70, 0xA1, 0x07, 0xDC,
 0x7E, 0x88, 0xF5, 0xE9, 0x3A, 0x18, 0x62, 0x8F, 0x58, 0x0D, 0xC4, 0x12, 0x0D, 0x06, 0x68, 0x5C,
 0xCC, 0x68, 0xC1, 0x2F, 0x63, 0x4D, 0x20, 0xD4, 0x2C, 0x0D, 0x3A, 0xEF, 0xFA, 0x4A, 0x51, 0xE1,
 0xAE, 0xA5, 0xC7, 0xEA, 0x0A, 0x15, 0x33, 0xEA, 0x7E, 0x2F, 0x28, 0x27, 0x11, 0xFB, 0xA8, 0x1B,
 0x32, 0x46, 0xBD, 0x60, 0x8D, 0x99, 0x3F, 0x86, 0x01, 0x23, 0xCE, 0xA4, 0x18, 0x83, 0x62, 0x06,
 0x43, 0x04, 0xF9, 0xAA, 0x8B, 0xD9, 0x08, 0x63, 0x08, 0x5D, 0x0F, 0xDD, 0x01, 0xDC, 0xBD, 0x9E,
 0x8B, 0xA7, 0x56, 0xE8, 0x07, 0x90, 0x39, 0x86, 0x94, 0x00, 0xA5, 0x5F, 0xCF, 0x01, 0x90, 0x25,
 0x2C, 0x5A, 0xDD, 0x29, 0x0D, 0x19, 0x17, 0x09, 0x44, 0xA4, 0xC0, 0x8F, 0xB0, 0x09, 0xB4, 0xA4,
 0xD9, 0xCB, 0x89, 0xCD, 0xCB, 0x73, 0x63, 0x4C, 0xAB, 0x8F, 0xAD, 0x5B, 0x6C, 0xFF, 0x9C, 0x4F,
 0x17, 0x22, 0xD5, 0x94, 0x88, 0x37, 0x0C, 0x59, 0x91, 0x27, 0x84, 0xE1, 0x1A, 0x7D, 0x84, 0x25,
 0xE2, 0x29, 0xAA, 0xD5, 0xD4, 0x67, 0xCD, 0xD3, 0xE1, 0x58, 0x29, 0xE7, 0x18, 0x35, 0x5D, 0xD4,
 0xC5, 0x6E, 0xCA, 0x2E, 0x36, 0xA2, 0xF4, 0xA7, 0xD8, 0x09, 0x32, 0xD9, 0x23, 0x93, 0xA1, 0x6A,
 0x4F, 0x7E, 0xCA, 0x31, 0x52, 0x44, 0xDB, 0xC8, 0x75, 0x05, 0xD8, 0x05, 0x18, 0x64, 0x42, 0x84,
 0x9E, 0x91, 0x59, 0x89, 0x4A, 0x3E, 0xF2, 0x7A, 0x18, 0x00, 0x1C, 0x1B, 0x49, 0x33, 0x93, 0x52,
 0x97, 0x4D, 0x6F, 0x96, 0x15, 0x10, 0x3B, 0x92, 0x40, 0x2E, 0x78, 0x7C, 0xA2, 0x56, 0x86, 0xBA,
 0x52, 0x4D, 0x73, 0x23, 0x18, 0x3A, 0x67, 0x0A, 0x9E, 0x35, 0xE7, 0x10, 0x8C, 0x2B, 0x81, 0xE3,
 0xE4, 0xEB, 0x84, 0xE3, 0x9C, 0x94, 0x4F, 0x6A, 0x73, 0xD1, 0xCF, 0xE7, 0xC9, 0xD7, 0x8A, 0x7A,
 0x8A, 0x71, 0x2C, 0xA0, 0xD9, 0xA7, 0x77, 0xD8, 0x9F, 0xE6, 0x59, 0xD5, 0x9E, 0xD6, 0xEC, 0xE4,
 0x3E, 0x02, 0xBF, 0xBC, 0xC3, 0x79, 0x82, 0x6A, 0xC5, 0xAA, 0x56, 0x62, 0x82, 0x12, 0x68, 0x88,
 0xBA, 0x2E, 0xB6, 0x13, 0x57, 0xB3, 0xB1, 0x83, 0x42, 0x97, 0xE5, 0xA4, 0x43, 0x65, 0xFE, 0x17,
 0x09, 0x5B, 0xFF, 0xCD, 0xCB, 0x78, 0x43, 0xD8, 0xF2, 0x9F, 0x69, 0x12, 0x20, 0x68, 0x38, 0xC4,
 0x08, 0xFA, 0x2C, 0x2C, 0x4B, 0xCD, 0x62, 0x72, 0x13, 0x6E, 0xB1, 0xA4, 0xC0, 0xCC, 0x99, 0x27,
 0x09, 0xFF, 0xC5, 0xB9, 0x4C, 0x87, 0x5A, 0x61, 0x30, 0x73, 0xF2, 0x25, 0x14, 0x66, 0x22, 0x4E,
 0xE0, 0x12, 0x61, 0xC6, 0xD0, 0xF3, 0xB8, 0x6E, 0x45, 0xE6, 0xC3, 0xC4, 0xD3, 0x25, 0x42, 0x2D,
 0xE2, 0x93, 0x15, 0x31, 0x2E, 0xD7, 0x79, 0x50, 0xCA, 0x29, 0xD6, 0x4A, 0x40, 0x61, 0x1E, 0x25,
 0x26, 0x7B, 0x80, 0x3C, 0xAC, 0x1F, 0x0E, 0xBA, 0xD3, 0x78, 0x78, 0x05, 0x62, 0x43, 0x32, 0xF0,
 0x7B, 0x5D, 0xA4, 0x95, 0x8D, 0xB2, 0x71, 0x02, 0x1F, 0x7A, 0xCE, 0x60, 0x52, 0xE4, 0x6A, 0x75,
 0xA1, 0xFA, 0x9E, 0xCE, 0xD7, 0xEB, 0xD8, 0x81, 0xE6, 0xB4, 0x59, 0x85, 0x4F, 0xAE, 0x70, 0x57,
 0x4A, 0xDC, 0xE1, 0x57, 0x18, 0x7C, 0x9D, 0x51, 0x17, 0xED, 0xB5, 0xD4, 0x10, 0x03, 0xFA, 0xB9,
 0x28, 0xE3, 0xEF, 0x60, 0x58, 0x64, 0x44, 0xD8, 0x37, 0x0E, 0xCB, 0xE5, 0x09, 0xB6, 0xB4, 0x45,
 0x59, 0x49, 0xF4, 0x2E, 0xCA, 0x6C, 0x02, 0x6C, 0x3C, 0x28, 0x21, 0x3E, 0x94, 0x92, 0xFA, 0x42,
 0xCF, 0xAA, 0xB9, 0x1D, 0xE2, 0xBA, 0x45, 0x97, 0x8E, 0xE6, 0xB2, 0x47, 0xCE, 0xCE, 0xF3, 0x76,
 0x9D, 0x37, 0xFF, 0xBD, 0xBC, 0x43, 0xF0, 0xB9, 0xAF, 0xC0, 0x7B, 0xFF, 0x41, 0x34, 0x03, 0xE5,
 0x9E, 0x20, 0x59, 0x67, 0xD1, 0x07, 0x0C, 0x5D, 0x34, 0x98, 0xCC, 0x91, 0x51, 0x29, 0x18, 0x11,
 0x58, 0x89, 0xCD, 0x15, 0xA3, 0x21, 0x0D, 0x88, 0x58, 0xE6, 0xF9, 0xD8, 0x45, 0x3C, 0xC9, 0x2F,
 0x96, 0xE1, 0xB9, 0xE2, 0x91, 0xB9, 0x95, 0xF0, 0x94, 0x65, 0xF4, 0x61, 0x4B, 0x87, 0x92, 0xCC,
 0x00, 0xB1, 0xBF, 0x0A, 0xE3, 0xE5, 0x92, 0x7B, 0xCE, 0xB6, 0xD5, 0x7B, 0x7D, 0x38, 0x76, 0xDC,
 0x9E, 0x8F, 0x27, 0x09, 0x5B, 0x23, 0xFE, 0x36, 0xE5, 0x4A, 0x6F, 0x79, 0x8D, 0x16, 0x7E, 0x2D,
 0xB5, 0x2E, 0xD5, 0x82, 0x68, 0x6E, 0xC8, 0xA2, 0x45, 0x92, 0x05, 0x96, 0xAA, 0x2E, 0x40, 0x9F,
 0x06, 0x9B, 0x30, 0x4D, 0x1C, 0x83, 0xBC, 0xE9, 0x62, 0x87, 0x89, 0x85, 0x37, 0xCF, 0x8E, 0x27,
 0x39, 0x0F, 0x29, 0xCE, 0xAA, 0xB7, 0xC4, 0x33, 0x5D, 0x3F, 0x25, 0xB6, 0x59, 0x46, 0xCB, 0x7D,
 0x6A, 0x39, 0x79, 0x22, 0x78, 0x92, 0x62, 0x85, 0x7A, 0xD0, 0x33, 0x90, 0x01, 0x0C, 0x4A, 0xE0,
 0xBF, 0xB4, 0xEA, 0x19, 0x5F, 0x3F, 0xAF, 0xBE, 0x15, 0xC5, 0xCB, 0x9E, 0x85, 0x90, 0x48, 0x4A,
 0x6C, 0xC6, 0x0B, 0x6A, 0x73, 0x98, 0xCD, 0x70, 0x5F, 0x58, 0x79, 0xC0, 0x6A, 0x6B, 0x80, 0x20,
 0x59, 0x72, 0x13, 0xC2, 0x36, 0x13, 0x74, 0x5B, 0x34, 0xEF, 0x6C, 0x79, 0x56, 0x39, 0xE3, 0x9B,
 0xBD, 0x92, 0xE5, 0xD2, 0x20, 0x83, 0x03, 0xEA, 0x82, 0x24, 0x21, 0xC3, 0x75, 0xB9, 0xA4, 0x3B,
 0x8D, 0x8D, 0x7A, 0xBA, 0x3C, 0xEC, 0x32, 0x18, 0x64, 0xA1, 0xC9, 0x4B, 0x56, 0xE1, 0x7B, 0x9D,
 0xEC, 0x2A, 0x8A, 0xE1, 0x31, 0xD4, 0x37, 0xBE, 0x6F, 0x31, 0x2D, 0x2C, 0xDC, 0x2C, 0x1B, 0x06,
 0x95, 0xC5, 0x25, 0x58, 0x54, 0xEA, 0x13, 0xDB, 0xC6, 0x5E, 0x6E, 0x73, 0x1C, 0xCD, 0x76, 0xFC,
 0xC7, 0xF1, 0x96, 0x5F, 0x5E, 0xCC, 0x9E, 0x4E, 0x5C, 0xF0, 0x67, 0x00, 0xD9, 0x27, 0x03, 0x72,
 0xC9, 0xAF, 0x58, 0x2E, 0x0A, 0x82, 0x86, 0xCA, 0xF7, 0xE2, 0x99, 0x87, 0x0B, 0x82, 0xC4, 0x26,
 0x77, 0x0A, 0xB1, 0x1B, 0xAA, 0x4B, 0x7B, 0x74, 0xEE, 0x9E, 0xB8, 0x2F, 0x16, 0xC3, 0x0A, 0xA0,
 0xDA, 0x50, 0x73, 0xCB, 0x72, 0x55, 0x8C, 0x9A, 0x75, 0xA9, 0xCD, 0xC7, 0x8F, 0x9E, 0x3E, 0x79,
 0x72, 0x56, 0x7F, 0xEC, 0x75, 0x83, 0x61, 0xFC, 0xF9, 0xBB, 0xB8, 0x05, 0x8B, 0x5E, 0xC6, 0x60,
 0x21, 0x1A, 0x5C, 0x1C, 0x0B, 0x6E, 0x73, 0x12, 0x1C, 0x83, 0x08, 0x2B, 0x84, 0x8A, 0x63, 0x63,
 0x99, 0x5C, 0x09, 0x49, 0x00, 0x4E, 0xDA, 0x45, 0xFE, 0x12, 0x12, 0x41, 0x26, 0x7C, 0x5A, 0x11,
 0x29, 0x4D, 0x15, 0x9E, 0xDD, 0xA5, 0xE3, 0x79, 0xD1, 0x85, 0x36, 0xB1, 0xDB, 0xC7, 0x54, 0xD8,
 0x5E, 0xC5, 0x10, 0x86, 0x89, 0xE1, 0x7C, 0x33, 0xB2, 0x82, 0x26, 0x95, 0x2F, 0x36, 0x7B, 0x66,
 0xFD, 0x2F, 0xA7, 0x76, 0x7C, 0x34, 0xC0, 0xDC, 0xDB, 0xE3, 0xCE, 0xD5, 0x6C, 0xE6, 0x21, 0x48,
 0x47, 0xAA, 0xCD, 0x77, 0x58, 0x38, 0x2E, 0xC0, 0xBB, 0xD4, 0xAC, 0x0B, 0x5C, 0x64, 0x08, 0xE6,
 0xE7, 0x57, 0x13, 0x11, 0xE3, 0x15, 0x75, 0x11, 0x09, 0x7F, 0x59, 0x23, 0x90, 0x60, 0x47, 0x87,
 0xC2, 0xB3, 0xEE, 0x90, 0x1B, 0x82, 0x69, 0x2B, 0x65, 0xB5, 0xF9, 0x9F, 0xBF, 0x9E, 0x5F, 0x69,
 0x10, 0x64, 0xE5, 0x71, 0xA5, 0x5A, 0x2E, 0xEB, 0x17, 0xC7, 0x92, 0x64, 0x63, 0x5E, 0x4F, 0xD5,
 0xE6, 0x8D, 0x60, 0x55, 0x3D, 0x07, 0x56, 0xE5, 0x6A, 0x6D, 0x7B, 0x56, 0xE7, 0x6A, 0x53, 0x70,
 0x02, 0x26, 0xE3, 0x27, 0x67, 0xE7, 0xDB, 0x33, 0x7A, 0x02, 0x32, 0xFD, 0x01, 0x9C, 0xCE, 0x41,
 0xBB, 0xB3, 0x5D, 0x94, 0x3B, 0x53, 0x9B, 0x9C, 0xCF, 0x59, 0xAD, 0x3C, 0xAE, 0x9D, 0xEF, 0xC0,
 0xE7, 0x54, 0x8D, 0xB7, 0x92, 0xDC, 0x65, 0x93, 0x96, 0xDA, 0x6C, 0xFF, 0xFA, 0x4C, 0xAB, 0x81,
 0x8C, 0xD5, 0xA7, 0x67, 0xDB, 0xF3, 0xAE, 0xA9, 0xCD, 0xDF, 0xB8, 0x90, 0x27, 0x55, 0x60, 0x54,
 0xDB, 0x41, 0xC8, 0x13, 0xB5, 0xF9, 0x42, 0x70, 0x02, 0x2E, 0xE3, 0xCA, 0x93, 0x1D, 0x44, 0x02,
 0xF7, 0xFA, 0x4D, 0x70, 0x02, 0xFF, 0xE2, 0xEE, 0xF5, 0x40, 0x4E, 0x90, 0x28, 0x85, 0x69, 0xEE,
 0x89, 0xD3, 0xC5, 0xEC, 0x93, 0xBB, 0x7D, 0x5F, 0x18, 0x7F, 0x0A, 0x21, 0xA7, 0xB3, 0xC9, 0xC6,
 0x41, 0x1C, 0x8F, 0x03, 0x95, 0x64, 0xE3, 0x61, 0xF1, 0x9B, 0x91, 0x24, 0x7D, 0x4A, 0xA0, 0x36,
 0x2B, 0xE5, 0x35, 0x1A, 0x88, 0xB1, 0xD9, 0x2C, 0x28, 0x06, 0xE7, 0x14, 0x50, 0x15, 0x60, 0x25,
 0x62, 0x58, 0x19, 0xA0, 0x31, 0xF8, 0xE8, 0x89, 0x9A, 0x89, 0xEB, 0xAD, 0x52, 0xC4, 0x12, 0x69,
 0xD1, 0x58, 0x6D, 0x9E, 0x9D, 0xAC, 0xB3, 0xF7, 0x0E, 0x70, 0x74, 0x45, 0x05, 0xF7, 0x70, 0x10,
 0x6C, 0x8C, 0xC8, 0x6C, 0xA8, 0xDA, 0x6C, 0xA5, 0xED, 0x5D, 0x70, 0x29, 0x56, 0x77, 0xC0, 0x25,
 0x23, 0x8E, 0x84, 0xA6, 0x58, 0x8D, 0xA1, 0xA9, 0xAA, 0xB3, 0x88, 0xF8, 0x92, 0xC0, 0xAC, 0x93,
 0x76, 0x17, 0x5C, 0x78, 0x11, 0xF7, 0x51, 0xC0, 0x36, 0x46, 0x25, 0x19, 0x08, 0x69, 0x2D, 0x6E,
 0x1D, 0x0C, 0x91, 0x54, 0x94, 0xEF, 0x00, 0x8F, 0x00, 0xB1, 0xD0, 0x17, 0x4F, 0xDF, 0x37, 0x46,
 0x64, 0x36, 0x14, 0xEA, 0x61, 0xDA, 0x3E, 0x18, 0x2A, 0x19, 0x71, 0xBE, 0x07, 0x5C, 0x86, 0xD8,
 0x22, 0xC8, 0x7D, 0x8F, 0x1D, 0x07, 0x4A, 0xD6, 0xE6, 0xD8, 0xE4, 0x86, 0x03, 0x3E, 0xF2, 0x5A,
 0xE9, 0x88, 0xEB, 0x8D, 0xD7, 0x88, 0x73, 0xEC, 0xBE, 0xD4, 0x42, 0xB1, 0xBC, 0x7C, 0xDD, 0xF2,
 0x9A, 0xA6, 0x72, 0x6E, 0xB9, 0x42, 0xA8, 0x00, 0x13, 0xDC, 0x13, 0x7B, 0xBE, 0xAD, 0x79, 0x54,
 0xD5, 0xE6, 0x73, 0x1F, 0x4D, 0xC4, 0xCF, 0xB0, 0xBB, 0x2C, 0x7A, 0xDE, 0x61, 0x5B, 0xF9, 0x1D,
 0x36, 0x72, 0xBB, 0xAC, 0xC0, 0x9E, 0xFB, 0x18, 0x7B, 0xBB, 0x71, 0x39, 0x85, 0x62, 0x06, 0x8D,
 0xDD, 0x98, 0xC0, 0x82, 0xF5, 0x06, 0x0F, 0x09, 0xFA, 0x16, 0x16, 0x5C, 0x68, 0xD4, 0xDD, 0x38,
 0x2C, 0x60, 0x8C, 0xDA, 0xBC, 0xFA, 0xB3, 0xB5, 0x71, 0x92, 0x92, 0x0F, 0x9F, 0x1E, 0xE2, 0xE1,
 0x32, 0x3B, 0xC5, 0x02, 0xAA, 0x0B, 0x9B, 0xCD, 0xE5, 0x91, 0xF3, 0xD0, 0x0D, 0xE7, 0x12, 0xBD,
 0x12, 0x01, 0xC5, 0xF3, 0x19, 0x35, 0xA3, 0xE6, 0xC3, 0x74, 0xFC, 0x7A, 0x19, 0x0C, 0x84, 0x78,
 0xDF, 0x43, 0x64, 0xF3, 0xBA, 0x92, 0x0C, 0x14, 0x48, 0x29, 0xCF, 0xA1, 0xB5, 0x2F, 0xB8, 0xE4,
 0xB4, 0x07, 0xC3, 0x2C, 0xD6, 0xFA, 0xD0, 0xC0, 0x81, 0x20, 0x03, 0x6A, 0x6F, 0xFE, 0x38, 0x22,
 0x1E, 0xA7, 0x36, 0x01, 0xB5, 0x57, 0xD0, 0xD8, 0xB8, 0xCA, 0x24, 0x0C, 0xBE, 0x72, 0x79, 0xB9,
 0x0A, 0x19, 0xDD, 0xA5, 0xB2, 0xDC, 0x84, 0x9E, 0x37, 0xD9, 0xA5, 0xAC, 0xB4, 0x5D, 0x1A, 0xDA,
 0xDB, 0x73, 0x80, 0x9A, 0xF2, 0xC6, 0x71, 0x88, 0xB5, 0x7D, 0x55, 0x82, 0x8A, 0xF2, 0x82, 0x0E,
 0x1E, 0x38, 0xFE, 0x2B, 0x67, 0x71, 0x6C, 0x6D, 0x9E, 0x20, 0xB0, 0x05, 0x28, 0x76, 0xDA, 0xCA,
 0x4D, 0xE7, 0xF5, 0xCD, 0x9B, 0x77, 0xFB, 0xC9, 0x0E, 0x30, 0xE7, 0x81, 0x12, 0x03, 0xD7, 0xF6,
 0xD0, 0x39, 0x01, 0x84, 0xA8, 0x6E, 0x83, 0x53, 0x55, 0x02, 0x75, 0x7D, 0xF3, 0x76, 0x5F, 0x28,
 0x55, 0x0F, 0x07, 0x53, 0xF5, 0x5B, 0xC0, 0xE9, 0xBD, 0x8B, 0xEF, 0xB0, 0xBB, 0x05, 0x56, 0x72,
 0x20, 0xC7, 0x4B, 0x79, 0xC9, 0x5B, 0x07, 0xDB, 0xC8, 0xA5, 0xA2, 0x7C, 0x07, 0xDB, 0x38, 0xF0,
 0x8A, 0xF7, 0x42, 0xE8, 0x6D, 0x82, 0x47, 0x8E, 0x54, 0x9B, 0x9D, 0xF1, 0x90, 0x06, 0xA1, 0xFF,
 0xC0, 0x82, 0xBA, 0x1C, 0x91, 0x5D, 0x9E, 0x0C, 0xCE, 0x44, 0x91, 0x88, 0x24, 0x8F, 0x06, 0xF9,
 0x93, 0xFD, 0x14, 0x93, 0x6A, 0xB9, 0xF6, 0x45, 0x51, 0xE1, 0xCC, 0xBF, 0x26, 0x30, 0xBD, 0x2D,
 0xEA, 0x4E, 0x8F, 0xD7, 0x9D, 0xE7, 0xED, 0xFD, 0xA4, 0xB2, 0xDE, 0xC1, 0x0A, 0x4E, 0xEF, 0xA0,
 0x05, 0x47, 0x91, 0xBF, 0x76, 0xA6, 0x30, 0x6D, 0xB9, 0x89, 0x88, 0x07, 0xC2, 0xDE, 0x79, 0x9B,
 0x0D, 0x44, 0xF6, 0xA1, 0xFA, 0x78, 0x97, 0xD0, 0x49, 0xC4, 0xC8, 0x47, 0xCE, 0xC9, 0x2C, 0x6E,
 0x4E, 0xBF, 0x68, 0xD4, 0x9C, 0xAC, 0x95, 0x76, 0x97, 0xA0, 0xE1, 0x9A, 0x58, 0x98, 0xB8, 0xFC,
 0xA5, 0xC7, 0x4D, 0x01, 0xC9, 0x8C, 0x95, 0x98, 0x28, 0x6D, 0x79, 0xB5, 0x0B, 0x36, 0xD5, 0x5D,
 0xB0, 0xC9, 0x4A, 0x94, 0x87, 0xE7, 0xEC, 0x2B, 0x55, 0x9A, 0x4A, 0xF5, 0xFC, 0x6B, 0xC2, 0xD3,
 0x1D, 0x6E, 0x9E, 0xD3, 0x60, 0x8C, 0xDA, 0x6C, 0xBD, 0xDD, 0x4F, 0x4E, 0xE3, 0x93, 0x3D, 0x30,
 0xA7, 0xED, 0x94, 0xC1, 0x84, 0x52, 0x87, 0x5E, 0x8A, 0x8D, 0xB6, 0x40, 0x63, 0xC4, 0x05, 0xFF,
 0x73, 0x4F, 0x68, 0x8C, 0x1E, 0x8E, 0xC6, 0x17, 0xAE, 0x30, 0xA3, 0x6F, 0x01, 0x1F, 0x1F, 0x8D,
 0xDE, 0xF7, 0x06, 0x68, 0x63, 0x8C, 0xE2, 0x71, 0x6A, 0xF3, 0x1D, 0x1A, 0x29, 0xCF, 0x5F, 0x5D,
 0xED, 0x05, 0xAB, 0x64, 0xD2, 0xC3, 0xE0, 0x95, 0xAA, 0x7C, 0x68, 0xCC, 0x5C, 0xEC, 0x6D, 0x1E,
 0x54, 0x7C, 0x90, 0xDA, 0x7C, 0x89, 0xBD, 0x40, 0x69, 0x53, 0x3F, 0x3E, 0x76, 0xB4, 0x17, 0xD4,
 0xC4, 0xCC, 0x87, 0x81, 0x4C, 0x2A, 0x7D, 0x68, 0xBC, 0xFA, 0x03, 0xE2, 0xFB, 0xD4, 0xDF, 0x18,
 0xB2, 0x78, 0x9C, 0xDA, 0x7C, 0x51, 0x7C, 0x25, 0x5A, 0x7B, 0x81, 0x2B, 0x99, 0xF5, 0x30, 0x88,
 0xA5, 0x3A, 0x1F, 0x1A, 0x34, 0xDB, 0x1A, 0x6D, 0x0C, 0x18, 0x8C, 0x51, 0x9B, 0xD7, 0xED, 0x3F,
 0x15, 0xED, 0x9A, 0x8E, 0x3C, 0xFE, 0x36, 0x99, 0xD2, 0x79, 0xAD, 0xEF, 0x05, 0x35, 0x3E, 0xF5,
 0x61, 0x10, 0x13, 0x4A, 0x1F, 0x1A, 0x2D, 0xF1, 0x66, 0x69, 0x17, 0x6D, 0x1E, 0x63, 0xC9, 0x40,
 0xFE, 0x42, 0x05, 0xB4, 0x94, 0x16, 0xDA, 0x4F, 0x94, 0xA5, 0xF3, 0xEE, 0x63, 0x25, 0x38, 0x53,
 0xF2, 0xD0, 0x38, 0x39, 0xC8, 0xC2, 0xEF, 0x6D, 0xCC, 0xB6, 0xF9, 0x35, 0x3F, 0x33, 0x56, 0x6D,
 0x3E, 0x83, 0x0B, 0xE5, 0x5A, 0x5C, 0xEC, 0xAB, 0x8E, 0x65, 0xE7, 0xDF, 0x07, 0x6A, 0x39, 0x7D,
 0xBF, 0x09, 0xE0, 0x60, 0xD5, 0x40, 0x7B, 0xDE, 0x56, 0x2F, 0xE9, 0xE6, 0x86, 0xC7, 0xF0, 0xBD,
 0x93, 0xD7, 0xFB, 0x05, 0x70, 0x26, 0xC4, 0xDE, 0x30, 0xCC, 0xE8, 0xBD, 0x0F, 0x18, 0x93, 0x37,
 0xDC, 0xC5, 0x5E, 0x53, 0x1E, 0x6C, 0x5D, 0x87, 0x94, 0x24, 0x93, 0xCF, 0x03, 0x30, 0x2B, 0x06,
 0x8C, 0xB8, 0xAE, 0xDA, 0x7C, 0x8E, 0x99, 0x72, 0xC3, 0x9B, 0x17, 0xC7, 0x92, 0xE0, 0xE1, 0x5C,
 0xE2, 0xB7, 0xC8, 0xF9, 0x61, 0x64, 0x34, 0x50, 0x9B, 0x37, 0xFC, 0x64, 0x2E, 0xF0, 0xE2, 0x57,
 0x9B, 0x33, 0x13, 0x46, 0xC4, 0x9E, 0x4F, 0x41, 0xA8, 0x14, 0xA4, 0xF8, 0xFC, 0xA3, 0xAA, 0x24,
 0xAD, 0x4C, 0x5F, 0xB3, 0x23, 0x88, 0x15, 0xEE, 0x65, 0xEB, 0xA7, 0xE3, 0x3F, 0xED, 0x59, 0xAB,
 0x7F, 0x01, 0xBC, 0x38, 0xF6, 0xD0, 0x12, 0x73, 0xAF, 0x40, 0xE1, 0x42, 0x1E, 0x8D, 0x5E, 0xC1,
 0x2A, 0x7D, 0x43, 0x5F, 0x58, 0x62, 0x76, 0x48, 0x23, 0x55, 0x6B, 0xEE, 0xF0, 0x46, 0xF2, 0x14,
 0xF0, 0x61, 0x41, 0x2B, 0x8E, 0x71, 0xC4, 0xF5, 0x90, 0x37, 0x53, 0xF3, 0xFF, 0xEF, 0xBF, 0xEB,
 0x7C, 0x86, 0x0C, 0x7A, 0x19, 0xC1, 0x54, 0x25, 0xF0, 0xAD, 0x86, 0xBA, 0xEA, 0x7D, 0xFF, 0x15,
 0x9A, 0x1F, 0x2F, 0x53, 0x7D, 0x8E, 0x78, 0x89, 0xAD, 0x2F, 0x02, 0xCB, 0x27, 0x43, 0xD6, 0xFC,
 0xC1, 0xA6, 0x56, 0x38, 0xC0, 0x1E, 0x2B, 0x21, 0xDB, 0xEE, 0xDC, 0x41, 0xE3, 0x25, 0x09, 0x18,
 0x06, 0x2B, 0x68, 0x85, 0xEB, 0x37, 0xAF, 0xDA, 0xF2, 0xDC, 0xC3, 0x4B, 0x8A, 0x6C, 0x6C, 0x17,
 0x0C, 0x27, 0xF4, 0x04, 0x1F, 0x4D, 0x9F, 0x26, 0x4D, 0xA5, 0xAB, 0xB5, 0xF4, 0xA9, 0x0B, 0x4E,
 0xDB, 0xAE, 0xCB, 0xF4, 0xA0, 0xB5, 0x4A, 0x3C, 0xC6, 0xF5, 0xA9, 0x85, 0x02, 0x5C, 0x48, 0x02,
 0xBD, 0x60, 0xB6, 0x1B, 0xAD, 0x52, 0xBC, 0xF6, 0xB9, 0xAC, 0xF0, 0x53, 0x34, 0xA0, 0xF4, 0x6D,
 0x5D, 0x10, 0x89, 0xE7, 0x54, 0x05, 0x53, 0xB4, 0xE5, 0x2F, 0xBE, 0x45, 0xEA, 0x61, 0x39, 0x44,
 0x3C, 0x0D, 0xCB, 0x12, 0x4B, 0xCF, 0x4A, 0xA8, 0xC3, 0xEE, 0x80, 0x30, 0x4E, 0x59, 0xA8, 0x14,
 0x62, 0xAA, 0x38, 0x95, 0x98, 0x3E, 0x66, 0xA1, 0xEF, 0xD5, 0x23, 0x00, 0x36, 0x60, 0xCA, 0x75,
 0xE3, 0xC3, 0x8F, 0x53, 0x2B, 0x3A, 0x16, 0x6F, 0x50, 0x52, 0xF7, 0xF2, 0x0E, 0xF9, 0x8D, 0x1F,
 0xA7, 0xAD, 0x12, 0xB1, 0xA3, 0xC7, 0x30, 0x07, 0xB4, 0xDB, 0xD1, 0x87, 0xBA, 0xC3, 0x8F, 0xF1,
 0x6B, 0xD7, 0x7A, 0x89, 0xF5, 0xB1, 0xA7, 0x75, 0x1A, 0xCD, 0x29, 0x1F, 0x4D, 0x5D, 0x5C, 0x72,
 0x69, 0x4F, 0xFB, 0xE0, 0xE3, 0x4F, 0x21, 0x06, 0x66, 0x8C, 0x2A, 0x3F, 0x4E, 0xAF, 0x23, 0xC5,
 0x21, 0x1E, 0x09, 0xFA, 0xD8, 0x36, 0x94, 0x80, 0x21, 0x16, 0x06, 0x26, 0x74, 0x77, 0x4A, 0xB2,
 0x1D, 0x7D, 0xD0, 0x23, 0x3D, 0x82, 0x69, 0x14, 0xAB, 0x91, 0x5A, 0xD9, 0xA5, 0x96, 0x78, 0x4F,
 0xB0, 0x44, 0x7D, 0xD2, 0x23, 0x5E, 0x5D, 0xCA, 0x86, 0x1B, 0x2D, 0x98, 0x09, 0xCC, 0xC3, 0x5D,
 0x8A, 0x03, 0xC0, 0xD1, 0xD0, 0x0A, 0xD2, 0x0F, 0x0B, 0x7A, 0x64, 0x38, 0x0B, 0x04, 0x3E, 0x1E,
 0xD0, 0x3B, 0x9C, 0xA5, 0xE9, 0x2D, 0x67, 0x92, 0xC4, 0x67, 0x41, 0x37, 0x5A, 0xE9, 0x01, 0xE6,
 0xC6, 0x51, 0x39, 0x32, 0xFA, 0x2B, 0x99, 0xAE, 0x18, 0x53, 0x89, 0x0C, 0xD2, 0xD0, 0x5A, 0x46,
 0xDB, 0xB8, 0xD6, 0x61, 0xE4, 0x75, 0xE3, 0x48, 0xF3, 0x42, 0xD7, 0x3D, 0x6A, 0x5C, 0xEB, 0xFF,
 0xFE, 0x7B, 0x5D, 0xE7, 0x4E, 0xD0, 0xA9, 0xCF, 0x10, 0x6F, 0x34, 0x1A, 0xD2, 0x15, 0x2E, 0xC1,
 0x90, 0x29, 0xF6, 0x46, 0xBB, 0x71, 0x74, 0xD4, 0x36, 0xD2, 0xEB, 0x46, 0x5B, 0x37, 0xC5, 0x7D,
 0x01, 0xB4, 0x11, 0x7F, 0x43, 0xAF, 0x71, 0xFD, 0xF8, 0x71, 0xE7, 0xA8, 0xD1, 0x68, 0x5F, 0x72,
 0x17, 0x33, 0x8F, 0xE0, 0x52, 0x2B, 0x20, 0x6C, 0x49, 0xBE, 0xC4, 0xBE, 0x6C, 0x5F, 0x62, 0xED,
 0x4E, 0x37, 0x1D, 0xFE, 0x51, 0x40, 0xBD, 0xEC, 0x0D, 0xCD, 0xD1, 0x98, 0x6E, 0x60, 0x2D, 0xD0,
 0x81, 0x39, 0xE6, 0x6D, 0x47, 0xB4, 0x0B, 0xC9, 0xAB, 0x2E, 0x19, 0x5A, 0x47, 0x1B, 0xEB, 0x26,
 0xE6, 0x1F, 0x85, 0x7C, 0xE1, 0x48, 0x68, 0x60, 0xDE, 0xF6, 0x65, 0x5F, 0xF3, 0x74, 0xB3, 0x07,
 0x1F, 0xBA, 0x1E, 0xD5, 0x53, 0x38, 0xC1, 0x1B, 0xFC, 0xC9, 0x8D, 0xF0, 0x58, 0xEA, 0x5F, 0xB9,
 0xAE, 0x56, 0x90, 0xC7, 0xBA, 0x0A, 0x7A, 0x09, 0x2A, 0x51, 0x07, 0xF1, 0x68, 0x10, 0x36, 0xA6,
 0x9E, 0xE5, 0x12, 0xEB, 0xB6, 0xA1, 0x71, 0xC3, 0x61, 0x08, 0x11, 0x79, 0xE0, 0xF4, 0x35, 0xB5,
 0xB1, 0x1E, 0x45, 0x20, 0x9E, 0xF0, 0x3B, 0xE9, 0xA1, 0xD2, 0x7D, 0x3E, 0xC4, 0x3E, 0x98, 0xC6,
 0x1C, 0x84, 0x99, 0xF4, 0x68, 0xA5, 0x55, 0xFA, 0x18, 0xF0, 0x20, 0x8C, 0x96, 0x90, 0xDC, 0x27,
 0x5A, 0xBE, 0xC6, 0x66, 0x64, 0x6C, 0x83, 0x50, 0x44, 0x03, 0x50, 0xFE, 0x6E, 0x83, 0xBE, 0xFF,
 0x18, 0x47, 0x15, 0xEE, 0xBA, 0x7A, 0xEC, 0x9D, 0x1F, 0x67, 0xEE, 0x0B, 0x75, 0xAA, 0xE3, 0x62,
 0xDE, 0x6C, 0x4D, 0x7E, 0x05, 0xE7, 0x92, 0x99, 0x0B, 0xDC, 0xE4, 0x76, 0x1D, 0xCD, 0x2C, 0xBD,
 0x02, 0xB5, 0xBB, 0x9A, 0x3A, 0xAD, 0x84, 0x40, 0x36, 0x58, 0x4D, 0x96, 0x2B, 0x75, 0x40, 0xEA,
 0xAD, 0x26, 0xCD, 0x14, 0x32, 0x20, 0xA4, 0xAB, 0x09, 0xB3, 0xE9, 0x1B, 0x28, 0x87, 0x12, 0xAC,
 0x11, 0xF1, 0x6C, 0x3A, 0x82, 0x98, 0xA6, 0x43, 0x0D, 0x44, 0x2A, 0x11, 0x0F, 0x74, 0x78, 0xF1,
 0xFB, 0xAB, 0x97, 0x8D, 0x42, 0xB6, 0xC0, 0x16, 0x22, 0xE3, 0x93, 0x1C, 0xF0, 0xB1, 0xC4, 0xF3,
 0x38, 0x87, 0xF2, 0xE7, 0x82, 0x79, 0x5E, 0x29, 0x70, 0x40, 0x39, 0xC5, 0x07, 0xF0, 0xC1, 0xDB,
 0x05, 0x0E, 0x74, 0x98, 0x32, 0xA8, 0xBB, 0x79, 0x37, 0xE1, 0xF3, 0xCD, 0x98, 0x41, 0xE6, 0x42,
 0x43, 0x80, 0x1F, 0x5F, 0xBE, 0xB7, 0xBA, 0x90, 0xAD, 0xAE, 0x11, 0xC3, 0x25, 0x8F, 0x8E, 0xC0,
 0x0D, 0x24, 0xE7, 0xC8, 0xA0, 0x8B, 0xE3, 0xB1, 0xB8, 0x31, 0xC8, 0xDF, 0x90, 0xB0, 0xB6, 0xF2,
 0xD3, 0x83, 0xB3, 0x67, 0x44, 0xAB, 0xB7, 0x2E, 0x61, 0xB8, 0xF9, 0x09, 0xB8, 0x1B, 0x5E, 0x7E,
 0x74, 0x17, 0x82, 0x20, 0x32, 0xB6, 0xF2, 0xB3, 0x34, 0x16, 0xFA, 0x3C, 0xE1, 0x0B, 0x76, 0x3C,
 0xB6, 0x53, 0x4F, 0xF3, 0x57, 0x83, 0xC3, 0xE3, 0x5B, 0x37, 0x82, 0x7B, 0x09, 0x32, 0x3F, 0xD7,
 0x01, 0x2D, 0xBB, 0xC7, 0xC9, 0xE6, 0x7F, 0x4C, 0x2A, 0xE8, 0x75, 0x3F, 0x2F, 0x17, 0xA8, 0xE9,
 0xEB, 0x86, 0x9F, 0x56, 0xAC, 0x15, 0x19, 0x25, 0x8A, 0x25, 0x0F, 0xEF, 0x11, 0x0C, 0x73, 0xC9,
 0xEF, 0xEE, 0x25, 0xC8, 0xFE, 0x50, 0x0F, 0xB2, 0x84, 0x0B, 0xB2, 0x84, 0xBA, 0x11, 0xA6, 0xB2,
 0xA4, 0x69, 0x2F, 0x99, 0x7D, 0x74, 0x0F, 0xF3, 0x24, 0xE1, 0xE9, 0xC6, 0x78, 0x35, 0x55, 0xEE,
 0xBD, 0x3B, 0x10, 0x60, 0xB4, 0x20, 0xC0, 0x48, 0x37, 0x46, 0xA9, 0x00, 0x69, 0xCA, 0x4C, 0x04,
 0x98, 0xAC, 0x09, 0x3F, 0xB9, 0xA1, 0x02, 0x19, 0x3E, 0xAF, 0x21, 0x9C, 0x25, 0x5F, 0xDD, 0xB8,
 0xBA, 0x87, 0x36, 0x39, 0x38, 0x08, 0xB2, 0x5E, 0x2D, 0xC8, 0x7A, 0xA5, 0x1B, 0xA7, 0x17, 0x57,
 0xB2, 0x90, 0x40, 0xF2, 0x26, 0xDA, 0x84, 0x67, 0x34, 0x83, 0x68, 0x9F, 0xF9, 0x37, 0x38, 0xEF,
 0x64, 0x6E, 0x48, 0x9C, 0x57, 0xD3, 0x41, 0x97, 0x1A, 0x72, 0xB1, 0xCF, 0xB4, 0xC2, 0x5B, 0x17,
 0xC3, 0x2A, 0x23, 0x7E, 0x95, 0x4F, 0x69, 0xFF, 0xFA, 0x4C, 0xA1, 0xBE, 0x22, 0x8E, 0xAD, 0x2B,
 0x7E, 0x7A, 0xEC, 0x51, 0x91, 0x27, 0x93, 0x15, 0xCC, 0xFF, 0xAF, 0x07, 0x70, 0x29, 0x85, 0xF5,
 0x49, 0xA0, 0x38, 0x98, 0x1F, 0x0A, 0xC0, 0x47, 0x1C, 0x7B, 0x4A, 0x6C, 0x25, 0x96, 0x42, 0x37,
 0xF9, 0x95, 0xD6, 0xD5, 0x26, 0xBA, 0x71, 0x34, 0x49, 0x2C, 0x0A, 0x52, 0xF2, 0xDA, 0x92, 0x8A,
 0x08, 0x32, 0x7E, 0x3E, 0x88, 0x8C, 0x9F, 0x73, 0x32, 0x7E, 0x06, 0xC0, 0x66, 0x11, 0xD0, 0x97,
 0x12, 0x82, 0x1A, 0x65, 0x3D, 0xAE, 0x85, 0x50, 0xBA, 0xEA, 0xD9, 0x65, 0x66, 0xBC, 0xA8, 0x94,
 0x57, 0xF2, 0x0C, 0xF0, 0xC5, 0xB1, 0xFC, 0xFF, 0xCB, 0xFE, 0x0F, 0x86, 0xED, 0x24, 0xF8, 0xD7,
 0x4C, 0x00, 0x00
};

The final working of the motion of the motor in Forward, Reverse, Left and Right is shown in the video below:

Chassis Design and Laser Cutting

The chassis for the model was designed in fusion360. Initially, I had planned on 3D printing the chassis design however owing to the size of the chassis, I decided to make the chassis using laser cut models. Therefore, I took individual components from the 3D design done above and used it to laser cut the parts for the model.

The video below show the laser cutting of the parts for the body of the robot.

Assembly of all components

Once all the components of the project was completed, I begin then with assembly of the components into a single box. Firstly, I worked with laser cut board based chassis to see how it turns out.

After that, I did an assembly of the same with Acrylic material. In the initial plan, I wanted to make the chassis of the robot with 3D printed parts however, owing to the size of the robot, I had to change the chassis design to laser cut parts that can join together to form a single module. The sizing of the robot changed from my initial plan mainly due to two things. They are the size of the PCB circuit and the need to mount the ESP32 camera module is an specific direction to get a clear and well oriented image. The second thing was the size of motors and power supply. The first design was aimed at using two 9V batteries in parallel to get the motors to run, however the 9V battery couldnot power the ESP board and the motor at the same time. Therefore, I made use of a RAV robot battery of 3000mAh to power the motor.

The video below shows the fast timelapse of the assembly process.

Test runs

During the test run, I found out that a single power supply was giving me problems when running motor and ESP. Therefore I created another power supply with 5V output to power the ESP32 separately. This worked well initially but started to restart the ESP board consistently and there was glitch in the movement of the robot. I tried with a couple of solutions posted online including grounding issues but was not solved. Finally, I tried using a USB power bank to power the ESP separately and it worked smoothly without restarting the ESP board. This i attributed to the fact that the brownout problem of the ESP board is solved by communicating a serial signal from the ESP and USB driver module.

The final run of the robot is shown below. Below is also the final video and the presentation I have prepared.

First Test RUN

Successive Test RUNs

Final Project Video

Final Presentation