반응형

ESP32 CAM & LED 플래시 라이트

ESP32 CAM은 와이파이와 블루투스 연결을 내장한 ESP32 칩셋을 사용하는 아두이노에서 사용할 수 있는 웹캠 개발보드입니다. 무선으로 영상을 수신할 수 있는 웹캠을 구현할 수 있으며 추가적인 코딩을 통해 여러 기능(와이파이를 통한 원격제어)들을 넣을 수도 있을 것입니다.

다만 만원 미만의 금액으로 무선 웹캠을 구현할 수 있다는 장점은 있지만 그 가격만큼 영상의 품질이나 성능은 크게 기대하지 않는 게 좋겠습니다. 또한 발열이 상당한 것으로 보아 ESP32 칩셋의 성능 한계까지 사용하고 있는 게 아닌가 생각합니다.

간단한 스펙을 살펴보겠습니다.
- ESP 32S 칩셋: 저전력 듀얼 코어 32 비트 cpu, 내장 520 KB SRAM, 와이파이 및 블루투스, 기타 ESP32 칩셋의 기능
- 온보드 8MB PSRAM (4MB 사용가능) : 이미지 버퍼용
- 온보드 플래시 라이트
- OV2640 및 OV7670 카메라 지원

 

 

▶ 카메라 연결 방법
아래 그림처럼 리본 케이블 커넥터(FPC)의 클립을 위로 올립니다.

 

카메라의 리본 케이블을 슬롯에 넣은 뒤 클립을 눌러서 닫으면 됩니다.

 

ESP32 CAM를 구동하기 위한 스케치를 업로드하기 위해서는 아두이노 IDE에 ESP32용 아두이노 코어가 설치되어 있어야 합니다. 설치방법은 아래 포스트를 참조하시기 바랍니다.

 

[Arduino] - ESP32의 특징 및 아두이노 IDE에 ESP32 사용 환경 설치하기

 

스케치 업로드는 ESP32 CAM 전용 업로드 보드를 사용하였습니다. 업로드 보드가 없다면 USB UART 보드(시리얼 컨버터)를 사용해서 업로드해야 합니다.

 

esp32 arduino core version 1.0.6을 기준으로 설명되었습니다.

esp32 arduino core version 2.0.xx에 대한 변경된 사항을 추가로 설명합니다. 

▶ 구동 스케치 업로드
아두이노 IDE에서 파일 => 예제 => ESP32 => Camera => CameraWebServer를 선택합니다.

 

예제코드에 설정된 "CAMERA_MODEL_WROVER_KIT"를 "CAMERA_MODEL_AI_THINKER"로 변경해 줍니다.

// Select camera model
//#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
//#define CAMERA_MODEL_ESP_EYE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
//#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM


ESP32 CAM에 와이파이를 통해 연결하는 방법으로는 공유기를 통해 접속하는 방법과 공유기가 없는 환경에서 와이파이 AP에 직접 접속하는 두 가지 방법이 있습니다. 예제 코드는 공유기를 통해서만 연결할 수 있도록 코딩되어 있습니다. 사용하는 공유기의 아이디와 비밀번호를 입력해 줍니다. 

const char* ssid = "skynet";       // 사용하고 있는 공유기 아이디
const char* password = "skynet00"; // 사용하고 있는 공유기 비밀번호

 

아두이노 IDE에서 툴 => 보드 => ESP32 Arduino => ESP32 Wrover Module을 선택합니다.

 

다음으로 툴 => 보드 => Partiion Scheme => Huge APP (3MB No OTA/1MB SPIFFS) 선택하고 ESP32 CAM이 연결된 포트를 선택한 뒤 스케치를 업로드합니다.

** esp32 arduino core version 2.0.xx에서는 Flash Mode: "DIO"를 선택해야합니다. QIO에서는 assert failed: do_core_init startup.c:328 (flash_ret == ESP_OK)에러가 발생됩니다.

공유기로부터 IP주소 192.168.1.9를 할당 받음

 

▶ 공유기를 통해 ESP32 CAM의 영상 받기

인터넷 공유기에 연결된 컴퓨터에서 웹브라우저를 열고 시리얼 모니터에 표시된 공유기로부터 할당받은 ESP32 CAM의 IP주소를 입력해 줍니다. 그러면 Toggle OV2640 settings라는 설정창 페이지가 로드되는데 맨 아래 "Start Stream" 버튼을 클릭하면 영상 스트림이 시작되고 영상이 표시됩니다.

▶ ESP32 캠 옵션 설정
"☰ Toggle OV2640 settings"를 클릭하면 옵션 설정창을 펼치고 닫을 수 있습니다. 설정창에서는 해상도와 밝기, 명도, 채도등을 설정할 수 있으며 안면 인식 기능은 정상 작동하지 않았습니다. 화면 해상도 CIF(400x296) 이하에서 안면 인식 기능을 활성화시키면 "CORRUPT HEAP: Bad head at 0x3ffe25dc. Expected 0xabba1234 got 0x00000008" 메모리 오류와 함께 ESP32 CAM이 리부팅되는 오류가 발생하여 확인할 수 없었습니다.

▶ OV2640 카메라 해상도별 화질
카메라가 움직일 경우 딜레이와 잔상 현상이 있으며 해상도를 CIF 이상으로 설정할 경우 와이파이를 통한 초당 전송 프레임이 급격히 감소합니다.

지원 해상도
UXGA(1600x1200) : 약 8 fps
SXGA(1280x1024) : 약 15 fps
XGA(1024x768) : 약 19 fps
SVGA(800x600) : 약 25 fps
VGA(640x480) : 약 37 fps
CIF(400x296) : 약 47 fps
QVGA(320x240) : 약 50 fps
HQVGA(240x176) : 약 50 fps
QQVGA(160x120) : 약 52 fps

1. QQVGA(160x120)

 

2. HQVGA(240x176)

 

3. QVGA(320x240)

 

4. CIF(400x296)

 

5. VGA(640x480)

 

6. SVGA(800x600)

 

7. XGA(1024x768)

 

8. SXGA(1280x1024)

온보드 플래시 꺼짐
온보드 플래시 켜짐

 

9. UXGA(1600x1200)

온보드 플래시 꺼짐
온보드 플래시 켜짐

 

공유기 없는 환경에서 휴대폰으로 ESP32 CAM에 연결하는 방법

ACCESS POINT 설정 코드를 추가해 줍니다.

#include <WiFiAP.h> // esp32 arduino core version 2.0.xx에서는 반드시 포함되어야합니다. 

const char* ssidAP = "ESP_CAM";
const char* passAP = "";

 

setup() 함수에 ACCESS POINT 실행 코드를 반영해 줍니다.

WiFi.mode(WIFI_AP_STA);                // 와이파이 모드 설정 (AP + STA)
if (passAP == "") WiFi.softAP(ssidAP); // 비밀번호 설정 유무
else WiFi.softAP(ssidAP, passAP);
Serial.println(WiFi.softAPIP());       // AP 주소 시리얼 모니터 출력

 

스케치 업로드 후 AP주소 확인; ESP32의 ACCESS POINT 기본 주소는 별도로 수정하지 않으면 항상 192.168.4.1입니다.

 

휴대폰의 와이파이 설정에서 ESP CAM 선택

 

휴대폰의 웹브라우저에서 AP주소 192.168.4.1을 입력해 주면 Toggle OV2640 settings 페이지에 들어갈 수 있습니다.

 

설정 페이지에 스트림 실행용 아이콘 추가 및 ESP32 CAM 온보드 LED 플래시를 제어하는 방법

esp32 arduino core version 1.0.6의 예제 스케치를 기준으로 설명되었습니다.

 

1. Toggle OV2640 settings 웹페이지 수정

ESP32 CAM의 설정용 웹페이지인 Toggle OV2640 settings는 기본값이 설정창을 펼치도록 되어있습니다. PC에서는 창이 넓어 영상과 동시에 확인할 수 있지만 휴대폰에서는 설정창이 화면의 대부분을 차지하여 불편합니다. 따라서 설정창이 닫힌 상태가 기본값이 되도록 변경해 주고 설정창이 닫힌 상태에서도 영상을 스트림 하도록 아이콘을 추가해 주었습니다.

또한 온보드 LED 플래시를 제어할 수 있도록 아이콘을 추가해 주었습니다. 

* SD카드를 사용하여 영상 저장용 블랙박스로 사용하는 경우 온보드 LED 플래시를 제어하지 못합니다. 

연결시 설정창이 펼쳐진 상태의 원본
연결시 설정창이 닫힌상태와 스트림 / 플래시 아이콘

https://youtu.be/19SKlTz1O2g

 

 

※ Toggle OV2640 settings 수정 방법

▶ Toggle OV2640 settings 웹페이지용 GZIP 압축 데이터 풀기

Toggle OV2640 settings 웹페이지는 예제 스케치의 camera_index.h 파일 안의 "index_ov2640_html_gz" 배열에 GZIP압축 데이터로 코딩되어 있습니다. 이 압축된 데이터는 웹브라우저에 내장된 압축해제 코드에 의해 풀리고 일반 HTML로 표시됩니다. 

 

참조 사이트 https://robotzero.one/esp32-cam-custom-html/

 

Editing the ESP32-CAM Camera Web Server HTML – Robot Zero One

An easy way to edit or add HTML in the source of the ESP-WHO camera web server example Using the CyberChef online tool makes it easy to convert from your own HTML to the Gzipped Hex format found in the the index_html_gz variable in the code for Arduino and

robotzero.one

 

데이터의 압축을 풀기 위해서는 웹기반 압축프로그램인 CyberChef online tool을 사용해야 합니다. 

 

프로그램의 검색창에서 Find / Replace를 검색하고 해당 툴을 Recipe 창으로 드래그합니다. 

Find / Replace 검색
Find / Replace 툴 드래그

 

추가로 Remove whitespace와 From Hex, Gunzip를 검색하여 드래그 한 뒤 아래 그림처럼 설정해 줍니다. 

 

아래 그림처럼 Input 창에 GZIP압축 데이터를 붙여 넣기 해주면 Output 창에 HTML 코드가 출력됩니다. 

 

