commit d526978c1837a64d6ab633ea04bf42613316f905 Author: 0xFEEDC0DE64 Date: Fri Apr 26 12:38:17 2019 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58e33b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pio +.pioenvs +.piolibdeps diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a0410c6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/OneButton"] + path = lib/OneButton + url = https://github.com/mathertel/OneButton.git +[submodule "lib/esp8266-oled-ssd1306"] + path = lib/esp8266-oled-ssd1306 + url = https://github.com/ThingPulse/esp8266-oled-ssd1306.git diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7c486f1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < https://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < https://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < https://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choose one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to be used as a library with examples. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/OneButton b/lib/OneButton new file mode 160000 index 0000000..f78433c --- /dev/null +++ b/lib/OneButton @@ -0,0 +1 @@ +Subproject commit f78433c20931eb0ec2df0a279692f06a4896fbf9 diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/esp8266-oled-ssd1306 b/lib/esp8266-oled-ssd1306 new file mode 160000 index 0000000..cb294b0 --- /dev/null +++ b/lib/esp8266-oled-ssd1306 @@ -0,0 +1 @@ +Subproject commit cb294b0d5e938864ba935a8e917082e5496399ca diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..4b30716 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,14 @@ +; 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 diff --git a/platformio.pro b/platformio.pro new file mode 100644 index 0000000..c42ebcf --- /dev/null +++ b/platformio.pro @@ -0,0 +1,117 @@ +win32 { + HOMEDIR += $$(USERPROFILE) +} +else { + HOMEDIR += $$(HOME) +} + +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/config" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/app_trace" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/app_update" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/asio" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/bootloader_support" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/bt" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/coap" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/console" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/driver" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp-tls" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp32" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp_adc_cal" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp_event" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp_http_client" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp_http_server" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp_https_ota" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp_ringbuf" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/ethernet" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/expat" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fatfs" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/freemodbus" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/freertos" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/heap" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/idf_test" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/jsmn" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/json" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/libsodium" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/log" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/lwip" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/mbedtls" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/mdns" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/micro-ecc" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/mqtt" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/newlib" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/nghttp" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/nvs_flash" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/openssl" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/protobuf-c" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/protocomm" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/pthread" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/sdmmc" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/smartconfig_ack" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/soc" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/spi_flash" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/spiffs" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/tcp_transport" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/tcpip_adapter" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/ulp" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/vfs" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/wear_levelling" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/wifi_provisioning" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/wpa_supplicant" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/xtensa-debug-module" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp32-camera" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/esp-face" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fb_gfx" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/cores/esp32" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/variants/esp32" +INCLUDEPATH += "$${HOMEDIR}/camera/lib/OneButton/src" +INCLUDEPATH += "$${HOMEDIR}/camera/lib/esp8266-oled-ssd1306/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/AzureIoT/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/BLE/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/BluetoothSerial/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/DNSServer/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/EEPROM/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/ESP32/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/FFat/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/FS/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdate/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/SD/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/Update/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/toolchain-xtensa32/xtensa-esp32-elf/include" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/toolchain-xtensa32/xtensa-esp32-elf/include/c++/5.2.0" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/toolchain-xtensa32/xtensa-esp32-elf/include/c++/5.2.0/xtensa-esp32-elf" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/toolchain-xtensa32/lib/gcc/xtensa-esp32-elf/5.2.0/include" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/toolchain-xtensa32/lib/gcc/xtensa-esp32-elf/5.2.0/include-fixed" +INCLUDEPATH += "$${HOMEDIR}/.platformio/packages/tool-unity" +INCLUDEPATH += "$${HOMEDIR}/camera/include" +INCLUDEPATH += "$${HOMEDIR}/camera/src" + +DEFINES += "PLATFORMIO=30603" +DEFINES += "ARDUINO_ESP32_DEV" +DEFINES += "ESP32" +DEFINES += "ESP_PLATFORM" +DEFINES += "F_CPU=240000000L" +DEFINES += "HAVE_CONFIG_H" +DEFINES += "MBEDTLS_CONFIG_FILE="mbedtls/esp_config.h"" +DEFINES += "ARDUINO=10805" +DEFINES += "ARDUINO_ARCH_ESP32" +DEFINES += "ARDUINO_VARIANT="esp32"" +DEFINES += "ARDUINO_BOARD="Espressif ESP32 Dev Module"" + +OTHER_FILES += platformio.ini + +SOURCES += src/main.cpp diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..b63896f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,507 @@ +#include + +#include +#include +#include "esp_camera.h" + +#include +#include + +#include + +const std::array 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,}; + +typedef struct { + httpd_req_t *req; + size_t len; +} jpg_chunking_t; + +constexpr auto WIFI_SSID = "McDonalds Free WiFi 2.4GHz"; +constexpr auto WIFI_PASSWD = "Passwort_123"; + +constexpr auto SSD1306_ADDRESS = 0x3c; +constexpr auto I2C_SDA = 21; +constexpr auto I2C_SCL = 22; + +constexpr auto BUTTON_1 = 34; + +constexpr auto Y9_GPIO_NUM = 39; +constexpr auto Y8_GPIO_NUM = 36; +constexpr auto Y7_GPIO_NUM = 23; +constexpr auto Y6_GPIO_NUM = 18; +constexpr auto Y5_GPIO_NUM = 15; +constexpr auto Y4_GPIO_NUM = 4; +constexpr auto Y3_GPIO_NUM = 14; +constexpr auto Y2_GPIO_NUM = 5; +constexpr auto VSYNC_GPIO_NUM = 27; +constexpr auto HREF_GPIO_NUM = 25; +constexpr auto PCLK_GPIO_NUM = 19; +constexpr auto PWDN_GPIO_NUM = 26; +constexpr auto RESET_GPIO_NUM = -1; +constexpr auto XCLK_GPIO_NUM = 32; +constexpr auto SIOD_GPIO_NUM = 13; +constexpr auto SIOC_GPIO_NUM = 12; + +SSD1306 oled(SSD1306_ADDRESS, I2C_SDA, I2C_SCL); +OLEDDisplayUi ui(&oled); + +OneButton button1(BUTTON_1, true); + +void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_CENTER); + + display->drawString(64 + x, 5 + y, "Status:"); + + const char *status; + switch(WiFi.status()) + { + case WL_NO_SHIELD: status = "NO_SHIELD"; break; + case WL_IDLE_STATUS: status = "IDLE_STATUS"; break; + case WL_NO_SSID_AVAIL: status = "NO_SSID_AVAIL"; break; + case WL_SCAN_COMPLETED: status = "SCAN_COMPLETED"; break; + case WL_CONNECTED: status = "CONNECTED"; break; + case WL_CONNECT_FAILED: status = "CONNECT_FAILED"; break; + case WL_CONNECTION_LOST: status = "CONNECTION_LOST"; break; + case WL_DISCONNECTED: status = "DISCONNECTED"; break; + default: status = "UNKNOWN"; + } + display->drawString(64 + x, 20 + y, status); +} + +void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_CENTER); + + display->drawString(64 + x, 5 + y, "MAC-Address:"); + display->drawString(64 + x, 20 + y, WiFi.macAddress()); +} + +void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_CENTER); + + display->drawString(64 + x, 5 + y, "IP-Address:"); + display->drawString(64 + x, 20 + y, WiFi.localIP().toString()); +} + +void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_CENTER); + + display->drawString(64 + x, 5 + y, "Hostname:"); + display->drawString(64 + x, 20 + y, WiFi.getHostname()); +} + +std::array frames { drawFrame1, drawFrame2, drawFrame3, drawFrame4 }; + +bool autoTransitionEnabled = true; + +void buttonLongPress() +{ + if (autoTransitionEnabled) + { + ui.disableAutoTransition(); + ui.disableAllIndicators(); + } + else + { + ui.enableAutoTransition(); + ui.enableAllIndicators(); + } + + autoTransitionEnabled = !autoTransitionEnabled; +} + +void buttonClick() +{ + ui.nextFrame(); +} + +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, reinterpret_cast(&index_html_gz[0]), index_html_gz.size()); +} + +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 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 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; + + 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); + 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]; + +#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"; + + res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); + if(res != ESP_OK){ + return res; + } + + while(true){ + + // xEventGroupWaitBits(evGroup, 1, pdFALSE, pdFALSE, portMAX_DELAY); + + 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; + } + + } + + return res; +} + +void setup() +{ + Serial.begin(115200); + Serial.setDebugOutput(true); + Serial.println(); + + WiFi.begin(WIFI_SSID, WIFI_PASSWD); + + Wire.begin(I2C_SDA, I2C_SCL); + + oled.init(); + oled.setTextAlignment(TEXT_ALIGN_CENTER); + Wire.setClock(100000); + + { + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; + //init with high specs to pre-allocate larger buffers + config.frame_size = FRAMESIZE_VGA; + config.jpeg_quality = 10; + config.fb_count = 2; + + const esp_err_t error = esp_camera_init(&config); + if (error != ESP_OK) + { + Serial.println("error initializing camera"); + Serial.println(error); + + oled.clear(); + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "Error camera"); + oled.drawString(oled.getWidth() / 2, (oled.getHeight() / 2) + 10, String(error)); + oled.display(); + while (true); + } + } + + + { + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + + { + httpd_handle_t webserver; + + { + const esp_err_t error = httpd_start(&webserver, &config); + if (error != ESP_OK) + { + Serial.println("error initializing webserver"); + Serial.println(error); + + oled.clear(); + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "Error webserver"); + oled.drawString(oled.getWidth() / 2, (oled.getHeight() / 2) + 10, String(error)); + oled.display(); + while (true); + } + } + + for (const httpd_uri_t &uri : { + httpd_uri_t { "/", HTTP_GET, index_handler, NULL }, + httpd_uri_t { "/status", HTTP_GET, status_handler, NULL }, + httpd_uri_t { "/control", HTTP_GET, cmd_handler, NULL }, + httpd_uri_t { "/capture", HTTP_GET, capture_handler, NULL } + }) + { + const esp_err_t error = httpd_register_uri_handler(webserver, &uri); + if (error != ESP_OK) + { + Serial.print("error registering uri "); + Serial.println(uri.uri); + Serial.println(error); + + oled.clear(); + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "Error webserver uri"); + oled.drawString(oled.getWidth() / 2, (oled.getHeight() / 2) + 10, uri.uri); + oled.drawString(oled.getWidth() / 2, (oled.getHeight() / 2) + 20, String(error)); + oled.display(); + while (true); + } + } + } + + config.server_port += 1; + config.ctrl_port += 1; + + { + httpd_handle_t webserver; + + { + const esp_err_t error = httpd_start(&webserver, &config); + if (error != ESP_OK) + { + Serial.println("error initializing streamserver"); + Serial.println(error); + + oled.clear(); + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "Error streamserver"); + oled.drawString(oled.getWidth() / 2, (oled.getHeight() / 2) + 10, String(error)); + oled.display(); + while (true); + } + } + + for (const httpd_uri_t &uri : { + httpd_uri_t { "/stream", HTTP_GET, stream_handler, NULL } + }) + { + const esp_err_t error = httpd_register_uri_handler(webserver, &uri); + if (error != ESP_OK) + { + Serial.print("error registering uri "); + Serial.println(uri.uri); + Serial.println(error); + + oled.clear(); + oled.drawString(oled.getWidth() / 2, oled.getHeight() / 2, "Error webserver uri"); + oled.drawString(oled.getWidth() / 2, (oled.getHeight() / 2) + 10, uri.uri); + oled.drawString(oled.getWidth() / 2, (oled.getHeight() / 2) + 20, String(error)); + oled.display(); + while (true); + } + } + } + } + + ui.setTargetFPS(30); + ui.setIndicatorPosition(BOTTOM); + ui.setIndicatorDirection(LEFT_RIGHT); + ui.setFrameAnimation(SLIDE_LEFT); + ui.setFrames(&frames[0], frames.size()); + ui.setTimePerFrame(6000); + + button1.attachLongPressStart(buttonLongPress); + button1.attachClick(buttonClick); +} + +void loop() +{ + if (ui.update()) + button1.tick(); +} diff --git a/test/README b/test/README new file mode 100644 index 0000000..df5066e --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html