{"id":4036,"date":"2022-05-29T19:00:47","date_gmt":"2022-05-30T02:00:47","guid":{"rendered":"https:\/\/www.cloudacm.com\/?p=4036"},"modified":"2022-05-29T18:14:57","modified_gmt":"2022-05-30T01:14:57","slug":"esp32-cam-headless-controller","status":"publish","type":"post","link":"https:\/\/www.cloudacm.com\/?p=4036","title":{"rendered":"ESP32-Cam Headless Controller"},"content":{"rendered":"<p>Brian Lough created a video on YouTube titled &#8220;WiFiManager &#8211; An Essential ESP32 library!&#8221;<\/p>\n<p><iframe loading=\"lazy\" title=\"WiFiManager - An Essential ESP32 library!\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/Errh7LEEug0?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>This video pointed out that hard coded settings on the ESP32 can be a limitation. It went on to demonstrate this with wireless network connectivity and the inherent dependency when preset values are used. The solution rested on a library that gives the ESP32 the ability to operate as a WiFi access point and for the ESP32 to provide a web portal for an attached device. The web page could then take form entries to set variables on the ESP32.<\/p>\n<p>In this post, I&#8217;ll be using this concept with the intent to set the time and date on the ESP32-Cam module. There are serveral demonstrations of setting time on the ESP32 online that utilize the network time protocol (here are two examples, see next video clip and <a href=\"https:\/\/randomnerdtutorials.com\/esp32-date-time-ntp-client-server-arduino\/\">https:\/\/randomnerdtutorials.com\/esp32-date-time-ntp-client-server-arduino\/<\/a>). However, this is dependent on an internet connection to a NTP host. I&#8217;ll be using the ESP32Time library, <a href=\"https:\/\/www.arduino.cc\/reference\/en\/libraries\/esp32time\/\">https:\/\/www.arduino.cc\/reference\/en\/libraries\/esp32time\/<\/a>, which has no need for an external RTC module or NTP time synchronization. This will allow the ESP32 module to be operated offline with a minimal set of hardware.<\/p>\n<p><iframe loading=\"lazy\" title=\"#299 Tricks to get NTP time for the ESP32 and the ESP8266 incl. Summer- and Daylight Saving time\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/r2UAmBLBBRM?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>The ability to control the ESP32 module using a webpage on a mobile device also reduces the need for control and indication hardware on the ESP32, which was a requirement on my earlier flight data logger, <a href=\"https:\/\/www.cloudacm.com\/?p=3713\">https:\/\/www.cloudacm.com\/?p=3713<\/a><\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Components.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-4039 size-full aligncenter\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Components.jpg\" alt=\"\" width=\"635\" height=\"414\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Components.jpg 635w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Components-300x196.jpg 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Components-414x270.jpg 414w\" sizes=\"auto, (max-width: 635px) 100vw, 635px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Connected.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-4040 size-full aligncenter\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Connected.jpg\" alt=\"\" width=\"682\" height=\"372\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Connected.jpg 682w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Connected-300x164.jpg 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/ESP32-Cam_Connected-495x270.jpg 495w\" sizes=\"auto, (max-width: 682px) 100vw, 682px\" \/><\/a><\/p>\n<p>Much of the working code is based off of the work of Rui Santos, <a href=\"https:\/\/randomnerdtutorials.com\/esp32-email-alert-temperature-threshold\/\">https:\/\/randomnerdtutorials.com\/esp32-email-alert-temperature-threshold\/<\/a>. There have been some adaptations, such as the webpage content and its appearence. The data logging function also expands on the base code, with the timestamp information available in both the file properties and also its content.<\/p>\n<p>Here is an example of the code used in this project.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/ Title and Description\r\n\r\n\/** \r\nESP32-Cam_AP_WebServer_Head-Controller_Example\r\n\r\nCode Base for ESP32-Cam module to allow it to operate in AP mode\r\nand provide a web portal used as a control interface\r\nvia a mobile device, such as a phone or tablet.\r\nThu May 26 2022 13:01:55 GMT+0000 (1653570115)\r\n**\/\r\n\r\n\r\n\/\/ Notes\r\n\/** \r\nThis code contains serial debug which will need to be removed before final deploy\r\n\r\n**\/\r\n\r\n\r\n\r\n\/\/ Credit and Sources\r\n\r\n\/*********\r\n  Rui Santos\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-email-alert-temperature-threshold\/\r\n  \r\n  Permission is hereby granted, free of charge, to any person obtaining a copy\r\n  of this software and associated documentation files.\r\n  \r\n  The above copyright notice and this permission notice shall be included in all\r\n  copies or substantial portions of the Software.\r\n*********\/\r\n\r\n\/** \r\n  Unix Epoch Time Calculator  \r\n  https:\/\/unixtime.org\/\r\n  NIST Offical U.S. Time\r\n  https:\/\/time.gov\/\r\n**\/\r\n\r\n\/** \r\n  WiFiManager - An Essential ESP32 library!\r\n  <iframe loading=\"lazy\" title=\"WiFiManager - An Essential ESP32 library!\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/Errh7LEEug0?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\r\n**\/\r\n\r\n\/\/ Libraries\r\n\r\n#include &lt;WiFi.h&gt;\r\n#include &lt;AsyncTCP.h&gt;\r\n#include &lt;ESPAsyncWebServer.h&gt;\r\n#include &lt;ESP32Time.h&gt;\r\n#include &lt;esp_wifi.h&gt;\r\n#include &lt;SD_MMC.h&gt;\r\n#include \"esp_camera.h\"\r\n\r\n\r\n\r\n\/\/ Definitions\r\n\r\nESP32Time rtc;\r\nAsyncWebServer server(80);\r\n\r\n\/\/ Pins for ESP32-CAM\r\n#define FLASH_PIN         4\r\n\r\n\/\/ Pins for Image Sensor\r\n#define PWDN_GPIO_NUM     32\r\n#define RESET_GPIO_NUM    -1\r\n#define XCLK_GPIO_NUM      0\r\n#define SIOD_GPIO_NUM     26\r\n#define SIOC_GPIO_NUM     27\r\n#define Y9_GPIO_NUM       35\r\n#define Y8_GPIO_NUM       34\r\n#define Y7_GPIO_NUM       39\r\n#define Y6_GPIO_NUM       36\r\n#define Y5_GPIO_NUM       21\r\n#define Y4_GPIO_NUM       19\r\n#define Y3_GPIO_NUM       18\r\n#define Y2_GPIO_NUM        5\r\n#define VSYNC_GPIO_NUM    25\r\n#define HREF_GPIO_NUM     23\r\n#define PCLK_GPIO_NUM     22\r\n\r\n\r\n\r\n\/\/ Variables and Constants\r\n\r\n\r\nconst char* TIME_INPUT = \"inputTime\";\r\nconst char* TIME_COMMIT = \"inputTimeCommit\";\r\nconst char* SCAN_TIME = \"ScanTime\";\r\nconst char* SCAN_COMMIT = \"ScanCommit\";\r\n\r\n\/\/ Interval between sensor readings. Learn more about timers: https:\/\/RandomNerdTutorials.com\/esp32-pir-motion-sensor-interrupts-timers\/\r\nunsigned long previousMillis = 0;     \r\nconst long interval = 1000;  \r\n\r\nunsigned long ScanSeconds = 1800;\r\nunsigned long ScanMillis = 0;    \r\nunsigned long previousScanMillis = 0;     \r\nconst long Scaninterval = 1000;  \r\n\r\nconst char* FileName = \"\/datalogger.csv\";\r\n\r\n\/\/ Set your new MAC Address\r\n\/\/ Example mac address A1:B2:C3:D4:E5:F6\r\nuint8_t newMACAddress[] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6};\r\n\r\n\/\/ REPLACE WITH YOUR NETWORK CREDENTIALS\r\nconst char* ssid     = \"ESP32-Cam-AP1\";\r\n\r\n\/\/ Default Value\r\n\/\/ Thu May 26 2022 13:01:55 GMT+0000 https:\/\/unixtime.org\/\r\nint epoch = 1653570115;\r\nString inputTime = \"1653570115\";\r\nString CurrentTime;\r\nString EpochValue;\r\nString inputTimeCommit;\r\nString ScanCommit;\r\nString ScanTime;\r\nString ScanEnabled;\r\nbool DisableAP = 0;\r\nbool EnableAP = 0;\r\n\r\n\/\/ int epochInt = inputTime.toInt(); - Example of converting string to int\r\n\/\/ String epochString = String(epoch); - Example of converting long to string\r\n\r\n\/\/ HTML Code - This should remain in the Constants and Variables section of the code\r\n\/\/ HTML web page to handle 3 input fields (email_input, enable_email_input, inputTime)\r\nconst char index_html[] PROGMEM = R\"rawliteral(\r\n\r\n\r\n&lt;!DOCTYPE HTML&gt;&lt;html&gt;\r\n&lt;head&gt;\r\n  &lt;title&gt;Headend Controller&lt;\/title&gt;\r\n  &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\r\n  &lt;link rel=\"icon\" href=\"data:,\"&gt;\r\n\r\n\r\n  &lt;style&gt;\r\n  html {\r\n    font-family: Arial, Helvetica, sans-serif; \r\n    text-align: center;\r\n  }\r\n  h1 {\r\n    font-size: 1.8rem;\r\n    color: rgba(238,238,238,1);\r\n  }\r\n  h2{\r\n    font-size: 1.5rem;\r\n    font-weight: bold;\r\n    color: rgba(238,238,238,1);\r\n  }\r\n  .topnav {\r\n    overflow: hidden;\r\n    background-color: green;\r\n  }\r\n  body {\r\n    margin: 0;\r\n    background-color: rgba(71,71,71,1);\r\n  }\r\n  .content {\r\n    padding: 30px;\r\n    max-width: 600px;\r\n    margin: 0 auto;\r\n  }\r\n  .card {\r\n    background-color: rgba(51,51,51,1);\r\n    border: 2px solid rgba(85,85,85,1);\r\n    padding-top:10px;\r\n    padding-bottom:20px;\r\n  }\r\n  .button {\r\n    padding: 15px 50px;\r\n    font-size: 24px;\r\n    text-align: center;\r\n    outline: none;\r\n    color: #fff;\r\n    background-color: grey;\r\n    border: none;\r\n    border-radius: 5px;\r\n    -webkit-touch-callout: none;\r\n    -webkit-user-select: none;\r\n    -khtml-user-select: none;\r\n    -moz-user-select: none;\r\n    -ms-user-select: none;\r\n    user-select: none;\r\n    -webkit-tap-highlight-color: rgba(0,0,0,0);\r\n   }\r\n   \/*.button:hover {background-color: green}*\/\r\n   .button:active {\r\n     background-color: green;\r\n     box-shadow: 2 2px #CDCDCD;\r\n     transform: translateY(2px);\r\n   }\r\n   .state {\r\n     font-size: 1.5rem;\r\n     color:rgba(238,238,238,1);\r\n     font-weight: bold;\r\n   }\r\n   .timeinfo {\r\n     font-size: 1rem;\r\n     color:rgba(238,238,238,1);\r\n     font-weight: bold;\r\n   }\r\n  &lt;\/style&gt;\r\n&lt;title&gt;Headend Controller&lt;\/title&gt;\r\n&lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\r\n&lt;link rel=\"icon\" href=\"data:,\"&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n\r\n  &lt;div class=\"topnav\"&gt;\r\n    &lt;h1&gt;&lt;img src=\"data:image\/jpg;base64,\/9j\/4AAQSkZJRgABAQEASABIAAD\/4SmGRXhpZgAASUkqAAgAAAAKAA8BAgAGAAAAhgAAABABAgAKAAAAjAAAABIBAwABAAAAAQAAABoBBQABAAAAlgAAABsBBQABAAAAngAAACgBAwABAAAAAgAAADEBAgANAAAApgAAADIBAgAUAAAAtAAAABMCAwABAAAAAQAAAGmHBAABAAAAyAAAAJYGAABBcHBsZQBpUGhvbmUgNnMASAAAAAEAAABIAAAAAQAAAEdJTVAgMi4xMC4xOAAAMjAyMjowNToyNSAwNjoyNTo1NwAgAJqCBQABAAAATgIAAJ2CBQABAAAAVgIAACKIAwABAAAAAgAAACeIAwABAAAAQAAAAACQBwAEAAAAMDIyMQOQAgAUAAAAXgIAAASQAgAUAAAAcgIAAAGRBwAEAAAAAQIDAAGSCgABAAAAhgIAAAKSBQABAAAAjgIAAAOSCgABAAAAlgIAAASSCgABAAAAngIAAAeSAwABAAAAAwAAAAmSAwABAAAAGAAAAAqSBQABAAAApgIAABSSAwAEAAAArgIAAHySBwCWAwAAtgIAAJGSAgAEAAAANTQ3AJKSAgAEAAAANTQ3AACgBwAEAAAAMDEwMAGgAwABAAAAAQAAAAKgBAABAAAAwA8AAAOgBAABAAAA0AsAABeiAwABAAAAAgAAAAGjBwABAAAAAQAAAAKkAwABAAAAAAAAAAOkAwABAAAAAAAAAAWkAwABAAAAHQAAAAakAwABAAAAAAAAADKkBQAEAAAATAYAADOkAgAGAAAAbAYAADSkAgAjAAAAcgYAAAAAAAABAAAAHgAAAAsAAAAFAAAAMjAxOTowNDoyNiAxNTo1NjozNQAyMDE5OjA0OjI2IDE1OjU2OjM1AFxKAQBTQwAAlfQCAJBMAQAnXgAA2h0AAAAAAAABAAAAUwAAABQAAABmCigH8QL0AkFwcGxlIGlPUwAAAU1NABEAAQAJAAAAAQAAAAoAAgAHAAACLgAAAOAAAwAHAAAAaAAAAw4ABAAJAAAAAQAAAAEABQAJAAAAAQAAAIkABgAJAAAAAQAAAJgABwAJAAAAAQAAAAEACAAKAAAAAwAAA3YACQAJAAAAAQAAERMADgAJAAAAAQAAAAAAFAAJAAAAAQAAAAQAFwAJAAAAAQAAAAAAGQAJAAAAAQAAAAAAHwAJAAAAAQAAAAAAJQAJAAAAAQAAAAAAJgAJAAAAAQAAAAAAJwAKAAAAAQAAA44AAAAAYnBsaXN0MDBPEQIA+QP6A\/4CSQOiA9sC+gP6AzID+gOgA2MBhAByAFQAWgD2AjwDCAPzAokDZQKKA9ICLQLfAgsCcABXAE0AOQBAAEACRgJJAlMC1QLlAW4BEwHwABMBSQHOAKwAZQBEAD0AIgIrAiMCLwLaAS4BIgHzAAIBHAEVAQABmwERAvcBxQEEAjQCGwIjAgMBxgABAfoABAGvAEoAZwDBAO0BTwJZAucAKAIqAg8C6wB8AM4A3QDJAM4AyACvAMgAJAEvAjgCpgHyAQYC4gHJAL8AlQCbALMApQCiAKYAbwCCABcCDwLiAfkB\/AHWAasAuQBRADkAYQCTAIYAfgB6AEkA5wEkAt4B+AH\/AfwBlgDHAKkAkACJAH0AdQBoAGUAfAC5AQkCxAH1AQcCDgLxAJQAsACAAGQAWQBQAEwASQBiAMEB+AHTAfoBCgLqAV0BjACAAHUAaQBHAEEAWQBRAMkA5gH7Ae0B+QH5AbgBeQEhAZYAYABkAGAAXABVAJsA0AECAgkCCQINAukBoAFRAS8B5wB8AFAASQBQAHwA2wDsATECMgIdAg4C4gGmAXsBPQEMAewAsgCAAHIAnQDmAC0CgAJfAicCEQLnAaIBdQFGASMBBAHdAMwAugC\/AE4BlwLPAp8C\/gH6Ad4BowF5AVYBKwEKAeoA3wDWAOsArAFSArQCiwIACAAAAAAAAAIBAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAIMYnBsaXN0MDDUAQIDBAUGBwhVZmxhZ3NVdmFsdWVZdGltZXNjYWxlVWVwb2NoEAETAAACOXHTLQoSO5rKABAACBEXHSctLzg9AAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAAAAAAAD\/\/\/7VSAABgR\/\/\/\/ZYAAgPn\/\/8rEQABTr4AAAAAAAAAAVMAAAAUAAAAUwAAABQAAAALAAAABQAAAAsAAAAFAAAAQXBwbGUAaVBob25lIDZzIGJhY2sgY2FtZXJhIDQuMTVtbSBmLzIuMgAACAAAAQQAAQAAAAABAAABAQQAAQAAAMwAAAACAQMAAwAAAPwGAAADAQMAAQAAAAYAAAAGAQMAAQAAAAYAAAAVAQMAAQAAAAMAAAABAgQAAQAAAAIHAAACAgQAAQAAAHwiAAAAAAAACAAIAAgA\/9j\/4AAQSkZJRgABAQAAAQABAAD\/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL\/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL\/wAARCADMAQADASIAAhEBAxEB\/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL\/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6\/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL\/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6\/9oADAMBAAIRAxEAPwDS8A+AdK8VaFPfX1xeRyx3LQgQOoXAVT3U8\/Ma6r\/hTfh7\/n81T\/v7H\/8AEUfBv\/kULv8A6\/3\/APRcdeh1xYfD0pUotxPps3zfHUcdUp06jST0R55\/wpvw9\/z+ap\/39j\/+Io\/4U34e\/wCfzVP+\/sf\/AMRXodFbfVqP8p539uZj\/wA\/Weef8Kb8Pf8AP5qn\/f2P\/wCIo\/4U34e\/5\/NU\/wC\/sf8A8RXodFH1aj\/KH9uZj\/z9Z55\/wpvw9\/z+ap\/39j\/+Io\/4U34e\/wCfzVP+\/sf\/AMRXodVp7+2t8CSZAT23DNH1Wj\/KH9uZj\/z9Zwv\/AApvw9\/z+ap\/39j\/APiKP+FN+Hv+fzVP+\/sf\/wARXT3HiCAKfLZgfov+NZdxrRk\/5aHp6LT+qUv5Rf27mH\/P1mS\/wf8ADiAk3mq8eksf\/wARWdc\/Dnwpa533OtHBx8rxf\/E1ryXocklv5VEbwYxu4\/Cn9Uo\/yh\/buY\/8\/Wc3L4S8GxMQZte4\/wBqH\/CoT4Z8GA\/63Xv++of8K6c3I9f5U1p1YYJ4o+qUf5Q\/t3Mf+fzOdh8LeC5pCol18YGeWh\/wrVsfhx4Uv4o5IrnWgJDgbni9cf3andYHOWXP41C1jYOPmhz6fOev50fVKP8AKH9u5j\/z+ZfPwi8NgE\/bNV4Gf9bH\/wDEU2P4R+G5Ol3q34yR\/wDxFZwtEhYNbbUYdCWJqxHd6nEwzdIYwc7Qozjv2pfVKP8AKH9u5j\/z9Zf\/AOFN+Hv+fzVP+\/sf\/wARR\/wpvw9\/z+ap\/wB\/Y\/8A4ipE164CgRO6OByzIuKlh8R6jFLma53x\/wB1I1z\/ACpfVaX8o\/7czH\/n8yt\/wpvw9\/z+ap\/39j\/+Io\/4U34e\/wCfzVP+\/sf\/AMRXSWfivT50AlMkTj5WMu1QSB169K14r6znUtDdQSKDglJARn8KPq1H+UP7czH\/AJ+s4T\/hTfh7\/n81T\/v7H\/8AEUf8Kb8Pf8\/mqf8Af2P\/AOIr0Oij6tR\/lD+3Mx\/5+s88\/wCFN+Hv+fzVP+\/sf\/xFH\/Cm\/D3\/AD+ap\/39j\/8AiK9Doo+rUf5Q\/tzMf+frPPP+FN+Hv+fzVP8Av7H\/APEUf8Kb8Pf8\/mqf9\/Y\/\/iK9Doo+rUf5Q\/tzMf8An6zzsfB3w6wyLzVP+\/sf\/wARS\/8ACm\/D3\/P5qn\/f2P8A+Ir0Oij6tR\/lD+3Mx\/5+s88\/4U34e\/5\/NU\/7+x\/\/ABFcD8QPCVh4UuLOOxmuZBNv3eeynGAuMYA\/vGvoGvHfjN\/x+6Z\/21\/lHXNi6FOFJuK1PZyDNMZiMfCnVqNxd9Pkzf8Ag3\/yKF3\/ANf7\/wDouOvQ688+Df8AyKF3\/wBf7\/8AouOvQ66cN\/BieNnn\/Ixq+oUUUhIAJJwB1NbnlC1Uv9RhsIGd3jLgAiMuFJBOKxfEHiNLS3MNttllYkExTYZMEen41xN5qVxPL5k0kr5GfnkJ289OaSd3oNq250t14qunbMJEYx0yrf0rIuNUnuHDSuCwGM8D+lZPnZ6N+tAkHfn8a0SJbLjXreoP4iozdsaqsAeQfwFVbq6FrGWYdieWx0piNE3TUxr0L1ZR9SK5C818sxCZQAdpfasibWZif9a5Gf8AnqaLgegtqkK9Zoh9XFRNrcC\/8tYj\/wBtBXm8mpSOTkt\/33UDXch53t\/31SuFj03+3oP70X\/f0VImt2zHBlhH\/bUV5YbqQj\/WOP8AgVOF5Ip5Zj\/wKi4WPWE1O1fpcwf9\/BUy3UTj5Zkb6MDXkqak6sMBv++6vQapdnHl+eATxtc0XA9O809jSeafWuM0+\/v3kCkXLDHdmPatlb+eMDfbSEY6kn\/CgDWcq+cj9adHe31nD5dldeSC24\/u1bP5\/QVnJfxPjc6I390uM1MJAejA\/Q0rDO70fxxHqOqy2l1ZrYwpFvW4ln+VjkfKMqBnk9+1dRb3MF5As9rPHPC2dskThlODg4I46ivGywPXBrU8OahLYalbxfa3jstwXyfMKxoCwJOOnr+ZpMZ6rRUcM0VxGJIZUkjPRkYEH8RUmc0gCiiigAooooAK8d+M3\/H7pn\/bX+UdexV478Zv+P3TP+2v8o65cb\/BZ7\/DP\/Iyh6P8mb\/wb\/5FC7\/6\/wB\/\/Rcdeh1558G\/+RQu\/wDr\/f8A9Fx16HV4b+DE5c8\/5GNX1CuX8QeIYoUe2tnZ5CJI5CpK+Wenpz36elbWq30dnYylpNkjRvs4PUD2\/CvKb7UA87s7\/MzMScHk5rV+87HmLTUdJNlizMWYnJYnmoHkDZzzVcz7uQ1MMh9a0WhDFIdDuUlgO2cU5LhX6Gmb6jZM8rlfpxVCLYk9zSP5coxLGrjp8wzVNZmU4c4qUSZ6GgCneaHb3e7Z5cOQR8sQ44rIfwac\/LfZ\/wC2P\/2VdKH96duz3pWC5y48Fuf+X7\/yF\/8AZU9fA5PXUMf9sf8A7KunVznrUo+YcE5osguc\/F4RgjB3XKP9YP8A69XY9AskPzRQP9YBWgWZfvcUb\/eiwEKaTpq9bC1P\/bFf8KkFjYr9yzt1A6YiA\/pTvM96Qygd6AHrHFGcpEi\/RQKViGGCARVZ7qNer\/oahbUIB1k\/8dNAE0ttC+SEQN\/e2jNUjbXMBykzyZ7bsf1qb+0ID\/y0\/Q04XEb9Gz+BoGQDVBHxcR+WR\/tbv5CrUdykgBRsj8aheKGT70aH3K1SltpIcvbvISP4S3FIDq9L8QXOnyRDzpjCjhvKEpCkZyRj3r0vSNXtdWhMlvJlwqtImD8hPbJAz0PT0rweHUGUhLghJM4wAT\/ntW3pmt3mmzpLbTuq7lZl3MFcA9CAeRUNa3KTPb6KydC1m21i0DwzB5lRDOoVgEYjoM9sg+vStahaiYUUUUwCvHfjN\/x+6Z\/21\/lHXsVeO\/Gb\/j90z\/tr\/KOuXG\/wWe\/wz\/yMoej\/ACZv\/Bv\/AJFC7\/6\/3\/8ARcdeh1558G\/+RQu\/+v8Af\/0XHXe3kzW9jcTqAWjjZwD0yBmrw38GPoc2ef8AIxq+pw3jXUWfUBaHZtgBxwc\/MqnmuD1HmBn9FY\/pWpr+oSX2rzXEiqHbbkKOOFA9fas5\/wB7bSL6oR+lbw2ueVLexy8GrLFOVYoDnA+U1tW9\/HKiksMn0BrhdURoLuVTggyMOPrUFvqElr9xVK5zyOf507iPSw2acGrk9P19XIAjPU\/w+31roLe7SYHaCMeoqkyS8cMMGoWUxnKjI96UNTt2aYCJMDwTzUoaoHQP61FvaHrgr+tAF3dSh8VVSYOMgGnb6ALyXBAxx+VDxoykxklvSqO+nCQjnFAGbq+pS2IZdqZBA+YE9Rnsa5m51iSYkuIxx2B\/xru5JFnhaGQEKwIJXrzXOah4Oiu3ea0mcORyJWGM4wOi+1J3A5d75CfvD8jURvFzwR+Ro1LSLjTD+\/eJuSPkJPQ+4HrWaXxUFGl9s57fkaet8B3X8jWT5nPSlEntQB0lvrLIPk8s8d1NblpryyFVmZFz6K3rXAb81NDcNGRgDFFwPSy0N5EdrkqQRkDH86pHzrNvlQeXnktzwK5ex1Aq2VUZBB5H\/wBeujtdWWYBHQgnA4H\/ANemB0fh\/XDpuoxXsPlsUBPzqSOVI7fWva9Kvl1HTLe6BBaSNWbaCAGKgkDP1r5ymh+zMbiM5ySSG9\/\/ANddj4L8atowW2lgDWTSNJKUTMmSgHy\/MBjIHX3qLWdx3urHtFFFIPeqELXjvxm\/4\/dM\/wC2v8o69irx34zf8fumf9tf5R1y43+Cz3+Gf+RlD0f5M3\/g3\/yKF3\/1\/v8A+i467PXZUh0G\/Z3Vc28gXJxk7TwPeuM+Df8AyKF3\/wBf7\/8AouOtzx5KYvDhwcbpNv8A441Og7UInPnSvmVX1PKJ5d8pYnrSQyANjPXFVnfmmiQg5rpR5D3MnxRpjPGs8SZwWLYyepFcXIGRyCMEGvVD5V3CYpcYIHf\/AD6Vzd\/4SSW4eSK5KqxJ2iMnGT65ptCOOyD061o6fq7W5YSuSD2AHtWqvg1iebwr\/wBsf\/r1Yg8EQRkmW+Ent5e3\/wBmosxmpZ3vmor5LIehAGOtaCyqw4NRQ2Fpa2ywxsoVc4G78fWkIji+4yn8aokshqUnI5qotwM4JA\/GpfMyM8UwGyw5JZcZqISvEcPkgegqbzKY+HHNAD1nVujD86dvHrVFoinMZx+GaRLk5w4x7k0gL\/mD1oEuO9VRID0I\/OjzKAL4ujjazZXuOKr3FjpN8hW6ty4PJ+cj+RqDfSeZQBWl8J6PJnyYI0z03TP\/AI1Rk8FRsf3cloPrM1a\/mmkM7r0NKwzmW8B6jn5buwx\/11b\/AOJoXwLqYYE3dhj\/AK6N\/wDE10f22QfxD9KPtsp\/i\/QUrAZlr4QMRQzyWrFTkkSt6\/SuigtbO1i2jZ90A4cnp+NZ\/wBrkPU\/yppnY96BluZouidPrWXOjQzedH93qQOSSaseYTTWbIwaQHrfw98X\/wBrRT2d\/c77pWaUSEIq7PkGOMc5J7V31fNOkarNoF68sB4eMqScdyPUH0r6QtrmO6jLxsrAHGVbNSt7FPuSg56GvHvjN\/x+6Z\/21\/lHXsIAUcV478ZebzTP+2v8o65sb\/BZ7vDP\/Izh8\/yZ0Hwb\/wCRQu\/+v9\/\/AEXHWt8RDjw2v\/Xcf+gPWT8G\/wDkULv\/AK\/3\/wDRcdbHxCTf4a\/3Zc\/+OPTou1BHPnKvmVX1PHWbmm7qY55pm6upHkMnSRgeDilN1Mpxu\/QVAGqQFZFx3qhAbyb+9+gphupT1b9BUbqU6iq0twkWC7YoAtmZj1NN3n1rJfWrNDgzf+Ot\/hUZ16y\/56\/o3+FFwNktSrcspweRWMNdsz\/y1\/Rv8KmTUraX7smfwP8AhRcDaWYNzkfnS+Z71lLOOqsSPyqdLlSMHrTuBe35qKSNXHvTPMo30CIyXiPBJHpilWfd1wD6ZpSwIxUMkeeRwaALHmUm+qXmNH97JHrmnLOrdDSGW\/MpRIO\/86q+YKQvQBbMKSDKuF9utVmMkZ+ZWA9SMU0SEdyPxqwtyjLtkG7PduaQEHnKBksB+NRPexL\/AMtE\/wC+hUOpWU0gMtq5ZQpJUfLjj6\/WuXkndXZGJ3AkH5u9S3YpI6n+04weqf8AfYqRL+J8fMg\/4GK47zm\/vH86kS4Zccn86nmHY7CVVnUAMOD25r1z4WeJrjVIrmxvGDzKzTBsgHb8gxtAHcnmvCLLUSmdxZh7sa7zwDqi6d4gnuFJ2NasnBI53qf6VMpW1KjG+h9Bnoa8c+MnN5pnGP8AW\/yjr2OvHfjN\/wAfumf9tf5R1jjf4LPb4Z\/5GUPn+TN\/4N\/8ihd\/9f7\/APouOtX4iuY\/DGR3lwf++HrK+Df\/ACKF3\/1\/v\/6LjrU+JCs3hY7RnEuT\/wB8PVUP4COfOv8AkZVfU8XL7uabuqpHcYupYmPTaB+IqcmuhHksk3Uocg9ai3UZqriLgZZRggZ+lcz4kt7qPy2haQLwDh8c810MsM9q2JF2nGev+FOKQ3ibZUVsHOCuf5\/WjcR5e8jnqzH6mhSxHU\/nW9rOhG1LyRRt5YI6svpWIqkduKTGIGYdzT1uJV6SOPoxp6he9BRfSgCSPUbmPGJnI9Cx\/wAa0bXWn4ErKPfB9KyCi+ppoX5qQHb296sgOHyPoatCTI61xNvc3MGdrEg+rH\/Gt+01DzQBuGe4wfWqTA191JuqBZg3el3e9MQ9wGHNVZEK8qT+dSl\/ems2etAEInYcMeal8w+tQuoOcdagLuh68e5pAXTJ70eYfWqomB70vmUhltbh14zkdwehqvd6XaX0ZdF2TAEgRgLuY+vHrTN59aVZmQ5DEUAcxd289nKUlTbyQOQen0qNXyOtddLDbajH5c6KG7Oq\/N6nkj2rmtQsJLK5YBT5XVSSDkZIHSpaKTC3kIJGeK6rw7cSC4kVXIbaTkE56iuNViOc1uWEzQysyuVJXGQfpUMpM+wa8c+M3\/H7pn\/bX+Udeu2s8VzbpLC5eNs4YgjPOO9eR\/Gb\/j90z\/tr\/KOscY70G\/Q9vhnTM4fP8mb\/AMG\/+RQu\/wDr\/f8A9Fx11Hi2zW88M325iPJhklGD1IRv8a5f4N\/8ihd\/9f7\/APouOu21dGl0W+jVSzPbyKABycqarD29gr9jmzr\/AJGVX1PlzVswagsnXkH8gKuxSebErY6qDV7xPpsgu5Y2iKvGp+VgQRlQelczZXDW0pilJGWCjjpW0XdJnltWdjazg1f0mNZdQiDZ79Poazshhkcg0qSNG4ZThhVCN6aWTWblUUKqFdpzweMn3rJlja2kCtgkjPFaX9tb7V0iLiYn5TtHt\/8AXqXUrNpEWGFCWyGJ5IxyKSdgauZyyrOhRgRkHOKzbzw5DdyGRZJAx9WGOn0qWVWglKN94UqXTqMbjj6CruSY03hO5B\/dzRY\/2mP+FVm8N3S8eZD\/AN9H\/CumF+F+9u\/IU7+0Y\/Vv0o0A5X\/hHrn\/AJ6Q\/wDfR\/wpR4euv+ekP\/fR\/wAK6n+0YvU\/pTTqcXq36UWQHPR+HbrOfMhx\/vH\/AAq\/baK8T7pHXGP4T\/8AWq8+px\/7X5Cqz6jnoW\/IUaAOktNhyp\/M1E2U4OPwqGS+kbPzH8hVdpmY8nP4UXAtF6QvVQSEd6d5me9K4yctTCc1HvpN3vQA1lwcimiTnBpxaoWeM8b1z9aQE4ejdVYMR9KeJAe9K4ybfirK3CyQG3kB2N1K9eP\/ANVUN1ODUXAp3GkNbShd4IIz1\/8ArVq\/2U69HX8\/\/rU61uEXKy5K9cD1rbMqP8wYEdOtLQZ9DeHTnQrY\/wC9\/wChGvMfjN\/x+6Z\/21\/lHXqGgxmLRbdCQSN3I\/3jXl3xm\/4\/dM\/7a\/yjrkxP+7fce\/w5\/wAjWP8A29+TOg+Df\/IoXf8A1\/v\/AOi469CZQylT0Iwa89+Df\/IoXf8A1\/v\/AOi469DrfDfwYnHnn\/Ixq+p4v8Q7GS28TTzNEywT7fLYg4bCKDgnrzXnl9pRkfzIAoIJbkmve\/H+hf2tpq3IZgbKGaQKELbuAcdePu14m7vGxBByDgg9qpS5Xys85q6uilb+ase2RWBUAZK4B+lSE1LJKHXnH51ATV3JHJIUYMpwRW\/HqxuZNsDMjAZJcDGK5vNPineFyyHBIxSeoHQX9nBP5jRqPOxnfuODx2\/SuflR4JCj8MK2Yr0Om5MMccRBskf5\/rUU9p9rjadoXSUqeMEnPb+VClbcbjfYxiQ3XrUTAj6VJNG8D7XVlP8AtDFR7+x5FWQRk00mpGQHkH8KhbIJoAQnNMJqtPexwkAsmTzgsBS+ejDiVfzFS5JbjsTEijNV2kPZwfpTHllGNqls+gpc6CxZLUbsVRa4lX7ykfUUxr5kGSmR9aOZBY0g3vRmqEV+j4ztX6tVlZQ3RgfoadwsJdTGOMFTznFZQuHLZDc\/StSVRIuD65rHKmKUBgcAjOeKALkV2R8spJ\/AVaVgRlSCKpeWkyFkIUgcgHNLBKyPsPQkClcZfDU4GoSccinBs0rjsTBq0rOaR8oCT3xistTXSeEtOOpajLFhjtiLfKpPdR2+tZVJWRpBXZ9OW8UUECxwgCMZwAc968i+M3\/H7pn\/AG1\/lHXsKqEUKowBXj3xm\/4\/dM\/7a\/yjqcZpQfyPY4Z\/5GcPn+TN\/wCDf\/IoXf8A1\/v\/AOi469DxXnnwb\/5FC7\/6\/wB\/\/Rcdeh1phv4MTlzz\/kY1fUbJGksbRyKrIwKsrDIIPYivE\/HPhWXSb57qPaYbmWaQKqhRGoIOOvPB9ule3VR1bSrbV7Ca2niiLPE8aSPGHMZYYyM\/h+VaTjzLTc82ErPU+ZW4qMmuk8U+FL\/w7cn7Qn7iSRxDJlRvUEc4BOOo4PrXMt1rNMtoXNNJpCaQmruTYkjmaKQOpII9DitEamXtGHmusm0\/xHrWOTSbvei1xJ2HyTSStmR2c+rHNRk0hNMJqiR28g9TS71YcioiaTdTuFjP1HS2uHV4mGQMYx9fesc+fbna4bI\/2q6jfUE9rFMhGxAx77QaNGIwBeuvZv8AvqpF1Bh1LD23GrUmkNyUYN7bQP61VewmT70YH4ilyodxzXyvjcx\/Ek1DNOrIQvJppgK9QBR5Ixxj8qXKkFyIuTjAxx61ZglkQnBY+26mrByOhq1FZTuTsjz+IphctRXSyDHf0pZoVlU8AH1xVm30\/Y2ZMJx\/dBprxlWODkUmrDRmeTLC3yk49jiiNHaQMR3z1q8y56ikCgdAKkY4dBRjFApwGalspD0Ga9Y+Dmml9Wu7t41ePyHiwwB53RmuB8N+GtS8R3cltp0HmyIhkI3KvAIH8RH94V9GeFPDlr4dspYIJI5neQuZFgEZAIUY6n0qEnKSKbSizoK8d+M3\/H7pn\/bX+UdexV478Zv+P3TP+2v8o6Mb\/BZ7HDP\/ACMoej\/Jm\/8ABv8A5FC7\/wCv9\/8A0XHXodeefBv\/AJFC7\/6\/3\/8ARcdeh1eG\/gxOXPP+RjV9Qooorc8ozda0Kx1+xNrfRBh\/DIFUunIJ2kg4zgZryTXvhfq0d9L\/AGPZSz2+SVaSeIfxH3HbFe20VEoJ6lKTR8pz200H+sTbxnqDVYmvq68tY721e3kLBHxkqeeDn+lMsdPi0+No4mdgTn5yD6e3tUqEkU5I+UiaaT719W6ppkOr2ElncNIscgIJjIB5BHcH1ryjxd8JhCq3Wi3eQEd7gXsnYAbdu1P97Ofam00JNM8nLU3NO1GJ9Lv5bO4KtJHjJj5HIB749arrMr9AaSkNxJSaQ0zdSbqdxWHZpNxpuaaTTuKxIHp2Y2+8AfqKgzRmi4WJ\/Ktz1hjP\/ABSeVb\/APPGL\/vgVATSbqOYLFnZAP8AljF\/3xSmRF+6FX6DFVCabmjmDlLTXGf4j+tQtITUdJmpbHYUnJoFFSRRGWVI1IBZgoz71LZSQ0Ctzw94bv8AxBexQWluZA7MvEiqchd38Rru\/Cvwiurv7PfaheQi1ljWRRbynfhlJGcpjuP1r2LRNHg0PTUsrd5HRccyEE9AOwHpSUZS9B8yiUfDfhHS\/CsEiafEWkkYkzSqpkwQPl3AD5flBx61uqqr91QPoKdRW6SWxk3cK8d+M3\/H7pn\/AG1\/lHXsJGa8d+MoxeaYP+uv8o65cb\/BZ73DP\/Iyh8\/yZ0Hwb\/5FC7\/6\/wB\/\/Rcdeh1558G\/+RQu\/wDr\/f8A9Fx16HWmG\/gxOXPP+RjV9Qooorc8oKKKKACiiigAooooAp3ul2eoSQvdQ+Y0JJQ7iMHj0PsK4Xxp8KtN1rTrh9ItYLfVJH3rPPPLtBLgsccjkbu3evRs0UmkxptHyF4k0DUPCOqjTr2eCSUx+ZmAllxuK9wD\/CazEuxwG3E\/QV9oVzup+BfDestM1\/p3nGZt0n7+RcnOezDvUcnYrn7nyuHDdM0ua9e8R\/BJ5beP+wo7K3k3DcZJ5myMHPZvauCj+G3iw3b24sJQFGfMMEmw9OAdnXn9DU2aK0ZzucCkJrrR8L\/FPe1P\/fqT\/wCIpf8AhWHifvaH\/v1J\/wDEUagchRXRSeAvE8cjJ\/Y1820kbltZCD7j5apv4Q8Tq7L\/AMI5rBwcZFjJg\/8AjtAGRmiuh0zwH4o1K\/itP7GvrXzM\/vrq1kSNcAnltvHTH1Ir1rw78HdNtjINftba6znZ5NxMMdMf3f8Aa\/OhJsHZHglORC5wMV9NL8LPBajjRsf9vU3\/AMXXWwQR20KxRLtRegzn3p8jFzI8Z+GPw3sNT0dtX1u2huorgg2gSaRWj2M6vuAwOSFxyenavZba2is7SG1gTZDCixxrknCgYAyeegqWitIxsiG7hRRRTEFFFFABXjvxm\/4\/dM\/7a\/yjr2KvHfjN\/wAfumf9tf5R1y43+Cz3+Gf+RlD0f5M3\/g3\/AMihd\/8AX+\/\/AKLjr0Ovlqz1nVdOhMNjqV5axFtxSCdkUnpnAPXgflVj\/hKfEP8A0HtU\/wDAyT\/GuSljowgotbHvZhwvWxWJnXjUSUnfZn07RXzF\/wAJT4h\/6D2qf+Bkn+NH\/CU+If8AoPap\/wCBkn+Naf2jH+U4\/wDU6v8A8\/V9zPp2ivmL\/hKfEP8A0HtU\/wDAyT\/Gj\/hKfEP\/AEHtU\/8AAyT\/ABo\/tGP8of6nV\/8An6vuZ9O0V8xf8JT4h\/6D2qf+Bkn+NH\/CU+If+g9qn\/gZJ\/jR\/aMf5Q\/1Or\/8\/V9zPp2ivmL\/AISnxD\/0HtU\/8DJP8aP+Ep8Q\/wDQe1T\/AMDJP8aP7Rj\/ACh\/qdX\/AOfq+5n07RXzF\/wlPiH\/AKD2qf8AgZJ\/jR\/wlPiH\/oPap\/4GSf40f2jH+UP9Tq\/\/AD9X3M+naK+Yv+Ep8Q\/9B7VP\/AyT\/Gj\/AISnxD\/0HtU\/8DJP8aP7Rj\/KH+p1f\/n6vuZ9O0V8xf8ACU+If+g9qn\/gZJ\/jR\/wlPiH\/AKD2qf8AgZJ\/jR\/aMf5Q\/wBTq\/8Az9X3M+naK+Yv+Ep8Q\/8AQe1T\/wADJP8AGj\/hKfEP\/Qe1T\/wMk\/xo\/tGP8of6nV\/+fq+5n07RXzF\/wlPiH\/oPap\/4GSf40f8ACU+If+g9qn\/gZJ\/jR\/aMf5Q\/1Or\/APP1fcz6do718xf8JT4h\/wCg9qn\/AIGSf40f8JT4h\/6D2qf+Bkn+NH9ox\/lD\/U6v\/wA\/V9zPp3vRXzF\/wlPiH\/oPap\/4GSf40f8ACU+If+g9qn\/gZJ\/jR\/aMf5Q\/1Or\/APP1fcz6dor5i\/4SnxD\/ANB7VP8AwMk\/xo\/4SnxD\/wBB7VP\/AAMk\/wAaP7Rj\/KH+p1f\/AJ+r7mfTtFfMX\/CU+If+g9qn\/gZJ\/jR\/wlPiH\/oPap\/4GSf40f2jH+UP9Tq\/\/P1fcz6dor5i\/wCEp8Q\/9B7VP\/AyT\/Gj\/hKfEP8A0HtU\/wDAyT\/Gj+0Y\/wAof6nV\/wDn6vuZ9O1478Zv+P3TP+2v8o64T\/hKfEP\/AEHtU\/8AAyT\/ABqne6nf6kytfX1zdFM7TPKz4z1xk+w\/Ksa+MjVpuCR6WU8N1cDio15TTSv36qx\/\/9n\/4gKwSUNDX1BST0ZJTEUAAQEAAAKgbGNtcwQwAABtbnRyUkdCIFhZWiAH5gAFABkADQAUAAphY3NwQVBQTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVFJDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7\/\/\/MlAAAHkwAA\/ZD\/\/\/uh\/\/\/9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AAJmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEL\/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz\/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz\/wgARCABQAGQDAREAAhEBAxEB\/8QAHAAAAQUBAQEAAAAAAAAAAAAACAMEBQYHAAIB\/8QAHAEAAgMBAQEBAAAAAAAAAAAAAQIAAwYIBAUH\/9oADAMBAAIQAxAAAAEqvl7uz+n4jKSmOlUYQ5kkpnVN1Vp+TpOkDTHdIGXseb4u\/wCEPvqpjXXNVMSJPSXYzlbcKX3GDjAWw\/UpW6vnsQPdn2VtUm4VgdyQCnJ639CWeEoqnJF1BbD9S7lqefRd+lm8gE2C1H5DWT0JFK1bUqAzak4EYYcV1HrWm5\/Gb6eZRcUFStJa3VQhkp9AslOdee4pVZbIdPa3qefRH9ueshGaulYUuJL04aCRylJS1VtJou0rJdOk\/qefQE9Hwl2V8JV7qo0GKrZqQqZXUMyj7xTdbMt00Xmt56wZfkiQw9sIm2qMK1dIxkkmLimxxVadElFy3TRea3nq2+vP5vRbQmAn025RB5I+EJI1xRzFdSd9nmBfD9SkrpPxWz+n4nSdJ0mfI2dI0fDo7LptiepOkDTHdIf\/xAAmEAAABgIBBAIDAQAAAAAAAAAAAgMEBQYBBwgRExY2EhQQFSEj\/9oACAEBAAEFAqTSYZ1TPAYIPKlW48jzNZSDv9UsDMmfVJRkmEX0AIiIqs4bwGCHgMEPAYIb9imsPd6D6Lc7riAxIWBZ66cOTGSkVbCooSJsS5mURPo5ameoo5mMtz0bZaeUfxyP9\/qr8sXrS63FRs7jrAhKp\/L4gjzrkrzGRYJpzFILbSdmyhtBfrG2ZtIjUmy8rOByP9\/m5D6GkJVmSdjUanMMnEeo4Qa5XIsQzkyIJIiQYMZXLmlw5wxrEUwVdqkVGqrka6VXkd7\/AHP+aQ7\/APXSnbRUvzYpkLw2OZnNJvSKnwbJn2U84f8AQSCh1EM2V2ktxrtRlLnyO9\/u7E7zQLV7h0gkyN9GZpJJBY9MflDGryTRXHeQSO5+WDqZIMPOofoJS5NBNXUTsjkf7\/Dw+LBqZxFZjZFjJ5YLSjYhymeKhaTVCrz+\/sCYB3P+aMmoYEX6jQWTvth8j\/f6D6LvDWSixzKBCUVZlOt1zIN05FJSLdt8qtXiwi41ykZyyx3EydC8bdduYZLkf7\/QfRRO6kr9icWrjpDSjG56ksFIa4dFUHd6D7RgZcxhSNfymwZDXPG5pXFxyO9\/pN2hmtM8+gh59BDz6CHnsELTFUO5ET1DrohHGlNfrGr1Z19XGJb3AkL59BDz6CG\/ZVrMXf8A\/8QAMREAAQICBwgCAgEFAAAAAAAAAQACAwUEERIWVJPRBgcQITVzsbIgMSVRYyIwMkFE\/9oACAEDAQE\/AdptppxCnFLhQqXEDREeAA91QFo8hzV7J3jIuY\/VXsneMi5j9Ve6d4yLmP1V7p5jIuY\/VXtnmMi5j9Ve2eYyLmP1V7Z3jIuY\/VXsneMi5j9Veyd4yLmP1V7J3jIuY\/VXsneMi5j9Vump9JpcldFpcRz3W3c3Ek\/Tf2trOt0zuxPcoVAVlHhyXL4Dnx3NdBd3HeGraoVzymd2J7lONZR+AVSq4fY4bmugu7jvDVtQPzdNP8kT3PCv+wE4Bbmugu7jvDVtOPzVO7kT3R4VKpVfAIAIgWStzXQXdx3hq2oP5um92J7lEKyq1Wq\/iEz+qsFbmugu7jvDVtQ6zPaYf5YnsU9haajwcz\/Y4VKyVUqkEAg2ppctzXQXdx3hq2s63TO7E9yob7QslFqrIRCqVZVpAoJoUZ4DbAW5roLu47w1bWdbpndie54CM76QiftCyfpFisqwg1GpvMox6v8AHhua6C7uO8NW02zM4izilxYVEiFpiPIIY6oi0eY5K6c7wcXLforpzvBxct+iunO8HFy36K6c7wcXLfohsvPR\/wAcXLforsT3Bxct+iuzPMFFy36I7MT3Bxct+iunO8HFy36K6c7wcXLforpzvBxct+i3TUCk0SSuhUuG5jrbuTgQfpv7X\/\/EADMRAAECAwQIBAUFAAAAAAAAAAEAAgMEERIUU5EGECExNHGx0QUVUXITJEFSYSAiQrLw\/9oACAECAQE\/AZKSlzLsJYNw+g9FcJbDbkFcJbDbkEPDZfDbkF5bK4YyC8tlcNuQXlsrhjII+HS2G3IK4S2GMgrhLYbcgrhLYbcgrhLYbcgvHoTIczZhimxSHDQ\/aOi\/cTZamimokratuop2zXpFxQ5d1JmkrD9o6JoshA\/oKtq0q1W46tIuKHLupQ\/LQh+B0qhtVCgSq6qohWQqBFNJ3LSLihy7qWPy8DkP6oarStoO1VVU78IlyBNsLSLihy7qUHy0LkOiBVvaqVVkoNOquqqKiVbRw9R1WkXFDl3Uq21KMH4CZEDhUI7U1\/0Kqi5fFb6q2FaRKJRfV7Wf7ZtWkXFDl3Uhw0P2joosOwbY3Jr0aFVVUYbShCA3ItTk5yl4bi\/4h3LSLihy7qQ4aH7R01GAytQnQvtRDm71bVpWyi9CrjQIStTV+rSLihy7qSnZcS7AXjcPqPRX+WxG5hX+WxG5hX+WxG5hX+WxG5hOm5Q\/zbmFeJTEGYRmJTEGYQmZP725hX+WxG5hX+WxG5hX+WxG5hePRWRJm1DNdi\/\/xAA\/EAABAgMDBA8GBQUAAAAAAAABAgMABBEFEiETIjFBEBQjMjVRYXF0kZShssHRBkJScoGzFTRikqJDgsLh8f\/aAAgBAQAGPwKyHXbIsxxxySZUpSpVBKjcGJwjgWyeyN+kVesqxm+eVbx7o3KxLKVymUb9IzLLsln5ZFrzSYzJazfrZ8uf8IzrF9nnR0FKD5wMp7N2VTXdl269VPOLsvZdkFwCpQZNAUO7ljgWyeyN+kcC2T2Rv0jgWyeyN+kNtSksxKtbVQq4y2EJreVjQRYvQGPtiMgxdVMqGvQ3\/uFOPuFZX70KyZF\/Ve0QQhsXf0LTGJUj5nhGM8wkcq1HyjdX2nV\/JSEl1KmighSVjEA8ddUNyk+6pStDb6jUEfqPn17LfQ0eJUWZMLpdZs1pWmldzGECadz0zCze5DFW1hXJGb1RxGMe6MoxLmZRrUn3fpGAbH0jdG21DmirKtruH3TvVQmyJ9V1WhhSj\/Hl5OrYb6GjxKizBrfk5ZvmzEnyhyWUbqjig8RirTJCgdOUSAe+EbZU1lKZ11VabHxp74qkxWZlGXFH3t6rrEbybZ+R31EBY226R8bgp3CBkxkije01Qh16m25dWRf5Tx\/UQ30NHiVFhmtKMy32tguY3Uip5IoEuq+kaVt84iqFg80VGBjP645IJlSA78B180EKUQoaUkQpitEzjBSpOoqTnA+LrhvoaPEqLOU3pl5SWc\/gB5wFjXp5IS83VZxKhqA0U54L0kUIUd82Y\/LdShF5Dakf3CBlbtaY0Oxm9WxRzNdG9c9YsxwpzS6po46ikiG+ho8SokJPCr9mNJSToCsmKd8PAbniQ40rApVFd8n4a01UjLS2sVKR5RmuK64xdV1xnKMb8QSnE0wiqxhyaRsSDRqptu+4eSiT50hvoaPEqLF6Ax9sQ5bNnt3sKzTaRj8\/r18ewoIWQFDjisXXK4aCNIjcnb474zkL\/ZGNUJ13oJGaTxa4A4octmcQWjNNXJdChiUHG99aYQ30NHiVFi9AY+2NgOv2c2lwHEsktX+e7\/2FfhuUs2aAzTfU42r5grHqMbYmpPLyoreelTlUt6cVawMNNKRgQdjTGmCxZzF\/J0LrqzdbaB1k+QxwMNzdruN2hNINUspG4JP132w30NHiVFkNO2vZjbjckylaVTSAUkIGBxjhqye1t+scNWT2tv1jhqye1t+scNWT2tv1he3n7AU6v+siZbbd\/cDWAPxdlVNZtRGMVT7RlrkTaTHmISyh32emSBi7NPtPOKPHU+VIAFs2QAMABNt4d8cNWT2tv1jhqye1t+sNuykyxNNbVQm+y4FprVWFRH\/\/xAAmEAEAAgEDAwUBAQEBAAAAAAABABEhMUFhEFFxgZGhscHwINHh\/9oACAEBAAE\/IUwee+6jZV36BaRqrKz4GTDEV7MRmnlrBnheM+kVKl2+mHwQb\/JuKr7I3YwvxQtOnDJjP+BgwKqbMRlbQC8Ht0Q6nOLkLopvw9fL3Ku31x9RaxeLc\/TM7ZCQD8j7y+l9\/wAKsPPYCvthMZ\/4Pc\/5HKNBRNgGUObxLuMDdBQal87u8t3qVyfFjoK7cuPWXx4wOSZh7W2SDzeO6FQ4NnWDVjzFZULuPrGfXSYXv97yt7jqn7HMlZzN+fEMSlZ\/e19ju\/4DoVxJhbf8Iz1gt8i7M0v1H9gJd00GU\/Zarvk1In8uyXdj2dIn1SEX9lfWBWZWy\/ZCRYsPqiWIf4XiLW30YpBK8g9rvTpRQhoPmMIAdTNxhLyqh+yyXvKPtPVM1yxHObypYdhozZIrUcjBJbdx5nfzEbbVAnFS5IKnsD7gDoorpXN\/\/eZwPB3bzN1lFrKctTXaZOrroviPVR5R+wO33cfrmD42Aw6mkYjaxHhMMUnozJx3H1AQijodma630KqVC4JYW1sAY8lqgoGmvXaDqp25Rarecw6xWgVFZRrUEexghi7ZnWeQ3L+n5ajNBkcpakS0sOBnXPmBYnS4vXbqFSNeXstrablfDkKnMrXaQR6+YyJVc3KX2VqpO8z8PSmotRy1bbMAZHhPbWKQcJdEVzsomq1UWBPsUdyZ0S+pVDcexvgWqBu9eGYNb5dL3DoswVd50irUFojQUVrWostHEF+cjrjEN03DKPrIBlHutRFA0y4HKVLRdW1yBwyulE8HlvtBsI7f5GDRlzPJdmrd3WWe9mvdiocKbXlrKOS1tTxlcQY2AjW5UF7EcQu30ADt1DBgpswGF9hLye8\/\/9oADAMBAAIAAwAAABAjd7PSBBd096oTr4kj4iUQEfdIxsia\/FUEC\/jjYzC\/7WJ2AuQPjqyD2DEqSGCB8GCL\/8QAJREBAAIBAgYCAwEAAAAAAAAAAQARIRAxIEFRYXGRscGh0fDx\/9oACAEDAQE\/EDAbhgMAIAFAYDBo5RLmXk8GTNmyPw5YuXLkcKLBQwWlotousujkbz6H77ePZGuhFxjQhMOfBbpX3Yy+iWz\/AHaB4BcNIlmTc\/vfz530t4s1X5w+4tREcuJVSpUFgsuJGyYCbOlMG9\/2wctTLWBAlSnOPxBE9k+Ph0BkH1+S+B0FpcKbwMRLvaVAlaQdiX0L9aUxU2rfCb+JyddYFNkwwqFtolynfLwNXLckqbkfOPvWm5Mt\/l37eenXbczXtADWgs2gUtdsphHaZcTnUvPauX9tXXbW25GKb85fe\/12jXzks0\/eIhGaQekARAUc9Xc8fvPamXpTcBuGhxBiI2JhMnC5cuXO3xKkqHCLbB8BwXLlyuFNgpIaA0001WGf\/8QAJBEAAgECBQUBAQAAAAAAAAAAAAERIfAxQVFhwRBxkaGxgdH\/2gAIAQIBAT8QYvtubcjcKuhaXAmOFdbGbsti7uC2uB5FtsJYWWw0Y32xaXBaXBaXAmIrFwkksXki06B5MWb07LXOtMKORCQToZHpgRqIO68V8u6VJ6ep+ideWlYQv10FJSvV\/opogz6O1kNjeQEONYP0\/wCfO2B6n6IIUz+MVvQkIYluggq9ElWIh4jCAVOg5LYr5d1PU\/Q5QLV8hzcpEghjaH0JQEniNNW6faJnyl5Z6H6GNqwUvr9aKYowY1qQ9ISwKrEaDegw6eI2yTyif09T9DU2M6yqkmp2lVE6lsQSCNimyGpS2MCdgyoRMMhoyScG1Z+Wz1P0W3QNbiqvZ5vs8Xo5blOkiEhnIVJF0ZVoHciTYkqMiRhAhTec9ttZ0ifU\/RadHRpER2ovGH7juIjIxFK+BIxaek3NjiXIbKWSwffVbU0cog9T9CF9pTTQ00lHUtLktLktLktLkxG63Eu+9jByyhu7u5LS5LS5LS5ExFYqU01i80f\/xAAmEAEAAgICAgEDBQEAAAAAAAABESEAMUFRYXGBEKGxIJHB0fHw\/9oACAEBAAE\/EHYEtJEqxVKqq\/R41m8QUb2TwC4evTCf8E\/xhuOoYjInkStSSYEre+MOr+lf7Z3xJBrE1Q2ju\/iCfgx1W7UomKEbJAmfq1ahhf5yD7sAEJKACxKDr6EXfAV+UTYWNKhUhidE08HhBAAoAAAEax01uxPJCD4clLzHrykuGTFGr319kwNp2XfufdgcYLJMdSCPmXrFSUHHoyAAQhBGTKmh6gL4gQJXaEOQyfVm5cLmGwFpgFNinWORVIJHSPbXijjB0MQ9jkT+MPs8mr4eMbM7VY\/v4yDCtTQns0\/9ebOUA+mY8opy4gtcR\/dWQRhyHHs\/g4XPMJlOEqT2C96wT10dqO6FTMiF5yTcc\/RmIDQnCyGOZgPbjIVO6\/Qnw6fbjUmICB5UGEj1o21Q+Eel27wkgRpIXw7HJIqXAg\/Jz+feACKrcCvyONN8YseVlezloRRK9f78sbQMeEnnvrDbaE1l17aTUejBygRK0aiQICEQQDnFxYpIebGPuvxjdkYcCSYkhBWi0jA4Q0SP7y+2GJO3+8pOHhbyIX\/uHHD3\/wBxzmrXX3V1kSdOEB5MGSk2vE6vCj2axe\/5URtIp+MkxnYC9BsnXthjlx9HPDajBFsrih9LuAiFw0Ph\/jJ5EnMUyHakC6PtQot+216B6a8mscNhUOfMmRM9mAZ0G5iKJQwHuP8AFx8lJ2Yyl8018dY0iUOOTHxvRItR\/wB\/DpeGSEsRJEKhsmFEr6MZ2WohZSZKgZBpxwEk9KTowRUa3UZfLs2YhjQFDE\/nFmiPgI2kA0kqLuZB3XO+T13jsMXYJesJS9Syq7w0qWNL74hgkEyKKMNhV9C+Tr2fOOaUAE0HnIMkkFmlwJZc9Q4MxE+LBApy2Is2wEQkskcDMUxPTo0KQFEgd4tEpJlXlnAHFDxoDh+cYq1oQeRT7LhSvKVYeYR1g83DoZuG29GB1Zfpdyecv4xJzWAu+4JjLiI5MB36LMiXbkQcgQqcslAqBhN8KoakLIBmgiMhHz8yGS6jcBIwmHhCx7N4qUj4cKqM7xG1PeCWNctsZtsA8CiDepiUaA6g5pK8CLBE9YYtXHCgbcgShEUCIj+hq0ahkKZViTT5dsizDSZCAhdylMESgEu6A6DFKGQhFrTtC57XBdOrkh3IjIdAgDXvA0IAEQCoPq1av7sAFJCAiTIDvP\/Z\"&gt;&lt;br&gt;Headend Controller&lt;\/h1&gt;\r\n  &lt;\/div&gt;\r\n\r\n  &lt;div class=\"content\"&gt;\r\n    &lt;div class=\"card\"&gt;\r\n    \r\n      &lt;p class=\"timeinfo\"&gt;&lt;form action=\"\/get\"&gt;&lt;\/p&gt;\r\n    \r\n      &lt;p class=\"state\"&gt;Current Time (GMT): &lt;span id=\"state\"&gt;%EPOCH%&lt;\/span&gt;&lt;\/p&gt;\r\n    \r\n      &lt;p class=\"timeinfo\"&gt;Enter New Unix Timestamp &lt;input type=\"number\" step=\"0.1\" name=\"inputTime\" value=\"%TIME%\" required&gt;&lt;\/p&gt;  \r\n      &lt;p class=\"timeinfo\"&gt;Entered Epoch: &lt;span id=\"state\"&gt;&amp;zwj;%TIME%&lt;\/span&gt;&lt;\/p&gt;    \r\n      &lt;p class=\"timeinfo\"&gt;Use New Timestamp &lt;input type=\"checkbox\" name=\"inputTimeCommit\" value=\"true\" %TIME_COMMIT%&gt;&lt;\/p&gt;\r\n    \r\n      &lt;p class=\"timeinfo\"&gt;Scan Duration (Minutes) &lt;input type=\"number\" step=\"0.1\" name=\"ScanTime\" value=\"%SCAN_TIME%\" max=\"120\"&gt;&lt;\/p&gt;\r\n      &lt;p class=\"timeinfo\"&gt;Entered Duration: &lt;span id=\"state\"&gt;&amp;zwj;%SCAN_TIME%&lt;\/span&gt;&lt;\/p&gt;    \r\n        \r\n      &lt;p class=\"timeinfo\"&gt;Start Wifi Scan &lt;input type=\"checkbox\" name=\"ScanCommit\" value=\"true\" %SCAN_COMMIT%&gt;&lt;\/p&gt;\r\n      &lt;p class=\"timeinfo\"&gt;&lt;input type=\"submit\" value=\"Submit\"&gt;&lt;\/p&gt;\r\n    \r\n      &lt;p class=\"timeinfo\"&gt;&lt;\/form&gt;&lt;\/p&gt;\r\n      &lt;p class=\"timeinfo\"&gt;&lt;iframe style=\"display:none\" name=\"hidden-form\"&gt;&lt;\/iframe&gt;&lt;\/p&gt;\r\n    \r\n      &lt;\/span&gt;&lt;\/p&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n  \r\n&lt;\/body&gt;&lt;\/html&gt;\r\n\r\n)rawliteral\";\r\n\r\n\r\n\/\/ Processor variable section to handle values from web form submitted by mobile device\r\nString processor(const String&amp; var){\r\n  if(var == \"EPOCH\"){\r\n    return EpochValue;\r\n  }\r\n  else if(var == \"TIME\"){\r\n    return inputTime;\r\n  }\r\n  else if(var == \"TIME_COMMIT\"){\r\n    return inputTimeCommit;\r\n  }\r\n  else if(var == \"SCAN_COMMIT\"){\r\n    return ScanCommit;\r\n  }\r\n  else if(var == \"SCAN_TIME\"){\r\n    return ScanTime;\r\n  }\r\n  return String();\r\n}\r\n\r\n\r\n\r\n\/\/ Routines and Subroutines\r\n\r\n\r\n\r\n\/\/ Start MicroSD Subroutine\r\nbool startMicroSD() {\r\n  Serial.print(\"Starting microSD... \");\r\n\r\n  \/\/ Pin 13 needs to be pulled-up\r\n  \/\/ https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-reference\/peripherals\/sd_pullup_requirements.html#pull-up-conflicts-on-gpio13\r\n  pinMode(13, OUTPUT);\r\n  digitalWrite(13, HIGH);\r\n\r\n  if(SD_MMC.begin(\"\/sdcard\", true)) {\r\n    Serial.println(\"OKAY\");\r\n    return true;\r\n  } else {\r\n    Serial.println(\"FAILED\");\r\n    return false;\r\n  }\r\n}\r\n\r\n\r\n\r\n\/\/ Start Camera Subroutine\r\nbool startCamera() {\r\n  \/\/ Turn off the flash - The flash still occurs when files are read or written to the microSD\r\n  pinMode(FLASH_PIN, OUTPUT);\r\n  digitalWrite(FLASH_PIN, LOW);\r\n\r\n  \/\/ Initialize the camera hardware\r\n  camera_config_t config;\r\n  config.ledc_channel = LEDC_CHANNEL_0;\r\n  config.ledc_timer = LEDC_TIMER_0;\r\n  config.pin_d0 = Y2_GPIO_NUM;\r\n  config.pin_d1 = Y3_GPIO_NUM;\r\n  config.pin_d2 = Y4_GPIO_NUM;\r\n  config.pin_d3 = Y5_GPIO_NUM;\r\n  config.pin_d4 = Y6_GPIO_NUM;\r\n  config.pin_d5 = Y7_GPIO_NUM;\r\n  config.pin_d6 = Y8_GPIO_NUM;\r\n  config.pin_d7 = Y9_GPIO_NUM;\r\n  config.pin_xclk = XCLK_GPIO_NUM;\r\n  config.pin_pclk = PCLK_GPIO_NUM;\r\n  config.pin_vsync = VSYNC_GPIO_NUM;\r\n  config.pin_href = HREF_GPIO_NUM;\r\n  config.pin_sscb_sda = SIOD_GPIO_NUM;\r\n  config.pin_sscb_scl = SIOC_GPIO_NUM;\r\n  config.pin_pwdn = PWDN_GPIO_NUM;\r\n  config.pin_reset = RESET_GPIO_NUM;\r\n  config.xclk_freq_hz = 20000000;\r\n  config.pixel_format = PIXFORMAT_JPEG;\r\n  \r\n  \/\/ Set resolution based on whether we have extra memory\r\n  if(psramFound()){\r\n    Serial.println(\"PSRAM found. Maximum XGA resolution supported.\");\r\n    config.frame_size = FRAMESIZE_XGA;\r\n    config.jpeg_quality = 10;\r\n    config.fb_count = 2;\r\n  } else {\r\n    Serial.println(\"PSRAM not found. Maximum SVGA resolution supported.\");\r\n    config.frame_size = FRAMESIZE_SVGA;\r\n    config.jpeg_quality = 12;\r\n    config.fb_count = 1;\r\n  }\r\n\r\n  \/\/ Start the camera\r\n  Serial.print(\"Starting camera... \");\r\n  esp_err_t err = esp_camera_init(&amp;config);\r\n  if (err != ESP_OK) {\r\n    Serial.println(\"FAILED\");\r\n    return false;\r\n  } else {\r\n    Serial.println(\"OKAY\");\r\n    return true;\r\n  }\r\n}\r\n\r\n\r\n\/\/Append to the end of file in SD card\r\nvoid appendFile(fs::FS &amp;fs, const char * path, const char * message){\r\n\r\n    File file = fs.open(path, FILE_APPEND);\r\n    if(!file){\r\n        return;\r\n    }\r\n    if(file.print(message)){\r\n    } else {\r\n    }\r\n}\r\n\r\n\/\/ Printout Header Routine\r\nvoid PrintHeader()\r\n{\r\n    appendFile(SD_MMC, FileName, \"WifiScan-Results\\n\");\r\n}\r\n\r\n\r\n\r\n\/\/ WebServer Fault Subroutine\r\nvoid notFound(AsyncWebServerRequest *request) {\r\n  request-&gt;send(404, \"text\/plain\", \"Not found\");\r\n} \r\n\r\n\r\n\/\/ Camera Operate and Image Save Subroutine\r\nvoid takePhoto(String filename) { \r\n  \/\/ Take a photo and get the data\r\n\r\n  camera_fb_t *fb = esp_camera_fb_get();\r\n  if (!fb) {\r\n    Serial.println(\"Unable to take a photo\");\r\n    return;\r\n  }\r\n\r\n  \/\/ Make sure it is a JPEG\r\n  if (fb-&gt;format != PIXFORMAT_JPEG) {\r\n     Serial.println(\"Capture format not JPEG\");\r\n     esp_camera_fb_return(fb); \/\/ Return the photo data\r\n     return;\r\n  }\r\n\r\n  \/\/ Save the picture to the SD card\r\n\r\n  File file = SD_MMC.open(filename.c_str(), \"w\");\r\n  if(file) {\r\n    Serial.println(\"Saving \" + filename);\r\n    file.write(fb-&gt;buf, fb-&gt;len);\r\n    file.close();\r\n  } else {\r\n    Serial.println(\"Unable to write \" + filename);\r\n  }\r\n\r\n  \/\/ Return the picture data\r\n  esp_camera_fb_return(fb);\r\n}\r\n\r\n\r\n\r\n\/\/ Capture Photo Routine\r\nvoid CapturePhoto() {\r\n  \r\n    \r\n        \/\/ Keep a count of the number of photos we have taken\r\n        static int number = 0;\r\n      \r\n        \/\/ Keep a count of the number of photos we have taken\r\n        number++;\r\n      \r\n        \/\/ Construct a filename that looks like \"\/1653482900.jpg\"\r\n        String filename = \"\/\";\r\n        filename += String(rtc.getEpoch());\r\n        filename += \".jpg\"; \r\n        takePhoto(filename);\r\n\r\n        \r\n        if (ScanSeconds &gt; 0) {\r\n           unsigned long ScanMillis = millis();\r\n           if (ScanMillis - previousScanMillis &gt;= Scaninterval) {\r\n              previousScanMillis = ScanMillis;              \r\n              \r\n              String countdown = \"Seconds remaining: \";\r\n              countdown += String(ScanSeconds);\r\n              Serial.println(countdown);\r\n                            \r\n                            \r\n              \/\/ Partial Wifi Scan code\r\n              if (DisableAP == 1) {\r\n                 \/\/ Disconnect WiFi to allow scanning\r\n                 WiFi.disconnect();\r\n                 delay(100);    \r\n                 DisableAP = 0;\r\n                 EnableAP = 1;\r\n              }\r\n              \/\/ WiFi.scanNetworks will return the number of networks found\r\n              \r\n              String TimeStamp = String(rtc.getEpoch());\r\n              appendFile(SD_MMC, FileName, TimeStamp.c_str()); \r\n              appendFile(SD_MMC, FileName, \",\");\r\n              TimeStamp = String(rtc.getDateTime());\r\n              appendFile(SD_MMC, FileName, TimeStamp.c_str()); \r\n              appendFile(SD_MMC, FileName, \",\");\r\n              int n = WiFi.scanNetworks();  \/\/ This number can be staticly set to limit results          \r\n              String StringWifiNetworks = String(n);\r\n              appendFile(SD_MMC, FileName, StringWifiNetworks.c_str()); \r\n              appendFile(SD_MMC, FileName, \",\");\r\n              for (int i = 0; i &lt; n; ++i) {\r\n                  \/\/ Print SSID and RSSI for each network found - Maybe append to string so a single write occurs\r\n                  String StringSSID = String(WiFi.SSID(i));\r\n                  appendFile(SD_MMC, FileName, StringSSID.c_str()); \r\n                  appendFile(SD_MMC, FileName, \",\");\r\n                  String StringRSSI = String(WiFi.RSSI(i));\r\n                  appendFile(SD_MMC, FileName, StringRSSI.c_str()); \r\n                  appendFile(SD_MMC, FileName, \",\");\r\n                  String StringEncryptionType = String((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?\" \":\"*\");\r\n                  appendFile(SD_MMC, FileName, StringEncryptionType.c_str()); \r\n                  appendFile(SD_MMC, FileName, \",\");   \r\n              }\r\n              appendFile(SD_MMC, FileName, \"\\n\");  \r\n              \/**\r\n              String networksfound = \"WiFi Networks Found: \";\r\n              networksfound += String(n);\r\n              Serial.println(networksfound);\r\n              **\/\r\n              \r\n              ScanSeconds--;\r\n           }\r\n          \r\n        }\r\n\r\n        if (ScanSeconds == 0) {\r\n          if (EnableAP == 1)  {\r\n            WiFi.softAP(ssid);\r\n            delay(100); \r\n            EnableAP = 0;  \r\n          }\r\n          ScanEnabled = \"Standby\";\r\n        }\r\n\r\n        \r\n}\r\n\r\n\/\/ Setup Routine\r\nvoid setup() {\r\n\r\n  ScanEnabled = \"Standby\";\r\n  \r\n  rtc.setTime(epoch);\r\n    \r\n  \/\/ Initialize the peripherals\r\n  Serial.begin(115200);\r\n  while(!Serial) delay(100);\r\n  \r\n  startMicroSD();\r\n  startCamera(); \r\n  PrintHeader();\r\n\r\n  delay(5000); \/\/ Delay 5 seconds before first photo\r\n  \r\n  WiFi.mode(WIFI_AP);\r\n\r\n  esp_wifi_set_mac(WIFI_IF_AP, &amp;newMACAddress[0]);\r\n  \r\n  WiFi.softAP(ssid);\r\n  delay(100);\r\n  \r\n  IPAddress IP = WiFi.softAPIP();\r\n\r\n  IPAddress Ip(192, 168, 100, 1);\r\n  IPAddress NMask(255, 255, 255, 0);\r\n  WiFi.softAPConfig(Ip, Ip, NMask);\r\n\r\n  IPAddress myIP = WiFi.softAPIP();\r\n\r\n  \/\/ Send web page to client\r\n  server.on(\"\/\", HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send_P(200, \"text\/html\", index_html, processor);\r\n  });\r\n\r\n  \/\/ Receive an HTTP GET request at &lt;ESP_IP&gt;\/get?email_input=&lt;inputMessage&gt;&amp;enable_email_input=&lt;inputMessage2&gt;&amp;inputTime=&lt;inputTime&gt;\r\n  server.on(\"\/get\", HTTP_GET, [] (AsyncWebServerRequest *request) {\r\n      \/\/ GET inputTime value on &lt;ESP_IP&gt;\/get?inputTime=&lt;inputTime&gt;\r\n\r\n  if (request-&gt;hasParam(TIME_INPUT)) {\r\n        inputTime = request-&gt;getParam(TIME_INPUT)-&gt;value();\r\n    if (request-&gt;hasParam(TIME_COMMIT)) {\r\n      \/\/ Pass the input and set the RTC time if the commit check box is checked.\r\n        rtc.setTime(inputTime.toInt()); \r\n      }\r\n    }\r\n\r\n  if (request-&gt;hasParam(SCAN_TIME)) {\r\n         ScanTime = request-&gt;getParam(SCAN_TIME)-&gt;value();\r\n         ScanSeconds = ScanTime.toInt();\r\n         ScanSeconds = 10*ScanSeconds; \/\/ Change ScanTime minutes to actual seconds (was 60, changed because wifi scan takes 6 seconds per 1 second)\r\n    if (request-&gt;hasParam(SCAN_COMMIT)) {\r\n      \/\/ Call the wifiscan function, this will break the Headend Controller AP mode\r\n      \/\/ The wifiscan function can have a nested image capture function and write to MicroSD function.\r\n      \/\/ Start WifiScan if this variable changes from the default via the web interface controller.\r\n      ScanEnabled = \"Enabled\";   \r\n      int DisableAP = 1;         \r\n      }\r\n    }\r\n    \r\n    \/\/ request-&gt;send(200, \"text\/html\", \"HTTP GET request sent to your ESP.&lt;br&gt;&lt;a href=\\\"\/\\\"&gt;Return to Home Page&lt;\/a&gt;\");\r\n    request-&gt;send_P(200, \"text\/html\", index_html, processor);\r\n  });\r\n  server.onNotFound(notFound);\r\n  server.begin();\r\n}\r\n\r\n\r\n\r\n\/\/ Super Loop Routine\r\nvoid loop() {\r\n  unsigned long currentMillis = millis();\r\n  if (currentMillis - previousMillis &gt;= interval) {\r\n    previousMillis = currentMillis;\r\n    EpochValue = String(rtc.getTime(\"%A, %B %d %Y %H:%M:%S\"));\r\n    \/\/ CurrentTime = String(rtc.getTime());\r\n\r\n      if (ScanEnabled.equals(\"Enabled\")) {\r\n        CapturePhoto();\r\n      }\r\n  \r\n      else {\r\n        Serial.println(ScanEnabled);\r\n      }\r\n  }\r\n}<\/pre>\n<p>Here is a video of what the mobile device setup looks like and its ease of use as a controller of the ESP32 module.<\/p>\n<p><iframe loading=\"lazy\" title=\"WebApp__DemoSetup.mp4\" src=\"https:\/\/player.vimeo.com\/video\/715019056?dnt=1&amp;app_id=122963\" width=\"640\" height=\"935\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write\"><\/iframe><\/p>\n<p>It demonstrates how to set the time and initiate the scan. It also shows the use of SensorLog, this is a separate app ran on the iphone to log other data. The timestamps and data from both devices can be coorelated to allow all of the data to be combined, which is the primary goal of this topic.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetTime.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-4045\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetTime-205x300.png\" alt=\"\" width=\"205\" height=\"300\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetTime-205x300.png 205w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetTime-185x270.png 185w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetTime.png 300w\" sizes=\"auto, (max-width: 205px) 100vw, 205px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetScanTime.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-4046\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetScanTime-205x300.png\" alt=\"\" width=\"205\" height=\"300\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetScanTime-205x300.png 205w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetScanTime-185x270.png 185w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_SetScanTime.png 300w\" sizes=\"auto, (max-width: 205px) 100vw, 205px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_UpdatedTime.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-4047\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_UpdatedTime-205x300.png\" alt=\"\" width=\"205\" height=\"300\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_UpdatedTime-205x300.png 205w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_UpdatedTime-185x270.png 185w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/05\/WebApp_UpdatedTime.png 300w\" sizes=\"auto, (max-width: 205px) 100vw, 205px\" \/><\/a><\/p>\n<p>Here is a list of items that were changed in the code that are customizable for any needed application.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">1. Set your new MAC Address\r\n\/\/ Located in Variables and Constants Function\r\n\/\/ Example mac address A1:B2:C3:D4:E5:F6\r\nuint8_t newMACAddress[] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6};\r\n\/\/ Located in Setup Function\r\nesp_wifi_set_mac(WIFI_IF_AP, &amp;newMACAddress[0]);\r\n\r\n2. Set your IP address\r\n\/\/ Located in Setup Function\r\nIPAddress Ip(192, 168, 100, 1);\r\nIPAddress NMask(255, 255, 255, 0);\r\nWiFi.softAPConfig(Ip, Ip, NMask);\r\n\r\n3. Set ESP32-Cam up as a wifi AP\r\n\/\/ Located in Variables and Constants Function\r\n\/\/ REPLACE WITH YOUR NETWORK CREDENTIALS\r\nconst char* ssid     = \"ESP32-Cam-AP1\";\r\n\/\/ Located in Setup Function\r\nWiFi.mode(WIFI_AP);  \r\nWiFi.softAP(ssid);\r\n\r\n4. File name of datascan log file\r\n\/\/ Located in Variables and Constants Function\r\n\/\/ This variable is called in several of the functions\r\nconst char* FileName = \"\/datalogger.csv\";\r\n\r\n5. File name that changes with time for photos\r\n\/\/ Located in the Capture Photo Routine\r\nString filename = \"\/\";\r\nfilename += String(rtc.getEpoch());\r\nfilename += \".jpg\"; \r\ntakePhoto(filename);\r\n\r\n6. Some numbers on mobile devices are seen as phone numbers which hyperlink\r\n\/\/ Located in the HTML code\r\n\/\/ &amp;zwj; prevents the hyperlinking and just displays the numbers.\r\n&lt;p class=\"timeinfo\"&gt;Entered Epoch: &lt;span id=\"state\"&gt;&amp;zwj;%TIME%&lt;\/span&gt;&lt;\/p&gt; \r\n\r\n7. Embedded image using Base64\r\n\/\/ Located in the HTML code\r\n&lt;img src=\"data:image\/jpg;base64,....\r\nI used a linux system to encode in Base64 using this script.\r\n\r\nImage2Base64.sh\r\n\r\n#!\/bin\/bash\r\n# https:\/\/stackoverflow.com\/questions\/32698451\/how-do-i-convert-a-base64-image\r\n# instead of uploading to a third party - https:\/\/www.base64-image.de\/\r\n\r\n# &lt;img src=\"&lt;the results here&gt;\"&gt;\r\n\r\n{ echo \"data:image\/jpg;base64,\"; openssl enc -base64 -in uvex.jpg; } &gt; uvex.txt\r\n\r\n# text file contents are pasted in HTML code, inside quotes of this tag &lt;img src=\"\"&gt;\r\n\r\nAlthough not done on this code, the icon for mobile devices of the website can use an embedded icon file using a similar approach.  Here are a couple of examples of code that would be placed in the HTML code.\r\n\r\n...\r\n&lt;link rel=\"icon\" type=\"image\/jpg\" href=\"data:image\/jpg;base64...\" \/&gt;\r\n...\r\n\r\n or\r\n \r\n...\r\n&lt;link rel=\"icon\" type=\"image\/png\" href=\"data:image\/png;base64...\" \/&gt;\r\n...<\/pre>\n<p>Here is a video that was compiled from the series of images taken during the scan duration. The script to create it is below the video<\/p>\n<p><iframe loading=\"lazy\" title=\"ESP32-Cam_Timelapse.mp4\" src=\"https:\/\/player.vimeo.com\/video\/715019378?dnt=1&amp;app_id=122963\" width=\"640\" height=\"480\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write\"><\/iframe><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\"># Image2Video.sh\r\n\r\n#!\/bin\/bash\r\n\r\ncd 'Path containing images'\r\nls *jpg &gt; imagelist1.txt\r\nsed \"s\/1653\/file \\'1653\/\" imagelist1.txt &gt; imagelist2.txt\r\nsed \"s\/jpg\/jpg\\'\/\" imagelist2.txt &gt; imagelist3.txt\r\nffmpeg -y -r 10 -f concat -safe 0 -i \"imagelist3.txt\" -c:v libx264 -vf \"fps=10,format=yuv420p\" \"ESP32-Cam_Timelapse.mp4\"\r\nrm *.txt\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Brian Lough created a video on YouTube titled &#8220;WiFiManager &#8211; An Essential ESP32 library!&#8221; This video pointed out that hard coded settings on the ESP32 can be a limitation. It went on to demonstrate this with wireless network connectivity and the inherent dependency when preset values are used. The solution rested on a library that gives the ESP32 the ability to operate as a WiFi access point and for the ESP32 to provide a web portal for an attached device&#8230;.<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.cloudacm.com\/?p=4036\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[],"class_list":["post-4036","post","type-post","status-publish","format-standard","hentry","category-esp32-cam"],"_links":{"self":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4036","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4036"}],"version-history":[{"count":9,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4036\/revisions"}],"predecessor-version":[{"id":4050,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4036\/revisions\/4050"}],"wp:attachment":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4036"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4036"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4036"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}