Toggle OV2640 settings 웹페이지 소스코드를 확보했으니 추가하고자 하는 아이콘 및 기능을 수정해 줍니다. 수정된 소스코드를 GZIP 데이터로 압축한 뒤 "index_ov2640_html_gz" 배열에 데이터를 코딩하면 됩니다. 

 

 Toggle OV2640 settings 페이지 HTML 코드 수정

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>ESP32 OV2460</title>
        <style>
            body {
                font-family: Arial,Helvetica,sans-serif;
                background: #181818;
                color: #EFEFEF;
                font-size: 16px
            }

            h2 {
                font-size: 18px
            }

            section.main {
                display: flex
            }

            #menu,section.main {
                flex-direction: column
            }

            #menu {
                display: none;
                flex-wrap: nowrap;
                min-width: 340px;
                background: #363636;
                padding: 8px;
                border-radius: 4px;
                margin-top: -10px;
                margin-right: 10px;
            }

            #content {
                display: flex;
                flex-wrap: wrap;
                align-items: stretch
            }

            figure {
                padding: 0px;
                margin: 0;
                -webkit-margin-before: 0;
                margin-block-start: 0;
                -webkit-margin-after: 0;
                margin-block-end: 0;
                -webkit-margin-start: 0;
                margin-inline-start: 0;
                -webkit-margin-end: 0;
                margin-inline-end: 0
            }

            figure img {
                display: block;
                width: 100%;
                height: auto;
                border-radius: 4px;
                margin-top: 8px;
            }

            @media (min-width: 800px) and (orientation:landscape) {
                #content {
                    display:flex;
                    flex-wrap: nowrap;
                    align-items: stretch
                }

                figure img {
                    display: block;
                    max-width: 100%;
                    max-height: calc(100vh - 40px);
                    width: auto;
                    height: auto
                }

                figure {
                    padding: 0 0 0 0px;
                    margin: 0;
                    -webkit-margin-before: 0;
                    margin-block-start: 0;
                    -webkit-margin-after: 0;
                    margin-block-end: 0;
                    -webkit-margin-start: 0;
                    margin-inline-start: 0;
                    -webkit-margin-end: 0;
                    margin-inline-end: 0
                }
            }

            section#buttons {
                display: flex;
                flex-wrap: nowrap;
                justify-content: space-between
            }

            #nav-toggle {
                cursor: pointer;
                display: block
            }

            #toggle-str {
                cursor: pointer;
                display: block
            }

            #toggle-fls { /* 플래시 아이콘 스타일 추가 */ 
                cursor: pointer;
                display: block
            }

            #nav-toggle-cb {
                outline: 0;
                opacity: 0;
                width: 0;
                height: 0
            }

            #nav-toggle-cb:checked+#menu {
                display: flex
            }

            .input-group {
                display: flex;
                flex-wrap: nowrap;
                line-height: 22px;
                margin: 5px 0
            }

            .input-group>label {
                display: inline-block;
                padding-right: 10px;
                min-width: 47%
            }

            .input-group input,.input-group select {
                flex-grow: 1
            }

            .range-max,.range-min {
                display: inline-block;
                padding: 0 5px
            }

            .left { /* div 플로팅 스타일 추가 */
                float: left;
            }

            button {
                display: block;
                margin: 5px;
                padding: 0 12px;
                border: 0;
                line-height: 28px;
                cursor: pointer;
                color: #fff;
                background: #ff3034;
                border-radius: 5px;
                font-size: 16px;
                outline: 0
            }

            button:hover {
                background: #ff494d
            }

            button:active {
                background: #f21c21
            }

            button.disabled {
                cursor: default;
                background: #a0a0a0
            }

            input[type=range] {
                -webkit-appearance: none;
                width: 100%;
                height: 22px;
                background: #363636;
                cursor: pointer;
                margin: 0
            }

            input[type=range]:focus {
                outline: 0
            }

            input[type=range]::-webkit-slider-runnable-track {
                width: 100%;
                height: 2px;
                cursor: pointer;
                background: #EFEFEF;
                border-radius: 0;
                border: 0 solid #EFEFEF
            }

            input[type=range]::-webkit-slider-thumb {
                border: 1px solid rgba(0,0,30,0);
                height: 22px;
                width: 22px;
                border-radius: 50px;
                background: #ff3034;
                cursor: pointer;
                -webkit-appearance: none;
                margin-top: -11.5px
            }

            input[type=range]:focus::-webkit-slider-runnable-track {
                background: #EFEFEF
            }

            input[type=range]::-moz-range-track {
                width: 100%;
                height: 2px;
                cursor: pointer;
                background: #EFEFEF;
                border-radius: 0;
                border: 0 solid #EFEFEF
            }

            input[type=range]::-moz-range-thumb {
                border: 1px solid rgba(0,0,30,0);
                height: 22px;
                width: 22px;
                border-radius: 50px;
                background: #ff3034;
                cursor: pointer
            }

            input[type=range]::-ms-track {
                width: 100%;
                height: 2px;
                cursor: pointer;
                background: 0 0;
                border-color: transparent;
                color: transparent
            }

            input[type=range]::-ms-fill-lower {
                background: #EFEFEF;
                border: 0 solid #EFEFEF;
                border-radius: 0
            }

            input[type=range]::-ms-fill-upper {
                background: #EFEFEF;
                border: 0 solid #EFEFEF;
                border-radius: 0
            }

            input[type=range]::-ms-thumb {
                border: 1px solid rgba(0,0,30,0);
                height: 22px;
                width: 22px;
                border-radius: 50px;
                background: #ff3034;
                cursor: pointer;
                height: 2px
            }

            input[type=range]:focus::-ms-fill-lower {
                background: #EFEFEF
            }

            input[type=range]:focus::-ms-fill-upper {
                background: #363636
            }

            .switch {
                display: block;
                position: relative;
                line-height: 22px;
                font-size: 16px;
                height: 22px
            }

            .switch input {
                outline: 0;
                opacity: 0;
                width: 0;
                height: 0
            }

            .slider {
                width: 50px;
                height: 22px;
                border-radius: 22px;
                cursor: pointer;
                background-color: grey
            }

            .slider,.slider:before {
                display: inline-block;
                transition: .4s
            }

            .slider:before {
                position: relative;
                content: "";
                border-radius: 50%;
                height: 16px;
                width: 16px;
                left: 4px;
                top: 3px;
                background-color: #fff
            }

            input:checked+.slider {
                background-color: #ff3034
            }

            input:checked+.slider:before {
                -webkit-transform: translateX(26px);
                transform: translateX(26px)
            }

            select {
                border: 1px solid #363636;
                font-size: 14px;
                height: 22px;
                outline: 0;
                border-radius: 5px
            }

            .image-container {
                position: relative;
                min-width: 160px
            }

            .close {
                position: absolute;
                right: 5px;
                top: 5px;
                background: #ff3034;
                width: 16px;
                height: 16px;
                border-radius: 100px;
                color: #fff;
                text-align: center;
                line-height: 18px;
                cursor: pointer
            }

            .hidden {
                display: none
            }

        </style>
    </head>
    <body>
        <section class="main">
            <div id="logo"> <!-- div 카메라 및 플래시 아이콘 추가 --> 
                <div class="left"><label for="nav-toggle-cb" id="nav-toggle">&#9776;&nbsp;&nbsp;Toggle OV2640 settings&nbsp;&nbsp;&nbsp;</label></div>
                <div class="left"><label id="toggle-str">&#128247&nbsp;&nbsp;</label></div>
                <div class="left"><input id="fls" type="checkbox" class="default-action hidden" checked="unchecked"><label for="fls" id="toggle-fls">&#128294&nbsp;</label></div>
            </div>
            <div id="content">
                <div id="sidebar">
                    <input type="checkbox" id="nav-toggle-cb">
                    <nav id="menu">
                        <div class="input-group" id="framesize-group">
                            <label for="framesize">Resolution</label>
                            <select id="framesize" class="default-action">
                                <option value="10">UXGA(1600x1200)</option>
                                <option value="9">SXGA(1280x1024)</option>
                                <option value="8">XGA(1024x768)</option>
                                <option value="7">SVGA(800x600)</option>
                                <option value="6">VGA(640x480)</option>
                                <option value="5" selected="selected">CIF(400x296)</option>
                                <option value="4">QVGA(320x240)</option>
                                <option value="3">HQVGA(240x176)</option>
                                <option value="0">QQVGA(160x120)</option>
                            </select>
                        </div>
                        <div class="input-group" id="quality-group">
                            <label for="quality">Quality</label>
                            <div class="range-min">10</div>
                            <input type="range" id="quality" min="10" max="63" value="10" class="default-action">
                            <div class="range-max">63</div>
                        </div>
                        <div class="input-group" id="brightness-group">
                            <label for="brightness">Brightness</label>
                            <div class="range-min">-2</div>
                            <input type="range" id="brightness" min="-2" max="2" value="0" class="default-action">
                            <div class="range-max">2</div>
                        </div>
                        <div class="input-group" id="contrast-group">
                            <label for="contrast">Contrast</label>
                            <div class="range-min">-2</div>
                            <input type="range" id="contrast" min="-2" max="2" value="0" class="default-action">
                            <div class="range-max">2</div>
                        </div>
                        <div class="input-group" id="saturation-group">
                            <label for="saturation">Saturation</label>
                            <div class="range-min">-2</div>
                            <input type="range" id="saturation" min="-2" max="2" value="0" class="default-action">
                            <div class="range-max">2</div>
                        </div>
                        <div class="input-group" id="special_effect-group">
                            <label for="special_effect">Special Effect</label>
                            <select id="special_effect" class="default-action">
                                <option value="0" selected="selected">No Effect</option>
                                <option value="1">Negative</option>
                                <option value="2">Grayscale</option>
                                <option value="3">Red Tint</option>
                                <option value="4">Green Tint</option>
                                <option value="5">Blue Tint</option>
                                <option value="6">Sepia</option>
                            </select>
                        </div>
                        <div class="input-group" id="awb-group">
                            <label for="awb">AWB</label>
                            <div class="switch">
                                <input id="awb" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="awb"></label>
                            </div>
                        </div>
                        <div class="input-group" id="awb_gain-group">
                            <label for="awb_gain">AWB Gain</label>
                            <div class="switch">
                                <input id="awb_gain" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="awb_gain"></label>
                            </div>
                        </div>
                        <div class="input-group" id="wb_mode-group">
                            <label for="wb_mode">WB Mode</label>
                            <select id="wb_mode" class="default-action">
                                <option value="0" selected="selected">Auto</option>
                                <option value="1">Sunny</option>
                                <option value="2">Cloudy</option>
                                <option value="3">Office</option>
                                <option value="4">Home</option>
                            </select>
                        </div>
                        <div class="input-group" id="aec-group">
                            <label for="aec">AEC SENSOR</label>
                            <div class="switch">
                                <input id="aec" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="aec"></label>
                            </div>
                        </div>
                        <div class="input-group" id="aec2-group">
                            <label for="aec2">AEC DSP</label>
                            <div class="switch">
                                <input id="aec2" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="aec2"></label>
                            </div>
                        </div>
                        <div class="input-group" id="ae_level-group">
                            <label for="ae_level">AE Level</label>
                            <div class="range-min">-2</div>
                            <input type="range" id="ae_level" min="-2" max="2" value="0" class="default-action">
                            <div class="range-max">2</div>
                        </div>
                        <div class="input-group" id="aec_value-group">
                            <label for="aec_value">Exposure</label>
                            <div class="range-min">0</div>
                            <input type="range" id="aec_value" min="0" max="1200" value="204" class="default-action">
                            <div class="range-max">1200</div>
                        </div>
                        <div class="input-group" id="agc-group">
                            <label for="agc">AGC</label>
                            <div class="switch">
                                <input id="agc" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="agc"></label>
                            </div>
                        </div>
                        <div class="input-group hidden" id="agc_gain-group">
                            <label for="agc_gain">Gain</label>
                            <div class="range-min">1x</div>
                            <input type="range" id="agc_gain" min="0" max="30" value="5" class="default-action">
                            <div class="range-max">31x</div>
                        </div>
                        <div class="input-group" id="gainceiling-group">
                            <label for="gainceiling">Gain Ceiling</label>
                            <div class="range-min">2x</div>
                            <input type="range" id="gainceiling" min="0" max="6" value="0" class="default-action">
                            <div class="range-max">128x</div>
                        </div>
                        <div class="input-group" id="bpc-group">
                            <label for="bpc">BPC</label>
                            <div class="switch">
                                <input id="bpc" type="checkbox" class="default-action">
                                <label class="slider" for="bpc"></label>
                            </div>
                        </div>
                        <div class="input-group" id="wpc-group">
                            <label for="wpc">WPC</label>
                            <div class="switch">
                                <input id="wpc" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="wpc"></label>
                            </div>
                        </div>
                        <div class="input-group" id="raw_gma-group">
                            <label for="raw_gma">Raw GMA</label>
                            <div class="switch">
                                <input id="raw_gma" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="raw_gma"></label>
                            </div>
                        </div>
                        <div class="input-group" id="lenc-group">
                            <label for="lenc">Lens Correction</label>
                            <div class="switch">
                                <input id="lenc" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="lenc"></label>
                            </div>
                        </div>
                        <div class="input-group" id="hmirror-group">
                            <label for="hmirror">H-Mirror</label>
                            <div class="switch">
                                <input id="hmirror" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="hmirror"></label>
                            </div>
                        </div>
                        <div class="input-group" id="vflip-group">
                            <label for="vflip">V-Flip</label>
                            <div class="switch">
                                <input id="vflip" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="vflip"></label>
                            </div>
                        </div>
                        <div class="input-group" id="dcw-group">
                            <label for="dcw">DCW (Downsize EN)</label>
                            <div class="switch">
                                <input id="dcw" type="checkbox" class="default-action" checked="checked">
                                <label class="slider" for="dcw"></label>
                            </div>
                        </div>
                        <div class="input-group" id="colorbar-group">
                            <label for="colorbar">Color Bar</label>
                            <div class="switch">
                                <input id="colorbar" type="checkbox" class="default-action">
                                <label class="slider" for="colorbar"></label>
                            </div>
                        </div>
                        <div class="input-group" id="face_detect-group">
                            <label for="face_detect">Face Detection</label>
                            <div class="switch">
                                <input id="face_detect" type="checkbox" class="default-action">
                                <label class="slider" for="face_detect"></label>
                            </div>
                        </div>
                        <div class="input-group" id="face_recognize-group">
                            <label for="face_recognize">Face Recognition</label>
                            <div class="switch">
                                <input id="face_recognize" type="checkbox" class="default-action">
                                <label class="slider" for="face_recognize"></label>
                            </div>
                        </div>
                        <section id="buttons">
                            <button id="get-still">Get Still</button>
                            <button id="toggle-stream">Start Stream</button>
                            <button id="face_enroll" class="disabled" disabled="disabled">Enroll Face</button>
                        </section>
                    </nav>
                </div>
                <figure>
                    <div id="stream-container" class="image-container hidden">
                        <div class="close" id="close-stream">X</div>
                        <img id="stream" src="">
                    </div>
                </figure>
            </div>
        </section>
        <script>
document.addEventListener('DOMContentLoaded', function (event) {
  var baseHost = document.location.origin
  var streamUrl = baseHost + ':81'

  const hide = el => {
    el.classList.add('hidden')
  }
  const show = el => {
    el.classList.remove('hidden')
  }

  const disable = el => {
    el.classList.add('disabled')
    el.disabled = true
  }

  const enable = el => {
    el.classList.remove('disabled')
    el.disabled = false
  }

  const updateValue = (el, value, updateRemote) => {
    updateRemote = updateRemote == null ? true : updateRemote
    let initialValue
    if (el.type === 'checkbox') {
      initialValue = el.checked
      value = !!value
      el.checked = value
    } else {
      initialValue = el.value
      el.value = value
    }

    if (updateRemote && initialValue !== value) {
      updateConfig(el);
    } else if(!updateRemote){
      if(el.id === "aec"){
        value ? hide(exposure) : show(exposure)
      } else if(el.id === "agc"){
        if (value) {
          show(gainCeiling)
          hide(agcGain)
        } else {
          hide(gainCeiling)
          show(agcGain)
        }
      } else if(el.id === "awb_gain"){
        value ? show(wb) : hide(wb)
      } else if(el.id === "face_recognize"){
        value ? enable(enrollButton) : disable(enrollButton)
      }
    }
  }

  function updateConfig (el) {
    let value
    switch (el.type) {
      case 'checkbox':
        value = el.checked ? 1 : 0
        break
      case 'range':
      case 'select-one':
        value = el.value
        break
      case 'button':
      case 'submit':
        value = '1'
        break
      default:
        return
    }

    const query = `${baseHost}/control?var=${el.id}&val=${value}`

    fetch(query)
      .then(response => {
        console.log(`request to ${query} finished, status: ${response.status}`)
      })
  }

  document
    .querySelectorAll('.close')
    .forEach(el => {
      el.onclick = () => {
        hide(el.parentNode)
      }
    })

  // read initial values
  fetch(`${baseHost}/status`)
    .then(function (response) {
      return response.json()
    })
    .then(function (state) {
      document
        .querySelectorAll('.default-action')
        .forEach(el => {
          updateValue(el, state[el.id], false)
        })
    })

  const view = document.getElementById('stream')
  const viewContainer = document.getElementById('stream-container')
  const stillButton = document.getElementById('get-still')
  const streamButton = document.getElementById('toggle-stream')
  const enrollButton = document.getElementById('face_enroll')
  const closeButton = document.getElementById('close-stream')
  const cameraButton = document.getElementById('toggle-str')
  const flashButton = document.getElementById('toggle-fls')

  const stopStream = () => {
    window.stop();
    streamButton.innerHTML = 'Start Stream'
    cameraButton.innerHTML = '&#128247&nbsp;&nbsp;'  //스트림 시 카메라 아이콘 변경 추가 --> 
  }

  const startStream = () => {
    view.src = `${streamUrl}/stream`
    show(viewContainer)
    streamButton.innerHTML = 'Stop Stream'
    cameraButton.innerHTML = '&#128248&nbsp;&nbsp;'  //스트림 시 카메라 아이콘 변경 추가 --> 
  }

  // Attach actions to buttons
  stillButton.onclick = () => {
    stopStream()
    view.src = `${baseHost}/capture?_cb=${Date.now()}`
    show(viewContainer)
  }

  closeButton.onclick = () => {
    stopStream()
    hide(viewContainer)
  }

  cameraButton.onclick = () => { // 카메라 아이콘 작동 코드 추가 --> 
    const streamEnabled = streamButton.innerHTML === 'Stop Stream'
    if (streamEnabled) {
      stopStream()
      hide(viewContainer)
    } else {
      startStream()
    }
  }

  flashButton.onclick = () => { // 플래시 아이콘 작동 코드 추가 --> 
    const flsOn = document.getElementById('fls')
    const is_checked = flsOn.checked;
    if (is_checked) { flashButton.innerHTML = '&#128262' }
    else { flashButton.innerHTML = '&#128294&nbsp;' } 
  }

  streamButton.onclick = () => {
    const streamEnabled = streamButton.innerHTML === 'Stop Stream'
    if (streamEnabled) {
      stopStream()
    } else {
      startStream()
    }
  }

  enrollButton.onclick = () => {
    updateConfig(enrollButton)
  }

  // Attach default on change action
  document
    .querySelectorAll('.default-action')
    .forEach(el => {
      el.onchange = () => updateConfig(el)
    })

  // Custom actions
  // Gain
  const agc = document.getElementById('agc')
  const agcGain = document.getElementById('agc_gain-group')
  const gainCeiling = document.getElementById('gainceiling-group')
  agc.onchange = () => {
    updateConfig(agc)
    if (agc.checked) {
      show(gainCeiling)
      hide(agcGain)
    } else {
      hide(gainCeiling)
      show(agcGain)
    }
  }

  // Exposure
  const aec = document.getElementById('aec')
  const exposure = document.getElementById('aec_value-group')
  aec.onchange = () => {
    updateConfig(aec)
    aec.checked ? hide(exposure) : show(exposure)
  }

  // AWB
  const awb = document.getElementById('awb_gain')
  const wb = document.getElementById('wb_mode-group')
  awb.onchange = () => {
    updateConfig(awb)
    awb.checked ? show(wb) : hide(wb)
  }

  // Detection and framesize
  const detect = document.getElementById('face_detect')
  const recognize = document.getElementById('face_recognize')
  const framesize = document.getElementById('framesize')

  framesize.onchange = () => {
    updateConfig(framesize)
    if (framesize.value > 5) {
      updateValue(detect, false)
      updateValue(recognize, false)
    }
  }

  detect.onchange = () => {
    if (framesize.value > 5) {
      alert("Please select CIF or lower resolution before enabling this feature!");
      updateValue(detect, false)
      return;
    }
    updateConfig(detect)
    if (!detect.checked) {
      disable(enrollButton)
      updateValue(recognize, false)
    }
  }

  recognize.onchange = () => {
    if (framesize.value > 5) {
      alert("Please select CIF or lower resolution before enabling this feature!");
      updateValue(recognize, false)
      return;
    }
    updateConfig(recognize)
    if (recognize.checked) {
      enable(enrollButton)
      updateValue(detect, true)
    } else {
      disable(enrollButton)
    }
  }
})

        </script>
    </body>
</html>

 

▶ 소스코드를 GZIP 데이터로 압축하기

압축하는 방법은 CyberChef online tool 프로그램에서 Gzip와 To Hex, split, URL Decode를 검색하여 드래그한 뒤 아래 그림처럼 설정해 줍니다. 

 

아래 그림처럼 Input 창에 수정한 HTML 소스코드를 붙여 넣기 해주면 Output 창에 압축된 데이터가 출력됩니다.

첫 번째 콤마 다음부터 마지막까지 드래그 한 뒤 복사하고 "index_ov2640_html_gz" 배열에 데이터를 붙여 넣기 합니다. 추가로 "index_ov2640_html_gz" 배열의 크기를 확인한 뒤 index_ov2640_html_gz_len 메크로 값을 수정해 줍니다. 

- 소스코드 크기: 29,601바이트

- GZIP으로 압축 후 크기: 4,552바이트

 

최종 수정한 데이터 파일: 수정한 스케치는 맨 아래에서 다운로드할 수 있습니다.

더보기
더보기
#define index_ov2640_html_gz_len 4552
const uint8_t index_ov2640_html_gz[] = {
 0x1f,0x8b,0x08,0x08,0x17,0xdf,0xcc,0x63,0x00,0xff,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,
 0x6c,0x2e,0x67,0x7a,0x00,0xe5,0x5d,0x7b,0x73,0xda,0xc6,0xd7,0xfe,0xbf,0x9f,0x42,0x51,0xd2,0x98,
 0x4c,0x0d,0x06,0x4c,0x1c,0x87,0xda,0xe4,0x4d,0x1c,0x27,0xe9,0x4c,0x6e,0x8d,0x7b,0x9b,0xe9,0x74,
 0x1c,0x21,0xad,0x40,0x8d,0x90,0xa8,0x24,0x8c,0xdd,0x4c,0xbe,0xfb,0xef,0xd9,0x8b,0xa4,0x95,0x58,
 0xdd,0x20,0x81,0xbc,0x29,0xcc,0x80,0x90,0xf6,0x9c,0x3d,0xf7,0x3d,0x7b,0xe5,0xe4,0x96,0xe5,0x9b,
 0xd1,0xcd,0x9c,0x68,0xd3,0x68,0xe6,0x8e,0xbe,0x3b,0xe1,0x5f,0x1a,0x5e,0x27,0x53,0x62,0x58,0xfc,
 0x92,0xfd,0x9c,0x91,0xc8,0xd0,0xcc,0xa9,0x11,0x84,0x24,0x3a,0xd5,0x17,0x91,0xdd,0x3e,0xd6,0xf3,
 0x8f,0x3d,0x63,0x46,0x4e,0xf5,0x2b,0x87,0x2c,0xe7,0x7e,0x10,0xe9,0x9a,0xe9,0x7b,0x11,0xf1,0x50,
 0x7c,0xe9,0x58,0xd1,0xf4,0xd4,0x22,0x57,0x8e,0x49,0xda,0xec,0xc7,0xbe,0xe3,0x39,0x91,0x63,0xb8,
 0xed,0xd0,0x34,0x5c,0x72,0xda,0x93,0x71,0x45,0x4e,0xe4,0x92,0xd1,0xf9,0xc5,0xdb,0xc3,0xbe,0xf6,
 0xe6,0xb7,0xfe,0xe0,0xa8,0x7b,0x72,0xc0,0xef,0xa5,0x65,0xc2,0xe8,0x46,0xfe,0x4d,0x5f,0x63,0xdf,
 0xba,0xd1,0x3e,0x66,0x6e,0xd1,0x97,0x0d,0x22,0xda,0xb6,0x31,0x73,0xdc,0x9b,0xa1,0xf6,0x38,0x40,
 0x9d,0xfb,0x2f,0x88,0x7b,0x45,0x22,0xc7,0x34,0xf6,0x43,0xc3,0x0b,0xdb,0x21,0x09,0x1c,0xfb,0xc7,
 0x15,0xc0,0xb1,0x61,0x7e,0x98,0x04,0xfe,0xc2,0xb3,0x86,0xda,0xed,0xde,0x31,0x7d,0xaf,0x16,0x32,
 0x7d,0xd7,0x0f,0xf0,0xfc,0xfc,0x19,0x7d,0xaf,0x3e,0x67,0xb5,0x87,0xce,0xbf,0x64,0xa8,0xf5,0x8e,
 0xe6,0xd7,0x99,0xe7,0x9f,0xbe,0xcb,0xfc,0x9c,0xf6,0x8b,0xa8,0x17,0xf0,0xc7,0xe5,0xf0,0x21,0x31,
 0x23,0xc7,0xf7,0x3a,0x33,0xc3,0xf1,0x14,0x98,0x2c,0x27,0x9c,0xbb,0x06,0x64,0x60,0xbb,0xa4,0x14,
 0xcf,0xed,0x19,0xf1,0x16,0xfb,0x15,0xd8,0x28,0x92,0xb6,0xe5,0x04,0xbc,0xd4,0x90,0xca,0x61,0x31,
 0xf3,0x2a,0xd1,0x96,0xd1,0xe5,0xf9,0x1e,0x51,0x08,0x90,0x56,0xb4,0x0c,0x8c,0x39,0x2d,0x40,0xbf,
 0x57,0x8b,0xcc,0x1c,0x8f,0x1b,0xd5,0x50,0x3b,0x1c,0x74,0xe7,0xd7,0x15,0xaa,0x3c,0x3c,0xa2,0xef,
 0xd5,0x42,0x73,0xc3,0xb2,0x1c,0x6f,0x32,0xd4,0x20,0x67,0x05,0x0a,0x3f,0xb0,0x48,0xd0,0x0e,0x0c,
 0xcb,0x59,0x84,0x43,0x6d,0xa0,0x2a,0x33,0x33,0x82,0x09,0x68,0x89,0x7c,0x10,0xdb,0xee,0x29,0x29,
 0x11,0x45,0x02,0x67,0x32,0x8d,0xa0,0xd2,0x95,0x32,0x79,0xa1,0x09,0x17,0xaa,0xd2,0x67,0xa9,0xdc,
 0xd4,0x52,0x33,0x5c,0x67,0xe2,0xb5,0x9d,0x88,0xcc,0xc0,0x4e,0x18,0x05,0x24,0x32,0xa7,0x65,0xa4,
 0xd8,0xce,0x64,0x11,0x10,0x05,0x21,0x89,0xdc,0x4a,0x18,0xc6,0xc3,0xd5,0x47,0xed,0x25,0x19,0x7f,
 0x70,0xa2,0xb6,0x90,0xc9,0x98,0xd8,0x7e,0x00,0x3b,0x57,0x94,0x8c,0x4b,0xb8,0xbe,0xf9,0xa1,0x1d,
 0x46,0x46,0x00,0xd9,0x55,0x23,0x34,0xec,0x88,0xc0,0x37,0xab,0xf0,0x11,0x6a,0x15,0xd5,0xd8,0x8a,
 0xab,0x15,0x05,0x1c,0xcf,0x75,0x3c,0x52,0x9f,0xbc,0xa2,0x7a,0xb3,0xe8,0x78,0xa9,0x1a,0x8a,0x71,
 0x66,0x93,0x32,0x2b,0x61,0xbc,0xae,0x56,0x26,0xfc,0xa6,0xd7,0xed,0x7e,0xbf,0xfa,0x70,0x4a,0xb8,
 0x99,0x1a,0x8b,0xc8,0xdf,0xdc,0x23,0x56,0xdc,0x2a,0xc7,0xc7,0xff,0xcd,0x88,0xe5,0x18,0x5a,0x4b,
 0x72,0xe7,0xe3,0x2e,0x6c,0xea,0x9e,0x66,0x78,0x96,0xd6,0xf2,0x03,0x07,0x8e,0x60,0xb0,0x70,0xe3,
 0xe2,0x0e,0x1a,0x8e,0x39,0xb9,0xa7,0x60,0xb9,0xc4,0x67,0x64,0x89,0xa8,0xdd,0xa6,0x66,0xc8,0xa9,
 0xe5,0x40,0x0a,0x1e,0x2b,0xf5,0x55,0x47,0x67,0x5c,0xb0,0x20,0xb1,0x4c,0x77,0x71,0xa1,0x58,0x87,
 0x68,0x66,0xcd,0x16,0x8a,0x5e,0x4d,0xb5,0xb6,0x46,0xa3,0xe4,0x3d,0x35,0x8c,0x40,0xaa,0x56,0x79,
 0xde,0x28,0x1a,0xb0,0xab,0x66,0x35,0x8d,0x1d,0xfc,0xad,0xb2,0xa1,0x8a,0x28,0xd2,0x2c,0x92,0x34,
 0x88,0x26,0x8d,0x22,0x4a,0xed,0xa8,0xd2,0x28,0xb2,0x34,0x89,0x2e,0x0d,0x22,0x4c,0xad,0x28,0xc3,
 0xd5,0x59,0x9d,0x6f,0xdc,0x1e,0x2f,0xa2,0xc8,0xf7,0xc2,0x8d,0x9a,0xa8,0x22,0x3f,0xfb,0x7b,0x11,
 0x46,0x8e,0x7d,0xd3,0x16,0x2e,0x0d,0x3f,0x9b,0x1b,0x48,0x21,0xc7,0x24,0x5a,0x12,0x52,0x9e,0x6e,
 0x78,0xc6,0x15,0xe2,0xce,0x64,0xe2,0xaa,0x6c,0xcf,0x5c,0x04,0x21,0xcd,0xdb,0xe6,0xbe,0x03,0xc4,
 0xc1,0x6a,0xc5,0x59,0x1f,0x2c,0xad,0x88,0x57,0x02,0xcd,0x04,0xdb,0xa9,0xc8,0x76,0x55,0xb2,0xfe,
 0x9c,0x15,0xa5,0xa2,0x6b,0x9b,0x63,0x45,0x5d,0xfe,0x22,0xa2,0x56,0xa3,0xb4,0x2d,0x1f,0x0a,0x72,
 0x22,0x54,0xa3,0x78,0x26,0x62,0x8b,0xe2,0x49,0x1c,0x54,0x4a,0x1b,0xba,0x2c,0x5d,0x43,0x73,0x4a,
 0xcc,0x0f,0xc4,0xfa,0xa1,0x32,0xb1,0xac,0x4a,0x78,0x3b,0x8e,0x37,0x5f,0x44,0x6d,0x9a,0x20,0xce,
 0xbf,0x88,0x15,0x33,0x17,0x8b,0x59,0xec,0xf7,0xcb,0xd2,0xa4,0xfb,0xf3,0xeb,0x72,0x21,0xc8,0xc4,
 0x8e,0x5c,0x63,0x4c,0xdc,0x32,0x92,0x85,0x7b,0x17,0x34,0x24,0x22,0xfa,0x16,0x67,0xa3,0xb9,0xec,
 0x7a,0xf0,0xe0,0xfb,0xda,0x72,0x64,0xd7,0xfb,0x99,0x5b,0x21,0x71,0x11,0x32,0x8a,0x3a,0x13,0x28,
 0xb3,0x04,0x0d,0xa5,0x15,0x04,0x86,0x37,0x21,0x88,0x6e,0xd7,0xfb,0xf1,0x65,0x79,0x57,0xa7,0x16,
 0xfb,0xb4,0xf1,0x81,0xd8,0x4b,0x2b,0x76,0x89,0xad,0x26,0xdc,0x37,0x20,0x38,0xfa,0xb4,0x34,0xb5,
 0xe1,0x21,0x72,0x8d,0xf4,0x4c,0x32,0x8b,0x52,0xfa,0x7b,0x4a,0xa3,0xe2,0x19,0x9a,0xd2,0xe1,0xb2,
 0x26,0xa9,0xec,0xf1,0x54,0x46,0x94,0xb8,0xef,0x6b,0xdb,0x55,0xbd,0x67,0xdb,0x3e,0xec,0x1e,0x0e,
 0x2a,0x53,0x48,0x25,0x97,0xb9,0x1e,0xb4,0x22,0xe2,0x24,0xd1,0xa8,0x5a,0x05,0xc3,0xa9,0x7f,0x45,
 0x54,0x71,0x3a,0x47,0xee,0xe0,0xe1,0xc0,0xaa,0x81,0xcd,0x40,0x0b,0x78,0xa5,0x6a,0x5f,0xb2,0xe8,
 0xfa,0x3d,0xb3,0x5f,0x6a,0xd8,0x1c,0x5d,0x07,0xd6,0x60,0x8c,0x5d,0x62,0x95,0x84,0x77,0x8b,0xd8,
 0xc6,0xc2,0xcd,0x59,0xdb,0x4a,0x8d,0x46,0x97,0xbe,0xcb,0x6a,0x64,0x7e,0xf9,0x27,0x1d,0xfa,0x39,
 0x65,0x9e,0xf4,0x97,0xa2,0xce,0x38,0x9b,0x30,0xe6,0x73,0x62,0xa0,0x94,0x09,0x19,0xab,0x3b,0xe9,
 0xb5,0x7a,0x11,0xea,0xc0,0x57,0xab,0x6b,0x5e,0x69,0x8a,0x49,0x7e,0xd8,0x88,0xe7,0xa1,0xed,0x9b,
 0x0b,0x55,0x63,0x5a,0xcf,0xa4,0x56,0xf1,0x0d,0x63,0x91,0x85,0xae,0xc3,0x0c,0x7b,0xe1,0x79,0x54,
 0xa3,0xed,0x28,0x00,0x9b,0x8a,0x8a,0xea,0x09,0x6e,0x2d,0xef,0xcc,0x08,0xb6,0x68,0x78,0x2a,0xe7,
 0x80,0x8a,0x40,0x91,0xc4,0x10,0x2d,0xf4,0xc1,0x54,0x8c,0x6a,0x33,0xb9,0x44,0xd3,0xc5,0x4c,0x95,
 0x58,0xc4,0x95,0xf5,0xd0,0x0a,0xf2,0xea,0x82,0xc9,0xd8,0x68,0x75,0xf7,0xbb,0xfb,0x87,0xf8,0x50,
 0x74,0x59,0xca,0x8d,0x4b,0x88,0xb7,0xc0,0xf2,0x72,0xc1,0xa7,0x7a,0xe4,0xa8,0x28,0x8c,0x55,0xea,
 0xa2,0xbe,0x27,0x65,0x87,0x90,0x7a,0x9d,0x8a,0x86,0xa9,0xc0,0xa4,0x9b,0x1b,0xa2,0xc2,0x5a,0x9a,
 0xaa,0x78,0xe6,0xff,0x0b,0x61,0xd2,0x56,0xf9,0x3f,0x6f,0xed,0x92,0x28,0xbe,0x69,0x4b,0x6f,0x2c,
 0x97,0x70,0xd7,0xb6,0x81,0x31,0x86,0x42,0xf9,0x88,0x7c,0x06,0x14,0x7a,0xe8,0x66,0x06,0xe8,0x6f,
 0x16,0xe6,0x3c,0x52,0x99,0x35,0x64,0x60,0x3b,0xae,0xdb,0x76,0xfd,0x65,0x75,0x26,0x52,0x6e,0xc9,
 0x2b,0x76,0x5a,0x6d,0xf2,0xeb,0x52,0xbb,0x40,0xe4,0xfa,0x7f,0x41,0xed,0x7f,0xad,0x69,0x91,0x5c,
 0x63,0xbd,0x86,0x62,0x0d,0x7b,0xdc,0xac,0xa2,0x5a,0xa6,0xc4,0x33,0xc1,0xd2,0x3e,0x59,0xb8,0x74,
 0x30,0xe2,0xba,0x46,0xa7,0x6a,0xee,0x87,0x98,0x7b,0xa4,0xb3,0x56,0x01,0x71,0x31,0xa0,0x7c,0xa5,
 0x68,0x87,0x6b,0x74,0xd9,0x2b,0x3b,0x26,0x32,0x78,0x1d,0x4e,0x98,0xe8,0xbe,0x9e,0xe1,0x96,0x0e,
 0xcf,0x1d,0x8a,0x63,0xb5,0xda,0xac,0x2b,0xd2,0xfd,0xac,0x67,0xa8,0x0b,0x35,0x88,0xe8,0x71,0xd0,
 0x9e,0x04,0xe4,0xa6,0x06,0x33,0xfb,0xe2,0x7b,0xc8,0x87,0x88,0xd7,0x1f,0x3b,0x60,0x0d,0x80,0xb0,
 0xa2,0xce,0x20,0xac,0x51,0x75,0x71,0x95,0x75,0xec,0x31,0x19,0x00,0xd5,0xf5,0x1a,0xe1,0xa6,0xa4,
 0x09,0x55,0x9b,0x6a,0xdc,0xfa,0x2a,0x1f,0xd2,0xe1,0x8d,0x82,0xf9,0x1d,0x96,0xa7,0x1e,0x96,0x47,
 0xb7,0x58,0x45,0x74,0x9c,0xa0,0x32,0x72,0x24,0xa3,0x7a,0xc5,0xd6,0xa7,0xc4,0x4c,0xa3,0x67,0x63,
 0xe4,0xc5,0x2a,0x89,0xd3,0x67,0xa6,0x66,0x94,0x99,0x89,0x26,0x1f,0xea,0x21,0x7f,0xb4,0xfa,0x10,
 0x93,0xa2,0xc1,0x28,0x29,0x5c,0x3e,0x8e,0x5e,0x30,0x2c,0xb6,0xda,0x64,0x15,0x76,0x90,0xe5,0x58,
 0xa4,0x54,0x54,0xb9,0x57,0x96,0x45,0x98,0xd5,0x31,0x9a,0xf2,0x31,0xc0,0x99,0x81,0xb4,0x97,0x9a,
 0x2b,0x16,0x0e,0x28,0xf5,0x57,0xc7,0xdc,0xa5,0x41,0xc7,0xde,0x11,0xc2,0x4c,0x69,0x95,0xa6,0xeb,
 0x87,0xe5,0x7e,0x65,0x8c,0x21,0xbf,0x45,0xa4,0xa8,0x48,0x0c,0x7d,0x2a,0x47,0x9e,0x98,0x71,0x2b,
 0x9f,0xd4,0x6a,0xba,0x4b,0x7d,0xaa,0xdc,0x1d,0x73,0x32,0x47,0x52,0xac,0x0c,0x93,0x65,0xe3,0x6f,
 0x11,0xb9,0x46,0x7f,0x93,0x4e,0x51,0x62,0xee,0x0f,0x91,0x43,0x15,0x46,0x33,0x8d,0x1c,0x5d,0x5e,
 0xb2,0x51,0xc2,0xdf,0x99,0x3a,0x96,0x45,0x4a,0x47,0x39,0x69,0x9f,0xb7,0x08,0xc5,0xc9,0x81,0xb4,
 0xa6,0xe7,0xe4,0x20,0x5d,0x7e,0x74,0x42,0x17,0xf6,0xc8,0x4b,0x7f,0xf8,0xbc,0x93,0x66,0xba,0x46,
 0x18,0x9e,0xea,0x74,0x81,0x8a,0xb4,0x7a,0x88,0x15,0xb1,0x9c,0x2b,0xcd,0xb1,0x4e,0x75,0xd7,0x9f,
 0xf8,0xb9,0x67,0xc9,0x73,0x01,0x4e,0x83,0x9b,0x3e,0x3a,0xe1,0x23,0xe9,0x70,0xde,0x53,0x3d,0x33,
 0xcb,0xa0,0x33,0x3c,0xe9,0x2d,0x7d,0x74,0xf7,0xf6,0xc3,0x07,0x0f,0x8e,0x7e,0xbc,0xeb,0x8d,0xc3,
 0xb9,0xf8,0xfc,0x85,0x4f,0x33,0x61,0xad,0xd2,0xd1,0x00,0x39,0x2e,0x89,0x22,0x0c,0xcb,0x86,0x72,
 0x09,0xfe,0x79,0x72,0xc0,0xaa,0x19,0x9d,0x1c,0x80,0x80,0x06,0x64,0x51,0x12,0xd2,0x59,0x26,0x4a,
 0x42,0xaf,0x7f,0xdc,0x1f,0x3c,0xd8,0x0c,0x37,0x4f,0x37,0x28,0x6e,0xcc,0x28,0xe9,0x1a,0x4b,0xd9,
 0x74,0x16,0x21,0xc7,0xfe,0x35,0x16,0x6f,0xf1,0xc2,0x62,0xc0,0xb1,0x4d,0x87,0x3a,0x21,0x74,0xae,
 0x63,0x3c,0xe5,0x91,0x14,0x2b,0xc1,0x3c,0x71,0x99,0x15,0x22,0x43,0x29,0xd1,0x4d,0x7f,0x0b,0xba,
 0x1f,0x0e,0x2a,0x29,0x56,0xdd,0x8a,0x75,0x2a,0x5a,0xc2,0x22,0xb5,0xd2,0x22,0x21,0x82,0xfb,0xd8,
 0x80,0xa0,0x94,0x33,0x9f,0x82,0xf1,0x3c,0xbf,0x59,0x3d,0x53,0xd5,0x17,0x80,0xa3,0x10,0x2b,0x4c,
 0xa7,0x9d,0x0a,0xca,0xe4,0x25,0x2e,0xcd,0x82,0xf0,0x8a,0xec,0x00,0x4b,0xe5,0x68,0xbc,0x16,0x37,
 0x8b,0xd1,0x30,0x54,0xb2,0x60,0x63,0x48,0x7d,0xf4,0x8e,0xb0,0xa8,0x06,0xb5,0xc4,0xa2,0x2c,0xc7,
 0x22,0x1a,0x9a,0x4c,0xfd,0x05,0x7a,0xae,0x20,0x88,0xa1,0xf3,0xe7,0xcc,0x22,0xae,0x0c,0x77,0x01,
 0x41,0xf6,0xba,0xfa,0xe8,0xd7,0x3f,0x9e,0x3f,0x6e,0x21,0x5e,0x77,0xaf,0x7b,0xfd,0x6e,0xf7,0xde,
 0xc9,0x01,0x2f,0xd2,0x18,0xd7,0x43,0x7d,0x74,0xc1,0x50,0xf5,0x8f,0x81,0xaa,0xdb,0x1f,0xac,0x8f,
 0x0a,0x8b,0x14,0x19,0x26,0x20,0xb9,0x7e,0x70,0x74,0xbc,0x3e,0xa2,0x07,0xa0,0xe9,0x37,0x60,0xc2,
 0x92,0x94,0x6b,0x70,0xb8,0x3e,0xa2,0x23,0x7d,0x44,0xf1,0x20,0x52,0x5c,0x0f,0x8e,0x37,0xc0,0x73,
 0x5f,0x17,0x99,0x03,0xf5,0xc2,0xf8,0x4a,0x1f,0x9d,0xfd,0xf4,0xac,0x35,0x00,0x8d,0xfd,0x87,0x47,
 0xeb,0xe3,0x1e,0xe8,0xa3,0x9f,0x29,0x91,0x87,0x7d,0x20,0x1a,0x6c,0x40,0xe4,0xa1,0x3e,0x7a,0xc1,
 0x30,0x01,0xcb,0x75,0xef,0xc1,0x06,0x24,0xc1,0xbc,0x7e,0x66,0x98,0x60,0x5f,0xd4,0xbc,0x6a,0x62,
 0x42,0xab,0xc2,0x44,0x53,0xe2,0xa7,0xea,0x80,0x59,0xcb,0x8d,0xff,0x59,0xa0,0x85,0x8d,0x6e,0x1a,
 0x3b,0xb1,0x80,0x03,0x4b,0xfc,0xa2,0x9e,0xff,0x4a,0x94,0x24,0xb3,0x9f,0xfa,0xa8,0x87,0xd5,0xb1,
 0xa5,0x1c,0xac,0xc4,0x3c,0x06,0x9c,0x61,0x40,0xa7,0x09,0x17,0xf3,0x61,0xba,0x66,0x08,0x36,0x7a,
 0xa8,0x4b,0x7e,0xbd,0x56,0x88,0x50,0x50,0x6b,0x5c,0xeb,0xa3,0xa3,0xc3,0x2a,0x79,0x6f,0xa0,0x8e,
 0x31,0xcb,0xe6,0x3c,0x12,0x86,0x8d,0x35,0x92,0x82,0xea,0xa3,0x27,0xc9,0xf5,0x26,0x7a,0x69,0xf7,
 0x37,0xd0,0x8b,0x44,0x0e,0x57,0x4d,0xbb,0x2f,0x54,0x83,0xef,0xc4,0x23,0x3e,0xa7,0x62,0xaa,0xa8,
 0xdd,0x44,0x2f,0xb4,0xc9,0x0e,0x8c,0x30,0xbe,0x57,0x5f,0x2b,0x31,0x20,0xc2,0x9a,0xb8,0xda,0x99,
 0x46,0x12,0x52,0xbe,0x01,0x7d,0x84,0x46,0xb4,0x08,0xd8,0x4a,0xca,0xc6,0x1a,0x49,0x41,0xd1,0x1e,
 0x26,0xd7,0x3b,0xd3,0x8a,0x44,0xce,0xb7,0xa0,0x97,0x39,0x31,0xb1,0x3f,0xe1,0x92,0xd8,0x36,0x9a,
 0xac,0xe6,0xba,0xc9,0x80,0x43,0x3f,0xfc,0xb7,0x76,0xce,0x7e,0x37,0xce,0x11,0x73,0xe8,0x3e,0x57,
 0xa2,0x08,0x75,0xa8,0xf2,0x96,0xd7,0x7e,0x42,0xe7,0x9a,0x19,0x02,0xf6,0x90,0xbc,0x26,0x13,0x36,
 0xa0,0xb0,0x36,0x8e,0xbe,0x3e,0x7a,0x1e,0x18,0x37,0x6c,0x53,0xca,0x26,0x49,0xcf,0x3b,0x2c,0xe2,
 0xf8,0x05,0x3d,0xe6,0x4d,0x32,0xb0,0xe7,0x01,0x96,0x38,0x6e,0x86,0xe5,0x3e,0x1a,0x33,0x5c,0x6c,
 0x86,0x04,0x09,0xeb,0x05,0x99,0x3b,0xc6,0xd7,0x90,0x70,0x19,0xcb,0x71,0x63,0xb7,0x00,0x8c,0x3e,
 0x7a,0xfc,0xfb,0x93,0xc6,0x41,0x8a,0x0f,0xcb,0xd7,0xb1,0xf0,0xb4,0x2b,0x4d,0x2b,0xab,0xd7,0x95,
 0x96,0xfa,0xd0,0x49,0x0f,0xba,0xba,0x26,0xce,0x57,0x4c,0x20,0x1b,0xc5,0xd4,0x25,0x36,0xeb,0xf1,
 0xf8,0xe5,0x22,0x18,0x88,0xb8,0x9c,0x60,0x40,0x66,0x1d,0x25,0x31,0x40,0xa6,0x29,0xed,0x39,0xae,
 0xb6,0xa5,0x2e,0x5e,0xed,0xce,0x74,0x26,0xb8,0xde,0xb5,0xe2,0x40,0xc8,0xcc,0xb7,0x9a,0x0f,0x47,
 0x08,0x38,0x7d,0x04,0xad,0xbd,0xc2,0x45,0xe3,0x56,0x26,0x46,0xf0,0x85,0x9b,0x97,0xc7,0xd8,0xad,
 0xb0,0x49,0xcb,0x72,0x81,0x05,0x34,0xe8,0xa6,0xad,0xdf,0xac,0x9c,0xb9,0xfe,0xc2,0x5a,0x1f,0x03,
 0xda,0x94,0x37,0xb6,0x8d,0xed,0x93,0x9b,0xb4,0x28,0x2f,0xfc,0x59,0x4d,0xf8,0x2f,0x1c,0xc5,0x89,
 0xd9,0x3c,0x40,0x10,0x13,0x5a,0x3c,0x3f,0xd3,0x2e,0xce,0x5f,0x5f,0xbc,0x79,0xb7,0x9d,0xe8,0x80,
 0x3a,0x77,0x14,0x18,0x28,0xb7,0x3b,0x0f,0xe6,0xc4,0xec,0xaf,0xa3,0x27,0x58,0x3b,0x55,0xd4,0xd3,
 0x8b,0xb7,0xdb,0xd2,0x12,0x92,0xfd,0x5d,0xa9,0x09,0xcc,0xee,0x5e,0x4f,0x97,0x2e,0xb9,0x22,0xee,
 0x1a,0xba,0xe2,0x80,0x54,0x5f,0xda,0x4b,0x7a,0xb5,0xb3,0x8e,0x5c,0x42,0xca,0x37,0xd0,0x8d,0x83,
 0x55,0x5c,0x32,0xa2,0xd7,0x71,0x1e,0x0e,0xa9,0x8f,0xce,0xaf,0x31,0x7d,0x89,0x8d,0x73,0x9b,0x68,
 0x64,0x93,0x91,0xc1,0x94,0x14,0xae,0x91,0x78,0x68,0x90,0x8e,0xec,0x27,0x3a,0xe9,0x77,0x07,0x9f,
 0x55,0x2b,0x14,0xf9,0x97,0x54,0xcc,0x64,0x8d,0x76,0x67,0x42,0xdb,0x9d,0xe7,0x67,0xdb,0x09,0x65,
 0xa8,0x6c,0x47,0x91,0x8c,0xb2,0xb9,0xbb,0x40,0x96,0xcc,0x2b,0x0a,0x29,0xac,0xd9,0x89,0x10,0x80,
 0xe8,0x3b,0xaf,0xd3,0x81,0x90,0x07,0xd5,0xaf,0x37,0x71,0x9d,0x98,0x8c,0xac,0xe7,0x1c,0xa6,0x7e,
 0x83,0xb9,0x9b,0xcf,0xe8,0x35,0x87,0x95,0xd4,0x6e,0xe2,0x34,0x94,0x13,0x93,0x38,0x58,0x2b,0x30,
 0x69,0xac,0x10,0x09,0x96,0xeb,0x44,0x3b,0xe3,0xbf,0x36,0xd1,0x4d,0x7f,0x13,0xdd,0xc8,0x14,0x65,
 0xd5,0x73,0xf4,0x85,0x5a,0x1a,0x4c,0x62,0x7e,0x49,0xf5,0x8c,0xe7,0xcd,0x63,0x1a,0x60,0x30,0x30,
 0xf4,0x76,0x3b,0x31,0x8d,0x56,0x56,0x33,0xa6,0x6d,0x14,0xc1,0x18,0x53,0x3b,0xef,0x46,0xaf,0xa1,
 0x0d,0xc0,0xa0,0xfb,0xbc,0x25,0x6d,0xd0,0xca,0x76,0xd3,0xc2,0x30,0x36,0x77,0xad,0x9f,0xc0,0x58,
 0x5e,0x4e,0x66,0x46,0x63,0x1d,0x09,0x38,0x0c,0xec,0x1a,0x4b,0xed,0xf9,0xab,0xc7,0x5b,0xd1,0x55,
 0x5c,0xe9,0x6e,0xf4,0x95,0xb0,0xbc,0x6b,0x9d,0xb9,0xc4,0x6b,0xee,0x54,0x14,0x48,0x1f,0xbd,0x24,
 0x38,0x97,0xe0,0xcc,0x0f,0xc4,0x31,0x45,0x5b,0xd1,0x1a,0xab,0x79,0x37,0x2a,0xe3,0x4c,0xef,0x5a,
 0x5f,0xd3,0x99,0x13,0x04,0x7e,0xd0,0x58,0x65,0x02,0x0e,0xc3,0x54,0xed,0x57,0xec,0x6a,0x2b,0xea,
 0x8a,0x6b,0xdd,0x8d,0xc6,0x12,0x9e,0x77,0xad,0xb4,0x2b,0xdb,0x75,0xe6,0x8d,0x55,0xc6,0xa0,0xb0,
 0x9e,0xa9,0xfd,0x0c,0xdf,0x5b,0x51,0x17,0xaf,0x71,0x37,0xca,0x12,0xdc,0xee,0x5a,0x55,0x96,0xb9,
 0x6c,0xac,0x28,0xc0,0xe8,0xa3,0xa7,0x67,0xbf,0x6b,0xad,0xa7,0xfe,0x12,0x1b,0x17,0xfe,0x25,0xda,
 0xf9,0x6b,0x2c,0xa1,0xda,0x82,0xc6,0x68,0xd5,0xbb,0xd1,0x17,0x63,0x7a,0xd7,0xda,0x62,0x8b,0xb4,
 0xb1,0x18,0x75,0x8d,0xb5,0x2f,0x1c,0x90,0xae,0x7d,0xc1,0x95,0xf6,0xc4,0xd8,0x4e,0x40,0x4c,0xea,
 0xdd,0x46,0xd2,0x9e,0x32,0xb9,0x6b,0x3d,0xd9,0x38,0x57,0xe8,0xd2,0x22,0xd1,0x3a,0x0b,0x2f,0x24,
 0x58,0x7d,0xf4,0x0c,0x3f,0xb4,0xa7,0xec,0xc7,0xb6,0x52,0x0e,0xb9,0xfe,0x6d,0x68,0x2d,0xc3,0xef,
 0x57,0xa1,0x38,0x24,0x78,0xfe,0xc4,0x5b,0x6b,0x3d,0x75,0x06,0x5c,0xa8,0xef,0x1d,0xff,0xbd,0x5d,
 0x05,0xa6,0x44,0x6c,0x4d,0x87,0x12,0xdf,0xdb,0x50,0x63,0xbc,0x73,0x83,0x0d,0x0b,0xf0,0x53,0xc3,
 0xaa,0x34,0x25,0x4e,0xce,0x61,0x43,0x37,0x04,0x5b,0x9c,0x22,0xec,0x20,0xc5,0x50,0x12,0x89,0xb4,
 0x0b,0x7a,0x79,0x72,0xc0,0x0b,0xd4,0xc7,0x92,0xee,0xa3,0x20,0xc6,0x0c,0xf3,0xba,0xf4,0x3c,0x35,
 0xe0,0xa2,0xbf,0x9a,0x23,0x63,0x42,0x24,0x5e,0xe0,0x83,0xa8,0x44,0x49,0xe2,0x10,0x17,0x9d,0x6e,
 0x7b,0x61,0x57,0xd2,0xbd,0xd1,0x39,0x2b,0xac,0x51,0x2b,0xab,0xae,0x8e,0xce,0xc2,0x32,0x89,0x15,
 0xec,0x44,0x38,0xc0,0x56,0x04,0xc5,0x36,0x88,0x82,0x1d,0x20,0xfc,0x20,0xbe,0x02,0x54,0xc9,0xd6,
 0x09,0x26,0x89,0x74,0xff,0x56,0xc2,0x56,0x7e,0x5f,0x97,0x18,0xb0,0xad,0xe7,0xb4,0x6c,0x87,0x96,
 0x68,0x0f,0xe9,0x65,0x22,0xfe,0x3f,0xaa,0x4c,0x86,0x9e,0x92,0x98,0xd2,0x85,0xb9,0xfd,0xc0,0x3c,
 0xd5,0x8b,0x76,0x66,0x14,0x30,0x7e,0xa0,0xe2,0x3c,0x57,0x58,0x21,0xea,0x93,0xd0,0x0c,0x9c,0x39,
 0xa6,0xc0,0x71,0x46,0xf2,0x02,0x9b,0x3d,0xa2,0x0e,0xce,0x5b,0x3a,0xbf,0xc2,0xc5,0x4b,0x27,0xc4,
 0x16,0x14,0x12,0xb4,0xf6,0x9e,0xbe,0x79,0x45,0x57,0xa6,0xd2,0x7b,0xbe,0x61,0x11,0x6b,0x6f,0x5f,
 0xb3,0xb1,0x23,0x86,0x59,0x79,0x0b,0xf3,0x59,0x5e,0xc4,0x4f,0xa8,0xbc,0x32,0x02,0x6c,0x1a,0x0b,
 0xc9,0x0b,0x3f,0x8c,0xb4,0x53,0x2d,0xc1,0x88,0xcd,0xa5,0x6c,0xf5,0x62,0x07,0xa7,0x5b,0xe2,0xdc,
 0x0f,0x51,0x92,0x33,0xfb,0x6b,0xe0,0xa2,0x68,0x02,0xf5,0x83,0xb6,0x37,0x3c,0xee,0xed,0xd1,0x0d,
 0x53,0xd0,0x02,0x6e,0x40,0x03,0x04,0x05,0xe0,0xdf,0xa7,0x23,0xb1,0xe9,0x8a,0xb8,0xd8,0x0c,0x07,
 0x89,0x53,0x02,0x29,0xb5,0xad,0x3d,0xae,0xa6,0x3d,0xba,0x01,0x91,0x1e,0xe6,0xc7,0x21,0xc3,0xa9,
 0xbf,0x2c,0x83,0x0c,0xc8,0x0c,0xc7,0x24,0xe5,0x80,0x13,0x68,0x61,0xcc,0x95,0x55,0xc7,0x46,0xcf,
 0xe0,0x59,0x81,0xe4,0x7c,0xa3,0x53,0x6c,0x90,0x5c,0xd0,0x9d,0x60,0x12,0x5a,0xc2,0x0e,0x28,0xa9,
 0x43,0x56,0x29,0x62,0xdb,0x70,0xc3,0x1c,0xe6,0xc5,0xdc,0xc2,0x4e,0xcc,0xdf,0xe8,0xe0,0x2e,0x0a,
 0xb4,0x88,0xbb,0xcf,0x47,0x7a,0xf7,0xc5,0x93,0x77,0xc0,0x1b,0xe1,0x28,0xd1,0xa4,0x56,0xf9,0x36,
 0x20,0xb2,0x3f,0x4f,0x35,0x6f,0x01,0x0f,0x7e,0xc4,0x58,0xd0,0x86,0x99,0xa7,0x0c,0xda,0x45,0x70,
 0x12,0xa7,0x5b,0xb3,0x3a,0xd9,0x4d,0xc7,0xa6,0x15,0x77,0xd8,0x59,0xdb,0xa7,0xc0,0xb1,0x17,0x07,
 0xf7,0xbd,0xf4,0x08,0x53,0x19,0x88,0xc9,0xa1,0x23,0xd2,0x60,0xf1,0x9c,0x11,0x8d,0x07,0xb7,0x6e,
 0xb1,0x2b,0x71,0x37,0x2d,0x86,0x47,0xe9,0x83,0x4f,0x78,0x20,0xed,0x8a,0x5c,0xc5,0x9d,0xc3,0x11,
 0x23,0x97,0x30,0xf0,0xbd,0x79,0x94,0xf2,0x8c,0x04,0xee,0xde,0xcd,0x62,0xbb,0x05,0x76,0x18,0x54,
 0xca,0x09,0x2f,0x0f,0xcf,0x80,0xe7,0x81,0x6d,0xb1,0x57,0x56,0x90,0xe4,0xd8,0xad,0x5b,0x19,0xc1,
 0x27,0x34,0xda,0x54,0x44,0xd8,0xe6,0x4a,0x05,0xc4,0x96,0x40,0x24,0x8f,0x62,0xe6,0x1f,0x31,0xab,
 0x6f,0x11,0x31,0x39,0x7a,0x0f,0xf2,0xa7,0xc6,0x9c,0xde,0x10,0xe5,0xd3,0xaa,0x64,0x8c,0x98,0xe3,
 0x92,0x30,0x52,0xc6,0x72,0x74,0xb3,0xed,0xb8,0x14,0x1f,0x9d,0x26,0x10,0x53,0x15,0xf2,0xde,0x5d,
 0x56,0x39,0xd0,0xd0,0xa9,0x8c,0xf4,0x7e,0x4e,0xd4,0x49,0xc1,0x02,0x24,0xac,0x82,0x55,0x24,0xa5,
 0x94,0xc7,0xeb,0xc4,0x14,0x02,0x61,0xe8,0x96,0x63,0x2a,0x0a,0x56,0x2b,0x2e,0xcb,0x50,0xe5,0xda,
 0x7d,0x05,0x42,0xee,0x88,0x2d,0xde,0xac,0x3d,0x61,0x4d,0x14,0x45,0x2e,0x7c,0x2c,0x7b,0x3f,0xae,
 0x49,0x18,0x8c,0x30,0x9a,0x24,0x04,0xca,0x76,0x40,0xed,0x3f,0x96,0x34,0x75,0x91,0xd4,0xd0,0xc4,
 0xf1,0x07,0xb1,0x7f,0xa4,0xea,0x30,0x11,0xfb,0x24,0x4f,0x19,0xe6,0x48,0x95,0x5d,0x04,0x74,0xf7,
 0x40,0x64,0x7a,0xa0,0xc1,0x18,0x21,0x34,0x3e,0xe5,0x92,0xe3,0x61,0x73,0x32,0x09,0x12,0x7e,0x8f,
 0xaf,0x72,0x6a,0x63,0x5b,0xaa,0x1a,0xbb,0xec,0x24,0x2a,0x9c,0xbc,0x05,0xcf,0x23,0x5d,0x8c,0x67,
 0x4e,0xa4,0x40,0xb8,0x87,0xf0,0xad,0xc2,0x25,0xf2,0xba,0x14,0x00,0x67,0x09,0x2f,0x02,0x7e,0xbc,
 0xa9,0xf0,0x42,0x1e,0xc9,0xfe,0x59,0x90,0xe0,0x06,0x88,0xde,0xdf,0xf9,0x18,0xb7,0x0b,0x9f,0x0e,
 0xd8,0xce,0x04,0xdf,0x7d,0x84,0x96,0xe3,0xf4,0xce,0x47,0xa6,0xea,0x4f,0x77,0x51,0x25,0x7e,0xb0,
 0x8a,0x3f,0xbd,0xe7,0x28,0x6c,0x7a,0x40,0x71,0x8b,0xa1,0x88,0xf5,0xd6,0x89,0xa6,0xc4,0x6b,0x05,
 0x24,0x9c,0x03,0x3d,0x28,0x8c,0x03,0x60,0x5c,0xa3,0xef,0x12,0x34,0x51,0x93,0xd6,0xfb,0x80,0x00,
 0x0e,0x04,0x44,0xbe,0x76,0xe7,0x23,0x43,0xf1,0x09,0x07,0xfb,0x7a,0x4e,0x38,0x25,0xd6,0x3e,0xda,
 0x2b,0x2c,0xc3,0xc7,0x3e,0xe5,0x3b,0x1f,0x63,0x54,0x1d,0x7e,0xeb,0xd3,0xfb,0xc4,0x42,0x92,0x46,
 0x24,0x6e,0xfb,0xd8,0x83,0x0e,0xc3,0x75,0xc1,0xb4,0xe0,0x07,0x8f,0x5d,0xb7,0xb5,0xc7,0xb7,0x73,
 0x8b,0xd8,0xde,0x41,0xae,0x7a,0x6e,0x80,0x6c,0xb9,0x51,0x60,0xf1,0xca,0xf7,0x4c,0xd7,0xc1,0x49,
 0x42,0x08,0xe8,0x52,0xe4,0x4e,0x7c,0x0f,0x25,0xf8,0xf1,0x3c,0xaf,0xb1,0xb8,0x30,0x67,0xa6,0xf7,
 0x28,0x19,0x07,0x07,0x90,0xb2,0x61,0xc5,0xa1,0x8c,0xeb,0x88,0x9e,0xe3,0xc0,0xc5,0x94,0x91,0x30,
 0x67,0x46,0xf0,0xc2,0x65,0x96,0xb6,0xf2,0x31,0xcb,0xa9,0xd9,0x72,0xed,0xe1,0x4b,0xc8,0xe2,0xef,
 0xd0,0xf7,0x5a,0x1c,0x98,0x89,0x61,0x15,0x07,0xad,0x40,0x42,0x90,0x11,0x51,0x91,0x98,0xb2,0x9d,
 0x01,0x21,0xaf,0x12,0x99,0xa5,0x71,0x99,0x85,0x6d,0xd6,0x0c,0xb2,0x7a,0xff,0x64,0x26,0xf3,0x17,
 0x32,0x17,0xda,0x74,0x4a,0x11,0x29,0x21,0x39,0x69,0x49,0xe9,0x5f,0x3b,0xc8,0xe9,0x0b,0x52,0xf2,
 0x73,0x97,0xd0,0xcb,0x27,0x37,0x3f,0xa1,0xc9,0xe7,0x89,0x0b,0xa3,0x25,0x05,0xa0,0x09,0x12,0xcf,
 0x19,0x2b,0x21,0xd3,0xfc,0x52,0xc2,0xc1,0x72,0x7e,0x1e,0x6f,0xca,0x30,0x24,0xdd,0x83,0x0c,0x28,
 0xc5,0x5a,0x0d,0x9b,0xe9,0x14,0x48,0xf0,0x72,0xac,0x2b,0x83,0x97,0xfa,0x01,0x12,0x34,0x33,0xe4,
 0x6a,0x60,0x39,0x23,0x96,0xa1,0xb1,0x43,0x37,0x30,0x9a,0xd0,0x2e,0x01,0xdb,0x48,0x9a,0xa6,0xb5,
 0x61,0xb1,0x39,0x1b,0xb0,0x92,0xd4,0xfc,0x39,0xef,0x11,0xe5,0xbc,0x6b,0xe9,0x78,0x96,0xbf,0x84,
 0x6f,0xfb,0xf3,0x96,0x68,0xd3,0x65,0x09,0xe3,0xb0,0x57,0x68,0xee,0xc5,0x2f,0xaf,0x5e,0xd2,0x58,
 0x27,0xf7,0xac,0x78,0xd8,0x93,0x39,0xca,0x96,0x55,0x6d,0x68,0xa7,0x30,0x52,0x12,0xc7,0x0e,0xbe,
 0x56,0x52,0x45,0x6d,0xac,0x83,0x7e,0x01,0x8f,0x8b,0x49,0xee,0x4c,0xdd,0x96,0x5e,0xbe,0xe7,0x74,
 0xd2,0x56,0x32,0x63,0x8d,0xdc,0xba,0xcb,0xe8,0xf7,0xe7,0xcd,0xc8,0x3f,0x56,0x92,0x8f,0x40,0xf3,
 0x38,0x8a,0xe0,0x8f,0x1a,0xf7,0xd4,0x90,0x06,0x51,0xd1,0xfb,0xc5,0x63,0xc9,0xba,0x0b,0x62,0x5a,
 0xaa,0x0e,0x11,0x44,0xb2,0x0c,0x4b,0x0d,0x81,0x31,0x47,0xe0,0x21,0x8f,0x2e,0xcd,0x31,0x62,0xff,
 0x53,0xb8,0x76,0x07,0x27,0x12,0xb7,0xee,0xa1,0x01,0x28,0x16,0x01,0x17,0x71,0x6a,0xa9,0x75,0x89,
 0x60,0x51,0xb6,0x00,0x9b,0x2c,0x28,0x35,0x3a,0xd9,0x3d,0xcf,0x59,0xbe,0x41,0xb3,0xd7,0x22,0x65,
 0xd0,0x7c,0x79,0x45,0x1d,0x34,0x7d,0xcb,0x20,0x48,0xe3,0xe7,0x0a,0xb1,0x45,0xe4,0xae,0xe4,0x6e,
 0x92,0x95,0xc5,0x11,0x3b,0x49,0x6a,0x52,0x97,0x2a,0x65,0x0a,0xce,0xf4,0xa6,0x3c,0x58,0x30,0x6f,
 0x4b,0x01,0x9c,0xf0,0x32,0x4d,0xe0,0x19,0x74,0x9c,0xd4,0x70,0x17,0xa3,0x8c,0xa6,0x65,0xc0,0x65,
 0x86,0x14,0x85,0x21,0x1e,0xf5,0xf7,0x44,0x53,0xc7,0x79,0xab,0x2a,0x1f,0x1f,0xc8,0x00,0x28,0x2d,
 0xe6,0x36,0xa3,0x8a,0xaf,0x42,0x87,0xf5,0x35,0x25,0x47,0xed,0x02,0xda,0xb3,0x1d,0x94,0x5c,0x46,
 0x9b,0x73,0x5b,0xd1,0xd0,0x6a,0xf4,0xc8,0x91,0x29,0xcd,0x21,0x85,0x23,0xd7,0xc9,0x65,0x94,0x8d,
 0x74,0x69,0x52,0xc3,0x6b,0x88,0xa9,0xcd,0x77,0xa4,0xb2,0x09,0xcc,0x19,0x4e,0xc7,0xf7,0x67,0x71,
 0x5c,0xe1,0xf7,0x68,0xaf,0x22,0x09,0x9a,0xe8,0x65,0x94,0x59,0x22,0x1e,0x4b,0x6d,0x86,0xe8,0x92,
 0x54,0x00,0x48,0x2b,0xf3,0x24,0x58,0xa9,0xab,0x53,0xda,0x46,0xe7,0xd7,0x92,0x31,0x14,0xc0,0xba,
 0xca,0xb9,0x42,0x4f,0x28,0xc7,0x05,0x40,0xcd,0x86,0x02,0xa5,0x2e,0x11,0x1b,0x45,0x41,0xdf,0x6d,
 0xb5,0xdf,0x96,0xb3,0xa6,0xa2,0xfe,0xda,0x6a,0x5f,0x2d,0xb1,0x32,0xc8,0x3a,0x5e,0xa3,0x9b,0x8a,
 0x90,0x94,0xcb,0x9b,0xc8,0xf2,0x8e,0xfb,0xac,0x15,0x10,0xf2,0x52,0x62,0x2e,0x2e,0x52,0x53,0x5c,
 0x44,0x88,0x8b,0x02,0xa4,0xdd,0xa4,0xea,0x0e,0x74,0x62,0xff,0xbf,0x3f,0x49,0x39,0x5b,0x8e,0x4b,
 0xe9,0x14,0x1d,0x54,0x89,0xbd,0x72,0x80,0xcc,0x7e,0x23,0xce,0xd6,0x72,0x5c,0x8f,0xad,0xb8,0x83,
 0x4b,0x01,0x52,0xb6,0xd4,0xdd,0xe0,0x98,0x95,0x64,0x3e,0x86,0xfd,0x97,0x4c,0x72,0xfa,0x49,0x3a,
 0xa6,0xc5,0x9e,0x57,0xa6,0x78,0xbc,0x98,0xc4,0x64,0xd2,0x91,0xae,0x04,0x4d,0x4a,0xca,0x59,0x5a,
 0x4c,0x47,0x29,0x74,0x5c,0x88,0xa7,0x68,0xc9,0xcf,0x5a,0xc2,0x4a,0x4a,0xa7,0x8e,0x93,0x22,0xe0,
 0xdd,0xd2,0x91,0x76,0x3f,0x3f,0x70,0xc3,0x3b,0x08,0x9c,0xd9,0x5c,0xb7,0x40,0x2e,0x90,0xb0,0x94,
 0x29,0x93,0x38,0x08,0x87,0x2f,0x22,0xb3,0x92,0x14,0xec,0xc0,0x0d,0xa2,0x96,0xfe,0xd6,0x25,0xb4,
 0x53,0x2d,0x76,0x8c,0xe1,0x74,0x13,0x0d,0x73,0xa1,0xfc,0xac,0x4a,0x74,0xb0,0xc4,0xf9,0x37,0x9a,
 0x38,0xc8,0x8d,0x0d,0x5d,0xd0,0x18,0x14,0x4d,0x9d,0x10,0x3d,0x39,0xba,0x3b,0x9c,0xdc,0xd2,0x93,
 0xb3,0xda,0x2a,0xd9,0xe3,0x5d,0x37,0x31,0x5c,0xb5,0x2a,0x4e,0x0e,0x93,0xca,0xf2,0x96,0xe0,0x71,
 0x25,0x10,0x95,0x0d,0x96,0x34,0x10,0x61,0xf2,0xf8,0xab,0x95,0xa2,0x9a,0x81,0x4a,0x41,0x26,0x60,
 0xa9,0x2c,0x53,0x5e,0x57,0xa4,0xa9,0x1a,0x91,0x2a,0xd1,0x28,0x1d,0x98,0x55,0x46,0xf9,0x62,0xad,
 0x70,0x89,0xf3,0x86,0x35,0x99,0x10,0x10,0xc3,0xff,0xfc,0x17,0x3f,0x84,0x0c,0xe7,0x92,0xd1,0x7f,
 0x48,0xfc,0x1f,0xb5,0x05,0x32,0xc6,0x38,0x71,0x00,0x00
};

 

 

 

2. 수정된 Toggle OV2640 settings 페이지의 플래시 코드에 맞게 LED 플래시 제어용 코드 추가하기

▶ ESP32 CAM 개발보드의 GPIO 명세

- 전원 관련
5V : 전원 입력
3.3V: 3.3V 전원 출력(3.3V 전원을 사용하는 센서등 모듈에 전원공급. ex.DFPlayer)
GND: 접지(그라운드)

- GPIO0    FLASH 업로드 모드(접지연결 시), 일반모드(접지 해제 시): 입/출력(디지털/아날로그) 가능

 

- 시리얼통신
GPIO1    U0TXD(UART 전송 핀)
GPIO3    UORXD(UART 수신 핀)

- SD 카드
GPIO2    Data0 핀 (RTC & ADC 지원)
GPIO4    Data1 핀 (RTC & ADC 지원), 플래시 라이트 겸용
GPIO12  Data2 핀 (RTC & ADC 지원)
GPIO13  Data3 핀 (RTC & ADC 지원)
GPIO14  CLK (RTC & ADC 지원)
GPIO15  CMD (RTC & ADC 지원)
* SD 카드를 사용하지 않는 경우 디지털 입력/출력, 아날로그 입력/출력에 사용가능.
* SD 카드를 사용하는 경우 플래시 라이트를 제어할 수 없음.
* 아날로그 입력은 와이파이가 작동 중 일경우 사용불가(입력값이 4095로 판독됨).
-> ESP32의 아날로그 채널 ADC2는 와이파이 드라이버와 공유되어 있습니다.
ADC2_CH0 (GPIO 4)
ADC2_CH1 (GPIO 0)
ADC2_CH2 (GPIO 2)
ADC2_CH3 (GPIO 15)
ADC2_CH4 (GPIO 13)
ADC2_CH5 (GPIO 12)
ADC2_CH6 (GPIO 14)
ADC2_CH7 (GPIO 27)
ADC2_CH8 (GPIO 25)
ADC2_CH9 (GPIO 26)

 

▶ setup() 함수에 LED 플래시에 할당된 핀 모드 설정

pinMode(4, OUTPUT); // onboard flash light

 

▶ 온보드 LED 플래시 제어용 플래그 설정

static bool flashL = false;

 

▶ esp_err_t cmd_handler() 함수에 플래시 제어용 코드 추가

    else if(!strcmp(variable, "fls")) {
      flashL = !flashL;
      if (flashL) digitalWrite(4, HIGH);   
      else digitalWrite(4, LOW);  
    }

 

▶ 웹페이지에 ESP32 CAM의 현재 상태를 반영하는 esp_err_t status_handler() 함수에 플래시 상태 코드 추가

    p+=sprintf(p, "\"fls\":%u", flashL);

 

CameraWebServer_modi_esp32_1_0_6.zip

CameraWebServer_modi_esp32_1_0_6.zip
0.02MB

 

 

웹페이지 응답 코드가 있는 아두이노 와이파이 제어에서 사용할 수 있는 연결 도우미 앱입니다. 

https://play.google.com/store/apps/details?id=com.tistory.postpop.MCUWiFi

 

MCUWiFi - Google Play 앱

스마트폰에서 아두이노 웹서버에 연결하기 위한 앱

play.google.com

특징:

1. 스마트폰에서 아두이노 웹서버에 연결할 때 에러 메세지 출력 및 연결 가이드 제시

