├── .gitignore ├── .vscode ├── arduino.json └── c_cpp_properties.json ├── LICENSE ├── README.md ├── TeleView.ino ├── camera_pins.h ├── display.h ├── docs ├── .gitignore ├── AddConfigAttribute.ipynb ├── Photo_sendPhoto.jpg ├── WebUI.jpg ├── changeRes.jpg ├── extra_options.jpg └── photo_sendOptions.jpg ├── motionDetect.h ├── persist.h ├── telegram_utils.h ├── webPages.h └── workspace.code-workspace /.gitignore: -------------------------------------------------------------------------------- 1 | docs/.ipynb_checkpoints/ 2 | *.h_orig -------------------------------------------------------------------------------- /.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "configuration": "PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none", 3 | "board": "esp32:esp32:ttgo-t1", 4 | "sketch": "TeleView.ino", 5 | "port": "COM4" 6 | } -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "configurations": [ 4 | { 5 | "name": "Arduino", 6 | "compilerPath": "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\bin\\xtensa-esp32-elf-g++", 7 | "compilerArgs": [ 8 | "-std=gnu++11", 9 | "-Wpointer-arith", 10 | "-fexceptions", 11 | "-fstack-protector", 12 | "-ffunction-sections", 13 | "-fdata-sections", 14 | "-fstrict-volatile-bitfields", 15 | "-mlongcalls", 16 | "-nostdlib", 17 | "-w", 18 | "-Wno-error=maybe-uninitialized", 19 | "-Wno-error=unused-function", 20 | "-Wno-error=unused-but-set-variable", 21 | "-Wno-error=unused-variable", 22 | "-Wno-error=deprecated-declarations", 23 | "-Wno-unused-parameter", 24 | "-Wno-unused-but-set-parameter", 25 | "-Wno-missing-field-initializers", 26 | "-Wno-sign-compare", 27 | "-fno-rtti" 28 | ], 29 | "intelliSenseMode": "gcc-x64", 30 | "includePath": [ 31 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\config", 32 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\app_trace", 33 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\app_update", 34 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\asio", 35 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\bootloader_support", 36 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\bt", 37 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\coap", 38 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\console", 39 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\driver", 40 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\efuse", 41 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp-tls", 42 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp32", 43 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_adc_cal", 44 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_event", 45 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_http_client", 46 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_http_server", 47 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_https_ota", 48 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_https_server", 49 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_ringbuf", 50 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp_websocket_client", 51 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\espcoredump", 52 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\ethernet", 53 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\expat", 54 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\fatfs", 55 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\freemodbus", 56 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\freertos", 57 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\heap", 58 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\idf_test", 59 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\jsmn", 60 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\json", 61 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\libsodium", 62 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\log", 63 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\lwip", 64 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\mbedtls", 65 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\mdns", 66 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\micro-ecc", 67 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\mqtt", 68 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\newlib", 69 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\nghttp", 70 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\nvs_flash", 71 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\openssl", 72 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\protobuf-c", 73 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\protocomm", 74 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\pthread", 75 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\sdmmc", 76 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\smartconfig_ack", 77 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\soc", 78 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\spi_flash", 79 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\spiffs", 80 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\tcp_transport", 81 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\tcpip_adapter", 82 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\ulp", 83 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\unity", 84 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\vfs", 85 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\wear_levelling", 86 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\wifi_provisioning", 87 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\wpa_supplicant", 88 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\xtensa-debug-module", 89 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp-face", 90 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp32-camera", 91 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\esp-face", 92 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\tools\\sdk\\include\\fb_gfx", 93 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\cores\\esp32", 94 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\variants\\ttgo-t1", 95 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\WiFi\\src", 96 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\WiFiClientSecure\\src", 97 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\WebServer\\src", 98 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\AutoConnect\\src", 99 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\DNSServer\\src", 100 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\EEPROM\\src", 101 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\PageBuilder\\src", 102 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\Preferences\\src", 103 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\Ticker\\src", 104 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\ArduinoJson\\src", 105 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\FS\\src", 106 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\SPIFFS\\src", 107 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\HTTPClient\\src", 108 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\HTTPUpdate\\src", 109 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\Update\\src", 110 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\ESPmDNS\\src", 111 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\Wire\\src", 112 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\Adafruit_GFX_Library", 113 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\Adafruit_SSD1306", 114 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\SPI\\src", 115 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\UniversalTelegramBot\\src", 116 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\ESP_Mail_Client\\src", 117 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\libraries\\SD\\src", 118 | "C:\\Users\\A.Maklad\\Documents\\Arduino\\libraries\\Adafruit_BusIO", 119 | "c:\\users\\a.maklad\\appdata\\local\\arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\xtensa-esp32-elf\\include\\c++\\5.2.0", 120 | "c:\\users\\a.maklad\\appdata\\local\\arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\xtensa-esp32-elf\\include\\c++\\5.2.0\\xtensa-esp32-elf", 121 | "c:\\users\\a.maklad\\appdata\\local\\arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\xtensa-esp32-elf\\include\\c++\\5.2.0\\backward", 122 | "c:\\users\\a.maklad\\appdata\\local\\arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\lib\\gcc\\xtensa-esp32-elf\\5.2.0\\include", 123 | "c:\\users\\a.maklad\\appdata\\local\\arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\lib\\gcc\\xtensa-esp32-elf\\5.2.0\\include-fixed", 124 | "c:\\users\\a.maklad\\appdata\\local\\arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\xtensa-esp32-elf\\include", 125 | "c:\\users\\a.maklad\\appdata\\local\\arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-97-gc752ad5-5.2.0\\xtensa-esp32-elf\\sysroot\\usr\\include" 126 | ], 127 | "forcedInclude": [ 128 | "C:\\Users\\A.Maklad\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.6\\cores\\esp32\\Arduino.h" 129 | ], 130 | "cStandard": "c11", 131 | "cppStandard": "c++11", 132 | "defines": [ 133 | "ESP_PLATFORM", 134 | "MBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"", 135 | "HAVE_CONFIG_H", 136 | "GCC_NOT_5_2_0=0", 137 | "WITH_POSIX", 138 | "F_CPU=240000000L", 139 | "ARDUINO=10813", 140 | "ARDUINO_TTGO_T1", 141 | "ARDUINO_ARCH_ESP32", 142 | "ARDUINO_BOARD=\"TTGO_T1\"", 143 | "ARDUINO_VARIANT=\"ttgo-t1\"", 144 | "ESP32", 145 | "CORE_DEBUG_LEVEL=0", 146 | "__DBL_MIN_EXP__=(-1021)", 147 | "__UINT_LEAST16_MAX__=0xffff", 148 | "__ATOMIC_ACQUIRE=2", 149 | "__FLT_MIN__=1.1754943508222875e-38F", 150 | "__GCC_IEC_559_COMPLEX=0", 151 | "__UINT_LEAST8_TYPE__=unsigned char", 152 | "__INTMAX_C(c)=c ## LL", 153 | "__CHAR_BIT__=8", 154 | "__UINT8_MAX__=0xff", 155 | "__WINT_MAX__=0xffffffffU", 156 | "__ORDER_LITTLE_ENDIAN__=1234", 157 | "__SIZE_MAX__=0xffffffffU", 158 | "__WCHAR_MAX__=0xffff", 159 | "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1=1", 160 | "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2=1", 161 | "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4=1", 162 | "__DBL_DENORM_MIN__=double(4.9406564584124654e-324L)", 163 | "__GCC_ATOMIC_CHAR_LOCK_FREE=2", 164 | "__GCC_IEC_559=0", 165 | "__FLT_EVAL_METHOD__=0", 166 | "__cpp_binary_literals=201304", 167 | "__GCC_ATOMIC_CHAR32_T_LOCK_FREE=2", 168 | "__UINT_FAST64_MAX__=0xffffffffffffffffULL", 169 | "__SIG_ATOMIC_TYPE__=int", 170 | "__DBL_MIN_10_EXP__=(-307)", 171 | "__FINITE_MATH_ONLY__=0", 172 | "__GNUC_PATCHLEVEL__=0", 173 | "__UINT_FAST8_MAX__=0xffffffffU", 174 | "__has_include(STR)=__has_include__(STR)", 175 | "__DEC64_MAX_EXP__=385", 176 | "__INT8_C(c)=c", 177 | "__UINT_LEAST64_MAX__=0xffffffffffffffffULL", 178 | "__SHRT_MAX__=0x7fff", 179 | "__LDBL_MAX__=1.7976931348623157e+308L", 180 | "__UINT_LEAST8_MAX__=0xff", 181 | "__GCC_ATOMIC_BOOL_LOCK_FREE=2", 182 | "__UINTMAX_TYPE__=long long unsigned int", 183 | "__DEC32_EPSILON__=1E-6DF", 184 | "__CHAR_UNSIGNED__=1", 185 | "__UINT32_MAX__=0xffffffffUL", 186 | "__LDBL_MAX_EXP__=1024", 187 | "__WINT_MIN__=0U", 188 | "__SCHAR_MAX__=0x7f", 189 | "__WCHAR_MIN__=0", 190 | "__INT64_C(c)=c ## LL", 191 | "__DBL_DIG__=15", 192 | "__GCC_ATOMIC_POINTER_LOCK_FREE=2", 193 | "__SIZEOF_INT__=4", 194 | "__SIZEOF_POINTER__=4", 195 | "__GCC_ATOMIC_CHAR16_T_LOCK_FREE=2", 196 | "__USER_LABEL_PREFIX__", 197 | "__STDC_HOSTED__=1", 198 | "__LDBL_HAS_INFINITY__=1", 199 | "__XTENSA_EL__=1", 200 | "__FLT_EPSILON__=1.1920928955078125e-7F", 201 | "__GXX_WEAK__=1", 202 | "__LDBL_MIN__=2.2250738585072014e-308L", 203 | "__DEC32_MAX__=9.999999E96DF", 204 | "__INT32_MAX__=0x7fffffffL", 205 | "__SIZEOF_LONG__=4", 206 | "__UINT16_C(c)=c", 207 | "__DECIMAL_DIG__=17", 208 | "__has_include_next(STR)=__has_include_next__(STR)", 209 | "__LDBL_HAS_QUIET_NAN__=1", 210 | "__GNUC__=5", 211 | "__GXX_RTTI=1", 212 | "__FLT_HAS_DENORM__=1", 213 | "__SIZEOF_LONG_DOUBLE__=8", 214 | "__BIGGEST_ALIGNMENT__=16", 215 | "__DBL_MAX__=double(1.7976931348623157e+308L)", 216 | "__INT_FAST32_MAX__=0x7fffffff", 217 | "__DBL_HAS_INFINITY__=1", 218 | "__INT64_MAX__=0x7fffffffffffffffLL", 219 | "__DEC32_MIN_EXP__=(-94)", 220 | "__INT_FAST16_TYPE__=int", 221 | "__LDBL_HAS_DENORM__=1", 222 | "__cplusplus=199711L", 223 | "__DEC128_MAX__=9.999999999999999999999999999999999E6144DL", 224 | "__INT_LEAST32_MAX__=0x7fffffffL", 225 | "__DEC32_MIN__=1E-95DF", 226 | "__DEPRECATED=1", 227 | "__DBL_MAX_EXP__=1024", 228 | "__DEC128_EPSILON__=1E-33DL", 229 | "__PTRDIFF_MAX__=0x7fffffff", 230 | "__GNUG__=5", 231 | "__LONG_LONG_MAX__=0x7fffffffffffffffLL", 232 | "__SIZEOF_SIZE_T__=4", 233 | "__SIZEOF_WINT_T__=4", 234 | "__GXX_ABI_VERSION=1009", 235 | "__FLT_MIN_EXP__=(-125)", 236 | "__INT_FAST64_TYPE__=long long int", 237 | "__FP_FAST_FMAF=1", 238 | "__DBL_MIN__=double(2.2250738585072014e-308L)", 239 | "__FLT_MIN_10_EXP__=(-37)", 240 | "__DEC128_MIN__=1E-6143DL", 241 | "__REGISTER_PREFIX__", 242 | "__UINT16_MAX__=0xffff", 243 | "__DBL_HAS_DENORM__=1", 244 | "__UINT8_TYPE__=unsigned char", 245 | "__NO_INLINE__=1", 246 | "__FLT_MANT_DIG__=24", 247 | "__VERSION__=\"5.2.0\"", 248 | "__UINT64_C(c)=c ## ULL", 249 | "__GCC_ATOMIC_INT_LOCK_FREE=2", 250 | "__FLOAT_WORD_ORDER__=__ORDER_LITTLE_ENDIAN__", 251 | "__INT32_C(c)=c ## L", 252 | "__DEC64_EPSILON__=1E-15DD", 253 | "__ORDER_PDP_ENDIAN__=3412", 254 | "__DEC128_MIN_EXP__=(-6142)", 255 | "__INT_FAST32_TYPE__=int", 256 | "__UINT_LEAST16_TYPE__=short unsigned int", 257 | "__INT16_MAX__=0x7fff", 258 | "__cpp_rtti=199711", 259 | "__SIZE_TYPE__=unsigned int", 260 | "__UINT64_MAX__=0xffffffffffffffffULL", 261 | "__INT8_TYPE__=signed char", 262 | "__ELF__=1", 263 | "__xtensa__=1", 264 | "__FLT_RADIX__=2", 265 | "__INT_LEAST16_TYPE__=short int", 266 | "__LDBL_EPSILON__=2.2204460492503131e-16L", 267 | "__UINTMAX_C(c)=c ## ULL", 268 | "__SIG_ATOMIC_MAX__=0x7fffffff", 269 | "__GCC_ATOMIC_WCHAR_T_LOCK_FREE=2", 270 | "__SIZEOF_PTRDIFF_T__=4", 271 | "__DEC32_SUBNORMAL_MIN__=0.000001E-95DF", 272 | "__INT_FAST16_MAX__=0x7fffffff", 273 | "__UINT_FAST32_MAX__=0xffffffffU", 274 | "__UINT_LEAST64_TYPE__=long long unsigned int", 275 | "__FLT_HAS_QUIET_NAN__=1", 276 | "__FLT_MAX_10_EXP__=38", 277 | "__LONG_MAX__=0x7fffffffL", 278 | "__DEC128_SUBNORMAL_MIN__=0.000000000000000000000000000000001E-6143DL", 279 | "__FLT_HAS_INFINITY__=1", 280 | "__UINT_FAST16_TYPE__=unsigned int", 281 | "__DEC64_MAX__=9.999999999999999E384DD", 282 | "__CHAR16_TYPE__=short unsigned int", 283 | "__PRAGMA_REDEFINE_EXTNAME=1", 284 | "__INT_LEAST16_MAX__=0x7fff", 285 | "__DEC64_MANT_DIG__=16", 286 | "__UINT_LEAST32_MAX__=0xffffffffUL", 287 | "__GCC_ATOMIC_LONG_LOCK_FREE=2", 288 | "__INT_LEAST64_TYPE__=long long int", 289 | "__INT16_TYPE__=short int", 290 | "__INT_LEAST8_TYPE__=signed char", 291 | "__DEC32_MAX_EXP__=97", 292 | "__INT_FAST8_MAX__=0x7fffffff", 293 | "__INTPTR_MAX__=0x7fffffff", 294 | "__EXCEPTIONS=1", 295 | "__LDBL_MANT_DIG__=53", 296 | "__DBL_HAS_QUIET_NAN__=1", 297 | "__SIG_ATOMIC_MIN__=(-__SIG_ATOMIC_MAX__ - 1)", 298 | "__INTPTR_TYPE__=int", 299 | "__UINT16_TYPE__=short unsigned int", 300 | "__WCHAR_TYPE__=short unsigned int", 301 | "__SIZEOF_FLOAT__=4", 302 | "__UINTPTR_MAX__=0xffffffffU", 303 | "__DEC64_MIN_EXP__=(-382)", 304 | "__INT_FAST64_MAX__=0x7fffffffffffffffLL", 305 | "__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1", 306 | "__FLT_DIG__=6", 307 | "__UINT_FAST64_TYPE__=long long unsigned int", 308 | "__INT_MAX__=0x7fffffff", 309 | "__INT64_TYPE__=long long int", 310 | "__FLT_MAX_EXP__=128", 311 | "__DBL_MANT_DIG__=53", 312 | "__INT_LEAST64_MAX__=0x7fffffffffffffffLL", 313 | "__DEC64_MIN__=1E-383DD", 314 | "__WINT_TYPE__=unsigned int", 315 | "__UINT_LEAST32_TYPE__=long unsigned int", 316 | "__SIZEOF_SHORT__=2", 317 | "__LDBL_MIN_EXP__=(-1021)", 318 | "__INT_LEAST8_MAX__=0x7f", 319 | "__WCHAR_UNSIGNED__=1", 320 | "__LDBL_MAX_10_EXP__=308", 321 | "__ATOMIC_RELAXED=0", 322 | "__DBL_EPSILON__=double(2.2204460492503131e-16L)", 323 | "__XTENSA_WINDOWED_ABI__=1", 324 | "__UINT8_C(c)=c", 325 | "__INT_LEAST32_TYPE__=long int", 326 | "__SIZEOF_WCHAR_T__=2", 327 | "__UINT64_TYPE__=long long unsigned int", 328 | "__INT_FAST8_TYPE__=int", 329 | "__DBL_DECIMAL_DIG__=17", 330 | "__DEC_EVAL_METHOD__=2", 331 | "__XTENSA__=1", 332 | "__ORDER_BIG_ENDIAN__=4321", 333 | "__cpp_runtime_arrays=198712", 334 | "__UINT32_C(c)=c ## UL", 335 | "__INTMAX_MAX__=0x7fffffffffffffffLL", 336 | "__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__", 337 | "__FLT_DENORM_MIN__=1.4012984643248171e-45F", 338 | "__INT8_MAX__=0x7f", 339 | "__UINT_FAST32_TYPE__=unsigned int", 340 | "__CHAR32_TYPE__=long unsigned int", 341 | "__FLT_MAX__=3.4028234663852886e+38F", 342 | "__INT32_TYPE__=long int", 343 | "__SIZEOF_DOUBLE__=8", 344 | "__cpp_exceptions=199711", 345 | "__INTMAX_TYPE__=long long int", 346 | "__DEC128_MAX_EXP__=6145", 347 | "__ATOMIC_CONSUME=1", 348 | "__GNUC_MINOR__=2", 349 | "__UINTMAX_MAX__=0xffffffffffffffffULL", 350 | "__DEC32_MANT_DIG__=7", 351 | "__DBL_MAX_10_EXP__=308", 352 | "__LDBL_DENORM_MIN__=4.9406564584124654e-324L", 353 | "__INT16_C(c)=c", 354 | "__STDC__=1", 355 | "__PTRDIFF_TYPE__=int", 356 | "__ATOMIC_SEQ_CST=5", 357 | "__UINT32_TYPE__=long unsigned int", 358 | "__UINTPTR_TYPE__=unsigned int", 359 | "__DEC64_SUBNORMAL_MIN__=0.000000000000001E-383DD", 360 | "__DEC128_MANT_DIG__=34", 361 | "__LDBL_MIN_10_EXP__=(-307)", 362 | "__SIZEOF_LONG_LONG__=8", 363 | "__GCC_ATOMIC_LLONG_LOCK_FREE=1", 364 | "__LDBL_DIG__=15", 365 | "__FLT_DECIMAL_DIG__=9", 366 | "__UINT_FAST16_MAX__=0xffffffffU", 367 | "__GNUC_GNU_INLINE__=1", 368 | "__GCC_ATOMIC_SHORT_LOCK_FREE=2", 369 | "__UINT_FAST8_TYPE__=unsigned int", 370 | "__ATOMIC_ACQ_REL=4", 371 | "__ATOMIC_RELEASE=3", 372 | "USBCON" 373 | ] 374 | } 375 | ] 376 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TeleView: 2 | A telegram-bot project to control an ESP32-Camera enabled board with many features. 3 | 4 | - [TeleView:](#teleview) 5 | - [Main features and snapshots](#main-features-and-snapshots) 6 | - [Generic Features:](#generic-features) 7 | - [Specific Features per Type of Board:](#specific-features-per-type-of-board) 8 | - [Tested Boards:](#tested-boards) 9 | - [Required Installation:](#required-installation) 10 | - [Required Libraries:](#required-libraries) 11 | - [User Guide:](#user-guide) 12 | - [Compile and Upload](#compile-and-upload) 13 | - [Enabling Disabeling Features at compile time](#enabling-disabeling-features-at-compile-time) 14 | - [Setting up the WiFi:](#setting-up-the-wifi) 15 | - [Setting up the Telegrambot:](#setting-up-the-telegrambot) 16 | - [OTA (it is there but doesnt work with all boards)](#ota-it-is-there-but-doesnt-work-with-all-boards) 17 | - [Having more than one user to use the bot:](#having-more-than-one-user-to-use-the-bot) 18 | - [Having multiple boards to use the same bot token:](#having-multiple-boards-to-use-the-same-bot-token) 19 | - [The Telegram Keyboard](#the-telegram-keyboard) 20 | - [A note about PIR motion Detection and Buzzer alerts:](#a-note-about-pir-motion-detection-and-buzzer-alerts) 21 | - [A note about Deep Sleep:](#a-note-about-deep-sleep) 22 | - [The Configuration Web interface](#the-configuration-web-interface) 23 | - [http://TeleView.local/](#httpteleviewlocal) 24 | - [http://TeleView.local/_ac](#httpteleviewlocal_ac) 25 | - [Intput Text](#intput-text) 26 | - [CheckBoxes:](#checkboxes) 27 | - [ComboBoxes:](#comboboxes) 28 | - [Buttons:](#buttons) 29 | - [Sending Emails](#sending-emails) 30 | ## Main features and snapshots 31 | * Send Photo through telegram upon request. 32 | /sendPhoto 33 | 34 | * Control camera options through Telegram Keyboard 35 | /options 36 | 37 | * Control camera resolution Inline keyboard: 38 | /options 39 | 40 | * View options through Telegram 41 | /options 42 | 43 | * Web interface 44 | Web Interface 45 | 46 | 47 | ## Generic Features: 48 | * Sends Photo through telegram upon request. 49 | * The telegram bot responds to the ADMINID or UserID (find your telegram CHATID through https://web.telegram.org/#/im?p=@chatid_echo_bot ) 50 | * Control options with Telegram : 51 | * Generic features: Camera Resolution, Camera-Flip ,Camera-Mirror ,image-resolution ,Time-Lapse and send photo. 52 | * Board Specific Features: Flash ,OLED display enable, OLED as Flash, Screen Flip,Motion Detector. 53 | * Telegram Keyboard. 54 | * AutoConnect / WiFi config feature (no hard coding of the WiFI SSID,Pass ) 55 | * Web Portal : 56 | * to configure the control options and WiFi AP. 57 | * configure Telegram-BOT-Token, AdminID of the BOT 58 | * Web Server for /capture.jpg to make photo available through web. (configurable through Telegram menue) 59 | * The Device-Name (configurable) decides the hostname of the device on LAN/WiFi 60 | * Time Lapse feature every X min, chose 0 to disable Time-Lapse. 61 | * A deep sleep mode is possible with time-lapse and PIR motiion detection for battery operated scenarios. 62 | * Usual case , only the admin will be notified of the eventd "Alive", "motion detection" and "time-lapse" , but you can also enable alerting of the userId. 63 | * Supports multiple Admins and multiple Users through Telegram Groups (explained further below). 64 | * OTA Features (In progress) 65 | * Send Email With Photos 66 | * Supports OV2640 & OV3660 resolutions 67 | 68 | ## Specific Features per Type of Board: 69 | * toggle flash when taking a photo : For the AI_Thinker board, there is a very strong led on the same side as camera, which could be used as a flash. 70 | This feature Will only be compiled in the sketch if the "#define CAMERA_MODEL_AI_THINKER" is chosen upon compile. 71 | 72 | * PIR motion detection : For the TTGO_TI board, there is a motion sensor , which could trigger a "sendPhoto". This will only be available when the sketch is compiled with "#define CAMERA_MODEL_TTGO_T1_CAMERA". 73 | 74 | * toggle Display : For the TTGO_TI board, there is a SSD1306 OLED display, which could display AutoConnect - AP , IP when connected and used as a weak-flash when taking a photo. 75 | This will only be available when the sketch is compiled with "#define CAMERA_MODEL_TTGO_T1_CAMERA". also you could flip the screen upside-down. 76 | 77 | * Save photos to SD as a configurable feature. file name by date-time. 78 | 79 | ## Tested Boards: 80 | * AI-Thinker ESP32-CAM Board 81 | * TTGO T1 Board 82 | * M5STACK CAM 83 | * M5Stack Camera-Timer-X/F 84 | 85 | ## Required Installation: 86 | * Arduino IDE 87 | * ESP32 for Arduino IDE https://github.com/espressif/arduino-esp32 88 | 89 | ## Required Libraries: 90 | 91 | * TeleView was tested/compiled with these library versions. Just use the latest and if you face problems, then downgrade to those versions (more about this after the table) 92 | 93 | Library | Version 94 | --------| --------- 95 | ESP32 board libs (in the board manager) | 1.0.6 96 | UniversalTelegramBot | 1.3.0 97 | ArduinoJson | 6.19.4 98 | Adafruit GFX Library | 1.11.3 99 | Adafruit BusIO | 1.14.1 100 | Adafruit SSD1306 | 2.5.7 101 | AutoConnect | 1.4.0 102 | ESP Mail Client(Mobizt) | 2.7.0 103 | 104 | ### A note about the ESP32 core Libraries: 105 | Online, there are two URL's to be added to the Arduino Prefrences -> "Additional Board Managers URL" 106 | 1. this one is the normal and mentioned everywhere : https://dl.espressif.com/dl/package_esp32_index.json 107 | this allows the installtion of ESP32 core up to 1.0.6 108 | Teleview project uses The ESP32-CAM API 1.0.6 . 109 | 2. this one which is mentioned by Espressif: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json . 110 | This one allows for more version, up to 2.0.3 (till the time of writing this). 111 | Also Allows ESP32-S2, S3 and C3 boards. 112 | Teleview project is not compatible The ESP32-CAM API beyond 1.0.6. 113 | Some how there are breaking changes introduced in 2.0.0 ! 114 | 115 | Version 1.0.6 works fine from both sources. However, from version 2 upward (availbale only on the 2nd link), 116 | a lot of exceptions and stack traces appears. 117 | 118 | > I recommend using ESP32 Core Libs version 1.0.6 from this link: https://dl.espressif.com/dl/package_esp32_index.json 119 | 120 | 121 | 122 | ## User Guide: 123 | ### Compile and Upload 124 | This project is Compiled and uploaded through ArduinoIDE, use the normal procedure for installing ESP32 in ArduinoIDE and install the following Libs: 125 | * AutoConnect 126 | * ArduinoJson 127 | * Adafruit SSD1306 and Dependecies such as Adaruit GFX..etc 128 | * please check the required libraries section above. 129 | 130 | Before uploading, chose the target ESP32-CAM Board : 131 | 132 | in Teleview.ino: 133 | ```CPP 134 | // Select camera model 135 | //#define CAMERA_MODEL_WROVER_KIT 136 | //#define CAMERA_MODEL_ESP_EYE 137 | //#define CAMERA_MODEL_M5STACK_PSRAM 138 | //#define CAMERA_MODEL_M5STACK_WIDE 139 | //#define CAMERA_MODEL_AI_THINKER // Board definition "AI Thinker ESP32-CAM" 140 | #define CAMERA_MODEL_TTGO_T1_CAMERA // Board definition "TTGO T1" 141 | ``` 142 | 143 | ### Enabling Disabeling Features at compile time 144 | The "camera_pins.h" has the speces of each board pins and also supported features such as OLED , Flash ..etc 145 | This code has been tested on CAMERA_MODEL_AI_THINKER and CAMERA_MODEL_TTGO_T1_CAMERA modules. please update the camera_pins.h for other boards and more features. 146 | For the CAMERA_MODEL_TTGO_T1_CAMERA borad, please chose the "TTGO T1" and and set Tools-> Partiton Scheme --> Huge App (3MB No OTA/1MB SPIFF) 147 | 148 | For example: in "camera_pins.h" inside the section "#elif defined(CAMERA_MODEL_TTGO_T1_CAMERA)", you will find these precompilers: 149 | * #define SDA_PIN 21 150 | * #define SCL_PIN 22 151 | 152 | * #define I2C_DISPLAY_ADDR 0x3c 153 | * #define USE_OLED_AS_FLASH 1 // the OLED is on the same side as the camera 154 | * #define PIR_PIN 33 //GPIO_INPUT_IO_33 AS312 155 | 156 | * #define BUTTON_PIN 34 157 | 158 | Also a section exists if an Active Buzzer is connected which is triggered with the motion detection. 159 | 160 | These precompiler defines, are used upon compilation to utilize or ommit parts of the code depending on the features of the board. 161 | 162 | ### Setting up the WiFi: 163 | This sektch uses the Autoconnect Arduino library (https://github.com/Hieromon/AutoConnect) to let the user configure WiFi SSID and Password at runtime. 164 | When the sketch starts, it looks for a familiar WiFi. If it can't find one, it will start its own Access Point where the owner can connect and configure the WIFI. 165 | 166 | > First Time installtion: SSID will be "TeleView" with the password "tv-ei-694" 167 | 168 | The AutoConnect library will start a portal with the page /config to configure the Wifi and other configuration parameters. 169 | 170 | After a scucessfull Wifi connection, the same portal interface will be exposed to the local Wifi. The borad couled be reached on the LAN through the address "{{The Device Name}}.local" 171 | 172 | The default Device Name is Teleview and the address would be: 173 | * http://TeleView.local/ : A general Info page 174 | * http://TeleView.local/_ac : The Configuration Portal for AutoConnect 175 | * http://TeleView.local/teleView : The Configuration Portal for The Bot 176 | * http://TeleView.local/capture : a capture of the camera JPEG 177 | 178 | ### Setting up the Telegrambot: 179 | A Telegram-bot-token is required to use the Telegrambot feature. 180 | You may follow these instructions to get a Telegram Bot Token through the @botFather. 181 | https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-telegram?view=azure-bot-service-4.0 182 | 183 | * You will also need to find out your UserId, or in Telegram it is refered as ChatID. 184 | This could be acquired through a visit to the @chatid_echo_bot Telegrambot https://web.telegram.org/#/im?p=@chatid_echo_bot 185 | * Insert the token and the ChatID of the Admin in the web-interface found at the url "http:///teleView" and save. 186 | * Add the Bot to your telegram contacts. 187 | * Reset your board and you should get an "I am Alive!" messgae from your bot. 188 | 189 | ### OTA (it is there but doesnt work with all boards) 190 | * Through the Autoconnect Library, Teleview supports OTA. 191 | * by default OTA is disabled. You can trigger it from Telegram , but you can upload the new firmware from the webUI. 192 | * To enable OTA, use telegram's /moreSettings button and from the inlinekeyboard, "Enable OTA". 193 | * Go to webUI and a new tab will appera with page title "Update". it wil have a browse and upload buttons use them upload the new bin file from the next step. 194 | * Then follow these Instructions to generate the new bin file : https://hieromon.github.io/AutoConnect/otabrowser.html#how-to-make-the-binary-sketch 195 | * once the new firmware is uplaoded and implemented, the ESP will Reboot and OTA will be disbaled again. 196 | * OTA configuration item is not stored. The ESP will boot up with status disabled by default. 197 | 198 | ### Having more than one user to use the bot: 199 | Q) If you are a family and would like to have two or more people access and manage the Telegram bot. how can this be preformed? 200 | Q) if I have multiple Bots and want to controll all of them from one chat group ? 201 | 202 | Answer-A) 203 | Use the Admin_id and User_id for the second user. However, remember that following events are sent only to the chat with the Admin_id: 204 | * I am Alive message 205 | * Motion Detection 206 | * Time-Lapse 207 | 208 | Answer-B) 209 | > this is cool, you shoul really try it ! 210 | Using Telegram Groups: 211 | 1) Create a Telegram group. Let us call it MyCameraBotGroup. 212 | 2) The MyCameraBotGroup has now only you as an administrator. 213 | 3) Add the bot(s) you want in that group and make them admins. 214 | 4) Add your other family members in that group as well and make them admins (if you want them to also send commands). 215 | 5) Edit the rights of the Bots and Members as you please. 216 | 6) type /start in the MyCameraBotGroup group. 217 | 7) the bot(s) will respond with the chat ID of the MyCameraBotGroup (notice the chat_id of the group has a negative number) for example ('' from -1234567890..) 218 | 8) Now go to the WebUI and put the chat_id of the group (don't forget the negative sign) in the "Admin Chat ID" textbox. 219 | 9) Now anyone in that group can request a photo with /sendPhoto and other options as the same when using a single user. 220 | 10) All "I am Alive!" events, Motion Detection and time lapse activities will be sent to that group as well. 221 | 222 | ### Having multiple boards to use the same bot token: 223 | As far as I tested, this also works as a way to manage multiple boards at the same time. However you will not be able to figuire out which board is sending the "photo" , "I am Alive!" ,options or "motion detected" events. 224 | ## The Telegram Keyboard 225 | Most of the buttons in the telegram Keyboard are on/off options. 226 | 227 | Feature | Description 228 | :------------ | :---------------- 229 | /start | Press this on the first time you use the bot , it will show some helpful info. 230 | /options | Will show the current status of the differnt options and flags. 231 | /sendPhoto | Sends a photo to the requester. 232 | /vFlip | Flips the camera-image upside down. 233 | /hmirror | Mirror the camera-image . (unfortunately; there is no 90 degrees rotate ) 234 | /setLapse | It will ask to insert the lapse time in minutes. insert 0 to disbale, 60 for every 1 Hour, 1440 for once a day ...etc 235 | /webCaptureOn | This will disbale/enable the ./capture and ./capture.jpg urls. 236 | 237 | Will only be shown in TTGO_T1 : (or when an old display is enabled in camera_pins.h) 238 | Feature | Description 239 | :------------ | :---------------- 240 | /screenOn | Use the screen to display useful information or switch it off. 241 | /screenFlip | Flips the screen upside down. 242 | /motDetectOn | Will enable/disable PIR motion detection feature. 243 | 244 | Will only be shown when an OLED or Flash led available: (or when enabled in camera_pins.h) 245 | Feature | Description 246 | :------------ | :---------------- 247 | /useFlash | will enable/disbale using the flash upon a camera snapshot. 248 | 249 | Other Custome Fatures: 250 | Feature | Description 251 | :------------ | :---------------- 252 | /saveToSD | Save Every photo taken to an SD as well as send it to telegram 253 | /useBuzzer | Trigger Buzzer whne motion is detected. 254 | 255 | Other Generic Fatures: 256 | Feature | Description 257 | :------------ | :---------------- 258 | /useDeepSleep | Goto Deep sleep between timelapse ticks and PIR motion detection 259 | /motionDetectVC | Use Computer Vision (CV) for motion dection 260 | /alertALL | Alert both Admin Id and Chat ID 261 | /sendEmail | sends emails to up to 2 emails (with Photo) 262 | 263 | ## A note about PIR motion Detection and Buzzer alerts: 264 | 1) IN ESP32-CAM AI Thinker the Buzzer and PIR are usually connected to PIN 12 and 13 which are also used for the SD card communication. so please pay attention in case you have issues afterwards. 265 | 2) PIR PIN could be connected to other sensors. Example: a reed switch ( door opening) , a button , a laser tripwire ..etc 266 | 3) Buzzer PIN could be connected to any other External Alert. Example: a siren , a water pistol , a nerf gun ..etc 267 | 268 | ## A note about Deep Sleep: 269 | Deep sleep will be only utilized if one of those options is enabled: 270 | 1) PIR motion detection 271 | 2) time-lapse 272 | 273 | Deep Sleep puts the ESP into sleep mode. so don't expect it to respond to any of your Telegram commands during its sleep. 274 | Deep Sleep will not play well with those features: 275 | 1) CV motion detection , since it uses the Camera and the microcontroller to make constant checks of the frame changes. 276 | 2) WebCapture for the obvious reasons. 277 | 278 | if it happens you put the ESP into Deep sleep and somehow you chnaged your mind, then follow these steps: 279 | 1) issue the command "/setLapse" by typing it or clicking the button. 280 | 2) restart your ESP from the physical button on the board or by disconnecting and then connecting it again. 281 | 282 | ## The Configuration Web interface 283 | 284 | ### http://TeleView.local/ 285 | This root home page displays a preview of the camera and some basis info. The cog-wheel icon is a link to the AutoConnect portal. 286 | 287 | ### http://TeleView.local/_ac 288 | The Autoconnect portal configures the WiFi access and has a link to the bot's configuration page "./teleView". It has the following elements: 289 | 290 | Please note the below list is not exahustive. 291 | 292 | ### Intput Text 293 | |UI Control | Description 294 | |:-------------------| :----------- 295 | |Device Name | The name of the device , this will be used as a DNS entry in the local LAN and identifying the device. 296 | |Lapse Time-min | Every X min , the board will send a photo to the admin. set this to 0 to disbale time-lapsing feature. 297 | |Telegram Bot Token | This is the token you get from the BotFather. 298 | |Admin Chat ID | Telegram commands will only be processed if the Chat_id of the incomming Message is the same as this Admin_id or User_id ( a Security feature ). This could be chat_id of a Person or a Group where the bot is registered as admin. 299 | |User Chat ID | a second chat_id for controlling Teleview .Motion detection , time lapse and "I am Alive!" message will only be addressed to the AdminID. This could be chat_id of a Person or a Group where the bot is registered as admin. 300 | 301 | * If the chat_Id of the incomming message doesn't match either Admin_ID or User_id, then an echo message will be sent back with the chat id of the sender. 302 | ### CheckBoxes: 303 | Checkbox | Description 304 | :--------| :------------ 305 | Use Falsh when snapping a photo | Enable/Disable using the flash when capturing a photo. 306 | Horizontal Mirror | Horizontal Mirror effect on the captured image (left-right). 307 | Vertical Flip | Upside down effect on the Captured image. 308 | Screen Flip | Upside down of the OLED display. 309 | OLED Display is On | Enable/Disable The OLED display. 310 | Motion Detection Enabled | Enable/Disable the Motion Detection Feature. 311 | ... 312 | 313 | ### ComboBoxes: 314 | ComboBoxes | Description 315 | :--------| :------------ 316 | Select TZ name | Select the Time Zone region (currently used for naming files stored on SD card) 317 | Select Resolution | Select the Camera Resolution. (never use the QXGA) 318 | 319 | ### Buttons: 320 | Buttons | Description 321 | :--------| :------------ 322 | OK | Submit and Save the configuration. 323 | Delete Everything | Deletes all configuration data. except Wifi credentials. 324 | Cancel | Go back to AutoConnect Portal without saving. 325 | 326 | ## Sending Emails 327 | In The configuration Page, you can set up the email sending feature through SMTP server. 328 | I do not recommend to sed emails from your mail email account, rather create a new email account dedicated for sending photos only. the password is stored plain text in the flash. 329 | 330 | Example: 331 | Gmail SMTP Server Settings 332 | If you’re using a Gmail account, these are the SMTP Server details: 333 | 334 | SMTP Server: smtp.gmail.com 335 | SMTP username: Complete Gmail address 336 | SMTP password: Your Gmail password 337 | SMTP port (TLS): 587 338 | SMTP port (SSL): 465 339 | SMTP TLS/SSL required: yes 340 | 341 | Outlook SMTP Server Settings 342 | For Outlook accounts, these are the SMTP Server settings: 343 | 344 | SMTP Server: smtp.office365.com 345 | SMTP Username: Complete Outlook email address 346 | SMTP Password: Your Outlook password 347 | SMTP Port: 587 348 | SMTP TLS/SSL Required: Yes 349 | Live or Hotmail SMTP Server Settings 350 | For Live or Hotmail accounts, these are the SMTP Server settings: 351 | 352 | SMTP Server: smtp.live.com 353 | SMTP Username: Complete Live/Hotmail email address 354 | SMTP Password: Your Windows Live Hotmail password 355 | SMTP Port: 587 356 | SMTP TLS/SSL Required: Yes 357 | 358 | ## The computer Vision Motion Detection 359 | > this is an experimental feature, and I could use some help testing and refining it. 360 | I have been looking around for a mature library online for the ESP32 that is flexible and expandable in the CAM boards that it supports. 361 | But unfortunately, I couldn't find anything I could use. so I hade to build a simple-toy-algorithm in motionDetect.h by my self. 362 | * It works by: 363 | * taking two consequetive GRAYSCAL 320x240 frame captures. 364 | * counts the number of BLACK, WHITE , GRAY pixels in each frame. 365 | * Black Pixels have teh value of 0 to 85 366 | * White Pixels have the value of 255-85 to 255 367 | * the Gray Pixels have the value of 85 to 255-85 368 | * Compare the number of BLACK, WHITE, GRAY Pixesl, and if there is difference of more than XX % percent to the total count of pixels between the frames, then an alert is triggered. 369 | * So far, there are two control parameters that are configurable through the WebUI: 370 | * The Interval between the two consequetive frame captures in ms (i.e. 500 = half a second) 371 | * The percentage of change in previous frame pixels and current frame pixels. 372 | * One important use I could have; is to identify day and night and decide whether asnapshot should be taken or not during using the timelapse features. (not yet implemented) 373 | 374 | ## capture and streaming 375 | There are three enpoints for jpeg capture and streaming. 376 | 377 | > It is highly recommended **NOT** to contact the ESP with more than one client at the same time. 378 | 379 | ### streaming : /streaming 380 | This will stream in MJPEG as fast as it can. 381 | However, it will check for the telegram events every 25 frames which will make it a bit choppy. 382 | The code will auotmatically stop of the client is closed (once you close the browser tab or move to a another endpoint) 383 | > Content-Type: multipart/x-mixed-replace;boundary=... 384 | 385 | The CV motion detection will produce alot of false events during the streaming. 386 | I recomend to deactivate CVMotionDetect feature during streaming. 387 | 388 | ### Capture as an image : /capture 389 | will send a jpeg with the header: ( the picture will be displayed directly in the brwoser ) 390 | > Content-Disposition:inline; filename=capture.jpg 391 | 392 | ### Capture as an atatchment jpg file: /capture.jpg 393 | will send a jpeg with the header: (as an attachement or downloadable file) 394 | > Content-Disposition:attachment; filename=capture.jpg 395 | 396 | 397 | -------------------------------------------------------------------------------- /TeleView.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 Ahmed Maklad. All right reserved. 3 | 4 | Teleview- A telegram photo sending bot using the ESP32 CAM on Arduino IDE. 5 | 6 | This Software is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This Software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this Software; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* 22 | * To compile and upload: 23 | * Required Installation: 24 | * ESP32 for Arduino IDE https://dl.espressif.com/dl/package_esp32_index.json 25 | * Required Libraries to compile: 26 | * AutoConnect 27 | * ArduinoJson - V5.13.5 28 | * Adafruit SSD1306 and Dependecies such as Adaruit GFX..etc 29 | */ 30 | //****************************************************************// 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "soc/soc.h" 42 | #include "soc/rtc_cntl_reg.h" 43 | #include "esp_camera.h" 44 | 45 | #include 46 | static const char* TAG_MAIN = "MAIN"; 47 | #include "esp_log.h" 48 | 49 | // Select camera model 50 | //#define CAMERA_MODEL_WROVER_KIT 51 | //#define CAMERA_MODEL_ESP_EYE 52 | //#define CAMERA_MODEL_M5STACK_PSRAM // Board definition Boards->ESP32 Arduino->"M5Stack Timer-CAM" 53 | // Don't use the Boards->M5Stack Arduino ->"M5Stack Timer CAM" 54 | //#define CAMERA_MODEL_M5STACK_WIDE 55 | //#define CAMERA_MODEL_AI_THINKER // Board definition "AI Thinker ESP32-CAM" 56 | #define CAMERA_MODEL_TTGO_T1_CAMERA // Board definition "ESP32 WROVER Module" or "TTGO T1" 57 | // to Have OTA Working: 58 | // tools->Patition Schema-> Minimal SPIFFS(1.9MB with OTA/190KB SPIFFS) 59 | //#define CAMERA_MODEL_TTGO_T1_CAMERA_162 // Board definition "ESP32 WROVER Module" or "TTGO T1" 60 | // to Have OTA Working: 61 | // tools->Patition Schema-> Minimal SPIFFS(1.9MB with OTA/190KB SPIFFS) 62 | //#define CAMERA_MODEL_M5CAM // Board Difinition "AI Thinker ESP32-CAM" 63 | ////////////////////////////////////// // and set Tools-> Partiton Scheme --> Huge App (3MB No OTA/1MB SPIFF) 64 | #include "camera_pins.h" 65 | 66 | framesize_t maxRes=MAX_RESOULTION; 67 | 68 | ////////////////////////////////////// 69 | String compileDate=String(__DATE__); 70 | String compileTime=String(__TIME__); 71 | String compileCompiler=String(__cplusplus); 72 | int PICTURES_COUNT=0; 73 | ////////////////////////////////////// 74 | #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE 75 | #include "esp_log.h" 76 | static const char* TAG01 = "SETUP"; 77 | 78 | ////////////////////////////////////// 79 | #include "persist.h" 80 | void applyConfigItem (config_item* ci); 81 | void handle_telegram(void * param); 82 | #include "webPages.h" 83 | #include "motionDetect.h" 84 | 85 | #if defined(I2C_DISPLAY_ADDR) 86 | #include "display.h" 87 | #endif 88 | 89 | #include "telegram_utils.h" 90 | #define uS_TO_S_FACTOR 1000000 91 | 92 | /* One I day I will give up and remove this.. but not today. 93 | #if defined(SD_CARD_ON) 94 | #include "FSBRowser.h" 95 | #endif 96 | */ 97 | 98 | const char* ntpServer = "pool.ntp.org"; 99 | long gmtOffset_sec = 0; 100 | int daylightOffset_sec = 0; 101 | 102 | bool bTakePhotoTick=false; 103 | boolean bMotionDetected=false; 104 | boolean bESPMayGoToSleep=false; 105 | Ticker tkTimeLapse; 106 | //int consequentChangedFrames=0; 107 | 108 | //motionDetection 109 | bool haveMotion = false; 110 | // struct tm *startTM; you find it in webPages.h 111 | 112 | esp_sleep_wakeup_cause_t print_wakeup_reason(); 113 | 114 | //****************************************************************// 115 | void setup() { 116 | WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disbale the burnout reset 117 | Serial.begin(115200); 118 | ESP_LOGV(TAG_MAIN,"SETUP START: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); 119 | ESP_LOGV(TAG_MAIN,CAMERA_NAME); 120 | psramInit(); 121 | ESP_LOGV(TAG_MAIN,"Total heap: %d", ESP.getHeapSize()); 122 | ESP_LOGV(TAG_MAIN,"Free heap: %d", ESP.getFreeHeap()); 123 | ESP_LOGV(TAG_MAIN,"Total PSRAM: %d", ESP.getPsramSize()); 124 | ESP_LOGV(TAG_MAIN,"Free PSRAM: %d", ESP.getFreePsram()); 125 | #ifdef CAMERA_MODEL_M5STACK_PSRAM 126 | // will hold bat output 127 | bat_init(); 128 | led_init(CAMERA_LED_GPIO); 129 | bmm8563_init(); 130 | #endif 131 | delay (1000); 132 | Serial.setDebugOutput(true); 133 | esp_log_level_set("*", ESP_LOG_INFO); 134 | //esp_log_level_set("*", ESP_LOG_ERROR); // set all components to ERROR level 135 | //esp_log_level_set("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack 136 | //esp_log_level_set("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client 137 | 138 | ESP_LOGV(TAG_MAIN,"Compile Date:%s",compileDate); 139 | ESP_LOGV(TAG_MAIN,"Compile Time:%s",compileTime); 140 | ESP_LOGV(TAG_MAIN,"Compile Compiler:",compileCompiler); 141 | 142 | //Print the wakeup reason for ESP32 143 | print_wakeup_reason(); 144 | // 145 | //ESP CAMERA 146 | camera_config_t config; 147 | config.ledc_channel = LEDC_CHANNEL_0; 148 | config.ledc_timer = LEDC_TIMER_0; 149 | config.pin_d0 = Y2_GPIO_NUM; 150 | config.pin_d1 = Y3_GPIO_NUM; 151 | config.pin_d2 = Y4_GPIO_NUM; 152 | config.pin_d3 = Y5_GPIO_NUM; 153 | config.pin_d4 = Y6_GPIO_NUM; 154 | config.pin_d5 = Y7_GPIO_NUM; 155 | config.pin_d6 = Y8_GPIO_NUM; 156 | config.pin_d7 = Y9_GPIO_NUM; 157 | config.pin_xclk = XCLK_GPIO_NUM; 158 | config.pin_pclk = PCLK_GPIO_NUM; 159 | config.pin_vsync = VSYNC_GPIO_NUM; 160 | config.pin_href = HREF_GPIO_NUM; 161 | config.pin_sscb_sda = SIOD_GPIO_NUM; 162 | config.pin_sscb_scl = SIOC_GPIO_NUM; 163 | config.pin_pwdn = PWDN_GPIO_NUM; 164 | config.pin_reset = RESET_GPIO_NUM; 165 | config.xclk_freq_hz = 20000000; 166 | config.pixel_format = PIXFORMAT_JPEG; 167 | //init with high specs to pre-allocate larger buffers 168 | if(psramFound()){ 169 | maxRes=MAX_RESOULTION; 170 | config.frame_size = FRAMESIZE_UXGA; 171 | config.jpeg_quality = 10; //0-63 lower number means higher quality 172 | config.fb_count = 2; 173 | } else { 174 | maxRes=FRAMESIZE_SVGA; 175 | config.frame_size = FRAMESIZE_SVGA; 176 | config.jpeg_quality = 12; //0-63 lower number means higher quality 177 | config.fb_count = 1; 178 | } 179 | ESP_LOGV(TAG_MAIN,"frame_size: %s:%s", 180 | resolutions[config.frame_size][0], 181 | resolutions[config.frame_size][1] 182 | ); 183 | ESP_LOGV(TAG_MAIN,"jpeg_quality: %d",config.jpeg_quality); 184 | // camera init 185 | esp_err_t err = esp_camera_init(&config); 186 | if (err != ESP_OK) { 187 | ESP_LOGE(TAG_MAIN,"Camera init failed with error 0x%x", err); 188 | delay(1000); 189 | ESP.restart(); 190 | } 191 | bCameraInitiated=true; 192 | //////////////////////////// 193 | configItems.frameSize=config.frame_size; 194 | keyboardJson=formulateKeyboardJson(); 195 | configItems=loadConfiguration(); 196 | applyConfigItem(&configItems); 197 | ESP_LOGV(TAG_MAIN,"%s",printConfiguration(&configItems,"")); 198 | 199 | //////////////////////////// 200 | #if defined(I2C_DISPLAY_ADDR) 201 | display_init(); 202 | #endif 203 | //////////////////////////// 204 | /* 205 | #if defined(SD_CARD_ON) 206 | setupFSBrowser(); 207 | #endif 208 | */ 209 | ESP_LOGV("SETUP","Loading rootpage"); 210 | Portal.host().on("/",rootPage); 211 | ESP_LOGV("SETUP","Loading deletePage"); 212 | Portal.host().on("/delete",deletePage); 213 | ESP_LOGV("SETUP","Loading capturePage"); 214 | Portal.host().on("/capture",capturePage); 215 | ESP_LOGV("SETUP","Loading capturePageJpeg"); 216 | Portal.host().on("/capture.jpg",capturePageJpeg); 217 | ESP_LOGV("SETUP","Loading stream_handler"); 218 | Portal.host().on("/stream",stream_handler); 219 | //Portal.host().on(); 220 | //Portal.append("/capture2","Capture2",capture2Page); 221 | //Portal.append("/stream","Stream",stream_handler); 222 | // 223 | ESP_LOGV("SETUP","Loading AUX_CONFIGPAGE"); 224 | auxPageConfig.load(AUX_CONFIGPAGE); 225 | populateResolutionsSelects(auxPageConfig); 226 | auxPageConfig.on(onPage); 227 | // 228 | ESP_LOGV("SETUP","Loading AUX_CONAUX_CONFIG_EMAIL_PAGEFIGPAGE"); 229 | auxPageConfigEmail.load(AUX_CONFIG_EMAIL_PAGE); 230 | auxPageConfigEmail.on(onPageEmail); 231 | // 232 | ESP_LOGV("SETUP","Loading AUX_CAPTURE"); 233 | auxPageCapture.load(AUX_CAPTURE); 234 | auxPageCapture.on(onCapture); 235 | // 236 | ESP_LOGV("SETUP","Loading AUX_STREAM"); 237 | auxPageStream.load(AUX_STREAM); 238 | auxPageStream.on(onStream); 239 | // 240 | // 241 | Portal.join(auxPageCapture); 242 | Portal.join(auxPageConfig); 243 | Portal.join(auxPageConfigEmail); 244 | Portal.join(auxPageStream); 245 | //////////////////////////// 246 | acConfig.apid = configItems.deviceName; 247 | acConfig.psk = "tv-ei-694"; 248 | acConfig.hostName=configItems.deviceName; 249 | acConfig.autoRise=true; 250 | acConfig.title = "TeleView"; 251 | //AUTOCONNECT_USE_PREFERENCES 252 | acConfig.autoSave=AC_SAVECREDENTIAL_AUTO; 253 | //acConfig.portalTimeout = 60000; // It will time out in 60 seconds 254 | #define AC_DEBUG 1 255 | Portal.config(acConfig); 256 | Portal.onDetect(captivePortalStarted); 257 | WiFi.setSleep(false); 258 | WiFi.setHostname(configItems.deviceName.c_str()); 259 | ///////////////////////////// 260 | #if defined(I2C_DISPLAY_ADDR) 261 | line1=String(acConfig.apid); 262 | line2=String(acConfig.psk); 263 | line3=String(acConfig.apip.toString()); 264 | display_Textlines(line1,line2,line3); 265 | #endif 266 | 267 | ///////////////////////////// 268 | if (Portal.begin()) { 269 | ESP_LOGV(TAG_MAIN,"WiFi connected: %s " , WiFi.localIP().toString().c_str() ); 270 | //MDNS 271 | if (!MDNS.begin(configItems.deviceName.c_str())) { 272 | ESP_LOGE(TAG_MAIN,"Error setting up MDNS responder!"); 273 | while(1) { 274 | delay(1000); 275 | } 276 | } 277 | // Add service to MDNS-SD 278 | // With applying AutoConnect, the MDNS service must be started after 279 | // establishing a WiFi connection. 280 | MDNS.addService("http", "tcp", 80); 281 | ESP_LOGV(TAG_MAIN,"mDNS responder started"); 282 | }else{ 283 | ESP_LOGE(TAG_MAIN,"Portal not startd"); 284 | } 285 | ESP_LOGV(TAG_MAIN,"HTTP server started"); 286 | ESP_LOGV(TAG_MAIN,"Connected SSID: %s",WiFi.SSID() ); 287 | ESP_LOGV(TAG_MAIN,"RSSI:%d",WiFi.RSSI()); 288 | // 289 | byte mac[6]; 290 | WiFi.macAddress(mac); 291 | ESP_LOGV(TAG_MAIN,"WiFi MAC= %x:%x:%x:%x:%x:%x", 292 | mac[5], 293 | mac[4], 294 | mac[3], 295 | mac[2], 296 | mac[1], 297 | mac[0] 298 | ); 299 | // 300 | ESP_LOGV(TAG_MAIN,"IP address: %s",WiFi.localIP().toString().c_str()); 301 | // NTP ////////////////////////////// 302 | //init and get the time 303 | for (int i=0;i<=23;i++) { 304 | Timezone_t tempTz = TZ [i]; 305 | //if (strcmp(tempTz.zone,configItems.timeZone.c_str())==0){ 306 | if (configItems.timeZone.equals(tempTz.zone) ){ 307 | //configItems.timeZone=tempTz.zone; 308 | ntpServer=(char*)tempTz.ntpServer; 309 | gmtOffset_sec=tempTz.tzoff*60*60; 310 | break; 311 | } 312 | } 313 | /////////////////////////////////// 314 | daylightOffset_sec=0; 315 | configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); 316 | #ifdef CAMERA_MODEL_M5STACK_PSRAM 317 | struct tm timeinfo; 318 | if(!getLocalTime(&timeinfo)){ 319 | ESP_LOGV(TAG_MAIN,"Failed to obtain time"); 320 | return; 321 | } 322 | _rtc_data_t timeToSet; 323 | timeToSet.year=timeinfo.tm_year+1900; 324 | timeToSet.month=timeinfo.tm_mon+1; 325 | timeToSet.day=timeinfo.tm_mday; 326 | timeToSet.hour=timeinfo.tm_hour; 327 | timeToSet.second=timeinfo.tm_sec; 328 | bmm8563_setTime(&timeToSet); 329 | #endif 330 | /////////////////////////////////// 331 | botClient.setCACert(TELEGRAM_CERTIFICATE_ROOT); 332 | //botClient.setInsecure(); 333 | bot.updateToken(configItems.botTTelegram); 334 | if ( !configItems.botTTelegram.equals("0123456789") ) { 335 | ESP_LOGV(TAG_MAIN,"I am Alive :-) "); 336 | //botClient.setCACert(TELEGRAM_CERTIFICATE_ROOT); 337 | //botClient.setInsecure(); 338 | if(bot.getMe()){ 339 | ESP_LOGV(TAG_MAIN,"bot.getMe():TRUE"); 340 | }else{ 341 | ESP_LOGV(TAG_MAIN,"bot.getMe():FALSE"); 342 | } 343 | //bot.sendMessage(configItems.adminChatIds, "I am Alive!!", ""); 344 | bool bSendMessageWithReplyKeyboard=bot.sendMessageWithReplyKeyboard(configItems.adminChatIds, "I am Alive :-) ", "Markdown", formulateKeyboardJson(), true); 345 | if(bSendMessageWithReplyKeyboard){ 346 | ESP_LOGV(TAG_MAIN,"bSendMessageWithReplyKeyboard:TRUE"); 347 | }else{ 348 | ESP_LOGV(TAG_MAIN,"bSendMessageWithReplyKeyboard:FALSE"); 349 | } 350 | if (configItems.alertALL && configItems.userChatIds.toDouble()>0){ 351 | bot.sendMessageWithReplyKeyboard(configItems.userChatIds,"I am Alive :-) ", "Markdown", formulateKeyboardJson(), true); 352 | } 353 | }else{ 354 | ESP_LOGV(TAG_MAIN,"Bot Token not yet set, I am not alive yet :-( "); 355 | } 356 | 357 | /////////////////// 358 | #if defined(FLASH_LAMP_PIN) 359 | pinMode(FLASH_LAMP_PIN, OUTPUT); 360 | #endif 361 | #if defined(PIR_PIN) 362 | // depends on which type of PIR your are using 363 | if (PIR_PIN_ON) 364 | pinMode(PIR_PIN, INPUT_PULLDOWN); //set default incomming signal to LOW 365 | else 366 | pinMode(PIR_PIN, INPUT_PULLUP); //set default incomming signal to HIGH 367 | #endif 368 | #if defined(BUZZER_PIN) 369 | pinMode(BUZZER_PIN,OUTPUT); 370 | #endif 371 | bTelegramBotInitiated=true; 372 | ESP_LOGV(TAG_MAIN,"SETUP END: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); 373 | } 374 | //****************************************************************// 375 | //********************** The LOOP ****************************// 376 | //****************************************************************// 377 | void loop() { 378 | ESP_LOGV(TAG_MAIN,"START LOOP *********"); 379 | Portal.handleClient(); 380 | 381 | #if defined(PIR_PIN) 382 | int vPIR = digitalRead(PIR_PIN); 383 | /* 384 | Serial.print("PIR VALUE:"); 385 | ESP_LOGV(TAG_MAIN,vPIR ); 386 | */ 387 | if ( configItems.motDetectOn && vPIR==PIR_PIN_ON ) { 388 | String result= alertTelegram("PIR Motion Detected."); 389 | ESP_LOGV(TAG_MAIN,"result: %s ",result); 390 | bMotionDetected=true; 391 | delay(100); 392 | } else{ 393 | bMotionDetected=false; 394 | } 395 | #endif 396 | #if defined(BUZZER_PIN) 397 | if (bMotionDetected && configItems.useBuzzer){ 398 | // Active Buzzer 399 | ESP_LOGV(TAG_MAIN,"Buzzer ON"); 400 | digitalWrite (BUZZER_PIN, BUZZER_PIN_ON); //turn buzzer on 401 | delay(1000); 402 | }else{ 403 | //ESP_LOGV(TAG_MAIN,"Buzzer OFF"); 404 | digitalWrite (BUZZER_PIN, !BUZZER_PIN_ON); //turn buzzer off 405 | } 406 | #endif 407 | #if defined(I2C_DISPLAY_ADDR) 408 | line1=String(WiFi.SSID()); 409 | line2=" "; 410 | line3=WiFi.localIP().toString(); 411 | if (displayEnabled){ 412 | display_Textlines( line1, line2 , line3 ); 413 | }else{ 414 | display.clearDisplay(); 415 | display.display(); 416 | } 417 | #endif 418 | ////////////////////////////////////////////// 419 | // Computer Vision Motion Detection 420 | if (configItems.motionDetectVC ){ 421 | ESP_LOGV(TAG_MAIN,"CheckMotion before."); 422 | haveMotion = checkMotion(haveMotion , (&configItems)->frameSize ,haveMotion ); 423 | ESP_LOGV(TAG_MAIN,"CheckMotion After."); 424 | if (haveMotion) { 425 | alertTelegram("CV Motion detected"); 426 | } 427 | } 428 | 429 | handle_telegram(NULL); 430 | ESP_LOGV(TAG_MAIN,"END LOOP **********"); 431 | } 432 | //****************************************************************// 433 | void handle_telegram(void * param ){ 434 | ////////////////////////////////////////////// 435 | if (millis() > Bot_lasttime + Bot_mtbs) { 436 | ESP_LOGV(TAG_MAIN,"bot.getUpdates() !"); 437 | int numNewMessages = bot.getUpdates((bot.last_message_received) + 1); 438 | while(numNewMessages) { 439 | ESP_LOGV(TAG_MAIN,"got response#1"); 440 | handleNewMessages(numNewMessages); 441 | ESP_LOGV(TAG_MAIN,"got response#2"); 442 | numNewMessages = bot.getUpdates((bot.last_message_received) + 1); 443 | ESP_LOGV(TAG_MAIN,"got response#3"); 444 | } 445 | if (bTakePhotoTick){ 446 | alertTelegram("time-lapse tick!"); 447 | bTakePhotoTick=false; 448 | } 449 | ///////////////////////////////////////////// 450 | #ifdef CAMERA_MODEL_M5STACK_PSRAM 451 | rtc_date_t date; 452 | bmm8563_getTime(&date); 453 | ESP_LOGV(TAG_MAIN,"Time: %d/%d/%d %02d:%02d:%-2d", date.year, date.month, date.day, date.hour, date.minute, date.second); 454 | ESP_LOGV(TAG_MAIN,"volt: %d mv", bat_get_voltage()); 455 | ESP_LOGV(TAG_MAIN,"ADC: %d mv", bat_get_adc_raw()); 456 | #endif 457 | ///////////////////////////////////////////// 458 | #if defined(PIR_PIN) 459 | if (configItems.useDeepSleep && configItems.motDetectOn) { 460 | bMotionDetected=true; 461 | esp_sleep_enable_ext0_wakeup((gpio_num_t)PIR_PIN,PIR_PIN_ON); //1 = High, 0 = Low 462 | bESPMayGoToSleep=true; 463 | //alertTelegram("ESP is going to sleep till PIR is active.",true ); 464 | } 465 | #endif 466 | ///////////////////////////////////////////// 467 | if (configItems.useDeepSleep && configItems.lapseTime >0){ 468 | bTakePhotoTick=true; 469 | #ifdef CAMERA_MODEL_M5STACK_PSRAM 470 | // esp_deep_sleep((uint64_t) configItems.lapseTime*60*uS_TO_S_FACTOR); 471 | // X sec later will wake up 472 | ESP_LOGV(TAG_MAIN,"bmm8563_setTimerIRQ"); 473 | bmm8563_setTimerIRQ(configItems.lapseTime*60); 474 | #endif 475 | ESP_LOGV(TAG_MAIN,"esp_sleep_enable_timer_wakeup"); 476 | //esp_deep_sleep((uint64_t) configItems.lapseTime*60*uS_TO_S_FACTOR); 477 | esp_sleep_enable_timer_wakeup( (uint64_t) configItems.lapseTime*60*uS_TO_S_FACTOR); 478 | bESPMayGoToSleep=true; 479 | //alertTelegram("Setup ESP32 to sleep for every " + String(configItems.lapseTime) + " minutes",true); 480 | } 481 | ///////////////////////////////////////////// 482 | if (configItems.useDeepSleep && bESPMayGoToSleep){ 483 | Serial.flush(); 484 | String extraMessage; 485 | if (bMotionDetected){ 486 | extraMessage="till PIR is active."; 487 | } 488 | if (bTakePhotoTick){ 489 | extraMessage="for the next " + String(configItems.lapseTime) + " minutes."; 490 | } 491 | alertTelegram("ESP is going to sleep "+extraMessage,false); 492 | #ifdef CAMERA_MODEL_M5STACK_PSRAM 493 | // disable bat output, will wake up after 5 sec, Sleep current is 1~2μA 494 | ESP_LOGV(TAG_MAIN,"bat_disable_output"); 495 | bat_disable_output(); 496 | #endif 497 | ESP_LOGV(TAG_MAIN,"esp_deep_sleep_start"); 498 | esp_wifi_stop(); 499 | esp_deep_sleep_start(); 500 | } 501 | ///////////////////////////////////////////// 502 | Bot_lasttime = millis(); 503 | } 504 | } 505 | 506 | 507 | //////////////////////////////////////////////////////////////////////////// 508 | #ifdef CAMERA_MODEL_M5STACK_PSRAM 509 | void led_breathe_test() { 510 | for (int16_t i = 0; i < 1024; i++) { 511 | led_brightness(i); 512 | vTaskDelay(pdMS_TO_TICKS(1)); 513 | } 514 | 515 | for (int16_t i = 1023; i >= 0; i--) { 516 | led_brightness(i); 517 | vTaskDelay(pdMS_TO_TICKS(1)); 518 | } 519 | } 520 | #endif 521 | //////////////////////////////////////////////////////////////////////////// 522 | esp_sleep_wakeup_cause_t print_wakeup_reason(){ 523 | esp_sleep_wakeup_cause_t wakeup_reason; 524 | wakeup_reason = esp_sleep_get_wakeup_cause(); 525 | switch(wakeup_reason) 526 | { 527 | case ESP_SLEEP_WAKEUP_EXT0 : ESP_LOGV(TAG_MAIN,"Wakeup caused by external signal using RTC_IO"); break; 528 | case ESP_SLEEP_WAKEUP_EXT1 : ESP_LOGV(TAG_MAIN,"Wakeup caused by external signal using RTC_CNTL"); break; 529 | case ESP_SLEEP_WAKEUP_TIMER : ESP_LOGV(TAG_MAIN,"Wakeup caused by timer"); break; 530 | case ESP_SLEEP_WAKEUP_TOUCHPAD : ESP_LOGV(TAG_MAIN,"Wakeup caused by touchpad"); break; 531 | case ESP_SLEEP_WAKEUP_ULP : ESP_LOGV(TAG_MAIN,"Wakeup caused by ULP program"); break; 532 | default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; 533 | } 534 | return (wakeup_reason); 535 | } 536 | //////////////////////////////////////////////////////////////////////////// 537 | void tick(){ 538 | bTakePhotoTick=true; 539 | } 540 | //////////////////////////////////////////////////////////////////////////// 541 | void applyConfigItem (config_item* ci) { 542 | sensor_t * s = esp_camera_sensor_get(); 543 | // 544 | s->set_pixformat(s, PIXFORMAT_JPEG ); 545 | s->set_framesize(s, ci->frameSize); // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA 546 | s->set_hmirror(s, ci->hMirror); 547 | s->set_vflip(s, ci->vFlip); 548 | // 549 | //s->set_wb_mode(s,ci->set_whitebal); 550 | s->set_brightness(s,ci->set_brightness); 551 | s->set_contrast(s,ci->set_contrast); 552 | s->set_saturation(s,ci->set_saturation); 553 | s->set_quality(s,ci->jpegQuality); 554 | // 555 | ESP_LOGV(TAG_MAIN,"* > s->status.scale:%d",s->status.scale); 556 | ESP_LOGV(TAG_MAIN,"* > s->status.binning:%d",s->status.binning); 557 | ESP_LOGV(TAG_MAIN,"* > s->status.quality:%d",s->status.quality); //0 - 63 558 | ESP_LOGV(TAG_MAIN,"* > s->status.brightness:%d",s->status.brightness); //-2 - 2 559 | ESP_LOGV(TAG_MAIN,"* > s->status.contrast:%d",s->status.contrast); //-2 - 2 560 | ESP_LOGV(TAG_MAIN,"* > s->status.saturation:%d",s->status.saturation); //-2 - 2 561 | ESP_LOGV(TAG_MAIN,"* > s->status.sharpness:%d",s->status.sharpness); //-2 - 2 562 | ESP_LOGV(TAG_MAIN,"* > s->status.denoise:%d",s->status.denoise); 563 | ESP_LOGV(TAG_MAIN,"* > s->status.special_effect:%d",s->status.special_effect); //0 - 6 564 | ESP_LOGV(TAG_MAIN,"* > s->status.wb_mode:%d",s->status.wb_mode); //0 - 4 565 | ESP_LOGV(TAG_MAIN,"* > s->status.awb:%d",s->status.awb); 566 | ESP_LOGV(TAG_MAIN,"* > s->status.awb_gain:%d",s->status.awb_gain); 567 | ESP_LOGV(TAG_MAIN,"* > s->status.aec:%d",s->status.aec); 568 | ESP_LOGV(TAG_MAIN,"* > s->status.aec2:%d",s->status.aec2); 569 | ESP_LOGV(TAG_MAIN,"* > s->status.ae_level:%d",s->status.ae_level); //-2 - 2 570 | ESP_LOGV(TAG_MAIN,"* > s->status.aec_value:%d",s->status.aec_value); //0 - 1200 571 | ESP_LOGV(TAG_MAIN,"* > s->status.agc:%d",s->status.agc); 572 | ESP_LOGV(TAG_MAIN,"* > s->status.agc_gain:%d",s->status.agc_gain); //0 - 30 573 | ESP_LOGV(TAG_MAIN,"* > s->status.gainceiling:%d",s->status.gainceiling); //0 - 6 574 | ESP_LOGV(TAG_MAIN,"* > s->status.bpc:%d",s->status.bpc); 575 | ESP_LOGV(TAG_MAIN,"* > s->status.wpc:%d",s->status.wpc); 576 | ESP_LOGV(TAG_MAIN,"* > s->status.raw_gma:%d",s->status.raw_gma); 577 | ESP_LOGV(TAG_MAIN,"* > s->status.lenc:%d",s->status.lenc); 578 | ESP_LOGV(TAG_MAIN,"* > s->status.hmirror:%d",s->status.hmirror); 579 | ESP_LOGV(TAG_MAIN,"* > s->status.vflip:%d",s->status.vflip); 580 | ESP_LOGV(TAG_MAIN,"* > s->status.dcw:%d",s->status.dcw); 581 | ESP_LOGV(TAG_MAIN,"* > s->status.colorbar:%d",s->status.colorbar); 582 | delay(500); 583 | //*/ 584 | // non configurable params: 585 | /* 586 | set_special_effect() 587 | 0 – No Effect 588 | 1 – Negative 589 | 2 – Grayscale 590 | 3 – Red Tint 591 | 4 – Green Tint 592 | 5 – Blue Tint 593 | 6 – Sepia 594 | set_wb_mode() 595 | 0 – Auto 596 | 1 – Sunny 597 | 2 – Cloudy 598 | 3 – Office 599 | 4 – Home 600 | https://github.com/espressif/esp32-camera/blob/ec14f1d6f718571d8a7d2e537e03cebcc05e0ac8/driver/include/sensor.h 601 | typedef struct { 602 | framesize_t framesize;//0 - 10 603 | bool scale; 604 | bool binning; 605 | uint8_t quality;//0 - 63 606 | int8_t brightness;//-2 - 2 607 | int8_t contrast;//-2 - 2 608 | int8_t saturation;//-2 - 2 609 | int8_t sharpness;//-2 - 2 610 | uint8_t denoise; 611 | uint8_t special_effect;//0 - 6 612 | uint8_t wb_mode;//0 - 4 613 | uint8_t awb; 614 | uint8_t awb_gain; 615 | uint8_t aec; 616 | uint8_t aec2; 617 | int8_t ae_level;//-2 - 2 618 | uint16_t aec_value;//0 - 1200 619 | uint8_t agc; 620 | uint8_t agc_gain;//0 - 30 621 | uint8_t gainceiling;//0 - 6 622 | uint8_t bpc; 623 | uint8_t wpc; 624 | uint8_t raw_gma; 625 | uint8_t lenc; 626 | uint8_t hmirror; 627 | uint8_t vflip; 628 | uint8_t dcw; 629 | uint8_t colorbar; 630 | } camera_status_t; 631 | */ 632 | //s->set_gain_ctrl(s, 0); // auto gain off (1 or 0) 633 | //s->set_exposure_ctrl(s, 0); // auto exposure off (1 or 0) 634 | //s->set_agc_gain(s, 0); // set gain manually (0 - 30) 635 | //s->set_aec_value(s, 600); // set exposure manually (0-1200) 636 | // s->set_brightness(s, 0); // (-2 to 2) - set brightness 637 | // s->set_awb_gain(s, 0); // Auto White Balance? 638 | // s->set_lenc(s, 0); // lens correction? (1 or 0) 639 | // s->set_raw_gma(s, 1); // (1 or 0)? 640 | // s->set_quality(s, 10); // (0 - 63) 641 | // s->set_whitebal(s, 1); // white balance 642 | // s->set_wb_mode(s, 1); // white balance mode (0 to 4) 643 | // s->set_aec2(s, 0); // automatic exposure sensor? (0 or 1) 644 | // s->set_aec_value(s, 0); // automatic exposure correction? (0-1200) 645 | // s->set_saturation(s, 0); // (-2 to 2) 646 | // s->set_hmirror(s, 0); // (0 or 1) flip horizontally 647 | // s->set_gainceiling(s, GAINCEILING_32X); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) 648 | // s->set_contrast(s, 0); // (-2 to 2) 649 | // s->set_sharpness(s, 0); // (-2 to 2) 650 | // s->set_colorbar(s, 0); // (0 or 1) - testcard 651 | // s->set_special_effect(s, 2); // 0 – No Effect,1 – Negative,2 – Grayscale,3 – Red Tint,4 – Green Tint,5 – Blue Tint,6 – Sepia 652 | // s->set_ae_level(s, 0); // auto exposure levels (-2 to 2) 653 | // s->set_bpc(s, 0); // black pixel correction 654 | // s->set_wpc(s, 0); // white pixel correction 655 | // s->set_dcw(s, 1); // downsize enable? (1 or 0)? 656 | // 657 | tkTimeLapse.detach(); 658 | if (ci->lapseTime >0){ 659 | tkTimeLapse.attach( ci->lapseTime*60 , tick ); 660 | } 661 | #if defined(I2C_DISPLAY_ADDR) 662 | if (ci->screenFlip){ 663 | display.setRotation(2); 664 | }else{ 665 | display.setRotation(0); 666 | } 667 | displayEnabled=ci->screenOn; 668 | #endif 669 | } 670 | -------------------------------------------------------------------------------- /camera_pins.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_PINS_H 2 | #define CAMERA_PINS_H 3 | 4 | #if defined(CAMERA_MODEL_WROVER_KIT) 5 | #define CAMERA_NAME "CAMERA_MODEL_WROVER_KIT" 6 | #define PWDN_GPIO_NUM -1 7 | #define RESET_GPIO_NUM -1 8 | #define XCLK_GPIO_NUM 21 9 | #define SIOD_GPIO_NUM 26 10 | #define SIOC_GPIO_NUM 27 11 | 12 | #define Y9_GPIO_NUM 35 13 | #define Y8_GPIO_NUM 34 14 | #define Y7_GPIO_NUM 39 15 | #define Y6_GPIO_NUM 36 16 | #define Y5_GPIO_NUM 19 17 | #define Y4_GPIO_NUM 18 18 | #define Y3_GPIO_NUM 5 19 | #define Y2_GPIO_NUM 4 20 | #define VSYNC_GPIO_NUM 25 21 | #define HREF_GPIO_NUM 23 22 | #define PCLK_GPIO_NUM 22 23 | #define MAX_RESOULTION FRAMESIZE_UXGA 24 | 25 | #elif defined(CAMERA_MODEL_ESP_EYE) 26 | #define CAMERA_NAME "CAMERA_MODEL_ESP_EYE" 27 | #define PWDN_GPIO_NUM -1 28 | #define RESET_GPIO_NUM -1 29 | #define XCLK_GPIO_NUM 4 30 | #define SIOD_GPIO_NUM 18 31 | #define SIOC_GPIO_NUM 23 32 | 33 | #define Y9_GPIO_NUM 36 34 | #define Y8_GPIO_NUM 37 35 | #define Y7_GPIO_NUM 38 36 | #define Y6_GPIO_NUM 39 37 | #define Y5_GPIO_NUM 35 38 | #define Y4_GPIO_NUM 14 39 | #define Y3_GPIO_NUM 13 40 | #define Y2_GPIO_NUM 34 41 | #define VSYNC_GPIO_NUM 5 42 | #define HREF_GPIO_NUM 27 43 | #define PCLK_GPIO_NUM 25 44 | #define MAX_RESOULTION FRAMESIZE_UXGA 45 | 46 | #elif defined(CAMERA_MODEL_M5CAM) 47 | #define CAMERA_NAME "CAMERA_MODEL_M5CAM" 48 | #define PWDN_GPIO_NUM -1 49 | #define RESET_GPIO_NUM 15 50 | #define XCLK_GPIO_NUM 27 51 | #define SIOD_GPIO_NUM 25 52 | #define SIOC_GPIO_NUM 23 53 | 54 | #define Y9_GPIO_NUM 19 55 | #define Y8_GPIO_NUM 36 56 | #define Y7_GPIO_NUM 18 57 | #define Y6_GPIO_NUM 39 58 | #define Y5_GPIO_NUM 5 59 | #define Y4_GPIO_NUM 34 60 | #define Y3_GPIO_NUM 35 61 | #define Y2_GPIO_NUM 17 62 | #define VSYNC_GPIO_NUM 22 63 | #define HREF_GPIO_NUM 26 64 | #define PCLK_GPIO_NUM 21 65 | #define MAX_RESOULTION FRAMESIZE_UXGA 66 | 67 | #elif defined(CAMERA_MODEL_M5STACK_PSRAM) 68 | #define CAMERA_NAME "CAMERA_MODEL_M5STACK_PSRAM" 69 | #define PWDN_GPIO_NUM -1 70 | #define RESET_GPIO_NUM 15 71 | #define XCLK_GPIO_NUM 27 72 | #define SIOD_GPIO_NUM 25 73 | #define SIOC_GPIO_NUM 23 74 | 75 | #define Y9_GPIO_NUM 19 76 | #define Y8_GPIO_NUM 36 77 | #define Y7_GPIO_NUM 18 78 | #define Y6_GPIO_NUM 39 79 | #define Y5_GPIO_NUM 5 80 | #define Y4_GPIO_NUM 34 81 | #define Y3_GPIO_NUM 35 82 | #define Y2_GPIO_NUM 32 83 | #define VSYNC_GPIO_NUM 22 84 | #define HREF_GPIO_NUM 26 85 | #define PCLK_GPIO_NUM 21 86 | #define MAX_RESOULTION FRAMESIZE_QXGA 87 | 88 | //#define LED_PIN 2 89 | //#define BAT_OUTPUT_HOLD_PIN 33 90 | //#define BAT_ADC_PIN 38 91 | 92 | #define BM8563_SDA_PIN 12 93 | #define BM8563_SCL_PIN 14 94 | 95 | #define CAMERA_LED_GPIO 2 96 | #define BAT_OUTPUT_HOLD_PIN 33 97 | #define BAT_ADC_PIN 38 98 | #define Ext_PIN_1 4 99 | #define Ext_PIN_2 13 100 | #include "Arduino.h" 101 | #include "freertos/FreeRTOS.h" 102 | #include 103 | #include "battery.h" 104 | #include "led.h" 105 | #include "bmm8563.h" 106 | 107 | #elif defined(CAMERA_MODEL_M5STACK_WIDE) 108 | #define CAMERA_NAME "CAMERA_MODEL_M5STACK_WIDE" 109 | #define PWDN_GPIO_NUM -1 110 | #define RESET_GPIO_NUM 15 111 | #define XCLK_GPIO_NUM 27 112 | #define SIOD_GPIO_NUM 22 113 | #define SIOC_GPIO_NUM 23 114 | 115 | #define Y9_GPIO_NUM 19 116 | #define Y8_GPIO_NUM 36 117 | #define Y7_GPIO_NUM 18 118 | #define Y6_GPIO_NUM 39 119 | #define Y5_GPIO_NUM 5 120 | #define Y4_GPIO_NUM 34 121 | #define Y3_GPIO_NUM 35 122 | #define Y2_GPIO_NUM 32 123 | #define VSYNC_GPIO_NUM 25 124 | #define HREF_GPIO_NUM 26 125 | #define PCLK_GPIO_NUM 21 126 | #define MAX_RESOULTION FRAMESIZE_UXGA 127 | 128 | #elif defined(CAMERA_MODEL_AI_THINKER) //Board definition "AI Thinker ESP32-CAM" 129 | #define CAMERA_NAME "CAMERA_MODEL_AI_THINKER" 130 | #define PWDN_GPIO_NUM 32 131 | #define RESET_GPIO_NUM -1 132 | #define XCLK_GPIO_NUM 0 133 | #define SIOD_GPIO_NUM 26 134 | #define SIOC_GPIO_NUM 27 135 | 136 | #define Y9_GPIO_NUM 35 137 | #define Y8_GPIO_NUM 34 138 | #define Y7_GPIO_NUM 39 139 | #define Y6_GPIO_NUM 36 140 | #define Y5_GPIO_NUM 21 141 | #define Y4_GPIO_NUM 19 142 | #define Y3_GPIO_NUM 18 143 | #define Y2_GPIO_NUM 5 144 | #define VSYNC_GPIO_NUM 25 145 | #define HREF_GPIO_NUM 23 146 | #define PCLK_GPIO_NUM 22 147 | 148 | #define FLASH_LAMP_PIN 4 149 | 150 | // AI thinker has an SD CARD attached to it 151 | #define SD_CARD_ON true 152 | #define MAX_RESOULTION FRAMESIZE_UXGA 153 | 154 | #elif defined(CAMERA_MODEL_TTGO_T1_CAMERA) // Board definition "ESP32 WROVER Module" 155 | #define CAMERA_NAME "CAMERA_MODEL_TTGO_T1_CAMERA" 156 | #define PWDN_GPIO_NUM 26 157 | #define RESET_GPIO_NUM -1 158 | #define XCLK_GPIO_NUM 32 159 | #define SIOD_GPIO_NUM 13 160 | #define SIOC_GPIO_NUM 12 161 | 162 | #define Y9_GPIO_NUM 39 163 | #define Y8_GPIO_NUM 36 164 | #define Y7_GPIO_NUM 23 165 | #define Y6_GPIO_NUM 18 166 | #define Y5_GPIO_NUM 15 167 | #define Y4_GPIO_NUM 4 168 | #define Y3_GPIO_NUM 14 169 | #define Y2_GPIO_NUM 5 170 | #define VSYNC_GPIO_NUM 27 171 | #define HREF_GPIO_NUM 25 172 | #define PCLK_GPIO_NUM 19 173 | #define MAX_RESOULTION FRAMESIZE_UXGA 174 | 175 | #define SDA_PIN 21 176 | #define SCL_PIN 22 177 | 178 | #define I2C_DISPLAY_ADDR 0x3c 179 | #define USE_OLED_AS_FLASH 1 // the OLEDis on the same side as the camera 180 | //#define I2C_BME280_ADDR 0x3d 181 | #define PIR_PIN 33 //GPIO_INPUT_IO_33 //AS312 182 | #define PIR_PIN_ON HIGH 183 | #define BUTTON_PIN 34 184 | #elif defined(CAMERA_MODEL_TTGO_T1_CAMERA_162) // Board definition "LILYGO T1 v1.6.2 White with Mic" 185 | #define CAMERA_NAME "CAMERA_MODEL_TTGO_T1_CAMERA_162" 186 | #define PWDN_GPIO_NUM -1 187 | #define RESET_GPIO_NUM -1 188 | #define XCLK_GPIO_NUM 4 189 | #define SIOD_GPIO_NUM 18 190 | #define SIOC_GPIO_NUM 23 191 | 192 | /* 193 | // Sources: 194 | // https://github.com/lewisxhe/esp32-camera-series 195 | // https://github.com/Xinyuan-LilyGO/LilyGo-Camera-Series/tree/master/esphome 196 | // same as before 197 | #define Y9_GPIO_NUM 36 198 | #define Y8_GPIO_NUM 37 199 | #define Y7_GPIO_NUM 38 200 | #define Y6_GPIO_NUM 39 201 | #define Y5_GPIO_NUM 35 202 | #define Y4_GPIO_NUM 14 203 | #define Y3_GPIO_NUM 13 204 | #define Y2_GPIO_NUM 34 205 | */ 206 | /* 207 | // Source: Whats written on the package 208 | #define Y9_GPIO_NUM 13 209 | #define Y8_GPIO_NUM 34 210 | #define Y7_GPIO_NUM 36 211 | #define Y6_GPIO_NUM 37 212 | #define Y5_GPIO_NUM 38 213 | #define Y4_GPIO_NUM 39 214 | #define Y3_GPIO_NUM 35 215 | #define Y2_GPIO_NUM 14 216 | */ 217 | 218 | //SOurce https://github.com/Xinyuan-LilyGO/LilyGo-Camera-Series/blob/master/docs/T_CarmerV16.md 219 | #define Y9_GPIO_NUM 36 220 | #define Y8_GPIO_NUM 15 221 | #define Y7_GPIO_NUM 12 222 | #define Y6_GPIO_NUM 39 223 | #define Y5_GPIO_NUM 35 224 | #define Y4_GPIO_NUM 14 225 | #define Y3_GPIO_NUM 13 226 | #define Y2_GPIO_NUM 34 227 | 228 | #define VSYNC_GPIO_NUM 5 229 | #define HREF_GPIO_NUM 27 230 | #define PCLK_GPIO_NUM 25 231 | #define MAX_RESOULTION FRAMESIZE_UXGA 232 | 233 | #define SDA_PIN 21 234 | #define SCL_PIN 22 235 | 236 | #define I2C_DISPLAY_ADDR 0x3c 237 | #define USE_OLED_AS_FLASH 1 // the OLEDis on the same side as the camera 238 | //#define I2C_BME280_ADDR 0x3d 239 | #define PIR_PIN 19 //NOT RTC IO, i.e. Can't wake up the board from sleep 240 | #define PIR_PIN_ON HIGH 241 | #define BUTTON_PIN 15 242 | 243 | #define MIC_SCK 26 244 | #define MIC_WS 32 245 | #define MIC_SDO 33 246 | 247 | #define ENABLE_IP5306 248 | 249 | #else 250 | #error "Camera model not selected" 251 | #endif 252 | //////////////////////////////////////// 253 | //if you attach your own PIR sensor 254 | // notice: please check the PINS and if they are shared with any other devices 255 | /* 256 | #define PIR_PIN 13 257 | #define PIR_PIN_ON HIGH 258 | //*/ 259 | //////////////////////////////////////// 260 | //if there is an SD Attached 261 | // note: PINs 12 and 13 are used for the SD card on AI Thinker board. 262 | // i.e. don't attach PIR or Buzzer while having an SD card. 263 | /* 264 | #define SD_CARD_ON true 265 | //*/ 266 | //////////////////////////////////////// 267 | // in case you want to add a buzzer 268 | /* 269 | #define BUZZER_PIN 12 270 | #define BUZZER_PIN_ON HIGH 271 | //*/ 272 | //////////////////////////////////////// 273 | #if defined(FLASH_LAMP_PIN) 274 | #define IS_THERE_A_FLASH 1 275 | #endif 276 | 277 | #if defined(USE_OLED_AS_FLASH) 278 | #define IS_THERE_A_FLASH 1 279 | #endif 280 | //////////////////////////////////////// 281 | #endif //CAMERA_PINS_H 282 | -------------------------------------------------------------------------------- /display.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef DISPLAY_H 4 | #define DISPLAY_H 5 | #if defined(I2C_DISPLAY_ADDR) 6 | 7 | static const char* TAG_OLED = "DISPLAY"; 8 | #include "esp_log.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define SCREEN_WIDTH 128 // OLED display width, in pixels 15 | #define SCREEN_HEIGHT 64 // OLED display height, in pixels 16 | 17 | // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 18 | #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) 19 | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 20 | 21 | String line1=""; 22 | String line2=""; 23 | String line3=""; 24 | boolean displayEnabled=true; 25 | /////////////////////////////////////////// 26 | void display_init(); 27 | void display_Textlines(String lline1, String lline2, String lline3); 28 | void display_AllWhite(); 29 | void display_Clear(); 30 | /////////////////////////////////////////// 31 | void display_init(){ 32 | Wire.begin(SDA_PIN, SCL_PIN); 33 | // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally 34 | if(!display.begin(SSD1306_SWITCHCAPVCC, I2C_DISPLAY_ADDR)) { // Address 0x3D for 128x64 35 | ESP_LOGV( TAG_PS, "SSD1306 allocation failed"); 36 | for(;;); // Don't proceed, loop forever 37 | } 38 | // Show initial display buffer contents on the screen -- 39 | // the library initializes this with an Adafruit splash screen. 40 | display.clearDisplay(); 41 | display.display(); 42 | } 43 | 44 | void display_Textlines(String lline1, String lline2="", String lline3=""){ 45 | display.clearDisplay(); 46 | if (displayEnabled) { 47 | display.setCursor(0, 0); 48 | // 49 | display.setTextColor(SSD1306_WHITE); 50 | display.setTextSize(2); // Draw 2X-scale text 51 | display.println(lline1); 52 | // 53 | display.setTextColor(SSD1306_BLACK,SSD1306_WHITE); 54 | display.setTextSize(1); 55 | display.println(lline2); 56 | // 57 | display.setTextColor(SSD1306_WHITE); 58 | display.setTextSize(2); 59 | display.println(lline3); 60 | } 61 | // 62 | display.display(); 63 | } 64 | 65 | void display_AllWhite(){ 66 | display.clearDisplay(); 67 | if (displayEnabled) { 68 | display.fillRect(0, 0, display.width()*2, display.height()*2, SSD1306_INVERSE); 69 | } 70 | display.display(); 71 | } 72 | 73 | void display_Clear(){ 74 | display.clearDisplay(); 75 | display.display(); 76 | } 77 | #endif //defined(I2C_DISPLAY_ADDR) 78 | 79 | #endif //CAMERA_PINS_H 80 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | */.ipynb_checkpoints/* -------------------------------------------------------------------------------- /docs/AddConfigAttribute.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Add Configuration attribute to Teleview\n", 8 | "\n", 9 | "This notebook Helps you add a configuration attribute to the Teleview Project.\n", 10 | "\n", 11 | "So far it works only for adding boolean,string, int, double config parameters\n" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## TODO\n", 19 | "\n", 20 | "Feature | Comments | Status\n", 21 | ":----- | :---------- | :-----\n", 22 | "jpeg_Qualtiy: value | |Done\n", 23 | "WhiteBalance: value | |Done\n", 24 | "Brightness: value | |Done\n", 25 | "/sendVideo ? | |\n", 26 | "/sendAudio ? | Integrate I2S Microphone Request Audio |\n", 27 | "CVMotionDetect | motion detect by Image not PIR ON/OFF | (Almost)\n", 28 | "faceDetect | ON/OFF and face recognize then add to Photo Caption. https://github.com/Xinyuan-LilyGo/esp32-face-recognition|\n", 29 | "send-email | email,server,token..etc | DONE\n", 30 | "motionDetect Active times| define times where motion detection is enabled. |\n", 31 | "send photo at: | sunrise, noon, afternoon, late afternoon, sunset. with offset time option | \n", 32 | "GETGEOlOCATION | send location telegram message to the bot | \n", 33 | "NTP syncing | Requires NTP syncing. uses time zone ...etc. | DONE\n", 34 | "MQTT | integrate MQTT: on all events ,send an MQTT message |\n", 35 | "Product WebSite | build a website to host the User's Guide, Registration & ecommerce |\n", 36 | "JavaScript Validation| in the options page, have the elements more interactive and dimm inputs that are possible. example: send email checkbox is only enabled, when all the other email settings entered |\n", 37 | "M5 Camera-TimerX/F Battery & RTC features | Integrate M5 Camera-Timer X&F Battery and RTC API |\n", 38 | "use \"src/U8x8lib.h\" for LCD | use a differnt library for the LCD display: #include \"src/U8x8lib.h\" https://robotzero.one/ttgo-security-camera-pir/ |\n", 39 | "\n", 40 | "\n", 41 | "better Config saving|https://hieromon.github.io/AutoConnect/achandling.html#saving-autoconnectelements-with-json|\n", 42 | "Java Script validation of Input | https://hieromon.github.io/AutoConnect/achandling.html#using-javascript | \n", 43 | " " 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "## Setup Variables" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 10, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "persist_h=r'..\\persist.h'\n", 60 | "telegramUtils_h=r'..\\telegram_utils.h'\n", 61 | "webPages_h=r'..\\webPages.h'\n", 62 | "\n", 63 | "# strings will be added after this attribute\n", 64 | "# anchor_attribute=\"vFlip\"\n", 65 | "\n", 66 | "boolean_anchor_attribute=\"// [:ADD BOOLEAN HERE:]\"\n", 67 | "int_anchor_attribute=\"// [:ADD INT HERE:]\"\n", 68 | "string_anchor_attribute=\"// [:ADD STRING HERE:]\"" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 41, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "import re \n", 78 | "\n", 79 | "def returnMatch(theHash, new_attribute_type, line):\n", 80 | " line=line.strip()\n", 81 | " match = line.startswith(F\"{boolean_anchor_attribute}{theHash}\")\n", 82 | " if new_attribute_type==\"boolean\" :\n", 83 | " match = line.startswith(F\"{boolean_anchor_attribute}{theHash}\")\n", 84 | " elif new_attribute_type==\"String\" :\n", 85 | " match = line.startswith(F\"{string_anchor_attribute}{theHash}\")\n", 86 | " elif new_attribute_type==\"int\" :\n", 87 | " match =line.startswith(F\"{int_anchor_attribute}{theHash}\")\n", 88 | " elif new_attribute_type==\"double\" :\n", 89 | " match = line.startswith(F\"{int_anchor_attribute}{theHash}\")\n", 90 | " if (match):\n", 91 | " print (F\"theHash:{theHash}, new_attribute_type:{new_attribute_type}, match:{match}, line:{line}\")\n", 92 | " \n", 93 | " return(match)" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "## Changes in persist.h" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 18, 106 | "metadata": { 107 | "tags": [] 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "\n", 112 | "\n", 113 | "def changePersist(new_attribute_name, new_attribute_type, new_attribute_desc):\n", 114 | " with open(persist_h,\"r\") as persistH:\n", 115 | " lines=[]\n", 116 | " for line in persistH: \n", 117 | " ############################\n", 118 | " \n", 119 | " theHash=\"#1\" # struct config_item\n", 120 | " match1 = returnMatch(theHash,new_attribute_type,line)\n", 121 | " \n", 122 | " theHash=\"#2\" # configItems\n", 123 | " match2 = returnMatch(theHash,new_attribute_type,line)\n", 124 | " \n", 125 | " #theHash=\"#3\" # remove/delete\n", 126 | " #match3 = returnMatch(theHash,new_attribute_type,line)\n", 127 | "\n", 128 | " theHash=\"#4\" # loadConfiguration\n", 129 | " match4 = returnMatch(theHash,new_attribute_type,line)\n", 130 | "\n", 131 | " theHash=\"#5\" # saveConfiguration\n", 132 | " match5 = returnMatch(theHash,new_attribute_type,line)\n", 133 | " \n", 134 | " theHash=\"#6\" # printConfiguration\n", 135 | " match6 = returnMatch(theHash,new_attribute_type,line)\n", 136 | " ############################\n", 137 | " lines.append(line)\n", 138 | " new_line=\"\"\n", 139 | " new_line2=\"\"\n", 140 | " if match1:\n", 141 | " new_line=F\" {new_attribute_type} {new_attribute_name};\"\n", 142 | " if match2:\n", 143 | " if new_attribute_type==\"boolean\" :\n", 144 | " new_line=F\" .{new_attribute_name} = true,\"\n", 145 | " elif new_attribute_type==\"String\" :\n", 146 | " new_line=F\" .{new_attribute_name} = \\\"\\\",\"\n", 147 | " elif new_attribute_type in (\"int\",\"double\") :\n", 148 | " new_line=F\" .{new_attribute_name} = 0,\"\n", 149 | "\n", 150 | " #if match3:\n", 151 | " # new_line=F\" prefs.remove(\\\"{new_attribute_name}\\\");\"\n", 152 | "\n", 153 | " if match4:\n", 154 | " if new_attribute_type==\"boolean\" :\n", 155 | " new_line=F\" ci.{new_attribute_name} = prefs.getBool(\\\"{new_attribute_name}\\\",configItems.{new_attribute_name});\"\n", 156 | " elif new_attribute_type==\"String\" :\n", 157 | " new_line=F\" ci.{new_attribute_name} = prefs.getString(\\\"{new_attribute_name}\\\",configItems.{new_attribute_name});\"\n", 158 | " elif new_attribute_type==\"int\" :\n", 159 | " new_line=F\" ci.{new_attribute_name} = prefs.getInt(\\\"{new_attribute_name}\\\",configItems.{new_attribute_name});\"\n", 160 | " elif new_attribute_type==\"double\" :\n", 161 | " new_line=F\" ci.{new_attribute_name} = prefs.getDouble(\\\"{new_attribute_name}\\\",configItems.{new_attribute_name});\"\n", 162 | "\n", 163 | " if match5:\n", 164 | " if new_attribute_type==\"boolean\" :\n", 165 | " new_line=F' {{ prefs.putBool(\"{new_attribute_name}\",ci->{new_attribute_name}); }}'\n", 166 | " elif new_attribute_type==\"String\" :\n", 167 | " new_line =F' {{ prefs.putString(\"{new_attribute_name}\",ci->{new_attribute_name}); }}'\n", 168 | " elif new_attribute_type==\"int\" :\n", 169 | " new_line =F' {{ prefs.putInt(\"{new_attribute_name}\",ci->{new_attribute_name}); }}'\n", 170 | " elif new_attribute_type==\"double\" :\n", 171 | " new_line =F' {{ prefs.putDouble(\"{new_attribute_name}\",ci->{new_attribute_name}); }}'\n", 172 | "\n", 173 | " if match6:\n", 174 | " if new_attribute_type==\"boolean\" :\n", 175 | " new_line =F' result += prefix+\"{new_attribute_name} \"+sep+\"\";'\n", 176 | " new_line2=F' result += (ci->{new_attribute_name} ? String(\"true\") : String(\"false\")) + suffix;'\n", 177 | " elif new_attribute_type in (\"String\",\"int\",\"double\") :\n", 178 | " new_line =F' result += prefix+\"{new_attribute_name} \"+sep+\"\";'\n", 179 | " new_line2=F' result += String(ci->{new_attribute_name}) + suffix;'\n", 180 | " \n", 181 | " ############################\n", 182 | " if new_line!=\"\":\n", 183 | " print (new_line)\n", 184 | " lines.append(new_line+\"\\n\")\n", 185 | " if new_line2!=\"\":\n", 186 | " print (new_line2)\n", 187 | " lines.append(new_line2+\"\\n\")\n", 188 | " ############################\n", 189 | " new_persist_h=open(F\"{persist_h}_new\",\"w\")\n", 190 | " for line in lines:\n", 191 | " #print (line)\n", 192 | " new_persist_h.write(line)\n", 193 | " new_persist_h.close()\n", 194 | "\n", 195 | " if os.path.exists(F\"{persist_h}_orig\"):\n", 196 | " os.unlink(F\"{persist_h}_orig\")\n", 197 | " os.rename(persist_h,F\"{persist_h}_orig\")\n", 198 | " os.rename(F\"{persist_h}_new\",persist_h)" 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "## Changes in telegram_utils.h" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 19, 211 | "metadata": { 212 | "tags": [] 213 | }, 214 | "outputs": [], 215 | "source": [ 216 | "import re \n", 217 | "import os\n", 218 | "\n", 219 | "def changeTelegram_utils(new_attribute_name, new_attribute_type, new_attribute_desc):\n", 220 | " with open(telegramUtils_h,\"r\") as telegramUtilsH:\n", 221 | " lines=[]\n", 222 | " for line in telegramUtilsH:\n", 223 | " #print (line)\n", 224 | " ############################\n", 225 | " \n", 226 | " theHash=\"#1\"\n", 227 | " match1 = returnMatch(theHash,new_attribute_type,line)\n", 228 | "\n", 229 | " theHash=\"#2\"\n", 230 | " match2 = returnMatch(theHash,new_attribute_type,line)\n", 231 | "\n", 232 | " theHash=\"#3\"\n", 233 | " match3 = returnMatch(theHash,new_attribute_type,line)\n", 234 | " \n", 235 | " theHash=\"#4\"\n", 236 | " match4 = returnMatch(theHash,new_attribute_type,line)\n", 237 | " \n", 238 | " heHash=\"#5\"\n", 239 | " match5 = returnMatch(theHash,new_attribute_type,line)\n", 240 | " \n", 241 | " heHash=\"#6\"\n", 242 | " match6 = returnMatch(theHash,new_attribute_type,line)\n", 243 | " \n", 244 | " heHash=\"#7\"\n", 245 | " match7 = returnMatch(theHash,new_attribute_type,line)\n", 246 | " \n", 247 | " heHash=\"#8\"\n", 248 | " match8 = returnMatch(theHash,new_attribute_type,line)\n", 249 | " \n", 250 | " ############################\n", 251 | " lines.append(line)\n", 252 | " new_line=\"\"\n", 253 | " new_line2=\"\"\n", 254 | " new_line3=\"\"\n", 255 | " if match1:\n", 256 | " if new_attribute_type==\"boolean\" :\n", 257 | " new_line =F'\\n keyboardJson += R\"([{{ \"text\" : \"{new_attribute_desc} )\";'\n", 258 | " new_line2=F' keyboardJson += (configItems.{new_attribute_name}?\"ON\\\\u2705\":\"OFF\\\\u274C\");'\n", 259 | " new_line3=F' keyboardJson += R\"(\", \"callback_data\" : \"/{new_attribute_name}\" }}],)\";'\n", 260 | " \n", 261 | " if match2:\n", 262 | " if new_attribute_type==\"boolean\" :\n", 263 | " new_line =F' }}else if (text == \"/{new_attribute_name}\") {{'\n", 264 | " new_line2=F' configItems.{new_attribute_name} = !configItems.{new_attribute_name};'\n", 265 | " \n", 266 | " if match3:\n", 267 | " new_line =F' welcome += \"\\\\t {new_attribute_name} | {new_attribute_desc}\\\\n\";'\n", 268 | " \n", 269 | " if match4:\n", 270 | " new_line =F' welcome += \"\\\\t ,MODE_SET_{new_attribute_name}=XX \\\\n\";'\n", 271 | " \n", 272 | " if match5:\n", 273 | " new_line =F' if (bTeleAnsMode==MODE_SET_{new_attribute_name}) { \\\\n\";'\n", 274 | " new_line2 =F' bTeleAnsMode=MODE_SET_NONE; \\\\n\";'\n", 275 | " new_line3 =F' configItems.{new_attribute_name}=({new_attribute_type}) text.toInt(); \\} \\\\n\";'\n", 276 | " \n", 277 | " if match6:\n", 278 | " new_line =F' else if(text == \"/set{new_attribute_name}\") \\{ \\\\n\";'\n", 279 | " new_line2 =F' configItems.{new_attribute_name}=0; \\\\n\";'\n", 280 | " new_line3 =F' bot.sendMessage(chat_id, \"Please insert {new_attribute_desc}\", \"\"); \\\\n\";'\n", 281 | " new_line4 =F' bTeleAnsMode=MODE_SET_{new_attribute_name}; \\\\n\";'\n", 282 | " new_line5 =F' bPrintOptions=false; \\} \\\\n\";'\n", 283 | " \n", 284 | " if match7:\n", 285 | " new_line =F' lkeyboardJson += \",[\\\"/set{new_attribute_name}\\\"\"; \\\\n\";'\n", 286 | "\n", 287 | " if match8:\n", 288 | " new_line =F' welcome += \"/set{new_attribute_name} | Sets the {new_attribute_description}\\n\"; \\\\n\";'\n", 289 | " \n", 290 | " \n", 291 | " ############################\n", 292 | " if new_line!=\"\":\n", 293 | " print (new_line)\n", 294 | " lines.append(new_line+\"\\n\")\n", 295 | " if new_line2!=\"\":\n", 296 | " print (new_line2)\n", 297 | " lines.append(new_line2+\"\\n\")\n", 298 | " if new_line3!=\"\":\n", 299 | " print (new_line3)\n", 300 | " lines.append(new_line3+\"\\n\")\n", 301 | " ############################\n", 302 | " new_telegramUtils_h=open(F\"{telegramUtils_h}_new\",\"w\")\n", 303 | " for line in lines:\n", 304 | " #print (line)\n", 305 | " new_telegramUtils_h.write(line)\n", 306 | " new_telegramUtils_h.close()\n", 307 | " \n", 308 | " if os.path.exists(F\"{telegramUtils_h}_orig\"):\n", 309 | " os.unlink(F\"{telegramUtils_h}_orig\")\n", 310 | " os.rename(telegramUtils_h,F\"{telegramUtils_h}_orig\")\n", 311 | " os.rename(F\"{telegramUtils_h}_new\",telegramUtils_h)" 312 | ] 313 | }, 314 | { 315 | "cell_type": "markdown", 316 | "metadata": {}, 317 | "source": [ 318 | "## Changes in webPages.h" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": 21, 324 | "metadata": {}, 325 | "outputs": [], 326 | "source": [ 327 | "import re \n", 328 | "\n", 329 | "def changeWebPages(new_attribute_name, new_attribute_type, new_attribute_desc):\n", 330 | "\n", 331 | " with open(webPages_h,\"r\") as webPagesH:\n", 332 | " lines=[]\n", 333 | " for line in webPagesH:\n", 334 | " #print (line)\n", 335 | " ############################\n", 336 | " theHash=\"#1\"\n", 337 | " match1 = returnMatch(theHash,new_attribute_type,line)\n", 338 | "\n", 339 | " theHash=\"#2\"\n", 340 | " match2 = returnMatch(theHash,new_attribute_type,line)\n", 341 | "\n", 342 | "\n", 343 | " theHash=\"#3\"\n", 344 | " match3 = returnMatch(theHash,new_attribute_type,line)\n", 345 | " #lookfor=r'{ \"name\": \"XvFlip\",\"type\": \"ACCheckbox\",\"value\": \"\",\"labelPosition\": \"AC_Infront\" ,\"label\": \"Vertical Flip\",\"checked\": false,\"global\": true},'\n", 346 | " #lookfor=lookfor.replace(\"vFlip\",anchor_attribute)\n", 347 | " #match3 = re.search(re.escape(lookfor), line)\n", 348 | " \n", 349 | " ############################\n", 350 | " lines.append(line)\n", 351 | " new_line=\"\"\n", 352 | " new_line2=\"\"\n", 353 | " new_line3=\"\"\n", 354 | " if match1:\n", 355 | " if new_attribute_type==\"boolean\" :\n", 356 | " new_line =F' configItems.{new_attribute_name}=(args.hasArg(\"X{new_attribute_name}\")?true:false);'\n", 357 | " elif new_attribute_type in (\"String\") :\n", 358 | " new_line =F' configItems.{new_attribute_name}=(args.hasArg(\"X{new_attribute_name}\")?args.arg(\"X{new_attribute_name}\"):\"\");'\n", 359 | " elif new_attribute_type in (\"int\") :\n", 360 | " new_line =F' configItems.{new_attribute_name}=(args.hasArg(\"X{new_attribute_name}\")?args.arg(\"X{new_attribute_name}\").toInt():0);' \n", 361 | " elif new_attribute_type in (\"double\") :\n", 362 | " new_line =F' configItems.{new_attribute_name}=(args.hasArg(\"X{new_attribute_name}\")?args.arg(\"X{new_attribute_name}\").toDouble():0);'\n", 363 | "\n", 364 | " if match2:\n", 365 | " if new_attribute_type==\"boolean\" :\n", 366 | " new_line =F' aux[\"X{new_attribute_name}\"].as().checked=configItems.{new_attribute_name};'\n", 367 | " elif new_attribute_type in (\"String\",\"int\",\"double\") :\n", 368 | " new_line =F' aux[\"X{new_attribute_name}\"].as().value=configItems.{new_attribute_name};'\n", 369 | "\n", 370 | " if match3:\n", 371 | " if new_attribute_type==\"boolean\" :\n", 372 | " new_line =F' {{ \"name\": \"X{new_attribute_name}\", \"type\": \"ACCheckbox\", \"value\": \"\", \"labelPosition\": \"AC_Infront\" , \"label\": \"{new_attribute_desc}\", \"checked\": false, \"global\": true }},'\n", 373 | " elif new_attribute_type==\"String\" :\n", 374 | " new_line =F' {{ \"name\": \"X{new_attribute_name}\", \"type\": \"ACInput\", \"value\": \"\", \"labelPosition\": \"AC_Infront\" , \"label\": \"{new_attribute_desc}\",\"global\": true , \"placeholder\": \"{new_attribute_name}\", \"pattern\": \"^(.+)$\"}},'\n", 375 | " elif new_attribute_type==\"int\" :\n", 376 | " new_line =F' {{ \"name\": \"X{new_attribute_name}\", \"type\": \"ACInput\", \"value\": \"\", \"labelPosition\": \"AC_Infront\" , \"label\": \"{new_attribute_desc}\",\"global\": true , \"placeholder\": \"{new_attribute_name}\", \"pattern\": \"^([+-]?([0-9]*))$\"}},'\n", 377 | " elif new_attribute_type==\"double\" :\n", 378 | " new_line =F' {{ \"name\": \"X{new_attribute_name}\", \"type\": \"ACInput\", \"value\": \"\", \"labelPosition\": \"AC_Infront\" , \"label\": \"{new_attribute_desc}\",\"global\": true , \"placeholder\": \"{new_attribute_name}\", \"pattern\": \"^([+-]?([0-9]*[.])?[0-9]+)$\"}},'\n", 379 | " \n", 380 | " ############################\n", 381 | " if new_line!=\"\":\n", 382 | " print (new_line)\n", 383 | " lines.append(new_line+\"\\n\")\n", 384 | " if new_line2!=\"\":\n", 385 | " print (new_line2)\n", 386 | " lines.append(new_line2+\"\\n\")\n", 387 | " if new_line3!=\"\":\n", 388 | " print (new_line3)\n", 389 | " lines.append(new_line3+\"\\n\")\n", 390 | " ############################\n", 391 | " new_webPages_h=open(F\"{webPages_h}_new\",\"w\")\n", 392 | " for line in lines:\n", 393 | " #print (line)\n", 394 | " new_webPages_h.write(line)\n", 395 | " new_webPages_h.close()\n", 396 | "\n", 397 | " if os.path.exists(F\"{webPages_h}_orig\"):\n", 398 | " os.unlink(F\"{webPages_h}_orig\")\n", 399 | " os.rename(webPages_h,F\"{webPages_h}_orig\",)\n", 400 | " os.rename(F\"{webPages_h}_new\",webPages_h)" 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": {}, 406 | "source": [ 407 | "{\n", 408 | " \"name\": \"date\",\n", 409 | " \"type\": \"ACElement\",\n", 410 | " \"value\": \"\"\n", 411 | "}\n", 412 | "\n", 413 | "https://hieromon.github.io/AutoConnect/achandling.html#place-the-input-elements-within-a-form\n" 414 | ] 415 | }, 416 | { 417 | "cell_type": "markdown", 418 | "metadata": {}, 419 | "source": [ 420 | "" 421 | ] 422 | }, 423 | { 424 | "cell_type": "code", 425 | "execution_count": null, 426 | "metadata": { 427 | "tags": [] 428 | }, 429 | "outputs": [], 430 | "source": [ 431 | "\"\"\"\n", 432 | " ,{\n", 433 | " new_attribute_name: \"ftpServer\",\n", 434 | " new_attribute_type: \"boolean\",\n", 435 | " new_attribute_desc: \"Enable FTP server\",\n", 436 | " }\n", 437 | " ,{\n", 438 | " new_attribute_name: \"faceDetect\",\n", 439 | " new_attribute_type: \"boolean\",\n", 440 | " new_attribute_desc: \"Alert on detecting a face\",\n", 441 | " }\n", 442 | " ,{\n", 443 | " new_attribute_name: \"takeVideo\",\n", 444 | " new_attribute_type: \"boolean\",\n", 445 | " new_attribute_desc: \"Take a Video\",\n", 446 | " }\n", 447 | " ,{\n", 448 | " new_attribute_name: \"jpegQulaity\",\n", 449 | " new_attribute_type: \"int\",\n", 450 | " new_attribute_desc: \"Jpeg Quality 1-62[10-12]\",\n", 451 | " }\n", 452 | " ,{\n", 453 | " new_attribute_name: \"brightness\",\n", 454 | " new_attribute_type: \"int\",\n", 455 | " new_attribute_desc: \"PhotoBrightness 1-62[10-12]\",\n", 456 | " }\n", 457 | " ,{\n", 458 | " new_attribute_name: \"whiteBalance\",\n", 459 | " new_attribute_type: \"int\",\n", 460 | " new_attribute_desc: \"PhotoBrightness 1-62[10-12]\",\n", 461 | " }\n", 462 | " ,{\n", 463 | " new_attribute_name: \"sendEmail\",\n", 464 | " new_attribute_type: \"boolean\",\n", 465 | " new_attribute_desc: \"Alert by Email.\",\n", 466 | " }\n", 467 | " ,{\n", 468 | " new_attribute_name: \"adminEmail\",\n", 469 | " new_attribute_type: \"String\",\n", 470 | " new_attribute_desc: \"Email 1-dont use your real email, create a new one\",\n", 471 | " }\n", 472 | " ,{\n", 473 | " new_attribute_name: \"userEmail\",\n", 474 | " new_attribute_type: \"String\",\n", 475 | " new_attribute_desc: \"Email 2.\",\n", 476 | " }\n", 477 | " ,{\n", 478 | " new_attribute_name: \"sMTPServer\",\n", 479 | " new_attribute_type: \"String\",\n", 480 | " new_attribute_desc: \"SMTP Email Server\",\n", 481 | " }\n", 482 | " ,{\n", 483 | " new_attribute_name: \"sMTPUsername\",\n", 484 | " new_attribute_type: \"String\",\n", 485 | " new_attribute_desc: \"SMTP Email username\",\n", 486 | " }\n", 487 | " ,{\n", 488 | " new_attribute_name: \"sMTPPassword\",\n", 489 | " new_attribute_type: \"String\",\n", 490 | " new_attribute_desc: \"SMTP Email Password\",\n", 491 | " }\n", 492 | " ,{\n", 493 | " new_attribute_name: \"sMTPPort\",\n", 494 | " new_attribute_type: \"int\",\n", 495 | " new_attribute_desc: \"SMTP Email Port\",\n", 496 | " }\n", 497 | " ,{\n", 498 | " new_attribute_name: \"sMTPTLS\",\n", 499 | " new_attribute_type: \"String\",\n", 500 | " new_attribute_desc: \"SMTP TLS/SSL Required\",\n", 501 | " }\n", 502 | " ,{\n", 503 | " new_attribute_name: \"motionDetectFrom\",\n", 504 | " new_attribute_type: \"int\",\n", 505 | " new_attribute_desc: \"MotionDetect active from\",\n", 506 | " }\n", 507 | " ,{\n", 508 | " new_attribute_name: \"motionDetectTo\",\n", 509 | " new_attribute_type: \"int\",\n", 510 | " new_attribute_desc: \"MotionDetect active from\",\n", 511 | " }\n", 512 | " \n", 513 | " ,{\n", 514 | " new_attribute_name: \"longitude\",\n", 515 | " new_attribute_type: \"double\",\n", 516 | " new_attribute_desc: \"Location Longitude\",\n", 517 | " }\n", 518 | " ,{\n", 519 | " new_attribute_name: \"latitude\",\n", 520 | " new_attribute_type: \"double\",\n", 521 | " new_attribute_desc: \"Location Latitude\",\n", 522 | " }\n", 523 | " ,{\n", 524 | " new_attribute_name: \"altitude\",\n", 525 | " new_attribute_type: \"double\",\n", 526 | " new_attribute_desc: \"Location Altitude\",\n", 527 | " }\n", 528 | " ,{\n", 529 | " new_attribute_name: \"snapSunrise\",\n", 530 | " new_attribute_type: \"boolean\",\n", 531 | " new_attribute_desc: \"Snap a photo on Sunrise\",\n", 532 | " }\n", 533 | " ,{\n", 534 | " new_attribute_name: \"snapNoon\",\n", 535 | " new_attribute_type: \"boolean\",\n", 536 | " new_attribute_desc: \"Snap a photo on Noon\",\n", 537 | " }\n", 538 | " ,{\n", 539 | " new_attribute_name: \"snapAfterNoon\",\n", 540 | " new_attribute_type: \"boolean\",\n", 541 | " new_attribute_desc: \"Snap a photo on After Noon\",\n", 542 | " }\n", 543 | " ,{\n", 544 | " new_attribute_name: \"snapLateAfterNoon\",\n", 545 | " new_attribute_type: \"boolean\",\n", 546 | " new_attribute_desc: \"Snap a photo on Late After Noon\",\n", 547 | " }\n", 548 | " ,{\n", 549 | " new_attribute_name: \"snapSunset\",\n", 550 | " new_attribute_type: \"boolean\",\n", 551 | " new_attribute_desc: \"Snap a photo on Sunset\",\n", 552 | " }\n", 553 | " ,{\n", 554 | " new_attribute_name: \"snapEvening\",\n", 555 | " new_attribute_type: \"boolean\",\n", 556 | " new_attribute_desc: \"Snap a photo on Evening\",\n", 557 | " }\n", 558 | " \n", 559 | " {\n", 560 | " 'new_attribute_name': \"cvChangePercent\",\n", 561 | " 'new_attribute_type': \"int\",\n", 562 | " 'new_attribute_desc': \"CV Motion Detect Percent change in number of DarkPixels:\"\n", 563 | " },\n", 564 | " {\n", 565 | " 'new_attribute_name': \"cvIntervalSec\",\n", 566 | " 'new_attribute_type': \"int\",\n", 567 | " 'new_attribute_desc': \"CV Motion Detect interval between each 2 consecutive photo capture:\"\n", 568 | " },\n", 569 | "\"\"\"" 570 | ] 571 | }, 572 | { 573 | "cell_type": "markdown", 574 | "metadata": {}, 575 | "source": [ 576 | "## Assembly : add multiple attributes" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": 42, 582 | "metadata": {}, 583 | "outputs": [ 584 | { 585 | "name": "stdout", 586 | "output_type": "stream", 587 | "text": [ 588 | "{'new_attribute_name': 'cvChangePercent', 'new_attribute_type': 'int', 'new_attribute_desc': 'CV Motion Detect Percent change in number of DarkPixels:'}\n", 589 | "theHash:#1, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#1\n", 590 | " int cvChangePercent;\n", 591 | "theHash:#2, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#2\n", 592 | " .cvChangePercent = 0,\n", 593 | "theHash:#4, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#4\n", 594 | " ci.cvChangePercent = prefs.getInt(\"cvChangePercent\",configItems.cvChangePercent);\n", 595 | "theHash:#4, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#4\n", 596 | " ci.cvChangePercent = prefs.getInt(\"cvChangePercent\",configItems.cvChangePercent);\n", 597 | "theHash:#6, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#6\n", 598 | " result += prefix+\"cvChangePercent \"+sep+\"\";\n", 599 | " result += String(ci->cvChangePercent) + suffix;\n", 600 | "theHash:#1, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#1\n", 601 | " configItems.cvChangePercent=(args.hasArg(\"XcvChangePercent\")?args.arg(\"cvChangePercent\").toInt():0);\n", 602 | "theHash:#2, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#2\n", 603 | " aux[\"XcvChangePercent\"].as().value=configItems.cvChangePercent;\n", 604 | "theHash:#3, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#3\n", 605 | " { \"name\": \"XcvChangePercent\", \"type\": \"ACInput\", \"value\": \"\", \"labelPosition\": \"AC_Infront\" , \"label\": \"CV Motion Detect Percent change in number of DarkPixels:\",\"global\": true , \"placeholder\": \"cvChangePercent\", \"pattern\": \"^([+-]?([0-9]*))$\"},\n", 606 | "{'new_attribute_name': 'cvIntervalSec', 'new_attribute_type': 'int', 'new_attribute_desc': 'CV Motion Detect interval between each 2 consecutive photo capture:'}\n", 607 | "theHash:#1, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#1\n", 608 | " int cvIntervalSec;\n", 609 | "theHash:#2, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#2\n", 610 | " .cvIntervalSec = 0,\n", 611 | "theHash:#4, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#4\n", 612 | " ci.cvIntervalSec = prefs.getInt(\"cvIntervalSec\",configItems.cvIntervalSec);\n", 613 | "theHash:#4, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#4\n", 614 | " ci.cvIntervalSec = prefs.getInt(\"cvIntervalSec\",configItems.cvIntervalSec);\n", 615 | "theHash:#6, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#6\n", 616 | " result += prefix+\"cvIntervalSec \"+sep+\"\";\n", 617 | " result += String(ci->cvIntervalSec) + suffix;\n", 618 | "theHash:#1, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#1\n", 619 | " configItems.cvIntervalSec=(args.hasArg(\"XcvIntervalSec\")?args.arg(\"cvIntervalSec\").toInt():0);\n", 620 | "theHash:#2, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#2\n", 621 | " aux[\"XcvIntervalSec\"].as().value=configItems.cvIntervalSec;\n", 622 | "theHash:#3, new_attribute_type:int, match:True, line:// [:ADD INT HERE:]#3\n", 623 | " { \"name\": \"XcvIntervalSec\", \"type\": \"ACInput\", \"value\": \"\", \"labelPosition\": \"AC_Infront\" , \"label\": \"CV Motion Detect interval between each 2 consecutive photo capture:\",\"global\": true , \"placeholder\": \"cvIntervalSec\", \"pattern\": \"^([+-]?([0-9]*))$\"},\n" 624 | ] 625 | } 626 | ], 627 | "source": [ 628 | "\n", 629 | "attsToAdd = [ \n", 630 | " {\n", 631 | " 'new_attribute_name': \"cvChangePercent\",\n", 632 | " 'new_attribute_type': \"int\",\n", 633 | " 'new_attribute_desc': \"CV Motion Detect Percent change in number of DarkPixels:\"\n", 634 | " },\n", 635 | " {\n", 636 | " 'new_attribute_name': \"cvIntervalSec\",\n", 637 | " 'new_attribute_type': \"int\",\n", 638 | " 'new_attribute_desc': \"CV Motion Detect interval between each 2 consecutive photo capture:\"\n", 639 | " },\n", 640 | "]\n", 641 | "\n", 642 | "for att in attsToAdd:\n", 643 | " print(att)\n", 644 | " changePersist (att['new_attribute_name'],att['new_attribute_type'],att['new_attribute_desc'])\n", 645 | " changeTelegram_utils (att['new_attribute_name'],att['new_attribute_type'],att['new_attribute_desc'])\n", 646 | " changeWebPages (att['new_attribute_name'],att['new_attribute_type'],att['new_attribute_desc'])" 647 | ] 648 | } 649 | ], 650 | "metadata": { 651 | "kernelspec": { 652 | "display_name": "Python 3 (ipykernel)", 653 | "language": "python", 654 | "name": "python3" 655 | }, 656 | "language_info": { 657 | "codemirror_mode": { 658 | "name": "ipython", 659 | "version": 3 660 | }, 661 | "file_extension": ".py", 662 | "mimetype": "text/x-python", 663 | "name": "python", 664 | "nbconvert_exporter": "python", 665 | "pygments_lexer": "ipython3", 666 | "version": "3.9.13" 667 | } 668 | }, 669 | "nbformat": 4, 670 | "nbformat_minor": 4 671 | } 672 | -------------------------------------------------------------------------------- /docs/Photo_sendPhoto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asmaklad/TeleView/42b4487b22e3429bee54120e29d240f6c04273de/docs/Photo_sendPhoto.jpg -------------------------------------------------------------------------------- /docs/WebUI.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asmaklad/TeleView/42b4487b22e3429bee54120e29d240f6c04273de/docs/WebUI.jpg -------------------------------------------------------------------------------- /docs/changeRes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asmaklad/TeleView/42b4487b22e3429bee54120e29d240f6c04273de/docs/changeRes.jpg -------------------------------------------------------------------------------- /docs/extra_options.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asmaklad/TeleView/42b4487b22e3429bee54120e29d240f6c04273de/docs/extra_options.jpg -------------------------------------------------------------------------------- /docs/photo_sendOptions.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asmaklad/TeleView/42b4487b22e3429bee54120e29d240f6c04273de/docs/photo_sendOptions.jpg -------------------------------------------------------------------------------- /motionDetect.h: -------------------------------------------------------------------------------- 1 | #ifndef MOTION_DETECT_H 2 | #define MOTION_DETECT_H 3 | 4 | 5 | /* 6 | add this to identify and control the logging per module 7 | static const char* TAG = "MOTIONDETECT"; 8 | * macros: 9 | ESP_LOGE - error 10 | ESP_LOGW - warning 11 | ESP_LOGI - info 12 | ESP_LOGD - debug 13 | ESP_LOGV - verbose 14 | * Ex.: ESP_LOGW(TAG, "Baud rate error %.1f%%. Requested: %d baud, actual: %d baud", error * 100, baud_req, baud_real); 15 | * Additionally there is an _EARLY_ variant for each of these macros (e.g. ESP_EARLY_LOGE ). 16 | * To override default verbosity level at file or component scope 17 | #define LOG_LOCAL_LEVEL ESP_ESP_LOGVERBOSE 18 | * LOG LEVELS enum: 19 | ESP_LOG_NONE // No log output 20 | ESP_LOG_ERROR // Critical errors, software module can not recover on its own 21 | ESP_LOG_WARN // Error conditions from which recovery measures have been taken 22 | ESP_LOG_INFO // Information messages which describe normal flow of events 23 | ESP_LOG_DEBUG // Extra information which is not necessary for normal use (values, pointers, sizes, etc). 24 | ESP_ESP_LOGVERBOSE // Bigger chunks of debugging information, or frequent messages which can potentially flood the output. 25 | * To configure logging output per module at runtime, add calls to esp_log_level_set function: 26 | esp_log_level_set("*", ESP_LOG_ERROR); // set all components to ERROR level 27 | esp_log_level_set("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack 28 | esp_log_level_set("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client 29 | * to log HEX/CHAR and Binary Data: 30 | ESP_LOG_BUFFER_HEX_LEVEL(tag, buffer, buff_len, level) // Log a buffer of hex bytes at specified level, seprated into 16 bytes each line. 31 | ESP_LOG_BUFFER_CHAR_LEVEL(tag, buffer, buff_len, level) // Log a buffer of characters at specified level, seprated into 16 bytes each line. Buffer should contain only printable characters. 32 | ESP_LOG_BUFFER_HEXDUMP(tag, buffer, buff_len, level) // Dump a buffer to the log at specified level. 33 | * ESP32 Arduino also supports five levels but its macros are all lower-case like so: 34 | log_e – error (lowest) 35 | log_w – warning 36 | log_i – info 37 | log_d – debug 38 | ESP_LOGV – verbose (highest) 39 | The ESP32 Arduino Core supports but redefined the ESP-IDF macros. 40 | It simply routes them to their Arduino counter part. Hence, ESP_LOGE is routed to log_e. 41 | Please note that their macros don’t enforce the use of a tag for each message to group them. 42 | */ 43 | // Declarations 44 | 45 | static const char* TAG_MD = "MOTIONDETECT"; 46 | #include "esp_log.h" 47 | #include "sensor.h" 48 | #include "esp_camera.h" 49 | 50 | unsigned long lastSnap=0; 51 | unsigned long timeBetweenSnaps=500; 52 | 53 | struct frameData { 54 | size_t frameSize =1; 55 | size_t darkPixelsCount=1; 56 | size_t lightPixelsCount=1; 57 | size_t grayPixelsCount=1; 58 | uint8_t maxValue=0; 59 | uint8_t minValue=255; 60 | } frameDataCurrent, frameDataPrev; 61 | 62 | char strBuffMD[1000]; 63 | 64 | void printFrameData( struct frameData fd , char strBuffMD[] ); 65 | bool checkMotion(bool prevResult, framesize_t origFrameSize, bool prevChechResult ); 66 | float calcFrameDataDiff (int pixelsCountCurrent,int pixelsCountPrev, int totalPixels); 67 | //////////////////////////////////////// 68 | 69 | void printFrameData( struct frameData fd , char strBuffMD[]) { 70 | sprintf(strBuffMD, 71 | "maxValue: %3d, minValue: %3d, darkPixels: %5d, lightPixels: %5d, grayPixels: %5d, frameSize: %5d", 72 | fd.maxValue, 73 | fd.minValue, 74 | fd.darkPixelsCount, 75 | fd.lightPixelsCount, 76 | fd.grayPixelsCount, 77 | fd.frameSize 78 | ); 79 | } 80 | //////////////////////////////////////// 81 | float calcFrameDataDiff (int pixelsCountCurrent,int pixelsCountPrev, int totalPixels){ 82 | int diff = pixelsCountCurrent - pixelsCountPrev ; 83 | int absInt = abs(diff); 84 | float diffPercent = (float) 100.0* absInt/totalPixels ; 85 | ESP_LOGV(TAG_MD,"PixelsCount diff: %d, PixelsCount absInt: %d, PixelsCount diffPercent: %f, cvChangePercent: %d\%,cvIntervalSec: %d ", 86 | diff, 87 | absInt, 88 | diffPercent, 89 | configItems.cvChangePercent, 90 | configItems.cvIntervalSec); 91 | //Serial.printf("\t %d\n",configItems.cvChangePercent); 92 | return(diffPercent); 93 | } 94 | //////////////////////////////////////// 95 | bool checkMotion(bool prevResult, framesize_t origFrameSize ,bool prevChechResult){ 96 | bool result=false; 97 | 98 | if ( (millis()-lastSnap) < configItems.cvIntervalSec ){ 99 | return(false); 100 | } 101 | //*// 102 | // sensor adjest 103 | sensor_t * s = esp_camera_sensor_get(); 104 | /* 105 | 1. GRAYSCALE means values from 0 to 255 for each pixel 106 | 2. RGB565 means that a 16 bit value is per pixel and is comprised from 5 bits red, 6 bits green and 5 bits blue. 107 | 3. RGB888 means that there are 3 bytes per pixel, one for red, one for green and one for blue. Each vary between 0 and 255. 108 | 4. YUV means that each pixel has it's Y channel as a separate byte (0 to 255) and each two adjacent pixels share their U and V values. 109 | 6. JPEG means that the image is encoded into JPEG format either by the camera itself or in software. 110 | */ 111 | s->set_pixformat(s, PIXFORMAT_GRAYSCALE ); 112 | s->set_framesize(s, FRAMESIZE_QVGA); 113 | s->set_special_effect(s,3); 114 | s->set_colorbar(s, 1); 115 | //*/ 116 | camera_fb_t* frame = esp_camera_fb_get(); 117 | if (!frame) { 118 | ESP_LOGV(TAG_MD,"Failed capture"); 119 | delay(3000); 120 | return(false); 121 | } 122 | ESP_LOGV(TAG_MD, "WIDTH:%d,HEIGHT:%d",frame->width,frame->height); 123 | 124 | // do the BIG thing START %%%%%%%%%%%%%%%%%%%% 125 | // IDEA 2: compare number of dark pixes between the two GRAYSLACE pictures 126 | 127 | esp_camera_fb_return(frame); 128 | 129 | frameDataCurrent.frameSize = frame->len; 130 | size_t darkPixels=0; 131 | size_t lightPixels=0; 132 | size_t grayPixels=0; 133 | uint8_t maxValue=0; 134 | uint8_t minValue=255; 135 | for (size_t i=0;ilen;i++){ 136 | maxValue=( (frame->buf[i] > maxValue) ?frame->buf[i]:maxValue ); 137 | minValue=( (frame->buf[i] < minValue) ?frame->buf[i]:minValue ); 138 | //if ( frame->buf[i] > maxValue) maxValue=frame->buf[i]; 139 | /*/ debug Efforts 140 | Serial.printf("[%d]: %d ",i,frame->buf[i]); 141 | if ( (i % 8) ==0){ 142 | ESP_LOGV(TAG_MD,(""); 143 | } 144 | //*/ 145 | int lightMargin=85; 146 | if ( frame->buf[i] < lightMargin ) { 147 | lightPixels++; 148 | } else if ( frame->buf[i] > 255-lightMargin ) { 149 | darkPixels++; 150 | } else { 151 | grayPixels++; 152 | } 153 | } 154 | frameDataCurrent.darkPixelsCount = darkPixels; 155 | frameDataCurrent.lightPixelsCount = lightPixels; 156 | frameDataCurrent.grayPixelsCount = grayPixels; 157 | frameDataCurrent.maxValue = maxValue; 158 | 159 | // night and Day 160 | if (darkPixels > lightPixels ) { 161 | ESP_LOGV(TAG_MD,"it is Night time !"); 162 | } else { 163 | ESP_LOGV(TAG_MD,"it is Day time !"); 164 | } 165 | 166 | printFrameData(frameDataCurrent,strBuffMD); 167 | ESP_LOGV(TAG_MD, "frameDataCurrent:%s",strBuffMD); 168 | printFrameData(frameDataPrev,strBuffMD); 169 | ESP_LOGV(TAG_MD, "frameDataPrev :%s",strBuffMD); 170 | 171 | ESP_LOGV(TAG_MD,"diffPercent_dark:"); 172 | float diffPercent_dark =calcFrameDataDiff(frameDataCurrent.darkPixelsCount,frameDataPrev.darkPixelsCount,frameDataCurrent.frameSize); 173 | ESP_LOGV(TAG_MD,"diffPercent_light:"); 174 | float diffPercent_light =calcFrameDataDiff(frameDataCurrent.lightPixelsCount,frameDataPrev.lightPixelsCount,frameDataCurrent.frameSize); 175 | ESP_LOGV(TAG_MD,"diffPercent_gray:"); 176 | float diffPercent_gray =calcFrameDataDiff(frameDataCurrent.grayPixelsCount,frameDataPrev.grayPixelsCount,frameDataCurrent.frameSize); 177 | 178 | // if diff between two frames more than 10% 179 | if ( diffPercent_dark >= (float) configItems.cvChangePercent || 180 | diffPercent_light >= (float) configItems.cvChangePercent || 181 | diffPercent_gray >= (float) configItems.cvChangePercent ) { 182 | result=true; 183 | ESP_LOGV(TAG_MD,"Change Detected !!"); 184 | } else { 185 | result=false; 186 | } 187 | // do the BIG thing END %%%%%%%%%%%%%%%%%%%% 188 | // copy Current into Prev ( present always becomes past) 189 | memcpy(&frameDataPrev,&frameDataCurrent, sizeof(frameData)); 190 | //*// 191 | // return to origional state 192 | s->set_pixformat(s, PIXFORMAT_JPEG ); 193 | s->set_framesize(s, origFrameSize); 194 | s->set_special_effect(s,0); 195 | lastSnap=millis(); 196 | //*/ 197 | //don't send two snapshots only one. 198 | //but make already a snapshot. 199 | if (prevChechResult){ 200 | return (false); 201 | } 202 | return(result); 203 | } 204 | 205 | 206 | 207 | //////////////////////////////////////// 208 | #endif //CAMERA_PINS_H 209 | -------------------------------------------------------------------------------- /persist.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PERSIST_H 3 | #define PERSIST_H 4 | 5 | #include "esp_camera.h" 6 | 7 | #include 8 | #include // for type definitions 9 | 10 | 11 | static const char* TAG_PS = "PERSIST"; 12 | #include "esp_log.h" 13 | 14 | Preferences prefs; 15 | 16 | time_t timeOfLastPhoto=0; 17 | /* 18 | look in resolution_info_t resolution[FRAMESIZE_INVALID] 19 | in https://github.com/espressif/esp32-camera/blob/master/driver/sensor.c 20 | in https://github.com/espressif/esp32-camera/blob/master/driver/include/sensor.h framesize_t 21 | */ 22 | String resolutions[][13]={ 23 | // OV2640 2MP 24 | {"96X96", "96x96" }, // FRAMESIZE_96X96, // 96x96 25 | {"QQVGA", "160x120" }, // FRAMESIZE_QQVGA, // 160x120 26 | {"QCIF", "176x144" }, // FRAMESIZE_QCIF, // 176x144 27 | {"HQVGA", "240x176" }, // FRAMESIZE_HQVGA, // 240x176 28 | {"240X240", "240x240" }, // FRAMESIZE_240X240, // 240x240 29 | {"QVGA", "320x240" }, // FRAMESIZE_QVGA, // 320x240 30 | {"CIF", "400x296" }, // FRAMESIZE_CIF, // 400x296 31 | {"HVGA", "480x320" }, // FRAMESIZE_HVGA, // 480x320 32 | {"VGA", "640x480" }, // FRAMESIZE_VGA, // 640x480 33 | {"SVGA", "800x600" }, // FRAMESIZE_SVGA, // 800x600 34 | {"XGA", "1024x768" }, // FRAMESIZE_XGA, // 1024x768 35 | {"HD", "1280x720" }, // FRAMESIZE_HD, // 1280x720 36 | {"SXGA", "1280x1024" }, // FRAMESIZE_SXGA, // 1280x1024 37 | {"UXGA", "1600x1200" }, // FRAMESIZE_UXGA, // 1600x1200 38 | // OV3660 3MP 39 | {"FHD", "1920x1080" }, // FRAMESIZE_FHD, // 1600x1200 40 | {"PortraitHD", "720x1280" }, // FRAMESIZE_P_HD, // 1600x1200 41 | {"Portrait3MP","864x1536" }, // FRAMESIZE_P_3MP, // 1600x1200 42 | {"QXGA", "2048x1536" } // FRAMESIZE_QXGA, // 1600x1200 43 | }; 44 | /////////////////////////////////////////////////////////// 45 | typedef struct { 46 | const char* zone; 47 | const char* ntpServer; 48 | int8_t tzoff; 49 | } Timezone_t; 50 | /////////////////////////////////////////////////////////// 51 | static const Timezone_t TZ[] = { 52 | { "Europe/London", "europe.pool.ntp.org", 0 }, 53 | { "Europe/Berlin", "europe.pool.ntp.org", 1 }, 54 | { "Europe/Helsinki", "europe.pool.ntp.org", 2 }, 55 | { "Europe/Moscow", "europe.pool.ntp.org", 3 }, 56 | { "Asia/Dubai", "asia.pool.ntp.org", 4 }, 57 | { "Asia/Karachi", "asia.pool.ntp.org", 5 }, 58 | { "Asia/Dhaka", "asia.pool.ntp.org", 6 }, 59 | { "Asia/Jakarta", "asia.pool.ntp.org", 7 }, 60 | { "Asia/Manila", "asia.pool.ntp.org", 8 }, 61 | { "Asia/Tokyo", "asia.pool.ntp.org", 9 }, 62 | { "Australia/Brisbane", "oceania.pool.ntp.org", 10 }, 63 | { "Pacific/Noumea", "oceania.pool.ntp.org", 11 }, 64 | { "Pacific/Auckland", "oceania.pool.ntp.org", 12 }, 65 | { "Atlantic/Azores", "europe.pool.ntp.org", -1 }, 66 | { "America/Noronha", "south-america.pool.ntp.org", -2 }, 67 | { "America/Araguaina", "south-america.pool.ntp.org", -3 }, 68 | { "America/Blanc-Sablon", "north-america.pool.ntp.org", -4}, 69 | { "America/New_York", "north-america.pool.ntp.org", -5 }, 70 | { "America/Chicago", "north-america.pool.ntp.org", -6 }, 71 | { "America/Denver", "north-america.pool.ntp.org", -7 }, 72 | { "America/Los_Angeles", "north-america.pool.ntp.org", -8 }, 73 | { "America/Anchorage", "north-america.pool.ntp.org", -9 }, 74 | { "Pacific/Honolulu", "north-america.pool.ntp.org", -10 }, 75 | { "Pacific/Samoa", "oceania.pool.ntp.org", -11 } 76 | }; 77 | //////////////////////////////////////////////////////////////////////////// 78 | int matchResolutionText(String text){ 79 | int result=-1; 80 | ESP_LOGV(TAG_PS,"TESTX:%s",text); 81 | for (int i=0;i<=maxRes;i++){ 82 | ESP_LOGV( TAG_PS,"compareTo:/%s",resolutions[i][0] ); 83 | if ((text.compareTo(String("/"+resolutions[i][0])))==0){ 84 | result=i; 85 | break; 86 | } 87 | } 88 | if (result==-1){ 89 | ESP_LOGV(TAG_PS,"matchResolutionText:No Match Found!"); 90 | } 91 | return( result); 92 | } 93 | //////////////////////////////////////////////////////////////////////////// 94 | struct config_item { 95 | // [:ADD BOOLEAN HERE:]#1 96 | boolean useFlash; 97 | boolean hMirror; 98 | boolean vFlip; 99 | boolean sendEmail; 100 | boolean motionDetectVC; 101 | boolean alertALL; 102 | boolean saveToSD; 103 | boolean useDeepSleep; 104 | boolean useBuzzer; 105 | boolean screenFlip; 106 | boolean screenOn; 107 | boolean motDetectOn; 108 | boolean sMTPTLS; 109 | boolean webCaptureOn; 110 | // [:ADD INT HERE:]#1 111 | int cvIntervalSec; 112 | int cvChangePercent; 113 | int set_whitebal; 114 | int set_saturation; 115 | int set_contrast; 116 | int set_brightness; 117 | int jpegQuality; 118 | int sMTPPort; 119 | int lapseTime; 120 | framesize_t frameSize; 121 | // [:ADD STRING HERE:]#1 122 | String version; 123 | String sMTPPassword; 124 | String sMTPUsername; 125 | String sMTPServer; 126 | String userEmail; 127 | String adminEmail; 128 | String deviceName; 129 | String botTTelegram; 130 | String adminChatIds; 131 | String userChatIds; 132 | String timeZone; 133 | } configItems { 134 | // [:ADD BOOLEAN HERE:]#2 135 | .useFlash = true, 136 | .hMirror = true, 137 | .vFlip = true, 138 | .sendEmail = false, 139 | .motionDetectVC = false, 140 | .alertALL = false, 141 | .saveToSD = true, 142 | .useDeepSleep = false, 143 | .useBuzzer = true, 144 | .screenFlip = true, 145 | .screenOn=true, 146 | .motDetectOn=false, 147 | .sMTPTLS = false, 148 | .webCaptureOn=true, 149 | // [:ADD INT HERE:]#2 150 | .cvIntervalSec = 500, 151 | .cvChangePercent = 10, 152 | .set_whitebal = 1, 153 | .set_saturation = 0, 154 | .set_contrast = 0, 155 | .set_brightness = 0, 156 | .jpegQuality = 12, 157 | .sMTPPort = 0, 158 | .lapseTime=60, 159 | .frameSize = FRAMESIZE_CIF, 160 | // [:ADD STRING HERE:]#2 161 | .version="v1.10", 162 | .sMTPPassword = "", 163 | .sMTPUsername = "", 164 | .sMTPServer = "", 165 | .userEmail = "", 166 | .adminEmail = "", 167 | .deviceName = String("TeleView"), 168 | .botTTelegram = String("0123456789"), 169 | .adminChatIds = String("0123456789"), 170 | .userChatIds = String("0123456789"), 171 | .timeZone="Europe/Berlin" 172 | }; 173 | 174 | //////////////////////////////////////////////////////////////////////////// 175 | config_item loadConfiguration(); 176 | boolean saveConfiguration(config_item* ci); 177 | void deleteConfiguration(); 178 | String printConfiguration(config_item* ci,char* prefixC="",char* suffixC="\n",char* sep="|"); 179 | //////////////////////////////////////////////////////////////////////////// 180 | void deleteConfiguration(){ 181 | if (!prefs.begin("settings",false)) // False=RW 182 | { 183 | ESP_LOGV(TAG_PS,"failed find settings prefrences! returning default."); 184 | prefs.end(); 185 | return ; 186 | }else{ 187 | ESP_LOGV(TAG_PS,"Deleting all settings"); 188 | prefs.clear(); 189 | prefs.end(); 190 | } 191 | } 192 | //////////////////////////////////////////////////////////////////////////// 193 | config_item loadConfiguration() { 194 | config_item ci ; 195 | if (!prefs.begin("settings",true)) 196 | { 197 | // Write Default 198 | saveConfiguration (&configItems); 199 | ESP_LOGV(TAG_PS,"failed find settings prefrences! returning default."); 200 | prefs.end(); 201 | return(configItems); 202 | }else{ 203 | ESP_LOGV(TAG_PS,"found settings prefrences."); 204 | // [:ADD BOOLEAN HERE:]#4 205 | ci.useFlash = prefs.getBool("useFlash",configItems.useFlash); 206 | ci.hMirror = prefs.getBool("hMirror",configItems.hMirror); 207 | ci.vFlip = prefs.getBool("vFlip",configItems.vFlip); 208 | ci.sMTPTLS = prefs.getBool("sMTPTLS",configItems.sMTPTLS); 209 | ci.sendEmail = prefs.getBool("sendEmail",configItems.sendEmail); 210 | ci.motionDetectVC = prefs.getBool("motionDetectVC",configItems.motionDetectVC); 211 | ci.alertALL = prefs.getBool("alertALL",configItems.alertALL); 212 | ci.saveToSD = prefs.getBool("saveToSD",configItems.saveToSD); 213 | ci.useDeepSleep = prefs.getBool("useDeepSleep",configItems.useDeepSleep); 214 | ci.useBuzzer = prefs.getBool("useBuzzer",configItems.useBuzzer); 215 | ci.screenFlip = prefs.getBool("screenFlip",configItems.screenFlip); 216 | ci.screenFlip = prefs.getBool("screenFlip",configItems.screenFlip); 217 | ci.screenOn = prefs.getBool("screenOn",configItems.screenOn); 218 | ci.motDetectOn = prefs.getBool("motDetectOn",configItems.motDetectOn); 219 | ci.webCaptureOn=prefs.getBool("webCaptureOn",configItems.webCaptureOn); 220 | // [:ADD INT HERE:]#4 221 | ci.cvIntervalSec = prefs.getInt("cvIntervalSec",configItems.cvIntervalSec); 222 | ci.cvChangePercent = prefs.getInt("cvChangePercent",configItems.cvChangePercent); 223 | ci.sMTPPort = prefs.getInt("sMTPPort",configItems.sMTPPort); 224 | ci.set_whitebal = prefs.getInt("set_whitebal",configItems.set_whitebal); 225 | ci.set_saturation = prefs.getInt("set_saturation",configItems.set_saturation); 226 | ci.set_contrast = prefs.getInt("set_contrast",configItems.set_contrast); 227 | ci.set_brightness = prefs.getInt("set_brightness",configItems.set_brightness); 228 | ci.jpegQuality = prefs.getInt("jpegQuality",configItems.jpegQuality); 229 | ci.lapseTime=prefs.getInt("lapseTime",configItems.lapseTime); 230 | ci.frameSize = (framesize_t) prefs.getUInt("frameSize",configItems.frameSize); 231 | // [:ADD STRING HERE:]#5 232 | ci.sMTPPassword = prefs.getString("sMTPPassword",configItems.sMTPPassword); 233 | ci.sMTPUsername = prefs.getString("sMTPUsername",configItems.sMTPUsername); 234 | ci.sMTPServer = prefs.getString("sMTPServer",configItems.sMTPServer); 235 | ci.userEmail = prefs.getString("userEmail",configItems.userEmail); 236 | ci.adminEmail = prefs.getString("adminEmail",configItems.adminEmail); 237 | ci.deviceName=prefs.getString("deviceName",configItems.deviceName); 238 | ci.botTTelegram=prefs.getString("botTTelegram",configItems.botTTelegram); 239 | ci.adminChatIds=prefs.getString("adminChatIds",configItems.adminChatIds); 240 | ci.userChatIds=prefs.getString("userChatIds",configItems.userChatIds); 241 | ci.timeZone=prefs.getString("timeZone",configItems.timeZone); 242 | ci.version=prefs.getString("version",configItems.version); 243 | 244 | prefs.end(); 245 | } 246 | return(ci); 247 | } 248 | //////////////////////////////////////////////////////////////////////////// 249 | boolean saveConfiguration(config_item* ci) { 250 | ESP_LOGV(TAG_PS,"saveConfiguration:EEPROM Write:start"); 251 | boolean bDirty=false; 252 | if (!prefs.begin("settings",false)){ //false=RW , true=RO 253 | ESP_LOGV(TAG_PS,"ERROR: failed to load settings for RW."); 254 | prefs.end(); 255 | return (false); 256 | }else{ 257 | // [:ADD BOOLEAN HERE:]#5 258 | prefs.putBool("useFlash",ci->useFlash); 259 | prefs.putBool("hMirror",ci->hMirror); 260 | prefs.putBool("vFlip",ci->vFlip); 261 | prefs.putBool("sendEmail",ci->sendEmail); 262 | prefs.putBool("motionDetectVC",ci->motionDetectVC); 263 | prefs.putBool("alertALL",ci->alertALL); 264 | prefs.putBool("saveToSD",ci->saveToSD); 265 | prefs.putBool("useDeepSleep",ci->useDeepSleep); 266 | prefs.putBool("useBuzzer",ci->useBuzzer); 267 | prefs.putBool("screenFlip",ci->screenFlip); 268 | prefs.putBool("screenOn",ci->screenOn); 269 | prefs.putBool("motDetectOn",ci->motDetectOn); 270 | prefs.putBool("webCaptureOn",ci->webCaptureOn); 271 | prefs.putBool("sMTPTLS",ci->sMTPTLS); 272 | // [:ADD INT HERE:]#5 273 | prefs.putInt("cvIntervalSec",ci->cvIntervalSec); 274 | prefs.putInt("cvChangePercent",ci->cvChangePercent); 275 | prefs.putInt("set_whitebal",ci->set_whitebal); 276 | prefs.putInt("set_saturation",ci->set_saturation); 277 | prefs.putInt("set_contrast",ci->set_contrast); 278 | prefs.putInt("set_brightness",ci->set_brightness); 279 | prefs.putInt("jpegQuality",ci->jpegQuality); 280 | prefs.putInt("sMTPPort",ci->sMTPPort); 281 | prefs.putInt("lapseTime",ci->lapseTime); 282 | prefs.putUInt("frameSize", (unsigned int) (ci->frameSize) ); 283 | // [:ADD STRING HERE:]#5 284 | prefs.putString("version",ci->version); 285 | prefs.putString("sMTPPassword",ci->sMTPPassword); 286 | prefs.putString("sMTPUsername",ci->sMTPUsername); 287 | prefs.putString("sMTPServer",ci->sMTPServer); 288 | prefs.putString("userEmail",ci->userEmail); 289 | prefs.putString("adminEmail",ci->adminEmail); 290 | prefs.putString("deviceName",ci->deviceName); 291 | prefs.putString("botTTelegram",ci->botTTelegram); 292 | prefs.putString("adminChatIds",ci->adminChatIds); 293 | prefs.putString("userChatIds",ci->userChatIds); 294 | prefs.putString("timeZone",ci->timeZone); 295 | prefs.end(); 296 | } 297 | prefs.end(); 298 | ESP_LOGV(TAG_PS,"saveConfiguration:EEPROM Write:End"); 299 | return(bDirty); 300 | } 301 | 302 | //////////////////////////////////////////////////////////////////////////// 303 | String printConfiguration(config_item* ci,char* prefixC,char* suffixC,char* sep) { 304 | String result = ""; 305 | String prefix=String(prefixC); 306 | String suffix=String(suffixC); 307 | ////// 308 | result += prefix+"Local IP:"+sep+""; 309 | result += "" + WiFi.localIP().toString() +"" + suffix; 310 | result += prefix+"Local URL:"+sep+""; 311 | result += "" + ci->deviceName +".local" + suffix; 312 | result += "
\n";
313 |   
314 |   result += prefix+" *Attribute*     "+sep+" *Value* "+suffix;
315 |   result += prefix+"Device Name      "+sep+"";
316 |   result += ci->deviceName + suffix;
317 |   result += prefix+"Version          "+sep+"";
318 |   result += ci->version + suffix;
319 |   result += prefix+"WIFI SSID        "+sep+"";
320 |   result += WiFi.SSID() + suffix;
321 |   
322 |   result += prefix+"PSRAM ?          "+sep+"";
323 |   result += (psramFound() ? String("true") : String("false")) +suffix;
324 |   result += prefix+"PSRAM SIZE       "+sep+"";
325 |   result += ESP.getPsramSize() +suffix;
326 |   result += prefix+"Sketch MD5 "+sep+"";
327 |   result += ESP.getSketchMD5() +suffix;
328 |   result += prefix+"compileDate      "+sep+"";
329 |   result += compileDate +suffix;
330 |   result += prefix+"compileTime      "+sep+"";
331 |   
332 |   result += compileTime +suffix;
333 |   result += prefix+"compileCompiler  "+sep+"";
334 |   result += compileCompiler +suffix;
335 |   
336 |   result += prefix+"Chip Model       "+sep+"";
337 |   result += ESP.getChipModel()  +suffix;
338 |   
339 |   result += prefix+"Chip Revision    "+sep+"";
340 |   result += ESP.getChipRevision()  +suffix;
341 |   
342 |   result += prefix+"Chip Cores       "+sep+"";
343 |   result += ESP.getChipCores()  +suffix;
344 |   //////////////////////////////////////////////
345 |   struct tm *tm;
346 |   time_t  t;
347 |   char    dateTime[100];
348 |   t = time(NULL);
349 |   tm = localtime(&t);
350 | 
351 |   sprintf(dateTime, "%04d-%02d-%02d %02d:%02d:%02d",
352 |     tm->tm_year + 1900, tm->tm_mon+1 , tm->tm_mday,
353 |     tm->tm_hour, tm->tm_min, tm->tm_sec);
354 | 
355 |   result += prefix+"Current Time     "+sep+"";
356 |   result += String(dateTime)  +suffix;
357 |   //////////////////////////////////////////////
358 |   
359 | #if defined(IS_THERE_A_FLASH)
360 |   result += prefix+"useFlash         "+sep+"";
361 |   result += (ci->useFlash ? String("true") : String("false")) +suffix;
362 | #endif
363 | #if defined(I2C_DISPLAY_ADDR)
364 |   result += prefix+"screenOn         "+sep+"";
365 |   result += (ci->screenOn ? String("true") : String("false")) +suffix;
366 |   result += prefix+"screenFlip       "+sep+"";
367 |   result += (ci->screenFlip ? String("true") : String("false")) +suffix;
368 | #endif
369 | #if defined(PIR_PIN)
370 |   result += prefix+"motDetectOn      "+sep+"";
371 |   result += (ci->motDetectOn ? String("true") : String("false")) + suffix;
372 | #endif
373 | #if defined(SD_CARD_ON)
374 |   result += prefix+"saveToSD         "+sep+"";
375 |   result += (ci->saveToSD ? String("true") : String("false"))  + suffix;
376 | #endif
377 | #if defined(BUZZER_PIN)
378 |   result += prefix+"useBuzzer        "+sep+"";
379 |   result += (ci->useBuzzer ? String("true") : String("false"))  + suffix;
380 | #endif
381 |   result += prefix+"hMirror          "+sep+"";
382 |   result += (ci->hMirror ? String("true") : String("false")) + suffix;
383 |   result += prefix+"vFlip            "+sep+"";
384 |   result += (ci->vFlip ? String("true") : String("false"))  + suffix;
385 |   //result += prefix+"set_whitebal     "+sep+"";
386 |   //result += String(ci->set_whitebal)  + suffix;
387 |   result += prefix+"set_saturation   "+sep+"";
388 |   result += String(ci->set_saturation)  + suffix;
389 |   result += prefix+"set_contrast     "+sep+"";
390 |   result += String(ci->set_contrast)  + suffix;
391 |   result += prefix+"set_brightness   "+sep+"";
392 |   result += String(ci->set_brightness)  + suffix;
393 |   result += prefix+"jpegQuality      "+sep+"";
394 |   result += String(ci->jpegQuality)  + suffix;
395 |   result += prefix+"sMTPTLS          "+sep+"";
396 |   result += (ci->sMTPTLS ? String("true") : String("false"))  + suffix;
397 |   result += prefix+"sMTPPort         "+sep+"";
398 |   result += String(ci->sMTPPort)  + suffix;
399 |   //result += prefix+"sMTPPassword     "+sep+"";
400 |   //result += String(ci->sMTPPassword)  + suffix;
401 |   result += prefix+"sMTPUsername     "+sep+"";
402 |   result += String(ci->sMTPUsername)  + suffix;
403 |   result += prefix+"sMTPServer       "+sep+"";
404 |   result += String(ci->sMTPServer)  + suffix;
405 |   result += prefix+"userEmail        "+sep+"";
406 |   result += String(ci->userEmail)  + suffix;
407 |   result += prefix+"adminEmail       "+sep+"";
408 |   result += String(ci->adminEmail)  + suffix;
409 |   result += prefix+"sendEmail        "+sep+"";
410 |   // [:ADD BOOLEAN HERE:]#6
411 |   // [:ADD INT HERE:]#6
412 |   // [:ADD STRING HERE:]#6
413 |   result += (ci->sendEmail ? String("true") : String("false"))  + suffix;
414 |   result += prefix+"motionDetectCV   "+sep+"";
415 |   result += (ci->motionDetectVC ? String("true") : String("false"))  + suffix;
416 |   result += prefix+"cvIntervalSec    "+sep+"";
417 |   result += String(ci->cvIntervalSec)  +" ms"+ suffix;
418 |   result += prefix+"cvChangePercent  "+sep+"";
419 |   result += String(ci->cvChangePercent)  +" %"+ suffix;
420 |   result += prefix+"alertALL         "+sep+"";
421 |   result += (ci->alertALL ? String("true") : String("false"))  + suffix;
422 |   result += prefix+"useDeepSleep     "+sep+"";
423 |   result += (ci->useDeepSleep ? String("true") : String("false"))  + suffix;
424 |   result += prefix+"webCaptureOn     "+sep+"";
425 |   result += (ci->webCaptureOn ? String("true") : String("false")) + suffix;
426 |   result += prefix+"frameSize        "+sep+"";
427 |   
428 |   ESP_LOGV(TAG_PS,"printConfiguration#10");
429 |   result += String((unsigned int) ci->frameSize) + ",";
430 |   ESP_LOGV(TAG_PS,"printConfiguration:ci->frameSize: %d", ci->frameSize);
431 | 
432 |   ci->frameSize = (ci->frameSize<0? (framesize_t) 0:ci->frameSize);
433 |   result += resolutions[((unsigned int) ci->frameSize)][0] + ",";
434 |   result += resolutions[((int) ci->frameSize)][1] +suffix;
435 |   //result += prefix+"botTTelegram   "+sep+"";
436 |   //result += ci->botTTelegram+suffix;
437 |   result += prefix+"adminChatIds     "+sep+"";
438 |   result += ci->adminChatIds+suffix;
439 |   result += prefix+"userChatIds      "+sep+"";
440 |   result += ci->userChatIds+suffix;
441 |   result += prefix+"lapseTime        "+sep+"";
442 |   result += String(ci->lapseTime)+suffix;
443 |   result += prefix+"timeZone         "+sep+"";
444 |   result += ci->timeZone+suffix;
445 |   ESP_LOGV(TAG_PS,"printConfiguration#11");
446 |   /*
447 |   struct tm *tm;
448 |   time_t  t;
449 |   char    dateTime[100];
450 |   */
451 |   result += prefix+"Last Photo taken "+sep+"";
452 |   if (timeOfLastPhoto>0){
453 |     t = time(NULL)-timeOfLastPhoto;
454 |     tm = localtime(&t);
455 |     sprintf(dateTime, "%02d Y %02d Mon %02d D,%02d H:%02d Min:%02d S",
456 |       tm->tm_year + 1900-1970, tm->tm_mon , tm->tm_mday-1,
457 |       tm->tm_hour, tm->tm_min, tm->tm_sec);
458 |     result += String(dateTime)+suffix;
459 |   }else{
460 |     result += "UNKNOWN" +suffix;
461 |   }
462 |   result += "
"; 463 | ESP_LOGV(TAG_PS,"printConfiguration#12"); 464 | return (result); 465 | } 466 | //////////////////////////////////////////////////////////////////////////// 467 | #endif // #PERSIST_H -------------------------------------------------------------------------------- /telegram_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef TELEGRAM_UTILS_H 2 | #define TELEGRAM_UTILS_H 3 | 4 | #define TELEGRAM_DEBUG 1 5 | 6 | #include 7 | #include "esp_camera.h" 8 | #include "persist.h" 9 | #include "webPages.h" 10 | #include 11 | #include 12 | static const char* TAG_TELE = "TELEGRAM"; 13 | #include "esp_log.h" 14 | 15 | 16 | #if defined(SD_CARD_ON) 17 | #include "FS.h" // SD Card ESP32 18 | #include "SD_MMC.h" // SD Card ESP32 19 | #endif 20 | 21 | //////////////////////////////////////////////// 22 | WiFiClientSecure botClient; 23 | UniversalTelegramBot bot("",botClient); 24 | camera_fb_t *fb = NULL; 25 | size_t currentByte; 26 | 27 | boolean bCameraInitiated=false; 28 | boolean bTelegramBotInitiated=false; 29 | 30 | boolean bInlineKeyboardResolution=false; 31 | boolean bInlineKeyboardExtraOptions=false; 32 | 33 | 34 | //////////////////////////////////////////////// 35 | /* The SMTP Session object used for Email sending */ 36 | SMTPSession smtp; 37 | /* Callback function to get the Email sending status */ 38 | void smtpCallback(SMTP_Status status); 39 | //////////////////////////////////////////////// 40 | int Bot_mtbs = 1000; //mean time between scan messages 41 | long Bot_lasttime; //last time messages' scan has been done 42 | String keyboardJson = "" ; 43 | 44 | //boolean bSetLapseMode=false; 45 | 46 | enum telegramAnswerCollectionMode { 47 | MODE_SET_NONE=0 48 | ,MODE_SET_lapseTime=1 49 | ,MODE_SET_cvIntervalSec=2 50 | ,MODE_SET_cvChangePercent=3 51 | //[:ADD INT HERE:]#4 52 | //[:ADD STRING HERE:]#4 53 | } bTeleAnsMode =MODE_SET_NONE ; 54 | 55 | //////////////////////////////////////////////// 56 | String alertTelegram(String msg,boolean messageOnly=false); 57 | String sendCapturedImage2Telegram2(String chat_id,String messageText="",uint16_t message_id=0); 58 | void handleNewMessages(int numNewMessages); 59 | String formulateKeyboardJson(); 60 | 61 | /* 62 | bool isMoreDataAvailable(); 63 | byte *getNextBuffer(); 64 | int getNextBufferLen(); 65 | bool dataAvailable = false; 66 | //uint8_t photoNextByte(); 67 | */ 68 | //////////////////////////////////////////////// 69 | int Counter_isMoreDataAvailable=0; 70 | int Counter_getNextBuffer=0; 71 | int Counter_getNextBufferLen=0; 72 | /////////////////////////////////////////////// 73 | String alertTelegram(String msg,boolean messageOnly){ 74 | String result=""; 75 | #ifdef CAMERA_MODEL_M5STACK_PSRAM 76 | msg+=", Battery Voltage "+String(bat_get_voltage())+"mv"; 77 | #endif 78 | ESP_LOGV(TAG_TELE,"AlertMessage:%s",msg); 79 | if (messageOnly){ 80 | bot.sendMessage(configItems.adminChatIds, msg,"" ); 81 | }else{ 82 | result= sendCapturedImage2Telegram2(configItems.adminChatIds,msg); 83 | } 84 | if (configItems.alertALL && configItems.userChatIds. toDouble()>0){ 85 | if (messageOnly){ 86 | bot.sendMessage(configItems.userChatIds, msg,"" ); 87 | }else{ 88 | String result= sendCapturedImage2Telegram2(configItems.userChatIds,msg); 89 | } 90 | } 91 | ESP_LOGV(TAG_TELE,"%s",result); 92 | return(result); 93 | } 94 | /////////////////////////////////////////////// 95 | String formulateKeyboardJson(){ 96 | 97 | static const char lkeyboardJson[] PROGMEM = R"( 98 | [ 99 | [ 100 | "/start \uD83D\uDCA1" 101 | ,"/options \uD83C\uDF33" 102 | ] 103 | ,[ 104 | "/sendPhoto \uD83D\uDCF7" 105 | ,"/restartESP \uD83D\uDD50" 106 | ] 107 | ,[ 108 | "/moreSettings \u2699" 109 | ,"/changeRes \uD83C\uDF3B" 110 | ] 111 | ,[ 112 | "/setlapse \u23F3" 113 | ,"/setcvChangePercent \uFF05" 114 | ,"/setcvIntervalSec \u23F0" 115 | ] 116 | ] 117 | )"; 118 | // [:ADD BOOLEAN HERE:]#7 119 | // [:ADD INT HERE:]#7 120 | 121 | 122 | ESP_LOGV(TAG_TELE,"formulateKeyboardJson: %s",lkeyboardJson); 123 | return(lkeyboardJson); 124 | } 125 | /////////////////////////////////////////////// 126 | /* Callback function to get the Email sending status */ 127 | void smtpCallback(SMTP_Status status) 128 | { 129 | /* Print the current status */ 130 | ESP_LOGV(TAG_TELE,"SMTP_Status.info: %s", status.info() ); 131 | 132 | /* Print the sending result */ 133 | if (status.success()) 134 | { 135 | ESP_LOGV(TAG_TELE,"Message sent success: %d", status.completedCount()); 136 | ESP_LOGV(TAG_TELE,"Message sent failled: %d", status.failedCount()); 137 | struct tm dt; 138 | 139 | for (size_t i = 0; i < smtp.sendingResult.size(); i++) 140 | { 141 | /* Get the result item */ 142 | SMTP_Result result = smtp.sendingResult.getItem(i); 143 | //localtime_r(&result.timesstamp, &dt); 144 | 145 | ESP_LOGV(TAG_TELE,"Message No: %d", i + 1); 146 | ESP_LOGV(TAG_TELE,"Status : %s", result.completed ? "success" : "failed"); 147 | //Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); 148 | ESP_LOGV(TAG_TELE,"Recipient : %s", result.recipients); 149 | ESP_LOGV(TAG_TELE,"Subject : %s", result.subject); 150 | } 151 | ESP_LOGV(TAG_TELE,"----------------\n"); 152 | } 153 | } 154 | 155 | /////////////////////////////////////////////// 156 | String formulateResolutionInlineKeyBoard(){ 157 | String lkeyboardJson = "["; 158 | String sep=""; 159 | /* 160 | int maxRes=0; 161 | if(psramFound()){ 162 | maxRes = FRAMESIZE_UXGA; 163 | } else { 164 | maxRes = FRAMESIZE_SVGA; 165 | } 166 | maxRes = FRAMESIZE_UXGA; 167 | */ 168 | ESP_LOGV(TAG_TELE,"formulateResolutionInlineKeyBoard:maxRes: %d",maxRes); 169 | for (int i=0;i<=maxRes;i++){ 170 | String checkMark=""; 171 | if (configItems.frameSize == i){ 172 | checkMark="\u2705"; 173 | } 174 | lkeyboardJson += sep+"[{"; 175 | lkeyboardJson += "\"text\":\""+ 176 | checkMark+ 177 | String(resolutions[i][0])+":"+ 178 | String(resolutions[i][1])+ 179 | "\",\"callback_data\":\"/"+String(resolutions[i][0])+"\""; 180 | lkeyboardJson += "}]"; 181 | sep=","; 182 | } 183 | lkeyboardJson += "]"; 184 | ESP_LOGV(TAG_TELE,"formulateResolutionInlineKeyBoard: %s",lkeyboardJson); 185 | return(lkeyboardJson); 186 | } 187 | /////////////////////////////////////////////// 188 | String formulateOptionsInlineKeyBoard(){ 189 | 190 | // get more unicodes from here https://apps.timwhitlock.info/emoji/tables/unicode 191 | String keyboardJson = "["; // start Json 192 | #if defined(IS_THERE_A_FLASH) 193 | keyboardJson += "[{ \"text\" : \"The Flash is "; 194 | keyboardJson += (configItems.useFlash?"ON\u2705":"OFF\u274C"); 195 | keyboardJson += "\", \"callback_data\" : \"/useFlash\" }],"; 196 | #endif 197 | 198 | #if defined(I2C_DISPLAY_ADDR) 199 | keyboardJson += "[{ \"text\" : \"Screen is "; 200 | keyboardJson += (configItems.screenOn?"ON\u2705":"OFF\u274C"); 201 | keyboardJson += "\", \"callback_data\" : \"/screenOn\" }],"; 202 | 203 | keyboardJson += "[{ \"text\" : \"screen Flip is "; 204 | keyboardJson += (configItems.screenFlip?"ON\u2705":"OFF\u274C"); 205 | keyboardJson += "\", \"callback_data\" : \"/screenFlip\" }],"; 206 | #endif 207 | 208 | #if defined(PIR_PIN) 209 | keyboardJson += "[{ \"text\" : \"PIR MotionDetect is:"; 210 | keyboardJson += (configItems.motDetectOn?"ON\u2705":"OFF\u274C"); 211 | keyboardJson += "\", \"callback_data\" : \"/motDetectOn\" }],"; 212 | #endif 213 | keyboardJson += R"([{ "text" : "CV MotionDetect is:)"; 214 | keyboardJson += (configItems.motionDetectVC?"ON\u2705":"OFF\u274C"); 215 | keyboardJson += R"(", "callback_data" : "/motionDetectVC" }],)"; 216 | if (psramFound()){ 217 | // for face detection 218 | } 219 | 220 | // [:ADD BOOLEAN HERE:]#1 221 | 222 | keyboardJson += "[{ \"text\" : \"Camera Mirror is:"; 223 | keyboardJson += (configItems.hMirror?"ON\u2705":"OFF\u274C"); 224 | keyboardJson += "\", \"callback_data\" : \"/hMirror\" }],"; 225 | 226 | keyboardJson += "[{ \"text\" : \"Camera Flip is:"; 227 | keyboardJson += (configItems.vFlip?"ON\u2705":"OFF\u274C"); 228 | keyboardJson += "\", \"callback_data\" : \"/vFlip\" }],"; 229 | 230 | keyboardJson += R"([{ "text" : "Alert by Email. )"; 231 | keyboardJson += (configItems.sendEmail?"ON\u2705":"OFF\u274C"); 232 | keyboardJson += R"(", "callback_data" : "/sendEmail" }],)"; 233 | 234 | keyboardJson += R"([{ "text" : "Alert all:)"; 235 | keyboardJson += (configItems.alertALL?"ON\u2705":"OFF\u274C"); 236 | keyboardJson += R"(", "callback_data" : "/alertALL" }],)"; 237 | 238 | #if defined(SD_CARD_ON) 239 | keyboardJson += R"([{ "text" : "Save to SD:)"; 240 | keyboardJson += (configItems.saveToSD?"ON\u2705":"OFF\u274C"); 241 | keyboardJson += R"(", "callback_data" : "/saveToSD" }],)"; 242 | #endif 243 | 244 | keyboardJson += R"([{ "text" : "Enable deep sleep:)"; 245 | keyboardJson += (configItems.useDeepSleep?"ON\u2705":"OFF\u274C"); 246 | keyboardJson += R"(", "callback_data" : "/useDeepSleep" }],)"; 247 | 248 | #if defined(BUZZER_PIN) 249 | keyboardJson += R"([{ "text" : "Buzz on MotionDetect:)"; 250 | keyboardJson += (configItems.useBuzzer?"ON\u2705":"OFF\u274C"); 251 | keyboardJson += R"(", "callback_data" : "/useBuzzer" }],)"; 252 | #endif 253 | 254 | keyboardJson += "[{ \"text\" : \"Web Capture is:"; 255 | keyboardJson += (configItems.webCaptureOn?"ON\u2705":"OFF\u274C"); 256 | keyboardJson += "\", \"callback_data\" : \"/webCaptureOn\" }],"; 257 | 258 | keyboardJson += "[{ \"text\" : \"OTA is:"; 259 | keyboardJson += (acConfig.ota==AC_OTA_BUILTIN?"ON\u2705":"OFF\u274C"); 260 | keyboardJson += "\", \"callback_data\" : \"/OTAOn\" }]"; 261 | 262 | keyboardJson += "]"; 263 | 264 | ESP_LOGV(TAG_TELE,"formulateOptionsInlineKeyBoard: %s", keyboardJson); 265 | return(keyboardJson); 266 | } 267 | 268 | //////////////////////////////////// 269 | void handleNewMessages(int numNewMessages) { 270 | ESP_LOGV(TAG_TELE,"handleNewMessages:BEGIN"); 271 | ESP_LOGV(TAG_TELE,"numNewMessages:%d",numNewMessages); 272 | boolean bPrintOptions=true; 273 | for (int i=0; iwidth; 518 | int fb_height=fb->height; 519 | String msg= R"( 520 | 521 | {{espMessage}} 522 | 523 |

524 | 525 | )"; 526 | msg.replace("{{espMessage}}",espMessage); 527 | msg.replace("{{WIDTH}}",String(fb_width) ); 528 | msg.replace("{{HEIGHT}}",String(fb_height) ); 529 | message.html.content =msg.c_str(); 530 | 531 | /** The HTML text message character set e.g. 532 | * us-ascii 533 | * utf-8 534 | * utf-7 535 | * The default value is utf-8 536 | */ 537 | message.html.charSet = "utf-8"; 538 | 539 | /** The content transfer encoding e.g. 540 | * enc_7bit or "7bit" (not encoded) 541 | * enc_qp or "quoted-printable" (encoded) 542 | * enc_base64 or "base64" (encoded) 543 | * enc_binary or "binary" (not encoded) 544 | * enc_8bit or "8bit" (not encoded) 545 | * The default value is "7bit" 546 | */ 547 | message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; 548 | 549 | message.text.content = "This message contains 1 inline image.\r\nThe inline images were not shown in the plain text message."; 550 | message.text.charSet = "utf-8"; 551 | message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; 552 | 553 | /** The message priority 554 | * esp_mail_smtp_priority_high or 1 555 | * esp_mail_smtp_priority_normal or 3 556 | * esp_mail_smtp_priority_low or 5 557 | * The default value is esp_mail_smtp_priority_low 558 | */ 559 | message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; 560 | 561 | /** The Delivery Status Notifications e.g. 562 | * esp_mail_smtp_notify_never 563 | * esp_mail_smtp_notify_success 564 | * esp_mail_smtp_notify_failure 565 | * esp_mail_smtp_notify_delay 566 | * The default value is esp_mail_smtp_notify_never 567 | */ 568 | message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; 569 | 570 | /* Set the custom message header */ 571 | //+configItems.sMTPUsername 572 | message.addHeader( "Message-ID: user@domain.org"); 573 | 574 | /* The attachment data item */ 575 | SMTP_Attachment att; 576 | 577 | /** Set the inline image info e.g. 578 | * file name, MIME type, BLOB data, BLOB data size, 579 | * transfer encoding (should be base64 for inline image) 580 | */ 581 | att.descr.filename = "firebase_logo.jpeg"; 582 | att.descr.mime = "image/jpeg"; 583 | att.blob.data = fb->buf; 584 | att.blob.size = fb->len; 585 | //att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; 586 | att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; 587 | att.descr.content_encoding = Content_Transfer_Encoding::enc_binary; 588 | 589 | // Add attachment to the message 590 | //message.addAttachment(att); 591 | 592 | /* Add inline image to the message */ 593 | message.addInlineImage(att); 594 | 595 | /* Connect to server with the session config */ 596 | if (!smtp.connect(&session)) 597 | return; 598 | 599 | /* Start sending the Email and close the session */ 600 | if (!MailClient.sendMail(&smtp, &message, true)) 601 | ESP_LOGE(TAG_TELE,"Error sending Email: %s " , smtp.errorReason()); 602 | } 603 | 604 | /////////////////////////////////////////////// 605 | String sendCapturedImage2Telegram2(String chat_id,String messageText ,uint16_t message_id) { 606 | const char* myDomain = "api.telegram.org"; 607 | String getAll="", getBody = ""; 608 | StaticJsonDocument<2048> doc; 609 | String result="success"; 610 | camera_fb_t * fb = NULL; 611 | 612 | #if defined(FLASH_LAMP_PIN) 613 | if (configItems.useFlash){ 614 | ESP_LOGV(TAG_TELE,"Switching Flash-lamp ON"); 615 | digitalWrite(FLASH_LAMP_PIN, HIGH); 616 | delay(250); 617 | ESP_LOGV(TAG_TELE,"The Flash-lamp is ON"); 618 | } 619 | #endif 620 | 621 | #if defined(USE_OLED_AS_FLASH) 622 | if (configItems.useFlash){ 623 | ESP_LOGV(TAG_TELE,"Switching Flash-OLED ON"); 624 | display_AllWhite(); 625 | delay(250); 626 | ESP_LOGV(TAG_TELE,"Flash-OLED is ON"); 627 | } 628 | #endif 629 | 630 | ESP_LOGV(TAG_TELE,"Capture Photo"); 631 | fb = esp_camera_fb_get(); 632 | if(!fb) { 633 | ESP_LOGE(TAG_TELE,"Camera capture failed"); 634 | delay(1000); 635 | ESP.restart(); 636 | } 637 | #if defined(FLASH_LAMP_PIN) 638 | if (configItems.useFlash){ 639 | delay(10); 640 | digitalWrite(FLASH_LAMP_PIN, LOW); 641 | ESP_LOGV(TAG_TELE,"Flash-lamp OFF"); 642 | } 643 | #endif 644 | int fb_width=fb->width; 645 | int fb_height=fb->height; 646 | ESP_LOGV(TAG_TELE,"sendChatAction#1"); 647 | bot.sendChatAction(chat_id, "upload_photo"); 648 | // reset the client connection 649 | if (botClient.connected()) { 650 | #ifdef TELEGRAM_DEBUG 651 | ESP_LOGV(TAG_TELE,"Closing client"); 652 | #endif 653 | botClient.stop(); 654 | } 655 | // Connect with api.telegram.org if not already connected 656 | if (!botClient.connected()) { 657 | #ifdef TELEGRAM_DEBUG 658 | ESP_LOGV(TAG_TELE,"[BOT Client]Connecting to server" ); 659 | #endif 660 | if (!botClient.connect(TELEGRAM_HOST, TELEGRAM_SSL_PORT)) { 661 | #ifdef TELEGRAM_DEBUG 662 | ESP_LOGE(TAG_TELE,"[BOT Client]Conection error" ); 663 | #endif 664 | } 665 | } 666 | 667 | String head =""; 668 | head += "--ef2ac69f9149220e889abc22b81d1401\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + chat_id +"\r\n"; 669 | head += "--ef2ac69f9149220e889abc22b81d1401\r\nContent-Disposition: form-data; name=\"caption\"; \r\n\r\n" + 670 | String(fb_width)+"x"+String(fb_height)+", "+String(fb->len) +" B"+ 671 | ","+messageText+ 672 | "\r\n"; 673 | if (message_id>0) 674 | head += "--ef2ac69f9149220e889abc22b81d1401\r\nContent-Disposition: form-data; name=\"reply_to_message_id\"; \r\n\r\n" + String(message_id) +"\r\n"; 675 | head += "--ef2ac69f9149220e889abc22b81d1401\r\nContent-Disposition: form-data; name=\"parse_mode\"; \r\n\r\nMarkdown\r\n"; 676 | head += "--ef2ac69f9149220e889abc22b81d1401\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\n"; 677 | //"Content-Type: image/jpeg\r\n\r\n"+ 678 | 679 | String tail = "\r\n--ef2ac69f9149220e889abc22b81d1401--\r\n"; 680 | 681 | uint32_t imageLen = fb->len; 682 | uint32_t extraLen = head.length() + tail.length(); 683 | uint32_t totalLen = imageLen + extraLen; 684 | 685 | botClient.println("POST /bot"+configItems.botTTelegram+"/sendPhoto HTTP/1.1"); 686 | botClient.println("Host: " + String(myDomain)); 687 | botClient.println("Connection: keep-alive"); 688 | botClient.println("Content-Length: " + String(totalLen)); 689 | botClient.println("Content-Type: multipart/form-data; boundary=ef2ac69f9149220e889abc22b81d1401"); 690 | botClient.println(); 691 | botClient.print(head); 692 | botClient.println(); 693 | 694 | uint8_t *fbBuf = fb->buf; 695 | size_t fbLen = fb->len; 696 | size_t sentB =0; 697 | Serial.print("/"); 698 | for (size_t n=0;n0) { 706 | Serial.print("+"); 707 | size_t remainder = fbLen%1024; 708 | botClient.write(fbBuf, remainder); 709 | sentB += remainder; 710 | } 711 | if(botClient.connected()) 712 | Serial.print("*"); 713 | else 714 | Serial.print("X"); 715 | } 716 | Serial.println("/"); 717 | botClient.print(tail); 718 | 719 | ESP_LOGV(TAG_TELE, "sendCapturedImage2Telegram2:sentB:%d , w%d X h%d", 720 | sentB, 721 | fb_width, 722 | fb_height 723 | ); 724 | 725 | int waitTime = 10000; // timeout 10 seconds 726 | long startTime = millis(); 727 | boolean state = false; 728 | 729 | while ((startTime + waitTime) > millis()) 730 | { 731 | Serial.print("."); 732 | //delay(100); 733 | while (botClient.available()) 734 | { 735 | char c = botClient.read(); 736 | if (state==true) { 737 | getBody += String(c); 738 | } 739 | /* 740 | else{ 741 | botClient.write(fbBufX,oneByte); 742 | } 743 | */ 744 | if (c == '\n') 745 | { 746 | if (getAll.length()==0) state=true; 747 | getAll = ""; 748 | } 749 | else if (c != '\r') 750 | getAll += String(c); 751 | startTime = millis(); 752 | } 753 | if (getBody.length()>0) break; 754 | } 755 | ESP_LOGV(TAG_TELE,"sendCapturedImage2Telegram2:getBody: %s",getBody); 756 | /* 757 | // Deserialize the JSON document 758 | // sample: 759 | // {"ok":false,"error_code":400,"description":"Bad Request: IMAGE_PROCESS_FAILED"} 760 | // {"ok":true,"result":{"message_id":3914,"from":{"id":1748863501,"is_bot":true,"first_name":"ESP32_CAM_04_bot","username":"ESP32_CAM_04_bot"},"chat":{"id":591479121,"first_name":"A","last_name":"Maklad","username":"asmaklad","type":"private"},"date":1620390975,"photo":[{"file_id":"AgACAgQAAxkDAAIPSmCVND8eYR6M8XHIZykZp_CHHBI-AAKhuDEbWquoUIa2-YNz89PNEjUyLl0AAwEAAwIAA20AAx4iAQABHwQ","file_unique_id":"AQADEjUyLl0AAx4iAQAB","file_size":9221,"width":320,"height":240},{"file_id":"AgACAgQAAxkDAAIPSmCVND8eYR6M8XHIZykZp_CHHBI-AAKhuDEbWquoUIa2-YNz89PNEjUyLl0AAwEAAwIAA3gAAx8iAQABHwQ","file_unique_id":"AQADEjUyLl0AAx8iAQAB","file_size":26810,"width":800,"height":600}],"caption":"800x600, 26810 B"}} 761 | */ 762 | if (getBody.equals("")){ 763 | result="Empty Response, Sending Failed."; 764 | }else{ 765 | DeserializationError error = deserializeJson(doc, getBody); 766 | // Test if parsing succeeds. 767 | if (error) { 768 | ESP_LOGE(TAG_TELE,"sendCapturedImage2Telegram2:deserializeJson() failed: "); 769 | ESP_LOGE(TAG_TELE,"%s",error.f_str()); 770 | result="Can't parse response."; 771 | } 772 | boolean responseOK=doc["ok"]; 773 | if (!responseOK){ 774 | result=doc["description"].as(); 775 | } 776 | } 777 | PICTURES_COUNT++; 778 | botClient.stop(); 779 | // send Email 780 | if (configItems.sendEmail){ 781 | if (configItems.sMTPPort>0 && 782 | configItems.sMTPPassword.length() >0 && 783 | configItems.sMTPUsername.length() >0 && 784 | configItems.sMTPServer.length() >0 && 785 | configItems.adminEmail.length() >0 786 | ) 787 | { 788 | sendEmailPhoto(fb,messageText); 789 | } 790 | } 791 | // saving to SD card. 792 | #if defined(SD_CARD_ON) 793 | if (configItems.saveToSD) { 794 | ESP_LOGV(TAG_TELE,"Saving to SD Card."); 795 | if(!SD_MMC.begin()){ 796 | ESP_LOGV(TAG_TELE,"SD Card Mount Failed"); 797 | result="SD Card Mount Failed"; 798 | }else{ 799 | uint8_t cardType = SD_MMC.cardType(); 800 | if(cardType == CARD_NONE){ 801 | ESP_LOGV(TAG_TELE,"No SD Card attached"); 802 | result="No SD Card attached"; 803 | }else{ 804 | struct tm *tm; 805 | time_t t; 806 | char dateTime[100]; 807 | t = time(NULL); 808 | tm = localtime(&t); 809 | sprintf(dateTime, "-%04d%02d%02d_%02d%02d%02d", 810 | tm->tm_year + 1900, tm->tm_mon+1 , tm->tm_mday, 811 | tm->tm_hour, tm->tm_min, tm->tm_sec); 812 | // Path where new picture will be saved in SD Card 813 | String path = "/picture" + String(dateTime) + "_" + messageText+".jpg"; 814 | fs::FS &fs = SD_MMC; 815 | ESP_LOGV(TAG_TELE,"Picture file name: %s\n", path.c_str()); 816 | File file = fs.open(path.c_str(), FILE_WRITE); 817 | if(!file){ 818 | ESP_LOGV(TAG_TELE,"Failed to open file in writing mode"); 819 | } else { 820 | file.write(fb->buf, fb->len); // payload (image), payload length 821 | ESP_LOGV(TAG_TELE, 822 | "Saved file to path: %s, Card Size: %llu, Total KBytes: %llu, Used KBytes: %llu" 823 | , path.c_str() 824 | , SD_MMC.cardSize() 825 | ,SD_MMC.totalBytes()/1024 826 | ,SD_MMC.usedBytes()/1024 827 | ); 828 | } 829 | file.close(); 830 | } 831 | } 832 | #if defined(FLASH_LAMP_PIN) 833 | if (configItems.useFlash){ 834 | delay(10); 835 | digitalWrite(FLASH_LAMP_PIN, LOW); 836 | ESP_LOGV(TAG_TELE,"Flash-lamp OFF"); 837 | } 838 | #endif 839 | } 840 | #endif 841 | esp_camera_fb_return(fb); 842 | 843 | #if defined(USE_OLED_AS_FLASH) 844 | display_Clear(); 845 | #endif 846 | if (!result.equals("success")){ 847 | bot.sendMessage(chat_id, String("Photo sent:"+String(fb_width)+"x"+String(fb_height))+","+String(fbLen/1024)+" KB:"+result,"" ); 848 | } 849 | return(result); 850 | } 851 | 852 | 853 | #endif //TELEGRAM_UTILS_H -------------------------------------------------------------------------------- /workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "name": "210424-064024-ttgo-t1", 8 | "path": "..\\..\\..\\PlatformIO\\Projects\\210424-064024-ttgo-t1" 9 | } 10 | ], 11 | "settings": { 12 | "files.associations": { 13 | "regex": "cpp", 14 | "xlocale": "cpp", 15 | "xlocmes": "cpp", 16 | "cmath": "cpp", 17 | "random": "cpp", 18 | "functional": "cpp", 19 | "xutility": "cpp", 20 | "xiosbase": "cpp", 21 | "chrono": "cpp", 22 | "optional": "cpp", 23 | "system_error": "cpp", 24 | "type_traits": "cpp", 25 | "xtr1common": "cpp", 26 | "ratio": "cpp", 27 | "array": "cpp", 28 | "tuple": "cpp", 29 | "utility": "cpp", 30 | "xstring": "cpp", 31 | "string_view": "cpp", 32 | "xlocinfo": "cpp", 33 | "string": "cpp", 34 | "deque": "cpp", 35 | "list": "cpp", 36 | "queue": "cpp", 37 | "stack": "cpp", 38 | "vector": "cpp", 39 | "xhash": "cpp", 40 | "xtree": "cpp" 41 | } 42 | } 43 | } --------------------------------------------------------------------------------