diff --git a/esp32_cam/.gitignore b/esp32_cam/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/esp32_cam/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/esp32_cam/.vscode/extensions.json b/esp32_cam/.vscode/extensions.json new file mode 100644 index 0000000..0f0d740 --- /dev/null +++ b/esp32_cam/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} diff --git a/esp32_cam/platformio.ini b/esp32_cam/platformio.ini new file mode 100644 index 0000000..affc073 --- /dev/null +++ b/esp32_cam/platformio.ini @@ -0,0 +1,11 @@ +[env:ttgo-camera] +platform = espressif32 +board = ttgo-t1 +upload_protocol = esptool + +upload_port = /dev/ttyUSB0 +monitor_speed = 115200 +framework = arduino +lib_deps = + https://github.com/mathertel/OneButton + https://github.com/ThingPulse/esp8266-oled-ssd1306 \ No newline at end of file diff --git a/esp32_cam/src/app_httpd.cpp b/esp32_cam/src/app_httpd.cpp new file mode 100644 index 0000000..b79c276 --- /dev/null +++ b/esp32_cam/src/app_httpd.cpp @@ -0,0 +1,393 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// 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" + + +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"; + +httpd_handle_t stream_httpd = NULL; +httpd_handle_t camera_httpd = NULL; + + +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[64]; + + 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; + + Serial.printf("MJPG: %uB %ums (%.1ffps)\n" + , (uint32_t)(_jpg_buf_len), + (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time + ); + } + + last_frame = 0; + return res; +} + +static esp_err_t stream_hmi_handler(httpd_req_t *req) +{ + Serial.println("stream_hmi_handler"); + camera_fb_t *fb = NULL; + esp_err_t res = ESP_OK; + size_t _jpg_buf_len = 0; + uint8_t *_jpg_buf = NULL; + + static int64_t last_frame = 0; + if (!last_frame) { + last_frame = esp_timer_get_time(); + } + const char *request = "HTTP/1.1 200 OK\r\nContent-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n"; + res = httpd_resp_send(req, request, strlen(request)); + 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) { + uint8_t buff[4]; + memcpy(buff, &_jpg_buf_len, sizeof(_jpg_buf_len)); + res = httpd_send(req, (const char *)buff, sizeof(buff)); + } + 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; + } + } + 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"); + httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); + return httpd_resp_send(req, (const char *)index_html_gz, index_html_gz_len); +} + +void startCameraServer() +{ + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + + httpd_uri_t index_uri = { + .uri = "/", + .method = HTTP_GET, + .handler = index_handler, + .user_ctx = NULL + }; + + httpd_uri_t status_uri = { + .uri = "/status", + .method = HTTP_GET, + .handler = status_handler, + .user_ctx = NULL + }; + + httpd_uri_t cmd_uri = { + .uri = "/control", + .method = HTTP_GET, + .handler = cmd_handler, + .user_ctx = NULL + }; + + httpd_uri_t capture_uri = { + .uri = "/capture", + .method = HTTP_GET, + .handler = capture_handler, + .user_ctx = NULL + }; + + httpd_uri_t stream_uri = { + .uri = "/stream", + .method = HTTP_GET, + .handler = stream_handler, + .user_ctx = NULL + }; + + httpd_uri_t hmi_uri = { + .uri = "/hmi", + .method = HTTP_GET, + .handler = stream_hmi_handler, + .user_ctx = NULL + }; + + Serial.printf("Starting web server on port: '%d'\n", config.server_port); + if (httpd_start(&camera_httpd, &config) == ESP_OK) { + httpd_register_uri_handler(camera_httpd, &index_uri); + httpd_register_uri_handler(camera_httpd, &cmd_uri); + httpd_register_uri_handler(camera_httpd, &status_uri); + httpd_register_uri_handler(camera_httpd, &capture_uri); + } + + config.server_port += 1; + config.ctrl_port += 1; + Serial.printf("Starting stream server on port: '%d'\n", config.server_port); + if (httpd_start(&stream_httpd, &config) == ESP_OK) { + httpd_register_uri_handler(stream_httpd, &stream_uri); + httpd_register_uri_handler(stream_httpd, &hmi_uri); + } +} diff --git a/esp32_cam/src/camera_index.h b/esp32_cam/src/camera_index.h new file mode 100644 index 0000000..6b55af1 --- /dev/null +++ b/esp32_cam/src/camera_index.h @@ -0,0 +1,2 @@ +const uint8_t index_html_gz[] = {0x1f,0x8b,0x8,0x8,0x11,0x66,0x35,0x5c,0x0,0x3,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x0,0xdd,0x5c,0x7b,0x73,0xda,0x48,0x12,0xff,0xff,0xaa,0xee,0x3b,0xc8,0xca,0x6e,0x90,0x6a,0x5,0x6,0x8c,0x1d,0x47,0x18,0x7c,0x36,0x76,0x92,0xad,0xca,0x63,0x37,0xce,0xdd,0x6e,0xd5,0xd6,0x56,0x32,0x48,0x23,0x98,0x58,0x68,0x88,0x34,0x32,0x10,0x56,0x9f,0xe3,0x3e,0xd0,0x7d,0xb1,0xeb,0x79,0x48,0x48,0x3c,0x8c,0xd,0x9,0x76,0x6d,0x5c,0x65,0x8f,0x46,0x3d,0x3d,0xdd,0xfd,0xeb,0xc7,0x8c,0xa4,0xc9,0xc9,0x9e,0x4b,0x1d,0x36,0x19,0x62,0xad,0xcf,0x6,0x7e,0xfb,0x9f,0xff,0x38,0x51,0x7f,0x35,0xf8,0x77,0xd2,0xc7,0xc8,0x55,0x6d,0x71,0x3d,0xc0,0xc,0x69,0x4e,0x1f,0x85,0x11,0x66,0x2d,0x3d,0x66,0x5e,0xf9,0x58,0x5f,0xb8,0x1f,0xa0,0x1,0x6e,0xe9,0x37,0x4,0x8f,0x86,0x34,0x64,0xba,0xe6,0xd0,0x80,0xe1,0x0,0xe8,0x47,0xc4,0x65,0xfd,0x96,0x8b,0x6f,0x88,0x83,0xcb,0xe2,0xc2,0x22,0x1,0x61,0x4,0xf9,0xe5,0xc8,0x41,0x3e,0x6e,0xd5,0xa,0xcc,0x18,0x61,0x3e,0x6e,0x7f,0xf8,0xf0,0xf2,0x9d,0xd6,0x1,0x96,0x21,0x3a,0xd9,0x97,0x5d,0x39,0x9a,0x88,0x4d,0x44,0x47,0x97,0xba,0x93,0xa9,0x7,0x13,0x95,0x3d,0x34,0x20,0xfe,0xc4,0x3e,0xb,0x81,0xad,0xf5,0xa,0xfb,0x37,0x98,0x11,0x7,0x59,0x11,0xa,0xa2,0x72,0x84,0x43,0xe2,0x35,0xbb,0xc8,0xb9,0xee,0x85,0x34,0xe,0x5c,0xfb,0x49,0xed,0x98,0xff,0x34,0x1d,0xea,0xd3,0xd0,0x7e,0x72,0xf9,0x82,0xff,0x34,0x5,0x9f,0x88,0x7c,0xc5,0x76,0xed,0x68,0x38,0x4e,0xfa,0xf5,0x69,0xae,0xe7,0x18,0x7a,0x22,0xec,0x30,0x42,0x83,0xca,0x0,0x91,0x60,0xea,0x92,0x68,0xe8,0xa3,0x89,0xed,0xf9,0x78,0x9c,0x3c,0x19,0xe0,0x20,0xb6,0xa,0xf7,0x79,0x7f,0xd9,0x25,0xa1,0xec,0xb3,0x61,0xaa,0x78,0x10,0x48,0xc2,0x6c,0x6c,0x40,0x3,0xdc,0x14,0x84,0xa3,0x10,0xd,0xe1,0x92,0xff,0x69,0xe,0x48,0x20,0xcd,0x64,0x1f,0x34,0xaa,0xc3,0x71,0x41,0xf0,0x83,0x23,0xfe,0xd3,0x1c,0x22,0xd7,0x25,0x41,0xcf,0x3e,0xe6,0xb7,0x69,0xe8,0xe2,0xb0,0x1c,0x22,0x97,0xc4,0x91,0xdd,0x80,0x9e,0x1,0xa,0x7b,0xc0,0x83,0xd1,0xa1,0x5d,0xae,0x55,0x67,0x1d,0x21,0xe9,0xf5,0x99,0xcd,0x7b,0x92,0x27,0xa,0x9d,0x82,0x1a,0x39,0x51,0x84,0x20,0xc8,0x27,0xbd,0xa0,0x4c,0x18,0x1e,0x44,0x76,0xc4,0x42,0xcc,0x9c,0x7e,0xe2,0x91,0x5e,0x1c,0xe2,0x69,0x2a,0x40,0x55,0xf1,0x86,0x46,0x79,0x84,0xbb,0xd7,0x84,0x95,0xd5,0x64,0x5d,0xec,0xd1,0x10,0x67,0x4,0xe5,0xae,0x4f,0x9d,0xeb,0x72,0xc4,0x50,0xc8,0x16,0x89,0x91,0xc7,0x70,0x38,0x4f,0x8b,0x41,0xe1,0x5,0xca,0x94,0x81,0xba,0x24,0x81,0x4f,0x2,0xbc,0x8a,0xad,0xe4,0x50,0x24,0x15,0x7d,0x4a,0xd,0x8d,0xc,0x7a,0x99,0x5,0xc4,0xa4,0x4d,0x69,0xf8,0x5a,0xb5,0xfa,0x63,0xb3,0x8f,0x85,0xbd,0x50,0xcc,0xe8,0xed,0x46,0xe6,0xbe,0xf1,0xaf,0x1,0x76,0x9,0xd2,0x8c,0x19,0x78,0xda,0x71,0x15,0x2c,0x6d,0x6a,0x28,0x70,0x35,0x83,0x86,0x4,0xac,0x8d,0x84,0x2b,0xf8,0xd0,0x3,0x8e,0x3f,0xc4,0xe6,0x74,0x1d,0xc,0xca,0x23,0x56,0x3,0xb1,0x44,0x83,0x1,0x1a,0x97,0x73,0x5a,0xf0,0x4b,0xa5,0x9,0x4,0x9b,0x63,0x40,0xe7,0x4d,0x5f,0x2b,0x6b,0xdc,0xb5,0x4c,0xa5,0xae,0x50,0x31,0xa7,0xee,0xdf,0x5,0xe5,0x34,0x62,0x9f,0x74,0x63,0xc6,0x68,0x10,0xad,0x31,0xf3,0xe7,0x38,0x62,0xc4,0x9b,0x94,0x15,0x28,0x76,0x34,0x44,0x90,0xb1,0xba,0x98,0x8d,0x30,0x86,0xd0,0xd,0xd0,0xd,0xc0,0xdd,0xeb,0xf9,0x78,0xea,0xc4,0x61,0x4,0x99,0x63,0x48,0x9,0x50,0x86,0xcd,0x2,0x0,0x79,0xc2,0xb2,0xd3,0x9d,0xd2,0x98,0x71,0x91,0x40,0x44,0xa,0xfc,0x8,0x9b,0x40,0x4b,0x9a,0xbd,0x9a,0xda,0xbc,0x3a,0x37,0xc6,0x76,0xfa,0xd8,0xb9,0xc6,0xee,0x4f,0xc5,0x74,0x21,0x52,0x4d,0x85,0x4,0xc3,0x98,0x95,0x79,0x42,0x18,0xae,0xd1,0x47,0x58,0x42,0x4d,0x51,0xaf,0x67,0x3e,0x6b,0x1f,0xe,0xc7,0x5a,0xb5,0xc0,0xa8,0xed,0xa3,0x2e,0xf6,0x33,0x76,0xca,0x88,0xd2,0x9f,0x94,0x13,0xe4,0xb2,0x47,0x2e,0x43,0x35,0x9e,0xfd,0x58,0x60,0xa4,0x89,0xb6,0x55,0xe8,0x8a,0xb0,0xf,0x30,0xc8,0x84,0x8,0x3d,0x23,0xbb,0x96,0x54,0x42,0x14,0xf4,0x30,0x0,0x38,0xb6,0xd2,0x66,0x2e,0xa5,0x2e,0x9b,0xde,0xae,0x6a,0x20,0x76,0x22,0x81,0x5c,0xf0,0xf8,0x54,0xad,0x1c,0x75,0xad,0x9e,0xe5,0x46,0x30,0x74,0xc1,0x14,0x3c,0x6b,0xce,0x21,0xa8,0x2a,0x81,0xe7,0x15,0xeb,0x84,0xe7,0x1d,0x54,0xf,0x1a,0x73,0xd1,0xcf,0xe7,0x29,0xd6,0x8a,0x66,0x86,0xb1,0x12,0xd0,0xee,0xd3,0x1b,0x1c,0x4e,0x8b,0xac,0x1a,0xcf,0x1b,0x6e,0x7a,0x1f,0x81,0x5f,0xde,0xe0,0x22,0x41,0xbd,0xe6,0xd4,0x6b,0x8a,0xa0,0x2,0x1a,0xa2,0xae,0x8f,0xdd,0xd4,0xd5,0x5c,0xec,0xa1,0xd8,0x67,0x5,0xe9,0x50,0x95,0xff,0x24,0xc2,0xd6,0x7f,0xf0,0x62,0xde,0x12,0xb6,0xfc,0x73,0x9a,0x6,0x8,0x1a,0xe,0x31,0x82,0x3e,0x7,0xcb,0x52,0xb3,0x98,0xdc,0x84,0x5b,0x2c,0x29,0x30,0x73,0xe6,0x49,0xc3,0x7f,0x71,0x2e,0xdb,0xa3,0x4e,0x1c,0xcd,0x9c,0x7c,0x9,0x85,0x9d,0x8a,0x13,0xf9,0x44,0x98,0x31,0xe,0x2,0xae,0x5b,0x99,0x85,0x30,0xf1,0x74,0x89,0x50,0x8b,0xf8,0xe4,0x45,0x54,0xe5,0xba,0x8,0x4a,0x35,0xc3,0x5a,0x8b,0x28,0xcc,0xa3,0x29,0xb2,0x3b,0xc8,0xc3,0xfa,0xf1,0xa0,0x3b,0x55,0xc3,0x6b,0x10,0x1b,0x92,0x41,0xd8,0xeb,0x22,0xa3,0x6a,0x55,0xad,0x3,0xf8,0x65,0x16,0xc,0x26,0x45,0xae,0xd7,0x17,0xaa,0xef,0xe1,0x7c,0xbd,0x56,0xe,0x34,0xa7,0xcd,0x2a,0x7c,0xa,0x85,0xbb,0x56,0xe1,0xe,0xbf,0xc2,0xe0,0xeb,0x8c,0xba,0x68,0xaf,0xa5,0x86,0x18,0xd0,0xaf,0x65,0x19,0x7f,0xf,0x86,0x45,0x4e,0x84,0x5d,0xe3,0xb0,0x5c,0x9e,0x68,0x43,0x5b,0x54,0xb5,0x54,0xef,0xb2,0xcc,0x26,0xc0,0x26,0x80,0x12,0x12,0x42,0x29,0x69,0x2e,0xf4,0xac,0x9a,0xdb,0x23,0xbe,0x5f,0xf6,0xe9,0x68,0x2e,0x7b,0x14,0xec,0x3c,0x6f,0xd7,0x79,0xf3,0xdf,0xca,0x3b,0x6,0x9f,0xfb,0xe,0xbc,0x77,0x1f,0x44,0x33,0x50,0x6e,0x9,0x92,0x75,0x16,0xbd,0xc3,0xd0,0x45,0x83,0xc9,0x1c,0x99,0x54,0xa2,0x11,0x81,0x95,0xd8,0x5c,0x31,0x1a,0xd2,0x88,0x88,0x65,0x5e,0x88,0x7d,0xc4,0x93,0xfc,0x62,0x19,0x9e,0x2b,0x1e,0xb9,0x5b,0x29,0x4f,0x59,0x46,0xef,0xb6,0x74,0xa8,0xc8,0xc,0xa0,0xfc,0x55,0x18,0xaf,0x90,0xdc,0xb,0xb6,0xad,0xdf,0xea,0xc3,0xca,0x71,0x7b,0x21,0x9e,0xa4,0x6c,0x2d,0xf5,0xd7,0x96,0x2b,0xbd,0xe5,0x35,0x5a,0xf8,0xb5,0xd4,0xba,0xd2,0x88,0x92,0xb9,0x21,0x8b,0x16,0x49,0x17,0x58,0xba,0xbe,0x0,0x7d,0x16,0x6c,0xc2,0x34,0x2a,0x6,0x79,0xd3,0xc7,0x1e,0x13,0xb,0x6f,0x9e,0x1d,0xf,0xa,0x1e,0x52,0x9e,0x55,0x6f,0x89,0x67,0xb6,0x7e,0x4a,0x6d,0xb3,0x8c,0x96,0xfb,0xd4,0x72,0xf2,0x54,0xf0,0x34,0xc5,0xa,0xf5,0xa0,0x67,0x20,0x3,0x18,0x94,0xc0,0xbf,0x1b,0xf5,0x23,0xbe,0x7e,0x5e,0x7d,0x2b,0x51,0xcb,0x9e,0x85,0x90,0x48,0x4b,0x6c,0xce,0xb,0x1a,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,0x3b,0xe2,0x9b,0xbd,0x8a,0xe3,0xd3,0x28,0x87,0x3,0xea,0x82,0x24,0x31,0xc3,0x4d,0xb9,0xa4,0x3b,0x54,0x46,0x3d,0x5c,0x1e,0x76,0x39,0xc,0xf2,0xd0,0x14,0x25,0xab,0xf1,0xbd,0x4e,0x7e,0x15,0xc5,0xf0,0x18,0xea,0x1b,0xdf,0xb7,0xd8,0xe,0x16,0x6e,0x96,0xf,0x83,0xda,0xe2,0x12,0x2c,0xa9,0xf4,0x89,0xeb,0xe2,0xa0,0xb0,0x39,0x4e,0x72,0x7b,0xfe,0xfd,0x74,0xd3,0x2f,0xaf,0x72,0xcf,0x28,0x4e,0xf8,0x63,0x80,0xc2,0xe3,0x1,0xb9,0xec,0xd7,0x1c,0x1f,0x45,0x51,0x4b,0xe7,0xfb,0xf1,0xfc,0x23,0x6,0x41,0xe3,0x92,0x1b,0x8d,0xb8,0x2d,0xdd,0xa7,0x3d,0x3a,0x7f,0x53,0x10,0x88,0x25,0xb1,0x6,0xd8,0xb6,0xf4,0xc2,0xe2,0x5c,0x17,0xc3,0x66,0x5d,0x7a,0xfb,0xe9,0x93,0xe7,0xcf,0x9e,0x1d,0x35,0x9f,0x6,0xdd,0x68,0xa8,0x7e,0x7f,0x10,0xb7,0x60,0xe9,0xcb,0x18,0x2c,0x47,0xa3,0x93,0x7d,0xc1,0x6d,0x5e,0x86,0x7d,0x10,0x62,0x95,0x5c,0x2a,0x46,0x96,0x8a,0x96,0xd2,0x44,0xe0,0xad,0x5d,0x14,0x2e,0xa3,0x11,0x74,0xc2,0xbb,0x35,0x91,0xdc,0x74,0xe1,0xe3,0x5d,0x3a,0x9e,0x17,0x5f,0x68,0xa4,0x2,0x40,0x51,0x61,0x77,0x25,0x47,0x18,0x27,0xc6,0xf3,0x7d,0xc9,0x2a,0xa2,0x4c,0x44,0x65,0xfe,0xdc,0x5e,0x40,0x4e,0xee,0x85,0x68,0x80,0xb9,0xe7,0xab,0xce,0x5b,0xf8,0xcc,0x23,0x91,0xd,0xd5,0xdb,0xef,0xb1,0xf0,0x62,0xc0,0x79,0xb9,0x75,0x17,0xd8,0xc8,0x80,0x2c,0x4a,0xa0,0xa7,0x42,0xaa,0xf5,0x75,0x19,0x9,0xcf,0x59,0x27,0x92,0xe0,0x47,0x87,0xc2,0xc9,0x6e,0x90,0x1f,0x83,0x7d,0x6b,0x55,0xbd,0xfd,0xef,0xdf,0x5f,0x9e,0x19,0x10,0x73,0xd5,0x71,0xad,0x5e,0xad,0x9a,0x27,0xfb,0x92,0xe4,0xfe,0xcc,0x9e,0xeb,0xed,0x2b,0xc1,0xab,0x7e,0xc,0xbc,0xaa,0xf5,0xc6,0x16,0xbc,0x8e,0xf5,0xb6,0x60,0x5,0x5c,0xc6,0xcf,0x8e,0x8e,0xb7,0xe0,0xf4,0xc,0xa4,0xfa,0xf,0xb0,0x3a,0x6,0x5,0x8f,0xb6,0xd2,0xef,0x48,0x6f,0x73,0x46,0x47,0x8d,0xea,0xb8,0x71,0xbc,0xd,0xa3,0x43,0x5d,0x6d,0x2f,0xb9,0xf3,0xa6,0x2d,0xbd,0xdd,0xf9,0xf9,0x85,0xd1,0x0,0x29,0xeb,0xcf,0x8f,0xb6,0x60,0xde,0xd0,0xdb,0xbf,0x72,0x31,0xf,0xea,0xc0,0xa9,0xb1,0x8d,0x98,0x7,0x7a,0xfb,0x95,0x60,0x5,0x6c,0xc6,0xb5,0x67,0xdb,0x8,0x5,0x6e,0xf6,0xab,0x60,0x5,0x7e,0xc6,0xdd,0xec,0xae,0xac,0x20,0x7f,0xa,0xf3,0xdc,0x16,0xb4,0x4b,0xf2,0x51,0xe1,0xfe,0x6d,0x41,0xfd,0x25,0x86,0x6c,0xcf,0x26,0xf7,0xf,0x69,0x35,0x10,0xd4,0x92,0x8d,0x3b,0x46,0x73,0x4e,0x96,0xec,0x9,0x82,0xde,0xae,0x55,0xd7,0x29,0x21,0x6,0xe7,0x13,0xa3,0x18,0x5d,0xd0,0x41,0xd7,0x80,0x97,0x88,0x68,0x6d,0x80,0xc6,0xe0,0xad,0x7,0x7a,0x2e,0xca,0x37,0xcb,0x18,0x4b,0xe4,0x45,0x63,0xbd,0x7d,0x74,0xb0,0xd6,0xe8,0xdb,0x80,0xd2,0x15,0x15,0x3e,0xc0,0x51,0x74,0x7f,0x5c,0x66,0x63,0xf5,0xf6,0x79,0xd6,0xde,0xa,0x9d,0x72,0x7d,0x1b,0x74,0x72,0x2,0x49,0x80,0xca,0x75,0x5,0x50,0x5d,0x9f,0x45,0xc7,0x37,0x85,0x67,0xad,0xbc,0x5b,0xa1,0xc3,0x4b,0x7c,0x88,0x22,0x76,0x7f,0x6c,0xd2,0x91,0x90,0xe9,0x54,0xeb,0x1,0x71,0xc9,0x84,0xf9,0x7b,0xa0,0x12,0x21,0x16,0x87,0xe2,0x49,0xfd,0xfd,0x71,0x99,0x8d,0x85,0x52,0x99,0xb5,0x1f,0x10,0x9b,0x9c,0x40,0x7f,0x13,0x74,0x86,0xd8,0x21,0xc8,0xff,0x88,0x3d,0xf,0x8a,0xd9,0x6,0x8,0x15,0xc6,0x3,0x4a,0xf2,0x5a,0xbb,0x14,0xd7,0xf7,0x5f,0x49,0xce,0xf1,0xfb,0x66,0xcb,0xc9,0xea,0xf2,0x85,0xcd,0x5b,0x9a,0x49,0xba,0xe9,0x2,0xa2,0x6,0x5c,0x70,0x4f,0xec,0x14,0x37,0x67,0x52,0xd7,0xdb,0x2f,0x43,0x34,0x11,0xef,0x6f,0xb7,0x5a,0x16,0xbd,0xc7,0xae,0xf6,0x1,0x36,0x80,0x5b,0xad,0xd2,0x5e,0x86,0x18,0x7,0x5b,0xb2,0x39,0x84,0x2a,0x7,0x8d,0x2d,0xb9,0xc0,0xc2,0xf6,0xa,0xf,0x9,0x7a,0x24,0x8b,0x32,0x34,0xea,0xde,0x3f,0x44,0x60,0x90,0xde,0x3e,0xfb,0xed,0xfc,0xfe,0x69,0x4b,0x3e,0xbc,0xba,0x93,0xb7,0xcb,0x7c,0xa5,0x64,0xd4,0x17,0xf6,0xa8,0xcb,0xc3,0xe8,0xce,0xfb,0xd4,0x25,0xaa,0xa5,0x22,0x8a,0x27,0x3c,0x7a,0x4e,0xd3,0x3b,0xaa,0xf9,0x3d,0x93,0x1a,0xc8,0xf1,0xb1,0x87,0xc8,0x6,0x5,0x27,0x1d,0x29,0x0,0xd3,0x5e,0x42,0x6b,0x77,0xa8,0xc9,0x89,0x1f,0xe,0x3a,0xa5,0xf8,0x23,0xc0,0xf,0x64,0x19,0x50,0x77,0x83,0xc7,0x19,0x6a,0xa0,0xde,0x6,0xf0,0xde,0x40,0xe3,0xfe,0xf5,0x27,0xe5,0xf0,0xbd,0xb,0xcf,0x59,0xcc,0xe8,0x56,0x35,0xe7,0x2a,0xe,0x82,0xc9,0x56,0x5,0xa7,0xe3,0xd3,0xd8,0xdd,0x82,0x5,0x54,0x9b,0x77,0x9e,0x47,0x9c,0x2d,0xa,0x16,0xd4,0x9a,0x57,0x74,0x70,0x57,0x6,0xdf,0x3d,0xbb,0x63,0x67,0x83,0x8c,0x81,0x1d,0x40,0xf3,0xb2,0xa3,0x5d,0x5d,0xbe,0xbd,0x7a,0xf7,0x7e,0x57,0xe9,0x2,0x66,0x7d,0xa8,0x4c,0xc1,0x15,0x7e,0x4,0x49,0x2,0xe4,0xa8,0x6f,0x4,0x57,0x5d,0xe2,0x75,0x71,0xf5,0xcb,0xee,0xc0,0xaa,0x3f,0x20,0x5a,0xf5,0x47,0x2,0xd7,0x47,0x1f,0xdf,0x60,0x7f,0x13,0xc8,0xe4,0x48,0xe,0x9b,0xf6,0x9a,0xb7,0x1e,0x70,0x3,0x98,0x9,0xf3,0xf7,0xd8,0xfe,0x81,0x7b,0x7c,0x14,0x72,0x6f,0x14,0x49,0x72,0xa8,0xde,0xbe,0x1c,0xf,0x69,0x14,0x87,0x77,0x2d,0xb7,0xcb,0x71,0xd9,0xea,0x49,0xe3,0x4c,0x18,0x89,0x4b,0xfa,0xa8,0x91,0xbf,0x37,0xc8,0x90,0xa9,0x57,0x1b,0xdf,0x16,0x1b,0xce,0xfd,0xfb,0xc2,0xd3,0xdb,0xa4,0x22,0xf5,0x78,0x45,0x7a,0xd9,0xd9,0x55,0x76,0xeb,0x3d,0x5c,0x29,0xea,0x3d,0x74,0x29,0xd2,0xe4,0x4b,0xd6,0xc,0xad,0x4d,0xb7,0x1d,0x6a,0x24,0x6c,0xbd,0x37,0xda,0x72,0xe4,0x9f,0xd8,0x8f,0xb7,0x8a,0xa3,0x54,0x90,0x62,0x18,0x1d,0xcc,0x82,0xe8,0xf0,0xdb,0x86,0xd0,0xc1,0x7a,0x79,0xb7,0x8a,0x20,0xae,0x8c,0x83,0x89,0xcf,0xbf,0xb8,0xbc,0x37,0x2c,0xb9,0xc1,0x12,0x19,0xad,0x23,0xaf,0xb6,0x42,0xa8,0xbe,0x15,0x42,0x79,0x99,0x8a,0x20,0x1d,0x7d,0xaf,0x12,0x54,0xab,0x1f,0x7f,0x5f,0x90,0xba,0xc3,0xd,0xd2,0x1c,0xc,0xd2,0xdb,0xe7,0xbf,0xec,0x2a,0xcd,0xf1,0xe9,0xee,0x98,0xe6,0xb6,0x4b,0x6a,0x42,0xaf,0x47,0xb0,0x60,0x1b,0x6d,0x2,0xca,0x88,0xb,0xff,0xdb,0xce,0x40,0x19,0xdd,0x1d,0x94,0x6f,0x5d,0x7b,0x46,0x8f,0x4,0xa6,0x10,0x8d,0x3e,0xf6,0x6,0xe8,0xfe,0x50,0xa9,0x81,0x7a,0xfb,0x3d,0x1a,0x69,0x2f,0xdf,0x9c,0xed,0x8,0xb2,0x74,0xda,0x7,0x82,0x2d,0xd3,0xfa,0x11,0x40,0xe7,0xe3,0x60,0x83,0x10,0xe3,0xa3,0xf4,0xf6,0x6b,0x1c,0x44,0x5a,0x87,0x86,0xea,0x3c,0xd4,0x8e,0xc0,0x13,0x73,0x3f,0x10,0x72,0x52,0xef,0x47,0x0,0x5b,0x7f,0x40,0xc2,0x90,0x86,0xf7,0x47,0x4e,0xd,0xd4,0xdb,0xaf,0xca,0x6f,0x44,0x6b,0x47,0xa8,0xa5,0xf3,0x3e,0x10,0x70,0x99,0xda,0x8f,0x0,0x3b,0xd7,0x19,0xdd,0x1f,0x37,0x18,0xa4,0xb7,0x2f,0x3a,0xbf,0x69,0xc6,0x5,0x1d,0x5,0xfc,0x33,0x37,0xed,0xf2,0xad,0xb9,0x23,0xf0,0xf8,0xe4,0xf,0x4,0x9c,0xd0,0xfb,0x11,0x80,0x26,0x3e,0x83,0xed,0xa2,0xd,0x22,0x2e,0x1d,0xc9,0xbf,0xeb,0x80,0x96,0x76,0x8e,0x76,0x15,0x73,0xd9,0xcc,0x3b,0x59,0x33,0xce,0xf4,0xdc,0x11,0x5c,0xe9,0x17,0xc1,0x62,0x65,0x2c,0xf,0x3,0xae,0xc5,0x45,0xd2,0xc9,0x4d,0xc,0x66,0xe5,0x88,0x11,0xdf,0x87,0x6d,0x15,0x66,0xda,0x15,0x6f,0x9e,0xec,0x4b,0x82,0x7b,0xb0,0x51,0x5f,0xdc,0xf2,0x23,0x9c,0x68,0xa0,0xb7,0xaf,0xf8,0x79,0x46,0x60,0xc6,0xaf,0x36,0xe0,0xe6,0x21,0x7,0x7f,0xc4,0x41,0x48,0x41,0xac,0xc,0x25,0x75,0x6c,0x4c,0xd7,0xd2,0x56,0xae,0xaf,0x7d,0x29,0x88,0xb5,0x17,0x88,0xbf,0x3,0x59,0x3b,0x1f,0x7f,0x7d,0xe1,0xdc,0xf2,0x9a,0xe3,0x64,0x3f,0x40,0xcb,0x8c,0xbe,0xa,0x8c,0x13,0x79,0xaa,0x74,0x15,0xb7,0xec,0xa3,0x66,0x61,0x8f,0xd9,0x7,0xee,0x99,0x6e,0x73,0x1f,0xbe,0xa7,0x8f,0x32,0xee,0x18,0xa5,0xe2,0x1b,0x78,0x15,0x9f,0xbc,0x99,0xa1,0xf0,0xbf,0xff,0xae,0xf5,0x1e,0x32,0xe8,0xe5,0x44,0xd3,0xb5,0x28,0x74,0x5a,0xfa,0xca,0x6f,0xa4,0x57,0xa9,0xbf,0xbf,0x54,0xff,0x79,0xf2,0x65,0x56,0x3f,0x89,0x9c,0x90,0xc,0xf9,0x7b,0x24,0x97,0x3a,0xf1,0x0,0x7,0xac,0x82,0x5c,0xf7,0xf2,0x6,0x1a,0xaf,0x49,0xc4,0x30,0x58,0xc3,0x28,0x5d,0xbc,0x7b,0xd3,0x91,0xdf,0x8c,0xbf,0xa6,0xc8,0xc5,0x6e,0xc9,0xf2,0xe2,0x40,0x70,0x32,0xcc,0x69,0xda,0xd4,0xba,0xc6,0xb9,0x39,0xf5,0xc1,0x87,0x3b,0x4d,0x99,0x27,0x8c,0xf3,0xa,0x8f,0x78,0x73,0xea,0xa0,0x8,0x97,0xd2,0xb0,0x2f,0xd9,0x9d,0xd6,0x79,0x45,0xa5,0xe5,0xd3,0x1a,0x3f,0x89,0x0,0xaa,0x5f,0x37,0x5,0x91,0xd8,0x6a,0x97,0x6c,0xd1,0x96,0x6f,0xb8,0xca,0x34,0xc0,0x72,0x88,0xd8,0xd1,0xe7,0x89,0xa5,0x9b,0xa5,0xd4,0x71,0x77,0x40,0x18,0xa7,0x2c,0xd5,0x4a,0x8a,0x4a,0x25,0x16,0x3b,0xc4,0x2c,0xe,0x83,0x66,0x2,0x0,0x47,0x4c,0xbb,0x68,0x7d,0xfa,0x61,0xea,0x24,0xfb,0xe2,0xe3,0x32,0xea,0x9f,0xde,0xa0,0xb0,0xf5,0xc3,0xf4,0xbc,0x42,0xdc,0xe4,0x29,0xcc,0x1,0xed,0x4e,0xf2,0xa9,0xe9,0xf1,0xa3,0xd0,0xc6,0x85,0x59,0x61,0x7d,0x1c,0x18,0x97,0xad,0xf6,0x94,0x8f,0xa6,0x3e,0xae,0xf8,0xb4,0x67,0x7c,0xa,0xf1,0x97,0x18,0x3,0x33,0x46,0xb5,0x1f,0xa6,0x17,0x89,0xe6,0x91,0x80,0x44,0x7d,0xec,0x5a,0x5a,0xc4,0x10,0x8b,0x23,0x1b,0xba,0x2f,0x2b,0xb2,0x9d,0x7c,0x32,0x13,0x33,0x81,0x69,0x34,0xa7,0x95,0x59,0xd9,0xa7,0x8e,0xf8,0x78,0xaa,0x42,0x43,0xd2,0x23,0x41,0x53,0xca,0x86,0x5b,0xe7,0x30,0x13,0x98,0x87,0x7b,0x16,0x7,0x80,0xa3,0x61,0x94,0xa4,0x3f,0x96,0xcc,0xc4,0xf2,0x16,0x8,0x42,0x3c,0xa0,0x37,0x38,0x4f,0xd3,0x5b,0xce,0x24,0xd,0xd6,0x92,0x69,0x9d,0x67,0x87,0x40,0x5b,0x7b,0xd5,0xc4,0xea,0xaf,0x64,0xba,0x62,0x4c,0x2d,0xb1,0x48,0xcb,0x38,0xb7,0x3a,0xd6,0x85,0x9,0x23,0x2f,0x5a,0x7b,0x46,0x10,0xfb,0xfe,0x5e,0xeb,0xc2,0xfc,0xeb,0xaf,0x8b,0x26,0x77,0x82,0xcb,0xe6,0xc,0xf1,0x56,0xab,0x25,0x5d,0xe1,0x14,0xc,0x99,0x61,0x6f,0x75,0x5a,0x7b,0x7b,0x1d,0x2b,0xbb,0x6e,0x75,0x4c,0x5b,0xdc,0x17,0x40,0x5b,0xea,0x2f,0xf4,0x5a,0x17,0x4f,0x9f,0x5e,0xee,0xb5,0x5a,0x9d,0x53,0xee,0x62,0xf6,0x1e,0x5c,0x1a,0x25,0x84,0x1d,0xc9,0x97,0xb8,0xa7,0x9d,0x53,0x6c,0xdc,0x98,0xb6,0xc7,0x7f,0x95,0x50,0x2f,0x7f,0xc3,0xf0,0xc,0x66,0x5a,0xd8,0x88,0x4c,0x60,0x8e,0x79,0xdb,0x13,0xed,0x52,0xfa,0x96,0x3f,0x47,0xeb,0x19,0x63,0xd3,0xc6,0xfc,0x57,0x49,0x24,0x40,0xd8,0x50,0xd0,0x5e,0x0,0x6b,0x9c,0x94,0x6,0xe6,0xed,0x9c,0xf6,0x8d,0xc0,0xb4,0x7b,0xf0,0xcb,0x34,0x93,0x66,0x6,0x27,0x78,0x43,0x38,0xb9,0x12,0x1e,0x4b,0xc3,0x33,0xdf,0x37,0x4a,0xf2,0x68,0x4c,0xc9,0xac,0x40,0x59,0xba,0x44,0x3c,0x1a,0x84,0x8d,0x69,0xe0,0xf8,0xc4,0xb9,0x6e,0x19,0xdc,0x70,0x18,0x42,0x44,0x1e,0xda,0x7b,0x4b,0x5d,0x6c,0x26,0x9,0x88,0x27,0xfc,0x4e,0x7a,0xa8,0x74,0x9f,0x4f,0xca,0x7,0xb3,0x98,0x83,0x30,0x93,0x1e,0xad,0x9d,0x57,0x3e,0x47,0x3c,0x8,0x93,0x25,0x24,0xb7,0x89,0x56,0xac,0xb8,0x39,0x19,0x3b,0x20,0x14,0x31,0x0,0x94,0x3f,0x3a,0xa0,0xef,0x9f,0xd6,0x5e,0x8d,0xbb,0xae,0xa9,0xbc,0xf3,0xf3,0xcc,0x7d,0xa1,0x6c,0x5d,0xfa,0x98,0x37,0xcf,0x27,0x3f,0x83,0x73,0xc9,0xfc,0x5,0x6e,0x72,0xbd,0x8e,0x66,0x96,0x66,0x81,0xda,0x5f,0x4d,0x9d,0x15,0x46,0x20,0x1b,0xac,0x26,0x2b,0x14,0x3e,0x20,0xd,0x56,0x93,0xe6,0xaa,0x1a,0x10,0xd2,0xd5,0x84,0xf9,0x2c,0xe,0x94,0x43,0x9,0xd6,0x88,0x4,0x2e,0x1d,0x41,0x4c,0xd3,0xa1,0x1,0x22,0x55,0x48,0x0,0x3a,0xbc,0xfa,0xf0,0xe6,0x75,0xab,0x94,0x2f,0xb7,0xa5,0xc4,0xfa,0x22,0x7,0x7c,0xae,0xf0,0x6c,0xce,0xa1,0xfc,0xa9,0x64,0x1f,0xd7,0x4a,0x1c,0x50,0x4e,0xf1,0x9,0x7c,0xf0,0x7a,0x81,0x3,0x1d,0x66,0xc,0x9a,0x7e,0xd1,0x4d,0xf8,0x7c,0x33,0x66,0x90,0xb9,0xd0,0x10,0xe0,0xc7,0xa7,0x1f,0x9d,0x2e,0x64,0xab,0xb,0xc4,0x70,0x25,0xa0,0x23,0x70,0x3,0xc9,0x39,0xb1,0xe8,0xe2,0x78,0x2c,0x6e,0xc,0x8a,0x37,0x24,0xac,0xe7,0xc5,0xe9,0xc1,0xd9,0x73,0xa2,0x35,0xcf,0x4f,0x61,0xb8,0xfd,0x5,0xb8,0x5b,0x41,0x71,0x74,0x17,0x82,0x20,0xb1,0x36,0xf2,0xb3,0x2c,0x16,0xfa,0x3c,0xe1,0xb,0x76,0x3c,0xb6,0x33,0x4f,0xb,0x57,0x83,0xc3,0xe3,0xdb,0xb4,0xa2,0x5b,0x9,0x72,0xef,0x1e,0x80,0x96,0xdd,0xe2,0x64,0xf3,0xcf,0xc4,0x4b,0x66,0x33,0x2c,0xca,0x5,0x6a,0x86,0xa6,0x15,0x66,0x15,0x6b,0x45,0x46,0x49,0x94,0xe4,0xf1,0x2d,0x82,0x61,0x2e,0xf9,0xcd,0xad,0x4,0xf9,0x57,0x90,0x20,0x4b,0xbc,0x20,0x4b,0x6c,0x5a,0x71,0x26,0x4b,0x96,0xf6,0xd2,0xd9,0x47,0xb7,0x30,0x4f,0x13,0x9e,0x69,0x8d,0x57,0x53,0x15,0xbe,0x37,0x2,0x1,0x46,0xb,0x2,0x8c,0x4c,0x6b,0x94,0x9,0x90,0xa5,0xcc,0x54,0x80,0xc9,0x9a,0xf0,0x73,0x31,0x3,0xff,0x0,0x19,0xbe,0xae,0x21,0x9c,0x25,0x5f,0xd3,0x3a,0xbb,0x85,0x36,0x3d,0x6e,0x5,0xb2,0x9e,0x2d,0xc8,0x7a,0x66,0x5a,0x87,0x27,0x67,0xb2,0x90,0x40,0xf2,0x26,0xc6,0x84,0x67,0x34,0x8b,0x18,0x5f,0xf9,0x5f,0x70,0xde,0xc9,0xdc,0x10,0x95,0x57,0xb3,0x41,0xa7,0x6,0xf2,0x71,0xc8,0x8c,0xd2,0x2f,0x3e,0x86,0x55,0x86,0xfa,0x7e,0x49,0xeb,0xfc,0xfc,0x42,0x83,0x3d,0x94,0x38,0xfa,0xab,0x85,0xd9,0x69,0x31,0x4d,0x9e,0xee,0xd4,0x30,0x3f,0x2f,0xf,0x2e,0xa5,0xb1,0x3e,0x89,0x34,0xf,0xf3,0x2f,0xa5,0xf1,0x1e,0xc7,0x9e,0x12,0x57,0x53,0x52,0x98,0x36,0xbf,0x32,0xba,0xc6,0xc4,0xb4,0xf6,0x26,0xa9,0x45,0x41,0x4a,0x5e,0x5b,0x32,0x11,0x41,0xc6,0xaf,0xf,0x22,0xe3,0xd7,0x82,0x8c,0x5f,0x1,0xb0,0x59,0x4,0xf4,0xa5,0x84,0xa0,0x46,0xd5,0x54,0xb5,0x10,0x4a,0x57,0xb3,0xb0,0xd2,0x4c,0x97,0x95,0xf2,0x52,0x1d,0xa3,0x3c,0xd9,0x57,0xff,0x17,0xd4,0xff,0x1,0xb5,0xe6,0xba,0xa1,0x25,0x4a,0x0,0x0,}; +#define index_html_gz_len 3592 diff --git a/esp32_cam/src/main.cpp b/esp32_cam/src/main.cpp new file mode 100644 index 0000000..6b31647 --- /dev/null +++ b/esp32_cam/src/main.cpp @@ -0,0 +1,560 @@ +/* + + _ _ _ _ +| | (_) | | | | +| | ___ __ __ _ ___ | |_| | ___ +| | / _ \\ \ /\ / /| |/ __|| _ | / _ \ +| |____| __/ \ V V / | |\__ \| | | || __/ +\_____/ \___| \_/\_/ |_||___/\_| |_/ \___| + +Compatible with all TTGO camera products, written by LewisHe +03/28/2020 +*/ + +#include +#include +#include "esp_camera.h" +/*************************************** + * Board select + **************************************/ +/* Select your board here, see the board list in the README for details*/ +// #define T_Camera_JORNAL_VERSION +// #define T_Camera_MINI_VERSION +// #define T_Camera_PLUS_VERSION +// #define T_Camera_V05_VERSION +// #define T_Camera_V16_VERSION +// #define T_Camera_V162_VERSION +#define T_Camera_V17_VERSION +// #define ESPRESSIF_ESP_EYE + +/*************************************** + * Function + **************************************/ +// #define SOFTAP_MODE //The comment will be connected to the specified ssid + +//When there is BME280, set the reading time here +#define DEFAULT_MEASUR_MILLIS 3000 /* Get sensor time by default (ms)*/ + +//When using timed sleep, set the sleep time here +#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ +#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */ + + +/*************************************** + * WiFi + **************************************/ +#define WIFI_SSID "WLAN" +#define WIFI_PASSWD "Bau3rWLAN" + + +#include "select_pins.h" + +#if defined(SOFTAP_MODE) +#endif +String macAddress = ""; +String ipAddress = ""; + +extern void startCameraServer(); + +#if defined(BUTTON_1) +//Depend BME280 library ,See https://github.com/mathertel/OneButton +#include +OneButton button(BUTTON_1, true); +#endif + + +#if defined(ENABLE_BEM280) +// Depend BME280 library ,See https://github.com/sparkfun/SparkFun_BME280_Arduino_Library +#include "SparkFunBME280.h" +BME280 sensor; +String temp, pressure, altitude, humidity; +#endif + +#if defined(SSD130_MODLE_TYPE) +// Depend OLED library ,See https://github.com/ThingPulse/esp8266-oled-ssd1306 +#include "SSD1306.h" +#include "OLEDDisplayUi.h" +#define SSD1306_ADDRESS 0x3c +SSD1306 oled(SSD1306_ADDRESS, I2C_SDA, I2C_SCL, (OLEDDISPLAY_GEOMETRY)SSD130_MODLE_TYPE); +OLEDDisplayUi ui(&oled); +#endif + +#if defined(ENABLE_TFT) +// Depend TFT_eSPI library ,See https://github.com/Bodmer/TFT_eSPI +#include +TFT_eSPI tft = TFT_eSPI(); +#endif + +bool setupSensor() +{ +#if defined(ENABLE_BEM280) + bool status = sensor.beginI2C(); + if (!status)return false; + sensor.setMode(MODE_SLEEP); //Sleep for now + sensor.setFilter(1); //0 to 4 is valid. Filter coefficient. See 3.4.4 + sensor.setStandbyTime(0); //0 to 7 valid. Time between readings. See table 27. + + sensor.setTempOverSample(1); //0 to 16 are valid. 0 disables temp sensing. See table 24. + sensor.setPressureOverSample(1); //0 to 16 are valid. 0 disables pressure sensing. See table 23. + sensor.setHumidityOverSample(1); //0 to 16 are valid. 0 disables humidity sensing. See table 19. + + sensor.setMode(MODE_SLEEP); //MODE_SLEEP, MODE_FORCED, MODE_NORMAL is valid. See 3.3 +#endif + +#if defined(AS312_PIN) + pinMode(AS312_PIN, INPUT); +#endif + return true; +} + +void readSensor() +{ +#if defined(ENABLE_BEM280) + + static long lastMillis; + + if (millis() - lastMillis < DEFAULT_MEASUR_MILLIS)return; + sensor.setMode(MODE_FORCED); //Wake up sensor and take reading + + long startTime = millis(); + while (sensor.isMeasuring() == false) ; //Wait for sensor to start measurment + while (sensor.isMeasuring() == true) ; //Hang out while sensor completes the reading + long endTime = millis(); + + //Sensor is now back asleep but we get get the data + Serial.print(" Measure time(ms): "); + Serial.print(endTime - startTime); + + Serial.print(" Humidity: "); + humidity = sensor.readFloatHumidity(); + Serial.print(humidity); + + Serial.print(" Pressure: "); + pressure = sensor.readFloatPressure(); + Serial.print(pressure); + + Serial.print(" Alt: "); + //Serial.print(sensor.readFloatAltitudeMeters(), 1); + altitude = String(sensor.readFloatAltitudeFeet()); + Serial.print(altitude); + + Serial.print(" Temp: "); + // Serial.print(sensor.readTempF(), 2); + temp = String(sensor.readTempC()); + Serial.print(temp); + + Serial.println(); + +#endif /*ENABLE_BEM280*/ +} + + + +bool deviceProbe(uint8_t addr) +{ + Wire.beginTransmission(addr); + return Wire.endTransmission() == 0; +} + +bool setupDisplay() +{ + +#if defined(ENABLE_TFT) +#if defined(T_Camera_PLUS_VERSION) + tft.init(); + tft.setRotation(0); + tft.fillScreen(TFT_BLACK); + tft.setTextSize(2); + tft.setTextDatum(MC_DATUM); + tft.drawString("TFT_eSPI", tft.width() / 2, tft.height() / 2); + tft.drawString("LilyGo Camera Plus", tft.width() / 2, tft.height() / 2 + 20); + pinMode(TFT_BL_PIN, OUTPUT); + digitalWrite(TFT_BL_PIN, HIGH); +#endif + +#elif defined(SSD130_MODLE_TYPE) + static FrameCallback frames[] = { + [](OLEDDisplay * display, OLEDDisplayUiState * state, int16_t x, int16_t y) + { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); +#if (SSD130_MODLE_TYPE) + display->drawString(64 + x, 0 + y, macAddress); + display->drawString(64 + x, 10 + y, ipAddress); +#else + display->drawString(64 + x, 9 + y, macAddress); + display->drawString(64 + x, 25 + y, ipAddress); +#endif + +#if defined(AS312_PIN) + if (digitalRead(AS312_PIN)) { + display->drawString(64 + x, 40 + y, "AS312 Trigger"); + } +#endif + + + }, + [](OLEDDisplay * display, OLEDDisplayUiState * state, int16_t x, int16_t y) + { +#if defined(ENABLE_BME280) + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(0 + x, 0 + y, temp); + display->drawString(0 + x, 16 + y, pressure); + display->drawString(0 + x, 32 + y, altitude); + display->drawString(0 + x, 48 + y, humidity); +#else + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + + +#if (SSD130_MODLE_TYPE) + // if (oled.getHeight() == 32) { + display->drawString( 64 + x, 0 + y, "Camera Ready! Use"); + display->drawString(64 + x, 10 + y, "http://" + ipAddress ); + display->drawString(64 + x, 16 + y, "to connect"); + // } else { +#else + display->drawString( 64 + x, 5 + y, "Camera Ready! Use"); + display->drawString(64 + x, 25 + y, "http://" + ipAddress ); + display->drawString(64 + x, 45 + y, "to connect"); + // } +#endif /*SSD130_MODLE_TYPE*/ + +#endif /*ENABLE_BME280*/ + } + }; + + if (!deviceProbe(SSD1306_ADDRESS))return false; + oled.init(); + // Wire.setClock(100000); //! Reduce the speed and prevent the speed from being too high, causing the screen + oled.setFont(ArialMT_Plain_16); + oled.setTextAlignment(TEXT_ALIGN_CENTER); + // delay(50); + oled.drawString( oled.getWidth() / 2, oled.getHeight() / 2 - 10, "LilyGo CAM"); + oled.display(); + ui.setTargetFPS(30); + ui.setIndicatorPosition(BOTTOM); + ui.setIndicatorDirection(LEFT_RIGHT); + ui.setFrameAnimation(SLIDE_LEFT); + ui.setFrames(frames, sizeof(frames) / sizeof(frames[0])); + ui.setTimePerFrame(6000); + ui.disableIndicator(); +#endif + return true; +} + + +void loopDisplay() +{ +#if defined(SSD130_MODLE_TYPE) + if (ui.update()) { +#endif /*SSD130_MODLE_TYPE*/ + +#if defined(BUTTON_1) + button.tick(); +#endif /*BUTTON_1*/ + +#if defined(SSD130_MODLE_TYPE) + } +#elif defined(ENABLE_TFT) + + // *** + +#endif /*SSD130_MODLE_TYPE & ENABLE_TFT*/ +} + + +bool setupPower() +{ +#if defined(ENABLE_IP5306) +#define IP5306_ADDR 0X75 +#define IP5306_REG_SYS_CTL0 0x00 + if (!deviceProbe(IP5306_ADDR))return false; + bool en = true; + Wire.beginTransmission(IP5306_ADDR); + Wire.write(IP5306_REG_SYS_CTL0); + if (en) + Wire.write(0x37); // Set bit1: 1 enable 0 disable boost keep on + else + Wire.write(0x35); // 0x37 is default reg value + return Wire.endTransmission() == 0; + +#elif defined(ENABLE_AXP192) +#define AXP192_ADDRESS 0x34 + if (!deviceProbe(AXP192_ADDRESS))return false; + //Turn off no use power channel , Or you can do nothing + uint8_t val; + Wire.beginTransmission(AXP192_ADDRESS); + Wire.write(0x30); + Wire.endTransmission(); + Wire.requestFrom(AXP192_ADDRESS, 1); + val = Wire.read(); + + Wire.beginTransmission(AXP192_ADDRESS); + Wire.write(0x30); + Wire.write( val & 0xFC); + Wire.endTransmission(); + + Wire.beginTransmission(AXP192_ADDRESS); + Wire.write(0x12); + Wire.endTransmission(); + Wire.requestFrom(AXP192_ADDRESS, 1); + val = Wire.read(); + + Wire.beginTransmission(AXP192_ADDRESS); + Wire.write(0x12); + Wire.write(val & 0b10100010); + Wire.endTransmission(); +#endif +#if defined(T_Camera_MINI_VERSION) + // There is a pin in the Mini to control the camera power + pinMode(POWER_CONTROL_PIN, OUTPUT); + digitalWrite(POWER_CONTROL_PIN, HIGH); +#endif + + return true; +} + +#if defined(SDCARD_CS_PIN) +#include +#endif +bool setupSDCard() +{ + /* + T-CameraPlus Board, SD shares the bus with the LCD screen. + It does not need to be re-initialized after the screen is initialized. + If the screen is not initialized, the initialization SPI bus needs to be turned on. + */ + // SPI.begin(TFT_SCLK_PIN, TFT_MISO_PIN, TFT_MOSI_PIN); + +#if defined(SDCARD_CS_PIN) + if (!SD.begin(SDCARD_CS_PIN)) { + tft.setTextColor(TFT_RED); + tft.drawString("SDCard begin failed", tft.width() / 2, tft.height() / 2 - 20); + tft.setTextColor(TFT_WHITE); + return false; + } else { + String cardInfo = String(((uint32_t)SD.cardSize() / 1024 / 1024)); + tft.setTextColor(TFT_GREEN); + tft.drawString("SDcardSize=[" + cardInfo + "]MB", tft.width() / 2, tft.height() / 2 + 92); + tft.setTextColor(TFT_WHITE); + + Serial.print("SDcardSize=["); + Serial.print(cardInfo); + Serial.println("]MB"); + } +#endif + return true; +} + + +bool setupCamera() +{ + camera_config_t config; + +#if defined(Y2_GPIO_NUM) + 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; + } +#endif + +#if defined(ESPRESSIF_ESP_EYE) || defined(T_Camera_V162_VERSION) || defined(T_Camera_MINI_VERSION) + /* IO13, IO14 is designed for JTAG by default, + * to use it as generalized input, + * firstly declair it as pullup input */ + pinMode(13, INPUT_PULLUP); + pinMode(14, INPUT_PULLUP); +#endif + + // camera init + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + Serial.printf("Camera init failed with error 0x%x\n", err); + return false; + } + + sensor_t *s = esp_camera_sensor_get(); + //initial sensors are flipped vertically and colors are a bit saturated + if (s->id.PID == OV3660_PID) { + s->set_vflip(s, 1);//flip it back + s->set_brightness(s, 1);//up the blightness just a bit + s->set_saturation(s, -2);//lower the saturation + } + //drop down frame size for higher initial frame rate + s->set_framesize(s, FRAMESIZE_QVGA); + +#if defined(T_Camera_V162_VERSION) + s->set_vflip(s, 1); + s->set_hmirror(s, 1); +#endif + return true; +} + +void setupNetwork() +{ + macAddress = "LilyGo-CAM-"; +#ifdef SOFTAP_MODE + WiFi.mode(WIFI_AP); + macAddress += WiFi.softAPmacAddress().substring(0, 5); + WiFi.softAP(macAddress.c_str()); + ipAddress = WiFi.softAPIP().toString(); +#else + WiFi.begin(WIFI_SSID, WIFI_PASSWD); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.println("WiFi connected"); + ipAddress = WiFi.localIP().toString(); + macAddress += WiFi.macAddress().substring(0, 5); +#endif +#if defined(ENABLE_TFT) +#if defined(T_Camera_PLUS_VERSION) + tft.drawString("ipAddress:", tft.width() / 2, tft.height() / 2 + 50); + tft.drawString(ipAddress, tft.width() / 2, tft.height() / 2 + 72); +#endif +#endif +} + +void setupButton() +{ +#if defined(BUTTON_1) + button.attachClick([]() { + static bool en = false; + sensor_t *s = esp_camera_sensor_get(); + en = en ? 0 : 1; + s->set_vflip(s, en); +#if defined(SSD130_MODLE_TYPE) + if (en) { + oled.resetOrientation(); + } else { + oled.flipScreenVertically(); + } +#endif + // delay(200); + }); + + button.attachDoubleClick([]() { + if (PWDN_GPIO_NUM > 0) { + pinMode(PWDN_GPIO_NUM, PULLUP); + digitalWrite(PWDN_GPIO_NUM, HIGH); + } + +#if defined(SSD130_MODLE_TYPE) + ui.disableAutoTransition(); + oled.setTextAlignment(TEXT_ALIGN_CENTER); + oled.setFont(ArialMT_Plain_10); + oled.clear(); +#if defined(AS312_PIN) && defined(PIR_SUPPORT_WAKEUP) + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "Set to wake up from PIR"); +#elif defined(BUTTON_1) + oled.drawString(oled.getWidth() / 2 - 5, oled.getHeight() / 2 - 20, "Deepsleep"); + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2 - 10, "Set to be awakened by"); + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "a key press"); +#else + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "Set to wake up by timer"); +#endif + oled.display(); + delay(3000); + oled.clear(); + oled.displayOff(); +#else + delay(2000); +#endif /*SSD130_MODLE_TYPE*/ + + +#if defined(AS312_PIN) && defined(PIR_SUPPORT_WAKEUP) + esp_sleep_enable_ext1_wakeup(((uint64_t)(((uint64_t)1) << AS312_PIN)), ESP_EXT1_WAKEUP_ANY_HIGH); +#elif defined(BUTTON_1) + // esp_sleep_enable_ext0_wakeup((gpio_num_t )BUTTON_1, LOW); +#if defined(T_Camera_MINI_VERSION) + esp_sleep_enable_ext1_wakeup(((uint64_t)(((uint64_t)1) << BUTTON_1)), ESP_EXT1_WAKEUP_ANY_HIGH); +#else + esp_sleep_enable_ext1_wakeup(((uint64_t)(((uint64_t)1) << BUTTON_1)), ESP_EXT1_WAKEUP_ALL_LOW); +#endif +#else + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); +#endif + esp_deep_sleep_start(); + }); +#if defined(T_Camera_MINI_VERSION) + button.setClickTicks(200); + button.setDebounceTicks(0); +#endif +#endif /*BUTTON_1*/ +} + + + +void setup() +{ + + Serial.begin(115200); + +#if defined(I2C_SDA) && defined(I2C_SCL) + Wire.begin(I2C_SDA, I2C_SCL); +#endif + + bool status; + status = setupDisplay(); + Serial.print("setupDisplay status "); Serial.println(status); + + status = setupSDCard(); + Serial.print("setupSDCard status "); Serial.println(status); + + status = setupPower(); + Serial.print("setupPower status "); Serial.println(status); + + status = setupSensor(); + Serial.print("setupSensor status "); Serial.println(status); + + status = setupCamera(); + Serial.print("setupCamera status "); Serial.println(status); + if (!status) { + delay(10000); esp_restart(); + } + + setupButton(); + + setupNetwork(); + + startCameraServer(); + + Serial.print("Camera Ready! Use 'http://"); + Serial.print(ipAddress); + Serial.println("' to connect"); +} + +void loop() +{ + loopDisplay(); +} diff --git a/esp32_cam/src/platformio.ini b/esp32_cam/src/platformio.ini new file mode 100644 index 0000000..44a83e1 --- /dev/null +++ b/esp32_cam/src/platformio.ini @@ -0,0 +1,27 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino + +; Serial Monitor options +monitor_speed = 115200 +monitor_port = COM137 +upload_port = COM137 + +lib_deps = + ; SparkFun BME280@2.0.8 + ; U8g2@2.28.0 + ; Adafruit BME280 Library@2.0.1 + ; OneButton@c8651328e3 + ; ESP8266_SSD1306@4.1.0 + ; TFT_eSPI diff --git a/esp32_cam/src/select_pins.h b/esp32_cam/src/select_pins.h new file mode 100644 index 0000000..f54d824 --- /dev/null +++ b/esp32_cam/src/select_pins.h @@ -0,0 +1,260 @@ +/* + + _ _ _ _ +| | (_) | | | | +| | ___ __ __ _ ___ | |_| | ___ +| | / _ \\ \ /\ / /| |/ __|| _ | / _ \ +| |____| __/ \ V V / | |\__ \| | | || __/ +\_____/ \___| \_/\_/ |_||___/\_| |_/ \___| + +Compatible with all TTGO camera products, written by LewisHe +03/28/2020 +*/ + +#if defined(T_Camera_V16_VERSION) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 15 +#define Y7_GPIO_NUM 12 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 35 +#define Y4_GPIO_NUM 14 +#define Y3_GPIO_NUM 13 +#define Y2_GPIO_NUM 34 + +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + +#define AS312_PIN 19 +#define BUTTON_1 0 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +#define SSD130_MODLE_TYPE 0 // 0 : GEOMETRY_128_64 // 1: GEOMETRY_128_32 + +#define IIS_SCK 26 +#define IIS_WS 32 +#define IIS_DOUT 33 + +#elif defined(T_Camera_V05_VERSION) +#define PWDN_GPIO_NUM 26 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 32 +#define SIOD_GPIO_NUM 13 +#define SIOC_GPIO_NUM 12 + +#define Y9_GPIO_NUM 39 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 23 +#define Y6_GPIO_NUM 18 +#define Y5_GPIO_NUM 15 +#define Y4_GPIO_NUM 4 +#define Y3_GPIO_NUM 14 +#define Y2_GPIO_NUM 5 + +#define VSYNC_GPIO_NUM 27 +#define HREF_GPIO_NUM 25 +#define PCLK_GPIO_NUM 19 + +#define AS312_PIN 33 +#define BUTTON_1 34 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +#define SSD130_MODLE_TYPE 0 // 0 : GEOMETRY_128_64 // 1: GEOMETRY_128_32 + + +#elif defined(T_Camera_JORNAL_VERSION) + +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM 15 +#define XCLK_GPIO_NUM 27 +#define SIOD_GPIO_NUM 25 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 19 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 5 +#define Y4_GPIO_NUM 34 +#define Y3_GPIO_NUM 35 +#define Y2_GPIO_NUM 17 + +#define VSYNC_GPIO_NUM 22 +#define HREF_GPIO_NUM 26 +#define PCLK_GPIO_NUM 21 + +#define I2C_SDA 14 +#define I2C_SCL 13 + +#define BUTTON_1 32 + +#define SSD130_MODLE_TYPE 1 // 0 : GEOMETRY_128_64 // 1: GEOMETRY_128_32 + +#elif defined(T_Camera_PLUS_VERSION) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 37 +#define Y7_GPIO_NUM 38 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 35 +#define Y4_GPIO_NUM 26 +#define Y3_GPIO_NUM 13 +#define Y2_GPIO_NUM 34 +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + +#define I2C_SDA 18 +#define I2C_SCL 23 + +#define IIS_SCK 14 +#define IIS_WS 32 +#define IIS_DOUT 33 + +#define TFT_CS_PIN 12 +#define TFT_DC_PIN 15 +#define TFT_MOSI_PIN 19 +#define TFT_MISO_PIN 22 +#define TFT_SCLK_PIN 21 +#define TFT_BL_PIN 2 + +#define SDCARD_CS_PIN 0 + +#define ENABLE_IP5306 +#define ENABLE_TFT + +#elif defined(T_Camera_V162_VERSION) + +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 37 +#define Y7_GPIO_NUM 38 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 35 +#define Y4_GPIO_NUM 14 +#define Y3_GPIO_NUM 13 +#define Y2_GPIO_NUM 34 +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +#define BUTTON_1 15 + +#define SSD130_MODLE_TYPE 0 // 0 : GEOMETRY_128_64 // 1: GEOMETRY_128_32 + +#define AS312_PIN 19 //not rtc io ,can't form deepsleep wakeup + +#define IIS_SCK 26 +#define IIS_WS 32 +#define IIS_DOUT 33 + +#define ENABLE_IP5306 + +#elif defined(T_Camera_MINI_VERSION) + +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 32 +#define SIOD_GPIO_NUM 13 +#define SIOC_GPIO_NUM 12 + +#define Y9_GPIO_NUM 39 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 38 +#define Y6_GPIO_NUM 37 +#define Y5_GPIO_NUM 15 +#define Y4_GPIO_NUM 4 +#define Y3_GPIO_NUM 14 +#define Y2_GPIO_NUM 5 + +#define VSYNC_GPIO_NUM 27 +#define HREF_GPIO_NUM 25 +#define PCLK_GPIO_NUM 19 +#define CAMERA_PWR_PIN 26 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +#define BUTTON_1 33 + +#define POWER_CONTROL_PIN 26 + +#define ENABLE_AXP192 +#elif defined(T_Camera_V17_VERSION) + +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 32 +#define SIOD_GPIO_NUM 13 +#define SIOC_GPIO_NUM 12 + +#define Y9_GPIO_NUM 39 +#define Y8_GPIO_NUM 36 +#define Y7_GPIO_NUM 23 +#define Y6_GPIO_NUM 18 +#define Y5_GPIO_NUM 15 +#define Y4_GPIO_NUM 4 +#define Y3_GPIO_NUM 14 +#define Y2_GPIO_NUM 5 + +#define VSYNC_GPIO_NUM 27 +#define HREF_GPIO_NUM 25 +#define PCLK_GPIO_NUM 19 + +#define I2C_SDA 21 +#define I2C_SCL 22 + +#define BUTTON_1 34 + +#define SSD130_MODLE_TYPE 0 // 0 : GEOMETRY_128_64 // 1: GEOMETRY_128_32 + +#define AS312_PIN 33 + +#define ENABLE_IP5306 + +#elif defined(ESPRESSIF_ESP_EYE) +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 37 +#define Y7_GPIO_NUM 38 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 35 +#define Y4_GPIO_NUM 14 +#define Y3_GPIO_NUM 13 +#define Y2_GPIO_NUM 34 +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + +#define BUTTON_1 0 +#else +#error "Please select the model of the board you want to use in main.cpp" +#endif \ No newline at end of file