2. 아두이노 웹서버의 html 페이지 연결시 스마트폰의 웹브라우저 대신 앱 내부 웹 뷰어를 통해 연결 및 표시

3. 앱 실행전 스마트폰의 와이파이 실행 유무에 따라 앱 종료시 와이파이 설정 페이지 이동 

자세한 설명은 아래 포스트를 참조하세요.

[Arduino] - 아두이노 와이파이 연결 도우미 앱

 

MCU WiFi앱을 통해 ESP32 CAM에 연결하기 위해서는 아두이노 코어 버전 2.0.xx에서 스케치를 업로드 해야합니다.

1.0.6버전에서 업로드한 ESP32 CAM에 와이파이를 통해 연결할 경우 "Header fields are too long for server to interpret"라는 메세지만 표시됩니다. 이 메세지의 의미는 ESP32 아두이노 코어의 와이파이 설정값

"CONFIG_HTTPD_MAX_REQ_HDR_LEN = 512"에 의해 정의된 512바이트를 초과한 헤더값이 들어온 경우 ESP32 웹서버에서 헤더값을 해석하지 못했다는 것입니다.

HTTP 헤더 예 -> 이 헤더의 총길이가 512바이트를 초과하면 1.0.6 버전의 ESP32 웹서버는 에러 메세지를 출력합니다.

GET /stream HTTP/1.1
Host: xxx.xxx.x.xxx:8000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; Android 8.1.0; Pixel Build/OPM1.171019.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome 78.0.3904.96 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
X-Requested-With: com.snc.test.webview2
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

 

2.0.xx 버전의 와이파이 설정값이 1024바이트로 변경되어 정상적으로 연결 및 작동됩니다.  

 

 

 

+ Recent posts