Compare commits
No commits in common. "main" and "R3.3b" have entirely different histories.
|
@ -1,3 +0,0 @@
|
|||
|
||||
.DS_Store
|
||||
/ESPMegaPRO-OS-SDK
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "windows-gcc-x64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"compilerPath": "gcc",
|
||||
"cStandard": "${default}",
|
||||
"cppStandard": "${default}",
|
||||
"intelliSenseMode": "windows-gcc-x64",
|
||||
"compilerArgs": [
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C/C++ Runner: Debug Session",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"cwd": "d:/Git/ESPMegaPRO-v3-SDK/ESPMegaPRO-OS-SDK/src",
|
||||
"program": "d:/Git/ESPMegaPRO-v3-SDK/ESPMegaPRO-OS-SDK/src/build/Debug/outDebug",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "gcc",
|
||||
"C_Cpp_Runner.cppCompilerPath": "g++",
|
||||
"C_Cpp_Runner.debuggerPath": "gdb",
|
||||
"C_Cpp_Runner.cStandard": "",
|
||||
"C_Cpp_Runner.cppStandard": "",
|
||||
"C_Cpp_Runner.msvcBatchPath": "",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
||||
"C_Cpp_Runner.msvcSecureNoWarnings": false
|
||||
}
|
|
@ -1,288 +0,0 @@
|
|||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "C:/Users/siwat/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/xtensa-esp32-elf-gcc.exe",
|
||||
"C_Cpp_Runner.cppCompilerPath": "C:/Users/siwat/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/xtensa-esp32-elf-g++.exe",
|
||||
"C_Cpp_Runner.debuggerPath": "C:/Users/siwat/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/xtensa-esp32-elf-gdb.exe",
|
||||
"C_Cpp_Runner.cStandard": "gnu99",
|
||||
"C_Cpp_Runner.cppStandard": "gnu++11",
|
||||
"C_Cpp_Runner.msvcBatchPath": "",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [
|
||||
"D:/Git/ESPMegaPRO-v3-SDK/ESPMegaPRO-DevKit-BTNLT/include",
|
||||
"D:/Git/ESPMegaPRO-v3-SDK/ESPMegaPRO-DevKit-BTNLT/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/newlib/platform_include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions/freertos",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/port/xtensa/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32/private_include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/heap/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/log/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps/sntp",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/lwip/src/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include/arch",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/platform_port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_common/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/soc",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/public_compat",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_pm/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ringbuf/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/vfs/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_wifi/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_event/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_netif/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_eth/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcpip_adapter/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ipc/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_trace/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_timer/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/mbedtls/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/esp_crt_bundle/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_update/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spi_flash/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bootloader_support/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nvs_flash/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/pthread/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/xtensa",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include/port/xtensa",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/esp_supplicant/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ieee802154/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/console",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/asio/asio/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/osi/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/include/esp32/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/api/include/api",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/blufi/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/host/bluedroid/api/include/api",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/tinycrypt/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/storage",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/btc/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/common/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/client/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/server/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/core/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/models/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cbor/port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/unity/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cmock/CMock/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/libcoap/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/nghttp2/lib/includes",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls/esp-tls-crypto",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_adc_cal/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hid/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcp_transport/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_client/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_server/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_ota/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_server/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/interface",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protobuf-c/protobuf-c",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/common",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/security",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/transports",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mdns/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_local_ctrl/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/sdmmc/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_serial_slave_link/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_websocket_client/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/expat/expat/lib",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/port/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wear_levelling/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/diskio",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/vfs",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freemodbus/freemodbus/common/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/jsmn/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json/cJSON",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/libsodium/src/libsodium/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/port_include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mqtt/esp-mqtt/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/openssl/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/perfmon/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spiffs/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ulp/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wifi_provisioning/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rmaker_common/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_diagnostics/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rtc_store/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_insights/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_generator/upstream",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_schedule/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp_secure_cert_mgr/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rainmaker/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/gpio_button/button/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/qrcode/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ws2812_led",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_littlefs/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/tool",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/typedef",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/image",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/math",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/nn",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/layer",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/detect",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/model_zoo",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-sr/src/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-sr/esp-tts/esp_tts_chinese/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-sr/include/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/driver/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/conversions/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dotprod/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/hann/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_harris/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_nuttall/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/nuttall/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/flat_top/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/iir/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fir/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/add/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sub/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mul/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/addc/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mulc/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sqrt/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fft/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dct/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/conv/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/common/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf_imu13states/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fb_gfx/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/qio_qspi/include",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/cores/esp32",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/variants/wt32-eth01",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/BLE/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/BluetoothSerial/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/DNSServer/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/EEPROM/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/ESP32/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/FFat/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/FS/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdate/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdateServer/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/I2S/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/Insights/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/LittleFS/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/RainMaker/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/SD/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/USB/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/Update/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src",
|
||||
"C:/Users/siwat/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src",
|
||||
""
|
||||
],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
||||
"C_Cpp_Runner.msvcSecureNoWarnings": false
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#include <ESPMegaProOS.hpp>
|
||||
|
||||
// Function Prototypes
|
||||
void setup();
|
||||
void loop();
|
||||
void inputCallback(uint8_t pin, uint8_t state);
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
|
||||
void setup() {
|
||||
espmega.begin();
|
||||
espmega.inputs.registerCallback(inputCallback);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
espmega.loop();
|
||||
}
|
||||
|
||||
void inputCallback(uint8_t pin, uint8_t state) {
|
||||
// Match PWM Channel with the same number as the input pin
|
||||
espmega.outputs.digitalWrite(pin, state);
|
||||
Serial.printf("Input %d changed to %d\n", pin, state);
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "gcc",
|
||||
"C_Cpp_Runner.cppCompilerPath": "g++",
|
||||
"C_Cpp_Runner.debuggerPath": "gdb",
|
||||
"C_Cpp_Runner.cStandard": "",
|
||||
"C_Cpp_Runner.cppStandard": "",
|
||||
"C_Cpp_Runner.msvcBatchPath": "",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
||||
"C_Cpp_Runner.msvcSecureNoWarnings": false,
|
||||
"files.associations": {
|
||||
"istream": "cpp",
|
||||
"map": "cpp"
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:wt32-eth01]
|
||||
platform = espressif32
|
||||
board = wt32-eth01
|
||||
framework = arduino
|
||||
lib_deps = siwats/ESPMegaPROR3@^2.0.2
|
||||
monitor_speed = 115200
|
|
@ -1,218 +0,0 @@
|
|||
#include <ESPMegaProOS.hpp>
|
||||
#include <IRReceiver.hpp>
|
||||
|
||||
/**
|
||||
* @brief This program helps construct an ir timing table for a air conditioner
|
||||
*
|
||||
* Each run of this program will generate a new table, for a specific mode and fan speed
|
||||
* It will iterate through all the temperature settings, and record the timing for each temperature
|
||||
* It will then print the timing table as a C++ 2D array, which can be copied into your main program
|
||||
* The first dimension is the temperature, the second dimension is the timing
|
||||
*/
|
||||
|
||||
#define min_temp 16
|
||||
#define max_temp 32
|
||||
#define MAX_TIMINGS 1000 // 1000 timings should be enough for any remote
|
||||
#define CAPTURE_TIMEOUT 5 // seconds
|
||||
|
||||
uint32_t timings[max_temp - min_temp + 1][MAX_TIMINGS] = {0};
|
||||
uint16_t timings_count[max_temp - min_temp + 1] = {0};
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
|
||||
void beginRoutine()
|
||||
{
|
||||
Serial.println("Beginning IR capture routine");
|
||||
for (int i = min_temp; i <= max_temp; i++)
|
||||
{
|
||||
Serial.printf("Please press the button on your remote for %d degrees\n", i);
|
||||
IRReceiver::start_long_receive();
|
||||
for (int i = 0; i < CAPTURE_TIMEOUT; i++)
|
||||
{
|
||||
Serial.printf("Waiting for IR signal... (%d seconds left)\n", CAPTURE_TIMEOUT - i);
|
||||
// During this period, if the user press any key, the routine will be restarted
|
||||
// Unless it is a space bar, which will be used to confirm the timings
|
||||
if (Serial.available())
|
||||
{
|
||||
char c = Serial.read();
|
||||
if (c != ' ')
|
||||
{
|
||||
Serial.println("User interrupted, restarting routine...");
|
||||
return;
|
||||
}
|
||||
}
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
ir_data_t data = IRReceiver::end_long_receive();
|
||||
// Retry if no data was received
|
||||
if (data.size == 0)
|
||||
{
|
||||
Serial.println("No data received, retrying...");
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
// Remove last timing
|
||||
data.size--;
|
||||
Serial.printf("Received timing of size %d\n", data.size);
|
||||
// Print out the timing array
|
||||
for (int i = 0; i < data.size; i++)
|
||||
{
|
||||
Serial.printf("%u%s", data.data[i], i == data.size - 1 ? "\n" : ", ");
|
||||
}
|
||||
// If any timings exceed 20000, print a warning
|
||||
for (int i = 0; i < data.size; i++)
|
||||
{
|
||||
if (data.data[i] > 50000U)
|
||||
{
|
||||
Serial.println("WARNING: Timing exceeds 50000, Possible data corruption!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Ask the user if the timings are correct
|
||||
Serial.println("Are the timings correct? (y/n)");
|
||||
// Flush the serial buffer
|
||||
while (Serial.available())
|
||||
{
|
||||
Serial.read();
|
||||
}
|
||||
// Wait for user input
|
||||
while (!Serial.available())
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
char c = Serial.read();
|
||||
if (c != 'y')
|
||||
{
|
||||
Serial.println("Retrying...");
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
// Store the timings count
|
||||
timings_count[i - min_temp] = data.size;
|
||||
// Copy the timings into the timings array
|
||||
memcpy(timings[i - min_temp], data.data, sizeof(uint32_t) * data.size);
|
||||
free(data.data);
|
||||
}
|
||||
Serial.println("Generating C++ code for the timings, please wait...");
|
||||
// Find the maximum number of timings
|
||||
int max_timings = 0;
|
||||
for (int i = 0; i < max_temp - min_temp + 1; i++)
|
||||
{
|
||||
if (timings_count[i] > max_timings)
|
||||
{
|
||||
max_timings = timings_count[i];
|
||||
}
|
||||
}
|
||||
// Print the timings
|
||||
Serial.println("Done!, please copy the following into your main program");
|
||||
Serial.printf("uint16_t timings[%d][%d] = {\n", max_temp - min_temp + 1, max_timings);
|
||||
for (int i = 0; i < max_temp - min_temp + 1; i++)
|
||||
{
|
||||
Serial.printf(" {");
|
||||
for (int j = 0; j < timings_count[i]; j++)
|
||||
{
|
||||
Serial.printf("%u%s", timings[i][j], j == timings_count[i] - 1 ? "" : ", ");
|
||||
}
|
||||
Serial.println(i == max_temp - min_temp ? "}" : "},");
|
||||
}
|
||||
Serial.println("};");
|
||||
Serial.println("Stopping IR capture routine");
|
||||
Serial.printf("IR Capture routine finished\n");
|
||||
}
|
||||
|
||||
void capture_single()
|
||||
{
|
||||
Serial.println("Please press the button on your remote");
|
||||
IRReceiver::start_long_receive();
|
||||
for (int i = 0; i < CAPTURE_TIMEOUT; i++)
|
||||
{
|
||||
Serial.printf("Waiting for IR signal... (%d seconds left)\n", CAPTURE_TIMEOUT - i);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
ir_data_t data = IRReceiver::end_long_receive();
|
||||
// Remove last timing
|
||||
data.size--;
|
||||
Serial.printf("Received timing of size %d\n", data.size);
|
||||
// If any timings exceed 20000, print a warning
|
||||
for (int i = 0; i < data.size; i++)
|
||||
{
|
||||
if (data.data[i] > 50000U)
|
||||
{
|
||||
Serial.println("WARNING: Timing exceeds 50000, Possible data corruption!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data.size == 0)
|
||||
{
|
||||
Serial.println("No data received, retrying...");
|
||||
capture_single();
|
||||
return;
|
||||
}
|
||||
// Print the timings
|
||||
Serial.println("Done!, please copy the following into your main program");
|
||||
Serial.printf("uint32_t timings[%d] = {", data.size);
|
||||
for (int i = 0; i < data.size; i++)
|
||||
{
|
||||
Serial.printf("%u%s", data.data[i], i == data.size - 1 ? "" : ", ");
|
||||
}
|
||||
Serial.println("};");
|
||||
free(data.data);
|
||||
Serial.println("Do you want to capture another signal? (y/n)");
|
||||
// Flush the serial buffer
|
||||
while (Serial.available())
|
||||
{
|
||||
Serial.read();
|
||||
}
|
||||
// Wait for user input
|
||||
while (!Serial.available())
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
char c = Serial.read();
|
||||
if (c == 'y')
|
||||
{
|
||||
capture_single();
|
||||
}
|
||||
}
|
||||
|
||||
void menu_init()
|
||||
{
|
||||
// Print the menu
|
||||
// The menu will have 2 options, one to start the routine, and one capture a single IR signal
|
||||
Serial.println("ESPMegaPRO IR Development Kit - IR Capture");
|
||||
Serial.println("1. Begin IR Capture Routine");
|
||||
Serial.println("2. Capture Single IR Signal");
|
||||
Serial.println("Please select an option:");
|
||||
// Wait for user input
|
||||
while (!Serial.available())
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
char c = Serial.read();
|
||||
// Start the routine
|
||||
if (c == '1')
|
||||
{
|
||||
beginRoutine();
|
||||
}
|
||||
// Capture a single IR signal
|
||||
else if (c == '2')
|
||||
{
|
||||
capture_single();
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
IRReceiver::begin(36);
|
||||
espmega.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
menu_init();
|
||||
Serial.println("Press any key to return to the main menu");
|
||||
while (!Serial.available())
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
.pio/
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
temp/
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"*.cps": "javascript",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"adafruit_ads1x15.h": "c",
|
||||
"*.tcc": "cpp",
|
||||
"memory": "cpp",
|
||||
"random": "cpp",
|
||||
"functional": "cpp",
|
||||
"atomic": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"iterator": "cpp",
|
||||
"map": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"bitset": "cpp",
|
||||
"regex": "cpp"
|
||||
},
|
||||
"cmake.configureOnOpen": true,
|
||||
"cmake.sourceDirectory": "D:/Git/ESPMegaPRO-v3-SDK/Template Project/.pio/libdeps/wt32-eth01/Adafruit BusIO",
|
||||
"editor.tokenColorCustomizations": {
|
||||
"comments": "",
|
||||
"textMateRules": []
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import re
|
||||
|
||||
html_files = []
|
||||
directory = "lib/ESPMegaPRO/html"
|
||||
|
||||
for filename in os.listdir(directory):
|
||||
if filename.endswith(".html"):
|
||||
html_files.append(os.path.join(directory, filename))
|
||||
|
||||
# Iterate over the list of html files
|
||||
for html_file in html_files:
|
||||
# Open the html file
|
||||
f = open(html_file, "r")
|
||||
# Read the content of the html file
|
||||
html_content = f.read()
|
||||
# Close the html file
|
||||
f.close()
|
||||
|
||||
# Section Disabled, Using client side JS to replace variables
|
||||
# First, replace % with %%
|
||||
#html_content = html_content.replace("%", "%%")
|
||||
# The html file contains placeholders for the variables
|
||||
# These placeholders are in the form of $(variable_name)$
|
||||
# Replace these with %variable_name%
|
||||
#html_content = re.sub(r"\$\((.*?)\)\$", r"%\1%", html_content)
|
||||
# Create the temp folder if it doesn't exist
|
||||
|
||||
|
||||
if not os.path.exists("temp"):
|
||||
os.makedirs("temp")
|
||||
# Save the new content to a new file in the temp directory
|
||||
# The new file will have the same name as the original file
|
||||
nf = open("temp/" + os.path.basename(html_file), "w")
|
||||
nf.write(html_content)
|
||||
nf.close()
|
||||
# Use xxd to convert the html file to a C++ file
|
||||
# The C++ file will have the same name as the original file but with a .h extension
|
||||
# Save the C++ file in lib/ESPMegaPRO/html
|
||||
os.system("xxd -i temp/" + os.path.basename(html_file) + " > lib/ESPMegaPRO/" + os.path.basename(html_file).replace(".html", "_html.h"))
|
||||
# Now delete the temp file
|
||||
os.remove("temp/" + os.path.basename(html_file))
|
||||
# Next we open the C++ file
|
||||
f = open("lib/ESPMegaPRO/" + os.path.basename(html_file).replace(".html", "_html.h"), "r")
|
||||
# Read the content of the C++ file
|
||||
cpp_content = f.read()
|
||||
# Close the C++ file
|
||||
f.close()
|
||||
# By default, xxd will create a unsigned char array
|
||||
# We need to change this to a const char array
|
||||
# We also need to add a null terminator at the end of the array
|
||||
# Additionally we want to place the array in PROGMEM
|
||||
# Ex. From unsigned char index_html[] = { to const char index_html[] PROGMEM = {
|
||||
# We will use regex to do this
|
||||
cpp_content = re.sub(r"unsigned char (.*?)\[\] = \{", r"const char \1[] PROGMEM = {", cpp_content)
|
||||
# Add null terminator
|
||||
cpp_content = cpp_content.replace("};", ", 0x00};")
|
||||
# Lastly, we do not need the length of the array so we remove it
|
||||
# Ex. From unsigned int index_html_len = 1000; to
|
||||
cpp_content = re.sub(r"unsigned int (.*?)_len = (.*?);", r"", cpp_content)
|
||||
# Change the name of the variable to <filename>_html where <filename> is the name of the html file
|
||||
filename = os.path.basename(html_file).replace(".html", "")
|
||||
cpp_content = re.sub(r"const char (.*?)\[\] PROGMEM = {", r"const char " + filename + r"_html[] PROGMEM = {", cpp_content)
|
||||
# Reopen the C++ file
|
||||
f = open("lib/ESPMegaPRO/" + os.path.basename(html_file).replace(".html", "_html.h"), "w")
|
||||
# Write the new content to the C++ file
|
||||
f.write(cpp_content)
|
||||
# Close the C++ file
|
||||
f.close()
|
||||
|
||||
# Lastly, we need to create a header file that includes all the html files
|
||||
# Open the header file
|
||||
f = open("lib/ESPMegaPRO/all_html.h", "w")
|
||||
# Write the pragma once directive
|
||||
f.write("#pragma once\n")
|
||||
# Write the includes for all the html files
|
||||
for html_file in html_files:
|
||||
f.write("#include \"" + os.path.basename(html_file).replace(".html", "_html.h") + "\"\n")
|
||||
# Close the header file
|
|
@ -1,39 +0,0 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -1,205 +0,0 @@
|
|||
/**
|
||||
* @file AnalogCard.cpp
|
||||
* @brief Implementation of the AnalogCard class.
|
||||
*/
|
||||
|
||||
#include <AnalogCard.hpp>
|
||||
#include "esp_log.h"
|
||||
|
||||
/**
|
||||
* @brief Default constructor for the AnalogCard class.
|
||||
*/
|
||||
AnalogCard::AnalogCard() : dac0(DAC0_ADDRESS),
|
||||
dac1(DAC1_ADDRESS),
|
||||
dac2(DAC2_ADDRESS),
|
||||
dac3(DAC3_ADDRESS),
|
||||
analogInputBankA(),
|
||||
analogInputBankB(),
|
||||
dac_change_callbacks()
|
||||
{
|
||||
this->handler_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a value to the specified DAC pin.
|
||||
* @param pin The DAC pin to write to.
|
||||
* @param value The value to write.
|
||||
*/
|
||||
void AnalogCard::dacWrite(uint8_t pin, uint16_t value)
|
||||
{
|
||||
ESP_LOGV("AnalogCard", "DAC Write: %d, %d", pin, value);
|
||||
this->setDACState(pin, value > 0);
|
||||
this->setDACValue(pin, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the state of the specified DAC pin.
|
||||
* @param pin The DAC pin to set the state of.
|
||||
* @param state The state to set (true = on, false = off).
|
||||
*/
|
||||
void AnalogCard::setDACState(uint8_t pin, bool state)
|
||||
{
|
||||
ESP_LOGD("AnalogCard", "Setting DAC state: %d, %d", pin, state);
|
||||
this->dac_state[pin] = state;
|
||||
this->sendDataToDAC(pin, this->dac_value[pin] * state);
|
||||
for (const auto& callback : this->dac_change_callbacks)
|
||||
{
|
||||
callback.second(pin, state, this->dac_value[pin]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the value of the specified DAC pin.
|
||||
* @param pin The DAC pin to set the value of.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void AnalogCard::setDACValue(uint8_t pin, uint16_t value)
|
||||
{
|
||||
ESP_LOGD("AnalogCard", "Setting DAC value: %d, %d", pin, value);
|
||||
this->dac_value[pin] = value;
|
||||
this->sendDataToDAC(pin, value * this->dac_state[pin]);
|
||||
for (const auto& callback : this->dac_change_callbacks)
|
||||
{
|
||||
callback.second(pin, this->dac_state[pin], value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of the specified DAC pin.
|
||||
* @param pin The DAC pin to get the value of.
|
||||
* @return The value of the DAC pin.
|
||||
*/
|
||||
uint16_t AnalogCard::getDACValue(uint8_t pin)
|
||||
{
|
||||
return this->dac_value[pin];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the state of the specified DAC pin.
|
||||
* @param pin The DAC pin to get the state of.
|
||||
* @return The state of the DAC pin (true = on, false = off).
|
||||
*/
|
||||
bool AnalogCard::getDACState(uint8_t pin)
|
||||
{
|
||||
return this->dac_state[pin];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends data to the specified DAC pin.
|
||||
* @param pin The DAC pin to send data to.
|
||||
* @param value The data to send.
|
||||
* @note This function does not call the DAC change callbacks.
|
||||
*/
|
||||
void AnalogCard::sendDataToDAC(uint8_t pin, uint16_t value)
|
||||
{
|
||||
switch (pin)
|
||||
{
|
||||
case 0:
|
||||
this->dac0.writeDAC(value);
|
||||
break;
|
||||
case 1:
|
||||
this->dac1.writeDAC(value);
|
||||
break;
|
||||
case 2:
|
||||
this->dac2.writeDAC(value);
|
||||
break;
|
||||
case 3:
|
||||
this->dac3.writeDAC(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the value from the specified analog pin.
|
||||
* @param pin The analog pin to read from.
|
||||
* @return The value read from the analog pin.
|
||||
*/
|
||||
uint16_t AnalogCard::analogRead(uint8_t pin)
|
||||
{
|
||||
if (pin >= 0 && pin <= 3)
|
||||
{
|
||||
return this->analogInputBankA.readADC_SingleEnded(pin);
|
||||
}
|
||||
else if (pin >= 4 && pin <= 7)
|
||||
{
|
||||
return this->analogInputBankB.readADC_SingleEnded(pin - 4);
|
||||
}
|
||||
return 65535;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the AnalogCard.
|
||||
* @return True if initialization is successful, false otherwise.
|
||||
*/
|
||||
bool AnalogCard::begin()
|
||||
{
|
||||
bool success = true;
|
||||
if (!this->dac0.begin())
|
||||
{
|
||||
ESP_LOGE("AnalogCard", "Card Analog ERROR: Failed to install DAC0");
|
||||
// success = false;
|
||||
}
|
||||
if (!this->dac1.begin())
|
||||
{
|
||||
ESP_LOGE("AnalogCard", "Card Analog ERROR: Failed to install DAC1");
|
||||
// success = false;
|
||||
}
|
||||
if (!this->dac2.begin())
|
||||
{
|
||||
ESP_LOGE("AnalogCard", "Card Analog ERROR: Failed to install DAC2");
|
||||
// success = false;
|
||||
}
|
||||
if (!this->dac3.begin())
|
||||
{
|
||||
ESP_LOGE("AnalogCard", "Card Analog ERROR: Failed to install DAC3");
|
||||
// success = false;
|
||||
}
|
||||
if (!this->analogInputBankA.begin(ANALOG_INPUT_BANK_A_ADDRESS))
|
||||
{
|
||||
ESP_LOGE("AnalogCard", "Card Analog ERROR: Failed to install analog input bank A");
|
||||
success = false;
|
||||
}
|
||||
if (!this->analogInputBankB.begin(ANALOG_INPUT_BANK_B_ADDRESS))
|
||||
{
|
||||
ESP_LOGE("AnalogCard", "Card Analog ERROR: Failed to install analog input bank B");
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop of the AnalogCard.
|
||||
* @note This function does nothing.
|
||||
*/
|
||||
void AnalogCard::loop()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the type of the AnalogCard.
|
||||
* @return The type of the AnalogCard.
|
||||
*/
|
||||
uint8_t AnalogCard::getType()
|
||||
{
|
||||
return CARD_TYPE_ANALOG;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback function to be called when the state or value of a DAC pin changes.
|
||||
* @param callback The callback function to register.
|
||||
* @return The handler ID of the registered callback.
|
||||
*/
|
||||
uint8_t AnalogCard::registerDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback)
|
||||
{
|
||||
this->dac_change_callbacks[this->handler_count] = callback;
|
||||
return this->handler_count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters a previously registered DAC change callback.
|
||||
* @param handler The handler ID of the callback to unregister.
|
||||
*/
|
||||
void AnalogCard::unregisterDACChangeCallback(uint8_t handler)
|
||||
{
|
||||
this->dac_change_callbacks.erase(handler);
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <Adafruit_ADS1X15.h>
|
||||
#include <MCP4725.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
// Analog Card
|
||||
#define CARD_TYPE_ANALOG 0x02
|
||||
|
||||
// Analog Card FRAM Address
|
||||
#define ANALOG_INPUT_BANK_A_ADDRESS 0x48
|
||||
#define ANALOG_INPUT_BANK_B_ADDRESS 0x49
|
||||
#define DAC0_ADDRESS 0x60
|
||||
#define DAC1_ADDRESS 0x61
|
||||
#define DAC2_ADDRESS 0x62
|
||||
#define DAC3_ADDRESS 0x63
|
||||
|
||||
/**
|
||||
* @brief This class represents the Analog Card.
|
||||
*
|
||||
* The analog card has 8 analog inputs accross two banks, and 4 DAC outputs.
|
||||
*
|
||||
* @note You do not need to specify the ESPMega I/O Address when creating an instance of this class as there can only be one Analog Card installed in the ESPMegaPRO board.
|
||||
* @warning There can only be one Analog Card installed in the ESPMegaPRO board.
|
||||
*
|
||||
*/
|
||||
class AnalogCard : public ExpansionCard {
|
||||
public:
|
||||
AnalogCard();
|
||||
void dacWrite(uint8_t pin, uint16_t value);
|
||||
void sendDataToDAC(uint8_t pin, uint16_t value);
|
||||
uint16_t analogRead(uint8_t pin);
|
||||
bool begin();
|
||||
void loop();
|
||||
bool getDACState(uint8_t pin);
|
||||
uint16_t getDACValue(uint8_t pin);
|
||||
void setDACState(uint8_t pin, bool state);
|
||||
void setDACValue(uint8_t pin, uint16_t value);
|
||||
uint8_t registerDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
void unregisterDACChangeCallback(uint8_t handler);
|
||||
uint8_t getType();
|
||||
private:
|
||||
uint8_t handler_count;
|
||||
// Map of handler IDs to callback functions
|
||||
std::map<uint8_t, std::function<void(uint8_t, bool, uint16_t)>> dac_change_callbacks;
|
||||
bool dac_state[4];
|
||||
uint16_t dac_value[4];
|
||||
MCP4725 dac0;
|
||||
MCP4725 dac1;
|
||||
MCP4725 dac2;
|
||||
MCP4725 dac3;
|
||||
Adafruit_ADS1115 analogInputBankA;
|
||||
Adafruit_ADS1115 analogInputBankB;
|
||||
};
|
|
@ -1,424 +0,0 @@
|
|||
/**
|
||||
* @file AnalogIoT.cpp
|
||||
* @brief Implementation of the AnalogIoT class.
|
||||
*
|
||||
* This file contains the implementation of the AnalogIoT class, which provides functionality for handling analog input and output operations in an IoT system.
|
||||
* The class allows for setting the state and value of digital-to-analog converters (DACs), as well as reading the value of analog-to-digital converters (ADCs).
|
||||
* It also supports publishing the state and value of DACs and ADCs over MQTT.
|
||||
*/
|
||||
#include <AnalogIoT.hpp>
|
||||
|
||||
|
||||
/**
|
||||
* @brief Default constructor for the AnalogIoT class.
|
||||
*
|
||||
* This constructor initializes the AnalogIoT object and sets up the ADC conversion callbacks.
|
||||
*/
|
||||
AnalogIoT::AnalogIoT() : adc_conversion_callbacks() {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
adc_publish_enabled[i] = false;
|
||||
adc_conversion_interval[i] = 1000;
|
||||
}
|
||||
this->adc_conversion_callback_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default destructor for the AnalogIoT class.
|
||||
*/
|
||||
AnalogIoT::~AnalogIoT() {
|
||||
this->adc_conversion_callbacks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the AnalogIoT object.
|
||||
* @param card_id The ID of the card.
|
||||
* @param card A pointer to the card object.
|
||||
* @param mqtt A pointer to the MQTT client object.
|
||||
* @param base_topic The base MQTT topic.
|
||||
* @return True if the initialization was successful, false otherwise.
|
||||
* @note This function can be called from the main program but it is recommended to use ESPMegaIoT to initialize the IoT Components.
|
||||
* This function initializes the AnalogIoT object and registers the callbacks for handling DAC changes.
|
||||
*/
|
||||
bool AnalogIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
this->card = (AnalogCard*)card;
|
||||
this-> card_id = card_id;
|
||||
this->dac_set_state_length = strlen(DAC_SET_STATE_TOPIC);
|
||||
this->dac_set_value_length = strlen(DAC_SET_VALUE_TOPIC);
|
||||
this->dac_state_length = strlen(DAC_STATE_TOPIC);
|
||||
this->dac_value_length = strlen(DAC_VALUE_TOPIC);
|
||||
this->request_state_length = strlen(REQUEST_STATE_TOPIC);
|
||||
this->dac_publish_enable_length = strlen(DAC_PUBLISH_ENABLE_TOPIC);
|
||||
// Register callbacks
|
||||
auto bindedCallback = std::bind(&AnalogIoT::handleDACChange, this, std::placeholders::_1, std::placeholders::_2);
|
||||
this->card->registerDACChangeCallback(bindedCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of all DACs.
|
||||
* @note This function is called when a request state message is received.
|
||||
*/
|
||||
void AnalogIoT::handleMqttMessage(char *topic, char *payload){
|
||||
uint8_t topic_length = strlen(topic);
|
||||
if(this-> processDACSetStateMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processDACSetValueMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processRequestStateMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processADCSetConversionIntervalMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processADCSetConversionEnabledMessage(topic, payload, topic_length)) return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of all DACs.
|
||||
*/
|
||||
void AnalogIoT::publishADCs() {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
this->publishADC(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of a DAC.
|
||||
* @param pin The pin of the DAC.
|
||||
*/
|
||||
void AnalogIoT::publishADC(uint8_t pin) {
|
||||
if (this->adc_publish_enabled[pin]) {
|
||||
uint16_t value = this->card->analogRead(pin);
|
||||
char *topic = new char[15];
|
||||
sprintf(topic, "adc/%02d/value", pin);
|
||||
char *payload = new char[10];
|
||||
sprintf(payload, "%d", value);
|
||||
this->publishRelative(topic, payload);
|
||||
delete[] topic;
|
||||
delete[] payload;
|
||||
// Call all callbacks
|
||||
for (auto& callback : this->adc_conversion_callbacks) {
|
||||
callback.second(pin, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the interval at which the state of all DACs is published.
|
||||
* @param interval The interval in milliseconds.
|
||||
*/
|
||||
void AnalogIoT::setADCsPublishInterval(uint32_t interval) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
adc_conversion_interval[i] = interval;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets whether the state of all DACs is published.
|
||||
* @param enabled True if the state of all DACs should be published, false otherwise.
|
||||
*/
|
||||
void AnalogIoT::setADCsPublishEnabled(bool enabled) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
adc_publish_enabled[i] = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback for handling ADC conversions.
|
||||
* @param callback The callback function.
|
||||
* @return The handler of the callback.
|
||||
*/
|
||||
uint8_t AnalogIoT::registerADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback) {
|
||||
this->adc_conversion_callbacks[this->adc_conversion_callback_index] = callback;
|
||||
return this->adc_conversion_callback_index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters a callback for handling ADC conversions.
|
||||
* @param handler The handler of the callback.
|
||||
*/
|
||||
void AnalogIoT::unregisterADCConversionCallback(uint8_t handler) {
|
||||
this->adc_conversion_callbacks.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the interval at which the value of an ADC channel is read.
|
||||
* @param pin The pin of the ADC channel.
|
||||
* @param interval The interval in milliseconds.
|
||||
*/
|
||||
void AnalogIoT::setADCConversionInterval(uint8_t pin, uint16_t interval) {
|
||||
adc_conversion_interval[pin] = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables or disables the periodic reading of the value of an ADC channel.
|
||||
* @param pin The pin of the ADC channel.
|
||||
* @param enabled True if the value of the ADC channel should be read, false otherwise.
|
||||
*/
|
||||
void AnalogIoT::setADCConversionEnabled(uint8_t pin, bool enabled) {
|
||||
adc_publish_enabled[pin] = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes a message received on the MQTT topic for setting the state of a DAC.
|
||||
* @param topic The topic of the message.
|
||||
* @param payload The payload of the message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @note This function is not meant to be called from user code.
|
||||
* @return True if the message was processed, false otherwise.
|
||||
*/
|
||||
bool AnalogIoT::processADCSetConversionIntervalMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// TODO: Process payload matching the criteria
|
||||
// Topic: adc/<%02d>/set/conversion_interval
|
||||
// The first 4 characters are "adc/"
|
||||
// The length of the topic must be 30 characters
|
||||
// The last 24 characters must be "/set/conversion_interval"
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 30) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "adc/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 26, "/set/conversion_interval", 24)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
uint16_t interval = atoi(payload);
|
||||
// Set the interval
|
||||
this->setADCConversionInterval(pin, interval);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes a message received on the MQTT topic for setting the value of a DAC.
|
||||
* @param topic The topic of the message.
|
||||
* @param payload The payload of the message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @note This function is not meant to be called from user code.
|
||||
* @return True if the message was processed, false otherwise.
|
||||
*/
|
||||
bool AnalogIoT::processADCSetConversionEnabledMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: adc/<%02d>/set/conversion_enabled
|
||||
// The first 4 characters are "adc/"
|
||||
// The length of the topic must be 29 characters
|
||||
// The last 23 characters must be ""/set/conversion_enabled
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 29) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "adc/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 25, "/set/conversion_enabled", 23)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
bool enabled = atoi(payload);
|
||||
// Set conversion enabled
|
||||
this->setADCConversionEnabled(pin, enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes a message received on the MQTT topic for setting the state of a DAC.
|
||||
* @param topic The topic of the message.
|
||||
* @param payload The payload of the message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @note This function is not meant to be called from user code.
|
||||
* @return True if the message was processed, false otherwise.
|
||||
*/
|
||||
bool AnalogIoT::processDACSetStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: dac/<%02d>/set/state
|
||||
// The first 4 characters are "dac/"
|
||||
// The length of the topic must be 16 characters
|
||||
// The last 10 characters must be "/set/state"
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 16) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "dac/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 12, "/set/state", 10)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
bool state = atoi(payload);
|
||||
// Set the state
|
||||
this->card->setDACState(pin, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes a message received on the MQTT topic for setting the value of a DAC.
|
||||
* @param topic The topic of the message.
|
||||
* @param payload The payload of the message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @note This function is not meant to be called from user code.
|
||||
* @return True if the message was processed, false otherwise.
|
||||
*/
|
||||
bool AnalogIoT::processDACSetValueMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: dac/<%02d>/set/value
|
||||
// The first 4 characters are "dac/"
|
||||
// The length of the topic must be 16 characters
|
||||
// The last 10 characters must be "/set/value"
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 16) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "dac/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 12, "/set/value", 10)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
uint16_t value = atoi(payload);
|
||||
// Set the value
|
||||
this->card->setDACValue(pin, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes a message received on the MQTT topic for requesting the state of all DACs.
|
||||
* @param topic The topic of the message.
|
||||
* @param payload The payload of the message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @note This function is not meant to be called from user code.
|
||||
* @return True if the message was processed, false otherwise.
|
||||
*/
|
||||
bool AnalogIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: requeststate
|
||||
// The length of the topic must be 12 characters
|
||||
// After all these conditions are met, the topic is valid
|
||||
if (topic_length != 12) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, REQUEST_STATE_TOPIC, 12)) {
|
||||
return false;
|
||||
}
|
||||
// Publish the state of all DACs
|
||||
this->publishDACs();
|
||||
// Publish the state of all ADCs
|
||||
this->publishADCs();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribes to all MQTT topics used by the AnalogIoT object.
|
||||
* @note This function is called when the MQTT client connects.
|
||||
*/
|
||||
void AnalogIoT::subscribe() {
|
||||
// There are 4 DACs and 8 ADCs
|
||||
// DACs: dac/<%02d>/set/state, dac/<%02d>/set/value, dac/publish_enable
|
||||
// ADCs: adc/<%02d>/set/conversion_interval, adc/<%02d>/set/conversion_enabled
|
||||
|
||||
// Subscribe to all set state topics
|
||||
char topic[20];
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
sprintf(topic, "dac/%02d/set/state", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set value topics
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
sprintf(topic, "dac/%02d/set/value", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set conversion interval topics
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
sprintf(topic, "adc/%02d/set/conversion_interval", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set conversion enabled topics
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
sprintf(topic, "adc/%02d/set/conversion_enabled", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to publish enable topic
|
||||
this->subscribeRelative("dac/publish_enable");
|
||||
}
|
||||
void AnalogIoT::loop() {
|
||||
// Iterate over all ADCs and publish if enabled and interval has passed
|
||||
uint32_t now = millis();
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (this->adc_publish_enabled[i] && now - this->last_adc_publish > this->adc_conversion_interval[i]) {
|
||||
this->publishADC(i);
|
||||
this->last_adc_publish = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of all DACs.
|
||||
*/
|
||||
void AnalogIoT::publishReport() {
|
||||
publishADCs();
|
||||
publishDACs();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the type of the card.
|
||||
* @return The type of the card.
|
||||
*/
|
||||
uint8_t AnalogIoT::getType() {
|
||||
return CARD_TYPE_ANALOG;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of all DACs.
|
||||
*/
|
||||
void AnalogIoT::publishDACs() {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
this->publishDAC(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of a DAC.
|
||||
* @param pin The pin of the DAC.
|
||||
*/
|
||||
void AnalogIoT::publishDAC(uint8_t pin) {
|
||||
this->publishDACState(pin);
|
||||
this->publishDACValue(pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of a DAC.
|
||||
* @param pin The pin of the DAC.
|
||||
*/
|
||||
void AnalogIoT::publishDACState(uint8_t pin) {
|
||||
char *topic = new char[15];
|
||||
sprintf(topic, "dac/%02d/state", pin);
|
||||
char *payload = new char[2];
|
||||
sprintf(payload, "%d", this->card->getDACState(pin));
|
||||
this->publishRelative(topic, payload);
|
||||
delete[] topic;
|
||||
delete[] payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the value of a DAC.
|
||||
* @param pin The pin of the DAC.
|
||||
*/
|
||||
void AnalogIoT::publishDACValue(uint8_t pin) {
|
||||
char *topic = new char[15];
|
||||
sprintf(topic, "dac/%02d/value", pin);
|
||||
char *payload = new char[5];
|
||||
sprintf(payload, "%d", this->card->getDACValue(pin));
|
||||
this->publishRelative(topic, payload);
|
||||
delete[] topic;
|
||||
delete[] payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the state of a DAC.
|
||||
* @param pin The pin of the DAC.
|
||||
*/
|
||||
void AnalogIoT::handleDACChange(uint8_t pin, uint16_t value) {
|
||||
this->publishDAC(pin);
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#pragma once
|
||||
#include <IoTComponent.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
#include <map>
|
||||
|
||||
// MQTT Topics
|
||||
#define DAC_SET_STATE_TOPIC "/set/state"
|
||||
#define DAC_SET_VALUE_TOPIC "/set/value"
|
||||
#define DAC_STATE_TOPIC "/dac/00/state"
|
||||
#define DAC_VALUE_TOPIC "/dac/00/value"
|
||||
#define DAC_PUBLISH_ENABLE_TOPIC "/publish_enable"
|
||||
#define REQUEST_STATE_TOPIC "requeststate"
|
||||
|
||||
/**
|
||||
* @brief The AnalogIoT class is a class for connecting the Analog Card to the IoT module.
|
||||
*
|
||||
* This function allows you to control the Analog Card using MQTT.
|
||||
*
|
||||
* @warning You should not instantiate this class directly, instead use ESPMegaIoT's registerCard function.
|
||||
*/
|
||||
class AnalogIoT : public IoTComponent {
|
||||
public:
|
||||
AnalogIoT();
|
||||
~AnalogIoT();
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void handleDACChange(uint8_t pin, uint16_t value);
|
||||
void publishADCs();
|
||||
void publishADC(uint8_t pin);
|
||||
void publishDACs();
|
||||
void publishDAC(uint8_t pin);
|
||||
void publishDACState(uint8_t pin);
|
||||
void publishDACValue(uint8_t pin);
|
||||
void setADCsPublishInterval(uint32_t interval);
|
||||
void setADCsPublishEnabled(bool enabled);
|
||||
uint8_t registerADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback);
|
||||
void unregisterADCConversionCallback(uint8_t handler);
|
||||
void setADCConversionInterval(uint8_t pin, uint16_t interval);
|
||||
void setADCConversionEnabled(uint8_t pin, bool enabled);
|
||||
bool processADCSetConversionIntervalMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processADCSetConversionEnabledMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processDACSetStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processDACSetValueMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processRequestStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
void loop();
|
||||
uint8_t getType();
|
||||
private:
|
||||
// The index of the next callback to be registered
|
||||
uint8_t adc_conversion_callback_index = 0;
|
||||
// We keep track of the length of the topics so we don't have to calculate it every time
|
||||
uint8_t dac_set_state_length;
|
||||
uint8_t dac_set_value_length;
|
||||
uint8_t dac_state_length;
|
||||
uint8_t dac_value_length;
|
||||
uint8_t request_state_length;
|
||||
uint8_t dac_publish_enable_length;
|
||||
uint32_t last_adc_publish = 0;
|
||||
AnalogCard *card;
|
||||
bool adc_publish_enabled[8];
|
||||
uint16_t adc_conversion_interval[8];
|
||||
uint32_t last_adc_conversion[8];
|
||||
std::map<uint8_t, std::function<void(uint8_t, uint16_t)>> adc_conversion_callbacks;
|
||||
};
|
|
@ -1,468 +0,0 @@
|
|||
/**
|
||||
* @file ClimateCard.cpp
|
||||
* @brief Implementation file for the ClimateCard class.
|
||||
*
|
||||
* This file contains the implementation of the ClimateCard class, which represents a climate control card.
|
||||
* The ClimateCard class provides methods for controlling an air conditioner, reading temperature and humidity
|
||||
* from sensors, and saving and loading the state to/from FRAM memory.
|
||||
*/
|
||||
#include <ClimateCard.hpp>
|
||||
|
||||
/**
|
||||
* @brief Construct a new ClimateCard object.
|
||||
*
|
||||
* @note RMT channel must be universally unique, this means that you can't use the same channel for multiple cards.
|
||||
*
|
||||
* @param ir_pin The GPIO pin number of the IR transmitter.
|
||||
* @param ac The AirConditioner object that represents the air conditioner.
|
||||
* @param sensor_type The type of the sensor connected to the card.
|
||||
* @param sensor_pin The GPIO pin number of the sensor.
|
||||
* @param channel The RMT channel to use for IR transmission.
|
||||
*/
|
||||
ClimateCard::ClimateCard(uint8_t ir_pin, AirConditioner ac, uint8_t sensor_type, uint8_t sensor_pin, rmt_channel_t channel) : ir_blaster(ir_pin, channel)
|
||||
{
|
||||
this->ir_pin = ir_pin;
|
||||
this->ac = ac;
|
||||
this->sensor_type = sensor_type;
|
||||
this->sensor_pin = sensor_pin;
|
||||
// Initialize Pointers
|
||||
this->dht = nullptr;
|
||||
this->ds18b20 = nullptr;
|
||||
this->fram = nullptr;
|
||||
// Initialize Variables
|
||||
this->fram_address = 0;
|
||||
this->fram_auto_save = false;
|
||||
this->state.ac_temperature = 0;
|
||||
this->state.ac_mode = 0;
|
||||
this->state.ac_fan_speed = 0;
|
||||
this->humidity = 0;
|
||||
this->room_temperature = 0;
|
||||
// Initialize state
|
||||
this->state.ac_temperature = 25;
|
||||
this->state.ac_mode = 0;
|
||||
this->state.ac_fan_speed = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a new ClimateCard object.
|
||||
*
|
||||
* @param ir_pin The GPIO pin number of the IR transmitter.
|
||||
* @param ac The AirConditioner object that represents the air conditioner.
|
||||
* @param channel The RMT channel to use for IR transmission.
|
||||
*
|
||||
* @note RMT channel must be universally unique, this means that you can't use the same channel for multiple cards.
|
||||
*
|
||||
* @note This constructor can be used when no sensor is connected to the card.
|
||||
*/
|
||||
ClimateCard::ClimateCard(uint8_t ir_pin, AirConditioner ac, rmt_channel_t channel) : ClimateCard(ir_pin, ac, AC_SENSOR_TYPE_NONE, 0, channel)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The destructor of the ClimateCard class.
|
||||
*/
|
||||
ClimateCard::~ClimateCard()
|
||||
{
|
||||
delete dht;
|
||||
delete ds18b20;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the ClimateCard object.
|
||||
*
|
||||
* @return true if initialization was successful.
|
||||
* @return false if initialization failed.
|
||||
*/
|
||||
bool ClimateCard::begin()
|
||||
{
|
||||
switch (sensor_type)
|
||||
{
|
||||
case AC_SENSOR_TYPE_DHT22:
|
||||
dht = new DHTNEW(sensor_pin);
|
||||
break;
|
||||
case AC_SENSOR_TYPE_DS18B20:
|
||||
OneWire oneWire(sensor_pin);
|
||||
ds18b20 = new DS18B20(&oneWire);
|
||||
break;
|
||||
}
|
||||
updateAirConditioner();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Loop function of the ClimateCard class.
|
||||
*
|
||||
* @note When this card is installed in an ESPMega, this function is called automatically by the ESPMega class.
|
||||
*/
|
||||
void ClimateCard::loop()
|
||||
{
|
||||
static uint32_t last_sensor_update = 0;
|
||||
if (millis() - last_sensor_update >= AC_SENSOR_READ_INTERVAL)
|
||||
{
|
||||
last_sensor_update = millis();
|
||||
updateSensor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bind FRAM memory to the ClimateCard object at the specified address.
|
||||
*
|
||||
* @note This function must be called before calling loadStateFromFRAM() or saveStateToFRAM().
|
||||
* @note This card takes up 3 bytes of FRAM memory.
|
||||
*
|
||||
* @param fram The FRAM object.
|
||||
* @param fram_address The starting address of the card in FRAM memory.
|
||||
*/
|
||||
void ClimateCard::bindFRAM(FRAM *fram, uint16_t fram_address)
|
||||
{
|
||||
this->fram = fram;
|
||||
this->fram_address = fram_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set whether the state should be automatically saved to FRAM memory.
|
||||
*
|
||||
* @note This function has no effect if bindFRAM() has not been called.
|
||||
* @param autoSave Whether the state should be automatically saved to FRAM memory.
|
||||
*/
|
||||
void ClimateCard::setFRAMAutoSave(bool autoSave)
|
||||
{
|
||||
this->fram_auto_save = autoSave;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the state to FRAM memory.
|
||||
* @note This function has no effect if bindFRAM() has not been called.
|
||||
*/
|
||||
void ClimateCard::saveStateToFRAM()
|
||||
{
|
||||
if (fram == nullptr)
|
||||
return;
|
||||
fram->write8(fram_address, state.ac_temperature);
|
||||
fram->write8(fram_address + 1, state.ac_mode);
|
||||
fram->write8(fram_address + 2, state.ac_fan_speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the state from FRAM memory.
|
||||
*
|
||||
* @note This function has no effect if bindFRAM() has not been called.
|
||||
*/
|
||||
void ClimateCard::loadStateFromFRAM()
|
||||
{
|
||||
if (fram == nullptr)
|
||||
return;
|
||||
// Retrieve state from FRAM
|
||||
state.ac_temperature = fram->read8(fram_address);
|
||||
state.ac_mode = fram->read8(fram_address + 1);
|
||||
state.ac_fan_speed = fram->read8(fram_address + 2);
|
||||
if (state.ac_temperature > ac.max_temperature)
|
||||
state.ac_temperature = ac.max_temperature;
|
||||
else if (state.ac_temperature < ac.min_temperature)
|
||||
state.ac_temperature = ac.min_temperature;
|
||||
// If mode is out of range, set to 0
|
||||
if (state.ac_mode > ac.modes)
|
||||
state.ac_mode = 0;
|
||||
// If fan speed is out of range, set to 0
|
||||
if (state.ac_fan_speed > ac.fan_speeds)
|
||||
state.ac_fan_speed = 0;
|
||||
updateAirConditioner();
|
||||
for (const auto &callback : callbacks)
|
||||
{
|
||||
callback.second(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the temperature of the air conditioner.
|
||||
*
|
||||
* @param temperature The temperature to set.
|
||||
* @note If the temperature is out of range, it will be set to its respective maximum or minimum.
|
||||
*/
|
||||
void ClimateCard::setTemperature(uint8_t temperature)
|
||||
{
|
||||
// If temperature is out of range, set to its respective maximum or minimum
|
||||
if (temperature > ac.max_temperature)
|
||||
temperature = ac.max_temperature;
|
||||
else if (temperature < ac.min_temperature)
|
||||
temperature = ac.min_temperature;
|
||||
this->state.ac_temperature = temperature;
|
||||
updateAirConditioner();
|
||||
if (fram_auto_save)
|
||||
saveStateToFRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the mode of the air conditioner.
|
||||
*
|
||||
* @note If the mode is out of range, it will be set to 0.
|
||||
* @param mode The mode to set.
|
||||
*/
|
||||
void ClimateCard::setMode(uint8_t mode)
|
||||
{
|
||||
if (mode > ac.modes)
|
||||
mode = 0;
|
||||
this->state.ac_mode = mode;
|
||||
updateAirConditioner();
|
||||
if (fram_auto_save)
|
||||
saveStateToFRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the current mode.
|
||||
* @return The name of the current mode.
|
||||
*/
|
||||
char *ClimateCard::getModeName()
|
||||
{
|
||||
return (char *)ac.mode_names[state.ac_mode];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the current fan speed.
|
||||
*
|
||||
* @return The name of the current fan speed.
|
||||
*/
|
||||
char *ClimateCard::getFanSpeedName()
|
||||
{
|
||||
return (char *)ac.fan_speed_names[state.ac_fan_speed];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the fan speed of the air conditioner.
|
||||
*
|
||||
* @note If the fan speed is out of range, it will be set to 0.
|
||||
* @param fan_speed The fan speed to set.
|
||||
*/
|
||||
void ClimateCard::setFanSpeed(uint8_t fan_speed)
|
||||
{
|
||||
if (fan_speed > ac.fan_speeds)
|
||||
fan_speed = 0;
|
||||
this->state.ac_fan_speed = fan_speed;
|
||||
updateAirConditioner();
|
||||
if (fram_auto_save)
|
||||
saveStateToFRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set fan speed by name.
|
||||
*
|
||||
* @param fan_speed_name The name of the fan speed to set.
|
||||
* @note If the fan speed is not found, the function will not do anything.
|
||||
*/
|
||||
void ClimateCard::setFanSpeedByName(const char *fan_speed_name)
|
||||
{
|
||||
for (uint8_t i = 0; i < ac.fan_speeds; i++)
|
||||
{
|
||||
if (strcmp(fan_speed_name, ac.fan_speed_names[i]) == 0)
|
||||
{
|
||||
setFanSpeed(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set mode by name.
|
||||
*
|
||||
* @param mode_name The name of the mode to set.
|
||||
* @note If the mode is not found, the function will not do anything.
|
||||
*/
|
||||
void ClimateCard::setModeByName(const char *mode_name)
|
||||
{
|
||||
for (uint8_t i = 0; i < ac.modes; i++)
|
||||
{
|
||||
if (strcmp(mode_name, ac.mode_names[i]) == 0)
|
||||
{
|
||||
setMode(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback function that will be called when the state of the air conditioner changes.
|
||||
*
|
||||
* @param callback The callback function to register.
|
||||
*
|
||||
* @return uint8_t The handler of the callback function.
|
||||
*/
|
||||
uint8_t ClimateCard::registerChangeCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback)
|
||||
{
|
||||
callbacks[callbacks_handler_count] = callback;
|
||||
return callbacks_handler_count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the type of the card.
|
||||
*
|
||||
* @return The handler of the callback function.
|
||||
*/
|
||||
uint8_t ClimateCard::getType()
|
||||
{
|
||||
return CARD_TYPE_CLIMATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief update environmental sensor data.
|
||||
*
|
||||
* @note This function is called automatically by the loop() function.
|
||||
* @note This function has no effect if no sensor is connected to the card.
|
||||
* @note This function also calls the sensor callbacks.
|
||||
*/
|
||||
void ClimateCard::updateSensor()
|
||||
{
|
||||
if (sensor_type == AC_SENSOR_TYPE_NONE)
|
||||
return;
|
||||
// Read sensor data and update variables
|
||||
switch (sensor_type)
|
||||
{
|
||||
case AC_SENSOR_TYPE_DHT22:
|
||||
if (millis() - dht->lastRead() < AC_SENSOR_READ_INTERVAL)
|
||||
return;
|
||||
dht->read();
|
||||
room_temperature = dht->getTemperature();
|
||||
humidity = dht->getHumidity();
|
||||
break;
|
||||
case AC_SENSOR_TYPE_DS18B20:
|
||||
ds18b20->requestTemperatures();
|
||||
uint32_t start = millis();
|
||||
while (!ds18b20->isConversionComplete())
|
||||
{
|
||||
if (millis() - start >= AC_SENSOR_READ_TIMEOUT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
room_temperature = ds18b20->getTempC();
|
||||
break;
|
||||
}
|
||||
for (const auto &callback : sensor_callbacks)
|
||||
{
|
||||
callback.second(room_temperature, humidity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the air conditioner state to match the state of the card.
|
||||
*
|
||||
* @warning This function is not working yet.
|
||||
*/
|
||||
void ClimateCard::updateAirConditioner()
|
||||
{
|
||||
const uint16_t* ir_code_ptr = nullptr;
|
||||
size_t itemCount = (*(this->ac.getInfraredCode))(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature-this->ac.min_temperature, &ir_code_ptr);
|
||||
|
||||
if (ir_code_ptr == nullptr)
|
||||
return;
|
||||
|
||||
ir_blaster.send(ir_code_ptr, itemCount);
|
||||
|
||||
// Publish state
|
||||
for (const auto &callback : callbacks)
|
||||
{
|
||||
callback.second(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the type of the sensor connected to the card.
|
||||
*
|
||||
* @return The type of the sensor connected to the card.
|
||||
*/
|
||||
uint8_t ClimateCard::getSensorType()
|
||||
{
|
||||
return sensor_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the room temperature in degrees Celsius.
|
||||
*
|
||||
* @return The room temperature.
|
||||
*/
|
||||
float ClimateCard::getRoomTemperature()
|
||||
{
|
||||
return room_temperature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the humidity in percent.
|
||||
*
|
||||
* @return The humidity.
|
||||
*/
|
||||
float ClimateCard::getHumidity()
|
||||
{
|
||||
return humidity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the temperature of the air conditioner.
|
||||
*
|
||||
* @return The temperature of the air conditioner.
|
||||
*/
|
||||
uint8_t ClimateCard::getTemperature()
|
||||
{
|
||||
return state.ac_temperature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the mode of the air conditioner.
|
||||
*
|
||||
* @return The mode of the air conditioner.
|
||||
*/
|
||||
uint8_t ClimateCard::getMode()
|
||||
{
|
||||
return state.ac_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the fan speed of the air conditioner.
|
||||
*
|
||||
* @return The fan speed of the air conditioner.
|
||||
*/
|
||||
uint8_t ClimateCard::getFanSpeed()
|
||||
{
|
||||
return state.ac_fan_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback function that will be called when the sensor data changes.
|
||||
*
|
||||
* @param callback The callback function to register.
|
||||
*
|
||||
* @return The handler of the callback function
|
||||
*/
|
||||
uint8_t ClimateCard::registerSensorCallback(std::function<void(float, float)> callback)
|
||||
{
|
||||
sensor_callbacks[sensor_callbacks_handler_count] = callback;
|
||||
return sensor_callbacks_handler_count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a callback function.
|
||||
*
|
||||
* @param handler The handler of the callback function to unregister.
|
||||
*/
|
||||
void ClimateCard::unregisterChangeCallback(uint8_t handler)
|
||||
{
|
||||
callbacks.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a sensor callback function.
|
||||
*
|
||||
* @param handler The handler of the callback function to unregister.
|
||||
*/
|
||||
void ClimateCard::unregisterSensorCallback(uint8_t handler)
|
||||
{
|
||||
sensor_callbacks.erase(handler);
|
||||
}
|
||||
|
||||
void ClimateCard::setState(uint8_t mode, uint8_t fan_speed, uint8_t temperature)
|
||||
{
|
||||
this->state.ac_mode = mode;
|
||||
this->state.ac_fan_speed = fan_speed;
|
||||
this->state.ac_temperature = temperature;
|
||||
updateAirConditioner();
|
||||
if (fram_auto_save)
|
||||
saveStateToFRAM();
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <IRBlaster.hpp>
|
||||
#include <FRAM.h>
|
||||
#include <OneWire.h>
|
||||
#include <DS18B20.h>
|
||||
#include <dhtnew.h>
|
||||
#include <map>
|
||||
|
||||
#define CARD_TYPE_CLIMATE 0x03
|
||||
|
||||
#define AC_SENSOR_TYPE_NONE 0x00
|
||||
#define AC_SENSOR_TYPE_DHT22 0x01
|
||||
#define AC_SENSOR_TYPE_DS18B20 0x02
|
||||
|
||||
#define AC_SENSOR_READ_INTERVAL 5000
|
||||
#define AC_SENSOR_READ_TIMEOUT 250
|
||||
|
||||
/**
|
||||
* @brief The struct is used to store the state of the air conditioner
|
||||
*
|
||||
* @note This struct is stored in FRAM if it is used
|
||||
* @note This struct is 3 bytes long
|
||||
*/
|
||||
struct ClimateCardData {
|
||||
uint8_t ac_temperature; ///< Temperature of the air conditioner
|
||||
uint8_t ac_mode; ///< Mode of the air conditioner
|
||||
uint8_t ac_fan_speed;///< Fan speed of the air conditioner
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This struct is used to store information about an air conditioner
|
||||
*/
|
||||
struct AirConditioner {
|
||||
uint8_t max_temperature; ///< Maximum temperature
|
||||
uint8_t min_temperature; ///< Minimum temperature
|
||||
uint8_t modes; ///< Number of modes
|
||||
const char **mode_names; ///< Names of modes in the form of an array of strings
|
||||
uint8_t fan_speeds; ///< Number of fan speeds
|
||||
const char **fan_speed_names; ///< Names of fan speeds in the form of an array of strings
|
||||
/**
|
||||
* @brief Function to get IR code
|
||||
*
|
||||
* @param mode Mode of the air conditioner
|
||||
* @param fan_speed Fan speed of the air conditioner
|
||||
* @param temperature Temperature of the air conditioner
|
||||
* @param code Pointer to the IR code array
|
||||
*
|
||||
* @return Size of the IR code array
|
||||
*/
|
||||
size_t (*getInfraredCode)(uint8_t, uint8_t, uint8_t, const uint16_t**);
|
||||
};
|
||||
// This requires 3 bytes of FRAM
|
||||
|
||||
/**
|
||||
* @brief The ClimateCard class is a class for controlling an air conditioner
|
||||
*
|
||||
* This class allows you to control an air conditioner using an IR LED.
|
||||
* It is meant to be used with the ESPMega Climate Card.
|
||||
*
|
||||
* @note You can also use a DHT22 or DS18B20 temperature sensor to get the room temperature (and humidity if using a DHT22). Although, this is optional.
|
||||
*
|
||||
*/
|
||||
class ClimateCard : public ExpansionCard {
|
||||
public:
|
||||
ClimateCard(uint8_t ir_pin, AirConditioner ac, uint8_t sensor_type, uint8_t sensor_pin, rmt_channel_t channel);
|
||||
ClimateCard(uint8_t ir_pin, AirConditioner ac, rmt_channel_t channel);
|
||||
~ClimateCard();
|
||||
bool begin();
|
||||
void loop();
|
||||
void bindFRAM(FRAM *fram, uint16_t fram_address);
|
||||
void setFRAMAutoSave(bool autoSave);
|
||||
void saveStateToFRAM();
|
||||
void loadStateFromFRAM();
|
||||
void setTemperature(uint8_t temperature);
|
||||
uint8_t getTemperature();
|
||||
void setMode(uint8_t mode);
|
||||
void setModeByName(const char* mode_name);
|
||||
uint8_t getMode();
|
||||
char* getModeName();
|
||||
void setFanSpeed(uint8_t fan_speed);
|
||||
void setFanSpeedByName(const char* fan_speed_name);
|
||||
uint8_t getFanSpeed();
|
||||
void setState(uint8_t mode, uint8_t fan_speed, uint8_t temperature);
|
||||
char* getFanSpeedName();
|
||||
float getRoomTemperature();
|
||||
float getHumidity();
|
||||
uint8_t getSensorType();
|
||||
uint8_t registerChangeCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback);
|
||||
uint8_t registerSensorCallback(std::function<void(float, float)> callback);
|
||||
void unregisterChangeCallback(uint8_t handler);
|
||||
void unregisterSensorCallback(uint8_t handler);
|
||||
uint8_t getType();
|
||||
private:
|
||||
// Sensor objects
|
||||
// We use pointers here because we don't know which sensor will be used
|
||||
DHTNEW *dht;
|
||||
DS18B20 *ds18b20;
|
||||
// Callbacks
|
||||
uint8_t callbacks_handler_count = 0;
|
||||
uint8_t sensor_callbacks_handler_count = 0;
|
||||
std::map<uint8_t,std::function<void(uint8_t, uint8_t, uint8_t)>> callbacks;
|
||||
std::map<uint8_t,std::function<void(float, float)>> sensor_callbacks;
|
||||
// Update functions
|
||||
void updateSensor();
|
||||
void updateAirConditioner();
|
||||
// IR variables
|
||||
uint8_t ir_pin;
|
||||
IRBlaster ir_blaster;
|
||||
// Air conditioner variables
|
||||
AirConditioner ac;
|
||||
ClimateCardData state;
|
||||
// Sensor variables
|
||||
uint8_t sensor_type;
|
||||
uint8_t sensor_pin;
|
||||
float humidity;
|
||||
float room_temperature;
|
||||
// FRAM variables
|
||||
FRAM *fram;
|
||||
uint16_t fram_address;
|
||||
bool fram_auto_save;
|
||||
uint16_t* getIrIndex(uint8_t mode, uint8_t fan_speed, uint8_t temperature);
|
||||
};
|
|
@ -1,258 +0,0 @@
|
|||
#include <ClimateIoT.hpp>
|
||||
|
||||
|
||||
ClimateIoT::ClimateIoT() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for the ClimateIoT class.
|
||||
*/
|
||||
ClimateIoT::~ClimateIoT() {
|
||||
// Destructor implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the ClimateIoT component.
|
||||
*
|
||||
* This function sets the MQTT client, base topic, card ID, and card pointer.
|
||||
* It also registers the sensor and air conditioner update callbacks.
|
||||
*
|
||||
* @param card_id The ID of the expansion card.
|
||||
* @param card A pointer to the ExpansionCard object.
|
||||
* @param mqtt A pointer to the PubSubClient object.
|
||||
* @param base_topic The base topic for MQTT communication.
|
||||
* @return True if the initialization is successful, false otherwise.
|
||||
*/
|
||||
bool ClimateIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
this->card_id = card_id;
|
||||
this->card = (ClimateCard *)card;
|
||||
// Reister Callbacks
|
||||
auto bindedSensorCallback = std::bind(&ClimateIoT::handleSensorUpdate, this, std::placeholders::_1, std::placeholders::_2);
|
||||
this->card->registerSensorCallback(bindedSensorCallback);
|
||||
auto bindedAirConditionerCallback = std::bind(&ClimateIoT::handleAirConditionerUpdate, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->card->registerChangeCallback(bindedAirConditionerCallback);
|
||||
ESP_LOGD("ClimateIoT", "Climate IoT Component initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles MQTT messages for the ClimateIoT component.
|
||||
*
|
||||
* @param topic The trimmed topic of the MQTT message.
|
||||
* @param payload The null-terminated payload of the MQTT message.
|
||||
*/
|
||||
void ClimateIoT::handleMqttMessage(char *topic, char *payload) {
|
||||
uint8_t topic_length = strlen(topic);
|
||||
if (this->processSetTemperatureMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processSetModeMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processSetFanSpeedMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processRequestStateMessage(topic, payload, topic_length))
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the temperature of the air conditioner to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishClimateTemperature() {
|
||||
char payload[5];
|
||||
itoa(this->card->getTemperature(), payload, 10);
|
||||
this->publishRelative(AC_TEMPERATURE_REPORT_TOPIC, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the mode of the air conditioner to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishClimateMode() {
|
||||
this->publishRelative(AC_MODE_REPORT_TOPIC, this->card->getModeName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the fan speed of the air conditioner to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishClimateFanSpeed() {
|
||||
this->publishRelative(AC_FAN_SPEED_REPORT_TOPIC, this->card->getFanSpeedName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the temperature and humidity of the room to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishSensor() {
|
||||
this->publishRoomTemperature();
|
||||
this->publishHumidity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the climate data (temperature, mode, fan speed) to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishClimate() {
|
||||
this->publishClimateTemperature();
|
||||
this->publishClimateMode();
|
||||
this->publishClimateFanSpeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the room temperature to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishRoomTemperature() {
|
||||
if (this->card->getSensorType() == AC_SENSOR_TYPE_NONE ) {
|
||||
return;
|
||||
}
|
||||
char payload[5];
|
||||
itoa(this->card->getRoomTemperature(), payload, 10);
|
||||
this->publishRelative(AC_ROOM_TEMPERATURE_REPORT_TOPIC, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the humidity of the room to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishHumidity() {
|
||||
if (this->card->getSensorType() == AC_SENSOR_TYPE_DHT22) {
|
||||
char payload[5];
|
||||
itoa(this->card->getHumidity(), payload, 10);
|
||||
this->publishRelative(AC_HUMIDITY_REPORT_TOPIC, payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle Air Conditioner state change.
|
||||
*
|
||||
* @note This function is called by the underlying ClimateCard object and is not meant to be called manually.
|
||||
*
|
||||
* @param temperature Temperature of the air conditioner
|
||||
* @param mode Mode of the air conditioner
|
||||
* @param fan_speed Fan speed of the air conditioner
|
||||
*/
|
||||
void ClimateIoT::handleStateChange(uint8_t temperature, uint8_t mode, uint8_t fan_speed) {
|
||||
this->publishClimate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publishes the climate and sensor data to the MQTT broker.
|
||||
*/
|
||||
void ClimateIoT::publishReport() {
|
||||
this->publishClimate();
|
||||
this->publishSensor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribes to MQTT topics.
|
||||
*/
|
||||
void ClimateIoT::subscribe() {
|
||||
ESP_LOGD("ClimateIoT", " topics");
|
||||
this->subscribeRelative(AC_TEMPERATURE_SET_TOPIC);
|
||||
this->subscribeRelative(AC_MODE_SET_TOPIC);
|
||||
this->subscribeRelative(AC_FAN_SPEED_SET_TOPIC);
|
||||
this->subscribeRelative(AC_REQUEST_STATE_TOPIC);
|
||||
ESP_LOGD("ClimateIoT", "Subscribed to topics");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The loop function for the ClimateIoT component.
|
||||
*
|
||||
* @note This function does nothing.
|
||||
*/
|
||||
void ClimateIoT::loop() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the type of the expansion card.
|
||||
*
|
||||
* @return The type of the expansion card.
|
||||
*/
|
||||
uint8_t ClimateIoT::getType() {
|
||||
return CARD_TYPE_CLIMATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the set temperature MQTT message.
|
||||
*
|
||||
* @param topic The trimmed topic of the MQTT message.
|
||||
* @param payload The null-terminated payload of the MQTT message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @return True if the message is processed, false otherwise.
|
||||
*/
|
||||
bool ClimateIoT::processSetTemperatureMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_TEMPERATURE_SET_TOPIC)) {
|
||||
uint8_t temperature = atoi(payload);
|
||||
this->card->setTemperature(temperature);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the set mode MQTT message.
|
||||
*
|
||||
* @param topic The trimmed topic of the MQTT message.
|
||||
* @param payload The null-terminated payload of the MQTT message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @return True if the message is processed, false otherwise.
|
||||
*/
|
||||
bool ClimateIoT::processSetModeMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_MODE_SET_TOPIC)) {
|
||||
this->card->setModeByName(payload);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the set fan speed MQTT message.
|
||||
*
|
||||
* @param topic The trimmed topic of the MQTT message.
|
||||
* @param payload The null-terminated payload of the MQTT message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @return True if the message is processed, false otherwise.
|
||||
*/
|
||||
bool ClimateIoT::processSetFanSpeedMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_FAN_SPEED_SET_TOPIC)) {
|
||||
this->card->setFanSpeedByName(payload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the request state MQTT message.
|
||||
*
|
||||
* @param topic The trimmed topic of the MQTT message.
|
||||
* @param payload The null-terminated payload of the MQTT message.
|
||||
* @param topic_length The length of the topic.
|
||||
* @return True if the message is processed, false otherwise.
|
||||
*/
|
||||
bool ClimateIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_REQUEST_STATE_TOPIC)) {
|
||||
this->publishReport();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function is a callback function registered with the Climate card to be called when the sensor data is updated.
|
||||
*
|
||||
* @param temperature The room temperature.
|
||||
* @param humidity The room humidity.
|
||||
*
|
||||
* @note The temperature and humidity are not used in this function but are required by the ClimateCard class to match the signature of the callback function.
|
||||
*/
|
||||
void ClimateIoT::handleSensorUpdate(float temperature, float humidity) {
|
||||
this->publishSensor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function is a callback function registered with the Climate card to be called when the air conditioner state is updated.
|
||||
*
|
||||
* @param mode The mode of the air conditioner.
|
||||
* @param fan_speed The fan speed of the air conditioner.
|
||||
* @param temperature The temperature of the air conditioner.
|
||||
*/
|
||||
void ClimateIoT::handleAirConditionerUpdate(uint8_t mode, uint8_t fan_speed, uint8_t temperature) {
|
||||
this->publishClimate();
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
#include <IoTComponent.hpp>
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <ClimateCard.hpp>
|
||||
|
||||
// MQTT Topics
|
||||
#define AC_MODE_REPORT_TOPIC "mode"
|
||||
#define AC_MODE_SET_TOPIC "set/mode"
|
||||
#define AC_TEMPERATURE_REPORT_TOPIC "temperature"
|
||||
#define AC_TEMPERATURE_SET_TOPIC "set/temperature"
|
||||
#define AC_FAN_SPEED_REPORT_TOPIC "fan_speed"
|
||||
#define AC_FAN_SPEED_SET_TOPIC "set/fan_speed"
|
||||
#define AC_ROOM_TEMPERATURE_REPORT_TOPIC "room_temperature"
|
||||
#define AC_HUMIDITY_REPORT_TOPIC "humidity"
|
||||
#define AC_REQUEST_STATE_TOPIC "requeststate"
|
||||
|
||||
/**
|
||||
* @brief The ClimateIoT class is a class for connecting the Climate Card to the IoT module.
|
||||
*
|
||||
* This function allows you to control the Climate Card using MQTT.
|
||||
*
|
||||
* @warning You should not instantiate this class directly, instead use ESPMegaIoT's registerCard function.
|
||||
*/
|
||||
class ClimateIoT : public IoTComponent {
|
||||
public:
|
||||
ClimateIoT();
|
||||
~ClimateIoT();
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void publishClimate();
|
||||
void publishClimateTemperature();
|
||||
void publishClimateMode();
|
||||
void publishClimateFanSpeed();
|
||||
void publishSensor();
|
||||
void publishRoomTemperature();
|
||||
void publishHumidity();
|
||||
void handleStateChange(uint8_t temperature, uint8_t mode, uint8_t fan_speed);
|
||||
void handleSensorUpdate(float temperature, float humidity);
|
||||
void handleAirConditionerUpdate(uint8_t mode, uint8_t fan_speed, uint8_t temperature);
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
void loop();
|
||||
uint8_t getType();
|
||||
private:
|
||||
ClimateCard *card;
|
||||
bool processSetTemperatureMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processSetModeMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processSetFanSpeedMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processRequestStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
};
|
|
@ -1,106 +0,0 @@
|
|||
#include <CurrentTransformerCard.hpp>
|
||||
|
||||
CurrentTransformerCard::CurrentTransformerCard(AnalogCard* analogCard, uint8_t pin, float *voltage, std::function<float(uint16_t)> adcToCurrent, uint32_t conversionInterval)
|
||||
{
|
||||
this->analogCard = analogCard;
|
||||
this->pin = pin;
|
||||
this->voltage = voltage;
|
||||
this->adcToCurrent = adcToCurrent;
|
||||
this->conversionInterval = conversionInterval;
|
||||
}
|
||||
|
||||
void CurrentTransformerCard::bindFRAM(FRAM *fram, uint32_t framAddress)
|
||||
{
|
||||
this->fram = fram;
|
||||
this->framAddress = framAddress;
|
||||
}
|
||||
|
||||
bool CurrentTransformerCard::begin()
|
||||
{
|
||||
// Is analogCard a nullptr?
|
||||
if (this->analogCard == nullptr) {
|
||||
return false;
|
||||
}
|
||||
this->beginConversion();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CurrentTransformerCard::loop()
|
||||
{
|
||||
if (this->lastConversionTime == 0) {
|
||||
this->lastConversionTime = millis();
|
||||
}
|
||||
|
||||
if (millis() - lastConversionLoopTime > this->conversionInterval) {
|
||||
this->beginConversion();
|
||||
lastConversionLoopTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void CurrentTransformerCard::beginConversion()
|
||||
{
|
||||
uint16_t adcValue = this->analogCard->analogRead(this->pin);
|
||||
this->current = this->adcToCurrent(adcValue);
|
||||
this->setEnergy(this->energy + this->current * *this->voltage * (millis() - this->lastConversionTime) / 3600000); // in Wh
|
||||
this->lastConversionTime = millis();
|
||||
}
|
||||
|
||||
void CurrentTransformerCard::setEnergy(float energy)
|
||||
{
|
||||
this->energy = energy;
|
||||
if (this->autoSave) {
|
||||
this->saveEnergy();
|
||||
}
|
||||
for (auto const& callback : this->callbacks) {
|
||||
callback.second(this->current, this->energy);
|
||||
}
|
||||
}
|
||||
|
||||
void CurrentTransformerCard::resetEnergy()
|
||||
{
|
||||
this->setEnergy(0);
|
||||
}
|
||||
|
||||
float CurrentTransformerCard::getCurrent()
|
||||
{
|
||||
return this->current;
|
||||
}
|
||||
|
||||
double CurrentTransformerCard::getEnergy()
|
||||
{
|
||||
return this->energy;
|
||||
}
|
||||
|
||||
uint8_t CurrentTransformerCard::registerCallback(std::function<void(float, double)> callback) {
|
||||
this->callbacks[this->handler_count] = callback;
|
||||
return this->handler_count++;
|
||||
}
|
||||
void CurrentTransformerCard::unregisterCallback(uint8_t handler) {
|
||||
this->callbacks.erase(handler);
|
||||
}
|
||||
|
||||
void CurrentTransformerCard::saveEnergy(){
|
||||
this->fram->write(this->framAddress, (uint8_t*)&this->energy, sizeof(this->energy));
|
||||
}
|
||||
void CurrentTransformerCard::loadEnergy(){
|
||||
this->fram->read(this->framAddress, (uint8_t*)&this->energy, sizeof(this->energy));
|
||||
if (this->energy < 0 || isnan(this->energy)) {
|
||||
this->energy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CurrentTransformerCard::setEnergyAutoSave(bool autoSave){
|
||||
this->autoSave = autoSave;
|
||||
}
|
||||
|
||||
float CurrentTransformerCard::getVoltage(){
|
||||
return *this->voltage;
|
||||
}
|
||||
|
||||
float CurrentTransformerCard::getPower(){
|
||||
return this->current * *this->voltage;
|
||||
}
|
||||
|
||||
uint8_t CurrentTransformerCard::getType() {
|
||||
return CARD_TYPE_CT;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
#pragma once
|
||||
#include <AnalogCard.hpp>
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <FRAM.h>
|
||||
#include <map>
|
||||
|
||||
#define CARD_TYPE_CT 4
|
||||
|
||||
/**
|
||||
* @brief The CurrentTransformer class is a class for reading the current and energy from a current transformer that's connected to the AnalogCard.
|
||||
* Also supports storing energy to FRAM.
|
||||
*/
|
||||
|
||||
class CurrentTransformerCard : public ExpansionCard
|
||||
{
|
||||
public:
|
||||
CurrentTransformerCard(AnalogCard* analogCard, uint8_t pin, float *voltage, std::function<float(uint16_t)> adcToCurrent, uint32_t conversionInterval);
|
||||
void bindFRAM(FRAM *fram, uint32_t framAddress); // Takes 16 bytes of FRAM (long double energy)
|
||||
bool begin();
|
||||
void loop();
|
||||
void beginConversion();
|
||||
void setEnergy(float energy);
|
||||
void resetEnergy();
|
||||
float getCurrent();
|
||||
double getEnergy();
|
||||
float getPower();
|
||||
void saveEnergy();
|
||||
void loadEnergy();
|
||||
void setEnergyAutoSave(bool autoSave);
|
||||
float getVoltage();
|
||||
uint8_t registerCallback(std::function<void(float, double)> callback);
|
||||
void unregisterCallback(uint8_t handler);
|
||||
uint8_t getType();
|
||||
private:
|
||||
AnalogCard* analogCard;
|
||||
uint8_t pin;
|
||||
uint32_t framAddress;
|
||||
FRAM *fram;
|
||||
uint32_t conversionInterval;
|
||||
bool autoSave;
|
||||
float calibration;
|
||||
double energy;
|
||||
float current;
|
||||
float *voltage;
|
||||
uint32_t lastConversionTime;
|
||||
std::function<float(uint16_t)> adcToCurrent; // std::function that convert adc value to current in amps
|
||||
uint8_t handler_count = 0;
|
||||
std::map<uint8_t,std::function<void(float, double)>> callbacks;
|
||||
uint32_t lastConversionLoopTime;
|
||||
};
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#include <CurrentTransformerIoT.hpp>
|
||||
|
||||
CurrentTransformerIoT::CurrentTransformerIoT() {
|
||||
|
||||
}
|
||||
|
||||
CurrentTransformerIoT::~CurrentTransformerIoT() {
|
||||
|
||||
}
|
||||
|
||||
bool CurrentTransformerIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
ESP_LOGD("CurrentTransformerIoT", "Beginning CurrentTransformerIoT");
|
||||
this->card_id = card_id;
|
||||
this->currentTransformerCard = (CurrentTransformerCard*) card;
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
auto bindedCTCallback = std::bind(&CurrentTransformerIoT::handleCTCallback, this, std::placeholders::_1, std::placeholders::_2);
|
||||
this->currentTransformerCard->registerCallback(bindedCTCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CurrentTransformerIoT::handleMqttMessage(char *topic, char *payload) {
|
||||
uint8_t payload_length = strlen(payload);
|
||||
if(this->processSetEnergyMessage(topic, payload, payload_length)) return;
|
||||
if (!strcmp(topic, CT_RESET_ENERGY_TOPIC)) {
|
||||
this->currentTransformerCard->resetEnergy();
|
||||
return;
|
||||
} else if (!strcmp(topic, CT_REQUESTSTATE_TOPIC)) {
|
||||
this->publishReport();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CurrentTransformerIoT::subscribe() {
|
||||
this->subscribeRelative(CT_SET_ENERGY_TOPIC);
|
||||
this->subscribeRelative(CT_RESET_ENERGY_TOPIC);
|
||||
this->subscribeRelative(CT_REQUESTSTATE_TOPIC);
|
||||
}
|
||||
|
||||
void CurrentTransformerIoT::loop() {
|
||||
// Not used, still need this to meet polymorphism requirements
|
||||
}
|
||||
|
||||
void CurrentTransformerIoT::publishReport() {
|
||||
char outputBuffer[256];
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%.2f", this->currentTransformerCard->getPower());
|
||||
this->publishRelative(CT_POWER_TOPIC, outputBuffer);
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%.2f", this->currentTransformerCard->getEnergy());
|
||||
this->publishRelative(CT_ENERGY_TOPIC, outputBuffer);
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%.2f", this->currentTransformerCard->getCurrent());
|
||||
this->publishRelative(CT_CURRENT_TOPIC, outputBuffer);
|
||||
}
|
||||
|
||||
uint8_t CurrentTransformerIoT::getType() {
|
||||
return this->currentTransformerCard->getType();
|
||||
}
|
||||
|
||||
bool CurrentTransformerIoT::processSetEnergyMessage(char* topic, char* payload, uint8_t topic_length) {
|
||||
if(strcmp(topic, CT_SET_ENERGY_TOPIC)) return false;
|
||||
this->currentTransformerCard->setEnergy(atof(payload));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CurrentTransformerIoT::handleCTCallback(float current, double energy) {
|
||||
this->publishReport();
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
#include <CurrentTransformerCard.hpp>
|
||||
#include <IoTComponent.hpp>
|
||||
#include <ExpansionCard.hpp>
|
||||
|
||||
#define CT_REQUESTSTATE_TOPIC "requeststate"
|
||||
#define CT_SET_ENERGY_TOPIC "energy/set"
|
||||
#define CT_RESET_ENERGY_TOPIC "energy/reset"
|
||||
#define CT_ENERGY_TOPIC "energy"
|
||||
#define CT_POWER_TOPIC "power"
|
||||
#define CT_CURRENT_TOPIC "current"
|
||||
|
||||
class CurrentTransformerIoT : public IoTComponent
|
||||
{
|
||||
public:
|
||||
CurrentTransformerIoT();
|
||||
~CurrentTransformerIoT();
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void subscribe();
|
||||
void loop();
|
||||
void publishReport();
|
||||
uint8_t getType();
|
||||
private:
|
||||
CurrentTransformerCard *currentTransformerCard;
|
||||
bool processSetEnergyMessage(char* topic, char* payload, uint8_t topic_length);
|
||||
void handleCTCallback(float current, double energy);
|
||||
};
|
|
@ -1,401 +0,0 @@
|
|||
#include <DigitalInputCard.hpp>
|
||||
|
||||
/**
|
||||
* @brief Create a new Digital Input Card object with the specified address
|
||||
* @note If you are using the ESPMegaI/O board, you should use the dip switch constructor
|
||||
*
|
||||
* @param address_a The ESPMegaI/O address of bank A
|
||||
* @param address_b The ESPMegaI/O address of bank B
|
||||
*/
|
||||
DigitalInputCard::DigitalInputCard(uint8_t address_a, uint8_t address_b) : callbacks()
|
||||
{
|
||||
this->address_a = address_a;
|
||||
this->address_b = address_b;
|
||||
this->callbacks_handler_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Digital Input Card object with the specified position on the dip switch
|
||||
*
|
||||
* @note The bit 0 are at the left of the dip switch
|
||||
*
|
||||
* @warning There are 6 switches on the dip switch, 3 for bank A and 3 for bank B, They should be unique for each bank accross all the cards
|
||||
*
|
||||
* @param bit0 The position of the first switch on the dip switch
|
||||
* @param bit1 The position of the second switch on the dip switch
|
||||
* @param bit2 The position of the third switch on the dip switch
|
||||
* @param bit3 The position of the fourth switch on the dip switch
|
||||
* @param bit4 The position of the fifth switch on the dip switch
|
||||
* @param bit5 The position of the sixth switch on the dip switch
|
||||
*/
|
||||
DigitalInputCard::DigitalInputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4, bool bit5)
|
||||
{
|
||||
this->address_a = 0x20;
|
||||
this->address_b = 0x20;
|
||||
this->inputBufferA = 0;
|
||||
this->inputBufferB = 0;
|
||||
if (bit0)
|
||||
this->address_a += 1;
|
||||
if (bit1)
|
||||
this->address_a += 2;
|
||||
if (bit2)
|
||||
this->address_a += 4;
|
||||
if (bit3)
|
||||
this->address_b += 1;
|
||||
if (bit4)
|
||||
this->address_b += 2;
|
||||
if (bit5)
|
||||
this->address_b += 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the Digital Input Card
|
||||
*
|
||||
* @return True if the initialization is successful, false otherwise
|
||||
*/
|
||||
bool DigitalInputCard::begin()
|
||||
{
|
||||
this->inputBankA = PCF8574(this->address_a);
|
||||
this->inputBankB = PCF8574(this->address_b);
|
||||
if (!this->inputBankA.begin())
|
||||
{
|
||||
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Failed to install input bank A");
|
||||
this->initOk = false;
|
||||
return false;
|
||||
}
|
||||
if (!this->inputBankB.begin())
|
||||
{
|
||||
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Failed to install input bank B");
|
||||
this->initOk = false;
|
||||
return false;
|
||||
}
|
||||
// Set the debounce time for all pins to 50ms
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
this->debounceTime[i] = 50;
|
||||
this->lastDebounceTime[i] = 0;
|
||||
}
|
||||
// Initialize the pin map to the default values
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
this->pinMap[i] = i;
|
||||
this->virtualPinMap[i] = i;
|
||||
}
|
||||
this->initOk = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the input from the specified pin, always refresh the input buffers
|
||||
*
|
||||
* @param pin The pin to read from
|
||||
* @return True if the pin is HIGH, false if the pin is LOW
|
||||
*/
|
||||
bool DigitalInputCard::digitalRead(uint8_t pin)
|
||||
{
|
||||
return this->digitalRead(pin, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the input from the specified pin, also refresh the input buffers if refresh is true
|
||||
*
|
||||
* @param pin The pin to read from
|
||||
* @param refresh If true, the input buffers will be refreshed before reading the pin
|
||||
* @return True if the pin is HIGH, false if the pin is LOW
|
||||
*/
|
||||
bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh)
|
||||
{
|
||||
if(!this->initOk)
|
||||
{
|
||||
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Card not initialized");
|
||||
return false;
|
||||
}
|
||||
pin = pinMap[pin];
|
||||
// First check if the pin is in bank A or B
|
||||
if (pin >= 0 && pin <= 7)
|
||||
{
|
||||
// Refresh the input buffers if refresh is true
|
||||
if (refresh)
|
||||
refreshInputBankA();
|
||||
// Extract the bit from the buffer
|
||||
return ((inputBufferA >> (7 - pin)) & 1);
|
||||
}
|
||||
else if (pin >= 8 && pin <= 15)
|
||||
{
|
||||
// Refresh the input buffers if refresh is true
|
||||
if (refresh)
|
||||
refreshInputBankB();
|
||||
// Extract the bit from the buffer
|
||||
return ((inputBufferB >> (15 - pin)) & 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the specified pin changed since the last call to this function
|
||||
*
|
||||
* @note This function compares the current input buffer with the previous input buffer to detect changes
|
||||
*
|
||||
* @param pin The pin to check
|
||||
* @param currentBuffer The current input buffer
|
||||
* @param previousBuffer The previous input buffer
|
||||
*/
|
||||
void DigitalInputCard::handlePinChange(int pin, uint8_t ¤tBuffer, uint8_t &previousBuffer)
|
||||
{
|
||||
// Get the index of the pin in the pin map
|
||||
uint8_t virtualPin = virtualPinMap[pin];
|
||||
if (pin < 8)
|
||||
{
|
||||
// Handle Bank A
|
||||
if (((previousBuffer >> (7 - pin)) & 1) != ((currentBuffer >> (7 - pin)) & 1))
|
||||
{
|
||||
// When the pin first change, store the time
|
||||
if (!pinChanged[virtualPin])
|
||||
{
|
||||
ESP_LOGD("DigitalInputCard", "Pin %d changed", virtualPin);
|
||||
pinChanged[virtualPin] = true;
|
||||
lastDebounceTime[virtualPin] = millis();
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGD("DigitalInputCard", "Pin %d (%d>%d) debounce time: %d", virtualPin, ((previousBuffer >> (7 - pin)) & 1), ((currentBuffer >> (7 - pin)) & 1), millis() - lastDebounceTime[virtualPin]);
|
||||
// If the pin was already changed, check if the debounce time has passed
|
||||
if ((millis() - lastDebounceTime[virtualPin]) > debounceTime[virtualPin])
|
||||
{
|
||||
ESP_LOGD("DigitalInputCard", "Pin %d triggered", virtualPin);
|
||||
// Call the callback function
|
||||
for (auto const &callback : callbacks)
|
||||
{
|
||||
callback.second(virtualPin, ((currentBuffer >> (7 - pin)) & 1));
|
||||
}
|
||||
// Store the previous buffer at the specified pin (bitwise operation)
|
||||
// new value : (currentBuffer >> (7 - pin)) & 1)
|
||||
previousBuffer = (previousBuffer & ~(1 << (7 - pin))) | (((currentBuffer >> (7 - pin)) & 1) << (7 - pin));
|
||||
// Reset the pin changed flag
|
||||
pinChanged[virtualPin] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pin bounce back to previous state, reset the debounce time
|
||||
lastDebounceTime[virtualPin] = millis();
|
||||
pinChanged[virtualPin] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle Bank B
|
||||
if (((previousBuffer >> (15 - pin)) & 1) != ((currentBuffer >> (15 - pin)) & 1))
|
||||
{
|
||||
// When the pin first change, store the time
|
||||
if (!pinChanged[virtualPin])
|
||||
{
|
||||
ESP_LOGD("DigitalInputCard", "Pin %d changed", virtualPin);
|
||||
pinChanged[virtualPin] = true;
|
||||
lastDebounceTime[virtualPin] = millis();
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGD("DigitalInputCard", "Pin %d (%d>%d) debounce time: %d", virtualPin, ((previousBuffer >> (15 - pin)) & 1), ((currentBuffer >> (15 - pin)) & 1), millis() - lastDebounceTime[virtualPin]);
|
||||
// If the pin was already changed, check if the debounce time has passed
|
||||
if ((millis() - lastDebounceTime[virtualPin]) > debounceTime[virtualPin])
|
||||
{
|
||||
ESP_LOGD("DigitalInputCard", "Pin %d triggered", virtualPin);
|
||||
// Call the callback function
|
||||
for (auto const &callback : callbacks)
|
||||
{
|
||||
callback.second(virtualPin, ((currentBuffer >> (15 - pin)) & 1));
|
||||
}
|
||||
// Store the previous buffer at the specified pin (bitwise operation)
|
||||
// new value : (currentBuffer >> (15 - pin)) & 1)
|
||||
previousBuffer = (previousBuffer & ~(1 << (15 - pin))) | (((currentBuffer >> (15 - pin)) & 1) << (15 - pin));
|
||||
// Reset the pin changed flag
|
||||
pinChanged[virtualPin] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pin bounce back to previous state, reset the debounce time
|
||||
lastDebounceTime[virtualPin] = millis();
|
||||
pinChanged[virtualPin] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A loop to refresh the input buffers and check for pin changes
|
||||
*
|
||||
* @note Although this function can be called in the main loop, it is recommended install the card in ESPMega to automatically manage the loop
|
||||
*/
|
||||
// Preform a loop to refresh the input buffers
|
||||
void DigitalInputCard::loop()
|
||||
{
|
||||
// Refresh the input buffers
|
||||
refreshInputBankA();
|
||||
refreshInputBankB();
|
||||
// Iterate over all pins and check if they changed
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
// Check which bank the pin is in
|
||||
if (i < 8)
|
||||
{
|
||||
handlePinChange(i, inputBufferA, previousInputBufferA);
|
||||
}
|
||||
else if (i >= 8 && i <= 15)
|
||||
{
|
||||
handlePinChange(i, inputBufferB, previousInputBufferB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the input buffer for bank A (the first 8 pins)
|
||||
*
|
||||
* @return The input buffer for bank A where the first bit is the first pin and the last bit is the last pin
|
||||
*/
|
||||
uint8_t DigitalInputCard::getInputBufferA()
|
||||
{
|
||||
// Rearrange the bits to match the pin map
|
||||
uint8_t inputBufferA_rearranged = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
inputBufferA_rearranged |= ((inputBufferA >> i) & 1) << (7 - i);
|
||||
}
|
||||
return inputBufferA_rearranged;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the input buffer for bank B (the last 8 pins)
|
||||
*
|
||||
* @return The input buffer for bank B where the first bit is the first pin and the last bit is the last pin
|
||||
*/
|
||||
uint8_t DigitalInputCard::getInputBufferB()
|
||||
{
|
||||
// Rearrange the bits to match the pin map
|
||||
uint8_t inputBufferB_rearranged = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
inputBufferB_rearranged |= ((inputBufferB >> i) & 1) << (7 - i);
|
||||
}
|
||||
return inputBufferB_rearranged;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback function to be called when a pin changes
|
||||
*
|
||||
* @param callback The callback function to be called
|
||||
* @return The handler of the callback function
|
||||
*/
|
||||
uint8_t DigitalInputCard::registerCallback(std::function<void(uint8_t, bool)> callback)
|
||||
{
|
||||
callbacks[this->callbacks_handler_index] = callback;
|
||||
return this->callbacks_handler_index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the input state from the input ic and store it in the input buffer for bank A
|
||||
*/
|
||||
void DigitalInputCard::refreshInputBankA()
|
||||
{
|
||||
if(!this->initOk)
|
||||
{
|
||||
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Card not initialized");
|
||||
return;
|
||||
}
|
||||
inputBufferA = inputBankA.read8();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the input state from the input ic and store it in the input buffer for bank B
|
||||
*/
|
||||
void DigitalInputCard::refreshInputBankB()
|
||||
{
|
||||
if(!this->initOk)
|
||||
{
|
||||
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Card not initialized");
|
||||
return;
|
||||
}
|
||||
inputBufferB = inputBankB.read8();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the debounce time for the specified pin
|
||||
*
|
||||
* Debounce is the time in milliseconds that the pin should be stable before the callback function is called
|
||||
* This is useful to prevent false triggers when the input is noisy
|
||||
* An example of this is when the input is connected to a mechanical switch
|
||||
*
|
||||
* @param pin The pin to set the debounce time for
|
||||
* @param debounceTime The debounce time in milliseconds
|
||||
*/
|
||||
void DigitalInputCard::setDebounceTime(uint8_t pin, uint32_t debounceTime)
|
||||
{
|
||||
pin = pinMap[pin];
|
||||
this->debounceTime[pin] = debounceTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a callback function
|
||||
*
|
||||
* @param handler The handler of the callback function to unregister
|
||||
*/
|
||||
void DigitalInputCard::unregisterCallback(uint8_t handler)
|
||||
{
|
||||
callbacks.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the pin map for the card
|
||||
*
|
||||
* A pin map is an array of 16 elements that maps the physical pins to virtual pins
|
||||
* The virtual pins are the pins that are used in the callback functions and are used for all the functions in this class
|
||||
* The physical pins are the pins on the Input IC, This can be found on the schematic of the ESPMegaI/O board
|
||||
* This function is useful if you want to change the number identification of the pins to match your project needs
|
||||
*
|
||||
* @param pinMap The pin map to load
|
||||
*/
|
||||
void DigitalInputCard::loadPinMap(uint8_t pinMap[16])
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
// Load the pin map (physical pin to virtual pin)
|
||||
this->pinMap[i] = pinMap[i];
|
||||
// Load the virtual pin map (virtual pin to physical pin)
|
||||
this->virtualPinMap[pinMap[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the type of the card
|
||||
*
|
||||
* @return The type of the card
|
||||
*/
|
||||
uint8_t DigitalInputCard::getType()
|
||||
{
|
||||
return CARD_TYPE_DIGITAL_INPUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Preload the previous input buffer and the input buffer
|
||||
*
|
||||
* @note This function is useful if you want to preload the input buffers with a run-time value
|
||||
*/
|
||||
void DigitalInputCard::preloadInputBuffer()
|
||||
{
|
||||
refreshInputBankA();
|
||||
refreshInputBankB();
|
||||
previousInputBufferA = inputBufferA;
|
||||
previousInputBufferB = inputBufferB;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the status of the card
|
||||
*
|
||||
* @return True if the card is initialized, false otherwise
|
||||
*/
|
||||
bool DigitalInputCard::getStatus()
|
||||
{
|
||||
return this->initOk;
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <PCF8574.h>
|
||||
#include <map>
|
||||
|
||||
// Card Type
|
||||
#define CARD_TYPE_DIGITAL_INPUT 0x01
|
||||
|
||||
/**
|
||||
* @brief ESPMegaPRO Digital Input Card
|
||||
*
|
||||
* This class represents the ESPMegaPRO Digital Input Card.
|
||||
* It allows you to read the state of the digital inputs from the ESPMegaPRO Digital Input Card.
|
||||
* It also allows you to register callback functions to be called when a pin changes.
|
||||
* The callback function also support debouncing.
|
||||
*
|
||||
*/
|
||||
class DigitalInputCard : public ExpansionCard {
|
||||
public:
|
||||
// Instantiate the card with the specified address
|
||||
DigitalInputCard(uint8_t address_a, uint8_t address_b);
|
||||
// Instantiate the card with the specified position on the dip switch
|
||||
DigitalInputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4, bool bit5);
|
||||
// Initialize the card
|
||||
bool begin();
|
||||
// Refresh and Read the input from the specified pin, always refresh the input buffers
|
||||
bool digitalRead(uint8_t pin);
|
||||
// Read the input from the specified pin, also refresh the input buffers if refresh is true
|
||||
bool digitalRead(uint8_t pin, bool refresh);
|
||||
// Preform a loop to refresh the input buffers
|
||||
void loop();
|
||||
// Get the input buffer for bank A
|
||||
uint8_t getInputBufferA();
|
||||
// Get the input buffer for bank B
|
||||
uint8_t getInputBufferB();
|
||||
// Set the debounce time for the specified pin
|
||||
void setDebounceTime(uint8_t pin, uint32_t debounceTime);
|
||||
// Register a callback function to be called when a pin changes
|
||||
uint8_t registerCallback(std::function<void(uint8_t, bool)> callback);
|
||||
// Unregister the callback function
|
||||
void unregisterCallback(uint8_t handler);
|
||||
// Load a new pin map
|
||||
void loadPinMap(uint8_t pinMap[16]);
|
||||
// Preload previousInputBuffer and inputBuffer
|
||||
void preloadInputBuffer();
|
||||
// Status of card
|
||||
bool getStatus();
|
||||
// Get type of card
|
||||
uint8_t getType();
|
||||
private:
|
||||
bool initOk = false;
|
||||
PCF8574 inputBankA;
|
||||
PCF8574 inputBankB;
|
||||
uint8_t address_a;
|
||||
uint8_t address_b;
|
||||
uint8_t inputBufferA;
|
||||
uint8_t inputBufferB;
|
||||
uint8_t previousInputBufferA;
|
||||
uint8_t previousInputBufferB;
|
||||
uint32_t debounceTime[16];
|
||||
uint32_t lastDebounceTime[16];
|
||||
bool pinChanged[16];
|
||||
// A map of the physical pin to the virtual pin
|
||||
uint8_t pinMap[16];
|
||||
// A map of the virtual pin to the physical pin
|
||||
uint8_t virtualPinMap[16];
|
||||
uint8_t callbacks_handler_index = 0;
|
||||
std::map<uint8_t, std::function<void(uint8_t, bool)>> callbacks;
|
||||
void refreshInputBankA();
|
||||
void refreshInputBankB();
|
||||
void handlePinChange(int pin, uint8_t& currentBuffer, uint8_t& previousBuffer);
|
||||
};
|
|
@ -1,128 +0,0 @@
|
|||
#include <DigitalInputIoT.hpp>
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initializes the DigitalInputIoT object.
|
||||
*
|
||||
* This function sets the necessary parameters for the DigitalInputIoT object, such as the card ID, expansion card, MQTT client, and base topic.
|
||||
* It also enables the publishing of digital input values and registers a callback function for handling value changes.
|
||||
*
|
||||
* @note Although this function can be called in the main program, it is recommended to use ESPMegaIoT::registerCard() to automatically manage the instantiation and initialization of this component.
|
||||
*
|
||||
* @param card_id The ID of the card.
|
||||
* @param card Pointer to the DigitalInputCard object.
|
||||
* @param mqtt Pointer to the PubSubClient object.
|
||||
* @param base_topic The base topic for MQTT communication.
|
||||
* @return True if the initialization is successful, false otherwise.
|
||||
*/
|
||||
bool DigitalInputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
this->card = (DigitalInputCard *)card;
|
||||
if(!this->card->getStatus()) {
|
||||
return false;
|
||||
}
|
||||
this->card_id = card_id;
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
this->setDigitalInputsPublishEnabled(true);
|
||||
this->card->registerCallback(std::bind(&DigitalInputIoT::handleValueChange, this, std::placeholders::_1, std::placeholders::_2));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribes to the MQTT topics for the DigitalInputIoT component.
|
||||
*/
|
||||
void DigitalInputIoT::subscribe() {
|
||||
this->subscribeRelative(PUBLISH_ENABLE_TOPIC);
|
||||
this->subscribeRelative(INPUT_REQUEST_STATE_TOPIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles MQTT messages for the DigitalInputIoT component.
|
||||
*
|
||||
* @param topic The trimmed topic of the MQTT message.
|
||||
* @param payload The null-terminated payload of the MQTT message.
|
||||
*/
|
||||
void DigitalInputIoT::handleMqttMessage(char *topic, char *payload) {
|
||||
if (!strcmp(topic, INPUT_REQUEST_STATE_TOPIC)) {
|
||||
this->publishDigitalInputs();
|
||||
}
|
||||
// payload is char '0' or '1'
|
||||
else if (!strcmp(topic, PUBLISH_ENABLE_TOPIC)) {
|
||||
if (payload[0] == '1') {
|
||||
this->setDigitalInputsPublishEnabled(true);
|
||||
} else {
|
||||
this->setDigitalInputsPublishEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish all digital inputs to the MQTT broker.
|
||||
*/
|
||||
void DigitalInputIoT::publishDigitalInputs() {
|
||||
if (!this->digital_inputs_publish_enabled) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
this->publishDigitalInput(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set if the digital inputs should be published to the MQTT broker.
|
||||
*
|
||||
* @param enabled True if the digital inputs should be published, false otherwise.
|
||||
*/
|
||||
void DigitalInputIoT::setDigitalInputsPublishEnabled(bool enabled) {
|
||||
this->digital_inputs_publish_enabled = enabled;
|
||||
if (enabled) {
|
||||
this->publishDigitalInputs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles a value change for a digital input.
|
||||
*
|
||||
* @note This function is registered as a callback function for the DigitalInputCard object.
|
||||
*
|
||||
* @param pin The pin that changed.
|
||||
* @param value The new value of the pin.
|
||||
*/
|
||||
void DigitalInputIoT::handleValueChange(uint8_t pin, uint8_t value) {
|
||||
if (this->digital_inputs_publish_enabled) {
|
||||
this->publishDigitalInput(pin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish all inputs to the MQTT Broker
|
||||
*
|
||||
* @note This function is overriden from the IoTComponent class and is called by ESPMegaIoT.
|
||||
*
|
||||
* @param pin The pin to publish.
|
||||
*/
|
||||
void DigitalInputIoT::publishReport() {
|
||||
this->publishDigitalInputs();
|
||||
}
|
||||
uint8_t DigitalInputIoT::getType() {
|
||||
return CARD_TYPE_DIGITAL_INPUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish a digital input to the MQTT broker.
|
||||
*
|
||||
* @param pin The pin to publish.
|
||||
*/
|
||||
void DigitalInputIoT::publishDigitalInput(uint8_t pin) {
|
||||
char topic[20] = {0};
|
||||
char payload[20] = {0};
|
||||
topic[0] = pin/10 + '0';
|
||||
topic[1] = pin%10 + '0';
|
||||
topic[2] = '\0';
|
||||
payload[0] = this->card->digitalRead(pin, false) + '0';
|
||||
payload[1] = '\0';
|
||||
this->publishRelative(topic, payload);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <IoTComponent.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <FRAM.h>
|
||||
|
||||
// MQTT Topics
|
||||
#define PUBLISH_ENABLE_TOPIC "publish_enable"
|
||||
#define INPUT_REQUEST_STATE_TOPIC "requeststate"
|
||||
|
||||
/**
|
||||
* @brief The DigitalInputIoT class is a class for connecting the Digital Input Card to the IoT module.
|
||||
*
|
||||
* This function allows you to control the Digital Input Card using MQTT.
|
||||
*
|
||||
* @warning You should not instantiate this class directly, instead use ESPMegaIoT's registerCard function.
|
||||
*/
|
||||
class DigitalInputIoT : public IoTComponent {
|
||||
public:
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void publishDigitalInputs();
|
||||
void publishDigitalInput(uint8_t pin);
|
||||
void setDigitalInputsPublishEnabled(bool enabled);
|
||||
void handleValueChange(uint8_t pin, uint8_t value);
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
uint8_t getType();
|
||||
private:
|
||||
bool digital_inputs_publish_enabled = false;
|
||||
DigitalInputCard *card;
|
||||
};
|
|
@ -1,83 +0,0 @@
|
|||
#include <DigitalInputRTU.hpp>
|
||||
|
||||
DigitalInputRTU::DigitalInputRTU()
|
||||
{
|
||||
this->iot = nullptr;
|
||||
this->size = 0;
|
||||
this->callbackCount = 0;
|
||||
for (int i = 0; i < 16; i++)
|
||||
this->inputBuffer[i] = false;
|
||||
}
|
||||
|
||||
DigitalInputRTU::~DigitalInputRTU()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DigitalInputRTU::begin(char *remoteBaseTopic, uint8_t remote_card_slot, ESPMegaIoT *iot)
|
||||
{
|
||||
this->remoteBaseTopic = remoteBaseTopic;
|
||||
this->remoteBaseTopicLength = strlen(remoteBaseTopic);
|
||||
this->cardSlot = remote_card_slot;
|
||||
this->iot = iot;
|
||||
}
|
||||
|
||||
void DigitalInputRTU::subscribe()
|
||||
{
|
||||
// Subscribe to all pins
|
||||
for(uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
char* topic = (char*)calloc(this->remoteBaseTopicLength + 6, sizeof(char));
|
||||
sprintf(topic, "%s/%02d/%02d", this->remoteBaseTopic, this->cardSlot, i);
|
||||
this->iot->subscribe(topic);
|
||||
free(topic);
|
||||
}
|
||||
}
|
||||
|
||||
bool DigitalInputRTU::digitalRead(uint8_t pin)
|
||||
{
|
||||
return this->inputBuffer[pin];
|
||||
}
|
||||
|
||||
uint8_t DigitalInputRTU::registerCallback(std::function<void(uint8_t, bool)> callback)
|
||||
{
|
||||
this->callbacks[callbackCount] = callback;
|
||||
return callbackCount++;
|
||||
}
|
||||
|
||||
void DigitalInputRTU::unregisterCallback(uint8_t handler)
|
||||
{
|
||||
this->callbacks.erase(handler);
|
||||
}
|
||||
|
||||
void DigitalInputRTU::mqttCallback(char *topic, char *payload)
|
||||
{
|
||||
// First trim base topic from topic
|
||||
char* topicWithoutBase = topic + this->remoteBaseTopicLength;
|
||||
// Then check if topic is in format /cardSlot/pin
|
||||
// Example trimmed topic: /01/00
|
||||
// Card slot range from 00 to 99
|
||||
// Pin range from 00 to 15
|
||||
// Note that both card and pin are padded with 0 if they are less than 10
|
||||
if(strlen(topicWithoutBase) != 6)
|
||||
return;
|
||||
if(topicWithoutBase[0] != '/')
|
||||
return;
|
||||
if(topicWithoutBase[3] != '/')
|
||||
return;
|
||||
// Extract card slot and pin
|
||||
uint8_t cardSlot = (topicWithoutBase[1] - '0') * 10 + (topicWithoutBase[2] - '0');
|
||||
uint8_t pin = (topicWithoutBase[4] - '0') * 10 + (topicWithoutBase[5] - '0');
|
||||
// Check if card slot is correct
|
||||
if(cardSlot != this->cardSlot)
|
||||
return;
|
||||
// Extract value
|
||||
bool value = strcmp(payload, "1") == 0;
|
||||
// Update input buffer
|
||||
this->inputBuffer[pin] = value;
|
||||
// Call callbacks
|
||||
for(auto const& callback : this->callbacks)
|
||||
{
|
||||
callback.second(pin, value);
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
#pragma once
|
||||
#include <ESPMegaRTU.hpp>
|
||||
|
||||
class DigitalInputRTU : public ESPMegaRTU {
|
||||
public:
|
||||
DigitalInputRTU();
|
||||
~DigitalInputRTU();
|
||||
void begin(char* remoteBaseTopic, uint8_t remote_card_slot, ESPMegaIoT* iot);
|
||||
void subscribe();
|
||||
bool digitalRead(uint8_t pin);
|
||||
uint8_t registerCallback(std::function<void(uint8_t, bool)> callback);
|
||||
void unregisterCallback(uint8_t handler);
|
||||
private:
|
||||
void mqttCallback(char* topic, char* payload);
|
||||
ESPMegaIoT* iot;
|
||||
size_t size;
|
||||
uint16_t callbackCount;
|
||||
std::map<uint8_t, std::function<void(uint8_t, bool)>> callbacks;
|
||||
bool inputBuffer[16];
|
||||
};
|
|
@ -1,318 +0,0 @@
|
|||
#include <DigitalOutputCard.hpp>
|
||||
|
||||
/**
|
||||
* @brief Create a new Digital Output Card object with the specified address
|
||||
*
|
||||
* @note If you are using the ESPMegaI/O board, you should use the dip switch constructor
|
||||
*
|
||||
* @param address The ESPMegaI/O address of the card
|
||||
*/
|
||||
DigitalOutputCard::DigitalOutputCard(uint8_t address) : change_callbacks(){
|
||||
this->address = address;
|
||||
// load default pin map
|
||||
for (int i = 0; i < 16; i++) {
|
||||
this->pinMap[i] = i;
|
||||
this->virtualPinMap[i] = i;
|
||||
}
|
||||
this->framBinded = false;
|
||||
this->callbacks_handler_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Digital Output Card object with the specified position on the dip switch
|
||||
*
|
||||
* @note The bit 0 are at the left of the dip switch
|
||||
*
|
||||
* @warning There are 5 switches on the dip switch, they should be unique accross all the cards
|
||||
*
|
||||
* @param bit0 The position of the first switch on the dip switch
|
||||
* @param bit1 The position of the second switch on the dip switch
|
||||
* @param bit2 The position of the third switch on the dip switch
|
||||
* @param bit3 The position of the fourth switch on the dip switch
|
||||
* @param bit4 The position of the fifth switch on the dip switch
|
||||
*/
|
||||
DigitalOutputCard::DigitalOutputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4) :
|
||||
DigitalOutputCard(0x40+bit0+bit1*2+bit2*4+bit3*8+bit4*16)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the Digital Output Card
|
||||
*
|
||||
* @note Although this function can be called inside the main program, it is recommended to use ESPMegaPRO::installCard() instead
|
||||
*
|
||||
* @return True if the initialization is successful, false otherwise
|
||||
*/
|
||||
bool DigitalOutputCard::begin() {
|
||||
this->pwm = Adafruit_PWMServoDriver(this->address);
|
||||
this->pwm.begin();
|
||||
pwm.setOutputMode(true);
|
||||
// Output card don't send ack, we can't check if it's connected
|
||||
// so we just return true
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a digtal LOW or HIGH to the specified pin
|
||||
*
|
||||
* @note This function set both the state and the pwm value of the pin
|
||||
*
|
||||
* @param pin The pin to set the state
|
||||
* @param state The logic level to set the pin to
|
||||
*/
|
||||
void DigitalOutputCard::digitalWrite(uint8_t pin, bool state) {
|
||||
this->pwm.setPin(virtualPinMap[pin], state ? 4095 : 0);
|
||||
this->state_buffer[pin] = state;
|
||||
this->value_buffer[pin] = state ? 4095 : 0;
|
||||
if (this->framAutoSave) {
|
||||
this->saveStateToFRAM();
|
||||
this->savePinValueToFRAM(pin);
|
||||
}
|
||||
for (const auto& callback : change_callbacks)
|
||||
{
|
||||
callback.second(pin, state, state ? 4095 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a pwm value to the specified pin
|
||||
*
|
||||
* @note This function set both the state and the pwm value of the pin
|
||||
*
|
||||
* @param pin The pin to set the pwm value
|
||||
* @param value The pwm value to set
|
||||
*/
|
||||
void DigitalOutputCard::analogWrite(uint8_t pin, uint16_t value) {
|
||||
// If value is greater than 4095, set it to 4095
|
||||
if (value > 4095) value = 4095;
|
||||
// Set the pwm value
|
||||
this->pwm.setPin(virtualPinMap[pin], value);
|
||||
if (this->framAutoSave) {
|
||||
this->saveStateToFRAM();
|
||||
this->savePinValueToFRAM(pin);
|
||||
}
|
||||
this->state_buffer[pin] = value > 0;
|
||||
this->value_buffer[pin] = value;
|
||||
for (const auto& callback : change_callbacks)
|
||||
{
|
||||
callback.second(pin, value > 0, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop for the Digital Output Card object.
|
||||
*
|
||||
* @note This function is not used, it is only here to implement the ExpansionCard interface
|
||||
*/
|
||||
void DigitalOutputCard::loop() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the state of the specified pin
|
||||
*
|
||||
* @param pin The pin to get the state
|
||||
* @return The state of the pin
|
||||
*/
|
||||
bool DigitalOutputCard::getState(uint8_t pin) {
|
||||
return this->state_buffer[pin];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the pwm value of the specified pin
|
||||
*
|
||||
* @param pin The pin to get the pwm value
|
||||
* @return The pwm value of the pin
|
||||
*/
|
||||
uint16_t DigitalOutputCard::getValue(uint8_t pin) {
|
||||
return this->value_buffer[pin];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the type of the card
|
||||
*
|
||||
* @return The type of the card
|
||||
*/
|
||||
uint8_t DigitalOutputCard::getType() {
|
||||
return CARD_TYPE_DIGITAL_OUTPUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the state of the specified pin
|
||||
*
|
||||
* @param pin The pin to set the state
|
||||
* @param state The state of the pin
|
||||
*/
|
||||
void DigitalOutputCard::setState(uint8_t pin, bool state) {
|
||||
this-> state_buffer[pin] = state;
|
||||
this->pwm.setPin(virtualPinMap[pin], state*value_buffer[pin]);
|
||||
if(this->framAutoSave) {
|
||||
this->saveStateToFRAM();
|
||||
}
|
||||
for(const auto& callback : change_callbacks) {
|
||||
callback.second(pin, state, value_buffer[pin]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the pwm value of the specified pin
|
||||
*
|
||||
* @param pin The pin to set the pwm value
|
||||
* @param value The pwm value to set
|
||||
*/
|
||||
void DigitalOutputCard::setValue(uint8_t pin, uint16_t value) {
|
||||
// If value is greater than 4095, set it to 4095
|
||||
if (value > 4095) value = 4095;
|
||||
this-> value_buffer[pin] = value;
|
||||
this->pwm.setPin(virtualPinMap[pin], state_buffer[pin]*value);
|
||||
if (this->framAutoSave) {
|
||||
this->savePinValueToFRAM(pin);
|
||||
}
|
||||
for (const auto& callback : change_callbacks)
|
||||
{
|
||||
callback.second(pin, state_buffer[pin], value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback function for the specified pin
|
||||
*
|
||||
* @param callback The callback function to be called, the first parameter is the pin, the second parameter is the state, the third parameter is the pwm value
|
||||
* @return The handler of the callback function
|
||||
*/
|
||||
uint8_t DigitalOutputCard::registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback) {
|
||||
this->change_callbacks[this->callbacks_handler_index] = callback;
|
||||
return this->callbacks_handler_index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a callback function
|
||||
*
|
||||
* @param handler The handler of the callback function to be unregistered
|
||||
*/
|
||||
void DigitalOutputCard::unregisterChangeCallback(uint8_t handler) {
|
||||
this->change_callbacks.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load a pin map
|
||||
*
|
||||
* A pin map is an array of 16 elements that maps the physical pins to virtual pins
|
||||
* The virtual pins are the pins that are used in the callback functions and are used for all the functions in this class
|
||||
* The physical pins are the pins on the Output IC, This can be found on the schematic of the ESPMegaI/O board
|
||||
* This function is useful if you want to change the number identification of the pins to match your project needs
|
||||
*
|
||||
* @param pinMap The pin map to load
|
||||
*/
|
||||
void DigitalOutputCard::loadPinMap(uint8_t pinMap[16]) {
|
||||
for(int i = 0; i < 16; i++) {
|
||||
this->pinMap[i] = pinMap[i];
|
||||
this->virtualPinMap[pinMap[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bind a FRAM to the card
|
||||
*
|
||||
* @note The Output Card use 34 bytes of FRAM
|
||||
*
|
||||
* @warning If the fram range overlap with another card, undefined behavior will occur
|
||||
*
|
||||
* @param fram The FRAM to bind
|
||||
* @param address The address of the card in the FRAM
|
||||
*/
|
||||
void DigitalOutputCard::bindFRAM(FRAM *fram, uint16_t address) {
|
||||
this->fram = fram;
|
||||
this->framBinded = true;
|
||||
this->framAddress = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pack the states of all the pins into a 16 bit integer
|
||||
*
|
||||
* @return The packed states
|
||||
*/
|
||||
uint16_t DigitalOutputCard::packStates() {
|
||||
uint16_t packed = 0;
|
||||
for(int i = 0; i < 16; i++) {
|
||||
packed |= (state_buffer[i] << i);
|
||||
}
|
||||
return packed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack the states of all the pins from a 16 bit integer
|
||||
*
|
||||
* @param states The packed states
|
||||
*/
|
||||
void DigitalOutputCard::unpackStates(uint16_t states) {
|
||||
for(int i = 0; i < 16; i++) {
|
||||
this->setState(i, (states >> i) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the states and values of all the pins to the FRAM
|
||||
*/
|
||||
void DigitalOutputCard::saveToFRAM() {
|
||||
if(!framBinded) return;
|
||||
// Save the state
|
||||
uint16_t packed = packStates();
|
||||
this->fram->write16(framAddress, packed);
|
||||
// Save the value
|
||||
this->fram->write(framAddress+2, (uint8_t*)value_buffer, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the states and values of all the pins from the FRAM
|
||||
*/
|
||||
void DigitalOutputCard::loadFromFRAM() {
|
||||
if(!framBinded) return;
|
||||
// Load the state
|
||||
uint16_t packed = this->fram->read16(framAddress);
|
||||
unpackStates(packed);
|
||||
// Load the value
|
||||
uint16_t value[16];
|
||||
this->fram->read(framAddress+2, (uint8_t*)value, 32);
|
||||
for(int i = 0; i < 16; i++) {
|
||||
this->setValue(i, value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the auto save to FRAM
|
||||
*
|
||||
* @param autoSave True to enable auto save, false to disable auto save
|
||||
*/
|
||||
void DigitalOutputCard::setAutoSaveToFRAM(bool autoSave) {
|
||||
this->framAutoSave = autoSave;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save a single pin value to FRAM
|
||||
*
|
||||
* @param pin The pin to save
|
||||
*/
|
||||
void DigitalOutputCard::savePinValueToFRAM(uint8_t pin) {
|
||||
if(!framBinded) return;
|
||||
this->fram->write(framAddress+2+pin*2, (uint8_t*)&value_buffer[pin], 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the states of all the pins to FRAM
|
||||
*/
|
||||
void DigitalOutputCard::saveStateToFRAM() {
|
||||
if(!framBinded) return;
|
||||
uint16_t packed = packStates();
|
||||
this->fram->write16(framAddress, packed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggle the state of the specified pin
|
||||
*
|
||||
* @param pin The pin to toggle
|
||||
*/
|
||||
void DigitalOutputCard::toggleState(uint8_t pin) {
|
||||
this->setState(pin, !this->state_buffer[pin]);
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <Adafruit_PWMServoDriver.h>
|
||||
#include <FRAM.h>
|
||||
#include <map>
|
||||
|
||||
// Protocol for digital output card
|
||||
// Note that pin is always 2 characters long and padded with 0 if necessary
|
||||
// Set pin state topic: <pin>/set/state payload: 0/1
|
||||
// Set pin pwm topic: <pin>/set/value payload: 0-4095
|
||||
// Publish pin state topic: <pin>/state payload: 0/1
|
||||
// Publish pin pwm topic: <pin>/value payload: 0-4095
|
||||
// Publish all topic: requeststate payload: N/A
|
||||
// Enable/disable publish topic: publish_enable payload: 0/1
|
||||
|
||||
// MQTT Topics
|
||||
#define SET_STATE_TOPIC "/set/state"
|
||||
#define SET_VALUE_TOPIC "/set/value"
|
||||
#define STATE_TOPIC "/state"
|
||||
#define VALUE_TOPIC "/value"
|
||||
#define REQUEST_STATE_TOPIC "requeststate"
|
||||
#define PUBLISH_ENABLE_TOPIC "publish_enable"
|
||||
|
||||
// Card type
|
||||
#define CARD_TYPE_DIGITAL_OUTPUT 0x00
|
||||
|
||||
/**
|
||||
* @brief The DigitalOutputCard class is a class for controlling the Digital Output Card.
|
||||
*
|
||||
* This Digital Output Card has 16 digital outputs.
|
||||
* All outputs are PWM capable.
|
||||
* ALl outputs are 12V Push-Pull outputs.
|
||||
* Outputs is grouped in 4 groups of 4 outputs.(0-3, 4-7, 8-11, 12-15)
|
||||
* Each pin is capable of 0.6A, however each group's total current is limited to 1.2A.
|
||||
*
|
||||
* @warning You should not instantiate this class directly, instead use ESPMegaIO's registerCard function.
|
||||
*/
|
||||
class DigitalOutputCard : public ExpansionCard
|
||||
{
|
||||
public:
|
||||
// Instantiate the card with the specified address
|
||||
DigitalOutputCard(uint8_t address);
|
||||
// Instantiate the card with the specified position on the dip switch
|
||||
DigitalOutputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4);
|
||||
// Initialize the card
|
||||
bool begin();
|
||||
// Dummy loop function
|
||||
void loop();
|
||||
// Set the output to the specified state
|
||||
// This function set both the state and the pwm value
|
||||
void digitalWrite(uint8_t pin, bool state);
|
||||
// Set the output to the specified pwm value
|
||||
// This function set both the state and the pwm value
|
||||
void analogWrite(uint8_t pin, uint16_t value);
|
||||
// Set the state of the specified pin
|
||||
void setState(uint8_t pin, bool state);
|
||||
// Set the pwm value of the specified pin
|
||||
void setValue(uint8_t pin, uint16_t value);
|
||||
// Get the state of the specified pin
|
||||
bool getState(uint8_t pin);
|
||||
// Toggle the state of the specified pin
|
||||
void toggleState(uint8_t pin);
|
||||
// Get the pwm value of the specified pin
|
||||
uint16_t getValue(uint8_t pin);
|
||||
// Register a callback function that will be called when the state of a pin changes
|
||||
uint8_t registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
// Unregister the callback function
|
||||
void unregisterChangeCallback(uint8_t handler);
|
||||
// Load a new pin map
|
||||
void loadPinMap(uint8_t pinMap[16]);
|
||||
// Bind the fram object to the card
|
||||
// The Output card use the fram in this layout:
|
||||
// [2 bytes] 0-1 : state
|
||||
// [32 bytes] 2-33 : value
|
||||
void bindFRAM(FRAM *fram, uint16_t address);
|
||||
// Save the state and value to the fram
|
||||
void saveToFRAM();
|
||||
// Load the state and value from the fram
|
||||
void loadFromFRAM();
|
||||
// Set the auto save to fram flag
|
||||
void setAutoSaveToFRAM(bool autoSave);
|
||||
// Save a single pin value to fram
|
||||
void savePinValueToFRAM(uint8_t pin);
|
||||
// Save state to fram
|
||||
void saveStateToFRAM();
|
||||
// Save value to fram
|
||||
void saveValueToFRAM();
|
||||
// Get type of card
|
||||
uint8_t getType();
|
||||
private:
|
||||
// FRAM address
|
||||
uint16_t framAddress;
|
||||
// FRAM is binded
|
||||
bool framBinded = false;
|
||||
// Auto save to fram
|
||||
bool framAutoSave = false;
|
||||
// The fram object pointer
|
||||
FRAM *fram;
|
||||
// The pwm driver
|
||||
Adafruit_PWMServoDriver pwm;
|
||||
// The address of the card
|
||||
uint8_t address;
|
||||
// The state of the card
|
||||
bool state_buffer[16];
|
||||
// The pwm value of the card
|
||||
uint16_t value_buffer[16];
|
||||
// The callback function
|
||||
uint8_t callbacks_handler_index = 0;
|
||||
std::map<uint8_t, std::function<void(uint8_t, bool, uint16_t)>> change_callbacks;
|
||||
// Physical pin to virtual pin map
|
||||
uint8_t pinMap[16];
|
||||
// Return 16 bit value representing all 16 channels
|
||||
uint16_t packStates();
|
||||
// Unpack the 16 bit value to the state buffer
|
||||
void unpackStates(uint16_t states);
|
||||
// Virtual pin to physical pin map
|
||||
uint8_t virtualPinMap[16];
|
||||
};
|
|
@ -1,325 +0,0 @@
|
|||
#include <DigitalOutputIoT.hpp>
|
||||
|
||||
/**
|
||||
* @brief Create a new DigitalOutputIoT object
|
||||
*/
|
||||
DigitalOutputIoT::DigitalOutputIoT()
|
||||
{
|
||||
this->state_report_topic = new char[10];
|
||||
this->value_report_topic = new char[10];
|
||||
this->digital_outputs_publish_enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy the DigitalOutputIoT object
|
||||
*/
|
||||
DigitalOutputIoT::~DigitalOutputIoT()
|
||||
{
|
||||
delete[] this->state_report_topic;
|
||||
delete[] this->value_report_topic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the DigitalOutputIoT object
|
||||
*
|
||||
* @note ALthough this function can be called inside the main program, it is recommended to use ESPMegaPRO::installCard() instead
|
||||
*
|
||||
* @param card_id The id of the card
|
||||
* @param card The card object
|
||||
* @param mqtt The PubSubClient object
|
||||
* @param base_topic The base topic of the card
|
||||
* @return True if the initialization is successful, false otherwise
|
||||
*/
|
||||
bool DigitalOutputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic)
|
||||
{
|
||||
ESP_LOGD("DigitalOutputIoT", "Beginning DigitalOutputIoT");
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
this->card = (DigitalOutputCard *)card;
|
||||
this->card_id = card_id;
|
||||
this->set_state_length = strlen(SET_STATE_TOPIC);
|
||||
this->set_value_length = strlen(SET_VALUE_TOPIC);
|
||||
this->state_length = strlen(STATE_TOPIC);
|
||||
this->value_length = strlen(VALUE_TOPIC);
|
||||
this->request_state_length = strlen(REQUEST_STATE_TOPIC);
|
||||
this->publish_enable_length = strlen(PUBLISH_ENABLE_TOPIC);
|
||||
strcpy(this->state_report_topic, "00/state");
|
||||
strcpy(this->value_report_topic, "00/value");
|
||||
ESP_LOGV("DigitalOutputIoT", "Registering callbacks inside DigitalOutputIoT::begin");
|
||||
// Register callbacks
|
||||
auto bindedCallback = std::bind(&DigitalOutputIoT::handleValueChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->card->registerChangeCallback(bindedCallback);
|
||||
ESP_LOGV("DigitalOutputIoT", "DigitalOutputIoT::begin complete");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the MQTT messages for the DigitalOutputIoT card
|
||||
*
|
||||
* @param topic The topic of the message
|
||||
* @param payload The payload of the message
|
||||
*/
|
||||
void DigitalOutputIoT::handleMqttMessage(char *topic, char *payload)
|
||||
{
|
||||
// Protocol for digital output card
|
||||
// Note that pin is always 2 characters long and padded with 0 if necessary
|
||||
// Set pin state topic: <pin>/set/state payload: 0/1
|
||||
// Set pin pwm topic: <pin>/set/value payload: 0-4095
|
||||
// Publish pin state topic: <pin>/state payload: 0/1
|
||||
// Publish pin pwm topic: <pin>/value payload: 0-4095
|
||||
// Publish all topic: requeststate payload: N/A
|
||||
// Enable/disable publish topic: publish_enable payload: 0/1
|
||||
uint8_t topic_length = strlen(topic);
|
||||
if (this->processSetStateMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processSetValueMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processRequestStateMessage(topic, payload, topic_length))
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process a set state message
|
||||
*
|
||||
* @param topic The null terminated topic
|
||||
* @param payload The null terminated payload
|
||||
* @param topic_length The length of the topic
|
||||
* @return True if the message is a set state message, false otherwise
|
||||
*/
|
||||
bool DigitalOutputIoT::processSetStateMessage(char *topic, char *payload, uint8_t topic_length)
|
||||
{
|
||||
// Check if the topic is a set state topic
|
||||
// The correct format is <pin>/set/state
|
||||
// This mean that the topic must end with /set/state
|
||||
// Check if the 3rd character is /
|
||||
if (topic[2] != '/')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// The topic must be set_state_length + 2 characters long
|
||||
if (topic_length != set_state_length + 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Check if the topic ends with /set/state
|
||||
if (!strncmp(topic + 2, SET_STATE_TOPIC, set_state_length))
|
||||
{
|
||||
// Get the pin number
|
||||
uint8_t pin = (topic[0] - '0') * 10 + (topic[1] - '0');
|
||||
// Get the state
|
||||
bool state = false;
|
||||
char state_char = payload[0];
|
||||
if (state_char == '0')
|
||||
{
|
||||
state = false;
|
||||
}
|
||||
else if (state_char == '1')
|
||||
{
|
||||
state = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Set the state
|
||||
card->setState(pin, state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process a set value message
|
||||
*
|
||||
* @param topic The null terminated topic
|
||||
* @param payload The null terminated payload
|
||||
* @param topic_length The length of the topic
|
||||
* @return True if the message is a set value message, false otherwise
|
||||
*/
|
||||
bool DigitalOutputIoT::processSetValueMessage(char *topic, char *payload, uint8_t topic_length)
|
||||
{
|
||||
// Check if the topic is a set value topic
|
||||
// The correct format is <pin>/set/value
|
||||
// This mean that the topic must end with /set/value
|
||||
// Check if the 3rd character is /
|
||||
if (topic[2] != '/')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// The topic must be set_value_length + 2 characters long
|
||||
if (topic_length != set_value_length + 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Check if the topic ends with /set/value
|
||||
if (!strncmp(topic + 2, SET_VALUE_TOPIC, set_value_length))
|
||||
{
|
||||
// Get the pin number
|
||||
uint8_t pin = (topic[0] - '0') * 10 + (topic[1] - '0');
|
||||
// Get the value
|
||||
uint16_t value = atoi(payload);
|
||||
// Set the value
|
||||
card->setValue(pin, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process a request state message
|
||||
*
|
||||
* @param topic The null terminated topic
|
||||
* @param payload The null terminated payload
|
||||
* @param topic_length The length of the topic
|
||||
* @return True if the message is a request state message, false otherwise
|
||||
*/
|
||||
bool DigitalOutputIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length)
|
||||
{
|
||||
// Check if the topic is a request state topic
|
||||
// The correct format is requeststate
|
||||
// This mean that the topic must be request_state_length characters long
|
||||
// The topic must be request_state_length characters long
|
||||
if (topic_length != request_state_length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Check if the topic is requeststate
|
||||
if (!strncmp(topic, REQUEST_STATE_TOPIC, request_state_length))
|
||||
{
|
||||
// Publish the state of all pins
|
||||
publishDigitalOutputs();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish the state of all digital outputs
|
||||
*/
|
||||
void DigitalOutputIoT::publishDigitalOutputs()
|
||||
{
|
||||
if (!digital_outputs_publish_enabled)
|
||||
return;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
publishDigitalOutput(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish the state and value of the specified digital output
|
||||
*
|
||||
* @param pin The pin to publish
|
||||
*/
|
||||
void DigitalOutputIoT::publishDigitalOutput(uint8_t pin)
|
||||
{
|
||||
publishDigitalOutputState(pin);
|
||||
publishDigitalOutputValue(pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish the state of the specified digital output
|
||||
*
|
||||
* @param pin The pin to publish
|
||||
*/
|
||||
void DigitalOutputIoT::publishDigitalOutputState(uint8_t pin)
|
||||
{
|
||||
if (!digital_outputs_publish_enabled)
|
||||
return;
|
||||
state_report_topic[0] = pin / 10 + '0';
|
||||
state_report_topic[1] = pin % 10 + '0';
|
||||
publishRelative(state_report_topic, card->getState(pin) ? "1" : "0");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish the value of the specified digital output
|
||||
*
|
||||
* @param pin The pin to publish
|
||||
*/
|
||||
void DigitalOutputIoT::publishDigitalOutputValue(uint8_t pin)
|
||||
{
|
||||
if (!digital_outputs_publish_enabled)
|
||||
return;
|
||||
value_report_topic[0] = pin / 10 + '0';
|
||||
value_report_topic[1] = pin % 10 + '0';
|
||||
char payload[5];
|
||||
sprintf(payload, "%d", card->getValue(pin));
|
||||
publishRelative(value_report_topic, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable/disable publishing of digital outputs
|
||||
*
|
||||
* @param enabled True to enable publishing, false to disable publishing
|
||||
*/
|
||||
void DigitalOutputIoT::setDigitalOutputsPublishEnabled(bool enabled)
|
||||
{
|
||||
digital_outputs_publish_enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the value change of a pin
|
||||
*
|
||||
* @note This function is registered as a callback function with the DigitalOutputCard object
|
||||
*
|
||||
* @param pin The pin that changed
|
||||
* @param state The new state of the pin
|
||||
* @param value The new value of the pin
|
||||
*/
|
||||
void DigitalOutputIoT::handleValueChange(uint8_t pin, bool state, uint16_t value)
|
||||
{
|
||||
publishDigitalOutput(pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish all digital outputs
|
||||
*
|
||||
* @note This function is called by the ESPMegaIoT object
|
||||
*/
|
||||
void DigitalOutputIoT::publishReport()
|
||||
{
|
||||
publishDigitalOutputs();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the type of the IoT component
|
||||
*
|
||||
* @return The type of the IoT component
|
||||
*/
|
||||
uint8_t DigitalOutputIoT::getType()
|
||||
{
|
||||
return CARD_TYPE_DIGITAL_OUTPUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe to the MQTT topics used by the DigitalOutputIoT object
|
||||
*/
|
||||
void DigitalOutputIoT::subscribe()
|
||||
{
|
||||
char topic[20];
|
||||
// Subscribe to all set state topics
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
sprintf(topic, "%02d/set/state", i);
|
||||
subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set value topics
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
sprintf(topic, "%02d/set/value", i);
|
||||
subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to request state topic
|
||||
subscribeRelative(REQUEST_STATE_TOPIC);
|
||||
// Subscribe to publish enable topic
|
||||
subscribeRelative(PUBLISH_ENABLE_TOPIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop for the DigitalOutputIoT object
|
||||
*
|
||||
* @note This function is not used, it is only here to implement the IoTComponent interface
|
||||
*/
|
||||
void DigitalOutputIoT::loop()
|
||||
{
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
#include <IoTComponent.hpp>
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
|
||||
/**
|
||||
* @brief The DigitalOutputIoT class is a class interfacing with the Digital Output Card through MQTT.
|
||||
*
|
||||
* @warning You should not instantiate this class directly, instead use ESPMegaIO's registerCard function.
|
||||
*/
|
||||
class DigitalOutputIoT : public IoTComponent {
|
||||
public:
|
||||
DigitalOutputIoT();
|
||||
~DigitalOutputIoT();
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void publishDigitalOutputs();
|
||||
void publishDigitalOutput(uint8_t pin);
|
||||
void publishDigitalOutputState(uint8_t pin);
|
||||
void publishDigitalOutputValue(uint8_t pin);
|
||||
void setDigitalOutputsPublishEnabled(bool enabled);
|
||||
void handleValueChange(uint8_t pin, bool state, uint16_t value);
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
void loop();
|
||||
uint8_t getType();
|
||||
private:
|
||||
bool digital_outputs_publish_enabled = false;
|
||||
bool processSetStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processSetValueMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processRequestStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
DigitalOutputCard *card;
|
||||
char *state_report_topic;
|
||||
char *value_report_topic;
|
||||
uint8_t set_state_length;
|
||||
uint8_t set_value_length;
|
||||
uint8_t state_length;
|
||||
uint8_t value_length;
|
||||
uint8_t request_state_length;
|
||||
uint8_t publish_enable_length;
|
||||
};
|
|
@ -1,145 +0,0 @@
|
|||
#include <DigitalOutputRTU.hpp>
|
||||
DigitalOutputRTU::DigitalOutputRTU()
|
||||
{
|
||||
// constructor implementation
|
||||
}
|
||||
|
||||
DigitalOutputRTU::~DigitalOutputRTU()
|
||||
{
|
||||
// destructor implementation
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::begin(char *remoteBaseTopic, uint8_t remote_card_slot, ESPMegaIoT *iot)
|
||||
{
|
||||
// begin implementation
|
||||
this->remoteBaseTopic = remoteBaseTopic;
|
||||
this->remoteBaseTopicLength = strlen(remoteBaseTopic);
|
||||
this->cardSlot = remote_card_slot;
|
||||
this->iot = iot;
|
||||
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::subscribe()
|
||||
{
|
||||
// Subscribe to all pins
|
||||
// State Topic format: <base_topic>/<card_slot>/<pin>/state
|
||||
// Value Topic format: <base_topic>/<card_slot>/<pin>/value
|
||||
char buffer[100];
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
sprintf(buffer, "%s/%02d/%02d/state", this->remoteBaseTopic, this->cardSlot, i);
|
||||
this->iot->subscribe(buffer);
|
||||
sprintf(buffer, "%s/%02d/%02d/value", this->remoteBaseTopic, this->cardSlot, i);
|
||||
this->iot->subscribe(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::digitalWrite(uint8_t pin, bool state)
|
||||
{
|
||||
// Publish state to topic <base_topic>/<card_slot>/<pin>/set/state
|
||||
// Publish 4095 to topic <base_topic>/<card_slot>/<pin>/set/value
|
||||
char buffer[100];
|
||||
sprintf(buffer, "%s/%02d/%02d/set/state", this->remoteBaseTopic, this->cardSlot, pin);
|
||||
this->iot->publish(buffer, state ? "1" : "0");
|
||||
sprintf(buffer, "%s/%02d/%02d/set/value", this->remoteBaseTopic, this->cardSlot, pin);
|
||||
this->iot->publish(buffer, "4095");
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::analogWrite(uint8_t pin, uint16_t value)
|
||||
{
|
||||
// Publish 1 to topic <base_topic>/<card_slot>/<pin>/set/state
|
||||
// Publish value to topic <base_topic>/<card_slot>/<pin>/set/value
|
||||
char buffer[100];
|
||||
sprintf(buffer, "%s/%02d/%02d/set/state", this->remoteBaseTopic, this->cardSlot, pin);
|
||||
this->iot->publish(buffer, "1");
|
||||
sprintf(buffer, "%s/%02d/%02d/set/value", this->remoteBaseTopic, this->cardSlot, pin);
|
||||
char valueBuffer[10];
|
||||
itoa(value, valueBuffer, 10);
|
||||
this->iot->publish(buffer, valueBuffer);
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::setState(uint8_t pin, bool state)
|
||||
{
|
||||
// Publish state to topic <base_topic>/<card_slot>/<pin>/set/state
|
||||
char buffer[100];
|
||||
sprintf(buffer, "%s/%02d/%02d/set/state", this->remoteBaseTopic, this->cardSlot, pin);
|
||||
this->iot->publish(buffer, state ? "1" : "0");
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::setValue(uint8_t pin, uint16_t value)
|
||||
{
|
||||
// Publish value to topic <base_topic>/<card_slot>/<pin>/set/value
|
||||
char buffer[100];
|
||||
sprintf(buffer, "%s/%02d/%02d/set/value", this->remoteBaseTopic, this->cardSlot, pin);
|
||||
char valueBuffer[10];
|
||||
itoa(value, valueBuffer, 10);
|
||||
this->iot->publish(buffer, valueBuffer);
|
||||
}
|
||||
|
||||
bool DigitalOutputRTU::getState(uint8_t pin)
|
||||
{
|
||||
return this->outputStateBuffer[pin];
|
||||
}
|
||||
|
||||
uint16_t DigitalOutputRTU::getValue(uint8_t pin)
|
||||
{
|
||||
return this->outputValueBuffer[pin];
|
||||
}
|
||||
|
||||
uint8_t DigitalOutputRTU::registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback)
|
||||
{
|
||||
this->change_callbacks[callbackCount] = callback;
|
||||
return callbackCount++;
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::unregisterChangeCallback(uint8_t handler)
|
||||
{
|
||||
this->change_callbacks.erase(handler);
|
||||
}
|
||||
|
||||
void DigitalOutputRTU::mqttCallback(char *topic, char *payload) {
|
||||
// First trim base topic from topic
|
||||
char* topicWithoutBase = topic + this->remoteBaseTopicLength;
|
||||
// Then check if topic is in format /<cardSlot>/<pin>/state or /<cardSlot>/<pin>/value
|
||||
// Example trimmed topic: /01/00/state, /01/00/value
|
||||
// Card slot range from 00 to 99
|
||||
// Pin range from 00 to 15
|
||||
// Note that both card and pin are padded with 0 if they are less than 10
|
||||
if(strlen(topicWithoutBase) != 12)
|
||||
return;
|
||||
if(topicWithoutBase[0] != '/')
|
||||
return;
|
||||
if(topicWithoutBase[3] != '/')
|
||||
return;
|
||||
if(topicWithoutBase[6] != '/')
|
||||
return;
|
||||
// Extract card slot and pin
|
||||
uint8_t cardSlot = (topicWithoutBase[1] - '0') * 10 + (topicWithoutBase[2] - '0');
|
||||
// Check if card slot is correct
|
||||
if(cardSlot != this->cardSlot)
|
||||
return;
|
||||
uint8_t pin = (topicWithoutBase[4] - '0') * 10 + (topicWithoutBase[5] - '0');
|
||||
// Extract value
|
||||
uint16_t value = atoi(payload);
|
||||
// Is it state or value?
|
||||
if(!strcmp(topicWithoutBase + 7, "state"))
|
||||
{
|
||||
// Update state buffer
|
||||
this->outputStateBuffer[pin] = strcmp(payload, "1") == 0;
|
||||
// Call callbacks
|
||||
for(auto const& callback : this->change_callbacks)
|
||||
{
|
||||
callback.second(pin, this->outputStateBuffer[pin], this->outputValueBuffer[pin]);
|
||||
}
|
||||
}
|
||||
else if(!strcmp(topicWithoutBase + 7, "value"))
|
||||
{
|
||||
// Update value buffer
|
||||
this->outputValueBuffer[pin] = value;
|
||||
// Call callbacks
|
||||
for(auto const& callback : this->change_callbacks)
|
||||
{
|
||||
callback.second(pin, this->outputStateBuffer[pin], this->outputValueBuffer[pin]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#pragma once
|
||||
#include <ESPMegaRTU.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
|
||||
class DigitalOutputRTU : public ESPMegaRTU
|
||||
{
|
||||
public:
|
||||
DigitalOutputRTU();
|
||||
~DigitalOutputRTU();
|
||||
void begin(char *remoteBaseTopic, uint8_t remote_card_slot, ESPMegaIoT *iot);
|
||||
void subscribe();
|
||||
void digitalWrite(uint8_t pin, bool state);
|
||||
void analogWrite(uint8_t pin, uint16_t value);
|
||||
void setState(uint8_t pin, bool state);
|
||||
void setValue(uint8_t pin, uint16_t value);
|
||||
bool getState(uint8_t pin);
|
||||
uint16_t getValue(uint8_t pin);
|
||||
uint8_t registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
void unregisterChangeCallback(uint8_t handler);
|
||||
|
||||
private:
|
||||
void mqttCallback(char *topic, char *payload);
|
||||
ESPMegaIoT *iot;
|
||||
size_t size;
|
||||
uint16_t callbackCount;
|
||||
std::map<uint8_t, std::function<void(uint8_t, bool, uint16_t)>> change_callbacks;
|
||||
bool outputStateBuffer[16];
|
||||
uint16_t outputValueBuffer[16];
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#define SDK_VESRION "2.10.0"
|
|
@ -1,778 +0,0 @@
|
|||
#include <ESPMegaDisplay.hpp>
|
||||
|
||||
/**
|
||||
* @brief Receives and processes serial commands from the display adapter.
|
||||
* @param process Flag indicating whether to process the received commands.
|
||||
* @return True if data is received, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::recieveSerialCommand(bool process)
|
||||
{
|
||||
bool dataRecieved = false;
|
||||
|
||||
while (displayAdapter->available())
|
||||
{
|
||||
// Read the serial buffer if available
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return false;
|
||||
}
|
||||
rx_buffer[rx_buffer_index] = displayAdapter->read();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
rx_buffer_index++;
|
||||
// Check for overflow
|
||||
if (rx_buffer_index >= 256)
|
||||
{
|
||||
rx_buffer_index = 0;
|
||||
}
|
||||
if (process)
|
||||
this->processSerialCommand();
|
||||
dataRecieved = true;
|
||||
}
|
||||
return dataRecieved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives and processes serial commands from the display adapter.
|
||||
* @return True if data is received, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::recieveSerialCommand()
|
||||
{
|
||||
return recieveSerialCommand(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the received serial command.
|
||||
* @note This function interacts directly with the rx_buffer.
|
||||
*/
|
||||
void ESPMegaDisplay::processSerialCommand()
|
||||
{
|
||||
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
|
||||
if (!payloadIsValid())
|
||||
return;
|
||||
// The rx buffer ended with stop bytes
|
||||
// Check if the rx buffer is a push payload
|
||||
// The payload type is the first byte of the payload
|
||||
// 0x00 is invalid instruction
|
||||
// 0x01 is success
|
||||
// 0x65 is a touch event
|
||||
// 0x66 is a page report event
|
||||
if (rx_buffer[0] == 0x65)
|
||||
{
|
||||
processTouchPayload();
|
||||
}
|
||||
else if (rx_buffer[0] == 0x66)
|
||||
{
|
||||
processPageReportPayload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The payload does not match any of the expected payload types
|
||||
// Pass the payload to the payload callbacks
|
||||
// type, payload, length
|
||||
for (auto const &callback : payload_callbacks)
|
||||
{
|
||||
callback.second(rx_buffer[0], reinterpret_cast<unsigned char *>(&rx_buffer[1]), rx_buffer_index - 4);
|
||||
;
|
||||
}
|
||||
}
|
||||
this->rx_buffer_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the touch event payload.
|
||||
* @note This function interacts directly with the rx_buffer.
|
||||
*/
|
||||
void ESPMegaDisplay::processTouchPayload()
|
||||
{
|
||||
if (rx_buffer_index != 7)
|
||||
return;
|
||||
// The second byte of the payload is the page number
|
||||
uint8_t page = rx_buffer[1];
|
||||
// The third byte of the payload is the component id
|
||||
uint8_t component = rx_buffer[2];
|
||||
// The fourth byte of the payload is the event
|
||||
uint8_t event = rx_buffer[3];
|
||||
// 0x01 is press, 0x00 is release
|
||||
ESP_LOGV("ESPMegaDisplay", "Touch event: page=%d, component=%d, event=%d", page, component, event);
|
||||
for (auto const &callback : touch_callbacks)
|
||||
{
|
||||
callback.second(page, component, event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the page report event payload.
|
||||
* @note This function interacts directly with the rx_buffer.
|
||||
*/
|
||||
void ESPMegaDisplay::processPageReportPayload()
|
||||
{
|
||||
if (rx_buffer_index != 5)
|
||||
return;
|
||||
// The second byte of the payload is the page number
|
||||
// Check if the page number is different from the current page
|
||||
// If it is different, we call the page change callbacks
|
||||
if (rx_buffer[1] == this->currentPage)
|
||||
return;
|
||||
this->currentPage = rx_buffer[1];
|
||||
ESP_LOGV("ESPMegaDisplay", "Page change event: page=%d", this->currentPage);
|
||||
for (auto const &callback : page_change_callbacks)
|
||||
{
|
||||
callback.second(this->currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends stop bytes to the display adapter.
|
||||
*
|
||||
* @note This function does not take the serial mutex, it is assumed that the caller has already taken the mutex.
|
||||
*/
|
||||
void ESPMegaDisplay::sendStopBytes()
|
||||
{
|
||||
displayAdapter->write(0xFF);
|
||||
displayAdapter->write(0xFF);
|
||||
displayAdapter->write(0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends a command to the display adapter.
|
||||
* @param command The command to send.
|
||||
*/
|
||||
void ESPMegaDisplay::sendCommand(char *command)
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
displayAdapter->print(command);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Jumps to the specified page on the display.
|
||||
* @param page The page number to jump to.
|
||||
*/
|
||||
void ESPMegaDisplay::jumpToPage(int page)
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
this->displayAdapter->print("page ");
|
||||
this->displayAdapter->print(page);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a number component on the display.
|
||||
* @param component The component name.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void ESPMegaDisplay::setNumber(const char *component, int value)
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
this->displayAdapter->print(component);
|
||||
this->displayAdapter->print("=");
|
||||
this->displayAdapter->print(value);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a string component on the display.
|
||||
* @param component The component name.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void ESPMegaDisplay::setString(const char *component, const char *value)
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
this->displayAdapter->print(component);
|
||||
this->displayAdapter->print("=\"");
|
||||
this->displayAdapter->print(value);
|
||||
this->displayAdapter->print("\"");
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a number component from the display.
|
||||
* @warning This function is blocking.
|
||||
* @warning If the display does not respond or is not connected, this function will block for up to DISPLAY_FETCH_RETRY_COUNT * DISPLAY_FETCH_TIMEOUT milliseconds.
|
||||
* @param component The component name.
|
||||
* @return The value of the number component.
|
||||
*/
|
||||
uint32_t ESPMegaDisplay::getNumber(const char *component)
|
||||
{
|
||||
// We might be in the middle of a serial command
|
||||
// We reset the rx buffer index to 0 to ensure that we don't read the wrong data
|
||||
this->rx_buffer_index = 0;
|
||||
uint32_t start = millis();
|
||||
// Send the get command
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return 0;
|
||||
}
|
||||
this->displayAdapter->print("get ");
|
||||
this->displayAdapter->print(component);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
// Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times
|
||||
// Wait for the response
|
||||
bool validPayload = false;
|
||||
for (int i = 0; i < DISPLAY_FETCH_RETRY_COUNT; i++)
|
||||
{
|
||||
if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT))
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response");
|
||||
return 0;
|
||||
}
|
||||
if (rx_buffer[0] != 0x71)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Invalid payload type: %d, retrying.", rx_buffer[0]);
|
||||
// The rx buffer is invalid, reset the rx buffer index
|
||||
rx_buffer_index = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
validPayload = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validPayload)
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The rx buffer is valid
|
||||
// The expected payload is type 0x71
|
||||
|
||||
// The 2nd to 5th byte of the payload is the value
|
||||
// It's a 4 byte 32-bit value in little endian order.
|
||||
// Convert the 4 bytes to a 32-bit value
|
||||
uint32_t value = 0;
|
||||
value |= rx_buffer[1];
|
||||
value |= rx_buffer[2] << 8;
|
||||
value |= rx_buffer[3] << 16;
|
||||
value |= rx_buffer[4] << 24;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a string component from the display.
|
||||
* @warning This function is blocking.
|
||||
* @warning If the display does not respond or is not connected, this function will block for up to DISPLAY_FETCH_RETRY_COUNT * DISPLAY_FETCH_TIMEOUT milliseconds.
|
||||
* @param component The component name.
|
||||
* @return The value of the string component.
|
||||
* @note The returned char array must be freed after use.
|
||||
*/
|
||||
const char *ESPMegaDisplay::getString(const char *component)
|
||||
{
|
||||
// We might be in the middle of a serial command
|
||||
// We reset the rx buffer index to 0 to ensure that we don't read the wrong data
|
||||
this->rx_buffer_index = 0;
|
||||
uint32_t start = millis();
|
||||
// Send the get command
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return nullptr;
|
||||
}
|
||||
this->displayAdapter->print("get ");
|
||||
this->displayAdapter->print(component);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
// Wait for the response
|
||||
// Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times
|
||||
// Wait for the response
|
||||
bool validPayload = false;
|
||||
for (int i = 0; i < DISPLAY_FETCH_RETRY_COUNT; i++)
|
||||
{
|
||||
if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT))
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response");
|
||||
return nullptr;
|
||||
}
|
||||
if (rx_buffer[0] != 0x70)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Invalid payload type: %d, retrying.", rx_buffer[0]);
|
||||
// The rx buffer is invalid, reset the rx buffer index
|
||||
rx_buffer_index = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
validPayload = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validPayload)
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
|
||||
return nullptr;
|
||||
}
|
||||
// The 2nd bytes onwards before the stop bytes is the string
|
||||
// The length of the string is the length of the payload minus 4
|
||||
uint8_t length = rx_buffer_index - 4;
|
||||
// First we malloc a char array with the length of the string
|
||||
char *value = (char *)malloc(length + 1);
|
||||
// Copy the string from the rx buffer to the char array
|
||||
memcpy(value, rx_buffer + 1, length);
|
||||
// Add the null terminator
|
||||
value[length] = '\0';
|
||||
// Return the char array
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a number component from the display and stores it in a buffer.
|
||||
* @warning This function is blocking.
|
||||
* @warning If the display does not respond or is not connected, this function will block for up to DISPLAY_FETCH_RETRY_COUNT * DISPLAY_FETCH_TIMEOUT milliseconds.
|
||||
* @param component The component name.
|
||||
* @param buffer The buffer to store the value.
|
||||
* @param buffer_size The size of the buffer.
|
||||
* @return True if the value is successfully stored in the buffer, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::getStringToBuffer(const char *component, char *buffer, uint8_t buffer_size)
|
||||
{
|
||||
// We might be in the middle of a serial command
|
||||
// We reset the rx buffer index to 0 to ensure that we don't read the wrong data
|
||||
this->rx_buffer_index = 0;
|
||||
uint32_t start = millis();
|
||||
// Send the get command
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return false;
|
||||
}
|
||||
this->displayAdapter->print("get ");
|
||||
this->displayAdapter->print(component);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
// Wait for the response
|
||||
// Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times
|
||||
// Wait for the response
|
||||
bool validPayload = false;
|
||||
for (int i = 0; i < DISPLAY_FETCH_RETRY_COUNT; i++)
|
||||
{
|
||||
if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT))
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response");
|
||||
this->sendStopBytes();
|
||||
return 0;
|
||||
}
|
||||
if (rx_buffer[0] != 0x70)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Invalid payload type: %d, retrying.", rx_buffer[0]);
|
||||
this->sendStopBytes();
|
||||
// The rx buffer is invalid, reset the rx buffer index
|
||||
rx_buffer_index = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
validPayload = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validPayload)
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
|
||||
this->sendStopBytes();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The 2nd bytes onwards before the stop bytes is the string
|
||||
// The length of the string is the length of the payload minus 4
|
||||
uint8_t length = rx_buffer_index - 4;
|
||||
// Check if the buffer is large enough to hold the string
|
||||
if (length > buffer_size)
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Buffer size too small");
|
||||
this->sendStopBytes();
|
||||
return false;
|
||||
}
|
||||
// Copy the string from the rx buffer to the char array
|
||||
memcpy(buffer, rx_buffer + 1, length);
|
||||
// Add the null terminator
|
||||
buffer[length] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits for a valid payload from the display adapter.
|
||||
* @param timeout The timeout value in milliseconds.
|
||||
* @return True if a valid payload is received, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::waitForValidPayload(uint32_t timeout)
|
||||
{
|
||||
uint32_t start = millis();
|
||||
// If the payload is not valid, keep reading the serial buffer until timeout
|
||||
while (!this->payloadIsValid())
|
||||
{
|
||||
if (millis() - start > timeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
recieveSerialCommand(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the received payload is valid.
|
||||
* @return True if the payload is valid, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::payloadIsValid()
|
||||
{
|
||||
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
|
||||
if (rx_buffer_index < 3)
|
||||
return false;
|
||||
if (rx_buffer[rx_buffer_index - 1] != 0xFF)
|
||||
return false;
|
||||
if (rx_buffer[rx_buffer_index - 2] != 0xFF)
|
||||
return false;
|
||||
if (rx_buffer[rx_buffer_index - 3] != 0xFF)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the brightness of the display.
|
||||
* @param value The brightness value.
|
||||
*/
|
||||
void ESPMegaDisplay::setBrightness(int value)
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
this->displayAdapter->print("dim=");
|
||||
this->displayAdapter->print(value);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the volume of the display.
|
||||
* @param value The volume value.
|
||||
*/
|
||||
void ESPMegaDisplay::setVolume(int value)
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
this->displayAdapter->print("vol=");
|
||||
this->displayAdapter->print(value);
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restarts the display.
|
||||
*/
|
||||
void ESPMegaDisplay::reset()
|
||||
{
|
||||
// First we send a stop bytes to clear the serial buffer
|
||||
// This ensures that the display is ready to receive the reset command
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
sendStopBytes();
|
||||
this->displayAdapter->print("rest");
|
||||
sendStopBytes();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor for the ESPMegaDisplay class.
|
||||
* @param displayAdapter The serial adapter connected to the display.
|
||||
*/
|
||||
ESPMegaDisplay::ESPMegaDisplay(HardwareSerial *displayAdapter, uint32_t baudRate, uint32_t uploadBaudRate, uint8_t txPin, uint8_t rxPin)
|
||||
{
|
||||
this->baudRate = baudRate;
|
||||
this->uploadBaudRate = uploadBaudRate;
|
||||
this->txPin = txPin;
|
||||
this->rxPin = rxPin;
|
||||
this->serialMutex = xSemaphoreCreateMutex();
|
||||
this->otaBytesWritten = 0;
|
||||
this->displayAdapter = displayAdapter;
|
||||
this->currentPage = 0;
|
||||
this->rx_buffer_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the display.
|
||||
*/
|
||||
void ESPMegaDisplay::begin()
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return;
|
||||
}
|
||||
this->displayAdapter->begin(this->baudRate, SERIAL_8N1, this->rxPin, this->txPin);
|
||||
this->displayAdapter->setTimeout(100);
|
||||
this->displayAdapter->flush();
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop function of the display.
|
||||
*/
|
||||
void ESPMegaDisplay::loop()
|
||||
{
|
||||
// Check if there is data in the serial buffer
|
||||
// If there is data, process the data
|
||||
recieveSerialCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback function for touch events.
|
||||
* @param callback The callback function.
|
||||
* @return The handle of the callback function.
|
||||
*/
|
||||
uint16_t ESPMegaDisplay::registerTouchCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback)
|
||||
{
|
||||
uint16_t handle = touch_callbacks.size();
|
||||
touch_callbacks[handle] = callback;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters a callback function for touch events.
|
||||
* @param handle The handle of the callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::unregisterTouchCallback(uint16_t handle)
|
||||
{
|
||||
touch_callbacks.erase(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback function for page change events.
|
||||
* @param callback The callback function.
|
||||
* @return The handle of the callback function.
|
||||
*/
|
||||
uint16_t ESPMegaDisplay::registerPageChangeCallback(std::function<void(uint8_t)> callback)
|
||||
{
|
||||
uint16_t handle = page_change_callbacks.size();
|
||||
page_change_callbacks[handle] = callback;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters a callback function for page change events.
|
||||
* @param handle The handle of the callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::unregisterPageChangeCallback(uint16_t handle)
|
||||
{
|
||||
page_change_callbacks.erase(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback function for payloads.
|
||||
* @param callback The callback function.
|
||||
* @return The handle of the callback function.
|
||||
*/
|
||||
uint16_t ESPMegaDisplay::registerPayloadCallback(std::function<void(uint8_t, uint8_t *, uint8_t)> callback)
|
||||
{
|
||||
uint16_t handle = payload_callbacks.size();
|
||||
payload_callbacks[handle] = callback;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters a callback function for payloads.
|
||||
* @param handle The handle of the callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::unregisterPayloadCallback(uint16_t handle)
|
||||
{
|
||||
payload_callbacks.erase(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Takes the serial mutex.
|
||||
*
|
||||
* @note only neccessary if you are using the display adapter directly, otherwise the mutex is taken by the helper functions.
|
||||
*/
|
||||
bool ESPMegaDisplay::takeSerialMutex()
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gives the serial mutex.
|
||||
*/
|
||||
void ESPMegaDisplay::giveSerialMutex()
|
||||
{
|
||||
ESP_LOGD("ESPMegaDisplay", "Giving serial mutex");
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
}
|
||||
|
||||
bool ESPMegaDisplay::beginUpdate(size_t size)
|
||||
{
|
||||
// The display's baudrate might be stuck at 9600 if the display is not initialized
|
||||
// We try to initiate the display at the user specified baud rate first, if it fails, we try again at 9600
|
||||
if (!beginUpdate(size, baudRate))
|
||||
{
|
||||
ESP_LOGW("ESPMegaDisplay", "Failed to initiate LCD update at %d baud, retrying at 9600 baud.", uploadBaudRate);
|
||||
if (!beginUpdate(size, 9600))
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "Failed to initiate LCD update at 9600 baud.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts an OTA update.
|
||||
* @param size The size of the update.
|
||||
* @param baudRate The baud rate use to connect to the display to initiate the update.
|
||||
* @note The baud rate that is used to transfer the data is defined by the uploadBaudRate parameter in the constructor.
|
||||
* @return True if the OTA update is started, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::beginUpdate(size_t size, uint32_t baudRate)
|
||||
{
|
||||
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
|
||||
{
|
||||
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGD("ESPMegaDisplay", "LCD OTA Subroutine has taken the serial mutex, all other tasks will be blocked until the OTA update is complete.");
|
||||
// We have taken the serial mutex, all helper functions will be blocked until the OTA update is complete
|
||||
// Thus, we have to interact directly with the display adapter
|
||||
this->displayAdapter->begin(baudRate, SERIAL_8N1, this->rxPin, this->txPin);
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("rest");
|
||||
this->sendStopBytes();
|
||||
delay(500);
|
||||
this->displayAdapter->print("connect");
|
||||
this->sendStopBytes();
|
||||
delay(1000);
|
||||
// Flush the serial recieve buffer
|
||||
while (this->displayAdapter->available())
|
||||
this->displayAdapter->read();
|
||||
this->displayAdapter->print("whmi-wri ");
|
||||
this->displayAdapter->print(size);
|
||||
this->displayAdapter->print(",");
|
||||
this->displayAdapter->print(uploadBaudRate);
|
||||
this->displayAdapter->print(",res0");
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->begin(uploadBaudRate, SERIAL_8N1, this->rxPin, this->txPin);
|
||||
delay(1000);
|
||||
// If the display is ready, it will send a 0x05 byte
|
||||
// If it does, return true, otherwise return false
|
||||
unsigned long startTime = millis();
|
||||
while (millis() - startTime < OTA_WAIT_TIMEOUT)
|
||||
{
|
||||
if (this->displayAdapter->available())
|
||||
{
|
||||
if (this->displayAdapter->read() == 0x05)
|
||||
{
|
||||
ESP_LOGV("ESPMegaDisplay", "LCD Update Subroutine is ready to receive data.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// FLush the serial recieve buffer
|
||||
while (this->displayAdapter->available())
|
||||
this->displayAdapter->read();
|
||||
ESP_LOGE("ESPMegaDisplay", "LCD Update Subroutine failed to initialize.");
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes data to the display during an update.
|
||||
* @param data The data to write.
|
||||
* @param size The size of the data.
|
||||
* @return True if the data is written, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::writeUpdate(uint8_t *data, size_t size)
|
||||
{
|
||||
// Check if the data size is too large
|
||||
if (size > 4096)
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "LCD Update Subroutine failed to write data, data size is too large.");
|
||||
return false;
|
||||
}
|
||||
// Flush the serial recieve buffer
|
||||
while (this->displayAdapter->available())
|
||||
this->displayAdapter->read();
|
||||
// Write the data
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
this->displayAdapter->write(data[i]);
|
||||
// After every 4096 bytes, we have to wait for the display to send a 0x05 byte
|
||||
// If it doesn't, return false
|
||||
otaBytesWritten ++;
|
||||
if (otaBytesWritten % 4096 == 0)
|
||||
{
|
||||
unsigned long startTime = millis();
|
||||
bool ready = false;
|
||||
while (millis() - startTime < OTA_WAIT_TIMEOUT)
|
||||
{
|
||||
if (this->displayAdapter->available())
|
||||
{
|
||||
if (this->displayAdapter->read() == 0x05)
|
||||
{
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ready)
|
||||
{
|
||||
ESP_LOGE("ESPMegaDisplay", "LCD Update Subroutine failed to write data, display is not ready.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ends an LCD update.
|
||||
*/
|
||||
void ESPMegaDisplay::endUpdate()
|
||||
{
|
||||
xSemaphoreGive(this->serialMutex);
|
||||
this->reset();
|
||||
delay(500);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of bytes written during an OTA update.
|
||||
* @return The number of bytes written.
|
||||
*/
|
||||
size_t ESPMegaDisplay::getUpdateBytesWritten()
|
||||
{
|
||||
return this->otaBytesWritten;
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <map>
|
||||
|
||||
#define DISPLAY_MUTEX_TAKE_TIMEOUT 1000 // ms
|
||||
#define OTA_WAIT_TIMEOUT 1000 // ms
|
||||
|
||||
#define DISPLAY_FETCH_TIMEOUT 100 // ms
|
||||
#define DISPLAY_FETCH_RETRY_COUNT 5
|
||||
|
||||
/**
|
||||
* @brief The ESPMegaDisplay class is a class for controlling the ESPMegaDisplay.
|
||||
*
|
||||
* @note The ESPMegaDisplay is a UART controlled display.
|
||||
* @note Connect the Display's TX pin to the ESPMega's RX pin and the Display's RX pin to the ESPMega's TX pin.
|
||||
*/
|
||||
class ESPMegaDisplay
|
||||
{
|
||||
public:
|
||||
ESPMegaDisplay(HardwareSerial *displayAdapter, uint32_t baudRate, uint32_t uploadBaudRate, uint8_t txPin, uint8_t rxPin);
|
||||
void begin();
|
||||
void loop();
|
||||
void reset();
|
||||
void setBrightness(int value);
|
||||
void setVolume(int value);
|
||||
void jumpToPage(int page);
|
||||
void setString(const char* component, const char* value);
|
||||
void setNumber(const char* component, int value);
|
||||
const char* getString(const char* component);
|
||||
bool getStringToBuffer(const char* component, char* buffer, uint8_t buffer_size);
|
||||
uint32_t getNumber(const char* component);
|
||||
uint16_t registerTouchCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback);
|
||||
void unregisterTouchCallback(uint16_t handle);
|
||||
uint16_t registerPageChangeCallback(std::function<void(uint8_t)> callback);
|
||||
void unregisterPageChangeCallback(uint16_t handle);
|
||||
uint16_t registerPayloadCallback(std::function<void(uint8_t, uint8_t*, uint8_t)> callback);
|
||||
void unregisterPayloadCallback(uint16_t handle);
|
||||
bool takeSerialMutex();
|
||||
void giveSerialMutex();
|
||||
SemaphoreHandle_t serialMutex;
|
||||
bool beginUpdate(size_t size);
|
||||
bool beginUpdate(size_t size, uint32_t baudRate);
|
||||
bool writeUpdate(uint8_t* data, size_t size);
|
||||
void endUpdate();
|
||||
size_t getUpdateBytesWritten();
|
||||
protected:
|
||||
uint32_t baudRate;
|
||||
uint32_t uploadBaudRate;
|
||||
uint8_t txPin;
|
||||
uint8_t rxPin;
|
||||
size_t otaBytesWritten;
|
||||
uint8_t currentPage;
|
||||
uint8_t rx_buffer_index;
|
||||
char rx_buffer[256];
|
||||
char tx_buffer[256];
|
||||
bool recieveSerialCommand();
|
||||
bool recieveSerialCommand(bool process);
|
||||
void processSerialCommand();
|
||||
void processTouchPayload();
|
||||
void processPageReportPayload();
|
||||
void sendStopBytes();
|
||||
void sendCommand(char* command);
|
||||
bool payloadIsValid();
|
||||
bool waitForValidPayload(uint32_t timeout);
|
||||
HardwareSerial *displayAdapter;
|
||||
std::map<uint16_t, std::function<void(uint8_t, uint8_t, uint8_t)>> touch_callbacks;
|
||||
std::map<uint16_t, std::function<void(uint8_t)>> page_change_callbacks;
|
||||
std::map<uint16_t, std::function<void(uint8_t, uint8_t*, uint8_t)>> payload_callbacks;
|
||||
};
|
|
@ -1,101 +0,0 @@
|
|||
#include <ESPMegaDisplayOTA.hpp>
|
||||
|
||||
ESPMegaDisplayOTA::ESPMegaDisplayOTA() {
|
||||
|
||||
}
|
||||
|
||||
void ESPMegaDisplayOTA::begin(const char* base_path, ESPMegaDisplay *display, ESPMegaWebServer *webServer) {
|
||||
this->display = display;
|
||||
this->webServer = webServer;
|
||||
this->server = webServer->getServer();
|
||||
this->base_path = base_path;
|
||||
char ota_begin_path[100];
|
||||
char ota_write_path[100];
|
||||
char ota_end_path[100];
|
||||
char ota_page_path[100];
|
||||
sprintf(ota_begin_path, "%s/ota/begin", base_path);
|
||||
sprintf(ota_write_path, "%s/ota/write", base_path);
|
||||
sprintf(ota_end_path, "%s/ota/end", base_path);
|
||||
sprintf(ota_page_path, "%s/index.html", base_path);
|
||||
this->otaUpdateBeginWebHandler = new AsyncCallbackJsonWebHandler(ota_begin_path, std::bind(&ESPMegaDisplayOTA::otaUpdateBeginHandler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
this->otaUpdateWriteWebHandler = new AsyncCallbackJsonWebHandler(ota_write_path, std::bind(&ESPMegaDisplayOTA::otaUpdateWriteHandler, this, std::placeholders::_1, std::placeholders::_2), 8192U);
|
||||
this->otaUpdateEndWebHandler = new AsyncCallbackJsonWebHandler(ota_end_path, std::bind(&ESPMegaDisplayOTA::otaUpdateEndHandler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
this->server->addHandler(this->otaUpdateBeginWebHandler);
|
||||
this->server->addHandler(this->otaUpdateWriteWebHandler);
|
||||
this->server->addHandler(this->otaUpdateEndWebHandler);
|
||||
this->server->on(ota_page_path, HTTP_GET, std::bind(&ESPMegaDisplayOTA::displayWebPageHandler, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void ESPMegaDisplayOTA::otaUpdateBeginHandler(AsyncWebServerRequest *request, JsonVariant &json) {
|
||||
this->webServer->checkAuthentication(request);
|
||||
// The content type of the request is application/json
|
||||
// The body of the request is a JSON object with the following field:
|
||||
// - size: the size of the update
|
||||
|
||||
// Parse the JSON object
|
||||
JsonObject content = json.as<JsonObject>();
|
||||
// Check if the size field is present
|
||||
if(!content.containsKey("size"))
|
||||
return;
|
||||
// Get the size field
|
||||
this->updateSize = content["size"].as<size_t>();
|
||||
// Begin the update
|
||||
if(!this->display->beginUpdate(this->updateSize)) {
|
||||
// If the update cannot be started, return an error
|
||||
request->send(500, "application/json", "{\"status\": \"error\"}");
|
||||
} else {
|
||||
// If the update can be started, return a success
|
||||
request->send(200, "application/json", "{\"status\": \"success\"}");
|
||||
}
|
||||
|
||||
}
|
||||
void ESPMegaDisplayOTA::otaUpdateWriteHandler(AsyncWebServerRequest *request, JsonVariant &json) {
|
||||
this->webServer->checkAuthentication(request);
|
||||
// The content type of the request is application/json
|
||||
// The body of the request is a JSON object with the following field:
|
||||
// - size: the size of the update in bytes
|
||||
// - data: the data to write
|
||||
|
||||
//Parse the JSON object
|
||||
JsonObject content = json.as<JsonObject>();
|
||||
// // Check if the size and data fields are present
|
||||
// Serial.println("Checking if size and data fields are present");
|
||||
// if(!content.containsKey("size") || !content.containsKey("data"))
|
||||
// Serial.println("Size or data field is missing");
|
||||
// request->send(500, "application/json", "{\"status\": \"error\", \"message\": \"The size or data field is missing\"}");
|
||||
// return;
|
||||
// Serial.println("Size and data fields are present, getting size");
|
||||
// Get the size field
|
||||
size_t size = content["size"].as<size_t>();
|
||||
if(size>4096) {
|
||||
// If the size is greater than 4096 bytes, return an error
|
||||
request->send(500, "application/json", "{\"status\": \"error\", \"message\": \"The size of the update is too big\"}");
|
||||
return;
|
||||
}
|
||||
// Get the data field
|
||||
JsonArray data = content["data"].as<JsonArray>();
|
||||
if(this->updateProgress+size>this->updateSize) {
|
||||
// If the update is too big, return an error
|
||||
request->send(500, "application/json", "{\"status\": \"error\", \"message\": \"The update chunk is too big\"}");
|
||||
return;
|
||||
}
|
||||
// Convert JsonArray to uint8_t*
|
||||
uint8_t data_array[4096];
|
||||
for(size_t i=0; i<size; i++) {
|
||||
data_array[i] = data[i].as<uint8_t>();
|
||||
}
|
||||
// Write the data to the display
|
||||
display->writeUpdate(data_array, size);
|
||||
request->send(200, "application/json", "{\"status\": \"success\",\"bytes_written\": "+String(this->display->getUpdateBytesWritten())+"}");
|
||||
}
|
||||
void ESPMegaDisplayOTA::otaUpdateEndHandler(AsyncWebServerRequest *request, JsonVariant &json) {
|
||||
this->webServer->checkAuthentication(request);
|
||||
display->endUpdate();
|
||||
request->send(200, "application/json", "{\"status\": \"success\"}");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void ESPMegaDisplayOTA::displayWebPageHandler(AsyncWebServerRequest *request) {
|
||||
this->webServer->checkAuthentication(request);
|
||||
request->send_P(200, "text/html", display_html);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#pragma once
|
||||
#include <ESPMegaDisplay.hpp>
|
||||
#include <ESPMegaWebServer.hpp>
|
||||
|
||||
class ESPMegaDisplayOTA {
|
||||
public:
|
||||
ESPMegaDisplayOTA();
|
||||
void begin(const char* base_path, ESPMegaDisplay *display, ESPMegaWebServer *webServer);
|
||||
private:
|
||||
AsyncCallbackJsonWebHandler *otaUpdateBeginWebHandler;
|
||||
AsyncCallbackJsonWebHandler *otaUpdateWriteWebHandler;
|
||||
AsyncCallbackJsonWebHandler *otaUpdateEndWebHandler;
|
||||
void otaUpdateBeginHandler(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void otaUpdateWriteHandler(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void otaUpdateEndHandler(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void displayWebPageHandler(AsyncWebServerRequest *request);
|
||||
const char *base_path;
|
||||
AsyncWebServer *server;
|
||||
ESPMegaDisplay *display;
|
||||
ESPMegaWebServer *webServer;
|
||||
size_t updateSize;
|
||||
size_t updateProgress;
|
||||
};
|
|
@ -1,931 +0,0 @@
|
|||
#include <ESPMegaIoT.hpp>
|
||||
#include <ETH.h>
|
||||
|
||||
/**
|
||||
* @brief Create a new ESPMegaIoT object
|
||||
*
|
||||
* @note You shold not create this object directly, Instead, you should use the ESPMegaPRO::iot object
|
||||
*/
|
||||
ESPMegaIoT::ESPMegaIoT() : mqtt(tcpClient)
|
||||
{
|
||||
tcpClient.setTimeout(TCP_TIMEOUT_SEC);
|
||||
// Initialize the components array
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
components[i] = NULL;
|
||||
}
|
||||
active = false;
|
||||
mqtt_connected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy the ESPMegaIoT object
|
||||
*/
|
||||
ESPMegaIoT::~ESPMegaIoT()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The mqtt callback function, This function is called when a message is received on a subscribed topic
|
||||
*
|
||||
* This function is called when a message is received on a subscribed topic
|
||||
* The payload is copied to a buffer and a null terminator is added
|
||||
* The payload is then passed to the respective card's mqtt callback
|
||||
*
|
||||
* @param topic The topic of the message
|
||||
* @param payload The payload of the message in byte form
|
||||
* @param length The length of the payload
|
||||
*/
|
||||
void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length)
|
||||
{
|
||||
// Create a null terminated string from the payload
|
||||
memcpy(payload_buffer, payload, length);
|
||||
payload_buffer[length] = '\0';
|
||||
// If the topic is not appended with the base topic, call only the absolute callbacks
|
||||
for (const auto &callback : mqtt_callbacks)
|
||||
{
|
||||
callback.second(topic, payload_buffer);
|
||||
}
|
||||
if (strncmp(topic, this->mqtt_config.base_topic, base_topic_length) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Remove the base topic from the topic
|
||||
char *topic_without_base = topic + strlen(this->mqtt_config.base_topic) + 1;
|
||||
for (const auto &callback : mqtt_relative_callbacks)
|
||||
{
|
||||
callback.second(topic_without_base, payload_buffer);
|
||||
}
|
||||
// Check for global state request
|
||||
if (!strcmp(topic_without_base,"requeststate")) {
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->publishReport();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Check for global summary request
|
||||
if (!strcmp(topic_without_base,"requestinfo")) {
|
||||
this->publishSystemSummary();
|
||||
return;
|
||||
}
|
||||
// Call the respective card's mqtt callback
|
||||
// Note that after the base topic, there should be the card id
|
||||
// /base_topic/card_id/...
|
||||
// First, get the card id in integer form
|
||||
char *card_id_str = strtok(topic_without_base, "/");
|
||||
uint8_t card_id = atoi(card_id_str);
|
||||
// Check if the card is registered
|
||||
if (components[card_id] == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
components[card_id]->handleMqttMessage(topic_without_base + 3, payload_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the base topic for the IoT
|
||||
*
|
||||
* @param base_topic The base topic
|
||||
*/
|
||||
void ESPMegaIoT::setBaseTopic(char *base_topic)
|
||||
{
|
||||
strcpy(this->mqtt_config.base_topic, base_topic);
|
||||
base_topic_length = strlen(this->mqtt_config.base_topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin the ESPMegaIoT object
|
||||
*
|
||||
* @param cards The array of ExpansionCard objects
|
||||
*/
|
||||
void ESPMegaIoT::intr_begin(ExpansionCard *cards[])
|
||||
{
|
||||
this->cards = cards;
|
||||
active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop for the ESPMegaIoT object
|
||||
*
|
||||
* @note Normally you should not call this function, Instead, you should call ESPMegaPRO::loop()
|
||||
*/
|
||||
void ESPMegaIoT::loop()
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
// Call each component's loop function
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->loop();
|
||||
}
|
||||
}
|
||||
mqtt.loop();
|
||||
sessionKeepAlive();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register an existing card for use with IoT
|
||||
*
|
||||
* This function registers an existing card for use with IoT
|
||||
* The card should be installed using ESPMegaPRO::installCard() before calling this function
|
||||
*
|
||||
* @param card_id The id of the card
|
||||
*/
|
||||
void ESPMegaIoT::registerCard(uint8_t card_id)
|
||||
{
|
||||
// Check if the card is already registered
|
||||
if (components[card_id] != NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Check if the physical card is installed
|
||||
if (cards[card_id] == NULL)
|
||||
{
|
||||
ESP_LOGE("ESPMegaIoT", "Registering card %d failed: Card not installed", card_id);
|
||||
return;
|
||||
}
|
||||
// Get the card type
|
||||
uint8_t card_type = cards[card_id]->getType();
|
||||
// Create the respective IoT component
|
||||
switch (card_type)
|
||||
{
|
||||
case CARD_TYPE_ANALOG:
|
||||
components[card_id] = new AnalogIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, this->mqtt_config.base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
break;
|
||||
case CARD_TYPE_DIGITAL_INPUT:
|
||||
components[card_id] = new DigitalInputIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, this->mqtt_config.base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
break;
|
||||
case CARD_TYPE_DIGITAL_OUTPUT:
|
||||
components[card_id] = new DigitalOutputIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, this->mqtt_config.base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
break;
|
||||
case CARD_TYPE_CLIMATE:
|
||||
components[card_id] = new ClimateIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, this->mqtt_config.base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
break;
|
||||
case CARD_TYPE_CT:
|
||||
components[card_id] = new CurrentTransformerIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, this->mqtt_config.base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
default:
|
||||
ESP_LOGE("ESPMegaIoT", "Registering card %d failed: Unknown card", card_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a card
|
||||
*
|
||||
* @param card_id The id of the card
|
||||
*/
|
||||
void ESPMegaIoT::unregisterCard(uint8_t card_id)
|
||||
{
|
||||
// Check if the card is registered
|
||||
if (components[card_id] == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Delete the IoT component
|
||||
delete components[card_id];
|
||||
components[card_id] = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish all cards's reports
|
||||
*/
|
||||
void ESPMegaIoT::publishCard(uint8_t card_id)
|
||||
{
|
||||
// Check if the card is registered
|
||||
if (components[card_id] == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Publish the card
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe to a topic
|
||||
*
|
||||
* @param topic The topic to subscribe to
|
||||
*/
|
||||
void ESPMegaIoT::subscribe(const char *topic)
|
||||
{
|
||||
mqtt.subscribe(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe from a topic
|
||||
*
|
||||
* @param topic The topic to unsubscribe from
|
||||
*/
|
||||
void ESPMegaIoT::unsubscribeFromTopic(const char *topic)
|
||||
{
|
||||
mqtt.unsubscribe(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to a wifi network
|
||||
*
|
||||
* @param ssid The SSID of the wifi network
|
||||
* @param password The password of the wifi network
|
||||
*/
|
||||
void ESPMegaIoT::connectToWifi(const char *ssid, const char *password)
|
||||
{
|
||||
WiFi.begin(ssid, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to a unsecured wifi network
|
||||
*
|
||||
* @param ssid The SSID of the wifi network
|
||||
*/
|
||||
void ESPMegaIoT::connectToWifi(const char *ssid)
|
||||
{
|
||||
WiFi.begin(ssid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the wifi network
|
||||
*/
|
||||
void ESPMegaIoT::disconnectFromWifi()
|
||||
{
|
||||
WiFi.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the wifi is connected
|
||||
*
|
||||
* @return True if the wifi is connected, false otherwise
|
||||
*/
|
||||
bool ESPMegaIoT::wifiConnected()
|
||||
{
|
||||
return WiFi.status() == WL_CONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to a MQTT broker with authentication
|
||||
*
|
||||
* @param client_id The client id to use
|
||||
* @param mqtt_server The MQTT server to connect to
|
||||
* @param mqtt_port The MQTT port to connect to
|
||||
* @param mqtt_user The MQTT username to use
|
||||
* @param mqtt_password The MQTT password to use
|
||||
* @return True if the connection is successful, false otherwise
|
||||
*/
|
||||
bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port, char *mqtt_user, char *mqtt_password)
|
||||
{
|
||||
mqtt.setServer(mqtt_server, mqtt_port);
|
||||
auto boundCallback = std::bind(&ESPMegaIoT::mqttCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
mqtt.setCallback(boundCallback);
|
||||
if (mqtt_user == nullptr || mqtt_password == nullptr || strlen(mqtt_user) == 0 || strlen(mqtt_password) == 0)
|
||||
{
|
||||
mqtt_connected = false;
|
||||
ESP_LOGE("ESPMegaIoT", "MQTT Connection failed: Username or password not set but MQTT use_auth is true");
|
||||
return false;
|
||||
}
|
||||
// Create availability topic
|
||||
char availability_topic[base_topic_length + 15];
|
||||
sprintf(availability_topic, "%s/availability", this->mqtt_config.base_topic);
|
||||
if (mqtt.connect(client_id, mqtt_user, mqtt_password, availability_topic, 0, true, "offline"))
|
||||
{
|
||||
sessionKeepAlive();
|
||||
mqttSubscribe();
|
||||
// Publish all cards
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->publishReport();
|
||||
}
|
||||
}
|
||||
mqtt_connected = true;
|
||||
mqtt.publish(availability_topic, "online", true);
|
||||
return true;
|
||||
}
|
||||
mqtt_connected = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to a MQTT broker without authentication
|
||||
*
|
||||
* @param client_id The client id to use
|
||||
* @param mqtt_server The MQTT server to connect to
|
||||
* @param mqtt_port The MQTT port to connect to
|
||||
* @return True if the connection is successful, false otherwise
|
||||
*/
|
||||
bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port)
|
||||
{
|
||||
ESP_LOGD("ESPMegaIoT", "Setting MQTT server to %s:%d", mqtt_server, mqtt_port);
|
||||
mqtt.setServer(mqtt_server, mqtt_port);
|
||||
auto boundCallback = std::bind(&ESPMegaIoT::mqttCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
ESP_LOGD("ESPMegaIoT", "Binding MQTT callback");
|
||||
mqtt.setCallback(boundCallback);
|
||||
// Create availability topic
|
||||
char availability_topic[base_topic_length + 15];
|
||||
sprintf(availability_topic, "%s/availability", this->mqtt_config.base_topic);
|
||||
if (mqtt.connect(client_id, availability_topic, 0, true, "offline"))
|
||||
{
|
||||
ESP_LOGD("ESPMegaIoT", "MQTT Connected, Calling session keep alive");
|
||||
sessionKeepAlive();
|
||||
ESP_LOGD("ESPMegaIoT", "Subscribing to topics");
|
||||
mqttSubscribe();
|
||||
ESP_LOGD("ESPMegaIoT", "Publishing reports");
|
||||
// Publish all cards
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->publishReport();
|
||||
}
|
||||
}
|
||||
ESP_LOGI("ESPMegaIoT", "MQTT Connected OK.");
|
||||
mqtt_connected = true;
|
||||
mqtt.publish(availability_topic, "online", true);
|
||||
return true;
|
||||
}
|
||||
ESP_LOGW("ESPMegaIoT", "MQTT Connection failed: %d", mqtt.state());
|
||||
mqtt_connected = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the MQTT broker
|
||||
*/
|
||||
void ESPMegaIoT::disconnectFromMqtt()
|
||||
{
|
||||
mqtt.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish a message to a topic
|
||||
*
|
||||
* @param topic The topic to publish to
|
||||
* @param payload The payload to publish
|
||||
*/
|
||||
void ESPMegaIoT::publish(const char *topic, const char *payload)
|
||||
{
|
||||
mqtt.publish(topic, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish a message to a topic
|
||||
*
|
||||
* @param topic The topic to publish to
|
||||
* @param payload The payload to publish
|
||||
* @param length The length of the payload
|
||||
*/
|
||||
void ESPMegaIoT::publish(const char *topic, const char *payload, unsigned int length)
|
||||
{
|
||||
mqtt.publish(topic, (const uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback for MQTT messages
|
||||
*
|
||||
* @param callback The callback function
|
||||
* @return The handler for the callback
|
||||
*/
|
||||
uint16_t ESPMegaIoT::registerMqttCallback(std::function<void(char *, char *)> callback)
|
||||
{
|
||||
mqtt_callbacks[mqtt_callbacks_handler_index] = callback;
|
||||
return mqtt_callbacks_handler_index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a callback
|
||||
*
|
||||
* @param handler The handler of the callback
|
||||
*/
|
||||
void ESPMegaIoT::unregisterMqttCallback(uint16_t handler)
|
||||
{
|
||||
mqtt_callbacks.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe to all components's topics and all other topics
|
||||
*/
|
||||
void ESPMegaIoT::mqttSubscribe()
|
||||
{
|
||||
ESP_LOGD("ESPMegaIoT", "Begin MQTT Subscription");
|
||||
for (const auto &callback : subscribe_callbacks)
|
||||
{
|
||||
callback.second();
|
||||
mqtt.loop();
|
||||
}
|
||||
// Subscribe to all topics
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
ESP_LOGD("ESPMegaIoT", "Subscribing component %d", i);
|
||||
components[i]->subscribe();
|
||||
mqtt.loop();
|
||||
}
|
||||
}
|
||||
// Global topics
|
||||
this->subscribeRelative("requeststate");
|
||||
this->subscribeRelative("requestinfo");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish relative to the base topic
|
||||
*/
|
||||
void ESPMegaIoT::publishRelative(uint8_t card_id, char *topic, char *payload)
|
||||
{
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%d/%s", this->mqtt_config.base_topic, card_id, topic);
|
||||
mqtt.publish(absolute_topic, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe relative to the base topic
|
||||
*/
|
||||
bool ESPMegaIoT::mqttReconnect()
|
||||
{
|
||||
if (this->mqtt_config.mqtt_useauth)
|
||||
{
|
||||
return this->connectToMqtt(this->network_config.hostname, this->mqtt_config.mqtt_server, this->mqtt_config.mqtt_port, this->mqtt_config.mqtt_user, this->mqtt_config.mqtt_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->connectToMqtt(this->network_config.hostname, this->mqtt_config.mqtt_server, this->mqtt_config.mqtt_port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Keep the MQTT session alive
|
||||
* @note This function is called automatically by the ESPMegaIoT object, You should not call this function directly
|
||||
*/
|
||||
void ESPMegaIoT::sessionKeepAlive()
|
||||
{
|
||||
// This reconnect the MQTT if it disconnect.
|
||||
// If a disconnect happens, this will reconnect the MQTT within 1 second.
|
||||
// A connection attempt will be made at most once every MQTT_RECONNECT_INTERVAL
|
||||
// This have the effect of reconnecting to the server immediately if the connection is lost
|
||||
// and the connection was previously stable for at least MQTT_RECONNECT_INTERVAL
|
||||
// But will not reconnect if the connection was unstable and the connection was lost
|
||||
static unsigned long lastSessionKeepAlive = 0;
|
||||
static unsigned long lastConnectionAttempt = 0;
|
||||
if (millis() - lastSessionKeepAlive > 1000)
|
||||
{
|
||||
lastSessionKeepAlive = millis();
|
||||
// Check if mqtt is connected
|
||||
if (!mqtt.connected())
|
||||
{
|
||||
// Try to reconnect if lastConnectionAttempt exceed MQTT_RECONNECT_INTERVAL
|
||||
if (millis() - lastConnectionAttempt > MQTT_RECONNECT_INTERVAL)
|
||||
{
|
||||
lastConnectionAttempt = millis();
|
||||
mqtt_connected = mqttReconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback for MQTT messages relative to the base topic
|
||||
*
|
||||
* The message's base topic will be removed before calling the callback
|
||||
*
|
||||
* @param callback The callback function
|
||||
* @return The handler for the callback
|
||||
*/
|
||||
uint16_t ESPMegaIoT::registerRelativeMqttCallback(std::function<void(char *, char *)> callback)
|
||||
{
|
||||
mqtt_relative_callbacks[mqtt_relative_callbacks_handler_index] = callback;
|
||||
return mqtt_relative_callbacks_handler_index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a relative MQTT callback
|
||||
*
|
||||
* @param handler The handler of the callback
|
||||
*/
|
||||
void ESPMegaIoT::unregisterRelativeMqttCallback(uint16_t handler)
|
||||
{
|
||||
mqtt_relative_callbacks.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish a message relative to the base topic
|
||||
*
|
||||
* @param topic The topic to publish to
|
||||
* @param payload The payload to publish
|
||||
*/
|
||||
void ESPMegaIoT::publishRelative(const char *topic, const char *payload)
|
||||
{
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%s", this->mqtt_config.base_topic, topic);
|
||||
mqtt.publish(absolute_topic, payload);
|
||||
mqtt.loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish a message relative to the base topic
|
||||
*
|
||||
* @param topic The topic to publish to
|
||||
* @param payload The payload to publish
|
||||
* @param length The length of the payload
|
||||
*/
|
||||
void ESPMegaIoT::publishRelative(const char *topic, const char *payload, unsigned int length)
|
||||
{
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%s", this->mqtt_config.base_topic, topic);
|
||||
mqtt.publish(absolute_topic, (const uint8_t *)payload, length);
|
||||
mqtt.loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe to a topic relative to the base topic
|
||||
*
|
||||
* @param topic The topic to subscribe to
|
||||
*/
|
||||
void ESPMegaIoT::subscribeRelative(const char *topic)
|
||||
{
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%s", this->mqtt_config.base_topic, topic);
|
||||
mqtt.subscribe(absolute_topic);
|
||||
mqtt.loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a function to be called when the ESPMegaIoT object is subscribing to topics
|
||||
*
|
||||
* @param callback The callback function
|
||||
* @return The handler for the callback
|
||||
*/
|
||||
uint16_t ESPMegaIoT::registerSubscribeCallback(std::function<void(void)> callback)
|
||||
{
|
||||
subscribe_callbacks[subscribe_callbacks_handler_index] = callback;
|
||||
return subscribe_callbacks_handler_index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a subscribe callback
|
||||
*
|
||||
* @param handler The handler of the callback
|
||||
*/
|
||||
void ESPMegaIoT::unregisterSubscribeCallback(uint16_t handler)
|
||||
{
|
||||
subscribe_callbacks.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the network config
|
||||
*
|
||||
* @param network_config The network config struct
|
||||
*/
|
||||
void ESPMegaIoT::setNetworkConfig(NetworkConfig network_config)
|
||||
{
|
||||
this->network_config = network_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the network config from FRAM
|
||||
*/
|
||||
void ESPMegaIoT::loadNetworkConfig()
|
||||
{
|
||||
// Load the network config from FRAM
|
||||
network_config.ip = fram->read32(IOT_FRAM_ADDRESS);
|
||||
network_config.gateway = fram->read32(IOT_FRAM_ADDRESS + 4);
|
||||
network_config.subnet = fram->read32(IOT_FRAM_ADDRESS + 8);
|
||||
network_config.dns1 = fram->read32(IOT_FRAM_ADDRESS + 12);
|
||||
network_config.dns2 = fram->read32(IOT_FRAM_ADDRESS + 16);
|
||||
fram->read(IOT_FRAM_ADDRESS + 20, (uint8_t *)network_config.hostname, 32);
|
||||
network_config.useStaticIp = fram->read8(IOT_FRAM_ADDRESS + 52);
|
||||
network_config.useWifi = fram->read8(IOT_FRAM_ADDRESS + 53);
|
||||
network_config.wifiUseAuth = fram->read8(IOT_FRAM_ADDRESS + 54);
|
||||
fram->read(IOT_FRAM_ADDRESS + 55, (uint8_t *)network_config.ssid, 32);
|
||||
fram->read(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32);
|
||||
|
||||
// If ip,gateway,subnet,dns1,dns2 is 0, the device is not configured
|
||||
// set to default values
|
||||
// ip: 192.168.0.99
|
||||
// gateway: 192.168.0.1
|
||||
// subnet: 255.255.255.0
|
||||
// dns1: 1.1.1.1
|
||||
// dns2: 9.9.9.9
|
||||
if ((uint32_t)network_config.ip == 0 && (uint32_t)network_config.gateway == 0 && (uint32_t)network_config.subnet == 0 && (uint32_t)network_config.dns1 == 0 && (uint32_t)network_config.dns2 == 0)
|
||||
{
|
||||
network_config.ip = IPAddress(192, 168, 0, 99);
|
||||
network_config.gateway = IPAddress(192, 168, 0, 1);
|
||||
network_config.subnet = IPAddress(255, 255, 255, 0);
|
||||
network_config.dns1 = IPAddress(1, 1, 1, 1);
|
||||
network_config.useStaticIp = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the network config to FRAM
|
||||
*/
|
||||
void ESPMegaIoT::saveNetworkConfig()
|
||||
{
|
||||
// Save the network config to FRAM
|
||||
fram->write32(IOT_FRAM_ADDRESS, network_config.ip);
|
||||
fram->write32(IOT_FRAM_ADDRESS + 4, network_config.gateway);
|
||||
fram->write32(IOT_FRAM_ADDRESS + 8, network_config.subnet);
|
||||
fram->write32(IOT_FRAM_ADDRESS + 12, network_config.dns1);
|
||||
fram->write32(IOT_FRAM_ADDRESS + 16, network_config.dns2);
|
||||
fram->write(IOT_FRAM_ADDRESS + 20, (uint8_t *)network_config.hostname, 32);
|
||||
fram->write8(IOT_FRAM_ADDRESS + 52, network_config.useStaticIp);
|
||||
fram->write8(IOT_FRAM_ADDRESS + 53, network_config.useWifi);
|
||||
fram->write8(IOT_FRAM_ADDRESS + 54, network_config.wifiUseAuth);
|
||||
fram->write(IOT_FRAM_ADDRESS + 55, (uint8_t *)network_config.ssid, 32);
|
||||
fram->write(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin the ethernet interface
|
||||
*/
|
||||
void ESPMegaIoT::ethernetBegin()
|
||||
{
|
||||
ethernetIface->setHostname(network_config.hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the MQTT config from FRAM
|
||||
*/
|
||||
void ESPMegaIoT::loadMqttConfig()
|
||||
{
|
||||
// Load the mqtt config from FRAM
|
||||
// We skip bytes 119-127 because they are reserved for the network config
|
||||
mqtt_config.mqtt_port = fram->read16(IOT_FRAM_ADDRESS + 128);
|
||||
fram->read(IOT_FRAM_ADDRESS + 130, (uint8_t *)mqtt_config.mqtt_server, 32);
|
||||
fram->read(IOT_FRAM_ADDRESS + 162, (uint8_t *)mqtt_config.mqtt_user, 32);
|
||||
fram->read(IOT_FRAM_ADDRESS + 194, (uint8_t *)mqtt_config.mqtt_password, 32);
|
||||
mqtt_config.mqtt_useauth = fram->read8(IOT_FRAM_ADDRESS + 226);
|
||||
fram->read(IOT_FRAM_ADDRESS + 227, (uint8_t *)mqtt_config.base_topic, 32);
|
||||
this->base_topic_length = strlen(mqtt_config.base_topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the MQTT config to FRAM
|
||||
*/
|
||||
void ESPMegaIoT::saveMqttConfig()
|
||||
{
|
||||
fram->write16(IOT_FRAM_ADDRESS + 128, mqtt_config.mqtt_port);
|
||||
fram->write(IOT_FRAM_ADDRESS + 130, (uint8_t *)mqtt_config.mqtt_server, 32);
|
||||
fram->write(IOT_FRAM_ADDRESS + 162, (uint8_t *)mqtt_config.mqtt_user, 32);
|
||||
fram->write(IOT_FRAM_ADDRESS + 194, (uint8_t *)mqtt_config.mqtt_password, 32);
|
||||
fram->write8(IOT_FRAM_ADDRESS + 226, mqtt_config.mqtt_useauth);
|
||||
fram->write(IOT_FRAM_ADDRESS + 227, (uint8_t *)mqtt_config.base_topic, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to MQTT with the current config
|
||||
*/
|
||||
void ESPMegaIoT::connectToMqtt()
|
||||
{
|
||||
if (mqtt_config.mqtt_useauth)
|
||||
{
|
||||
ESP_LOGD("ESPMegaIoT", "Connecting to MQTT with auth");
|
||||
this->connectToMqtt(network_config.hostname, mqtt_config.mqtt_server, mqtt_config.mqtt_port, mqtt_config.mqtt_user, mqtt_config.mqtt_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGD("ESPMegaIoT", "Connecting to MQTT without auth");
|
||||
this->connectToMqtt(network_config.hostname, mqtt_config.mqtt_server, mqtt_config.mqtt_port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to the network using the current config
|
||||
*/
|
||||
void ESPMegaIoT::connectNetwork()
|
||||
{
|
||||
if (network_config.useWifi)
|
||||
{
|
||||
if (network_config.wifiUseAuth)
|
||||
this->connectToWifi(network_config.ssid, network_config.password);
|
||||
else
|
||||
this->connectToWifi(network_config.ssid);
|
||||
if (network_config.useStaticIp)
|
||||
WiFi.config(network_config.ip, network_config.gateway, network_config.subnet);
|
||||
else
|
||||
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ethernetBegin();
|
||||
if (network_config.useStaticIp)
|
||||
ethernetIface->config(network_config.ip, network_config.gateway, network_config.subnet, network_config.dns1, network_config.dns2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the MQTT config
|
||||
*
|
||||
* @param mqtt_config The MQTT config struct
|
||||
*/
|
||||
void ESPMegaIoT::setMqttConfig(MqttConfig mqtt_config)
|
||||
{
|
||||
this->mqtt_config = mqtt_config;
|
||||
this->base_topic_length = strlen(mqtt_config.base_topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bind an ethernet interface to the ESPMegaIoT object
|
||||
*
|
||||
* @param ethernetIface The ethernet interface to bind (ETH for ESPMegaPRO R3)
|
||||
*/
|
||||
void ESPMegaIoT::bindEthernetInterface(ETHClass *ethernetIface)
|
||||
{
|
||||
this->ethernetIface = ethernetIface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the IoTComponent object for a card
|
||||
*
|
||||
* @param card_id The id of the card
|
||||
* @return The IoTComponent object for the card
|
||||
*/
|
||||
IoTComponent *ESPMegaIoT::getComponent(uint8_t card_id)
|
||||
{
|
||||
return components[card_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the network config
|
||||
*
|
||||
* @warning You should not modify the returned struct directly
|
||||
*
|
||||
* @return The network config struct
|
||||
*/
|
||||
NetworkConfig *ESPMegaIoT::getNetworkConfig()
|
||||
{
|
||||
return &network_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the MQTT config
|
||||
*
|
||||
* @warning You should not modify the returned struct directly
|
||||
*
|
||||
* @return The MQTT config struct
|
||||
*/
|
||||
MqttConfig *ESPMegaIoT::getMqttConfig()
|
||||
{
|
||||
return &mqtt_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the MQTT is connected
|
||||
*
|
||||
* @return True if the MQTT is connected, false otherwise
|
||||
*/
|
||||
bool ESPMegaIoT::mqttConnected()
|
||||
{
|
||||
// return mqtt_connected;
|
||||
return mqtt.connected();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the network is connected
|
||||
*
|
||||
* @return True if the network is connected, false otherwise
|
||||
*/
|
||||
bool ESPMegaIoT::networkConnected()
|
||||
{
|
||||
if (network_config.useWifi)
|
||||
return WiFi.status() == WL_CONNECTED;
|
||||
else
|
||||
return ethernetIface->linkUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bind a FRAM object to the ESPMegaIoT object
|
||||
* @note This class is hardcode to use the FRAM address 34-300
|
||||
*
|
||||
* @param fram The FRAM object to bind
|
||||
*/
|
||||
void ESPMegaIoT::bindFRAM(FRAM *fram)
|
||||
{
|
||||
this->fram = fram;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the Wifi IP address
|
||||
*
|
||||
* @return The Wifi IP address
|
||||
*/
|
||||
IPAddress ESPMegaIoT::getWifiIp()
|
||||
{
|
||||
return WiFi.localIP();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the Ethernet IP Address
|
||||
*
|
||||
* @return The Ethernet IP Address
|
||||
*/
|
||||
IPAddress ESPMegaIoT::getETHIp()
|
||||
{
|
||||
return ETH.localIP();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the IP address of the currently active network interface
|
||||
*
|
||||
* @return The IP address of the currently active network interface
|
||||
*/
|
||||
IPAddress ESPMegaIoT::getIp()
|
||||
{
|
||||
if (network_config.useWifi)
|
||||
return this->getWifiIp();
|
||||
else
|
||||
return this->getETHIp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the MAC Address of the Ethernet interface
|
||||
*
|
||||
* @return The MAC Address of the Ethernet interface
|
||||
*/
|
||||
String ESPMegaIoT::getETHMac()
|
||||
{
|
||||
return ETH.macAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the MAC Address of the Wifi interface
|
||||
*
|
||||
* @return The MAC Address of the Wifi interface
|
||||
*/
|
||||
String ESPMegaIoT::getWifiMac()
|
||||
{
|
||||
return WiFi.macAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the MAC Address of the currently active network interface
|
||||
*
|
||||
* @return The MAC Address of the currently active network interface
|
||||
*/
|
||||
String ESPMegaIoT::getMac()
|
||||
{
|
||||
if (network_config.useWifi)
|
||||
return this->getWifiMac();
|
||||
else
|
||||
return this->getETHMac();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish a json object containing the system summary
|
||||
*/
|
||||
void ESPMegaIoT::publishSystemSummary() {
|
||||
char payload[1024];
|
||||
StaticJsonDocument<1024> doc;
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
root["ip"] = this->getIp().toString();
|
||||
root["firmware"] = SW_VERSION;
|
||||
root["sdk_version"] = SDK_VESRION;
|
||||
root["board_model"] = BOARD_MODEL;
|
||||
JsonArray cards = root.createNestedArray("cards");
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
JsonObject card = cards.createNestedObject();
|
||||
card["id"] = i;
|
||||
card["type"] = components[i]->getType();
|
||||
}
|
||||
}
|
||||
serializeJson(doc, payload);
|
||||
ESP_LOGD("ESPMegaIoT", "Publishing system summary: %s", payload);
|
||||
mqtt.loop();
|
||||
this->publishRelative("info", payload, strlen(payload));
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
#include <AnalogIoT.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <DigitalInputIoT.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
#include <DigitalOutputIoT.hpp>
|
||||
#include <ClimateCard.hpp>
|
||||
#include <ClimateIoT.hpp>
|
||||
#include <CurrentTransformerCard.hpp>
|
||||
#include <CurrentTransformerIoT.hpp>
|
||||
#include <IoTComponent.hpp>
|
||||
#include <PubSubClient.h>
|
||||
#include <ETH.h>
|
||||
#include <WiFi.h>
|
||||
#include <FRAM.h>
|
||||
#include <map>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPMegaCommon.hpp>
|
||||
|
||||
// MQTT Connection Parameters
|
||||
#define TCP_TIMEOUT_SEC 5
|
||||
#define MQTT_RECONNECT_INTERVAL 30000
|
||||
|
||||
// FRAM Address for ESPMegaPROIoT
|
||||
// Starts from 34
|
||||
// Ends at 300 (inclusive)
|
||||
// Total of 267 bytes
|
||||
#define IOT_FRAM_ADDRESS 34
|
||||
|
||||
/**
|
||||
* @brief The network configuration struct
|
||||
* @note This struct will be saved to FRAM when calling saveNetworkConfig
|
||||
*/
|
||||
struct NetworkConfig
|
||||
{
|
||||
IPAddress ip; ///< The IP address
|
||||
IPAddress gateway; ///< The gateway address
|
||||
IPAddress subnet; ///< The subnet mask
|
||||
IPAddress dns1; ///< The primary DNS server
|
||||
IPAddress dns2; ///< The secondary DNS server
|
||||
char hostname[32]; ///< The hostname
|
||||
bool useStaticIp; ///< Whether to use a static IP, if false, DHCP will be used
|
||||
bool useWifi; ///< Whether to use WiFi or Ethernet, if false, Ethernet will be used
|
||||
bool wifiUseAuth; ///< Whether to use WiFi authentication, if false, ssid and password will be ignored
|
||||
char ssid[32]; ///< The WiFi SSID, even if wifiUseAuth is false, this should be set
|
||||
char password[32]; ///< The WiFi password, even if wifiUseAuth is false, this should be set
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The MQTT configuration struct
|
||||
* @note This struct will be saved to FRAM when calling saveMqttConfig
|
||||
*/
|
||||
struct MqttConfig
|
||||
{
|
||||
char mqtt_server[32]; ///< The MQTT server address
|
||||
uint16_t mqtt_port; ///< The MQTT server port
|
||||
char mqtt_user[32]; ///< The MQTT username, even if mqtt_useauth is false, this should be set
|
||||
char mqtt_password[32]; ///< The MQTT password, even if mqtt_useauth is false, this should be set
|
||||
bool mqtt_useauth; ///< Whether to use MQTT authentication, if false, mqtt_user and mqtt_password will be ignored
|
||||
char base_topic[32]; ///< The base topic for the MQTT messages
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ESPMegaIoT class is a class that is used to interface with the ESPMegaPRO IoT module
|
||||
*
|
||||
* This class allows you to register IoT components and interface with them through MQTT.
|
||||
* This class also manages the network and MQTT connections for you.
|
||||
* Supports both WiFi and Ethernet.
|
||||
* Also allows you to save and load network and MQTT configurations to and from FRAM.
|
||||
* Also provides MQTT helpers for publishing and subscribing to topics.
|
||||
*/
|
||||
class ESPMegaIoT
|
||||
{
|
||||
public:
|
||||
ESPMegaIoT();
|
||||
~ESPMegaIoT();
|
||||
void intr_begin(ExpansionCard *cards[]);
|
||||
void loop();
|
||||
void registerCard(uint8_t card_id);
|
||||
void unregisterCard(uint8_t card_id);
|
||||
void publishCard(uint8_t card_id);
|
||||
// Publish topic appended with base topic
|
||||
void publishRelative(const char *topic, const char *payload);
|
||||
void publishRelative(const char *topic, const char *payload, unsigned int length);
|
||||
// Subscribe topic appended with base topic
|
||||
void subscribeRelative(const char *topic);
|
||||
void subscribe(const char *topic);
|
||||
void unsubscribeFromTopic(const char *topic);
|
||||
void connectToWifi(const char *ssid, const char *password);
|
||||
void connectToWifi(const char *ssid);
|
||||
void disconnectFromWifi();
|
||||
bool wifiConnected();
|
||||
void ethernetBegin();
|
||||
void loadNetworkConfig();
|
||||
void saveNetworkConfig();
|
||||
NetworkConfig* getNetworkConfig();
|
||||
MqttConfig* getMqttConfig();
|
||||
void setMqttConfig(MqttConfig mqtt_config);
|
||||
void saveMqttConfig();
|
||||
void loadMqttConfig();
|
||||
void connectNetwork();
|
||||
void setNetworkConfig(NetworkConfig network_config);
|
||||
void connectToMqtt();
|
||||
bool connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port, char *mqtt_user, char *mqtt_password);
|
||||
bool connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port);
|
||||
bool mqttConnected();
|
||||
void disconnectFromMqtt();
|
||||
void publish(const char *topic, const char *payload);
|
||||
void publish(const char *topic, const char *payload, unsigned int length);
|
||||
uint16_t registerMqttCallback(std::function<void(char *, char *)> callback);
|
||||
void unregisterMqttCallback(uint16_t handler);
|
||||
uint16_t registerRelativeMqttCallback(std::function<void(char *, char *)> callback);
|
||||
void unregisterRelativeMqttCallback(uint16_t handler);
|
||||
uint16_t registerSubscribeCallback(std::function<void(void)> callback);
|
||||
void unregisterSubscribeCallback(uint16_t handler);
|
||||
void setBaseTopic(char *base_topic);
|
||||
void bindEthernetInterface(ETHClass *ethernetIface);
|
||||
bool networkConnected();
|
||||
void bindFRAM(FRAM *fram);
|
||||
void publishSystemSummary();
|
||||
|
||||
IoTComponent* getComponent(uint8_t card_id);
|
||||
IPAddress getETHIp();
|
||||
IPAddress getWifiIp();
|
||||
IPAddress getIp();
|
||||
|
||||
String getETHMac();
|
||||
String getWifiMac();
|
||||
String getMac();
|
||||
|
||||
private:
|
||||
FRAM *fram;
|
||||
bool useWifi;
|
||||
bool WifiUseAuth;
|
||||
char ssid[32];
|
||||
char password[32];
|
||||
WiFiClient tcpClient;
|
||||
void sessionKeepAlive();
|
||||
bool mqttReconnect();
|
||||
void wifiReconnect();
|
||||
void mqttSubscribe();
|
||||
void mqttCallback(char *topic, byte *payload, unsigned int length);
|
||||
uint16_t mqtt_callbacks_handler_index;
|
||||
uint16_t mqtt_relative_callbacks_handler_index;
|
||||
uint16_t subscribe_callbacks_handler_index;
|
||||
std::map<uint16_t, std::function<void(char*, char*)>> mqtt_callbacks;
|
||||
std::map<uint16_t, std::function<void(char*, char*)>> mqtt_relative_callbacks;
|
||||
std::map<uint16_t, std::function<void(void)>> subscribe_callbacks;
|
||||
void publishRelative(uint8_t card_id, char *topic, char *payload);
|
||||
bool active;
|
||||
PubSubClient mqtt;
|
||||
IoTComponent *components[255];
|
||||
char payload_buffer[200];
|
||||
uint8_t base_topic_length;
|
||||
ExpansionCard **cards; // Points to card array in ESPMegaPRO Core
|
||||
// MQTT Connection Parameters
|
||||
bool mqtt_connected;
|
||||
NetworkConfig network_config;
|
||||
MqttConfig mqtt_config;
|
||||
ETHClass *ethernetIface;
|
||||
};
|
|
@ -1,358 +0,0 @@
|
|||
#include <ESPMegaProOS.hpp>
|
||||
#include "esp_sntp.h"
|
||||
|
||||
// Reserve FRAM address 0 - 1000 for ESPMegaPRO Internal Use
|
||||
// (34 Bytes) Address 0-33 for Built-in Digital Output Card
|
||||
// (266 Bytes) Address 34-300 for ESPMegaPRO IoT Module
|
||||
|
||||
/**
|
||||
* @brief Create a new ESPMegaPRO object
|
||||
*
|
||||
* @warning Only one ESPMegaPRO object can be created, creating more than one will result in undefined behavior
|
||||
*/
|
||||
ESPMegaPRO::ESPMegaPRO()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the ESPMegaPRO object.
|
||||
*
|
||||
* This function initializes the ESPMegaPRO object and all of its components.
|
||||
* It also initializes the built-in Digital Input and Digital Output cards.
|
||||
*
|
||||
* @return True if the initialization is successful, false otherwise.
|
||||
*/
|
||||
bool ESPMegaPRO::begin()
|
||||
{
|
||||
Wire.begin(14, 33);
|
||||
fram.begin(FRAM_ADDRESS);
|
||||
Serial.begin(115200);
|
||||
this->installCard(1, &outputs);
|
||||
outputs.bindFRAM(&fram, 0);
|
||||
uint8_t outputPinMap[16] = {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
outputs.loadPinMap(outputPinMap);
|
||||
outputs.loadFromFRAM();
|
||||
outputs.setAutoSaveToFRAM(true);
|
||||
if (!this->installCard(0, &inputs))
|
||||
{
|
||||
ESP_LOGE("ESPMegaPRO", "Failed to initialize inputs");
|
||||
ESP_LOGE("ESPMegaPRO", "Is this an ESPMegaPRO device?");
|
||||
return false;
|
||||
}
|
||||
uint8_t inputPinMap[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 14, 13, 12};
|
||||
inputs.loadPinMap(inputPinMap);
|
||||
// Detect GPIO2 Reset
|
||||
gpio_num_t buttonPin = GPIO_NUM_2;
|
||||
gpio_pad_select_gpio(buttonPin);
|
||||
gpio_set_direction(buttonPin, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(buttonPin, GPIO_PULLUP_ONLY);
|
||||
if (gpio_get_level(buttonPin) == 0)
|
||||
{
|
||||
ESP_LOGW("ESPMegaPRO", "GPIO2 is low, if this condition persists for 5 more seconds, the device will factory reset");
|
||||
bool shouldReset = true;
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
if (gpio_get_level(buttonPin) == 1)
|
||||
{
|
||||
ESP_LOGI("ESPMegaPRO", "Reset condition cleared, Continuing boot process");
|
||||
shouldReset = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shouldReset)
|
||||
{
|
||||
ESP_LOGW("ESPMegaPRO", "Reset condition met, Factory Resetting device");
|
||||
for (int i = 0; i < fram.getSizeBytes(); i++)
|
||||
{
|
||||
if (i % 1024 == 0)
|
||||
ESP_LOGV("ESPMegaPRO", "Clearing FRAM Address %d", i);
|
||||
fram.write8(i, 0);
|
||||
}
|
||||
ESP_LOGI("ESPMegaPRO", "Factory Reset Complete");
|
||||
ESP_LOGI("ESPMegaPRO", "Rebooting device");
|
||||
esp_restart();
|
||||
}
|
||||
}
|
||||
// Recovery Mode
|
||||
recovery.bindFRAM(&fram, 600);
|
||||
recovery.begin();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop for the ESPMegaPRO object.
|
||||
*
|
||||
* @note This function must be called in the main loop of the program.
|
||||
*
|
||||
* It will call the loop() function of all installed expansion cards, the ESPMegaIoT module, and the internal display.
|
||||
*
|
||||
*/
|
||||
void ESPMegaPRO::loop()
|
||||
{
|
||||
inputs.loop();
|
||||
outputs.loop();
|
||||
recovery.loop();
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (cardInstalled[i])
|
||||
{
|
||||
cards[i]->loop();
|
||||
}
|
||||
}
|
||||
if (iotEnabled)
|
||||
{
|
||||
iot->loop();
|
||||
static int64_t lastNTPUpdate = (esp_timer_get_time() / 1000) - NTP_UPDATE_INTERVAL_MS + NTP_INITIAL_SYNC_DELAY_MS;
|
||||
if ((esp_timer_get_time() / 1000) - lastNTPUpdate > NTP_UPDATE_INTERVAL_MS)
|
||||
{
|
||||
ESP_LOGV("ESPMegaPRO", "Updating time from NTP");
|
||||
lastNTPUpdate = esp_timer_get_time() / 1000;
|
||||
this->updateTimeFromNTP();
|
||||
}
|
||||
}
|
||||
if (internalDisplayEnabled)
|
||||
{
|
||||
display->loop();
|
||||
}
|
||||
if (webServerEnabled)
|
||||
{
|
||||
webServer->loop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Installs an expansion card to the specified slot.
|
||||
*
|
||||
* @note This function automatically initializes the expansion card.
|
||||
*
|
||||
* @param slot The slot to install the card to.
|
||||
* @param card Pointer to the ExpansionCard object.
|
||||
*
|
||||
* @return True if the installation is successful, false otherwise.
|
||||
*/
|
||||
bool ESPMegaPRO::installCard(uint8_t slot, ExpansionCard *card)
|
||||
{
|
||||
if (slot > 255)
|
||||
return false;
|
||||
if (cardInstalled[slot])
|
||||
{
|
||||
ESP_LOGE("ESPMegaPRO", "Card already installed at slot %d", slot);
|
||||
return false;
|
||||
}
|
||||
if (!card->begin())
|
||||
{
|
||||
ESP_LOGE("ESPMegaPRO", "Failed to initialize card at slot %d", slot);
|
||||
return false;
|
||||
}
|
||||
cards[slot] = card;
|
||||
cardInstalled[slot] = true;
|
||||
cardCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the internal RTC from NTP.
|
||||
*
|
||||
* @note Network must be connected before calling this function (see ESPMegaPRO.ESPMegaIoT::connectNetwork()).
|
||||
*
|
||||
* @return True if the update is successful, false otherwise.
|
||||
*/
|
||||
bool ESPMegaPRO::updateTimeFromNTP()
|
||||
{
|
||||
struct tm timeinfo;
|
||||
uint32_t start = esp_timer_get_time() / 1000;
|
||||
time_t now;
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
if (!(timeinfo.tm_year > (2016 - 1900)))
|
||||
{
|
||||
ESP_LOGI("ESPMegaPRO", "NTP is not ready yet!");
|
||||
return false;
|
||||
}
|
||||
rtctime_t rtctime = this->getTime();
|
||||
if (rtctime.hours != timeinfo.tm_hour || rtctime.minutes != timeinfo.tm_min ||
|
||||
rtctime.seconds != timeinfo.tm_sec || rtctime.day != timeinfo.tm_mday ||
|
||||
rtctime.month != timeinfo.tm_mon + 1 || rtctime.year != timeinfo.tm_year + 1900)
|
||||
{
|
||||
this->setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
|
||||
timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
|
||||
}
|
||||
ESP_LOGV("ESPMegaPRO", "Time updated from NTP: %s", asctime(&timeinfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the timezone for the internal RTC.
|
||||
*
|
||||
* @note This function takes POSIX timezone strings (e.g. "EST5EDT,M3.2.0,M11.1.0").
|
||||
*/
|
||||
void ESPMegaPRO::setTimezone(const char* offset)
|
||||
{
|
||||
setenv("TZ", offset, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the current time from the internal RTC.
|
||||
*
|
||||
* @return The current time as a rtctime_t struct.
|
||||
*/
|
||||
rtctime_t ESPMegaPRO::getTime()
|
||||
{
|
||||
tmElements_t timeElement;
|
||||
RTC.read(timeElement);
|
||||
rtctime_t time;
|
||||
time.hours = timeElement.Hour;
|
||||
time.minutes = timeElement.Minute;
|
||||
time.seconds = timeElement.Second;
|
||||
time.day = timeElement.Day;
|
||||
time.month = timeElement.Month;
|
||||
time.year = timeElement.Year + 1970;
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the current time of the internal RTC.
|
||||
*
|
||||
* @param hours The hours.
|
||||
* @param minutes The minutes.
|
||||
* @param seconds The seconds.
|
||||
* @param day The day.
|
||||
* @param month The month.
|
||||
* @param year The year.
|
||||
*/
|
||||
void ESPMegaPRO::setTime(int hours, int minutes, int seconds, int day, int month, int year)
|
||||
{
|
||||
tmElements_t timeElement;
|
||||
timeElement.Hour = hours;
|
||||
timeElement.Minute = minutes;
|
||||
timeElement.Second = seconds;
|
||||
timeElement.Day = day;
|
||||
timeElement.Month = month;
|
||||
timeElement.Year = year - 1970;
|
||||
RTC.write(timeElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables, Instanitates, and Initializes the ESPMegaIoT module.
|
||||
*
|
||||
* @note This function must be called before using the ESPMegaIoT module.
|
||||
*/
|
||||
void ESPMegaPRO::enableIotModule()
|
||||
{
|
||||
if (iotEnabled)
|
||||
return;
|
||||
this->iot = new ESPMegaIoT();
|
||||
this->iot->bindFRAM(&fram);
|
||||
this->iot->intr_begin(cards);
|
||||
iotEnabled = true;
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, this->iot->getMqttConfig()->mqtt_server);
|
||||
sntp_setservername(1, "pool.ntp.org");
|
||||
sntp_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the expansion card installed at the specified slot.
|
||||
*
|
||||
* @param slot The slot to get the card from.
|
||||
*
|
||||
* @return Pointer to the ExpansionCard object, or nullptr if no card is installed at the specified slot.
|
||||
*/
|
||||
ExpansionCard *ESPMegaPRO::getCard(uint8_t slot)
|
||||
{
|
||||
if (slot > 255)
|
||||
return nullptr;
|
||||
if (!cardInstalled[slot])
|
||||
return nullptr;
|
||||
return cards[slot];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables, Instanitates, and Initializes the internal display.
|
||||
*
|
||||
* @note &Serial is used for the internal display on ESPMegaPRO R3.
|
||||
* @note This function can only be called if the ESPMegaIoT module is enabled.
|
||||
* @note This function must be called before using the internal display.
|
||||
*
|
||||
* @param serial Pointer to the HardwareSerial object to use for the internal display (Serial for ESPMegaPRO R3).
|
||||
*/
|
||||
void ESPMegaPRO::enableInternalDisplay(HardwareSerial *serial)
|
||||
{
|
||||
if (internalDisplayEnabled)
|
||||
return;
|
||||
if (!iotEnabled)
|
||||
{
|
||||
ESP_LOGE("ESPMegaPRO", "Cannot enable internal display without IoT module enabled");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD("ESPMegaPRO", "Enabling Internal Display");
|
||||
display = new InternalDisplay(serial);
|
||||
ESP_LOGD("ESPMegaPRO", "Binding Internal Display to IoT Module");
|
||||
auto bindedGetTime = std::bind(&ESPMegaPRO::getTime, this);
|
||||
ESP_LOGD("ESPMegaPRO", "Binding Internal Display to Input/Output Cards");
|
||||
display->bindInputCard(&inputs);
|
||||
display->bindOutputCard(&outputs);
|
||||
display->begin(this->iot, bindedGetTime);
|
||||
internalDisplayEnabled = true;
|
||||
ESP_LOGD("ESPMegaPRO", "Internal Display Enabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dumps the contents of the internal FRAM to the serial port.
|
||||
*
|
||||
* @param start The starting address.
|
||||
* @param end The ending address.
|
||||
*/
|
||||
void ESPMegaPRO::dumpFRAMtoSerial(uint16_t start, uint16_t end)
|
||||
{
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
if (i % 16 == 0)
|
||||
{
|
||||
Serial.printf("\n%03d: ", i);
|
||||
}
|
||||
Serial.printf("%03d ", this->fram.read8(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dumps the contents of the internal FRAM to the serial port in ASCII.
|
||||
*
|
||||
* @param start The starting address.
|
||||
* @param end The ending address.
|
||||
*/
|
||||
void ESPMegaPRO::dumpFRAMtoSerialASCII(uint16_t start, uint16_t end)
|
||||
{
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
Serial.printf("%d: %c\n", i, this->fram.read8(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables the internal web server.
|
||||
*
|
||||
* @note This function can only be called if the ESPMegaIoT module is enabled.
|
||||
* @note This function can only be called once.
|
||||
*
|
||||
* @param port The port to use for the web server.
|
||||
*/
|
||||
void ESPMegaPRO::enableWebServer(uint16_t port)
|
||||
{
|
||||
if (!iotEnabled)
|
||||
{
|
||||
ESP_LOGE("ESPMegaPRO", "Cannot enable web server without IoT module enabled");
|
||||
return;
|
||||
}
|
||||
if (webServerEnabled)
|
||||
{
|
||||
ESP_LOGE("ESPMegaPRO", "Web server already enabled");
|
||||
return;
|
||||
}
|
||||
webServer = new ESPMegaWebServer(port, this->iot);
|
||||
webServer->bindFRAM(&fram);
|
||||
webServer->begin();
|
||||
webServerEnabled = true;
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
#include <ClimateCard.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <FRAM.h>
|
||||
#include <TimeLib.h>
|
||||
#include <DS1307RTC.h>
|
||||
#include <time.h>
|
||||
#include <TimeStructure.hpp>
|
||||
#include <ESPMegaDisplay.hpp>
|
||||
#include <InternalDisplay.hpp>
|
||||
#include <ESPMegaWebServer.hpp>
|
||||
#include <ESPMegaRecovery.hpp>
|
||||
|
||||
// ESPMega Pro R3 Board Address
|
||||
#define FRAM_ADDRESS 0x56
|
||||
#define INPUT_BANK_A_ADDRESS 0x21
|
||||
#define INPUT_BANK_B_ADDRESS 0x22
|
||||
#define PWM_BANK_ADDRESS 0x5F
|
||||
#define RTC_ADDRESS 0x68
|
||||
|
||||
// Constants
|
||||
#define NTP_TIMEOUT_MS 5000
|
||||
#define NTP_UPDATE_INTERVAL_MS 60000
|
||||
#define NTP_INITIAL_SYNC_DELAY_MS 15000
|
||||
|
||||
/**
|
||||
* @brief The ESPMegaPRO class is the main class for the ESPMegaPRO library.
|
||||
*
|
||||
* This class provides functions for managing the ESPMegaPRO board, such as installing expansion cards, managing the internal RTC, and managing the internal FRAM.
|
||||
* This class also provides functions for managing the ESPMegaIoT module and the internal display.
|
||||
*
|
||||
* This class provide a Object Oriented Programming (OOP) interface for the ESPMegaPRO board.
|
||||
* If you are looking for a more simple and a more procedural interface, please use the ESPMegaPRO class in ESPMegaPRO.hpp.
|
||||
* But note that the ESPMegaPRO class only interfaces with the built-in Digital Input and Digital Output cards and other onboard components.
|
||||
* It does not provide an interface for expansion cards, the ESPMegaIoT module, and the internal display.
|
||||
*
|
||||
* @warning Only one ESPMegaPRO object can be created, creating more than one will result in undefined behavior.
|
||||
*/
|
||||
class ESPMegaPRO {
|
||||
public:
|
||||
ESPMegaPRO();
|
||||
bool begin();
|
||||
void loop();
|
||||
bool installCard(uint8_t slot, ExpansionCard* card);
|
||||
bool updateTimeFromNTP();
|
||||
void enableIotModule();
|
||||
void enableInternalDisplay(HardwareSerial *serial);
|
||||
void enableWebServer(uint16_t port);
|
||||
void setTimezone(const char* offset);
|
||||
rtctime_t getTime();
|
||||
void dumpFRAMtoSerial(uint16_t start, uint16_t end);
|
||||
void dumpFRAMtoSerialASCII(uint16_t start, uint16_t end);
|
||||
void setTime(int hours, int minutes, int seconds, int day, int month, int year);
|
||||
ExpansionCard* getCard(uint8_t slot);
|
||||
FRAM fram;
|
||||
/**
|
||||
* @brief The Digital Input Card Built-in to the ESPMegaPRO board.
|
||||
* @typedef DigitalInputCard
|
||||
* @note This card is installed by default at slot 0 on the ESPMegaPRO R3 board.
|
||||
*/
|
||||
DigitalInputCard inputs = DigitalInputCard(INPUT_BANK_A_ADDRESS, INPUT_BANK_B_ADDRESS);
|
||||
/**
|
||||
* @brief The Digital Output Card Built-in to the ESPMegaPRO board.
|
||||
* @typedef DigitalOutputCard
|
||||
* @note This card is installed by default at slot 1 on the ESPMegaPRO R3 board.
|
||||
*/
|
||||
DigitalOutputCard outputs = DigitalOutputCard(PWM_BANK_ADDRESS);
|
||||
/**
|
||||
* @brief The Display Built-in to the ESPMegaPRO board.
|
||||
* @typedef InternalDisplay
|
||||
* @note SKU EMG-PRO-R3-XXX-(F)-(12/24)V does not have a built-in display.
|
||||
*/
|
||||
InternalDisplay *display;
|
||||
/**
|
||||
* @brief This component is used to connect the ESPMegaPRO board to the internet and communicate with it through MQTT.
|
||||
* @typedef ESPMegaIoT
|
||||
* @note You must call enableIotModule() before using this component.
|
||||
*/
|
||||
ESPMegaIoT *iot;
|
||||
/**
|
||||
* @brief This component is used to create a web server on the ESPMegaPRO board.
|
||||
* @typedef ESPMegaWebServer
|
||||
* @note You must call enableWebServer() before using this component.
|
||||
*/
|
||||
ESPMegaWebServer *webServer;
|
||||
/**
|
||||
* @brief This component is used to enter recovery mode when the ESPMegaPRO board is in a bootloop.
|
||||
* @typedef ESPMegaRecovery
|
||||
*/
|
||||
ESPMegaRecovery recovery;
|
||||
private:
|
||||
bool iotEnabled = false;
|
||||
bool internalDisplayEnabled = false;
|
||||
bool webServerEnabled = false;
|
||||
ExpansionCard* cards[255];
|
||||
bool cardInstalled[255];
|
||||
uint8_t cardCount = 0;
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
#include <ESPMegaIoT.hpp>
|
||||
|
||||
class ESPMegaRTU {
|
||||
public:
|
||||
ESPMegaRTU();
|
||||
~ESPMegaRTU();
|
||||
virtual void subscribe();
|
||||
virtual void begin(char* remote_base_topic, uint8_t remote_card_slot, ESPMegaIoT* iot);
|
||||
protected:
|
||||
char* remoteBaseTopic;
|
||||
uint8_t remoteBaseTopicLength;
|
||||
uint8_t cardSlot;
|
||||
ESPMegaIoT* iot;
|
||||
};
|
|
@ -1,141 +0,0 @@
|
|||
#include <ESPMegaRecovery.hpp>
|
||||
ESPMegaRecovery::ESPMegaRecovery()
|
||||
{
|
||||
// Initialize all the pointers to null
|
||||
this->fram = nullptr;
|
||||
this->fram_address = 0;
|
||||
this->bootloop_counter = 0;
|
||||
this->recovery_mode = false;
|
||||
this->web_server = nullptr;
|
||||
this->iot = nullptr;
|
||||
}
|
||||
void ESPMegaRecovery::begin() {
|
||||
// Retrieve the bootloop counter from the FRAM
|
||||
if(this->fram != nullptr) {
|
||||
this->bootloop_counter = this->fram->read8(this->fram_address);
|
||||
}
|
||||
// Inclement the bootloop counter
|
||||
this->inclementBootloopCounter();
|
||||
ESP_LOGV("ESPMegaRecovery", "Bootloop counter: %d", this->getBootloopCounter());
|
||||
// If the bootloop counter is greater than 5, enter recovery mode
|
||||
if(this->getBootloopCounter() > 5) {
|
||||
ESP_LOGE("ESPMegaRecovery", "Bootloop detected");
|
||||
// Reset the bootloop counter to prevent re-entering recovery mode
|
||||
// The device might unintentionally restart multiple times
|
||||
// By resetting the counter, the user can press reset once in recovery mode to exit
|
||||
ESP_LOGD("ESPMegaRecovery", "Resetting bootloop counter");
|
||||
this->resetBootloopCounter();
|
||||
ESP_LOGW("ESPMegaRecovery", "Entering recovery mode");
|
||||
this->enterRecoveryMode();
|
||||
this->loop();
|
||||
}
|
||||
}
|
||||
void ESPMegaRecovery::loop() {
|
||||
// If the device is in recovery mode, block all other tasks
|
||||
if(this->isRecoveryMode()) {
|
||||
int i = 0;
|
||||
while(true) {
|
||||
if (i%10 == 0) {
|
||||
ESP_LOGV("ESPMegaRecovery", "System is in recovery mode, no tasks will be executed");
|
||||
ESP_LOGV("ESPMegaRecovery", "Please upload a new firmware to exit recovery mode");
|
||||
}
|
||||
// This code will become the new loop
|
||||
delay(1000);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// Watchdog timer
|
||||
static bool booted = false;
|
||||
static uint32_t boot_time = millis();
|
||||
if(!booted) {
|
||||
if(millis() - boot_time > RECOVERY_WATCHDOG_TIMEOUT * 1000) {
|
||||
ESP_LOGI("ESPMegaRecovery", "System booted successfully, resetting bootloop counter");
|
||||
this->resetBootloopCounter();
|
||||
booted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
void ESPMegaRecovery::enterRecoveryMode() {
|
||||
// Set the recovery mode flag
|
||||
this->recovery_mode = true;
|
||||
// Enabling the IoT module
|
||||
ESP_LOGI("ESPMegaRecovery", "Enabling the IoT module");
|
||||
this->iot = new ESPMegaIoT();
|
||||
this->iot->bindFRAM(this->fram);
|
||||
ETH.begin();
|
||||
this->iot->bindEthernetInterface(Ð);
|
||||
ESP_LOGI("ESPMegaRecovery", "Loading network configuration");
|
||||
this->iot->loadNetworkConfig();
|
||||
ESP_LOGI("ESPMegaRecovery", "Attempting to connect to the network");
|
||||
this->iot->connectNetwork();
|
||||
// Start the web server
|
||||
ESP_LOGI("ESPMegaRecovery", "Starting the web server");
|
||||
this->web_server = new ESPMegaWebServer(80, this->iot);
|
||||
this->web_server->bindFRAM(this->fram);
|
||||
this->web_server->loadCredentialsFromFRAM();
|
||||
ESP_LOGI("ESPMegaRecovery", "Aquiring the web server instance");
|
||||
AsyncWebServer *server = this->web_server->getServer();
|
||||
server->begin();
|
||||
// Add OTA update and restart endpoint
|
||||
ESP_LOGI("ESPMegaRecovery", "Adding OTA update and reboot endpoints");
|
||||
auto bindedDashboardHandler = std::bind(&ESPMegaWebServer::dashboardHandler, this->web_server, std::placeholders::_1);
|
||||
auto bindedOtaRequestHandler = std::bind(&ESPMegaWebServer::otaRequestHandler, this->web_server, std::placeholders::_1);
|
||||
auto bindedOtaUploadHandler = std::bind(&ESPMegaWebServer::otaUploadHandler, this->web_server, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6);
|
||||
auto bindedDeviceInfoHandler = std::bind(&ESPMegaRecovery::getDeviceInfoHandler, this, std::placeholders::_1);
|
||||
auto bindedConfigHandler = std::bind(&ESPMegaRecovery::configHandler, this, std::placeholders::_1);
|
||||
server->on("/", HTTP_GET, bindedDashboardHandler);
|
||||
server->on("/ota_update", HTTP_POST, bindedOtaRequestHandler, bindedOtaUploadHandler);
|
||||
server->on("/reboot", HTTP_GET, std::bind(&ESPMegaWebServer::rebootHandler, this->web_server, std::placeholders::_1));
|
||||
server->on("/get_device_info", HTTP_GET, bindedDeviceInfoHandler);
|
||||
server->on("/config", HTTP_GET, bindedConfigHandler);
|
||||
}
|
||||
void ESPMegaRecovery::bindFRAM(FRAM *fram, uint32_t address) {
|
||||
this->fram = fram;
|
||||
this->fram_address = address;
|
||||
}
|
||||
uint8_t ESPMegaRecovery::getBootloopCounter() {
|
||||
return this->bootloop_counter;
|
||||
}
|
||||
void ESPMegaRecovery::inclementBootloopCounter() {
|
||||
this->bootloop_counter++;
|
||||
if(this->fram != nullptr) {
|
||||
this->fram->write8(this->fram_address, this->bootloop_counter);
|
||||
}
|
||||
}
|
||||
void ESPMegaRecovery::resetBootloopCounter() {
|
||||
this->bootloop_counter = 0;
|
||||
if(this->fram != nullptr) {
|
||||
this->fram->write8(this->fram_address, this->bootloop_counter);
|
||||
}
|
||||
}
|
||||
bool ESPMegaRecovery::isRecoveryMode() {
|
||||
return this->recovery_mode;
|
||||
}
|
||||
|
||||
void ESPMegaRecovery::getDeviceInfoHandler(AsyncWebServerRequest *request) {
|
||||
if (!request->authenticate(this->web_server->getWebUsername(), this->web_server->getWebPassword()))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
StaticJsonDocument<512> doc;
|
||||
doc["hostname"] = this->iot->getNetworkConfig()->hostname;
|
||||
doc["ip_address"] = this->iot->getIp().toString();
|
||||
doc["mac_address"] = this->iot->getMac();
|
||||
doc["model"] = BOARD_MODEL;
|
||||
doc["mqtt_server"] = "Recovery";
|
||||
doc["mqtt_port"] = "Mode";
|
||||
doc["base_topic"] = "Recovery Mode";
|
||||
doc["mqtt_connected"] = "Recovery Mode";
|
||||
doc["software_version"] = "EMG-SAFE-1.0.0";
|
||||
doc["sdk_version"] = SDK_VESRION;
|
||||
doc["idf_version"] = IDF_VER;
|
||||
char buffer[512];
|
||||
serializeJson(doc, buffer);
|
||||
request->send(200, "application/json", buffer);
|
||||
}
|
||||
|
||||
void ESPMegaRecovery::configHandler(AsyncWebServerRequest *request){
|
||||
// Say Not Available in Recovery Mode
|
||||
// Wait 3s then redirect to /
|
||||
request->send(500, "text/html", "<h1>RECOVERY MODE</h1><p>Configuration is not available in recovery mode</p><script>setTimeout(function(){window.location.href = '/';}, 1500);</script>");
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
#include <FRAM.h>
|
||||
#include <ESPMegaWebServer.hpp>
|
||||
#include <esp_log.h>
|
||||
/**
|
||||
* @brief Recovery mode for ESPMega
|
||||
* Recovery mode is a mode that is entered when the device is in a bootloop
|
||||
* In this mode, the device will block all other tasks and wait for a new firmware to be uploaded over the air
|
||||
* To exit recovery mode, the device must be restarted or a new firmware must be uploaded
|
||||
* This is useful when the device is in a bootloop and the user can't access the device to upload a new firmware (ota bricking)
|
||||
* The device is considered to be in a bootloop when it is restarted before RECOVERY_WATCHDOG_TIMEOUT seconds for 5 consecutive times
|
||||
* The timer is started when the device's initialization is complete (enter loop)
|
||||
*/
|
||||
|
||||
#define RECOVERY_WATCHDOG_TIMEOUT 15 // The time in seconds to wait for a restart before considering it a bootloop
|
||||
|
||||
class ESPMegaRecovery {
|
||||
public:
|
||||
ESPMegaRecovery();
|
||||
void begin();
|
||||
void loop();
|
||||
void enterRecoveryMode(); // Enter recovery mode, Block all other tasks, start OTA server
|
||||
void bindFRAM(FRAM* fram, uint32_t address); // Bind the FRAM block to store the bootloop counter
|
||||
uint8_t getBootloopCounter(); // Get the bootloop counter
|
||||
void inclementBootloopCounter(); // Increment the bootloop counter
|
||||
void resetBootloopCounter(); // Reset the bootloop counter
|
||||
bool isRecoveryMode(); // Check if the device is in recovery mode
|
||||
private:
|
||||
void getDeviceInfoHandler(AsyncWebServerRequest *request);
|
||||
void configHandler(AsyncWebServerRequest *request);
|
||||
FRAM* fram;
|
||||
uint32_t fram_address;
|
||||
uint8_t bootloop_counter;
|
||||
ESPMegaIoT* iot;
|
||||
ESPMegaWebServer* web_server;
|
||||
bool recovery_mode;
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
// ESPMega TCP API
|
||||
#include <Arduino.h>
|
||||
|
||||
class ESPMegaTCP
|
||||
{
|
||||
public:
|
||||
void begin();
|
||||
void loop();
|
||||
};
|
|
@ -1,489 +0,0 @@
|
|||
/**
|
||||
* @file ESPMegaWebServer.cpp
|
||||
* @brief Implementation file for the ESPMegaWebServer class.
|
||||
*
|
||||
* This file contains the implementation of the ESPMegaWebServer class, which is responsible for handling web server functionality for the ESPMegaPRO firmware.
|
||||
* The ESPMegaWebServer class provides methods for starting the web server, handling HTTP requests, and managing credentials and configurations.
|
||||
*/
|
||||
#include <ESPMegaWebServer.hpp>
|
||||
|
||||
/**
|
||||
* @brief Construct a new ESPMegaWebServer::ESPMegaWebServer objecy
|
||||
*
|
||||
* @note Although you can instantiate this class directly, it is recommended to use the ESPMegaPRO.webServer object instead.
|
||||
*
|
||||
* @param port The TCP port to listen on
|
||||
* @param iot A pointer to the ESPMegaIoT object
|
||||
*/
|
||||
ESPMegaWebServer::ESPMegaWebServer(uint16_t port, ESPMegaIoT *iot)
|
||||
{
|
||||
this->port = port;
|
||||
this->iot = iot;
|
||||
this->server = new AsyncWebServer(port);
|
||||
this->saveConfigHandler = new AsyncCallbackJsonWebHandler("/save_config", std::bind(&ESPMegaWebServer::saveConfigJSONHandler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy the ESPMegaWebServer::ESPMegaWebServer object
|
||||
*/
|
||||
ESPMegaWebServer::~ESPMegaWebServer()
|
||||
{
|
||||
delete this->server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start the web server
|
||||
*
|
||||
* This method starts the web server and registers the necessary handlers.
|
||||
*
|
||||
* @note This method should be called after the ESPMegaIoT object has been initialized.
|
||||
* @note This method is automatically called if you use ESPMegaPRO::enableWebServer()
|
||||
*/
|
||||
void ESPMegaWebServer::begin()
|
||||
{
|
||||
this->loadCredentialsFromFRAM();
|
||||
this->server->begin();
|
||||
auto bindedDashboardHandler = std::bind(&ESPMegaWebServer::dashboardHandler, this, std::placeholders::_1);
|
||||
this->server->on("/", HTTP_GET, bindedDashboardHandler);
|
||||
auto bindedConfigHandler = std::bind(&ESPMegaWebServer::configHandler, this, std::placeholders::_1);
|
||||
this->server->on("/config", HTTP_GET, bindedConfigHandler);
|
||||
this->server->addHandler(saveConfigHandler);
|
||||
auto bindedOtaRequestHandler = std::bind(&ESPMegaWebServer::otaRequestHandler, this, std::placeholders::_1);
|
||||
auto bindedOtaUploadHandler = std::bind(&ESPMegaWebServer::otaUploadHandler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6);
|
||||
this->server->on("/ota_update", HTTP_POST, bindedOtaRequestHandler, bindedOtaUploadHandler);
|
||||
this->server->on("/reboot", HTTP_GET, std::bind(&ESPMegaWebServer::rebootHandler, this, std::placeholders::_1));
|
||||
auto bindedGetConfigHandler = std::bind(&ESPMegaWebServer::getConfigHandler, this, std::placeholders::_1);
|
||||
this->server->on("/get_config", HTTP_GET, bindedGetConfigHandler);
|
||||
auto bindedGetDeviceInfoHandler = std::bind(&ESPMegaWebServer::getDeviceInfoHandler, this, std::placeholders::_1);
|
||||
this->server->on("/get_device_info", HTTP_GET, bindedGetDeviceInfoHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The loop function for the web server
|
||||
*
|
||||
* @note This method is not used by the ESPMegaWebServer class as of now.
|
||||
*/
|
||||
void ESPMegaWebServer::loop()
|
||||
{
|
||||
// AsyncWebServer doesn't have a loop function
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bind the FRAM object to the web server
|
||||
*
|
||||
* This method binds the FRAM object to the web server so that the web server can read and write credentials to the FRAM.
|
||||
*
|
||||
* @note The FRAM object must be bound to the web server before calling ESPMegaWebServer::begin()
|
||||
* @note This class takes 64 bytes of FRAM starting from address 301, however address 301-400 is reserved for it.
|
||||
*
|
||||
* @param fram A pointer to the FRAM object
|
||||
*/
|
||||
void ESPMegaWebServer::bindFRAM(FRAM *fram)
|
||||
{
|
||||
this->fram = fram;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load web username and password from FRAM
|
||||
*
|
||||
* This method loads the web server credentials from the FRAM.
|
||||
*
|
||||
* @note This method is automatically called by ESPMegaWebServer::begin()
|
||||
*/
|
||||
void ESPMegaWebServer::loadCredentialsFromFRAM()
|
||||
{
|
||||
this->fram->read(301, (uint8_t *)this->webUsername, 32);
|
||||
this->fram->read(333, (uint8_t *)this->webPassword, 32);
|
||||
// Verify if credentials are valid
|
||||
// A valid username and password is null terminated
|
||||
// Scan for null terminator
|
||||
bool validUsername = false;
|
||||
bool validPassword = false;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if (this->webUsername[i] == '\0')
|
||||
{
|
||||
validUsername = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if (this->webPassword[i] == '\0')
|
||||
{
|
||||
validPassword = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validUsername || !validPassword)
|
||||
{
|
||||
this->resetCredentials();
|
||||
return;
|
||||
}
|
||||
// A valid username and password is at least 1 character long
|
||||
if (strlen(this->webUsername) == 0 || strlen(this->webPassword) == 0)
|
||||
{
|
||||
this->resetCredentials();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save web username and password to FRAM
|
||||
*
|
||||
* This method saves the web server credentials to the FRAM.
|
||||
*/
|
||||
void ESPMegaWebServer::saveCredentialsToFRAM()
|
||||
{
|
||||
this->fram->write(301, (uint8_t *)this->webUsername, 32);
|
||||
this->fram->write(333, (uint8_t *)this->webPassword, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset web username and password to default
|
||||
*
|
||||
* This method resets the web server credentials to the default username and password.
|
||||
*
|
||||
* @note The default username and password is both "admin"
|
||||
*/
|
||||
void ESPMegaWebServer::resetCredentials()
|
||||
{
|
||||
// The default username and password is "admin"
|
||||
strcpy(this->webUsername, "admin");
|
||||
strcpy(this->webPassword, "admin");
|
||||
this->saveCredentialsToFRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the web username
|
||||
*
|
||||
* @warning The returned pointer should not be freed or modified.
|
||||
*
|
||||
* @return The web username
|
||||
*/
|
||||
char *ESPMegaWebServer::getWebUsername()
|
||||
{
|
||||
return this->webUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the web password
|
||||
*
|
||||
* @warning The returned pointer should not be freed or modified.
|
||||
*
|
||||
* @return The web password
|
||||
*/
|
||||
char *ESPMegaWebServer::getWebPassword()
|
||||
{
|
||||
return this->webPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the web username
|
||||
*
|
||||
* @param username The new web username
|
||||
*/
|
||||
void ESPMegaWebServer::setWebUsername(const char *username)
|
||||
{
|
||||
strcpy(this->webUsername, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the web password
|
||||
*
|
||||
* @param password The new web password
|
||||
*/
|
||||
void ESPMegaWebServer::setWebPassword(const char *password)
|
||||
{
|
||||
strcpy(this->webPassword, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle HTTP requests to the dashboard (/) page
|
||||
*
|
||||
* @param request The AsyncWebServerRequest object
|
||||
*/
|
||||
void ESPMegaWebServer::dashboardHandler(AsyncWebServerRequest *request)
|
||||
{
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
request->send_P(200, "text/html", ota_html);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle HTTP requests to the config (/config) page
|
||||
*
|
||||
* @param request The AsyncWebServerRequest object
|
||||
*/
|
||||
void ESPMegaWebServer::configHandler(AsyncWebServerRequest *request)
|
||||
{
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
request->send_P(200, "text/html", config_html);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle HTTP requests to the OTA update (/ota_update) page
|
||||
*
|
||||
* @param request The AsyncWebServerRequest object
|
||||
*/
|
||||
void ESPMegaWebServer::otaRequestHandler(AsyncWebServerRequest *request)
|
||||
{
|
||||
// Prepare to receive firmware
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
// Restart ESPMega
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle HTTP upload session to the OTA update (/ota_update) page
|
||||
*
|
||||
* @param request The AsyncWebServerRequest object
|
||||
* @param filename The filename of the firmware
|
||||
* @param index The index of the firmware
|
||||
* @param data The firmware data
|
||||
* @param len The length of the firmware data
|
||||
* @param final Whether this is the final chunk of firmware
|
||||
*/
|
||||
void ESPMegaWebServer::otaUploadHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||||
{
|
||||
// Receive firmware
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
if (index == 0)
|
||||
{
|
||||
ESP_LOGI("ESPMegaWebServer", "OTA Update Start");
|
||||
if (!Update.begin(UPDATE_SIZE_UNKNOWN))
|
||||
{ // start with max available size
|
||||
ESP_LOGE("ESPMegaWebServer", "OTA Update Start Error");
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
if (Update.write(data, len) != len)
|
||||
{
|
||||
ESP_LOGE("ESPMegaWebServer", "OTA Update Write Error");
|
||||
Update.printError(Serial);
|
||||
} else {
|
||||
ESP_LOGI("ESPMegaWebServer", "OTA Update Write Success: %uB", index + len);
|
||||
}
|
||||
if (final)
|
||||
{
|
||||
if (Update.end(true))
|
||||
{ // true to set the size to the current progress
|
||||
ESP_LOGI("ESPMegaWebServer", "OTA Update Success: %uB", index + len);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE("ESPMegaWebServer", "OTA Update End Error");
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle JSON POST requests to the save_config (/save_config) page
|
||||
*
|
||||
* @param request The AsyncWebServerRequest object
|
||||
* @param json The JSON object representing the request body
|
||||
*/
|
||||
void ESPMegaWebServer::saveConfigJSONHandler(AsyncWebServerRequest *request, JsonVariant &json)
|
||||
{
|
||||
/**
|
||||
* Request POST body should be a JSON object
|
||||
* containing the following fields:
|
||||
* ip_address: String, the IP address of the device
|
||||
* netmask: String, the netmask of the device
|
||||
* gateway: String, the gateway of the device
|
||||
* dns: String, the DNS of the device
|
||||
* hostname: String, the hostname of the device
|
||||
* bms_ip: String, the IP address of the MQTT broker
|
||||
* bms_port: int, the port of the MQTT broker
|
||||
* bms_useauth: Boolean, true if the MQTT broker requires authentication
|
||||
* bms_username: String, the username of the MQTT broker
|
||||
* bms_password: String, the password of the MQTT broker
|
||||
* bms_endpoint: String, the base topic of the MQTT broker
|
||||
* web_username: String, the username of the web server
|
||||
* web_password: String, the password of the web server
|
||||
*/
|
||||
ESP_LOGD("ESPMegaWebServer", "Saving config");
|
||||
JsonObject root = json.as<JsonObject>();
|
||||
// Network Config
|
||||
NetworkConfig networkConfig;
|
||||
networkConfig.useStaticIp = true;
|
||||
networkConfig.useWifi = false;
|
||||
IPAddress ip;
|
||||
ESP_LOGD("ESPMegaWebServer", "Checking IP Address");
|
||||
if (!ip.fromString(root["ip_address"].as<String>()))
|
||||
{
|
||||
ESP_LOGE("ESPMegaWebServer", "Invalid Config IP Address");
|
||||
request->send(400, "text/plain", "Invalid IP Address");
|
||||
return;
|
||||
}
|
||||
networkConfig.ip = ip;
|
||||
ESP_LOGD("ESPMegaWebServer", "Checking Netmask");
|
||||
if (!ip.fromString(root["netmask"].as<String>()))
|
||||
{
|
||||
ESP_LOGE("ESPMegaWebServer", "Invalid Config Netmask");
|
||||
request->send(400, "text/plain", "Invalid Netmask");
|
||||
return;
|
||||
}
|
||||
networkConfig.subnet = ip;
|
||||
ESP_LOGD("ESPMegaWebServer", "Checking Gateway");
|
||||
if (!ip.fromString(root["gateway"].as<String>()))
|
||||
{
|
||||
ESP_LOGE("ESPMegaWebServer", "Invalid Config Gateway");
|
||||
request->send(400, "text/plain", "Invalid Gateway");
|
||||
return;
|
||||
}
|
||||
networkConfig.gateway = ip;
|
||||
ESP_LOGD("ESPMegaWebServer", "Checking DNS");
|
||||
if (!ip.fromString(root["dns"].as<String>()))
|
||||
{
|
||||
ESP_LOGE("ESPMegaWebServer", "Invalid Config DNS");
|
||||
request->send(400, "text/plain", "Invalid DNS");
|
||||
return;
|
||||
}
|
||||
networkConfig.dns1 = ip;
|
||||
ESP_LOGD("ESPMegaWebServer", "Setting Hostname");
|
||||
strcpy(networkConfig.hostname, root["hostname"].as<String>().c_str());
|
||||
// MQTT Config
|
||||
MqttConfig mqttConfig;
|
||||
ESP_LOGD("ESPMegaWebServer", "Setting MQTT Server");
|
||||
strcpy(mqttConfig.mqtt_server, root["bms_ip"].as<String>().c_str());
|
||||
ESP_LOGD("ESPMegaWebServer", "Checking MQTT Port");
|
||||
uint16_t mqttPort = root["bms_port"].as<int>();
|
||||
if (mqttConfig.mqtt_port <= 0 || mqttConfig.mqtt_port > 65535)
|
||||
{
|
||||
ESP_LOGE("ESPMegaWebServer", "Invalid Config MQTT Port");
|
||||
request->send(400, "text/plain", "Invalid MQTT Port");
|
||||
return;
|
||||
}
|
||||
mqttConfig.mqtt_port = mqttPort;
|
||||
ESP_LOGD("ESPMegaWebServer", "Checking MQTT Use Auth");
|
||||
mqttConfig.mqtt_useauth = root["bms_useauth"].as<bool>();
|
||||
ESP_LOGD("ESPMegaWebServer", "Setting MQTT Username");
|
||||
strcpy(mqttConfig.mqtt_user, root["bms_username"].as<String>().c_str());
|
||||
ESP_LOGD("ESPMegaWebServer", "Setting MQTT Password");
|
||||
strcpy(mqttConfig.mqtt_password, root["bms_password"].as<String>().c_str());
|
||||
ESP_LOGD("ESPMegaWebServer", "Setting MQTT Base Topic");
|
||||
strcpy(mqttConfig.base_topic, root["bms_endpoint"].as<String>().c_str());
|
||||
// Web Server Config
|
||||
ESP_LOGD("ESPMegaWebServer", "Setting Web Username");
|
||||
strcpy(this->webUsername, root["web_username"].as<String>().c_str());
|
||||
ESP_LOGD("ESPMegaWebServer", "Setting Web Password");
|
||||
strcpy(this->webPassword, root["web_password"].as<String>().c_str());
|
||||
// Commit changes to FRAM
|
||||
ESP_LOGD("ESPMegaWebServer", "Committing Network Config to FRAM");
|
||||
this->iot->setNetworkConfig(networkConfig);
|
||||
this->iot->saveNetworkConfig();
|
||||
ESP_LOGD("ESPMegaWebServer", "Committing MQTT Config to FRAM");
|
||||
this->iot->setMqttConfig(mqttConfig);
|
||||
this->iot->saveMqttConfig();
|
||||
ESP_LOGD("ESPMegaWebServer", "Committing Web Server Config to FRAM");
|
||||
this->saveCredentialsToFRAM();
|
||||
ESP_LOGD("ESPMegaWebServer", "Config saved");
|
||||
// Send response
|
||||
request->send(200, "text/plain", "OK");
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the AsyncWebServer object
|
||||
*
|
||||
* @return The AsyncWebServer object
|
||||
*/
|
||||
AsyncWebServer *ESPMegaWebServer::getServer() {
|
||||
return this->server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request authentication from the client
|
||||
*
|
||||
* This method requests authentication from the client.
|
||||
*
|
||||
* @param request The AsyncWebServerRequest object
|
||||
*/
|
||||
bool ESPMegaWebServer::checkAuthentication(AsyncWebServerRequest *request) {
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
request->requestAuthentication();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle HTTP requests to the reboot (/reboot) page
|
||||
*
|
||||
* @param request The AsyncWebServerRequest object
|
||||
*/
|
||||
void ESPMegaWebServer::rebootHandler(AsyncWebServerRequest *request)
|
||||
{
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
request->send(200, "text/plain", "Rebooting ESPMega PRO...");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void ESPMegaWebServer::getConfigHandler(AsyncWebServerRequest *request) {
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
StaticJsonDocument<512> doc;
|
||||
NetworkConfig *networkConfig = this->iot->getNetworkConfig();
|
||||
MqttConfig *mqttConfig = this->iot->getMqttConfig();
|
||||
doc["ip_address"] = networkConfig->ip.toString();
|
||||
doc["netmask"] = networkConfig->subnet.toString();
|
||||
doc["gateway"] = networkConfig->gateway.toString();
|
||||
doc["dns"] = networkConfig->dns1.toString();
|
||||
doc["hostname"] = networkConfig->hostname;
|
||||
doc["bms_ip"] = mqttConfig->mqtt_server;
|
||||
doc["bms_port"] = mqttConfig->mqtt_port;
|
||||
doc["bms_useauth"] = mqttConfig->mqtt_useauth;
|
||||
doc["bms_username"] = mqttConfig->mqtt_user;
|
||||
doc["bms_password"] = mqttConfig->mqtt_password;
|
||||
doc["bms_endpoint"] = mqttConfig->base_topic;
|
||||
doc["web_username"] = this->webUsername;
|
||||
doc["web_password"] = this->webPassword;
|
||||
char buffer[512];
|
||||
serializeJson(doc, buffer);
|
||||
request->send(200, "application/json", buffer);
|
||||
}
|
||||
|
||||
void ESPMegaWebServer::getDeviceInfoHandler(AsyncWebServerRequest *request) {
|
||||
if (!request->authenticate(this->webUsername, this->webPassword))
|
||||
{
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
StaticJsonDocument<512> doc;
|
||||
doc["hostname"] = this->iot->getNetworkConfig()->hostname;
|
||||
doc["ip_address"] = this->iot->getIp().toString();
|
||||
doc["mac_address"] = this->iot->getMac();
|
||||
doc["model"] = BOARD_MODEL;
|
||||
doc["mqtt_server"] = this->iot->getMqttConfig()->mqtt_server;
|
||||
doc["mqtt_port"] = this->iot->getMqttConfig()->mqtt_port;
|
||||
doc["base_topic"] = this->iot->getMqttConfig()->base_topic;
|
||||
doc["mqtt_connected"] = this->iot->mqttConnected() ? "Connected" : "Standalone";
|
||||
doc["software_version"] = SW_VERSION;
|
||||
doc["sdk_version"] = SDK_VESRION;
|
||||
doc["idf_version"] = IDF_VER;
|
||||
doc["uptime"] = esp_timer_get_time() / 1000000; // Uptime in seconds
|
||||
char buffer[512];
|
||||
serializeJson(doc, buffer);
|
||||
request->send(200, "application/json", buffer);
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
#pragma once
|
||||
#include <FS.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <Update.h>
|
||||
#include <FRAM.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <all_html.h>
|
||||
#include <ESPMegaCommon.hpp>
|
||||
|
||||
/**
|
||||
* @brief Provides a web server for ESPMegaPRO
|
||||
*
|
||||
* This class provides a web server for ESPMegaPRO. It is used to configure the device and to update the firmware.
|
||||
* This class also allows to save the credentials to access the web server in the FRAM memory.
|
||||
* User can also add custom endpoints to the web server.
|
||||
*
|
||||
* This class use FRAM address 301-400
|
||||
*/
|
||||
class ESPMegaWebServer
|
||||
{
|
||||
public:
|
||||
ESPMegaWebServer(uint16_t port, ESPMegaIoT *iot);
|
||||
~ESPMegaWebServer();
|
||||
void begin();
|
||||
void loop();
|
||||
void resetCredentials();
|
||||
char* getWebUsername();
|
||||
char* getWebPassword();
|
||||
void setWebUsername(const char* username);
|
||||
void setWebPassword(const char* password);
|
||||
void bindFRAM(FRAM *fram);
|
||||
void loadCredentialsFromFRAM();
|
||||
void saveCredentialsToFRAM();
|
||||
AsyncWebServer* getServer();
|
||||
bool checkAuthentication(AsyncWebServerRequest *request);
|
||||
// Endpoints Handlers
|
||||
void dashboardHandler(AsyncWebServerRequest *request);
|
||||
void configHandler(AsyncWebServerRequest *request);
|
||||
AsyncCallbackJsonWebHandler *saveConfigHandler;
|
||||
void saveConfigJSONHandler(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void getConfigHandler(AsyncWebServerRequest *request);
|
||||
void getDeviceInfoHandler(AsyncWebServerRequest *request);
|
||||
void otaRequestHandler(AsyncWebServerRequest *request);
|
||||
void otaUploadHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||
void restAPIHandler(AsyncWebServerRequest *request);
|
||||
void rebootHandler(AsyncWebServerRequest *request);
|
||||
private:
|
||||
// FRAM
|
||||
FRAM *fram;
|
||||
// Credentials
|
||||
char webUsername[32];
|
||||
char webPassword[32];
|
||||
// Web Server
|
||||
AsyncWebServer *server;
|
||||
uint16_t port;
|
||||
// ESPMegaIoT
|
||||
ESPMegaIoT *iot;
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
/**
|
||||
* @brief The base class for all expansion cards
|
||||
*
|
||||
* In order to create a new expansion card, you should create a new class that inherits from this class.
|
||||
* Your class should implement the following functions:
|
||||
* - begin() : Initialize the card
|
||||
* - loop() : A function that is called in the main loop
|
||||
* - getType() : Get the type of the card, The type should be a unique number between 0 and 255
|
||||
*
|
||||
* @warning This class is abstract and should not be instantiated directly.
|
||||
*/
|
||||
class ExpansionCard {
|
||||
public:
|
||||
// Instantiate the card with the specified address
|
||||
ExpansionCard() {}
|
||||
virtual bool begin();
|
||||
// Preform a loop to refresh the input buffers
|
||||
virtual void loop();
|
||||
// Get the card type
|
||||
virtual uint8_t getType();
|
||||
};
|
|
@ -1,67 +0,0 @@
|
|||
#include <IRBlaster.hpp>
|
||||
|
||||
/**
|
||||
* @brief Destroy the IRBlaster object
|
||||
*/
|
||||
IRBlaster::~IRBlaster()
|
||||
{
|
||||
ESP_ERROR_CHECK(rmt_driver_uninstall(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an IR signal
|
||||
*
|
||||
* @param data The microseconds timing array produced by IRReceiver
|
||||
* @param size The number of elements in the array
|
||||
*/
|
||||
void IRBlaster::send(const uint16_t *data, const size_t size)
|
||||
{
|
||||
// Send a raw IR signal
|
||||
rmt_item32_t *items = new rmt_item32_t[size / 2 + size % 2];
|
||||
// data is in microseconds, we need to convert it to ticks
|
||||
// If the number of elements is odd, we need to add a 0 at the end
|
||||
for (size_t i = 0, j = 0; i < size; i += 2, j++)
|
||||
{
|
||||
items[j].level0 = 1;
|
||||
items[j].duration0 = data[i];
|
||||
items[j].level1 = 0;
|
||||
if (i + 1 < size)
|
||||
{
|
||||
items[j].duration1 = data[i+1];
|
||||
} else {
|
||||
items[j].duration1 = 0;
|
||||
}
|
||||
}
|
||||
ESP_ERROR_CHECK(rmt_write_items(channel, items, size / 2 + size % 2, false));
|
||||
delete[] items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a new IRBlaster object
|
||||
*
|
||||
* @param pin The pin to use for IR transmission
|
||||
* @param channel The RMT channel to use
|
||||
*/
|
||||
|
||||
IRBlaster::IRBlaster(const uint8_t pin, rmt_channel_t channel)
|
||||
{
|
||||
this->channel = channel;
|
||||
gpio_num_t gpio = gpio_num_t(pin);
|
||||
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, channel);
|
||||
config.clk_div = 80;
|
||||
config.tx_config.carrier_en = true;
|
||||
config.tx_config.carrier_freq_hz = 38000;
|
||||
ESP_ERROR_CHECK(rmt_config(&config));
|
||||
ESP_ERROR_CHECK(rmt_driver_install(channel, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a new IRBlaster object
|
||||
* @note This constructor uses RMT_CHANNEL_0
|
||||
*
|
||||
* @param pin The pin to use for IR transmission
|
||||
*/
|
||||
IRBlaster::IRBlaster(const uint8_t pin)
|
||||
{
|
||||
IRBlaster(pin, RMT_CHANNEL_0);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
#include <driver/rmt.h>
|
||||
|
||||
/**
|
||||
* @brief Class for sending IR signals
|
||||
*
|
||||
* @warning This class takes one of the RMT channels, The ESP32 has 8 RMT channels, so you can use 7 IRBlaster objects at the same time (or 6 if you use the IRReceiver class)
|
||||
*/
|
||||
class IRBlaster
|
||||
{
|
||||
public:
|
||||
IRBlaster(const uint8_t pin, rmt_channel_t channel);
|
||||
IRBlaster(const uint8_t pin);
|
||||
~IRBlaster();
|
||||
void send(const uint16_t *data, const size_t size);
|
||||
private:
|
||||
rmt_channel_t channel;
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
#include <IRReceiver.hpp>
|
||||
#include <functional>
|
||||
|
||||
volatile unsigned int IRReceiver::irBufferPtr = 0;
|
||||
volatile unsigned int IRReceiver::irBuffer[1000];
|
||||
uint8_t IRReceiver::pin;
|
||||
|
||||
void IRReceiver::begin(uint8_t pin) {
|
||||
IRReceiver::pin = pin;
|
||||
IRReceiver::irBufferPtr = 0;
|
||||
}
|
||||
|
||||
void IRReceiver::start_long_receive() {
|
||||
irBufferPtr = 0;
|
||||
attachInterrupt(digitalPinToInterrupt(pin), IRReceiver::handleInterrupt, CHANGE);
|
||||
}
|
||||
|
||||
ir_data_t IRReceiver::end_long_receive() {
|
||||
detachInterrupt(digitalPinToInterrupt(pin));
|
||||
// The data in the array is the time between each transition, so we need to convert it to the time of each transition
|
||||
ir_data_t data;
|
||||
const size_t size = irBufferPtr-1;
|
||||
data.data = (unsigned int*)calloc(size, sizeof(unsigned int));
|
||||
if (data.data == nullptr) {
|
||||
data.size = 0;
|
||||
return data;
|
||||
}
|
||||
// The data in the array is the time between each transition, so we need to convert it to the time of each transition
|
||||
for (size_t i = 1; i <= size; i++) {
|
||||
data.data[i-1] = irBuffer[i] - irBuffer[i-1];
|
||||
}
|
||||
//memcpy(data.data, (const unsigned int*)irBuffer, (size)*sizeof(unsigned int));
|
||||
Serial.println("Done");
|
||||
data.size = irBufferPtr;
|
||||
return data;
|
||||
}
|
||||
|
||||
void IRAM_ATTR IRReceiver::handleInterrupt() {
|
||||
if (irBufferPtr > 1000) return; //full buffer = bad data
|
||||
irBuffer[irBufferPtr++] = micros(); //just continually record the time-stamp of signal transitions
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
struct ir_data_t {
|
||||
unsigned int *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for receiving IR signals
|
||||
*
|
||||
* @warning Only one IRReceiver can be used at a time
|
||||
*/
|
||||
class IRReceiver {
|
||||
public:
|
||||
static void begin(uint8_t pin);
|
||||
static void start_long_receive();
|
||||
static ir_data_t end_long_receive();
|
||||
private:
|
||||
static uint8_t pin;
|
||||
static void IRAM_ATTR handleInterrupt();
|
||||
static volatile unsigned int irBuffer[1000]; //stores timings - volatile because changed by ISR
|
||||
static volatile unsigned int irBufferPtr; //Pointer thru irBuffer - volatile because changed by ISR
|
||||
};
|
|
@ -1,943 +0,0 @@
|
|||
#include <InternalDisplay.hpp>
|
||||
|
||||
/**
|
||||
* @brief Initialize the Internal Display
|
||||
*
|
||||
* @note You should not call this function directly, instead use ESPMegaIoT::enableInternalDisplay()
|
||||
*
|
||||
* @param iot The ESPMegaIoT object
|
||||
* @param getRtcTime A function that returns the current time
|
||||
*/
|
||||
void InternalDisplay::begin(ESPMegaIoT *iot, std::function<rtctime_t()> getRtcTime)
|
||||
{
|
||||
this->iot = iot;
|
||||
this->getRtcTime = getRtcTime;
|
||||
this->mqttConfig = this->iot->getMqttConfig();
|
||||
this->networkConfig = this->iot->getNetworkConfig();
|
||||
// Register callbacks
|
||||
auto bindedPageChangeCallback = std::bind(&InternalDisplay::handlePageChange, this, std::placeholders::_1);
|
||||
this->registerPageChangeCallback(bindedPageChangeCallback);
|
||||
auto bindedPayloadCallback = std::bind(&InternalDisplay::handlePayload, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->registerPayloadCallback(bindedPayloadCallback);
|
||||
auto bindedTouchCallback = std::bind(&InternalDisplay::handleTouch, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->registerTouchCallback(bindedTouchCallback);
|
||||
// Initialize the display
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->begin(115200);
|
||||
this->displayAdapter->setTimeout(100);
|
||||
this->displayAdapter->flush();
|
||||
this->giveSerialMutex();
|
||||
this->reset();
|
||||
delay(500);
|
||||
this->jumpToPage(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop of the Internal Display
|
||||
*
|
||||
* @note You should not call this function directly, instead use ESPMega::loop()
|
||||
*/
|
||||
void InternalDisplay::loop()
|
||||
{
|
||||
// Keep reading the Serial Adapter
|
||||
this->recieveSerialCommand();
|
||||
|
||||
// Refresh the top bar every 5 seconds
|
||||
static uint32_t lastTopBarRefresh;
|
||||
if (millis() - lastTopBarRefresh > INTERNAL_DISPLAY_TOP_BAR_REFRESH_INTERVAL)
|
||||
{
|
||||
this->updateStatusIcons(this->iot->networkConnected(), this->iot->mqttConnected());
|
||||
lastTopBarRefresh = millis();
|
||||
}
|
||||
// Refresh the clock every 10 seconds
|
||||
static uint32_t lastClockRefresh;
|
||||
if (millis() - lastClockRefresh > INTERNAL_DISPLAY_CLOCK_REFRESH_INTERVAL)
|
||||
{
|
||||
this->updateClock();
|
||||
lastClockRefresh = millis();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the display in response to a change in the input state
|
||||
*
|
||||
* @param pin The pin that changed
|
||||
* @param state The new state of the pin
|
||||
*/
|
||||
void InternalDisplay::handleInputStateChange(uint8_t pin, bool state)
|
||||
{
|
||||
// If the input card is binded to the display and the current page is the input page
|
||||
// then update the respective input component
|
||||
if (this->inputCard == nullptr || this->currentPage != INTERNAL_DISPLAY_INPUT_PAGE)
|
||||
return;
|
||||
// Update the input state
|
||||
this->setInputMarker(pin, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the display in response to a change in the PWM state
|
||||
*
|
||||
* @param pin The pin that changed
|
||||
* @param state The new state of the pin
|
||||
* @param value The new value of the pin
|
||||
*/
|
||||
void InternalDisplay::handlePwmStateChange(uint8_t pin, bool state, uint16_t value)
|
||||
{
|
||||
// If the output card is binded to the display and the current page is the output page
|
||||
// then update the respective output component
|
||||
if (this->outputCard == nullptr)
|
||||
return;
|
||||
if (this->currentPage == INTERNAL_DISPLAY_OUTPUT_PAGE)
|
||||
{
|
||||
// Update the output state
|
||||
this->setOutputBar(pin, value);
|
||||
this->setOutputStateColor(pin, state);
|
||||
}
|
||||
// Refresh the PWM Adjustment page if the current page is the PWM Adjustment page and the pin is the same
|
||||
else if (this->currentPage == INTERNAL_DISPLAY_PWM_ADJUSTMENT_PAGE && this->pmwAdjustmentPin == pin)
|
||||
{
|
||||
this->refreshPWMAdjustment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the display in response to page change
|
||||
*
|
||||
* @param page The new page
|
||||
*/
|
||||
void InternalDisplay::handlePageChange(uint8_t page)
|
||||
{
|
||||
// Refresh the page
|
||||
this->refreshPage(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the network config to the FRAM
|
||||
*/
|
||||
void InternalDisplay::saveNetworkConfig()
|
||||
{
|
||||
// The network config page have the following components:
|
||||
// ip_set -> a text input to set the ip address
|
||||
// netmask_set -> a text input to set the netmask
|
||||
// gateway_set -> a text input to set the gateway
|
||||
// dns_set -> a text input to set the dns
|
||||
// host_set -> a text input to set the hostname
|
||||
|
||||
// Save the ip address
|
||||
IPAddress ip;
|
||||
// 000.000.000.000, 16 characters, 3 dots, 3 characters per octet, 1 null terminator
|
||||
char ip_buffer[30];
|
||||
if (!this->getStringToBuffer("ip_set.txt", ip_buffer, 30))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the ip address
|
||||
if (!ip.fromString(ip_buffer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the netmask
|
||||
IPAddress netmask;
|
||||
if (!this->getStringToBuffer("netmask_set.txt", ip_buffer, 30))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Validate the netmask
|
||||
if (!netmask.fromString(ip_buffer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the gateway
|
||||
IPAddress gateway;
|
||||
if (!this->getStringToBuffer("gateway_set.txt", ip_buffer, 30))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Validate the gateway
|
||||
if (!gateway.fromString(ip_buffer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the dns
|
||||
IPAddress dns;
|
||||
if (!this->getStringToBuffer("dns_set.txt", ip_buffer, 30))
|
||||
return;
|
||||
// Validate the dns
|
||||
if (!dns.fromString(ip_buffer))
|
||||
return;
|
||||
|
||||
// Save the hostname
|
||||
if (!this->getStringToBuffer("host_set.txt", this->networkConfig->hostname, 32))
|
||||
return;
|
||||
|
||||
// Write the ip address, netmask and gateway to the network config
|
||||
this->networkConfig->ip = ip;
|
||||
this->networkConfig->subnet = netmask;
|
||||
this->networkConfig->gateway = gateway;
|
||||
this->networkConfig->dns1 = dns;
|
||||
this->networkConfig->dns2 = dns;
|
||||
this->networkConfig->useStaticIp = true;
|
||||
this->networkConfig->useWifi = false;
|
||||
this->networkConfig->wifiUseAuth = false;
|
||||
this->iot->saveNetworkConfig();
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the MQTT config to the FRAM
|
||||
*/
|
||||
void InternalDisplay::saveMQTTConfig()
|
||||
{
|
||||
// The MQTT config page have the following components:
|
||||
// mqttsv_set -> a text input to set the mqtt server
|
||||
// port_set -> a text input to set the mqtt port
|
||||
// use_auth -> a checkbox to enable/disable mqtt authentication
|
||||
// user_set -> a text input to set the mqtt username
|
||||
// password_set -> a text input to set the mqtt password
|
||||
// topic_set -> a text input to set the mqtt base topic
|
||||
|
||||
// Send the stop bytes to flush the serial buffer
|
||||
this->sendStopBytes();
|
||||
|
||||
// Save the mqtt server
|
||||
if (!this->getStringToBuffer("mqttsv_set.txt", this->mqttConfig->mqtt_server, 16))
|
||||
return;
|
||||
|
||||
// Save the mqtt port
|
||||
this->mqttConfig->mqtt_port = this->getNumber("port_set.val");
|
||||
|
||||
// Save the mqtt username
|
||||
if (!this->getStringToBuffer("user_set.txt", this->mqttConfig->mqtt_user, 16))
|
||||
return;
|
||||
|
||||
// Check if the password should be
|
||||
char password_temp[32];
|
||||
// Get the passwords
|
||||
if (!this->getStringToBuffer("password_set.txt", password_temp, 16))
|
||||
return;
|
||||
|
||||
// Check if the password should be updated
|
||||
if (strcmp(password_temp, PASSWORD_OBFUSCATION_STRING))
|
||||
{
|
||||
strcpy(this->mqttConfig->mqtt_password, password_temp);
|
||||
}
|
||||
|
||||
// Save the mqtt base topic
|
||||
if (!this->getStringToBuffer("topic_set.txt", this->mqttConfig->base_topic, 16))
|
||||
return;
|
||||
|
||||
// Save the mqtt use auth
|
||||
uint8_t use_auth = this->getNumber("use_auth.val");
|
||||
this->mqttConfig->mqtt_useauth = use_auth == 1 ? true : false;
|
||||
this->iot->saveMqttConfig();
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the status icons on the Internal Display's top bar
|
||||
*
|
||||
* @param networkStatus The network status
|
||||
* @param mqttStatus The MQTT status
|
||||
*/
|
||||
void InternalDisplay::updateStatusIcons(bool networkStatus, bool mqttStatus)
|
||||
{
|
||||
this->setNumber("server.pic", mqttStatus ? PIC_MQTT_CONNECTED : PIC_MQTT_DISCONNECTED);
|
||||
this->setNumber("lan.pic", networkStatus ? PIC_LAN_CONNECTED : PIC_LAN_DISCONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the clock on the Internal Display's top bar
|
||||
*/
|
||||
void InternalDisplay::updateClock()
|
||||
{
|
||||
rtctime_t time = this->getRtcTime();
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->printf("time.txt=\"%02d:%02d %s\"", time.hours % 12, time.minutes, time.hours / 12 ? "PM" : "AM");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the current page
|
||||
*/
|
||||
void InternalDisplay::refreshPage()
|
||||
{
|
||||
this->refreshPage(this->currentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the specified page
|
||||
*
|
||||
* @note The current page must be the specified page
|
||||
*
|
||||
* @param page The page to refresh
|
||||
*/
|
||||
void InternalDisplay::refreshPage(uint8_t page)
|
||||
{
|
||||
switch (page)
|
||||
{
|
||||
case INTERNAL_DISPLAY_DASHBOARD_PAGE:
|
||||
this->refreshDashboard();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_INPUT_PAGE:
|
||||
if (this->inputCard == nullptr)
|
||||
{
|
||||
this->jumpToPage(INTERNAL_DISPLAY_INPUT_NULL_PTR_PAGE);
|
||||
break;
|
||||
}
|
||||
this->refreshInput();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_OUTPUT_PAGE:
|
||||
if (this->outputCard == nullptr)
|
||||
{
|
||||
this->jumpToPage(INTERNAL_DISPLAY_OUTPUT_NULL_PTR_PAGE);
|
||||
break;
|
||||
}
|
||||
this->refreshOutput();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_AC_PAGE:
|
||||
if (this->climateCard == nullptr)
|
||||
{
|
||||
this->jumpToPage(INTERNAL_DISPLAY_CLIMATE_NULL_PTR_PAGE);
|
||||
break;
|
||||
}
|
||||
this->refreshAC();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_PWM_ADJUSTMENT_PAGE:
|
||||
this->refreshPWMAdjustment();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_NETWORK_CONFIG_PAGE:
|
||||
this->refreshNetworkConfig();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_MQTT_CONFIG_PAGE:
|
||||
this->refreshMQTTConfig();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the dashboard page
|
||||
*/
|
||||
void InternalDisplay::refreshDashboard()
|
||||
{
|
||||
// The dashboard have the following components:
|
||||
// 1. Hostname
|
||||
// 2. IP Address
|
||||
// 3. MQTT Server with port
|
||||
// 4. MQTT Connection status
|
||||
this->setString("hostname.txt", this->networkConfig->hostname);
|
||||
// Construct the IP address string
|
||||
static char ip_address[25];
|
||||
sprintf(ip_address, "%d.%d.%d.%d", this->networkConfig->ip[0], this->networkConfig->ip[1], this->networkConfig->ip[2], this->networkConfig->ip[3]);
|
||||
this->setString("ip_address.txt", ip_address);
|
||||
// Send the MQTT server and port
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->print("server_address.txt=\"");
|
||||
this->displayAdapter->print(this->mqttConfig->mqtt_server);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
// Send the MQTT connection status
|
||||
this->setString("status_txt.txt", this->iot->mqttConnected() ? MSG_MQTT_CONNECTED : MSG_MQTT_DISCONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the input page
|
||||
*/
|
||||
void InternalDisplay::refreshInput()
|
||||
{
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
this->setInputMarker(i, this->inputCard->digitalRead(i, false));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the output page
|
||||
*/
|
||||
void InternalDisplay::refreshOutput()
|
||||
{
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
this->setOutputBar(i, this->outputCard->getValue(i));
|
||||
this->setOutputStateColor(i, this->outputCard->getState(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the AC page
|
||||
*/
|
||||
void InternalDisplay::refreshAC()
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->print("temp.txt=\"");
|
||||
this->displayAdapter->print(this->climateCard->getTemperature());
|
||||
this->displayAdapter->print("C\"");
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("fan_auto.pic=");
|
||||
this->displayAdapter->print(this->climateCard->getFanSpeed() == AC_FAN_SPEED_AUTO ? PIC_AC_FAN_SPEED_AUTO_ACTIVE : PIC_AC_FAN_SPEED_AUTO_INACTIVE);
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("fan_low.pic=");
|
||||
this->displayAdapter->print(this->climateCard->getFanSpeed() == AC_FAN_SPEED_LOW ? PIC_AC_FAN_SPEED_LOW_ACTIVE : PIC_AC_FAN_SPEED_LOW_INACTIVE);
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("fan_mid.pic=");
|
||||
this->displayAdapter->print(this->climateCard->getFanSpeed() == AC_FAN_SPEED_MEDIUM ? PIC_AC_FAN_SPEED_MEDIUM_ACTIVE : PIC_AC_FAN_SPEED_MEDIUM_INACTIVE);
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("fan_high.pic=");
|
||||
this->displayAdapter->print(this->climateCard->getFanSpeed() == AC_FAN_SPEED_HIGH ? PIC_AC_FAN_SPEED_HIGH_ACTIVE : PIC_AC_FAN_SPEED_HIGH_INACTIVE);
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("mode_off.pic=");
|
||||
this->displayAdapter->print(this->climateCard->getMode() == AC_MODE_OFF ? PIC_AC_MODE_OFF_ACTIVE : PIC_AC_MODE_OFF_INACTIVE);
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("mode_fan.pic=");
|
||||
this->displayAdapter->print(this->climateCard->getMode() == AC_MODE_FAN_ONLY ? PIC_AC_MODE_FAN_ACTIVE : PIC_AC_MODE_FAN_INACTIVE);
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("mode_cool.pic=");
|
||||
this->displayAdapter->print(this->climateCard->getMode() == AC_MODE_COOL ? PIC_AC_MODE_COOL_ACTIVE : PIC_AC_MODE_COOL_INACTIVE);
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
if (this->climateCard->getSensorType() == AC_SENSOR_TYPE_DHT22)
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->print("roomtemp.txt=\"");
|
||||
this->displayAdapter->print(this->climateCard->getRoomTemperature());
|
||||
this->displayAdapter->print("C\"");
|
||||
this->sendStopBytes();
|
||||
this->displayAdapter->print("roomhumid.txt=\"");
|
||||
this->displayAdapter->print(this->climateCard->getHumidity());
|
||||
this->displayAdapter->print("%\"");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
else if (this->climateCard->getSensorType() == AC_SENSOR_TYPE_DS18B20)
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->print("roomtemp.txt=\"");
|
||||
this->displayAdapter->print(this->climateCard->getRoomTemperature());
|
||||
this->displayAdapter->print("C\"");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
this->setString("roomhumid.txt", "N/A");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setString("roomtemp.txt", "N/A");
|
||||
this->setString("roomhumid.txt", "N/A");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the PWM status output bar value (Fullness of the bar)
|
||||
*
|
||||
* @param pin The pin of the PWM
|
||||
* @param value The value of the PWM (0 - 4095)
|
||||
*/
|
||||
void InternalDisplay::setOutputBar(uint8_t pin, uint16_t value)
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
// Write the value to the output bar
|
||||
this->displayAdapter->print("j");
|
||||
this->displayAdapter->print(pin);
|
||||
this->displayAdapter->print(".val=");
|
||||
this->displayAdapter->print((int)(value * 100 / 4095));
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the PWM status output bar color to match the PWM state
|
||||
*
|
||||
* @param pin The pin of the PWM
|
||||
* @param state The state of the PWM
|
||||
*/
|
||||
void InternalDisplay::setOutputStateColor(uint8_t pin, bool state)
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->print("j");
|
||||
this->displayAdapter->print(pin);
|
||||
this->displayAdapter->print(".ppic=");
|
||||
this->displayAdapter->print(state ? PIC_PWM_BAR_ON : PIC_PWM_BAR_OFF);
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set Input Marker to match the input state
|
||||
*
|
||||
* @param pin The pin of the input
|
||||
* @param state The state of the input
|
||||
*/
|
||||
void InternalDisplay::setInputMarker(uint8_t pin, bool state)
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->print("I");
|
||||
this->displayAdapter->print(pin);
|
||||
this->displayAdapter->print(".val=");
|
||||
this->displayAdapter->print(state ? 1 : 0);
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Internal Display object
|
||||
*
|
||||
* @param displayAdapter The HardwareSerial object that is connected to the display
|
||||
*/
|
||||
InternalDisplay::InternalDisplay(HardwareSerial *displayAdapter) : ESPMegaDisplay(displayAdapter, 115200, 921600, 1, 3)
|
||||
{
|
||||
this->currentPage = INTERNAL_DISPLAY_DASHBOARD_PAGE;
|
||||
this->iot = nullptr;
|
||||
this->inputCard = nullptr;
|
||||
this->outputCard = nullptr;
|
||||
this->climateCard = nullptr;
|
||||
this->pmwAdjustmentPin = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the input card to be be shown on the input page
|
||||
*
|
||||
* @param inputCard The input card object to be shown
|
||||
*/
|
||||
void InternalDisplay::bindInputCard(DigitalInputCard *inputCard)
|
||||
{
|
||||
// Check if the input card is already binded
|
||||
// If it is, then unbind it first
|
||||
if (this->inputCard != nullptr)
|
||||
this->unbindInputCard();
|
||||
this->inputCard = inputCard;
|
||||
auto bindedInputStateChangeCallback =
|
||||
std::bind(&InternalDisplay::handleInputStateChange, this,
|
||||
std::placeholders::_1, std::placeholders::_2);
|
||||
this->bindedInputCardCallbackHandler =
|
||||
this->inputCard->registerCallback(bindedInputStateChangeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unbind the input card from the display
|
||||
*/
|
||||
void InternalDisplay::unbindInputCard()
|
||||
{
|
||||
if (this->inputCard == nullptr)
|
||||
return;
|
||||
this->inputCard->unregisterCallback(this->bindedInputCardCallbackHandler);
|
||||
this->inputCard = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the output card to be be shown on the output page
|
||||
*
|
||||
* @param outputCard The output card object to be shown
|
||||
*/
|
||||
void InternalDisplay::bindOutputCard(DigitalOutputCard *outputCard)
|
||||
{
|
||||
// Check if the output card is already binded
|
||||
// If it is, then unbind it first
|
||||
if (this->outputCard != nullptr)
|
||||
this->unbindOutputCard();
|
||||
this->outputCard = outputCard;
|
||||
auto bindedPwmStateChangeCallback = std::bind(&InternalDisplay::handlePwmStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->bindedOutputCardCallbackHandler =
|
||||
this->outputCard->registerChangeCallback(bindedPwmStateChangeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unbind the output card from the display
|
||||
*/
|
||||
void InternalDisplay::unbindOutputCard()
|
||||
{
|
||||
if (this->outputCard == nullptr)
|
||||
return;
|
||||
this->outputCard->unregisterChangeCallback(this->bindedOutputCardCallbackHandler);
|
||||
this->outputCard = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the climate card to be be shown on the AC page
|
||||
*
|
||||
* This assume that your ClimeateCard has the mode and fan speed names in the following order:
|
||||
* mode: [off, fan_only, cool]
|
||||
* fan_speed: [auto, low, medium, high]
|
||||
*
|
||||
* @param climateCard The climate card object to be shown
|
||||
*/
|
||||
void InternalDisplay::bindClimateCard(ClimateCard *climateCard)
|
||||
{
|
||||
// Check if the climate card is already binded
|
||||
// If it is, then unbind it first
|
||||
if (this->climateCard != nullptr)
|
||||
this->unbindClimateCard();
|
||||
this->climateCard = climateCard;
|
||||
auto bindedACStateChangeCallback = std::bind(&InternalDisplay::handleACStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->bindedClimateCardCallbackHandler =
|
||||
this->climateCard->registerChangeCallback(bindedACStateChangeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unbind the climate card from the display
|
||||
*/
|
||||
void InternalDisplay::unbindClimateCard()
|
||||
{
|
||||
if (this->climateCard == nullptr)
|
||||
return;
|
||||
this->climateCard->unregisterChangeCallback(this->bindedClimateCardCallbackHandler);
|
||||
this->climateCard = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the PWM Adjustment page
|
||||
*/
|
||||
void InternalDisplay::refreshPWMAdjustment()
|
||||
{
|
||||
// The PWM Adjustment page have the following components:
|
||||
// pwm_value -> a slider to adjust the PWM value
|
||||
// pwm_state -> a button to toggle the PWM state
|
||||
// pwm_id -> a text to show the PWM pin
|
||||
|
||||
// Refresh the PWM pin
|
||||
this->refreshPWMAdjustmentId();
|
||||
// Refresh the PWM value
|
||||
this->refreshPWMAdjustmentSlider();
|
||||
// Refresh the PWM state
|
||||
this->refreshPWMAdjustmentState();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the PWM pin id to the display on the PWM Adjustment page
|
||||
*/
|
||||
void InternalDisplay::refreshPWMAdjustmentId()
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
// Send the PWM pin
|
||||
this->displayAdapter->print("pwm_id.txt=\"P");
|
||||
this->displayAdapter->print(pmwAdjustmentPin);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the PWM value to the display on the PWM Adjustment page
|
||||
*/
|
||||
void InternalDisplay::refreshPWMAdjustmentSlider()
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
// Send the PWM value
|
||||
this->displayAdapter->print("pwm_value.val=");
|
||||
this->displayAdapter->print(this->outputCard->getValue(this->pmwAdjustmentPin));
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the PWM state to the display on the PWM Adjustment page
|
||||
*/
|
||||
void InternalDisplay::refreshPWMAdjustmentState()
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
// Send the PWM state
|
||||
this->displayAdapter->print("pwm_state.txt=\"");
|
||||
this->displayAdapter->print(this->outputCard->getState(this->pmwAdjustmentPin) ? MSG_PWM_ADJUSTMENT_STATE_ON : MSG_PWM_ADJUSTMENT_STATE_OFF);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the touch event on the display
|
||||
*
|
||||
* @param page The page that the touch event occured
|
||||
* @param component The component that the touch event occured
|
||||
* @param type The type of the touch event
|
||||
*/
|
||||
void InternalDisplay::handleTouch(uint8_t page, uint8_t component, uint8_t type)
|
||||
{
|
||||
// Switch based on the page
|
||||
switch (page)
|
||||
{
|
||||
case INTERNAL_DISPLAY_AC_PAGE:
|
||||
this->handleACTouch(type, component);
|
||||
break;
|
||||
case INTERNAL_DISPLAY_PWM_ADJUSTMENT_PAGE:
|
||||
this->handlePWMAdjustmentTouch(type, component);
|
||||
break;
|
||||
case INTERNAL_DISPLAY_NETWORK_CONFIG_PAGE:
|
||||
if (type == TOUCH_TYPE_RELEASE && component == 7)
|
||||
this->saveNetworkConfig();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_MQTT_CONFIG_PAGE:
|
||||
if (type == TOUCH_TYPE_RELEASE && component == 2)
|
||||
this->saveMQTTConfig();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the touch event on the AC page
|
||||
*
|
||||
* @param type The type of the touch event
|
||||
* @param component The component that the touch event occured
|
||||
*/
|
||||
void InternalDisplay::handleACTouch(uint8_t type, uint8_t component)
|
||||
{
|
||||
// b1 [component 18] -> inclement AC temperature by 1
|
||||
// b0 [component 17] -> declement AC temperature by 1
|
||||
// fan_auto [component 4] -> set the fan speed to auto
|
||||
// fan_low [component 5] -> set the fan speed to low
|
||||
// fan_med [component 6] -> set the fan speed to medium
|
||||
// fan_high [component 7] -> set the fan speed to high
|
||||
// mode_off [component 10] -> set the mode to off
|
||||
// mode_fan [component 9] -> set the mode to fan only
|
||||
// mode_cool [component 8] -> set the mode to cool
|
||||
|
||||
// For b0 and b1, if the type is not release then return
|
||||
// For other components, if the type is not press then return
|
||||
if ((component == 17 || component == 18) && type != TOUCH_TYPE_RELEASE)
|
||||
return;
|
||||
if ((component != 17 && component != 18) && type != TOUCH_TYPE_PRESS)
|
||||
return;
|
||||
|
||||
// Switch based on the component
|
||||
switch (component)
|
||||
{
|
||||
case 17:
|
||||
// Decrement the temperature
|
||||
this->climateCard->setTemperature(this->climateCard->getTemperature() - 1);
|
||||
break;
|
||||
case 18:
|
||||
// Increment the temperature
|
||||
this->climateCard->setTemperature(this->climateCard->getTemperature() + 1);
|
||||
break;
|
||||
case 4:
|
||||
// Set the fan speed to auto
|
||||
this->climateCard->setFanSpeed(AC_FAN_SPEED_AUTO);
|
||||
break;
|
||||
case 5:
|
||||
// Set the fan speed to low
|
||||
this->climateCard->setFanSpeed(AC_FAN_SPEED_LOW);
|
||||
break;
|
||||
case 6:
|
||||
// Set the fan speed to medium
|
||||
this->climateCard->setFanSpeed(AC_FAN_SPEED_MEDIUM);
|
||||
break;
|
||||
case 7:
|
||||
// Set the fan speed to high
|
||||
this->climateCard->setFanSpeed(AC_FAN_SPEED_HIGH);
|
||||
break;
|
||||
case 10:
|
||||
// Set the mode to off
|
||||
this->climateCard->setMode(AC_MODE_OFF);
|
||||
break;
|
||||
case 9:
|
||||
// Set the mode to fan only
|
||||
this->climateCard->setMode(AC_MODE_FAN_ONLY);
|
||||
break;
|
||||
case 8:
|
||||
// Set the mode to cool
|
||||
this->climateCard->setMode(AC_MODE_COOL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the touch event on the PWM Adjustment page
|
||||
*
|
||||
* @param type The type of the touch event
|
||||
* @param component The component that the touch event occured
|
||||
*/
|
||||
void InternalDisplay::handlePWMAdjustmentTouch(uint8_t type, uint8_t component)
|
||||
{
|
||||
// b0 [component 5] -> decrement the PWM id if its greater than 0, else set it to 15
|
||||
// b1 [component 6] -> increment the PWM id if its less than 15, else set it to 0
|
||||
// pwm_state [component 4] -> toggle the PWM state
|
||||
// pwm_value [component 1] -> set the PWM value based on the slider value
|
||||
// If the type is not release then return
|
||||
if (type != TOUCH_TYPE_RELEASE)
|
||||
return;
|
||||
uint16_t val = 0;
|
||||
// switch based on the component
|
||||
switch (component)
|
||||
{
|
||||
case 5:
|
||||
// Decrement the PWM id
|
||||
this->pmwAdjustmentPin = this->pmwAdjustmentPin > 0 ? this->pmwAdjustmentPin - 1 : 15;
|
||||
this->refreshPWMAdjustment();
|
||||
break;
|
||||
case 6:
|
||||
// Increment the PWM id
|
||||
this->pmwAdjustmentPin = this->pmwAdjustmentPin < 15 ? this->pmwAdjustmentPin + 1 : 0;
|
||||
this->refreshPWMAdjustment();
|
||||
break;
|
||||
case 4:
|
||||
// Toggle the PWM state
|
||||
this->outputCard->setState(this->pmwAdjustmentPin, !this->outputCard->getState(this->pmwAdjustmentPin));
|
||||
this->refreshPWMAdjustmentState();
|
||||
break;
|
||||
case 1:
|
||||
// Set the PWM value
|
||||
val = (uint16_t)this->getNumber("pwm_value.val");
|
||||
this->outputCard->setValue(this->pmwAdjustmentPin, val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the network config page
|
||||
*/
|
||||
void InternalDisplay::refreshNetworkConfig()
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
// The network config page have the following components:
|
||||
// ip_set -> a text input to set the ip address
|
||||
// netmask_set -> a text input to set the netmask
|
||||
// gateway_set -> a text input to set the gateway
|
||||
// dns_set -> a text input to set the dns
|
||||
// host_set -> a text input to set the hostname
|
||||
// Refresh the ip address
|
||||
this->displayAdapter->print("ip_set.txt=\"");
|
||||
this->sendIpToDisplay(this->networkConfig->ip);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the netmask
|
||||
this->displayAdapter->print("netmask_set.txt=\"");
|
||||
this->sendIpToDisplay(this->networkConfig->subnet);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the gateway
|
||||
this->displayAdapter->print("gateway_set.txt=\"");
|
||||
this->sendIpToDisplay(this->networkConfig->gateway);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the dns
|
||||
this->displayAdapter->print("dns_set.txt=\"");
|
||||
this->sendIpToDisplay(this->networkConfig->dns1);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the hostname
|
||||
this->displayAdapter->print("host_set.txt=\"");
|
||||
this->displayAdapter->print(this->networkConfig->hostname);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to display element on the mqtt config page
|
||||
*/
|
||||
void InternalDisplay::refreshMQTTConfig()
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
// The MQTT config page have the following components:
|
||||
// mqttsv_set -> a text input to set the mqtt server
|
||||
// port_set -> a text input to set the mqtt port
|
||||
// use_auth -> a checkbox to enable/disable mqtt authentication
|
||||
// user_set -> a text input to set the mqtt username
|
||||
// password_set -> a text input to set the mqtt password
|
||||
// topic_set -> a text input to set the mqtt base topic
|
||||
|
||||
// Refresh the mqtt server
|
||||
this->displayAdapter->print("mqttsv_set.txt=\"");
|
||||
this->displayAdapter->print(this->mqttConfig->mqtt_server);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the mqtt port
|
||||
this->displayAdapter->print("port_set.val=");
|
||||
this->displayAdapter->print(this->mqttConfig->mqtt_port);
|
||||
this->sendStopBytes();
|
||||
// Refresh the mqtt username
|
||||
this->displayAdapter->print("user_set.txt=\"");
|
||||
this->displayAdapter->print(this->mqttConfig->mqtt_user);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the mqtt password
|
||||
this->displayAdapter->print("password_set.txt=\"");
|
||||
this->displayAdapter->print(PASSWORD_OBFUSCATION_STRING);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the mqtt base topic
|
||||
this->displayAdapter->print("topic_set.txt=\"");
|
||||
this->displayAdapter->print(this->mqttConfig->base_topic);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
// Refresh the mqtt use auth
|
||||
this->displayAdapter->print("use_auth.val=");
|
||||
this->displayAdapter->print(this->mqttConfig->mqtt_useauth ? 1 : 0);
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write an ip address to the display
|
||||
*
|
||||
* @note This function only writes the ip address to the display, you need to send the prefix and suffix yourself
|
||||
* @warning This function does not take the serial mutex, you need to take it yourself
|
||||
*
|
||||
* @param ip The ip address to send
|
||||
*/
|
||||
void InternalDisplay::sendIpToDisplay(IPAddress ip)
|
||||
{
|
||||
// Send the ip address
|
||||
this->displayAdapter->print(ip[0]);
|
||||
this->displayAdapter->print(".");
|
||||
this->displayAdapter->print(ip[1]);
|
||||
this->displayAdapter->print(".");
|
||||
this->displayAdapter->print(ip[2]);
|
||||
this->displayAdapter->print(".");
|
||||
this->displayAdapter->print(ip[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the AC state change
|
||||
*
|
||||
* @note This function is registered as a callback to the ClimateCard
|
||||
*
|
||||
* @param mode The new mode
|
||||
* @param fan_speed The new fan speed
|
||||
* @param temperature The new temperature
|
||||
*/
|
||||
void InternalDisplay::handleACStateChange(uint8_t mode, uint8_t fan_speed, uint8_t temperature)
|
||||
{
|
||||
// If the climate card is binded to the display and the current page is the AC page
|
||||
// then update the respective AC component
|
||||
if (this->climateCard == nullptr || this->currentPage != INTERNAL_DISPLAY_AC_PAGE)
|
||||
return;
|
||||
this->sendStopBytes();
|
||||
// Update the AC state
|
||||
this->refreshAC();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the boot status text
|
||||
*
|
||||
* @param text The text to set
|
||||
*/
|
||||
void InternalDisplay::setBootStatus(const char *text)
|
||||
{
|
||||
if(!this->takeSerialMutex()) return;
|
||||
this->displayAdapter->print("boot_state.txt=\"");
|
||||
this->displayAdapter->print(text);
|
||||
this->displayAdapter->print("\"");
|
||||
this->sendStopBytes();
|
||||
this->giveSerialMutex();
|
||||
}
|
||||
|
||||
|
||||
void InternalDisplay::handlePayload(uint8_t type, uint8_t *payload, uint8_t length) {
|
||||
// If payload of type 0x92 is received
|
||||
// Send the display to page 1
|
||||
if (type == 0x92)
|
||||
{
|
||||
this->jumpToPage(1);
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
#pragma once
|
||||
#include <ESPMegaDisplay.hpp>
|
||||
#include <TimeStructure.hpp>
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
#include <ClimateCard.hpp>
|
||||
|
||||
// Password Obfuscation
|
||||
#define PASSWORD_OBFUSCATION_STRING "********"
|
||||
|
||||
// Page IDs
|
||||
#define INTERNAL_DISPLAY_BOOT_PAGE 0
|
||||
#define INTERNAL_DISPLAY_DASHBOARD_PAGE 1
|
||||
#define INTERNAL_DISPLAY_INPUT_PAGE 2
|
||||
#define INTERNAL_DISPLAY_OUTPUT_PAGE 3
|
||||
#define INTERNAL_DISPLAY_AC_PAGE 4
|
||||
#define INTERNAL_DISPLAY_PWM_ADJUSTMENT_PAGE 5
|
||||
#define INTERNAL_DISPLAY_NETWORK_CONFIG_PAGE 6
|
||||
#define INTERNAL_DISPLAY_OTA_PAGE 9
|
||||
#define INTERNAL_DISPLAY_CLIMATE_NULL_PTR_PAGE 10
|
||||
#define INTERNAL_DISPLAY_MQTT_CONFIG_PAGE 11
|
||||
#define INTERNAL_DISPLAY_INPUT_NULL_PTR_PAGE 12
|
||||
#define INTERNAL_DISPLAY_OUTPUT_NULL_PTR_PAGE 13
|
||||
|
||||
// Picture IDs
|
||||
#define PIC_LAN_DISCONNECTED 2
|
||||
#define PIC_LAN_CONNECTED 3
|
||||
#define PIC_MQTT_DISCONNECTED 4
|
||||
#define PIC_MQTT_CONNECTED 5
|
||||
#define PIC_PWM_BAR_ON 33
|
||||
#define PIC_PWM_BAR_OFF 48
|
||||
#define PIC_AC_MODE_OFF_ACTIVE 24
|
||||
#define PIC_AC_MODE_OFF_INACTIVE 25
|
||||
#define PIC_AC_MODE_FAN_ACTIVE 22
|
||||
#define PIC_AC_MODE_FAN_INACTIVE 23
|
||||
#define PIC_AC_MODE_COOL_ACTIVE 12
|
||||
#define PIC_AC_MODE_COOL_INACTIVE 13
|
||||
#define PIC_AC_FAN_SPEED_AUTO_ACTIVE 14
|
||||
#define PIC_AC_FAN_SPEED_AUTO_INACTIVE 15
|
||||
#define PIC_AC_FAN_SPEED_LOW_ACTIVE 18
|
||||
#define PIC_AC_FAN_SPEED_LOW_INACTIVE 19
|
||||
#define PIC_AC_FAN_SPEED_MEDIUM_ACTIVE 20
|
||||
#define PIC_AC_FAN_SPEED_MEDIUM_INACTIVE 21
|
||||
#define PIC_AC_FAN_SPEED_HIGH_ACTIVE 16
|
||||
#define PIC_AC_FAN_SPEED_HIGH_INACTIVE 17
|
||||
|
||||
// AC Fan Speeds and Mode Position Assumptions
|
||||
#define AC_FAN_SPEED_AUTO 0
|
||||
#define AC_FAN_SPEED_LOW 1
|
||||
#define AC_FAN_SPEED_MEDIUM 2
|
||||
#define AC_FAN_SPEED_HIGH 3
|
||||
#define AC_MODE_OFF 0
|
||||
#define AC_MODE_FAN_ONLY 1
|
||||
#define AC_MODE_COOL 2
|
||||
|
||||
// Messages
|
||||
#define MSG_MQTT_CONNECTED "BMS Managed"
|
||||
#define MSG_MQTT_DISCONNECTED "Standalone"
|
||||
#define MSG_PWM_ADJUSTMENT_STATE_ON "ON"
|
||||
#define MSG_PWM_ADJUSTMENT_STATE_OFF "OFF"
|
||||
|
||||
// Refresh Interval
|
||||
#define INTERNAL_DISPLAY_CLOCK_REFRESH_INTERVAL 15000
|
||||
#define INTERNAL_DISPLAY_TOP_BAR_REFRESH_INTERVAL 5000
|
||||
|
||||
// Touch Types
|
||||
#define TOUCH_TYPE_PRESS 0x01
|
||||
#define TOUCH_TYPE_RELEASE 0x0
|
||||
|
||||
/**
|
||||
* @brief The internal display of the ESPMegaPRO
|
||||
*
|
||||
* This is the display that is installed on some ESPMegaPRO Chassis. It is a 3.5" TFT LCD with a resistive touch screen.
|
||||
*
|
||||
* You can use this display to monitor the status of the ESPMegaPRO and also to control the various components of the
|
||||
* ESPMegaPRO.
|
||||
*
|
||||
* If you are using a custom display, you need to create a class that inherits from ESPMegaDisplay and implement the
|
||||
* methods in that class, you may refer to this class for reference.
|
||||
*
|
||||
* @note This class is automatically instantiated by the ESPMegaPRO and can be accessed via the `display` variable.
|
||||
*/
|
||||
class InternalDisplay : public ESPMegaDisplay {
|
||||
public:
|
||||
InternalDisplay(HardwareSerial *displayAdapter);
|
||||
void begin(ESPMegaIoT *iot, std::function<rtctime_t()> getRtcTime);
|
||||
void loop();
|
||||
void bindInputCard(DigitalInputCard *inputCard);
|
||||
void bindOutputCard(DigitalOutputCard *outputCard);
|
||||
void bindClimateCard(ClimateCard *climateCard);
|
||||
void unbindInputCard();
|
||||
void unbindOutputCard();
|
||||
void unbindClimateCard();
|
||||
|
||||
private:
|
||||
uint8_t bindedInputCardCallbackHandler;
|
||||
uint8_t bindedOutputCardCallbackHandler;
|
||||
uint8_t bindedClimateCardCallbackHandler;
|
||||
uint8_t bindedClimateCardSensorCallbackHandler;
|
||||
DigitalInputCard *inputCard;
|
||||
DigitalOutputCard *outputCard;
|
||||
ClimateCard *climateCard;
|
||||
void handleInputStateChange(uint8_t pin, bool state);
|
||||
void handlePwmStateChange(uint8_t pin, bool state, uint16_t value);
|
||||
void handlePageChange(uint8_t page);
|
||||
void handlePayload(uint8_t type, uint8_t *payload, uint8_t length);
|
||||
void setOutputBar(uint8_t pin, uint16_t value);
|
||||
void setOutputStateColor(uint8_t pin, bool state);
|
||||
void setInputMarker(uint8_t pin, bool state);
|
||||
void handleACStateChange(uint8_t mode, uint8_t fan_speed, uint8_t temperature);
|
||||
void saveNetworkConfig();
|
||||
void saveMQTTConfig();
|
||||
void updateStatusIcons(bool networkStatus, bool mqttStatus);
|
||||
void updateClock();
|
||||
void refreshPage();
|
||||
void refreshPage(uint8_t page);
|
||||
void refreshDashboard();
|
||||
void refreshInput();
|
||||
void refreshOutput();
|
||||
void refreshAC();
|
||||
void refreshPWMAdjustment();
|
||||
void refreshPWMAdjustmentSlider();
|
||||
void refreshPWMAdjustmentState();
|
||||
void refreshPWMAdjustmentId();
|
||||
void refreshNetworkConfig();
|
||||
void refreshMQTTConfig();
|
||||
void setBootStatus(const char* status);
|
||||
void sendIpToDisplay(IPAddress ip);
|
||||
uint8_t pmwAdjustmentPin;
|
||||
// Touch handlers
|
||||
void handleTouch(uint8_t page, uint8_t component, uint8_t type);
|
||||
void handlePWMAdjustmentTouch(uint8_t component, uint8_t type);
|
||||
void handleACTouch(uint8_t component, uint8_t type);
|
||||
MqttConfig *mqttConfig;
|
||||
NetworkConfig *networkConfig;
|
||||
// Pointers to various data
|
||||
ESPMegaIoT *iot;
|
||||
std::function<rtctime_t()> getRtcTime;
|
||||
};
|
|
@ -1,25 +0,0 @@
|
|||
#include <IoTComponent.hpp>
|
||||
|
||||
void IoTComponent::setMqttClient(PubSubClient *mqtt) {
|
||||
this->mqtt = mqtt;
|
||||
}
|
||||
|
||||
void IoTComponent::publishRelative(const char *topic, const char *payload) {
|
||||
static char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%02d/%s", base_topic, card_id, topic);
|
||||
ESP_LOGD("IoTComponent", "Publishing to %s : %s", absolute_topic, payload);
|
||||
mqtt->publish(absolute_topic, payload);
|
||||
mqtt->loop();
|
||||
}
|
||||
|
||||
void IoTComponent::subscribeRelative(const char *topic) {
|
||||
char absolute_topic[50];
|
||||
sprintf(absolute_topic, "%s/%02d/%s", base_topic, card_id, topic);
|
||||
ESP_LOGD("IoTComponent", "Subscribing to %s", absolute_topic);
|
||||
mqtt->subscribe(absolute_topic);
|
||||
mqtt->loop();
|
||||
}
|
||||
|
||||
void IoTComponent::loop() {
|
||||
// Placeholder, Do nothing
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <PubSubClient.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
/**
|
||||
* @brief The IoTComponent class is a base class that is used to interface with an expansion card through MQTT.
|
||||
*
|
||||
* In order to create a new IoTComponent, you should create a new class that inherits from this class.
|
||||
* Your class should implement the following functions:
|
||||
* - begin() : Initialize the component, record the card id, ExpansionCard object, the PubSubClient object and the base topic
|
||||
* - handleMqttMessage() : Handle the MQTT messages for the component
|
||||
* - publishReport() : Publish all the reports for the component
|
||||
* - getType() : Get the type of the component, This should return the underlying ExpansionCard type
|
||||
* - subscribe() : Subscribe to the MQTT topics used by the component
|
||||
* - loop() : A function that is called in the main loop
|
||||
*
|
||||
* Additionally, the inherited class will have access to these helper functions:
|
||||
* - publishRelative() : Publish a message to a topic relative to the base topic and the card id
|
||||
* - subscribeRelative() : Subscribe to a topic relative to the base topic and the card id
|
||||
*
|
||||
* @warning This class is abstract and should not be instantiated directly.
|
||||
*/
|
||||
class IoTComponent {
|
||||
public:
|
||||
virtual bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
virtual void handleMqttMessage(char *topic, char *payload);
|
||||
void setMqttClient(PubSubClient *mqtt);
|
||||
virtual void publishReport();
|
||||
virtual uint8_t getType();
|
||||
virtual void subscribe();
|
||||
void loop();
|
||||
protected:
|
||||
char *base_topic;
|
||||
void publishRelative(const char *topic, const char *payload);
|
||||
void subscribeRelative(const char *topic);
|
||||
PubSubClient *mqtt;
|
||||
uint8_t card_id;
|
||||
};
|
|
@ -1,219 +0,0 @@
|
|||
#include <RemoteVariable.hpp>
|
||||
|
||||
/**
|
||||
* @brief Construct a new Remote Variable object
|
||||
* This constructor does not take any arguments.
|
||||
* Please use the begin method to initialize the object.
|
||||
*/
|
||||
RemoteVariable::RemoteVariable() {
|
||||
this->topic = nullptr;
|
||||
this->value = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy the Remote Variable object
|
||||
* This destructor does not take any arguments.
|
||||
* It will free the memory allocated for the topic and value.
|
||||
*/
|
||||
RemoteVariable::~RemoteVariable() {
|
||||
free(this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the RemoteVariable object with value request
|
||||
*
|
||||
* This method is used to initialize the RemoteVariable object.
|
||||
*
|
||||
* @param size The maximum size of the variable in bytes
|
||||
* @param topic The topic that the variable is published to
|
||||
* @param iot The ESPMegaIoT object
|
||||
* @param useValueRequest Whether to use value request
|
||||
* @param valueRequestTopic The topic to request value
|
||||
*
|
||||
* @note Because the value is null terminated, the size should be the desired text length + 1
|
||||
* @warning This use dynamic memory allocation, so it is important to call the destructor when the object is no longer needed
|
||||
*/
|
||||
void RemoteVariable::begin(size_t size, const char* topic, ESPMegaIoT* iot, bool useValueRequest, const char* valueRequestTopic) {
|
||||
this->iot = iot;
|
||||
this->size = size;
|
||||
this->topic = topic;
|
||||
this->useValueRequest = useValueRequest;
|
||||
this->valueRequestTopic = valueRequestTopic;
|
||||
value = (char*)calloc(size, sizeof(char));
|
||||
auto bindedMqttCallback = std::bind(&RemoteVariable::mqtt_callback, this, std::placeholders::_1, std::placeholders::_2);
|
||||
this->iot->registerMqttCallback(bindedMqttCallback);
|
||||
auto bindedSubscribe = std::bind(&RemoteVariable::subscribe, this);
|
||||
this->iot->registerSubscribeCallback(bindedSubscribe);
|
||||
this->subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the RemoteVariable object
|
||||
*
|
||||
* This method is used to initialize the RemoteVariable object.
|
||||
*
|
||||
* @param size The maximum size of the variable in bytes
|
||||
* @param topic The topic that the variable is published to
|
||||
* @param iot The ESPMegaIoT object
|
||||
*
|
||||
* @note Because the value is null terminated, the size should be the desired text length + 1
|
||||
* @warning This use dynamic memory allocation, so it is important to call the destructor when the object is no longer needed
|
||||
*/
|
||||
void RemoteVariable::begin(size_t size, const char* topic, ESPMegaIoT* iot) {
|
||||
this->begin(size, topic, iot, false, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe to the topic
|
||||
* This method is used internally to subscribe to the topic from iot core
|
||||
*/
|
||||
void RemoteVariable::subscribe() {
|
||||
ESP_LOGD("RemoteVariable", "Subscribing to %s", this->topic);
|
||||
this->iot->subscribe(this->topic);
|
||||
if(this->useValueRequest) {
|
||||
ESP_LOGD("RemoteVariable", "Subscribing to %s", this->valueRequestTopic);
|
||||
this->requestValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value of the variable
|
||||
*
|
||||
* This method is used to get the value of the variable.
|
||||
*
|
||||
* @return char* The null terminated string of the value
|
||||
*/
|
||||
char* RemoteVariable::getValue() {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The MQTT callback
|
||||
* This method is binded to the MQTT callback in iot core
|
||||
*/
|
||||
void RemoteVariable::mqtt_callback(char* topic, char* payload) {
|
||||
if (strcmp(topic, this->topic) == 0) {
|
||||
ESP_LOGD("RemoteVariable", "Received MQTT message from %s", topic);
|
||||
strcpy(this->value, payload);
|
||||
for (auto& callback : this->valueChangeCallback) {
|
||||
callback.second(this->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request the value of the variable
|
||||
* This method request a value update from the device that has the variable
|
||||
* @note This method is only functional if the useValueRequest is set to true
|
||||
*/
|
||||
void RemoteVariable::requestValue() {
|
||||
if(!this->useValueRequest)
|
||||
return;
|
||||
ESP_LOGD("RemoteVariable", "Sending request to %s", this->valueRequestTopic);
|
||||
this->iot->publish(this->valueRequestTopic, "request");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the set value feature
|
||||
* This method is used to enable the set value feature
|
||||
* @param setValueTopic The topic to send the value to
|
||||
*/
|
||||
void RemoteVariable::enableSetValue(const char* setValueTopic) {
|
||||
this->useSetValue = true;
|
||||
this->setValueTopic = setValueTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the value of the variable
|
||||
* This method is used to set the value of the variable
|
||||
* @param value The value to set
|
||||
* @note This method is only functional if the enableSetValue is already called
|
||||
*/
|
||||
void RemoteVariable::setValue(const char* value) {
|
||||
if(!this->useSetValue)
|
||||
return;
|
||||
this->iot->publish(this->setValueTopic, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback for value change
|
||||
* This method will be called when the value of the variable changes on the remote device
|
||||
* @param callback The callback function
|
||||
* @return uint8_t The handler for the callback
|
||||
*/
|
||||
uint8_t RemoteVariable::registerCallback(std::function<void(char*)> callback) {
|
||||
this->valueChangeCallback[this->valueChangeCallbackCount] = callback;
|
||||
return valueChangeCallbackCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a callback
|
||||
* This method is used to unregister a callback
|
||||
* @param handler The handler of the callback
|
||||
*/
|
||||
void RemoteVariable::unregisterCallback(uint8_t handler) {
|
||||
this->valueChangeCallback.erase(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value of the variable as an integer
|
||||
* This method is used to get the value of the variable as an integer
|
||||
* @return int The value of the variable as an integer
|
||||
* @note If the value is not a valid integer, it will return 0
|
||||
*/
|
||||
int RemoteVariable::getValueAsInt() {
|
||||
return atoi(this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value of the variable as a long
|
||||
* This method is used to get the value of the variable as a long
|
||||
* @return long The value of the variable as a long
|
||||
* @note If the value is not a valid long, it will return 0
|
||||
*/
|
||||
long RemoteVariable::getValueAsLong() {
|
||||
return atol(this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value of the variable as a double
|
||||
* This method is used to get the value of the variable as a double
|
||||
* @return double The value of the variable as a double
|
||||
* @note If the value is not a valid double, it will return 0
|
||||
*/
|
||||
double RemoteVariable::getValueAsDouble() {
|
||||
return atof(this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the value of the variable as an integer
|
||||
* This method is used to set the value of the variable as an integer
|
||||
* @param value The value to set
|
||||
*/
|
||||
void RemoteVariable::setIntValue(int value) {
|
||||
char buffer[this->size];
|
||||
itoa(value, buffer, DEC);
|
||||
this->setValue(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the value of the variable as a long
|
||||
* This method is used to set the value of the variable as a long
|
||||
* @param value The value to set
|
||||
*/
|
||||
void RemoteVariable::setLongValue(long value) {
|
||||
char buffer[this->size];
|
||||
ltoa(value, buffer, DEC);
|
||||
this->setValue(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the value of the variable as a double
|
||||
* This method is used to set the value of the variable as a double
|
||||
* @param value The value to set
|
||||
*/
|
||||
void RemoteVariable::setDoubleValue(double value) {
|
||||
char buffer[this->size];
|
||||
snprintf(buffer, this->size, "%f", value);
|
||||
this->setValue(buffer);
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <map>
|
||||
/**
|
||||
* @brief A class that create a variable that exists on other devices and can be accessed remotely
|
||||
*
|
||||
* This class is used to create a variable that exists on other devices and can be accessed remotely.
|
||||
* Supports setting and getting values from the variable.
|
||||
* Also support value request.
|
||||
*/
|
||||
class RemoteVariable
|
||||
{
|
||||
public:
|
||||
RemoteVariable();
|
||||
~RemoteVariable();
|
||||
void begin(size_t size, const char* topic, ESPMegaIoT* iot);
|
||||
void begin(size_t size, const char* topic, ESPMegaIoT* iot, bool useValueRequest, const char* valueRequestTopic);
|
||||
void setValue(const char* value);
|
||||
void enableSetValue(const char* setValueTopic);
|
||||
void subscribe();
|
||||
void requestValue();
|
||||
char* getValue();
|
||||
int getValueAsInt();
|
||||
long getValueAsLong();
|
||||
double getValueAsDouble();
|
||||
void setIntValue(int value);
|
||||
void setLongValue(long value);
|
||||
void setDoubleValue(double value);
|
||||
uint8_t registerCallback(std::function<void(char*)>);
|
||||
void unregisterCallback(uint8_t handler);
|
||||
|
||||
private:
|
||||
void mqtt_callback(char* topic, char* payload);
|
||||
ESPMegaIoT* iot;
|
||||
const char* topic;
|
||||
char* value;
|
||||
size_t size;
|
||||
bool useValueRequest;
|
||||
const char* valueRequestTopic;
|
||||
bool useSetValue;
|
||||
const char* setValueTopic;
|
||||
uint8_t valueChangeCallbackCount;
|
||||
std::map<uint8_t,std::function<void(char*)>> valueChangeCallback;
|
||||
};
|
|
@ -1,184 +0,0 @@
|
|||
#include "SmartVariable.hpp"
|
||||
SmartVariable::SmartVariable()
|
||||
{
|
||||
}
|
||||
|
||||
SmartVariable::~SmartVariable()
|
||||
{
|
||||
if (this->value != nullptr)
|
||||
free(this->value);
|
||||
}
|
||||
|
||||
void SmartVariable::begin(size_t size)
|
||||
{
|
||||
this->value = (char *)calloc(size, sizeof(char));
|
||||
this->size = size;
|
||||
}
|
||||
|
||||
void SmartVariable::enableIoT(ESPMegaIoT *iot, const char *topic)
|
||||
{
|
||||
this->iot = iot;
|
||||
this->iotEnabled = true;
|
||||
this->topic = topic;
|
||||
ESP_LOGV("SmartVariable", "Binding MQTT Callback");
|
||||
auto bindedMqttCallback = std::bind(&SmartVariable::handleMqttCallback, this, std::placeholders::_1, std::placeholders::_2);
|
||||
this->iot->registerMqttCallback(bindedMqttCallback);
|
||||
ESP_LOGV("SmartVariable", "Binding MQTT Subscription");
|
||||
auto bindedMqttSubscription = std::bind(&SmartVariable::subscribeMqtt, this);
|
||||
this->iot->registerSubscribeCallback(bindedMqttSubscription);
|
||||
ESP_LOGI("SmartVariable", "Calling MQTT Subscribe");
|
||||
this->subscribeMqtt();
|
||||
}
|
||||
|
||||
void SmartVariable::enableValueRequest(const char *valueRequestTopic)
|
||||
{
|
||||
ESP_LOGD("SmartVariable", "Enabling Value Request");
|
||||
this->useValueRequest = true;
|
||||
this->valueRequestTopic = valueRequestTopic;
|
||||
this->subscribeMqtt();
|
||||
}
|
||||
|
||||
void SmartVariable::setValue(const char *value)
|
||||
{
|
||||
strncpy(this->value, value, this->size - 1);
|
||||
this->value[this->size - 1] = '\0';
|
||||
if (this->autoSave)
|
||||
this->saveValue();
|
||||
if (this->iotEnabled)
|
||||
this->publishValue();
|
||||
// Call Callbacks
|
||||
for (auto const &callback : this->valueChangeCallbacks)
|
||||
{
|
||||
callback.second(this->value);
|
||||
}
|
||||
}
|
||||
|
||||
char *SmartVariable::getValue()
|
||||
{
|
||||
return this->value;
|
||||
}
|
||||
|
||||
void SmartVariable::enableSetValue(const char *setValueTopic)
|
||||
{
|
||||
this->setValueEnabled = true;
|
||||
this->setValueTopic = setValueTopic;
|
||||
this->subscribeMqtt();
|
||||
}
|
||||
|
||||
void SmartVariable::publishValue()
|
||||
{
|
||||
if (this->iotEnabled) {
|
||||
if (this->value == nullptr) {
|
||||
ESP_LOGE("SmartVariable", "Value is NULL");
|
||||
return;
|
||||
}
|
||||
if (this->topic == nullptr) {
|
||||
ESP_LOGE("SmartVariable", "Topic is NULL");
|
||||
return;
|
||||
}
|
||||
ESP_LOGV("SmartVariable", "Publishing Value: %s to %s", this->value, this->topic);
|
||||
this->iot->publish(this->topic, this->value);
|
||||
}
|
||||
}
|
||||
|
||||
void SmartVariable::bindFRAM(FRAM *fram, uint32_t framAddress)
|
||||
{
|
||||
this->bindFRAM(fram, framAddress, true);
|
||||
}
|
||||
|
||||
void SmartVariable::bindFRAM(FRAM *fram, uint32_t framAddress, bool loadValue)
|
||||
{
|
||||
this->framAddress = framAddress;
|
||||
this->fram = fram;
|
||||
if (loadValue)
|
||||
this->loadValue();
|
||||
}
|
||||
|
||||
void SmartVariable::loadValue()
|
||||
{
|
||||
this->fram->read(this->framAddress, (uint8_t *)this->value, this->size);
|
||||
this->setValue(this->value);
|
||||
}
|
||||
|
||||
void SmartVariable::saveValue()
|
||||
{
|
||||
this->fram->write(this->framAddress, (uint8_t *)this->value, this->size);
|
||||
}
|
||||
|
||||
void SmartVariable::setValueAutoSave(bool autoSave)
|
||||
{
|
||||
this->autoSave = autoSave;
|
||||
}
|
||||
|
||||
uint16_t SmartVariable::registerCallback(std::function<void(char *)> callback)
|
||||
{
|
||||
this->valueChangeCallbacks[this->currentHandlerId] = callback;
|
||||
return this->currentHandlerId++;
|
||||
}
|
||||
|
||||
void SmartVariable::unregisterCallback(uint16_t handlerId)
|
||||
{
|
||||
this->valueChangeCallbacks.erase(handlerId);
|
||||
}
|
||||
|
||||
void SmartVariable::handleMqttCallback(char *topic, char *payload)
|
||||
{
|
||||
if (!strcmp(topic, this->valueRequestTopic))
|
||||
{
|
||||
this->publishValue();
|
||||
}
|
||||
else if (!strcmp(topic, this->setValueTopic))
|
||||
{
|
||||
this->setValue(payload);
|
||||
}
|
||||
}
|
||||
|
||||
void SmartVariable::subscribeMqtt()
|
||||
{
|
||||
|
||||
if (this->iotEnabled)
|
||||
{
|
||||
ESP_LOGV("SmartVariable", "IoT Enabled, running MQTT Subscribe");
|
||||
ESP_LOGV("SmartVariable", "Value Request: %d, Set Value: %d", this->useValueRequest, this->setValueEnabled);
|
||||
if (this->useValueRequest) {
|
||||
if (this->valueRequestTopic == nullptr) {
|
||||
ESP_LOGE("SmartVariable", "Value Request Topic is NULL");
|
||||
return;
|
||||
}
|
||||
ESP_LOGV("SmartVariable", "Subscribing to %s", this->valueRequestTopic);
|
||||
this->iot->subscribe(this->valueRequestTopic);
|
||||
}
|
||||
if (this->setValueEnabled) {
|
||||
if (this->setValueTopic == nullptr) {
|
||||
ESP_LOGE("SmartVariable", "Set Value Topic is NULL");
|
||||
return;
|
||||
}
|
||||
ESP_LOGV("SmartVariable", "Subscribing to %s", this->setValueTopic);
|
||||
this->iot->subscribe(this->setValueTopic);
|
||||
}
|
||||
ESP_LOGV("SmartVariable", "Publishing Value");
|
||||
this->publishValue();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t SmartVariable::getIntValue()
|
||||
{
|
||||
return atoi(this->value);
|
||||
}
|
||||
|
||||
void SmartVariable::setIntValue(int32_t value)
|
||||
{
|
||||
itoa(value, this->value, 10);
|
||||
this->setValue(this->value);
|
||||
}
|
||||
|
||||
double SmartVariable::getDoubleValue()
|
||||
{
|
||||
return atof(this->value);
|
||||
}
|
||||
|
||||
void SmartVariable::setDoubleValue(double value)
|
||||
{
|
||||
dtostrf(value, 0, 2, this->value);
|
||||
this->setValue(this->value);
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
#include <FRAM.h>
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* @brief SmartVariable is a local variable that can be accessed remotely and have FRAM support
|
||||
*/
|
||||
|
||||
class SmartVariable {
|
||||
public:
|
||||
SmartVariable();
|
||||
~SmartVariable();
|
||||
void begin(size_t size);
|
||||
void enableIoT(ESPMegaIoT* iot, const char* topic);
|
||||
void enableValueRequest(const char* valueRequestTopic);
|
||||
void setValue(const char* value);
|
||||
char* getValue();
|
||||
void enableSetValue(const char* setValueTopic);
|
||||
void publishValue();
|
||||
void bindFRAM(FRAM *fram, uint32_t framAddress);
|
||||
void bindFRAM(FRAM *fram, uint32_t framAddress, bool loadValue);
|
||||
void loadValue();
|
||||
void saveValue();
|
||||
void setValueAutoSave(bool autoSave);
|
||||
uint16_t registerCallback(std::function<void(char*)> callback);
|
||||
void unregisterCallback(uint16_t handlerId);
|
||||
int32_t getIntValue();
|
||||
void setIntValue(int32_t value);
|
||||
double getDoubleValue();
|
||||
void setDoubleValue(double value);
|
||||
protected:
|
||||
ESPMegaIoT* iot;
|
||||
bool iotEnabled;
|
||||
const char* topic;
|
||||
char* value;
|
||||
size_t size;
|
||||
bool useValueRequest;
|
||||
const char* valueRequestTopic;
|
||||
bool setValueEnabled;
|
||||
const char* setValueTopic;
|
||||
bool autoSave;
|
||||
FRAM *fram;
|
||||
uint32_t framAddress;
|
||||
void handleMqttCallback(char* topic, char* payload);
|
||||
void subscribeMqtt();
|
||||
// Value Change Callback
|
||||
uint16_t currentHandlerId;
|
||||
std::map<uint16_t, std::function<void(char*)>> valueChangeCallbacks;
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief The rtctime_t struct is a structure for storing the time.
|
||||
*
|
||||
* This structure is used by the ESPMegaPRO library to store the time.
|
||||
*
|
||||
* @warning This structure is not compatible with the Arduino Time library.
|
||||
*/
|
||||
struct rtctime_t {
|
||||
uint8_t hours;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint16_t year;
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
#include "config_html.h"
|
||||
#include "display_html.h"
|
||||
#include "ota_html.h"
|
|
@ -1,821 +0,0 @@
|
|||
const char config_html[] PROGMEM = {
|
||||
0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6c, 0x6f, 0x61,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x53, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x22,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x73, 0x70, 0x69, 0x6e,
|
||||
0x6e, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
|
||||
0x72, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x64, 0x69,
|
||||
0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b,
|
||||
0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63,
|
||||
0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x73, 0x70, 0x69, 0x6e, 0x6e, 0x65,
|
||||
0x72, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x3c, 0x70, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6c, 0x6f, 0x61, 0x64,
|
||||
0x69, 0x6e, 0x67, 0x54, 0x65, 0x78, 0x74, 0x22, 0x3e, 0x53, 0x61, 0x76,
|
||||
0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65,
|
||||
0x20, 0x77, 0x61, 0x69, 0x74, 0x20, 0x28, 0x3c, 0x73, 0x70, 0x61, 0x6e,
|
||||
0x20, 0x69, 0x64, 0x3d, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x22, 0x3e, 0x31, 0x35, 0x73, 0x3c, 0x2f, 0x73, 0x70, 0x61,
|
||||
0x6e, 0x3e, 0x29, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x64,
|
||||
0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d,
|
||||
0x0a, 0x3c, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x65, 0x6e, 0x63, 0x74, 0x79,
|
||||
0x70, 0x65, 0x3d, 0x22, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72,
|
||||
0x74, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x22,
|
||||
0x20, 0x69, 0x64, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
|
||||
0x66, 0x6f, 0x72, 0x6d, 0x22, 0x20, 0x6f, 0x6e, 0x73, 0x75, 0x62, 0x6d,
|
||||
0x69, 0x74, 0x3d, 0x22, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x22, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x31, 0x3e, 0x45, 0x53, 0x50, 0x4d,
|
||||
0x65, 0x67, 0x61, 0x20, 0x50, 0x52, 0x4f, 0x3c, 0x2f, 0x68, 0x31, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x33, 0x3e, 0x44, 0x65, 0x76, 0x69,
|
||||
0x63, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x70, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22,
|
||||
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65,
|
||||
0x22, 0x3e, 0x49, 0x50, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70,
|
||||
0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78,
|
||||
0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x69, 0x70, 0x5f, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
|
||||
0x22, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x5f, 0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d,
|
||||
0x22, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e,
|
||||
0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e, 0x4e, 0x65,
|
||||
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x4d, 0x61, 0x73, 0x6b, 0x3c, 0x2f,
|
||||
0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74,
|
||||
0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22,
|
||||
0x20, 0x69, 0x64, 0x3d, 0x22, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b,
|
||||
0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x6e, 0x65, 0x74, 0x6d,
|
||||
0x61, 0x73, 0x6b, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22,
|
||||
0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x3d, 0x22, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67,
|
||||
0x20, 0x2e, 0x2e, 0x2e, 0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x70, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22,
|
||||
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65,
|
||||
0x22, 0x3e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x3c, 0x2f, 0x70,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
|
||||
0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22, 0x20,
|
||||
0x69, 0x64, 0x3d, 0x22, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22,
|
||||
0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x67, 0x61, 0x74, 0x65, 0x77,
|
||||
0x61, 0x79, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x3d, 0x22, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
|
||||
0x2e, 0x2e, 0x2e, 0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x3c, 0x70, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22,
|
||||
0x3e, 0x44, 0x4e, 0x53, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3c,
|
||||
0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74,
|
||||
0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x64, 0x6e, 0x73, 0x22, 0x20, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x3d, 0x22, 0x64, 0x6e, 0x73, 0x22, 0x20, 0x63, 0x6c,
|
||||
0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x78,
|
||||
0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x4c, 0x6f,
|
||||
0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x22, 0x3e, 0x3c,
|
||||
0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x63, 0x6c,
|
||||
0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
|
||||
0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e, 0x48, 0x6f, 0x73, 0x74, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22,
|
||||
0x74, 0x65, 0x78, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x68, 0x6f,
|
||||
0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x3d, 0x22, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x5f,
|
||||
0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22,
|
||||
0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x22,
|
||||
0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e, 0x42, 0x4d, 0x53,
|
||||
0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x49, 0x50,
|
||||
0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3c, 0x2f, 0x70, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74,
|
||||
0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22, 0x20, 0x69,
|
||||
0x64, 0x3d, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x69, 0x70, 0x22, 0x20, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x3d, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x69, 0x70, 0x22,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x5f, 0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d,
|
||||
0x22, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e,
|
||||
0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e, 0x42, 0x4d,
|
||||
0x53, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x50,
|
||||
0x6f, 0x72, 0x74, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22,
|
||||
0x74, 0x65, 0x78, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x6d,
|
||||
0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x3d, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x20,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x5f,
|
||||
0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22,
|
||||
0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x22,
|
||||
0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x6c, 0x61,
|
||||
0x62, 0x65, 0x6c, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x3e, 0x41, 0x75,
|
||||
0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74,
|
||||
0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x63, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x62, 0x6f, 0x78, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x62,
|
||||
0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x61, 0x75, 0x74, 0x68, 0x22, 0x20,
|
||||
0x69, 0x64, 0x3d, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x61,
|
||||
0x75, 0x74, 0x68, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22,
|
||||
0x79, 0x65, 0x73, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c,
|
||||
0x73, 0x70, 0x61, 0x6e, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22,
|
||||
0x63, 0x68, 0x65, 0x63, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x22, 0x3e, 0x3c,
|
||||
0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f,
|
||||
0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e, 0x42, 0x4d,
|
||||
0x53, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x55,
|
||||
0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x70, 0x3e, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79,
|
||||
0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22, 0x20, 0x69, 0x64,
|
||||
0x3d, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x62, 0x6d,
|
||||
0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x5f,
|
||||
0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22,
|
||||
0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x22,
|
||||
0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e, 0x42, 0x4d, 0x53,
|
||||
0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x50, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70,
|
||||
0x65, 0x3d, 0x22, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22,
|
||||
0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
|
||||
0x22, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
||||
0x64, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x5f, 0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x3d, 0x22, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e,
|
||||
0x2e, 0x2e, 0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x3c, 0x70, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e,
|
||||
0x42, 0x4d, 0x53, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2d,
|
||||
0x20, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x3c, 0x2f, 0x70,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
|
||||
0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22, 0x20,
|
||||
0x69, 0x64, 0x3d, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x65, 0x6e, 0x64, 0x70,
|
||||
0x6f, 0x69, 0x6e, 0x74, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22,
|
||||
0x62, 0x6d, 0x73, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||
0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x5f, 0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x3d, 0x22, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e,
|
||||
0x2e, 0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x70, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3e, 0x57,
|
||||
0x65, 0x62, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65,
|
||||
0x20, 0x2d, 0x20, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x3c,
|
||||
0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74,
|
||||
0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x73,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x3d, 0x22, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x78, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x3d, 0x22, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
|
||||
0x2e, 0x2e, 0x2e, 0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x3c, 0x70, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22,
|
||||
0x3e, 0x57, 0x65, 0x62, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61,
|
||||
0x63, 0x65, 0x20, 0x2d, 0x20, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
||||
0x64, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e,
|
||||
0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x70, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22,
|
||||
0x77, 0x65, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
||||
0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x77, 0x65, 0x62, 0x5f,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x20, 0x63, 0x6c,
|
||||
0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x78,
|
||||
0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x4c, 0x6f,
|
||||
0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x22, 0x3e, 0x3c,
|
||||
0x62, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x73, 0x61, 0x76, 0x65, 0x5f, 0x62,
|
||||
0x74, 0x6e, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x73, 0x75,
|
||||
0x62, 0x6d, 0x69, 0x74, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d,
|
||||
0x22, 0x62, 0x74, 0x6e, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d,
|
||||
0x22, 0x53, 0x61, 0x76, 0x65, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d,
|
||||
0x22, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20, 0x63, 0x6c, 0x61,
|
||||
0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x20, 0x6f, 0x6e,
|
||||
0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f,
|
||||
0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x68,
|
||||
0x72, 0x65, 0x66, 0x3d, 0x27, 0x2f, 0x27, 0x22, 0x3e, 0x42, 0x61, 0x63,
|
||||
0x6b, 0x3c, 0x2f, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x3c, 0x62,
|
||||
0x72, 0x20, 0x2f, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x62, 0x3e, 0x53, 0x49, 0x57, 0x41, 0x54, 0x20, 0x53,
|
||||
0x59, 0x53, 0x54, 0x45, 0x4d, 0x20, 0x32, 0x30, 0x32, 0x33, 0x3c, 0x2f,
|
||||
0x62, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x0d,
|
||||
0x0a, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f, 0x6e, 0x6c, 0x6f,
|
||||
0x61, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x76, 0x61, 0x72, 0x20, 0x73, 0x61, 0x76, 0x65, 0x42, 0x75, 0x74, 0x74,
|
||||
0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x73, 0x61, 0x76, 0x65, 0x5f, 0x62,
|
||||
0x74, 0x6e, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
|
||||
0x61, 0x76, 0x65, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x2e, 0x73, 0x74,
|
||||
0x79, 0x6c, 0x65, 0x2e, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x45,
|
||||
0x76, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x22, 0x6e, 0x6f, 0x6e,
|
||||
0x65, 0x22, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x61, 0x76,
|
||||
0x65, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x2e, 0x73, 0x74, 0x79, 0x6c,
|
||||
0x65, 0x2e, 0x6f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20,
|
||||
0x22, 0x30, 0x2e, 0x36, 0x22, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x2f, 0x2f, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66,
|
||||
0x65, 0x74, 0x63, 0x68, 0x28, 0x22, 0x2f, 0x67, 0x65, 0x74, 0x5f, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x2e, 0x74, 0x68, 0x65, 0x6e, 0x28, 0x72, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x3d, 0x3e, 0x20, 0x72, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x6a, 0x73, 0x6f, 0x6e, 0x28, 0x29,
|
||||
0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x74, 0x68,
|
||||
0x65, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x3e, 0x20, 0x7b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f,
|
||||
0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
|
||||
0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x64, 0x61, 0x74,
|
||||
0x61, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63,
|
||||
0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x64,
|
||||
0x61, 0x74, 0x61, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79,
|
||||
0x49, 0x64, 0x28, 0x22, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79,
|
||||
0x49, 0x64, 0x28, 0x22, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22,
|
||||
0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x61,
|
||||
0x74, 0x61, 0x2e, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63,
|
||||
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x67, 0x61,
|
||||
0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x67, 0x61, 0x74,
|
||||
0x65, 0x77, 0x61, 0x79, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79,
|
||||
0x49, 0x64, 0x28, 0x22, 0x64, 0x6e, 0x73, 0x22, 0x29, 0x2e, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x64,
|
||||
0x6e, 0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65,
|
||||
0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64,
|
||||
0x28, 0x22, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29,
|
||||
0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74,
|
||||
0x61, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63,
|
||||
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x62, 0x6d,
|
||||
0x73, 0x5f, 0x69, 0x70, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x62, 0x6d, 0x73, 0x5f,
|
||||
0x69, 0x70, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65,
|
||||
0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64,
|
||||
0x28, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x29,
|
||||
0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74,
|
||||
0x61, 0x2e, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63,
|
||||
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x62, 0x6d,
|
||||
0x73, 0x5f, 0x75, 0x73, 0x65, 0x61, 0x75, 0x74, 0x68, 0x22, 0x29, 0x2e,
|
||||
0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x64, 0x61,
|
||||
0x74, 0x61, 0x2e, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x61, 0x75,
|
||||
0x74, 0x68, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65,
|
||||
0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64,
|
||||
0x28, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x62, 0x6d,
|
||||
0x73, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63,
|
||||
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x62, 0x6d,
|
||||
0x73, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x29,
|
||||
0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74,
|
||||
0x61, 0x2e, 0x62, 0x6d, 0x73, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65,
|
||||
0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64,
|
||||
0x28, 0x22, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x73,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x77, 0x65, 0x62, 0x5f, 0x70, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x77, 0x65,
|
||||
0x62, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x61, 0x76,
|
||||
0x65, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x2e, 0x73, 0x74, 0x79, 0x6c,
|
||||
0x65, 0x2e, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65,
|
||||
0x6e, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x22, 0x61, 0x75, 0x74, 0x6f, 0x22,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
|
||||
0x61, 0x76, 0x65, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x2e, 0x73, 0x74,
|
||||
0x79, 0x6c, 0x65, 0x2e, 0x6f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x20,
|
||||
0x3d, 0x20, 0x22, 0x31, 0x22, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x7d, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x2e, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x20, 0x3d, 0x3e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65,
|
||||
0x20, 0x61, 0x6e, 0x79, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e,
|
||||
0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x20, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
|
||||
0x73, 0x65, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x28,
|
||||
0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x65,
|
||||
0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65,
|
||||
0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65,
|
||||
0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x6d,
|
||||
0x44, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
|
||||
0x79, 0x49, 0x64, 0x28, 0x22, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x65, 0x74, 0x6d,
|
||||
0x61, 0x73, 0x6b, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
|
||||
0x79, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79,
|
||||
0x49, 0x64, 0x28, 0x22, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22,
|
||||
0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x64, 0x6e, 0x73, 0x3a, 0x20, 0x64, 0x6f, 0x63,
|
||||
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x64, 0x6e,
|
||||
0x73, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
|
||||
0x79, 0x49, 0x64, 0x28, 0x22, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6d, 0x73, 0x5f, 0x69, 0x70,
|
||||
0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67,
|
||||
0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49,
|
||||
0x64, 0x28, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x69, 0x70, 0x22, 0x29, 0x2e,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x3a, 0x20,
|
||||
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
|
||||
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
|
||||
0x22, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x29, 0x2e,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x61, 0x75, 0x74,
|
||||
0x68, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79,
|
||||
0x49, 0x64, 0x28, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x61,
|
||||
0x75, 0x74, 0x68, 0x22, 0x29, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65,
|
||||
0x64, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6d,
|
||||
0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20,
|
||||
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
|
||||
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
|
||||
0x22, 0x62, 0x6d, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6d, 0x73, 0x5f, 0x70, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x62, 0x6d, 0x73,
|
||||
0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x29, 0x2e,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x62, 0x6d, 0x73, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
|
||||
0x79, 0x49, 0x64, 0x28, 0x22, 0x62, 0x6d, 0x73, 0x5f, 0x65, 0x6e, 0x64,
|
||||
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x65,
|
||||
0x62, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20,
|
||||
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
|
||||
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
|
||||
0x22, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x65, 0x62, 0x5f, 0x70, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x77, 0x65, 0x62,
|
||||
0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x29, 0x2e,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
|
||||
0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20,
|
||||
0x53, 0x65, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x50, 0x4f, 0x53,
|
||||
0x54, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x28, 0x22, 0x2f, 0x73,
|
||||
0x61, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x2c,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65,
|
||||
0x74, 0x68, 0x6f, 0x64, 0x3a, 0x20, 0x22, 0x50, 0x4f, 0x53, 0x54, 0x22,
|
||||
0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x61,
|
||||
0x64, 0x65, 0x72, 0x73, 0x3a, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x61, 0x70,
|
||||
0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73,
|
||||
0x6f, 0x6e, 0x22, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
|
||||
0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x64,
|
||||
0x79, 0x3a, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69,
|
||||
0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x44, 0x61,
|
||||
0x74, 0x61, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x74, 0x68, 0x65, 0x6e,
|
||||
0x28, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x3d, 0x3e,
|
||||
0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x6a, 0x73,
|
||||
0x6f, 0x6e, 0x28, 0x29, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x2e, 0x74, 0x68, 0x65, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x20,
|
||||
0x3d, 0x3e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x2f, 0x2f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20,
|
||||
0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c,
|
||||
0x6f, 0x67, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x2e, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x65, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x20, 0x3d, 0x3e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x48, 0x61, 0x6e,
|
||||
0x64, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x65, 0x72, 0x72, 0x6f,
|
||||
0x72, 0x73, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f,
|
||||
0x72, 0x28, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x68, 0x6f, 0x77, 0x20, 0x74, 0x68,
|
||||
0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x70,
|
||||
0x69, 0x6e, 0x6e, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
|
||||
0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45,
|
||||
0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27,
|
||||
0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x70, 0x69, 0x6e, 0x6e,
|
||||
0x65, 0x72, 0x27, 0x29, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x64,
|
||||
0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x27, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x27, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x2f, 0x2f, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x68,
|
||||
0x65, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63,
|
||||
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x27, 0x29, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x4c, 0x65, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x35, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x20, 0x3d,
|
||||
0x20, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
|
||||
0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x4c, 0x65, 0x66, 0x74, 0x2d, 0x2d, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x65, 0x66,
|
||||
0x74, 0x20, 0x2b, 0x20, 0x22, 0x73, 0x22, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x4c, 0x65, 0x66, 0x74, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x63,
|
||||
0x6c, 0x65, 0x61, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
|
||||
0x28, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x54, 0x69,
|
||||
0x6d, 0x65, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
|
||||
0x2c, 0x20, 0x31, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x57, 0x61, 0x69, 0x74, 0x20,
|
||||
0x66, 0x6f, 0x72, 0x20, 0x31, 0x35, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e,
|
||||
0x64, 0x73, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x64, 0x69, 0x72,
|
||||
0x65, 0x63, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20,
|
||||
0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67, 0x65, 0x20, 0x62, 0x61, 0x73,
|
||||
0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x27, 0x73,
|
||||
0x20, 0x49, 0x50, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77,
|
||||
0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x28, 0x22,
|
||||
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x22, 0x20, 0x2b, 0x20, 0x66,
|
||||
0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x69, 0x70, 0x5f, 0x61,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x7d, 0x2c, 0x20, 0x31, 0x35, 0x30, 0x30, 0x30, 0x29, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x2f, 0x73, 0x63, 0x72,
|
||||
0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x3c, 0x73, 0x74, 0x79, 0x6c, 0x65,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x3a, 0x20, 0x31, 0x32, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66,
|
||||
0x6f, 0x6e, 0x74, 0x2d, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20,
|
||||
0x62, 0x6f, 0x6c, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74,
|
||||
0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c,
|
||||
0x65, 0x66, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6c,
|
||||
0x69, 0x67, 0x6e, 0x2d, 0x73, 0x65, 0x6c, 0x66, 0x3a, 0x20, 0x6c, 0x65,
|
||||
0x66, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20,
|
||||
0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67,
|
||||
0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x68, 0x72, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c,
|
||||
0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23,
|
||||
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d,
|
||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x61, 0x61, 0x61,
|
||||
0x61, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x2e, 0x35,
|
||||
0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20,
|
||||
0x30, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x3a,
|
||||
0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74,
|
||||
0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x79, 0x6c,
|
||||
0x65, 0x3a, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x74, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x77, 0x69,
|
||||
0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x33,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68,
|
||||
0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x34, 0x34, 0x70,
|
||||
0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64,
|
||||
0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x34,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x61, 0x75,
|
||||
0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e,
|
||||
0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x35, 0x70, 0x78,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a,
|
||||
0x20, 0x23, 0x66, 0x31, 0x66, 0x31, 0x66, 0x31, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69,
|
||||
0x6e, 0x67, 0x3a, 0x20, 0x30, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x62, 0x6f,
|
||||
0x64, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61,
|
||||
0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x69, 0x6d, 0x61,
|
||||
0x67, 0x65, 0x3a, 0x20, 0x75, 0x72, 0x6c, 0x28, 0x22, 0x68, 0x74, 0x74,
|
||||
0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x66, 0x73, 0x2e, 0x73, 0x69, 0x77, 0x61,
|
||||
0x74, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||
0x61, 0x72, 0x6f, 0x6e, 0x61, 0x5f, 0x62, 0x67, 0x2e, 0x70, 0x6e, 0x67,
|
||||
0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63,
|
||||
0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x3a, 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c,
|
||||
0x79, 0x3a, 0x20, 0x73, 0x61, 0x6e, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x69,
|
||||
0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74,
|
||||
0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x34, 0x70, 0x78, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
|
||||
0x20, 0x23, 0x37, 0x37, 0x37, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d,
|
||||
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69,
|
||||
0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63,
|
||||
0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x43, 0x43, 0x43, 0x43, 0x43,
|
||||
0x43, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x35, 0x45, 0x35, 0x45, 0x35, 0x45, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
|
||||
0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f,
|
||||
0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6f,
|
||||
0x6c, 0x69, 0x64, 0x20, 0x23, 0x64, 0x64, 0x64, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67,
|
||||
0x68, 0x74, 0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67,
|
||||
0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
|
||||
0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72, 0x2c, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x23, 0x70, 0x72, 0x67, 0x62, 0x61, 0x72, 0x20, 0x7b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x44,
|
||||
0x39, 0x44, 0x39, 0x44, 0x39, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75,
|
||||
0x73, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
|
||||
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
|
||||
0x20, 0x23, 0x32, 0x39, 0x43, 0x44, 0x31, 0x46, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x25,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
|
||||
0x74, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
|
||||
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x72, 0x67, 0x62, 0x61, 0x28,
|
||||
0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35,
|
||||
0x35, 0x2c, 0x20, 0x30, 0x2e, 0x39, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68,
|
||||
0x3a, 0x20, 0x32, 0x35, 0x38, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x37, 0x35,
|
||||
0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x33,
|
||||
0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f,
|
||||
0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a,
|
||||
0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20,
|
||||
0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d,
|
||||
0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x62, 0x74, 0x6e, 0x20, 0x7b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x43, 0x41, 0x33, 0x44, 0x33,
|
||||
0x44, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d,
|
||||
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x7b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x34, 0x31, 0x37, 0x64, 0x66,
|
||||
0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
|
||||
0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69,
|
||||
0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30,
|
||||
0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a,
|
||||
0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x5f, 0x74, 0x78, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64,
|
||||
0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x65, 0x32, 0x65,
|
||||
0x32, 0x65, 0x32, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x6f, 0x78,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x3a, 0x20, 0x34, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69,
|
||||
0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e,
|
||||
0x65, 0x2d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
|
||||
0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f,
|
||||
0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x72, 0x65, 0x6c, 0x61,
|
||||
0x74, 0x69, 0x76, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70,
|
||||
0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x3a,
|
||||
0x20, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d,
|
||||
0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d,
|
||||
0x3a, 0x20, 0x31, 0x32, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66,
|
||||
0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x32, 0x30,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x77, 0x65,
|
||||
0x62, 0x6b, 0x69, 0x74, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x73, 0x65,
|
||||
0x6c, 0x65, 0x63, 0x74, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x6d, 0x6f, 0x7a, 0x2d, 0x75, 0x73,
|
||||
0x65, 0x72, 0x2d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x20, 0x6e,
|
||||
0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x6d,
|
||||
0x73, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
||||
0x74, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
||||
0x74, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
|
||||
0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74,
|
||||
0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x70, 0x61, 0x63,
|
||||
0x69, 0x74, 0x79, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68,
|
||||
0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e,
|
||||
0x63, 0x68, 0x65, 0x63, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x20, 0x7b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x3a, 0x20, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3a,
|
||||
0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69,
|
||||
0x67, 0x68, 0x74, 0x3a, 0x20, 0x32, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32,
|
||||
0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61,
|
||||
0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c,
|
||||
0x6f, 0x72, 0x3a, 0x20, 0x23, 0x65, 0x65, 0x65, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72,
|
||||
0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x7e, 0x2e, 0x63, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d,
|
||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x63, 0x63, 0x63, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x69, 0x6e,
|
||||
0x70, 0x75, 0x74, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x7e,
|
||||
0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x20, 0x7b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20,
|
||||
0x23, 0x32, 0x31, 0x39, 0x36, 0x46, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x3a, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x3a, 0x20, 0x22, 0x22, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x61,
|
||||
0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x6e,
|
||||
0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
|
||||
0x72, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x63, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x65, 0x64, 0x7e, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x6d, 0x61,
|
||||
0x72, 0x6b, 0x3a, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
|
||||
0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d,
|
||||
0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61,
|
||||
0x69, 0x6e, 0x65, 0x72, 0x20, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x6d,
|
||||
0x61, 0x72, 0x6b, 0x3a, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x7b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x39,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x70,
|
||||
0x3a, 0x20, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x35, 0x70, 0x78, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a,
|
||||
0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x73, 0x6f, 0x6c, 0x69,
|
||||
0x64, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x64,
|
||||
0x74, 0x68, 0x3a, 0x20, 0x30, 0x20, 0x33, 0x70, 0x78, 0x20, 0x33, 0x70,
|
||||
0x78, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x77,
|
||||
0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66,
|
||||
0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x28,
|
||||
0x34, 0x35, 0x64, 0x65, 0x67, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x2d, 0x6d, 0x73, 0x2d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f,
|
||||
0x72, 0x6d, 0x3a, 0x20, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x28, 0x34,
|
||||
0x35, 0x64, 0x65, 0x67, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x72,
|
||||
0x6f, 0x74, 0x61, 0x74, 0x65, 0x28, 0x34, 0x35, 0x64, 0x65, 0x67, 0x29,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x2e, 0x73, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x3a, 0x20, 0x66, 0x69, 0x78, 0x65, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x34, 0x30, 0x25, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x34,
|
||||
0x34, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x6c, 0x61, 0x74, 0x65, 0x28, 0x2d, 0x35, 0x30, 0x25, 0x2c, 0x20,
|
||||
0x2d, 0x35, 0x30, 0x25, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x70, 0x78,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
|
||||
0x74, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x31,
|
||||
0x36, 0x70, 0x78, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x20, 0x23, 0x66,
|
||||
0x33, 0x66, 0x33, 0x66, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20,
|
||||
0x31, 0x36, 0x70, 0x78, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x20, 0x23,
|
||||
0x33, 0x34, 0x39, 0x38, 0x64, 0x62, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69,
|
||||
0x75, 0x73, 0x3a, 0x20, 0x35, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a,
|
||||
0x20, 0x73, 0x70, 0x69, 0x6e, 0x20, 0x32, 0x73, 0x20, 0x6c, 0x69, 0x6e,
|
||||
0x65, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x40, 0x6b, 0x65, 0x79, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x73,
|
||||
0x70, 0x69, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30,
|
||||
0x25, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x72, 0x6f,
|
||||
0x74, 0x61, 0x74, 0x65, 0x28, 0x30, 0x64, 0x65, 0x67, 0x29, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x31, 0x30, 0x30, 0x25, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72,
|
||||
0x6d, 0x3a, 0x20, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x28, 0x33, 0x36,
|
||||
0x30, 0x64, 0x65, 0x67, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x23, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x78, 0x74,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a,
|
||||
0x20, 0x34, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20,
|
||||
0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x66,
|
||||
0x69, 0x78, 0x65, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74,
|
||||
0x6f, 0x70, 0x3a, 0x20, 0x36, 0x35, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x35, 0x30, 0x25, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66,
|
||||
0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61,
|
||||
0x74, 0x65, 0x28, 0x2d, 0x35, 0x30, 0x25, 0x2c, 0x20, 0x2d, 0x35, 0x30,
|
||||
0x25, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x2e, 0x73, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x2d, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
|
||||
0x20, 0x66, 0x6c, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x79, 0x2d, 0x63, 0x6f, 0x6e, 0x74,
|
||||
0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x2d,
|
||||
0x69, 0x74, 0x65, 0x6d, 0x73, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x73, 0x69,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x66, 0x69, 0x78, 0x65, 0x64, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7a, 0x2d, 0x69, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x3a, 0x20, 0x39, 0x39, 0x39, 0x39, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20,
|
||||
0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68,
|
||||
0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x72, 0x67, 0x62, 0x61, 0x28, 0x30,
|
||||
0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2e, 0x39, 0x29,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x73, 0x65,
|
||||
0x6d, 0x69, 0x2d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65,
|
||||
0x6e, 0x74, 0x20, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x20, 0x62, 0x61, 0x63,
|
||||
0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x2a, 0x2f, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65,
|
||||
0x3e
|
||||
, 0x00};
|
||||
|
|
@ -1,426 +0,0 @@
|
|||
const char display_html[] PROGMEM = {
|
||||
0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x73, 0x72, 0x63, 0x3d,
|
||||
0x22, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x6a, 0x61,
|
||||
0x78, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x6a, 0x61, 0x78, 0x2f, 0x6c, 0x69,
|
||||
0x62, 0x73, 0x2f, 0x6a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x33, 0x2e,
|
||||
0x32, 0x2e, 0x31, 0x2f, 0x6a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x6d,
|
||||
0x69, 0x6e, 0x2e, 0x6a, 0x73, 0x22, 0x3e, 0x3c, 0x2f, 0x73, 0x63, 0x72,
|
||||
0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x3c, 0x66, 0x6f, 0x72, 0x6d, 0x20,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3d, 0x22, 0x50, 0x4f, 0x53, 0x54,
|
||||
0x22, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x23, 0x22,
|
||||
0x20, 0x65, 0x6e, 0x63, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x6d, 0x75,
|
||||
0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x2f, 0x66, 0x6f, 0x72, 0x6d,
|
||||
0x2d, 0x64, 0x61, 0x74, 0x61, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x75,
|
||||
0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x31, 0x3e, 0x45, 0x53,
|
||||
0x50, 0x4d, 0x65, 0x67, 0x61, 0x20, 0x50, 0x52, 0x4f, 0x3c, 0x2f, 0x68,
|
||||
0x31, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x33, 0x3e,
|
||||
0x4c, 0x43, 0x44, 0x20, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x20,
|
||||
0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x2f,
|
||||
0x68, 0x33, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x72,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x33, 0x3e, 0x55,
|
||||
0x70, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x54, 0x46, 0x54, 0x20, 0x46, 0x69,
|
||||
0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
|
||||
0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x20,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
|
||||
0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x20,
|
||||
0x6f, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x22, 0x73, 0x75,
|
||||
0x62, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x22, 0x20, 0x73, 0x74, 0x79,
|
||||
0x6c, 0x65, 0x3d, 0x22, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
|
||||
0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x2c, 0x20, 0x61, 0x63, 0x63,
|
||||
0x65, 0x70, 0x74, 0x3d, 0x22, 0x2e, 0x74, 0x66, 0x74, 0x22, 0x20, 0x2f,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6c, 0x61, 0x62, 0x65,
|
||||
0x6c, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69,
|
||||
0x6e, 0x70, 0x75, 0x74, 0x22, 0x20, 0x66, 0x6f, 0x72, 0x3d, 0x22, 0x66,
|
||||
0x69, 0x6c, 0x65, 0x22, 0x3e, 0x43, 0x68, 0x6f, 0x6f, 0x73, 0x65, 0x20,
|
||||
0x66, 0x69, 0x6c, 0x65, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x6c, 0x61, 0x62,
|
||||
0x65, 0x6c, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x69, 0x6e,
|
||||
0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x62, 0x75,
|
||||
0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d,
|
||||
0x22, 0x62, 0x74, 0x6e, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d,
|
||||
0x22, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x6f, 0x6e, 0x63,
|
||||
0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64,
|
||||
0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x28, 0x29, 0x22, 0x20,
|
||||
0x2f, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x3c, 0x62, 0x72, 0x20,
|
||||
0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76,
|
||||
0x20, 0x69, 0x64, 0x3d, 0x22, 0x70, 0x72, 0x67, 0x22, 0x3e, 0x3c, 0x2f,
|
||||
0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x62,
|
||||
0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64,
|
||||
0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x70, 0x72, 0x67, 0x62, 0x61,
|
||||
0x72, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x61,
|
||||
0x72, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x3c, 0x62, 0x3e, 0x53, 0x49, 0x57, 0x41, 0x54, 0x20,
|
||||
0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x20, 0x32, 0x30, 0x32, 0x33, 0x3c,
|
||||
0x2f, 0x62, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x3e,
|
||||
0x0d, 0x0a, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x20, 0x73, 0x75, 0x62, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72,
|
||||
0x20, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20,
|
||||
0x6f, 0x62, 0x6a, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x73, 0x70,
|
||||
0x6c, 0x69, 0x74, 0x28, 0x22, 0x5c, 0x5c, 0x22, 0x29, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x66, 0x69, 0x6c,
|
||||
0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e,
|
||||
0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||
0x20, 0x20, 0x20, 0x22, 0x20, 0x2b, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x4e,
|
||||
0x61, 0x6d, 0x65, 0x5b, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65,
|
||||
0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x20, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x72, 0x6d, 0x77,
|
||||
0x61, 0x72, 0x65, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x69, 0x6c,
|
||||
0x65, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
|
||||
0x79, 0x49, 0x64, 0x28, 0x27, 0x66, 0x69, 0x6c, 0x65, 0x27, 0x29, 0x2e,
|
||||
0x66, 0x69, 0x6c, 0x65, 0x73, 0x5b, 0x30, 0x5d, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72,
|
||||
0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20,
|
||||
0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x29,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76,
|
||||
0x61, 0x72, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x77, 0x69,
|
||||
0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20,
|
||||
0x50, 0x61, 0x74, 0x68, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x69, 0x6e,
|
||||
0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x61, 0x74, 0x20,
|
||||
0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20,
|
||||
0x2f, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x65, 0x67,
|
||||
0x69, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f,
|
||||
0x76, 0x65, 0x20, 0x69, 0x74, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x70, 0x61,
|
||||
0x74, 0x68, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
|
||||
0x28, 0x30, 0x2c, 0x20, 0x70, 0x61, 0x74, 0x68, 0x2e, 0x6c, 0x65, 0x6e,
|
||||
0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x30, 0x29, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64,
|
||||
0x65, 0x72, 0x2e, 0x6f, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x3d, 0x20,
|
||||
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x29,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61,
|
||||
0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x55, 0x69, 0x6e, 0x74, 0x38,
|
||||
0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x65, 0x2e, 0x74, 0x61, 0x72, 0x67,
|
||||
0x65, 0x74, 0x2e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x29, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20,
|
||||
0x64, 0x61, 0x74, 0x61, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53,
|
||||
0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20,
|
||||
0x30, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78,
|
||||
0x28, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x3a,
|
||||
0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x2b, 0x20, 0x27, 0x6f, 0x74, 0x61,
|
||||
0x2f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x27, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x50, 0x4f, 0x53,
|
||||
0x54, 0x27, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x74,
|
||||
0x61, 0x3a, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69,
|
||||
0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x7b, 0x20, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x3a, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x7d, 0x29, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54,
|
||||
0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,
|
||||
0x73, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
|
||||
0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x20, 0x74, 0x68,
|
||||
0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x68,
|
||||
0x75, 0x6e, 0x6b, 0x73, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73,
|
||||
0x65, 0x6e, 0x64, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b,
|
||||
0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x6e,
|
||||
0x64, 0x65, 0x78, 0x20, 0x3c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63,
|
||||
0x68, 0x75, 0x6e, 0x6b, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e,
|
||||
0x73, 0x75, 0x62, 0x61, 0x72, 0x72, 0x61, 0x79, 0x28, 0x69, 0x6e, 0x64,
|
||||
0x65, 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2b, 0x20,
|
||||
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x68, 0x75,
|
||||
0x6e, 0x6b, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x41, 0x72,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x66, 0x72, 0x6f, 0x6d, 0x28, 0x63, 0x68, 0x75,
|
||||
0x6e, 0x6b, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e,
|
||||
0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x70, 0x61, 0x74,
|
||||
0x68, 0x20, 0x2b, 0x20, 0x27, 0x6f, 0x74, 0x61, 0x2f, 0x77, 0x72, 0x69,
|
||||
0x74, 0x65, 0x27, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x50, 0x4f, 0x53,
|
||||
0x54, 0x27, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x2e,
|
||||
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x7b, 0x20,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x2e,
|
||||
0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61,
|
||||
0x3a, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x41, 0x72, 0x72, 0x61, 0x79,
|
||||
0x20, 0x7d, 0x29, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
|
||||
0x65, 0x3a, 0x20, 0x27, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63,
|
||||
0x65, 0x73, 0x73, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x20, 0x2b, 0x3d, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69,
|
||||
0x7a, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x70, 0x64, 0x61,
|
||||
0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
|
||||
0x65, 0x73, 0x73, 0x20, 0x62, 0x61, 0x72, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72,
|
||||
0x20, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x4d,
|
||||
0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x69, 0x6e,
|
||||
0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2a,
|
||||
0x20, 0x31, 0x30, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x27, 0x23,
|
||||
0x62, 0x61, 0x72, 0x27, 0x29, 0x2e, 0x63, 0x73, 0x73, 0x28, 0x27, 0x77,
|
||||
0x69, 0x64, 0x74, 0x68, 0x27, 0x2c, 0x20, 0x70, 0x65, 0x72, 0x63, 0x65,
|
||||
0x6e, 0x74, 0x20, 0x2b, 0x20, 0x27, 0x25, 0x27, 0x29, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x24, 0x28, 0x27, 0x23, 0x70, 0x72, 0x67, 0x27, 0x29, 0x2e, 0x68, 0x74,
|
||||
0x6d, 0x6c, 0x28, 0x22, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e,
|
||||
0x67, 0x3a, 0x20, 0x22, 0x20, 0x2b, 0x20, 0x70, 0x65, 0x72, 0x63, 0x65,
|
||||
0x6e, 0x74, 0x20, 0x2b, 0x20, 0x22, 0x25, 0x22, 0x20, 0x2b, 0x27, 0x20,
|
||||
0x28, 0x27, 0x20, 0x2b, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2b,
|
||||
0x20, 0x27, 0x2f, 0x27, 0x20, 0x2b, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20,
|
||||
0x2b, 0x20, 0x27, 0x29, 0x27, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6e,
|
||||
0x64, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x28, 0x29,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
|
||||
0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x2f, 0x2f, 0x20, 0x45, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20,
|
||||
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x70,
|
||||
0x61, 0x74, 0x68, 0x20, 0x2b, 0x20, 0x27, 0x6f, 0x74, 0x61, 0x2f, 0x65,
|
||||
0x6e, 0x64, 0x27, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x50, 0x4f, 0x53,
|
||||
0x54, 0x27, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
|
||||
0x65, 0x6e, 0x64, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b,
|
||||
0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x7d, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x7d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x41,
|
||||
0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72,
|
||||
0x28, 0x66, 0x69, 0x6c, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||
0x3e, 0x0d, 0x0a, 0x0d, 0x0a, 0x3c, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x72, 0x20, 0x7b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70,
|
||||
0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c,
|
||||
0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61,
|
||||
0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c,
|
||||
0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
|
||||
0x72, 0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x2e,
|
||||
0x35, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74,
|
||||
0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x61, 0x75,
|
||||
0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x72, 0x69, 0x67, 0x68,
|
||||
0x74, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72,
|
||||
0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3a, 0x20, 0x69, 0x6e, 0x73, 0x65,
|
||||
0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68,
|
||||
0x3a, 0x20, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20,
|
||||
0x33, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d,
|
||||
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65,
|
||||
0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a,
|
||||
0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20,
|
||||
0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61,
|
||||
0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67,
|
||||
0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x61, 0x75, 0x74,
|
||||
0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31,
|
||||
0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d,
|
||||
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20,
|
||||
0x23, 0x66, 0x31, 0x66, 0x31, 0x66, 0x31, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72,
|
||||
0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x30,
|
||||
0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x64,
|
||||
0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d,
|
||||
0x69, 0x6d, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x75, 0x72, 0x6c, 0x28, 0x22,
|
||||
0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x66, 0x73, 0x2e, 0x73,
|
||||
0x69, 0x77, 0x61, 0x74, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6f, 0x6e, 0x61, 0x5f, 0x62, 0x67, 0x2e,
|
||||
0x70, 0x6e, 0x67, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x63, 0x6f, 0x76,
|
||||
0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79,
|
||||
0x3a, 0x20, 0x73, 0x61, 0x6e, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x69, 0x66,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66,
|
||||
0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x34,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x37, 0x37, 0x37,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e,
|
||||
0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e,
|
||||
0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x43, 0x43,
|
||||
0x43, 0x43, 0x43, 0x43, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x35,
|
||||
0x45, 0x35, 0x45, 0x35, 0x45, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a,
|
||||
0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x70, 0x78,
|
||||
0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x20, 0x23, 0x64, 0x64, 0x64, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69,
|
||||
0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x34,
|
||||
0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e,
|
||||
0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c,
|
||||
0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73,
|
||||
0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x23, 0x70, 0x72, 0x67, 0x62, 0x61, 0x72, 0x20, 0x7b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63,
|
||||
0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x44, 0x39, 0x44, 0x39, 0x44, 0x39, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72,
|
||||
0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20,
|
||||
0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
|
||||
0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63,
|
||||
0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x32, 0x39, 0x43, 0x44, 0x31,
|
||||
0x46, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x25, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67,
|
||||
0x68, 0x74, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x66, 0x6f, 0x72, 0x6d, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x3a, 0x20, 0x72, 0x67, 0x62, 0x61, 0x28, 0x32, 0x35, 0x35,
|
||||
0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20,
|
||||
0x30, 0x2e, 0x39, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74,
|
||||
0x68, 0x3a, 0x20, 0x32, 0x35, 0x38, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69,
|
||||
0x6e, 0x3a, 0x20, 0x37, 0x35, 0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70,
|
||||
0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x33, 0x30, 0x70, 0x78,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62,
|
||||
0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73,
|
||||
0x3a, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c,
|
||||
0x69, 0x67, 0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x2e, 0x62, 0x74, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
|
||||
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x43, 0x41, 0x33, 0x44,
|
||||
0x33, 0x44, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63,
|
||||
0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62,
|
||||
0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23,
|
||||
0x34, 0x31, 0x37, 0x64, 0x66, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20,
|
||||
0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70,
|
||||
0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20,
|
||||
0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x34,
|
||||
0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64,
|
||||
0x69, 0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69,
|
||||
0x6e, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66,
|
||||
0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x35,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x2f, 0x73, 0x74,
|
||||
0x79, 0x6c, 0x65, 0x3e
|
||||
, 0x00};
|
||||
|
|
@ -1,339 +0,0 @@
|
|||
<div id="loadingSpinner" class="spinner-container" style="display: none;">
|
||||
<div class="spinner"></div>
|
||||
<p id="loadingText">Saving Configuration Please wait (<span id="countdown">15s</span>)</p>
|
||||
</div>
|
||||
</div>
|
||||
<form enctype="multipart/form-data" id="config_form" onsubmit="send_config(event)">
|
||||
<h1>ESPMega PRO</h1>
|
||||
<h3>Device Configurations</h3>
|
||||
<p class="config_title">IP Address</p>
|
||||
<input type="text" id="ip_address" name="ip_address" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">Network Mask</p>
|
||||
<input type="text" id="netmask" name="netmask" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">Gateway</p>
|
||||
<input type="text" id="gateway" name="gateway" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">DNS Server</p>
|
||||
<input type="text" id="dns" name="dns" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">Hostname</p>
|
||||
<input type="text" id="hostname" name="hostname" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">BMS Server - IP Address</p>
|
||||
<input type="text" id="bms_ip" name="bms_ip" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">BMS Server - Port</p>
|
||||
<input type="text" id="bms_port" name="bms_port" class="conf_txt" value="Loading ..."><br>
|
||||
<label class="container">Authentication
|
||||
<input type="checkbox" name="bms_useauth" id="bms_useauth" value="yes">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<p class="config_title">BMS Server - Username</p>
|
||||
<input type="text" id="bms_username" name="bms_username" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">BMS Server - Password</p>
|
||||
<input type="password" id="bms_password" name="bms_password" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">BMS Server - Endpoint</p>
|
||||
<input type="text" id="bms_endpoint" name="bms_endpoint" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">Web Interface - Username</p>
|
||||
<input type="text" id="web_username" name="web_username" class="conf_txt" value="Loading ..."><br>
|
||||
<p class="config_title">Web Interface - Password</p>
|
||||
<input type="password" id="web_password" name="web_password" class="conf_txt" value="Loading ..."><br>
|
||||
<input id="save_btn" type="submit" class="btn" value="Save">
|
||||
<button type="button" class="conf" onclick="window.location.href='/'">Back</button><br /><br />
|
||||
<b>SIWAT SYSTEM 2023</b>
|
||||
</form>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
var saveButton = document.getElementById("save_btn");
|
||||
saveButton.style.pointerEvents = "none";
|
||||
saveButton.style.opacity = "0.6";
|
||||
// Get the configuration data
|
||||
fetch("/get_config")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Handle the response data
|
||||
console.log(data);
|
||||
document.getElementById("ip_address").value = data.ip_address;
|
||||
document.getElementById("netmask").value = data.netmask;
|
||||
document.getElementById("gateway").value = data.gateway;
|
||||
document.getElementById("dns").value = data.dns;
|
||||
document.getElementById("hostname").value = data.hostname;
|
||||
document.getElementById("bms_ip").value = data.bms_ip;
|
||||
document.getElementById("bms_port").value = data.bms_port;
|
||||
document.getElementById("bms_useauth").checked = data.bms_useauth;
|
||||
document.getElementById("bms_username").value = data.bms_username;
|
||||
document.getElementById("bms_password").value = data.bms_password;
|
||||
document.getElementById("bms_endpoint").value = data.bms_endpoint;
|
||||
document.getElementById("web_username").value = data.web_username;
|
||||
document.getElementById("web_password").value = data.web_password;
|
||||
saveButton.style.pointerEvents = "auto";
|
||||
saveButton.style.opacity = "1";
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle any errors
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
function send_config() {
|
||||
event.preventDefault();
|
||||
// Get the form data
|
||||
var formData = {
|
||||
ip_address: document.getElementById("ip_address").value,
|
||||
netmask: document.getElementById("netmask").value,
|
||||
gateway: document.getElementById("gateway").value,
|
||||
dns: document.getElementById("dns").value,
|
||||
hostname: document.getElementById("hostname").value,
|
||||
bms_ip: document.getElementById("bms_ip").value,
|
||||
bms_port: document.getElementById("bms_port").value,
|
||||
bms_useauth: document.getElementById("bms_useauth").checked,
|
||||
bms_username: document.getElementById("bms_username").value,
|
||||
bms_password: document.getElementById("bms_password").value,
|
||||
bms_endpoint: document.getElementById("bms_endpoint").value,
|
||||
web_username: document.getElementById("web_username").value,
|
||||
web_password: document.getElementById("web_password").value
|
||||
};
|
||||
|
||||
// Send the POST request
|
||||
fetch("/save_config", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Handle the response data
|
||||
console.log(data);
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle any errors
|
||||
console.error(error);
|
||||
});
|
||||
// Show the loading spinner
|
||||
document.getElementById('loadingSpinner').style.display = 'block';
|
||||
|
||||
// Start the countdown
|
||||
var countdown = document.getElementById('countdown');
|
||||
var timeLeft = 15;
|
||||
var countdownTimer = setInterval(function () {
|
||||
timeLeft--;
|
||||
countdown.textContent = timeLeft + "s";
|
||||
if (timeLeft <= 0) clearInterval(countdownTimer);
|
||||
}, 1000);
|
||||
|
||||
// Wait for 15 seconds
|
||||
setTimeout(function () {
|
||||
// Redirect user to homepage based on Form's IP
|
||||
window.location.replace("http://" + formData.ip_address);
|
||||
}, 15000);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
p.config_title {
|
||||
font-size: 12;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
align-self: left;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
color: #aaaaaa;
|
||||
background-color: #aaaaaa;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-style: inset;
|
||||
border-width: 0px;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
#file-input,
|
||||
input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
margin: 10px auto;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
background: #f1f1f1;
|
||||
border: 0;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: url("https://fs.siwatsystem.com/arona_bg.png");
|
||||
background-size: cover;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#file-input {
|
||||
background-color: #CCCCCC;
|
||||
color: #5E5E5E;
|
||||
padding: 0;
|
||||
border: 1px solid #ddd;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#bar,
|
||||
#prgbar {
|
||||
background-color: #D9D9D9;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#bar {
|
||||
background-color: #29CD1F;
|
||||
width: 0%;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
form {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
max-width: 258px;
|
||||
margin: 75px auto;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: #CA3D3D;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.conf {
|
||||
background: #417df3;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
margin: 10px auto;
|
||||
font-size: 15px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.conf_txt {
|
||||
background-color: #e2e2e2;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
size: 4;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 0px;
|
||||
margin-bottom: 12px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.container input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.container:hover input~.checkmark {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.container input:checked~.checkmark {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.container input:checked~.checkmark:after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.container .checkmark:after {
|
||||
left: 9px;
|
||||
top: 5px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 3px 3px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
position: fixed;
|
||||
top: 40%;
|
||||
left: 44%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 16px solid #f3f3f3;
|
||||
border-top: 16px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#loadingText {
|
||||
color: #fff;
|
||||
font-size: 40px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 65%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.spinner-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
/* semi-transparent black background */
|
||||
}
|
||||
</style>
|
|
@ -1,164 +0,0 @@
|
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||
<form method="POST" action="#" enctype="multipart/form-data" id="upload_form">
|
||||
<h1>ESPMega PRO</h1>
|
||||
<h3>LCD Display Management</h3>
|
||||
<hr>
|
||||
<h3>Upload TFT Firmware</h3>
|
||||
<input type="file" name="update" id="file" onchange="sub(this)" style="display: none" , accept=".tft" />
|
||||
<label id="file-input" for="file">Choose file...</label>
|
||||
<input type="button" class="btn" value="Upload" onclick="uploadFirmware()" /><br /><br />
|
||||
<div id="prg"></div>
|
||||
<br />
|
||||
<div id="prgbar">
|
||||
<div id="bar"></div>
|
||||
</div>
|
||||
<br />
|
||||
<b>SIWAT SYSTEM 2023</b>
|
||||
</form>
|
||||
<script>
|
||||
function sub(obj) {
|
||||
var fileName = obj.value.split("\\");
|
||||
document.getElementById("file-input").innerHTML =
|
||||
" " + fileName[fileName.length - 1];
|
||||
}
|
||||
|
||||
function uploadFirmware() {
|
||||
var file = document.getElementById('file').files[0];
|
||||
var reader = new FileReader();
|
||||
var path = window.location.pathname;
|
||||
// Path have index.html at the end and / at the beginning, remove it
|
||||
path = path.substring(0, path.length - 10);
|
||||
reader.onload = function (e) {
|
||||
var data = new Uint8Array(e.target.result);
|
||||
var size = data.length;
|
||||
var chunkSize = 256;
|
||||
var index = 0;
|
||||
|
||||
$.ajax({
|
||||
url: path + 'ota/begin',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({ size: size }),
|
||||
contentType: 'application/json',
|
||||
success: function () {
|
||||
// Send the data in chunks
|
||||
function sendNextChunk() {
|
||||
if (index < size) {
|
||||
var chunk = data.subarray(index, index + chunkSize);
|
||||
var chunkArray = Array.from(chunk);
|
||||
$.ajax({
|
||||
url: path + 'ota/write',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({ size: chunk.length, data: chunkArray }),
|
||||
contentType: 'application/json',
|
||||
success: function () {
|
||||
index += chunkSize;
|
||||
// Update the progress bar
|
||||
var percent = Math.floor(index / size * 100);
|
||||
$('#bar').css('width', percent + '%');
|
||||
$('#prg').html("Uploading: " + percent + "%" +' (' + index + '/' + size + ')');
|
||||
sendNextChunk();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// End the update
|
||||
$.ajax({
|
||||
url: path + 'ota/end',
|
||||
type: 'POST'
|
||||
});
|
||||
}
|
||||
}
|
||||
sendNextChunk();
|
||||
}
|
||||
})
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
hr {
|
||||
display: block;
|
||||
color: #aaaaaa;
|
||||
background-color: #aaaaaa;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-style: inset;
|
||||
border-width: 0px;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
#file-input,
|
||||
input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
margin: 10px auto;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
background: #f1f1f1;
|
||||
border: 0;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: url("https://fs.siwatsystem.com/arona_bg.png");
|
||||
background-size: cover;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#file-input {
|
||||
background-color: #CCCCCC;
|
||||
color: #5E5E5E;
|
||||
padding: 0;
|
||||
border: 1px solid #ddd;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#bar,
|
||||
#prgbar {
|
||||
background-color: #D9D9D9;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#bar {
|
||||
background-color: #29CD1F;
|
||||
width: 0%;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
form {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
max-width: 258px;
|
||||
margin: 75px auto;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: #CA3D3D;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.conf {
|
||||
background: #417df3;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
margin: 10px auto;
|
||||
font-size: 15px;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
|
@ -1,227 +0,0 @@
|
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||
<form method="POST" action="#" enctype="multipart/form-data" id="upload_form">
|
||||
<h1>ESPMega PRO</h1>
|
||||
<h3>Device Information</h3>
|
||||
<p style="text-align: left">
|
||||
Hostname
|
||||
<span style="float: right" id="hostname">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
IP Address
|
||||
<span style="float: right" id="ip_address">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
MAC Address
|
||||
<span style="float: right" id="mac_address">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
Uptime
|
||||
<span style="float: right" id="uptime">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
Model
|
||||
<span style="float: right" id="model">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
FW Version
|
||||
<span style="float: right" id="fw_version">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
SDK Version
|
||||
<span style="float: right" id="sdk_version">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
IDF Version
|
||||
<span style="float: right" id="idf_version">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
API Server
|
||||
<span style="float: right" id="api_server">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
API Endpoint
|
||||
<span style="float: right" id="api_endpoint">Loading ...</span>
|
||||
</p>
|
||||
<p style="text-align: left">
|
||||
Centrally Managed
|
||||
<span style="float: right" id="centrally_managed">Loading ...</span>
|
||||
</p>
|
||||
<button type="button" class="conf" onclick="window.location.href='config'">Settings</button><br /><br />
|
||||
<hr>
|
||||
<h3>Upload Software Package</h3>
|
||||
<input type="file" name="update" id="file" onchange="sub(this)" style="display: none" accept=".bin" />
|
||||
<label id="file-input" for="file">Choose file...</label>
|
||||
<input type="submit" class="btn" value="Program" /><br /><br />
|
||||
<div id="prg"></div>
|
||||
<br />
|
||||
<div id="prgbar">
|
||||
<div id="bar"></div>
|
||||
</div>
|
||||
<br />
|
||||
<hr>
|
||||
<h3>Device Control</h3>
|
||||
<input type="button" class="btn" value="Reboot" onclick="window.location.href='/reboot'" />
|
||||
<b>SIWAT SYSTEM 2023</b>
|
||||
</form>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
fetch("/get_device_info")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
document.getElementById("hostname").innerHTML = data.hostname;
|
||||
document.getElementById("ip_address").innerHTML = data.ip_address;
|
||||
document.getElementById("mac_address").innerHTML = data.mac_address;
|
||||
document.getElementById("model").innerHTML = data.model;
|
||||
document.getElementById("fw_version").innerHTML = data.software_version;
|
||||
document.getElementById("sdk_version").innerHTML = data.sdk_version;
|
||||
document.getElementById("idf_version").innerHTML = data.idf_version;
|
||||
document.getElementById("api_server").innerHTML = data.mqtt_server + ":" + data.mqtt_port;
|
||||
document.getElementById("api_endpoint").innerHTML = data.base_topic;
|
||||
document.getElementById("centrally_managed").innerHTML = data.mqtt_connected;
|
||||
var uptime = data.uptime;
|
||||
var uptime_string = "";
|
||||
// Uptime is in seconds, convert it to X days, HH:MM:SS
|
||||
var days = Math.floor(uptime / (24 * 3600));
|
||||
uptime -= days * 24 * 3600;
|
||||
var hours = Math.floor(uptime / 3600);
|
||||
uptime -= hours * 3600;
|
||||
var minutes = Math.floor(uptime / 60);
|
||||
uptime -= minutes * 60;
|
||||
var seconds = uptime;
|
||||
if (days > 0) uptime_string += days + " days, ";
|
||||
uptime_string += hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + seconds.toString().padStart(2, "0");
|
||||
document.getElementById("uptime").innerHTML = uptime_string;
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
function sub(obj) {
|
||||
var fileName = obj.value.split("\\");
|
||||
document.getElementById("file-input").innerHTML =
|
||||
" " + fileName[fileName.length - 1];
|
||||
}
|
||||
$("form").submit(function (e) {
|
||||
e.preventDefault();
|
||||
var form = $("#upload_form")[0];
|
||||
var data = new FormData(form);
|
||||
$.ajax({
|
||||
url: "/ota_update",
|
||||
type: "POST",
|
||||
data: data,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
xhr: function () {
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
xhr.upload.addEventListener(
|
||||
"progress",
|
||||
function (evt) {
|
||||
if (evt.lengthComputable) {
|
||||
var per = evt.loaded / evt.total;
|
||||
if (Math.round(per * 100) < 100) {
|
||||
$("#prg").html("Updating . . . (" + Math.round(per * 100) + "%)");
|
||||
}
|
||||
else {
|
||||
$("#prg").html("Update Completed, Rebooting . . .");
|
||||
}
|
||||
$("#bar").css("width", Math.round(per * 100) + "%");
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
return xhr;
|
||||
},
|
||||
success: function (d, s) {
|
||||
console.log("success!");
|
||||
},
|
||||
error: function (a, b, c) { },
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
hr {
|
||||
display: block;
|
||||
color: #aaaaaa;
|
||||
background-color: #aaaaaa;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-style: inset;
|
||||
border-width: 0px;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
#file-input,
|
||||
input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
margin: 10px auto;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
background: #f1f1f1;
|
||||
border: 0;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: url("https://fs.siwatsystem.com/arona_bg.png");
|
||||
background-size: cover;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#file-input {
|
||||
background-color: #CCCCCC;
|
||||
color: #5E5E5E;
|
||||
padding: 0;
|
||||
border: 1px solid #ddd;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#bar,
|
||||
#prgbar {
|
||||
background-color: #D9D9D9;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#bar {
|
||||
background-color: #29CD1F;
|
||||
width: 0%;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
form {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
max-width: 258px;
|
||||
margin: 75px auto;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: #CA3D3D;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.conf {
|
||||
background: #417df3;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
margin: 10px auto;
|
||||
font-size: 15px;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
|
@ -1,567 +0,0 @@
|
|||
const char ota_html[] PROGMEM = {
|
||||
0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x73, 0x72, 0x63, 0x3d,
|
||||
0x22, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x6a, 0x61,
|
||||
0x78, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x6a, 0x61, 0x78, 0x2f, 0x6c, 0x69,
|
||||
0x62, 0x73, 0x2f, 0x6a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x33, 0x2e,
|
||||
0x32, 0x2e, 0x31, 0x2f, 0x6a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x6d,
|
||||
0x69, 0x6e, 0x2e, 0x6a, 0x73, 0x22, 0x3e, 0x3c, 0x2f, 0x73, 0x63, 0x72,
|
||||
0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x3c, 0x66, 0x6f, 0x72, 0x6d, 0x20,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3d, 0x22, 0x50, 0x4f, 0x53, 0x54,
|
||||
0x22, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x23, 0x22,
|
||||
0x20, 0x65, 0x6e, 0x63, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x6d, 0x75,
|
||||
0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x2f, 0x66, 0x6f, 0x72, 0x6d,
|
||||
0x2d, 0x64, 0x61, 0x74, 0x61, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x75,
|
||||
0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x31, 0x3e, 0x45, 0x53, 0x50, 0x4d,
|
||||
0x65, 0x67, 0x61, 0x20, 0x50, 0x52, 0x4f, 0x3c, 0x2f, 0x68, 0x31, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x33, 0x3e, 0x44, 0x65, 0x76, 0x69,
|
||||
0x63, 0x65, 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78,
|
||||
0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66,
|
||||
0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x48, 0x6f, 0x73,
|
||||
0x74, 0x6e, 0x61, 0x6d, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c,
|
||||
0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22,
|
||||
0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74,
|
||||
0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
|
||||
0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70,
|
||||
0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74,
|
||||
0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74,
|
||||
0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x49, 0x50, 0x20, 0x41,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d,
|
||||
0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68,
|
||||
0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x69, 0x70, 0x5f, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69,
|
||||
0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74,
|
||||
0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c,
|
||||
0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x4d,
|
||||
0x41, 0x43, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74,
|
||||
0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20,
|
||||
0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6d,
|
||||
0x61, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x3e,
|
||||
0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c,
|
||||
0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f,
|
||||
0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79,
|
||||
0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69,
|
||||
0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74,
|
||||
0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20,
|
||||
0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x75,
|
||||
0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69,
|
||||
0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74,
|
||||
0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c,
|
||||
0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x4d,
|
||||
0x6f, 0x64, 0x65, 0x6c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73,
|
||||
0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66,
|
||||
0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22,
|
||||
0x20, 0x69, 0x64, 0x3d, 0x22, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0x3e,
|
||||
0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c,
|
||||
0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f,
|
||||
0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79,
|
||||
0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69,
|
||||
0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x46, 0x57, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61,
|
||||
0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f,
|
||||
0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69,
|
||||
0x64, 0x3d, 0x22, 0x66, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e,
|
||||
0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20,
|
||||
0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d,
|
||||
0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x53, 0x44, 0x4b, 0x20, 0x56,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d,
|
||||
0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68,
|
||||
0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x73, 0x64, 0x6b, 0x5f, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64,
|
||||
0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61,
|
||||
0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22,
|
||||
0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20,
|
||||
0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x49, 0x44, 0x46, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73,
|
||||
0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a,
|
||||
0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22,
|
||||
0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22,
|
||||
0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e,
|
||||
0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74,
|
||||
0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c,
|
||||
0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x41, 0x50, 0x49, 0x20, 0x53, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70,
|
||||
0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c,
|
||||
0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20,
|
||||
0x69, 0x64, 0x3d, 0x22, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76,
|
||||
0x65, 0x72, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
|
||||
0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70,
|
||||
0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74,
|
||||
0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74,
|
||||
0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x41, 0x50, 0x49, 0x20,
|
||||
0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c,
|
||||
0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69,
|
||||
0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x61, 0x70, 0x69,
|
||||
0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x3e, 0x4c,
|
||||
0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f,
|
||||
0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70,
|
||||
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c,
|
||||
0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67,
|
||||
0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79,
|
||||
0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c,
|
||||
0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69,
|
||||
0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x63, 0x65, 0x6e,
|
||||
0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||
0x65, 0x64, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
|
||||
0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x62,
|
||||
0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22,
|
||||
0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73,
|
||||
0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x20, 0x6f, 0x6e, 0x63,
|
||||
0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77,
|
||||
0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x68, 0x72,
|
||||
0x65, 0x66, 0x3d, 0x27, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x27, 0x22,
|
||||
0x3e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x3c, 0x2f, 0x62,
|
||||
0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e,
|
||||
0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68,
|
||||
0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x33, 0x3e, 0x55, 0x70,
|
||||
0x6c, 0x6f, 0x61, 0x64, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
|
||||
0x65, 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x3c, 0x2f, 0x68,
|
||||
0x33, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74,
|
||||
0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x22,
|
||||
0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x75, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x22,
|
||||
0x20, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x22, 0x73,
|
||||
0x75, 0x62, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x22, 0x20, 0x73, 0x74,
|
||||
0x79, 0x6c, 0x65, 0x3d, 0x22, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
|
||||
0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x61, 0x63, 0x63, 0x65,
|
||||
0x70, 0x74, 0x3d, 0x22, 0x2e, 0x62, 0x69, 0x6e, 0x22, 0x20, 0x2f, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x69,
|
||||
0x64, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x22, 0x20, 0x66, 0x6f, 0x72, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65,
|
||||
0x22, 0x3e, 0x43, 0x68, 0x6f, 0x6f, 0x73, 0x65, 0x20, 0x66, 0x69, 0x6c,
|
||||
0x65, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74,
|
||||
0x79, 0x70, 0x65, 0x3d, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x22,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x74, 0x6e, 0x22,
|
||||
0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x50, 0x72, 0x6f, 0x67,
|
||||
0x72, 0x61, 0x6d, 0x22, 0x20, 0x2f, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f,
|
||||
0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x70, 0x72, 0x67, 0x22,
|
||||
0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
|
||||
0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x64, 0x69,
|
||||
0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x70, 0x72, 0x67, 0x62, 0x61, 0x72,
|
||||
0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76,
|
||||
0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x61, 0x72, 0x22, 0x3e, 0x3c, 0x2f,
|
||||
0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x69,
|
||||
0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x3c, 0x68, 0x33, 0x3e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x43,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79,
|
||||
0x70, 0x65, 0x3d, 0x22, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x74, 0x6e, 0x22, 0x20,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x52, 0x65, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22,
|
||||
0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x2e, 0x68, 0x72, 0x65, 0x66, 0x3d, 0x27, 0x2f, 0x72,
|
||||
0x65, 0x62, 0x6f, 0x6f, 0x74, 0x27, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x3c, 0x62, 0x3e, 0x53, 0x49, 0x57, 0x41, 0x54, 0x20, 0x53,
|
||||
0x59, 0x53, 0x54, 0x45, 0x4d, 0x20, 0x32, 0x30, 0x32, 0x33, 0x3c, 0x2f,
|
||||
0x62, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x0d,
|
||||
0x0a, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f, 0x6e, 0x6c, 0x6f,
|
||||
0x61, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x66, 0x65, 0x74, 0x63, 0x68, 0x28, 0x22, 0x2f, 0x67, 0x65, 0x74, 0x5f,
|
||||
0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22,
|
||||
0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x74, 0x68,
|
||||
0x65, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20,
|
||||
0x3d, 0x3e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e,
|
||||
0x6a, 0x73, 0x6f, 0x6e, 0x28, 0x29, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x2e, 0x74, 0x68, 0x65, 0x6e, 0x28, 0x64, 0x61, 0x74,
|
||||
0x61, 0x20, 0x3d, 0x3e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e,
|
||||
0x6c, 0x6f, 0x67, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x68, 0x6f, 0x73,
|
||||
0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65,
|
||||
0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61,
|
||||
0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x69, 0x70, 0x5f,
|
||||
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x29, 0x2e, 0x69, 0x6e,
|
||||
0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61,
|
||||
0x74, 0x61, 0x2e, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
|
||||
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
|
||||
0x22, 0x6d, 0x61, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c,
|
||||
0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x61, 0x63, 0x5f,
|
||||
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x6d, 0x6f, 0x64, 0x65, 0x6c,
|
||||
0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c,
|
||||
0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x6f, 0x64, 0x65,
|
||||
0x6c, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
|
||||
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
|
||||
0x22, 0x66, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22,
|
||||
0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20,
|
||||
0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x73, 0x6f, 0x66, 0x74, 0x77,
|
||||
0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
|
||||
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
|
||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x73,
|
||||
0x64, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x29,
|
||||
0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x73, 0x64, 0x6b, 0x5f, 0x76, 0x65,
|
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
|
||||
0x79, 0x49, 0x64, 0x28, 0x22, 0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72,
|
||||
0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e,
|
||||
0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
|
||||
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
|
||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x61,
|
||||
0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x29, 0x2e,
|
||||
0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20,
|
||||
0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, 0x20, 0x22, 0x3a, 0x22, 0x20, 0x2b,
|
||||
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67,
|
||||
0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49,
|
||||
0x64, 0x28, 0x22, 0x61, 0x70, 0x69, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f,
|
||||
0x69, 0x6e, 0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48,
|
||||
0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x62,
|
||||
0x61, 0x73, 0x65, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x63, 0x65, 0x6e,
|
||||
0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||
0x65, 0x64, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54,
|
||||
0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71,
|
||||
0x74, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76,
|
||||
0x61, 0x72, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x3d, 0x20,
|
||||
0x64, 0x61, 0x74, 0x61, 0x2e, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61,
|
||||
0x72, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x72,
|
||||
0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x70,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x73,
|
||||
0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x76,
|
||||
0x65, 0x72, 0x74, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x58, 0x20,
|
||||
0x64, 0x61, 0x79, 0x73, 0x2c, 0x20, 0x48, 0x48, 0x3a, 0x4d, 0x4d, 0x3a,
|
||||
0x53, 0x53, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x4d,
|
||||
0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x75, 0x70,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x20, 0x2f, 0x20, 0x28, 0x32, 0x34, 0x20, 0x2a,
|
||||
0x20, 0x33, 0x36, 0x30, 0x30, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x20, 0x2d, 0x3d, 0x20, 0x64, 0x61, 0x79, 0x73, 0x20, 0x2a, 0x20, 0x32,
|
||||
0x34, 0x20, 0x2a, 0x20, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x68,
|
||||
0x6f, 0x75, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e,
|
||||
0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x20, 0x2f, 0x20, 0x33, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x20, 0x2d, 0x3d, 0x20, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x20, 0x2a,
|
||||
0x20, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6d, 0x69, 0x6e, 0x75,
|
||||
0x74, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x66,
|
||||
0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x20,
|
||||
0x2f, 0x20, 0x36, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x2d,
|
||||
0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x20, 0x2a, 0x20,
|
||||
0x36, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73,
|
||||
0x20, 0x3d, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28,
|
||||
0x64, 0x61, 0x79, 0x73, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x20, 0x75, 0x70,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20,
|
||||
0x2b, 0x3d, 0x20, 0x64, 0x61, 0x79, 0x73, 0x20, 0x2b, 0x20, 0x22, 0x20,
|
||||
0x64, 0x61, 0x79, 0x73, 0x2c, 0x20, 0x22, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x2b, 0x3d, 0x20, 0x68,
|
||||
0x6f, 0x75, 0x72, 0x73, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e,
|
||||
0x67, 0x28, 0x29, 0x2e, 0x70, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74,
|
||||
0x28, 0x32, 0x2c, 0x20, 0x22, 0x30, 0x22, 0x29, 0x20, 0x2b, 0x20, 0x22,
|
||||
0x3a, 0x22, 0x20, 0x2b, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73,
|
||||
0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x2e,
|
||||
0x70, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x28, 0x32, 0x2c, 0x20,
|
||||
0x22, 0x30, 0x22, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x3a, 0x22, 0x20, 0x2b,
|
||||
0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x74, 0x6f, 0x53,
|
||||
0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x2e, 0x70, 0x61, 0x64, 0x53,
|
||||
0x74, 0x61, 0x72, 0x74, 0x28, 0x32, 0x2c, 0x20, 0x22, 0x30, 0x22, 0x29,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64,
|
||||
0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45,
|
||||
0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22,
|
||||
0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e,
|
||||
0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x75, 0x70, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x2e, 0x63, 0x61,
|
||||
0x74, 0x63, 0x68, 0x28, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x3e,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f,
|
||||
0x72, 0x28, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x20, 0x73, 0x75, 0x62, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66,
|
||||
0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x6f, 0x62,
|
||||
0x6a, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x73, 0x70, 0x6c, 0x69,
|
||||
0x74, 0x28, 0x22, 0x5c, 0x5c, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67,
|
||||
0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49,
|
||||
0x64, 0x28, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d,
|
||||
0x4c, 0x20, 0x3d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||
0x20, 0x20, 0x20, 0x22, 0x20, 0x2b, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x4e,
|
||||
0x61, 0x6d, 0x65, 0x5b, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65,
|
||||
0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x24, 0x28,
|
||||
0x22, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x29, 0x2e, 0x73, 0x75, 0x62, 0x6d,
|
||||
0x69, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
|
||||
0x28, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61,
|
||||
0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x76, 0x61, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x3d, 0x20, 0x24,
|
||||
0x28, 0x22, 0x23, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f,
|
||||
0x72, 0x6d, 0x22, 0x29, 0x5b, 0x30, 0x5d, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d,
|
||||
0x20, 0x6e, 0x65, 0x77, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74,
|
||||
0x61, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x22,
|
||||
0x2f, 0x6f, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22,
|
||||
0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
|
||||
0x65, 0x3a, 0x20, 0x22, 0x50, 0x4f, 0x53, 0x54, 0x22, 0x2c, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x20,
|
||||
0x64, 0x61, 0x74, 0x61, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65,
|
||||
0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44,
|
||||
0x61, 0x74, 0x61, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x68, 0x72, 0x3a, 0x20,
|
||||
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76,
|
||||
0x61, 0x72, 0x20, 0x78, 0x68, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77,
|
||||
0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x58, 0x4d, 0x4c, 0x48,
|
||||
0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x29,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78,
|
||||
0x68, 0x72, 0x2e, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x61, 0x64,
|
||||
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||
0x65, 0x72, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73,
|
||||
0x22, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28,
|
||||
0x65, 0x76, 0x74, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28,
|
||||
0x65, 0x76, 0x74, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x43, 0x6f,
|
||||
0x6d, 0x70, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x7b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x65, 0x72, 0x20, 0x3d,
|
||||
0x20, 0x65, 0x76, 0x74, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20,
|
||||
0x2f, 0x20, 0x65, 0x76, 0x74, 0x2e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x4d, 0x61, 0x74, 0x68,
|
||||
0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x70, 0x65, 0x72, 0x20, 0x2a,
|
||||
0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x30, 0x30, 0x29,
|
||||
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23,
|
||||
0x70, 0x72, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x22,
|
||||
0x55, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x20, 0x2e,
|
||||
0x20, 0x2e, 0x20, 0x28, 0x22, 0x20, 0x2b, 0x20, 0x4d, 0x61, 0x74, 0x68,
|
||||
0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x70, 0x65, 0x72, 0x20, 0x2a,
|
||||
0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x25, 0x29, 0x22,
|
||||
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
|
||||
0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24,
|
||||
0x28, 0x22, 0x23, 0x70, 0x72, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d,
|
||||
0x6c, 0x28, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x43, 0x6f,
|
||||
0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x52, 0x65, 0x62,
|
||||
0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x20, 0x2e, 0x20, 0x2e,
|
||||
0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x24, 0x28, 0x22, 0x23, 0x62, 0x61, 0x72, 0x22, 0x29, 0x2e, 0x63, 0x73,
|
||||
0x73, 0x28, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x2c, 0x20, 0x4d,
|
||||
0x61, 0x74, 0x68, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x70, 0x65,
|
||||
0x72, 0x20, 0x2a, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x22,
|
||||
0x25, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x61, 0x6c,
|
||||
0x73, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x78, 0x68, 0x72, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
|
||||
0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28,
|
||||
0x64, 0x2c, 0x20, 0x73, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65,
|
||||
0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x22, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,
|
||||
0x73, 0x21, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x63, 0x29,
|
||||
0x20, 0x7b, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
|
||||
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x3c,
|
||||
0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x3c, 0x73,
|
||||
0x74, 0x79, 0x6c, 0x65, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x68, 0x72, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c,
|
||||
0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23,
|
||||
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d,
|
||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x61, 0x61, 0x61,
|
||||
0x61, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x2e, 0x35,
|
||||
0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20,
|
||||
0x30, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x3a,
|
||||
0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74,
|
||||
0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x79, 0x6c,
|
||||
0x65, 0x3a, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x74, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x77, 0x69,
|
||||
0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x33,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68,
|
||||
0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x34, 0x34, 0x70,
|
||||
0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64,
|
||||
0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x34,
|
||||
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
|
||||
0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x61, 0x75,
|
||||
0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e,
|
||||
0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x35, 0x70, 0x78,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a,
|
||||
0x20, 0x23, 0x66, 0x31, 0x66, 0x31, 0x66, 0x31, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69,
|
||||
0x6e, 0x67, 0x3a, 0x20, 0x30, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x62, 0x6f,
|
||||
0x64, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61,
|
||||
0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x69, 0x6d, 0x61,
|
||||
0x67, 0x65, 0x3a, 0x20, 0x75, 0x72, 0x6c, 0x28, 0x22, 0x68, 0x74, 0x74,
|
||||
0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x66, 0x73, 0x2e, 0x73, 0x69, 0x77, 0x61,
|
||||
0x74, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||
0x61, 0x72, 0x6f, 0x6e, 0x61, 0x5f, 0x62, 0x67, 0x2e, 0x70, 0x6e, 0x67,
|
||||
0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63,
|
||||
0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x3a, 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c,
|
||||
0x79, 0x3a, 0x20, 0x73, 0x61, 0x6e, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x69,
|
||||
0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74,
|
||||
0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x34, 0x70, 0x78, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
|
||||
0x20, 0x23, 0x37, 0x37, 0x37, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d,
|
||||
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69,
|
||||
0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63,
|
||||
0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x43, 0x43, 0x43, 0x43, 0x43,
|
||||
0x43, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x35, 0x45, 0x35, 0x45, 0x35, 0x45, 0x3b, 0x0d,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
|
||||
0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f,
|
||||
0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6f,
|
||||
0x6c, 0x69, 0x64, 0x20, 0x23, 0x64, 0x64, 0x64, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67,
|
||||
0x68, 0x74, 0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67,
|
||||
0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
|
||||
0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72, 0x2c, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x23, 0x70, 0x72, 0x67, 0x62, 0x61, 0x72, 0x20, 0x7b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x44,
|
||||
0x39, 0x44, 0x39, 0x44, 0x39, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75,
|
||||
0x73, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
|
||||
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
|
||||
0x20, 0x23, 0x32, 0x39, 0x43, 0x44, 0x31, 0x46, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x25,
|
||||
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
|
||||
0x74, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20,
|
||||
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
|
||||
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x72, 0x67, 0x62, 0x61, 0x28,
|
||||
0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35,
|
||||
0x35, 0x2c, 0x20, 0x30, 0x2e, 0x39, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68,
|
||||
0x3a, 0x20, 0x32, 0x35, 0x38, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x37, 0x35,
|
||||
0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x33,
|
||||
0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f,
|
||||
0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a,
|
||||
0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20,
|
||||
0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d,
|
||||
0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x62, 0x74, 0x6e, 0x20, 0x7b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x43, 0x41, 0x33, 0x44, 0x33,
|
||||
0x44, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d,
|
||||
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x7b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x34, 0x31, 0x37, 0x64, 0x66,
|
||||
0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||
0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b,
|
||||
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
|
||||
0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69,
|
||||
0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30,
|
||||
0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a,
|
||||
0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a,
|
||||
0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65,
|
||||
0x3e
|
||||
, 0x00};
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "windows-gcc-x64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"compilerPath": "gcc",
|
||||
"cStandard": "${default}",
|
||||
"cppStandard": "${default}",
|
||||
"intelliSenseMode": "windows-gcc-x64",
|
||||
"compilerArgs": [
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C/C++ Runner: Debug Session",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"cwd": "d:/Git/ESPMegaPRO-v3-SDK/Template Project/src",
|
||||
"program": "d:/Git/ESPMegaPRO-v3-SDK/Template Project/src/build/Debug/outDebug",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "gcc",
|
||||
"C_Cpp_Runner.cppCompilerPath": "g++",
|
||||
"C_Cpp_Runner.debuggerPath": "gdb",
|
||||
"C_Cpp_Runner.cStandard": "",
|
||||
"C_Cpp_Runner.cppStandard": "",
|
||||
"C_Cpp_Runner.msvcBatchPath": "",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
#include <ESPMegaPRO_OOP.hpp>
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
|
||||
void setup() {
|
||||
espmega.begin();
|
||||
//espmega.fram.write8(301, 25);
|
||||
// Dump FRAM to a prettified table
|
||||
for (int i = 0; i < 500; i++) {
|
||||
if (i % 16 == 0) {
|
||||
Serial.printf("\n%03d: ", i);
|
||||
}
|
||||
Serial.printf("%03d ", espmega.fram.read8(i));
|
||||
}
|
||||
|
||||
// Dump FRAM again but treat it as a long string
|
||||
Serial.printf("\n\n");
|
||||
for (int i = 0; i < 500; i++) {
|
||||
Serial.printf("%d: %c\n", i,espmega.fram.read8(i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
espmega.loop();
|
||||
}
|
|
@ -1,505 +0,0 @@
|
|||
//
|
||||
// FILE: MultiSpeedI2CScanner.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.17
|
||||
// PURPOSE: I2C scanner at different speeds
|
||||
// DATE: 2013-11-05
|
||||
// URL: https://github.com/RobTillaart/MultiSpeedI2CScanner
|
||||
// URL: http://forum.arduino.cc/index.php?topic=197360
|
||||
//
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
// FOR INTERNAL I2C BUS NANO 33 BLE
|
||||
// #define WIRE_IMPLEMENT_WIRE1 1
|
||||
// extern TwoWire Wire1;
|
||||
|
||||
void I2Cscan();
|
||||
void displayHelp();
|
||||
void reset();
|
||||
void setAddress();
|
||||
void setSpeed(char sp);
|
||||
char getCommand();
|
||||
|
||||
|
||||
TwoWire *wire;
|
||||
|
||||
const char version[] = "0.1.16";
|
||||
|
||||
|
||||
// INTERFACE COUNT (TESTED TEENSY 3.5 AND ARDUINO DUE ONLY)
|
||||
int wirePortCount = 1;
|
||||
int selectedWirePort = 0;
|
||||
|
||||
|
||||
// scans devices from 50 to 800 KHz I2C speeds.
|
||||
// speed lower than 50 and above 400 can cause problems
|
||||
long speed[10] = { 100, 200, 300, 400 };
|
||||
int speeds;
|
||||
|
||||
int addressStart = 8;
|
||||
int addressEnd = 119;
|
||||
|
||||
|
||||
// DELAY BETWEEN TESTS
|
||||
// for delay between tests of found devices.
|
||||
#ifndef RESTORE_LATENCY
|
||||
#define RESTORE_LATENCY 5
|
||||
#endif
|
||||
|
||||
bool delayFlag = false;
|
||||
|
||||
|
||||
// MINIMIZE OUTPUT
|
||||
bool printAll = true;
|
||||
bool header = true;
|
||||
bool disableIRQ = false;
|
||||
|
||||
|
||||
// STATE MACHINE
|
||||
enum states {
|
||||
STOP, ONCE, CONT, HELP
|
||||
};
|
||||
states state = STOP;
|
||||
|
||||
|
||||
// TIMING
|
||||
uint32_t startScan;
|
||||
uint32_t stopScan;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MAIN CODE
|
||||
//
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
while (!Serial);
|
||||
|
||||
Wire.begin(14,33);
|
||||
|
||||
#if defined WIRE_IMPLEMENT_WIRE1 || WIRE_INTERFACES_COUNT > 1
|
||||
Wire1.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE2 || WIRE_INTERFACES_COUNT > 2
|
||||
Wire2.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE3 || WIRE_INTERFACES_COUNT > 3
|
||||
Wire3.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE4 || WIRE_INTERFACES_COUNT > 4
|
||||
Wire4.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE5 || WIRE_INTERFACES_COUNT > 5
|
||||
Wire5.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
|
||||
wire = &Wire;
|
||||
|
||||
Serial.println();
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
yield();
|
||||
char command = getCommand();
|
||||
switch (command)
|
||||
{
|
||||
case '@':
|
||||
selectedWirePort = (selectedWirePort + 1) % wirePortCount;
|
||||
Serial.print(F("<I2C PORT=Wire"));
|
||||
Serial.print(selectedWirePort);
|
||||
Serial.println(F(">"));
|
||||
switch (selectedWirePort)
|
||||
{
|
||||
case 0:
|
||||
wire = &Wire;
|
||||
break;
|
||||
#if defined WIRE_IMPLEMENT_WIRE1 || WIRE_INTERFACES_COUNT > 1
|
||||
case 1:
|
||||
wire = &Wire1;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE2 || WIRE_INTERFACES_COUNT > 2
|
||||
case 2:
|
||||
wire = &Wire2;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE3 || WIRE_INTERFACES_COUNT > 3
|
||||
case 3:
|
||||
wire = &Wire3;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE4 || WIRE_INTERFACES_COUNT > 4
|
||||
case 4:
|
||||
wire = &Wire4;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE5 || WIRE_INTERFACES_COUNT > 5
|
||||
case 5:
|
||||
wire = &Wire5;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
state = ONCE;
|
||||
break;
|
||||
case 'c':
|
||||
state = CONT;
|
||||
break;
|
||||
case 'd':
|
||||
delayFlag = !delayFlag;
|
||||
Serial.print(F("<delay="));
|
||||
Serial.println(delayFlag ? F("5>") : F("0>"));
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
// eeprom test TODO
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
header = !header;
|
||||
Serial.print(F("<header="));
|
||||
Serial.println(header ? F("yes>") : F("no>"));
|
||||
break;
|
||||
case 'p':
|
||||
printAll = !printAll;
|
||||
Serial.print(F("<print="));
|
||||
Serial.println(printAll ? F("all>") : F("found>"));
|
||||
break;
|
||||
case 'i':
|
||||
disableIRQ = !disableIRQ;
|
||||
Serial.print(F("<irq="));
|
||||
Serial.println(disableIRQ ? F("diabled>") : F("enabled>"));
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '4':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'O':
|
||||
case 'P':
|
||||
setSpeed(command);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
reset();
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
setAddress();
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
case '?':
|
||||
state = HELP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ONCE:
|
||||
I2Cscan();
|
||||
state = HELP;
|
||||
break;
|
||||
case CONT:
|
||||
I2Cscan();
|
||||
delay(1000);
|
||||
break;
|
||||
case HELP:
|
||||
displayHelp();
|
||||
state = STOP;
|
||||
break;
|
||||
case STOP:
|
||||
break;
|
||||
default: // ignore all non commands
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void reset()
|
||||
{
|
||||
setSpeed('9');
|
||||
selectedWirePort = 0;
|
||||
addressStart = 8;
|
||||
addressEnd = 119;
|
||||
|
||||
delayFlag = false;
|
||||
printAll = true;
|
||||
header = true;
|
||||
disableIRQ = false;
|
||||
|
||||
state = STOP;
|
||||
|
||||
displayHelp();
|
||||
}
|
||||
|
||||
|
||||
void setAddress()
|
||||
{
|
||||
if (addressStart == 0)
|
||||
{
|
||||
addressStart = 8;
|
||||
addressEnd = 119;
|
||||
}
|
||||
else
|
||||
{
|
||||
addressStart = 0;
|
||||
addressEnd = 127;
|
||||
}
|
||||
Serial.print(F("<address Range = "));
|
||||
Serial.print(addressStart);
|
||||
Serial.print(F(".."));
|
||||
Serial.print(addressEnd);
|
||||
Serial.println(F(">"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void setSpeed(char sp)
|
||||
{
|
||||
switch (sp)
|
||||
{
|
||||
case '1':
|
||||
speed[0] = 100;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '2':
|
||||
speed[0] = 200;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '4':
|
||||
speed[0] = 400;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '8':
|
||||
speed[0] = 800;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '9': // limited to 400 KHz
|
||||
speeds = 8;
|
||||
for (int i = 1; i <= speeds; i++) speed[i - 1] = i * 50;
|
||||
break;
|
||||
case '0': // limited to 800 KHz
|
||||
speeds = 8;
|
||||
for (int i = 1; i <= speeds; i++) speed[i - 1] = i * 100;
|
||||
break;
|
||||
|
||||
// new in 0.1.10 - experimental
|
||||
case 'M':
|
||||
speed[0] = 1000;
|
||||
speeds = 1;
|
||||
break;
|
||||
case 'N':
|
||||
speed[0] = 3400;
|
||||
speeds = 1;
|
||||
break;
|
||||
case 'O':
|
||||
speed[0] = 5000;
|
||||
speeds = 1;
|
||||
break;
|
||||
case 'P':
|
||||
speed[0] = 100;
|
||||
speed[1] = 400;
|
||||
speed[2] = 1000;
|
||||
speed[3] = 3400;
|
||||
speed[4] = 5000;
|
||||
speeds = 5;
|
||||
break;
|
||||
}
|
||||
Serial.print("<speeds =");
|
||||
for (int i = 0; i < speeds; i++)
|
||||
{
|
||||
Serial.print(' ');
|
||||
Serial.print(speed[i]);
|
||||
}
|
||||
Serial.println(" >");
|
||||
}
|
||||
|
||||
|
||||
char getCommand()
|
||||
{
|
||||
char c = '\0';
|
||||
if (Serial.available())
|
||||
{
|
||||
c = Serial.read();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void displayHelp()
|
||||
{
|
||||
Serial.print(F("\nArduino MultiSpeed I2C Scanner - "));
|
||||
Serial.println(version);
|
||||
Serial.println();
|
||||
Serial.print(F("I2C ports: "));
|
||||
Serial.print(wirePortCount);
|
||||
Serial.print(F(" Current: Wire"));
|
||||
Serial.println(selectedWirePort);
|
||||
Serial.println(F("\t@ = toggle Wire - Wire1 .. Wire5 [e.g. TEENSY or Arduino Due]"));
|
||||
|
||||
Serial.println(F("Scan mode:"));
|
||||
Serial.println(F("\ts = single scan"));
|
||||
Serial.println(F("\tc = continuous scan - 1 second delay"));
|
||||
Serial.println(F("\tq = quit continuous scan"));
|
||||
Serial.println(F("\td = toggle latency delay between successful tests. 0 - 5 ms"));
|
||||
Serial.println(F("\ti = toggle enable/disable interrupts"));
|
||||
|
||||
Serial.println(F("Output:"));
|
||||
Serial.println(F("\tp = toggle printAll - printFound."));
|
||||
Serial.println(F("\th = toggle header - noHeader."));
|
||||
Serial.println(F("\ta = toggle address range, 0..127 - 8..119 (default)"));
|
||||
|
||||
Serial.println(F("Speeds:"));
|
||||
Serial.println(F("\t0 = 100..800 KHz - step 100 (warning - can block!!)"));
|
||||
Serial.println(F("\t1 = 100 KHz"));
|
||||
Serial.println(F("\t2 = 200 KHz"));
|
||||
Serial.println(F("\t4 = 400 KHz"));
|
||||
Serial.println(F("\t9 = 50..400 KHz - step 50 < DEFAULT >"));
|
||||
Serial.println();
|
||||
Serial.println(F("\t!! HIGH SPEEDS - WARNING - can block - not applicable for UNO"));
|
||||
Serial.println(F("\t8 = 800 KHz"));
|
||||
Serial.println(F("\tM = 1000 KHz"));
|
||||
Serial.println(F("\tN = 3400 KHz"));
|
||||
Serial.println(F("\tO = 5000 KHz"));
|
||||
Serial.println(F("\tP = 100 400 1000 3400 5000 KHz (standards)"));
|
||||
Serial.println(F("\n\tr = reset to startup defaults."));
|
||||
Serial.println(F("\t? = help - this page"));
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
|
||||
void I2Cscan()
|
||||
{
|
||||
startScan = millis();
|
||||
uint8_t count = 0;
|
||||
|
||||
if (disableIRQ)
|
||||
{
|
||||
noInterrupts();
|
||||
}
|
||||
|
||||
if (header)
|
||||
{
|
||||
Serial.print(F("TIME\tDEC\tHEX\t"));
|
||||
for (uint8_t s = 0; s < speeds; s++)
|
||||
{
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(speed[s]);
|
||||
}
|
||||
Serial.println(F("\t[KHz]"));
|
||||
for (uint8_t s = 0; s < speeds + 5; s++)
|
||||
{
|
||||
Serial.print(F("--------"));
|
||||
}
|
||||
Serial.println();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
for (uint8_t address = addressStart; address <= addressEnd; address++)
|
||||
{
|
||||
bool printLine = printAll;
|
||||
bool found[speeds];
|
||||
bool fnd = false;
|
||||
|
||||
for (uint8_t s = 0; s < speeds ; s++)
|
||||
{
|
||||
yield(); // keep ESP happy
|
||||
|
||||
#if ARDUINO < 158 && defined (TWBR)
|
||||
uint16_t PREV_TWBR = TWBR;
|
||||
TWBR = (F_CPU / (speed[s] * 1000) - 16) / 2;
|
||||
if (TWBR < 2)
|
||||
{
|
||||
Serial.println("ERROR: not supported speed");
|
||||
TWBR = PREV_TWBR;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
wire->setClock(speed[s] * 1000UL);
|
||||
#endif
|
||||
wire->beginTransmission (address);
|
||||
found[s] = (wire->endTransmission () == 0);
|
||||
fnd |= found[s];
|
||||
// give device 5 millis
|
||||
if (fnd && delayFlag) delay(RESTORE_LATENCY);
|
||||
}
|
||||
|
||||
if (fnd) count++;
|
||||
printLine |= fnd;
|
||||
|
||||
if (printLine)
|
||||
{
|
||||
Serial.print(millis());
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(address, DEC);
|
||||
Serial.print(F("\t0x"));
|
||||
if (address < 0x10) Serial.print(0, HEX);
|
||||
Serial.print(address, HEX);
|
||||
Serial.print(F("\t"));
|
||||
|
||||
for (uint8_t s = 0; s < speeds ; s++)
|
||||
{
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(found[s] ? F("V") : F("."));
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// FOOTER
|
||||
if (header)
|
||||
{
|
||||
for (uint8_t s = 0; s < speeds + 5; s++)
|
||||
{
|
||||
Serial.print(F("--------"));
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
Serial.print(F("TIME\tDEC\tHEX\t"));
|
||||
for (uint8_t s = 0; s < speeds; s++)
|
||||
{
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(speed[s]);
|
||||
}
|
||||
Serial.println(F("\t[KHz]"));
|
||||
}
|
||||
*/
|
||||
|
||||
stopScan = millis();
|
||||
if (header)
|
||||
{
|
||||
Serial.println();
|
||||
Serial.print(count);
|
||||
Serial.print(F(" devices found in "));
|
||||
Serial.print(stopScan - startScan);
|
||||
Serial.println(F(" milliseconds."));
|
||||
}
|
||||
|
||||
interrupts();
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
|
@ -1,382 +0,0 @@
|
|||
/**
|
||||
* @file main.cpp
|
||||
* @brief Test firmware for the ESPMegaPRO OOP library
|
||||
*/
|
||||
#include <ESPMegaProOS.hpp>
|
||||
#include <InternalDisplay.hpp>
|
||||
#include <ETH.h>
|
||||
#include <ClimateCard.hpp>
|
||||
#include <ESPMegaDisplayOTA.hpp>
|
||||
#include <RemoteVariable.hpp>
|
||||
#include <CurrentTransformerCard.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
#include <SmartVariable.hpp>
|
||||
|
||||
#define INPUT_DEBOUNCE_TIME_MS 1000
|
||||
|
||||
// #define FRAM_DEBUG
|
||||
// #define MQTT_DEBUG
|
||||
// #define RTC_DEBUG
|
||||
// #define WRITE_DEFAULT_NETCONF
|
||||
// #define CLIMATE_CARD_ENABLE
|
||||
#define MQTT_CARD_REGISTER
|
||||
// #define DISPLAY_ENABLE
|
||||
#define WEB_SERVER_ENABLE
|
||||
// #define LCD_OTA_ENABLE
|
||||
// #define REMOTE_VARIABLE_ENABLE
|
||||
// #define CT_ENABLE
|
||||
// #define SMART_VARIABLE_ENABLE
|
||||
#define EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
|
||||
#define EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
|
||||
// Demo PLC firmware using the ESPMegaPRO OOP library
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
|
||||
#ifdef EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
|
||||
DigitalOutputCard externalDigitalOutputCard = DigitalOutputCard(1, 0, 1, 1, 0);
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
|
||||
void handleExternalDigitalInput(uint8_t pin, uint8_t state)
|
||||
{
|
||||
Serial.printf("Digital Input External %d: %d\n", pin, state);
|
||||
}
|
||||
|
||||
DigitalInputCard externalDigitalInputCard = DigitalInputCard(0, 0, 1, 0, 1, 1);
|
||||
#endif
|
||||
|
||||
// Remote Variable
|
||||
#ifdef REMOTE_VARIABLE_ENABLE
|
||||
RemoteVariable testVar = RemoteVariable();
|
||||
#endif
|
||||
|
||||
#ifdef LCD_OTA_ENABLE
|
||||
ESPMegaDisplayOTA internalDisplayOTA = ESPMegaDisplayOTA();
|
||||
#endif
|
||||
|
||||
#ifdef CT_ENABLE
|
||||
float adc2current(uint16_t adcValue)
|
||||
{
|
||||
return (adcValue - 2048) * 0.0005;
|
||||
}
|
||||
AnalogCard analogCard = AnalogCard();
|
||||
float voltage = 220.0;
|
||||
CurrentTransformerCard ct = CurrentTransformerCard(&analogCard, 0, &voltage, adc2current, 1000);
|
||||
#endif
|
||||
|
||||
#ifdef SMART_VARIABLE_ENABLE
|
||||
SmartVariable smartVar = SmartVariable();
|
||||
#endif
|
||||
|
||||
#ifdef CLIMATE_CARD_ENABLE
|
||||
// Climate Card
|
||||
const char *mode_names[] = {"off", "fan_only", "cool"};
|
||||
const char *fan_speed_names[] = {"auto"};
|
||||
|
||||
const uint16_t code_off[] = {3478, 1717, 452, 418, 452, 1285, 452, 418, 452, 424, 452, 419, 451, 420, 452, 419, 451, 424, 451, 420, 451, 420, 452, 419, 451, 424, 451, 419, 451, 1285, 452, 419, 452, 424, 451, 419, 452, 419, 452, 419, 452, 423, 452, 418, 451, 1284, 452, 1284, 451, 1289, 452, 419, 452, 418, 452, 1285, 452, 423, 451, 420, 451, 420, 452, 419, 452, 423, 451, 420, 452, 420, 451, 419, 452, 423, 452, 419, 452, 419, 452, 419, 452, 423, 452, 420, 451, 420, 451, 420, 451, 423, 452, 419, 452, 419, 452, 420, 451, 423, 452, 419, 452, 420, 451, 419, 452, 423, 452, 419, 452, 419, 452, 420, 451, 423, 452, 418, 452, 1283, 452, 1285, 452, 424, 451, 420, 451, 420, 451, 420, 451, 419, 451, 9924, 3478, 1716, 452, 419, 451, 1284, 453, 420, 450, 425, 450, 420, 452, 419, 452, 419, 452, 423, 452, 420, 451, 420, 451, 420, 451, 423, 452, 418, 451, 1286, 451, 420, 451, 423, 452, 420, 451, 420, 451, 420, 451, 424, 451, 418, 451, 1285, 503, 1232, 451, 1289, 452, 420, 451, 418, 452, 1285, 451, 424, 451, 421, 450, 420, 451, 421, 450, 424, 451, 420, 451, 421, 450, 420, 451, 424, 451, 420, 451, 420, 451, 419, 452, 424, 451, 420, 451, 420, 451, 419, 450, 1290, 451, 419, 450, 1287, 504, 367, 451, 423, 451, 419, 450, 1285, 451, 1286, 450, 423, 451, 1284, 451, 1286, 506, 365, 506, 369, 451, 420, 451, 419, 506, 366, 505, 369, 505, 367, 450, 420, 506, 364, 506, 1236, 508, 1227, 506, 1230, 508, 1228, 506, 1234, 511, 358, 506, 1231, 505, 365, 504, 1235, 507, 1230, 505, 364, 508, 1228, 505, 1236, 507, 364, 505, 366, 506, 365, 507, 368, 506, 366, 505, 365, 451, 420, 506, 369, 506, 365, 506, 365, 506, 364, 507, 368, 506, 365, 505, 1229, 508, 1228, 451, 1289, 507, 364, 507, 365, 506, 365, 504, 370, 451, 421, 506, 366, 504, 366, 450, 425, 504, 365, 450, 1285, 451, 1285, 450, 1291, 504, 366, 505, 367, 504, 367, 450, 425, 506, 365, 450, 421, 450, 421, 510, 366, 449, 421, 450, 421, 506, 365, 504, 371, 506, 364, 506, 366, 505, 366, 506, 367, 451, 1286, 506, 366, 449, 422, 532, 342, 451, 420, 507, 364, 507, 363, 504, 1238, 506, 365, 504, 367, 505, 366, 506, 370, 508, 364, 450, 422, 449, 421, 506, 372, 506, 364, 506, 366, 450, 421, 450, 427, 450, 421, 450, 421, 532, 339, 450, 425, 507, 1229, 450, 1285, 506, 1229, 451, 1292, 507, 364, 507, 365, 535, 336, 450, 422, 506};
|
||||
const uint16_t code_cool_auto[3][439] = {
|
||||
{3477, 1718, 451, 419, 451, 1286, 450, 421, 451, 425, 449, 421, 451, 421, 450, 420, 451, 424, 451, 420, 451, 421, 450, 421, 450, 424, 451, 419, 451, 1286, 451, 420, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 419, 451, 1285, 450, 1285, 451, 1289, 452, 420, 450, 420, 450, 1287, 450, 424, 451, 421, 450, 420, 451, 420, 451, 424, 451, 421, 450, 421, 450, 421, 450, 425, 450, 421, 450, 421, 450, 421, 451, 424, 451, 420, 450, 421, 451, 420, 451, 424, 451, 420, 450, 421, 451, 421, 450, 424, 451, 421, 450, 420, 451, 421, 450, 424, 451, 421, 450, 421, 450, 421, 451, 423, 451, 419, 451, 1285, 450, 1286, 451, 424, 451, 420, 451, 420, 451, 421, 451, 418, 451, 9926, 3477, 1717, 451, 419, 451, 1286, 450, 421, 451, 423, 452, 421, 450, 420, 451, 420, 451, 424, 451, 420, 451, 420, 451, 421, 450, 424, 451, 419, 451, 1285, 452, 420, 451, 424, 450, 421, 451, 420, 451, 420, 451, 424, 451, 419, 450, 1285, 451, 1285, 451, 1289, 451, 421, 450, 419, 451, 1286, 451, 424, 451, 420, 451, 420, 451, 420, 451, 425, 450, 421, 450, 420, 451, 421, 450, 424, 451, 420, 451, 421, 450, 421, 450, 425, 451, 420, 451, 420, 451, 418, 451, 1290, 451, 419, 450, 1286, 452, 420, 450, 425, 451, 420, 451, 420, 451, 420, 451, 422, 451, 1285, 451, 1286, 450, 420, 452, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 420, 451, 420, 451, 418, 451, 1292, 451, 1284, 452, 1285, 450, 1285, 451, 1290, 451, 419, 450, 1287, 450, 419, 451, 1289, 450, 1287, 450, 419, 451, 1284, 451, 1290, 451, 420, 451, 421, 450, 421, 450, 424, 451, 421, 450, 421, 451, 420, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 419, 450, 1284, 452, 1285, 451, 1289, 451, 420, 451, 420, 451, 420, 451, 424, 451, 420, 452, 420, 450, 421, 450, 425, 451, 418, 451, 1285, 451, 1284, 451, 1290, 450, 421, 451, 420, 451, 421, 450, 424, 451, 421, 450, 420, 451, 421, 450, 424, 451, 420, 451, 421, 450, 421, 450, 424, 451, 420, 452, 419, 451, 421, 451, 423, 450, 1286, 451, 420, 451, 420, 451, 424, 451, 420, 451, 420, 451, 419, 451, 1290, 451, 420, 451, 421, 451, 420, 451, 426, 451, 420, 451, 420, 451, 420, 451, 426, 452, 420, 451, 419, 451, 421, 451, 425, 452, 420, 451, 420, 451, 420, 451, 425, 451, 1286, 450, 421, 451, 418, 451, 1292, 451, 420, 452, 419, 452, 420, 451, 420, 451}, // 24
|
||||
{3478, 1717, 452, 417, 452, 1285, 452, 420, 451, 424, 451, 420, 451, 419, 452, 420, 451, 424, 451, 420, 452, 419, 451, 420, 452, 423, 452, 418, 451, 1286, 451, 420, 452, 423, 451, 420, 451, 419, 452, 420, 452, 423, 451, 419, 451, 1284, 452, 1283, 452, 1289, 452, 419, 452, 418, 452, 1285, 452, 423, 452, 419, 452, 419, 452, 420, 451, 423, 452, 418, 453, 419, 452, 419, 452, 423, 452, 420, 451, 420, 451, 420, 451, 424, 451, 420, 451, 420, 451, 420, 452, 423, 451, 420, 451, 420, 452, 419, 452, 423, 452, 419, 452, 420, 451, 419, 452, 423, 452, 419, 452, 419, 452, 420, 456, 419, 451, 418, 452, 1283, 452, 1286, 451, 423, 452, 420, 451, 419, 452, 420, 451, 418, 452, 9925, 3477, 1718, 451, 418, 451, 1286, 451, 420, 452, 423, 452, 419, 452, 419, 452, 420, 451, 423, 452, 419, 452, 420, 451, 420, 451, 423, 452, 418, 452, 1285, 452, 419, 452, 423, 451, 420, 452, 419, 452, 420, 502, 372, 452, 417, 452, 1284, 452, 1284, 451, 1289, 452, 420, 451, 418, 452, 1285, 452, 422, 453, 418, 453, 419, 452, 420, 451, 423, 452, 420, 451, 419, 452, 420, 452, 422, 452, 419, 452, 420, 452, 419, 452, 424, 450, 420, 452, 419, 452, 418, 451, 1289, 452, 418, 452, 1285, 452, 419, 452, 423, 451, 419, 451, 1285, 452, 419, 452, 422, 452, 1284, 451, 1285, 452, 420, 451, 424, 451, 420, 451, 420, 451, 419, 453, 423, 451, 420, 452, 419, 451, 419, 451, 1292, 451, 1284, 452, 1284, 451, 1284, 452, 1289, 452, 417, 452, 1285, 452, 418, 452, 1287, 452, 1285, 452, 418, 452, 1283, 452, 1290, 451, 419, 452, 419, 452, 420, 451, 423, 452, 419, 452, 420, 451, 420, 451, 423, 452, 420, 452, 419, 452, 419, 452, 423, 452, 417, 452, 1284, 452, 1283, 452, 1289, 452, 419, 452, 419, 452, 420, 451, 423, 452, 420, 451, 420, 451, 420, 452, 423, 452, 417, 452, 1284, 452, 1283, 452, 1289, 452, 420, 451, 419, 452, 420, 451, 423, 452, 419, 452, 420, 451, 420, 451, 423, 452, 419, 452, 420, 451, 420, 451, 424, 451, 419, 452, 420, 452, 419, 452, 422, 451, 1286, 451, 420, 451, 420, 451, 424, 451, 420, 452, 418, 453, 417, 452, 1291, 451, 419, 452, 419, 452, 422, 449, 426, 451, 420, 451, 420, 451, 420, 452, 425, 451, 420, 452, 419, 452, 420, 451, 424, 453, 419, 452, 419, 452, 420, 452, 424, 451, 1284, 451, 1285, 453, 418, 451, 1291, 452, 420, 451, 420, 451, 420, 451, 421, 451}, // 25
|
||||
{3478, 1717, 451, 419, 451, 1286, 451, 420, 451, 424, 451, 421, 450, 421, 450, 421, 451, 423, 451, 420, 452, 420, 451, 420, 451, 424, 451, 418, 451, 1286, 451, 420, 452, 423, 451, 421, 450, 421, 451, 420, 451, 424, 450, 419, 451, 1285, 451, 1284, 451, 1290, 451, 420, 451, 419, 451, 1286, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 420, 451, 421, 450, 420, 451, 424, 451, 420, 451, 421, 450, 420, 451, 425, 450, 421, 450, 421, 450, 421, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 419, 451, 1285, 450, 1287, 451, 424, 450, 420, 451, 420, 452, 420, 451, 418, 451, 9926, 3477, 1717, 451, 419, 451, 1286, 451, 420, 451, 424, 451, 421, 450, 420, 451, 420, 451, 424, 451, 420, 451, 420, 452, 419, 452, 424, 450, 419, 451, 1286, 451, 420, 451, 424, 451, 420, 451, 420, 451, 421, 450, 424, 451, 419, 451, 1285, 450, 1285, 451, 1290, 450, 421, 451, 418, 451, 1287, 451, 423, 451, 420, 451, 421, 450, 421, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 420, 451, 420, 451, 420, 451, 424, 451, 421, 450, 420, 451, 419, 451, 1290, 450, 420, 450, 1286, 451, 421, 451, 423, 451, 421, 451, 418, 451, 1286, 451, 423, 451, 1284, 451, 1286, 451, 420, 451, 424, 451, 420, 451, 420, 451, 420, 451, 425, 450, 420, 451, 421, 450, 419, 451, 1292, 451, 1284, 451, 1286, 450, 1285, 451, 1290, 450, 419, 451, 1286, 451, 419, 451, 1288, 451, 1286, 456, 414, 450, 1285, 451, 1290, 450, 421, 451, 420, 451, 420, 451, 424, 451, 420, 451, 421, 450, 421, 450, 425, 450, 421, 450, 421, 451, 420, 451, 424, 451, 419, 450, 1285, 450, 1286, 450, 1290, 451, 420, 451, 420, 451, 420, 451, 424, 451, 420, 451, 421, 450, 421, 451, 424, 450, 419, 451, 1285, 450, 1285, 451, 1290, 450, 421, 451, 420, 451, 420, 451, 425, 450, 421, 450, 421, 450, 420, 451, 425, 450, 420, 451, 421, 451, 420, 451, 424, 450, 421, 450, 421, 451, 421, 450, 423, 450, 1286, 451, 421, 450, 421, 450, 425, 451, 419, 452, 420, 450, 420, 450, 1291, 451, 420, 451, 420, 451, 421, 450, 426, 451, 421, 450, 421, 451, 420, 451, 426, 451, 420, 451, 420, 451, 420, 451, 427, 450, 420, 452, 420, 450, 421, 451, 425, 450, 1287, 450, 419, 451, 1285, 450, 1293, 450, 421, 451, 420, 451, 420, 451, 421, 451} // 26
|
||||
};
|
||||
|
||||
size_t getInfraredCode(uint8_t mode, uint8_t fan_speed, uint8_t temperature_index, const uint16_t **codePtr)
|
||||
{
|
||||
if (mode == 0)
|
||||
{
|
||||
// Off
|
||||
*codePtr = &(code_off[0]);
|
||||
return sizeof(code_off) / sizeof(uint16_t);
|
||||
}
|
||||
else if (mode == 1)
|
||||
{
|
||||
*codePtr = &(code_off[0]);
|
||||
return sizeof(code_off) / sizeof(uint16_t);
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
// Cool
|
||||
if (fan_speed == 0)
|
||||
{
|
||||
// Auto
|
||||
Serial.printf("Returning cool auto code at temperature index %d\n", temperature_index);
|
||||
*codePtr = &(code_cool_auto[temperature_index][0]);
|
||||
return sizeof(code_cool_auto[temperature_index]) / sizeof(uint16_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Didn't capture the other fan speeds yet
|
||||
// Will do that later
|
||||
*codePtr = &(code_off[0]);
|
||||
return sizeof(code_off) / sizeof(uint16_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
AirConditioner ac = {
|
||||
.max_temperature = 26,
|
||||
.min_temperature = 24,
|
||||
.modes = 3,
|
||||
.mode_names = mode_names,
|
||||
.fan_speeds = 4,
|
||||
.fan_speed_names = fan_speed_names,
|
||||
.getInfraredCode = &getInfraredCode};
|
||||
|
||||
ClimateCard climateCard = ClimateCard(4, ac, RMT_CHANNEL_1);
|
||||
#endif
|
||||
|
||||
void input_change_callback(uint8_t pin, uint8_t value)
|
||||
{
|
||||
Serial.print("Input change callback: ");
|
||||
Serial.print(pin);
|
||||
Serial.print(" ");
|
||||
Serial.println(value);
|
||||
espmega.outputs.toggleState(pin);
|
||||
}
|
||||
|
||||
void mqtt_callback(char *topic, char *payload)
|
||||
{
|
||||
Serial.print("MQTT Callback: ");
|
||||
Serial.print(topic);
|
||||
Serial.print(" ");
|
||||
Serial.println(payload);
|
||||
}
|
||||
|
||||
#ifdef WRITE_DEFAULT_NETCONF
|
||||
void setNetworkConfig()
|
||||
{
|
||||
NetworkConfig config = {
|
||||
.ip = {192, 168, 0, 249},
|
||||
.gateway = {192, 168, 0, 1},
|
||||
.subnet = {255, 255, 255, 0},
|
||||
.dns1 = {10, 192, 1, 1},
|
||||
.dns2 = {10, 192, 1, 1},
|
||||
.useStaticIp = true,
|
||||
.useWifi = false,
|
||||
.wifiUseAuth = false,
|
||||
};
|
||||
strcpy(config.ssid, "ssid");
|
||||
strcpy(config.password, "password");
|
||||
strcpy(config.hostname, "espmega");
|
||||
Serial.println("Setting network config");
|
||||
espmega.iot->setNetworkConfig(config);
|
||||
espmega.iot->saveNetworkConfig();
|
||||
}
|
||||
|
||||
void setMqttConfig()
|
||||
{
|
||||
MqttConfig config = {
|
||||
.mqtt_port = 1883,
|
||||
.mqtt_useauth = false};
|
||||
strcpy(config.mqtt_server, "192.168.0.26");
|
||||
strcpy(config.base_topic, "/espmegacud");
|
||||
espmega.iot->setMqttConfig(config);
|
||||
espmega.iot->saveMqttConfig();
|
||||
}
|
||||
#endif
|
||||
|
||||
void setup()
|
||||
{
|
||||
ESP_LOGI("Initializer", "Starting ESPMegaPRO OOP demo");
|
||||
espmega.begin();
|
||||
#ifdef EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
|
||||
espmega.installCard(6, &externalDigitalOutputCard);
|
||||
#endif
|
||||
#ifdef EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
|
||||
espmega.installCard(7, &externalDigitalInputCard);
|
||||
externalDigitalInputCard.registerCallback(handleExternalDigitalInput);
|
||||
#endif
|
||||
espmega.setTimezone("UTC-7");
|
||||
ESP_LOGI("Initializer", "Enabling IOT module");
|
||||
espmega.enableIotModule();
|
||||
ESP_LOGI("Initializer", "Enabling Ethernet");
|
||||
ETH.begin();
|
||||
ESP_LOGI("Initializer", "Binding Ethernet to IOT module");
|
||||
espmega.iot->bindEthernetInterface(Ð);
|
||||
#ifdef WRITE_DEFAULT_NETCONF
|
||||
setNetworkConfig();
|
||||
#else
|
||||
ESP_LOGI("Initializer", "Loading network config");
|
||||
espmega.iot->loadNetworkConfig();
|
||||
#endif
|
||||
ESP_LOGI("Initializer", "Connecting to network");
|
||||
espmega.iot->connectNetwork();
|
||||
#ifdef WRITE_DEFAULT_NETCONF
|
||||
setMqttConfig();
|
||||
#else
|
||||
ESP_LOGI("Initializer", "Loading MQTT config");
|
||||
espmega.iot->loadMqttConfig();
|
||||
#endif
|
||||
ESP_LOGI("Initializer", "Connecting to MQTT");
|
||||
espmega.iot->connectToMqtt();
|
||||
espmega.iot->registerMqttCallback(mqtt_callback);
|
||||
// Set debounce time
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
espmega.inputs.setDebounceTime(i, INPUT_DEBOUNCE_TIME_MS);
|
||||
}
|
||||
// Set all pin values to 4095
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
espmega.outputs.setValue(i, 4095);
|
||||
}
|
||||
#ifdef MQTT_CARD_REGISTER
|
||||
ESP_LOGI("Initializer", "Registering cards 0");
|
||||
espmega.iot->registerCard(0);
|
||||
ESP_LOGI("Initializer", "Registering cards 1");
|
||||
espmega.iot->registerCard(1);
|
||||
#ifdef EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
|
||||
ESP_LOGI("Initializer", "Registering cards 6");
|
||||
espmega.iot->registerCard(6);
|
||||
#endif
|
||||
#ifdef EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
|
||||
ESP_LOGI("Initializer", "Registering cards 7");
|
||||
espmega.iot->registerCard(7);
|
||||
#endif
|
||||
#endif
|
||||
ESP_LOGI("Initializer", "Registering Input change callback");
|
||||
espmega.inputs.registerCallback(input_change_callback);
|
||||
#ifdef CLIMATE_CARD_ENABLE
|
||||
ESP_LOGI("Initializer", "Installing climate card");
|
||||
espmega.installCard(2, &climateCard);
|
||||
ESP_LOGI("Initializer", "Binding climate card to FRAM");
|
||||
climateCard.bindFRAM(&espmega.fram, 1001);
|
||||
ESP_LOGI("Initializer", "Loading climate card state from FRAM");
|
||||
climateCard.loadStateFromFRAM();
|
||||
ESP_LOGI("Initializer", "Enabling climate card FRAM autosave");
|
||||
climateCard.setFRAMAutoSave(true);
|
||||
ESP_LOGI("Initializer", "Registering cards 2");
|
||||
espmega.iot->registerCard(2);
|
||||
#endif
|
||||
#ifdef DISPLAY_ENABLE
|
||||
ESP_LOGI("Initializer", "Enabling internal display");
|
||||
espmega.enableInternalDisplay(&Serial);
|
||||
ESP_LOGI("Initializer", "Binding climate card to internal display");
|
||||
#ifdef CLIMATE_CARD_ENABLE
|
||||
espmega.display->bindClimateCard(&climateCard);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WEB_SERVER_ENABLE
|
||||
ESP_LOGI("Initializer", "Enabling web server");
|
||||
espmega.enableWebServer(80);
|
||||
espmega.webServer->setWebUsername("admin");
|
||||
espmega.webServer->setWebPassword("Passw0rd");
|
||||
espmega.webServer->saveCredentialsToFRAM();
|
||||
#endif
|
||||
#ifdef LCD_OTA_ENABLE
|
||||
internalDisplayOTA.begin("/display", espmega.display, espmega.webServer);
|
||||
#endif
|
||||
#ifdef REMOTE_VARIABLE_ENABLE
|
||||
ESP_LOGI("Initializer", "Initializing testvar");
|
||||
testVar.begin(32, "/xm/fan_speed", espmega.iot, true, "/pm/request_fan_speed");
|
||||
testVar.enableSetValue("/pm/request_switch_state");
|
||||
#endif
|
||||
#ifdef CT_ENABLE
|
||||
ESP_LOGI("Initializer", "Initializing analog card");
|
||||
analogCard.begin();
|
||||
ESP_LOGI("Initializer", "Initializing current transformer");
|
||||
ct.begin();
|
||||
ct.bindFRAM(&espmega.fram, 7000);
|
||||
ct.loadEnergy();
|
||||
ct.setEnergyAutoSave(true);
|
||||
espmega.iot->registerCard(3);
|
||||
#endif
|
||||
#ifdef SMART_VARIABLE_ENABLE
|
||||
ESP_LOGI("Initializer", "Initializing smart variable");
|
||||
smartVar.begin(16);
|
||||
ESP_LOGI("Initializer", "Binding smart variable to FRAM");
|
||||
smartVar.bindFRAM(&espmega.fram, 8000);
|
||||
ESP_LOGI("Initializer", "Enabling smart variable autosave");
|
||||
smartVar.setValueAutoSave(true);
|
||||
ESP_LOGI("Initializer", "Enabling IoT for smart variable");
|
||||
smartVar.enableIoT(espmega.iot, "/smartvar");
|
||||
ESP_LOGI("Initializer", "Enabling smart variable set value");
|
||||
smartVar.enableSetValue("/smartvar/set");
|
||||
ESP_LOGI("Initializer", "Enabling smart variable value request");
|
||||
smartVar.enableValueRequest("/smartvar/request");
|
||||
#endif
|
||||
ESP_LOGI("Initializer", "Setup complete");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
espmega.loop();
|
||||
#ifdef FRAM_DEBUG
|
||||
// Every 20 seconds, dump FRAM 0-500 to serial
|
||||
static uint32_t last_fram_dump = 0;
|
||||
if (millis() - last_fram_dump >= 20000)
|
||||
{
|
||||
last_fram_dump = millis();
|
||||
Serial.println("Dumping FRAM");
|
||||
espmega.dumpFRAMtoSerial(0, 500);
|
||||
Serial.println("Dumping FRAM ASCII");
|
||||
espmega.dumpFRAMtoSerialASCII(0, 500);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Every 5 seconds, publish "I'm alive" to MQTT
|
||||
#ifdef MQTT_DEBUG
|
||||
static uint32_t last_mqtt_publish = 0;
|
||||
if (millis() - last_mqtt_publish >= 5000)
|
||||
{
|
||||
last_mqtt_publish = millis();
|
||||
espmega.iot->publish("/espmegai/alive", "true");
|
||||
}
|
||||
static uint32_t last_mqtt_status = 0;
|
||||
if (millis() - last_mqtt_status >= 1000)
|
||||
{
|
||||
last_mqtt_status = millis();
|
||||
Serial.print("MQTT Status: ");
|
||||
Serial.println(espmega.iot->mqttConnected() ? "Connected" : "Disconnected");
|
||||
}
|
||||
#endif
|
||||
#ifdef REMOTE_VARIABLE_ENABLE
|
||||
// Print out testvar value every 5 seconds
|
||||
static uint32_t last_testvar_print = 0;
|
||||
if (millis() - last_testvar_print >= 5000)
|
||||
{
|
||||
last_testvar_print = millis();
|
||||
if (testVar.getValue() != nullptr)
|
||||
ESP_LOGI("TestVar", "Value: %s", testVar.getValue());
|
||||
testVar.setValue("Hello World");
|
||||
}
|
||||
#endif
|
||||
#ifdef CT_ENABLE
|
||||
ct.loop();
|
||||
static uint32_t last_ct_print = 0;
|
||||
if (millis() - last_ct_print >= 1000)
|
||||
{
|
||||
last_ct_print = millis();
|
||||
Serial.print("Current: ");
|
||||
Serial.println(ct.getCurrent());
|
||||
}
|
||||
#endif
|
||||
#ifdef SMART_VARIABLE_ENABLE
|
||||
static uint32_t last_smartvar_print = 0;
|
||||
if (millis() - last_smartvar_print >= 1000)
|
||||
{
|
||||
last_smartvar_print = millis();
|
||||
Serial.print("SmartVar: ");
|
||||
Serial.println(smartVar.getValue());
|
||||
}
|
||||
|
||||
static bool last_smartvar_state = false;
|
||||
static uint32_t last_smartvar_state_change = 0;
|
||||
if (millis() - last_smartvar_state_change >= 5000)
|
||||
{
|
||||
last_smartvar_state_change = millis();
|
||||
last_smartvar_state = !last_smartvar_state;
|
||||
smartVar.setValue(last_smartvar_state ? "true" : "false");
|
||||
}
|
||||
#endif
|
||||
#ifdef RTC_DEBUG
|
||||
static uint32_t last_time_print = 0;
|
||||
if (millis() - last_time_print >= 1000)
|
||||
{
|
||||
last_time_print = millis();
|
||||
rtctime_t time = espmega.getTime();
|
||||
Serial.printf("Time: %02d:%02d:%02d %02d/%02d/%04d\n", time.hours, time.minutes, time.seconds, time.day, time.month, time.year);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
#include <ESPMegaProOS.hpp>
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
|
||||
void setup() {
|
||||
espmega.begin();
|
||||
uint8_t outputPinMap[16] = {8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7};
|
||||
espmega.outputs.loadPinMap(outputPinMap);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
espmega.loop();
|
||||
// Loop through all outputs, turning them on one at a time
|
||||
// for 1.5 seconds each
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
Serial.printf("Turning on output %d\n", i);
|
||||
espmega.outputs.digitalWrite(i, true);
|
||||
// Print out the state of all outputs
|
||||
for (uint8_t j = 0; j < 16; j++) {
|
||||
Serial.printf("Output %d: %d\n", j, espmega.outputs.getState(j));
|
||||
}
|
||||
delay(1500);
|
||||
espmega.outputs.digitalWrite(i, false);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#include <ESPMegaProOS.hpp>
|
||||
#include <IRBlaster.hpp>
|
||||
#include <IRReceiver.hpp>
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
IRBlaster irBlaster = IRBlaster(4);
|
||||
|
||||
uint16_t data[] = { 2441, 579, 621, 580, 620, 580, 620, 580, 621, 579, 621, 579, 1220, 580, 1220, 580, 1247, 554, 619, 580, 621, 580, 619, 581, 619, 26392, 2420, 580, 621, 580, 620, 579, 620, 581, 619, 581, 620, 580, 1219, 581, 1220, 580, 1220, 581, 619, 580, 620, 580, 620, 580, 621, 26385, 2421, 580, 621, 580, 620, 580, 620, 579, 621, 579, 621, 579, 1221, 579, 1221, 580, 1219, 580, 621, 580, 620, 580, 621, 579, 621, 26385, 2421, 580, 621, 579, 620, 580, 620, 580, 621, 579, 621, 579, 1221, 579, 1221, 580, 1219, 580, 621, 580, 620, 580, 620, 580, 621, 26384, 2422, 580, 621, 579, 621, 579, 620, 580, 620, 580, 621, 579, 1221, 579, 1221, 580, 1220, 580, 620, 580, 620, 579, 620, 581, 620};
|
||||
|
||||
void setup() {
|
||||
espmega.begin();
|
||||
IRReceiver::begin(15);
|
||||
|
||||
irBlaster.send(data, sizeof(data)/sizeof(uint16_t));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Starting long receive");
|
||||
IRReceiver::start_long_receive();
|
||||
delay(5000);
|
||||
Serial.println("Ending long receive");
|
||||
ir_data_t data = IRReceiver::end_long_receive();
|
||||
Serial.printf("Recived %d data points\n", data.size);
|
||||
for (size_t i = 0; i < data.size; i++) {
|
||||
Serial.print(data.data[i]);
|
||||
Serial.print(i == data.size-1 ? "\n" : ", ");
|
||||
}
|
||||
free(data.data);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "windows-gcc-x64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"compilerPath": "gcc",
|
||||
"cStandard": "${default}",
|
||||
"cppStandard": "${default}",
|
||||
"intelliSenseMode": "windows-gcc-x64",
|
||||
"compilerArgs": [
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C/C++ Runner: Debug Session",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"cwd": "d:/Git/ESPMegaPRO-v3-SDK/IRLearner",
|
||||
"program": "d:/Git/ESPMegaPRO-v3-SDK/IRLearner/build/Debug/outDebug",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "gcc",
|
||||
"C_Cpp_Runner.cppCompilerPath": "g++",
|
||||
"C_Cpp_Runner.debuggerPath": "gdb",
|
||||
"C_Cpp_Runner.cStandard": "",
|
||||
"C_Cpp_Runner.cppStandard": "",
|
||||
"C_Cpp_Runner.msvcBatchPath": "",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
||||
"C_Cpp_Runner.msvcSecureNoWarnings": false
|
||||
}
|
|
@ -12,5 +12,4 @@
|
|||
platform = espressif32
|
||||
board = wt32-eth01
|
||||
framework = arduino
|
||||
lib_deps = siwats/ESPMegaPROR3@^2.0.7
|
||||
monitor_speed = 115200
|
||||
lib_deps = siwats/espmegapror3
|
|
@ -0,0 +1,51 @@
|
|||
#include <ESPMegaPRO.h>
|
||||
|
||||
// Inputs
|
||||
const int DEBOUNCE_TIME_MS = 50;
|
||||
const int virtual_interrupt_pins[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
int virtual_interupt_state[16];
|
||||
unsigned long virtual_interupt_timer[16];
|
||||
|
||||
// Outputs
|
||||
const int pwm_pins[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
int pwm_states[16];
|
||||
int pwm_values[16];
|
||||
|
||||
// Forward declaration
|
||||
void virtual_interrupt_loop();
|
||||
void virtual_interrupt_callback(int pin, int state);
|
||||
|
||||
void setup()
|
||||
{
|
||||
ESPMega_begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
ESPMega_loop();
|
||||
}
|
||||
|
||||
void virtual_interrupt_callback(int pin, int state)
|
||||
{
|
||||
}
|
||||
|
||||
void virtual_interrupt_loop()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int current_pin_value = ESPMega_digitalRead(virtual_interrupt_pins[i]);
|
||||
if (virtual_interupt_state[i] != current_pin_value)
|
||||
{
|
||||
if (millis() - virtual_interupt_timer[i] > DEBOUNCE_TIME_MS)
|
||||
{
|
||||
virtual_interupt_state[i] = current_pin_value;
|
||||
virtual_interrupt_callback(i, current_pin_value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
virtual_interupt_timer[i] = millis();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"adafruit_ads1x15.h": "c"
|
||||
},
|
||||
"cmake.configureOnOpen": true,
|
||||
"cmake.sourceDirectory": "D:/Git/ESPMegaPRO-v3-SDK/Template Project/.pio/libdeps/wt32-eth01/Adafruit BusIO"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue